Repository: kennyalive/Quake-III-Arena-Kenny-Edition Branch: master Commit: 4a09579fb953 Files: 367 Total size: 7.7 MB Directory structure: gitextract_lfy_jed8/ ├── .github/ │ └── workflows/ │ └── msbuild.yml ├── .gitignore ├── COPYING.txt ├── README.md ├── README.txt ├── src/ │ ├── cgame/ │ │ ├── cg_consolecmds.c │ │ ├── cg_draw.c │ │ ├── cg_drawtools.c │ │ ├── cg_effects.c │ │ ├── cg_ents.c │ │ ├── cg_event.c │ │ ├── cg_info.c │ │ ├── cg_local.h │ │ ├── cg_localents.c │ │ ├── cg_main.c │ │ ├── cg_marks.c │ │ ├── cg_particles.c │ │ ├── cg_players.c │ │ ├── cg_playerstate.c │ │ ├── cg_predict.c │ │ ├── cg_public.h │ │ ├── cg_scoreboard.c │ │ ├── cg_servercmds.c │ │ ├── cg_snapshot.c │ │ ├── cg_syscalls.asm │ │ ├── cg_syscalls.c │ │ ├── cg_view.c │ │ ├── cg_weapons.c │ │ ├── cgame.bat │ │ ├── cgame.q3asm │ │ └── tr_types.h │ ├── engine/ │ │ ├── botlib/ │ │ │ ├── aasfile.h │ │ │ ├── be_aas_bsp.h │ │ │ ├── be_aas_bspq3.c │ │ │ ├── be_aas_cluster.c │ │ │ ├── be_aas_cluster.h │ │ │ ├── be_aas_debug.c │ │ │ ├── be_aas_debug.h │ │ │ ├── be_aas_def.h │ │ │ ├── be_aas_entity.c │ │ │ ├── be_aas_entity.h │ │ │ ├── be_aas_file.c │ │ │ ├── be_aas_file.h │ │ │ ├── be_aas_funcs.h │ │ │ ├── be_aas_main.c │ │ │ ├── be_aas_main.h │ │ │ ├── be_aas_move.c │ │ │ ├── be_aas_move.h │ │ │ ├── be_aas_optimize.c │ │ │ ├── be_aas_optimize.h │ │ │ ├── be_aas_reach.c │ │ │ ├── be_aas_reach.h │ │ │ ├── be_aas_route.c │ │ │ ├── be_aas_route.h │ │ │ ├── be_aas_routealt.c │ │ │ ├── be_aas_routealt.h │ │ │ ├── be_aas_sample.c │ │ │ ├── be_aas_sample.h │ │ │ ├── be_ai_char.c │ │ │ ├── be_ai_chat.c │ │ │ ├── be_ai_gen.c │ │ │ ├── be_ai_goal.c │ │ │ ├── be_ai_move.c │ │ │ ├── be_ai_weap.c │ │ │ ├── be_ai_weight.c │ │ │ ├── be_ai_weight.h │ │ │ ├── be_ea.c │ │ │ ├── be_interface.c │ │ │ ├── be_interface.h │ │ │ ├── l_crc.c │ │ │ ├── l_crc.h │ │ │ ├── l_libvar.c │ │ │ ├── l_libvar.h │ │ │ ├── l_log.c │ │ │ ├── l_log.h │ │ │ ├── l_memory.c │ │ │ ├── l_memory.h │ │ │ ├── l_precomp.c │ │ │ ├── l_precomp.h │ │ │ ├── l_script.c │ │ │ ├── l_script.h │ │ │ ├── l_struct.c │ │ │ ├── l_struct.h │ │ │ └── l_utils.h │ │ ├── client/ │ │ │ ├── cl_cgame.c │ │ │ ├── cl_cin.c │ │ │ ├── cl_console.c │ │ │ ├── cl_input.c │ │ │ ├── cl_keys.c │ │ │ ├── cl_main.c │ │ │ ├── cl_net_chan.c │ │ │ ├── cl_parse.c │ │ │ ├── cl_scrn.c │ │ │ ├── cl_ui.c │ │ │ ├── client.h │ │ │ ├── keys.h │ │ │ ├── snd_adpcm.c │ │ │ ├── snd_dma.c │ │ │ ├── snd_local.h │ │ │ ├── snd_mem.c │ │ │ ├── snd_mix.c │ │ │ ├── snd_public.h │ │ │ └── snd_wavelet.c │ │ ├── platform/ │ │ │ ├── resource.h │ │ │ ├── win_gamma.c │ │ │ ├── win_glimp.c │ │ │ ├── win_input.c │ │ │ ├── win_local.h │ │ │ ├── win_main.c │ │ │ ├── win_net.c │ │ │ ├── win_qgl.c │ │ │ ├── win_shared.c │ │ │ ├── win_snd.c │ │ │ ├── win_syscon.c │ │ │ ├── win_wndproc.c │ │ │ └── winquake.rc │ │ ├── qcommon/ │ │ │ ├── cm_load.c │ │ │ ├── cm_local.h │ │ │ ├── cm_patch.c │ │ │ ├── cm_patch.h │ │ │ ├── cm_polylib.c │ │ │ ├── cm_polylib.h │ │ │ ├── cm_public.h │ │ │ ├── cm_test.c │ │ │ ├── cm_trace.c │ │ │ ├── cmd.c │ │ │ ├── common.c │ │ │ ├── cvar.c │ │ │ ├── files.c │ │ │ ├── huffman.c │ │ │ ├── md4.c │ │ │ ├── msg.c │ │ │ ├── net_chan.c │ │ │ ├── qcommon.h │ │ │ ├── qfiles.h │ │ │ ├── unzip.c │ │ │ ├── unzip.h │ │ │ ├── vm.c │ │ │ ├── vm_interpreted.c │ │ │ └── vm_local.h │ │ ├── renderer/ │ │ │ ├── dx.cpp │ │ │ ├── dx.h │ │ │ ├── jpeg/ │ │ │ │ ├── stb_image.h │ │ │ │ └── tiny_jpeg.h │ │ │ ├── qgl.h │ │ │ ├── shaders/ │ │ │ │ ├── apply_gamma.comp │ │ │ │ ├── compile.bat │ │ │ │ ├── compile_hlsl.bat │ │ │ │ ├── hlsl_compiled/ │ │ │ │ │ ├── multi_texture_add_ge80_ps.cpp │ │ │ │ │ ├── multi_texture_add_gt0_ps.cpp │ │ │ │ │ ├── multi_texture_add_lt80_ps.cpp │ │ │ │ │ ├── multi_texture_add_ps.cpp │ │ │ │ │ ├── multi_texture_clipping_plane_vs.cpp │ │ │ │ │ ├── multi_texture_mul_ge80_ps.cpp │ │ │ │ │ ├── multi_texture_mul_gt0_ps.cpp │ │ │ │ │ ├── multi_texture_mul_lt80_ps.cpp │ │ │ │ │ ├── multi_texture_mul_ps.cpp │ │ │ │ │ ├── multi_texture_vs.cpp │ │ │ │ │ ├── single_texture_clipping_plane_vs.cpp │ │ │ │ │ ├── single_texture_ge80_ps.cpp │ │ │ │ │ ├── single_texture_gt0_ps.cpp │ │ │ │ │ ├── single_texture_lt80_ps.cpp │ │ │ │ │ ├── single_texture_ps.cpp │ │ │ │ │ └── single_texture_vs.cpp │ │ │ │ ├── multi_texture.vert │ │ │ │ ├── multi_texture_add.frag │ │ │ │ ├── multi_texture_clipping_plane.vert │ │ │ │ ├── multi_texture_mul.frag │ │ │ │ ├── shaders.hlsl │ │ │ │ ├── single_texture.frag │ │ │ │ ├── single_texture.vert │ │ │ │ ├── single_texture_clipping_plane.vert │ │ │ │ └── spirv/ │ │ │ │ ├── apply_gamma_comp.cpp │ │ │ │ ├── multi_texture_add_frag.cpp │ │ │ │ ├── multi_texture_clipping_plane_vert.cpp │ │ │ │ ├── multi_texture_mul_frag.cpp │ │ │ │ ├── multi_texture_vert.cpp │ │ │ │ ├── single_texture_clipping_plane_vert.cpp │ │ │ │ ├── single_texture_frag.cpp │ │ │ │ └── single_texture_vert.cpp │ │ │ ├── tr_animation.c │ │ │ ├── tr_backend.c │ │ │ ├── tr_bsp.c │ │ │ ├── tr_cmds.c │ │ │ ├── tr_curve.c │ │ │ ├── tr_font.c │ │ │ ├── tr_image.c │ │ │ ├── tr_init.c │ │ │ ├── tr_light.c │ │ │ ├── tr_local.h │ │ │ ├── tr_main.c │ │ │ ├── tr_marks.c │ │ │ ├── tr_mesh.c │ │ │ ├── tr_model.c │ │ │ ├── tr_noise.c │ │ │ ├── tr_public.h │ │ │ ├── tr_scene.c │ │ │ ├── tr_shade.c │ │ │ ├── tr_shade_calc.c │ │ │ ├── tr_shader.c │ │ │ ├── tr_shadows.c │ │ │ ├── tr_sky.c │ │ │ ├── tr_surface.c │ │ │ ├── tr_world.c │ │ │ ├── vk.cpp │ │ │ ├── vk.h │ │ │ └── vulkan/ │ │ │ ├── GLSL.std.450.h │ │ │ ├── spirv.h │ │ │ ├── spirv.hpp │ │ │ ├── spirv.hpp11 │ │ │ ├── spirv.json │ │ │ ├── spirv.lua │ │ │ ├── spirv.py │ │ │ ├── vk_icd.h │ │ │ ├── vk_layer.h │ │ │ ├── vk_layer_dispatch_table.h │ │ │ ├── vk_platform.h │ │ │ ├── vk_sdk_platform.h │ │ │ ├── vulkan.h │ │ │ └── vulkan.hpp │ │ └── server/ │ │ ├── server.h │ │ ├── sv_bot.c │ │ ├── sv_ccmds.c │ │ ├── sv_client.c │ │ ├── sv_game.c │ │ ├── sv_init.c │ │ ├── sv_main.c │ │ ├── sv_net_chan.c │ │ ├── sv_snapshot.c │ │ └── sv_world.c │ ├── game/ │ │ ├── ai_chat.c │ │ ├── ai_chat.h │ │ ├── ai_cmd.c │ │ ├── ai_cmd.h │ │ ├── ai_dmnet.c │ │ ├── ai_dmnet.h │ │ ├── ai_dmq3.c │ │ ├── ai_dmq3.h │ │ ├── ai_main.c │ │ ├── ai_main.h │ │ ├── ai_team.c │ │ ├── ai_team.h │ │ ├── ai_vcmd.c │ │ ├── ai_vcmd.h │ │ ├── be_aas.h │ │ ├── be_ai_char.h │ │ ├── be_ai_chat.h │ │ ├── be_ai_gen.h │ │ ├── be_ai_goal.h │ │ ├── be_ai_move.h │ │ ├── be_ai_weap.h │ │ ├── be_ea.h │ │ ├── bg_lib.c │ │ ├── bg_lib.h │ │ ├── bg_local.h │ │ ├── bg_misc.c │ │ ├── bg_pmove.c │ │ ├── bg_public.h │ │ ├── bg_slidemove.c │ │ ├── botlib.h │ │ ├── chars.h │ │ ├── g_active.c │ │ ├── g_arenas.c │ │ ├── g_bot.c │ │ ├── g_client.c │ │ ├── g_cmds.c │ │ ├── g_combat.c │ │ ├── g_items.c │ │ ├── g_local.h │ │ ├── g_main.c │ │ ├── g_mem.c │ │ ├── g_misc.c │ │ ├── g_missile.c │ │ ├── g_mover.c │ │ ├── g_public.h │ │ ├── g_rankings.c │ │ ├── g_rankings.h │ │ ├── g_session.c │ │ ├── g_spawn.c │ │ ├── g_svcmds.c │ │ ├── g_syscalls.asm │ │ ├── g_syscalls.c │ │ ├── g_target.c │ │ ├── g_team.c │ │ ├── g_team.h │ │ ├── g_trigger.c │ │ ├── g_utils.c │ │ ├── g_weapon.c │ │ ├── game.bat │ │ ├── game.q3asm │ │ ├── inv.h │ │ ├── match.h │ │ ├── menudef.h │ │ ├── q_math.c │ │ ├── q_shared.c │ │ ├── q_shared.h │ │ ├── surfaceflags.h │ │ └── syn.h │ └── q3_ui/ │ ├── keycodes.h │ ├── q3_ui.bat │ ├── q3_ui.q3asm │ ├── ui_addbots.c │ ├── ui_atoms.c │ ├── ui_cdkey.c │ ├── ui_cinematics.c │ ├── ui_confirm.c │ ├── ui_connect.c │ ├── ui_controls2.c │ ├── ui_credits.c │ ├── ui_demo2.c │ ├── ui_display.c │ ├── ui_gameinfo.c │ ├── ui_ingame.c │ ├── ui_local.h │ ├── ui_login.c │ ├── ui_main.c │ ├── ui_menu.c │ ├── ui_mfield.c │ ├── ui_mods.c │ ├── ui_network.c │ ├── ui_options.c │ ├── ui_playermodel.c │ ├── ui_players.c │ ├── ui_playersettings.c │ ├── ui_preferences.c │ ├── ui_public.h │ ├── ui_qmenu.c │ ├── ui_rankings.c │ ├── ui_rankstatus.c │ ├── ui_removebots.c │ ├── ui_serverinfo.c │ ├── ui_servers2.c │ ├── ui_setup.c │ ├── ui_signup.c │ ├── ui_sound.c │ ├── ui_sparena.c │ ├── ui_specifyleague.c │ ├── ui_specifyserver.c │ ├── ui_splevel.c │ ├── ui_sppostgame.c │ ├── ui_spreset.c │ ├── ui_spskill.c │ ├── ui_startserver.c │ ├── ui_syscalls.asm │ ├── ui_syscalls.c │ ├── ui_team.c │ ├── ui_teamorders.c │ └── ui_video.c ├── tools/ │ └── bin2hex.cpp └── visual-studio/ ├── DeclareDPIAware.manifest ├── botlib.vcxproj ├── botlib.vcxproj.filters ├── cgame.def ├── cgame.vcxproj ├── cgame.vcxproj.filters ├── game.def ├── game.vcxproj ├── game.vcxproj.filters ├── props/ │ └── shared.props ├── q3_ui.vcxproj ├── q3_ui.vcxproj.filters ├── quake3.sln ├── quake3.vcxproj ├── quake3.vcxproj.filters ├── renderer.vcxproj ├── renderer.vcxproj.filters └── ui.def ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/msbuild.yml ================================================ name: MSBuild on: push: branches: [ "master" ] paths-ignore: - '*.md' pull_request: branches: [ "master" ] paths-ignore: - '*.md' env: SOLUTION_FILE_PATH: "visual-studio/quake3.sln" BUILD_CONFIGURATION: Release jobs: build: runs-on: windows-latest steps: - uses: actions/checkout@v4 - name: Add MSBuild to PATH uses: microsoft/setup-msbuild@v2 - name: Restore NuGet packages working-directory: ${{env.GITHUB_WORKSPACE}} run: nuget restore ${{env.SOLUTION_FILE_PATH}} - name: Build working-directory: ${{env.GITHUB_WORKSPACE}} run: msbuild /m /p:Configuration=${{env.BUILD_CONFIGURATION}} ${{env.SOLUTION_FILE_PATH}} - name: Upload Executable uses: actions/upload-artifact@v4 with: name: quake3 path: bin/x64/Release/quake3-ke.exe ================================================ FILE: .gitignore ================================================ *.opendb *.opensdf *.sdf *.suo *.user bin/ tools/bin2hex.exe tools/bin2hex.obj visual-studio/.vs/ visual-studio/vk_report visual-studio/*.log visual-studio/*.psess visual-studio/*.vspx ================================================ FILE: COPYING.txt ================================================ GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS ================================================ FILE: README.md ================================================ # Welcome to Quake 3 source code! ![Actions Status](https://github.com/kennyalive/Quake-III-Arena-Kenny-Edition/actions/workflows/msbuild.yml/badge.svg) ## What's in this repository * This repository contains Quake 3 Arena source code which can be built with modern versions of Visual Studio. * Only Windows x64 platform is supported. * I don't try to fix bugs inherited from the original Q3 source code distribution - in this regard the project is Q3-bugs-friendly. I still make fixes to functionality that did not stand the test of time (SetDeviceGammaRamp). * Some functionality related to ancient graphics hardware was removed. Also I removed compilation of qvm code to native instructions to simplify maintenance. This means all game code is run through QVM which is slower than native execution but fast enough for modern computers. * No changes to visuals or gameplay. Vulkan backend is now enabled by default. This change was made due to issues with the SetDeviceGammaRamp API. ## Usage * Build `visual-studio/quake3.sln` solution and copy `quake3-ke.exe` to your local Quake-III-Arena installation folder. * To debug the game from Visual Studio, go to `quake3` project settings -> Debugging -> Command Arguments. Specify the game installation location: `+set fs_basepath ` ## Vulkan support The Vulkan backend supports everything provided by the original OpenGL version, including all available `r_` cvars. No new features have been added; the goal is to preserve existing functionality rather than expand it. #### New cvars: * **r_renderAPI** - 3D API to use. Requires vid_restart. * 0 - OpenGL * 1 - Vulkan * **r_gpu** - Select GPU in multi-GPU system (zero-based index). By default, GPU 0 is selected. Requires vid_restart. * **r_vsync** - Enable vsync in Vulkan. Requires vid_restart. * **r_shaderGamma** - Use compute shader to apply gamma instead of legacy HW gamma API (SetDeviceGammaRamp). * **r_twinMode** - Debug feature to compare rendering output between OpenGL/Vulkan APIs. Requires vid_restart. ![twin_mode](https://user-images.githubusercontent.com/4964024/34961607-48aae882-fa40-11e7-9bf0-d4400afdad34.jpg) #### Additional information: * Q: How to start game with vulkan support? A: `quake3-ke.exe +set r_renderAPI 1`. * Q: How to enable vulkan support from Q3 console? A: `\r_renderAPI 1` then `\vid_restart`. * Q: How to enable twin mode from Q3 console? A: `\r_twinMode 1` or `\r_twinMode 1` then `\vid_restart`. * Q: How to check that Vulkan backend is really active? A: `gfxinfo` console command reports information about active rendering backend. ![quake3-ke](https://user-images.githubusercontent.com/4964024/28160268-4f0707d4-67c8-11e7-9009-8540789aab0b.jpeg) ================================================ FILE: README.txt ================================================ Quake III Arena GPL source release ================================== This file contains the following sections: LICENSE GENERAL NOTES COMPILING ON WIN32 COMPILING ON GNU/LINUX COMPILING ON MAC LICENSE ======= See COPYING.txt for the GNU GENERAL PUBLIC LICENSE Some source code in this release is not covered by the GPL: IO on .zip files using portions of zlib ----------------------------------------------------------------------------- lines file(s) 4299 code/qcommon/unzip.c 4546 libs/pak/unzip.cpp Copyright (C) 1998 Gilles Vollant zlib is Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. MD4 Message-Digest Algorithm ----------------------------------------------------------------------------- lines file(s) 299 code/qcommon/md4.c 277 common/md4.c Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All rights reserved. License to copy and use this software is granted provided that it is identified as the <93>RSA Data Security, Inc. MD4 Message-Digest Algorithm<94> in all mater ial mentioning or referencing this software or this function. License is also granted to make and use derivative works provided that such work s are identified as <93>derived from the RSA Data Security, Inc. MD4 Message-Dig est Algorithm<94> in all material mentioning or referencing the derived work. RSA Data Security, Inc. makes no representations concerning either the merchanta bility of this software or the suitability of this software for any particular p urpose. It is provided <93>as is<94> without express or implied warranty of any kind. checksums are used to validate pak files standard C library replacement routines ----------------------------------------------------------------------------- lines file(s) 1324 code/game/bg_lib.c Copyright (c) 1992, 1993 The Regents of the University of California. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by the University of California, Berkeley and its contributors. 4. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ADPCM coder/decoder ----------------------------------------------------------------------------- lines file(s) 330 code/client/snd_adpcm.c Copyright 1992 by Stichting Mathematisch Centrum, Amsterdam, The Netherlands. All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the names of Stichting Mathematisch Centrum or CWI not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. JPEG library ----------------------------------------------------------------------------- code/jpeg-6 libs/jpeg6 Copyright (C) 1991-1995, Thomas G. Lane Permission is hereby granted to use, copy, modify, and distribute this software (or portions thereof) for any purpose, without fee, subject to these conditions: (1) If any part of the source code for this software is distributed, then this README file must be included, with this copyright and no-warranty notice unaltered; and any additions, deletions, or changes to the original files must be clearly indicated in accompanying documentation. (2) If only executable code is distributed, then the accompanying documentation must state that "this software is based in part on the work of the Independent JPEG Group". (3) Permission for use of this software is granted only if the user accepts full responsibility for any undesirable consequences; the authors accept NO LIABILITY for damages of any kind. These conditions apply to any software derived from or based on the IJG code, not just to the unmodified library. If you use our work, you ought to acknowledge us. NOTE: unfortunately the README that came with our copy of the library has been lost, so the one from release 6b is included instead. There are a few 'glue type' modifications to the library to make it easier to use from the engine, but otherwise the dependency can be easily cleaned up to a better release of the library. GENERAL NOTES ============= A short summary of the file layout: code/ Quake III Arena source code ( renderer, game code, OS layer etc. ) code/bspc bot routes compiler source code lcc/ the retargetable C compiler ( produces assembly to be turned into qvm bytecode by q3asm ) q3asm/ assembly to qvm bytecode compiler q3map/ map compiler ( .map -> .bsp ) - this is the version that comes with Q3Radiant 200f q3radiant/ Q3Radiant map editor build 200f ( common/ and libs/ are support dirs for radiant ) While we made sure we were still able to compile the game on Windows, GNU/Linux and Mac, this build didn't get any kind of extensive testing so it may not work completely right. Whenever an id game is released under GPL, several projects start making the source code more friendly to nowaday's compilers and environements. If you are picking up this release weeks/months/years after we uploaded it, you probably want to look around on the net for cleaned up versions of this codebase as well. COMPILING ON WIN32 ================== VC7 / Visual C++ 2003 project files are provided: code/quake3.sln q3radiant/Radiant.sln To compile the qvms, you need to run some batch files: you will need to have lcc.exe q3cpp.exe q3rcc.exe and q3asm.exe in your path ( some precompiled binaries are provided in lcc/bin and code/win32/mod-sdk-setup/bin ) the qvm batch files are in code/game code/cgame code/q3_ui code/ui .. COMPILING ON GNU/LINUX ================== the build system using cons, which may be known as scons's perl ancestor now you don't have to track it down though, the build script is provided in the tree you will need nasm and gcc 2.95 make sure you have the X Direct Graphics Access and X Video Mode extensions headers for your X11 a typical compile command goes like this: [..]/code$ ./unix/cons -- gcc=gcc-2.95 g++=g++-2.95 COMPILING ON MAC ================ project file for OSX compile is in code/macosx/Quake3.pbproj ================================================ FILE: src/cgame/cg_consolecmds.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_consolecmds.c -- text commands typed in at the local console, or // executed by a key binding #include "cg_local.h" void CG_TargetCommand_f( void ) { int targetNum; char test[4]; targetNum = CG_CrosshairPlayer(); if (!targetNum ) { return; } trap_Argv( 1, test, 4 ); trap_SendConsoleCommand( va( "gc %i %i", targetNum, atoi( test ) ) ); } /* ================= CG_SizeUp_f Keybinding command ================= */ static void CG_SizeUp_f (void) { trap_Cvar_Set("cg_viewsize", va("%i",(int)(cg_viewsize.integer+10))); } /* ================= CG_SizeDown_f Keybinding command ================= */ static void CG_SizeDown_f (void) { trap_Cvar_Set("cg_viewsize", va("%i",(int)(cg_viewsize.integer-10))); } /* ============= CG_Viewpos_f Debugging command to print the current position ============= */ static void CG_Viewpos_f (void) { CG_Printf ("(%i %i %i) : %i\n", (int)cg.refdef.vieworg[0], (int)cg.refdef.vieworg[1], (int)cg.refdef.vieworg[2], (int)cg.refdefViewAngles[YAW]); } static void CG_ScoresDown_f( void ) { if ( cg.scoresRequestTime + 2000 < cg.time ) { // the scores are more than two seconds out of data, // so request new ones cg.scoresRequestTime = cg.time; trap_SendClientCommand( "score" ); // leave the current scores up if they were already // displayed, but if this is the first hit, clear them out if ( !cg.showScores ) { cg.showScores = qtrue; cg.numScores = 0; } } else { // show the cached contents even if they just pressed if it // is within two seconds cg.showScores = qtrue; } } static void CG_ScoresUp_f( void ) { if ( cg.showScores ) { cg.showScores = qfalse; cg.scoreFadeTime = cg.time; } } static void CG_TellTarget_f( void ) { int clientNum; char command[128]; char message[128]; clientNum = CG_CrosshairPlayer(); if ( clientNum == -1 ) { return; } trap_Args( message, 128 ); Com_sprintf( command, 128, "tell %i %s", clientNum, message ); trap_SendClientCommand( command ); } static void CG_TellAttacker_f( void ) { int clientNum; char command[128]; char message[128]; clientNum = CG_LastAttacker(); if ( clientNum == -1 ) { return; } trap_Args( message, 128 ); Com_sprintf( command, 128, "tell %i %s", clientNum, message ); trap_SendClientCommand( command ); } static void CG_VoiceTellTarget_f( void ) { int clientNum; char command[128]; char message[128]; clientNum = CG_CrosshairPlayer(); if ( clientNum == -1 ) { return; } trap_Args( message, 128 ); Com_sprintf( command, 128, "vtell %i %s", clientNum, message ); trap_SendClientCommand( command ); } static void CG_VoiceTellAttacker_f( void ) { int clientNum; char command[128]; char message[128]; clientNum = CG_LastAttacker(); if ( clientNum == -1 ) { return; } trap_Args( message, 128 ); Com_sprintf( command, 128, "vtell %i %s", clientNum, message ); trap_SendClientCommand( command ); } /* ================== CG_StartOrbit_f ================== */ static void CG_StartOrbit_f( void ) { char var[MAX_TOKEN_CHARS]; trap_Cvar_VariableStringBuffer( "developer", var, sizeof( var ) ); if ( !atoi(var) ) { return; } if (cg_cameraOrbit.value != 0) { trap_Cvar_Set ("cg_cameraOrbit", "0"); trap_Cvar_Set("cg_thirdPerson", "0"); } else { trap_Cvar_Set("cg_cameraOrbit", "5"); trap_Cvar_Set("cg_thirdPerson", "1"); trap_Cvar_Set("cg_thirdPersonAngle", "0"); trap_Cvar_Set("cg_thirdPersonRange", "100"); } } /* static void CG_Camera_f( void ) { char name[1024]; trap_Argv( 1, name, sizeof(name)); if (trap_loadCamera(name)) { cg.cameraMode = qtrue; trap_startCamera(cg.time); } else { CG_Printf ("Unable to load camera %s\n",name); } } */ typedef struct { char *cmd; void (*function)(void); } consoleCommand_t; static consoleCommand_t commands[] = { { "testgun", CG_TestGun_f }, { "testmodel", CG_TestModel_f }, { "nextframe", CG_TestModelNextFrame_f }, { "prevframe", CG_TestModelPrevFrame_f }, { "nextskin", CG_TestModelNextSkin_f }, { "prevskin", CG_TestModelPrevSkin_f }, { "viewpos", CG_Viewpos_f }, { "+scores", CG_ScoresDown_f }, { "-scores", CG_ScoresUp_f }, { "+zoom", CG_ZoomDown_f }, { "-zoom", CG_ZoomUp_f }, { "sizeup", CG_SizeUp_f }, { "sizedown", CG_SizeDown_f }, { "weapnext", CG_NextWeapon_f }, { "weapprev", CG_PrevWeapon_f }, { "weapon", CG_Weapon_f }, { "tell_target", CG_TellTarget_f }, { "tell_attacker", CG_TellAttacker_f }, { "vtell_target", CG_VoiceTellTarget_f }, { "vtell_attacker", CG_VoiceTellAttacker_f }, { "tcmd", CG_TargetCommand_f }, { "startOrbit", CG_StartOrbit_f }, //{ "camera", CG_Camera_f }, { "loaddeferred", CG_LoadDeferredPlayers } }; /* ================= CG_ConsoleCommand The string has been tokenized and can be retrieved with Cmd_Argc() / Cmd_Argv() ================= */ qboolean CG_ConsoleCommand( void ) { const char *cmd; int i; cmd = CG_Argv(0); for ( i = 0 ; i < sizeof( commands ) / sizeof( commands[0] ) ; i++ ) { if ( !Q_stricmp( cmd, commands[i].cmd ) ) { commands[i].function(); return qtrue; } } return qfalse; } /* ================= CG_InitConsoleCommands Let the client system know about all of our commands so it can perform tab completion ================= */ void CG_InitConsoleCommands( void ) { int i; for ( i = 0 ; i < sizeof( commands ) / sizeof( commands[0] ) ; i++ ) { trap_AddCommand( commands[i].cmd ); } // // the game server will interpret these commands, which will be automatically // forwarded to the server after they are not recognized locally // trap_AddCommand ("kill"); trap_AddCommand ("say"); trap_AddCommand ("say_team"); trap_AddCommand ("tell"); trap_AddCommand ("vsay"); trap_AddCommand ("vsay_team"); trap_AddCommand ("vtell"); trap_AddCommand ("vtaunt"); trap_AddCommand ("vosay"); trap_AddCommand ("vosay_team"); trap_AddCommand ("votell"); trap_AddCommand ("give"); trap_AddCommand ("god"); trap_AddCommand ("notarget"); trap_AddCommand ("noclip"); trap_AddCommand ("team"); trap_AddCommand ("follow"); trap_AddCommand ("levelshot"); trap_AddCommand ("addbot"); trap_AddCommand ("setviewpos"); trap_AddCommand ("callvote"); trap_AddCommand ("vote"); trap_AddCommand ("callteamvote"); trap_AddCommand ("teamvote"); trap_AddCommand ("stats"); trap_AddCommand ("teamtask"); trap_AddCommand ("loaddefered"); // spelled wrong, but not changing for demo } ================================================ FILE: src/cgame/cg_draw.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_draw.c -- draw all of the graphical elements during // active (after loading) gameplay #include "cg_local.h" int drawTeamOverlayModificationCount = -1; int sortedTeamPlayers[TEAM_MAXOVERLAY]; int numSortedTeamPlayers; char systemChat[256]; char teamChat1[256]; char teamChat2[256]; /* ============== CG_DrawField Draws large numbers for status bar and powerups ============== */ static void CG_DrawField (int x, int y, int width, int value) { char num[16], *ptr; int l; int frame; if ( width < 1 ) { return; } // draw number string if ( width > 5 ) { width = 5; } switch ( width ) { case 1: value = value > 9 ? 9 : value; value = value < 0 ? 0 : value; break; case 2: value = value > 99 ? 99 : value; value = value < -9 ? -9 : value; break; case 3: value = value > 999 ? 999 : value; value = value < -99 ? -99 : value; break; case 4: value = value > 9999 ? 9999 : value; value = value < -999 ? -999 : value; break; } Com_sprintf (num, sizeof(num), "%i", value); l = (int)strlen(num); if (l > width) l = width; x += 2 + CHAR_WIDTH*(width - l); ptr = num; while (*ptr && l) { if (*ptr == '-') frame = STAT_MINUS; else frame = *ptr -'0'; CG_DrawPic( x,y, CHAR_WIDTH, CHAR_HEIGHT, cgs.media.numberShaders[frame] ); x += CHAR_WIDTH; ptr++; l--; } } /* ================ CG_Draw3DModel ================ */ void CG_Draw3DModel( float x, float y, float w, float h, qhandle_t model, qhandle_t skin, vec3_t origin, vec3_t angles ) { refdef_t refdef; refEntity_t ent; if ( !cg_draw3dIcons.integer || !cg_drawIcons.integer ) { return; } CG_AdjustFrom640( &x, &y, &w, &h ); memset( &refdef, 0, sizeof( refdef ) ); memset( &ent, 0, sizeof( ent ) ); AnglesToAxis( angles, ent.axis ); VectorCopy( origin, ent.origin ); ent.hModel = model; ent.customSkin = skin; ent.renderfx = RF_NOSHADOW; // no stencil shadows refdef.rdflags = RDF_NOWORLDMODEL; AxisClear( refdef.viewaxis ); refdef.fov_x = 30; refdef.fov_y = 30; refdef.x = x; refdef.y = y; refdef.width = w; refdef.height = h; refdef.time = cg.time; trap_R_ClearScene(); trap_R_AddRefEntityToScene( &ent ); trap_R_RenderScene( &refdef ); } /* ================ CG_DrawHead Used for both the status bar and the scoreboard ================ */ void CG_DrawHead( float x, float y, float w, float h, int clientNum, vec3_t headAngles ) { clipHandle_t cm; clientInfo_t *ci; float len; vec3_t origin; vec3_t mins, maxs; ci = &cgs.clientinfo[ clientNum ]; if ( cg_draw3dIcons.integer ) { cm = ci->headModel; if ( !cm ) { return; } // offset the origin y and z to center the head trap_R_ModelBounds( cm, mins, maxs ); origin[2] = -0.5 * ( mins[2] + maxs[2] ); origin[1] = 0.5 * ( mins[1] + maxs[1] ); // calculate distance so the head nearly fills the box // assume heads are taller than wide len = 0.7 * ( maxs[2] - mins[2] ); origin[0] = len / 0.268; // len / tan( fov/2 ) // allow per-model tweaking VectorAdd( origin, ci->headOffset, origin ); CG_Draw3DModel( x, y, w, h, ci->headModel, ci->headSkin, origin, headAngles ); } else if ( cg_drawIcons.integer ) { CG_DrawPic( x, y, w, h, ci->modelIcon ); } // if they are deferred, draw a cross out if ( ci->deferred ) { CG_DrawPic( x, y, w, h, cgs.media.deferShader ); } } /* ================ CG_DrawFlagModel Used for both the status bar and the scoreboard ================ */ void CG_DrawFlagModel( float x, float y, float w, float h, int team, qboolean force2D ) { qhandle_t cm; float len; vec3_t origin, angles; vec3_t mins, maxs; qhandle_t handle; if ( !force2D && cg_draw3dIcons.integer ) { VectorClear( angles ); cm = cgs.media.redFlagModel; // offset the origin y and z to center the flag trap_R_ModelBounds( cm, mins, maxs ); origin[2] = -0.5 * ( mins[2] + maxs[2] ); origin[1] = 0.5 * ( mins[1] + maxs[1] ); // calculate distance so the flag nearly fills the box // assume heads are taller than wide len = 0.5 * ( maxs[2] - mins[2] ); origin[0] = len / 0.268; // len / tan( fov/2 ) angles[YAW] = 60 * sin( cg.time / 2000.0 );; if( team == TEAM_RED ) { handle = cgs.media.redFlagModel; } else if( team == TEAM_BLUE ) { handle = cgs.media.blueFlagModel; } else if( team == TEAM_FREE ) { handle = cgs.media.neutralFlagModel; } else { return; } CG_Draw3DModel( x, y, w, h, handle, 0, origin, angles ); } else if ( cg_drawIcons.integer ) { gitem_t *item; if( team == TEAM_RED ) { item = BG_FindItemForPowerup( PW_REDFLAG ); } else if( team == TEAM_BLUE ) { item = BG_FindItemForPowerup( PW_BLUEFLAG ); } else if( team == TEAM_FREE ) { item = BG_FindItemForPowerup( PW_NEUTRALFLAG ); } else { return; } if (item) { CG_DrawPic( x, y, w, h, cg_items[ ITEM_INDEX(item) ].icon ); } } } /* ================ CG_DrawStatusBarHead ================ */ static void CG_DrawStatusBarHead( float x ) { vec3_t angles; float size, stretch; float frac; VectorClear( angles ); if ( cg.damageTime && cg.time - cg.damageTime < DAMAGE_TIME ) { frac = (float)(cg.time - cg.damageTime ) / DAMAGE_TIME; size = ICON_SIZE * 1.25 * ( 1.5 - frac * 0.5 ); stretch = size - ICON_SIZE * 1.25; // kick in the direction of damage x -= stretch * 0.5 + cg.damageX * stretch * 0.5; cg.headStartYaw = 180 + cg.damageX * 45; cg.headEndYaw = 180 + 20 * cos( crandom()*M_PI ); cg.headEndPitch = 5 * cos( crandom()*M_PI ); cg.headStartTime = cg.time; cg.headEndTime = cg.time + 100 + random() * 2000; } else { if ( cg.time >= cg.headEndTime ) { // select a new head angle cg.headStartYaw = cg.headEndYaw; cg.headStartPitch = cg.headEndPitch; cg.headStartTime = cg.headEndTime; cg.headEndTime = cg.time + 100 + random() * 2000; cg.headEndYaw = 180 + 20 * cos( crandom()*M_PI ); cg.headEndPitch = 5 * cos( crandom()*M_PI ); } size = ICON_SIZE * 1.25; } // if the server was frozen for a while we may have a bad head start time if ( cg.headStartTime > cg.time ) { cg.headStartTime = cg.time; } frac = ( cg.time - cg.headStartTime ) / (float)( cg.headEndTime - cg.headStartTime ); frac = frac * frac * ( 3 - 2 * frac ); angles[YAW] = cg.headStartYaw + ( cg.headEndYaw - cg.headStartYaw ) * frac; angles[PITCH] = cg.headStartPitch + ( cg.headEndPitch - cg.headStartPitch ) * frac; CG_DrawHead( x, 480 - size, size, size, cg.snap->ps.clientNum, angles ); } /* ================ CG_DrawStatusBarFlag ================ */ static void CG_DrawStatusBarFlag( float x, int team ) { CG_DrawFlagModel( x, 480 - ICON_SIZE, ICON_SIZE, ICON_SIZE, team, qfalse ); } /* ================ CG_DrawTeamBackground ================ */ void CG_DrawTeamBackground( int x, int y, int w, int h, float alpha, int team ) { vec4_t hcolor; hcolor[3] = alpha; if ( team == TEAM_RED ) { hcolor[0] = 1; hcolor[1] = 0; hcolor[2] = 0; } else if ( team == TEAM_BLUE ) { hcolor[0] = 0; hcolor[1] = 0; hcolor[2] = 1; } else { return; } trap_R_SetColor( hcolor ); CG_DrawPic( x, y, w, h, cgs.media.teamStatusBar ); trap_R_SetColor( NULL ); } /* ================ CG_DrawStatusBar ================ */ static void CG_DrawStatusBar( void ) { int color; centity_t *cent; playerState_t *ps; int value; vec4_t hcolor; vec3_t angles; vec3_t origin; static float colors[4][4] = { // { 0.2, 1.0, 0.2, 1.0 } , { 1.0, 0.2, 0.2, 1.0 }, {0.5, 0.5, 0.5, 1} }; { 1.0f, 0.69f, 0.0f, 1.0f }, // normal { 1.0f, 0.2f, 0.2f, 1.0f }, // low health { 0.5f, 0.5f, 0.5f, 1.0f }, // weapon firing { 1.0f, 1.0f, 1.0f, 1.0f } }; // health > 100 if ( cg_drawStatus.integer == 0 ) { return; } // draw the team background CG_DrawTeamBackground( 0, 420, 640, 60, 0.33f, cg.snap->ps.persistant[PERS_TEAM] ); cent = &cg_entities[cg.snap->ps.clientNum]; ps = &cg.snap->ps; VectorClear( angles ); // draw any 3D icons first, so the changes back to 2D are minimized if ( cent->currentState.weapon && cg_weapons[ cent->currentState.weapon ].ammoModel ) { origin[0] = 70; origin[1] = 0; origin[2] = 0; angles[YAW] = 90 + 20 * sin( cg.time / 1000.0 ); CG_Draw3DModel( CHAR_WIDTH*3 + TEXT_ICON_SPACE, 432, ICON_SIZE, ICON_SIZE, cg_weapons[ cent->currentState.weapon ].ammoModel, 0, origin, angles ); } CG_DrawStatusBarHead( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE ); if( cg.predictedPlayerState.powerups[PW_REDFLAG] ) { CG_DrawStatusBarFlag( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE + ICON_SIZE, TEAM_RED ); } else if( cg.predictedPlayerState.powerups[PW_BLUEFLAG] ) { CG_DrawStatusBarFlag( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE + ICON_SIZE, TEAM_BLUE ); } else if( cg.predictedPlayerState.powerups[PW_NEUTRALFLAG] ) { CG_DrawStatusBarFlag( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE + ICON_SIZE, TEAM_FREE ); } if ( ps->stats[ STAT_ARMOR ] ) { origin[0] = 90; origin[1] = 0; origin[2] = -10; angles[YAW] = ( cg.time & 2047 ) * 360 / 2048.0; CG_Draw3DModel( 370 + CHAR_WIDTH*3 + TEXT_ICON_SPACE, 432, ICON_SIZE, ICON_SIZE, cgs.media.armorModel, 0, origin, angles ); } // // ammo // if ( cent->currentState.weapon ) { value = ps->ammo[cent->currentState.weapon]; if ( value > -1 ) { if ( cg.predictedPlayerState.weaponstate == WEAPON_FIRING && cg.predictedPlayerState.weaponTime > 100 ) { // draw as dark grey when reloading color = 2; // dark grey } else { if ( value >= 0 ) { color = 0; // green } else { color = 1; // red } } trap_R_SetColor( colors[color] ); CG_DrawField (0, 432, 3, value); trap_R_SetColor( NULL ); // if we didn't draw a 3D icon, draw a 2D icon for ammo if ( !cg_draw3dIcons.integer && cg_drawIcons.integer ) { qhandle_t icon; icon = cg_weapons[ cg.predictedPlayerState.weapon ].ammoIcon; if ( icon ) { CG_DrawPic( CHAR_WIDTH*3 + TEXT_ICON_SPACE, 432, ICON_SIZE, ICON_SIZE, icon ); } } } } // // health // value = ps->stats[STAT_HEALTH]; if ( value > 100 ) { trap_R_SetColor( colors[3] ); // white } else if (value > 25) { trap_R_SetColor( colors[0] ); // green } else if (value > 0) { color = (cg.time >> 8) & 1; // flash trap_R_SetColor( colors[color] ); } else { trap_R_SetColor( colors[1] ); // red } // stretch the health up when taking damage CG_DrawField ( 185, 432, 3, value); CG_ColorForHealth( hcolor ); trap_R_SetColor( hcolor ); // // armor // value = ps->stats[STAT_ARMOR]; if (value > 0 ) { trap_R_SetColor( colors[0] ); CG_DrawField (370, 432, 3, value); trap_R_SetColor( NULL ); // if we didn't draw a 3D icon, draw a 2D icon for armor if ( !cg_draw3dIcons.integer && cg_drawIcons.integer ) { CG_DrawPic( 370 + CHAR_WIDTH*3 + TEXT_ICON_SPACE, 432, ICON_SIZE, ICON_SIZE, cgs.media.armorIcon ); } } } /* =========================================================================================== UPPER RIGHT CORNER =========================================================================================== */ /* ================ CG_DrawAttacker ================ */ static float CG_DrawAttacker( float y ) { int t; float size; vec3_t angles; const char *info; const char *name; int clientNum; if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) { return y; } if ( !cg.attackerTime ) { return y; } clientNum = cg.predictedPlayerState.persistant[PERS_ATTACKER]; if ( clientNum < 0 || clientNum >= MAX_CLIENTS || clientNum == cg.snap->ps.clientNum ) { return y; } t = cg.time - cg.attackerTime; if ( t > ATTACKER_HEAD_TIME ) { cg.attackerTime = 0; return y; } size = ICON_SIZE * 1.25; angles[PITCH] = 0; angles[YAW] = 180; angles[ROLL] = 0; CG_DrawHead( 640 - size, y, size, size, clientNum, angles ); info = CG_ConfigString( CS_PLAYERS + clientNum ); name = Info_ValueForKey( info, "n" ); y += size; CG_DrawBigString( 640 - ( Q_PrintStrlen( name ) * BIGCHAR_WIDTH), y, name, 0.5 ); return y + BIGCHAR_HEIGHT + 2; } /* ================== CG_DrawSnapshot ================== */ static float CG_DrawSnapshot( float y ) { char *s; int w; s = va( "time:%i snap:%i cmd:%i", cg.snap->serverTime, cg.latestSnapshotNum, cgs.serverCommandSequence ); w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; CG_DrawBigString( 635 - w, y + 2, s, 1.0F); return y + BIGCHAR_HEIGHT + 4; } /* ================== CG_DrawFPS ================== */ #define FPS_FRAMES 4 static float CG_DrawFPS( float y ) { char *s; int w; static int previousTimes[FPS_FRAMES]; static int index; int i, total; int fps; static int previous; int t, frameTime; // don't use serverTime, because that will be drifting to // correct for internet lag changes, timescales, timedemos, etc t = trap_Milliseconds(); frameTime = t - previous; previous = t; previousTimes[index % FPS_FRAMES] = frameTime; index++; if ( index > FPS_FRAMES ) { // average multiple frames together to smooth changes out a bit total = 0; for ( i = 0 ; i < FPS_FRAMES ; i++ ) { total += previousTimes[i]; } if ( !total ) { total = 1; } fps = 1000 * FPS_FRAMES / total; s = va( "%ifps", fps ); w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; CG_DrawBigString( 635 - w, y + 2, s, 1.0F); } return y + BIGCHAR_HEIGHT + 4; } /* ================= CG_DrawTimer ================= */ static float CG_DrawTimer( float y ) { char *s; int w; int mins, seconds, tens; int msec; msec = cg.time - cgs.levelStartTime; seconds = msec / 1000; mins = seconds / 60; seconds -= mins * 60; tens = seconds / 10; seconds -= tens * 10; s = va( "%i:%i%i", mins, tens, seconds ); w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; CG_DrawBigString( 635 - w, y + 2, s, 1.0F); return y + BIGCHAR_HEIGHT + 4; } /* ================= CG_DrawTeamOverlay ================= */ static float CG_DrawTeamOverlay( float y, qboolean right, qboolean upper ) { int x, w, h, xx; int i, j, len; const char *p; vec4_t hcolor; int pwidth, lwidth; int plyrs; char st[16]; clientInfo_t *ci; gitem_t *item; int ret_y, count; if ( !cg_drawTeamOverlay.integer ) { return y; } if ( cg.snap->ps.persistant[PERS_TEAM] != TEAM_RED && cg.snap->ps.persistant[PERS_TEAM] != TEAM_BLUE ) { return y; // Not on any team } plyrs = 0; // max player name width pwidth = 0; count = (numSortedTeamPlayers > 8) ? 8 : numSortedTeamPlayers; for (i = 0; i < count; i++) { ci = cgs.clientinfo + sortedTeamPlayers[i]; if ( ci->infoValid && ci->team == cg.snap->ps.persistant[PERS_TEAM]) { plyrs++; len = CG_DrawStrlen(ci->name); if (len > pwidth) pwidth = len; } } if (!plyrs) return y; if (pwidth > TEAM_OVERLAY_MAXNAME_WIDTH) pwidth = TEAM_OVERLAY_MAXNAME_WIDTH; // max location name width lwidth = 0; for (i = 1; i < MAX_LOCATIONS; i++) { p = CG_ConfigString(CS_LOCATIONS + i); if (p && *p) { len = CG_DrawStrlen(p); if (len > lwidth) lwidth = len; } } if (lwidth > TEAM_OVERLAY_MAXLOCATION_WIDTH) lwidth = TEAM_OVERLAY_MAXLOCATION_WIDTH; w = (pwidth + lwidth + 4 + 7) * TINYCHAR_WIDTH; if ( right ) x = 640 - w; else x = 0; h = plyrs * TINYCHAR_HEIGHT; if ( upper ) { ret_y = y + h; } else { y -= h; ret_y = y; } if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_RED ) { hcolor[0] = 1.0f; hcolor[1] = 0.0f; hcolor[2] = 0.0f; hcolor[3] = 0.33f; } else { // if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE ) hcolor[0] = 0.0f; hcolor[1] = 0.0f; hcolor[2] = 1.0f; hcolor[3] = 0.33f; } trap_R_SetColor( hcolor ); CG_DrawPic( x, y, w, h, cgs.media.teamStatusBar ); trap_R_SetColor( NULL ); for (i = 0; i < count; i++) { ci = cgs.clientinfo + sortedTeamPlayers[i]; if ( ci->infoValid && ci->team == cg.snap->ps.persistant[PERS_TEAM]) { hcolor[0] = hcolor[1] = hcolor[2] = hcolor[3] = 1.0; xx = x + TINYCHAR_WIDTH; CG_DrawStringExt( xx, y, ci->name, hcolor, qfalse, qfalse, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, TEAM_OVERLAY_MAXNAME_WIDTH); if (lwidth) { p = CG_ConfigString(CS_LOCATIONS + ci->location); if (!p || !*p) p = "unknown"; len = CG_DrawStrlen(p); if (len > lwidth) len = lwidth; // xx = x + TINYCHAR_WIDTH * 2 + TINYCHAR_WIDTH * pwidth + // ((lwidth/2 - len/2) * TINYCHAR_WIDTH); xx = x + TINYCHAR_WIDTH * 2 + TINYCHAR_WIDTH * pwidth; CG_DrawStringExt( xx, y, p, hcolor, qfalse, qfalse, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, TEAM_OVERLAY_MAXLOCATION_WIDTH); } CG_GetColorForHealth( ci->health, ci->armor, hcolor ); Com_sprintf (st, sizeof(st), "%3i %3i", ci->health, ci->armor); xx = x + TINYCHAR_WIDTH * 3 + TINYCHAR_WIDTH * pwidth + TINYCHAR_WIDTH * lwidth; CG_DrawStringExt( xx, y, st, hcolor, qfalse, qfalse, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 0 ); // draw weapon icon xx += TINYCHAR_WIDTH * 3; if ( cg_weapons[ci->curWeapon].weaponIcon ) { CG_DrawPic( xx, y, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, cg_weapons[ci->curWeapon].weaponIcon ); } else { CG_DrawPic( xx, y, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, cgs.media.deferShader ); } // Draw powerup icons if (right) { xx = x; } else { xx = x + w - TINYCHAR_WIDTH; } for (j = 0; j <= PW_NUM_POWERUPS; j++) { if (ci->powerups & (1 << j)) { item = BG_FindItemForPowerup( j ); if (item) { CG_DrawPic( xx, y, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, trap_R_RegisterShader( item->icon ) ); if (right) { xx -= TINYCHAR_WIDTH; } else { xx += TINYCHAR_WIDTH; } } } } y += TINYCHAR_HEIGHT; } } return ret_y; //#endif } /* ===================== CG_DrawUpperRight ===================== */ static void CG_DrawUpperRight( void ) { float y; y = 0; if ( cgs.gametype >= GT_TEAM && cg_drawTeamOverlay.integer == 1 ) { y = CG_DrawTeamOverlay( y, qtrue, qtrue ); } if ( cg_drawSnapshot.integer ) { y = CG_DrawSnapshot( y ); } if ( cg_drawFPS.integer ) { y = CG_DrawFPS( y ); } if ( cg_drawTimer.integer ) { y = CG_DrawTimer( y ); } if ( cg_drawAttacker.integer ) { y = CG_DrawAttacker( y ); } } /* =========================================================================================== LOWER RIGHT CORNER =========================================================================================== */ /* ================= CG_DrawScores Draw the small two score display ================= */ static float CG_DrawScores( float y ) { const char *s; int s1, s2, score; int x, w; int v; vec4_t color; float y1; gitem_t *item; s1 = cgs.scores1; s2 = cgs.scores2; y -= BIGCHAR_HEIGHT + 8; y1 = y; // draw from the right side to left if ( cgs.gametype >= GT_TEAM ) { x = 640; color[0] = 0.0f; color[1] = 0.0f; color[2] = 1.0f; color[3] = 0.33f; s = va( "%2i", s2 ); w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8; x -= w; CG_FillRect( x, y-4, w, BIGCHAR_HEIGHT+8, color ); if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE ) { CG_DrawPic( x, y-4, w, BIGCHAR_HEIGHT+8, cgs.media.selectShader ); } CG_DrawBigString( x + 4, y, s, 1.0F); if ( cgs.gametype == GT_CTF ) { // Display flag status item = BG_FindItemForPowerup( PW_BLUEFLAG ); if (item) { y1 = y - BIGCHAR_HEIGHT - 8; if( cgs.blueflag >= 0 && cgs.blueflag <= 2 ) { CG_DrawPic( x, y1-4, w, BIGCHAR_HEIGHT+8, cgs.media.blueFlagShader[cgs.blueflag] ); } } } color[0] = 1.0f; color[1] = 0.0f; color[2] = 0.0f; color[3] = 0.33f; s = va( "%2i", s1 ); w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8; x -= w; CG_FillRect( x, y-4, w, BIGCHAR_HEIGHT+8, color ); if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_RED ) { CG_DrawPic( x, y-4, w, BIGCHAR_HEIGHT+8, cgs.media.selectShader ); } CG_DrawBigString( x + 4, y, s, 1.0F); if ( cgs.gametype == GT_CTF ) { // Display flag status item = BG_FindItemForPowerup( PW_REDFLAG ); if (item) { y1 = y - BIGCHAR_HEIGHT - 8; if( cgs.redflag >= 0 && cgs.redflag <= 2 ) { CG_DrawPic( x, y1-4, w, BIGCHAR_HEIGHT+8, cgs.media.redFlagShader[cgs.redflag] ); } } } if ( cgs.gametype >= GT_CTF ) { v = cgs.capturelimit; } else { v = cgs.fraglimit; } if ( v ) { s = va( "%2i", v ); w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8; x -= w; CG_DrawBigString( x + 4, y, s, 1.0F); } } else { qboolean spectator; x = 640; score = cg.snap->ps.persistant[PERS_SCORE]; spectator = ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR ); // always show your score in the second box if not in first place if ( s1 != score ) { s2 = score; } if ( s2 != SCORE_NOT_PRESENT ) { s = va( "%2i", s2 ); w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8; x -= w; if ( !spectator && score == s2 && score != s1 ) { color[0] = 1.0f; color[1] = 0.0f; color[2] = 0.0f; color[3] = 0.33f; CG_FillRect( x, y-4, w, BIGCHAR_HEIGHT+8, color ); CG_DrawPic( x, y-4, w, BIGCHAR_HEIGHT+8, cgs.media.selectShader ); } else { color[0] = 0.5f; color[1] = 0.5f; color[2] = 0.5f; color[3] = 0.33f; CG_FillRect( x, y-4, w, BIGCHAR_HEIGHT+8, color ); } CG_DrawBigString( x + 4, y, s, 1.0F); } // first place if ( s1 != SCORE_NOT_PRESENT ) { s = va( "%2i", s1 ); w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8; x -= w; if ( !spectator && score == s1 ) { color[0] = 0.0f; color[1] = 0.0f; color[2] = 1.0f; color[3] = 0.33f; CG_FillRect( x, y-4, w, BIGCHAR_HEIGHT+8, color ); CG_DrawPic( x, y-4, w, BIGCHAR_HEIGHT+8, cgs.media.selectShader ); } else { color[0] = 0.5f; color[1] = 0.5f; color[2] = 0.5f; color[3] = 0.33f; CG_FillRect( x, y-4, w, BIGCHAR_HEIGHT+8, color ); } CG_DrawBigString( x + 4, y, s, 1.0F); } if ( cgs.fraglimit ) { s = va( "%2i", cgs.fraglimit ); w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8; x -= w; CG_DrawBigString( x + 4, y, s, 1.0F); } } return y1 - 8; } /* ================ CG_DrawPowerups ================ */ static float CG_DrawPowerups( float y ) { int sorted[MAX_POWERUPS]; int sortedTime[MAX_POWERUPS]; int i, j, k; int active; playerState_t *ps; int t; gitem_t *item; int x; int color; float size; float f; static float colors[2][4] = { { 0.2f, 1.0f, 0.2f, 1.0f } , { 1.0f, 0.2f, 0.2f, 1.0f } }; ps = &cg.snap->ps; if ( ps->stats[STAT_HEALTH] <= 0 ) { return y; } // sort the list by time remaining active = 0; for ( i = 0 ; i < MAX_POWERUPS ; i++ ) { if ( !ps->powerups[ i ] ) { continue; } t = ps->powerups[ i ] - cg.time; // ZOID--don't draw if the power up has unlimited time (999 seconds) // This is true of the CTF flags if ( t < 0 || t > 999000) { continue; } // insert into the list for ( j = 0 ; j < active ; j++ ) { if ( sortedTime[j] >= t ) { for ( k = active - 1 ; k >= j ; k-- ) { sorted[k+1] = sorted[k]; sortedTime[k+1] = sortedTime[k]; } break; } } sorted[j] = i; sortedTime[j] = t; active++; } // draw the icons and timers x = 640 - ICON_SIZE - CHAR_WIDTH * 2; for ( i = 0 ; i < active ; i++ ) { item = BG_FindItemForPowerup( sorted[i] ); if (item) { color = 1; y -= ICON_SIZE; trap_R_SetColor( colors[color] ); CG_DrawField( x, y, 2, sortedTime[ i ] / 1000 ); t = ps->powerups[ sorted[i] ]; if ( t - cg.time >= POWERUP_BLINKS * POWERUP_BLINK_TIME ) { trap_R_SetColor( NULL ); } else { vec4_t modulate; f = (float)( t - cg.time ) / POWERUP_BLINK_TIME; f -= (int)f; modulate[0] = modulate[1] = modulate[2] = modulate[3] = f; trap_R_SetColor( modulate ); } if ( cg.powerupActive == sorted[i] && cg.time - cg.powerupTime < PULSE_TIME ) { f = 1.0 - ( ( (float)cg.time - cg.powerupTime ) / PULSE_TIME ); size = ICON_SIZE * ( 1.0 + ( PULSE_SCALE - 1.0 ) * f ); } else { size = ICON_SIZE; } CG_DrawPic( 640 - size, y + ICON_SIZE / 2 - size / 2, size, size, trap_R_RegisterShader( item->icon ) ); } } trap_R_SetColor( NULL ); return y; } /* ===================== CG_DrawLowerRight ===================== */ static void CG_DrawLowerRight( void ) { float y; y = 480 - ICON_SIZE; if ( cgs.gametype >= GT_TEAM && cg_drawTeamOverlay.integer == 2 ) { y = CG_DrawTeamOverlay( y, qtrue, qfalse ); } y = CG_DrawScores( y ); y = CG_DrawPowerups( y ); } /* =================== CG_DrawPickupItem =================== */ static int CG_DrawPickupItem( int y ) { int value; float *fadeColor; if ( cg.snap->ps.stats[STAT_HEALTH] <= 0 ) { return y; } y -= ICON_SIZE; value = cg.itemPickup; if ( value ) { fadeColor = CG_FadeColor( cg.itemPickupTime, 3000 ); if ( fadeColor ) { CG_RegisterItemVisuals( value ); trap_R_SetColor( fadeColor ); CG_DrawPic( 8, y, ICON_SIZE, ICON_SIZE, cg_items[ value ].icon ); CG_DrawBigString( ICON_SIZE + 16, y + (ICON_SIZE/2 - BIGCHAR_HEIGHT/2), bg_itemlist[ value ].pickup_name, fadeColor[0] ); trap_R_SetColor( NULL ); } } return y; } /* ===================== CG_DrawLowerLeft ===================== */ static void CG_DrawLowerLeft( void ) { float y; y = 480 - ICON_SIZE; if ( cgs.gametype >= GT_TEAM && cg_drawTeamOverlay.integer == 3 ) { y = CG_DrawTeamOverlay( y, qfalse, qfalse ); } y = CG_DrawPickupItem( y ); } //=========================================================================================== /* ================= CG_DrawTeamInfo ================= */ static void CG_DrawTeamInfo( void ) { int w, h; int i, len; vec4_t hcolor; int chatHeight; #define CHATLOC_Y 420 // bottom end #define CHATLOC_X 0 if (cg_teamChatHeight.integer < TEAMCHAT_HEIGHT) chatHeight = cg_teamChatHeight.integer; else chatHeight = TEAMCHAT_HEIGHT; if (chatHeight <= 0) return; // disabled if (cgs.teamLastChatPos != cgs.teamChatPos) { if (cg.time - cgs.teamChatMsgTimes[cgs.teamLastChatPos % chatHeight] > cg_teamChatTime.integer) { cgs.teamLastChatPos++; } h = (cgs.teamChatPos - cgs.teamLastChatPos) * TINYCHAR_HEIGHT; w = 0; for (i = cgs.teamLastChatPos; i < cgs.teamChatPos; i++) { len = CG_DrawStrlen(cgs.teamChatMsgs[i % chatHeight]); if (len > w) w = len; } w *= TINYCHAR_WIDTH; w += TINYCHAR_WIDTH * 2; if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_RED ) { hcolor[0] = 1.0f; hcolor[1] = 0.0f; hcolor[2] = 0.0f; hcolor[3] = 0.33f; } else if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE ) { hcolor[0] = 0.0f; hcolor[1] = 0.0f; hcolor[2] = 1.0f; hcolor[3] = 0.33f; } else { hcolor[0] = 0.0f; hcolor[1] = 1.0f; hcolor[2] = 0.0f; hcolor[3] = 0.33f; } trap_R_SetColor( hcolor ); CG_DrawPic( CHATLOC_X, CHATLOC_Y - h, 640, h, cgs.media.teamStatusBar ); trap_R_SetColor( NULL ); hcolor[0] = hcolor[1] = hcolor[2] = 1.0f; hcolor[3] = 1.0f; for (i = cgs.teamChatPos - 1; i >= cgs.teamLastChatPos; i--) { CG_DrawStringExt( CHATLOC_X + TINYCHAR_WIDTH, CHATLOC_Y - (cgs.teamChatPos - i)*TINYCHAR_HEIGHT, cgs.teamChatMsgs[i % chatHeight], hcolor, qfalse, qfalse, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 0 ); } } } /* =================== CG_DrawHoldableItem =================== */ static void CG_DrawHoldableItem( void ) { int value; value = cg.snap->ps.stats[STAT_HOLDABLE_ITEM]; if ( value ) { CG_RegisterItemVisuals( value ); CG_DrawPic( 640-ICON_SIZE, (SCREEN_HEIGHT-ICON_SIZE)/2, ICON_SIZE, ICON_SIZE, cg_items[ value ].icon ); } } /* =================== CG_DrawReward =================== */ static void CG_DrawReward( void ) { float *color; int i, count; float x, y; char buf[32]; if ( !cg_drawRewards.integer ) { return; } color = CG_FadeColor( cg.rewardTime, REWARD_TIME ); if ( !color ) { if (cg.rewardStack > 0) { for(i = 0; i < cg.rewardStack; i++) { cg.rewardSound[i] = cg.rewardSound[i+1]; cg.rewardShader[i] = cg.rewardShader[i+1]; cg.rewardCount[i] = cg.rewardCount[i+1]; } cg.rewardTime = cg.time; cg.rewardStack--; color = CG_FadeColor( cg.rewardTime, REWARD_TIME ); trap_S_StartLocalSound(cg.rewardSound[0], CHAN_ANNOUNCER); } else { return; } } trap_R_SetColor( color ); /* count = cg.rewardCount[0]/10; // number of big rewards to draw if (count) { y = 4; x = 320 - count * ICON_SIZE; for ( i = 0 ; i < count ; i++ ) { CG_DrawPic( x, y, (ICON_SIZE*2)-4, (ICON_SIZE*2)-4, cg.rewardShader[0] ); x += (ICON_SIZE*2); } } count = cg.rewardCount[0] - count*10; // number of small rewards to draw */ if ( cg.rewardCount[0] >= 10 ) { y = 56; x = 320 - ICON_SIZE/2; CG_DrawPic( x, y, ICON_SIZE-4, ICON_SIZE-4, cg.rewardShader[0] ); Com_sprintf(buf, sizeof(buf), "%d", cg.rewardCount[0]); x = ( SCREEN_WIDTH - SMALLCHAR_WIDTH * CG_DrawStrlen( buf ) ) / 2; CG_DrawStringExt( x, y+ICON_SIZE, buf, color, qfalse, qtrue, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, 0 ); } else { count = cg.rewardCount[0]; y = 56; x = 320 - count * ICON_SIZE/2; for ( i = 0 ; i < count ; i++ ) { CG_DrawPic( x, y, ICON_SIZE-4, ICON_SIZE-4, cg.rewardShader[0] ); x += ICON_SIZE; } } trap_R_SetColor( NULL ); } /* =============================================================================== LAGOMETER =============================================================================== */ #define LAG_SAMPLES 128 typedef struct { int frameSamples[LAG_SAMPLES]; int frameCount; int snapshotFlags[LAG_SAMPLES]; int snapshotSamples[LAG_SAMPLES]; int snapshotCount; } lagometer_t; lagometer_t lagometer; /* ============== CG_AddLagometerFrameInfo Adds the current interpolate / extrapolate bar for this frame ============== */ void CG_AddLagometerFrameInfo( void ) { int offset; offset = cg.time - cg.latestSnapshotTime; lagometer.frameSamples[ lagometer.frameCount & ( LAG_SAMPLES - 1) ] = offset; lagometer.frameCount++; } /* ============== CG_AddLagometerSnapshotInfo Each time a snapshot is received, log its ping time and the number of snapshots that were dropped before it. Pass NULL for a dropped packet. ============== */ void CG_AddLagometerSnapshotInfo( snapshot_t *snap ) { // dropped packet if ( !snap ) { lagometer.snapshotSamples[ lagometer.snapshotCount & ( LAG_SAMPLES - 1) ] = -1; lagometer.snapshotCount++; return; } // add this snapshot's info lagometer.snapshotSamples[ lagometer.snapshotCount & ( LAG_SAMPLES - 1) ] = snap->ping; lagometer.snapshotFlags[ lagometer.snapshotCount & ( LAG_SAMPLES - 1) ] = snap->snapFlags; lagometer.snapshotCount++; } /* ============== CG_DrawDisconnect Should we draw something differnet for long lag vs no packets? ============== */ static void CG_DrawDisconnect( void ) { float x, y; int cmdNum; usercmd_t cmd; const char *s; int w; // bk010215 - FIXME char message[1024]; // draw the phone jack if we are completely past our buffers cmdNum = trap_GetCurrentCmdNumber() - CMD_BACKUP + 1; trap_GetUserCmd( cmdNum, &cmd ); if ( cmd.serverTime <= cg.snap->ps.commandTime || cmd.serverTime > cg.time ) { // special check for map_restart // bk 0102165 - FIXME return; } // also add text in center of screen s = "Connection Interrupted"; // bk 010215 - FIXME w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; CG_DrawBigString( 320 - w/2, 100, s, 1.0F); // blink the icon if ( ( cg.time >> 9 ) & 1 ) { return; } x = 640 - 48; y = 480 - 48; CG_DrawPic( x, y, 48, 48, trap_R_RegisterShader("gfx/2d/net.tga" ) ); } #define MAX_LAGOMETER_PING 900 #define MAX_LAGOMETER_RANGE 300 /* ============== CG_DrawLagometer ============== */ static void CG_DrawLagometer( void ) { int a, x, y, i; float v; float ax, ay, aw, ah, mid, range; int color; float vscale; if ( !cg_lagometer.integer || cgs.localServer ) { CG_DrawDisconnect(); return; } // // draw the graph // x = 640 - 48; y = 480 - 48; trap_R_SetColor( NULL ); CG_DrawPic( x, y, 48, 48, cgs.media.lagometerShader ); ax = x; ay = y; aw = 48; ah = 48; CG_AdjustFrom640( &ax, &ay, &aw, &ah ); color = -1; range = ah / 3; mid = ay + range; vscale = range / MAX_LAGOMETER_RANGE; // draw the frame interpoalte / extrapolate graph for ( a = 0 ; a < aw ; a++ ) { i = ( lagometer.frameCount - 1 - a ) & (LAG_SAMPLES - 1); v = lagometer.frameSamples[i]; v *= vscale; if ( v > 0 ) { if ( color != 1 ) { color = 1; trap_R_SetColor( g_color_table[ColorIndex(COLOR_YELLOW)] ); } if ( v > range ) { v = range; } trap_R_DrawStretchPic ( ax + aw - a, mid - v, 1, v, 0, 0, 0, 0, cgs.media.whiteShader ); } else if ( v < 0 ) { if ( color != 2 ) { color = 2; trap_R_SetColor( g_color_table[ColorIndex(COLOR_BLUE)] ); } v = -v; if ( v > range ) { v = range; } trap_R_DrawStretchPic( ax + aw - a, mid, 1, v, 0, 0, 0, 0, cgs.media.whiteShader ); } } // draw the snapshot latency / drop graph range = ah / 2; vscale = range / MAX_LAGOMETER_PING; for ( a = 0 ; a < aw ; a++ ) { i = ( lagometer.snapshotCount - 1 - a ) & (LAG_SAMPLES - 1); v = lagometer.snapshotSamples[i]; if ( v > 0 ) { if ( lagometer.snapshotFlags[i] & SNAPFLAG_RATE_DELAYED ) { if ( color != 5 ) { color = 5; // YELLOW for rate delay trap_R_SetColor( g_color_table[ColorIndex(COLOR_YELLOW)] ); } } else { if ( color != 3 ) { color = 3; trap_R_SetColor( g_color_table[ColorIndex(COLOR_GREEN)] ); } } v = v * vscale; if ( v > range ) { v = range; } trap_R_DrawStretchPic( ax + aw - a, ay + ah - v, 1, v, 0, 0, 0, 0, cgs.media.whiteShader ); } else if ( v < 0 ) { if ( color != 4 ) { color = 4; // RED for dropped snapshots trap_R_SetColor( g_color_table[ColorIndex(COLOR_RED)] ); } trap_R_DrawStretchPic( ax + aw - a, ay + ah - range, 1, range, 0, 0, 0, 0, cgs.media.whiteShader ); } } trap_R_SetColor( NULL ); if ( cg_nopredict.integer || cg_synchronousClients.integer ) { CG_DrawBigString( ax, ay, "snc", 1.0 ); } CG_DrawDisconnect(); } /* =============================================================================== CENTER PRINTING =============================================================================== */ /* ============== CG_CenterPrint Called for important messages that should stay in the center of the screen for a few moments ============== */ void CG_CenterPrint( const char *str, int y, int charWidth ) { char *s; Q_strncpyz( cg.centerPrint, str, sizeof(cg.centerPrint) ); cg.centerPrintTime = cg.time; cg.centerPrintY = y; cg.centerPrintCharWidth = charWidth; // count the number of lines for centering cg.centerPrintLines = 1; s = cg.centerPrint; while( *s ) { if (*s == '\n') cg.centerPrintLines++; s++; } } /* =================== CG_DrawCenterString =================== */ static void CG_DrawCenterString( void ) { char *start; int l; int x, y, w; float *color; if ( !cg.centerPrintTime ) { return; } color = CG_FadeColor( cg.centerPrintTime, 1000 * cg_centertime.value ); if ( !color ) { return; } trap_R_SetColor( color ); start = cg.centerPrint; y = cg.centerPrintY - cg.centerPrintLines * BIGCHAR_HEIGHT / 2; while ( 1 ) { char linebuffer[1024]; for ( l = 0; l < 50; l++ ) { if ( !start[l] || start[l] == '\n' ) { break; } linebuffer[l] = start[l]; } linebuffer[l] = 0; w = cg.centerPrintCharWidth * CG_DrawStrlen( linebuffer ); x = ( SCREEN_WIDTH - w ) / 2; CG_DrawStringExt( x, y, linebuffer, color, qfalse, qtrue, cg.centerPrintCharWidth, (int)(cg.centerPrintCharWidth * 1.5), 0 ); y += cg.centerPrintCharWidth * 1.5; while ( *start && ( *start != '\n' ) ) { start++; } if ( !*start ) { break; } start++; } trap_R_SetColor( NULL ); } /* ================================================================================ CROSSHAIR ================================================================================ */ /* ================= CG_DrawCrosshair ================= */ static void CG_DrawCrosshair(void) { float w, h; qhandle_t hShader; float f; float x, y; int ca; if ( !cg_drawCrosshair.integer ) { return; } if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR) { return; } if ( cg.renderingThirdPerson ) { return; } // set color based on health if ( cg_crosshairHealth.integer ) { vec4_t hcolor; CG_ColorForHealth( hcolor ); trap_R_SetColor( hcolor ); } else { trap_R_SetColor( NULL ); } w = h = cg_crosshairSize.value; // pulse the size of the crosshair when picking up items f = cg.time - cg.itemPickupBlendTime; if ( f > 0 && f < ITEM_BLOB_TIME ) { f /= ITEM_BLOB_TIME; w *= ( 1 + f ); h *= ( 1 + f ); } x = cg_crosshairX.integer; y = cg_crosshairY.integer; CG_AdjustFrom640( &x, &y, &w, &h ); ca = cg_drawCrosshair.integer; if (ca < 0) { ca = 0; } hShader = cgs.media.crosshairShader[ ca % NUM_CROSSHAIRS ]; trap_R_DrawStretchPic( x + cg.refdef.x + 0.5 * (cg.refdef.width - w), y + cg.refdef.y + 0.5 * (cg.refdef.height - h), w, h, 0, 0, 1, 1, hShader ); } /* ================= CG_ScanForCrosshairEntity ================= */ static void CG_ScanForCrosshairEntity( void ) { trace_t trace; vec3_t start, end; int content; VectorCopy( cg.refdef.vieworg, start ); VectorMA( start, 131072, cg.refdef.viewaxis[0], end ); CG_Trace( &trace, start, vec3_origin, vec3_origin, end, cg.snap->ps.clientNum, CONTENTS_SOLID|CONTENTS_BODY ); if ( trace.entityNum >= MAX_CLIENTS ) { return; } // if the player is in fog, don't show it content = trap_CM_PointContents( trace.endpos, 0 ); if ( content & CONTENTS_FOG ) { return; } // if the player is invisible, don't show it if ( cg_entities[ trace.entityNum ].currentState.powerups & ( 1 << PW_INVIS ) ) { return; } // update the fade timer cg.crosshairClientNum = trace.entityNum; cg.crosshairClientTime = cg.time; } /* ===================== CG_DrawCrosshairNames ===================== */ static void CG_DrawCrosshairNames( void ) { float *color; char *name; float w; if ( !cg_drawCrosshair.integer ) { return; } if ( !cg_drawCrosshairNames.integer ) { return; } if ( cg.renderingThirdPerson ) { return; } // scan the known entities to see if the crosshair is sighted on one CG_ScanForCrosshairEntity(); // draw the name of the player being looked at color = CG_FadeColor( cg.crosshairClientTime, 1000 ); if ( !color ) { trap_R_SetColor( NULL ); return; } name = cgs.clientinfo[ cg.crosshairClientNum ].name; w = CG_DrawStrlen( name ) * BIGCHAR_WIDTH; CG_DrawBigString( 320 - w / 2, 170, name, color[3] * 0.5f ); trap_R_SetColor( NULL ); } //============================================================================== /* ================= CG_DrawSpectator ================= */ static void CG_DrawSpectator(void) { CG_DrawBigString(320 - 9 * 8, 440, "SPECTATOR", 1.0F); if ( cgs.gametype == GT_TOURNAMENT ) { CG_DrawBigString(320 - 15 * 8, 460, "waiting to play", 1.0F); } else if ( cgs.gametype >= GT_TEAM ) { CG_DrawBigString(320 - 39 * 8, 460, "press ESC and use the JOIN menu to play", 1.0F); } } /* ================= CG_DrawVote ================= */ static void CG_DrawVote(void) { char *s; int sec; if ( !cgs.voteTime ) { return; } // play a talk beep whenever it is modified if ( cgs.voteModified ) { cgs.voteModified = qfalse; trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND ); } sec = ( VOTE_TIME - ( cg.time - cgs.voteTime ) ) / 1000; if ( sec < 0 ) { sec = 0; } s = va("VOTE(%i):%s yes:%i no:%i", sec, cgs.voteString, cgs.voteYes, cgs.voteNo ); CG_DrawSmallString( 0, 58, s, 1.0F ); } /* ================= CG_DrawTeamVote ================= */ static void CG_DrawTeamVote(void) { char *s; int sec, cs_offset; if ( cgs.clientinfo->team == TEAM_RED ) cs_offset = 0; else if ( cgs.clientinfo->team == TEAM_BLUE ) cs_offset = 1; else return; if ( !cgs.teamVoteTime[cs_offset] ) { return; } // play a talk beep whenever it is modified if ( cgs.teamVoteModified[cs_offset] ) { cgs.teamVoteModified[cs_offset] = qfalse; trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND ); } sec = ( VOTE_TIME - ( cg.time - cgs.teamVoteTime[cs_offset] ) ) / 1000; if ( sec < 0 ) { sec = 0; } s = va("TEAMVOTE(%i):%s yes:%i no:%i", sec, cgs.teamVoteString[cs_offset], cgs.teamVoteYes[cs_offset], cgs.teamVoteNo[cs_offset] ); CG_DrawSmallString( 0, 90, s, 1.0F ); } static qboolean CG_DrawScoreboard() { return CG_DrawOldScoreboard(); } /* ================= CG_DrawIntermission ================= */ static void CG_DrawIntermission( void ) { // int key; if ( cgs.gametype == GT_SINGLE_PLAYER ) { CG_DrawCenterString(); return; } cg.scoreFadeTime = cg.time; cg.scoreBoardShowing = CG_DrawScoreboard(); } /* ================= CG_DrawFollow ================= */ static qboolean CG_DrawFollow( void ) { float x; vec4_t color; const char *name; if ( !(cg.snap->ps.pm_flags & PMF_FOLLOW) ) { return qfalse; } color[0] = 1; color[1] = 1; color[2] = 1; color[3] = 1; CG_DrawBigString( 320 - 9 * 8, 24, "following", 1.0F ); name = cgs.clientinfo[ cg.snap->ps.clientNum ].name; x = 0.5 * ( 640 - GIANT_WIDTH * CG_DrawStrlen( name ) ); CG_DrawStringExt( x, 40, name, color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 ); return qtrue; } /* ================= CG_DrawAmmoWarning ================= */ static void CG_DrawAmmoWarning( void ) { const char *s; int w; if ( cg_drawAmmoWarning.integer == 0 ) { return; } if ( !cg.lowAmmoWarning ) { return; } if ( cg.lowAmmoWarning == 2 ) { s = "OUT OF AMMO"; } else { s = "LOW AMMO WARNING"; } w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; CG_DrawBigString(320 - w / 2, 64, s, 1.0F); } /* ================= CG_DrawWarmup ================= */ static void CG_DrawWarmup( void ) { int w; int sec; int i; float scale; clientInfo_t *ci1, *ci2; int cw; const char *s; sec = cg.warmup; if ( !sec ) { return; } if ( sec < 0 ) { s = "Waiting for players"; w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; CG_DrawBigString(320 - w / 2, 24, s, 1.0F); cg.warmupCount = 0; return; } if (cgs.gametype == GT_TOURNAMENT) { // find the two active players ci1 = NULL; ci2 = NULL; for ( i = 0 ; i < cgs.maxclients ; i++ ) { if ( cgs.clientinfo[i].infoValid && cgs.clientinfo[i].team == TEAM_FREE ) { if ( !ci1 ) { ci1 = &cgs.clientinfo[i]; } else { ci2 = &cgs.clientinfo[i]; } } } if ( ci1 && ci2 ) { s = va( "%s vs %s", ci1->name, ci2->name ); w = CG_DrawStrlen( s ); if ( w > 640 / GIANT_WIDTH ) { cw = 640 / w; } else { cw = GIANT_WIDTH; } CG_DrawStringExt( 320 - w * cw/2, 20,s, colorWhite, qfalse, qtrue, cw, (int)(cw * 1.5f), 0 ); } } else { if ( cgs.gametype == GT_FFA ) { s = "Free For All"; } else if ( cgs.gametype == GT_TEAM ) { s = "Team Deathmatch"; } else if ( cgs.gametype == GT_CTF ) { s = "Capture the Flag"; } else { s = ""; } w = CG_DrawStrlen( s ); if ( w > 640 / GIANT_WIDTH ) { cw = 640 / w; } else { cw = GIANT_WIDTH; } CG_DrawStringExt( 320 - w * cw/2, 25,s, colorWhite, qfalse, qtrue, cw, (int)(cw * 1.1f), 0 ); } sec = ( sec - cg.time ) / 1000; if ( sec < 0 ) { cg.warmup = 0; sec = 0; } s = va( "Starts in: %i", sec + 1 ); if ( sec != cg.warmupCount ) { cg.warmupCount = sec; switch ( sec ) { case 0: trap_S_StartLocalSound( cgs.media.count1Sound, CHAN_ANNOUNCER ); break; case 1: trap_S_StartLocalSound( cgs.media.count2Sound, CHAN_ANNOUNCER ); break; case 2: trap_S_StartLocalSound( cgs.media.count3Sound, CHAN_ANNOUNCER ); break; default: break; } } scale = 0.45f; switch ( cg.warmupCount ) { case 0: cw = 28; scale = 0.54f; break; case 1: cw = 24; scale = 0.51f; break; case 2: cw = 20; scale = 0.48f; break; default: cw = 16; scale = 0.45f; break; } w = CG_DrawStrlen( s ); CG_DrawStringExt( 320 - w * cw/2, 70, s, colorWhite, qfalse, qtrue, cw, (int)(cw * 1.5), 0 ); } /* ================= CG_Draw2D ================= */ static void CG_Draw2D( void ) { // if we are taking a levelshot for the menu, don't draw anything if ( cg.levelShot ) { return; } if ( cg_draw2D.integer == 0 ) { return; } if ( cg.snap->ps.pm_type == PM_INTERMISSION ) { CG_DrawIntermission(); return; } /* if (cg.cameraMode) { return; } */ if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR ) { CG_DrawSpectator(); CG_DrawCrosshair(); CG_DrawCrosshairNames(); } else { // don't draw any status if dead or the scoreboard is being explicitly shown if ( !cg.showScores && cg.snap->ps.stats[STAT_HEALTH] > 0 ) { CG_DrawStatusBar(); CG_DrawAmmoWarning(); CG_DrawCrosshair(); CG_DrawCrosshairNames(); CG_DrawWeaponSelect(); CG_DrawHoldableItem(); CG_DrawReward(); } if ( cgs.gametype >= GT_TEAM ) { CG_DrawTeamInfo(); } } CG_DrawVote(); CG_DrawTeamVote(); CG_DrawLagometer(); CG_DrawUpperRight(); CG_DrawLowerRight(); CG_DrawLowerLeft(); if ( !CG_DrawFollow() ) { CG_DrawWarmup(); } // don't draw center string if scoreboard is up cg.scoreBoardShowing = CG_DrawScoreboard(); if ( !cg.scoreBoardShowing) { CG_DrawCenterString(); } } static void CG_DrawTourneyScoreboard() { CG_DrawOldTourneyScoreboard(); } /* ===================== CG_DrawActive Perform all drawing needed to completely fill the screen ===================== */ void CG_DrawActive( stereoFrame_t stereoView ) { float separation; vec3_t baseOrg; // optionally draw the info screen instead if ( !cg.snap ) { CG_DrawInformation(); return; } // optionally draw the tournement scoreboard instead if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR && ( cg.snap->ps.pm_flags & PMF_SCOREBOARD ) ) { CG_DrawTourneyScoreboard(); return; } switch ( stereoView ) { case STEREO_CENTER: separation = 0; break; case STEREO_LEFT: separation = -cg_stereoSeparation.value / 2; break; case STEREO_RIGHT: separation = cg_stereoSeparation.value / 2; break; default: separation = 0; CG_Error( "CG_DrawActive: Undefined stereoView" ); } // clear around the rendered view if sized down CG_TileClear(); // offset vieworg appropriately if we're doing stereo separation VectorCopy( cg.refdef.vieworg, baseOrg ); if ( separation != 0 ) { VectorMA( cg.refdef.vieworg, -separation, cg.refdef.viewaxis[1], cg.refdef.vieworg ); } // draw 3D view trap_R_RenderScene( &cg.refdef ); // restore original viewpoint if running stereo if ( separation != 0 ) { VectorCopy( baseOrg, cg.refdef.vieworg ); } // draw status bar and other floating elements CG_Draw2D(); } ================================================ FILE: src/cgame/cg_drawtools.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_drawtools.c -- helper functions called by cg_draw, cg_scoreboard, cg_info, etc #include "cg_local.h" /* ================ CG_AdjustFrom640 Adjusted for resolution and screen aspect ratio ================ */ void CG_AdjustFrom640( float *x, float *y, float *w, float *h ) { #if 0 // adjust for wide screens if ( cgs.glconfig.vidWidth * 480 > cgs.glconfig.vidHeight * 640 ) { *x += 0.5 * ( cgs.glconfig.vidWidth - ( cgs.glconfig.vidHeight * 640 / 480 ) ); } #endif // scale for screen sizes *x *= cgs.screenXScale; *y *= cgs.screenYScale; *w *= cgs.screenXScale; *h *= cgs.screenYScale; } /* ================ CG_FillRect Coordinates are 640*480 virtual values ================= */ void CG_FillRect( float x, float y, float width, float height, const float *color ) { trap_R_SetColor( color ); CG_AdjustFrom640( &x, &y, &width, &height ); trap_R_DrawStretchPic( x, y, width, height, 0, 0, 0, 0, cgs.media.whiteShader ); trap_R_SetColor( NULL ); } /* ================ CG_DrawSides Coords are virtual 640x480 ================ */ void CG_DrawSides(float x, float y, float w, float h, float size) { CG_AdjustFrom640( &x, &y, &w, &h ); size *= cgs.screenXScale; trap_R_DrawStretchPic( x, y, size, h, 0, 0, 0, 0, cgs.media.whiteShader ); trap_R_DrawStretchPic( x + w - size, y, size, h, 0, 0, 0, 0, cgs.media.whiteShader ); } void CG_DrawTopBottom(float x, float y, float w, float h, float size) { CG_AdjustFrom640( &x, &y, &w, &h ); size *= cgs.screenYScale; trap_R_DrawStretchPic( x, y, w, size, 0, 0, 0, 0, cgs.media.whiteShader ); trap_R_DrawStretchPic( x, y + h - size, w, size, 0, 0, 0, 0, cgs.media.whiteShader ); } /* ================ UI_DrawRect Coordinates are 640*480 virtual values ================= */ void CG_DrawRect( float x, float y, float width, float height, float size, const float *color ) { trap_R_SetColor( color ); CG_DrawTopBottom(x, y, width, height, size); CG_DrawSides(x, y, width, height, size); trap_R_SetColor( NULL ); } /* ================ CG_DrawPic Coordinates are 640*480 virtual values ================= */ void CG_DrawPic( float x, float y, float width, float height, qhandle_t hShader ) { CG_AdjustFrom640( &x, &y, &width, &height ); trap_R_DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader ); } /* =============== CG_DrawChar Coordinates and size in 640*480 virtual screen size =============== */ void CG_DrawChar( int x, int y, int width, int height, int ch ) { int row, col; float frow, fcol; float size; float ax, ay, aw, ah; ch &= 255; if ( ch == ' ' ) { return; } ax = x; ay = y; aw = width; ah = height; CG_AdjustFrom640( &ax, &ay, &aw, &ah ); row = ch>>4; col = ch&15; frow = row*0.0625; fcol = col*0.0625; size = 0.0625; trap_R_DrawStretchPic( ax, ay, aw, ah, fcol, frow, fcol + size, frow + size, cgs.media.charsetShader ); } /* ================== CG_DrawStringExt Draws a multi-colored string with a drop shadow, optionally forcing to a fixed color. Coordinates are at 640 by 480 virtual resolution ================== */ void CG_DrawStringExt( int x, int y, const char *string, const float *setColor, qboolean forceColor, qboolean shadow, int charWidth, int charHeight, int maxChars ) { vec4_t color; const char *s; int xx; int cnt; if (maxChars <= 0) maxChars = 32767; // do them all! // draw the drop shadow if (shadow) { color[0] = color[1] = color[2] = 0; color[3] = setColor[3]; trap_R_SetColor( color ); s = string; xx = x; cnt = 0; while ( *s && cnt < maxChars) { if ( Q_IsColorString( s ) ) { s += 2; continue; } CG_DrawChar( xx + 2, y + 2, charWidth, charHeight, *s ); cnt++; xx += charWidth; s++; } } // draw the colored text s = string; xx = x; cnt = 0; trap_R_SetColor( setColor ); while ( *s && cnt < maxChars) { if ( Q_IsColorString( s ) ) { if ( !forceColor ) { memcpy( color, g_color_table[ColorIndex(*(s+1))], sizeof( color ) ); color[3] = setColor[3]; trap_R_SetColor( color ); } s += 2; continue; } CG_DrawChar( xx, y, charWidth, charHeight, *s ); xx += charWidth; cnt++; s++; } trap_R_SetColor( NULL ); } void CG_DrawBigString( int x, int y, const char *s, float alpha ) { float color[4]; color[0] = color[1] = color[2] = 1.0; color[3] = alpha; CG_DrawStringExt( x, y, s, color, qfalse, qtrue, BIGCHAR_WIDTH, BIGCHAR_HEIGHT, 0 ); } void CG_DrawBigStringColor( int x, int y, const char *s, vec4_t color ) { CG_DrawStringExt( x, y, s, color, qtrue, qtrue, BIGCHAR_WIDTH, BIGCHAR_HEIGHT, 0 ); } void CG_DrawSmallString( int x, int y, const char *s, float alpha ) { float color[4]; color[0] = color[1] = color[2] = 1.0; color[3] = alpha; CG_DrawStringExt( x, y, s, color, qfalse, qfalse, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, 0 ); } void CG_DrawSmallStringColor( int x, int y, const char *s, vec4_t color ) { CG_DrawStringExt( x, y, s, color, qtrue, qfalse, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, 0 ); } /* ================= CG_DrawStrlen Returns character count, skiping color escape codes ================= */ int CG_DrawStrlen( const char *str ) { const char *s = str; int count = 0; while ( *s ) { if ( Q_IsColorString( s ) ) { s += 2; } else { count++; s++; } } return count; } /* ============= CG_TileClearBox This repeats a 64*64 tile graphic to fill the screen around a sized down refresh window. ============= */ static void CG_TileClearBox( int x, int y, int w, int h, qhandle_t hShader ) { float s1, t1, s2, t2; s1 = x/64.0; t1 = y/64.0; s2 = (x+w)/64.0; t2 = (y+h)/64.0; trap_R_DrawStretchPic( x, y, w, h, s1, t1, s2, t2, hShader ); } /* ============== CG_TileClear Clear around a sized down screen ============== */ void CG_TileClear( void ) { int top, bottom, left, right; int w, h; w = cgs.glconfig.vidWidth; h = cgs.glconfig.vidHeight; if ( cg.refdef.x == 0 && cg.refdef.y == 0 && cg.refdef.width == w && cg.refdef.height == h ) { return; // full screen rendering } top = cg.refdef.y; bottom = top + cg.refdef.height-1; left = cg.refdef.x; right = left + cg.refdef.width-1; // clear above view screen CG_TileClearBox( 0, 0, w, top, cgs.media.backTileShader ); // clear below view screen CG_TileClearBox( 0, bottom, w, h - bottom, cgs.media.backTileShader ); // clear left of view screen CG_TileClearBox( 0, top, left, bottom - top + 1, cgs.media.backTileShader ); // clear right of view screen CG_TileClearBox( right, top, w - right, bottom - top + 1, cgs.media.backTileShader ); } /* ================ CG_FadeColor ================ */ float *CG_FadeColor( int startMsec, int totalMsec ) { static vec4_t color; int t; if ( startMsec == 0 ) { return NULL; } t = cg.time - startMsec; if ( t >= totalMsec ) { return NULL; } // fade out if ( totalMsec - t < FADE_TIME ) { color[3] = ( totalMsec - t ) * 1.0/FADE_TIME; } else { color[3] = 1.0; } color[0] = color[1] = color[2] = 1; return color; } /* ================ CG_TeamColor ================ */ float *CG_TeamColor( int team ) { static vec4_t red = {1, 0.2f, 0.2f, 1}; static vec4_t blue = {0.2f, 0.2f, 1, 1}; static vec4_t other = {1, 1, 1, 1}; static vec4_t spectator = {0.7f, 0.7f, 0.7f, 1}; switch ( team ) { case TEAM_RED: return red; case TEAM_BLUE: return blue; case TEAM_SPECTATOR: return spectator; default: return other; } } /* ================= CG_GetColorForHealth ================= */ void CG_GetColorForHealth( int health, int armor, vec4_t hcolor ) { int count; int max; // calculate the total points of damage that can // be sustained at the current health / armor level if ( health <= 0 ) { VectorClear( hcolor ); // black hcolor[3] = 1; return; } count = armor; max = health * ARMOR_PROTECTION / ( 1.0 - ARMOR_PROTECTION ); if ( max < count ) { count = max; } health += count; // set the color based on health hcolor[0] = 1.0; hcolor[3] = 1.0; if ( health >= 100 ) { hcolor[2] = 1.0; } else if ( health < 66 ) { hcolor[2] = 0; } else { hcolor[2] = ( health - 66 ) / 33.0; } if ( health > 60 ) { hcolor[1] = 1.0; } else if ( health < 30 ) { hcolor[1] = 0; } else { hcolor[1] = ( health - 30 ) / 30.0; } } /* ================= CG_ColorForHealth ================= */ void CG_ColorForHealth( vec4_t hcolor ) { CG_GetColorForHealth( cg.snap->ps.stats[STAT_HEALTH], cg.snap->ps.stats[STAT_ARMOR], hcolor ); } // bk001205 - code below duplicated in q3_ui/ui-atoms.c // bk001205 - FIXME: does this belong in ui_shared.c? // bk001205 - FIXME: HARD_LINKED flags not visible here #ifndef Q3_STATIC // bk001205 - q_shared defines not visible here /* ================= UI_DrawProportionalString2 ================= */ static int propMap[128][3] = { {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, PROP_SPACE_WIDTH}, // SPACE {11, 122, 7}, // ! {154, 181, 14}, // " {55, 122, 17}, // # {79, 122, 18}, // $ {101, 122, 23}, // % {153, 122, 18}, // & {9, 93, 7}, // ' {207, 122, 8}, // ( {230, 122, 9}, // ) {177, 122, 18}, // * {30, 152, 18}, // + {85, 181, 7}, // , {34, 93, 11}, // - {110, 181, 6}, // . {130, 152, 14}, // / {22, 64, 17}, // 0 {41, 64, 12}, // 1 {58, 64, 17}, // 2 {78, 64, 18}, // 3 {98, 64, 19}, // 4 {120, 64, 18}, // 5 {141, 64, 18}, // 6 {204, 64, 16}, // 7 {162, 64, 17}, // 8 {182, 64, 18}, // 9 {59, 181, 7}, // : {35,181, 7}, // ; {203, 152, 14}, // < {56, 93, 14}, // = {228, 152, 14}, // > {177, 181, 18}, // ? {28, 122, 22}, // @ {5, 4, 18}, // A {27, 4, 18}, // B {48, 4, 18}, // C {69, 4, 17}, // D {90, 4, 13}, // E {106, 4, 13}, // F {121, 4, 18}, // G {143, 4, 17}, // H {164, 4, 8}, // I {175, 4, 16}, // J {195, 4, 18}, // K {216, 4, 12}, // L {230, 4, 23}, // M {6, 34, 18}, // N {27, 34, 18}, // O {48, 34, 18}, // P {68, 34, 18}, // Q {90, 34, 17}, // R {110, 34, 18}, // S {130, 34, 14}, // T {146, 34, 18}, // U {166, 34, 19}, // V {185, 34, 29}, // W {215, 34, 18}, // X {234, 34, 18}, // Y {5, 64, 14}, // Z {60, 152, 7}, // [ {106, 151, 13}, // '\' {83, 152, 7}, // ] {128, 122, 17}, // ^ {4, 152, 21}, // _ {134, 181, 5}, // ' {5, 4, 18}, // A {27, 4, 18}, // B {48, 4, 18}, // C {69, 4, 17}, // D {90, 4, 13}, // E {106, 4, 13}, // F {121, 4, 18}, // G {143, 4, 17}, // H {164, 4, 8}, // I {175, 4, 16}, // J {195, 4, 18}, // K {216, 4, 12}, // L {230, 4, 23}, // M {6, 34, 18}, // N {27, 34, 18}, // O {48, 34, 18}, // P {68, 34, 18}, // Q {90, 34, 17}, // R {110, 34, 18}, // S {130, 34, 14}, // T {146, 34, 18}, // U {166, 34, 19}, // V {185, 34, 29}, // W {215, 34, 18}, // X {234, 34, 18}, // Y {5, 64, 14}, // Z {153, 152, 13}, // { {11, 181, 5}, // | {180, 152, 13}, // } {79, 93, 17}, // ~ {0, 0, -1} // DEL }; static int propMapB[26][3] = { {11, 12, 33}, {49, 12, 31}, {85, 12, 31}, {120, 12, 30}, {156, 12, 21}, {183, 12, 21}, {207, 12, 32}, {13, 55, 30}, {49, 55, 13}, {66, 55, 29}, {101, 55, 31}, {135, 55, 21}, {158, 55, 40}, {204, 55, 32}, {12, 97, 31}, {48, 97, 31}, {82, 97, 30}, {118, 97, 30}, {153, 97, 30}, {185, 97, 25}, {213, 97, 30}, {11, 139, 32}, {42, 139, 51}, {93, 139, 32}, {126, 139, 31}, {158, 139, 25}, }; #define PROPB_GAP_WIDTH 4 #define PROPB_SPACE_WIDTH 12 #define PROPB_HEIGHT 36 /* ================= UI_DrawBannerString ================= */ static void UI_DrawBannerString2( int x, int y, const char* str, vec4_t color ) { const char* s; unsigned char ch; // bk001204 : array subscript float ax; float ay; float aw; float ah; float frow; float fcol; float fwidth; float fheight; // draw the colored text trap_R_SetColor( color ); ax = x * cgs.screenXScale + cgs.screenXBias; ay = y * cgs.screenXScale; s = str; while ( *s ) { ch = *s & 127; if ( ch == ' ' ) { ax += ((float)PROPB_SPACE_WIDTH + (float)PROPB_GAP_WIDTH)* cgs.screenXScale; } else if ( ch >= 'A' && ch <= 'Z' ) { ch -= 'A'; fcol = (float)propMapB[ch][0] / 256.0f; frow = (float)propMapB[ch][1] / 256.0f; fwidth = (float)propMapB[ch][2] / 256.0f; fheight = (float)PROPB_HEIGHT / 256.0f; aw = (float)propMapB[ch][2] * cgs.screenXScale; ah = (float)PROPB_HEIGHT * cgs.screenXScale; trap_R_DrawStretchPic( ax, ay, aw, ah, fcol, frow, fcol+fwidth, frow+fheight, cgs.media.charsetPropB ); ax += (aw + (float)PROPB_GAP_WIDTH * cgs.screenXScale); } s++; } trap_R_SetColor( NULL ); } void UI_DrawBannerString( int x, int y, const char* str, int style, vec4_t color ) { const char * s; int ch; int width; vec4_t drawcolor; // find the width of the drawn text s = str; width = 0; while ( *s ) { ch = *s; if ( ch == ' ' ) { width += PROPB_SPACE_WIDTH; } else if ( ch >= 'A' && ch <= 'Z' ) { width += propMapB[ch - 'A'][2] + PROPB_GAP_WIDTH; } s++; } width -= PROPB_GAP_WIDTH; switch( style & UI_FORMATMASK ) { case UI_CENTER: x -= width / 2; break; case UI_RIGHT: x -= width; break; case UI_LEFT: default: break; } if ( style & UI_DROPSHADOW ) { drawcolor[0] = drawcolor[1] = drawcolor[2] = 0; drawcolor[3] = color[3]; UI_DrawBannerString2( x+2, y+2, str, drawcolor ); } UI_DrawBannerString2( x, y, str, color ); } int UI_ProportionalStringWidth( const char* str ) { const char * s; int ch; int charWidth; int width; s = str; width = 0; while ( *s ) { ch = *s & 127; charWidth = propMap[ch][2]; if ( charWidth != -1 ) { width += charWidth; width += PROP_GAP_WIDTH; } s++; } width -= PROP_GAP_WIDTH; return width; } static void UI_DrawProportionalString2( int x, int y, const char* str, vec4_t color, float sizeScale, qhandle_t charset ) { const char* s; unsigned char ch; // bk001204 - unsigned float ax; float ay; float aw; float ah; float frow; float fcol; float fwidth; float fheight; // draw the colored text trap_R_SetColor( color ); ax = x * cgs.screenXScale + cgs.screenXBias; ay = y * cgs.screenXScale; s = str; while ( *s ) { ch = *s & 127; if ( ch == ' ' ) { aw = (float)PROP_SPACE_WIDTH * cgs.screenXScale * sizeScale; } else if ( propMap[ch][2] != -1 ) { fcol = (float)propMap[ch][0] / 256.0f; frow = (float)propMap[ch][1] / 256.0f; fwidth = (float)propMap[ch][2] / 256.0f; fheight = (float)PROP_HEIGHT / 256.0f; aw = (float)propMap[ch][2] * cgs.screenXScale * sizeScale; ah = (float)PROP_HEIGHT * cgs.screenXScale * sizeScale; trap_R_DrawStretchPic( ax, ay, aw, ah, fcol, frow, fcol+fwidth, frow+fheight, charset ); } else { aw = 0; } ax += (aw + (float)PROP_GAP_WIDTH * cgs.screenXScale * sizeScale); s++; } trap_R_SetColor( NULL ); } /* ================= UI_ProportionalSizeScale ================= */ float UI_ProportionalSizeScale( int style ) { if( style & UI_SMALLFONT ) { return 0.75; } return 1.00; } /* ================= UI_DrawProportionalString ================= */ void UI_DrawProportionalString( int x, int y, const char* str, int style, vec4_t color ) { vec4_t drawcolor; int width; float sizeScale; sizeScale = UI_ProportionalSizeScale( style ); switch( style & UI_FORMATMASK ) { case UI_CENTER: width = UI_ProportionalStringWidth( str ) * sizeScale; x -= width / 2; break; case UI_RIGHT: width = UI_ProportionalStringWidth( str ) * sizeScale; x -= width; break; case UI_LEFT: default: break; } if ( style & UI_DROPSHADOW ) { drawcolor[0] = drawcolor[1] = drawcolor[2] = 0; drawcolor[3] = color[3]; UI_DrawProportionalString2( x+2, y+2, str, drawcolor, sizeScale, cgs.media.charsetProp ); } if ( style & UI_INVERSE ) { drawcolor[0] = color[0] * 0.8; drawcolor[1] = color[1] * 0.8; drawcolor[2] = color[2] * 0.8; drawcolor[3] = color[3]; UI_DrawProportionalString2( x, y, str, drawcolor, sizeScale, cgs.media.charsetProp ); return; } if ( style & UI_PULSE ) { drawcolor[0] = color[0] * 0.8; drawcolor[1] = color[1] * 0.8; drawcolor[2] = color[2] * 0.8; drawcolor[3] = color[3]; UI_DrawProportionalString2( x, y, str, color, sizeScale, cgs.media.charsetProp ); drawcolor[0] = color[0]; drawcolor[1] = color[1]; drawcolor[2] = color[2]; drawcolor[3] = 0.5 + 0.5 * sin( cg.time / PULSE_DIVISOR ); UI_DrawProportionalString2( x, y, str, drawcolor, sizeScale, cgs.media.charsetPropGlow ); return; } UI_DrawProportionalString2( x, y, str, color, sizeScale, cgs.media.charsetProp ); } #endif // Q3STATIC ================================================ FILE: src/cgame/cg_effects.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_effects.c -- these functions generate localentities, usually as a result // of event processing #include "cg_local.h" /* ================== CG_BubbleTrail Bullets shot underwater ================== */ void CG_BubbleTrail( vec3_t start, vec3_t end, float spacing ) { vec3_t move; vec3_t vec; float len; int i; if ( cg_noProjectileTrail.integer ) { return; } VectorCopy (start, move); VectorSubtract (end, start, vec); len = VectorNormalize (vec); // advance a random amount first i = rand() % (int)spacing; VectorMA( move, i, vec, move ); VectorScale (vec, spacing, vec); for ( ; i < len; i += spacing ) { localEntity_t *le; refEntity_t *re; le = CG_AllocLocalEntity(); le->leFlags = LEF_PUFF_DONT_SCALE; le->leType = LE_MOVE_SCALE_FADE; le->startTime = cg.time; le->endTime = cg.time + 1000 + random() * 250; le->lifeRate = 1.0 / ( le->endTime - le->startTime ); re = &le->refEntity; re->shaderTime = cg.time / 1000.0f; re->reType = RT_SPRITE; re->rotation = 0; re->radius = 3; re->customShader = cgs.media.waterBubbleShader; re->shaderRGBA[0] = 0xff; re->shaderRGBA[1] = 0xff; re->shaderRGBA[2] = 0xff; re->shaderRGBA[3] = 0xff; le->color[3] = 1.0; le->pos.trType = TR_LINEAR; le->pos.trTime = cg.time; VectorCopy( move, le->pos.trBase ); le->pos.trDelta[0] = crandom()*5; le->pos.trDelta[1] = crandom()*5; le->pos.trDelta[2] = crandom()*5 + 6; VectorAdd (move, vec, move); } } /* ===================== CG_SmokePuff Adds a smoke puff or blood trail localEntity. ===================== */ localEntity_t *CG_SmokePuff( const vec3_t p, const vec3_t vel, float radius, float r, float g, float b, float a, float duration, int startTime, int fadeInTime, int leFlags, qhandle_t hShader ) { static int seed = 0x92; localEntity_t *le; refEntity_t *re; // int fadeInTime = startTime + duration / 2; le = CG_AllocLocalEntity(); le->leFlags = leFlags; le->radius = radius; re = &le->refEntity; re->rotation = Q_random( &seed ) * 360; re->radius = radius; re->shaderTime = startTime / 1000.0f; le->leType = LE_MOVE_SCALE_FADE; le->startTime = startTime; le->fadeInTime = fadeInTime; le->endTime = startTime + duration; if ( fadeInTime > startTime ) { le->lifeRate = 1.0 / ( le->endTime - le->fadeInTime ); } else { le->lifeRate = 1.0 / ( le->endTime - le->startTime ); } le->color[0] = r; le->color[1] = g; le->color[2] = b; le->color[3] = a; le->pos.trType = TR_LINEAR; le->pos.trTime = startTime; VectorCopy( vel, le->pos.trDelta ); VectorCopy( p, le->pos.trBase ); VectorCopy( p, re->origin ); re->customShader = hShader; re->shaderRGBA[0] = le->color[0] * 0xff; re->shaderRGBA[1] = le->color[1] * 0xff; re->shaderRGBA[2] = le->color[2] * 0xff; re->shaderRGBA[3] = 0xff; re->reType = RT_SPRITE; re->radius = le->radius; return le; } /* ================== CG_SpawnEffect Player teleporting in or out ================== */ void CG_SpawnEffect( vec3_t org ) { localEntity_t *le; refEntity_t *re; le = CG_AllocLocalEntity(); le->leFlags = 0; le->leType = LE_FADE_RGB; le->startTime = cg.time; le->endTime = cg.time + 500; le->lifeRate = 1.0 / ( le->endTime - le->startTime ); le->color[0] = le->color[1] = le->color[2] = le->color[3] = 1.0; re = &le->refEntity; re->reType = RT_MODEL; re->shaderTime = cg.time / 1000.0f; re->customShader = cgs.media.teleportEffectShader; re->hModel = cgs.media.teleportEffectModel; AxisClear( re->axis ); VectorCopy( org, re->origin ); re->origin[2] -= 24; } /* ================== CG_ScorePlum ================== */ void CG_ScorePlum( int client, vec3_t org, int score ) { localEntity_t *le; refEntity_t *re; vec3_t angles; static vec3_t lastPos; // only visualize for the client that scored if (client != cg.predictedPlayerState.clientNum || cg_scorePlum.integer == 0) { return; } le = CG_AllocLocalEntity(); le->leFlags = 0; le->leType = LE_SCOREPLUM; le->startTime = cg.time; le->endTime = cg.time + 4000; le->lifeRate = 1.0 / ( le->endTime - le->startTime ); le->color[0] = le->color[1] = le->color[2] = le->color[3] = 1.0; le->radius = score; VectorCopy( org, le->pos.trBase ); if (org[2] >= lastPos[2] - 20 && org[2] <= lastPos[2] + 20) { le->pos.trBase[2] -= 20; } //CG_Printf( "Plum origin %i %i %i -- %i\n", (int)org[0], (int)org[1], (int)org[2], (int)Distance(org, lastPos)); VectorCopy(org, lastPos); re = &le->refEntity; re->reType = RT_SPRITE; re->radius = 16; VectorClear(angles); AnglesToAxis( angles, re->axis ); } /* ==================== CG_MakeExplosion ==================== */ localEntity_t *CG_MakeExplosion( vec3_t origin, vec3_t dir, qhandle_t hModel, qhandle_t shader, int msec, qboolean isSprite ) { float ang; localEntity_t *ex; int offset; vec3_t tmpVec, newOrigin; if ( msec <= 0 ) { CG_Error( "CG_MakeExplosion: msec = %i", msec ); } // skew the time a bit so they aren't all in sync offset = rand() & 63; ex = CG_AllocLocalEntity(); if ( isSprite ) { ex->leType = LE_SPRITE_EXPLOSION; // randomly rotate sprite orientation ex->refEntity.rotation = rand() % 360; VectorScale( dir, 16, tmpVec ); VectorAdd( tmpVec, origin, newOrigin ); } else { ex->leType = LE_EXPLOSION; VectorCopy( origin, newOrigin ); // set axis with random rotate if ( !dir ) { AxisClear( ex->refEntity.axis ); } else { ang = rand() % 360; VectorCopy( dir, ex->refEntity.axis[0] ); RotateAroundDirection( ex->refEntity.axis, ang ); } } ex->startTime = cg.time - offset; ex->endTime = ex->startTime + msec; // bias the time so all shader effects start correctly ex->refEntity.shaderTime = ex->startTime / 1000.0f; ex->refEntity.hModel = hModel; ex->refEntity.customShader = shader; // set origin VectorCopy( newOrigin, ex->refEntity.origin ); VectorCopy( newOrigin, ex->refEntity.oldorigin ); ex->color[0] = ex->color[1] = ex->color[2] = 1.0; return ex; } /* ================= CG_Bleed This is the spurt of blood when a character gets hit ================= */ void CG_Bleed( vec3_t origin, int entityNum ) { localEntity_t *ex; if ( !cg_blood.integer ) { return; } ex = CG_AllocLocalEntity(); ex->leType = LE_EXPLOSION; ex->startTime = cg.time; ex->endTime = ex->startTime + 500; VectorCopy ( origin, ex->refEntity.origin); ex->refEntity.reType = RT_SPRITE; ex->refEntity.rotation = rand() % 360; ex->refEntity.radius = 24; ex->refEntity.customShader = cgs.media.bloodExplosionShader; // don't show player's own blood in view if ( entityNum == cg.snap->ps.clientNum ) { ex->refEntity.renderfx |= RF_THIRD_PERSON; } } /* ================== CG_LaunchGib ================== */ void CG_LaunchGib( vec3_t origin, vec3_t velocity, qhandle_t hModel ) { localEntity_t *le; refEntity_t *re; le = CG_AllocLocalEntity(); re = &le->refEntity; le->leType = LE_FRAGMENT; le->startTime = cg.time; le->endTime = le->startTime + 5000 + random() * 3000; VectorCopy( origin, re->origin ); AxisCopy( axisDefault, re->axis ); re->hModel = hModel; le->pos.trType = TR_GRAVITY; VectorCopy( origin, le->pos.trBase ); VectorCopy( velocity, le->pos.trDelta ); le->pos.trTime = cg.time; le->bounceFactor = 0.6f; le->leBounceSoundType = LEBS_BLOOD; le->leMarkType = LEMT_BLOOD; } /* =================== CG_GibPlayer Generated a bunch of gibs launching out from the bodies location =================== */ #define GIB_VELOCITY 250 #define GIB_JUMP 250 void CG_GibPlayer( vec3_t playerOrigin ) { vec3_t origin, velocity; if ( !cg_blood.integer ) { return; } VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*GIB_VELOCITY; velocity[1] = crandom()*GIB_VELOCITY; velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; if ( rand() & 1 ) { CG_LaunchGib( origin, velocity, cgs.media.gibSkull ); } else { CG_LaunchGib( origin, velocity, cgs.media.gibBrain ); } // allow gibs to be turned off for speed if ( !cg_gibs.integer ) { return; } VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*GIB_VELOCITY; velocity[1] = crandom()*GIB_VELOCITY; velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; CG_LaunchGib( origin, velocity, cgs.media.gibAbdomen ); VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*GIB_VELOCITY; velocity[1] = crandom()*GIB_VELOCITY; velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; CG_LaunchGib( origin, velocity, cgs.media.gibArm ); VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*GIB_VELOCITY; velocity[1] = crandom()*GIB_VELOCITY; velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; CG_LaunchGib( origin, velocity, cgs.media.gibChest ); VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*GIB_VELOCITY; velocity[1] = crandom()*GIB_VELOCITY; velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; CG_LaunchGib( origin, velocity, cgs.media.gibFist ); VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*GIB_VELOCITY; velocity[1] = crandom()*GIB_VELOCITY; velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; CG_LaunchGib( origin, velocity, cgs.media.gibFoot ); VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*GIB_VELOCITY; velocity[1] = crandom()*GIB_VELOCITY; velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; CG_LaunchGib( origin, velocity, cgs.media.gibForearm ); VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*GIB_VELOCITY; velocity[1] = crandom()*GIB_VELOCITY; velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; CG_LaunchGib( origin, velocity, cgs.media.gibIntestine ); VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*GIB_VELOCITY; velocity[1] = crandom()*GIB_VELOCITY; velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; CG_LaunchGib( origin, velocity, cgs.media.gibLeg ); VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*GIB_VELOCITY; velocity[1] = crandom()*GIB_VELOCITY; velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; CG_LaunchGib( origin, velocity, cgs.media.gibLeg ); } /* ================== CG_LaunchGib ================== */ void CG_LaunchExplode( vec3_t origin, vec3_t velocity, qhandle_t hModel ) { localEntity_t *le; refEntity_t *re; le = CG_AllocLocalEntity(); re = &le->refEntity; le->leType = LE_FRAGMENT; le->startTime = cg.time; le->endTime = le->startTime + 10000 + random() * 6000; VectorCopy( origin, re->origin ); AxisCopy( axisDefault, re->axis ); re->hModel = hModel; le->pos.trType = TR_GRAVITY; VectorCopy( origin, le->pos.trBase ); VectorCopy( velocity, le->pos.trDelta ); le->pos.trTime = cg.time; le->bounceFactor = 0.1f; le->leBounceSoundType = LEBS_BRASS; le->leMarkType = LEMT_NONE; } #define EXP_VELOCITY 100 #define EXP_JUMP 150 /* =================== CG_GibPlayer Generated a bunch of gibs launching out from the bodies location =================== */ void CG_BigExplode( vec3_t playerOrigin ) { vec3_t origin, velocity; if ( !cg_blood.integer ) { return; } VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*EXP_VELOCITY; velocity[1] = crandom()*EXP_VELOCITY; velocity[2] = EXP_JUMP + crandom()*EXP_VELOCITY; CG_LaunchExplode( origin, velocity, cgs.media.smoke2 ); VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*EXP_VELOCITY; velocity[1] = crandom()*EXP_VELOCITY; velocity[2] = EXP_JUMP + crandom()*EXP_VELOCITY; CG_LaunchExplode( origin, velocity, cgs.media.smoke2 ); VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*EXP_VELOCITY*1.5; velocity[1] = crandom()*EXP_VELOCITY*1.5; velocity[2] = EXP_JUMP + crandom()*EXP_VELOCITY; CG_LaunchExplode( origin, velocity, cgs.media.smoke2 ); VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*EXP_VELOCITY*2.0; velocity[1] = crandom()*EXP_VELOCITY*2.0; velocity[2] = EXP_JUMP + crandom()*EXP_VELOCITY; CG_LaunchExplode( origin, velocity, cgs.media.smoke2 ); VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*EXP_VELOCITY*2.5; velocity[1] = crandom()*EXP_VELOCITY*2.5; velocity[2] = EXP_JUMP + crandom()*EXP_VELOCITY; CG_LaunchExplode( origin, velocity, cgs.media.smoke2 ); } ================================================ FILE: src/cgame/cg_ents.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_ents.c -- present snapshot entities, happens every single frame #include "cg_local.h" /* ====================== CG_PositionEntityOnTag Modifies the entities position and axis by the given tag location ====================== */ void CG_PositionEntityOnTag( refEntity_t *entity, const refEntity_t *parent, qhandle_t parentModel, char *tagName ) { int i; orientation_t lerped; // lerp the tag trap_R_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame, 1.0 - parent->backlerp, tagName ); // FIXME: allow origin offsets along tag? VectorCopy( parent->origin, entity->origin ); for ( i = 0 ; i < 3 ; i++ ) { VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin ); } // had to cast away the const to avoid compiler problems... MatrixMultiply( lerped.axis, ((refEntity_t *)parent)->axis, entity->axis ); entity->backlerp = parent->backlerp; } /* ====================== CG_PositionRotatedEntityOnTag Modifies the entities position and axis by the given tag location ====================== */ void CG_PositionRotatedEntityOnTag( refEntity_t *entity, const refEntity_t *parent, qhandle_t parentModel, char *tagName ) { int i; orientation_t lerped; vec3_t tempAxis[3]; //AxisClear( entity->axis ); // lerp the tag trap_R_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame, 1.0 - parent->backlerp, tagName ); // FIXME: allow origin offsets along tag? VectorCopy( parent->origin, entity->origin ); for ( i = 0 ; i < 3 ; i++ ) { VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin ); } // had to cast away the const to avoid compiler problems... MatrixMultiply( entity->axis, lerped.axis, tempAxis ); MatrixMultiply( tempAxis, ((refEntity_t *)parent)->axis, entity->axis ); } /* ========================================================================== FUNCTIONS CALLED EACH FRAME ========================================================================== */ /* ====================== CG_SetEntitySoundPosition Also called by event processing code ====================== */ void CG_SetEntitySoundPosition( centity_t *cent ) { if ( cent->currentState.solid == SOLID_BMODEL ) { vec3_t origin; float *v; v = cgs.inlineModelMidpoints[ cent->currentState.modelindex ]; VectorAdd( cent->lerpOrigin, v, origin ); trap_S_UpdateEntityPosition( cent->currentState.number, origin ); } else { trap_S_UpdateEntityPosition( cent->currentState.number, cent->lerpOrigin ); } } /* ================== CG_EntityEffects Add continuous entity effects, like local entity emission and lighting ================== */ static void CG_EntityEffects( centity_t *cent ) { // update sound origins CG_SetEntitySoundPosition( cent ); // add loop sound if ( cent->currentState.loopSound ) { if (cent->currentState.eType != ET_SPEAKER) { trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.gameSounds[ cent->currentState.loopSound ] ); } else { trap_S_AddRealLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.gameSounds[ cent->currentState.loopSound ] ); } } // constant light glow if ( cent->currentState.constantLight ) { int cl; int i, r, g, b; cl = cent->currentState.constantLight; r = cl & 255; g = ( cl >> 8 ) & 255; b = ( cl >> 16 ) & 255; i = ( ( cl >> 24 ) & 255 ) * 4; trap_R_AddLightToScene( cent->lerpOrigin, i, r, g, b ); } } /* ================== CG_General ================== */ static void CG_General( centity_t *cent ) { refEntity_t ent; entityState_t *s1; s1 = ¢->currentState; // if set to invisible, skip if (!s1->modelindex) { return; } memset (&ent, 0, sizeof(ent)); // set frame ent.frame = s1->frame; ent.oldframe = ent.frame; ent.backlerp = 0; VectorCopy( cent->lerpOrigin, ent.origin); VectorCopy( cent->lerpOrigin, ent.oldorigin); ent.hModel = cgs.gameModels[s1->modelindex]; // player model if (s1->number == cg.snap->ps.clientNum) { ent.renderfx |= RF_THIRD_PERSON; // only draw from mirrors } // convert angles to axis AnglesToAxis( cent->lerpAngles, ent.axis ); // add to refresh list trap_R_AddRefEntityToScene (&ent); } /* ================== CG_Speaker Speaker entities can automatically play sounds ================== */ static void CG_Speaker( centity_t *cent ) { if ( ! cent->currentState.clientNum ) { // FIXME: use something other than clientNum... return; // not auto triggering } if ( cg.time < cent->miscTime ) { return; } trap_S_StartSound (NULL, cent->currentState.number, CHAN_ITEM, cgs.gameSounds[cent->currentState.eventParm] ); // ent->s.frame = ent->wait * 10; // ent->s.clientNum = ent->random * 10; cent->miscTime = cg.time + cent->currentState.frame * 100 + cent->currentState.clientNum * 100 * crandom(); } /* ================== CG_Item ================== */ static void CG_Item( centity_t *cent ) { refEntity_t ent; entityState_t *es; gitem_t *item; int msec; float frac; float scale; weaponInfo_t *wi; es = ¢->currentState; if ( es->modelindex >= bg_numItems ) { CG_Error( "Bad item index %i on entity", es->modelindex ); } // if set to invisible, skip if ( !es->modelindex || ( es->eFlags & EF_NODRAW ) ) { return; } item = &bg_itemlist[ es->modelindex ]; if ( cg_simpleItems.integer && item->giType != IT_TEAM ) { memset( &ent, 0, sizeof( ent ) ); ent.reType = RT_SPRITE; VectorCopy( cent->lerpOrigin, ent.origin ); ent.radius = 14; ent.customShader = cg_items[es->modelindex].icon; ent.shaderRGBA[0] = 255; ent.shaderRGBA[1] = 255; ent.shaderRGBA[2] = 255; ent.shaderRGBA[3] = 255; trap_R_AddRefEntityToScene(&ent); return; } // items bob up and down continuously scale = 0.005 + cent->currentState.number * 0.00001; cent->lerpOrigin[2] += 4 + cos( ( cg.time + 1000 ) * scale ) * 4; memset (&ent, 0, sizeof(ent)); // autorotate at one of two speeds if ( item->giType == IT_HEALTH ) { VectorCopy( cg.autoAnglesFast, cent->lerpAngles ); AxisCopy( cg.autoAxisFast, ent.axis ); } else { VectorCopy( cg.autoAngles, cent->lerpAngles ); AxisCopy( cg.autoAxis, ent.axis ); } wi = NULL; // the weapons have their origin where they attatch to player // models, so we need to offset them or they will rotate // eccentricly if ( item->giType == IT_WEAPON ) { wi = &cg_weapons[item->giTag]; cent->lerpOrigin[0] -= wi->weaponMidpoint[0] * ent.axis[0][0] + wi->weaponMidpoint[1] * ent.axis[1][0] + wi->weaponMidpoint[2] * ent.axis[2][0]; cent->lerpOrigin[1] -= wi->weaponMidpoint[0] * ent.axis[0][1] + wi->weaponMidpoint[1] * ent.axis[1][1] + wi->weaponMidpoint[2] * ent.axis[2][1]; cent->lerpOrigin[2] -= wi->weaponMidpoint[0] * ent.axis[0][2] + wi->weaponMidpoint[1] * ent.axis[1][2] + wi->weaponMidpoint[2] * ent.axis[2][2]; cent->lerpOrigin[2] += 8; // an extra height boost } ent.hModel = cg_items[es->modelindex].models[0]; VectorCopy( cent->lerpOrigin, ent.origin); VectorCopy( cent->lerpOrigin, ent.oldorigin); ent.nonNormalizedAxes = qfalse; // if just respawned, slowly scale up msec = cg.time - cent->miscTime; if ( msec >= 0 && msec < ITEM_SCALEUP_TIME ) { frac = (float)msec / ITEM_SCALEUP_TIME; VectorScale( ent.axis[0], frac, ent.axis[0] ); VectorScale( ent.axis[1], frac, ent.axis[1] ); VectorScale( ent.axis[2], frac, ent.axis[2] ); ent.nonNormalizedAxes = qtrue; } else { frac = 1.0; } // items without glow textures need to keep a minimum light value // so they are always visible if ( ( item->giType == IT_WEAPON ) || ( item->giType == IT_ARMOR ) ) { ent.renderfx |= RF_MINLIGHT; } // increase the size of the weapons when they are presented as items if ( item->giType == IT_WEAPON ) { VectorScale( ent.axis[0], 1.5, ent.axis[0] ); VectorScale( ent.axis[1], 1.5, ent.axis[1] ); VectorScale( ent.axis[2], 1.5, ent.axis[2] ); ent.nonNormalizedAxes = qtrue; } // add to refresh list trap_R_AddRefEntityToScene(&ent); // accompanying rings / spheres for powerups if ( !cg_simpleItems.integer ) { vec3_t spinAngles; VectorClear( spinAngles ); if ( item->giType == IT_HEALTH || item->giType == IT_POWERUP ) { if ( ( ent.hModel = cg_items[es->modelindex].models[1] ) != 0 ) { if ( item->giType == IT_POWERUP ) { ent.origin[2] += 12; spinAngles[1] = ( cg.time & 1023 ) * 360 / -1024.0f; } AnglesToAxis( spinAngles, ent.axis ); // scale up if respawning if ( frac != 1.0 ) { VectorScale( ent.axis[0], frac, ent.axis[0] ); VectorScale( ent.axis[1], frac, ent.axis[1] ); VectorScale( ent.axis[2], frac, ent.axis[2] ); ent.nonNormalizedAxes = qtrue; } trap_R_AddRefEntityToScene( &ent ); } } } } //============================================================================ /* =============== CG_Missile =============== */ static void CG_Missile( centity_t *cent ) { refEntity_t ent; entityState_t *s1; const weaponInfo_t *weapon; // int col; s1 = ¢->currentState; if ( s1->weapon > WP_NUM_WEAPONS ) { s1->weapon = 0; } weapon = &cg_weapons[s1->weapon]; // calculate the axis VectorCopy( s1->angles, cent->lerpAngles); // add trails if ( weapon->missileTrailFunc ) { weapon->missileTrailFunc( cent, weapon ); } /* if ( cent->currentState.modelindex == TEAM_RED ) { col = 1; } else if ( cent->currentState.modelindex == TEAM_BLUE ) { col = 2; } else { col = 0; } // add dynamic light if ( weapon->missileDlight ) { trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight, weapon->missileDlightColor[col][0], weapon->missileDlightColor[col][1], weapon->missileDlightColor[col][2] ); } */ // add dynamic light if ( weapon->missileDlight ) { trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight, weapon->missileDlightColor[0], weapon->missileDlightColor[1], weapon->missileDlightColor[2] ); } // add missile sound if ( weapon->missileSound ) { vec3_t velocity; BG_EvaluateTrajectoryDelta( ¢->currentState.pos, cg.time, velocity ); trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, velocity, weapon->missileSound ); } // create the render entity memset (&ent, 0, sizeof(ent)); VectorCopy( cent->lerpOrigin, ent.origin); VectorCopy( cent->lerpOrigin, ent.oldorigin); if ( cent->currentState.weapon == WP_PLASMAGUN ) { ent.reType = RT_SPRITE; ent.radius = 16; ent.rotation = 0; ent.customShader = cgs.media.plasmaBallShader; trap_R_AddRefEntityToScene( &ent ); return; } // flicker between two skins ent.skinNum = cg.clientFrame & 1; ent.hModel = weapon->missileModel; ent.renderfx = weapon->missileRenderfx | RF_NOSHADOW; // convert direction of travel into axis if ( VectorNormalize2( s1->pos.trDelta, ent.axis[0] ) == 0 ) { ent.axis[0][2] = 1; } // spin as it moves if ( s1->pos.trType != TR_STATIONARY ) { RotateAroundDirection( ent.axis, cg.time / 4 ); } else { { RotateAroundDirection( ent.axis, s1->time ); } } // add to refresh list, possibly with quad glow CG_AddRefEntityWithPowerups( &ent, s1, TEAM_FREE ); } /* =============== CG_Grapple This is called when the grapple is sitting up against the wall =============== */ static void CG_Grapple( centity_t *cent ) { refEntity_t ent; entityState_t *s1; const weaponInfo_t *weapon; s1 = ¢->currentState; if ( s1->weapon > WP_NUM_WEAPONS ) { s1->weapon = 0; } weapon = &cg_weapons[s1->weapon]; // calculate the axis VectorCopy( s1->angles, cent->lerpAngles); #if 0 // FIXME add grapple pull sound here..? // add missile sound if ( weapon->missileSound ) { trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, weapon->missileSound ); } #endif // Will draw cable if needed CG_GrappleTrail ( cent, weapon ); // create the render entity memset (&ent, 0, sizeof(ent)); VectorCopy( cent->lerpOrigin, ent.origin); VectorCopy( cent->lerpOrigin, ent.oldorigin); // flicker between two skins ent.skinNum = cg.clientFrame & 1; ent.hModel = weapon->missileModel; ent.renderfx = weapon->missileRenderfx | RF_NOSHADOW; // convert direction of travel into axis if ( VectorNormalize2( s1->pos.trDelta, ent.axis[0] ) == 0 ) { ent.axis[0][2] = 1; } trap_R_AddRefEntityToScene( &ent ); } /* =============== CG_Mover =============== */ static void CG_Mover( centity_t *cent ) { refEntity_t ent; entityState_t *s1; s1 = ¢->currentState; // create the render entity memset (&ent, 0, sizeof(ent)); VectorCopy( cent->lerpOrigin, ent.origin); VectorCopy( cent->lerpOrigin, ent.oldorigin); AnglesToAxis( cent->lerpAngles, ent.axis ); ent.renderfx = RF_NOSHADOW; // flicker between two skins (FIXME?) ent.skinNum = ( cg.time >> 6 ) & 1; // get the model, either as a bmodel or a modelindex if ( s1->solid == SOLID_BMODEL ) { ent.hModel = cgs.inlineDrawModel[s1->modelindex]; } else { ent.hModel = cgs.gameModels[s1->modelindex]; } // add to refresh list trap_R_AddRefEntityToScene(&ent); // add the secondary model if ( s1->modelindex2 ) { ent.skinNum = 0; ent.hModel = cgs.gameModels[s1->modelindex2]; trap_R_AddRefEntityToScene(&ent); } } /* =============== CG_Beam Also called as an event =============== */ void CG_Beam( centity_t *cent ) { refEntity_t ent; entityState_t *s1; s1 = ¢->currentState; // create the render entity memset (&ent, 0, sizeof(ent)); VectorCopy( s1->pos.trBase, ent.origin ); VectorCopy( s1->origin2, ent.oldorigin ); AxisClear( ent.axis ); ent.reType = RT_BEAM; ent.renderfx = RF_NOSHADOW; // add to refresh list trap_R_AddRefEntityToScene(&ent); } /* =============== CG_Portal =============== */ static void CG_Portal( centity_t *cent ) { refEntity_t ent; entityState_t *s1; s1 = ¢->currentState; // create the render entity memset (&ent, 0, sizeof(ent)); VectorCopy( cent->lerpOrigin, ent.origin ); VectorCopy( s1->origin2, ent.oldorigin ); ByteToDir( s1->eventParm, ent.axis[0] ); PerpendicularVector( ent.axis[1], ent.axis[0] ); // negating this tends to get the directions like they want // we really should have a camera roll value VectorSubtract( vec3_origin, ent.axis[1], ent.axis[1] ); CrossProduct( ent.axis[0], ent.axis[1], ent.axis[2] ); ent.reType = RT_PORTALSURFACE; ent.oldframe = s1->powerups; ent.frame = s1->frame; // rotation speed ent.skinNum = s1->clientNum/256.0 * 360; // roll offset // add to refresh list trap_R_AddRefEntityToScene(&ent); } /* ========================= CG_AdjustPositionForMover Also called by client movement prediction code ========================= */ void CG_AdjustPositionForMover( const vec3_t in, int moverNum, int fromTime, int toTime, vec3_t out ) { centity_t *cent; vec3_t oldOrigin, origin, deltaOrigin; vec3_t oldAngles, angles, deltaAngles; if ( moverNum <= 0 || moverNum >= ENTITYNUM_MAX_NORMAL ) { VectorCopy( in, out ); return; } cent = &cg_entities[ moverNum ]; if ( cent->currentState.eType != ET_MOVER ) { VectorCopy( in, out ); return; } BG_EvaluateTrajectory( ¢->currentState.pos, fromTime, oldOrigin ); BG_EvaluateTrajectory( ¢->currentState.apos, fromTime, oldAngles ); BG_EvaluateTrajectory( ¢->currentState.pos, toTime, origin ); BG_EvaluateTrajectory( ¢->currentState.apos, toTime, angles ); VectorSubtract( origin, oldOrigin, deltaOrigin ); VectorSubtract( angles, oldAngles, deltaAngles ); VectorAdd( in, deltaOrigin, out ); // FIXME: origin change when on a rotating object } /* ============================= CG_InterpolateEntityPosition ============================= */ static void CG_InterpolateEntityPosition( centity_t *cent ) { vec3_t current, next; float f; // it would be an internal error to find an entity that interpolates without // a snapshot ahead of the current one if ( cg.nextSnap == NULL ) { CG_Error( "CG_InterpoateEntityPosition: cg.nextSnap == NULL" ); } f = cg.frameInterpolation; // this will linearize a sine or parabolic curve, but it is important // to not extrapolate player positions if more recent data is available BG_EvaluateTrajectory( ¢->currentState.pos, cg.snap->serverTime, current ); BG_EvaluateTrajectory( ¢->nextState.pos, cg.nextSnap->serverTime, next ); cent->lerpOrigin[0] = current[0] + f * ( next[0] - current[0] ); cent->lerpOrigin[1] = current[1] + f * ( next[1] - current[1] ); cent->lerpOrigin[2] = current[2] + f * ( next[2] - current[2] ); BG_EvaluateTrajectory( ¢->currentState.apos, cg.snap->serverTime, current ); BG_EvaluateTrajectory( ¢->nextState.apos, cg.nextSnap->serverTime, next ); cent->lerpAngles[0] = LerpAngle( current[0], next[0], f ); cent->lerpAngles[1] = LerpAngle( current[1], next[1], f ); cent->lerpAngles[2] = LerpAngle( current[2], next[2], f ); } /* =============== CG_CalcEntityLerpPositions =============== */ static void CG_CalcEntityLerpPositions( centity_t *cent ) { // if this player does not want to see extrapolated players if ( !cg_smoothClients.integer ) { // make sure the clients use TR_INTERPOLATE if ( cent->currentState.number < MAX_CLIENTS ) { cent->currentState.pos.trType = TR_INTERPOLATE; cent->nextState.pos.trType = TR_INTERPOLATE; } } if ( cent->interpolate && cent->currentState.pos.trType == TR_INTERPOLATE ) { CG_InterpolateEntityPosition( cent ); return; } // first see if we can interpolate between two snaps for // linear extrapolated clients if ( cent->interpolate && cent->currentState.pos.trType == TR_LINEAR_STOP && cent->currentState.number < MAX_CLIENTS) { CG_InterpolateEntityPosition( cent ); return; } // just use the current frame and evaluate as best we can BG_EvaluateTrajectory( ¢->currentState.pos, cg.time, cent->lerpOrigin ); BG_EvaluateTrajectory( ¢->currentState.apos, cg.time, cent->lerpAngles ); // adjust for riding a mover if it wasn't rolled into the predicted // player state if ( cent != &cg.predictedPlayerEntity ) { CG_AdjustPositionForMover( cent->lerpOrigin, cent->currentState.groundEntityNum, cg.snap->serverTime, cg.time, cent->lerpOrigin ); } } /* =============== CG_TeamBase =============== */ static void CG_TeamBase( centity_t *cent ) { refEntity_t model; if ( cgs.gametype == GT_CTF) { // show the flag base memset(&model, 0, sizeof(model)); model.reType = RT_MODEL; VectorCopy( cent->lerpOrigin, model.lightingOrigin ); VectorCopy( cent->lerpOrigin, model.origin ); AnglesToAxis( cent->currentState.angles, model.axis ); if ( cent->currentState.modelindex == TEAM_RED ) { model.hModel = cgs.media.redFlagBaseModel; } else if ( cent->currentState.modelindex == TEAM_BLUE ) { model.hModel = cgs.media.blueFlagBaseModel; } else { model.hModel = cgs.media.neutralFlagBaseModel; } trap_R_AddRefEntityToScene( &model ); } } /* =============== CG_AddCEntity =============== */ static void CG_AddCEntity( centity_t *cent ) { // event-only entities will have been dealt with already if ( cent->currentState.eType >= ET_EVENTS ) { return; } // calculate the current origin CG_CalcEntityLerpPositions( cent ); // add automatic effects CG_EntityEffects( cent ); switch ( cent->currentState.eType ) { default: CG_Error( "Bad entity type: %i\n", cent->currentState.eType ); break; case ET_INVISIBLE: case ET_PUSH_TRIGGER: case ET_TELEPORT_TRIGGER: break; case ET_GENERAL: CG_General( cent ); break; case ET_PLAYER: CG_Player( cent ); break; case ET_ITEM: CG_Item( cent ); break; case ET_MISSILE: CG_Missile( cent ); break; case ET_MOVER: CG_Mover( cent ); break; case ET_BEAM: CG_Beam( cent ); break; case ET_PORTAL: CG_Portal( cent ); break; case ET_SPEAKER: CG_Speaker( cent ); break; case ET_GRAPPLE: CG_Grapple( cent ); break; case ET_TEAM: CG_TeamBase( cent ); break; } } /* =============== CG_AddPacketEntities =============== */ void CG_AddPacketEntities( void ) { int num; centity_t *cent; playerState_t *ps; // set cg.frameInterpolation if ( cg.nextSnap ) { int delta; delta = (cg.nextSnap->serverTime - cg.snap->serverTime); if ( delta == 0 ) { cg.frameInterpolation = 0; } else { cg.frameInterpolation = (float)( cg.time - cg.snap->serverTime ) / delta; } } else { cg.frameInterpolation = 0; // actually, it should never be used, because // no entities should be marked as interpolating } // the auto-rotating items will all have the same axis cg.autoAngles[0] = 0; cg.autoAngles[1] = ( cg.time & 2047 ) * 360 / 2048.0; cg.autoAngles[2] = 0; cg.autoAnglesFast[0] = 0; cg.autoAnglesFast[1] = ( cg.time & 1023 ) * 360 / 1024.0f; cg.autoAnglesFast[2] = 0; AnglesToAxis( cg.autoAngles, cg.autoAxis ); AnglesToAxis( cg.autoAnglesFast, cg.autoAxisFast ); // generate and add the entity from the playerstate ps = &cg.predictedPlayerState; BG_PlayerStateToEntityState( ps, &cg.predictedPlayerEntity.currentState, qfalse ); CG_AddCEntity( &cg.predictedPlayerEntity ); // lerp the non-predicted value for lightning gun origins CG_CalcEntityLerpPositions( &cg_entities[ cg.snap->ps.clientNum ] ); // add each entity sent over by the server for ( num = 0 ; num < cg.snap->numEntities ; num++ ) { cent = &cg_entities[ cg.snap->entities[ num ].number ]; CG_AddCEntity( cent ); } } ================================================ FILE: src/cgame/cg_event.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_event.c -- handle entity events at snapshot or playerstate transitions #include "cg_local.h" /* =================== CG_PlaceString Also called by scoreboard drawing =================== */ const char *CG_PlaceString( int rank ) { static char str[64]; char *s, *t; if ( rank & RANK_TIED_FLAG ) { rank &= ~RANK_TIED_FLAG; t = "Tied for "; } else { t = ""; } if ( rank == 1 ) { s = S_COLOR_BLUE "1st" S_COLOR_WHITE; // draw in blue } else if ( rank == 2 ) { s = S_COLOR_RED "2nd" S_COLOR_WHITE; // draw in red } else if ( rank == 3 ) { s = S_COLOR_YELLOW "3rd" S_COLOR_WHITE; // draw in yellow } else if ( rank == 11 ) { s = "11th"; } else if ( rank == 12 ) { s = "12th"; } else if ( rank == 13 ) { s = "13th"; } else if ( rank % 10 == 1 ) { s = va("%ist", rank); } else if ( rank % 10 == 2 ) { s = va("%ind", rank); } else if ( rank % 10 == 3 ) { s = va("%ird", rank); } else { s = va("%ith", rank); } Com_sprintf( str, sizeof( str ), "%s%s", t, s ); return str; } /* ============= CG_Obituary ============= */ static void CG_Obituary( entityState_t *ent ) { int mod; int target, attacker; char *message; char *message2; const char *targetInfo; const char *attackerInfo; char targetName[32]; char attackerName[32]; gender_t gender; clientInfo_t *ci; target = ent->otherEntityNum; attacker = ent->otherEntityNum2; mod = ent->eventParm; if ( target < 0 || target >= MAX_CLIENTS ) { CG_Error( "CG_Obituary: target out of range" ); } ci = &cgs.clientinfo[target]; if ( attacker < 0 || attacker >= MAX_CLIENTS ) { attacker = ENTITYNUM_WORLD; attackerInfo = NULL; } else { attackerInfo = CG_ConfigString( CS_PLAYERS + attacker ); } targetInfo = CG_ConfigString( CS_PLAYERS + target ); if ( !targetInfo ) { return; } Q_strncpyz( targetName, Info_ValueForKey( targetInfo, "n" ), sizeof(targetName) - 2); strcat( targetName, S_COLOR_WHITE ); message2 = ""; // check for single client messages switch( mod ) { case MOD_SUICIDE: message = "suicides"; break; case MOD_FALLING: message = "cratered"; break; case MOD_CRUSH: message = "was squished"; break; case MOD_WATER: message = "sank like a rock"; break; case MOD_SLIME: message = "melted"; break; case MOD_LAVA: message = "does a back flip into the lava"; break; case MOD_TARGET_LASER: message = "saw the light"; break; case MOD_TRIGGER_HURT: message = "was in the wrong place"; break; default: message = NULL; break; } if (attacker == target) { gender = ci->gender; switch (mod) { case MOD_GRENADE_SPLASH: if ( gender == GENDER_FEMALE ) message = "tripped on her own grenade"; else if ( gender == GENDER_NEUTER ) message = "tripped on its own grenade"; else message = "tripped on his own grenade"; break; case MOD_ROCKET_SPLASH: if ( gender == GENDER_FEMALE ) message = "blew herself up"; else if ( gender == GENDER_NEUTER ) message = "blew itself up"; else message = "blew himself up"; break; case MOD_PLASMA_SPLASH: if ( gender == GENDER_FEMALE ) message = "melted herself"; else if ( gender == GENDER_NEUTER ) message = "melted itself"; else message = "melted himself"; break; case MOD_BFG_SPLASH: message = "should have used a smaller gun"; break; default: if ( gender == GENDER_FEMALE ) message = "killed herself"; else if ( gender == GENDER_NEUTER ) message = "killed itself"; else message = "killed himself"; break; } } if (message) { CG_Printf( "%s %s.\n", targetName, message); return; } // check for kill messages from the current clientNum if ( attacker == cg.snap->ps.clientNum ) { char *s; if ( cgs.gametype < GT_TEAM ) { s = va("You fragged %s\n%s place with %i", targetName, CG_PlaceString( cg.snap->ps.persistant[PERS_RANK] + 1 ), cg.snap->ps.persistant[PERS_SCORE] ); } else { s = va("You fragged %s", targetName ); } CG_CenterPrint( s, SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH ); // print the text message as well } // check for double client messages if ( !attackerInfo ) { attacker = ENTITYNUM_WORLD; strcpy( attackerName, "noname" ); } else { Q_strncpyz( attackerName, Info_ValueForKey( attackerInfo, "n" ), sizeof(attackerName) - 2); strcat( attackerName, S_COLOR_WHITE ); // check for kill messages about the current clientNum if ( target == cg.snap->ps.clientNum ) { Q_strncpyz( cg.killerName, attackerName, sizeof( cg.killerName ) ); } } if ( attacker != ENTITYNUM_WORLD ) { switch (mod) { case MOD_GRAPPLE: message = "was caught by"; break; case MOD_GAUNTLET: message = "was pummeled by"; break; case MOD_MACHINEGUN: message = "was machinegunned by"; break; case MOD_SHOTGUN: message = "was gunned down by"; break; case MOD_GRENADE: message = "ate"; message2 = "'s grenade"; break; case MOD_GRENADE_SPLASH: message = "was shredded by"; message2 = "'s shrapnel"; break; case MOD_ROCKET: message = "ate"; message2 = "'s rocket"; break; case MOD_ROCKET_SPLASH: message = "almost dodged"; message2 = "'s rocket"; break; case MOD_PLASMA: message = "was melted by"; message2 = "'s plasmagun"; break; case MOD_PLASMA_SPLASH: message = "was melted by"; message2 = "'s plasmagun"; break; case MOD_RAILGUN: message = "was railed by"; break; case MOD_LIGHTNING: message = "was electrocuted by"; break; case MOD_BFG: case MOD_BFG_SPLASH: message = "was blasted by"; message2 = "'s BFG"; break; case MOD_TELEFRAG: message = "tried to invade"; message2 = "'s personal space"; break; default: message = "was killed by"; break; } if (message) { CG_Printf( "%s %s %s%s\n", targetName, message, attackerName, message2); return; } } // we don't know what it was CG_Printf( "%s died.\n", targetName ); } //========================================================================== /* =============== CG_UseItem =============== */ static void CG_UseItem( centity_t *cent ) { clientInfo_t *ci; int itemNum, clientNum; gitem_t *item; entityState_t *es; es = ¢->currentState; itemNum = (es->event & ~EV_EVENT_BITS) - EV_USE_ITEM0; if ( itemNum < 0 || itemNum > HI_NUM_HOLDABLE ) { itemNum = 0; } // print a message if the local player if ( es->number == cg.snap->ps.clientNum ) { if ( !itemNum ) { CG_CenterPrint( "No item to use", SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH ); } else { item = BG_FindItemForHoldable( itemNum ); CG_CenterPrint( va("Use %s", item->pickup_name), SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH ); } } switch ( itemNum ) { default: case HI_NONE: trap_S_StartSound (NULL, es->number, CHAN_BODY, cgs.media.useNothingSound ); break; case HI_TELEPORTER: break; case HI_MEDKIT: clientNum = cent->currentState.clientNum; if ( clientNum >= 0 && clientNum < MAX_CLIENTS ) { ci = &cgs.clientinfo[ clientNum ]; ci->medkitUsageTime = cg.time; } trap_S_StartSound (NULL, es->number, CHAN_BODY, cgs.media.medkitSound ); break; } } /* ================ CG_ItemPickup A new item was picked up this frame ================ */ static void CG_ItemPickup( int itemNum ) { cg.itemPickup = itemNum; cg.itemPickupTime = cg.time; cg.itemPickupBlendTime = cg.time; // see if it should be the grabbed weapon if ( bg_itemlist[itemNum].giType == IT_WEAPON ) { // select it immediately if ( cg_autoswitch.integer && bg_itemlist[itemNum].giTag != WP_MACHINEGUN ) { cg.weaponSelectTime = cg.time; cg.weaponSelect = bg_itemlist[itemNum].giTag; } } } /* ================ CG_PainEvent Also called by playerstate transition ================ */ void CG_PainEvent( centity_t *cent, int health ) { char *snd; // don't do more than two pain sounds a second if ( cg.time - cent->pe.painTime < 500 ) { return; } if ( health < 25 ) { snd = "*pain25_1.wav"; } else if ( health < 50 ) { snd = "*pain50_1.wav"; } else if ( health < 75 ) { snd = "*pain75_1.wav"; } else { snd = "*pain100_1.wav"; } trap_S_StartSound( NULL, cent->currentState.number, CHAN_VOICE, CG_CustomSound( cent->currentState.number, snd ) ); // save pain time for programitic twitch animation cent->pe.painTime = cg.time; cent->pe.painDirection ^= 1; } /* ============== CG_EntityEvent An entity has an event value also called by CG_CheckPlayerstateEvents ============== */ #define DEBUGNAME(x) if(cg_debugEvents.integer){CG_Printf(x"\n");} void CG_EntityEvent( centity_t *cent, vec3_t position ) { entityState_t *es; int event; vec3_t dir; const char *s; int clientNum; clientInfo_t *ci; es = ¢->currentState; event = es->event & ~EV_EVENT_BITS; if ( cg_debugEvents.integer ) { CG_Printf( "ent:%3i event:%3i ", es->number, event ); } if ( !event ) { DEBUGNAME("ZEROEVENT"); return; } clientNum = es->clientNum; if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) { clientNum = 0; } ci = &cgs.clientinfo[ clientNum ]; switch ( event ) { // // movement generated events // case EV_FOOTSTEP: DEBUGNAME("EV_FOOTSTEP"); if (cg_footsteps.integer) { trap_S_StartSound (NULL, es->number, CHAN_BODY, cgs.media.footsteps[ ci->footsteps ][rand()&3] ); } break; case EV_FOOTSTEP_METAL: DEBUGNAME("EV_FOOTSTEP_METAL"); if (cg_footsteps.integer) { trap_S_StartSound (NULL, es->number, CHAN_BODY, cgs.media.footsteps[ FOOTSTEP_METAL ][rand()&3] ); } break; case EV_FOOTSPLASH: DEBUGNAME("EV_FOOTSPLASH"); if (cg_footsteps.integer) { trap_S_StartSound (NULL, es->number, CHAN_BODY, cgs.media.footsteps[ FOOTSTEP_SPLASH ][rand()&3] ); } break; case EV_FOOTWADE: DEBUGNAME("EV_FOOTWADE"); if (cg_footsteps.integer) { trap_S_StartSound (NULL, es->number, CHAN_BODY, cgs.media.footsteps[ FOOTSTEP_SPLASH ][rand()&3] ); } break; case EV_SWIM: DEBUGNAME("EV_SWIM"); if (cg_footsteps.integer) { trap_S_StartSound (NULL, es->number, CHAN_BODY, cgs.media.footsteps[ FOOTSTEP_SPLASH ][rand()&3] ); } break; case EV_FALL_SHORT: DEBUGNAME("EV_FALL_SHORT"); trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.landSound ); if ( clientNum == cg.predictedPlayerState.clientNum ) { // smooth landing z changes cg.landChange = -8; cg.landTime = cg.time; } break; case EV_FALL_MEDIUM: DEBUGNAME("EV_FALL_MEDIUM"); // use normal pain sound trap_S_StartSound( NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*pain100_1.wav" ) ); if ( clientNum == cg.predictedPlayerState.clientNum ) { // smooth landing z changes cg.landChange = -16; cg.landTime = cg.time; } break; case EV_FALL_FAR: DEBUGNAME("EV_FALL_FAR"); trap_S_StartSound (NULL, es->number, CHAN_AUTO, CG_CustomSound( es->number, "*fall1.wav" ) ); cent->pe.painTime = cg.time; // don't play a pain sound right after this if ( clientNum == cg.predictedPlayerState.clientNum ) { // smooth landing z changes cg.landChange = -24; cg.landTime = cg.time; } break; case EV_STEP_4: case EV_STEP_8: case EV_STEP_12: case EV_STEP_16: // smooth out step up transitions DEBUGNAME("EV_STEP"); { float oldStep; int delta; int step; if ( clientNum != cg.predictedPlayerState.clientNum ) { break; } // if we are interpolating, we don't need to smooth steps if ( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW) || cg_nopredict.integer || cg_synchronousClients.integer ) { break; } // check for stepping up before a previous step is completed delta = cg.time - cg.stepTime; if (delta < STEP_TIME) { oldStep = cg.stepChange * (STEP_TIME - delta) / STEP_TIME; } else { oldStep = 0; } // add this amount step = 4 * (event - EV_STEP_4 + 1 ); cg.stepChange = oldStep + step; if ( cg.stepChange > MAX_STEP_CHANGE ) { cg.stepChange = MAX_STEP_CHANGE; } cg.stepTime = cg.time; break; } case EV_JUMP_PAD: DEBUGNAME("EV_JUMP_PAD"); // CG_Printf( "EV_JUMP_PAD w/effect #%i\n", es->eventParm ); { localEntity_t *smoke; vec3_t up = {0, 0, 1}; smoke = CG_SmokePuff( cent->lerpOrigin, up, 32, 1, 1, 1, 0.33f, 1000, cg.time, 0, LEF_PUFF_DONT_SCALE, cgs.media.smokePuffShader ); } // boing sound at origin, jump sound on player trap_S_StartSound ( cent->lerpOrigin, -1, CHAN_VOICE, cgs.media.jumpPadSound ); trap_S_StartSound (NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*jump1.wav" ) ); break; case EV_JUMP: DEBUGNAME("EV_JUMP"); trap_S_StartSound (NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*jump1.wav" ) ); break; case EV_TAUNT: DEBUGNAME("EV_TAUNT"); trap_S_StartSound (NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*taunt.wav" ) ); break; case EV_WATER_TOUCH: DEBUGNAME("EV_WATER_TOUCH"); trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.watrInSound ); break; case EV_WATER_LEAVE: DEBUGNAME("EV_WATER_LEAVE"); trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.watrOutSound ); break; case EV_WATER_UNDER: DEBUGNAME("EV_WATER_UNDER"); trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.watrUnSound ); break; case EV_WATER_CLEAR: DEBUGNAME("EV_WATER_CLEAR"); trap_S_StartSound (NULL, es->number, CHAN_AUTO, CG_CustomSound( es->number, "*gasp.wav" ) ); break; case EV_ITEM_PICKUP: DEBUGNAME("EV_ITEM_PICKUP"); { gitem_t *item; int index; index = es->eventParm; // player predicted if ( index < 1 || index >= bg_numItems ) { break; } item = &bg_itemlist[ index ]; // powerups and team items will have a separate global sound, this one // will be played at prediction time if ( item->giType == IT_POWERUP || item->giType == IT_TEAM) { trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.n_healthSound ); } else if (item->giType == IT_PERSISTANT_POWERUP) { } else { trap_S_StartSound (NULL, es->number, CHAN_AUTO, trap_S_RegisterSound( item->pickup_sound, qfalse ) ); } // show icon and name on status bar if ( es->number == cg.snap->ps.clientNum ) { CG_ItemPickup( index ); } } break; case EV_GLOBAL_ITEM_PICKUP: DEBUGNAME("EV_GLOBAL_ITEM_PICKUP"); { gitem_t *item; int index; index = es->eventParm; // player predicted if ( index < 1 || index >= bg_numItems ) { break; } item = &bg_itemlist[ index ]; // powerup pickups are global if( item->pickup_sound ) { trap_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_AUTO, trap_S_RegisterSound( item->pickup_sound, qfalse ) ); } // show icon and name on status bar if ( es->number == cg.snap->ps.clientNum ) { CG_ItemPickup( index ); } } break; // // weapon events // case EV_NOAMMO: DEBUGNAME("EV_NOAMMO"); // trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.noAmmoSound ); if ( es->number == cg.snap->ps.clientNum ) { CG_OutOfAmmoChange(); } break; case EV_CHANGE_WEAPON: DEBUGNAME("EV_CHANGE_WEAPON"); trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.selectSound ); break; case EV_FIRE_WEAPON: DEBUGNAME("EV_FIRE_WEAPON"); CG_FireWeapon( cent ); break; case EV_USE_ITEM0: DEBUGNAME("EV_USE_ITEM0"); CG_UseItem( cent ); break; case EV_USE_ITEM1: DEBUGNAME("EV_USE_ITEM1"); CG_UseItem( cent ); break; case EV_USE_ITEM2: DEBUGNAME("EV_USE_ITEM2"); CG_UseItem( cent ); break; case EV_USE_ITEM3: DEBUGNAME("EV_USE_ITEM3"); CG_UseItem( cent ); break; case EV_USE_ITEM4: DEBUGNAME("EV_USE_ITEM4"); CG_UseItem( cent ); break; case EV_USE_ITEM5: DEBUGNAME("EV_USE_ITEM5"); CG_UseItem( cent ); break; case EV_USE_ITEM6: DEBUGNAME("EV_USE_ITEM6"); CG_UseItem( cent ); break; case EV_USE_ITEM7: DEBUGNAME("EV_USE_ITEM7"); CG_UseItem( cent ); break; case EV_USE_ITEM8: DEBUGNAME("EV_USE_ITEM8"); CG_UseItem( cent ); break; case EV_USE_ITEM9: DEBUGNAME("EV_USE_ITEM9"); CG_UseItem( cent ); break; case EV_USE_ITEM10: DEBUGNAME("EV_USE_ITEM10"); CG_UseItem( cent ); break; case EV_USE_ITEM11: DEBUGNAME("EV_USE_ITEM11"); CG_UseItem( cent ); break; case EV_USE_ITEM12: DEBUGNAME("EV_USE_ITEM12"); CG_UseItem( cent ); break; case EV_USE_ITEM13: DEBUGNAME("EV_USE_ITEM13"); CG_UseItem( cent ); break; case EV_USE_ITEM14: DEBUGNAME("EV_USE_ITEM14"); CG_UseItem( cent ); break; //================================================================= // // other events // case EV_PLAYER_TELEPORT_IN: DEBUGNAME("EV_PLAYER_TELEPORT_IN"); trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.teleInSound ); CG_SpawnEffect( position); break; case EV_PLAYER_TELEPORT_OUT: DEBUGNAME("EV_PLAYER_TELEPORT_OUT"); trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.teleOutSound ); CG_SpawnEffect( position); break; case EV_ITEM_POP: DEBUGNAME("EV_ITEM_POP"); trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.respawnSound ); break; case EV_ITEM_RESPAWN: DEBUGNAME("EV_ITEM_RESPAWN"); cent->miscTime = cg.time; // scale up from this trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.respawnSound ); break; case EV_GRENADE_BOUNCE: DEBUGNAME("EV_GRENADE_BOUNCE"); if ( rand() & 1 ) { trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.hgrenb1aSound ); } else { trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.hgrenb2aSound ); } break; case EV_SCOREPLUM: DEBUGNAME("EV_SCOREPLUM"); CG_ScorePlum( cent->currentState.otherEntityNum, cent->lerpOrigin, cent->currentState.time ); break; // // missile impacts // case EV_MISSILE_HIT: DEBUGNAME("EV_MISSILE_HIT"); ByteToDir( es->eventParm, dir ); CG_MissileHitPlayer( es->weapon, position, dir, es->otherEntityNum ); break; case EV_MISSILE_MISS: DEBUGNAME("EV_MISSILE_MISS"); ByteToDir( es->eventParm, dir ); CG_MissileHitWall( es->weapon, 0, position, dir, IMPACTSOUND_DEFAULT ); break; case EV_MISSILE_MISS_METAL: DEBUGNAME("EV_MISSILE_MISS_METAL"); ByteToDir( es->eventParm, dir ); CG_MissileHitWall( es->weapon, 0, position, dir, IMPACTSOUND_METAL ); break; case EV_RAILTRAIL: DEBUGNAME("EV_RAILTRAIL"); cent->currentState.weapon = WP_RAILGUN; // if the end was on a nomark surface, don't make an explosion CG_RailTrail( ci, es->origin2, es->pos.trBase ); if ( es->eventParm != 255 ) { ByteToDir( es->eventParm, dir ); CG_MissileHitWall( es->weapon, es->clientNum, position, dir, IMPACTSOUND_DEFAULT ); } break; case EV_BULLET_HIT_WALL: DEBUGNAME("EV_BULLET_HIT_WALL"); ByteToDir( es->eventParm, dir ); CG_Bullet( es->pos.trBase, es->otherEntityNum, dir, qfalse, ENTITYNUM_WORLD ); break; case EV_BULLET_HIT_FLESH: DEBUGNAME("EV_BULLET_HIT_FLESH"); CG_Bullet( es->pos.trBase, es->otherEntityNum, dir, qtrue, es->eventParm ); break; case EV_SHOTGUN: DEBUGNAME("EV_SHOTGUN"); CG_ShotgunFire( es ); break; case EV_GENERAL_SOUND: DEBUGNAME("EV_GENERAL_SOUND"); if ( cgs.gameSounds[ es->eventParm ] ) { trap_S_StartSound (NULL, es->number, CHAN_VOICE, cgs.gameSounds[ es->eventParm ] ); } else { s = CG_ConfigString( CS_SOUNDS + es->eventParm ); trap_S_StartSound (NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, s ) ); } break; case EV_GLOBAL_SOUND: // play from the player's head so it never diminishes DEBUGNAME("EV_GLOBAL_SOUND"); if ( cgs.gameSounds[ es->eventParm ] ) { trap_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_AUTO, cgs.gameSounds[ es->eventParm ] ); } else { s = CG_ConfigString( CS_SOUNDS + es->eventParm ); trap_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_AUTO, CG_CustomSound( es->number, s ) ); } break; case EV_GLOBAL_TEAM_SOUND: // play from the player's head so it never diminishes { DEBUGNAME("EV_GLOBAL_TEAM_SOUND"); switch( es->eventParm ) { case GTS_RED_CAPTURE: // CTF: red team captured the blue flag, 1FCTF: red team captured the neutral flag if ( cgs.clientinfo[cg.clientNum].team == TEAM_RED ) CG_AddBufferedSound( cgs.media.captureYourTeamSound ); else CG_AddBufferedSound( cgs.media.captureOpponentSound ); break; case GTS_BLUE_CAPTURE: // CTF: blue team captured the red flag, 1FCTF: blue team captured the neutral flag if ( cgs.clientinfo[cg.clientNum].team == TEAM_BLUE ) CG_AddBufferedSound( cgs.media.captureYourTeamSound ); else CG_AddBufferedSound( cgs.media.captureOpponentSound ); break; case GTS_RED_RETURN: // CTF: blue flag returned, 1FCTF: never used if ( cgs.clientinfo[cg.clientNum].team == TEAM_RED ) CG_AddBufferedSound( cgs.media.returnYourTeamSound ); else CG_AddBufferedSound( cgs.media.returnOpponentSound ); // CG_AddBufferedSound( cgs.media.blueFlagReturnedSound ); break; case GTS_BLUE_RETURN: // CTF red flag returned, 1FCTF: neutral flag returned if ( cgs.clientinfo[cg.clientNum].team == TEAM_BLUE ) CG_AddBufferedSound( cgs.media.returnYourTeamSound ); else CG_AddBufferedSound( cgs.media.returnOpponentSound ); // CG_AddBufferedSound( cgs.media.redFlagReturnedSound ); break; case GTS_RED_TAKEN: // CTF: red team took blue flag, 1FCTF: blue team took the neutral flag // if this player picked up the flag then a sound is played in CG_CheckLocalSounds if (cg.snap->ps.powerups[PW_BLUEFLAG] || cg.snap->ps.powerups[PW_NEUTRALFLAG]) { } else { if (cgs.clientinfo[cg.clientNum].team == TEAM_BLUE) { CG_AddBufferedSound( cgs.media.enemyTookYourFlagSound ); } else if (cgs.clientinfo[cg.clientNum].team == TEAM_RED) { CG_AddBufferedSound( cgs.media.yourTeamTookEnemyFlagSound ); } } break; case GTS_BLUE_TAKEN: // CTF: blue team took the red flag, 1FCTF red team took the neutral flag // if this player picked up the flag then a sound is played in CG_CheckLocalSounds if (cg.snap->ps.powerups[PW_REDFLAG] || cg.snap->ps.powerups[PW_NEUTRALFLAG]) { } else { if (cgs.clientinfo[cg.clientNum].team == TEAM_RED) { CG_AddBufferedSound( cgs.media.enemyTookYourFlagSound ); } else if (cgs.clientinfo[cg.clientNum].team == TEAM_BLUE) { CG_AddBufferedSound( cgs.media.yourTeamTookEnemyFlagSound ); } } break; case GTS_REDOBELISK_ATTACKED: // Overload: red obelisk is being attacked if (cgs.clientinfo[cg.clientNum].team == TEAM_RED) { CG_AddBufferedSound( cgs.media.yourBaseIsUnderAttackSound ); } break; case GTS_BLUEOBELISK_ATTACKED: // Overload: blue obelisk is being attacked if (cgs.clientinfo[cg.clientNum].team == TEAM_BLUE) { CG_AddBufferedSound( cgs.media.yourBaseIsUnderAttackSound ); } break; case GTS_REDTEAM_SCORED: CG_AddBufferedSound(cgs.media.redScoredSound); break; case GTS_BLUETEAM_SCORED: CG_AddBufferedSound(cgs.media.blueScoredSound); break; case GTS_REDTEAM_TOOK_LEAD: CG_AddBufferedSound(cgs.media.redLeadsSound); break; case GTS_BLUETEAM_TOOK_LEAD: CG_AddBufferedSound(cgs.media.blueLeadsSound); break; case GTS_TEAMS_ARE_TIED: CG_AddBufferedSound( cgs.media.teamsTiedSound ); break; default: break; } break; } case EV_PAIN: // local player sounds are triggered in CG_CheckLocalSounds, // so ignore events on the player DEBUGNAME("EV_PAIN"); if ( cent->currentState.number != cg.snap->ps.clientNum ) { CG_PainEvent( cent, es->eventParm ); } break; case EV_DEATH1: case EV_DEATH2: case EV_DEATH3: DEBUGNAME("EV_DEATHx"); trap_S_StartSound( NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, va("*death%i.wav", event - EV_DEATH1 + 1) ) ); break; case EV_OBITUARY: DEBUGNAME("EV_OBITUARY"); CG_Obituary( es ); break; // // powerup events // case EV_POWERUP_QUAD: DEBUGNAME("EV_POWERUP_QUAD"); if ( es->number == cg.snap->ps.clientNum ) { cg.powerupActive = PW_QUAD; cg.powerupTime = cg.time; } trap_S_StartSound (NULL, es->number, CHAN_ITEM, cgs.media.quadSound ); break; case EV_POWERUP_BATTLESUIT: DEBUGNAME("EV_POWERUP_BATTLESUIT"); if ( es->number == cg.snap->ps.clientNum ) { cg.powerupActive = PW_BATTLESUIT; cg.powerupTime = cg.time; } trap_S_StartSound (NULL, es->number, CHAN_ITEM, cgs.media.protectSound ); break; case EV_POWERUP_REGEN: DEBUGNAME("EV_POWERUP_REGEN"); if ( es->number == cg.snap->ps.clientNum ) { cg.powerupActive = PW_REGEN; cg.powerupTime = cg.time; } trap_S_StartSound (NULL, es->number, CHAN_ITEM, cgs.media.regenSound ); break; case EV_GIB_PLAYER: DEBUGNAME("EV_GIB_PLAYER"); // don't play gib sound when using the kamikaze because it interferes // with the kamikaze sound, downside is that the gib sound will also // not be played when someone is gibbed while just carrying the kamikaze if ( !(es->eFlags & EF_KAMIKAZE) ) { trap_S_StartSound( NULL, es->number, CHAN_BODY, cgs.media.gibSound ); } CG_GibPlayer( cent->lerpOrigin ); break; case EV_STOPLOOPINGSOUND: DEBUGNAME("EV_STOPLOOPINGSOUND"); trap_S_StopLoopingSound( es->number ); es->loopSound = 0; break; case EV_DEBUG_LINE: DEBUGNAME("EV_DEBUG_LINE"); CG_Beam( cent ); break; default: DEBUGNAME("UNKNOWN"); CG_Error( "Unknown event: %i", event ); break; } } /* ============== CG_CheckEvents ============== */ void CG_CheckEvents( centity_t *cent ) { // check for event-only entities if ( cent->currentState.eType > ET_EVENTS ) { if ( cent->previousEvent ) { return; // already fired } // if this is a player event set the entity number of the client entity number if ( cent->currentState.eFlags & EF_PLAYER_EVENT ) { cent->currentState.number = cent->currentState.otherEntityNum; } cent->previousEvent = 1; cent->currentState.event = cent->currentState.eType - ET_EVENTS; } else { // check for events riding with another entity if ( cent->currentState.event == cent->previousEvent ) { return; } cent->previousEvent = cent->currentState.event; if ( ( cent->currentState.event & ~EV_EVENT_BITS ) == 0 ) { return; } } // calculate the position at exactly the frame time BG_EvaluateTrajectory( ¢->currentState.pos, cg.snap->serverTime, cent->lerpOrigin ); CG_SetEntitySoundPosition( cent ); CG_EntityEvent( cent, cent->lerpOrigin ); } ================================================ FILE: src/cgame/cg_info.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_info.c -- display information while data is being loading #include "cg_local.h" #define MAX_LOADING_PLAYER_ICONS 16 #define MAX_LOADING_ITEM_ICONS 26 static int loadingPlayerIconCount; static int loadingItemIconCount; static qhandle_t loadingPlayerIcons[MAX_LOADING_PLAYER_ICONS]; static qhandle_t loadingItemIcons[MAX_LOADING_ITEM_ICONS]; /* =================== CG_DrawLoadingIcons =================== */ static void CG_DrawLoadingIcons( void ) { int n; int x, y; for( n = 0; n < loadingPlayerIconCount; n++ ) { x = 16 + n * 78; y = 324-40; CG_DrawPic( x, y, 64, 64, loadingPlayerIcons[n] ); } for( n = 0; n < loadingItemIconCount; n++ ) { y = 400-40; if( n >= 13 ) { y += 40; } x = 16 + n % 13 * 48; CG_DrawPic( x, y, 32, 32, loadingItemIcons[n] ); } } /* ====================== CG_LoadingString ====================== */ void CG_LoadingString( const char *s ) { Q_strncpyz( cg.infoScreenText, s, sizeof( cg.infoScreenText ) ); trap_UpdateScreen(); } /* =================== CG_LoadingItem =================== */ void CG_LoadingItem( int itemNum ) { gitem_t *item; item = &bg_itemlist[itemNum]; if ( item->icon && loadingItemIconCount < MAX_LOADING_ITEM_ICONS ) { loadingItemIcons[loadingItemIconCount++] = trap_R_RegisterShaderNoMip( item->icon ); } CG_LoadingString( item->pickup_name ); } /* =================== CG_LoadingClient =================== */ void CG_LoadingClient( int clientNum ) { const char *info; char *skin; char personality[MAX_QPATH]; char model[MAX_QPATH]; char iconName[MAX_QPATH]; info = CG_ConfigString( CS_PLAYERS + clientNum ); if ( loadingPlayerIconCount < MAX_LOADING_PLAYER_ICONS ) { Q_strncpyz( model, Info_ValueForKey( info, "model" ), sizeof( model ) ); skin = Q_strrchr( model, '/' ); if ( skin ) { *skin++ = '\0'; } else { skin = "default"; } Com_sprintf( iconName, MAX_QPATH, "models/players/%s/icon_%s.tga", model, skin ); loadingPlayerIcons[loadingPlayerIconCount] = trap_R_RegisterShaderNoMip( iconName ); if ( !loadingPlayerIcons[loadingPlayerIconCount] ) { Com_sprintf( iconName, MAX_QPATH, "models/players/characters/%s/icon_%s.tga", model, skin ); loadingPlayerIcons[loadingPlayerIconCount] = trap_R_RegisterShaderNoMip( iconName ); } if ( !loadingPlayerIcons[loadingPlayerIconCount] ) { Com_sprintf( iconName, MAX_QPATH, "models/players/%s/icon_%s.tga", DEFAULT_MODEL, "default" ); loadingPlayerIcons[loadingPlayerIconCount] = trap_R_RegisterShaderNoMip( iconName ); } if ( loadingPlayerIcons[loadingPlayerIconCount] ) { loadingPlayerIconCount++; } } Q_strncpyz( personality, Info_ValueForKey( info, "n" ), sizeof(personality) ); Q_CleanStr( personality ); if( cgs.gametype == GT_SINGLE_PLAYER ) { trap_S_RegisterSound( va( "sound/player/announce/%s.wav", personality ), qtrue ); } CG_LoadingString( personality ); } /* ==================== CG_DrawInformation Draw all the status / pacifier stuff during level loading ==================== */ void CG_DrawInformation( void ) { const char *s; const char *info; const char *sysInfo; int y; int value; qhandle_t levelshot; qhandle_t detail; char buf[1024]; info = CG_ConfigString( CS_SERVERINFO ); sysInfo = CG_ConfigString( CS_SYSTEMINFO ); s = Info_ValueForKey( info, "mapname" ); levelshot = trap_R_RegisterShaderNoMip( va( "levelshots/%s.tga", s ) ); if ( !levelshot ) { levelshot = trap_R_RegisterShaderNoMip( "menu/art/unknownmap" ); } trap_R_SetColor( NULL ); CG_DrawPic( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, levelshot ); // blend a detail texture over it detail = trap_R_RegisterShader( "levelShotDetail" ); trap_R_DrawStretchPic( 0, 0, cgs.glconfig.vidWidth, cgs.glconfig.vidHeight, 0, 0, 2.5, 2, detail ); // draw the icons of things as they are loaded CG_DrawLoadingIcons(); // the first 150 rows are reserved for the client connection // screen to write into if ( cg.infoScreenText[0] ) { UI_DrawProportionalString( 320, 128-32, va("Loading... %s", cg.infoScreenText), UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); } else { UI_DrawProportionalString( 320, 128-32, "Awaiting snapshot...", UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); } // draw info string information y = 180-32; // don't print server lines if playing a local game trap_Cvar_VariableStringBuffer( "sv_running", buf, sizeof( buf ) ); if ( !atoi( buf ) ) { // server hostname Q_strncpyz(buf, Info_ValueForKey( info, "sv_hostname" ), 1024); Q_CleanStr(buf); UI_DrawProportionalString( 320, y, buf, UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); y += PROP_HEIGHT; // pure server s = Info_ValueForKey( sysInfo, "sv_pure" ); if ( s[0] == '1' ) { UI_DrawProportionalString( 320, y, "Pure Server", UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); y += PROP_HEIGHT; } // server-specific message of the day s = CG_ConfigString( CS_MOTD ); if ( s[0] ) { UI_DrawProportionalString( 320, y, s, UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); y += PROP_HEIGHT; } // some extra space after hostname and motd y += 10; } // map-specific message (long map name) s = CG_ConfigString( CS_MESSAGE ); if ( s[0] ) { UI_DrawProportionalString( 320, y, s, UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); y += PROP_HEIGHT; } // cheats warning s = Info_ValueForKey( sysInfo, "sv_cheats" ); if ( s[0] == '1' ) { UI_DrawProportionalString( 320, y, "CHEATS ARE ENABLED", UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); y += PROP_HEIGHT; } // game type switch ( cgs.gametype ) { case GT_FFA: s = "Free For All"; break; case GT_SINGLE_PLAYER: s = "Single Player"; break; case GT_TOURNAMENT: s = "Tournament"; break; case GT_TEAM: s = "Team Deathmatch"; break; case GT_CTF: s = "Capture The Flag"; break; default: s = "Unknown Gametype"; break; } UI_DrawProportionalString( 320, y, s, UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); y += PROP_HEIGHT; value = atoi( Info_ValueForKey( info, "timelimit" ) ); if ( value ) { UI_DrawProportionalString( 320, y, va( "timelimit %i", value ), UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); y += PROP_HEIGHT; } if (cgs.gametype < GT_CTF ) { value = atoi( Info_ValueForKey( info, "fraglimit" ) ); if ( value ) { UI_DrawProportionalString( 320, y, va( "fraglimit %i", value ), UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); y += PROP_HEIGHT; } } if (cgs.gametype >= GT_CTF) { value = atoi( Info_ValueForKey( info, "capturelimit" ) ); if ( value ) { UI_DrawProportionalString( 320, y, va( "capturelimit %i", value ), UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, colorWhite ); y += PROP_HEIGHT; } } } ================================================ FILE: src/cgame/cg_local.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #include "../game/q_shared.h" #include "tr_types.h" #include "../game/bg_public.h" #include "cg_public.h" // The entire cgame module is unloaded and reloaded on each level change, // so there is NO persistant data between levels on the client side. // If you absolutely need something stored, it can either be kept // by the server in the server stored userinfos, or stashed in a cvar. #define POWERUP_BLINKS 5 #define POWERUP_BLINK_TIME 1000 #define FADE_TIME 200 #define PULSE_TIME 200 #define DAMAGE_DEFLECT_TIME 100 #define DAMAGE_RETURN_TIME 400 #define DAMAGE_TIME 500 #define LAND_DEFLECT_TIME 150 #define LAND_RETURN_TIME 300 #define STEP_TIME 200 #define DUCK_TIME 100 #define PAIN_TWITCH_TIME 200 #define WEAPON_SELECT_TIME 1400 #define ITEM_SCALEUP_TIME 1000 #define ZOOM_TIME 150 #define ITEM_BLOB_TIME 200 #define MUZZLE_FLASH_TIME 20 #define SINK_TIME 1000 // time for fragments to sink into ground before going away #define ATTACKER_HEAD_TIME 10000 #define REWARD_TIME 3000 #define PULSE_SCALE 1.5 // amount to scale up the icons when activating #define MAX_STEP_CHANGE 32 #define MAX_VERTS_ON_POLY 10 #define MAX_MARK_POLYS 256 #define STAT_MINUS 10 // num frame for '-' stats digit #define ICON_SIZE 48 #define CHAR_WIDTH 32 #define CHAR_HEIGHT 48 #define TEXT_ICON_SPACE 4 #define TEAMCHAT_WIDTH 80 #define TEAMCHAT_HEIGHT 8 // very large characters #define GIANT_WIDTH 32 #define GIANT_HEIGHT 48 #define NUM_CROSSHAIRS 10 #define TEAM_OVERLAY_MAXNAME_WIDTH 12 #define TEAM_OVERLAY_MAXLOCATION_WIDTH 16 #define DEFAULT_MODEL "sarge" #define DEFAULT_TEAM_MODEL "sarge" #define DEFAULT_TEAM_HEAD "sarge" #define DEFAULT_REDTEAM_NAME "Stroggs" #define DEFAULT_BLUETEAM_NAME "Pagans" typedef enum { FOOTSTEP_NORMAL, FOOTSTEP_BOOT, FOOTSTEP_FLESH, FOOTSTEP_MECH, FOOTSTEP_ENERGY, FOOTSTEP_METAL, FOOTSTEP_SPLASH, FOOTSTEP_TOTAL } footstep_t; typedef enum { IMPACTSOUND_DEFAULT, IMPACTSOUND_METAL, IMPACTSOUND_FLESH } impactSound_t; //================================================= // player entities need to track more information // than any other type of entity. // note that not every player entity is a client entity, // because corpses after respawn are outside the normal // client numbering range // when changing animation, set animationTime to frameTime + lerping time // The current lerp will finish out, then it will lerp to the new animation typedef struct { int oldFrame; int oldFrameTime; // time when ->oldFrame was exactly on int frame; int frameTime; // time when ->frame will be exactly on float backlerp; float yawAngle; qboolean yawing; float pitchAngle; qboolean pitching; int animationNumber; // may include ANIM_TOGGLEBIT animation_t *animation; int animationTime; // time when the first frame of the animation will be exact } lerpFrame_t; typedef struct { lerpFrame_t legs, torso, flag; int painTime; int painDirection; // flip from 0 to 1 int lightningFiring; // railgun trail spawning vec3_t railgunImpact; qboolean railgunFlash; // machinegun spinning float barrelAngle; int barrelTime; qboolean barrelSpinning; } playerEntity_t; //================================================= // centity_t have a direct corespondence with gentity_t in the game, but // only the entityState_t is directly communicated to the cgame typedef struct centity_s { entityState_t currentState; // from cg.frame entityState_t nextState; // from cg.nextFrame, if available qboolean interpolate; // true if next is valid to interpolate to qboolean currentValid; // true if cg.frame holds this entity int muzzleFlashTime; // move to playerEntity? int previousEvent; int teleportFlag; int trailTime; // so missile trails can handle dropped initial packets int dustTrailTime; int miscTime; int snapShotTime; // last time this entity was found in a snapshot playerEntity_t pe; int errorTime; // decay the error from this time vec3_t errorOrigin; vec3_t errorAngles; qboolean extrapolated; // false if origin / angles is an interpolation vec3_t rawOrigin; vec3_t rawAngles; vec3_t beamEnd; // exact interpolated position of entity on this frame vec3_t lerpOrigin; vec3_t lerpAngles; } centity_t; //====================================================================== // local entities are created as a result of events or predicted actions, // and live independantly from all server transmitted entities typedef struct markPoly_s { struct markPoly_s *prevMark, *nextMark; int time; qhandle_t markShader; qboolean alphaFade; // fade alpha instead of rgb float color[4]; poly_t poly; polyVert_t verts[MAX_VERTS_ON_POLY]; } markPoly_t; typedef enum { LE_MARK, LE_EXPLOSION, LE_SPRITE_EXPLOSION, LE_FRAGMENT, LE_MOVE_SCALE_FADE, LE_FALL_SCALE_FADE, LE_FADE_RGB, LE_SCALE_FADE, LE_SCOREPLUM, } leType_t; typedef enum { LEF_PUFF_DONT_SCALE = 0x0001, // do not scale size over time LEF_TUMBLE = 0x0002, // tumble over time, used for ejecting shells LEF_SOUND1 = 0x0004, // sound 1 for kamikaze LEF_SOUND2 = 0x0008 // sound 2 for kamikaze } leFlag_t; typedef enum { LEMT_NONE, LEMT_BURN, LEMT_BLOOD } leMarkType_t; // fragment local entities can leave marks on walls typedef enum { LEBS_NONE, LEBS_BLOOD, LEBS_BRASS } leBounceSoundType_t; // fragment local entities can make sounds on impacts typedef struct localEntity_s { struct localEntity_s *prev, *next; leType_t leType; int leFlags; int startTime; int endTime; int fadeInTime; float lifeRate; // 1.0 / (endTime - startTime) trajectory_t pos; trajectory_t angles; float bounceFactor; // 0.0 = no bounce, 1.0 = perfect float color[4]; float radius; float light; vec3_t lightColor; leMarkType_t leMarkType; // mark to leave on fragment impact leBounceSoundType_t leBounceSoundType; refEntity_t refEntity; } localEntity_t; //====================================================================== typedef struct { int client; int score; int ping; int time; int scoreFlags; int powerUps; int accuracy; int impressiveCount; int excellentCount; int guantletCount; int defendCount; int assistCount; int captures; qboolean perfect; int team; } score_t; // each client has an associated clientInfo_t // that contains media references necessary to present the // client model and other color coded effects // this is regenerated each time a client's configstring changes, // usually as a result of a userinfo (name, model, etc) change #define MAX_CUSTOM_SOUNDS 32 typedef struct { qboolean infoValid; char name[MAX_QPATH]; team_t team; int botSkill; // 0 = not bot, 1-5 = bot vec3_t color1; vec3_t color2; int score; // updated by score servercmds int location; // location index for team mode int health; // you only get this info about your teammates int armor; int curWeapon; int handicap; int wins, losses; // in tourney mode int teamTask; // task in teamplay (offence/defence) qboolean teamLeader; // true when this is a team leader int powerups; // so can display quad/flag status int medkitUsageTime; int invulnerabilityStartTime; int invulnerabilityStopTime; int breathPuffTime; // when clientinfo is changed, the loading of models/skins/sounds // can be deferred until you are dead, to prevent hitches in // gameplay char modelName[MAX_QPATH]; char skinName[MAX_QPATH]; char headModelName[MAX_QPATH]; char headSkinName[MAX_QPATH]; char redTeam[MAX_TEAMNAME]; char blueTeam[MAX_TEAMNAME]; qboolean deferred; qboolean newAnims; // true if using the new mission pack animations qboolean fixedlegs; // true if legs yaw is always the same as torso yaw qboolean fixedtorso; // true if torso never changes yaw vec3_t headOffset; // move head in icon views footstep_t footsteps; gender_t gender; // from model qhandle_t legsModel; qhandle_t legsSkin; qhandle_t torsoModel; qhandle_t torsoSkin; qhandle_t headModel; qhandle_t headSkin; qhandle_t modelIcon; animation_t animations[MAX_TOTALANIMATIONS]; sfxHandle_t sounds[MAX_CUSTOM_SOUNDS]; } clientInfo_t; // each WP_* weapon enum has an associated weaponInfo_t // that contains media references necessary to present the // weapon and its effects typedef struct weaponInfo_s { qboolean registered; gitem_t *item; qhandle_t handsModel; // the hands don't actually draw, they just position the weapon qhandle_t weaponModel; qhandle_t barrelModel; qhandle_t flashModel; vec3_t weaponMidpoint; // so it will rotate centered instead of by tag float flashDlight; vec3_t flashDlightColor; sfxHandle_t flashSound[4]; // fast firing weapons randomly choose qhandle_t weaponIcon; qhandle_t ammoIcon; qhandle_t ammoModel; qhandle_t missileModel; sfxHandle_t missileSound; void (*missileTrailFunc)( centity_t *, const struct weaponInfo_s *wi ); float missileDlight; vec3_t missileDlightColor; int missileRenderfx; void (*ejectBrassFunc)( centity_t * ); float trailRadius; float wiTrailTime; sfxHandle_t readySound; sfxHandle_t firingSound; qboolean loopFireSound; } weaponInfo_t; // each IT_* item has an associated itemInfo_t // that constains media references necessary to present the // item and its effects typedef struct { qboolean registered; qhandle_t models[MAX_ITEM_MODELS]; qhandle_t icon; } itemInfo_t; typedef struct { int itemNum; } powerupInfo_t; #define MAX_SKULLTRAIL 10 typedef struct { vec3_t positions[MAX_SKULLTRAIL]; int numpositions; } skulltrail_t; #define MAX_REWARDSTACK 10 #define MAX_SOUNDBUFFER 20 //====================================================================== // all cg.stepTime, cg.duckTime, cg.landTime, etc are set to cg.time when the action // occurs, and they will have visible effects for #define STEP_TIME or whatever msec after #define MAX_PREDICTED_EVENTS 16 typedef struct { int clientFrame; // incremented each frame int clientNum; qboolean demoPlayback; qboolean levelShot; // taking a level menu screenshot int deferredPlayerLoading; qboolean loading; // don't defer players at initial startup qboolean intermissionStarted; // don't play voice rewards, because game will end shortly // there are only one or two snapshot_t that are relevent at a time int latestSnapshotNum; // the number of snapshots the client system has received int latestSnapshotTime; // the time from latestSnapshotNum, so we don't need to read the snapshot yet snapshot_t *snap; // cg.snap->serverTime <= cg.time snapshot_t *nextSnap; // cg.nextSnap->serverTime > cg.time, or NULL snapshot_t activeSnapshots[2]; float frameInterpolation; // (float)( cg.time - cg.frame->serverTime ) / (cg.nextFrame->serverTime - cg.frame->serverTime) qboolean thisFrameTeleport; qboolean nextFrameTeleport; int frametime; // cg.time - cg.oldTime int time; // this is the time value that the client // is rendering at. int oldTime; // time at last frame, used for missile trails and prediction checking int physicsTime; // either cg.snap->time or cg.nextSnap->time int timelimitWarnings; // 5 min, 1 min, overtime int fraglimitWarnings; qboolean mapRestart; // set on a map restart to set back the weapon qboolean renderingThirdPerson; // during deaths, chasecams, etc // prediction state qboolean hyperspace; // true if prediction has hit a trigger_teleport playerState_t predictedPlayerState; centity_t predictedPlayerEntity; qboolean validPPS; // clear until the first call to CG_PredictPlayerState int predictedErrorTime; vec3_t predictedError; int eventSequence; int predictableEvents[MAX_PREDICTED_EVENTS]; float stepChange; // for stair up smoothing int stepTime; float duckChange; // for duck viewheight smoothing int duckTime; float landChange; // for landing hard int landTime; // input state sent to server int weaponSelect; // auto rotating items vec3_t autoAngles; vec3_t autoAxis[3]; vec3_t autoAnglesFast; vec3_t autoAxisFast[3]; // view rendering refdef_t refdef; vec3_t refdefViewAngles; // will be converted to refdef.viewaxis // zoom key qboolean zoomed; int zoomTime; float zoomSensitivity; // information screen text during loading char infoScreenText[MAX_STRING_CHARS]; // scoreboard int scoresRequestTime; int numScores; int selectedScore; int teamScores[2]; score_t scores[MAX_CLIENTS]; qboolean showScores; qboolean scoreBoardShowing; int scoreFadeTime; char killerName[MAX_NAME_LENGTH]; char spectatorList[MAX_STRING_CHARS]; // list of names int spectatorLen; // length of list float spectatorWidth; // width in device units int spectatorTime; // next time to offset int spectatorPaintX; // current paint x int spectatorPaintX2; // current paint x int spectatorOffset; // current offset from start int spectatorPaintLen; // current offset from start // skull trails skulltrail_t skulltrails[MAX_CLIENTS]; // centerprinting int centerPrintTime; int centerPrintCharWidth; int centerPrintY; char centerPrint[1024]; int centerPrintLines; // low ammo warning state int lowAmmoWarning; // 1 = low, 2 = empty // kill timers for carnage reward int lastKillTime; // crosshair client ID int crosshairClientNum; int crosshairClientTime; // powerup active flashing int powerupActive; int powerupTime; // attacking player int attackerTime; int voiceTime; // reward medals int rewardStack; int rewardTime; int rewardCount[MAX_REWARDSTACK]; qhandle_t rewardShader[MAX_REWARDSTACK]; qhandle_t rewardSound[MAX_REWARDSTACK]; // sound buffer mainly for announcer sounds int soundBufferIn; int soundBufferOut; int soundTime; qhandle_t soundBuffer[MAX_SOUNDBUFFER]; // for voice chat buffer int voiceChatTime; int voiceChatBufferIn; int voiceChatBufferOut; // warmup countdown int warmup; int warmupCount; //========================== int itemPickup; int itemPickupTime; int itemPickupBlendTime; // the pulse around the crosshair is timed seperately int weaponSelectTime; int weaponAnimation; int weaponAnimationTime; // blend blobs float damageTime; float damageX, damageY, damageValue; // status bar head float headYaw; float headEndPitch; float headEndYaw; int headEndTime; float headStartPitch; float headStartYaw; int headStartTime; // view movement float v_dmg_time; float v_dmg_pitch; float v_dmg_roll; vec3_t kick_angles; // weapon kicks vec3_t kick_origin; // temp working variables for player view float bobfracsin; int bobcycle; float xyspeed; int nextOrbitTime; //qboolean cameraMode; // if rendering from a loaded camera // development tool refEntity_t testModelEntity; char testModelName[MAX_QPATH]; qboolean testGun; } cg_t; // all of the model, shader, and sound references that are // loaded at gamestate time are stored in cgMedia_t // Other media that can be tied to clients, weapons, or items are // stored in the clientInfo_t, itemInfo_t, weaponInfo_t, and powerupInfo_t typedef struct { qhandle_t charsetShader; qhandle_t charsetProp; qhandle_t charsetPropGlow; qhandle_t charsetPropB; qhandle_t whiteShader; qhandle_t redCubeModel; qhandle_t blueCubeModel; qhandle_t redCubeIcon; qhandle_t blueCubeIcon; qhandle_t redFlagModel; qhandle_t blueFlagModel; qhandle_t neutralFlagModel; qhandle_t redFlagShader[3]; qhandle_t blueFlagShader[3]; qhandle_t flagShader[4]; qhandle_t flagPoleModel; qhandle_t flagFlapModel; qhandle_t redFlagFlapSkin; qhandle_t blueFlagFlapSkin; qhandle_t neutralFlagFlapSkin; qhandle_t redFlagBaseModel; qhandle_t blueFlagBaseModel; qhandle_t neutralFlagBaseModel; qhandle_t armorModel; qhandle_t armorIcon; qhandle_t teamStatusBar; qhandle_t deferShader; // gib explosions qhandle_t gibAbdomen; qhandle_t gibArm; qhandle_t gibChest; qhandle_t gibFist; qhandle_t gibFoot; qhandle_t gibForearm; qhandle_t gibIntestine; qhandle_t gibLeg; qhandle_t gibSkull; qhandle_t gibBrain; qhandle_t smoke2; qhandle_t machinegunBrassModel; qhandle_t shotgunBrassModel; qhandle_t railRingsShader; qhandle_t railCoreShader; qhandle_t lightningShader; qhandle_t friendShader; qhandle_t balloonShader; qhandle_t connectionShader; qhandle_t selectShader; qhandle_t viewBloodShader; qhandle_t tracerShader; qhandle_t crosshairShader[NUM_CROSSHAIRS]; qhandle_t lagometerShader; qhandle_t backTileShader; qhandle_t noammoShader; qhandle_t smokePuffShader; qhandle_t smokePuffRageProShader; qhandle_t shotgunSmokePuffShader; qhandle_t plasmaBallShader; qhandle_t waterBubbleShader; qhandle_t bloodTrailShader; qhandle_t numberShaders[11]; qhandle_t shadowMarkShader; qhandle_t botSkillShaders[5]; // wall mark shaders qhandle_t wakeMarkShader; qhandle_t bloodMarkShader; qhandle_t bulletMarkShader; qhandle_t burnMarkShader; qhandle_t holeMarkShader; qhandle_t energyMarkShader; // powerup shaders qhandle_t quadShader; qhandle_t redQuadShader; qhandle_t quadWeaponShader; qhandle_t invisShader; qhandle_t regenShader; qhandle_t battleSuitShader; qhandle_t battleWeaponShader; qhandle_t hastePuffShader; qhandle_t redKamikazeShader; qhandle_t blueKamikazeShader; // weapon effect models qhandle_t bulletFlashModel; qhandle_t ringFlashModel; qhandle_t dishFlashModel; qhandle_t lightningExplosionModel; // weapon effect shaders qhandle_t railExplosionShader; qhandle_t plasmaExplosionShader; qhandle_t bulletExplosionShader; qhandle_t rocketExplosionShader; qhandle_t grenadeExplosionShader; qhandle_t bfgExplosionShader; qhandle_t bloodExplosionShader; // special effects models qhandle_t teleportEffectModel; qhandle_t teleportEffectShader; qhandle_t invulnerabilityPowerupModel; // scoreboard headers qhandle_t scoreboardName; qhandle_t scoreboardPing; qhandle_t scoreboardScore; qhandle_t scoreboardTime; // medals shown during gameplay qhandle_t medalImpressive; qhandle_t medalExcellent; qhandle_t medalGauntlet; qhandle_t medalDefend; qhandle_t medalAssist; qhandle_t medalCapture; // sounds sfxHandle_t quadSound; sfxHandle_t tracerSound; sfxHandle_t selectSound; sfxHandle_t useNothingSound; sfxHandle_t wearOffSound; sfxHandle_t footsteps[FOOTSTEP_TOTAL][4]; sfxHandle_t sfx_lghit1; sfxHandle_t sfx_lghit2; sfxHandle_t sfx_lghit3; sfxHandle_t sfx_ric1; sfxHandle_t sfx_ric2; sfxHandle_t sfx_ric3; sfxHandle_t sfx_railg; sfxHandle_t sfx_rockexp; sfxHandle_t sfx_plasmaexp; sfxHandle_t gibSound; sfxHandle_t gibBounce1Sound; sfxHandle_t gibBounce2Sound; sfxHandle_t gibBounce3Sound; sfxHandle_t teleInSound; sfxHandle_t teleOutSound; sfxHandle_t noAmmoSound; sfxHandle_t respawnSound; sfxHandle_t talkSound; sfxHandle_t landSound; sfxHandle_t fallSound; sfxHandle_t jumpPadSound; sfxHandle_t oneMinuteSound; sfxHandle_t fiveMinuteSound; sfxHandle_t suddenDeathSound; sfxHandle_t threeFragSound; sfxHandle_t twoFragSound; sfxHandle_t oneFragSound; sfxHandle_t hitSound; sfxHandle_t hitSoundHighArmor; sfxHandle_t hitSoundLowArmor; sfxHandle_t hitTeamSound; sfxHandle_t impressiveSound; sfxHandle_t excellentSound; sfxHandle_t deniedSound; sfxHandle_t humiliationSound; sfxHandle_t assistSound; sfxHandle_t defendSound; sfxHandle_t firstImpressiveSound; sfxHandle_t firstExcellentSound; sfxHandle_t firstHumiliationSound; sfxHandle_t takenLeadSound; sfxHandle_t tiedLeadSound; sfxHandle_t lostLeadSound; sfxHandle_t voteNow; sfxHandle_t votePassed; sfxHandle_t voteFailed; sfxHandle_t watrInSound; sfxHandle_t watrOutSound; sfxHandle_t watrUnSound; sfxHandle_t flightSound; sfxHandle_t medkitSound; sfxHandle_t weaponHoverSound; // teamplay sounds sfxHandle_t captureAwardSound; sfxHandle_t redScoredSound; sfxHandle_t blueScoredSound; sfxHandle_t redLeadsSound; sfxHandle_t blueLeadsSound; sfxHandle_t teamsTiedSound; sfxHandle_t captureYourTeamSound; sfxHandle_t captureOpponentSound; sfxHandle_t returnYourTeamSound; sfxHandle_t returnOpponentSound; sfxHandle_t takenYourTeamSound; sfxHandle_t takenOpponentSound; sfxHandle_t redFlagReturnedSound; sfxHandle_t blueFlagReturnedSound; sfxHandle_t neutralFlagReturnedSound; sfxHandle_t enemyTookYourFlagSound; sfxHandle_t enemyTookTheFlagSound; sfxHandle_t yourTeamTookEnemyFlagSound; sfxHandle_t yourTeamTookTheFlagSound; sfxHandle_t youHaveFlagSound; sfxHandle_t yourBaseIsUnderAttackSound; sfxHandle_t holyShitSound; // tournament sounds sfxHandle_t count3Sound; sfxHandle_t count2Sound; sfxHandle_t count1Sound; sfxHandle_t countFightSound; sfxHandle_t countPrepareSound; qhandle_t cursor; qhandle_t selectCursor; qhandle_t sizeCursor; sfxHandle_t regenSound; sfxHandle_t protectSound; sfxHandle_t n_healthSound; sfxHandle_t hgrenb1aSound; sfxHandle_t hgrenb2aSound; sfxHandle_t wstbimplSound; sfxHandle_t wstbimpmSound; sfxHandle_t wstbimpdSound; sfxHandle_t wstbactvSound; } cgMedia_t; // The client game static (cgs) structure hold everything // loaded or calculated from the gamestate. It will NOT // be cleared when a tournement restart is done, allowing // all clients to begin playing instantly typedef struct { gameState_t gameState; // gamestate from server glconfig_t glconfig; // rendering configuration float screenXScale; // derived from glconfig float screenYScale; float screenXBias; int serverCommandSequence; // reliable command stream counter int processedSnapshotNum;// the number of snapshots cgame has requested qboolean localServer; // detected on startup by checking sv_running // parsed from serverinfo gametype_t gametype; int dmflags; int teamflags; int fraglimit; int capturelimit; int timelimit; int maxclients; char mapname[MAX_QPATH]; char redTeam[MAX_QPATH]; char blueTeam[MAX_QPATH]; int voteTime; int voteYes; int voteNo; qboolean voteModified; // beep whenever changed char voteString[MAX_STRING_TOKENS]; int teamVoteTime[2]; int teamVoteYes[2]; int teamVoteNo[2]; qboolean teamVoteModified[2]; // beep whenever changed char teamVoteString[2][MAX_STRING_TOKENS]; int levelStartTime; int scores1, scores2; // from configstrings int redflag, blueflag; // flag status from configstrings int flagStatus; qboolean newHud; // // locally derived information from gamestate // qhandle_t gameModels[MAX_MODELS]; sfxHandle_t gameSounds[MAX_SOUNDS]; int numInlineModels; qhandle_t inlineDrawModel[MAX_MODELS]; vec3_t inlineModelMidpoints[MAX_MODELS]; clientInfo_t clientinfo[MAX_CLIENTS]; // teamchat width is *3 because of embedded color codes char teamChatMsgs[TEAMCHAT_HEIGHT][TEAMCHAT_WIDTH*3+1]; int teamChatMsgTimes[TEAMCHAT_HEIGHT]; int teamChatPos; int teamLastChatPos; int cursorX; int cursorY; qboolean eventHandling; qboolean mouseCaptured; qboolean sizingHud; void *capturedItem; qhandle_t activeCursor; // orders int currentOrder; qboolean orderPending; int orderTime; int currentVoiceClient; int acceptOrderTime; int acceptTask; int acceptLeader; char acceptVoice[MAX_NAME_LENGTH]; // media cgMedia_t media; } cgs_t; //============================================================================== extern cgs_t cgs; extern cg_t cg; extern centity_t cg_entities[MAX_GENTITIES]; extern weaponInfo_t cg_weapons[MAX_WEAPONS]; extern itemInfo_t cg_items[MAX_ITEMS]; extern markPoly_t cg_markPolys[MAX_MARK_POLYS]; extern vmCvar_t cg_centertime; extern vmCvar_t cg_runpitch; extern vmCvar_t cg_runroll; extern vmCvar_t cg_bobup; extern vmCvar_t cg_bobpitch; extern vmCvar_t cg_bobroll; extern vmCvar_t cg_swingSpeed; extern vmCvar_t cg_shadows; extern vmCvar_t cg_gibs; extern vmCvar_t cg_drawTimer; extern vmCvar_t cg_drawFPS; extern vmCvar_t cg_drawSnapshot; extern vmCvar_t cg_draw3dIcons; extern vmCvar_t cg_drawIcons; extern vmCvar_t cg_drawAmmoWarning; extern vmCvar_t cg_drawCrosshair; extern vmCvar_t cg_drawCrosshairNames; extern vmCvar_t cg_drawRewards; extern vmCvar_t cg_drawTeamOverlay; extern vmCvar_t cg_teamOverlayUserinfo; extern vmCvar_t cg_crosshairX; extern vmCvar_t cg_crosshairY; extern vmCvar_t cg_crosshairSize; extern vmCvar_t cg_crosshairHealth; extern vmCvar_t cg_drawStatus; extern vmCvar_t cg_draw2D; extern vmCvar_t cg_animSpeed; extern vmCvar_t cg_debugAnim; extern vmCvar_t cg_debugPosition; extern vmCvar_t cg_debugEvents; extern vmCvar_t cg_railTrailTime; extern vmCvar_t cg_errorDecay; extern vmCvar_t cg_nopredict; extern vmCvar_t cg_noPlayerAnims; extern vmCvar_t cg_showmiss; extern vmCvar_t cg_footsteps; extern vmCvar_t cg_addMarks; extern vmCvar_t cg_brassTime; extern vmCvar_t cg_gun_frame; extern vmCvar_t cg_gun_x; extern vmCvar_t cg_gun_y; extern vmCvar_t cg_gun_z; extern vmCvar_t cg_drawGun; extern vmCvar_t cg_viewsize; extern vmCvar_t cg_tracerChance; extern vmCvar_t cg_tracerWidth; extern vmCvar_t cg_tracerLength; extern vmCvar_t cg_autoswitch; extern vmCvar_t cg_ignore; extern vmCvar_t cg_simpleItems; extern vmCvar_t cg_fov; extern vmCvar_t cg_zoomFov; extern vmCvar_t cg_thirdPersonRange; extern vmCvar_t cg_thirdPersonAngle; extern vmCvar_t cg_thirdPerson; extern vmCvar_t cg_stereoSeparation; extern vmCvar_t cg_lagometer; extern vmCvar_t cg_drawAttacker; extern vmCvar_t cg_synchronousClients; extern vmCvar_t cg_teamChatTime; extern vmCvar_t cg_teamChatHeight; extern vmCvar_t cg_stats; extern vmCvar_t cg_forceModel; extern vmCvar_t cg_buildScript; extern vmCvar_t cg_paused; extern vmCvar_t cg_blood; extern vmCvar_t cg_predictItems; extern vmCvar_t cg_deferPlayers; extern vmCvar_t cg_drawFriend; extern vmCvar_t cg_teamChatsOnly; extern vmCvar_t cg_noVoiceChats; extern vmCvar_t cg_noVoiceText; extern vmCvar_t cg_scorePlum; extern vmCvar_t cg_smoothClients; extern vmCvar_t pmove_fixed; extern vmCvar_t pmove_msec; //extern vmCvar_t cg_pmove_fixed; extern vmCvar_t cg_cameraOrbit; extern vmCvar_t cg_cameraOrbitDelay; extern vmCvar_t cg_timescaleFadeEnd; extern vmCvar_t cg_timescaleFadeSpeed; extern vmCvar_t cg_timescale; extern vmCvar_t cg_cameraMode; extern vmCvar_t cg_smallFont; extern vmCvar_t cg_bigFont; extern vmCvar_t cg_noTaunt; extern vmCvar_t cg_noProjectileTrail; extern vmCvar_t cg_oldRail; extern vmCvar_t cg_oldRocket; extern vmCvar_t cg_oldPlasma; extern vmCvar_t cg_trueLightning; // // cg_main.c // const char *CG_ConfigString( int index ); const char *CG_Argv( int arg ); void QDECL CG_Printf( const char *msg, ... ); void QDECL CG_Error( const char *msg, ... ); void CG_StartMusic( void ); void CG_UpdateCvars( void ); int CG_CrosshairPlayer( void ); int CG_LastAttacker( void ); void CG_LoadMenus(const char *menuFile); void CG_KeyEvent(int key, qboolean down); void CG_MouseEvent(int x, int y); void CG_EventHandling(int type); void CG_RankRunFrame( void ); void CG_SetScoreSelection(void *menu); score_t *CG_GetSelectedScore(); void CG_BuildSpectatorString(); // // cg_view.c // void CG_TestModel_f (void); void CG_TestGun_f (void); void CG_TestModelNextFrame_f (void); void CG_TestModelPrevFrame_f (void); void CG_TestModelNextSkin_f (void); void CG_TestModelPrevSkin_f (void); void CG_ZoomDown_f( void ); void CG_ZoomUp_f( void ); void CG_AddBufferedSound( sfxHandle_t sfx); void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView, qboolean demoPlayback ); // // cg_drawtools.c // void CG_AdjustFrom640( float *x, float *y, float *w, float *h ); void CG_FillRect( float x, float y, float width, float height, const float *color ); void CG_DrawPic( float x, float y, float width, float height, qhandle_t hShader ); void CG_DrawString( float x, float y, const char *string, float charWidth, float charHeight, const float *modulate ); void CG_DrawStringExt( int x, int y, const char *string, const float *setColor, qboolean forceColor, qboolean shadow, int charWidth, int charHeight, int maxChars ); void CG_DrawBigString( int x, int y, const char *s, float alpha ); void CG_DrawBigStringColor( int x, int y, const char *s, vec4_t color ); void CG_DrawSmallString( int x, int y, const char *s, float alpha ); void CG_DrawSmallStringColor( int x, int y, const char *s, vec4_t color ); int CG_DrawStrlen( const char *str ); float *CG_FadeColor( int startMsec, int totalMsec ); float *CG_TeamColor( int team ); void CG_TileClear( void ); void CG_ColorForHealth( vec4_t hcolor ); void CG_GetColorForHealth( int health, int armor, vec4_t hcolor ); void UI_DrawProportionalString( int x, int y, const char* str, int style, vec4_t color ); void CG_DrawRect( float x, float y, float width, float height, float size, const float *color ); void CG_DrawSides(float x, float y, float w, float h, float size); void CG_DrawTopBottom(float x, float y, float w, float h, float size); // // cg_draw.c, cg_newDraw.c // extern int sortedTeamPlayers[TEAM_MAXOVERLAY]; extern int numSortedTeamPlayers; extern int drawTeamOverlayModificationCount; extern char systemChat[256]; extern char teamChat1[256]; extern char teamChat2[256]; void CG_AddLagometerFrameInfo( void ); void CG_AddLagometerSnapshotInfo( snapshot_t *snap ); void CG_CenterPrint( const char *str, int y, int charWidth ); void CG_DrawHead( float x, float y, float w, float h, int clientNum, vec3_t headAngles ); void CG_DrawActive( stereoFrame_t stereoView ); void CG_DrawFlagModel( float x, float y, float w, float h, int team, qboolean force2D ); void CG_DrawTeamBackground( int x, int y, int w, int h, float alpha, int team ); void CG_OwnerDraw(float x, float y, float w, float h, float text_x, float text_y, int ownerDraw, int ownerDrawFlags, int align, float special, float scale, vec4_t color, qhandle_t shader, int textStyle); void CG_Text_Paint(float x, float y, float scale, vec4_t color, const char *text, float adjust, int limit, int style); int CG_Text_Width(const char *text, float scale, int limit); int CG_Text_Height(const char *text, float scale, int limit); void CG_SelectPrevPlayer(); void CG_SelectNextPlayer(); float CG_GetValue(int ownerDraw); qboolean CG_OwnerDrawVisible(int flags); void CG_RunMenuScript(char **args); void CG_ShowResponseHead(); void CG_SetPrintString(int type, const char *p); void CG_InitTeamChat(); void CG_GetTeamColor(vec4_t *color); const char *CG_GetGameStatusText(); const char *CG_GetKillerText(); void CG_Draw3DModel( float x, float y, float w, float h, qhandle_t model, qhandle_t skin, vec3_t origin, vec3_t angles ); void CG_Text_PaintChar(float x, float y, float width, float height, float scale, float s, float t, float s2, float t2, qhandle_t hShader); void CG_CheckOrderPending(); const char *CG_GameTypeString(); qboolean CG_YourTeamHasFlag(); qboolean CG_OtherTeamHasFlag(); qhandle_t CG_StatusHandle(int task); // // cg_player.c // void CG_Player( centity_t *cent ); void CG_ResetPlayerEntity( centity_t *cent ); void CG_AddRefEntityWithPowerups( refEntity_t *ent, entityState_t *state, int team ); void CG_NewClientInfo( int clientNum ); sfxHandle_t CG_CustomSound( int clientNum, const char *soundName ); // // cg_predict.c // void CG_BuildSolidList( void ); int CG_PointContents( const vec3_t point, int passEntityNum ); void CG_Trace( trace_t *result, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int skipNumber, int mask ); void CG_PredictPlayerState( void ); void CG_LoadDeferredPlayers( void ); // // cg_events.c // void CG_CheckEvents( centity_t *cent ); const char *CG_PlaceString( int rank ); void CG_EntityEvent( centity_t *cent, vec3_t position ); void CG_PainEvent( centity_t *cent, int health ); // // cg_ents.c // void CG_SetEntitySoundPosition( centity_t *cent ); void CG_AddPacketEntities( void ); void CG_Beam( centity_t *cent ); void CG_AdjustPositionForMover( const vec3_t in, int moverNum, int fromTime, int toTime, vec3_t out ); void CG_PositionEntityOnTag( refEntity_t *entity, const refEntity_t *parent, qhandle_t parentModel, char *tagName ); void CG_PositionRotatedEntityOnTag( refEntity_t *entity, const refEntity_t *parent, qhandle_t parentModel, char *tagName ); // // cg_weapons.c // void CG_NextWeapon_f( void ); void CG_PrevWeapon_f( void ); void CG_Weapon_f( void ); void CG_RegisterWeapon( int weaponNum ); void CG_RegisterItemVisuals( int itemNum ); void CG_FireWeapon( centity_t *cent ); void CG_MissileHitWall( int weapon, int clientNum, vec3_t origin, vec3_t dir, impactSound_t soundType ); void CG_MissileHitPlayer( int weapon, vec3_t origin, vec3_t dir, int entityNum ); void CG_ShotgunFire( entityState_t *es ); void CG_Bullet( vec3_t origin, int sourceEntityNum, vec3_t normal, qboolean flesh, int fleshEntityNum ); void CG_RailTrail( clientInfo_t *ci, vec3_t start, vec3_t end ); void CG_GrappleTrail( centity_t *ent, const weaponInfo_t *wi ); void CG_AddViewWeapon (playerState_t *ps); void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent, int team ); void CG_DrawWeaponSelect( void ); void CG_OutOfAmmoChange( void ); // should this be in pmove? // // cg_marks.c // void CG_InitMarkPolys( void ); void CG_AddMarks( void ); void CG_ImpactMark( qhandle_t markShader, const vec3_t origin, const vec3_t dir, float orientation, float r, float g, float b, float a, qboolean alphaFade, float radius, qboolean temporary ); // // cg_localents.c // void CG_InitLocalEntities( void ); localEntity_t *CG_AllocLocalEntity( void ); void CG_AddLocalEntities( void ); // // cg_effects.c // localEntity_t *CG_SmokePuff( const vec3_t p, const vec3_t vel, float radius, float r, float g, float b, float a, float duration, int startTime, int fadeInTime, int leFlags, qhandle_t hShader ); void CG_BubbleTrail( vec3_t start, vec3_t end, float spacing ); void CG_SpawnEffect( vec3_t org ); void CG_ScorePlum( int client, vec3_t org, int score ); void CG_GibPlayer( vec3_t playerOrigin ); void CG_BigExplode( vec3_t playerOrigin ); void CG_Bleed( vec3_t origin, int entityNum ); localEntity_t *CG_MakeExplosion( vec3_t origin, vec3_t dir, qhandle_t hModel, qhandle_t shader, int msec, qboolean isSprite ); // // cg_snapshot.c // void CG_ProcessSnapshots( void ); // // cg_info.c // void CG_LoadingString( const char *s ); void CG_LoadingItem( int itemNum ); void CG_LoadingClient( int clientNum ); void CG_DrawInformation( void ); // // cg_scoreboard.c // qboolean CG_DrawOldScoreboard( void ); void CG_DrawOldTourneyScoreboard( void ); // // cg_consolecmds.c // qboolean CG_ConsoleCommand( void ); void CG_InitConsoleCommands( void ); // // cg_servercmds.c // void CG_ExecuteNewServerCommands( int latestSequence ); void CG_ParseServerinfo( void ); void CG_SetConfigValues( void ); void CG_LoadVoiceChats( void ); void CG_ShaderStateChanged(void); void CG_VoiceChatLocal( int mode, qboolean voiceOnly, int clientNum, int color, const char *cmd ); void CG_PlayBufferedVoiceChats( void ); // // cg_playerstate.c // void CG_Respawn( void ); void CG_TransitionPlayerState( playerState_t *ps, playerState_t *ops ); void CG_CheckChangedPredictableEvents( playerState_t *ps ); //=============================================== // // system traps // These functions are how the cgame communicates with the main game system // // print message on the local console void trap_Print( const char *fmt ); // abort the game void trap_Error( const char *fmt ); // milliseconds should only be used for performance tuning, never // for anything game related. Get time from the CG_DrawActiveFrame parameter int trap_Milliseconds( void ); // console variable interaction void trap_Cvar_Register( vmCvar_t *vmCvar, const char *varName, const char *defaultValue, int flags ); void trap_Cvar_Update( vmCvar_t *vmCvar ); void trap_Cvar_Set( const char *var_name, const char *value ); void trap_Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize ); // ServerCommand and ConsoleCommand parameter access int trap_Argc( void ); void trap_Argv( int n, char *buffer, int bufferLength ); void trap_Args( char *buffer, int bufferLength ); // filesystem access // returns length of file int trap_FS_FOpenFile( const char *qpath, fileHandle_t *f, fsMode_t mode ); void trap_FS_Read( void *buffer, int len, fileHandle_t f ); void trap_FS_Write( const void *buffer, int len, fileHandle_t f ); void trap_FS_FCloseFile( fileHandle_t f ); int trap_FS_Seek( fileHandle_t f, long offset, int origin ); // fsOrigin_t // add commands to the local console as if they were typed in // for map changing, etc. The command is not executed immediately, // but will be executed in order the next time console commands // are processed void trap_SendConsoleCommand( const char *text ); // register a command name so the console can perform command completion. // FIXME: replace this with a normal console command "defineCommand"? void trap_AddCommand( const char *cmdName ); // send a string to the server over the network void trap_SendClientCommand( const char *s ); // force a screen update, only used during gamestate load void trap_UpdateScreen( void ); // model collision void trap_CM_LoadMap( const char *mapname ); int trap_CM_NumInlineModels( void ); clipHandle_t trap_CM_InlineModel( int index ); // 0 = world, 1+ = bmodels clipHandle_t trap_CM_TempBoxModel( const vec3_t mins, const vec3_t maxs ); int trap_CM_PointContents( const vec3_t p, clipHandle_t model ); int trap_CM_TransformedPointContents( const vec3_t p, clipHandle_t model, const vec3_t origin, const vec3_t angles ); void trap_CM_BoxTrace( trace_t *results, const vec3_t start, const vec3_t end, const vec3_t mins, const vec3_t maxs, clipHandle_t model, int brushmask ); void trap_CM_TransformedBoxTrace( trace_t *results, const vec3_t start, const vec3_t end, const vec3_t mins, const vec3_t maxs, clipHandle_t model, int brushmask, const vec3_t origin, const vec3_t angles ); // Returns the projection of a polygon onto the solid brushes in the world int trap_CM_MarkFragments( int numPoints, const vec3_t *points, const vec3_t projection, int maxPoints, vec3_t pointBuffer, int maxFragments, markFragment_t *fragmentBuffer ); // normal sounds will have their volume dynamically changed as their entity // moves and the listener moves void trap_S_StartSound( vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfx ); void trap_S_StopLoopingSound(int entnum); // a local sound is always played full volume void trap_S_StartLocalSound( sfxHandle_t sfx, int channelNum ); void trap_S_ClearLoopingSounds( qboolean killall ); void trap_S_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx ); void trap_S_AddRealLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx ); void trap_S_UpdateEntityPosition( int entityNum, const vec3_t origin ); // respatialize recalculates the volumes of sound as they should be heard by the // given entityNum and position void trap_S_Respatialize( int entityNum, const vec3_t origin, vec3_t axis[3], int inwater ); sfxHandle_t trap_S_RegisterSound( const char *sample, qboolean compressed ); // returns buzz if not found void trap_S_StartBackgroundTrack( const char *intro, const char *loop ); // empty name stops music void trap_S_StopBackgroundTrack( void ); void trap_R_LoadWorldMap( const char *mapname ); // all media should be registered during level startup to prevent // hitches during gameplay qhandle_t trap_R_RegisterModel( const char *name ); // returns rgb axis if not found qhandle_t trap_R_RegisterSkin( const char *name ); // returns all white if not found qhandle_t trap_R_RegisterShader( const char *name ); // returns all white if not found qhandle_t trap_R_RegisterShaderNoMip( const char *name ); // returns all white if not found // 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 trap_R_ClearScene( void ); void trap_R_AddRefEntityToScene( const refEntity_t *re ); // polys are intended for simple wall marks, not really for doing // significant construction void trap_R_AddPolyToScene( qhandle_t hShader , int numVerts, const polyVert_t *verts ); void trap_R_AddPolysToScene( qhandle_t hShader , int numVerts, const polyVert_t *verts, int numPolys ); void trap_R_AddLightToScene( const vec3_t org, float intensity, float r, float g, float b ); int trap_R_LightForPoint( vec3_t point, vec3_t ambientLight, vec3_t directedLight, vec3_t lightDir ); void trap_R_RenderScene( const refdef_t *fd ); void trap_R_SetColor( const float *rgba ); // NULL = 1,1,1,1 void trap_R_DrawStretchPic( float x, float y, float w, float h, float s1, float t1, float s2, float t2, qhandle_t hShader ); void trap_R_ModelBounds( clipHandle_t model, vec3_t mins, vec3_t maxs ); int trap_R_LerpTag( orientation_t *tag, clipHandle_t mod, int startFrame, int endFrame, float frac, const char *tagName ); void trap_R_RemapShader( const char *oldShader, const char *newShader, const char *timeOffset ); // The glconfig_t will not change during the life of a cgame. // If it needs to change, the entire cgame will be restarted, because // all the qhandle_t are then invalid. void trap_GetGlconfig( glconfig_t *glconfig ); // the gamestate should be grabbed at startup, and whenever a // configstring changes void trap_GetGameState( gameState_t *gamestate ); // cgame will poll each frame to see if a newer snapshot has arrived // that it is interested in. The time is returned seperately so that // snapshot latency can be calculated. void trap_GetCurrentSnapshotNumber( int *snapshotNumber, int *serverTime ); // a snapshot get can fail if the snapshot (or the entties it holds) is so // old that it has fallen out of the client system queue qboolean trap_GetSnapshot( int snapshotNumber, snapshot_t *snapshot ); // retrieve a text command from the server stream // the current snapshot will hold the number of the most recent command // qfalse can be returned if the client system handled the command // argc() / argv() can be used to examine the parameters of the command qboolean trap_GetServerCommand( int serverCommandNumber ); // returns the most recent command number that can be passed to GetUserCmd // this will always be at least one higher than the number in the current // snapshot, and it may be quite a few higher if it is a fast computer on // a lagged connection int trap_GetCurrentCmdNumber( void ); qboolean trap_GetUserCmd( int cmdNumber, usercmd_t *ucmd ); // used for the weapon select and zoom void trap_SetUserCmdValue( int stateValue, float sensitivityScale ); // aids for VM testing void testPrintInt( char *string, int i ); void testPrintFloat( char *string, float f ); int trap_MemoryRemaining( void ); void trap_R_RegisterFont(const char *fontName, int pointSize, fontInfo_t *font); qboolean trap_Key_IsDown( int keynum ); int trap_Key_GetCatcher( void ); void trap_Key_SetCatcher( int catcher ); int trap_Key_GetKey( const char *binding ); typedef enum { SYSTEM_PRINT, CHAT_PRINT, TEAMCHAT_PRINT } q3print_t; // bk001201 - warning: useless keyword or type name in empty declaration int trap_CIN_PlayCinematic( const char *arg0, int xpos, int ypos, int width, int height, int bits); e_status trap_CIN_StopCinematic(int handle); e_status trap_CIN_RunCinematic (int handle); void trap_CIN_DrawCinematic (int handle); void trap_CIN_SetExtents (int handle, int x, int y, int w, int h); void trap_SnapVector( float *v ); qboolean trap_GetEntityToken( char *buffer, int bufferSize ); void CG_ClearParticles (void); void CG_AddParticles (void); void CG_ParticleSnow (qhandle_t pshader, vec3_t origin, vec3_t origin2, int turb, float range, int snum); void CG_ParticleSmoke (qhandle_t pshader, centity_t *cent); void CG_AddParticleShrapnel (localEntity_t *le); void CG_ParticleSnowFlurry (qhandle_t pshader, centity_t *cent); void CG_ParticleBulletDebris (vec3_t org, vec3_t vel, int duration); void CG_ParticleSparks (vec3_t org, vec3_t vel, int duration, float x, float y, float speed); void CG_ParticleDust (centity_t *cent, vec3_t origin, vec3_t dir); void CG_ParticleMisc (qhandle_t pshader, vec3_t origin, int size, int duration, float alpha); void CG_ParticleExplosion (char *animStr, vec3_t origin, vec3_t vel, int duration, int sizeStart, int sizeEnd); extern qboolean initparticles; int CG_NewParticleArea ( int num ); ================================================ FILE: src/cgame/cg_localents.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_localents.c -- every frame, generate renderer commands for locally // processed entities, like smoke puffs, gibs, shells, etc. #include "cg_local.h" #define MAX_LOCAL_ENTITIES 512 localEntity_t cg_localEntities[MAX_LOCAL_ENTITIES]; localEntity_t cg_activeLocalEntities; // double linked list localEntity_t *cg_freeLocalEntities; // single linked list /* =================== CG_InitLocalEntities This is called at startup and for tournement restarts =================== */ void CG_InitLocalEntities( void ) { int i; memset( cg_localEntities, 0, sizeof( cg_localEntities ) ); cg_activeLocalEntities.next = &cg_activeLocalEntities; cg_activeLocalEntities.prev = &cg_activeLocalEntities; cg_freeLocalEntities = cg_localEntities; for ( i = 0 ; i < MAX_LOCAL_ENTITIES - 1 ; i++ ) { cg_localEntities[i].next = &cg_localEntities[i+1]; } } /* ================== CG_FreeLocalEntity ================== */ void CG_FreeLocalEntity( localEntity_t *le ) { if ( !le->prev ) { CG_Error( "CG_FreeLocalEntity: not active" ); } // remove from the doubly linked active list le->prev->next = le->next; le->next->prev = le->prev; // the free list is only singly linked le->next = cg_freeLocalEntities; cg_freeLocalEntities = le; } /* =================== CG_AllocLocalEntity Will allways succeed, even if it requires freeing an old active entity =================== */ localEntity_t *CG_AllocLocalEntity( void ) { localEntity_t *le; if ( !cg_freeLocalEntities ) { // no free entities, so free the one at the end of the chain // remove the oldest active entity CG_FreeLocalEntity( cg_activeLocalEntities.prev ); } le = cg_freeLocalEntities; cg_freeLocalEntities = cg_freeLocalEntities->next; memset( le, 0, sizeof( *le ) ); // link into the active list le->next = cg_activeLocalEntities.next; le->prev = &cg_activeLocalEntities; cg_activeLocalEntities.next->prev = le; cg_activeLocalEntities.next = le; return le; } /* ==================================================================================== FRAGMENT PROCESSING A fragment localentity interacts with the environment in some way (hitting walls), or generates more localentities along a trail. ==================================================================================== */ /* ================ CG_BloodTrail Leave expanding blood puffs behind gibs ================ */ void CG_BloodTrail( localEntity_t *le ) { int t; int t2; int step; vec3_t newOrigin; localEntity_t *blood; step = 150; t = step * ( (cg.time - cg.frametime + step ) / step ); t2 = step * ( cg.time / step ); for ( ; t <= t2; t += step ) { BG_EvaluateTrajectory( &le->pos, t, newOrigin ); blood = CG_SmokePuff( newOrigin, vec3_origin, 20, // radius 1, 1, 1, 1, // color 2000, // trailTime t, // startTime 0, // fadeInTime 0, // flags cgs.media.bloodTrailShader ); // use the optimized version blood->leType = LE_FALL_SCALE_FADE; // drop a total of 40 units over its lifetime blood->pos.trDelta[2] = 40; } } /* ================ CG_FragmentBounceMark ================ */ void CG_FragmentBounceMark( localEntity_t *le, trace_t *trace ) { int radius; if ( le->leMarkType == LEMT_BLOOD ) { radius = 16 + (rand()&31); CG_ImpactMark( cgs.media.bloodMarkShader, trace->endpos, trace->plane.normal, random()*360, 1,1,1,1, qtrue, radius, qfalse ); } else if ( le->leMarkType == LEMT_BURN ) { radius = 8 + (rand()&15); CG_ImpactMark( cgs.media.burnMarkShader, trace->endpos, trace->plane.normal, random()*360, 1,1,1,1, qtrue, radius, qfalse ); } // don't allow a fragment to make multiple marks, or they // pile up while settling le->leMarkType = LEMT_NONE; } /* ================ CG_FragmentBounceSound ================ */ void CG_FragmentBounceSound( localEntity_t *le, trace_t *trace ) { if ( le->leBounceSoundType == LEBS_BLOOD ) { // half the gibs will make splat sounds if ( rand() & 1 ) { int r = rand()&3; sfxHandle_t s; if ( r == 0 ) { s = cgs.media.gibBounce1Sound; } else if ( r == 1 ) { s = cgs.media.gibBounce2Sound; } else { s = cgs.media.gibBounce3Sound; } trap_S_StartSound( trace->endpos, ENTITYNUM_WORLD, CHAN_AUTO, s ); } } else if ( le->leBounceSoundType == LEBS_BRASS ) { } // don't allow a fragment to make multiple bounce sounds, // or it gets too noisy as they settle le->leBounceSoundType = LEBS_NONE; } /* ================ CG_ReflectVelocity ================ */ void CG_ReflectVelocity( localEntity_t *le, trace_t *trace ) { vec3_t velocity; float dot; int hitTime; // reflect the velocity on the trace plane hitTime = cg.time - cg.frametime + cg.frametime * trace->fraction; BG_EvaluateTrajectoryDelta( &le->pos, hitTime, velocity ); dot = DotProduct( velocity, trace->plane.normal ); VectorMA( velocity, -2*dot, trace->plane.normal, le->pos.trDelta ); VectorScale( le->pos.trDelta, le->bounceFactor, le->pos.trDelta ); VectorCopy( trace->endpos, le->pos.trBase ); le->pos.trTime = cg.time; // check for stop, making sure that even on low FPS systems it doesn't bobble if ( trace->allsolid || ( trace->plane.normal[2] > 0 && ( le->pos.trDelta[2] < 40 || le->pos.trDelta[2] < -cg.frametime * le->pos.trDelta[2] ) ) ) { le->pos.trType = TR_STATIONARY; } else { } } /* ================ CG_AddFragment ================ */ void CG_AddFragment( localEntity_t *le ) { vec3_t newOrigin; trace_t trace; if ( le->pos.trType == TR_STATIONARY ) { // sink into the ground if near the removal time int t; float oldZ; t = le->endTime - cg.time; if ( t < SINK_TIME ) { // we must use an explicit lighting origin, otherwise the // lighting would be lost as soon as the origin went // into the ground VectorCopy( le->refEntity.origin, le->refEntity.lightingOrigin ); le->refEntity.renderfx |= RF_LIGHTING_ORIGIN; oldZ = le->refEntity.origin[2]; le->refEntity.origin[2] -= 16 * ( 1.0 - (float)t / SINK_TIME ); trap_R_AddRefEntityToScene( &le->refEntity ); le->refEntity.origin[2] = oldZ; } else { trap_R_AddRefEntityToScene( &le->refEntity ); } return; } // calculate new position BG_EvaluateTrajectory( &le->pos, cg.time, newOrigin ); // trace a line from previous position to new position CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, -1, CONTENTS_SOLID ); if ( trace.fraction == 1.0 ) { // still in free fall VectorCopy( newOrigin, le->refEntity.origin ); if ( le->leFlags & LEF_TUMBLE ) { vec3_t angles; BG_EvaluateTrajectory( &le->angles, cg.time, angles ); AnglesToAxis( angles, le->refEntity.axis ); } trap_R_AddRefEntityToScene( &le->refEntity ); // add a blood trail if ( le->leBounceSoundType == LEBS_BLOOD ) { CG_BloodTrail( le ); } return; } // if it is in a nodrop zone, remove it // this keeps gibs from waiting at the bottom of pits of death // and floating levels if ( trap_CM_PointContents( trace.endpos, 0 ) & CONTENTS_NODROP ) { CG_FreeLocalEntity( le ); return; } // leave a mark CG_FragmentBounceMark( le, &trace ); // do a bouncy sound CG_FragmentBounceSound( le, &trace ); // reflect the velocity on the trace plane CG_ReflectVelocity( le, &trace ); trap_R_AddRefEntityToScene( &le->refEntity ); } /* ===================================================================== TRIVIAL LOCAL ENTITIES These only do simple scaling or modulation before passing to the renderer ===================================================================== */ /* ==================== CG_AddFadeRGB ==================== */ void CG_AddFadeRGB( localEntity_t *le ) { refEntity_t *re; float c; re = &le->refEntity; c = ( le->endTime - cg.time ) * le->lifeRate; c *= 0xff; re->shaderRGBA[0] = le->color[0] * c; re->shaderRGBA[1] = le->color[1] * c; re->shaderRGBA[2] = le->color[2] * c; re->shaderRGBA[3] = le->color[3] * c; trap_R_AddRefEntityToScene( re ); } /* ================== CG_AddMoveScaleFade ================== */ static void CG_AddMoveScaleFade( localEntity_t *le ) { refEntity_t *re; float c; vec3_t delta; float len; re = &le->refEntity; if ( le->fadeInTime > le->startTime && cg.time < le->fadeInTime ) { // fade / grow time c = 1.0 - (float) ( le->fadeInTime - cg.time ) / ( le->fadeInTime - le->startTime ); } else { // fade / grow time c = ( le->endTime - cg.time ) * le->lifeRate; } re->shaderRGBA[3] = 0xff * c * le->color[3]; if ( !( le->leFlags & LEF_PUFF_DONT_SCALE ) ) { re->radius = le->radius * ( 1.0 - c ) + 8; } BG_EvaluateTrajectory( &le->pos, cg.time, re->origin ); // if the view would be "inside" the sprite, kill the sprite // so it doesn't add too much overdraw VectorSubtract( re->origin, cg.refdef.vieworg, delta ); len = VectorLength( delta ); if ( len < le->radius ) { CG_FreeLocalEntity( le ); return; } trap_R_AddRefEntityToScene( re ); } /* =================== CG_AddScaleFade For rocket smokes that hang in place, fade out, and are removed if the view passes through them. There are often many of these, so it needs to be simple. =================== */ static void CG_AddScaleFade( localEntity_t *le ) { refEntity_t *re; float c; vec3_t delta; float len; re = &le->refEntity; // fade / grow time c = ( le->endTime - cg.time ) * le->lifeRate; re->shaderRGBA[3] = 0xff * c * le->color[3]; re->radius = le->radius * ( 1.0 - c ) + 8; // if the view would be "inside" the sprite, kill the sprite // so it doesn't add too much overdraw VectorSubtract( re->origin, cg.refdef.vieworg, delta ); len = VectorLength( delta ); if ( len < le->radius ) { CG_FreeLocalEntity( le ); return; } trap_R_AddRefEntityToScene( re ); } /* ================= CG_AddFallScaleFade This is just an optimized CG_AddMoveScaleFade For blood mists that drift down, fade out, and are removed if the view passes through them. There are often 100+ of these, so it needs to be simple. ================= */ static void CG_AddFallScaleFade( localEntity_t *le ) { refEntity_t *re; float c; vec3_t delta; float len; re = &le->refEntity; // fade time c = ( le->endTime - cg.time ) * le->lifeRate; re->shaderRGBA[3] = 0xff * c * le->color[3]; re->origin[2] = le->pos.trBase[2] - ( 1.0 - c ) * le->pos.trDelta[2]; re->radius = le->radius * ( 1.0 - c ) + 16; // if the view would be "inside" the sprite, kill the sprite // so it doesn't add too much overdraw VectorSubtract( re->origin, cg.refdef.vieworg, delta ); len = VectorLength( delta ); if ( len < le->radius ) { CG_FreeLocalEntity( le ); return; } trap_R_AddRefEntityToScene( re ); } /* ================ CG_AddExplosion ================ */ static void CG_AddExplosion( localEntity_t *ex ) { refEntity_t *ent; ent = &ex->refEntity; // add the entity trap_R_AddRefEntityToScene(ent); // add the dlight if ( ex->light ) { float light; light = (float)( cg.time - ex->startTime ) / ( ex->endTime - ex->startTime ); if ( light < 0.5 ) { light = 1.0; } else { light = 1.0 - ( light - 0.5 ) * 2; } light = ex->light * light; trap_R_AddLightToScene(ent->origin, light, ex->lightColor[0], ex->lightColor[1], ex->lightColor[2] ); } } /* ================ CG_AddSpriteExplosion ================ */ static void CG_AddSpriteExplosion( localEntity_t *le ) { refEntity_t re; float c; re = le->refEntity; c = ( le->endTime - cg.time ) / ( float ) ( le->endTime - le->startTime ); if ( c > 1 ) { c = 1.0; // can happen during connection problems } re.shaderRGBA[0] = 0xff; re.shaderRGBA[1] = 0xff; re.shaderRGBA[2] = 0xff; re.shaderRGBA[3] = 0xff * c * 0.33; re.reType = RT_SPRITE; re.radius = 42 * ( 1.0 - c ) + 30; trap_R_AddRefEntityToScene( &re ); // add the dlight if ( le->light ) { float light; light = (float)( cg.time - le->startTime ) / ( le->endTime - le->startTime ); if ( light < 0.5 ) { light = 1.0; } else { light = 1.0 - ( light - 0.5 ) * 2; } light = le->light * light; trap_R_AddLightToScene(re.origin, light, le->lightColor[0], le->lightColor[1], le->lightColor[2] ); } } /* =================== CG_AddScorePlum =================== */ #define NUMBER_SIZE 8 void CG_AddScorePlum( localEntity_t *le ) { refEntity_t *re; vec3_t origin, delta, dir, vec, up = {0, 0, 1}; float c, len; int i, score, digits[10], numdigits, negative; re = &le->refEntity; c = ( le->endTime - cg.time ) * le->lifeRate; score = le->radius; if (score < 0) { re->shaderRGBA[0] = 0xff; re->shaderRGBA[1] = 0x11; re->shaderRGBA[2] = 0x11; } else { re->shaderRGBA[0] = 0xff; re->shaderRGBA[1] = 0xff; re->shaderRGBA[2] = 0xff; if (score >= 50) { re->shaderRGBA[1] = 0; } else if (score >= 20) { re->shaderRGBA[0] = re->shaderRGBA[1] = 0; } else if (score >= 10) { re->shaderRGBA[2] = 0; } else if (score >= 2) { re->shaderRGBA[0] = re->shaderRGBA[2] = 0; } } if (c < 0.25) re->shaderRGBA[3] = 0xff * 4 * c; else re->shaderRGBA[3] = 0xff; re->radius = NUMBER_SIZE / 2; VectorCopy(le->pos.trBase, origin); origin[2] += 110 - c * 100; VectorSubtract(cg.refdef.vieworg, origin, dir); CrossProduct(dir, up, vec); VectorNormalize(vec); VectorMA(origin, -10 + 20 * sin(c * 2 * M_PI), vec, origin); // if the view would be "inside" the sprite, kill the sprite // so it doesn't add too much overdraw VectorSubtract( origin, cg.refdef.vieworg, delta ); len = VectorLength( delta ); if ( len < 20 ) { CG_FreeLocalEntity( le ); return; } negative = qfalse; if (score < 0) { negative = qtrue; score = -score; } for (numdigits = 0; !(numdigits && !score); numdigits++) { digits[numdigits] = score % 10; score = score / 10; } if (negative) { digits[numdigits] = 10; numdigits++; } for (i = 0; i < numdigits; i++) { VectorMA(origin, (float) (((float) numdigits / 2) - i) * NUMBER_SIZE, vec, re->origin); re->customShader = cgs.media.numberShaders[digits[numdigits-1-i]]; trap_R_AddRefEntityToScene( re ); } } //============================================================================== /* =================== CG_AddLocalEntities =================== */ void CG_AddLocalEntities( void ) { localEntity_t *le, *next; // walk the list backwards, so any new local entities generated // (trails, marks, etc) will be present this frame le = cg_activeLocalEntities.prev; for ( ; le != &cg_activeLocalEntities ; le = next ) { // grab next now, so if the local entity is freed we // still have it next = le->prev; if ( cg.time >= le->endTime ) { CG_FreeLocalEntity( le ); continue; } switch ( le->leType ) { default: CG_Error( "Bad leType: %i", le->leType ); break; case LE_MARK: break; case LE_SPRITE_EXPLOSION: CG_AddSpriteExplosion( le ); break; case LE_EXPLOSION: CG_AddExplosion( le ); break; case LE_FRAGMENT: // gibs and brass CG_AddFragment( le ); break; case LE_MOVE_SCALE_FADE: // water bubbles CG_AddMoveScaleFade( le ); break; case LE_FADE_RGB: // teleporters, railtrails CG_AddFadeRGB( le ); break; case LE_FALL_SCALE_FADE: // gib blood trails CG_AddFallScaleFade( le ); break; case LE_SCALE_FADE: // rocket trails CG_AddScaleFade( le ); break; case LE_SCOREPLUM: CG_AddScorePlum( le ); break; } } } ================================================ FILE: src/cgame/cg_main.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_main.c -- initialization and primary entry point for cgame #include "cg_local.h" int forceModelModificationCount = -1; void CG_Init( int serverMessageNum, int serverCommandSequence, int clientNum ); void CG_Shutdown( void ); /* ================ vmMain This is the only way control passes into the module. This must be the very first function compiled into the .q3vm file ================ */ intptr_t vmMain( int command, int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8, int arg9, int arg10, int arg11 ) { switch ( command ) { case CG_INIT: CG_Init( arg0, arg1, arg2 ); return 0; case CG_SHUTDOWN: CG_Shutdown(); return 0; case CG_CONSOLE_COMMAND: return CG_ConsoleCommand(); case CG_DRAW_ACTIVE_FRAME: CG_DrawActiveFrame( arg0, arg1, arg2 ); return 0; case CG_CROSSHAIR_PLAYER: return CG_CrosshairPlayer(); case CG_LAST_ATTACKER: return CG_LastAttacker(); case CG_KEY_EVENT: CG_KeyEvent(arg0, arg1); return 0; case CG_MOUSE_EVENT: CG_MouseEvent(arg0, arg1); return 0; case CG_EVENT_HANDLING: CG_EventHandling(arg0); return 0; default: CG_Error( "vmMain: unknown command %i", command ); break; } return -1; } cg_t cg; cgs_t cgs; centity_t cg_entities[MAX_GENTITIES]; weaponInfo_t cg_weapons[MAX_WEAPONS]; itemInfo_t cg_items[MAX_ITEMS]; vmCvar_t cg_railTrailTime; vmCvar_t cg_centertime; vmCvar_t cg_runpitch; vmCvar_t cg_runroll; vmCvar_t cg_bobup; vmCvar_t cg_bobpitch; vmCvar_t cg_bobroll; vmCvar_t cg_swingSpeed; vmCvar_t cg_shadows; vmCvar_t cg_gibs; vmCvar_t cg_drawTimer; vmCvar_t cg_drawFPS; vmCvar_t cg_drawSnapshot; vmCvar_t cg_draw3dIcons; vmCvar_t cg_drawIcons; vmCvar_t cg_drawAmmoWarning; vmCvar_t cg_drawCrosshair; vmCvar_t cg_drawCrosshairNames; vmCvar_t cg_drawRewards; vmCvar_t cg_crosshairSize; vmCvar_t cg_crosshairX; vmCvar_t cg_crosshairY; vmCvar_t cg_crosshairHealth; vmCvar_t cg_draw2D; vmCvar_t cg_drawStatus; vmCvar_t cg_animSpeed; vmCvar_t cg_debugAnim; vmCvar_t cg_debugPosition; vmCvar_t cg_debugEvents; vmCvar_t cg_errorDecay; vmCvar_t cg_nopredict; vmCvar_t cg_noPlayerAnims; vmCvar_t cg_showmiss; vmCvar_t cg_footsteps; vmCvar_t cg_addMarks; vmCvar_t cg_brassTime; vmCvar_t cg_viewsize; vmCvar_t cg_drawGun; vmCvar_t cg_gun_frame; vmCvar_t cg_gun_x; vmCvar_t cg_gun_y; vmCvar_t cg_gun_z; vmCvar_t cg_tracerChance; vmCvar_t cg_tracerWidth; vmCvar_t cg_tracerLength; vmCvar_t cg_autoswitch; vmCvar_t cg_ignore; vmCvar_t cg_simpleItems; vmCvar_t cg_fov; vmCvar_t cg_zoomFov; vmCvar_t cg_thirdPerson; vmCvar_t cg_thirdPersonRange; vmCvar_t cg_thirdPersonAngle; vmCvar_t cg_stereoSeparation; vmCvar_t cg_lagometer; vmCvar_t cg_drawAttacker; vmCvar_t cg_synchronousClients; vmCvar_t cg_teamChatTime; vmCvar_t cg_teamChatHeight; vmCvar_t cg_stats; vmCvar_t cg_buildScript; vmCvar_t cg_forceModel; vmCvar_t cg_paused; vmCvar_t cg_blood; vmCvar_t cg_predictItems; vmCvar_t cg_deferPlayers; vmCvar_t cg_drawTeamOverlay; vmCvar_t cg_teamOverlayUserinfo; vmCvar_t cg_drawFriend; vmCvar_t cg_teamChatsOnly; vmCvar_t cg_noVoiceChats; vmCvar_t cg_noVoiceText; vmCvar_t cg_hudFiles; vmCvar_t cg_scorePlum; vmCvar_t cg_smoothClients; vmCvar_t pmove_fixed; //vmCvar_t cg_pmove_fixed; vmCvar_t pmove_msec; vmCvar_t cg_pmove_msec; vmCvar_t cg_cameraMode; vmCvar_t cg_cameraOrbit; vmCvar_t cg_cameraOrbitDelay; vmCvar_t cg_timescaleFadeEnd; vmCvar_t cg_timescaleFadeSpeed; vmCvar_t cg_timescale; vmCvar_t cg_smallFont; vmCvar_t cg_bigFont; vmCvar_t cg_noTaunt; vmCvar_t cg_noProjectileTrail; vmCvar_t cg_oldRail; vmCvar_t cg_oldRocket; vmCvar_t cg_oldPlasma; vmCvar_t cg_trueLightning; typedef struct { vmCvar_t *vmCvar; char *cvarName; char *defaultString; int cvarFlags; } cvarTable_t; static cvarTable_t cvarTable[] = { // bk001129 { &cg_ignore, "cg_ignore", "0", 0 }, // used for debugging { &cg_autoswitch, "cg_autoswitch", "1", CVAR_ARCHIVE }, { &cg_drawGun, "cg_drawGun", "1", CVAR_ARCHIVE }, { &cg_zoomFov, "cg_zoomfov", "22.5", CVAR_ARCHIVE }, { &cg_fov, "cg_fov", "90", CVAR_ARCHIVE }, { &cg_viewsize, "cg_viewsize", "100", CVAR_ARCHIVE }, { &cg_stereoSeparation, "cg_stereoSeparation", "0.4", CVAR_ARCHIVE }, { &cg_shadows, "cg_shadows", "1", CVAR_ARCHIVE }, { &cg_gibs, "cg_gibs", "1", CVAR_ARCHIVE }, { &cg_draw2D, "cg_draw2D", "1", CVAR_ARCHIVE }, { &cg_drawStatus, "cg_drawStatus", "1", CVAR_ARCHIVE }, { &cg_drawTimer, "cg_drawTimer", "0", CVAR_ARCHIVE }, { &cg_drawFPS, "cg_drawFPS", "0", CVAR_ARCHIVE }, { &cg_drawSnapshot, "cg_drawSnapshot", "0", CVAR_ARCHIVE }, { &cg_draw3dIcons, "cg_draw3dIcons", "1", CVAR_ARCHIVE }, { &cg_drawIcons, "cg_drawIcons", "1", CVAR_ARCHIVE }, { &cg_drawAmmoWarning, "cg_drawAmmoWarning", "1", CVAR_ARCHIVE }, { &cg_drawAttacker, "cg_drawAttacker", "1", CVAR_ARCHIVE }, { &cg_drawCrosshair, "cg_drawCrosshair", "4", CVAR_ARCHIVE }, { &cg_drawCrosshairNames, "cg_drawCrosshairNames", "1", CVAR_ARCHIVE }, { &cg_drawRewards, "cg_drawRewards", "1", CVAR_ARCHIVE }, { &cg_crosshairSize, "cg_crosshairSize", "24", CVAR_ARCHIVE }, { &cg_crosshairHealth, "cg_crosshairHealth", "1", CVAR_ARCHIVE }, { &cg_crosshairX, "cg_crosshairX", "0", CVAR_ARCHIVE }, { &cg_crosshairY, "cg_crosshairY", "0", CVAR_ARCHIVE }, { &cg_brassTime, "cg_brassTime", "2500", CVAR_ARCHIVE }, { &cg_simpleItems, "cg_simpleItems", "0", CVAR_ARCHIVE }, { &cg_addMarks, "cg_marks", "1", CVAR_ARCHIVE }, { &cg_lagometer, "cg_lagometer", "1", CVAR_ARCHIVE }, { &cg_railTrailTime, "cg_railTrailTime", "400", CVAR_ARCHIVE }, { &cg_gun_x, "cg_gunX", "0", CVAR_CHEAT }, { &cg_gun_y, "cg_gunY", "0", CVAR_CHEAT }, { &cg_gun_z, "cg_gunZ", "0", CVAR_CHEAT }, { &cg_centertime, "cg_centertime", "3", CVAR_CHEAT }, { &cg_runpitch, "cg_runpitch", "0.002", CVAR_ARCHIVE}, { &cg_runroll, "cg_runroll", "0.005", CVAR_ARCHIVE }, { &cg_bobup , "cg_bobup", "0.005", CVAR_CHEAT }, { &cg_bobpitch, "cg_bobpitch", "0.002", CVAR_ARCHIVE }, { &cg_bobroll, "cg_bobroll", "0.002", CVAR_ARCHIVE }, { &cg_swingSpeed, "cg_swingSpeed", "0.3", CVAR_CHEAT }, { &cg_animSpeed, "cg_animspeed", "1", CVAR_CHEAT }, { &cg_debugAnim, "cg_debuganim", "0", CVAR_CHEAT }, { &cg_debugPosition, "cg_debugposition", "0", CVAR_CHEAT }, { &cg_debugEvents, "cg_debugevents", "0", CVAR_CHEAT }, { &cg_errorDecay, "cg_errordecay", "100", 0 }, { &cg_nopredict, "cg_nopredict", "0", 0 }, { &cg_noPlayerAnims, "cg_noplayeranims", "0", CVAR_CHEAT }, { &cg_showmiss, "cg_showmiss", "0", 0 }, { &cg_footsteps, "cg_footsteps", "1", CVAR_CHEAT }, { &cg_tracerChance, "cg_tracerchance", "0.4", CVAR_CHEAT }, { &cg_tracerWidth, "cg_tracerwidth", "1", CVAR_CHEAT }, { &cg_tracerLength, "cg_tracerlength", "100", CVAR_CHEAT }, { &cg_thirdPersonRange, "cg_thirdPersonRange", "40", CVAR_CHEAT }, { &cg_thirdPersonAngle, "cg_thirdPersonAngle", "0", CVAR_CHEAT }, { &cg_thirdPerson, "cg_thirdPerson", "0", 0 }, { &cg_teamChatTime, "cg_teamChatTime", "3000", CVAR_ARCHIVE }, { &cg_teamChatHeight, "cg_teamChatHeight", "0", CVAR_ARCHIVE }, { &cg_forceModel, "cg_forceModel", "0", CVAR_ARCHIVE }, { &cg_predictItems, "cg_predictItems", "1", CVAR_ARCHIVE }, { &cg_deferPlayers, "cg_deferPlayers", "1", CVAR_ARCHIVE }, { &cg_drawTeamOverlay, "cg_drawTeamOverlay", "0", CVAR_ARCHIVE }, { &cg_teamOverlayUserinfo, "teamoverlay", "0", CVAR_ROM | CVAR_USERINFO }, { &cg_stats, "cg_stats", "0", 0 }, { &cg_drawFriend, "cg_drawFriend", "1", CVAR_ARCHIVE }, { &cg_teamChatsOnly, "cg_teamChatsOnly", "0", CVAR_ARCHIVE }, { &cg_noVoiceChats, "cg_noVoiceChats", "0", CVAR_ARCHIVE }, { &cg_noVoiceText, "cg_noVoiceText", "0", CVAR_ARCHIVE }, // the following variables are created in other parts of the system, // but we also reference them here { &cg_buildScript, "com_buildScript", "0", 0 }, // force loading of all possible data amd error on failures { &cg_paused, "cl_paused", "0", CVAR_ROM }, { &cg_blood, "com_blood", "1", CVAR_ARCHIVE }, { &cg_synchronousClients, "g_synchronousClients", "0", 0 }, // communicated by systeminfo { &cg_cameraOrbit, "cg_cameraOrbit", "0", CVAR_CHEAT}, { &cg_cameraOrbitDelay, "cg_cameraOrbitDelay", "50", CVAR_ARCHIVE}, { &cg_timescaleFadeEnd, "cg_timescaleFadeEnd", "1", 0}, { &cg_timescaleFadeSpeed, "cg_timescaleFadeSpeed", "0", 0}, { &cg_timescale, "timescale", "1", 0}, { &cg_scorePlum, "cg_scorePlums", "1", CVAR_USERINFO | CVAR_ARCHIVE}, { &cg_smoothClients, "cg_smoothClients", "0", CVAR_USERINFO | CVAR_ARCHIVE}, { &cg_cameraMode, "com_cameraMode", "0", CVAR_CHEAT}, { &pmove_fixed, "pmove_fixed", "0", 0}, { &pmove_msec, "pmove_msec", "8", 0}, { &cg_noTaunt, "cg_noTaunt", "0", CVAR_ARCHIVE}, { &cg_noProjectileTrail, "cg_noProjectileTrail", "0", CVAR_ARCHIVE}, { &cg_smallFont, "ui_smallFont", "0.25", CVAR_ARCHIVE}, { &cg_bigFont, "ui_bigFont", "0.4", CVAR_ARCHIVE}, { &cg_oldRail, "cg_oldRail", "1", CVAR_ARCHIVE}, { &cg_oldRocket, "cg_oldRocket", "1", CVAR_ARCHIVE}, { &cg_oldPlasma, "cg_oldPlasma", "1", CVAR_ARCHIVE}, { &cg_trueLightning, "cg_trueLightning", "0.0", CVAR_ARCHIVE} // { &cg_pmove_fixed, "cg_pmove_fixed", "0", CVAR_USERINFO | CVAR_ARCHIVE } }; static int cvarTableSize = sizeof( cvarTable ) / sizeof( cvarTable[0] ); /* ================= CG_RegisterCvars ================= */ void CG_RegisterCvars( void ) { int i; cvarTable_t *cv; char var[MAX_TOKEN_CHARS]; for ( i = 0, cv = cvarTable ; i < cvarTableSize ; i++, cv++ ) { trap_Cvar_Register( cv->vmCvar, cv->cvarName, cv->defaultString, cv->cvarFlags ); } // see if we are also running the server on this machine trap_Cvar_VariableStringBuffer( "sv_running", var, sizeof( var ) ); cgs.localServer = atoi( var ); forceModelModificationCount = cg_forceModel.modificationCount; trap_Cvar_Register(NULL, "model", DEFAULT_MODEL, CVAR_USERINFO | CVAR_ARCHIVE ); trap_Cvar_Register(NULL, "headmodel", DEFAULT_MODEL, CVAR_USERINFO | CVAR_ARCHIVE ); trap_Cvar_Register(NULL, "team_model", DEFAULT_TEAM_MODEL, CVAR_USERINFO | CVAR_ARCHIVE ); trap_Cvar_Register(NULL, "team_headmodel", DEFAULT_TEAM_HEAD, CVAR_USERINFO | CVAR_ARCHIVE ); } /* =================== CG_ForceModelChange =================== */ static void CG_ForceModelChange( void ) { int i; for (i=0 ; ivmCvar ); } // check for modications here // If team overlay is on, ask for updates from the server. If its off, // let the server know so we don't receive it if ( drawTeamOverlayModificationCount != cg_drawTeamOverlay.modificationCount ) { drawTeamOverlayModificationCount = cg_drawTeamOverlay.modificationCount; if ( cg_drawTeamOverlay.integer > 0 ) { trap_Cvar_Set( "teamoverlay", "1" ); } else { trap_Cvar_Set( "teamoverlay", "0" ); } // FIXME E3 HACK trap_Cvar_Set( "teamoverlay", "1" ); } // if force model changed if ( forceModelModificationCount != cg_forceModel.modificationCount ) { forceModelModificationCount = cg_forceModel.modificationCount; CG_ForceModelChange(); } } int CG_CrosshairPlayer( void ) { if ( cg.time > ( cg.crosshairClientTime + 1000 ) ) { return -1; } return cg.crosshairClientNum; } int CG_LastAttacker( void ) { if ( !cg.attackerTime ) { return -1; } return cg.snap->ps.persistant[PERS_ATTACKER]; } void QDECL CG_Printf( const char *msg, ... ) { va_list argptr; char text[1024]; va_start (argptr, msg); vsprintf (text, msg, argptr); va_end (argptr); trap_Print( text ); } void QDECL CG_Error( const char *msg, ... ) { va_list argptr; char text[1024]; va_start (argptr, msg); vsprintf (text, msg, argptr); va_end (argptr); trap_Error( text ); } #ifndef CGAME_HARD_LINKED // this is only here so the functions in q_shared.c and bg_*.c can link (FIXME) void QDECL Com_Error( int level, const char *error, ... ) { va_list argptr; char text[1024]; va_start (argptr, error); vsprintf (text, error, argptr); va_end (argptr); CG_Error( "%s", text); } void QDECL Com_Printf( const char *msg, ... ) { va_list argptr; char text[1024]; va_start (argptr, msg); vsprintf (text, msg, argptr); va_end (argptr); CG_Printf ("%s", text); } #endif /* ================ CG_Argv ================ */ const char *CG_Argv( int arg ) { static char buffer[MAX_STRING_CHARS]; trap_Argv( arg, buffer, sizeof( buffer ) ); return buffer; } //======================================================================== /* ================= CG_RegisterItemSounds The server says this item is used on this level ================= */ static void CG_RegisterItemSounds( int itemNum ) { gitem_t *item; char data[MAX_QPATH]; char *s, *start; int len; item = &bg_itemlist[ itemNum ]; if( item->pickup_sound ) { trap_S_RegisterSound( item->pickup_sound, qfalse ); } // parse the space seperated precache string for other media s = item->sounds; if (!s || !s[0]) return; while (*s) { start = s; while (*s && *s != ' ') { s++; } len = s-start; if (len >= MAX_QPATH || len < 5) { CG_Error( "PrecacheItem: %s has bad precache string", item->classname); return; } memcpy (data, start, len); data[len] = 0; if ( *s ) { s++; } if ( !strcmp(data+len-3, "wav" )) { trap_S_RegisterSound( data, qfalse ); } } } /* ================= CG_RegisterSounds called during a precache command ================= */ static void CG_RegisterSounds( void ) { int i; char items[MAX_ITEMS+1]; char name[MAX_QPATH]; const char *soundName; cgs.media.oneMinuteSound = trap_S_RegisterSound( "sound/feedback/1_minute.wav", qtrue ); cgs.media.fiveMinuteSound = trap_S_RegisterSound( "sound/feedback/5_minute.wav", qtrue ); cgs.media.suddenDeathSound = trap_S_RegisterSound( "sound/feedback/sudden_death.wav", qtrue ); cgs.media.oneFragSound = trap_S_RegisterSound( "sound/feedback/1_frag.wav", qtrue ); cgs.media.twoFragSound = trap_S_RegisterSound( "sound/feedback/2_frags.wav", qtrue ); cgs.media.threeFragSound = trap_S_RegisterSound( "sound/feedback/3_frags.wav", qtrue ); cgs.media.count3Sound = trap_S_RegisterSound( "sound/feedback/three.wav", qtrue ); cgs.media.count2Sound = trap_S_RegisterSound( "sound/feedback/two.wav", qtrue ); cgs.media.count1Sound = trap_S_RegisterSound( "sound/feedback/one.wav", qtrue ); cgs.media.countFightSound = trap_S_RegisterSound( "sound/feedback/fight.wav", qtrue ); cgs.media.countPrepareSound = trap_S_RegisterSound( "sound/feedback/prepare.wav", qtrue ); if ( cgs.gametype >= GT_TEAM || cg_buildScript.integer ) { cgs.media.captureAwardSound = trap_S_RegisterSound( "sound/teamplay/flagcapture_yourteam.wav", qtrue ); cgs.media.redLeadsSound = trap_S_RegisterSound( "sound/feedback/redleads.wav", qtrue ); cgs.media.blueLeadsSound = trap_S_RegisterSound( "sound/feedback/blueleads.wav", qtrue ); cgs.media.teamsTiedSound = trap_S_RegisterSound( "sound/feedback/teamstied.wav", qtrue ); cgs.media.hitTeamSound = trap_S_RegisterSound( "sound/feedback/hit_teammate.wav", qtrue ); cgs.media.redScoredSound = trap_S_RegisterSound( "sound/teamplay/voc_red_scores.wav", qtrue ); cgs.media.blueScoredSound = trap_S_RegisterSound( "sound/teamplay/voc_blue_scores.wav", qtrue ); cgs.media.captureYourTeamSound = trap_S_RegisterSound( "sound/teamplay/flagcapture_yourteam.wav", qtrue ); cgs.media.captureOpponentSound = trap_S_RegisterSound( "sound/teamplay/flagcapture_opponent.wav", qtrue ); cgs.media.returnYourTeamSound = trap_S_RegisterSound( "sound/teamplay/flagreturn_yourteam.wav", qtrue ); cgs.media.returnOpponentSound = trap_S_RegisterSound( "sound/teamplay/flagreturn_opponent.wav", qtrue ); cgs.media.takenYourTeamSound = trap_S_RegisterSound( "sound/teamplay/flagtaken_yourteam.wav", qtrue ); cgs.media.takenOpponentSound = trap_S_RegisterSound( "sound/teamplay/flagtaken_opponent.wav", qtrue ); if ( cgs.gametype == GT_CTF || cg_buildScript.integer ) { cgs.media.redFlagReturnedSound = trap_S_RegisterSound( "sound/teamplay/voc_red_returned.wav", qtrue ); cgs.media.blueFlagReturnedSound = trap_S_RegisterSound( "sound/teamplay/voc_blue_returned.wav", qtrue ); cgs.media.enemyTookYourFlagSound = trap_S_RegisterSound( "sound/teamplay/voc_enemy_flag.wav", qtrue ); cgs.media.yourTeamTookEnemyFlagSound = trap_S_RegisterSound( "sound/teamplay/voc_team_flag.wav", qtrue ); } cgs.media.youHaveFlagSound = trap_S_RegisterSound( "sound/teamplay/voc_you_flag.wav", qtrue ); cgs.media.holyShitSound = trap_S_RegisterSound("sound/feedback/voc_holyshit.wav", qtrue); cgs.media.neutralFlagReturnedSound = trap_S_RegisterSound( "sound/teamplay/flagreturn_opponent.wav", qtrue ); cgs.media.yourTeamTookTheFlagSound = trap_S_RegisterSound( "sound/teamplay/voc_team_1flag.wav", qtrue ); cgs.media.enemyTookTheFlagSound = trap_S_RegisterSound( "sound/teamplay/voc_enemy_1flag.wav", qtrue ); } cgs.media.tracerSound = trap_S_RegisterSound( "sound/weapons/machinegun/buletby1.wav", qfalse ); cgs.media.selectSound = trap_S_RegisterSound( "sound/weapons/change.wav", qfalse ); cgs.media.wearOffSound = trap_S_RegisterSound( "sound/items/wearoff.wav", qfalse ); cgs.media.useNothingSound = trap_S_RegisterSound( "sound/items/use_nothing.wav", qfalse ); cgs.media.gibSound = trap_S_RegisterSound( "sound/player/gibsplt1.wav", qfalse ); cgs.media.gibBounce1Sound = trap_S_RegisterSound( "sound/player/gibimp1.wav", qfalse ); cgs.media.gibBounce2Sound = trap_S_RegisterSound( "sound/player/gibimp2.wav", qfalse ); cgs.media.gibBounce3Sound = trap_S_RegisterSound( "sound/player/gibimp3.wav", qfalse ); cgs.media.teleInSound = trap_S_RegisterSound( "sound/world/telein.wav", qfalse ); cgs.media.teleOutSound = trap_S_RegisterSound( "sound/world/teleout.wav", qfalse ); cgs.media.respawnSound = trap_S_RegisterSound( "sound/items/respawn1.wav", qfalse ); cgs.media.noAmmoSound = trap_S_RegisterSound( "sound/weapons/noammo.wav", qfalse ); cgs.media.talkSound = trap_S_RegisterSound( "sound/player/talk.wav", qfalse ); cgs.media.landSound = trap_S_RegisterSound( "sound/player/land1.wav", qfalse); cgs.media.hitSound = trap_S_RegisterSound( "sound/feedback/hit.wav", qfalse ); cgs.media.impressiveSound = trap_S_RegisterSound( "sound/feedback/impressive.wav", qtrue ); cgs.media.excellentSound = trap_S_RegisterSound( "sound/feedback/excellent.wav", qtrue ); cgs.media.deniedSound = trap_S_RegisterSound( "sound/feedback/denied.wav", qtrue ); cgs.media.humiliationSound = trap_S_RegisterSound( "sound/feedback/humiliation.wav", qtrue ); cgs.media.assistSound = trap_S_RegisterSound( "sound/feedback/assist.wav", qtrue ); cgs.media.defendSound = trap_S_RegisterSound( "sound/feedback/defense.wav", qtrue ); cgs.media.takenLeadSound = trap_S_RegisterSound( "sound/feedback/takenlead.wav", qtrue); cgs.media.tiedLeadSound = trap_S_RegisterSound( "sound/feedback/tiedlead.wav", qtrue); cgs.media.lostLeadSound = trap_S_RegisterSound( "sound/feedback/lostlead.wav", qtrue); cgs.media.watrInSound = trap_S_RegisterSound( "sound/player/watr_in.wav", qfalse); cgs.media.watrOutSound = trap_S_RegisterSound( "sound/player/watr_out.wav", qfalse); cgs.media.watrUnSound = trap_S_RegisterSound( "sound/player/watr_un.wav", qfalse); cgs.media.jumpPadSound = trap_S_RegisterSound ("sound/world/jumppad.wav", qfalse ); for (i=0 ; i<4 ; i++) { Com_sprintf (name, sizeof(name), "sound/player/footsteps/step%i.wav", i+1); cgs.media.footsteps[FOOTSTEP_NORMAL][i] = trap_S_RegisterSound (name, qfalse); Com_sprintf (name, sizeof(name), "sound/player/footsteps/boot%i.wav", i+1); cgs.media.footsteps[FOOTSTEP_BOOT][i] = trap_S_RegisterSound (name, qfalse); Com_sprintf (name, sizeof(name), "sound/player/footsteps/flesh%i.wav", i+1); cgs.media.footsteps[FOOTSTEP_FLESH][i] = trap_S_RegisterSound (name, qfalse); Com_sprintf (name, sizeof(name), "sound/player/footsteps/mech%i.wav", i+1); cgs.media.footsteps[FOOTSTEP_MECH][i] = trap_S_RegisterSound (name, qfalse); Com_sprintf (name, sizeof(name), "sound/player/footsteps/energy%i.wav", i+1); cgs.media.footsteps[FOOTSTEP_ENERGY][i] = trap_S_RegisterSound (name, qfalse); Com_sprintf (name, sizeof(name), "sound/player/footsteps/splash%i.wav", i+1); cgs.media.footsteps[FOOTSTEP_SPLASH][i] = trap_S_RegisterSound (name, qfalse); Com_sprintf (name, sizeof(name), "sound/player/footsteps/clank%i.wav", i+1); cgs.media.footsteps[FOOTSTEP_METAL][i] = trap_S_RegisterSound (name, qfalse); } // only register the items that the server says we need strcpy( items, CG_ConfigString( CS_ITEMS ) ); for ( i = 1 ; i < bg_numItems ; i++ ) { // if ( items[ i ] == '1' || cg_buildScript.integer ) { CG_RegisterItemSounds( i ); // } } for ( i = 1 ; i < MAX_SOUNDS ; i++ ) { soundName = CG_ConfigString( CS_SOUNDS+i ); if ( !soundName[0] ) { break; } if ( soundName[0] == '*' ) { continue; // custom sound } cgs.gameSounds[i] = trap_S_RegisterSound( soundName, qfalse ); } // FIXME: only needed with item cgs.media.flightSound = trap_S_RegisterSound( "sound/items/flight.wav", qfalse ); cgs.media.medkitSound = trap_S_RegisterSound ("sound/items/use_medkit.wav", qfalse); cgs.media.quadSound = trap_S_RegisterSound("sound/items/damage3.wav", qfalse); cgs.media.sfx_ric1 = trap_S_RegisterSound ("sound/weapons/machinegun/ric1.wav", qfalse); cgs.media.sfx_ric2 = trap_S_RegisterSound ("sound/weapons/machinegun/ric2.wav", qfalse); cgs.media.sfx_ric3 = trap_S_RegisterSound ("sound/weapons/machinegun/ric3.wav", qfalse); cgs.media.sfx_railg = trap_S_RegisterSound ("sound/weapons/railgun/railgf1a.wav", qfalse); cgs.media.sfx_rockexp = trap_S_RegisterSound ("sound/weapons/rocket/rocklx1a.wav", qfalse); cgs.media.sfx_plasmaexp = trap_S_RegisterSound ("sound/weapons/plasma/plasmx1a.wav", qfalse); cgs.media.regenSound = trap_S_RegisterSound("sound/items/regen.wav", qfalse); cgs.media.protectSound = trap_S_RegisterSound("sound/items/protect3.wav", qfalse); cgs.media.n_healthSound = trap_S_RegisterSound("sound/items/n_health.wav", qfalse ); cgs.media.hgrenb1aSound = trap_S_RegisterSound("sound/weapons/grenade/hgrenb1a.wav", qfalse); cgs.media.hgrenb2aSound = trap_S_RegisterSound("sound/weapons/grenade/hgrenb2a.wav", qfalse); } //=================================================================================== /* ================= CG_RegisterGraphics This function may execute for a couple of minutes with a slow disk. ================= */ static void CG_RegisterGraphics( void ) { int i; char items[MAX_ITEMS+1]; static char *sb_nums[11] = { "gfx/2d/numbers/zero_32b", "gfx/2d/numbers/one_32b", "gfx/2d/numbers/two_32b", "gfx/2d/numbers/three_32b", "gfx/2d/numbers/four_32b", "gfx/2d/numbers/five_32b", "gfx/2d/numbers/six_32b", "gfx/2d/numbers/seven_32b", "gfx/2d/numbers/eight_32b", "gfx/2d/numbers/nine_32b", "gfx/2d/numbers/minus_32b", }; // clear any references to old media memset( &cg.refdef, 0, sizeof( cg.refdef ) ); trap_R_ClearScene(); CG_LoadingString( cgs.mapname ); trap_R_LoadWorldMap( cgs.mapname ); // precache status bar pics CG_LoadingString( "game media" ); for ( i=0 ; i<11 ; i++) { cgs.media.numberShaders[i] = trap_R_RegisterShader( sb_nums[i] ); } cgs.media.botSkillShaders[0] = trap_R_RegisterShader( "menu/art/skill1.tga" ); cgs.media.botSkillShaders[1] = trap_R_RegisterShader( "menu/art/skill2.tga" ); cgs.media.botSkillShaders[2] = trap_R_RegisterShader( "menu/art/skill3.tga" ); cgs.media.botSkillShaders[3] = trap_R_RegisterShader( "menu/art/skill4.tga" ); cgs.media.botSkillShaders[4] = trap_R_RegisterShader( "menu/art/skill5.tga" ); cgs.media.viewBloodShader = trap_R_RegisterShader( "viewBloodBlend" ); cgs.media.deferShader = trap_R_RegisterShaderNoMip( "gfx/2d/defer.tga" ); cgs.media.scoreboardName = trap_R_RegisterShaderNoMip( "menu/tab/name.tga" ); cgs.media.scoreboardPing = trap_R_RegisterShaderNoMip( "menu/tab/ping.tga" ); cgs.media.scoreboardScore = trap_R_RegisterShaderNoMip( "menu/tab/score.tga" ); cgs.media.scoreboardTime = trap_R_RegisterShaderNoMip( "menu/tab/time.tga" ); cgs.media.smokePuffShader = trap_R_RegisterShader( "smokePuff" ); cgs.media.smokePuffRageProShader = trap_R_RegisterShader( "smokePuffRagePro" ); cgs.media.shotgunSmokePuffShader = trap_R_RegisterShader( "shotgunSmokePuff" ); cgs.media.plasmaBallShader = trap_R_RegisterShader( "sprites/plasma1" ); cgs.media.bloodTrailShader = trap_R_RegisterShader( "bloodTrail" ); cgs.media.lagometerShader = trap_R_RegisterShader("lagometer" ); cgs.media.connectionShader = trap_R_RegisterShader( "disconnected" ); cgs.media.waterBubbleShader = trap_R_RegisterShader( "waterBubble" ); cgs.media.tracerShader = trap_R_RegisterShader( "gfx/misc/tracer" ); cgs.media.selectShader = trap_R_RegisterShader( "gfx/2d/select" ); for ( i = 0 ; i < NUM_CROSSHAIRS ; i++ ) { cgs.media.crosshairShader[i] = trap_R_RegisterShader( va("gfx/2d/crosshair%c", 'a'+i) ); } cgs.media.backTileShader = trap_R_RegisterShader( "gfx/2d/backtile" ); cgs.media.noammoShader = trap_R_RegisterShader( "icons/noammo" ); // powerup shaders cgs.media.quadShader = trap_R_RegisterShader("powerups/quad" ); cgs.media.quadWeaponShader = trap_R_RegisterShader("powerups/quadWeapon" ); cgs.media.battleSuitShader = trap_R_RegisterShader("powerups/battleSuit" ); cgs.media.battleWeaponShader = trap_R_RegisterShader("powerups/battleWeapon" ); cgs.media.invisShader = trap_R_RegisterShader("powerups/invisibility" ); cgs.media.regenShader = trap_R_RegisterShader("powerups/regen" ); cgs.media.hastePuffShader = trap_R_RegisterShader("hasteSmokePuff" ); if ( cgs.gametype == GT_CTF || cg_buildScript.integer ) { cgs.media.redCubeModel = trap_R_RegisterModel( "models/powerups/orb/r_orb.md3" ); cgs.media.blueCubeModel = trap_R_RegisterModel( "models/powerups/orb/b_orb.md3" ); cgs.media.redCubeIcon = trap_R_RegisterShader( "icons/skull_red" ); cgs.media.blueCubeIcon = trap_R_RegisterShader( "icons/skull_blue" ); } if ( cgs.gametype == GT_CTF || cg_buildScript.integer ) { cgs.media.redFlagModel = trap_R_RegisterModel( "models/flags/r_flag.md3" ); cgs.media.blueFlagModel = trap_R_RegisterModel( "models/flags/b_flag.md3" ); cgs.media.redFlagShader[0] = trap_R_RegisterShaderNoMip( "icons/iconf_red1" ); cgs.media.redFlagShader[1] = trap_R_RegisterShaderNoMip( "icons/iconf_red2" ); cgs.media.redFlagShader[2] = trap_R_RegisterShaderNoMip( "icons/iconf_red3" ); cgs.media.blueFlagShader[0] = trap_R_RegisterShaderNoMip( "icons/iconf_blu1" ); cgs.media.blueFlagShader[1] = trap_R_RegisterShaderNoMip( "icons/iconf_blu2" ); cgs.media.blueFlagShader[2] = trap_R_RegisterShaderNoMip( "icons/iconf_blu3" ); } if ( cgs.gametype >= GT_TEAM || cg_buildScript.integer ) { cgs.media.friendShader = trap_R_RegisterShader( "sprites/foe" ); cgs.media.redQuadShader = trap_R_RegisterShader("powerups/blueflag" ); cgs.media.teamStatusBar = trap_R_RegisterShader( "gfx/2d/colorbar.tga" ); } cgs.media.armorModel = trap_R_RegisterModel( "models/powerups/armor/armor_yel.md3" ); cgs.media.armorIcon = trap_R_RegisterShaderNoMip( "icons/iconr_yellow" ); cgs.media.machinegunBrassModel = trap_R_RegisterModel( "models/weapons2/shells/m_shell.md3" ); cgs.media.shotgunBrassModel = trap_R_RegisterModel( "models/weapons2/shells/s_shell.md3" ); cgs.media.gibAbdomen = trap_R_RegisterModel( "models/gibs/abdomen.md3" ); cgs.media.gibArm = trap_R_RegisterModel( "models/gibs/arm.md3" ); cgs.media.gibChest = trap_R_RegisterModel( "models/gibs/chest.md3" ); cgs.media.gibFist = trap_R_RegisterModel( "models/gibs/fist.md3" ); cgs.media.gibFoot = trap_R_RegisterModel( "models/gibs/foot.md3" ); cgs.media.gibForearm = trap_R_RegisterModel( "models/gibs/forearm.md3" ); cgs.media.gibIntestine = trap_R_RegisterModel( "models/gibs/intestine.md3" ); cgs.media.gibLeg = trap_R_RegisterModel( "models/gibs/leg.md3" ); cgs.media.gibSkull = trap_R_RegisterModel( "models/gibs/skull.md3" ); cgs.media.gibBrain = trap_R_RegisterModel( "models/gibs/brain.md3" ); cgs.media.smoke2 = trap_R_RegisterModel( "models/weapons2/shells/s_shell.md3" ); cgs.media.balloonShader = trap_R_RegisterShader( "sprites/balloon3" ); cgs.media.bloodExplosionShader = trap_R_RegisterShader( "bloodExplosion" ); cgs.media.bulletFlashModel = trap_R_RegisterModel("models/weaphits/bullet.md3"); cgs.media.ringFlashModel = trap_R_RegisterModel("models/weaphits/ring02.md3"); cgs.media.dishFlashModel = trap_R_RegisterModel("models/weaphits/boom01.md3"); cgs.media.teleportEffectModel = trap_R_RegisterModel( "models/misc/telep.md3" ); cgs.media.teleportEffectShader = trap_R_RegisterShader( "teleportEffect" ); cgs.media.invulnerabilityPowerupModel = trap_R_RegisterModel( "models/powerups/shield/shield.md3" ); cgs.media.medalImpressive = trap_R_RegisterShaderNoMip( "medal_impressive" ); cgs.media.medalExcellent = trap_R_RegisterShaderNoMip( "medal_excellent" ); cgs.media.medalGauntlet = trap_R_RegisterShaderNoMip( "medal_gauntlet" ); cgs.media.medalDefend = trap_R_RegisterShaderNoMip( "medal_defend" ); cgs.media.medalAssist = trap_R_RegisterShaderNoMip( "medal_assist" ); cgs.media.medalCapture = trap_R_RegisterShaderNoMip( "medal_capture" ); memset( cg_items, 0, sizeof( cg_items ) ); memset( cg_weapons, 0, sizeof( cg_weapons ) ); // only register the items that the server says we need strcpy( items, CG_ConfigString( CS_ITEMS) ); for ( i = 1 ; i < bg_numItems ; i++ ) { if ( items[ i ] == '1' || cg_buildScript.integer ) { CG_LoadingItem( i ); CG_RegisterItemVisuals( i ); } } // wall marks cgs.media.bulletMarkShader = trap_R_RegisterShader( "gfx/damage/bullet_mrk" ); cgs.media.burnMarkShader = trap_R_RegisterShader( "gfx/damage/burn_med_mrk" ); cgs.media.holeMarkShader = trap_R_RegisterShader( "gfx/damage/hole_lg_mrk" ); cgs.media.energyMarkShader = trap_R_RegisterShader( "gfx/damage/plasma_mrk" ); cgs.media.shadowMarkShader = trap_R_RegisterShader( "markShadow" ); cgs.media.wakeMarkShader = trap_R_RegisterShader( "wake" ); cgs.media.bloodMarkShader = trap_R_RegisterShader( "bloodMark" ); // register the inline models cgs.numInlineModels = trap_CM_NumInlineModels(); for ( i = 1 ; i < cgs.numInlineModels ; i++ ) { char name[10]; vec3_t mins, maxs; int j; Com_sprintf( name, sizeof(name), "*%i", i ); cgs.inlineDrawModel[i] = trap_R_RegisterModel( name ); trap_R_ModelBounds( cgs.inlineDrawModel[i], mins, maxs ); for ( j = 0 ; j < 3 ; j++ ) { cgs.inlineModelMidpoints[i][j] = mins[j] + 0.5 * ( maxs[j] - mins[j] ); } } // register all the server specified models for (i=1 ; i= MAX_CONFIGSTRINGS ) { CG_Error( "CG_ConfigString: bad index: %i", index ); } return cgs.gameState.stringData + cgs.gameState.stringOffsets[ index ]; } //================================================================== /* ====================== CG_StartMusic ====================== */ void CG_StartMusic( void ) { char *s; char parm1[MAX_QPATH], parm2[MAX_QPATH]; // start the background music s = (char *)CG_ConfigString( CS_MUSIC ); Q_strncpyz( parm1, COM_Parse( &s ), sizeof( parm1 ) ); Q_strncpyz( parm2, COM_Parse( &s ), sizeof( parm2 ) ); trap_S_StartBackgroundTrack( parm1, parm2 ); } /* ================= CG_Init Called after every level change or subsystem restart Will perform callbacks to make the loading info screen update. ================= */ void CG_Init( int serverMessageNum, int serverCommandSequence, int clientNum ) { const char *s; // clear everything memset( &cgs, 0, sizeof( cgs ) ); memset( &cg, 0, sizeof( cg ) ); memset( cg_entities, 0, sizeof(cg_entities) ); memset( cg_weapons, 0, sizeof(cg_weapons) ); memset( cg_items, 0, sizeof(cg_items) ); cg.clientNum = clientNum; cgs.processedSnapshotNum = serverMessageNum; cgs.serverCommandSequence = serverCommandSequence; // load a few needed things before we do any screen updates cgs.media.charsetShader = trap_R_RegisterShader( "gfx/2d/bigchars" ); cgs.media.whiteShader = trap_R_RegisterShader( "white" ); cgs.media.charsetProp = trap_R_RegisterShaderNoMip( "menu/art/font1_prop.tga" ); cgs.media.charsetPropGlow = trap_R_RegisterShaderNoMip( "menu/art/font1_prop_glo.tga" ); cgs.media.charsetPropB = trap_R_RegisterShaderNoMip( "menu/art/font2_prop.tga" ); CG_RegisterCvars(); CG_InitConsoleCommands(); cg.weaponSelect = WP_MACHINEGUN; cgs.redflag = cgs.blueflag = -1; // For compatibily, default to unset for cgs.flagStatus = -1; // old servers // get the rendering configuration from the client system trap_GetGlconfig( &cgs.glconfig ); cgs.screenXScale = cgs.glconfig.vidWidth / 640.0; cgs.screenYScale = cgs.glconfig.vidHeight / 480.0; // get the gamestate from the client system trap_GetGameState( &cgs.gameState ); // check version s = CG_ConfigString( CS_GAME_VERSION ); if ( strcmp( s, GAME_VERSION ) ) { CG_Error( "Client/Server game mismatch: %s/%s", GAME_VERSION, s ); } s = CG_ConfigString( CS_LEVEL_START_TIME ); cgs.levelStartTime = atoi( s ); CG_ParseServerinfo(); // load the new map CG_LoadingString( "collision map" ); trap_CM_LoadMap( cgs.mapname ); cg.loading = qtrue; // force players to load instead of defer CG_LoadingString( "sounds" ); CG_RegisterSounds(); CG_LoadingString( "graphics" ); CG_RegisterGraphics(); CG_LoadingString( "clients" ); CG_RegisterClients(); // if low on memory, some clients will be deferred cg.loading = qfalse; // future players will be deferred CG_InitLocalEntities(); CG_InitMarkPolys(); // remove the last loading update cg.infoScreenText[0] = 0; // Make sure we have update values (scores) CG_SetConfigValues(); CG_StartMusic(); CG_LoadingString( "" ); CG_ShaderStateChanged(); trap_S_ClearLoopingSounds( qtrue ); } /* ================= CG_Shutdown Called before every level change or subsystem restart ================= */ void CG_Shutdown( void ) { // some mods may need to do cleanup work here, // like closing files or archiving session data } /* ================== CG_EventHandling ================== type 0 - no event handling 1 - team menu 2 - hud editor */ void CG_EventHandling(int type) { } void CG_KeyEvent(int key, qboolean down) { } void CG_MouseEvent(int x, int y) { } ================================================ FILE: src/cgame/cg_marks.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_marks.c -- wall marks #include "cg_local.h" /* =================================================================== MARK POLYS =================================================================== */ markPoly_t cg_activeMarkPolys; // double linked list markPoly_t *cg_freeMarkPolys; // single linked list markPoly_t cg_markPolys[MAX_MARK_POLYS]; static int markTotal; /* =================== CG_InitMarkPolys This is called at startup and for tournement restarts =================== */ void CG_InitMarkPolys( void ) { int i; memset( cg_markPolys, 0, sizeof(cg_markPolys) ); cg_activeMarkPolys.nextMark = &cg_activeMarkPolys; cg_activeMarkPolys.prevMark = &cg_activeMarkPolys; cg_freeMarkPolys = cg_markPolys; for ( i = 0 ; i < MAX_MARK_POLYS - 1 ; i++ ) { cg_markPolys[i].nextMark = &cg_markPolys[i+1]; } } /* ================== CG_FreeMarkPoly ================== */ void CG_FreeMarkPoly( markPoly_t *le ) { if ( !le->prevMark ) { CG_Error( "CG_FreeLocalEntity: not active" ); } // remove from the doubly linked active list le->prevMark->nextMark = le->nextMark; le->nextMark->prevMark = le->prevMark; // the free list is only singly linked le->nextMark = cg_freeMarkPolys; cg_freeMarkPolys = le; } /* =================== CG_AllocMark Will allways succeed, even if it requires freeing an old active mark =================== */ markPoly_t *CG_AllocMark( void ) { markPoly_t *le; int time; if ( !cg_freeMarkPolys ) { // no free entities, so free the one at the end of the chain // remove the oldest active entity time = cg_activeMarkPolys.prevMark->time; while (cg_activeMarkPolys.prevMark && time == cg_activeMarkPolys.prevMark->time) { CG_FreeMarkPoly( cg_activeMarkPolys.prevMark ); } } le = cg_freeMarkPolys; cg_freeMarkPolys = cg_freeMarkPolys->nextMark; memset( le, 0, sizeof( *le ) ); // link into the active list le->nextMark = cg_activeMarkPolys.nextMark; le->prevMark = &cg_activeMarkPolys; cg_activeMarkPolys.nextMark->prevMark = le; cg_activeMarkPolys.nextMark = le; return le; } /* ================= CG_ImpactMark origin should be a point within a unit of the plane dir should be the plane normal temporary marks will not be stored or randomly oriented, but immediately passed to the renderer. ================= */ #define MAX_MARK_FRAGMENTS 128 #define MAX_MARK_POINTS 384 void CG_ImpactMark( qhandle_t markShader, const vec3_t origin, const vec3_t dir, float orientation, float red, float green, float blue, float alpha, qboolean alphaFade, float radius, qboolean temporary ) { vec3_t axis[3]; float texCoordScale; vec3_t originalPoints[4]; byte colors[4]; int i, j; int numFragments; markFragment_t markFragments[MAX_MARK_FRAGMENTS], *mf; vec3_t markPoints[MAX_MARK_POINTS]; vec3_t projection; if ( !cg_addMarks.integer ) { return; } if ( radius <= 0 ) { CG_Error( "CG_ImpactMark called with <= 0 radius" ); } //if ( markTotal >= MAX_MARK_POLYS ) { // return; //} // create the texture axis VectorNormalize2( dir, axis[0] ); PerpendicularVector( axis[1], axis[0] ); RotatePointAroundVector( axis[2], axis[0], axis[1], orientation ); CrossProduct( axis[0], axis[2], axis[1] ); texCoordScale = 0.5 * 1.0 / radius; // create the full polygon for ( i = 0 ; i < 3 ; i++ ) { originalPoints[0][i] = origin[i] - radius * axis[1][i] - radius * axis[2][i]; originalPoints[1][i] = origin[i] + radius * axis[1][i] - radius * axis[2][i]; originalPoints[2][i] = origin[i] + radius * axis[1][i] + radius * axis[2][i]; originalPoints[3][i] = origin[i] - radius * axis[1][i] + radius * axis[2][i]; } // get the fragments VectorScale( dir, -20, projection ); numFragments = trap_CM_MarkFragments( 4, (void *)originalPoints, projection, MAX_MARK_POINTS, markPoints[0], MAX_MARK_FRAGMENTS, markFragments ); colors[0] = red * 255; colors[1] = green * 255; colors[2] = blue * 255; colors[3] = alpha * 255; for ( i = 0, mf = markFragments ; i < numFragments ; i++, mf++ ) { polyVert_t *v; polyVert_t verts[MAX_VERTS_ON_POLY]; markPoly_t *mark; // we have an upper limit on the complexity of polygons // that we store persistantly if ( mf->numPoints > MAX_VERTS_ON_POLY ) { mf->numPoints = MAX_VERTS_ON_POLY; } for ( j = 0, v = verts ; j < mf->numPoints ; j++, v++ ) { vec3_t delta; VectorCopy( markPoints[mf->firstPoint + j], v->xyz ); VectorSubtract( v->xyz, origin, delta ); v->st[0] = 0.5 + DotProduct( delta, axis[1] ) * texCoordScale; v->st[1] = 0.5 + DotProduct( delta, axis[2] ) * texCoordScale; *(int *)v->modulate = *(int *)colors; } // if it is a temporary (shadow) mark, add it immediately and forget about it if ( temporary ) { trap_R_AddPolyToScene( markShader, mf->numPoints, verts ); continue; } // otherwise save it persistantly mark = CG_AllocMark(); mark->time = cg.time; mark->alphaFade = alphaFade; mark->markShader = markShader; mark->poly.numVerts = mf->numPoints; mark->color[0] = red; mark->color[1] = green; mark->color[2] = blue; mark->color[3] = alpha; memcpy( mark->verts, verts, mf->numPoints * sizeof( verts[0] ) ); markTotal++; } } /* =============== CG_AddMarks =============== */ #define MARK_TOTAL_TIME 10000 #define MARK_FADE_TIME 1000 void CG_AddMarks( void ) { int j; markPoly_t *mp, *next; int t; int fade; if ( !cg_addMarks.integer ) { return; } mp = cg_activeMarkPolys.nextMark; for ( ; mp != &cg_activeMarkPolys ; mp = next ) { // grab next now, so if the local entity is freed we // still have it next = mp->nextMark; // see if it is time to completely remove it if ( cg.time > mp->time + MARK_TOTAL_TIME ) { CG_FreeMarkPoly( mp ); continue; } // fade out the energy bursts if ( mp->markShader == cgs.media.energyMarkShader ) { fade = 450 - 450 * ( (cg.time - mp->time ) / 3000.0 ); if ( fade < 255 ) { if ( fade < 0 ) { fade = 0; } if ( mp->verts[0].modulate[0] != 0 ) { for ( j = 0 ; j < mp->poly.numVerts ; j++ ) { mp->verts[j].modulate[0] = mp->color[0] * fade; mp->verts[j].modulate[1] = mp->color[1] * fade; mp->verts[j].modulate[2] = mp->color[2] * fade; } } } } // fade all marks out with time t = mp->time + MARK_TOTAL_TIME - cg.time; if ( t < MARK_FADE_TIME ) { fade = 255 * t / MARK_FADE_TIME; if ( mp->alphaFade ) { for ( j = 0 ; j < mp->poly.numVerts ; j++ ) { mp->verts[j].modulate[3] = fade; } } else { for ( j = 0 ; j < mp->poly.numVerts ; j++ ) { mp->verts[j].modulate[0] = mp->color[0] * fade; mp->verts[j].modulate[1] = mp->color[1] * fade; mp->verts[j].modulate[2] = mp->color[2] * fade; } } } trap_R_AddPolyToScene( mp->markShader, mp->poly.numVerts, mp->verts ); } } // cg_particles.c #define BLOODRED 2 #define EMISIVEFADE 3 #define GREY75 4 typedef struct particle_s { struct particle_s *next; float time; float endtime; vec3_t org; vec3_t vel; vec3_t accel; int color; float colorvel; float alpha; float alphavel; int type; qhandle_t pshader; float height; float width; float endheight; float endwidth; float start; float end; float startfade; qboolean rotate; int snum; qboolean link; // Ridah int shaderAnim; int roll; int accumroll; } cparticle_t; typedef enum { P_NONE, P_WEATHER, P_FLAT, P_SMOKE, P_ROTATE, P_WEATHER_TURBULENT, P_ANIM, // Ridah P_BAT, P_BLEED, P_FLAT_SCALEUP, P_FLAT_SCALEUP_FADE, P_WEATHER_FLURRY, P_SMOKE_IMPACT, P_BUBBLE, P_BUBBLE_TURBULENT, P_SPRITE } particle_type_t; #define MAX_SHADER_ANIMS 32 #define MAX_SHADER_ANIM_FRAMES 64 static char *shaderAnimNames[MAX_SHADER_ANIMS] = { "explode1", NULL }; static qhandle_t shaderAnims[MAX_SHADER_ANIMS][MAX_SHADER_ANIM_FRAMES]; static int shaderAnimCounts[MAX_SHADER_ANIMS] = { 23 }; static float shaderAnimSTRatio[MAX_SHADER_ANIMS] = { 1.0f }; static int numShaderAnims; // done. #define PARTICLE_GRAVITY 40 #define MAX_PARTICLES 1024 cparticle_t *active_particles, *free_particles; cparticle_t particles[MAX_PARTICLES]; int cl_numparticles = MAX_PARTICLES; qboolean initparticles = qfalse; vec3_t pvforward, pvright, pvup; vec3_t rforward, rright, rup; float oldtime; /* =============== CL_ClearParticles =============== */ void CG_ClearParticles (void) { int i; memset( particles, 0, sizeof(particles) ); free_particles = &particles[0]; active_particles = NULL; for (i=0 ;itype == P_WEATHER || p->type == P_WEATHER_TURBULENT || p->type == P_WEATHER_FLURRY || p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT) {// create a front facing polygon if (p->type != P_WEATHER_FLURRY) { if (p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT) { if (org[2] > p->end) { p->time = cg.time; VectorCopy (org, p->org); // Ridah, fixes rare snow flakes that flicker on the ground p->org[2] = ( p->start + crandom () * 4 ); if (p->type == P_BUBBLE_TURBULENT) { p->vel[0] = crandom() * 4; p->vel[1] = crandom() * 4; } } } else { if (org[2] < p->end) { p->time = cg.time; VectorCopy (org, p->org); // Ridah, fixes rare snow flakes that flicker on the ground while (p->org[2] < p->end) { p->org[2] += (p->start - p->end); } if (p->type == P_WEATHER_TURBULENT) { p->vel[0] = crandom() * 16; p->vel[1] = crandom() * 16; } } } // Rafael snow pvs check if (!p->link) return; p->alpha = 1; } // Ridah, had to do this or MAX_POLYS is being exceeded in village1.bsp if (Distance( cg.snap->ps.origin, org ) > 1024) { return; } // done. if (p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT) { VectorMA (org, -p->height, pvup, point); VectorMA (point, -p->width, pvright, point); VectorCopy (point, verts[0].xyz); verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255; verts[0].modulate[1] = 255; verts[0].modulate[2] = 255; verts[0].modulate[3] = 255 * p->alpha; VectorMA (org, -p->height, pvup, point); VectorMA (point, p->width, pvright, point); VectorCopy (point, verts[1].xyz); verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255; verts[1].modulate[1] = 255; verts[1].modulate[2] = 255; verts[1].modulate[3] = 255 * p->alpha; VectorMA (org, p->height, pvup, point); VectorMA (point, p->width, pvright, point); VectorCopy (point, verts[2].xyz); verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255; verts[2].modulate[1] = 255; verts[2].modulate[2] = 255; verts[2].modulate[3] = 255 * p->alpha; VectorMA (org, p->height, pvup, point); VectorMA (point, -p->width, pvright, point); VectorCopy (point, verts[3].xyz); verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255; verts[3].modulate[1] = 255; verts[3].modulate[2] = 255; verts[3].modulate[3] = 255 * p->alpha; } else { VectorMA (org, -p->height, pvup, point); VectorMA (point, -p->width, pvright, point); VectorCopy( point, TRIverts[0].xyz ); TRIverts[0].st[0] = 1; TRIverts[0].st[1] = 0; TRIverts[0].modulate[0] = 255; TRIverts[0].modulate[1] = 255; TRIverts[0].modulate[2] = 255; TRIverts[0].modulate[3] = 255 * p->alpha; VectorMA (org, p->height, pvup, point); VectorMA (point, -p->width, pvright, point); VectorCopy (point, TRIverts[1].xyz); TRIverts[1].st[0] = 0; TRIverts[1].st[1] = 0; TRIverts[1].modulate[0] = 255; TRIverts[1].modulate[1] = 255; TRIverts[1].modulate[2] = 255; TRIverts[1].modulate[3] = 255 * p->alpha; VectorMA (org, p->height, pvup, point); VectorMA (point, p->width, pvright, point); VectorCopy (point, TRIverts[2].xyz); TRIverts[2].st[0] = 0; TRIverts[2].st[1] = 1; TRIverts[2].modulate[0] = 255; TRIverts[2].modulate[1] = 255; TRIverts[2].modulate[2] = 255; TRIverts[2].modulate[3] = 255 * p->alpha; } } else if (p->type == P_SPRITE) { vec3_t rr, ru; vec3_t rotate_ang; VectorSet (color, 1.0, 1.0, 0.5); time = cg.time - p->time; time2 = p->endtime - p->time; ratio = time / time2; width = p->width + ( ratio * ( p->endwidth - p->width) ); height = p->height + ( ratio * ( p->endheight - p->height) ); if (p->roll) { vectoangles( cg.refdef.viewaxis[0], rotate_ang ); rotate_ang[ROLL] += p->roll; AngleVectors ( rotate_ang, NULL, rr, ru); } if (p->roll) { VectorMA (org, -height, ru, point); VectorMA (point, -width, rr, point); } else { VectorMA (org, -height, pvup, point); VectorMA (point, -width, pvright, point); } VectorCopy (point, verts[0].xyz); verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255; verts[0].modulate[1] = 255; verts[0].modulate[2] = 255; verts[0].modulate[3] = 255; if (p->roll) { VectorMA (point, 2*height, ru, point); } else { VectorMA (point, 2*height, pvup, point); } VectorCopy (point, verts[1].xyz); verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255; verts[1].modulate[1] = 255; verts[1].modulate[2] = 255; verts[1].modulate[3] = 255; if (p->roll) { VectorMA (point, 2*width, rr, point); } else { VectorMA (point, 2*width, pvright, point); } VectorCopy (point, verts[2].xyz); verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255; verts[2].modulate[1] = 255; verts[2].modulate[2] = 255; verts[2].modulate[3] = 255; if (p->roll) { VectorMA (point, -2*height, ru, point); } else { VectorMA (point, -2*height, pvup, point); } VectorCopy (point, verts[3].xyz); verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255; verts[3].modulate[1] = 255; verts[3].modulate[2] = 255; verts[3].modulate[3] = 255; } else if (p->type == P_SMOKE || p->type == P_SMOKE_IMPACT) {// create a front rotating facing polygon if ( p->type == P_SMOKE_IMPACT && Distance( cg.snap->ps.origin, org ) > 1024) { return; } if (p->color == BLOODRED) VectorSet (color, 0.22f, 0.0f, 0.0f); else if (p->color == GREY75) { float len; float greyit; float val; len = Distance (cg.snap->ps.origin, org); if (!len) len = 1; val = 4096/len; greyit = 0.25 * val; if (greyit > 0.5) greyit = 0.5; VectorSet (color, greyit, greyit, greyit); } else VectorSet (color, 1.0, 1.0, 1.0); time = cg.time - p->time; time2 = p->endtime - p->time; ratio = time / time2; if (cg.time > p->startfade) { invratio = 1 - ( (cg.time - p->startfade) / (p->endtime - p->startfade) ); if (p->color == EMISIVEFADE) { float fval; fval = (invratio * invratio); if (fval < 0) fval = 0; VectorSet (color, fval , fval , fval ); } invratio *= p->alpha; } else invratio = 1 * p->alpha; if (invratio > 1) invratio = 1; width = p->width + ( ratio * ( p->endwidth - p->width) ); height = p->height + ( ratio * ( p->endheight - p->height) ); if (p->type != P_SMOKE_IMPACT) { vec3_t temp; vectoangles (rforward, temp); p->accumroll += p->roll; temp[ROLL] += p->accumroll * 0.1; AngleVectors ( temp, NULL, rright2, rup2); } else { VectorCopy (rright, rright2); VectorCopy (rup, rup2); } if (p->rotate) { VectorMA (org, -height, rup2, point); VectorMA (point, -width, rright2, point); } else { VectorMA (org, -p->height, pvup, point); VectorMA (point, -p->width, pvright, point); } VectorCopy (point, verts[0].xyz); verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255 * color[0]; verts[0].modulate[1] = 255 * color[1]; verts[0].modulate[2] = 255 * color[2]; verts[0].modulate[3] = 255 * invratio; if (p->rotate) { VectorMA (org, -height, rup2, point); VectorMA (point, width, rright2, point); } else { VectorMA (org, -p->height, pvup, point); VectorMA (point, p->width, pvright, point); } VectorCopy (point, verts[1].xyz); verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255 * color[0]; verts[1].modulate[1] = 255 * color[1]; verts[1].modulate[2] = 255 * color[2]; verts[1].modulate[3] = 255 * invratio; if (p->rotate) { VectorMA (org, height, rup2, point); VectorMA (point, width, rright2, point); } else { VectorMA (org, p->height, pvup, point); VectorMA (point, p->width, pvright, point); } VectorCopy (point, verts[2].xyz); verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255 * color[0]; verts[2].modulate[1] = 255 * color[1]; verts[2].modulate[2] = 255 * color[2]; verts[2].modulate[3] = 255 * invratio; if (p->rotate) { VectorMA (org, height, rup2, point); VectorMA (point, -width, rright2, point); } else { VectorMA (org, p->height, pvup, point); VectorMA (point, -p->width, pvright, point); } VectorCopy (point, verts[3].xyz); verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255 * color[0]; verts[3].modulate[1] = 255 * color[1]; verts[3].modulate[2] = 255 * color[2]; verts[3].modulate[3] = 255 * invratio; } else if (p->type == P_BLEED) { vec3_t rr, ru; vec3_t rotate_ang; float alpha; alpha = p->alpha; if (p->roll) { vectoangles( cg.refdef.viewaxis[0], rotate_ang ); rotate_ang[ROLL] += p->roll; AngleVectors ( rotate_ang, NULL, rr, ru); } else { VectorCopy (pvup, ru); VectorCopy (pvright, rr); } VectorMA (org, -p->height, ru, point); VectorMA (point, -p->width, rr, point); VectorCopy (point, verts[0].xyz); verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 111; verts[0].modulate[1] = 19; verts[0].modulate[2] = 9; verts[0].modulate[3] = 255 * alpha; VectorMA (org, -p->height, ru, point); VectorMA (point, p->width, rr, point); VectorCopy (point, verts[1].xyz); verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 111; verts[1].modulate[1] = 19; verts[1].modulate[2] = 9; verts[1].modulate[3] = 255 * alpha; VectorMA (org, p->height, ru, point); VectorMA (point, p->width, rr, point); VectorCopy (point, verts[2].xyz); verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 111; verts[2].modulate[1] = 19; verts[2].modulate[2] = 9; verts[2].modulate[3] = 255 * alpha; VectorMA (org, p->height, ru, point); VectorMA (point, -p->width, rr, point); VectorCopy (point, verts[3].xyz); verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 111; verts[3].modulate[1] = 19; verts[3].modulate[2] = 9; verts[3].modulate[3] = 255 * alpha; } else if (p->type == P_FLAT_SCALEUP) { float width, height; float sinR, cosR; if (p->color == BLOODRED) VectorSet (color, 1, 1, 1); else VectorSet (color, 0.5, 0.5, 0.5); time = cg.time - p->time; time2 = p->endtime - p->time; ratio = time / time2; width = p->width + ( ratio * ( p->endwidth - p->width) ); height = p->height + ( ratio * ( p->endheight - p->height) ); if (width > p->endwidth) width = p->endwidth; if (height > p->endheight) height = p->endheight; sinR = height * sin(DEG2RAD(p->roll)) * sqrt(2); cosR = width * cos(DEG2RAD(p->roll)) * sqrt(2); VectorCopy (org, verts[0].xyz); verts[0].xyz[0] -= sinR; verts[0].xyz[1] -= cosR; verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255 * color[0]; verts[0].modulate[1] = 255 * color[1]; verts[0].modulate[2] = 255 * color[2]; verts[0].modulate[3] = 255; VectorCopy (org, verts[1].xyz); verts[1].xyz[0] -= cosR; verts[1].xyz[1] += sinR; verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255 * color[0]; verts[1].modulate[1] = 255 * color[1]; verts[1].modulate[2] = 255 * color[2]; verts[1].modulate[3] = 255; VectorCopy (org, verts[2].xyz); verts[2].xyz[0] += sinR; verts[2].xyz[1] += cosR; verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255 * color[0]; verts[2].modulate[1] = 255 * color[1]; verts[2].modulate[2] = 255 * color[2]; verts[2].modulate[3] = 255; VectorCopy (org, verts[3].xyz); verts[3].xyz[0] += cosR; verts[3].xyz[1] -= sinR; verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255 * color[0]; verts[3].modulate[1] = 255 * color[1]; verts[3].modulate[2] = 255 * color[2]; verts[3].modulate[3] = 255; } else if (p->type == P_FLAT) { VectorCopy (org, verts[0].xyz); verts[0].xyz[0] -= p->height; verts[0].xyz[1] -= p->width; verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255; verts[0].modulate[1] = 255; verts[0].modulate[2] = 255; verts[0].modulate[3] = 255; VectorCopy (org, verts[1].xyz); verts[1].xyz[0] -= p->height; verts[1].xyz[1] += p->width; verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255; verts[1].modulate[1] = 255; verts[1].modulate[2] = 255; verts[1].modulate[3] = 255; VectorCopy (org, verts[2].xyz); verts[2].xyz[0] += p->height; verts[2].xyz[1] += p->width; verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255; verts[2].modulate[1] = 255; verts[2].modulate[2] = 255; verts[2].modulate[3] = 255; VectorCopy (org, verts[3].xyz); verts[3].xyz[0] += p->height; verts[3].xyz[1] -= p->width; verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255; verts[3].modulate[1] = 255; verts[3].modulate[2] = 255; verts[3].modulate[3] = 255; } // Ridah else if (p->type == P_ANIM) { vec3_t rr, ru; vec3_t rotate_ang; int i, j; time = cg.time - p->time; time2 = p->endtime - p->time; ratio = time / time2; if (ratio >= 1.0f) { ratio = 0.9999f; } width = p->width + ( ratio * ( p->endwidth - p->width) ); height = p->height + ( ratio * ( p->endheight - p->height) ); // if we are "inside" this sprite, don't draw if (Distance( cg.snap->ps.origin, org ) < width/1.5) { return; } i = p->shaderAnim; j = (int)floor(ratio * shaderAnimCounts[p->shaderAnim]); p->pshader = shaderAnims[i][j]; if (p->roll) { vectoangles( cg.refdef.viewaxis[0], rotate_ang ); rotate_ang[ROLL] += p->roll; AngleVectors ( rotate_ang, NULL, rr, ru); } if (p->roll) { VectorMA (org, -height, ru, point); VectorMA (point, -width, rr, point); } else { VectorMA (org, -height, pvup, point); VectorMA (point, -width, pvright, point); } VectorCopy (point, verts[0].xyz); verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255; verts[0].modulate[1] = 255; verts[0].modulate[2] = 255; verts[0].modulate[3] = 255; if (p->roll) { VectorMA (point, 2*height, ru, point); } else { VectorMA (point, 2*height, pvup, point); } VectorCopy (point, verts[1].xyz); verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255; verts[1].modulate[1] = 255; verts[1].modulate[2] = 255; verts[1].modulate[3] = 255; if (p->roll) { VectorMA (point, 2*width, rr, point); } else { VectorMA (point, 2*width, pvright, point); } VectorCopy (point, verts[2].xyz); verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255; verts[2].modulate[1] = 255; verts[2].modulate[2] = 255; verts[2].modulate[3] = 255; if (p->roll) { VectorMA (point, -2*height, ru, point); } else { VectorMA (point, -2*height, pvup, point); } VectorCopy (point, verts[3].xyz); verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255; verts[3].modulate[1] = 255; verts[3].modulate[2] = 255; verts[3].modulate[3] = 255; } // done. if (!p->pshader) { // (SA) temp commented out for DM // CG_Printf ("CG_AddParticleToScene type %d p->pshader == ZERO\n", p->type); return; } if (p->type == P_WEATHER || p->type == P_WEATHER_TURBULENT || p->type == P_WEATHER_FLURRY) trap_R_AddPolyToScene( p->pshader, 3, TRIverts ); else trap_R_AddPolyToScene( p->pshader, 4, verts ); } // Ridah, made this static so it doesn't interfere with other files static float roll = 0.0; /* =============== CG_AddParticles =============== */ void CG_AddParticles (void) { cparticle_t *p, *next; float alpha; float time, time2; vec3_t org; int color; cparticle_t *active, *tail; int type; vec3_t rotate_ang; if (!initparticles) CG_ClearParticles (); VectorCopy( cg.refdef.viewaxis[0], pvforward ); VectorCopy( cg.refdef.viewaxis[1], pvright ); VectorCopy( cg.refdef.viewaxis[2], pvup ); vectoangles( cg.refdef.viewaxis[0], rotate_ang ); roll += ((cg.time - oldtime) * 0.1) ; rotate_ang[ROLL] += (roll*0.9); AngleVectors ( rotate_ang, rforward, rright, rup); oldtime = cg.time; active = NULL; tail = NULL; for (p=active_particles ; p ; p=next) { next = p->next; time = (cg.time - p->time)*0.001; alpha = p->alpha + time*p->alphavel; if (alpha <= 0) { // faded out p->next = free_particles; free_particles = p; p->type = 0; p->color = 0; p->alpha = 0; continue; } if (p->type == P_SMOKE || p->type == P_ANIM || p->type == P_BLEED || p->type == P_SMOKE_IMPACT) { if (cg.time > p->endtime) { p->next = free_particles; free_particles = p; p->type = 0; p->color = 0; p->alpha = 0; continue; } } if (p->type == P_WEATHER_FLURRY) { if (cg.time > p->endtime) { p->next = free_particles; free_particles = p; p->type = 0; p->color = 0; p->alpha = 0; continue; } } if (p->type == P_FLAT_SCALEUP_FADE) { if (cg.time > p->endtime) { p->next = free_particles; free_particles = p; p->type = 0; p->color = 0; p->alpha = 0; continue; } } if ((p->type == P_BAT || p->type == P_SPRITE) && p->endtime < 0) { // temporary sprite CG_AddParticleToScene (p, p->org, alpha); p->next = free_particles; free_particles = p; p->type = 0; p->color = 0; p->alpha = 0; continue; } p->next = NULL; if (!tail) active = tail = p; else { tail->next = p; tail = p; } if (alpha > 1.0) alpha = 1; color = p->color; time2 = time*time; org[0] = p->org[0] + p->vel[0]*time + p->accel[0]*time2; org[1] = p->org[1] + p->vel[1]*time + p->accel[1]*time2; org[2] = p->org[2] + p->vel[2]*time + p->accel[2]*time2; type = p->type; CG_AddParticleToScene (p, org, alpha); } active_particles = active; } /* ====================== CG_AddParticles ====================== */ void CG_ParticleSnowFlurry (qhandle_t pshader, centity_t *cent) { cparticle_t *p; qboolean turb = qtrue; if (!pshader) CG_Printf ("CG_ParticleSnowFlurry pshader == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->color = 0; p->alpha = 0.90f; p->alphavel = 0; p->start = cent->currentState.origin2[0]; p->end = cent->currentState.origin2[1]; p->endtime = cg.time + cent->currentState.time; p->startfade = cg.time + cent->currentState.time2; p->pshader = pshader; if (rand()%100 > 90) { p->height = 32; p->width = 32; p->alpha = 0.10f; } else { p->height = 1; p->width = 1; } p->vel[2] = -20; p->type = P_WEATHER_FLURRY; if (turb) p->vel[2] = -10; VectorCopy(cent->currentState.origin, p->org); p->org[0] = p->org[0]; p->org[1] = p->org[1]; p->org[2] = p->org[2]; p->vel[0] = p->vel[1] = 0; p->accel[0] = p->accel[1] = p->accel[2] = 0; p->vel[0] += cent->currentState.angles[0] * 32 + (crandom() * 16); p->vel[1] += cent->currentState.angles[1] * 32 + (crandom() * 16); p->vel[2] += cent->currentState.angles[2]; if (turb) { p->accel[0] = crandom () * 16; p->accel[1] = crandom () * 16; } } void CG_ParticleSnow (qhandle_t pshader, vec3_t origin, vec3_t origin2, int turb, float range, int snum) { cparticle_t *p; if (!pshader) CG_Printf ("CG_ParticleSnow pshader == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->color = 0; p->alpha = 0.40f; p->alphavel = 0; p->start = origin[2]; p->end = origin2[2]; p->pshader = pshader; p->height = 1; p->width = 1; p->vel[2] = -50; if (turb) { p->type = P_WEATHER_TURBULENT; p->vel[2] = -50 * 1.3; } else { p->type = P_WEATHER; } VectorCopy(origin, p->org); p->org[0] = p->org[0] + ( crandom() * range); p->org[1] = p->org[1] + ( crandom() * range); p->org[2] = p->org[2] + ( crandom() * (p->start - p->end)); p->vel[0] = p->vel[1] = 0; p->accel[0] = p->accel[1] = p->accel[2] = 0; if (turb) { p->vel[0] = crandom() * 16; p->vel[1] = crandom() * 16; } // Rafael snow pvs check p->snum = snum; p->link = qtrue; } void CG_ParticleBubble (qhandle_t pshader, vec3_t origin, vec3_t origin2, int turb, float range, int snum) { cparticle_t *p; float randsize; if (!pshader) CG_Printf ("CG_ParticleSnow pshader == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->color = 0; p->alpha = 0.40f; p->alphavel = 0; p->start = origin[2]; p->end = origin2[2]; p->pshader = pshader; randsize = 1 + (crandom() * 0.5); p->height = randsize; p->width = randsize; p->vel[2] = 50 + ( crandom() * 10 ); if (turb) { p->type = P_BUBBLE_TURBULENT; p->vel[2] = 50 * 1.3; } else { p->type = P_BUBBLE; } VectorCopy(origin, p->org); p->org[0] = p->org[0] + ( crandom() * range); p->org[1] = p->org[1] + ( crandom() * range); p->org[2] = p->org[2] + ( crandom() * (p->start - p->end)); p->vel[0] = p->vel[1] = 0; p->accel[0] = p->accel[1] = p->accel[2] = 0; if (turb) { p->vel[0] = crandom() * 4; p->vel[1] = crandom() * 4; } // Rafael snow pvs check p->snum = snum; p->link = qtrue; } void CG_ParticleSmoke (qhandle_t pshader, centity_t *cent) { // using cent->density = enttime // cent->frame = startfade cparticle_t *p; if (!pshader) CG_Printf ("CG_ParticleSmoke == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->endtime = cg.time + cent->currentState.time; p->startfade = cg.time + cent->currentState.time2; p->color = 0; p->alpha = 1.0; p->alphavel = 0; p->start = cent->currentState.origin[2]; p->end = cent->currentState.origin2[2]; p->pshader = pshader; p->rotate = qfalse; p->height = 8; p->width = 8; p->endheight = 32; p->endwidth = 32; p->type = P_SMOKE; VectorCopy(cent->currentState.origin, p->org); p->vel[0] = p->vel[1] = 0; p->accel[0] = p->accel[1] = p->accel[2] = 0; p->vel[2] = 5; if (cent->currentState.frame == 1)// reverse gravity p->vel[2] *= -1; p->roll = 8 + (crandom() * 4); } void CG_ParticleBulletDebris (vec3_t org, vec3_t vel, int duration) { cparticle_t *p; if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->endtime = cg.time + duration; p->startfade = cg.time + duration/2; p->color = EMISIVEFADE; p->alpha = 1.0; p->alphavel = 0; p->height = 0.5; p->width = 0.5; p->endheight = 0.5; p->endwidth = 0.5; p->pshader = cgs.media.tracerShader; p->type = P_SMOKE; VectorCopy(org, p->org); p->vel[0] = vel[0]; p->vel[1] = vel[1]; p->vel[2] = vel[2]; p->accel[0] = p->accel[1] = p->accel[2] = 0; p->accel[2] = -60; p->vel[2] += -20; } /* ====================== CG_ParticleExplosion ====================== */ void CG_ParticleExplosion (char *animStr, vec3_t origin, vec3_t vel, int duration, int sizeStart, int sizeEnd) { cparticle_t *p; int anim; if (animStr < (char *)10) CG_Error( "CG_ParticleExplosion: animStr is probably an index rather than a string" ); // find the animation string for (anim=0; shaderAnimNames[anim]; anim++) { if (!Q_stricmp( animStr, shaderAnimNames[anim] )) break; } if (!shaderAnimNames[anim]) { CG_Error("CG_ParticleExplosion: unknown animation string: %s\n", animStr); return; } if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->alpha = 0.5; p->alphavel = 0; if (duration < 0) { duration *= -1; p->roll = 0; } else { p->roll = crandom()*179; } p->shaderAnim = anim; p->width = sizeStart; p->height = sizeStart*shaderAnimSTRatio[anim]; // for sprites that are stretch in either direction p->endheight = sizeEnd; p->endwidth = sizeEnd*shaderAnimSTRatio[anim]; p->endtime = cg.time + duration; p->type = P_ANIM; VectorCopy( origin, p->org ); VectorCopy( vel, p->vel ); VectorClear( p->accel ); } // Rafael Shrapnel void CG_AddParticleShrapnel (localEntity_t *le) { return; } // done. int CG_NewParticleArea (int num) { // const char *str; char *str; char *token; int type; vec3_t origin, origin2; int i; float range = 0; int turb; int numparticles; int snum; str = (char *) CG_ConfigString (num); if (!str[0]) return (0); // returns type 128 64 or 32 token = COM_Parse (&str); type = atoi (token); if (type == 1) range = 128; else if (type == 2) range = 64; else if (type == 3) range = 32; else if (type == 0) range = 256; else if (type == 4) range = 8; else if (type == 5) range = 16; else if (type == 6) range = 32; else if (type == 7) range = 64; for (i=0; i<3; i++) { token = COM_Parse (&str); origin[i] = atof (token); } for (i=0; i<3; i++) { token = COM_Parse (&str); origin2[i] = atof (token); } token = COM_Parse (&str); numparticles = atoi (token); token = COM_Parse (&str); turb = atoi (token); token = COM_Parse (&str); snum = atoi (token); for (i=0; i= 4) CG_ParticleBubble (cgs.media.waterBubbleShader, origin, origin2, turb, range, snum); else CG_ParticleSnow (cgs.media.waterBubbleShader, origin, origin2, turb, range, snum); } return (1); } void CG_SnowLink (centity_t *cent, qboolean particleOn) { cparticle_t *p, *next; int id; id = cent->currentState.frame; for (p=active_particles ; p ; p=next) { next = p->next; if (p->type == P_WEATHER || p->type == P_WEATHER_TURBULENT) { if (p->snum == id) { if (particleOn) p->link = qtrue; else p->link = qfalse; } } } } void CG_ParticleImpactSmokePuff (qhandle_t pshader, vec3_t origin) { cparticle_t *p; if (!pshader) CG_Printf ("CG_ParticleImpactSmokePuff pshader == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->alpha = 0.25; p->alphavel = 0; p->roll = crandom()*179; p->pshader = pshader; p->endtime = cg.time + 1000; p->startfade = cg.time + 100; p->width = rand()%4 + 8; p->height = rand()%4 + 8; p->endheight = p->height *2; p->endwidth = p->width * 2; p->endtime = cg.time + 500; p->type = P_SMOKE_IMPACT; VectorCopy( origin, p->org ); VectorSet(p->vel, 0, 0, 20); VectorSet(p->accel, 0, 0, 20); p->rotate = qtrue; } void CG_Particle_Bleed (qhandle_t pshader, vec3_t start, vec3_t dir, int fleshEntityNum, int duration) { cparticle_t *p; if (!pshader) CG_Printf ("CG_Particle_Bleed pshader == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->alpha = 1.0; p->alphavel = 0; p->roll = 0; p->pshader = pshader; p->endtime = cg.time + duration; if (fleshEntityNum) p->startfade = cg.time; else p->startfade = cg.time + 100; p->width = 4; p->height = 4; p->endheight = 4+rand()%3; p->endwidth = p->endheight; p->type = P_SMOKE; VectorCopy( start, p->org ); p->vel[0] = 0; p->vel[1] = 0; p->vel[2] = -20; VectorClear( p->accel ); p->rotate = qfalse; p->roll = rand()%179; p->color = BLOODRED; p->alpha = 0.75; } void CG_Particle_OilParticle (qhandle_t pshader, centity_t *cent) { cparticle_t *p; int time; int time2; float ratio; float duration = 1500; time = cg.time; time2 = cg.time + cent->currentState.time; ratio =(float)1 - ((float)time / (float)time2); if (!pshader) CG_Printf ("CG_Particle_OilParticle == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->alpha = 1.0; p->alphavel = 0; p->roll = 0; p->pshader = pshader; p->endtime = cg.time + duration; p->startfade = p->endtime; p->width = 1; p->height = 3; p->endheight = 3; p->endwidth = 1; p->type = P_SMOKE; VectorCopy(cent->currentState.origin, p->org ); p->vel[0] = (cent->currentState.origin2[0] * (16 * ratio)); p->vel[1] = (cent->currentState.origin2[1] * (16 * ratio)); p->vel[2] = (cent->currentState.origin2[2]); p->snum = 1.0f; VectorClear( p->accel ); p->accel[2] = -20; p->rotate = qfalse; p->roll = rand()%179; p->alpha = 0.75; } void CG_Particle_OilSlick (qhandle_t pshader, centity_t *cent) { cparticle_t *p; if (!pshader) CG_Printf ("CG_Particle_OilSlick == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; if (cent->currentState.angles2[2]) p->endtime = cg.time + cent->currentState.angles2[2]; else p->endtime = cg.time + 60000; p->startfade = p->endtime; p->alpha = 1.0; p->alphavel = 0; p->roll = 0; p->pshader = pshader; if (cent->currentState.angles2[0] || cent->currentState.angles2[1]) { p->width = cent->currentState.angles2[0]; p->height = cent->currentState.angles2[0]; p->endheight = cent->currentState.angles2[1]; p->endwidth = cent->currentState.angles2[1]; } else { p->width = 8; p->height = 8; p->endheight = 16; p->endwidth = 16; } p->type = P_FLAT_SCALEUP; p->snum = 1.0; VectorCopy(cent->currentState.origin, p->org ); p->org[2]+= 0.55 + (crandom() * 0.5); p->vel[0] = 0; p->vel[1] = 0; p->vel[2] = 0; VectorClear( p->accel ); p->rotate = qfalse; p->roll = rand()%179; p->alpha = 0.75; } void CG_OilSlickRemove (centity_t *cent) { cparticle_t *p, *next; int id; id = 1.0f; if (!id) CG_Printf ("CG_OilSlickRevove NULL id\n"); for (p=active_particles ; p ; p=next) { next = p->next; if (p->type == P_FLAT_SCALEUP) { if (p->snum == id) { p->endtime = cg.time + 100; p->startfade = p->endtime; p->type = P_FLAT_SCALEUP_FADE; } } } } qboolean ValidBloodPool (vec3_t start) { #define EXTRUDE_DIST 0.5 vec3_t angles; vec3_t right, up; vec3_t this_pos, x_pos, center_pos, end_pos; float x, y; float fwidth, fheight; trace_t trace; vec3_t normal; fwidth = 16; fheight = 16; VectorSet (normal, 0, 0, 1); vectoangles (normal, angles); AngleVectors (angles, NULL, right, up); VectorMA (start, EXTRUDE_DIST, normal, center_pos); for (x= -fwidth/2; xendpos, start); legit = ValidBloodPool (start); if (!legit) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->endtime = cg.time + 3000; p->startfade = p->endtime; p->alpha = 1.0; p->alphavel = 0; p->roll = 0; p->pshader = pshader; rndSize = 0.4 + random()*0.6; p->width = 8*rndSize; p->height = 8*rndSize; p->endheight = 16*rndSize; p->endwidth = 16*rndSize; p->type = P_FLAT_SCALEUP; VectorCopy(start, p->org ); p->vel[0] = 0; p->vel[1] = 0; p->vel[2] = 0; VectorClear( p->accel ); p->rotate = qfalse; p->roll = rand()%179; p->alpha = 0.75; p->color = BLOODRED; } #define NORMALSIZE 16 #define LARGESIZE 32 void CG_ParticleBloodCloud (centity_t *cent, vec3_t origin, vec3_t dir) { float length; float dist; float crittersize; vec3_t angles, forward; vec3_t point; cparticle_t *p; int i; dist = 0; length = VectorLength (dir); vectoangles (dir, angles); AngleVectors (angles, forward, NULL, NULL); crittersize = LARGESIZE; if (length) dist = length / crittersize; if (dist < 1) dist = 1; VectorCopy (origin, point); for (i=0; inext; p->next = active_particles; active_particles = p; p->time = cg.time; p->alpha = 1.0; p->alphavel = 0; p->roll = 0; p->pshader = cgs.media.smokePuffShader; p->endtime = cg.time + 350 + (crandom() * 100); p->startfade = cg.time; p->width = LARGESIZE; p->height = LARGESIZE; p->endheight = LARGESIZE; p->endwidth = LARGESIZE; p->type = P_SMOKE; VectorCopy( origin, p->org ); p->vel[0] = 0; p->vel[1] = 0; p->vel[2] = -1; VectorClear( p->accel ); p->rotate = qfalse; p->roll = rand()%179; p->color = BLOODRED; p->alpha = 0.75; } } void CG_ParticleSparks (vec3_t org, vec3_t vel, int duration, float x, float y, float speed) { cparticle_t *p; if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->endtime = cg.time + duration; p->startfade = cg.time + duration/2; p->color = EMISIVEFADE; p->alpha = 0.4f; p->alphavel = 0; p->height = 0.5; p->width = 0.5; p->endheight = 0.5; p->endwidth = 0.5; p->pshader = cgs.media.tracerShader; p->type = P_SMOKE; VectorCopy(org, p->org); p->org[0] += (crandom() * x); p->org[1] += (crandom() * y); p->vel[0] = vel[0]; p->vel[1] = vel[1]; p->vel[2] = vel[2]; p->accel[0] = p->accel[1] = p->accel[2] = 0; p->vel[0] += (crandom() * 4); p->vel[1] += (crandom() * 4); p->vel[2] += (20 + (crandom() * 10)) * speed; p->accel[0] = crandom () * 4; p->accel[1] = crandom () * 4; } void CG_ParticleDust (centity_t *cent, vec3_t origin, vec3_t dir) { float length; float dist; float crittersize; vec3_t angles, forward; vec3_t point; cparticle_t *p; int i; dist = 0; VectorNegate (dir, dir); length = VectorLength (dir); vectoangles (dir, angles); AngleVectors (angles, forward, NULL, NULL); crittersize = LARGESIZE; if (length) dist = length / crittersize; if (dist < 1) dist = 1; VectorCopy (origin, point); for (i=0; inext; p->next = active_particles; active_particles = p; p->time = cg.time; p->alpha = 5.0; p->alphavel = 0; p->roll = 0; p->pshader = cgs.media.smokePuffShader; // RF, stay around for long enough to expand and dissipate naturally if (length) p->endtime = cg.time + 4500 + (crandom() * 3500); else p->endtime = cg.time + 750 + (crandom() * 500); p->startfade = cg.time; p->width = LARGESIZE; p->height = LARGESIZE; // RF, expand while falling p->endheight = LARGESIZE*3.0; p->endwidth = LARGESIZE*3.0; if (!length) { p->width *= 0.2f; p->height *= 0.2f; p->endheight = NORMALSIZE; p->endwidth = NORMALSIZE; } p->type = P_SMOKE; VectorCopy( point, p->org ); p->vel[0] = crandom()*6; p->vel[1] = crandom()*6; p->vel[2] = random()*20; // RF, add some gravity/randomness p->accel[0] = crandom()*3; p->accel[1] = crandom()*3; p->accel[2] = -PARTICLE_GRAVITY*0.4; VectorClear( p->accel ); p->rotate = qfalse; p->roll = rand()%179; p->alpha = 0.75; } } void CG_ParticleMisc (qhandle_t pshader, vec3_t origin, int size, int duration, float alpha) { cparticle_t *p; if (!pshader) CG_Printf ("CG_ParticleImpactSmokePuff pshader == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->alpha = 1.0; p->alphavel = 0; p->roll = rand()%179; p->pshader = pshader; if (duration > 0) p->endtime = cg.time + duration; else p->endtime = duration; p->startfade = cg.time; p->width = size; p->height = size; p->endheight = size; p->endwidth = size; p->type = P_SPRITE; VectorCopy( origin, p->org ); p->rotate = qfalse; } ================================================ FILE: src/cgame/cg_particles.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // Rafael particles // cg_particles.c #include "cg_local.h" #define BLOODRED 2 #define EMISIVEFADE 3 #define GREY75 4 typedef struct particle_s { struct particle_s *next; float time; float endtime; vec3_t org; vec3_t vel; vec3_t accel; int color; float colorvel; float alpha; float alphavel; int type; qhandle_t pshader; float height; float width; float endheight; float endwidth; float start; float end; float startfade; qboolean rotate; int snum; qboolean link; // Ridah int shaderAnim; int roll; int accumroll; } cparticle_t; typedef enum { P_NONE, P_WEATHER, P_FLAT, P_SMOKE, P_ROTATE, P_WEATHER_TURBULENT, P_ANIM, // Ridah P_BAT, P_BLEED, P_FLAT_SCALEUP, P_FLAT_SCALEUP_FADE, P_WEATHER_FLURRY, P_SMOKE_IMPACT, P_BUBBLE, P_BUBBLE_TURBULENT, P_SPRITE } particle_type_t; #define MAX_SHADER_ANIMS 32 #define MAX_SHADER_ANIM_FRAMES 64 static char *shaderAnimNames[MAX_SHADER_ANIMS] = { "explode1", "blacksmokeanim", "twiltb2", "expblue", "blacksmokeanimb", // uses 'explode1' sequence "blood", NULL }; static qhandle_t shaderAnims[MAX_SHADER_ANIMS][MAX_SHADER_ANIM_FRAMES]; static int shaderAnimCounts[MAX_SHADER_ANIMS] = { 23, 25, 45, 25, 23, 5, }; static float shaderAnimSTRatio[MAX_SHADER_ANIMS] = { 1.405f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, }; static int numShaderAnims; // done. #define PARTICLE_GRAVITY 40 #define MAX_PARTICLES 1024 * 8 cparticle_t *active_particles, *free_particles; cparticle_t particles[MAX_PARTICLES]; int cl_numparticles = MAX_PARTICLES; qboolean initparticles = qfalse; vec3_t vforward, vright, vup; vec3_t rforward, rright, rup; float oldtime; /* =============== CL_ClearParticles =============== */ void CG_ClearParticles (void) { int i; memset( particles, 0, sizeof(particles) ); free_particles = &particles[0]; active_particles = NULL; for (i=0 ;itype == P_WEATHER || p->type == P_WEATHER_TURBULENT || p->type == P_WEATHER_FLURRY || p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT) {// create a front facing polygon if (p->type != P_WEATHER_FLURRY) { if (p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT) { if (org[2] > p->end) { p->time = cg.time; VectorCopy (org, p->org); // Ridah, fixes rare snow flakes that flicker on the ground p->org[2] = ( p->start + crandom () * 4 ); if (p->type == P_BUBBLE_TURBULENT) { p->vel[0] = crandom() * 4; p->vel[1] = crandom() * 4; } } } else { if (org[2] < p->end) { p->time = cg.time; VectorCopy (org, p->org); // Ridah, fixes rare snow flakes that flicker on the ground while (p->org[2] < p->end) { p->org[2] += (p->start - p->end); } if (p->type == P_WEATHER_TURBULENT) { p->vel[0] = crandom() * 16; p->vel[1] = crandom() * 16; } } } // Rafael snow pvs check if (!p->link) return; p->alpha = 1; } // Ridah, had to do this or MAX_POLYS is being exceeded in village1.bsp if (Distance( cg.snap->ps.origin, org ) > 1024) { return; } // done. if (p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT) { VectorMA (org, -p->height, vup, point); VectorMA (point, -p->width, vright, point); VectorCopy (point, verts[0].xyz); verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255; verts[0].modulate[1] = 255; verts[0].modulate[2] = 255; verts[0].modulate[3] = 255 * p->alpha; VectorMA (org, -p->height, vup, point); VectorMA (point, p->width, vright, point); VectorCopy (point, verts[1].xyz); verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255; verts[1].modulate[1] = 255; verts[1].modulate[2] = 255; verts[1].modulate[3] = 255 * p->alpha; VectorMA (org, p->height, vup, point); VectorMA (point, p->width, vright, point); VectorCopy (point, verts[2].xyz); verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255; verts[2].modulate[1] = 255; verts[2].modulate[2] = 255; verts[2].modulate[3] = 255 * p->alpha; VectorMA (org, p->height, vup, point); VectorMA (point, -p->width, vright, point); VectorCopy (point, verts[3].xyz); verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255; verts[3].modulate[1] = 255; verts[3].modulate[2] = 255; verts[3].modulate[3] = 255 * p->alpha; } else { VectorMA (org, -p->height, vup, point); VectorMA (point, -p->width, vright, point); VectorCopy( point, TRIverts[0].xyz ); TRIverts[0].st[0] = 1; TRIverts[0].st[1] = 0; TRIverts[0].modulate[0] = 255; TRIverts[0].modulate[1] = 255; TRIverts[0].modulate[2] = 255; TRIverts[0].modulate[3] = 255 * p->alpha; VectorMA (org, p->height, vup, point); VectorMA (point, -p->width, vright, point); VectorCopy (point, TRIverts[1].xyz); TRIverts[1].st[0] = 0; TRIverts[1].st[1] = 0; TRIverts[1].modulate[0] = 255; TRIverts[1].modulate[1] = 255; TRIverts[1].modulate[2] = 255; TRIverts[1].modulate[3] = 255 * p->alpha; VectorMA (org, p->height, vup, point); VectorMA (point, p->width, vright, point); VectorCopy (point, TRIverts[2].xyz); TRIverts[2].st[0] = 0; TRIverts[2].st[1] = 1; TRIverts[2].modulate[0] = 255; TRIverts[2].modulate[1] = 255; TRIverts[2].modulate[2] = 255; TRIverts[2].modulate[3] = 255 * p->alpha; } } else if (p->type == P_SPRITE) { vec3_t rr, ru; vec3_t rotate_ang; VectorSet (color, 1.0, 1.0, 1.0); time = cg.time - p->time; time2 = p->endtime - p->time; ratio = time / time2; width = p->width + ( ratio * ( p->endwidth - p->width) ); height = p->height + ( ratio * ( p->endheight - p->height) ); if (p->roll) { vectoangles( cg.refdef.viewaxis[0], rotate_ang ); rotate_ang[ROLL] += p->roll; AngleVectors ( rotate_ang, NULL, rr, ru); } if (p->roll) { VectorMA (org, -height, ru, point); VectorMA (point, -width, rr, point); } else { VectorMA (org, -height, vup, point); VectorMA (point, -width, vright, point); } VectorCopy (point, verts[0].xyz); verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255; verts[0].modulate[1] = 255; verts[0].modulate[2] = 255; verts[0].modulate[3] = 255; if (p->roll) { VectorMA (point, 2*height, ru, point); } else { VectorMA (point, 2*height, vup, point); } VectorCopy (point, verts[1].xyz); verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255; verts[1].modulate[1] = 255; verts[1].modulate[2] = 255; verts[1].modulate[3] = 255; if (p->roll) { VectorMA (point, 2*width, rr, point); } else { VectorMA (point, 2*width, vright, point); } VectorCopy (point, verts[2].xyz); verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255; verts[2].modulate[1] = 255; verts[2].modulate[2] = 255; verts[2].modulate[3] = 255; if (p->roll) { VectorMA (point, -2*height, ru, point); } else { VectorMA (point, -2*height, vup, point); } VectorCopy (point, verts[3].xyz); verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255; verts[3].modulate[1] = 255; verts[3].modulate[2] = 255; verts[3].modulate[3] = 255; } else if (p->type == P_SMOKE || p->type == P_SMOKE_IMPACT) {// create a front rotating facing polygon if ( p->type == P_SMOKE_IMPACT && Distance( cg.snap->ps.origin, org ) > 1024) { return; } if (p->color == BLOODRED) VectorSet (color, 0.22f, 0.0f, 0.0f); else if (p->color == GREY75) { float len; float greyit; float val; len = Distance (cg.snap->ps.origin, org); if (!len) len = 1; val = 4096/len; greyit = 0.25 * val; if (greyit > 0.5) greyit = 0.5; VectorSet (color, greyit, greyit, greyit); } else VectorSet (color, 1.0, 1.0, 1.0); time = cg.time - p->time; time2 = p->endtime - p->time; ratio = time / time2; if (cg.time > p->startfade) { invratio = 1 - ( (cg.time - p->startfade) / (p->endtime - p->startfade) ); if (p->color == EMISIVEFADE) { float fval; fval = (invratio * invratio); if (fval < 0) fval = 0; VectorSet (color, fval , fval , fval ); } invratio *= p->alpha; } else invratio = 1 * p->alpha; if ( cgs.glconfig.hardwareType == GLHW_RAGEPRO ) invratio = 1; if (invratio > 1) invratio = 1; width = p->width + ( ratio * ( p->endwidth - p->width) ); height = p->height + ( ratio * ( p->endheight - p->height) ); if (p->type != P_SMOKE_IMPACT) { vec3_t temp; vectoangles (rforward, temp); p->accumroll += p->roll; temp[ROLL] += p->accumroll * 0.1; AngleVectors ( temp, NULL, rright2, rup2); } else { VectorCopy (rright, rright2); VectorCopy (rup, rup2); } if (p->rotate) { VectorMA (org, -height, rup2, point); VectorMA (point, -width, rright2, point); } else { VectorMA (org, -p->height, vup, point); VectorMA (point, -p->width, vright, point); } VectorCopy (point, verts[0].xyz); verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255 * color[0]; verts[0].modulate[1] = 255 * color[1]; verts[0].modulate[2] = 255 * color[2]; verts[0].modulate[3] = 255 * invratio; if (p->rotate) { VectorMA (org, -height, rup2, point); VectorMA (point, width, rright2, point); } else { VectorMA (org, -p->height, vup, point); VectorMA (point, p->width, vright, point); } VectorCopy (point, verts[1].xyz); verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255 * color[0]; verts[1].modulate[1] = 255 * color[1]; verts[1].modulate[2] = 255 * color[2]; verts[1].modulate[3] = 255 * invratio; if (p->rotate) { VectorMA (org, height, rup2, point); VectorMA (point, width, rright2, point); } else { VectorMA (org, p->height, vup, point); VectorMA (point, p->width, vright, point); } VectorCopy (point, verts[2].xyz); verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255 * color[0]; verts[2].modulate[1] = 255 * color[1]; verts[2].modulate[2] = 255 * color[2]; verts[2].modulate[3] = 255 * invratio; if (p->rotate) { VectorMA (org, height, rup2, point); VectorMA (point, -width, rright2, point); } else { VectorMA (org, p->height, vup, point); VectorMA (point, -p->width, vright, point); } VectorCopy (point, verts[3].xyz); verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255 * color[0]; verts[3].modulate[1] = 255 * color[1]; verts[3].modulate[2] = 255 * color[2]; verts[3].modulate[3] = 255 * invratio; } else if (p->type == P_BLEED) { vec3_t rr, ru; vec3_t rotate_ang; float alpha; alpha = p->alpha; if ( cgs.glconfig.hardwareType == GLHW_RAGEPRO ) alpha = 1; if (p->roll) { vectoangles( cg.refdef.viewaxis[0], rotate_ang ); rotate_ang[ROLL] += p->roll; AngleVectors ( rotate_ang, NULL, rr, ru); } else { VectorCopy (vup, ru); VectorCopy (vright, rr); } VectorMA (org, -p->height, ru, point); VectorMA (point, -p->width, rr, point); VectorCopy (point, verts[0].xyz); verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 111; verts[0].modulate[1] = 19; verts[0].modulate[2] = 9; verts[0].modulate[3] = 255 * alpha; VectorMA (org, -p->height, ru, point); VectorMA (point, p->width, rr, point); VectorCopy (point, verts[1].xyz); verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 111; verts[1].modulate[1] = 19; verts[1].modulate[2] = 9; verts[1].modulate[3] = 255 * alpha; VectorMA (org, p->height, ru, point); VectorMA (point, p->width, rr, point); VectorCopy (point, verts[2].xyz); verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 111; verts[2].modulate[1] = 19; verts[2].modulate[2] = 9; verts[2].modulate[3] = 255 * alpha; VectorMA (org, p->height, ru, point); VectorMA (point, -p->width, rr, point); VectorCopy (point, verts[3].xyz); verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 111; verts[3].modulate[1] = 19; verts[3].modulate[2] = 9; verts[3].modulate[3] = 255 * alpha; } else if (p->type == P_FLAT_SCALEUP) { float width, height; float sinR, cosR; if (p->color == BLOODRED) VectorSet (color, 1, 1, 1); else VectorSet (color, 0.5, 0.5, 0.5); time = cg.time - p->time; time2 = p->endtime - p->time; ratio = time / time2; width = p->width + ( ratio * ( p->endwidth - p->width) ); height = p->height + ( ratio * ( p->endheight - p->height) ); if (width > p->endwidth) width = p->endwidth; if (height > p->endheight) height = p->endheight; sinR = height * sin(DEG2RAD(p->roll)) * sqrt(2); cosR = width * cos(DEG2RAD(p->roll)) * sqrt(2); VectorCopy (org, verts[0].xyz); verts[0].xyz[0] -= sinR; verts[0].xyz[1] -= cosR; verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255 * color[0]; verts[0].modulate[1] = 255 * color[1]; verts[0].modulate[2] = 255 * color[2]; verts[0].modulate[3] = 255; VectorCopy (org, verts[1].xyz); verts[1].xyz[0] -= cosR; verts[1].xyz[1] += sinR; verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255 * color[0]; verts[1].modulate[1] = 255 * color[1]; verts[1].modulate[2] = 255 * color[2]; verts[1].modulate[3] = 255; VectorCopy (org, verts[2].xyz); verts[2].xyz[0] += sinR; verts[2].xyz[1] += cosR; verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255 * color[0]; verts[2].modulate[1] = 255 * color[1]; verts[2].modulate[2] = 255 * color[2]; verts[2].modulate[3] = 255; VectorCopy (org, verts[3].xyz); verts[3].xyz[0] += cosR; verts[3].xyz[1] -= sinR; verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255 * color[0]; verts[3].modulate[1] = 255 * color[1]; verts[3].modulate[2] = 255 * color[2]; verts[3].modulate[3] = 255; } else if (p->type == P_FLAT) { VectorCopy (org, verts[0].xyz); verts[0].xyz[0] -= p->height; verts[0].xyz[1] -= p->width; verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255; verts[0].modulate[1] = 255; verts[0].modulate[2] = 255; verts[0].modulate[3] = 255; VectorCopy (org, verts[1].xyz); verts[1].xyz[0] -= p->height; verts[1].xyz[1] += p->width; verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255; verts[1].modulate[1] = 255; verts[1].modulate[2] = 255; verts[1].modulate[3] = 255; VectorCopy (org, verts[2].xyz); verts[2].xyz[0] += p->height; verts[2].xyz[1] += p->width; verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255; verts[2].modulate[1] = 255; verts[2].modulate[2] = 255; verts[2].modulate[3] = 255; VectorCopy (org, verts[3].xyz); verts[3].xyz[0] += p->height; verts[3].xyz[1] -= p->width; verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255; verts[3].modulate[1] = 255; verts[3].modulate[2] = 255; verts[3].modulate[3] = 255; } // Ridah else if (p->type == P_ANIM) { vec3_t rr, ru; vec3_t rotate_ang; int i, j; time = cg.time - p->time; time2 = p->endtime - p->time; ratio = time / time2; if (ratio >= 1.0f) { ratio = 0.9999f; } width = p->width + ( ratio * ( p->endwidth - p->width) ); height = p->height + ( ratio * ( p->endheight - p->height) ); // if we are "inside" this sprite, don't draw if (Distance( cg.snap->ps.origin, org ) < width/1.5) { return; } i = p->shaderAnim; j = (int)floor(ratio * shaderAnimCounts[p->shaderAnim]); p->pshader = shaderAnims[i][j]; if (p->roll) { vectoangles( cg.refdef.viewaxis[0], rotate_ang ); rotate_ang[ROLL] += p->roll; AngleVectors ( rotate_ang, NULL, rr, ru); } if (p->roll) { VectorMA (org, -height, ru, point); VectorMA (point, -width, rr, point); } else { VectorMA (org, -height, vup, point); VectorMA (point, -width, vright, point); } VectorCopy (point, verts[0].xyz); verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255; verts[0].modulate[1] = 255; verts[0].modulate[2] = 255; verts[0].modulate[3] = 255; if (p->roll) { VectorMA (point, 2*height, ru, point); } else { VectorMA (point, 2*height, vup, point); } VectorCopy (point, verts[1].xyz); verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255; verts[1].modulate[1] = 255; verts[1].modulate[2] = 255; verts[1].modulate[3] = 255; if (p->roll) { VectorMA (point, 2*width, rr, point); } else { VectorMA (point, 2*width, vright, point); } VectorCopy (point, verts[2].xyz); verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255; verts[2].modulate[1] = 255; verts[2].modulate[2] = 255; verts[2].modulate[3] = 255; if (p->roll) { VectorMA (point, -2*height, ru, point); } else { VectorMA (point, -2*height, vup, point); } VectorCopy (point, verts[3].xyz); verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255; verts[3].modulate[1] = 255; verts[3].modulate[2] = 255; verts[3].modulate[3] = 255; } // done. if (!p->pshader) { // (SA) temp commented out for DM // CG_Printf ("CG_AddParticleToScene type %d p->pshader == ZERO\n", p->type); return; } if (p->type == P_WEATHER || p->type == P_WEATHER_TURBULENT || p->type == P_WEATHER_FLURRY) trap_R_AddPolyToScene( p->pshader, 3, TRIverts ); else trap_R_AddPolyToScene( p->pshader, 4, verts ); } // Ridah, made this static so it doesn't interfere with other files static float roll = 0.0; /* =============== CG_AddParticles =============== */ void CG_AddParticles (void) { cparticle_t *p, *next; float alpha; float time, time2; vec3_t org; int color; cparticle_t *active, *tail; int type; vec3_t rotate_ang; if (!initparticles) CG_ClearParticles (); VectorCopy( cg.refdef.viewaxis[0], vforward ); VectorCopy( cg.refdef.viewaxis[1], vright ); VectorCopy( cg.refdef.viewaxis[2], vup ); vectoangles( cg.refdef.viewaxis[0], rotate_ang ); roll += ((cg.time - oldtime) * 0.1) ; rotate_ang[ROLL] += (roll*0.9); AngleVectors ( rotate_ang, rforward, rright, rup); oldtime = cg.time; active = NULL; tail = NULL; for (p=active_particles ; p ; p=next) { next = p->next; time = (cg.time - p->time)*0.001; alpha = p->alpha + time*p->alphavel; if (alpha <= 0) { // faded out p->next = free_particles; free_particles = p; p->type = 0; p->color = 0; p->alpha = 0; continue; } if (p->type == P_SMOKE || p->type == P_ANIM || p->type == P_BLEED || p->type == P_SMOKE_IMPACT) { if (cg.time > p->endtime) { p->next = free_particles; free_particles = p; p->type = 0; p->color = 0; p->alpha = 0; continue; } } if (p->type == P_WEATHER_FLURRY) { if (cg.time > p->endtime) { p->next = free_particles; free_particles = p; p->type = 0; p->color = 0; p->alpha = 0; continue; } } if (p->type == P_FLAT_SCALEUP_FADE) { if (cg.time > p->endtime) { p->next = free_particles; free_particles = p; p->type = 0; p->color = 0; p->alpha = 0; continue; } } if ((p->type == P_BAT || p->type == P_SPRITE) && p->endtime < 0) { // temporary sprite CG_AddParticleToScene (p, p->org, alpha); p->next = free_particles; free_particles = p; p->type = 0; p->color = 0; p->alpha = 0; continue; } p->next = NULL; if (!tail) active = tail = p; else { tail->next = p; tail = p; } if (alpha > 1.0) alpha = 1; color = p->color; time2 = time*time; org[0] = p->org[0] + p->vel[0]*time + p->accel[0]*time2; org[1] = p->org[1] + p->vel[1]*time + p->accel[1]*time2; org[2] = p->org[2] + p->vel[2]*time + p->accel[2]*time2; type = p->type; CG_AddParticleToScene (p, org, alpha); } active_particles = active; } /* ====================== CG_AddParticles ====================== */ void CG_ParticleSnowFlurry (qhandle_t pshader, centity_t *cent) { cparticle_t *p; qboolean turb = qtrue; if (!pshader) CG_Printf ("CG_ParticleSnowFlurry pshader == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->color = 0; p->alpha = 0.90f; p->alphavel = 0; p->start = cent->currentState.origin2[0]; p->end = cent->currentState.origin2[1]; p->endtime = cg.time + cent->currentState.time; p->startfade = cg.time + cent->currentState.time2; p->pshader = pshader; if (rand()%100 > 90) { p->height = 32; p->width = 32; p->alpha = 0.10f; } else { p->height = 1; p->width = 1; } p->vel[2] = -20; p->type = P_WEATHER_FLURRY; if (turb) p->vel[2] = -10; VectorCopy(cent->currentState.origin, p->org); p->org[0] = p->org[0]; p->org[1] = p->org[1]; p->org[2] = p->org[2]; p->vel[0] = p->vel[1] = 0; p->accel[0] = p->accel[1] = p->accel[2] = 0; p->vel[0] += cent->currentState.angles[0] * 32 + (crandom() * 16); p->vel[1] += cent->currentState.angles[1] * 32 + (crandom() * 16); p->vel[2] += cent->currentState.angles[2]; if (turb) { p->accel[0] = crandom () * 16; p->accel[1] = crandom () * 16; } } void CG_ParticleSnow (qhandle_t pshader, vec3_t origin, vec3_t origin2, int turb, float range, int snum) { cparticle_t *p; if (!pshader) CG_Printf ("CG_ParticleSnow pshader == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->color = 0; p->alpha = 0.40f; p->alphavel = 0; p->start = origin[2]; p->end = origin2[2]; p->pshader = pshader; p->height = 1; p->width = 1; p->vel[2] = -50; if (turb) { p->type = P_WEATHER_TURBULENT; p->vel[2] = -50 * 1.3; } else { p->type = P_WEATHER; } VectorCopy(origin, p->org); p->org[0] = p->org[0] + ( crandom() * range); p->org[1] = p->org[1] + ( crandom() * range); p->org[2] = p->org[2] + ( crandom() * (p->start - p->end)); p->vel[0] = p->vel[1] = 0; p->accel[0] = p->accel[1] = p->accel[2] = 0; if (turb) { p->vel[0] = crandom() * 16; p->vel[1] = crandom() * 16; } // Rafael snow pvs check p->snum = snum; p->link = qtrue; } void CG_ParticleBubble (qhandle_t pshader, vec3_t origin, vec3_t origin2, int turb, float range, int snum) { cparticle_t *p; float randsize; if (!pshader) CG_Printf ("CG_ParticleSnow pshader == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->color = 0; p->alpha = 0.40f; p->alphavel = 0; p->start = origin[2]; p->end = origin2[2]; p->pshader = pshader; randsize = 1 + (crandom() * 0.5); p->height = randsize; p->width = randsize; p->vel[2] = 50 + ( crandom() * 10 ); if (turb) { p->type = P_BUBBLE_TURBULENT; p->vel[2] = 50 * 1.3; } else { p->type = P_BUBBLE; } VectorCopy(origin, p->org); p->org[0] = p->org[0] + ( crandom() * range); p->org[1] = p->org[1] + ( crandom() * range); p->org[2] = p->org[2] + ( crandom() * (p->start - p->end)); p->vel[0] = p->vel[1] = 0; p->accel[0] = p->accel[1] = p->accel[2] = 0; if (turb) { p->vel[0] = crandom() * 4; p->vel[1] = crandom() * 4; } // Rafael snow pvs check p->snum = snum; p->link = qtrue; } void CG_ParticleSmoke (qhandle_t pshader, centity_t *cent) { // using cent->density = enttime // cent->frame = startfade cparticle_t *p; if (!pshader) CG_Printf ("CG_ParticleSmoke == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->endtime = cg.time + cent->currentState.time; p->startfade = cg.time + cent->currentState.time2; p->color = 0; p->alpha = 1.0; p->alphavel = 0; p->start = cent->currentState.origin[2]; p->end = cent->currentState.origin2[2]; p->pshader = pshader; p->rotate = qfalse; p->height = 8; p->width = 8; p->endheight = 32; p->endwidth = 32; p->type = P_SMOKE; VectorCopy(cent->currentState.origin, p->org); p->vel[0] = p->vel[1] = 0; p->accel[0] = p->accel[1] = p->accel[2] = 0; p->vel[2] = 5; if (cent->currentState.frame == 1)// reverse gravity p->vel[2] *= -1; p->roll = 8 + (crandom() * 4); } void CG_ParticleBulletDebris (vec3_t org, vec3_t vel, int duration) { cparticle_t *p; if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->endtime = cg.time + duration; p->startfade = cg.time + duration/2; p->color = EMISIVEFADE; p->alpha = 1.0; p->alphavel = 0; p->height = 0.5; p->width = 0.5; p->endheight = 0.5; p->endwidth = 0.5; p->pshader = cgs.media.tracerShader; p->type = P_SMOKE; VectorCopy(org, p->org); p->vel[0] = vel[0]; p->vel[1] = vel[1]; p->vel[2] = vel[2]; p->accel[0] = p->accel[1] = p->accel[2] = 0; p->accel[2] = -60; p->vel[2] += -20; } /* ====================== CG_ParticleExplosion ====================== */ void CG_ParticleExplosion (char *animStr, vec3_t origin, vec3_t vel, int duration, int sizeStart, int sizeEnd) { cparticle_t *p; int anim; if (animStr < (char *)10) CG_Error( "CG_ParticleExplosion: animStr is probably an index rather than a string" ); // find the animation string for (anim=0; shaderAnimNames[anim]; anim++) { if (!stricmp( animStr, shaderAnimNames[anim] )) break; } if (!shaderAnimNames[anim]) { CG_Error("CG_ParticleExplosion: unknown animation string: %s\n", animStr); return; } if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->alpha = 1.0; p->alphavel = 0; if (duration < 0) { duration *= -1; p->roll = 0; } else { p->roll = crandom()*179; } p->shaderAnim = anim; p->width = sizeStart; p->height = sizeStart*shaderAnimSTRatio[anim]; // for sprites that are stretch in either direction p->endheight = sizeEnd; p->endwidth = sizeEnd*shaderAnimSTRatio[anim]; p->endtime = cg.time + duration; p->type = P_ANIM; VectorCopy( origin, p->org ); VectorCopy( vel, p->vel ); VectorClear( p->accel ); } // Rafael Shrapnel void CG_AddParticleShrapnel (localEntity_t *le) { return; } // done. int CG_NewParticleArea (int num) { // const char *str; char *str; char *token; int type; vec3_t origin, origin2; int i; float range = 0; int turb; int numparticles; int snum; str = (char *) CG_ConfigString (num); if (!str[0]) return (0); // returns type 128 64 or 32 token = COM_Parse (&str); type = atoi (token); if (type == 1) range = 128; else if (type == 2) range = 64; else if (type == 3) range = 32; else if (type == 0) range = 256; else if (type == 4) range = 8; else if (type == 5) range = 16; else if (type == 6) range = 32; else if (type == 7) range = 64; for (i=0; i<3; i++) { token = COM_Parse (&str); origin[i] = atof (token); } for (i=0; i<3; i++) { token = COM_Parse (&str); origin2[i] = atof (token); } token = COM_Parse (&str); numparticles = atoi (token); token = COM_Parse (&str); turb = atoi (token); token = COM_Parse (&str); snum = atoi (token); for (i=0; i= 4) CG_ParticleBubble (cgs.media.waterBubbleShader, origin, origin2, turb, range, snum); else CG_ParticleSnow (cgs.media.waterBubbleShader, origin, origin2, turb, range, snum); } return (1); } void CG_SnowLink (centity_t *cent, qboolean particleOn) { cparticle_t *p, *next; int id; id = cent->currentState.frame; for (p=active_particles ; p ; p=next) { next = p->next; if (p->type == P_WEATHER || p->type == P_WEATHER_TURBULENT) { if (p->snum == id) { if (particleOn) p->link = qtrue; else p->link = qfalse; } } } } void CG_ParticleImpactSmokePuff (qhandle_t pshader, vec3_t origin) { cparticle_t *p; if (!pshader) CG_Printf ("CG_ParticleImpactSmokePuff pshader == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->alpha = 0.25; p->alphavel = 0; p->roll = crandom()*179; p->pshader = pshader; p->endtime = cg.time + 1000; p->startfade = cg.time + 100; p->width = rand()%4 + 8; p->height = rand()%4 + 8; p->endheight = p->height *2; p->endwidth = p->width * 2; p->endtime = cg.time + 500; p->type = P_SMOKE_IMPACT; VectorCopy( origin, p->org ); VectorSet(p->vel, 0, 0, 20); VectorSet(p->accel, 0, 0, 20); p->rotate = qtrue; } void CG_Particle_Bleed (qhandle_t pshader, vec3_t start, vec3_t dir, int fleshEntityNum, int duration) { cparticle_t *p; if (!pshader) CG_Printf ("CG_Particle_Bleed pshader == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->alpha = 1.0; p->alphavel = 0; p->roll = 0; p->pshader = pshader; p->endtime = cg.time + duration; if (fleshEntityNum) p->startfade = cg.time; else p->startfade = cg.time + 100; p->width = 4; p->height = 4; p->endheight = 4+rand()%3; p->endwidth = p->endheight; p->type = P_SMOKE; VectorCopy( start, p->org ); p->vel[0] = 0; p->vel[1] = 0; p->vel[2] = -20; VectorClear( p->accel ); p->rotate = qfalse; p->roll = rand()%179; p->color = BLOODRED; p->alpha = 0.75; } void CG_Particle_OilParticle (qhandle_t pshader, centity_t *cent) { cparticle_t *p; int time; int time2; float ratio; float duration = 1500; time = cg.time; time2 = cg.time + cent->currentState.time; ratio =(float)1 - ((float)time / (float)time2); if (!pshader) CG_Printf ("CG_Particle_OilParticle == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->alpha = 1.0; p->alphavel = 0; p->roll = 0; p->pshader = pshader; p->endtime = cg.time + duration; p->startfade = p->endtime; p->width = 1; p->height = 3; p->endheight = 3; p->endwidth = 1; p->type = P_SMOKE; VectorCopy(cent->currentState.origin, p->org ); p->vel[0] = (cent->currentState.origin2[0] * (16 * ratio)); p->vel[1] = (cent->currentState.origin2[1] * (16 * ratio)); p->vel[2] = (cent->currentState.origin2[2]); p->snum = 1.0f; VectorClear( p->accel ); p->accel[2] = -20; p->rotate = qfalse; p->roll = rand()%179; p->alpha = 0.75; } void CG_Particle_OilSlick (qhandle_t pshader, centity_t *cent) { cparticle_t *p; if (!pshader) CG_Printf ("CG_Particle_OilSlick == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; if (cent->currentState.angles2[2]) p->endtime = cg.time + cent->currentState.angles2[2]; else p->endtime = cg.time + 60000; p->startfade = p->endtime; p->alpha = 1.0; p->alphavel = 0; p->roll = 0; p->pshader = pshader; if (cent->currentState.angles2[0] || cent->currentState.angles2[1]) { p->width = cent->currentState.angles2[0]; p->height = cent->currentState.angles2[0]; p->endheight = cent->currentState.angles2[1]; p->endwidth = cent->currentState.angles2[1]; } else { p->width = 8; p->height = 8; p->endheight = 16; p->endwidth = 16; } p->type = P_FLAT_SCALEUP; p->snum = 1.0; VectorCopy(cent->currentState.origin, p->org ); p->org[2]+= 0.55 + (crandom() * 0.5); p->vel[0] = 0; p->vel[1] = 0; p->vel[2] = 0; VectorClear( p->accel ); p->rotate = qfalse; p->roll = rand()%179; p->alpha = 0.75; } void CG_OilSlickRemove (centity_t *cent) { cparticle_t *p, *next; int id; id = 1.0f; if (!id) CG_Printf ("CG_OilSlickRevove NULL id\n"); for (p=active_particles ; p ; p=next) { next = p->next; if (p->type == P_FLAT_SCALEUP) { if (p->snum == id) { p->endtime = cg.time + 100; p->startfade = p->endtime; p->type = P_FLAT_SCALEUP_FADE; } } } } qboolean ValidBloodPool (vec3_t start) { #define EXTRUDE_DIST 0.5 vec3_t angles; vec3_t right, up; vec3_t this_pos, x_pos, center_pos, end_pos; float x, y; float fwidth, fheight; trace_t trace; vec3_t normal; fwidth = 16; fheight = 16; VectorSet (normal, 0, 0, 1); vectoangles (normal, angles); AngleVectors (angles, NULL, right, up); VectorMA (start, EXTRUDE_DIST, normal, center_pos); for (x= -fwidth/2; xendpos, start); legit = ValidBloodPool (start); if (!legit) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->endtime = cg.time + 3000; p->startfade = p->endtime; p->alpha = 1.0; p->alphavel = 0; p->roll = 0; p->pshader = pshader; rndSize = 0.4 + random()*0.6; p->width = 8*rndSize; p->height = 8*rndSize; p->endheight = 16*rndSize; p->endwidth = 16*rndSize; p->type = P_FLAT_SCALEUP; VectorCopy(start, p->org ); p->vel[0] = 0; p->vel[1] = 0; p->vel[2] = 0; VectorClear( p->accel ); p->rotate = qfalse; p->roll = rand()%179; p->alpha = 0.75; p->color = BLOODRED; } #define NORMALSIZE 16 #define LARGESIZE 32 void CG_ParticleBloodCloud (centity_t *cent, vec3_t origin, vec3_t dir) { float length; float dist; float crittersize; vec3_t angles, forward; vec3_t point; cparticle_t *p; int i; dist = 0; length = VectorLength (dir); vectoangles (dir, angles); AngleVectors (angles, forward, NULL, NULL); crittersize = LARGESIZE; if (length) dist = length / crittersize; if (dist < 1) dist = 1; VectorCopy (origin, point); for (i=0; inext; p->next = active_particles; active_particles = p; p->time = cg.time; p->alpha = 1.0; p->alphavel = 0; p->roll = 0; p->pshader = cgs.media.smokePuffShader; p->endtime = cg.time + 350 + (crandom() * 100); p->startfade = cg.time; p->width = LARGESIZE; p->height = LARGESIZE; p->endheight = LARGESIZE; p->endwidth = LARGESIZE; p->type = P_SMOKE; VectorCopy( origin, p->org ); p->vel[0] = 0; p->vel[1] = 0; p->vel[2] = -1; VectorClear( p->accel ); p->rotate = qfalse; p->roll = rand()%179; p->color = BLOODRED; p->alpha = 0.75; } } void CG_ParticleSparks (vec3_t org, vec3_t vel, int duration, float x, float y, float speed) { cparticle_t *p; if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->endtime = cg.time + duration; p->startfade = cg.time + duration/2; p->color = EMISIVEFADE; p->alpha = 0.4f; p->alphavel = 0; p->height = 0.5; p->width = 0.5; p->endheight = 0.5; p->endwidth = 0.5; p->pshader = cgs.media.tracerShader; p->type = P_SMOKE; VectorCopy(org, p->org); p->org[0] += (crandom() * x); p->org[1] += (crandom() * y); p->vel[0] = vel[0]; p->vel[1] = vel[1]; p->vel[2] = vel[2]; p->accel[0] = p->accel[1] = p->accel[2] = 0; p->vel[0] += (crandom() * 4); p->vel[1] += (crandom() * 4); p->vel[2] += (20 + (crandom() * 10)) * speed; p->accel[0] = crandom () * 4; p->accel[1] = crandom () * 4; } void CG_ParticleDust (centity_t *cent, vec3_t origin, vec3_t dir) { float length; float dist; float crittersize; vec3_t angles, forward; vec3_t point; cparticle_t *p; int i; dist = 0; VectorNegate (dir, dir); length = VectorLength (dir); vectoangles (dir, angles); AngleVectors (angles, forward, NULL, NULL); crittersize = LARGESIZE; if (length) dist = length / crittersize; if (dist < 1) dist = 1; VectorCopy (origin, point); for (i=0; inext; p->next = active_particles; active_particles = p; p->time = cg.time; p->alpha = 5.0; p->alphavel = 0; p->roll = 0; p->pshader = cgs.media.smokePuffShader; // RF, stay around for long enough to expand and dissipate naturally if (length) p->endtime = cg.time + 4500 + (crandom() * 3500); else p->endtime = cg.time + 750 + (crandom() * 500); p->startfade = cg.time; p->width = LARGESIZE; p->height = LARGESIZE; // RF, expand while falling p->endheight = LARGESIZE*3.0; p->endwidth = LARGESIZE*3.0; if (!length) { p->width *= 0.2f; p->height *= 0.2f; p->endheight = NORMALSIZE; p->endwidth = NORMALSIZE; } p->type = P_SMOKE; VectorCopy( point, p->org ); p->vel[0] = crandom()*6; p->vel[1] = crandom()*6; p->vel[2] = random()*20; // RF, add some gravity/randomness p->accel[0] = crandom()*3; p->accel[1] = crandom()*3; p->accel[2] = -PARTICLE_GRAVITY*0.4; VectorClear( p->accel ); p->rotate = qfalse; p->roll = rand()%179; p->alpha = 0.75; } } void CG_ParticleMisc (qhandle_t pshader, vec3_t origin, int size, int duration, float alpha) { cparticle_t *p; if (!pshader) CG_Printf ("CG_ParticleImpactSmokePuff pshader == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->alpha = 1.0; p->alphavel = 0; p->roll = rand()%179; p->pshader = pshader; if (duration > 0) p->endtime = cg.time + duration; else p->endtime = duration; p->startfade = cg.time; p->width = size; p->height = size; p->endheight = size; p->endwidth = size; p->type = P_SPRITE; VectorCopy( origin, p->org ); p->rotate = qfalse; } ================================================ FILE: src/cgame/cg_players.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_players.c -- handle the media and animation for player entities #include "cg_local.h" char *cg_customSoundNames[MAX_CUSTOM_SOUNDS] = { "*death1.wav", "*death2.wav", "*death3.wav", "*jump1.wav", "*pain25_1.wav", "*pain50_1.wav", "*pain75_1.wav", "*pain100_1.wav", "*falling1.wav", "*gasp.wav", "*drown.wav", "*fall1.wav", "*taunt.wav" }; /* ================ CG_CustomSound ================ */ sfxHandle_t CG_CustomSound( int clientNum, const char *soundName ) { clientInfo_t *ci; int i; if ( soundName[0] != '*' ) { return trap_S_RegisterSound( soundName, qfalse ); } if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) { clientNum = 0; } ci = &cgs.clientinfo[ clientNum ]; for ( i = 0 ; i < MAX_CUSTOM_SOUNDS && cg_customSoundNames[i] ; i++ ) { if ( !strcmp( soundName, cg_customSoundNames[i] ) ) { return ci->sounds[i]; } } CG_Error( "Unknown custom sound: %s", soundName ); return 0; } /* ============================================================================= CLIENT INFO ============================================================================= */ /* ====================== CG_ParseAnimationFile Read a configuration file containing animation coutns and rates models/players/visor/animation.cfg, etc ====================== */ static qboolean CG_ParseAnimationFile( const char *filename, clientInfo_t *ci ) { char *text_p, *prev; int len; int i; char *token; float fps; int skip; char text[20000]; fileHandle_t f; animation_t *animations; animations = ci->animations; // load the file len = trap_FS_FOpenFile( filename, &f, FS_READ ); if ( len <= 0 ) { return qfalse; } if ( len >= sizeof( text ) - 1 ) { CG_Printf( "File %s too long\n", filename ); return qfalse; } trap_FS_Read( text, len, f ); text[len] = 0; trap_FS_FCloseFile( f ); // parse the text text_p = text; skip = 0; // quite the compiler warning ci->footsteps = FOOTSTEP_NORMAL; VectorClear( ci->headOffset ); ci->gender = GENDER_MALE; ci->fixedlegs = qfalse; ci->fixedtorso = qfalse; // read optional parameters while ( 1 ) { prev = text_p; // so we can unget token = COM_Parse( &text_p ); if ( !token ) { break; } if ( !Q_stricmp( token, "footsteps" ) ) { token = COM_Parse( &text_p ); if ( !token ) { break; } if ( !Q_stricmp( token, "default" ) || !Q_stricmp( token, "normal" ) ) { ci->footsteps = FOOTSTEP_NORMAL; } else if ( !Q_stricmp( token, "boot" ) ) { ci->footsteps = FOOTSTEP_BOOT; } else if ( !Q_stricmp( token, "flesh" ) ) { ci->footsteps = FOOTSTEP_FLESH; } else if ( !Q_stricmp( token, "mech" ) ) { ci->footsteps = FOOTSTEP_MECH; } else if ( !Q_stricmp( token, "energy" ) ) { ci->footsteps = FOOTSTEP_ENERGY; } else { CG_Printf( "Bad footsteps parm in %s: %s\n", filename, token ); } continue; } else if ( !Q_stricmp( token, "headoffset" ) ) { for ( i = 0 ; i < 3 ; i++ ) { token = COM_Parse( &text_p ); if ( !token ) { break; } ci->headOffset[i] = atof( token ); } continue; } else if ( !Q_stricmp( token, "sex" ) ) { token = COM_Parse( &text_p ); if ( !token ) { break; } if ( token[0] == 'f' || token[0] == 'F' ) { ci->gender = GENDER_FEMALE; } else if ( token[0] == 'n' || token[0] == 'N' ) { ci->gender = GENDER_NEUTER; } else { ci->gender = GENDER_MALE; } continue; } else if ( !Q_stricmp( token, "fixedlegs" ) ) { ci->fixedlegs = qtrue; continue; } else if ( !Q_stricmp( token, "fixedtorso" ) ) { ci->fixedtorso = qtrue; continue; } // if it is a number, start parsing animations if ( token[0] >= '0' && token[0] <= '9' ) { text_p = prev; // unget the token break; } Com_Printf( "unknown token '%s' is %s\n", token, filename ); } // read information for each frame for ( i = 0 ; i < MAX_ANIMATIONS ; i++ ) { token = COM_Parse( &text_p ); if ( !*token ) { if( i >= TORSO_GETFLAG && i <= TORSO_NEGATIVE ) { animations[i].firstFrame = animations[TORSO_GESTURE].firstFrame; animations[i].frameLerp = animations[TORSO_GESTURE].frameLerp; animations[i].initialLerp = animations[TORSO_GESTURE].initialLerp; animations[i].loopFrames = animations[TORSO_GESTURE].loopFrames; animations[i].numFrames = animations[TORSO_GESTURE].numFrames; animations[i].reversed = qfalse; animations[i].flipflop = qfalse; continue; } break; } animations[i].firstFrame = atoi( token ); // leg only frames are adjusted to not count the upper body only frames if ( i == LEGS_WALKCR ) { skip = animations[LEGS_WALKCR].firstFrame - animations[TORSO_GESTURE].firstFrame; } if ( i >= LEGS_WALKCR && i0) { return qtrue; } return qfalse; } /* ========================== CG_FindClientModelFile ========================== */ static qboolean CG_FindClientModelFile( char *filename, int length, clientInfo_t *ci, const char *teamName, const char *modelName, const char *skinName, const char *base, const char *ext ) { char *team, *charactersFolder; int i; if ( cgs.gametype >= GT_TEAM ) { switch ( ci->team ) { case TEAM_BLUE: { team = "blue"; break; } default: { team = "red"; break; } } } else { team = "default"; } charactersFolder = ""; while(1) { for ( i = 0; i < 2; i++ ) { if ( i == 0 && teamName && *teamName ) { // "models/players/characters/james/stroggs/lower_lily_red.skin" Com_sprintf( filename, length, "models/players/%s%s/%s%s_%s_%s.%s", charactersFolder, modelName, teamName, base, skinName, team, ext ); } else { // "models/players/characters/james/lower_lily_red.skin" Com_sprintf( filename, length, "models/players/%s%s/%s_%s_%s.%s", charactersFolder, modelName, base, skinName, team, ext ); } if ( CG_FileExists( filename ) ) { return qtrue; } if ( cgs.gametype >= GT_TEAM ) { if ( i == 0 && teamName && *teamName ) { // "models/players/characters/james/stroggs/lower_red.skin" Com_sprintf( filename, length, "models/players/%s%s/%s%s_%s.%s", charactersFolder, modelName, teamName, base, team, ext ); } else { // "models/players/characters/james/lower_red.skin" Com_sprintf( filename, length, "models/players/%s%s/%s_%s.%s", charactersFolder, modelName, base, team, ext ); } } else { if ( i == 0 && teamName && *teamName ) { // "models/players/characters/james/stroggs/lower_lily.skin" Com_sprintf( filename, length, "models/players/%s%s/%s%s_%s.%s", charactersFolder, modelName, teamName, base, skinName, ext ); } else { // "models/players/characters/james/lower_lily.skin" Com_sprintf( filename, length, "models/players/%s%s/%s_%s.%s", charactersFolder, modelName, base, skinName, ext ); } } if ( CG_FileExists( filename ) ) { return qtrue; } if ( !teamName || !*teamName ) { break; } } // if tried the heads folder first if ( charactersFolder[0] ) { break; } charactersFolder = "characters/"; } return qfalse; } /* ========================== CG_FindClientHeadFile ========================== */ static qboolean CG_FindClientHeadFile( char *filename, int length, clientInfo_t *ci, const char *teamName, const char *headModelName, const char *headSkinName, const char *base, const char *ext ) { char *team, *headsFolder; int i; if ( cgs.gametype >= GT_TEAM ) { switch ( ci->team ) { case TEAM_BLUE: { team = "blue"; break; } default: { team = "red"; break; } } } else { team = "default"; } if ( headModelName[0] == '*' ) { headsFolder = "heads/"; headModelName++; } else { headsFolder = ""; } while(1) { for ( i = 0; i < 2; i++ ) { if ( i == 0 && teamName && *teamName ) { Com_sprintf( filename, length, "models/players/%s%s/%s/%s%s_%s.%s", headsFolder, headModelName, headSkinName, teamName, base, team, ext ); } else { Com_sprintf( filename, length, "models/players/%s%s/%s/%s_%s.%s", headsFolder, headModelName, headSkinName, base, team, ext ); } if ( CG_FileExists( filename ) ) { return qtrue; } if ( cgs.gametype >= GT_TEAM ) { if ( i == 0 && teamName && *teamName ) { Com_sprintf( filename, length, "models/players/%s%s/%s%s_%s.%s", headsFolder, headModelName, teamName, base, team, ext ); } else { Com_sprintf( filename, length, "models/players/%s%s/%s_%s.%s", headsFolder, headModelName, base, team, ext ); } } else { if ( i == 0 && teamName && *teamName ) { Com_sprintf( filename, length, "models/players/%s%s/%s%s_%s.%s", headsFolder, headModelName, teamName, base, headSkinName, ext ); } else { Com_sprintf( filename, length, "models/players/%s%s/%s_%s.%s", headsFolder, headModelName, base, headSkinName, ext ); } } if ( CG_FileExists( filename ) ) { return qtrue; } if ( !teamName || !*teamName ) { break; } } // if tried the heads folder first if ( headsFolder[0] ) { break; } headsFolder = "heads/"; } return qfalse; } /* ========================== CG_RegisterClientSkin ========================== */ static qboolean CG_RegisterClientSkin( clientInfo_t *ci, const char *teamName, const char *modelName, const char *skinName, const char *headModelName, const char *headSkinName ) { char filename[MAX_QPATH]; /* Com_sprintf( filename, sizeof( filename ), "models/players/%s/%slower_%s.skin", modelName, teamName, skinName ); ci->legsSkin = trap_R_RegisterSkin( filename ); if (!ci->legsSkin) { Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/%slower_%s.skin", modelName, teamName, skinName ); ci->legsSkin = trap_R_RegisterSkin( filename ); if (!ci->legsSkin) { Com_Printf( "Leg skin load failure: %s\n", filename ); } } Com_sprintf( filename, sizeof( filename ), "models/players/%s/%supper_%s.skin", modelName, teamName, skinName ); ci->torsoSkin = trap_R_RegisterSkin( filename ); if (!ci->torsoSkin) { Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/%supper_%s.skin", modelName, teamName, skinName ); ci->torsoSkin = trap_R_RegisterSkin( filename ); if (!ci->torsoSkin) { Com_Printf( "Torso skin load failure: %s\n", filename ); } } */ if ( CG_FindClientModelFile( filename, sizeof(filename), ci, teamName, modelName, skinName, "lower", "skin" ) ) { ci->legsSkin = trap_R_RegisterSkin( filename ); } if (!ci->legsSkin) { Com_Printf( "Leg skin load failure: %s\n", filename ); } if ( CG_FindClientModelFile( filename, sizeof(filename), ci, teamName, modelName, skinName, "upper", "skin" ) ) { ci->torsoSkin = trap_R_RegisterSkin( filename ); } if (!ci->torsoSkin) { Com_Printf( "Torso skin load failure: %s\n", filename ); } if ( CG_FindClientHeadFile( filename, sizeof(filename), ci, teamName, headModelName, headSkinName, "head", "skin" ) ) { ci->headSkin = trap_R_RegisterSkin( filename ); } if (!ci->headSkin) { Com_Printf( "Head skin load failure: %s\n", filename ); } // if any skins failed to load if ( !ci->legsSkin || !ci->torsoSkin || !ci->headSkin ) { return qfalse; } return qtrue; } /* ========================== CG_RegisterClientModelname ========================== */ static qboolean CG_RegisterClientModelname( clientInfo_t *ci, const char *modelName, const char *skinName, const char *headModelName, const char *headSkinName, const char *teamName ) { char filename[MAX_QPATH*2]; const char *headName; char newTeamName[MAX_QPATH*2]; if ( headModelName[0] == '\0' ) { headName = modelName; } else { headName = headModelName; } Com_sprintf( filename, sizeof( filename ), "models/players/%s/lower.md3", modelName ); ci->legsModel = trap_R_RegisterModel( filename ); if ( !ci->legsModel ) { Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/lower.md3", modelName ); ci->legsModel = trap_R_RegisterModel( filename ); if ( !ci->legsModel ) { Com_Printf( "Failed to load model file %s\n", filename ); return qfalse; } } Com_sprintf( filename, sizeof( filename ), "models/players/%s/upper.md3", modelName ); ci->torsoModel = trap_R_RegisterModel( filename ); if ( !ci->torsoModel ) { Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/upper.md3", modelName ); ci->torsoModel = trap_R_RegisterModel( filename ); if ( !ci->torsoModel ) { Com_Printf( "Failed to load model file %s\n", filename ); return qfalse; } } if( headName[0] == '*' ) { Com_sprintf( filename, sizeof( filename ), "models/players/heads/%s/%s.md3", &headModelName[1], &headModelName[1] ); } else { Com_sprintf( filename, sizeof( filename ), "models/players/%s/head.md3", headName ); } ci->headModel = trap_R_RegisterModel( filename ); // if the head model could not be found and we didn't load from the heads folder try to load from there if ( !ci->headModel && headName[0] != '*' ) { Com_sprintf( filename, sizeof( filename ), "models/players/heads/%s/%s.md3", headModelName, headModelName ); ci->headModel = trap_R_RegisterModel( filename ); } if ( !ci->headModel ) { Com_Printf( "Failed to load model file %s\n", filename ); return qfalse; } // if any skins failed to load, return failure if ( !CG_RegisterClientSkin( ci, teamName, modelName, skinName, headName, headSkinName ) ) { if ( teamName && *teamName) { Com_Printf( "Failed to load skin file: %s : %s : %s, %s : %s\n", teamName, modelName, skinName, headName, headSkinName ); if( ci->team == TEAM_BLUE ) { Com_sprintf(newTeamName, sizeof(newTeamName), "%s/", DEFAULT_BLUETEAM_NAME); } else { Com_sprintf(newTeamName, sizeof(newTeamName), "%s/", DEFAULT_REDTEAM_NAME); } if ( !CG_RegisterClientSkin( ci, newTeamName, modelName, skinName, headName, headSkinName ) ) { Com_Printf( "Failed to load skin file: %s : %s : %s, %s : %s\n", newTeamName, modelName, skinName, headName, headSkinName ); return qfalse; } } else { Com_Printf( "Failed to load skin file: %s : %s, %s : %s\n", modelName, skinName, headName, headSkinName ); return qfalse; } } // load the animations Com_sprintf( filename, sizeof( filename ), "models/players/%s/animation.cfg", modelName ); if ( !CG_ParseAnimationFile( filename, ci ) ) { Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/animation.cfg", modelName ); if ( !CG_ParseAnimationFile( filename, ci ) ) { Com_Printf( "Failed to load animation file %s\n", filename ); return qfalse; } } if ( CG_FindClientHeadFile( filename, sizeof(filename), ci, teamName, headName, headSkinName, "icon", "skin" ) ) { ci->modelIcon = trap_R_RegisterShaderNoMip( filename ); } else if ( CG_FindClientHeadFile( filename, sizeof(filename), ci, teamName, headName, headSkinName, "icon", "tga" ) ) { ci->modelIcon = trap_R_RegisterShaderNoMip( filename ); } if ( !ci->modelIcon ) { return qfalse; } return qtrue; } /* ==================== CG_ColorFromString ==================== */ static void CG_ColorFromString( const char *v, vec3_t color ) { int val; VectorClear( color ); val = atoi( v ); if ( val < 1 || val > 7 ) { VectorSet( color, 1, 1, 1 ); return; } if ( val & 1 ) { color[2] = 1.0f; } if ( val & 2 ) { color[1] = 1.0f; } if ( val & 4 ) { color[0] = 1.0f; } } /* =================== CG_LoadClientInfo Load it now, taking the disk hits. This will usually be deferred to a safe time =================== */ static void CG_LoadClientInfo( clientInfo_t *ci ) { const char *dir, *fallback; int i, modelloaded; const char *s; int clientNum; char teamname[MAX_QPATH]; teamname[0] = 0; modelloaded = qtrue; if ( !CG_RegisterClientModelname( ci, ci->modelName, ci->skinName, ci->headModelName, ci->headSkinName, teamname ) ) { if ( cg_buildScript.integer ) { CG_Error( "CG_RegisterClientModelname( %s, %s, %s, %s %s ) failed", ci->modelName, ci->skinName, ci->headModelName, ci->headSkinName, teamname ); } // fall back to default team name if( cgs.gametype >= GT_TEAM) { // keep skin name if( ci->team == TEAM_BLUE ) { Q_strncpyz(teamname, DEFAULT_BLUETEAM_NAME, sizeof(teamname) ); } else { Q_strncpyz(teamname, DEFAULT_REDTEAM_NAME, sizeof(teamname) ); } if ( !CG_RegisterClientModelname( ci, DEFAULT_TEAM_MODEL, ci->skinName, DEFAULT_TEAM_HEAD, ci->skinName, teamname ) ) { CG_Error( "DEFAULT_TEAM_MODEL / skin (%s/%s) failed to register", DEFAULT_TEAM_MODEL, ci->skinName ); } } else { if ( !CG_RegisterClientModelname( ci, DEFAULT_MODEL, "default", DEFAULT_MODEL, "default", teamname ) ) { CG_Error( "DEFAULT_MODEL (%s) failed to register", DEFAULT_MODEL ); } } modelloaded = qfalse; } ci->newAnims = qfalse; if ( ci->torsoModel ) { orientation_t tag; // if the torso model has the "tag_flag" if ( trap_R_LerpTag( &tag, ci->torsoModel, 0, 0, 1, "tag_flag" ) ) { ci->newAnims = qtrue; } } // sounds dir = ci->modelName; fallback = (cgs.gametype >= GT_TEAM) ? DEFAULT_TEAM_MODEL : DEFAULT_MODEL; for ( i = 0 ; i < MAX_CUSTOM_SOUNDS ; i++ ) { s = cg_customSoundNames[i]; if ( !s ) { break; } ci->sounds[i] = 0; // if the model didn't load use the sounds of the default model if (modelloaded) { ci->sounds[i] = trap_S_RegisterSound( va("sound/player/%s/%s", dir, s + 1), qfalse ); } if ( !ci->sounds[i] ) { ci->sounds[i] = trap_S_RegisterSound( va("sound/player/%s/%s", fallback, s + 1), qfalse ); } } ci->deferred = qfalse; // reset any existing players and bodies, because they might be in bad // frames for this new model clientNum = ci - cgs.clientinfo; for ( i = 0 ; i < MAX_GENTITIES ; i++ ) { if ( cg_entities[i].currentState.clientNum == clientNum && cg_entities[i].currentState.eType == ET_PLAYER ) { CG_ResetPlayerEntity( &cg_entities[i] ); } } } /* ====================== CG_CopyClientInfoModel ====================== */ static void CG_CopyClientInfoModel( clientInfo_t *from, clientInfo_t *to ) { VectorCopy( from->headOffset, to->headOffset ); to->footsteps = from->footsteps; to->gender = from->gender; to->legsModel = from->legsModel; to->legsSkin = from->legsSkin; to->torsoModel = from->torsoModel; to->torsoSkin = from->torsoSkin; to->headModel = from->headModel; to->headSkin = from->headSkin; to->modelIcon = from->modelIcon; to->newAnims = from->newAnims; memcpy( to->animations, from->animations, sizeof( to->animations ) ); memcpy( to->sounds, from->sounds, sizeof( to->sounds ) ); } /* ====================== CG_ScanForExistingClientInfo ====================== */ static qboolean CG_ScanForExistingClientInfo( clientInfo_t *ci ) { int i; clientInfo_t *match; for ( i = 0 ; i < cgs.maxclients ; i++ ) { match = &cgs.clientinfo[ i ]; if ( !match->infoValid ) { continue; } if ( match->deferred ) { continue; } if ( !Q_stricmp( ci->modelName, match->modelName ) && !Q_stricmp( ci->skinName, match->skinName ) && !Q_stricmp( ci->headModelName, match->headModelName ) && !Q_stricmp( ci->headSkinName, match->headSkinName ) && !Q_stricmp( ci->blueTeam, match->blueTeam ) && !Q_stricmp( ci->redTeam, match->redTeam ) && (cgs.gametype < GT_TEAM || ci->team == match->team) ) { // this clientinfo is identical, so use it's handles ci->deferred = qfalse; CG_CopyClientInfoModel( match, ci ); return qtrue; } } // nothing matches, so defer the load return qfalse; } /* ====================== CG_SetDeferredClientInfo We aren't going to load it now, so grab some other client's info to use until we have some spare time. ====================== */ static void CG_SetDeferredClientInfo( clientInfo_t *ci ) { int i; clientInfo_t *match; // if someone else is already the same models and skins we // can just load the client info for ( i = 0 ; i < cgs.maxclients ; i++ ) { match = &cgs.clientinfo[ i ]; if ( !match->infoValid || match->deferred ) { continue; } if ( Q_stricmp( ci->skinName, match->skinName ) || Q_stricmp( ci->modelName, match->modelName ) || // Q_stricmp( ci->headModelName, match->headModelName ) || // Q_stricmp( ci->headSkinName, match->headSkinName ) || (cgs.gametype >= GT_TEAM && ci->team != match->team) ) { continue; } // just load the real info cause it uses the same models and skins CG_LoadClientInfo( ci ); return; } // if we are in teamplay, only grab a model if the skin is correct if ( cgs.gametype >= GT_TEAM ) { for ( i = 0 ; i < cgs.maxclients ; i++ ) { match = &cgs.clientinfo[ i ]; if ( !match->infoValid || match->deferred ) { continue; } if ( Q_stricmp( ci->skinName, match->skinName ) || (cgs.gametype >= GT_TEAM && ci->team != match->team) ) { continue; } ci->deferred = qtrue; CG_CopyClientInfoModel( match, ci ); return; } // load the full model, because we don't ever want to show // an improper team skin. This will cause a hitch for the first // player, when the second enters. Combat shouldn't be going on // yet, so it shouldn't matter CG_LoadClientInfo( ci ); return; } // find the first valid clientinfo and grab its stuff for ( i = 0 ; i < cgs.maxclients ; i++ ) { match = &cgs.clientinfo[ i ]; if ( !match->infoValid ) { continue; } ci->deferred = qtrue; CG_CopyClientInfoModel( match, ci ); return; } // we should never get here... CG_Printf( "CG_SetDeferredClientInfo: no valid clients!\n" ); CG_LoadClientInfo( ci ); } /* ====================== CG_NewClientInfo ====================== */ void CG_NewClientInfo( int clientNum ) { clientInfo_t *ci; clientInfo_t newInfo; const char *configstring; const char *v; char *slash; ci = &cgs.clientinfo[clientNum]; configstring = CG_ConfigString( clientNum + CS_PLAYERS ); if ( !configstring[0] ) { memset( ci, 0, sizeof( *ci ) ); return; // player just left } // build into a temp buffer so the defer checks can use // the old value memset( &newInfo, 0, sizeof( newInfo ) ); // isolate the player's name v = Info_ValueForKey(configstring, "n"); Q_strncpyz( newInfo.name, v, sizeof( newInfo.name ) ); // colors v = Info_ValueForKey( configstring, "c1" ); CG_ColorFromString( v, newInfo.color1 ); v = Info_ValueForKey( configstring, "c2" ); CG_ColorFromString( v, newInfo.color2 ); // bot skill v = Info_ValueForKey( configstring, "skill" ); newInfo.botSkill = atoi( v ); // handicap v = Info_ValueForKey( configstring, "hc" ); newInfo.handicap = atoi( v ); // wins v = Info_ValueForKey( configstring, "w" ); newInfo.wins = atoi( v ); // losses v = Info_ValueForKey( configstring, "l" ); newInfo.losses = atoi( v ); // team v = Info_ValueForKey( configstring, "t" ); newInfo.team = atoi( v ); // team task v = Info_ValueForKey( configstring, "tt" ); newInfo.teamTask = atoi(v); // team leader v = Info_ValueForKey( configstring, "tl" ); newInfo.teamLeader = atoi(v); v = Info_ValueForKey( configstring, "g_redteam" ); Q_strncpyz(newInfo.redTeam, v, MAX_TEAMNAME); v = Info_ValueForKey( configstring, "g_blueteam" ); Q_strncpyz(newInfo.blueTeam, v, MAX_TEAMNAME); // model v = Info_ValueForKey( configstring, "model" ); if ( cg_forceModel.integer ) { // forcemodel makes everyone use a single model // to prevent load hitches char modelStr[MAX_QPATH]; char *skin; if( cgs.gametype >= GT_TEAM ) { Q_strncpyz( newInfo.modelName, DEFAULT_TEAM_MODEL, sizeof( newInfo.modelName ) ); Q_strncpyz( newInfo.skinName, "default", sizeof( newInfo.skinName ) ); } else { trap_Cvar_VariableStringBuffer( "model", modelStr, sizeof( modelStr ) ); if ( ( skin = strchr( modelStr, '/' ) ) == NULL) { skin = "default"; } else { *skin++ = 0; } Q_strncpyz( newInfo.skinName, skin, sizeof( newInfo.skinName ) ); Q_strncpyz( newInfo.modelName, modelStr, sizeof( newInfo.modelName ) ); } if ( cgs.gametype >= GT_TEAM ) { // keep skin name slash = strchr( v, '/' ); if ( slash ) { Q_strncpyz( newInfo.skinName, slash + 1, sizeof( newInfo.skinName ) ); } } } else { Q_strncpyz( newInfo.modelName, v, sizeof( newInfo.modelName ) ); slash = strchr( newInfo.modelName, '/' ); if ( !slash ) { // modelName didn not include a skin name Q_strncpyz( newInfo.skinName, "default", sizeof( newInfo.skinName ) ); } else { Q_strncpyz( newInfo.skinName, slash + 1, sizeof( newInfo.skinName ) ); // truncate modelName *slash = 0; } } // head model v = Info_ValueForKey( configstring, "hmodel" ); if ( cg_forceModel.integer ) { // forcemodel makes everyone use a single model // to prevent load hitches char modelStr[MAX_QPATH]; char *skin; if( cgs.gametype >= GT_TEAM ) { Q_strncpyz( newInfo.headModelName, DEFAULT_TEAM_MODEL, sizeof( newInfo.headModelName ) ); Q_strncpyz( newInfo.headSkinName, "default", sizeof( newInfo.headSkinName ) ); } else { trap_Cvar_VariableStringBuffer( "headmodel", modelStr, sizeof( modelStr ) ); if ( ( skin = strchr( modelStr, '/' ) ) == NULL) { skin = "default"; } else { *skin++ = 0; } Q_strncpyz( newInfo.headSkinName, skin, sizeof( newInfo.headSkinName ) ); Q_strncpyz( newInfo.headModelName, modelStr, sizeof( newInfo.headModelName ) ); } if ( cgs.gametype >= GT_TEAM ) { // keep skin name slash = strchr( v, '/' ); if ( slash ) { Q_strncpyz( newInfo.headSkinName, slash + 1, sizeof( newInfo.headSkinName ) ); } } } else { Q_strncpyz( newInfo.headModelName, v, sizeof( newInfo.headModelName ) ); slash = strchr( newInfo.headModelName, '/' ); if ( !slash ) { // modelName didn not include a skin name Q_strncpyz( newInfo.headSkinName, "default", sizeof( newInfo.headSkinName ) ); } else { Q_strncpyz( newInfo.headSkinName, slash + 1, sizeof( newInfo.headSkinName ) ); // truncate modelName *slash = 0; } } // scan for an existing clientinfo that matches this modelname // so we can avoid loading checks if possible if ( !CG_ScanForExistingClientInfo( &newInfo ) ) { qboolean forceDefer; forceDefer = trap_MemoryRemaining() < 4000000; // if we are defering loads, just have it pick the first valid if ( forceDefer || (cg_deferPlayers.integer && !cg_buildScript.integer && !cg.loading ) ) { // keep whatever they had if it won't violate team skins CG_SetDeferredClientInfo( &newInfo ); // if we are low on memory, leave them with this model if ( forceDefer ) { CG_Printf( "Memory is low. Using deferred model.\n" ); newInfo.deferred = qfalse; } } else { CG_LoadClientInfo( &newInfo ); } } // replace whatever was there with the new one newInfo.infoValid = qtrue; *ci = newInfo; } /* ====================== CG_LoadDeferredPlayers Called each frame when a player is dead and the scoreboard is up so deferred players can be loaded ====================== */ void CG_LoadDeferredPlayers( void ) { int i; clientInfo_t *ci; // scan for a deferred player to load for ( i = 0, ci = cgs.clientinfo ; i < cgs.maxclients ; i++, ci++ ) { if ( ci->infoValid && ci->deferred ) { // if we are low on memory, leave it deferred if ( trap_MemoryRemaining() < 4000000 ) { CG_Printf( "Memory is low. Using deferred model.\n" ); ci->deferred = qfalse; continue; } CG_LoadClientInfo( ci ); // break; } } } /* ============================================================================= PLAYER ANIMATION ============================================================================= */ /* =============== CG_SetLerpFrameAnimation may include ANIM_TOGGLEBIT =============== */ static void CG_SetLerpFrameAnimation( clientInfo_t *ci, lerpFrame_t *lf, int newAnimation ) { animation_t *anim; lf->animationNumber = newAnimation; newAnimation &= ~ANIM_TOGGLEBIT; if ( newAnimation < 0 || newAnimation >= MAX_TOTALANIMATIONS ) { CG_Error( "Bad animation number: %i", newAnimation ); } anim = &ci->animations[ newAnimation ]; lf->animation = anim; lf->animationTime = lf->frameTime + anim->initialLerp; if ( cg_debugAnim.integer ) { CG_Printf( "Anim: %i\n", newAnimation ); } } /* =============== CG_RunLerpFrame Sets cg.snap, cg.oldFrame, and cg.backlerp cg.time should be between oldFrameTime and frameTime after exit =============== */ static void CG_RunLerpFrame( clientInfo_t *ci, lerpFrame_t *lf, int newAnimation, float speedScale ) { int f, numFrames; animation_t *anim; // debugging tool to get no animations if ( cg_animSpeed.integer == 0 ) { lf->oldFrame = lf->frame = lf->backlerp = 0; return; } // see if the animation sequence is switching if ( newAnimation != lf->animationNumber || !lf->animation ) { CG_SetLerpFrameAnimation( ci, lf, newAnimation ); } // if we have passed the current frame, move it to // oldFrame and calculate a new frame if ( cg.time >= lf->frameTime ) { lf->oldFrame = lf->frame; lf->oldFrameTime = lf->frameTime; // get the next frame based on the animation anim = lf->animation; if ( !anim->frameLerp ) { return; // shouldn't happen } if ( cg.time < lf->animationTime ) { lf->frameTime = lf->animationTime; // initial lerp } else { lf->frameTime = lf->oldFrameTime + anim->frameLerp; } f = ( lf->frameTime - lf->animationTime ) / anim->frameLerp; f *= speedScale; // adjust for haste, etc numFrames = anim->numFrames; if (anim->flipflop) { numFrames *= 2; } if ( f >= numFrames ) { f -= numFrames; if ( anim->loopFrames ) { f %= anim->loopFrames; f += anim->numFrames - anim->loopFrames; } else { f = numFrames - 1; // the animation is stuck at the end, so it // can immediately transition to another sequence lf->frameTime = cg.time; } } if ( anim->reversed ) { lf->frame = anim->firstFrame + anim->numFrames - 1 - f; } else if (anim->flipflop && f>=anim->numFrames) { lf->frame = anim->firstFrame + anim->numFrames - 1 - (f%anim->numFrames); } else { lf->frame = anim->firstFrame + f; } if ( cg.time > lf->frameTime ) { lf->frameTime = cg.time; if ( cg_debugAnim.integer ) { CG_Printf( "Clamp lf->frameTime\n"); } } } if ( lf->frameTime > cg.time + 200 ) { lf->frameTime = cg.time; } if ( lf->oldFrameTime > cg.time ) { lf->oldFrameTime = cg.time; } // calculate current lerp value if ( lf->frameTime == lf->oldFrameTime ) { lf->backlerp = 0; } else { lf->backlerp = 1.0 - (float)( cg.time - lf->oldFrameTime ) / ( lf->frameTime - lf->oldFrameTime ); } } /* =============== CG_ClearLerpFrame =============== */ static void CG_ClearLerpFrame( clientInfo_t *ci, lerpFrame_t *lf, int animationNumber ) { lf->frameTime = lf->oldFrameTime = cg.time; CG_SetLerpFrameAnimation( ci, lf, animationNumber ); lf->oldFrame = lf->frame = lf->animation->firstFrame; } /* =============== CG_PlayerAnimation =============== */ static void CG_PlayerAnimation( centity_t *cent, int *legsOld, int *legs, float *legsBackLerp, int *torsoOld, int *torso, float *torsoBackLerp ) { clientInfo_t *ci; int clientNum; float speedScale; clientNum = cent->currentState.clientNum; if ( cg_noPlayerAnims.integer ) { *legsOld = *legs = *torsoOld = *torso = 0; return; } if ( cent->currentState.powerups & ( 1 << PW_HASTE ) ) { speedScale = 1.5; } else { speedScale = 1; } ci = &cgs.clientinfo[ clientNum ]; // do the shuffle turn frames locally if ( cent->pe.legs.yawing && ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_IDLE ) { CG_RunLerpFrame( ci, ¢->pe.legs, LEGS_TURN, speedScale ); } else { CG_RunLerpFrame( ci, ¢->pe.legs, cent->currentState.legsAnim, speedScale ); } *legsOld = cent->pe.legs.oldFrame; *legs = cent->pe.legs.frame; *legsBackLerp = cent->pe.legs.backlerp; CG_RunLerpFrame( ci, ¢->pe.torso, cent->currentState.torsoAnim, speedScale ); *torsoOld = cent->pe.torso.oldFrame; *torso = cent->pe.torso.frame; *torsoBackLerp = cent->pe.torso.backlerp; } /* ============================================================================= PLAYER ANGLES ============================================================================= */ /* ================== CG_SwingAngles ================== */ static void CG_SwingAngles( float destination, float swingTolerance, float clampTolerance, float speed, float *angle, qboolean *swinging ) { float swing; float move; float scale; if ( !*swinging ) { // see if a swing should be started swing = AngleSubtract( *angle, destination ); if ( swing > swingTolerance || swing < -swingTolerance ) { *swinging = qtrue; } } if ( !*swinging ) { return; } // modify the speed depending on the delta // so it doesn't seem so linear swing = AngleSubtract( destination, *angle ); scale = fabs( swing ); if ( scale < swingTolerance * 0.5 ) { scale = 0.5; } else if ( scale < swingTolerance ) { scale = 1.0; } else { scale = 2.0; } // swing towards the destination angle if ( swing >= 0 ) { move = cg.frametime * scale * speed; if ( move >= swing ) { move = swing; *swinging = qfalse; } *angle = AngleMod( *angle + move ); } else if ( swing < 0 ) { move = cg.frametime * scale * -speed; if ( move <= swing ) { move = swing; *swinging = qfalse; } *angle = AngleMod( *angle + move ); } // clamp to no more than tolerance swing = AngleSubtract( destination, *angle ); if ( swing > clampTolerance ) { *angle = AngleMod( destination - (clampTolerance - 1) ); } else if ( swing < -clampTolerance ) { *angle = AngleMod( destination + (clampTolerance - 1) ); } } /* ================= CG_AddPainTwitch ================= */ static void CG_AddPainTwitch( centity_t *cent, vec3_t torsoAngles ) { int t; float f; t = cg.time - cent->pe.painTime; if ( t >= PAIN_TWITCH_TIME ) { return; } f = 1.0 - (float)t / PAIN_TWITCH_TIME; if ( cent->pe.painDirection ) { torsoAngles[ROLL] += 20 * f; } else { torsoAngles[ROLL] -= 20 * f; } } /* =============== CG_PlayerAngles Handles seperate torso motion legs pivot based on direction of movement head always looks exactly at cent->lerpAngles if motion < 20 degrees, show in head only if < 45 degrees, also show in torso =============== */ static void CG_PlayerAngles( centity_t *cent, vec3_t legs[3], vec3_t torso[3], vec3_t head[3] ) { vec3_t legsAngles, torsoAngles, headAngles; float dest; static int movementOffsets[8] = { 0, 22, 45, -22, 0, 22, -45, -22 }; vec3_t velocity; float speed; int dir, clientNum; clientInfo_t *ci; VectorCopy( cent->lerpAngles, headAngles ); headAngles[YAW] = AngleMod( headAngles[YAW] ); VectorClear( legsAngles ); VectorClear( torsoAngles ); // --------- yaw ------------- // allow yaw to drift a bit if ( ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) != LEGS_IDLE || ( cent->currentState.torsoAnim & ~ANIM_TOGGLEBIT ) != TORSO_STAND ) { // if not standing still, always point all in the same direction cent->pe.torso.yawing = qtrue; // always center cent->pe.torso.pitching = qtrue; // always center cent->pe.legs.yawing = qtrue; // always center } // adjust legs for movement dir if ( cent->currentState.eFlags & EF_DEAD ) { // don't let dead bodies twitch dir = 0; } else { dir = cent->currentState.angles2[YAW]; if ( dir < 0 || dir > 7 ) { CG_Error( "Bad player movement angle" ); } } legsAngles[YAW] = headAngles[YAW] + movementOffsets[ dir ]; torsoAngles[YAW] = headAngles[YAW] + 0.25 * movementOffsets[ dir ]; // torso CG_SwingAngles( torsoAngles[YAW], 25, 90, cg_swingSpeed.value, ¢->pe.torso.yawAngle, ¢->pe.torso.yawing ); CG_SwingAngles( legsAngles[YAW], 40, 90, cg_swingSpeed.value, ¢->pe.legs.yawAngle, ¢->pe.legs.yawing ); torsoAngles[YAW] = cent->pe.torso.yawAngle; legsAngles[YAW] = cent->pe.legs.yawAngle; // --------- pitch ------------- // only show a fraction of the pitch angle in the torso if ( headAngles[PITCH] > 180 ) { dest = (-360 + headAngles[PITCH]) * 0.75f; } else { dest = headAngles[PITCH] * 0.75f; } CG_SwingAngles( dest, 15, 30, 0.1f, ¢->pe.torso.pitchAngle, ¢->pe.torso.pitching ); torsoAngles[PITCH] = cent->pe.torso.pitchAngle; // clientNum = cent->currentState.clientNum; if ( clientNum >= 0 && clientNum < MAX_CLIENTS ) { ci = &cgs.clientinfo[ clientNum ]; if ( ci->fixedtorso ) { torsoAngles[PITCH] = 0.0f; } } // --------- roll ------------- // lean towards the direction of travel VectorCopy( cent->currentState.pos.trDelta, velocity ); speed = VectorNormalize( velocity ); if ( speed ) { vec3_t axis[3]; float side; speed *= 0.05f; AnglesToAxis( legsAngles, axis ); side = speed * DotProduct( velocity, axis[1] ); legsAngles[ROLL] -= side; side = speed * DotProduct( velocity, axis[0] ); legsAngles[PITCH] += side; } // clientNum = cent->currentState.clientNum; if ( clientNum >= 0 && clientNum < MAX_CLIENTS ) { ci = &cgs.clientinfo[ clientNum ]; if ( ci->fixedlegs ) { legsAngles[YAW] = torsoAngles[YAW]; legsAngles[PITCH] = 0.0f; legsAngles[ROLL] = 0.0f; } } // pain twitch CG_AddPainTwitch( cent, torsoAngles ); // pull the angles back out of the hierarchial chain AnglesSubtract( headAngles, torsoAngles, headAngles ); AnglesSubtract( torsoAngles, legsAngles, torsoAngles ); AnglesToAxis( legsAngles, legs ); AnglesToAxis( torsoAngles, torso ); AnglesToAxis( headAngles, head ); } //========================================================================== /* =============== CG_HasteTrail =============== */ static void CG_HasteTrail( centity_t *cent ) { localEntity_t *smoke; vec3_t origin; int anim; if ( cent->trailTime > cg.time ) { return; } anim = cent->pe.legs.animationNumber & ~ANIM_TOGGLEBIT; if ( anim != LEGS_RUN && anim != LEGS_BACK ) { return; } cent->trailTime += 100; if ( cent->trailTime < cg.time ) { cent->trailTime = cg.time; } VectorCopy( cent->lerpOrigin, origin ); origin[2] -= 16; smoke = CG_SmokePuff( origin, vec3_origin, 8, 1, 1, 1, 1, 500, cg.time, 0, 0, cgs.media.hastePuffShader ); // use the optimized local entity add smoke->leType = LE_SCALE_FADE; } /* =============== CG_TrailItem =============== */ static void CG_TrailItem( centity_t *cent, qhandle_t hModel ) { refEntity_t ent; vec3_t angles; vec3_t axis[3]; VectorCopy( cent->lerpAngles, angles ); angles[PITCH] = 0; angles[ROLL] = 0; AnglesToAxis( angles, axis ); memset( &ent, 0, sizeof( ent ) ); VectorMA( cent->lerpOrigin, -16, axis[0], ent.origin ); ent.origin[2] += 16; angles[YAW] += 90; AnglesToAxis( angles, ent.axis ); ent.hModel = hModel; trap_R_AddRefEntityToScene( &ent ); } /* =============== CG_PlayerFlag =============== */ static void CG_PlayerFlag( centity_t *cent, qhandle_t hSkin, refEntity_t *torso ) { clientInfo_t *ci; refEntity_t pole; refEntity_t flag; vec3_t angles, dir; int legsAnim, flagAnim, updateangles; float angle, d; // show the flag pole model memset( &pole, 0, sizeof(pole) ); pole.hModel = cgs.media.flagPoleModel; VectorCopy( torso->lightingOrigin, pole.lightingOrigin ); pole.shadowPlane = torso->shadowPlane; pole.renderfx = torso->renderfx; CG_PositionEntityOnTag( &pole, torso, torso->hModel, "tag_flag" ); trap_R_AddRefEntityToScene( &pole ); // show the flag model memset( &flag, 0, sizeof(flag) ); flag.hModel = cgs.media.flagFlapModel; flag.customSkin = hSkin; VectorCopy( torso->lightingOrigin, flag.lightingOrigin ); flag.shadowPlane = torso->shadowPlane; flag.renderfx = torso->renderfx; VectorClear(angles); updateangles = qfalse; legsAnim = cent->currentState.legsAnim & ~ANIM_TOGGLEBIT; if( legsAnim == LEGS_IDLE || legsAnim == LEGS_IDLECR ) { flagAnim = FLAG_STAND; } else if ( legsAnim == LEGS_WALK || legsAnim == LEGS_WALKCR ) { flagAnim = FLAG_STAND; updateangles = qtrue; } else { flagAnim = FLAG_RUN; updateangles = qtrue; } if ( updateangles ) { VectorCopy( cent->currentState.pos.trDelta, dir ); // add gravity dir[2] += 100; VectorNormalize( dir ); d = DotProduct(pole.axis[2], dir); // if there is anough movement orthogonal to the flag pole if (fabs(d) < 0.9) { // d = DotProduct(pole.axis[0], dir); if (d > 1.0f) { d = 1.0f; } else if (d < -1.0f) { d = -1.0f; } angle = acos(d); d = DotProduct(pole.axis[1], dir); if (d < 0) { angles[YAW] = 360 - angle * 180 / M_PI; } else { angles[YAW] = angle * 180 / M_PI; } if (angles[YAW] < 0) angles[YAW] += 360; if (angles[YAW] > 360) angles[YAW] -= 360; //vectoangles( cent->currentState.pos.trDelta, tmpangles ); //angles[YAW] = tmpangles[YAW] + 45 - cent->pe.torso.yawAngle; // change the yaw angle CG_SwingAngles( angles[YAW], 25, 90, 0.15f, ¢->pe.flag.yawAngle, ¢->pe.flag.yawing ); } /* d = DotProduct(pole.axis[2], dir); angle = Q_acos(d); d = DotProduct(pole.axis[1], dir); if (d < 0) { angle = 360 - angle * 180 / M_PI; } else { angle = angle * 180 / M_PI; } if (angle > 340 && angle < 20) { flagAnim = FLAG_RUNUP; } if (angle > 160 && angle < 200) { flagAnim = FLAG_RUNDOWN; } */ } // set the yaw angle angles[YAW] = cent->pe.flag.yawAngle; // lerp the flag animation frames ci = &cgs.clientinfo[ cent->currentState.clientNum ]; CG_RunLerpFrame( ci, ¢->pe.flag, flagAnim, 1 ); flag.oldframe = cent->pe.flag.oldFrame; flag.frame = cent->pe.flag.frame; flag.backlerp = cent->pe.flag.backlerp; AnglesToAxis( angles, flag.axis ); CG_PositionRotatedEntityOnTag( &flag, &pole, pole.hModel, "tag_flag" ); trap_R_AddRefEntityToScene( &flag ); } /* =============== CG_PlayerPowerups =============== */ static void CG_PlayerPowerups( centity_t *cent, refEntity_t *torso ) { int powerups; clientInfo_t *ci; powerups = cent->currentState.powerups; if ( !powerups ) { return; } // quad gives a dlight if ( powerups & ( 1 << PW_QUAD ) ) { trap_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 0.2f, 0.2f, 1 ); } // flight plays a looped sound if ( powerups & ( 1 << PW_FLIGHT ) ) { trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.media.flightSound ); } ci = &cgs.clientinfo[ cent->currentState.clientNum ]; // redflag if ( powerups & ( 1 << PW_REDFLAG ) ) { if (ci->newAnims) { CG_PlayerFlag( cent, cgs.media.redFlagFlapSkin, torso ); } else { CG_TrailItem( cent, cgs.media.redFlagModel ); } trap_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 1.0, 0.2f, 0.2f ); } // blueflag if ( powerups & ( 1 << PW_BLUEFLAG ) ) { if (ci->newAnims){ CG_PlayerFlag( cent, cgs.media.blueFlagFlapSkin, torso ); } else { CG_TrailItem( cent, cgs.media.blueFlagModel ); } trap_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 0.2f, 0.2f, 1.0 ); } // neutralflag if ( powerups & ( 1 << PW_NEUTRALFLAG ) ) { if (ci->newAnims) { CG_PlayerFlag( cent, cgs.media.neutralFlagFlapSkin, torso ); } else { CG_TrailItem( cent, cgs.media.neutralFlagModel ); } trap_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 1.0, 1.0, 1.0 ); } // haste leaves smoke trails if ( powerups & ( 1 << PW_HASTE ) ) { CG_HasteTrail( cent ); } } /* =============== CG_PlayerFloatSprite Float a sprite over the player's head =============== */ static void CG_PlayerFloatSprite( centity_t *cent, qhandle_t shader ) { int rf; refEntity_t ent; if ( cent->currentState.number == cg.snap->ps.clientNum && !cg.renderingThirdPerson ) { rf = RF_THIRD_PERSON; // only show in mirrors } else { rf = 0; } memset( &ent, 0, sizeof( ent ) ); VectorCopy( cent->lerpOrigin, ent.origin ); ent.origin[2] += 48; ent.reType = RT_SPRITE; ent.customShader = shader; ent.radius = 10; ent.renderfx = rf; ent.shaderRGBA[0] = 255; ent.shaderRGBA[1] = 255; ent.shaderRGBA[2] = 255; ent.shaderRGBA[3] = 255; trap_R_AddRefEntityToScene( &ent ); } /* =============== CG_PlayerSprites Float sprites over the player's head =============== */ static void CG_PlayerSprites( centity_t *cent ) { int team; if ( cent->currentState.eFlags & EF_CONNECTION ) { CG_PlayerFloatSprite( cent, cgs.media.connectionShader ); return; } if ( cent->currentState.eFlags & EF_TALK ) { CG_PlayerFloatSprite( cent, cgs.media.balloonShader ); return; } if ( cent->currentState.eFlags & EF_AWARD_IMPRESSIVE ) { CG_PlayerFloatSprite( cent, cgs.media.medalImpressive ); return; } if ( cent->currentState.eFlags & EF_AWARD_EXCELLENT ) { CG_PlayerFloatSprite( cent, cgs.media.medalExcellent ); return; } if ( cent->currentState.eFlags & EF_AWARD_GAUNTLET ) { CG_PlayerFloatSprite( cent, cgs.media.medalGauntlet ); return; } if ( cent->currentState.eFlags & EF_AWARD_DEFEND ) { CG_PlayerFloatSprite( cent, cgs.media.medalDefend ); return; } if ( cent->currentState.eFlags & EF_AWARD_ASSIST ) { CG_PlayerFloatSprite( cent, cgs.media.medalAssist ); return; } if ( cent->currentState.eFlags & EF_AWARD_CAP ) { CG_PlayerFloatSprite( cent, cgs.media.medalCapture ); return; } team = cgs.clientinfo[ cent->currentState.clientNum ].team; if ( !(cent->currentState.eFlags & EF_DEAD) && cg.snap->ps.persistant[PERS_TEAM] == team && cgs.gametype >= GT_TEAM) { if (cg_drawFriend.integer) { CG_PlayerFloatSprite( cent, cgs.media.friendShader ); } return; } } /* =============== CG_PlayerShadow Returns the Z component of the surface being shadowed should it return a full plane instead of a Z? =============== */ #define SHADOW_DISTANCE 128 static qboolean CG_PlayerShadow( centity_t *cent, float *shadowPlane ) { vec3_t end, mins = {-15, -15, 0}, maxs = {15, 15, 2}; trace_t trace; float alpha; *shadowPlane = 0; if ( cg_shadows.integer == 0 ) { return qfalse; } // no shadows when invisible if ( cent->currentState.powerups & ( 1 << PW_INVIS ) ) { return qfalse; } // send a trace down from the player to the ground VectorCopy( cent->lerpOrigin, end ); end[2] -= SHADOW_DISTANCE; trap_CM_BoxTrace( &trace, cent->lerpOrigin, end, mins, maxs, 0, MASK_PLAYERSOLID ); // no shadow if too high if ( trace.fraction == 1.0 || trace.startsolid || trace.allsolid ) { return qfalse; } *shadowPlane = trace.endpos[2] + 1; if ( cg_shadows.integer != 1 ) { // no mark for stencil or projection shadows return qtrue; } // fade the shadow out with height alpha = 1.0 - trace.fraction; // bk0101022 - hack / FPE - bogus planes? //assert( DotProduct( trace.plane.normal, trace.plane.normal ) != 0.0f ) // add the mark as a temporary, so it goes directly to the renderer // without taking a spot in the cg_marks array CG_ImpactMark( cgs.media.shadowMarkShader, trace.endpos, trace.plane.normal, cent->pe.legs.yawAngle, alpha,alpha,alpha,1, qfalse, 24, qtrue ); return qtrue; } /* =============== CG_PlayerSplash Draw a mark at the water surface =============== */ static void CG_PlayerSplash( centity_t *cent ) { vec3_t start, end; trace_t trace; int contents; polyVert_t verts[4]; if ( !cg_shadows.integer ) { return; } VectorCopy( cent->lerpOrigin, end ); end[2] -= 24; // if the feet aren't in liquid, don't make a mark // this won't handle moving water brushes, but they wouldn't draw right anyway... contents = trap_CM_PointContents( end, 0 ); if ( !( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) ) { return; } VectorCopy( cent->lerpOrigin, start ); start[2] += 32; // if the head isn't out of liquid, don't make a mark contents = trap_CM_PointContents( start, 0 ); if ( contents & ( CONTENTS_SOLID | CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) { return; } // trace down to find the surface trap_CM_BoxTrace( &trace, start, end, NULL, NULL, 0, ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ); if ( trace.fraction == 1.0 ) { return; } // create a mark polygon VectorCopy( trace.endpos, verts[0].xyz ); verts[0].xyz[0] -= 32; verts[0].xyz[1] -= 32; verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255; verts[0].modulate[1] = 255; verts[0].modulate[2] = 255; verts[0].modulate[3] = 255; VectorCopy( trace.endpos, verts[1].xyz ); verts[1].xyz[0] -= 32; verts[1].xyz[1] += 32; verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255; verts[1].modulate[1] = 255; verts[1].modulate[2] = 255; verts[1].modulate[3] = 255; VectorCopy( trace.endpos, verts[2].xyz ); verts[2].xyz[0] += 32; verts[2].xyz[1] += 32; verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255; verts[2].modulate[1] = 255; verts[2].modulate[2] = 255; verts[2].modulate[3] = 255; VectorCopy( trace.endpos, verts[3].xyz ); verts[3].xyz[0] += 32; verts[3].xyz[1] -= 32; verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255; verts[3].modulate[1] = 255; verts[3].modulate[2] = 255; verts[3].modulate[3] = 255; trap_R_AddPolyToScene( cgs.media.wakeMarkShader, 4, verts ); } /* =============== CG_AddRefEntityWithPowerups Adds a piece with modifications or duplications for powerups Also called by CG_Missile for quad rockets, but nobody can tell... =============== */ void CG_AddRefEntityWithPowerups( refEntity_t *ent, entityState_t *state, int team ) { if ( state->powerups & ( 1 << PW_INVIS ) ) { ent->customShader = cgs.media.invisShader; trap_R_AddRefEntityToScene( ent ); } else { /* if ( state->eFlags & EF_KAMIKAZE ) { if (team == TEAM_BLUE) ent->customShader = cgs.media.blueKamikazeShader; else ent->customShader = cgs.media.redKamikazeShader; trap_R_AddRefEntityToScene( ent ); } else {*/ trap_R_AddRefEntityToScene( ent ); //} if ( state->powerups & ( 1 << PW_QUAD ) ) { if (team == TEAM_RED) ent->customShader = cgs.media.redQuadShader; else ent->customShader = cgs.media.quadShader; trap_R_AddRefEntityToScene( ent ); } if ( state->powerups & ( 1 << PW_REGEN ) ) { if ( ( ( cg.time / 100 ) % 10 ) == 1 ) { ent->customShader = cgs.media.regenShader; trap_R_AddRefEntityToScene( ent ); } } if ( state->powerups & ( 1 << PW_BATTLESUIT ) ) { ent->customShader = cgs.media.battleSuitShader; trap_R_AddRefEntityToScene( ent ); } } } /* ================= CG_LightVerts ================= */ int CG_LightVerts( vec3_t normal, int numVerts, polyVert_t *verts ) { int i, j; float incoming; vec3_t ambientLight; vec3_t lightDir; vec3_t directedLight; trap_R_LightForPoint( verts[0].xyz, ambientLight, directedLight, lightDir ); for (i = 0; i < numVerts; i++) { incoming = DotProduct (normal, lightDir); if ( incoming <= 0 ) { verts[i].modulate[0] = ambientLight[0]; verts[i].modulate[1] = ambientLight[1]; verts[i].modulate[2] = ambientLight[2]; verts[i].modulate[3] = 255; continue; } j = ( ambientLight[0] + incoming * directedLight[0] ); if ( j > 255 ) { j = 255; } verts[i].modulate[0] = j; j = ( ambientLight[1] + incoming * directedLight[1] ); if ( j > 255 ) { j = 255; } verts[i].modulate[1] = j; j = ( ambientLight[2] + incoming * directedLight[2] ); if ( j > 255 ) { j = 255; } verts[i].modulate[2] = j; verts[i].modulate[3] = 255; } return qtrue; } /* =============== CG_Player =============== */ void CG_Player( centity_t *cent ) { clientInfo_t *ci; refEntity_t legs; refEntity_t torso; refEntity_t head; int clientNum; int renderfx; qboolean shadow; float shadowPlane; // the client number is stored in clientNum. It can't be derived // from the entity number, because a single client may have // multiple corpses on the level using the same clientinfo clientNum = cent->currentState.clientNum; if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) { CG_Error( "Bad clientNum on player entity"); } ci = &cgs.clientinfo[ clientNum ]; // it is possible to see corpses from disconnected players that may // not have valid clientinfo if ( !ci->infoValid ) { return; } // get the player model information renderfx = 0; if ( cent->currentState.number == cg.snap->ps.clientNum) { if (!cg.renderingThirdPerson) { renderfx = RF_THIRD_PERSON; // only draw in mirrors } else { if (cg_cameraMode.integer) { return; } } } memset( &legs, 0, sizeof(legs) ); memset( &torso, 0, sizeof(torso) ); memset( &head, 0, sizeof(head) ); // get the rotation information CG_PlayerAngles( cent, legs.axis, torso.axis, head.axis ); // get the animation state (after rotation, to allow feet shuffle) CG_PlayerAnimation( cent, &legs.oldframe, &legs.frame, &legs.backlerp, &torso.oldframe, &torso.frame, &torso.backlerp ); // add the talk baloon or disconnect icon CG_PlayerSprites( cent ); // add the shadow shadow = CG_PlayerShadow( cent, &shadowPlane ); // add a water splash if partially in and out of water CG_PlayerSplash( cent ); if ( cg_shadows.integer == 3 && shadow ) { renderfx |= RF_SHADOW_PLANE; } renderfx |= RF_LIGHTING_ORIGIN; // use the same origin for all // // add the legs // legs.hModel = ci->legsModel; legs.customSkin = ci->legsSkin; VectorCopy( cent->lerpOrigin, legs.origin ); VectorCopy( cent->lerpOrigin, legs.lightingOrigin ); legs.shadowPlane = shadowPlane; legs.renderfx = renderfx; VectorCopy (legs.origin, legs.oldorigin); // don't positionally lerp at all CG_AddRefEntityWithPowerups( &legs, ¢->currentState, ci->team ); // if the model failed, allow the default nullmodel to be displayed if (!legs.hModel) { return; } // // add the torso // torso.hModel = ci->torsoModel; if (!torso.hModel) { return; } torso.customSkin = ci->torsoSkin; VectorCopy( cent->lerpOrigin, torso.lightingOrigin ); CG_PositionRotatedEntityOnTag( &torso, &legs, ci->legsModel, "tag_torso"); torso.shadowPlane = shadowPlane; torso.renderfx = renderfx; CG_AddRefEntityWithPowerups( &torso, ¢->currentState, ci->team ); // // add the head // head.hModel = ci->headModel; if (!head.hModel) { return; } head.customSkin = ci->headSkin; VectorCopy( cent->lerpOrigin, head.lightingOrigin ); CG_PositionRotatedEntityOnTag( &head, &torso, ci->torsoModel, "tag_head"); head.shadowPlane = shadowPlane; head.renderfx = renderfx; CG_AddRefEntityWithPowerups( &head, ¢->currentState, ci->team ); // // add the gun / barrel / flash // CG_AddPlayerWeapon( &torso, NULL, cent, ci->team ); // add powerups floating behind the player CG_PlayerPowerups( cent, &torso ); } //===================================================================== /* =============== CG_ResetPlayerEntity A player just came into view or teleported, so reset all animation info =============== */ void CG_ResetPlayerEntity( centity_t *cent ) { cent->errorTime = -99999; // guarantee no error decay added cent->extrapolated = qfalse; CG_ClearLerpFrame( &cgs.clientinfo[ cent->currentState.clientNum ], ¢->pe.legs, cent->currentState.legsAnim ); CG_ClearLerpFrame( &cgs.clientinfo[ cent->currentState.clientNum ], ¢->pe.torso, cent->currentState.torsoAnim ); BG_EvaluateTrajectory( ¢->currentState.pos, cg.time, cent->lerpOrigin ); BG_EvaluateTrajectory( ¢->currentState.apos, cg.time, cent->lerpAngles ); VectorCopy( cent->lerpOrigin, cent->rawOrigin ); VectorCopy( cent->lerpAngles, cent->rawAngles ); memset( ¢->pe.legs, 0, sizeof( cent->pe.legs ) ); cent->pe.legs.yawAngle = cent->rawAngles[YAW]; cent->pe.legs.yawing = qfalse; cent->pe.legs.pitchAngle = 0; cent->pe.legs.pitching = qfalse; memset( ¢->pe.torso, 0, sizeof( cent->pe.legs ) ); cent->pe.torso.yawAngle = cent->rawAngles[YAW]; cent->pe.torso.yawing = qfalse; cent->pe.torso.pitchAngle = cent->rawAngles[PITCH]; cent->pe.torso.pitching = qfalse; if ( cg_debugPosition.integer ) { CG_Printf("%i ResetPlayerEntity yaw=%i\n", cent->currentState.number, cent->pe.torso.yawAngle ); } } ================================================ FILE: src/cgame/cg_playerstate.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_playerstate.c -- this file acts on changes in a new playerState_t // With normal play, this will be done after local prediction, but when // following another player or playing back a demo, it will be checked // when the snapshot transitions like all the other entities #include "cg_local.h" /* ============== CG_CheckAmmo If the ammo has gone low enough to generate the warning, play a sound ============== */ void CG_CheckAmmo( void ) { int i; int total; int previous; int weapons; // see about how many seconds of ammo we have remaining weapons = cg.snap->ps.stats[ STAT_WEAPONS ]; total = 0; for ( i = WP_MACHINEGUN ; i < WP_NUM_WEAPONS ; i++ ) { if ( ! ( weapons & ( 1 << i ) ) ) { continue; } switch ( i ) { case WP_ROCKET_LAUNCHER: case WP_GRENADE_LAUNCHER: case WP_RAILGUN: case WP_SHOTGUN: total += cg.snap->ps.ammo[i] * 1000; break; default: total += cg.snap->ps.ammo[i] * 200; break; } if ( total >= 5000 ) { cg.lowAmmoWarning = 0; return; } } previous = cg.lowAmmoWarning; if ( total == 0 ) { cg.lowAmmoWarning = 2; } else { cg.lowAmmoWarning = 1; } // play a sound on transitions if ( cg.lowAmmoWarning != previous ) { trap_S_StartLocalSound( cgs.media.noAmmoSound, CHAN_LOCAL_SOUND ); } } /* ============== CG_DamageFeedback ============== */ void CG_DamageFeedback( int yawByte, int pitchByte, int damage ) { float left, front, up; float kick; int health; float scale; vec3_t dir; vec3_t angles; float dist; float yaw, pitch; // show the attacking player's head and name in corner cg.attackerTime = cg.time; // the lower on health you are, the greater the view kick will be health = cg.snap->ps.stats[STAT_HEALTH]; if ( health < 40 ) { scale = 1; } else { scale = 40.0 / health; } kick = damage * scale; if (kick < 5) kick = 5; if (kick > 10) kick = 10; // if yaw and pitch are both 255, make the damage always centered (falling, etc) if ( yawByte == 255 && pitchByte == 255 ) { cg.damageX = 0; cg.damageY = 0; cg.v_dmg_roll = 0; cg.v_dmg_pitch = -kick; } else { // positional pitch = pitchByte / 255.0 * 360; yaw = yawByte / 255.0 * 360; angles[PITCH] = pitch; angles[YAW] = yaw; angles[ROLL] = 0; AngleVectors( angles, dir, NULL, NULL ); VectorSubtract( vec3_origin, dir, dir ); front = DotProduct (dir, cg.refdef.viewaxis[0] ); left = DotProduct (dir, cg.refdef.viewaxis[1] ); up = DotProduct (dir, cg.refdef.viewaxis[2] ); dir[0] = front; dir[1] = left; dir[2] = 0; dist = VectorLength( dir ); if ( dist < 0.1 ) { dist = 0.1f; } cg.v_dmg_roll = kick * left; cg.v_dmg_pitch = -kick * front; if ( front <= 0.1 ) { front = 0.1f; } cg.damageX = -left / front; cg.damageY = up / dist; } // clamp the position if ( cg.damageX > 1.0 ) { cg.damageX = 1.0; } if ( cg.damageX < - 1.0 ) { cg.damageX = -1.0; } if ( cg.damageY > 1.0 ) { cg.damageY = 1.0; } if ( cg.damageY < - 1.0 ) { cg.damageY = -1.0; } // don't let the screen flashes vary as much if ( kick > 10 ) { kick = 10; } cg.damageValue = kick; cg.v_dmg_time = cg.time + DAMAGE_TIME; cg.damageTime = cg.snap->serverTime; } /* ================ CG_Respawn A respawn happened this snapshot ================ */ void CG_Respawn( void ) { // no error decay on player movement cg.thisFrameTeleport = qtrue; // display weapons available cg.weaponSelectTime = cg.time; // select the weapon the server says we are using cg.weaponSelect = cg.snap->ps.weapon; } extern char *eventnames[]; /* ============== CG_CheckPlayerstateEvents ============== */ void CG_CheckPlayerstateEvents( playerState_t *ps, playerState_t *ops ) { int i; int event; centity_t *cent; if ( ps->externalEvent && ps->externalEvent != ops->externalEvent ) { cent = &cg_entities[ ps->clientNum ]; cent->currentState.event = ps->externalEvent; cent->currentState.eventParm = ps->externalEventParm; CG_EntityEvent( cent, cent->lerpOrigin ); } cent = &cg.predictedPlayerEntity; // cg_entities[ ps->clientNum ]; // go through the predictable events buffer for ( i = ps->eventSequence - MAX_PS_EVENTS ; i < ps->eventSequence ; i++ ) { // if we have a new predictable event if ( i >= ops->eventSequence // or the server told us to play another event instead of a predicted event we already issued // or something the server told us changed our prediction causing a different event || (i > ops->eventSequence - MAX_PS_EVENTS && ps->events[i & (MAX_PS_EVENTS-1)] != ops->events[i & (MAX_PS_EVENTS-1)]) ) { event = ps->events[ i & (MAX_PS_EVENTS-1) ]; cent->currentState.event = event; cent->currentState.eventParm = ps->eventParms[ i & (MAX_PS_EVENTS-1) ]; CG_EntityEvent( cent, cent->lerpOrigin ); cg.predictableEvents[ i & (MAX_PREDICTED_EVENTS-1) ] = event; cg.eventSequence++; } } } /* ================== CG_CheckChangedPredictableEvents ================== */ void CG_CheckChangedPredictableEvents( playerState_t *ps ) { int i; int event; centity_t *cent; cent = &cg.predictedPlayerEntity; for ( i = ps->eventSequence - MAX_PS_EVENTS ; i < ps->eventSequence ; i++ ) { // if (i >= cg.eventSequence) { continue; } // if this event is not further back in than the maximum predictable events we remember if (i > cg.eventSequence - MAX_PREDICTED_EVENTS) { // if the new playerstate event is different from a previously predicted one if ( ps->events[i & (MAX_PS_EVENTS-1)] != cg.predictableEvents[i & (MAX_PREDICTED_EVENTS-1) ] ) { event = ps->events[ i & (MAX_PS_EVENTS-1) ]; cent->currentState.event = event; cent->currentState.eventParm = ps->eventParms[ i & (MAX_PS_EVENTS-1) ]; CG_EntityEvent( cent, cent->lerpOrigin ); cg.predictableEvents[ i & (MAX_PREDICTED_EVENTS-1) ] = event; if ( cg_showmiss.integer ) { CG_Printf("WARNING: changed predicted event\n"); } } } } } /* ================== pushReward ================== */ static void pushReward(sfxHandle_t sfx, qhandle_t shader, int rewardCount) { if (cg.rewardStack < (MAX_REWARDSTACK-1)) { cg.rewardStack++; cg.rewardSound[cg.rewardStack] = sfx; cg.rewardShader[cg.rewardStack] = shader; cg.rewardCount[cg.rewardStack] = rewardCount; } } /* ================== CG_CheckLocalSounds ================== */ void CG_CheckLocalSounds( playerState_t *ps, playerState_t *ops ) { int highScore, health, armor, reward; sfxHandle_t sfx; // don't play the sounds if the player just changed teams if ( ps->persistant[PERS_TEAM] != ops->persistant[PERS_TEAM] ) { return; } // hit changes if ( ps->persistant[PERS_HITS] > ops->persistant[PERS_HITS] ) { armor = ps->persistant[PERS_ATTACKEE_ARMOR] & 0xff; health = ps->persistant[PERS_ATTACKEE_ARMOR] >> 8; trap_S_StartLocalSound( cgs.media.hitSound, CHAN_LOCAL_SOUND ); } else if ( ps->persistant[PERS_HITS] < ops->persistant[PERS_HITS] ) { trap_S_StartLocalSound( cgs.media.hitTeamSound, CHAN_LOCAL_SOUND ); } // health changes of more than -1 should make pain sounds if ( ps->stats[STAT_HEALTH] < ops->stats[STAT_HEALTH] - 1 ) { if ( ps->stats[STAT_HEALTH] > 0 ) { CG_PainEvent( &cg.predictedPlayerEntity, ps->stats[STAT_HEALTH] ); } } // if we are going into the intermission, don't start any voices if ( cg.intermissionStarted ) { return; } // reward sounds reward = qfalse; if (ps->persistant[PERS_CAPTURES] != ops->persistant[PERS_CAPTURES]) { pushReward(cgs.media.captureAwardSound, cgs.media.medalCapture, ps->persistant[PERS_CAPTURES]); reward = qtrue; //Com_Printf("capture\n"); } if (ps->persistant[PERS_IMPRESSIVE_COUNT] != ops->persistant[PERS_IMPRESSIVE_COUNT]) { sfx = cgs.media.impressiveSound; pushReward(sfx, cgs.media.medalImpressive, ps->persistant[PERS_IMPRESSIVE_COUNT]); reward = qtrue; //Com_Printf("impressive\n"); } if (ps->persistant[PERS_EXCELLENT_COUNT] != ops->persistant[PERS_EXCELLENT_COUNT]) { sfx = cgs.media.excellentSound; pushReward(sfx, cgs.media.medalExcellent, ps->persistant[PERS_EXCELLENT_COUNT]); reward = qtrue; //Com_Printf("excellent\n"); } if (ps->persistant[PERS_GAUNTLET_FRAG_COUNT] != ops->persistant[PERS_GAUNTLET_FRAG_COUNT]) { sfx = cgs.media.humiliationSound; pushReward(sfx, cgs.media.medalGauntlet, ps->persistant[PERS_GAUNTLET_FRAG_COUNT]); reward = qtrue; //Com_Printf("guantlet frag\n"); } if (ps->persistant[PERS_DEFEND_COUNT] != ops->persistant[PERS_DEFEND_COUNT]) { pushReward(cgs.media.defendSound, cgs.media.medalDefend, ps->persistant[PERS_DEFEND_COUNT]); reward = qtrue; //Com_Printf("defend\n"); } if (ps->persistant[PERS_ASSIST_COUNT] != ops->persistant[PERS_ASSIST_COUNT]) { pushReward(cgs.media.assistSound, cgs.media.medalAssist, ps->persistant[PERS_ASSIST_COUNT]); reward = qtrue; //Com_Printf("assist\n"); } // if any of the player event bits changed if (ps->persistant[PERS_PLAYEREVENTS] != ops->persistant[PERS_PLAYEREVENTS]) { if ((ps->persistant[PERS_PLAYEREVENTS] & PLAYEREVENT_DENIEDREWARD) != (ops->persistant[PERS_PLAYEREVENTS] & PLAYEREVENT_DENIEDREWARD)) { trap_S_StartLocalSound( cgs.media.deniedSound, CHAN_ANNOUNCER ); } else if ((ps->persistant[PERS_PLAYEREVENTS] & PLAYEREVENT_GAUNTLETREWARD) != (ops->persistant[PERS_PLAYEREVENTS] & PLAYEREVENT_GAUNTLETREWARD)) { trap_S_StartLocalSound( cgs.media.humiliationSound, CHAN_ANNOUNCER ); } else if ((ps->persistant[PERS_PLAYEREVENTS] & PLAYEREVENT_HOLYSHIT) != (ops->persistant[PERS_PLAYEREVENTS] & PLAYEREVENT_HOLYSHIT)) { trap_S_StartLocalSound( cgs.media.holyShitSound, CHAN_ANNOUNCER ); } reward = qtrue; } // check for flag pickup if ( cgs.gametype >= GT_TEAM ) { if ((ps->powerups[PW_REDFLAG] != ops->powerups[PW_REDFLAG] && ps->powerups[PW_REDFLAG]) || (ps->powerups[PW_BLUEFLAG] != ops->powerups[PW_BLUEFLAG] && ps->powerups[PW_BLUEFLAG]) || (ps->powerups[PW_NEUTRALFLAG] != ops->powerups[PW_NEUTRALFLAG] && ps->powerups[PW_NEUTRALFLAG]) ) { trap_S_StartLocalSound( cgs.media.youHaveFlagSound, CHAN_ANNOUNCER ); } } // lead changes if (!reward) { // if ( !cg.warmup ) { // never play lead changes during warmup if ( ps->persistant[PERS_RANK] != ops->persistant[PERS_RANK] ) { if ( cgs.gametype < GT_TEAM) { if ( ps->persistant[PERS_RANK] == 0 ) { CG_AddBufferedSound(cgs.media.takenLeadSound); } else if ( ps->persistant[PERS_RANK] == RANK_TIED_FLAG ) { CG_AddBufferedSound(cgs.media.tiedLeadSound); } else if ( ( ops->persistant[PERS_RANK] & ~RANK_TIED_FLAG ) == 0 ) { CG_AddBufferedSound(cgs.media.lostLeadSound); } } } } } // timelimit warnings if ( cgs.timelimit > 0 ) { int msec; msec = cg.time - cgs.levelStartTime; if ( !( cg.timelimitWarnings & 4 ) && msec > ( cgs.timelimit * 60 + 2 ) * 1000 ) { cg.timelimitWarnings |= 1 | 2 | 4; trap_S_StartLocalSound( cgs.media.suddenDeathSound, CHAN_ANNOUNCER ); } else if ( !( cg.timelimitWarnings & 2 ) && msec > (cgs.timelimit - 1) * 60 * 1000 ) { cg.timelimitWarnings |= 1 | 2; trap_S_StartLocalSound( cgs.media.oneMinuteSound, CHAN_ANNOUNCER ); } else if ( cgs.timelimit > 5 && !( cg.timelimitWarnings & 1 ) && msec > (cgs.timelimit - 5) * 60 * 1000 ) { cg.timelimitWarnings |= 1; trap_S_StartLocalSound( cgs.media.fiveMinuteSound, CHAN_ANNOUNCER ); } } // fraglimit warnings if ( cgs.fraglimit > 0 && cgs.gametype < GT_CTF) { highScore = cgs.scores1; if ( !( cg.fraglimitWarnings & 4 ) && highScore == (cgs.fraglimit - 1) ) { cg.fraglimitWarnings |= 1 | 2 | 4; CG_AddBufferedSound(cgs.media.oneFragSound); } else if ( cgs.fraglimit > 2 && !( cg.fraglimitWarnings & 2 ) && highScore == (cgs.fraglimit - 2) ) { cg.fraglimitWarnings |= 1 | 2; CG_AddBufferedSound(cgs.media.twoFragSound); } else if ( cgs.fraglimit > 3 && !( cg.fraglimitWarnings & 1 ) && highScore == (cgs.fraglimit - 3) ) { cg.fraglimitWarnings |= 1; CG_AddBufferedSound(cgs.media.threeFragSound); } } } /* =============== CG_TransitionPlayerState =============== */ void CG_TransitionPlayerState( playerState_t *ps, playerState_t *ops ) { // check for changing follow mode if ( ps->clientNum != ops->clientNum ) { cg.thisFrameTeleport = qtrue; // make sure we don't get any unwanted transition effects *ops = *ps; } // damage events (player is getting wounded) if ( ps->damageEvent != ops->damageEvent && ps->damageCount ) { CG_DamageFeedback( ps->damageYaw, ps->damagePitch, ps->damageCount ); } // respawning if ( ps->persistant[PERS_SPAWN_COUNT] != ops->persistant[PERS_SPAWN_COUNT] ) { CG_Respawn(); } if ( cg.mapRestart ) { CG_Respawn(); cg.mapRestart = qfalse; } if ( cg.snap->ps.pm_type != PM_INTERMISSION && ps->persistant[PERS_TEAM] != TEAM_SPECTATOR ) { CG_CheckLocalSounds( ps, ops ); } // check for going low on ammo CG_CheckAmmo(); // run events CG_CheckPlayerstateEvents( ps, ops ); // smooth the ducking viewheight change if ( ps->viewheight != ops->viewheight ) { cg.duckChange = ps->viewheight - ops->viewheight; cg.duckTime = cg.time; } } ================================================ FILE: src/cgame/cg_predict.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_predict.c -- this file generates cg.predictedPlayerState by either // interpolating between snapshots from the server or locally predicting // ahead the client's movement. // It also handles local physics interaction, like fragments bouncing off walls #include "cg_local.h" static pmove_t cg_pmove; static int cg_numSolidEntities; static centity_t *cg_solidEntities[MAX_ENTITIES_IN_SNAPSHOT]; static int cg_numTriggerEntities; static centity_t *cg_triggerEntities[MAX_ENTITIES_IN_SNAPSHOT]; /* ==================== CG_BuildSolidList When a new cg.snap has been set, this function builds a sublist of the entities that are actually solid, to make for more efficient collision detection ==================== */ void CG_BuildSolidList( void ) { int i; centity_t *cent; snapshot_t *snap; entityState_t *ent; cg_numSolidEntities = 0; cg_numTriggerEntities = 0; if ( cg.nextSnap && !cg.nextFrameTeleport && !cg.thisFrameTeleport ) { snap = cg.nextSnap; } else { snap = cg.snap; } for ( i = 0 ; i < snap->numEntities ; i++ ) { cent = &cg_entities[ snap->entities[ i ].number ]; ent = ¢->currentState; if ( ent->eType == ET_ITEM || ent->eType == ET_PUSH_TRIGGER || ent->eType == ET_TELEPORT_TRIGGER ) { cg_triggerEntities[cg_numTriggerEntities] = cent; cg_numTriggerEntities++; continue; } if ( cent->nextState.solid ) { cg_solidEntities[cg_numSolidEntities] = cent; cg_numSolidEntities++; continue; } } } /* ==================== CG_ClipMoveToEntities ==================== */ static void CG_ClipMoveToEntities ( const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int skipNumber, int mask, trace_t *tr ) { int i, x, zd, zu; trace_t trace; entityState_t *ent; clipHandle_t cmodel; vec3_t bmins, bmaxs; vec3_t origin, angles; centity_t *cent; for ( i = 0 ; i < cg_numSolidEntities ; i++ ) { cent = cg_solidEntities[ i ]; ent = ¢->currentState; if ( ent->number == skipNumber ) { continue; } if ( ent->solid == SOLID_BMODEL ) { // special value for bmodel cmodel = trap_CM_InlineModel( ent->modelindex ); VectorCopy( cent->lerpAngles, angles ); BG_EvaluateTrajectory( ¢->currentState.pos, cg.physicsTime, origin ); } else { // encoded bbox x = (ent->solid & 255); zd = ((ent->solid>>8) & 255); zu = ((ent->solid>>16) & 255) - 32; bmins[0] = bmins[1] = -x; bmaxs[0] = bmaxs[1] = x; bmins[2] = -zd; bmaxs[2] = zu; cmodel = trap_CM_TempBoxModel( bmins, bmaxs ); VectorCopy( vec3_origin, angles ); VectorCopy( cent->lerpOrigin, origin ); } trap_CM_TransformedBoxTrace ( &trace, start, end, mins, maxs, cmodel, mask, origin, angles); if (trace.allsolid || trace.fraction < tr->fraction) { trace.entityNum = ent->number; *tr = trace; } else if (trace.startsolid) { tr->startsolid = qtrue; } if ( tr->allsolid ) { return; } } } /* ================ CG_Trace ================ */ void CG_Trace( trace_t *result, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int skipNumber, int mask ) { trace_t t; trap_CM_BoxTrace ( &t, start, end, mins, maxs, 0, mask); t.entityNum = t.fraction != 1.0 ? ENTITYNUM_WORLD : ENTITYNUM_NONE; // check all other solid models CG_ClipMoveToEntities (start, mins, maxs, end, skipNumber, mask, &t); *result = t; } /* ================ CG_PointContents ================ */ int CG_PointContents( const vec3_t point, int passEntityNum ) { int i; entityState_t *ent; centity_t *cent; clipHandle_t cmodel; int contents; contents = trap_CM_PointContents (point, 0); for ( i = 0 ; i < cg_numSolidEntities ; i++ ) { cent = cg_solidEntities[ i ]; ent = ¢->currentState; if ( ent->number == passEntityNum ) { continue; } if (ent->solid != SOLID_BMODEL) { // special value for bmodel continue; } cmodel = trap_CM_InlineModel( ent->modelindex ); if ( !cmodel ) { continue; } contents |= trap_CM_TransformedPointContents( point, cmodel, ent->origin, ent->angles ); } return contents; } /* ======================== CG_InterpolatePlayerState Generates cg.predictedPlayerState by interpolating between cg.snap->player_state and cg.nextFrame->player_state ======================== */ static void CG_InterpolatePlayerState( qboolean grabAngles ) { float f; int i; playerState_t *out; snapshot_t *prev, *next; out = &cg.predictedPlayerState; prev = cg.snap; next = cg.nextSnap; *out = cg.snap->ps; // if we are still allowing local input, short circuit the view angles if ( grabAngles ) { usercmd_t cmd; int cmdNum; cmdNum = trap_GetCurrentCmdNumber(); trap_GetUserCmd( cmdNum, &cmd ); PM_UpdateViewAngles( out, &cmd ); } // if the next frame is a teleport, we can't lerp to it if ( cg.nextFrameTeleport ) { return; } if ( !next || next->serverTime <= prev->serverTime ) { return; } f = (float)( cg.time - prev->serverTime ) / ( next->serverTime - prev->serverTime ); i = next->ps.bobCycle; if ( i < prev->ps.bobCycle ) { i += 256; // handle wraparound } out->bobCycle = prev->ps.bobCycle + f * ( i - prev->ps.bobCycle ); for ( i = 0 ; i < 3 ; i++ ) { out->origin[i] = prev->ps.origin[i] + f * (next->ps.origin[i] - prev->ps.origin[i] ); if ( !grabAngles ) { out->viewangles[i] = LerpAngle( prev->ps.viewangles[i], next->ps.viewangles[i], f ); } out->velocity[i] = prev->ps.velocity[i] + f * (next->ps.velocity[i] - prev->ps.velocity[i] ); } } /* =================== CG_TouchItem =================== */ static void CG_TouchItem( centity_t *cent ) { gitem_t *item; if ( !cg_predictItems.integer ) { return; } if ( !BG_PlayerTouchesItem( &cg.predictedPlayerState, ¢->currentState, cg.time ) ) { return; } // never pick an item up twice in a prediction if ( cent->miscTime == cg.time ) { return; } if ( !BG_CanItemBeGrabbed( cgs.gametype, ¢->currentState, &cg.predictedPlayerState ) ) { return; // can't hold it } item = &bg_itemlist[ cent->currentState.modelindex ]; // Special case for flags. // We don't predict touching our own flag if( cgs.gametype == GT_CTF ) { if (cg.predictedPlayerState.persistant[PERS_TEAM] == TEAM_RED && item->giTag == PW_REDFLAG) return; if (cg.predictedPlayerState.persistant[PERS_TEAM] == TEAM_BLUE && item->giTag == PW_BLUEFLAG) return; } // grab it BG_AddPredictableEventToPlayerstate( EV_ITEM_PICKUP, cent->currentState.modelindex , &cg.predictedPlayerState); // remove it from the frame so it won't be drawn cent->currentState.eFlags |= EF_NODRAW; // don't touch it again this prediction cent->miscTime = cg.time; // if its a weapon, give them some predicted ammo so the autoswitch will work if ( item->giType == IT_WEAPON ) { cg.predictedPlayerState.stats[ STAT_WEAPONS ] |= 1 << item->giTag; if ( !cg.predictedPlayerState.ammo[ item->giTag ] ) { cg.predictedPlayerState.ammo[ item->giTag ] = 1; } } } /* ========================= CG_TouchTriggerPrediction Predict push triggers and items ========================= */ static void CG_TouchTriggerPrediction( void ) { int i; trace_t trace; entityState_t *ent; clipHandle_t cmodel; centity_t *cent; qboolean spectator; // dead clients don't activate triggers if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) { return; } spectator = ( cg.predictedPlayerState.pm_type == PM_SPECTATOR ); if ( cg.predictedPlayerState.pm_type != PM_NORMAL && !spectator ) { return; } for ( i = 0 ; i < cg_numTriggerEntities ; i++ ) { cent = cg_triggerEntities[ i ]; ent = ¢->currentState; if ( ent->eType == ET_ITEM && !spectator ) { CG_TouchItem( cent ); continue; } if ( ent->solid != SOLID_BMODEL ) { continue; } cmodel = trap_CM_InlineModel( ent->modelindex ); if ( !cmodel ) { continue; } trap_CM_BoxTrace( &trace, cg.predictedPlayerState.origin, cg.predictedPlayerState.origin, cg_pmove.mins, cg_pmove.maxs, cmodel, -1 ); if ( !trace.startsolid ) { continue; } if ( ent->eType == ET_TELEPORT_TRIGGER ) { cg.hyperspace = qtrue; } else if ( ent->eType == ET_PUSH_TRIGGER ) { BG_TouchJumpPad( &cg.predictedPlayerState, ent ); } } // if we didn't touch a jump pad this pmove frame if ( cg.predictedPlayerState.jumppad_frame != cg.predictedPlayerState.pmove_framecount ) { cg.predictedPlayerState.jumppad_frame = 0; cg.predictedPlayerState.jumppad_ent = 0; } } /* ================= CG_PredictPlayerState Generates cg.predictedPlayerState for the current cg.time cg.predictedPlayerState is guaranteed to be valid after exiting. For demo playback, this will be an interpolation between two valid playerState_t. For normal gameplay, it will be the result of predicted usercmd_t on top of the most recent playerState_t received from the server. Each new snapshot will usually have one or more new usercmd over the last, but we simulate all unacknowledged commands each time, not just the new ones. This means that on an internet connection, quite a few pmoves may be issued each frame. OPTIMIZE: don't re-simulate unless the newly arrived snapshot playerState_t differs from the predicted one. Would require saving all intermediate playerState_t during prediction. We detect prediction errors and allow them to be decayed off over several frames to ease the jerk. ================= */ void CG_PredictPlayerState( void ) { int cmdNum, current; playerState_t oldPlayerState; qboolean moved; usercmd_t oldestCmd; usercmd_t latestCmd; cg.hyperspace = qfalse; // will be set if touching a trigger_teleport // if this is the first frame we must guarantee // predictedPlayerState is valid even if there is some // other error condition if ( !cg.validPPS ) { cg.validPPS = qtrue; cg.predictedPlayerState = cg.snap->ps; } // demo playback just copies the moves if ( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW) ) { CG_InterpolatePlayerState( qfalse ); return; } // non-predicting local movement will grab the latest angles if ( cg_nopredict.integer || cg_synchronousClients.integer ) { CG_InterpolatePlayerState( qtrue ); return; } // prepare for pmove cg_pmove.ps = &cg.predictedPlayerState; cg_pmove.trace = CG_Trace; cg_pmove.pointcontents = CG_PointContents; if ( cg_pmove.ps->pm_type == PM_DEAD ) { cg_pmove.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY; } else { cg_pmove.tracemask = MASK_PLAYERSOLID; } if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR ) { cg_pmove.tracemask &= ~CONTENTS_BODY; // spectators can fly through bodies } cg_pmove.noFootsteps = ( cgs.dmflags & DF_NO_FOOTSTEPS ) > 0; // save the state before the pmove so we can detect transitions oldPlayerState = cg.predictedPlayerState; current = trap_GetCurrentCmdNumber(); // if we don't have the commands right after the snapshot, we // can't accurately predict a current position, so just freeze at // the last good position we had cmdNum = current - CMD_BACKUP + 1; trap_GetUserCmd( cmdNum, &oldestCmd ); if ( oldestCmd.serverTime > cg.snap->ps.commandTime && oldestCmd.serverTime < cg.time ) { // special check for map_restart if ( cg_showmiss.integer ) { CG_Printf ("exceeded PACKET_BACKUP on commands\n"); } return; } // get the latest command so we can know which commands are from previous map_restarts trap_GetUserCmd( current, &latestCmd ); // get the most recent information we have, even if // the server time is beyond our current cg.time, // because predicted player positions are going to // be ahead of everything else anyway if ( cg.nextSnap && !cg.nextFrameTeleport && !cg.thisFrameTeleport ) { cg.predictedPlayerState = cg.nextSnap->ps; cg.physicsTime = cg.nextSnap->serverTime; } else { cg.predictedPlayerState = cg.snap->ps; cg.physicsTime = cg.snap->serverTime; } if ( pmove_msec.integer < 8 ) { trap_Cvar_Set("pmove_msec", "8"); } else if (pmove_msec.integer > 33) { trap_Cvar_Set("pmove_msec", "33"); } cg_pmove.pmove_fixed = pmove_fixed.integer;// | cg_pmove_fixed.integer; cg_pmove.pmove_msec = pmove_msec.integer; // run cmds moved = qfalse; for ( cmdNum = current - CMD_BACKUP + 1 ; cmdNum <= current ; cmdNum++ ) { // get the command trap_GetUserCmd( cmdNum, &cg_pmove.cmd ); if ( cg_pmove.pmove_fixed ) { PM_UpdateViewAngles( cg_pmove.ps, &cg_pmove.cmd ); } // don't do anything if the time is before the snapshot player time if ( cg_pmove.cmd.serverTime <= cg.predictedPlayerState.commandTime ) { continue; } // don't do anything if the command was from a previous map_restart if ( cg_pmove.cmd.serverTime > latestCmd.serverTime ) { continue; } // check for a prediction error from last frame // on a lan, this will often be the exact value // from the snapshot, but on a wan we will have // to predict several commands to get to the point // we want to compare if ( cg.predictedPlayerState.commandTime == oldPlayerState.commandTime ) { vec3_t delta; float len; if ( cg.thisFrameTeleport ) { // a teleport will not cause an error decay VectorClear( cg.predictedError ); if ( cg_showmiss.integer ) { CG_Printf( "PredictionTeleport\n" ); } cg.thisFrameTeleport = qfalse; } else { vec3_t adjusted; CG_AdjustPositionForMover( cg.predictedPlayerState.origin, cg.predictedPlayerState.groundEntityNum, cg.physicsTime, cg.oldTime, adjusted ); if ( cg_showmiss.integer ) { if (!VectorCompare( oldPlayerState.origin, adjusted )) { CG_Printf("prediction error\n"); } } VectorSubtract( oldPlayerState.origin, adjusted, delta ); len = VectorLength( delta ); if ( len > 0.1 ) { if ( cg_showmiss.integer ) { CG_Printf("Prediction miss: %f\n", len); } if ( cg_errorDecay.integer ) { int t; float f; t = cg.time - cg.predictedErrorTime; f = ( cg_errorDecay.value - t ) / cg_errorDecay.value; if ( f < 0 ) { f = 0; } if ( f > 0 && cg_showmiss.integer ) { CG_Printf("Double prediction decay: %f\n", f); } VectorScale( cg.predictedError, f, cg.predictedError ); } else { VectorClear( cg.predictedError ); } VectorAdd( delta, cg.predictedError, cg.predictedError ); cg.predictedErrorTime = cg.oldTime; } } } // don't predict gauntlet firing, which is only supposed to happen // when it actually inflicts damage cg_pmove.gauntletHit = qfalse; if ( cg_pmove.pmove_fixed ) { cg_pmove.cmd.serverTime = ((cg_pmove.cmd.serverTime + pmove_msec.integer-1) / pmove_msec.integer) * pmove_msec.integer; } Pmove (&cg_pmove); moved = qtrue; // add push trigger movement effects CG_TouchTriggerPrediction(); // check for predictable events that changed from previous predictions //CG_CheckChangedPredictableEvents(&cg.predictedPlayerState); } if ( cg_showmiss.integer > 1 ) { CG_Printf( "[%i : %i] ", cg_pmove.cmd.serverTime, cg.time ); } if ( !moved ) { if ( cg_showmiss.integer ) { CG_Printf( "not moved\n" ); } return; } // adjust for the movement of the groundentity CG_AdjustPositionForMover( cg.predictedPlayerState.origin, cg.predictedPlayerState.groundEntityNum, cg.physicsTime, cg.time, cg.predictedPlayerState.origin ); if ( cg_showmiss.integer ) { if (cg.predictedPlayerState.eventSequence > oldPlayerState.eventSequence + MAX_PS_EVENTS) { CG_Printf("WARNING: dropped event\n"); } } // fire events and other transition triggered things CG_TransitionPlayerState( &cg.predictedPlayerState, &oldPlayerState ); if ( cg_showmiss.integer ) { if (cg.eventSequence > cg.predictedPlayerState.eventSequence) { CG_Printf("WARNING: double event\n"); cg.eventSequence = cg.predictedPlayerState.eventSequence; } } } ================================================ FILE: src/cgame/cg_public.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #define CMD_BACKUP 64 #define CMD_MASK (CMD_BACKUP - 1) // allow a lot of command backups for very fast systems // multiple commands may be combined into a single packet, so this // needs to be larger than PACKET_BACKUP #define MAX_ENTITIES_IN_SNAPSHOT 256 // snapshots are a view of the server at a given time // Snapshots are generated at regular time intervals by the server, // but they may not be sent if a client's rate level is exceeded, or // they may be dropped by the network. typedef struct { int snapFlags; // SNAPFLAG_RATE_DELAYED, etc int ping; int serverTime; // server time the message is valid for (in msec) byte areamask[MAX_MAP_AREA_BYTES]; // portalarea visibility bits playerState_t ps; // complete information about the current player at this time int numEntities; // all of the entities that need to be presented entityState_t entities[MAX_ENTITIES_IN_SNAPSHOT]; // at the time of this snapshot int numServerCommands; // text based server commands to execute when this int serverCommandSequence; // snapshot becomes current } snapshot_t; enum { CGAME_EVENT_NONE, CGAME_EVENT_TEAMMENU, CGAME_EVENT_SCOREBOARD, CGAME_EVENT_EDITHUD }; /* ================================================================== functions imported from the main executable ================================================================== */ #define CGAME_IMPORT_API_VERSION 4 typedef enum { CG_PRINT, CG_ERROR, CG_MILLISECONDS, CG_CVAR_REGISTER, CG_CVAR_UPDATE, CG_CVAR_SET, CG_CVAR_VARIABLESTRINGBUFFER, CG_ARGC, CG_ARGV, CG_ARGS, CG_FS_FOPENFILE, CG_FS_READ, CG_FS_WRITE, CG_FS_FCLOSEFILE, CG_SENDCONSOLECOMMAND, CG_ADDCOMMAND, CG_SENDCLIENTCOMMAND, CG_UPDATESCREEN, CG_CM_LOADMAP, CG_CM_NUMINLINEMODELS, CG_CM_INLINEMODEL, CG_CM_LOADMODEL, CG_CM_TEMPBOXMODEL, CG_CM_POINTCONTENTS, CG_CM_TRANSFORMEDPOINTCONTENTS, CG_CM_BOXTRACE, CG_CM_TRANSFORMEDBOXTRACE, CG_CM_MARKFRAGMENTS, CG_S_STARTSOUND, CG_S_STARTLOCALSOUND, CG_S_CLEARLOOPINGSOUNDS, CG_S_ADDLOOPINGSOUND, CG_S_UPDATEENTITYPOSITION, CG_S_RESPATIALIZE, CG_S_REGISTERSOUND, CG_S_STARTBACKGROUNDTRACK, CG_R_LOADWORLDMAP, CG_R_REGISTERMODEL, CG_R_REGISTERSKIN, CG_R_REGISTERSHADER, CG_R_CLEARSCENE, CG_R_ADDREFENTITYTOSCENE, CG_R_ADDPOLYTOSCENE, CG_R_ADDLIGHTTOSCENE, CG_R_RENDERSCENE, CG_R_SETCOLOR, CG_R_DRAWSTRETCHPIC, CG_R_MODELBOUNDS, CG_R_LERPTAG, CG_GETGLCONFIG, CG_GETGAMESTATE, CG_GETCURRENTSNAPSHOTNUMBER, CG_GETSNAPSHOT, CG_GETSERVERCOMMAND, CG_GETCURRENTCMDNUMBER, CG_GETUSERCMD, CG_SETUSERCMDVALUE, CG_R_REGISTERSHADERNOMIP, CG_MEMORY_REMAINING, CG_R_REGISTERFONT, CG_KEY_ISDOWN, CG_KEY_GETCATCHER, CG_KEY_SETCATCHER, CG_KEY_GETKEY, CG_PC_ADD_GLOBAL_DEFINE, CG_PC_LOAD_SOURCE, CG_PC_FREE_SOURCE, CG_PC_READ_TOKEN, CG_PC_SOURCE_FILE_AND_LINE, CG_S_STOPBACKGROUNDTRACK, CG_REAL_TIME, CG_SNAPVECTOR, CG_REMOVECOMMAND, CG_R_LIGHTFORPOINT, CG_CIN_PLAYCINEMATIC, CG_CIN_STOPCINEMATIC, CG_CIN_RUNCINEMATIC, CG_CIN_DRAWCINEMATIC, CG_CIN_SETEXTENTS, CG_R_REMAP_SHADER, CG_S_ADDREALLOOPINGSOUND, CG_S_STOPLOOPINGSOUND, CG_CM_TEMPCAPSULEMODEL, CG_CM_CAPSULETRACE, CG_CM_TRANSFORMEDCAPSULETRACE, CG_R_ADDADDITIVELIGHTTOSCENE, CG_GET_ENTITY_TOKEN, CG_R_ADDPOLYSTOSCENE, CG_R_INPVS, // 1.32 CG_FS_SEEK, /* CG_LOADCAMERA, CG_STARTCAMERA, CG_GETCAMERAINFO, */ CG_MEMSET = 100, CG_MEMCPY, CG_STRNCPY, CG_SIN, CG_COS, CG_ATAN2, CG_SQRT, CG_FLOOR, CG_CEIL, CG_TESTPRINTINT, CG_TESTPRINTFLOAT, CG_ACOS } cgameImport_t; /* ================================================================== functions exported to the main executable ================================================================== */ typedef enum { CG_INIT, // void CG_Init( int serverMessageNum, int serverCommandSequence, int clientNum ) // called when the level loads or when the renderer is restarted // all media should be registered at this time // cgame will display loading status by calling SCR_Update, which // will call CG_DrawInformation during the loading process // reliableCommandSequence will be 0 on fresh loads, but higher for // demos, tourney restarts, or vid_restarts CG_SHUTDOWN, // void (*CG_Shutdown)( void ); // oportunity to flush and close any open files CG_CONSOLE_COMMAND, // qboolean (*CG_ConsoleCommand)( void ); // a console command has been issued locally that is not recognized by the // main game system. // use Cmd_Argc() / Cmd_Argv() to read the command, return qfalse if the // command is not known to the game CG_DRAW_ACTIVE_FRAME, // void (*CG_DrawActiveFrame)( int serverTime, stereoFrame_t stereoView, qboolean demoPlayback ); // Generates and draws a game scene and status information at the given time. // If demoPlayback is set, local movement prediction will not be enabled CG_CROSSHAIR_PLAYER, // int (*CG_CrosshairPlayer)( void ); CG_LAST_ATTACKER, // int (*CG_LastAttacker)( void ); CG_KEY_EVENT, // void (*CG_KeyEvent)( int key, qboolean down ); CG_MOUSE_EVENT, // void (*CG_MouseEvent)( int dx, int dy ); CG_EVENT_HANDLING // void (*CG_EventHandling)(int type); } cgameExport_t; //---------------------------------------------- ================================================ FILE: src/cgame/cg_scoreboard.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_scoreboard -- draw the scoreboard on top of the game screen #include "cg_local.h" #define SCOREBOARD_X (0) #define SB_HEADER 86 #define SB_TOP (SB_HEADER+32) // Where the status bar starts, so we don't overwrite it #define SB_STATUSBAR 420 #define SB_NORMAL_HEIGHT 40 #define SB_INTER_HEIGHT 16 // interleaved height #define SB_MAXCLIENTS_NORMAL ((SB_STATUSBAR - SB_TOP) / SB_NORMAL_HEIGHT) #define SB_MAXCLIENTS_INTER ((SB_STATUSBAR - SB_TOP) / SB_INTER_HEIGHT - 1) // Used when interleaved #define SB_LEFT_BOTICON_X (SCOREBOARD_X+0) #define SB_LEFT_HEAD_X (SCOREBOARD_X+32) #define SB_RIGHT_BOTICON_X (SCOREBOARD_X+64) #define SB_RIGHT_HEAD_X (SCOREBOARD_X+96) // Normal #define SB_BOTICON_X (SCOREBOARD_X+32) #define SB_HEAD_X (SCOREBOARD_X+64) #define SB_SCORELINE_X 112 #define SB_RATING_WIDTH (6 * BIGCHAR_WIDTH) // width 6 #define SB_SCORE_X (SB_SCORELINE_X + BIGCHAR_WIDTH) // width 6 #define SB_RATING_X (SB_SCORELINE_X + 6 * BIGCHAR_WIDTH) // width 6 #define SB_PING_X (SB_SCORELINE_X + 12 * BIGCHAR_WIDTH + 8) // width 5 #define SB_TIME_X (SB_SCORELINE_X + 17 * BIGCHAR_WIDTH + 8) // width 5 #define SB_NAME_X (SB_SCORELINE_X + 22 * BIGCHAR_WIDTH) // width 15 // The new and improved score board // // In cases where the number of clients is high, the score board heads are interleaved // here's the layout // // 0 32 80 112 144 240 320 400 <-- pixel position // bot head bot head score ping time name // // wins/losses are drawn on bot icon now static qboolean localClient; // true if local client has been displayed /* ================= CG_DrawScoreboard ================= */ static void CG_DrawClientScore( int y, score_t *score, float *color, float fade, qboolean largeFormat ) { char string[1024]; vec3_t headAngles; clientInfo_t *ci; int iconx, headx; if ( score->client < 0 || score->client >= cgs.maxclients ) { Com_Printf( "Bad score->client: %i\n", score->client ); return; } ci = &cgs.clientinfo[score->client]; iconx = SB_BOTICON_X + (SB_RATING_WIDTH / 2); headx = SB_HEAD_X + (SB_RATING_WIDTH / 2); // draw the handicap or bot skill marker (unless player has flag) if ( ci->powerups & ( 1 << PW_NEUTRALFLAG ) ) { if( largeFormat ) { CG_DrawFlagModel( iconx, y - ( 32 - BIGCHAR_HEIGHT ) / 2, 32, 32, TEAM_FREE, qfalse ); } else { CG_DrawFlagModel( iconx, y, 16, 16, TEAM_FREE, qfalse ); } } else if ( ci->powerups & ( 1 << PW_REDFLAG ) ) { if( largeFormat ) { CG_DrawFlagModel( iconx, y - ( 32 - BIGCHAR_HEIGHT ) / 2, 32, 32, TEAM_RED, qfalse ); } else { CG_DrawFlagModel( iconx, y, 16, 16, TEAM_RED, qfalse ); } } else if ( ci->powerups & ( 1 << PW_BLUEFLAG ) ) { if( largeFormat ) { CG_DrawFlagModel( iconx, y - ( 32 - BIGCHAR_HEIGHT ) / 2, 32, 32, TEAM_BLUE, qfalse ); } else { CG_DrawFlagModel( iconx, y, 16, 16, TEAM_BLUE, qfalse ); } } else { if ( ci->botSkill > 0 && ci->botSkill <= 5 ) { if ( cg_drawIcons.integer ) { if( largeFormat ) { CG_DrawPic( iconx, y - ( 32 - BIGCHAR_HEIGHT ) / 2, 32, 32, cgs.media.botSkillShaders[ ci->botSkill - 1 ] ); } else { CG_DrawPic( iconx, y, 16, 16, cgs.media.botSkillShaders[ ci->botSkill - 1 ] ); } } } else if ( ci->handicap < 100 ) { Com_sprintf( string, sizeof( string ), "%i", ci->handicap ); if ( cgs.gametype == GT_TOURNAMENT ) CG_DrawSmallStringColor( iconx, y - SMALLCHAR_HEIGHT/2, string, color ); else CG_DrawSmallStringColor( iconx, y, string, color ); } // draw the wins / losses if ( cgs.gametype == GT_TOURNAMENT ) { Com_sprintf( string, sizeof( string ), "%i/%i", ci->wins, ci->losses ); if( ci->handicap < 100 && !ci->botSkill ) { CG_DrawSmallStringColor( iconx, y + SMALLCHAR_HEIGHT/2, string, color ); } else { CG_DrawSmallStringColor( iconx, y, string, color ); } } } // draw the face VectorClear( headAngles ); headAngles[YAW] = 180; if( largeFormat ) { CG_DrawHead( headx, y - ( ICON_SIZE - BIGCHAR_HEIGHT ) / 2, ICON_SIZE, ICON_SIZE, score->client, headAngles ); } else { CG_DrawHead( headx, y, 16, 16, score->client, headAngles ); } // draw the score line if ( score->ping == -1 ) { Com_sprintf(string, sizeof(string), " connecting %s", ci->name); } else if ( ci->team == TEAM_SPECTATOR ) { Com_sprintf(string, sizeof(string), " SPECT %3i %4i %s", score->ping, score->time, ci->name); } else { Com_sprintf(string, sizeof(string), "%5i %4i %4i %s", score->score, score->ping, score->time, ci->name); } // highlight your position if ( score->client == cg.snap->ps.clientNum ) { float hcolor[4]; int rank; localClient = qtrue; if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR || cgs.gametype >= GT_TEAM ) { rank = -1; } else { rank = cg.snap->ps.persistant[PERS_RANK] & ~RANK_TIED_FLAG; } if ( rank == 0 ) { hcolor[0] = 0; hcolor[1] = 0; hcolor[2] = 0.7f; } else if ( rank == 1 ) { hcolor[0] = 0.7f; hcolor[1] = 0; hcolor[2] = 0; } else if ( rank == 2 ) { hcolor[0] = 0.7f; hcolor[1] = 0.7f; hcolor[2] = 0; } else { hcolor[0] = 0.7f; hcolor[1] = 0.7f; hcolor[2] = 0.7f; } hcolor[3] = fade * 0.7; CG_FillRect( SB_SCORELINE_X + BIGCHAR_WIDTH + (SB_RATING_WIDTH / 2), y, 640 - SB_SCORELINE_X - BIGCHAR_WIDTH, BIGCHAR_HEIGHT+1, hcolor ); } CG_DrawBigString( SB_SCORELINE_X + (SB_RATING_WIDTH / 2), y, string, fade ); // add the "ready" marker for intermission exiting if ( cg.snap->ps.stats[ STAT_CLIENTS_READY ] & ( 1 << score->client ) ) { CG_DrawBigStringColor( iconx, y, "READY", color ); } } /* ================= CG_TeamScoreboard ================= */ static int CG_TeamScoreboard( int y, team_t team, float fade, int maxClients, int lineHeight ) { int i; score_t *score; float color[4]; int count; clientInfo_t *ci; color[0] = color[1] = color[2] = 1.0; color[3] = fade; count = 0; for ( i = 0 ; i < cg.numScores && count < maxClients ; i++ ) { score = &cg.scores[i]; ci = &cgs.clientinfo[ score->client ]; if ( team != ci->team ) { continue; } CG_DrawClientScore( y + lineHeight * count, score, color, fade, lineHeight == SB_NORMAL_HEIGHT ); count++; } return count; } /* ================= CG_DrawScoreboard Draw the normal in-game scoreboard ================= */ qboolean CG_DrawOldScoreboard( void ) { int x, y, w, i, n1, n2; float fade; float *fadeColor; char *s; int maxClients; int lineHeight; int topBorderSize, bottomBorderSize; // don't draw amuthing if the menu or console is up if ( cg_paused.integer ) { cg.deferredPlayerLoading = 0; return qfalse; } if ( cgs.gametype == GT_SINGLE_PLAYER && cg.predictedPlayerState.pm_type == PM_INTERMISSION ) { cg.deferredPlayerLoading = 0; return qfalse; } // don't draw scoreboard during death while warmup up if ( cg.warmup && !cg.showScores ) { return qfalse; } if ( cg.showScores || cg.predictedPlayerState.pm_type == PM_DEAD || cg.predictedPlayerState.pm_type == PM_INTERMISSION ) { fade = 1.0; fadeColor = colorWhite; } else { fadeColor = CG_FadeColor( cg.scoreFadeTime, FADE_TIME ); if ( !fadeColor ) { // next time scoreboard comes up, don't print killer cg.deferredPlayerLoading = 0; cg.killerName[0] = 0; return qfalse; } fade = *fadeColor; } // fragged by ... line if ( cg.killerName[0] ) { s = va("Fragged by %s", cg.killerName ); w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; x = ( SCREEN_WIDTH - w ) / 2; y = 40; CG_DrawBigString( x, y, s, fade ); } // current rank if ( cgs.gametype < GT_TEAM) { if (cg.snap->ps.persistant[PERS_TEAM] != TEAM_SPECTATOR ) { s = va("%s place with %i", CG_PlaceString( cg.snap->ps.persistant[PERS_RANK] + 1 ), cg.snap->ps.persistant[PERS_SCORE] ); w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; x = ( SCREEN_WIDTH - w ) / 2; y = 60; CG_DrawBigString( x, y, s, fade ); } } else { if ( cg.teamScores[0] == cg.teamScores[1] ) { s = va("Teams are tied at %i", cg.teamScores[0] ); } else if ( cg.teamScores[0] >= cg.teamScores[1] ) { s = va("Red leads %i to %i",cg.teamScores[0], cg.teamScores[1] ); } else { s = va("Blue leads %i to %i",cg.teamScores[1], cg.teamScores[0] ); } w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; x = ( SCREEN_WIDTH - w ) / 2; y = 60; CG_DrawBigString( x, y, s, fade ); } // scoreboard y = SB_HEADER; CG_DrawPic( SB_SCORE_X + (SB_RATING_WIDTH / 2), y, 64, 32, cgs.media.scoreboardScore ); CG_DrawPic( SB_PING_X - (SB_RATING_WIDTH / 2), y, 64, 32, cgs.media.scoreboardPing ); CG_DrawPic( SB_TIME_X - (SB_RATING_WIDTH / 2), y, 64, 32, cgs.media.scoreboardTime ); CG_DrawPic( SB_NAME_X - (SB_RATING_WIDTH / 2), y, 64, 32, cgs.media.scoreboardName ); y = SB_TOP; // If there are more than SB_MAXCLIENTS_NORMAL, use the interleaved scores if ( cg.numScores > SB_MAXCLIENTS_NORMAL ) { maxClients = SB_MAXCLIENTS_INTER; lineHeight = SB_INTER_HEIGHT; topBorderSize = 8; bottomBorderSize = 16; } else { maxClients = SB_MAXCLIENTS_NORMAL; lineHeight = SB_NORMAL_HEIGHT; topBorderSize = 16; bottomBorderSize = 16; } localClient = qfalse; if ( cgs.gametype >= GT_TEAM ) { // // teamplay scoreboard // y += lineHeight/2; if ( cg.teamScores[0] >= cg.teamScores[1] ) { n1 = CG_TeamScoreboard( y, TEAM_RED, fade, maxClients, lineHeight ); CG_DrawTeamBackground( 0, y - topBorderSize, 640, n1 * lineHeight + bottomBorderSize, 0.33f, TEAM_RED ); y += (n1 * lineHeight) + BIGCHAR_HEIGHT; maxClients -= n1; n2 = CG_TeamScoreboard( y, TEAM_BLUE, fade, maxClients, lineHeight ); CG_DrawTeamBackground( 0, y - topBorderSize, 640, n2 * lineHeight + bottomBorderSize, 0.33f, TEAM_BLUE ); y += (n2 * lineHeight) + BIGCHAR_HEIGHT; maxClients -= n2; } else { n1 = CG_TeamScoreboard( y, TEAM_BLUE, fade, maxClients, lineHeight ); CG_DrawTeamBackground( 0, y - topBorderSize, 640, n1 * lineHeight + bottomBorderSize, 0.33f, TEAM_BLUE ); y += (n1 * lineHeight) + BIGCHAR_HEIGHT; maxClients -= n1; n2 = CG_TeamScoreboard( y, TEAM_RED, fade, maxClients, lineHeight ); CG_DrawTeamBackground( 0, y - topBorderSize, 640, n2 * lineHeight + bottomBorderSize, 0.33f, TEAM_RED ); y += (n2 * lineHeight) + BIGCHAR_HEIGHT; maxClients -= n2; } n1 = CG_TeamScoreboard( y, TEAM_SPECTATOR, fade, maxClients, lineHeight ); y += (n1 * lineHeight) + BIGCHAR_HEIGHT; } else { // // free for all scoreboard // n1 = CG_TeamScoreboard( y, TEAM_FREE, fade, maxClients, lineHeight ); y += (n1 * lineHeight) + BIGCHAR_HEIGHT; n2 = CG_TeamScoreboard( y, TEAM_SPECTATOR, fade, maxClients - n1, lineHeight ); y += (n2 * lineHeight) + BIGCHAR_HEIGHT; } if (!localClient) { // draw local client at the bottom for ( i = 0 ; i < cg.numScores ; i++ ) { if ( cg.scores[i].client == cg.snap->ps.clientNum ) { CG_DrawClientScore( y, &cg.scores[i], fadeColor, fade, lineHeight == SB_NORMAL_HEIGHT ); break; } } } // load any models that have been deferred if ( ++cg.deferredPlayerLoading > 10 ) { CG_LoadDeferredPlayers(); } return qtrue; } //================================================================================ /* ================ CG_CenterGiantLine ================ */ static void CG_CenterGiantLine( float y, const char *string ) { float x; vec4_t color; color[0] = 1; color[1] = 1; color[2] = 1; color[3] = 1; x = 0.5 * ( 640 - GIANT_WIDTH * CG_DrawStrlen( string ) ); CG_DrawStringExt( x, y, string, color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 ); } /* ================= CG_DrawTourneyScoreboard Draw the oversize scoreboard for tournements ================= */ void CG_DrawOldTourneyScoreboard( void ) { const char *s; vec4_t color; int min, tens, ones; clientInfo_t *ci; int y; int i; // request more scores regularly if ( cg.scoresRequestTime + 2000 < cg.time ) { cg.scoresRequestTime = cg.time; trap_SendClientCommand( "score" ); } color[0] = 1; color[1] = 1; color[2] = 1; color[3] = 1; // draw the dialog background color[0] = color[1] = color[2] = 0; color[3] = 1; CG_FillRect( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, color ); // print the mesage of the day s = CG_ConfigString( CS_MOTD ); if ( !s[0] ) { s = "Scoreboard"; } // print optional title CG_CenterGiantLine( 8, s ); // print server time ones = cg.time / 1000; min = ones / 60; ones %= 60; tens = ones / 10; ones %= 10; s = va("%i:%i%i", min, tens, ones ); CG_CenterGiantLine( 64, s ); // print the two scores y = 160; if ( cgs.gametype >= GT_TEAM ) { // // teamplay scoreboard // CG_DrawStringExt( 8, y, "Red Team", color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 ); s = va("%i", cg.teamScores[0] ); CG_DrawStringExt( 632 - GIANT_WIDTH * (int)strlen(s), y, s, color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 ); y += 64; CG_DrawStringExt( 8, y, "Blue Team", color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 ); s = va("%i", cg.teamScores[1] ); CG_DrawStringExt( 632 - GIANT_WIDTH * (int)strlen(s), y, s, color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 ); } else { // // free for all scoreboard // for ( i = 0 ; i < MAX_CLIENTS ; i++ ) { ci = &cgs.clientinfo[i]; if ( !ci->infoValid ) { continue; } if ( ci->team != TEAM_FREE ) { continue; } CG_DrawStringExt( 8, y, ci->name, color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 ); s = va("%i", ci->score ); CG_DrawStringExt( 632 - GIANT_WIDTH * (int)strlen(s), y, s, color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 ); y += 64; } } } ================================================ FILE: src/cgame/cg_servercmds.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_servercmds.c -- reliably sequenced text commands sent by the server // these are processed at snapshot transition time, so there will definately // be a valid snapshot this frame #include "cg_local.h" /* ================= CG_ParseScores ================= */ static void CG_ParseScores( void ) { int i, powerups; cg.numScores = atoi( CG_Argv( 1 ) ); if ( cg.numScores > MAX_CLIENTS ) { cg.numScores = MAX_CLIENTS; } cg.teamScores[0] = atoi( CG_Argv( 2 ) ); cg.teamScores[1] = atoi( CG_Argv( 3 ) ); memset( cg.scores, 0, sizeof( cg.scores ) ); for ( i = 0 ; i < cg.numScores ; i++ ) { // cg.scores[i].client = atoi( CG_Argv( i * 14 + 4 ) ); cg.scores[i].score = atoi( CG_Argv( i * 14 + 5 ) ); cg.scores[i].ping = atoi( CG_Argv( i * 14 + 6 ) ); cg.scores[i].time = atoi( CG_Argv( i * 14 + 7 ) ); cg.scores[i].scoreFlags = atoi( CG_Argv( i * 14 + 8 ) ); powerups = atoi( CG_Argv( i * 14 + 9 ) ); cg.scores[i].accuracy = atoi(CG_Argv(i * 14 + 10)); cg.scores[i].impressiveCount = atoi(CG_Argv(i * 14 + 11)); cg.scores[i].excellentCount = atoi(CG_Argv(i * 14 + 12)); cg.scores[i].guantletCount = atoi(CG_Argv(i * 14 + 13)); cg.scores[i].defendCount = atoi(CG_Argv(i * 14 + 14)); cg.scores[i].assistCount = atoi(CG_Argv(i * 14 + 15)); cg.scores[i].perfect = atoi(CG_Argv(i * 14 + 16)); cg.scores[i].captures = atoi(CG_Argv(i * 14 + 17)); if ( cg.scores[i].client < 0 || cg.scores[i].client >= MAX_CLIENTS ) { cg.scores[i].client = 0; } cgs.clientinfo[ cg.scores[i].client ].score = cg.scores[i].score; cgs.clientinfo[ cg.scores[i].client ].powerups = powerups; cg.scores[i].team = cgs.clientinfo[cg.scores[i].client].team; } } /* ================= CG_ParseTeamInfo ================= */ static void CG_ParseTeamInfo( void ) { int i; int client; numSortedTeamPlayers = atoi( CG_Argv( 1 ) ); for ( i = 0 ; i < numSortedTeamPlayers ; i++ ) { client = atoi( CG_Argv( i * 6 + 2 ) ); sortedTeamPlayers[i] = client; cgs.clientinfo[ client ].location = atoi( CG_Argv( i * 6 + 3 ) ); cgs.clientinfo[ client ].health = atoi( CG_Argv( i * 6 + 4 ) ); cgs.clientinfo[ client ].armor = atoi( CG_Argv( i * 6 + 5 ) ); cgs.clientinfo[ client ].curWeapon = atoi( CG_Argv( i * 6 + 6 ) ); cgs.clientinfo[ client ].powerups = atoi( CG_Argv( i * 6 + 7 ) ); } } /* ================ CG_ParseServerinfo This is called explicitly when the gamestate is first received, and whenever the server updates any serverinfo flagged cvars ================ */ void CG_ParseServerinfo( void ) { const char *info; char *mapname; info = CG_ConfigString( CS_SERVERINFO ); cgs.gametype = atoi( Info_ValueForKey( info, "g_gametype" ) ); trap_Cvar_Set("g_gametype", va("%i", cgs.gametype)); cgs.dmflags = atoi( Info_ValueForKey( info, "dmflags" ) ); cgs.teamflags = atoi( Info_ValueForKey( info, "teamflags" ) ); cgs.fraglimit = atoi( Info_ValueForKey( info, "fraglimit" ) ); cgs.capturelimit = atoi( Info_ValueForKey( info, "capturelimit" ) ); cgs.timelimit = atoi( Info_ValueForKey( info, "timelimit" ) ); cgs.maxclients = atoi( Info_ValueForKey( info, "sv_maxclients" ) ); mapname = Info_ValueForKey( info, "mapname" ); Com_sprintf( cgs.mapname, sizeof( cgs.mapname ), "maps/%s.bsp", mapname ); Q_strncpyz( cgs.redTeam, Info_ValueForKey( info, "g_redTeam" ), sizeof(cgs.redTeam) ); trap_Cvar_Set("g_redTeam", cgs.redTeam); Q_strncpyz( cgs.blueTeam, Info_ValueForKey( info, "g_blueTeam" ), sizeof(cgs.blueTeam) ); trap_Cvar_Set("g_blueTeam", cgs.blueTeam); } /* ================== CG_ParseWarmup ================== */ static void CG_ParseWarmup( void ) { const char *info; int warmup; info = CG_ConfigString( CS_WARMUP ); warmup = atoi( info ); cg.warmupCount = -1; if ( warmup == 0 && cg.warmup ) { } else if ( warmup > 0 && cg.warmup <= 0 ) { { trap_S_StartLocalSound( cgs.media.countPrepareSound, CHAN_ANNOUNCER ); } } cg.warmup = warmup; } /* ================ CG_SetConfigValues Called on load to set the initial values from configure strings ================ */ void CG_SetConfigValues( void ) { const char *s; cgs.scores1 = atoi( CG_ConfigString( CS_SCORES1 ) ); cgs.scores2 = atoi( CG_ConfigString( CS_SCORES2 ) ); cgs.levelStartTime = atoi( CG_ConfigString( CS_LEVEL_START_TIME ) ); if( cgs.gametype == GT_CTF ) { s = CG_ConfigString( CS_FLAGSTATUS ); cgs.redflag = s[0] - '0'; cgs.blueflag = s[1] - '0'; } cg.warmup = atoi( CG_ConfigString( CS_WARMUP ) ); } /* ===================== CG_ShaderStateChanged ===================== */ void CG_ShaderStateChanged(void) { char originalShader[MAX_QPATH]; char newShader[MAX_QPATH]; char timeOffset[16]; const char *o; char *n,*t; o = CG_ConfigString( CS_SHADERSTATE ); while (o && *o) { n = strstr(o, "="); if (n && *n) { strncpy(originalShader, o, n-o); originalShader[n-o] = 0; n++; t = strstr(n, ":"); if (t && *t) { strncpy(newShader, n, t-n); newShader[t-n] = 0; } else { break; } t++; o = strstr(t, "@"); if (o) { strncpy(timeOffset, t, o-t); timeOffset[o-t] = 0; o++; trap_R_RemapShader( originalShader, newShader, timeOffset ); } } else { break; } } } /* ================ CG_ConfigStringModified ================ */ static void CG_ConfigStringModified( void ) { const char *str; int num; num = atoi( CG_Argv( 1 ) ); // get the gamestate from the client system, which will have the // new configstring already integrated trap_GetGameState( &cgs.gameState ); // look up the individual string that was modified str = CG_ConfigString( num ); // do something with it if necessary if ( num == CS_MUSIC ) { CG_StartMusic(); } else if ( num == CS_SERVERINFO ) { CG_ParseServerinfo(); } else if ( num == CS_WARMUP ) { CG_ParseWarmup(); } else if ( num == CS_SCORES1 ) { cgs.scores1 = atoi( str ); } else if ( num == CS_SCORES2 ) { cgs.scores2 = atoi( str ); } else if ( num == CS_LEVEL_START_TIME ) { cgs.levelStartTime = atoi( str ); } else if ( num == CS_VOTE_TIME ) { cgs.voteTime = atoi( str ); cgs.voteModified = qtrue; } else if ( num == CS_VOTE_YES ) { cgs.voteYes = atoi( str ); cgs.voteModified = qtrue; } else if ( num == CS_VOTE_NO ) { cgs.voteNo = atoi( str ); cgs.voteModified = qtrue; } else if ( num == CS_VOTE_STRING ) { Q_strncpyz( cgs.voteString, str, sizeof( cgs.voteString ) ); } else if ( num >= CS_TEAMVOTE_TIME && num <= CS_TEAMVOTE_TIME + 1) { cgs.teamVoteTime[num-CS_TEAMVOTE_TIME] = atoi( str ); cgs.teamVoteModified[num-CS_TEAMVOTE_TIME] = qtrue; } else if ( num >= CS_TEAMVOTE_YES && num <= CS_TEAMVOTE_YES + 1) { cgs.teamVoteYes[num-CS_TEAMVOTE_YES] = atoi( str ); cgs.teamVoteModified[num-CS_TEAMVOTE_YES] = qtrue; } else if ( num >= CS_TEAMVOTE_NO && num <= CS_TEAMVOTE_NO + 1) { cgs.teamVoteNo[num-CS_TEAMVOTE_NO] = atoi( str ); cgs.teamVoteModified[num-CS_TEAMVOTE_NO] = qtrue; } else if ( num >= CS_TEAMVOTE_STRING && num <= CS_TEAMVOTE_STRING + 1) { Q_strncpyz( cgs.teamVoteString[num-CS_TEAMVOTE_STRING], str, sizeof( cgs.teamVoteString ) ); } else if ( num == CS_INTERMISSION ) { cg.intermissionStarted = atoi( str ); } else if ( num >= CS_MODELS && num < CS_MODELS+MAX_MODELS ) { cgs.gameModels[ num-CS_MODELS ] = trap_R_RegisterModel( str ); } else if ( num >= CS_SOUNDS && num < CS_SOUNDS+MAX_MODELS ) { if ( str[0] != '*' ) { // player specific sounds don't register here cgs.gameSounds[ num-CS_SOUNDS] = trap_S_RegisterSound( str, qfalse ); } } else if ( num >= CS_PLAYERS && num < CS_PLAYERS+MAX_CLIENTS ) { CG_NewClientInfo( num - CS_PLAYERS ); CG_BuildSpectatorString(); } else if ( num == CS_FLAGSTATUS ) { if( cgs.gametype == GT_CTF ) { // format is rb where its red/blue, 0 is at base, 1 is taken, 2 is dropped cgs.redflag = str[0] - '0'; cgs.blueflag = str[1] - '0'; } } else if ( num == CS_SHADERSTATE ) { CG_ShaderStateChanged(); } } /* ======================= CG_AddToTeamChat ======================= */ static void CG_AddToTeamChat( const char *str ) { int len; char *p, *ls; int lastcolor; int chatHeight; if (cg_teamChatHeight.integer < TEAMCHAT_HEIGHT) { chatHeight = cg_teamChatHeight.integer; } else { chatHeight = TEAMCHAT_HEIGHT; } if (chatHeight <= 0 || cg_teamChatTime.integer <= 0) { // team chat disabled, dump into normal chat cgs.teamChatPos = cgs.teamLastChatPos = 0; return; } len = 0; p = cgs.teamChatMsgs[cgs.teamChatPos % chatHeight]; *p = 0; lastcolor = '7'; ls = NULL; while (*str) { if (len > TEAMCHAT_WIDTH - 1) { if (ls) { str -= (p - ls); str++; p -= (p - ls); } *p = 0; cgs.teamChatMsgTimes[cgs.teamChatPos % chatHeight] = cg.time; cgs.teamChatPos++; p = cgs.teamChatMsgs[cgs.teamChatPos % chatHeight]; *p = 0; *p++ = Q_COLOR_ESCAPE; *p++ = lastcolor; len = 0; ls = NULL; } if ( Q_IsColorString( str ) ) { *p++ = *str++; lastcolor = *str; *p++ = *str++; continue; } if (*str == ' ') { ls = p; } *p++ = *str++; len++; } *p = 0; cgs.teamChatMsgTimes[cgs.teamChatPos % chatHeight] = cg.time; cgs.teamChatPos++; if (cgs.teamChatPos - cgs.teamLastChatPos > chatHeight) cgs.teamLastChatPos = cgs.teamChatPos - chatHeight; } /* =============== CG_MapRestart The server has issued a map_restart, so the next snapshot is completely new and should not be interpolated to. A tournement restart will clear everything, but doesn't require a reload of all the media =============== */ static void CG_MapRestart( void ) { if ( cg_showmiss.integer ) { CG_Printf( "CG_MapRestart\n" ); } CG_InitLocalEntities(); CG_InitMarkPolys(); CG_ClearParticles (); // make sure the "3 frags left" warnings play again cg.fraglimitWarnings = 0; cg.timelimitWarnings = 0; cg.intermissionStarted = qfalse; cgs.voteTime = 0; cg.mapRestart = qtrue; CG_StartMusic(); trap_S_ClearLoopingSounds(qtrue); // we really should clear more parts of cg here and stop sounds // play the "fight" sound if this is a restart without warmup if ( cg.warmup == 0 /* && cgs.gametype == GT_TOURNAMENT */) { trap_S_StartLocalSound( cgs.media.countFightSound, CHAN_ANNOUNCER ); CG_CenterPrint( "FIGHT!", 120, GIANTCHAR_WIDTH*2 ); } trap_Cvar_Set("cg_thirdPerson", "0"); } #define MAX_VOICEFILESIZE 16384 #define MAX_VOICEFILES 8 #define MAX_VOICECHATS 64 #define MAX_VOICESOUNDS 64 #define MAX_CHATSIZE 64 #define MAX_HEADMODELS 64 typedef struct voiceChat_s { char id[64]; int numSounds; sfxHandle_t sounds[MAX_VOICESOUNDS]; char chats[MAX_VOICESOUNDS][MAX_CHATSIZE]; } voiceChat_t; typedef struct voiceChatList_s { char name[64]; int gender; int numVoiceChats; voiceChat_t voiceChats[MAX_VOICECHATS]; } voiceChatList_t; typedef struct headModelVoiceChat_s { char headmodel[64]; int voiceChatNum; } headModelVoiceChat_t; voiceChatList_t voiceChatLists[MAX_VOICEFILES]; headModelVoiceChat_t headModelVoiceChat[MAX_HEADMODELS]; /* ================= CG_ParseVoiceChats ================= */ int CG_ParseVoiceChats( const char *filename, voiceChatList_t *voiceChatList, int maxVoiceChats ) { int len, i; fileHandle_t f; char buf[MAX_VOICEFILESIZE]; char **p, *ptr; char *token; voiceChat_t *voiceChats; qboolean compress; sfxHandle_t sound; compress = qtrue; if (cg_buildScript.integer) { compress = qfalse; } len = trap_FS_FOpenFile( filename, &f, FS_READ ); if ( !f ) { trap_Print( va( S_COLOR_RED "voice chat file not found: %s\n", filename ) ); return qfalse; } if ( len >= MAX_VOICEFILESIZE ) { trap_Print( va( S_COLOR_RED "voice chat file too large: %s is %i, max allowed is %i", filename, len, MAX_VOICEFILESIZE ) ); trap_FS_FCloseFile( f ); return qfalse; } trap_FS_Read( buf, len, f ); buf[len] = 0; trap_FS_FCloseFile( f ); ptr = buf; p = &ptr; Com_sprintf(voiceChatList->name, sizeof(voiceChatList->name), "%s", filename); voiceChats = voiceChatList->voiceChats; for ( i = 0; i < maxVoiceChats; i++ ) { voiceChats[i].id[0] = 0; } token = COM_ParseExt(p, qtrue); if (!token || token[0] == 0) { return qtrue; } if (!Q_stricmp(token, "female")) { voiceChatList->gender = GENDER_FEMALE; } else if (!Q_stricmp(token, "male")) { voiceChatList->gender = GENDER_MALE; } else if (!Q_stricmp(token, "neuter")) { voiceChatList->gender = GENDER_NEUTER; } else { trap_Print( va( S_COLOR_RED "expected gender not found in voice chat file: %s\n", filename ) ); return qfalse; } voiceChatList->numVoiceChats = 0; while ( 1 ) { token = COM_ParseExt(p, qtrue); if (!token || token[0] == 0) { return qtrue; } Com_sprintf(voiceChats[voiceChatList->numVoiceChats].id, sizeof( voiceChats[voiceChatList->numVoiceChats].id ), "%s", token); token = COM_ParseExt(p, qtrue); if (Q_stricmp(token, "{")) { trap_Print( va( S_COLOR_RED "expected { found %s in voice chat file: %s\n", token, filename ) ); return qfalse; } voiceChats[voiceChatList->numVoiceChats].numSounds = 0; while(1) { token = COM_ParseExt(p, qtrue); if (!token || token[0] == 0) { return qtrue; } if (!Q_stricmp(token, "}")) break; sound = trap_S_RegisterSound( token, compress ); voiceChats[voiceChatList->numVoiceChats].sounds[voiceChats[voiceChatList->numVoiceChats].numSounds] = sound; token = COM_ParseExt(p, qtrue); if (!token || token[0] == 0) { return qtrue; } Com_sprintf(voiceChats[voiceChatList->numVoiceChats].chats[ voiceChats[voiceChatList->numVoiceChats].numSounds], MAX_CHATSIZE, "%s", token); if (sound) voiceChats[voiceChatList->numVoiceChats].numSounds++; if (voiceChats[voiceChatList->numVoiceChats].numSounds >= MAX_VOICESOUNDS) break; } voiceChatList->numVoiceChats++; if (voiceChatList->numVoiceChats >= maxVoiceChats) return qtrue; } return qtrue; } /* ================= CG_LoadVoiceChats ================= */ void CG_LoadVoiceChats( void ) { int size; size = trap_MemoryRemaining(); CG_ParseVoiceChats( "scripts/female1.voice", &voiceChatLists[0], MAX_VOICECHATS ); CG_ParseVoiceChats( "scripts/female2.voice", &voiceChatLists[1], MAX_VOICECHATS ); CG_ParseVoiceChats( "scripts/female3.voice", &voiceChatLists[2], MAX_VOICECHATS ); CG_ParseVoiceChats( "scripts/male1.voice", &voiceChatLists[3], MAX_VOICECHATS ); CG_ParseVoiceChats( "scripts/male2.voice", &voiceChatLists[4], MAX_VOICECHATS ); CG_ParseVoiceChats( "scripts/male3.voice", &voiceChatLists[5], MAX_VOICECHATS ); CG_ParseVoiceChats( "scripts/male4.voice", &voiceChatLists[6], MAX_VOICECHATS ); CG_ParseVoiceChats( "scripts/male5.voice", &voiceChatLists[7], MAX_VOICECHATS ); CG_Printf("voice chat memory size = %d\n", size - trap_MemoryRemaining()); } /* ================= CG_HeadModelVoiceChats ================= */ int CG_HeadModelVoiceChats( char *filename ) { int len, i; fileHandle_t f; char buf[MAX_VOICEFILESIZE]; char **p, *ptr; char *token; len = trap_FS_FOpenFile( filename, &f, FS_READ ); if ( !f ) { //trap_Print( va( "voice chat file not found: %s\n", filename ) ); return -1; } if ( len >= MAX_VOICEFILESIZE ) { trap_Print( va( S_COLOR_RED "voice chat file too large: %s is %i, max allowed is %i", filename, len, MAX_VOICEFILESIZE ) ); trap_FS_FCloseFile( f ); return -1; } trap_FS_Read( buf, len, f ); buf[len] = 0; trap_FS_FCloseFile( f ); ptr = buf; p = &ptr; token = COM_ParseExt(p, qtrue); if (!token || token[0] == 0) { return -1; } for ( i = 0; i < MAX_VOICEFILES; i++ ) { if ( !Q_stricmp(token, voiceChatLists[i].name) ) { return i; } } //FIXME: maybe try to load the .voice file which name is stored in token? return -1; } /* ================= CG_GetVoiceChat ================= */ int CG_GetVoiceChat( voiceChatList_t *voiceChatList, const char *id, sfxHandle_t *snd, char **chat) { int i, rnd; for ( i = 0; i < voiceChatList->numVoiceChats; i++ ) { if ( !Q_stricmp( id, voiceChatList->voiceChats[i].id ) ) { rnd = random() * voiceChatList->voiceChats[i].numSounds; *snd = voiceChatList->voiceChats[i].sounds[rnd]; *chat = voiceChatList->voiceChats[i].chats[rnd]; return qtrue; } } return qfalse; } /* ================= CG_VoiceChatListForClient ================= */ voiceChatList_t *CG_VoiceChatListForClient( int clientNum ) { clientInfo_t *ci; int voiceChatNum, i, j, k, gender; char filename[MAX_QPATH], headModelName[MAX_QPATH]; if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) { clientNum = 0; } ci = &cgs.clientinfo[ clientNum ]; for ( k = 0; k < 2; k++ ) { if ( k == 0 ) { if (ci->headModelName[0] == '*') { Com_sprintf( headModelName, sizeof(headModelName), "%s/%s", ci->headModelName+1, ci->headSkinName ); } else { Com_sprintf( headModelName, sizeof(headModelName), "%s/%s", ci->headModelName, ci->headSkinName ); } } else { if (ci->headModelName[0] == '*') { Com_sprintf( headModelName, sizeof(headModelName), "%s", ci->headModelName+1 ); } else { Com_sprintf( headModelName, sizeof(headModelName), "%s", ci->headModelName ); } } // find the voice file for the head model the client uses for ( i = 0; i < MAX_HEADMODELS; i++ ) { if (!Q_stricmp(headModelVoiceChat[i].headmodel, headModelName)) { break; } } if (i < MAX_HEADMODELS) { return &voiceChatLists[headModelVoiceChat[i].voiceChatNum]; } // find a .vc file for ( i = 0; i < MAX_HEADMODELS; i++ ) { if (!strlen(headModelVoiceChat[i].headmodel)) { Com_sprintf(filename, sizeof(filename), "scripts/%s.vc", headModelName); voiceChatNum = CG_HeadModelVoiceChats(filename); if (voiceChatNum == -1) break; Com_sprintf(headModelVoiceChat[i].headmodel, sizeof ( headModelVoiceChat[i].headmodel ), "%s", headModelName); headModelVoiceChat[i].voiceChatNum = voiceChatNum; return &voiceChatLists[headModelVoiceChat[i].voiceChatNum]; } } } gender = ci->gender; for (k = 0; k < 2; k++) { // just pick the first with the right gender for ( i = 0; i < MAX_VOICEFILES; i++ ) { if (strlen(voiceChatLists[i].name)) { if (voiceChatLists[i].gender == gender) { // store this head model with voice chat for future reference for ( j = 0; j < MAX_HEADMODELS; j++ ) { if (!strlen(headModelVoiceChat[j].headmodel)) { Com_sprintf(headModelVoiceChat[j].headmodel, sizeof ( headModelVoiceChat[j].headmodel ), "%s", headModelName); headModelVoiceChat[j].voiceChatNum = i; break; } } return &voiceChatLists[i]; } } } // fall back to male gender because we don't have neuter in the mission pack if (gender == GENDER_MALE) break; gender = GENDER_MALE; } // store this head model with voice chat for future reference for ( j = 0; j < MAX_HEADMODELS; j++ ) { if (!strlen(headModelVoiceChat[j].headmodel)) { Com_sprintf(headModelVoiceChat[j].headmodel, sizeof ( headModelVoiceChat[j].headmodel ), "%s", headModelName); headModelVoiceChat[j].voiceChatNum = 0; break; } } // just return the first voice chat list return &voiceChatLists[0]; } #define MAX_VOICECHATBUFFER 32 typedef struct bufferedVoiceChat_s { int clientNum; sfxHandle_t snd; int voiceOnly; char cmd[MAX_SAY_TEXT]; char message[MAX_SAY_TEXT]; } bufferedVoiceChat_t; bufferedVoiceChat_t voiceChatBuffer[MAX_VOICECHATBUFFER]; /* ================= CG_PlayVoiceChat ================= */ void CG_PlayVoiceChat( bufferedVoiceChat_t *vchat ) { } /* ===================== CG_PlayBufferedVoieChats ===================== */ void CG_PlayBufferedVoiceChats( void ) { } /* ===================== CG_AddBufferedVoiceChat ===================== */ void CG_AddBufferedVoiceChat( bufferedVoiceChat_t *vchat ) { } /* ================= CG_VoiceChatLocal ================= */ void CG_VoiceChatLocal( int mode, qboolean voiceOnly, int clientNum, int color, const char *cmd ) { } /* ================= CG_VoiceChat ================= */ void CG_VoiceChat( int mode ) { } /* ================= CG_RemoveChatEscapeChar ================= */ static void CG_RemoveChatEscapeChar( char *text ) { int i, l; l = 0; for ( i = 0; text[i]; i++ ) { if (text[i] == '\x19') continue; text[l++] = text[i]; } text[l] = '\0'; } /* ================= CG_ServerCommand The string has been tokenized and can be retrieved with Cmd_Argc() / Cmd_Argv() ================= */ static void CG_ServerCommand( void ) { const char *cmd; char text[MAX_SAY_TEXT]; cmd = CG_Argv(0); if ( !cmd[0] ) { // server claimed the command return; } if ( !strcmp( cmd, "cp" ) ) { CG_CenterPrint( CG_Argv(1), SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH ); return; } if ( !strcmp( cmd, "cs" ) ) { CG_ConfigStringModified(); return; } if ( !strcmp( cmd, "print" ) ) { CG_Printf( "%s", CG_Argv(1) ); return; } if ( !strcmp( cmd, "chat" ) ) { if ( !cg_teamChatsOnly.integer ) { trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND ); Q_strncpyz( text, CG_Argv(1), MAX_SAY_TEXT ); CG_RemoveChatEscapeChar( text ); CG_Printf( "%s\n", text ); } return; } if ( !strcmp( cmd, "tchat" ) ) { trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND ); Q_strncpyz( text, CG_Argv(1), MAX_SAY_TEXT ); CG_RemoveChatEscapeChar( text ); CG_AddToTeamChat( text ); CG_Printf( "%s\n", text ); return; } if ( !strcmp( cmd, "vchat" ) ) { CG_VoiceChat( SAY_ALL ); return; } if ( !strcmp( cmd, "vtchat" ) ) { CG_VoiceChat( SAY_TEAM ); return; } if ( !strcmp( cmd, "vtell" ) ) { CG_VoiceChat( SAY_TELL ); return; } if ( !strcmp( cmd, "scores" ) ) { CG_ParseScores(); return; } if ( !strcmp( cmd, "tinfo" ) ) { CG_ParseTeamInfo(); return; } if ( !strcmp( cmd, "map_restart" ) ) { CG_MapRestart(); return; } if ( Q_stricmp (cmd, "remapShader") == 0 ) { if (trap_Argc() == 4) { trap_R_RemapShader(CG_Argv(1), CG_Argv(2), CG_Argv(3)); } } // loaddeferred can be both a servercmd and a consolecmd if ( !strcmp( cmd, "loaddefered" ) ) { // FIXME: spelled wrong, but not changing for demo CG_LoadDeferredPlayers(); return; } // clientLevelShot is sent before taking a special screenshot for // the menu system during development if ( !strcmp( cmd, "clientLevelShot" ) ) { cg.levelShot = qtrue; return; } CG_Printf( "Unknown client game command: %s\n", cmd ); } /* ==================== CG_ExecuteNewServerCommands Execute all of the server commands that were received along with this this snapshot. ==================== */ void CG_ExecuteNewServerCommands( int latestSequence ) { while ( cgs.serverCommandSequence < latestSequence ) { if ( trap_GetServerCommand( ++cgs.serverCommandSequence ) ) { CG_ServerCommand(); } } } ================================================ FILE: src/cgame/cg_snapshot.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_snapshot.c -- things that happen on snapshot transition, // not necessarily every single rendered frame #include "cg_local.h" /* ================== CG_ResetEntity ================== */ static void CG_ResetEntity( centity_t *cent ) { // if the previous snapshot this entity was updated in is at least // an event window back in time then we can reset the previous event if ( cent->snapShotTime < cg.time - EVENT_VALID_MSEC ) { cent->previousEvent = 0; } cent->trailTime = cg.snap->serverTime; VectorCopy (cent->currentState.origin, cent->lerpOrigin); VectorCopy (cent->currentState.angles, cent->lerpAngles); if ( cent->currentState.eType == ET_PLAYER ) { CG_ResetPlayerEntity( cent ); } } /* =============== CG_TransitionEntity cent->nextState is moved to cent->currentState and events are fired =============== */ static void CG_TransitionEntity( centity_t *cent ) { cent->currentState = cent->nextState; cent->currentValid = qtrue; // reset if the entity wasn't in the last frame or was teleported if ( !cent->interpolate ) { CG_ResetEntity( cent ); } // clear the next state. if will be set by the next CG_SetNextSnap cent->interpolate = qfalse; // check for events CG_CheckEvents( cent ); } /* ================== CG_SetInitialSnapshot This will only happen on the very first snapshot, or on tourney restarts. All other times will use CG_TransitionSnapshot instead. FIXME: Also called by map_restart? ================== */ void CG_SetInitialSnapshot( snapshot_t *snap ) { int i; centity_t *cent; entityState_t *state; cg.snap = snap; BG_PlayerStateToEntityState( &snap->ps, &cg_entities[ snap->ps.clientNum ].currentState, qfalse ); // sort out solid entities CG_BuildSolidList(); CG_ExecuteNewServerCommands( snap->serverCommandSequence ); // set our local weapon selection pointer to // what the server has indicated the current weapon is CG_Respawn(); for ( i = 0 ; i < cg.snap->numEntities ; i++ ) { state = &cg.snap->entities[ i ]; cent = &cg_entities[ state->number ]; memcpy(¢->currentState, state, sizeof(entityState_t)); //cent->currentState = *state; cent->interpolate = qfalse; cent->currentValid = qtrue; CG_ResetEntity( cent ); // check for events CG_CheckEvents( cent ); } } /* =================== CG_TransitionSnapshot The transition point from snap to nextSnap has passed =================== */ static void CG_TransitionSnapshot( void ) { centity_t *cent; snapshot_t *oldFrame; int i; if ( !cg.snap ) { CG_Error( "CG_TransitionSnapshot: NULL cg.snap" ); } if ( !cg.nextSnap ) { CG_Error( "CG_TransitionSnapshot: NULL cg.nextSnap" ); } // execute any server string commands before transitioning entities CG_ExecuteNewServerCommands( cg.nextSnap->serverCommandSequence ); // if we had a map_restart, set everthing with initial if ( !cg.snap ) { } // clear the currentValid flag for all entities in the existing snapshot for ( i = 0 ; i < cg.snap->numEntities ; i++ ) { cent = &cg_entities[ cg.snap->entities[ i ].number ]; cent->currentValid = qfalse; } // move nextSnap to snap and do the transitions oldFrame = cg.snap; cg.snap = cg.nextSnap; BG_PlayerStateToEntityState( &cg.snap->ps, &cg_entities[ cg.snap->ps.clientNum ].currentState, qfalse ); cg_entities[ cg.snap->ps.clientNum ].interpolate = qfalse; for ( i = 0 ; i < cg.snap->numEntities ; i++ ) { cent = &cg_entities[ cg.snap->entities[ i ].number ]; CG_TransitionEntity( cent ); // remember time of snapshot this entity was last updated in cent->snapShotTime = cg.snap->serverTime; } cg.nextSnap = NULL; // check for playerstate transition events if ( oldFrame ) { playerState_t *ops, *ps; ops = &oldFrame->ps; ps = &cg.snap->ps; // teleporting checks are irrespective of prediction if ( ( ps->eFlags ^ ops->eFlags ) & EF_TELEPORT_BIT ) { cg.thisFrameTeleport = qtrue; // will be cleared by prediction code } // if we are not doing client side movement prediction for any // reason, then the client events and view changes will be issued now if ( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW) || cg_nopredict.integer || cg_synchronousClients.integer ) { CG_TransitionPlayerState( ps, ops ); } } } /* =================== CG_SetNextSnap A new snapshot has just been read in from the client system. =================== */ static void CG_SetNextSnap( snapshot_t *snap ) { int num; entityState_t *es; centity_t *cent; cg.nextSnap = snap; BG_PlayerStateToEntityState( &snap->ps, &cg_entities[ snap->ps.clientNum ].nextState, qfalse ); cg_entities[ cg.snap->ps.clientNum ].interpolate = qtrue; // check for extrapolation errors for ( num = 0 ; num < snap->numEntities ; num++ ) { es = &snap->entities[num]; cent = &cg_entities[ es->number ]; memcpy(¢->nextState, es, sizeof(entityState_t)); //cent->nextState = *es; // if this frame is a teleport, or the entity wasn't in the // previous frame, don't interpolate if ( !cent->currentValid || ( ( cent->currentState.eFlags ^ es->eFlags ) & EF_TELEPORT_BIT ) ) { cent->interpolate = qfalse; } else { cent->interpolate = qtrue; } } // if the next frame is a teleport for the playerstate, we // can't interpolate during demos if ( cg.snap && ( ( snap->ps.eFlags ^ cg.snap->ps.eFlags ) & EF_TELEPORT_BIT ) ) { cg.nextFrameTeleport = qtrue; } else { cg.nextFrameTeleport = qfalse; } // if changing follow mode, don't interpolate if ( cg.nextSnap->ps.clientNum != cg.snap->ps.clientNum ) { cg.nextFrameTeleport = qtrue; } // if changing server restarts, don't interpolate if ( ( cg.nextSnap->snapFlags ^ cg.snap->snapFlags ) & SNAPFLAG_SERVERCOUNT ) { cg.nextFrameTeleport = qtrue; } // sort out solid entities CG_BuildSolidList(); } /* ======================== CG_ReadNextSnapshot This is the only place new snapshots are requested This may increment cgs.processedSnapshotNum multiple times if the client system fails to return a valid snapshot. ======================== */ static snapshot_t *CG_ReadNextSnapshot( void ) { qboolean r; snapshot_t *dest; if ( cg.latestSnapshotNum > cgs.processedSnapshotNum + 1000 ) { CG_Printf( "WARNING: CG_ReadNextSnapshot: way out of range, %i > %i", cg.latestSnapshotNum, cgs.processedSnapshotNum ); } while ( cgs.processedSnapshotNum < cg.latestSnapshotNum ) { // decide which of the two slots to load it into if ( cg.snap == &cg.activeSnapshots[0] ) { dest = &cg.activeSnapshots[1]; } else { dest = &cg.activeSnapshots[0]; } // try to read the snapshot from the client system cgs.processedSnapshotNum++; r = trap_GetSnapshot( cgs.processedSnapshotNum, dest ); // FIXME: why would trap_GetSnapshot return a snapshot with the same server time if ( cg.snap && r && dest->serverTime == cg.snap->serverTime ) { //continue; } // if it succeeded, return if ( r ) { CG_AddLagometerSnapshotInfo( dest ); return dest; } // a GetSnapshot will return failure if the snapshot // never arrived, or is so old that its entities // have been shoved off the end of the circular // buffer in the client system. // record as a dropped packet CG_AddLagometerSnapshotInfo( NULL ); // If there are additional snapshots, continue trying to // read them. } // nothing left to read return NULL; } /* ============ CG_ProcessSnapshots We are trying to set up a renderable view, so determine what the simulated time is, and try to get snapshots both before and after that time if available. If we don't have a valid cg.snap after exiting this function, then a 3D game view cannot be rendered. This should only happen right after the initial connection. After cg.snap has been valid once, it will never turn invalid. Even if cg.snap is valid, cg.nextSnap may not be, if the snapshot hasn't arrived yet (it becomes an extrapolating situation instead of an interpolating one) ============ */ void CG_ProcessSnapshots( void ) { snapshot_t *snap; int n; // see what the latest snapshot the client system has is trap_GetCurrentSnapshotNumber( &n, &cg.latestSnapshotTime ); if ( n != cg.latestSnapshotNum ) { if ( n < cg.latestSnapshotNum ) { // this should never happen CG_Error( "CG_ProcessSnapshots: n < cg.latestSnapshotNum" ); } cg.latestSnapshotNum = n; } // If we have yet to receive a snapshot, check for it. // Once we have gotten the first snapshot, cg.snap will // always have valid data for the rest of the game while ( !cg.snap ) { snap = CG_ReadNextSnapshot(); if ( !snap ) { // we can't continue until we get a snapshot return; } // set our weapon selection to what // the playerstate is currently using if ( !( snap->snapFlags & SNAPFLAG_NOT_ACTIVE ) ) { CG_SetInitialSnapshot( snap ); } } // loop until we either have a valid nextSnap with a serverTime // greater than cg.time to interpolate towards, or we run // out of available snapshots do { // if we don't have a nextframe, try and read a new one in if ( !cg.nextSnap ) { snap = CG_ReadNextSnapshot(); // if we still don't have a nextframe, we will just have to // extrapolate if ( !snap ) { break; } CG_SetNextSnap( snap ); // if time went backwards, we have a level restart if ( cg.nextSnap->serverTime < cg.snap->serverTime ) { CG_Error( "CG_ProcessSnapshots: Server time went backwards" ); } } // if our time is < nextFrame's, we have a nice interpolating state if ( cg.time >= cg.snap->serverTime && cg.time < cg.nextSnap->serverTime ) { break; } // we have passed the transition from nextFrame to frame CG_TransitionSnapshot(); } while ( 1 ); // assert our valid conditions upon exiting if ( cg.snap == NULL ) { CG_Error( "CG_ProcessSnapshots: cg.snap == NULL" ); } if ( cg.time < cg.snap->serverTime ) { // this can happen right after a vid_restart cg.time = cg.snap->serverTime; } if ( cg.nextSnap != NULL && cg.nextSnap->serverTime <= cg.time ) { CG_Error( "CG_ProcessSnapshots: cg.nextSnap->serverTime <= cg.time" ); } } ================================================ FILE: src/cgame/cg_syscalls.asm ================================================ code equ trap_Print -1 equ trap_Error -2 equ trap_Milliseconds -3 equ trap_Cvar_Register -4 equ trap_Cvar_Update -5 equ trap_Cvar_Set -6 equ trap_Cvar_VariableStringBuffer -7 equ trap_Argc -8 equ trap_Argv -9 equ trap_Args -10 equ trap_FS_FOpenFile -11 equ trap_FS_Read -12 equ trap_FS_Write -13 equ trap_FS_FCloseFile -14 equ trap_SendConsoleCommand -15 equ trap_AddCommand -16 equ trap_SendClientCommand -17 equ trap_UpdateScreen -18 equ trap_CM_LoadMap -19 equ trap_CM_NumInlineModels -20 equ trap_CM_InlineModel -21 equ trap_CM_LoadModel -22 equ trap_CM_TempBoxModel -23 equ trap_CM_PointContents -24 equ trap_CM_TransformedPointContents -25 equ trap_CM_BoxTrace -26 equ trap_CM_TransformedBoxTrace -27 equ trap_CM_MarkFragments -28 equ trap_S_StartSound -29 equ trap_S_StartLocalSound -30 equ trap_S_ClearLoopingSounds -31 equ trap_S_AddLoopingSound -32 equ trap_S_UpdateEntityPosition -33 equ trap_S_Respatialize -34 equ trap_S_RegisterSound -35 equ trap_S_StartBackgroundTrack -36 equ trap_R_LoadWorldMap -37 equ trap_R_RegisterModel -38 equ trap_R_RegisterSkin -39 equ trap_R_RegisterShader -40 equ trap_R_ClearScene -41 equ trap_R_AddRefEntityToScene -42 equ trap_R_AddPolyToScene -43 equ trap_R_AddLightToScene -44 equ trap_R_RenderScene -45 equ trap_R_SetColor -46 equ trap_R_DrawStretchPic -47 equ trap_R_ModelBounds -48 equ trap_R_LerpTag -49 equ trap_GetGlconfig -50 equ trap_GetGameState -51 equ trap_GetCurrentSnapshotNumber -52 equ trap_GetSnapshot -53 equ trap_GetServerCommand -54 equ trap_GetCurrentCmdNumber -55 equ trap_GetUserCmd -56 equ trap_SetUserCmdValue -57 equ trap_R_RegisterShaderNoMip -58 equ trap_MemoryRemaining -59 equ trap_R_RegisterFont -60 equ trap_Key_IsDown -61 equ trap_Key_GetCatcher -62 equ trap_Key_SetCatcher -63 equ trap_Key_GetKey -64 equ trap_PC_AddGlobalDefine -65 equ trap_PC_LoadSource -66 equ trap_PC_FreeSource -67 equ trap_PC_ReadToken -68 equ trap_PC_SourceFileAndLine -69 equ trap_S_StopBackgroundTrack -70 equ trap_RealTime -71 equ trap_SnapVector -72 equ trap_RemoveCommand -73 equ trap_R_LightForPoint -74 equ trap_CIN_PlayCinematic -75 equ trap_CIN_StopCinematic -76 equ trap_CIN_RunCinematic -77 equ trap_CIN_DrawCinematic -78 equ trap_CIN_SetExtents -79 equ trap_R_RemapShader -80 equ trap_S_AddRealLoopingSound -81 equ trap_S_StopLoopingSound -82 equ trap_CM_TempCapsuleModel -83 equ trap_CM_CapsuleTrace -84 equ trap_CM_TransformedCapsuleTrace -85 equ trap_R_AddAdditiveLightToScene -86 equ trap_GetEntityToken -87 equ trap_R_AddPolysToScene -88 equ trap_R_inPVS -89 equ trap_FS_Seek -90 equ memset -101 equ memcpy -102 equ strncpy -103 equ sin -104 equ cos -105 equ atan2 -106 equ sqrt -107 equ floor -108 equ ceil -109 equ testPrintInt -110 equ testPrintFloat -111 equ acos -112 ================================================ FILE: src/cgame/cg_syscalls.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_syscalls.c -- this file is only included when building a dll // cg_syscalls.asm is included instead when building a qvm #ifdef Q3_VM #error "Do not use in VM build" #endif #include "cg_local.h" static intptr_t (QDECL *syscall)( intptr_t arg, ... ) = (intptr_t (QDECL *)( intptr_t, ...))-1; void dllEntry( intptr_t (QDECL *syscallptr)( intptr_t arg,... ) ) { syscall = syscallptr; } int PASSFLOAT( float x ) { float floatTemp; floatTemp = x; return *(int *)&floatTemp; } void trap_Print( const char *fmt ) { syscall( CG_PRINT, fmt ); } void trap_Error( const char *fmt ) { syscall( CG_ERROR, fmt ); } int trap_Milliseconds( void ) { return syscall( CG_MILLISECONDS ); } void trap_Cvar_Register( vmCvar_t *vmCvar, const char *varName, const char *defaultValue, int flags ) { syscall( CG_CVAR_REGISTER, vmCvar, varName, defaultValue, flags ); } void trap_Cvar_Update( vmCvar_t *vmCvar ) { syscall( CG_CVAR_UPDATE, vmCvar ); } void trap_Cvar_Set( const char *var_name, const char *value ) { syscall( CG_CVAR_SET, var_name, value ); } void trap_Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize ) { syscall( CG_CVAR_VARIABLESTRINGBUFFER, var_name, buffer, bufsize ); } int trap_Argc( void ) { return syscall( CG_ARGC ); } void trap_Argv( int n, char *buffer, int bufferLength ) { syscall( CG_ARGV, n, buffer, bufferLength ); } void trap_Args( char *buffer, int bufferLength ) { syscall( CG_ARGS, buffer, bufferLength ); } int trap_FS_FOpenFile( const char *qpath, fileHandle_t *f, fsMode_t mode ) { return syscall( CG_FS_FOPENFILE, qpath, f, mode ); } void trap_FS_Read( void *buffer, int len, fileHandle_t f ) { syscall( CG_FS_READ, buffer, len, f ); } void trap_FS_Write( const void *buffer, int len, fileHandle_t f ) { syscall( CG_FS_WRITE, buffer, len, f ); } void trap_FS_FCloseFile( fileHandle_t f ) { syscall( CG_FS_FCLOSEFILE, f ); } int trap_FS_Seek( fileHandle_t f, long offset, int origin ) { return syscall( CG_FS_SEEK, f, offset, origin ); } void trap_SendConsoleCommand( const char *text ) { syscall( CG_SENDCONSOLECOMMAND, text ); } void trap_AddCommand( const char *cmdName ) { syscall( CG_ADDCOMMAND, cmdName ); } void trap_RemoveCommand( const char *cmdName ) { syscall( CG_REMOVECOMMAND, cmdName ); } void trap_SendClientCommand( const char *s ) { syscall( CG_SENDCLIENTCOMMAND, s ); } void trap_UpdateScreen( void ) { syscall( CG_UPDATESCREEN ); } void trap_CM_LoadMap( const char *mapname ) { syscall( CG_CM_LOADMAP, mapname ); } int trap_CM_NumInlineModels( void ) { return syscall( CG_CM_NUMINLINEMODELS ); } clipHandle_t trap_CM_InlineModel( int index ) { return syscall( CG_CM_INLINEMODEL, index ); } clipHandle_t trap_CM_TempBoxModel( const vec3_t mins, const vec3_t maxs ) { return syscall( CG_CM_TEMPBOXMODEL, mins, maxs ); } clipHandle_t trap_CM_TempCapsuleModel( const vec3_t mins, const vec3_t maxs ) { return syscall( CG_CM_TEMPCAPSULEMODEL, mins, maxs ); } int trap_CM_PointContents( const vec3_t p, clipHandle_t model ) { return syscall( CG_CM_POINTCONTENTS, p, model ); } int trap_CM_TransformedPointContents( const vec3_t p, clipHandle_t model, const vec3_t origin, const vec3_t angles ) { return syscall( CG_CM_TRANSFORMEDPOINTCONTENTS, p, model, origin, angles ); } void trap_CM_BoxTrace( trace_t *results, const vec3_t start, const vec3_t end, const vec3_t mins, const vec3_t maxs, clipHandle_t model, int brushmask ) { syscall( CG_CM_BOXTRACE, results, start, end, mins, maxs, model, brushmask ); } void trap_CM_CapsuleTrace( trace_t *results, const vec3_t start, const vec3_t end, const vec3_t mins, const vec3_t maxs, clipHandle_t model, int brushmask ) { syscall( CG_CM_CAPSULETRACE, results, start, end, mins, maxs, model, brushmask ); } void trap_CM_TransformedBoxTrace( trace_t *results, const vec3_t start, const vec3_t end, const vec3_t mins, const vec3_t maxs, clipHandle_t model, int brushmask, const vec3_t origin, const vec3_t angles ) { syscall( CG_CM_TRANSFORMEDBOXTRACE, results, start, end, mins, maxs, model, brushmask, origin, angles ); } void trap_CM_TransformedCapsuleTrace( trace_t *results, const vec3_t start, const vec3_t end, const vec3_t mins, const vec3_t maxs, clipHandle_t model, int brushmask, const vec3_t origin, const vec3_t angles ) { syscall( CG_CM_TRANSFORMEDCAPSULETRACE, results, start, end, mins, maxs, model, brushmask, origin, angles ); } int trap_CM_MarkFragments( int numPoints, const vec3_t *points, const vec3_t projection, int maxPoints, vec3_t pointBuffer, int maxFragments, markFragment_t *fragmentBuffer ) { return syscall( CG_CM_MARKFRAGMENTS, numPoints, points, projection, maxPoints, pointBuffer, maxFragments, fragmentBuffer ); } void trap_S_StartSound( vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfx ) { syscall( CG_S_STARTSOUND, origin, entityNum, entchannel, sfx ); } void trap_S_StartLocalSound( sfxHandle_t sfx, int channelNum ) { syscall( CG_S_STARTLOCALSOUND, sfx, channelNum ); } void trap_S_ClearLoopingSounds( qboolean killall ) { syscall( CG_S_CLEARLOOPINGSOUNDS, killall ); } void trap_S_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx ) { syscall( CG_S_ADDLOOPINGSOUND, entityNum, origin, velocity, sfx ); } void trap_S_AddRealLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx ) { syscall( CG_S_ADDREALLOOPINGSOUND, entityNum, origin, velocity, sfx ); } void trap_S_StopLoopingSound( int entityNum ) { syscall( CG_S_STOPLOOPINGSOUND, entityNum ); } void trap_S_UpdateEntityPosition( int entityNum, const vec3_t origin ) { syscall( CG_S_UPDATEENTITYPOSITION, entityNum, origin ); } void trap_S_Respatialize( int entityNum, const vec3_t origin, vec3_t axis[3], int inwater ) { syscall( CG_S_RESPATIALIZE, entityNum, origin, axis, inwater ); } sfxHandle_t trap_S_RegisterSound( const char *sample, qboolean compressed ) { return syscall( CG_S_REGISTERSOUND, sample, compressed ); } void trap_S_StartBackgroundTrack( const char *intro, const char *loop ) { syscall( CG_S_STARTBACKGROUNDTRACK, intro, loop ); } void trap_R_LoadWorldMap( const char *mapname ) { syscall( CG_R_LOADWORLDMAP, mapname ); } qhandle_t trap_R_RegisterModel( const char *name ) { return syscall( CG_R_REGISTERMODEL, name ); } qhandle_t trap_R_RegisterSkin( const char *name ) { return syscall( CG_R_REGISTERSKIN, name ); } qhandle_t trap_R_RegisterShader( const char *name ) { return syscall( CG_R_REGISTERSHADER, name ); } qhandle_t trap_R_RegisterShaderNoMip( const char *name ) { return syscall( CG_R_REGISTERSHADERNOMIP, name ); } void trap_R_RegisterFont(const char *fontName, int pointSize, fontInfo_t *font) { syscall(CG_R_REGISTERFONT, fontName, pointSize, font ); } void trap_R_ClearScene( void ) { syscall( CG_R_CLEARSCENE ); } void trap_R_AddRefEntityToScene( const refEntity_t *re ) { syscall( CG_R_ADDREFENTITYTOSCENE, re ); } void trap_R_AddPolyToScene( qhandle_t hShader , int numVerts, const polyVert_t *verts ) { syscall( CG_R_ADDPOLYTOSCENE, hShader, numVerts, verts ); } void trap_R_AddPolysToScene( qhandle_t hShader , int numVerts, const polyVert_t *verts, int num ) { syscall( CG_R_ADDPOLYSTOSCENE, hShader, numVerts, verts, num ); } int trap_R_LightForPoint( vec3_t point, vec3_t ambientLight, vec3_t directedLight, vec3_t lightDir ) { return syscall( CG_R_LIGHTFORPOINT, point, ambientLight, directedLight, lightDir ); } void trap_R_AddLightToScene( const vec3_t org, float intensity, float r, float g, float b ) { syscall( CG_R_ADDLIGHTTOSCENE, org, PASSFLOAT(intensity), PASSFLOAT(r), PASSFLOAT(g), PASSFLOAT(b) ); } void trap_R_AddAdditiveLightToScene( const vec3_t org, float intensity, float r, float g, float b ) { syscall( CG_R_ADDADDITIVELIGHTTOSCENE, org, PASSFLOAT(intensity), PASSFLOAT(r), PASSFLOAT(g), PASSFLOAT(b) ); } void trap_R_RenderScene( const refdef_t *fd ) { syscall( CG_R_RENDERSCENE, fd ); } void trap_R_SetColor( const float *rgba ) { syscall( CG_R_SETCOLOR, rgba ); } void trap_R_DrawStretchPic( float x, float y, float w, float h, float s1, float t1, float s2, float t2, qhandle_t hShader ) { syscall( CG_R_DRAWSTRETCHPIC, PASSFLOAT(x), PASSFLOAT(y), PASSFLOAT(w), PASSFLOAT(h), PASSFLOAT(s1), PASSFLOAT(t1), PASSFLOAT(s2), PASSFLOAT(t2), hShader ); } void trap_R_ModelBounds( clipHandle_t model, vec3_t mins, vec3_t maxs ) { syscall( CG_R_MODELBOUNDS, model, mins, maxs ); } int trap_R_LerpTag( orientation_t *tag, clipHandle_t mod, int startFrame, int endFrame, float frac, const char *tagName ) { return syscall( CG_R_LERPTAG, tag, mod, startFrame, endFrame, PASSFLOAT(frac), tagName ); } void trap_R_RemapShader( const char *oldShader, const char *newShader, const char *timeOffset ) { syscall( CG_R_REMAP_SHADER, oldShader, newShader, timeOffset ); } void trap_GetGlconfig( glconfig_t *glconfig ) { syscall( CG_GETGLCONFIG, glconfig ); } void trap_GetGameState( gameState_t *gamestate ) { syscall( CG_GETGAMESTATE, gamestate ); } void trap_GetCurrentSnapshotNumber( int *snapshotNumber, int *serverTime ) { syscall( CG_GETCURRENTSNAPSHOTNUMBER, snapshotNumber, serverTime ); } qboolean trap_GetSnapshot( int snapshotNumber, snapshot_t *snapshot ) { return syscall( CG_GETSNAPSHOT, snapshotNumber, snapshot ); } qboolean trap_GetServerCommand( int serverCommandNumber ) { return syscall( CG_GETSERVERCOMMAND, serverCommandNumber ); } int trap_GetCurrentCmdNumber( void ) { return syscall( CG_GETCURRENTCMDNUMBER ); } qboolean trap_GetUserCmd( int cmdNumber, usercmd_t *ucmd ) { return syscall( CG_GETUSERCMD, cmdNumber, ucmd ); } void trap_SetUserCmdValue( int stateValue, float sensitivityScale ) { syscall( CG_SETUSERCMDVALUE, stateValue, PASSFLOAT(sensitivityScale) ); } void testPrintInt( char *string, int i ) { syscall( CG_TESTPRINTINT, string, i ); } void testPrintFloat( char *string, float f ) { syscall( CG_TESTPRINTFLOAT, string, PASSFLOAT(f) ); } int trap_MemoryRemaining( void ) { return syscall( CG_MEMORY_REMAINING ); } qboolean trap_Key_IsDown( int keynum ) { return syscall( CG_KEY_ISDOWN, keynum ); } int trap_Key_GetCatcher( void ) { return syscall( CG_KEY_GETCATCHER ); } void trap_Key_SetCatcher( int catcher ) { syscall( CG_KEY_SETCATCHER, catcher ); } int trap_Key_GetKey( const char *binding ) { return syscall( CG_KEY_GETKEY, binding ); } int trap_PC_AddGlobalDefine( char *define ) { return syscall( CG_PC_ADD_GLOBAL_DEFINE, define ); } int trap_PC_LoadSource( const char *filename ) { return syscall( CG_PC_LOAD_SOURCE, filename ); } int trap_PC_FreeSource( int handle ) { return syscall( CG_PC_FREE_SOURCE, handle ); } int trap_PC_ReadToken( int handle, pc_token_t *pc_token ) { return syscall( CG_PC_READ_TOKEN, handle, pc_token ); } int trap_PC_SourceFileAndLine( int handle, char *filename, int *line ) { return syscall( CG_PC_SOURCE_FILE_AND_LINE, handle, filename, line ); } void trap_S_StopBackgroundTrack( void ) { syscall( CG_S_STOPBACKGROUNDTRACK ); } int trap_RealTime(qtime_t *qtime) { return syscall( CG_REAL_TIME, qtime ); } void trap_SnapVector( float *v ) { syscall( CG_SNAPVECTOR, v ); } // this returns a handle. arg0 is the name in the format "idlogo.roq", set arg1 to NULL, alteredstates to qfalse (do not alter gamestate) int trap_CIN_PlayCinematic( const char *arg0, int xpos, int ypos, int width, int height, int bits) { return syscall(CG_CIN_PLAYCINEMATIC, arg0, xpos, ypos, width, height, bits); } // stops playing the cinematic and ends it. should always return FMV_EOF // cinematics must be stopped in reverse order of when they are started e_status trap_CIN_StopCinematic(int handle) { return syscall(CG_CIN_STOPCINEMATIC, handle); } // will run a frame of the cinematic but will not draw it. Will return FMV_EOF if the end of the cinematic has been reached. e_status trap_CIN_RunCinematic (int handle) { return syscall(CG_CIN_RUNCINEMATIC, handle); } // draws the current frame void trap_CIN_DrawCinematic (int handle) { syscall(CG_CIN_DRAWCINEMATIC, handle); } // allows you to resize the animation dynamically void trap_CIN_SetExtents (int handle, int x, int y, int w, int h) { syscall(CG_CIN_SETEXTENTS, handle, x, y, w, h); } /* qboolean trap_loadCamera( const char *name ) { return syscall( CG_LOADCAMERA, name ); } void trap_startCamera(int time) { syscall(CG_STARTCAMERA, time); } qboolean trap_getCameraInfo( int time, vec3_t *origin, vec3_t *angles) { return syscall( CG_GETCAMERAINFO, time, origin, angles ); } */ qboolean trap_GetEntityToken( char *buffer, int bufferSize ) { return syscall( CG_GET_ENTITY_TOKEN, buffer, bufferSize ); } qboolean trap_R_inPVS( const vec3_t p1, const vec3_t p2 ) { return syscall( CG_R_INPVS, p1, p2 ); } ================================================ FILE: src/cgame/cg_view.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_view.c -- setup all the parameters (position, angle, etc) // for a 3D rendering #include "cg_local.h" /* ============================================================================= MODEL TESTING The viewthing and gun positioning tools from Q2 have been integrated and enhanced into a single model testing facility. Model viewing can begin with either "testmodel " or "testgun ". The names must be the full pathname after the basedir, like "models/weapons/v_launch/tris.md3" or "players/male/tris.md3" Testmodel will create a fake entity 100 units in front of the current view position, directly facing the viewer. It will remain immobile, so you can move around it to view it from different angles. Testgun will cause the model to follow the player around and supress the real view weapon model. The default frame 0 of most guns is completely off screen, so you will probably have to cycle a couple frames to see it. "nextframe", "prevframe", "nextskin", and "prevskin" commands will change the frame or skin of the testmodel. These are bound to F5, F6, F7, and F8 in q3default.cfg. If a gun is being tested, the "gun_x", "gun_y", and "gun_z" variables will let you adjust the positioning. Note that none of the model testing features update while the game is paused, so it may be convenient to test with deathmatch set to 1 so that bringing down the console doesn't pause the game. ============================================================================= */ /* ================= CG_TestModel_f Creates an entity in front of the current position, which can then be moved around ================= */ void CG_TestModel_f (void) { vec3_t angles; memset( &cg.testModelEntity, 0, sizeof(cg.testModelEntity) ); if ( trap_Argc() < 2 ) { return; } Q_strncpyz (cg.testModelName, CG_Argv( 1 ), MAX_QPATH ); cg.testModelEntity.hModel = trap_R_RegisterModel( cg.testModelName ); if ( trap_Argc() == 3 ) { cg.testModelEntity.backlerp = atof( CG_Argv( 2 ) ); cg.testModelEntity.frame = 1; cg.testModelEntity.oldframe = 0; } if (! cg.testModelEntity.hModel ) { CG_Printf( "Can't register model\n" ); return; } VectorMA( cg.refdef.vieworg, 100, cg.refdef.viewaxis[0], cg.testModelEntity.origin ); angles[PITCH] = 0; angles[YAW] = 180 + cg.refdefViewAngles[1]; angles[ROLL] = 0; AnglesToAxis( angles, cg.testModelEntity.axis ); cg.testGun = qfalse; } /* ================= CG_TestGun_f Replaces the current view weapon with the given model ================= */ void CG_TestGun_f (void) { CG_TestModel_f(); cg.testGun = qtrue; cg.testModelEntity.renderfx = RF_MINLIGHT | RF_DEPTHHACK | RF_FIRST_PERSON; } void CG_TestModelNextFrame_f (void) { cg.testModelEntity.frame++; CG_Printf( "frame %i\n", cg.testModelEntity.frame ); } void CG_TestModelPrevFrame_f (void) { cg.testModelEntity.frame--; if ( cg.testModelEntity.frame < 0 ) { cg.testModelEntity.frame = 0; } CG_Printf( "frame %i\n", cg.testModelEntity.frame ); } void CG_TestModelNextSkin_f (void) { cg.testModelEntity.skinNum++; CG_Printf( "skin %i\n", cg.testModelEntity.skinNum ); } void CG_TestModelPrevSkin_f (void) { cg.testModelEntity.skinNum--; if ( cg.testModelEntity.skinNum < 0 ) { cg.testModelEntity.skinNum = 0; } CG_Printf( "skin %i\n", cg.testModelEntity.skinNum ); } static void CG_AddTestModel (void) { int i; // re-register the model, because the level may have changed cg.testModelEntity.hModel = trap_R_RegisterModel( cg.testModelName ); if (! cg.testModelEntity.hModel ) { CG_Printf ("Can't register model\n"); return; } // if testing a gun, set the origin reletive to the view origin if ( cg.testGun ) { VectorCopy( cg.refdef.vieworg, cg.testModelEntity.origin ); VectorCopy( cg.refdef.viewaxis[0], cg.testModelEntity.axis[0] ); VectorCopy( cg.refdef.viewaxis[1], cg.testModelEntity.axis[1] ); VectorCopy( cg.refdef.viewaxis[2], cg.testModelEntity.axis[2] ); // allow the position to be adjusted for (i=0 ; i<3 ; i++) { cg.testModelEntity.origin[i] += cg.refdef.viewaxis[0][i] * cg_gun_x.value; cg.testModelEntity.origin[i] += cg.refdef.viewaxis[1][i] * cg_gun_y.value; cg.testModelEntity.origin[i] += cg.refdef.viewaxis[2][i] * cg_gun_z.value; } } trap_R_AddRefEntityToScene( &cg.testModelEntity ); } //============================================================================ /* ================= CG_CalcVrect Sets the coordinates of the rendered window ================= */ static void CG_CalcVrect (void) { int size; // the intermission should allways be full screen if ( cg.snap->ps.pm_type == PM_INTERMISSION ) { size = 100; } else { // bound normal viewsize if (cg_viewsize.integer < 30) { trap_Cvar_Set ("cg_viewsize","30"); size = 30; } else if (cg_viewsize.integer > 100) { trap_Cvar_Set ("cg_viewsize","100"); size = 100; } else { size = cg_viewsize.integer; } } cg.refdef.width = cgs.glconfig.vidWidth*size/100; cg.refdef.width &= ~1; cg.refdef.height = cgs.glconfig.vidHeight*size/100; cg.refdef.height &= ~1; cg.refdef.x = (cgs.glconfig.vidWidth - cg.refdef.width)/2; cg.refdef.y = (cgs.glconfig.vidHeight - cg.refdef.height)/2; } //============================================================================== /* =============== CG_OffsetThirdPersonView =============== */ #define FOCUS_DISTANCE 512 static void CG_OffsetThirdPersonView( void ) { vec3_t forward, right, up; vec3_t view; vec3_t focusAngles; trace_t trace; static vec3_t mins = { -4, -4, -4 }; static vec3_t maxs = { 4, 4, 4 }; vec3_t focusPoint; float focusDist; float forwardScale, sideScale; cg.refdef.vieworg[2] += cg.predictedPlayerState.viewheight; VectorCopy( cg.refdefViewAngles, focusAngles ); // if dead, look at killer if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) { focusAngles[YAW] = cg.predictedPlayerState.stats[STAT_DEAD_YAW]; cg.refdefViewAngles[YAW] = cg.predictedPlayerState.stats[STAT_DEAD_YAW]; } if ( focusAngles[PITCH] > 45 ) { focusAngles[PITCH] = 45; // don't go too far overhead } AngleVectors( focusAngles, forward, NULL, NULL ); VectorMA( cg.refdef.vieworg, FOCUS_DISTANCE, forward, focusPoint ); VectorCopy( cg.refdef.vieworg, view ); view[2] += 8; cg.refdefViewAngles[PITCH] *= 0.5; AngleVectors( cg.refdefViewAngles, forward, right, up ); forwardScale = cos( cg_thirdPersonAngle.value / 180 * M_PI ); sideScale = sin( cg_thirdPersonAngle.value / 180 * M_PI ); VectorMA( view, -cg_thirdPersonRange.value * forwardScale, forward, view ); VectorMA( view, -cg_thirdPersonRange.value * sideScale, right, view ); // trace a ray from the origin to the viewpoint to make sure the view isn't // in a solid block. Use an 8 by 8 block to prevent the view from near clipping anything if (!cg_cameraMode.integer) { CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID ); if ( trace.fraction != 1.0 ) { VectorCopy( trace.endpos, view ); view[2] += (1.0 - trace.fraction) * 32; // try another trace to this position, because a tunnel may have the ceiling // close enogh that this is poking out CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID ); VectorCopy( trace.endpos, view ); } } VectorCopy( view, cg.refdef.vieworg ); // select pitch to look at focus point from vieword VectorSubtract( focusPoint, cg.refdef.vieworg, focusPoint ); focusDist = sqrt( focusPoint[0] * focusPoint[0] + focusPoint[1] * focusPoint[1] ); if ( focusDist < 1 ) { focusDist = 1; // should never happen } cg.refdefViewAngles[PITCH] = -180 / M_PI * atan2( focusPoint[2], focusDist ); cg.refdefViewAngles[YAW] -= cg_thirdPersonAngle.value; } // this causes a compiler bug on mac MrC compiler static void CG_StepOffset( void ) { int timeDelta; // smooth out stair climbing timeDelta = cg.time - cg.stepTime; if ( timeDelta < STEP_TIME ) { cg.refdef.vieworg[2] -= cg.stepChange * (STEP_TIME - timeDelta) / STEP_TIME; } } /* =============== CG_OffsetFirstPersonView =============== */ static void CG_OffsetFirstPersonView( void ) { float *origin; float *angles; float bob; float ratio; float delta; float speed; float f; vec3_t predictedVelocity; int timeDelta; if ( cg.snap->ps.pm_type == PM_INTERMISSION ) { return; } origin = cg.refdef.vieworg; angles = cg.refdefViewAngles; // if dead, fix the angle and don't add any kick if ( cg.snap->ps.stats[STAT_HEALTH] <= 0 ) { angles[ROLL] = 40; angles[PITCH] = -15; angles[YAW] = cg.snap->ps.stats[STAT_DEAD_YAW]; origin[2] += cg.predictedPlayerState.viewheight; return; } // add angles based on weapon kick VectorAdd (angles, cg.kick_angles, angles); // add angles based on damage kick if ( cg.damageTime ) { ratio = cg.time - cg.damageTime; if ( ratio < DAMAGE_DEFLECT_TIME ) { ratio /= DAMAGE_DEFLECT_TIME; angles[PITCH] += ratio * cg.v_dmg_pitch; angles[ROLL] += ratio * cg.v_dmg_roll; } else { ratio = 1.0 - ( ratio - DAMAGE_DEFLECT_TIME ) / DAMAGE_RETURN_TIME; if ( ratio > 0 ) { angles[PITCH] += ratio * cg.v_dmg_pitch; angles[ROLL] += ratio * cg.v_dmg_roll; } } } // add pitch based on fall kick #if 0 ratio = ( cg.time - cg.landTime) / FALL_TIME; if (ratio < 0) ratio = 0; angles[PITCH] += ratio * cg.fall_value; #endif // add angles based on velocity VectorCopy( cg.predictedPlayerState.velocity, predictedVelocity ); delta = DotProduct ( predictedVelocity, cg.refdef.viewaxis[0]); angles[PITCH] += delta * cg_runpitch.value; delta = DotProduct ( predictedVelocity, cg.refdef.viewaxis[1]); angles[ROLL] -= delta * cg_runroll.value; // add angles based on bob // make sure the bob is visible even at low speeds speed = cg.xyspeed > 200 ? cg.xyspeed : 200; delta = cg.bobfracsin * cg_bobpitch.value * speed; if (cg.predictedPlayerState.pm_flags & PMF_DUCKED) delta *= 3; // crouching angles[PITCH] += delta; delta = cg.bobfracsin * cg_bobroll.value * speed; if (cg.predictedPlayerState.pm_flags & PMF_DUCKED) delta *= 3; // crouching accentuates roll if (cg.bobcycle & 1) delta = -delta; angles[ROLL] += delta; //=================================== // add view height origin[2] += cg.predictedPlayerState.viewheight; // smooth out duck height changes timeDelta = cg.time - cg.duckTime; if ( timeDelta < DUCK_TIME) { cg.refdef.vieworg[2] -= cg.duckChange * (DUCK_TIME - timeDelta) / DUCK_TIME; } // add bob height bob = cg.bobfracsin * cg.xyspeed * cg_bobup.value; if (bob > 6) { bob = 6; } origin[2] += bob; // add fall height delta = cg.time - cg.landTime; if ( delta < LAND_DEFLECT_TIME ) { f = delta / LAND_DEFLECT_TIME; cg.refdef.vieworg[2] += cg.landChange * f; } else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) { delta -= LAND_DEFLECT_TIME; f = 1.0 - ( delta / LAND_RETURN_TIME ); cg.refdef.vieworg[2] += cg.landChange * f; } // add step offset CG_StepOffset(); // add kick offset VectorAdd (origin, cg.kick_origin, origin); // pivot the eye based on a neck length #if 0 { #define NECK_LENGTH 8 vec3_t forward, up; cg.refdef.vieworg[2] -= NECK_LENGTH; AngleVectors( cg.refdefViewAngles, forward, NULL, up ); VectorMA( cg.refdef.vieworg, 3, forward, cg.refdef.vieworg ); VectorMA( cg.refdef.vieworg, NECK_LENGTH, up, cg.refdef.vieworg ); } #endif } //====================================================================== void CG_ZoomDown_f( void ) { if ( cg.zoomed ) { return; } cg.zoomed = qtrue; cg.zoomTime = cg.time; } void CG_ZoomUp_f( void ) { if ( !cg.zoomed ) { return; } cg.zoomed = qfalse; cg.zoomTime = cg.time; } /* ==================== CG_CalcFov Fixed fov at intermissions, otherwise account for fov variable and zooms. ==================== */ #define WAVE_AMPLITUDE 1 #define WAVE_FREQUENCY 0.4 static int CG_CalcFov( void ) { float x; float phase; float v; int contents; float fov_x, fov_y; float zoomFov; float f; int inwater; if ( cg.predictedPlayerState.pm_type == PM_INTERMISSION ) { // if in intermission, use a fixed value fov_x = 90; } else { // user selectable if ( cgs.dmflags & DF_FIXED_FOV ) { // dmflag to prevent wide fov for all clients fov_x = 90; } else { fov_x = cg_fov.value; if ( fov_x < 1 ) { fov_x = 1; } else if ( fov_x > 160 ) { fov_x = 160; } } // account for zooms zoomFov = cg_zoomFov.value; if ( zoomFov < 1 ) { zoomFov = 1; } else if ( zoomFov > 160 ) { zoomFov = 160; } if ( cg.zoomed ) { f = ( cg.time - cg.zoomTime ) / (float)ZOOM_TIME; if ( f > 1.0 ) { fov_x = zoomFov; } else { fov_x = fov_x + f * ( zoomFov - fov_x ); } } else { f = ( cg.time - cg.zoomTime ) / (float)ZOOM_TIME; if ( f > 1.0 ) { fov_x = fov_x; } else { fov_x = zoomFov + f * ( fov_x - zoomFov ); } } } x = cg.refdef.width / tan( fov_x / 360 * M_PI ); fov_y = atan2( cg.refdef.height, x ); fov_y = fov_y * 360 / M_PI; // warp if underwater contents = CG_PointContents( cg.refdef.vieworg, -1 ); if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ){ phase = cg.time / 1000.0 * WAVE_FREQUENCY * M_PI * 2; v = WAVE_AMPLITUDE * sin( phase ); fov_x += v; fov_y -= v; inwater = qtrue; } else { inwater = qfalse; } // set it cg.refdef.fov_x = fov_x; cg.refdef.fov_y = fov_y; if ( !cg.zoomed ) { cg.zoomSensitivity = 1; } else { cg.zoomSensitivity = cg.refdef.fov_y / 75.0; } return inwater; } /* =============== CG_DamageBlendBlob =============== */ static void CG_DamageBlendBlob( void ) { int t; int maxTime; refEntity_t ent; if ( !cg.damageValue ) { return; } //if (cg.cameraMode) { // return; //} maxTime = DAMAGE_TIME; t = cg.time - cg.damageTime; if ( t <= 0 || t >= maxTime ) { return; } memset( &ent, 0, sizeof( ent ) ); ent.reType = RT_SPRITE; ent.renderfx = RF_FIRST_PERSON; VectorMA( cg.refdef.vieworg, 8, cg.refdef.viewaxis[0], ent.origin ); VectorMA( ent.origin, cg.damageX * -8, cg.refdef.viewaxis[1], ent.origin ); VectorMA( ent.origin, cg.damageY * 8, cg.refdef.viewaxis[2], ent.origin ); ent.radius = cg.damageValue * 3; ent.customShader = cgs.media.viewBloodShader; ent.shaderRGBA[0] = 255; ent.shaderRGBA[1] = 255; ent.shaderRGBA[2] = 255; ent.shaderRGBA[3] = 200 * ( 1.0 - ((float)t / maxTime) ); trap_R_AddRefEntityToScene( &ent ); } /* =============== CG_CalcViewValues Sets cg.refdef view values =============== */ static int CG_CalcViewValues( void ) { playerState_t *ps; memset( &cg.refdef, 0, sizeof( cg.refdef ) ); // strings for in game rendering // Q_strncpyz( cg.refdef.text[0], "Park Ranger", sizeof(cg.refdef.text[0]) ); // Q_strncpyz( cg.refdef.text[1], "19", sizeof(cg.refdef.text[1]) ); // calculate size of 3D view CG_CalcVrect(); ps = &cg.predictedPlayerState; /* if (cg.cameraMode) { vec3_t origin, angles; if (trap_getCameraInfo(cg.time, &origin, &angles)) { VectorCopy(origin, cg.refdef.vieworg); angles[ROLL] = 0; VectorCopy(angles, cg.refdefViewAngles); AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis ); return CG_CalcFov(); } else { cg.cameraMode = qfalse; } } */ // intermission view if ( ps->pm_type == PM_INTERMISSION ) { VectorCopy( ps->origin, cg.refdef.vieworg ); VectorCopy( ps->viewangles, cg.refdefViewAngles ); AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis ); return CG_CalcFov(); } cg.bobcycle = ( ps->bobCycle & 128 ) >> 7; cg.bobfracsin = fabs( sin( ( ps->bobCycle & 127 ) / 127.0 * M_PI ) ); cg.xyspeed = sqrt( ps->velocity[0] * ps->velocity[0] + ps->velocity[1] * ps->velocity[1] ); VectorCopy( ps->origin, cg.refdef.vieworg ); VectorCopy( ps->viewangles, cg.refdefViewAngles ); if (cg_cameraOrbit.integer) { if (cg.time > cg.nextOrbitTime) { cg.nextOrbitTime = cg.time + cg_cameraOrbitDelay.integer; cg_thirdPersonAngle.value += cg_cameraOrbit.value; } } // add error decay if ( cg_errorDecay.value > 0 ) { int t; float f; t = cg.time - cg.predictedErrorTime; f = ( cg_errorDecay.value - t ) / cg_errorDecay.value; if ( f > 0 && f < 1 ) { VectorMA( cg.refdef.vieworg, f, cg.predictedError, cg.refdef.vieworg ); } else { cg.predictedErrorTime = 0; } } if ( cg.renderingThirdPerson ) { // back away from character CG_OffsetThirdPersonView(); } else { // offset for local bobbing and kicks CG_OffsetFirstPersonView(); } // position eye reletive to origin AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis ); if ( cg.hyperspace ) { cg.refdef.rdflags |= RDF_NOWORLDMODEL | RDF_HYPERSPACE; } // field of view return CG_CalcFov(); } /* ===================== CG_PowerupTimerSounds ===================== */ static void CG_PowerupTimerSounds( void ) { int i; int t; // powerup timers going away for ( i = 0 ; i < MAX_POWERUPS ; i++ ) { t = cg.snap->ps.powerups[i]; if ( t <= cg.time ) { continue; } if ( t - cg.time >= POWERUP_BLINKS * POWERUP_BLINK_TIME ) { continue; } if ( ( t - cg.time ) / POWERUP_BLINK_TIME != ( t - cg.oldTime ) / POWERUP_BLINK_TIME ) { trap_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_ITEM, cgs.media.wearOffSound ); } } } /* ===================== CG_AddBufferedSound ===================== */ void CG_AddBufferedSound( sfxHandle_t sfx ) { if ( !sfx ) return; cg.soundBuffer[cg.soundBufferIn] = sfx; cg.soundBufferIn = (cg.soundBufferIn + 1) % MAX_SOUNDBUFFER; if (cg.soundBufferIn == cg.soundBufferOut) { cg.soundBufferOut++; } } /* ===================== CG_PlayBufferedSounds ===================== */ static void CG_PlayBufferedSounds( void ) { if ( cg.soundTime < cg.time ) { if (cg.soundBufferOut != cg.soundBufferIn && cg.soundBuffer[cg.soundBufferOut]) { trap_S_StartLocalSound(cg.soundBuffer[cg.soundBufferOut], CHAN_ANNOUNCER); cg.soundBuffer[cg.soundBufferOut] = 0; cg.soundBufferOut = (cg.soundBufferOut + 1) % MAX_SOUNDBUFFER; cg.soundTime = cg.time + 750; } } } //========================================================================= /* ================= CG_DrawActiveFrame Generates and draws a game scene and status information at the given time. ================= */ void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView, qboolean demoPlayback ) { int inwater; cg.time = serverTime; cg.demoPlayback = demoPlayback; // update cvars CG_UpdateCvars(); // if we are only updating the screen as a loading // pacifier, don't even try to read snapshots if ( cg.infoScreenText[0] != 0 ) { CG_DrawInformation(); return; } // any looped sounds will be respecified as entities // are added to the render list trap_S_ClearLoopingSounds(qfalse); // clear all the render lists trap_R_ClearScene(); // set up cg.snap and possibly cg.nextSnap CG_ProcessSnapshots(); // if we haven't received any snapshots yet, all // we can draw is the information screen if ( !cg.snap || ( cg.snap->snapFlags & SNAPFLAG_NOT_ACTIVE ) ) { CG_DrawInformation(); return; } // let the client system know what our weapon and zoom settings are trap_SetUserCmdValue( cg.weaponSelect, cg.zoomSensitivity ); // this counter will be bumped for every valid scene we generate cg.clientFrame++; // update cg.predictedPlayerState CG_PredictPlayerState(); // decide on third person view cg.renderingThirdPerson = cg_thirdPerson.integer || (cg.snap->ps.stats[STAT_HEALTH] <= 0); // build cg.refdef inwater = CG_CalcViewValues(); // first person blend blobs, done after AnglesToAxis if ( !cg.renderingThirdPerson ) { CG_DamageBlendBlob(); } // build the render lists if ( !cg.hyperspace ) { CG_AddPacketEntities(); // adter calcViewValues, so predicted player state is correct CG_AddMarks(); CG_AddParticles (); CG_AddLocalEntities(); } CG_AddViewWeapon( &cg.predictedPlayerState ); // add buffered sounds CG_PlayBufferedSounds(); // play buffered voice chats CG_PlayBufferedVoiceChats(); // finish up the rest of the refdef if ( cg.testModelEntity.hModel ) { CG_AddTestModel(); } cg.refdef.time = cg.time; memcpy( cg.refdef.areamask, cg.snap->areamask, sizeof( cg.refdef.areamask ) ); // warning sounds when powerup is wearing off CG_PowerupTimerSounds(); // update audio positions trap_S_Respatialize( cg.snap->ps.clientNum, cg.refdef.vieworg, cg.refdef.viewaxis, inwater ); // make sure the lagometerSample and frame timing isn't done twice when in stereo if ( stereoView != STEREO_RIGHT ) { cg.frametime = cg.time - cg.oldTime; if ( cg.frametime < 0 ) { cg.frametime = 0; } cg.oldTime = cg.time; CG_AddLagometerFrameInfo(); } if (cg_timescale.value != cg_timescaleFadeEnd.value) { if (cg_timescale.value < cg_timescaleFadeEnd.value) { cg_timescale.value += cg_timescaleFadeSpeed.value * ((float)cg.frametime) / 1000; if (cg_timescale.value > cg_timescaleFadeEnd.value) cg_timescale.value = cg_timescaleFadeEnd.value; } else { cg_timescale.value -= cg_timescaleFadeSpeed.value * ((float)cg.frametime) / 1000; if (cg_timescale.value < cg_timescaleFadeEnd.value) cg_timescale.value = cg_timescaleFadeEnd.value; } if (cg_timescaleFadeSpeed.value) { trap_Cvar_Set("timescale", va("%f", cg_timescale.value)); } } // actually issue the rendering calls CG_DrawActive( stereoView ); if ( cg_stats.integer ) { CG_Printf( "cg.clientFrame:%i\n", cg.clientFrame ); } } ================================================ FILE: src/cgame/cg_weapons.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // cg_weapons.c -- events and effects dealing with weapons #include "cg_local.h" /* ========================== CG_MachineGunEjectBrass ========================== */ static void CG_MachineGunEjectBrass( centity_t *cent ) { localEntity_t *le; refEntity_t *re; vec3_t velocity, xvelocity; vec3_t offset, xoffset; float waterScale = 1.0f; vec3_t v[3]; if ( cg_brassTime.integer <= 0 ) { return; } le = CG_AllocLocalEntity(); re = &le->refEntity; velocity[0] = 0; velocity[1] = -50 + 40 * crandom(); velocity[2] = 100 + 50 * crandom(); le->leType = LE_FRAGMENT; le->startTime = cg.time; le->endTime = le->startTime + cg_brassTime.integer + ( cg_brassTime.integer / 4 ) * random(); le->pos.trType = TR_GRAVITY; le->pos.trTime = cg.time - (rand()&15); AnglesToAxis( cent->lerpAngles, v ); offset[0] = 8; offset[1] = -4; offset[2] = 24; xoffset[0] = offset[0] * v[0][0] + offset[1] * v[1][0] + offset[2] * v[2][0]; xoffset[1] = offset[0] * v[0][1] + offset[1] * v[1][1] + offset[2] * v[2][1]; xoffset[2] = offset[0] * v[0][2] + offset[1] * v[1][2] + offset[2] * v[2][2]; VectorAdd( cent->lerpOrigin, xoffset, re->origin ); VectorCopy( re->origin, le->pos.trBase ); if ( CG_PointContents( re->origin, -1 ) & CONTENTS_WATER ) { waterScale = 0.10f; } xvelocity[0] = velocity[0] * v[0][0] + velocity[1] * v[1][0] + velocity[2] * v[2][0]; xvelocity[1] = velocity[0] * v[0][1] + velocity[1] * v[1][1] + velocity[2] * v[2][1]; xvelocity[2] = velocity[0] * v[0][2] + velocity[1] * v[1][2] + velocity[2] * v[2][2]; VectorScale( xvelocity, waterScale, le->pos.trDelta ); AxisCopy( axisDefault, re->axis ); re->hModel = cgs.media.machinegunBrassModel; le->bounceFactor = 0.4 * waterScale; le->angles.trType = TR_LINEAR; le->angles.trTime = cg.time; le->angles.trBase[0] = rand()&31; le->angles.trBase[1] = rand()&31; le->angles.trBase[2] = rand()&31; le->angles.trDelta[0] = 2; le->angles.trDelta[1] = 1; le->angles.trDelta[2] = 0; le->leFlags = LEF_TUMBLE; le->leBounceSoundType = LEBS_BRASS; le->leMarkType = LEMT_NONE; } /* ========================== CG_ShotgunEjectBrass ========================== */ static void CG_ShotgunEjectBrass( centity_t *cent ) { localEntity_t *le; refEntity_t *re; vec3_t velocity, xvelocity; vec3_t offset, xoffset; vec3_t v[3]; int i; if ( cg_brassTime.integer <= 0 ) { return; } for ( i = 0; i < 2; i++ ) { float waterScale = 1.0f; le = CG_AllocLocalEntity(); re = &le->refEntity; velocity[0] = 60 + 60 * crandom(); if ( i == 0 ) { velocity[1] = 40 + 10 * crandom(); } else { velocity[1] = -40 + 10 * crandom(); } velocity[2] = 100 + 50 * crandom(); le->leType = LE_FRAGMENT; le->startTime = cg.time; le->endTime = le->startTime + cg_brassTime.integer*3 + cg_brassTime.integer * random(); le->pos.trType = TR_GRAVITY; le->pos.trTime = cg.time; AnglesToAxis( cent->lerpAngles, v ); offset[0] = 8; offset[1] = 0; offset[2] = 24; xoffset[0] = offset[0] * v[0][0] + offset[1] * v[1][0] + offset[2] * v[2][0]; xoffset[1] = offset[0] * v[0][1] + offset[1] * v[1][1] + offset[2] * v[2][1]; xoffset[2] = offset[0] * v[0][2] + offset[1] * v[1][2] + offset[2] * v[2][2]; VectorAdd( cent->lerpOrigin, xoffset, re->origin ); VectorCopy( re->origin, le->pos.trBase ); if ( CG_PointContents( re->origin, -1 ) & CONTENTS_WATER ) { waterScale = 0.10f; } xvelocity[0] = velocity[0] * v[0][0] + velocity[1] * v[1][0] + velocity[2] * v[2][0]; xvelocity[1] = velocity[0] * v[0][1] + velocity[1] * v[1][1] + velocity[2] * v[2][1]; xvelocity[2] = velocity[0] * v[0][2] + velocity[1] * v[1][2] + velocity[2] * v[2][2]; VectorScale( xvelocity, waterScale, le->pos.trDelta ); AxisCopy( axisDefault, re->axis ); re->hModel = cgs.media.shotgunBrassModel; le->bounceFactor = 0.3f; le->angles.trType = TR_LINEAR; le->angles.trTime = cg.time; le->angles.trBase[0] = rand()&31; le->angles.trBase[1] = rand()&31; le->angles.trBase[2] = rand()&31; le->angles.trDelta[0] = 1; le->angles.trDelta[1] = 0.5; le->angles.trDelta[2] = 0; le->leFlags = LEF_TUMBLE; le->leBounceSoundType = LEBS_BRASS; le->leMarkType = LEMT_NONE; } } /* ========================== CG_RailTrail ========================== */ void CG_RailTrail (clientInfo_t *ci, vec3_t start, vec3_t end) { vec3_t axis[36], move, move2, next_move, vec, temp; float len; int i, j, skip; localEntity_t *le; refEntity_t *re; #define RADIUS 4 #define ROTATION 1 #define SPACING 5 start[2] -= 4; VectorCopy (start, move); VectorSubtract (end, start, vec); len = VectorNormalize (vec); PerpendicularVector(temp, vec); for (i = 0 ; i < 36; i++) { RotatePointAroundVector(axis[i], vec, temp, i * 10);//banshee 2.4 was 10 } le = CG_AllocLocalEntity(); re = &le->refEntity; le->leType = LE_FADE_RGB; le->startTime = cg.time; le->endTime = cg.time + cg_railTrailTime.value; le->lifeRate = 1.0 / (le->endTime - le->startTime); re->shaderTime = cg.time / 1000.0f; re->reType = RT_RAIL_CORE; re->customShader = cgs.media.railCoreShader; VectorCopy(start, re->origin); VectorCopy(end, re->oldorigin); re->shaderRGBA[0] = ci->color1[0] * 255; re->shaderRGBA[1] = ci->color1[1] * 255; re->shaderRGBA[2] = ci->color1[2] * 255; re->shaderRGBA[3] = 255; le->color[0] = ci->color1[0] * 0.75; le->color[1] = ci->color1[1] * 0.75; le->color[2] = ci->color1[2] * 0.75; le->color[3] = 1.0f; AxisClear( re->axis ); VectorMA(move, 20, vec, move); VectorCopy(move, next_move); VectorScale (vec, SPACING, vec); if (cg_oldRail.integer != 0) { // nudge down a bit so it isn't exactly in center re->origin[2] -= 8; re->oldorigin[2] -= 8; return; } skip = -1; j = 18; for (i = 0; i < len; i += SPACING) { if (i != skip) { skip = i + SPACING; le = CG_AllocLocalEntity(); re = &le->refEntity; le->leFlags = LEF_PUFF_DONT_SCALE; le->leType = LE_MOVE_SCALE_FADE; le->startTime = cg.time; le->endTime = cg.time + (i>>1) + 600; le->lifeRate = 1.0 / (le->endTime - le->startTime); re->shaderTime = cg.time / 1000.0f; re->reType = RT_SPRITE; re->radius = 1.1f; re->customShader = cgs.media.railRingsShader; re->shaderRGBA[0] = ci->color2[0] * 255; re->shaderRGBA[1] = ci->color2[1] * 255; re->shaderRGBA[2] = ci->color2[2] * 255; re->shaderRGBA[3] = 255; le->color[0] = ci->color2[0] * 0.75; le->color[1] = ci->color2[1] * 0.75; le->color[2] = ci->color2[2] * 0.75; le->color[3] = 1.0f; le->pos.trType = TR_LINEAR; le->pos.trTime = cg.time; VectorCopy( move, move2); VectorMA(move2, RADIUS , axis[j], move2); VectorCopy(move2, le->pos.trBase); le->pos.trDelta[0] = axis[j][0]*6; le->pos.trDelta[1] = axis[j][1]*6; le->pos.trDelta[2] = axis[j][2]*6; } VectorAdd (move, vec, move); j = j + ROTATION < 36 ? j + ROTATION : (j + ROTATION) % 36; } } /* ========================== CG_RocketTrail ========================== */ static void CG_RocketTrail( centity_t *ent, const weaponInfo_t *wi ) { int step; vec3_t origin, lastPos; int t; int startTime, contents; int lastContents; entityState_t *es; vec3_t up; localEntity_t *smoke; if ( cg_noProjectileTrail.integer ) { return; } up[0] = 0; up[1] = 0; up[2] = 0; step = 50; es = &ent->currentState; startTime = ent->trailTime; t = step * ( (startTime + step) / step ); BG_EvaluateTrajectory( &es->pos, cg.time, origin ); contents = CG_PointContents( origin, -1 ); // if object (e.g. grenade) is stationary, don't toss up smoke if ( es->pos.trType == TR_STATIONARY ) { ent->trailTime = cg.time; return; } BG_EvaluateTrajectory( &es->pos, ent->trailTime, lastPos ); lastContents = CG_PointContents( lastPos, -1 ); ent->trailTime = cg.time; if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) { if ( contents & lastContents & CONTENTS_WATER ) { CG_BubbleTrail( lastPos, origin, 8 ); } return; } for ( ; t <= ent->trailTime ; t += step ) { BG_EvaluateTrajectory( &es->pos, t, lastPos ); smoke = CG_SmokePuff( lastPos, up, wi->trailRadius, 1, 1, 1, 0.33f, wi->wiTrailTime, t, 0, 0, cgs.media.smokePuffShader ); // use the optimized local entity add smoke->leType = LE_SCALE_FADE; } } /* ========================== CG_NailTrail ========================== */ static void CG_PlasmaTrail( centity_t *cent, const weaponInfo_t *wi ) { localEntity_t *le; refEntity_t *re; entityState_t *es; vec3_t velocity, xvelocity, origin; vec3_t offset, xoffset; vec3_t v[3]; int t, startTime, step; float waterScale = 1.0f; if ( cg_noProjectileTrail.integer || cg_oldPlasma.integer ) { return; } step = 50; es = ¢->currentState; startTime = cent->trailTime; t = step * ( (startTime + step) / step ); BG_EvaluateTrajectory( &es->pos, cg.time, origin ); le = CG_AllocLocalEntity(); re = &le->refEntity; velocity[0] = 60 - 120 * crandom(); velocity[1] = 40 - 80 * crandom(); velocity[2] = 100 - 200 * crandom(); le->leType = LE_MOVE_SCALE_FADE; le->leFlags = LEF_TUMBLE; le->leBounceSoundType = LEBS_NONE; le->leMarkType = LEMT_NONE; le->startTime = cg.time; le->endTime = le->startTime + 600; le->pos.trType = TR_GRAVITY; le->pos.trTime = cg.time; AnglesToAxis( cent->lerpAngles, v ); offset[0] = 2; offset[1] = 2; offset[2] = 2; xoffset[0] = offset[0] * v[0][0] + offset[1] * v[1][0] + offset[2] * v[2][0]; xoffset[1] = offset[0] * v[0][1] + offset[1] * v[1][1] + offset[2] * v[2][1]; xoffset[2] = offset[0] * v[0][2] + offset[1] * v[1][2] + offset[2] * v[2][2]; VectorAdd( origin, xoffset, re->origin ); VectorCopy( re->origin, le->pos.trBase ); if ( CG_PointContents( re->origin, -1 ) & CONTENTS_WATER ) { waterScale = 0.10f; } xvelocity[0] = velocity[0] * v[0][0] + velocity[1] * v[1][0] + velocity[2] * v[2][0]; xvelocity[1] = velocity[0] * v[0][1] + velocity[1] * v[1][1] + velocity[2] * v[2][1]; xvelocity[2] = velocity[0] * v[0][2] + velocity[1] * v[1][2] + velocity[2] * v[2][2]; VectorScale( xvelocity, waterScale, le->pos.trDelta ); AxisCopy( axisDefault, re->axis ); re->shaderTime = cg.time / 1000.0f; re->reType = RT_SPRITE; re->radius = 0.25f; re->customShader = cgs.media.railRingsShader; le->bounceFactor = 0.3f; re->shaderRGBA[0] = wi->flashDlightColor[0] * 63; re->shaderRGBA[1] = wi->flashDlightColor[1] * 63; re->shaderRGBA[2] = wi->flashDlightColor[2] * 63; re->shaderRGBA[3] = 63; le->color[0] = wi->flashDlightColor[0] * 0.2; le->color[1] = wi->flashDlightColor[1] * 0.2; le->color[2] = wi->flashDlightColor[2] * 0.2; le->color[3] = 0.25f; le->angles.trType = TR_LINEAR; le->angles.trTime = cg.time; le->angles.trBase[0] = rand()&31; le->angles.trBase[1] = rand()&31; le->angles.trBase[2] = rand()&31; le->angles.trDelta[0] = 1; le->angles.trDelta[1] = 0.5; le->angles.trDelta[2] = 0; } /* ========================== CG_GrappleTrail ========================== */ void CG_GrappleTrail( centity_t *ent, const weaponInfo_t *wi ) { vec3_t origin; entityState_t *es; vec3_t forward, up; refEntity_t beam; es = &ent->currentState; BG_EvaluateTrajectory( &es->pos, cg.time, origin ); ent->trailTime = cg.time; memset( &beam, 0, sizeof( beam ) ); //FIXME adjust for muzzle position VectorCopy ( cg_entities[ ent->currentState.otherEntityNum ].lerpOrigin, beam.origin ); beam.origin[2] += 26; AngleVectors( cg_entities[ ent->currentState.otherEntityNum ].lerpAngles, forward, NULL, up ); VectorMA( beam.origin, -6, up, beam.origin ); VectorCopy( origin, beam.oldorigin ); if (Distance( beam.origin, beam.oldorigin ) < 64 ) return; // Don't draw if close beam.reType = RT_LIGHTNING; beam.customShader = cgs.media.lightningShader; AxisClear( beam.axis ); beam.shaderRGBA[0] = 0xff; beam.shaderRGBA[1] = 0xff; beam.shaderRGBA[2] = 0xff; beam.shaderRGBA[3] = 0xff; trap_R_AddRefEntityToScene( &beam ); } /* ========================== CG_GrenadeTrail ========================== */ static void CG_GrenadeTrail( centity_t *ent, const weaponInfo_t *wi ) { CG_RocketTrail( ent, wi ); } /* ================= CG_RegisterWeapon The server says this item is used on this level ================= */ void CG_RegisterWeapon( int weaponNum ) { weaponInfo_t *weaponInfo; gitem_t *item, *ammo; char path[MAX_QPATH]; vec3_t mins, maxs; int i; weaponInfo = &cg_weapons[weaponNum]; if ( weaponNum == 0 ) { return; } if ( weaponInfo->registered ) { return; } memset( weaponInfo, 0, sizeof( *weaponInfo ) ); weaponInfo->registered = qtrue; for ( item = bg_itemlist + 1 ; item->classname ; item++ ) { if ( item->giType == IT_WEAPON && item->giTag == weaponNum ) { weaponInfo->item = item; break; } } if ( !item->classname ) { CG_Error( "Couldn't find weapon %i", weaponNum ); } CG_RegisterItemVisuals( item - bg_itemlist ); // load cmodel before model so filecache works weaponInfo->weaponModel = trap_R_RegisterModel( item->world_model[0] ); // calc midpoint for rotation trap_R_ModelBounds( weaponInfo->weaponModel, mins, maxs ); for ( i = 0 ; i < 3 ; i++ ) { weaponInfo->weaponMidpoint[i] = mins[i] + 0.5 * ( maxs[i] - mins[i] ); } weaponInfo->weaponIcon = trap_R_RegisterShader( item->icon ); weaponInfo->ammoIcon = trap_R_RegisterShader( item->icon ); for ( ammo = bg_itemlist + 1 ; ammo->classname ; ammo++ ) { if ( ammo->giType == IT_AMMO && ammo->giTag == weaponNum ) { break; } } if ( ammo->classname && ammo->world_model[0] ) { weaponInfo->ammoModel = trap_R_RegisterModel( ammo->world_model[0] ); } strcpy( path, item->world_model[0] ); COM_StripExtension( path, path ); strcat( path, "_flash.md3" ); weaponInfo->flashModel = trap_R_RegisterModel( path ); strcpy( path, item->world_model[0] ); COM_StripExtension( path, path ); strcat( path, "_barrel.md3" ); weaponInfo->barrelModel = trap_R_RegisterModel( path ); strcpy( path, item->world_model[0] ); COM_StripExtension( path, path ); strcat( path, "_hand.md3" ); weaponInfo->handsModel = trap_R_RegisterModel( path ); if ( !weaponInfo->handsModel ) { weaponInfo->handsModel = trap_R_RegisterModel( "models/weapons2/shotgun/shotgun_hand.md3" ); } weaponInfo->loopFireSound = qfalse; switch ( weaponNum ) { case WP_GAUNTLET: MAKERGB( weaponInfo->flashDlightColor, 0.6f, 0.6f, 1.0f ); weaponInfo->firingSound = trap_S_RegisterSound( "sound/weapons/melee/fstrun.wav", qfalse ); weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/melee/fstatck.wav", qfalse ); break; case WP_LIGHTNING: MAKERGB( weaponInfo->flashDlightColor, 0.6f, 0.6f, 1.0f ); weaponInfo->readySound = trap_S_RegisterSound( "sound/weapons/melee/fsthum.wav", qfalse ); weaponInfo->firingSound = trap_S_RegisterSound( "sound/weapons/lightning/lg_hum.wav", qfalse ); weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/lightning/lg_fire.wav", qfalse ); cgs.media.lightningShader = trap_R_RegisterShader( "lightningBoltNew"); cgs.media.lightningExplosionModel = trap_R_RegisterModel( "models/weaphits/crackle.md3" ); cgs.media.sfx_lghit1 = trap_S_RegisterSound( "sound/weapons/lightning/lg_hit.wav", qfalse ); cgs.media.sfx_lghit2 = trap_S_RegisterSound( "sound/weapons/lightning/lg_hit2.wav", qfalse ); cgs.media.sfx_lghit3 = trap_S_RegisterSound( "sound/weapons/lightning/lg_hit3.wav", qfalse ); break; case WP_GRAPPLING_HOOK: MAKERGB( weaponInfo->flashDlightColor, 0.6f, 0.6f, 1.0f ); weaponInfo->missileModel = trap_R_RegisterModel( "models/ammo/rocket/rocket.md3" ); weaponInfo->missileTrailFunc = CG_GrappleTrail; weaponInfo->missileDlight = 200; weaponInfo->wiTrailTime = 2000; weaponInfo->trailRadius = 64; MAKERGB( weaponInfo->missileDlightColor, 1, 0.75f, 0 ); weaponInfo->readySound = trap_S_RegisterSound( "sound/weapons/melee/fsthum.wav", qfalse ); weaponInfo->firingSound = trap_S_RegisterSound( "sound/weapons/melee/fstrun.wav", qfalse ); break; case WP_MACHINEGUN: MAKERGB( weaponInfo->flashDlightColor, 1, 1, 0 ); weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/machinegun/machgf1b.wav", qfalse ); weaponInfo->flashSound[1] = trap_S_RegisterSound( "sound/weapons/machinegun/machgf2b.wav", qfalse ); weaponInfo->flashSound[2] = trap_S_RegisterSound( "sound/weapons/machinegun/machgf3b.wav", qfalse ); weaponInfo->flashSound[3] = trap_S_RegisterSound( "sound/weapons/machinegun/machgf4b.wav", qfalse ); weaponInfo->ejectBrassFunc = CG_MachineGunEjectBrass; cgs.media.bulletExplosionShader = trap_R_RegisterShader( "bulletExplosion" ); break; case WP_SHOTGUN: MAKERGB( weaponInfo->flashDlightColor, 1, 1, 0 ); weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/shotgun/sshotf1b.wav", qfalse ); weaponInfo->ejectBrassFunc = CG_ShotgunEjectBrass; break; case WP_ROCKET_LAUNCHER: weaponInfo->missileModel = trap_R_RegisterModel( "models/ammo/rocket/rocket.md3" ); weaponInfo->missileSound = trap_S_RegisterSound( "sound/weapons/rocket/rockfly.wav", qfalse ); weaponInfo->missileTrailFunc = CG_RocketTrail; weaponInfo->missileDlight = 200; weaponInfo->wiTrailTime = 2000; weaponInfo->trailRadius = 64; MAKERGB( weaponInfo->missileDlightColor, 1, 0.75f, 0 ); MAKERGB( weaponInfo->flashDlightColor, 1, 0.75f, 0 ); weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/rocket/rocklf1a.wav", qfalse ); cgs.media.rocketExplosionShader = trap_R_RegisterShader( "rocketExplosion" ); break; case WP_GRENADE_LAUNCHER: weaponInfo->missileModel = trap_R_RegisterModel( "models/ammo/grenade1.md3" ); weaponInfo->missileTrailFunc = CG_GrenadeTrail; weaponInfo->wiTrailTime = 700; weaponInfo->trailRadius = 32; MAKERGB( weaponInfo->flashDlightColor, 1, 0.70f, 0 ); weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/grenade/grenlf1a.wav", qfalse ); cgs.media.grenadeExplosionShader = trap_R_RegisterShader( "grenadeExplosion" ); break; case WP_PLASMAGUN: // weaponInfo->missileModel = cgs.media.invulnerabilityPowerupModel; weaponInfo->missileTrailFunc = CG_PlasmaTrail; weaponInfo->missileSound = trap_S_RegisterSound( "sound/weapons/plasma/lasfly.wav", qfalse ); MAKERGB( weaponInfo->flashDlightColor, 0.6f, 0.6f, 1.0f ); weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/plasma/hyprbf1a.wav", qfalse ); cgs.media.plasmaExplosionShader = trap_R_RegisterShader( "plasmaExplosion" ); cgs.media.railRingsShader = trap_R_RegisterShader( "railDisc" ); break; case WP_RAILGUN: weaponInfo->readySound = trap_S_RegisterSound( "sound/weapons/railgun/rg_hum.wav", qfalse ); MAKERGB( weaponInfo->flashDlightColor, 1, 0.5f, 0 ); weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/railgun/railgf1a.wav", qfalse ); cgs.media.railExplosionShader = trap_R_RegisterShader( "railExplosion" ); cgs.media.railRingsShader = trap_R_RegisterShader( "railDisc" ); cgs.media.railCoreShader = trap_R_RegisterShader( "railCore" ); break; case WP_BFG: weaponInfo->readySound = trap_S_RegisterSound( "sound/weapons/bfg/bfg_hum.wav", qfalse ); MAKERGB( weaponInfo->flashDlightColor, 1, 0.7f, 1 ); weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/bfg/bfg_fire.wav", qfalse ); cgs.media.bfgExplosionShader = trap_R_RegisterShader( "bfgExplosion" ); weaponInfo->missileModel = trap_R_RegisterModel( "models/weaphits/bfg.md3" ); weaponInfo->missileSound = trap_S_RegisterSound( "sound/weapons/rocket/rockfly.wav", qfalse ); break; default: MAKERGB( weaponInfo->flashDlightColor, 1, 1, 1 ); weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/rocket/rocklf1a.wav", qfalse ); break; } } /* ================= CG_RegisterItemVisuals The server says this item is used on this level ================= */ void CG_RegisterItemVisuals( int itemNum ) { itemInfo_t *itemInfo; gitem_t *item; if ( itemNum < 0 || itemNum >= bg_numItems ) { CG_Error( "CG_RegisterItemVisuals: itemNum %d out of range [0-%d]", itemNum, bg_numItems-1 ); } itemInfo = &cg_items[ itemNum ]; if ( itemInfo->registered ) { return; } item = &bg_itemlist[ itemNum ]; memset( itemInfo, 0, sizeof( &itemInfo ) ); itemInfo->registered = qtrue; itemInfo->models[0] = trap_R_RegisterModel( item->world_model[0] ); itemInfo->icon = trap_R_RegisterShader( item->icon ); if ( item->giType == IT_WEAPON ) { CG_RegisterWeapon( item->giTag ); } // // powerups have an accompanying ring or sphere // if ( item->giType == IT_POWERUP || item->giType == IT_HEALTH || item->giType == IT_ARMOR || item->giType == IT_HOLDABLE ) { if ( item->world_model[1] ) { itemInfo->models[1] = trap_R_RegisterModel( item->world_model[1] ); } } } /* ======================================================================================== VIEW WEAPON ======================================================================================== */ /* ================= CG_MapTorsoToWeaponFrame ================= */ static int CG_MapTorsoToWeaponFrame( clientInfo_t *ci, int frame ) { // change weapon if ( frame >= ci->animations[TORSO_DROP].firstFrame && frame < ci->animations[TORSO_DROP].firstFrame + 9 ) { return frame - ci->animations[TORSO_DROP].firstFrame + 6; } // stand attack if ( frame >= ci->animations[TORSO_ATTACK].firstFrame && frame < ci->animations[TORSO_ATTACK].firstFrame + 6 ) { return 1 + frame - ci->animations[TORSO_ATTACK].firstFrame; } // stand attack 2 if ( frame >= ci->animations[TORSO_ATTACK2].firstFrame && frame < ci->animations[TORSO_ATTACK2].firstFrame + 6 ) { return 1 + frame - ci->animations[TORSO_ATTACK2].firstFrame; } return 0; } /* ============== CG_CalculateWeaponPosition ============== */ static void CG_CalculateWeaponPosition( vec3_t origin, vec3_t angles ) { float scale; int delta; float fracsin; VectorCopy( cg.refdef.vieworg, origin ); VectorCopy( cg.refdefViewAngles, angles ); // on odd legs, invert some angles if ( cg.bobcycle & 1 ) { scale = -cg.xyspeed; } else { scale = cg.xyspeed; } // gun angles from bobbing angles[ROLL] += scale * cg.bobfracsin * 0.005; angles[YAW] += scale * cg.bobfracsin * 0.01; angles[PITCH] += cg.xyspeed * cg.bobfracsin * 0.005; // drop the weapon when landing delta = cg.time - cg.landTime; if ( delta < LAND_DEFLECT_TIME ) { origin[2] += cg.landChange*0.25 * delta / LAND_DEFLECT_TIME; } else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) { origin[2] += cg.landChange*0.25 * (LAND_DEFLECT_TIME + LAND_RETURN_TIME - delta) / LAND_RETURN_TIME; } #if 0 // drop the weapon when stair climbing delta = cg.time - cg.stepTime; if ( delta < STEP_TIME/2 ) { origin[2] -= cg.stepChange*0.25 * delta / (STEP_TIME/2); } else if ( delta < STEP_TIME ) { origin[2] -= cg.stepChange*0.25 * (STEP_TIME - delta) / (STEP_TIME/2); } #endif // idle drift scale = cg.xyspeed + 40; fracsin = sin( cg.time * 0.001 ); angles[ROLL] += scale * fracsin * 0.01; angles[YAW] += scale * fracsin * 0.01; angles[PITCH] += scale * fracsin * 0.01; } /* =============== CG_LightningBolt Origin will be the exact tag point, which is slightly different than the muzzle point used for determining hits. The cent should be the non-predicted cent if it is from the player, so the endpoint will reflect the simulated strike (lagging the predicted angle) =============== */ static void CG_LightningBolt( centity_t *cent, vec3_t origin ) { trace_t trace; refEntity_t beam; vec3_t forward; vec3_t muzzlePoint, endPoint; if (cent->currentState.weapon != WP_LIGHTNING) { return; } memset( &beam, 0, sizeof( beam ) ); // CPMA "true" lightning if ((cent->currentState.number == cg.predictedPlayerState.clientNum) && (cg_trueLightning.value != 0)) { vec3_t angle; int i; for (i = 0; i < 3; i++) { float a = cent->lerpAngles[i] - cg.refdefViewAngles[i]; if (a > 180) { a -= 360; } if (a < -180) { a += 360; } angle[i] = cg.refdefViewAngles[i] + a * (1.0 - cg_trueLightning.value); if (angle[i] < 0) { angle[i] += 360; } if (angle[i] > 360) { angle[i] -= 360; } } AngleVectors(angle, forward, NULL, NULL ); VectorCopy(cent->lerpOrigin, muzzlePoint ); // VectorCopy(cg.refdef.vieworg, muzzlePoint ); } else { // !CPMA AngleVectors( cent->lerpAngles, forward, NULL, NULL ); VectorCopy(cent->lerpOrigin, muzzlePoint ); } // FIXME: crouch muzzlePoint[2] += DEFAULT_VIEWHEIGHT; VectorMA( muzzlePoint, 14, forward, muzzlePoint ); // project forward by the lightning range VectorMA( muzzlePoint, LIGHTNING_RANGE, forward, endPoint ); // see if it hit a wall CG_Trace( &trace, muzzlePoint, vec3_origin, vec3_origin, endPoint, cent->currentState.number, MASK_SHOT ); // this is the endpoint VectorCopy( trace.endpos, beam.oldorigin ); // use the provided origin, even though it may be slightly // different than the muzzle origin VectorCopy( origin, beam.origin ); beam.reType = RT_LIGHTNING; beam.customShader = cgs.media.lightningShader; trap_R_AddRefEntityToScene( &beam ); // add the impact flare if it hit something if ( trace.fraction < 1.0 ) { vec3_t angles; vec3_t dir; VectorSubtract( beam.oldorigin, beam.origin, dir ); VectorNormalize( dir ); memset( &beam, 0, sizeof( beam ) ); beam.hModel = cgs.media.lightningExplosionModel; VectorMA( trace.endpos, -16, dir, beam.origin ); // make a random orientation angles[0] = rand() % 360; angles[1] = rand() % 360; angles[2] = rand() % 360; AnglesToAxis( angles, beam.axis ); trap_R_AddRefEntityToScene( &beam ); } } /* static void CG_LightningBolt( centity_t *cent, vec3_t origin ) { trace_t trace; refEntity_t beam; vec3_t forward; vec3_t muzzlePoint, endPoint; if ( cent->currentState.weapon != WP_LIGHTNING ) { return; } memset( &beam, 0, sizeof( beam ) ); // find muzzle point for this frame VectorCopy( cent->lerpOrigin, muzzlePoint ); AngleVectors( cent->lerpAngles, forward, NULL, NULL ); // FIXME: crouch muzzlePoint[2] += DEFAULT_VIEWHEIGHT; VectorMA( muzzlePoint, 14, forward, muzzlePoint ); // project forward by the lightning range VectorMA( muzzlePoint, LIGHTNING_RANGE, forward, endPoint ); // see if it hit a wall CG_Trace( &trace, muzzlePoint, vec3_origin, vec3_origin, endPoint, cent->currentState.number, MASK_SHOT ); // this is the endpoint VectorCopy( trace.endpos, beam.oldorigin ); // use the provided origin, even though it may be slightly // different than the muzzle origin VectorCopy( origin, beam.origin ); beam.reType = RT_LIGHTNING; beam.customShader = cgs.media.lightningShader; trap_R_AddRefEntityToScene( &beam ); // add the impact flare if it hit something if ( trace.fraction < 1.0 ) { vec3_t angles; vec3_t dir; VectorSubtract( beam.oldorigin, beam.origin, dir ); VectorNormalize( dir ); memset( &beam, 0, sizeof( beam ) ); beam.hModel = cgs.media.lightningExplosionModel; VectorMA( trace.endpos, -16, dir, beam.origin ); // make a random orientation angles[0] = rand() % 360; angles[1] = rand() % 360; angles[2] = rand() % 360; AnglesToAxis( angles, beam.axis ); trap_R_AddRefEntityToScene( &beam ); } } */ /* =============== CG_SpawnRailTrail Origin will be the exact tag point, which is slightly different than the muzzle point used for determining hits. =============== */ static void CG_SpawnRailTrail( centity_t *cent, vec3_t origin ) { clientInfo_t *ci; if ( cent->currentState.weapon != WP_RAILGUN ) { return; } if ( !cent->pe.railgunFlash ) { return; } cent->pe.railgunFlash = qtrue; ci = &cgs.clientinfo[ cent->currentState.clientNum ]; CG_RailTrail( ci, origin, cent->pe.railgunImpact ); } /* ====================== CG_MachinegunSpinAngle ====================== */ #define SPIN_SPEED 0.9 #define COAST_TIME 1000 static float CG_MachinegunSpinAngle( centity_t *cent ) { int delta; float angle; float speed; delta = cg.time - cent->pe.barrelTime; if ( cent->pe.barrelSpinning ) { angle = cent->pe.barrelAngle + delta * SPIN_SPEED; } else { if ( delta > COAST_TIME ) { delta = COAST_TIME; } speed = 0.5 * ( SPIN_SPEED + (float)( COAST_TIME - delta ) / COAST_TIME ); angle = cent->pe.barrelAngle + delta * speed; } if ( cent->pe.barrelSpinning == !(cent->currentState.eFlags & EF_FIRING) ) { cent->pe.barrelTime = cg.time; cent->pe.barrelAngle = AngleMod( angle ); cent->pe.barrelSpinning = !!(cent->currentState.eFlags & EF_FIRING); } return angle; } /* ======================== CG_AddWeaponWithPowerups ======================== */ static void CG_AddWeaponWithPowerups( refEntity_t *gun, int powerups ) { // add powerup effects if ( powerups & ( 1 << PW_INVIS ) ) { gun->customShader = cgs.media.invisShader; trap_R_AddRefEntityToScene( gun ); } else { trap_R_AddRefEntityToScene( gun ); if ( powerups & ( 1 << PW_BATTLESUIT ) ) { gun->customShader = cgs.media.battleWeaponShader; trap_R_AddRefEntityToScene( gun ); } if ( powerups & ( 1 << PW_QUAD ) ) { gun->customShader = cgs.media.quadWeaponShader; trap_R_AddRefEntityToScene( gun ); } } } /* ============= CG_AddPlayerWeapon Used for both the view weapon (ps is valid) and the world modelother character models (ps is NULL) The main player will have this called for BOTH cases, so effects like light and sound should only be done on the world model case. ============= */ void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent, int team ) { refEntity_t gun; refEntity_t barrel; refEntity_t flash; vec3_t angles; weapon_t weaponNum; weaponInfo_t *weapon; centity_t *nonPredictedCent; // int col; weaponNum = cent->currentState.weapon; CG_RegisterWeapon( weaponNum ); weapon = &cg_weapons[weaponNum]; // add the weapon memset( &gun, 0, sizeof( gun ) ); VectorCopy( parent->lightingOrigin, gun.lightingOrigin ); gun.shadowPlane = parent->shadowPlane; gun.renderfx = parent->renderfx; // set custom shading for railgun refire rate if ( ps ) { if ( cg.predictedPlayerState.weapon == WP_RAILGUN && cg.predictedPlayerState.weaponstate == WEAPON_FIRING ) { float f; f = (float)cg.predictedPlayerState.weaponTime / 1500; gun.shaderRGBA[1] = 0; gun.shaderRGBA[0] = gun.shaderRGBA[2] = 255 * ( 1.0 - f ); } else { gun.shaderRGBA[0] = 255; gun.shaderRGBA[1] = 255; gun.shaderRGBA[2] = 255; gun.shaderRGBA[3] = 255; } } gun.hModel = weapon->weaponModel; if (!gun.hModel) { return; } if ( !ps ) { // add weapon ready sound cent->pe.lightningFiring = qfalse; if ( ( cent->currentState.eFlags & EF_FIRING ) && weapon->firingSound ) { // lightning gun and guantlet make a different sound when fire is held down trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, weapon->firingSound ); cent->pe.lightningFiring = qtrue; } else if ( weapon->readySound ) { trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, weapon->readySound ); } } CG_PositionEntityOnTag( &gun, parent, parent->hModel, "tag_weapon"); CG_AddWeaponWithPowerups( &gun, cent->currentState.powerups ); // add the spinning barrel if ( weapon->barrelModel ) { memset( &barrel, 0, sizeof( barrel ) ); VectorCopy( parent->lightingOrigin, barrel.lightingOrigin ); barrel.shadowPlane = parent->shadowPlane; barrel.renderfx = parent->renderfx; barrel.hModel = weapon->barrelModel; angles[YAW] = 0; angles[PITCH] = 0; angles[ROLL] = CG_MachinegunSpinAngle( cent ); AnglesToAxis( angles, barrel.axis ); CG_PositionRotatedEntityOnTag( &barrel, &gun, weapon->weaponModel, "tag_barrel" ); CG_AddWeaponWithPowerups( &barrel, cent->currentState.powerups ); } // make sure we aren't looking at cg.predictedPlayerEntity for LG nonPredictedCent = &cg_entities[cent->currentState.clientNum]; // if the index of the nonPredictedCent is not the same as the clientNum // then this is a fake player (like on teh single player podiums), so // go ahead and use the cent if( ( nonPredictedCent - cg_entities ) != cent->currentState.clientNum ) { nonPredictedCent = cent; } // add the flash if ( ( weaponNum == WP_LIGHTNING || weaponNum == WP_GAUNTLET || weaponNum == WP_GRAPPLING_HOOK ) && ( nonPredictedCent->currentState.eFlags & EF_FIRING ) ) { // continuous flash } else { // impulse flash if ( cg.time - cent->muzzleFlashTime > MUZZLE_FLASH_TIME && !cent->pe.railgunFlash ) { return; } } memset( &flash, 0, sizeof( flash ) ); VectorCopy( parent->lightingOrigin, flash.lightingOrigin ); flash.shadowPlane = parent->shadowPlane; flash.renderfx = parent->renderfx; flash.hModel = weapon->flashModel; if (!flash.hModel) { return; } angles[YAW] = 0; angles[PITCH] = 0; angles[ROLL] = crandom() * 10; AnglesToAxis( angles, flash.axis ); // colorize the railgun blast if ( weaponNum == WP_RAILGUN ) { clientInfo_t *ci; ci = &cgs.clientinfo[ cent->currentState.clientNum ]; flash.shaderRGBA[0] = 255 * ci->color1[0]; flash.shaderRGBA[1] = 255 * ci->color1[1]; flash.shaderRGBA[2] = 255 * ci->color1[2]; } CG_PositionRotatedEntityOnTag( &flash, &gun, weapon->weaponModel, "tag_flash"); trap_R_AddRefEntityToScene( &flash ); if ( ps || cg.renderingThirdPerson || cent->currentState.number != cg.predictedPlayerState.clientNum ) { // add lightning bolt CG_LightningBolt( nonPredictedCent, flash.origin ); // add rail trail CG_SpawnRailTrail( cent, flash.origin ); if ( weapon->flashDlightColor[0] || weapon->flashDlightColor[1] || weapon->flashDlightColor[2] ) { trap_R_AddLightToScene( flash.origin, 300 + (rand()&31), weapon->flashDlightColor[0], weapon->flashDlightColor[1], weapon->flashDlightColor[2] ); } } } /* ============== CG_AddViewWeapon Add the weapon, and flash for the player's view ============== */ void CG_AddViewWeapon( playerState_t *ps ) { refEntity_t hand; centity_t *cent; clientInfo_t *ci; float fovOffset; vec3_t angles; weaponInfo_t *weapon; if ( ps->persistant[PERS_TEAM] == TEAM_SPECTATOR ) { return; } if ( ps->pm_type == PM_INTERMISSION ) { return; } // no gun if in third person view or a camera is active //if ( cg.renderingThirdPerson || cg.cameraMode) { if ( cg.renderingThirdPerson ) { return; } // allow the gun to be completely removed if ( !cg_drawGun.integer ) { vec3_t origin; if ( cg.predictedPlayerState.eFlags & EF_FIRING ) { // special hack for lightning gun... VectorCopy( cg.refdef.vieworg, origin ); VectorMA( origin, -8, cg.refdef.viewaxis[2], origin ); CG_LightningBolt( &cg_entities[ps->clientNum], origin ); } return; } // don't draw if testing a gun model if ( cg.testGun ) { return; } // drop gun lower at higher fov if ( cg_fov.integer > 90 ) { fovOffset = -0.2 * ( cg_fov.integer - 90 ); } else { fovOffset = 0; } cent = &cg.predictedPlayerEntity; // &cg_entities[cg.snap->ps.clientNum]; CG_RegisterWeapon( ps->weapon ); weapon = &cg_weapons[ ps->weapon ]; memset (&hand, 0, sizeof(hand)); // set up gun position CG_CalculateWeaponPosition( hand.origin, angles ); VectorMA( hand.origin, cg_gun_x.value, cg.refdef.viewaxis[0], hand.origin ); VectorMA( hand.origin, cg_gun_y.value, cg.refdef.viewaxis[1], hand.origin ); VectorMA( hand.origin, (cg_gun_z.value+fovOffset), cg.refdef.viewaxis[2], hand.origin ); AnglesToAxis( angles, hand.axis ); // map torso animations to weapon animations if ( cg_gun_frame.integer ) { // development tool hand.frame = hand.oldframe = cg_gun_frame.integer; hand.backlerp = 0; } else { // get clientinfo for animation map ci = &cgs.clientinfo[ cent->currentState.clientNum ]; hand.frame = CG_MapTorsoToWeaponFrame( ci, cent->pe.torso.frame ); hand.oldframe = CG_MapTorsoToWeaponFrame( ci, cent->pe.torso.oldFrame ); hand.backlerp = cent->pe.torso.backlerp; } hand.hModel = weapon->handsModel; hand.renderfx = RF_DEPTHHACK | RF_FIRST_PERSON | RF_MINLIGHT; // add everything onto the hand CG_AddPlayerWeapon( &hand, ps, &cg.predictedPlayerEntity, ps->persistant[PERS_TEAM] ); } /* ============================================================================== WEAPON SELECTION ============================================================================== */ /* =================== CG_DrawWeaponSelect =================== */ void CG_DrawWeaponSelect( void ) { int i; int bits; int count; int x, y, w; char *name; float *color; // don't display if dead if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) { return; } color = CG_FadeColor( cg.weaponSelectTime, WEAPON_SELECT_TIME ); if ( !color ) { return; } trap_R_SetColor( color ); // showing weapon select clears pickup item display, but not the blend blob cg.itemPickupTime = 0; // count the number of weapons owned bits = cg.snap->ps.stats[ STAT_WEAPONS ]; count = 0; for ( i = 1 ; i < 16 ; i++ ) { if ( bits & ( 1 << i ) ) { count++; } } x = 320 - count * 20; y = 380; for ( i = 1 ; i < 16 ; i++ ) { if ( !( bits & ( 1 << i ) ) ) { continue; } CG_RegisterWeapon( i ); // draw weapon icon CG_DrawPic( x, y, 32, 32, cg_weapons[i].weaponIcon ); // draw selection marker if ( i == cg.weaponSelect ) { CG_DrawPic( x-4, y-4, 40, 40, cgs.media.selectShader ); } // no ammo cross on top if ( !cg.snap->ps.ammo[ i ] ) { CG_DrawPic( x, y, 32, 32, cgs.media.noammoShader ); } x += 40; } // draw the selected name if ( cg_weapons[ cg.weaponSelect ].item ) { name = cg_weapons[ cg.weaponSelect ].item->pickup_name; if ( name ) { w = CG_DrawStrlen( name ) * BIGCHAR_WIDTH; x = ( SCREEN_WIDTH - w ) / 2; CG_DrawBigStringColor(x, y - 22, name, color); } } trap_R_SetColor( NULL ); } /* =============== CG_WeaponSelectable =============== */ static qboolean CG_WeaponSelectable( int i ) { if ( !cg.snap->ps.ammo[i] ) { return qfalse; } if ( ! (cg.snap->ps.stats[ STAT_WEAPONS ] & ( 1 << i ) ) ) { return qfalse; } return qtrue; } /* =============== CG_NextWeapon_f =============== */ void CG_NextWeapon_f( void ) { int i; int original; if ( !cg.snap ) { return; } if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) { return; } cg.weaponSelectTime = cg.time; original = cg.weaponSelect; for ( i = 0 ; i < 16 ; i++ ) { cg.weaponSelect++; if ( cg.weaponSelect == 16 ) { cg.weaponSelect = 0; } if ( cg.weaponSelect == WP_GAUNTLET ) { continue; // never cycle to gauntlet } if ( CG_WeaponSelectable( cg.weaponSelect ) ) { break; } } if ( i == 16 ) { cg.weaponSelect = original; } } /* =============== CG_PrevWeapon_f =============== */ void CG_PrevWeapon_f( void ) { int i; int original; if ( !cg.snap ) { return; } if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) { return; } cg.weaponSelectTime = cg.time; original = cg.weaponSelect; for ( i = 0 ; i < 16 ; i++ ) { cg.weaponSelect--; if ( cg.weaponSelect == -1 ) { cg.weaponSelect = 15; } if ( cg.weaponSelect == WP_GAUNTLET ) { continue; // never cycle to gauntlet } if ( CG_WeaponSelectable( cg.weaponSelect ) ) { break; } } if ( i == 16 ) { cg.weaponSelect = original; } } /* =============== CG_Weapon_f =============== */ void CG_Weapon_f( void ) { int num; if ( !cg.snap ) { return; } if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) { return; } num = atoi( CG_Argv( 1 ) ); if ( num < 1 || num > 15 ) { return; } cg.weaponSelectTime = cg.time; if ( ! ( cg.snap->ps.stats[STAT_WEAPONS] & ( 1 << num ) ) ) { return; // don't have the weapon } cg.weaponSelect = num; } /* =================== CG_OutOfAmmoChange The current weapon has just run out of ammo =================== */ void CG_OutOfAmmoChange( void ) { int i; cg.weaponSelectTime = cg.time; for ( i = 15 ; i > 0 ; i-- ) { if ( CG_WeaponSelectable( i ) ) { cg.weaponSelect = i; break; } } } /* =================================================================================================== WEAPON EVENTS =================================================================================================== */ /* ================ CG_FireWeapon Caused by an EV_FIRE_WEAPON event ================ */ void CG_FireWeapon( centity_t *cent ) { entityState_t *ent; int c; weaponInfo_t *weap; ent = ¢->currentState; if ( ent->weapon == WP_NONE ) { return; } if ( ent->weapon >= WP_NUM_WEAPONS ) { CG_Error( "CG_FireWeapon: ent->weapon >= WP_NUM_WEAPONS" ); return; } weap = &cg_weapons[ ent->weapon ]; // mark the entity as muzzle flashing, so when it is added it will // append the flash to the weapon model cent->muzzleFlashTime = cg.time; // lightning gun only does this this on initial press if ( ent->weapon == WP_LIGHTNING ) { if ( cent->pe.lightningFiring ) { return; } } // play quad sound if needed if ( cent->currentState.powerups & ( 1 << PW_QUAD ) ) { trap_S_StartSound (NULL, cent->currentState.number, CHAN_ITEM, cgs.media.quadSound ); } // play a sound for ( c = 0 ; c < 4 ; c++ ) { if ( !weap->flashSound[c] ) { break; } } if ( c > 0 ) { c = rand() % c; if ( weap->flashSound[c] ) { trap_S_StartSound( NULL, ent->number, CHAN_WEAPON, weap->flashSound[c] ); } } // do brass ejection if ( weap->ejectBrassFunc && cg_brassTime.integer > 0 ) { weap->ejectBrassFunc( cent ); } } /* ================= CG_MissileHitWall Caused by an EV_MISSILE_MISS event, or directly by local bullet tracing ================= */ void CG_MissileHitWall( int weapon, int clientNum, vec3_t origin, vec3_t dir, impactSound_t soundType ) { qhandle_t mod; qhandle_t mark; qhandle_t shader; sfxHandle_t sfx; float radius; float light; vec3_t lightColor; localEntity_t *le; int r; qboolean alphaFade; qboolean isSprite; int duration; vec3_t sprOrg; vec3_t sprVel; mark = 0; radius = 32; sfx = 0; mod = 0; shader = 0; light = 0; lightColor[0] = 1; lightColor[1] = 1; lightColor[2] = 0; // set defaults isSprite = qfalse; duration = 600; switch ( weapon ) { default: case WP_LIGHTNING: // no explosion at LG impact, it is added with the beam r = rand() & 3; if ( r < 2 ) { sfx = cgs.media.sfx_lghit2; } else if ( r == 2 ) { sfx = cgs.media.sfx_lghit1; } else { sfx = cgs.media.sfx_lghit3; } mark = cgs.media.holeMarkShader; radius = 12; break; case WP_GRENADE_LAUNCHER: mod = cgs.media.dishFlashModel; shader = cgs.media.grenadeExplosionShader; sfx = cgs.media.sfx_rockexp; mark = cgs.media.burnMarkShader; radius = 64; light = 300; isSprite = qtrue; break; case WP_ROCKET_LAUNCHER: mod = cgs.media.dishFlashModel; shader = cgs.media.rocketExplosionShader; sfx = cgs.media.sfx_rockexp; mark = cgs.media.burnMarkShader; radius = 64; light = 300; isSprite = qtrue; duration = 1000; lightColor[0] = 1; lightColor[1] = 0.75; lightColor[2] = 0.0; if (cg_oldRocket.integer == 0) { // explosion sprite animation VectorMA( origin, 24, dir, sprOrg ); VectorScale( dir, 64, sprVel ); CG_ParticleExplosion( "explode1", sprOrg, sprVel, 1400, 20, 30 ); } break; case WP_RAILGUN: mod = cgs.media.ringFlashModel; shader = cgs.media.railExplosionShader; sfx = cgs.media.sfx_plasmaexp; mark = cgs.media.energyMarkShader; radius = 24; break; case WP_PLASMAGUN: mod = cgs.media.ringFlashModel; shader = cgs.media.plasmaExplosionShader; sfx = cgs.media.sfx_plasmaexp; mark = cgs.media.energyMarkShader; radius = 16; break; case WP_BFG: mod = cgs.media.dishFlashModel; shader = cgs.media.bfgExplosionShader; sfx = cgs.media.sfx_rockexp; mark = cgs.media.burnMarkShader; radius = 32; isSprite = qtrue; break; case WP_SHOTGUN: mod = cgs.media.bulletFlashModel; shader = cgs.media.bulletExplosionShader; mark = cgs.media.bulletMarkShader; sfx = 0; radius = 4; break; case WP_MACHINEGUN: mod = cgs.media.bulletFlashModel; shader = cgs.media.bulletExplosionShader; mark = cgs.media.bulletMarkShader; r = rand() & 3; if ( r == 0 ) { sfx = cgs.media.sfx_ric1; } else if ( r == 1 ) { sfx = cgs.media.sfx_ric2; } else { sfx = cgs.media.sfx_ric3; } radius = 8; break; } if ( sfx ) { trap_S_StartSound( origin, ENTITYNUM_WORLD, CHAN_AUTO, sfx ); } // // create the explosion // if ( mod ) { le = CG_MakeExplosion( origin, dir, mod, shader, duration, isSprite ); le->light = light; VectorCopy( lightColor, le->lightColor ); if ( weapon == WP_RAILGUN ) { // colorize with client color VectorCopy( cgs.clientinfo[clientNum].color1, le->color ); } } // // impact mark // alphaFade = (mark == cgs.media.energyMarkShader); // plasma fades alpha, all others fade color if ( weapon == WP_RAILGUN ) { float *color; // colorize with client color color = cgs.clientinfo[clientNum].color2; CG_ImpactMark( mark, origin, dir, random()*360, color[0],color[1], color[2],1, alphaFade, radius, qfalse ); } else { CG_ImpactMark( mark, origin, dir, random()*360, 1,1,1,1, alphaFade, radius, qfalse ); } } /* ================= CG_MissileHitPlayer ================= */ void CG_MissileHitPlayer( int weapon, vec3_t origin, vec3_t dir, int entityNum ) { CG_Bleed( origin, entityNum ); // some weapons will make an explosion with the blood, while // others will just make the blood switch ( weapon ) { case WP_GRENADE_LAUNCHER: case WP_ROCKET_LAUNCHER: CG_MissileHitWall( weapon, 0, origin, dir, IMPACTSOUND_FLESH ); break; default: break; } } /* ============================================================================ SHOTGUN TRACING ============================================================================ */ /* ================ CG_ShotgunPellet ================ */ static void CG_ShotgunPellet( vec3_t start, vec3_t end, int skipNum ) { trace_t tr; int sourceContentType, destContentType; CG_Trace( &tr, start, NULL, NULL, end, skipNum, MASK_SHOT ); sourceContentType = trap_CM_PointContents( start, 0 ); destContentType = trap_CM_PointContents( tr.endpos, 0 ); // FIXME: should probably move this cruft into CG_BubbleTrail if ( sourceContentType == destContentType ) { if ( sourceContentType & CONTENTS_WATER ) { CG_BubbleTrail( start, tr.endpos, 32 ); } } else if ( sourceContentType & CONTENTS_WATER ) { trace_t trace; trap_CM_BoxTrace( &trace, end, start, NULL, NULL, 0, CONTENTS_WATER ); CG_BubbleTrail( start, trace.endpos, 32 ); } else if ( destContentType & CONTENTS_WATER ) { trace_t trace; trap_CM_BoxTrace( &trace, start, end, NULL, NULL, 0, CONTENTS_WATER ); CG_BubbleTrail( tr.endpos, trace.endpos, 32 ); } if ( tr.surfaceFlags & SURF_NOIMPACT ) { return; } if ( cg_entities[tr.entityNum].currentState.eType == ET_PLAYER ) { CG_MissileHitPlayer( WP_SHOTGUN, tr.endpos, tr.plane.normal, tr.entityNum ); } else { if ( tr.surfaceFlags & SURF_NOIMPACT ) { // SURF_NOIMPACT will not make a flame puff or a mark return; } if ( tr.surfaceFlags & SURF_METALSTEPS ) { CG_MissileHitWall( WP_SHOTGUN, 0, tr.endpos, tr.plane.normal, IMPACTSOUND_METAL ); } else { CG_MissileHitWall( WP_SHOTGUN, 0, tr.endpos, tr.plane.normal, IMPACTSOUND_DEFAULT ); } } } /* ================ CG_ShotgunPattern Perform the same traces the server did to locate the hit splashes ================ */ static void CG_ShotgunPattern( vec3_t origin, vec3_t origin2, int seed, int otherEntNum ) { int i; float r, u; vec3_t end; vec3_t forward, right, up; // derive the right and up vectors from the forward vector, because // the client won't have any other information VectorNormalize2( origin2, forward ); PerpendicularVector( right, forward ); CrossProduct( forward, right, up ); // generate the "random" spread pattern for ( i = 0 ; i < DEFAULT_SHOTGUN_COUNT ; i++ ) { r = Q_crandom( &seed ) * DEFAULT_SHOTGUN_SPREAD * 16; u = Q_crandom( &seed ) * DEFAULT_SHOTGUN_SPREAD * 16; VectorMA( origin, 8192 * 16, forward, end); VectorMA (end, r, right, end); VectorMA (end, u, up, end); CG_ShotgunPellet( origin, end, otherEntNum ); } } /* ============== CG_ShotgunFire ============== */ void CG_ShotgunFire( entityState_t *es ) { vec3_t v; vec3_t up; int contents; VectorSubtract( es->origin2, es->pos.trBase, v ); VectorNormalize( v ); VectorScale( v, 32, v ); VectorAdd( es->pos.trBase, v, v ); contents = trap_CM_PointContents( es->pos.trBase, 0 ); if ( !( contents & CONTENTS_WATER ) ) { VectorSet( up, 0, 0, 8 ); CG_SmokePuff( v, up, 32, 1, 1, 1, 0.33f, 900, cg.time, 0, LEF_PUFF_DONT_SCALE, cgs.media.shotgunSmokePuffShader ); } CG_ShotgunPattern( es->pos.trBase, es->origin2, es->eventParm, es->otherEntityNum ); } /* ============================================================================ BULLETS ============================================================================ */ /* =============== CG_Tracer =============== */ void CG_Tracer( vec3_t source, vec3_t dest ) { vec3_t forward, right; polyVert_t verts[4]; vec3_t line; float len, begin, end; vec3_t start, finish; vec3_t midpoint; // tracer VectorSubtract( dest, source, forward ); len = VectorNormalize( forward ); // start at least a little ways from the muzzle if ( len < 100 ) { return; } begin = 50 + random() * (len - 60); end = begin + cg_tracerLength.value; if ( end > len ) { end = len; } VectorMA( source, begin, forward, start ); VectorMA( source, end, forward, finish ); line[0] = DotProduct( forward, cg.refdef.viewaxis[1] ); line[1] = DotProduct( forward, cg.refdef.viewaxis[2] ); VectorScale( cg.refdef.viewaxis[1], line[1], right ); VectorMA( right, -line[0], cg.refdef.viewaxis[2], right ); VectorNormalize( right ); VectorMA( finish, cg_tracerWidth.value, right, verts[0].xyz ); verts[0].st[0] = 0; verts[0].st[1] = 1; verts[0].modulate[0] = 255; verts[0].modulate[1] = 255; verts[0].modulate[2] = 255; verts[0].modulate[3] = 255; VectorMA( finish, -cg_tracerWidth.value, right, verts[1].xyz ); verts[1].st[0] = 1; verts[1].st[1] = 0; verts[1].modulate[0] = 255; verts[1].modulate[1] = 255; verts[1].modulate[2] = 255; verts[1].modulate[3] = 255; VectorMA( start, -cg_tracerWidth.value, right, verts[2].xyz ); verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255; verts[2].modulate[1] = 255; verts[2].modulate[2] = 255; verts[2].modulate[3] = 255; VectorMA( start, cg_tracerWidth.value, right, verts[3].xyz ); verts[3].st[0] = 0; verts[3].st[1] = 0; verts[3].modulate[0] = 255; verts[3].modulate[1] = 255; verts[3].modulate[2] = 255; verts[3].modulate[3] = 255; trap_R_AddPolyToScene( cgs.media.tracerShader, 4, verts ); midpoint[0] = ( start[0] + finish[0] ) * 0.5; midpoint[1] = ( start[1] + finish[1] ) * 0.5; midpoint[2] = ( start[2] + finish[2] ) * 0.5; // add the tracer sound trap_S_StartSound( midpoint, ENTITYNUM_WORLD, CHAN_AUTO, cgs.media.tracerSound ); } /* ====================== CG_CalcMuzzlePoint ====================== */ static qboolean CG_CalcMuzzlePoint( int entityNum, vec3_t muzzle ) { vec3_t forward; centity_t *cent; int anim; if ( entityNum == cg.snap->ps.clientNum ) { VectorCopy( cg.snap->ps.origin, muzzle ); muzzle[2] += cg.snap->ps.viewheight; AngleVectors( cg.snap->ps.viewangles, forward, NULL, NULL ); VectorMA( muzzle, 14, forward, muzzle ); return qtrue; } cent = &cg_entities[entityNum]; if ( !cent->currentValid ) { return qfalse; } VectorCopy( cent->currentState.pos.trBase, muzzle ); AngleVectors( cent->currentState.apos.trBase, forward, NULL, NULL ); anim = cent->currentState.legsAnim & ~ANIM_TOGGLEBIT; if ( anim == LEGS_WALKCR || anim == LEGS_IDLECR ) { muzzle[2] += CROUCH_VIEWHEIGHT; } else { muzzle[2] += DEFAULT_VIEWHEIGHT; } VectorMA( muzzle, 14, forward, muzzle ); return qtrue; } /* ====================== CG_Bullet Renders bullet effects. ====================== */ void CG_Bullet( vec3_t end, int sourceEntityNum, vec3_t normal, qboolean flesh, int fleshEntityNum ) { trace_t trace; int sourceContentType, destContentType; vec3_t start; // if the shooter is currently valid, calc a source point and possibly // do trail effects if ( sourceEntityNum >= 0 && cg_tracerChance.value > 0 ) { if ( CG_CalcMuzzlePoint( sourceEntityNum, start ) ) { sourceContentType = trap_CM_PointContents( start, 0 ); destContentType = trap_CM_PointContents( end, 0 ); // do a complete bubble trail if necessary if ( ( sourceContentType == destContentType ) && ( sourceContentType & CONTENTS_WATER ) ) { CG_BubbleTrail( start, end, 32 ); } // bubble trail from water into air else if ( ( sourceContentType & CONTENTS_WATER ) ) { trap_CM_BoxTrace( &trace, end, start, NULL, NULL, 0, CONTENTS_WATER ); CG_BubbleTrail( start, trace.endpos, 32 ); } // bubble trail from air into water else if ( ( destContentType & CONTENTS_WATER ) ) { trap_CM_BoxTrace( &trace, start, end, NULL, NULL, 0, CONTENTS_WATER ); CG_BubbleTrail( trace.endpos, end, 32 ); } // draw a tracer if ( random() < cg_tracerChance.value ) { CG_Tracer( start, end ); } } } // impact splash and mark if ( flesh ) { CG_Bleed( end, fleshEntityNum ); } else { CG_MissileHitWall( WP_MACHINEGUN, 0, end, normal, IMPACTSOUND_DEFAULT ); } } ================================================ FILE: src/cgame/cgame.bat ================================================ rem make sure we have a safe environement set LIBRARY= set INCLUDE= mkdir ..\..\intermediate\vm\cgame cd ..\..\intermediate\vm\cgame set PATH=..\..\..\tools\bin;%PATH% set src=..\..\..\source set cc=lcc -DQ3_VM -S -Wf-target=bytecode -Wf-g -I%src%\cgame -I%src%\game -I%src%\ui %1 %cc% %src%/game/bg_misc.c @if errorlevel 1 goto quit %cc% %src%/game/bg_pmove.c @if errorlevel 1 goto quit %cc% %src%/game/bg_slidemove.c @if errorlevel 1 goto quit %cc% %src%/game/bg_lib.c @if errorlevel 1 goto quit %cc% %src%/game/q_math.c @if errorlevel 1 goto quit %cc% %src%/game/q_shared.c @if errorlevel 1 goto quit %cc% %src%/cgame/cg_consolecmds.c @if errorlevel 1 goto quit %cc% %src%/cgame/cg_draw.c @if errorlevel 1 goto quit %cc% %src%/cgame/cg_drawtools.c @if errorlevel 1 goto quit %cc% %src%/cgame/cg_effects.c @if errorlevel 1 goto quit %cc% %src%/cgame/cg_ents.c @if errorlevel 1 goto quit %cc% %src%/cgame/cg_event.c @if errorlevel 1 goto quit %cc% %src%/cgame/cg_info.c @if errorlevel 1 goto quit %cc% %src%/cgame/cg_localents.c @if errorlevel 1 goto quit %cc% %src%/cgame/cg_main.c @if errorlevel 1 goto quit %cc% %src%/cgame/cg_marks.c @if errorlevel 1 goto quit %cc% %src%/cgame/cg_players.c @if errorlevel 1 goto quit %cc% %src%/cgame/cg_playerstate.c @if errorlevel 1 goto quit %cc% %src%/cgame/cg_predict.c @if errorlevel 1 goto quit %cc% %src%/cgame/cg_scoreboard.c @if errorlevel 1 goto quit %cc% %src%/cgame/cg_servercmds.c @if errorlevel 1 goto quit %cc% %src%/cgame/cg_snapshot.c @if errorlevel 1 goto quit %cc% %src%/cgame/cg_view.c @if errorlevel 1 goto quit %cc% %src%/cgame/cg_weapons.c @if errorlevel 1 goto quit q3asm -f %src%/cgame/cgame :quit cd %src%/cgame ================================================ FILE: src/cgame/cgame.q3asm ================================================ -o "..\..\..\binaries\vm\cgame.qvm" cg_main ..\..\..\source\cgame\cg_syscalls cg_consolecmds cg_draw cg_drawtools cg_effects cg_ents cg_event cg_info cg_localents cg_marks cg_players cg_playerstate cg_predict cg_scoreboard cg_servercmds cg_snapshot cg_view cg_weapons bg_slidemove bg_pmove bg_lib bg_misc q_math q_shared ================================================ FILE: src/cgame/tr_types.h ================================================ /* =========================================================================== 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 Foobar; 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 MAX_ENTITIES 1023 // can't be increased without changing drawsurf bit packing // renderfx flags #define RF_MINLIGHT 1 // allways have some light (viewmodel, some items) #define RF_THIRD_PERSON 2 // don't draw through eyes, only mirrors (player bodies, chat sprites) #define RF_FIRST_PERSON 4 // only draw through eyes (view weapon, damage blood blob) #define RF_DEPTHHACK 8 // for view weapon Z crunching #define RF_NOSHADOW 64 // don't add stencil shadows #define RF_LIGHTING_ORIGIN 128 // use refEntity->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 256 // use refEntity->shadowPlane #define RF_WRAP_FRAMES 512 // mod the model frames by the maxframes to allow continuous // animation without needing to know the frame count // refdef flags #define RDF_NOWORLDMODEL 1 // used for player configuration screen #define RDF_HYPERSPACE 4 // 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 } textureCompression_t; // // glDriverType_t is not used by the engine and quake3 game anymore. // Single value (GLDRV_ICD) is left only for compatibility with other mods. typedef enum { GLDRV_ICD // driver is integrated with window system } glDriverType_t; // // // glHardwareType_t is not used by the engine and quake3 game anymore. // Single value (GLHW_GENERIC) is left only for compatibility with other mods. typedef enum { GLHW_GENERIC // where everthing works the way it should } 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 maxActiveTextures; // multitexture ability int colorBits, depthBits, stencilBits; // // Obsolete. Should be here for compatibility with other mods. glDriverType_t UNUSED_driverType; // // // Obsolete. Should be here for compatibility with other mods. glHardwareType_t UNUSED_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; // // Obsolete. Should be here for compatibility with other mods. int UNUSED_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 smpActive; // dual processor } glconfig_t; #if defined(Q3_VM) || defined(_WIN32) #define OPENGL_DRIVER_NAME "opengl32" #endif // !defined _WIN32 #endif // __TR_TYPES_H ================================================ FILE: src/engine/botlib/aasfile.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ //NOTE: int = default signed // default long #define AASID (('S'<<24)+('A'<<16)+('A'<<8)+'E') #define AASVERSION_OLD 4 #define AASVERSION 5 //presence types #define PRESENCE_NONE 1 #define PRESENCE_NORMAL 2 #define PRESENCE_CROUCH 4 //travel types #define MAX_TRAVELTYPES 32 #define TRAVEL_INVALID 1 //temporary not possible #define TRAVEL_WALK 2 //walking #define TRAVEL_CROUCH 3 //crouching #define TRAVEL_BARRIERJUMP 4 //jumping onto a barrier #define TRAVEL_JUMP 5 //jumping #define TRAVEL_LADDER 6 //climbing a ladder #define TRAVEL_WALKOFFLEDGE 7 //walking of a ledge #define TRAVEL_SWIM 8 //swimming #define TRAVEL_WATERJUMP 9 //jump out of the water #define TRAVEL_TELEPORT 10 //teleportation #define TRAVEL_ELEVATOR 11 //travel by elevator #define TRAVEL_ROCKETJUMP 12 //rocket jumping required for travel #define TRAVEL_BFGJUMP 13 //bfg jumping required for travel #define TRAVEL_GRAPPLEHOOK 14 //grappling hook required for travel #define TRAVEL_DOUBLEJUMP 15 //double jump #define TRAVEL_RAMPJUMP 16 //ramp jump #define TRAVEL_STRAFEJUMP 17 //strafe jump #define TRAVEL_JUMPPAD 18 //jump pad #define TRAVEL_FUNCBOB 19 //func bob //additional travel flags #define TRAVELTYPE_MASK 0xFFFFFF #define TRAVELFLAG_NOTTEAM1 (1 << 24) #define TRAVELFLAG_NOTTEAM2 (2 << 24) //face flags #define FACE_SOLID 1 //just solid at the other side #define FACE_LADDER 2 //ladder #define FACE_GROUND 4 //standing on ground when in this face #define FACE_GAP 8 //gap in the ground #define FACE_LIQUID 16 //face seperating two areas with liquid #define FACE_LIQUIDSURFACE 32 //face seperating liquid and air #define FACE_BRIDGE 64 //can walk over this face if bridge is closed //area contents #define AREACONTENTS_WATER 1 #define AREACONTENTS_LAVA 2 #define AREACONTENTS_SLIME 4 #define AREACONTENTS_CLUSTERPORTAL 8 #define AREACONTENTS_TELEPORTAL 16 #define AREACONTENTS_ROUTEPORTAL 32 #define AREACONTENTS_TELEPORTER 64 #define AREACONTENTS_JUMPPAD 128 #define AREACONTENTS_DONOTENTER 256 #define AREACONTENTS_VIEWPORTAL 512 #define AREACONTENTS_MOVER 1024 #define AREACONTENTS_NOTTEAM1 2048 #define AREACONTENTS_NOTTEAM2 4096 //number of model of the mover inside this area #define AREACONTENTS_MODELNUMSHIFT 24 #define AREACONTENTS_MAXMODELNUM 0xFF #define AREACONTENTS_MODELNUM (AREACONTENTS_MAXMODELNUM << AREACONTENTS_MODELNUMSHIFT) //area flags #define AREA_GROUNDED 1 //bot can stand on the ground #define AREA_LADDER 2 //area contains one or more ladder faces #define AREA_LIQUID 4 //area contains a liquid #define AREA_DISABLED 8 //area is disabled for routing when set #define AREA_BRIDGE 16 //area ontop of a bridge //aas file header lumps #define AAS_LUMPS 14 #define AASLUMP_BBOXES 0 #define AASLUMP_VERTEXES 1 #define AASLUMP_PLANES 2 #define AASLUMP_EDGES 3 #define AASLUMP_EDGEINDEX 4 #define AASLUMP_FACES 5 #define AASLUMP_FACEINDEX 6 #define AASLUMP_AREAS 7 #define AASLUMP_AREASETTINGS 8 #define AASLUMP_REACHABILITY 9 #define AASLUMP_NODES 10 #define AASLUMP_PORTALS 11 #define AASLUMP_PORTALINDEX 12 #define AASLUMP_CLUSTERS 13 //========== bounding box ========= //bounding box typedef struct aas_bbox_s { int presencetype; int flags; vec3_t mins, maxs; } aas_bbox_t; //============ settings =========== //reachability to another area typedef struct aas_reachability_s { int areanum; //number of the reachable area int facenum; //number of the face towards the other area int edgenum; //number of the edge towards the other area vec3_t start; //start point of inter area movement vec3_t end; //end point of inter area movement int traveltype; //type of travel required to get to the area unsigned short int traveltime;//travel time of the inter area movement } aas_reachability_t; //area settings typedef struct aas_areasettings_s { //could also add all kind of statistic fields int contents; //contents of the area int areaflags; //several area flags int presencetype; //how a bot can be present in this area int cluster; //cluster the area belongs to, if negative it's a portal int clusterareanum; //number of the area in the cluster int numreachableareas; //number of reachable areas from this one int firstreachablearea; //first reachable area in the reachable area index } aas_areasettings_t; //cluster portal typedef struct aas_portal_s { int areanum; //area that is the actual portal int frontcluster; //cluster at front of portal int backcluster; //cluster at back of portal int clusterareanum[2]; //number of the area in the front and back cluster } aas_portal_t; //cluster portal index typedef int aas_portalindex_t; //cluster typedef struct aas_cluster_s { int numareas; //number of areas in the cluster int numreachabilityareas; //number of areas with reachabilities int numportals; //number of cluster portals int firstportal; //first cluster portal in the index } aas_cluster_t; //============ 3d definition ============ typedef vec3_t aas_vertex_t; //just a plane in the third dimension typedef struct aas_plane_s { vec3_t normal; //normal vector of the plane float dist; //distance of the plane (normal vector * distance = point in plane) int type; } aas_plane_t; //edge typedef struct aas_edge_s { int v[2]; //numbers of the vertexes of this edge } aas_edge_t; //edge index, negative if vertexes are reversed typedef int aas_edgeindex_t; //a face bounds an area, often it will also seperate two areas typedef struct aas_face_s { int planenum; //number of the plane this face is in int faceflags; //face flags (no use to create face settings for just this field) int numedges; //number of edges in the boundary of the face int firstedge; //first edge in the edge index int frontarea; //area at the front of this face int backarea; //area at the back of this face } aas_face_t; //face index, stores a negative index if backside of face typedef int aas_faceindex_t; //area with a boundary of faces typedef struct aas_area_s { int areanum; //number of this area //3d definition int numfaces; //number of faces used for the boundary of the area int firstface; //first face in the face index used for the boundary of the area vec3_t mins; //mins of the area vec3_t maxs; //maxs of the area vec3_t center; //'center' of the area } aas_area_t; //nodes of the bsp tree typedef struct aas_node_s { int planenum; int children[2]; //child nodes of this node, or areas as leaves when negative //when a child is zero it's a solid leaf } aas_node_t; //=========== aas file =============== //header lump typedef struct { int fileofs; int filelen; } aas_lump_t; //aas file header typedef struct aas_header_s { int ident; int version; int bspchecksum; //data entries aas_lump_t lumps[AAS_LUMPS]; } aas_header_t; //====== additional information ====== /* - when a node child is a solid leaf the node child number is zero - two adjacent areas (sharing a plane at opposite sides) share a face this face is a portal between the areas - when an area uses a face from the faceindex with a positive index then the face plane normal points into the area - the face edges are stored counter clockwise using the edgeindex - two adjacent convex areas (sharing a face) only share One face this is a simple result of the areas being convex - the areas can't have a mixture of ground and gap faces other mixtures of faces in one area are allowed - areas with the AREACONTENTS_CLUSTERPORTAL in the settings have the cluster number set to the negative portal number - edge zero is a dummy - face zero is a dummy - area zero is a dummy - node zero is a dummy */ ================================================ FILE: src/engine/botlib/be_aas_bsp.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_bsp.h * * desc: AAS * * $Archive: /source/code/botlib/be_aas_bsp.h $ * *****************************************************************************/ #ifdef AASINTERN //loads the given BSP file int AAS_LoadBSPFile(void); //dump the loaded BSP data void AAS_DumpBSPData(void); //unlink the given entity from the bsp tree leaves void AAS_UnlinkFromBSPLeaves(bsp_link_t *leaves); //link the given entity to the bsp tree leaves of the given model bsp_link_t *AAS_BSPLinkEntity(vec3_t absmins, vec3_t absmaxs, int entnum, int modelnum); //calculates collision with given entity qboolean AAS_EntityCollision(int entnum, vec3_t start, vec3_t boxmins, vec3_t boxmaxs, vec3_t end, int contentmask, bsp_trace_t *trace); //for debugging void AAS_PrintFreeBSPLinks(char *str); // #endif //AASINTERN #define MAX_EPAIRKEY 128 //trace through the world bsp_trace_t AAS_Trace( vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask); //returns the contents at the given point int AAS_PointContents(vec3_t point); //returns true when p2 is in the PVS of p1 qboolean AAS_inPVS(vec3_t p1, vec3_t p2); //returns true when p2 is in the PHS of p1 qboolean AAS_inPHS(vec3_t p1, vec3_t p2); //returns true if the given areas are connected qboolean AAS_AreasConnected(int area1, int area2); //creates a list with entities totally or partly within the given box int AAS_BoxEntities(vec3_t absmins, vec3_t absmaxs, int *list, int maxcount); //gets the mins, maxs and origin of a BSP model void AAS_BSPModelMinsMaxsOrigin(int modelnum, vec3_t angles, vec3_t mins, vec3_t maxs, vec3_t origin); //handle to the next bsp entity int AAS_NextBSPEntity(int ent); //return the value of the BSP epair key int AAS_ValueForBSPEpairKey(int ent, char *key, char *value, int size); //get a vector for the BSP epair key int AAS_VectorForBSPEpairKey(int ent, char *key, vec3_t v); //get a float for the BSP epair key int AAS_FloatForBSPEpairKey(int ent, char *key, float *value); //get an integer for the BSP epair key int AAS_IntForBSPEpairKey(int ent, char *key, int *value); ================================================ FILE: src/engine/botlib/be_aas_bspq3.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_bspq3.c * * desc: BSP, Environment Sampling * * $Archive: /MissionPack/code/botlib/be_aas_bspq3.c $ * *****************************************************************************/ #include "../../game/q_shared.h" #include "l_memory.h" #include "l_script.h" #include "l_precomp.h" #include "l_struct.h" #include "aasfile.h" #include "../../game/botlib.h" #include "../../game/be_aas.h" #include "be_aas_funcs.h" #include "be_aas_def.h" extern botlib_import_t botimport; //#define TRACE_DEBUG #define ON_EPSILON 0.005 //#define DEG2RAD( a ) (( a * M_PI ) / 180.0F) #define MAX_BSPENTITIES 2048 typedef struct rgb_s { int red; int green; int blue; } rgb_t; //bsp entity epair typedef struct bsp_epair_s { char *key; char *value; struct bsp_epair_s *next; } bsp_epair_t; //bsp data entity typedef struct bsp_entity_s { bsp_epair_t *epairs; } bsp_entity_t; //id Sofware BSP data typedef struct bsp_s { //true when bsp file is loaded int loaded; //entity data int entdatasize; char *dentdata; //bsp entities int numentities; bsp_entity_t entities[MAX_BSPENTITIES]; } bsp_t; //global bsp bsp_t bspworld; #ifdef BSP_DEBUG typedef struct cname_s { int value; char *name; } cname_t; cname_t contentnames[] = { {CONTENTS_SOLID,"CONTENTS_SOLID"}, {CONTENTS_WINDOW,"CONTENTS_WINDOW"}, {CONTENTS_AUX,"CONTENTS_AUX"}, {CONTENTS_LAVA,"CONTENTS_LAVA"}, {CONTENTS_SLIME,"CONTENTS_SLIME"}, {CONTENTS_WATER,"CONTENTS_WATER"}, {CONTENTS_MIST,"CONTENTS_MIST"}, {LAST_VISIBLE_CONTENTS,"LAST_VISIBLE_CONTENTS"}, {CONTENTS_AREAPORTAL,"CONTENTS_AREAPORTAL"}, {CONTENTS_PLAYERCLIP,"CONTENTS_PLAYERCLIP"}, {CONTENTS_MONSTERCLIP,"CONTENTS_MONSTERCLIP"}, {CONTENTS_CURRENT_0,"CONTENTS_CURRENT_0"}, {CONTENTS_CURRENT_90,"CONTENTS_CURRENT_90"}, {CONTENTS_CURRENT_180,"CONTENTS_CURRENT_180"}, {CONTENTS_CURRENT_270,"CONTENTS_CURRENT_270"}, {CONTENTS_CURRENT_UP,"CONTENTS_CURRENT_UP"}, {CONTENTS_CURRENT_DOWN,"CONTENTS_CURRENT_DOWN"}, {CONTENTS_ORIGIN,"CONTENTS_ORIGIN"}, {CONTENTS_MONSTER,"CONTENTS_MONSTER"}, {CONTENTS_DEADMONSTER,"CONTENTS_DEADMONSTER"}, {CONTENTS_DETAIL,"CONTENTS_DETAIL"}, {CONTENTS_TRANSLUCENT,"CONTENTS_TRANSLUCENT"}, {CONTENTS_LADDER,"CONTENTS_LADDER"}, {0, 0} }; void PrintContents(int contents) { int i; for (i = 0; contentnames[i].value; i++) { if (contents & contentnames[i].value) { botimport.Print(PRT_MESSAGE, "%s\n", contentnames[i].name); } //end if } //end for } //end of the function PrintContents #endif // BSP_DEBUG //=========================================================================== // traces axial boxes of any size through the world // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bsp_trace_t AAS_Trace(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask) { bsp_trace_t bsptrace; botimport.Trace(&bsptrace, start, mins, maxs, end, passent, contentmask); return bsptrace; } //end of the function AAS_Trace //=========================================================================== // returns the contents at the given point // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_PointContents(vec3_t point) { return botimport.PointContents(point); } //end of the function AAS_PointContents //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== qboolean AAS_EntityCollision(int entnum, vec3_t start, vec3_t boxmins, vec3_t boxmaxs, vec3_t end, int contentmask, bsp_trace_t *trace) { bsp_trace_t enttrace; botimport.EntityTrace(&enttrace, start, boxmins, boxmaxs, end, entnum, contentmask); if (enttrace.fraction < trace->fraction) { Com_Memcpy(trace, &enttrace, sizeof(bsp_trace_t)); return qtrue; } //end if return qfalse; } //end of the function AAS_EntityCollision //=========================================================================== // returns true if in Potentially Hearable Set // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== qboolean AAS_inPVS(vec3_t p1, vec3_t p2) { return (qboolean) botimport.inPVS(p1, p2); } //end of the function AAS_InPVS //=========================================================================== // returns true if in Potentially Visible Set // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== qboolean AAS_inPHS(vec3_t p1, vec3_t p2) { return qtrue; } //end of the function AAS_inPHS //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_BSPModelMinsMaxsOrigin(int modelnum, vec3_t angles, vec3_t mins, vec3_t maxs, vec3_t origin) { botimport.BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin); } //end of the function AAS_BSPModelMinsMaxs //=========================================================================== // unlinks the entity from all leaves // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_UnlinkFromBSPLeaves(bsp_link_t *leaves) { } //end of the function AAS_UnlinkFromBSPLeaves //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bsp_link_t *AAS_BSPLinkEntity(vec3_t absmins, vec3_t absmaxs, int entnum, int modelnum) { return NULL; } //end of the function AAS_BSPLinkEntity //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_BoxEntities(vec3_t absmins, vec3_t absmaxs, int *list, int maxcount) { return 0; } //end of the function AAS_BoxEntities //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_NextBSPEntity(int ent) { ent++; if (ent >= 1 && ent < bspworld.numentities) return ent; return 0; } //end of the function AAS_NextBSPEntity //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_BSPEntityInRange(int ent) { if (ent <= 0 || ent >= bspworld.numentities) { botimport.Print(PRT_MESSAGE, "bsp entity out of range\n"); return qfalse; } //end if return qtrue; } //end of the function AAS_BSPEntityInRange //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_ValueForBSPEpairKey(int ent, char *key, char *value, int size) { bsp_epair_t *epair; value[0] = '\0'; if (!AAS_BSPEntityInRange(ent)) return qfalse; for (epair = bspworld.entities[ent].epairs; epair; epair = epair->next) { if (!strcmp(epair->key, key)) { strncpy(value, epair->value, size-1); value[size-1] = '\0'; return qtrue; } //end if } //end for return qfalse; } //end of the function AAS_FindBSPEpair //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_VectorForBSPEpairKey(int ent, char *key, vec3_t v) { char buf[MAX_EPAIRKEY]; double v1, v2, v3; VectorClear(v); if (!AAS_ValueForBSPEpairKey(ent, key, buf, MAX_EPAIRKEY)) return qfalse; //scanf into doubles, then assign, so it is vec_t size independent v1 = v2 = v3 = 0; sscanf(buf, "%lf %lf %lf", &v1, &v2, &v3); v[0] = v1; v[1] = v2; v[2] = v3; return qtrue; } //end of the function AAS_VectorForBSPEpairKey //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_FloatForBSPEpairKey(int ent, char *key, float *value) { char buf[MAX_EPAIRKEY]; *value = 0; if (!AAS_ValueForBSPEpairKey(ent, key, buf, MAX_EPAIRKEY)) return qfalse; *value = atof(buf); return qtrue; } //end of the function AAS_FloatForBSPEpairKey //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_IntForBSPEpairKey(int ent, char *key, int *value) { char buf[MAX_EPAIRKEY]; *value = 0; if (!AAS_ValueForBSPEpairKey(ent, key, buf, MAX_EPAIRKEY)) return qfalse; *value = atoi(buf); return qtrue; } //end of the function AAS_IntForBSPEpairKey //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_FreeBSPEntities(void) { int i; bsp_entity_t *ent; bsp_epair_t *epair, *nextepair; for (i = 1; i < bspworld.numentities; i++) { ent = &bspworld.entities[i]; for (epair = ent->epairs; epair; epair = nextepair) { nextepair = epair->next; // if (epair->key) FreeMemory(epair->key); if (epair->value) FreeMemory(epair->value); FreeMemory(epair); } //end for } //end for bspworld.numentities = 0; } //end of the function AAS_FreeBSPEntities //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_ParseBSPEntities(void) { script_t *script; token_t token; bsp_entity_t *ent; bsp_epair_t *epair; script = LoadScriptMemory(bspworld.dentdata, bspworld.entdatasize, "entdata"); SetScriptFlags(script, SCFL_NOSTRINGWHITESPACES|SCFL_NOSTRINGESCAPECHARS);//SCFL_PRIMITIVE); bspworld.numentities = 1; while(PS_ReadToken(script, &token)) { if (strcmp(token.string, "{")) { ScriptError(script, "invalid %s\n", token.string); AAS_FreeBSPEntities(); FreeScript(script); return; } //end if if (bspworld.numentities >= MAX_BSPENTITIES) { botimport.Print(PRT_MESSAGE, "too many entities in BSP file\n"); break; } //end if ent = &bspworld.entities[bspworld.numentities]; bspworld.numentities++; ent->epairs = NULL; while(PS_ReadToken(script, &token)) { if (!strcmp(token.string, "}")) break; epair = (bsp_epair_t *) GetClearedHunkMemory(sizeof(bsp_epair_t)); epair->next = ent->epairs; ent->epairs = epair; if (token.type != TT_STRING) { ScriptError(script, "invalid %s\n", token.string); AAS_FreeBSPEntities(); FreeScript(script); return; } //end if StripDoubleQuotes(token.string); epair->key = (char *) GetHunkMemory((int)strlen(token.string) + 1); strcpy(epair->key, token.string); if (!PS_ExpectTokenType(script, TT_STRING, 0, &token)) { AAS_FreeBSPEntities(); FreeScript(script); return; } //end if StripDoubleQuotes(token.string); epair->value = (char *) GetHunkMemory((int)strlen(token.string) + 1); strcpy(epair->value, token.string); } //end while if (strcmp(token.string, "}")) { ScriptError(script, "missing }\n"); AAS_FreeBSPEntities(); FreeScript(script); return; } //end if } //end while FreeScript(script); } //end of the function AAS_ParseBSPEntities //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_BSPTraceLight(vec3_t start, vec3_t end, vec3_t endpos, int *red, int *green, int *blue) { return 0; } //end of the function AAS_BSPTraceLight //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_DumpBSPData(void) { AAS_FreeBSPEntities(); if (bspworld.dentdata) FreeMemory(bspworld.dentdata); bspworld.dentdata = NULL; bspworld.entdatasize = 0; // bspworld.loaded = qfalse; Com_Memset( &bspworld, 0, sizeof(bspworld) ); } //end of the function AAS_DumpBSPData //=========================================================================== // load an bsp file // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_LoadBSPFile(void) { AAS_DumpBSPData(); bspworld.entdatasize = (int)strlen(botimport.BSPEntityData()) + 1; bspworld.dentdata = (char *) GetClearedHunkMemory(bspworld.entdatasize); Com_Memcpy(bspworld.dentdata, botimport.BSPEntityData(), bspworld.entdatasize); AAS_ParseBSPEntities(); bspworld.loaded = qtrue; return BLERR_NOERROR; } //end of the function AAS_LoadBSPFile ================================================ FILE: src/engine/botlib/be_aas_cluster.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_cluster.c * * desc: area clustering * * $Archive: /MissionPack/code/botlib/be_aas_cluster.c $ * *****************************************************************************/ #include "../../game/q_shared.h" #include "l_memory.h" #include "l_script.h" #include "l_precomp.h" #include "l_struct.h" #include "l_log.h" #include "l_memory.h" #include "l_libvar.h" #include "aasfile.h" #include "../../game/botlib.h" #include "../../game/be_aas.h" #include "be_aas_funcs.h" #include "be_aas_def.h" extern botlib_import_t botimport; #define AAS_MAX_PORTALS 65536 #define AAS_MAX_PORTALINDEXSIZE 65536 #define AAS_MAX_CLUSTERS 65536 // #define MAX_PORTALAREAS 1024 // do not flood through area faces, only use reachabilities int nofaceflood = qtrue; //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_RemoveClusterAreas(void) { int i; for (i = 1; i < aasworld.numareas; i++) { aasworld.areasettings[i].cluster = 0; } //end for } //end of the function AAS_RemoveClusterAreas //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_ClearCluster(int clusternum) { int i; for (i = 1; i < aasworld.numareas; i++) { if (aasworld.areasettings[i].cluster == clusternum) { aasworld.areasettings[i].cluster = 0; } //end if } //end for } //end of the function AAS_ClearCluster //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_RemovePortalsClusterReference(int clusternum) { int portalnum; for (portalnum = 1; portalnum < aasworld.numportals; portalnum++) { if (aasworld.portals[portalnum].frontcluster == clusternum) { aasworld.portals[portalnum].frontcluster = 0; } //end if if (aasworld.portals[portalnum].backcluster == clusternum) { aasworld.portals[portalnum].backcluster = 0; } //end if } //end for } //end of the function AAS_RemovePortalsClusterReference //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_UpdatePortal(int areanum, int clusternum) { int portalnum; aas_portal_t *portal; aas_cluster_t *cluster; //find the portal of the area for (portalnum = 1; portalnum < aasworld.numportals; portalnum++) { if (aasworld.portals[portalnum].areanum == areanum) break; } //end for // if (portalnum == aasworld.numportals) { AAS_Error("no portal of area %d", areanum); return qtrue; } //end if // portal = &aasworld.portals[portalnum]; //if the portal is already fully updated if (portal->frontcluster == clusternum) return qtrue; if (portal->backcluster == clusternum) return qtrue; //if the portal has no front cluster yet if (!portal->frontcluster) { portal->frontcluster = clusternum; } //end if //if the portal has no back cluster yet else if (!portal->backcluster) { portal->backcluster = clusternum; } //end else if else { //remove the cluster portal flag contents aasworld.areasettings[areanum].contents &= ~AREACONTENTS_CLUSTERPORTAL; Log_Write("portal area %d is seperating more than two clusters\r\n", areanum); return qfalse; } //end else if (aasworld.portalindexsize >= AAS_MAX_PORTALINDEXSIZE) { AAS_Error("AAS_MAX_PORTALINDEXSIZE"); return qtrue; } //end if //set the area cluster number to the negative portal number aasworld.areasettings[areanum].cluster = -portalnum; //add the portal to the cluster using the portal index cluster = &aasworld.clusters[clusternum]; aasworld.portalindex[cluster->firstportal + cluster->numportals] = portalnum; aasworld.portalindexsize++; cluster->numportals++; return qtrue; } //end of the function AAS_UpdatePortal //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_FloodClusterAreas_r(int areanum, int clusternum) { aas_area_t *area; aas_face_t *face; int facenum, i; // if (areanum <= 0 || areanum >= aasworld.numareas) { AAS_Error("AAS_FloodClusterAreas_r: areanum out of range"); return qfalse; } //end if //if the area is already part of a cluster if (aasworld.areasettings[areanum].cluster > 0) { if (aasworld.areasettings[areanum].cluster == clusternum) return qtrue; // //there's a reachability going from one cluster to another only in one direction // AAS_Error("cluster %d touched cluster %d at area %d\r\n", clusternum, aasworld.areasettings[areanum].cluster, areanum); return qfalse; } //end if //don't add the cluster portal areas to the clusters if (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL) { return AAS_UpdatePortal(areanum, clusternum); } //end if //set the area cluster number aasworld.areasettings[areanum].cluster = clusternum; aasworld.areasettings[areanum].clusterareanum = aasworld.clusters[clusternum].numareas; //the cluster has an extra area aasworld.clusters[clusternum].numareas++; area = &aasworld.areas[areanum]; //use area faces to flood into adjacent areas if (!nofaceflood) { for (i = 0; i < area->numfaces; i++) { facenum = abs(aasworld.faceindex[area->firstface + i]); face = &aasworld.faces[facenum]; if (face->frontarea == areanum) { if (face->backarea) if (!AAS_FloodClusterAreas_r(face->backarea, clusternum)) return qfalse; } //end if else { if (face->frontarea) if (!AAS_FloodClusterAreas_r(face->frontarea, clusternum)) return qfalse; } //end else } //end for } //end if //use the reachabilities to flood into other areas for (i = 0; i < aasworld.areasettings[areanum].numreachableareas; i++) { if (!aasworld.reachability[ aasworld.areasettings[areanum].firstreachablearea + i].areanum) { continue; } //end if if (!AAS_FloodClusterAreas_r(aasworld.reachability[ aasworld.areasettings[areanum].firstreachablearea + i].areanum, clusternum)) return qfalse; } //end for return qtrue; } //end of the function AAS_FloodClusterAreas_r //=========================================================================== // try to flood from all areas without cluster into areas with a cluster set // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_FloodClusterAreasUsingReachabilities(int clusternum) { int i, j, areanum; for (i = 1; i < aasworld.numareas; i++) { //if this area already has a cluster set if (aasworld.areasettings[i].cluster) continue; //if this area is a cluster portal if (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL) continue; //loop over the reachable areas from this area for (j = 0; j < aasworld.areasettings[i].numreachableareas; j++) { //the reachable area areanum = aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].areanum; //if this area is a cluster portal if (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL) continue; //if this area has a cluster set if (aasworld.areasettings[areanum].cluster) { if (!AAS_FloodClusterAreas_r(i, clusternum)) return qfalse; i = 0; break; } //end if } //end for } //end for return qtrue; } //end of the function AAS_FloodClusterAreasUsingReachabilities //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_NumberClusterPortals(int clusternum) { int i, portalnum; aas_cluster_t *cluster; aas_portal_t *portal; cluster = &aasworld.clusters[clusternum]; for (i = 0; i < cluster->numportals; i++) { portalnum = aasworld.portalindex[cluster->firstportal + i]; portal = &aasworld.portals[portalnum]; if (portal->frontcluster == clusternum) { portal->clusterareanum[0] = cluster->numareas++; } //end if else { portal->clusterareanum[1] = cluster->numareas++; } //end else } //end for } //end of the function AAS_NumberClusterPortals //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_NumberClusterAreas(int clusternum) { int i, portalnum; aas_cluster_t *cluster; aas_portal_t *portal; aasworld.clusters[clusternum].numareas = 0; aasworld.clusters[clusternum].numreachabilityareas = 0; //number all areas in this cluster WITH reachabilities for (i = 1; i < aasworld.numareas; i++) { // if (aasworld.areasettings[i].cluster != clusternum) continue; // if (!AAS_AreaReachability(i)) continue; // aasworld.areasettings[i].clusterareanum = aasworld.clusters[clusternum].numareas; //the cluster has an extra area aasworld.clusters[clusternum].numareas++; aasworld.clusters[clusternum].numreachabilityareas++; } //end for //number all portals in this cluster WITH reachabilities cluster = &aasworld.clusters[clusternum]; for (i = 0; i < cluster->numportals; i++) { portalnum = aasworld.portalindex[cluster->firstportal + i]; portal = &aasworld.portals[portalnum]; if (!AAS_AreaReachability(portal->areanum)) continue; if (portal->frontcluster == clusternum) { portal->clusterareanum[0] = cluster->numareas++; aasworld.clusters[clusternum].numreachabilityareas++; } //end if else { portal->clusterareanum[1] = cluster->numareas++; aasworld.clusters[clusternum].numreachabilityareas++; } //end else } //end for //number all areas in this cluster WITHOUT reachabilities for (i = 1; i < aasworld.numareas; i++) { // if (aasworld.areasettings[i].cluster != clusternum) continue; // if (AAS_AreaReachability(i)) continue; // aasworld.areasettings[i].clusterareanum = aasworld.clusters[clusternum].numareas; //the cluster has an extra area aasworld.clusters[clusternum].numareas++; } //end for //number all portals in this cluster WITHOUT reachabilities cluster = &aasworld.clusters[clusternum]; for (i = 0; i < cluster->numportals; i++) { portalnum = aasworld.portalindex[cluster->firstportal + i]; portal = &aasworld.portals[portalnum]; if (AAS_AreaReachability(portal->areanum)) continue; if (portal->frontcluster == clusternum) { portal->clusterareanum[0] = cluster->numareas++; } //end if else { portal->clusterareanum[1] = cluster->numareas++; } //end else } //end for } //end of the function AAS_NumberClusterAreas //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_FindClusters(void) { int i; aas_cluster_t *cluster; AAS_RemoveClusterAreas(); // for (i = 1; i < aasworld.numareas; i++) { //if the area is already part of a cluster if (aasworld.areasettings[i].cluster) continue; // if not flooding through faces only use areas that have reachabilities if (nofaceflood) { if (!aasworld.areasettings[i].numreachableareas) continue; } //end if //if the area is a cluster portal if (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL) continue; if (aasworld.numclusters >= AAS_MAX_CLUSTERS) { AAS_Error("AAS_MAX_CLUSTERS"); return qfalse; } //end if cluster = &aasworld.clusters[aasworld.numclusters]; cluster->numareas = 0; cluster->numreachabilityareas = 0; cluster->firstportal = aasworld.portalindexsize; cluster->numportals = 0; //flood the areas in this cluster if (!AAS_FloodClusterAreas_r(i, aasworld.numclusters)) return qfalse; if (!AAS_FloodClusterAreasUsingReachabilities(aasworld.numclusters)) return qfalse; //number the cluster areas //AAS_NumberClusterPortals(aasworld.numclusters); AAS_NumberClusterAreas(aasworld.numclusters); //Log_Write("cluster %d has %d areas\r\n", aasworld.numclusters, cluster->numareas); aasworld.numclusters++; } //end for return qtrue; } //end of the function AAS_FindClusters //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_CreatePortals(void) { int i; aas_portal_t *portal; for (i = 1; i < aasworld.numareas; i++) { //if the area is a cluster portal if (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL) { if (aasworld.numportals >= AAS_MAX_PORTALS) { AAS_Error("AAS_MAX_PORTALS"); return; } //end if portal = &aasworld.portals[aasworld.numportals]; portal->areanum = i; portal->frontcluster = 0; portal->backcluster = 0; aasworld.numportals++; } //end if } //end for } //end of the function AAS_CreatePortals /* //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_MapContainsTeleporters(void) { bsp_entity_t *entities, *ent; char *classname; entities = AAS_ParseBSPEntities(); for (ent = entities; ent; ent = ent->next) { classname = AAS_ValueForBSPEpairKey(ent, "classname"); if (classname && !strcmp(classname, "misc_teleporter")) { AAS_FreeBSPEntities(entities); return qtrue; } //end if } //end for return qfalse; } //end of the function AAS_MapContainsTeleporters //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_NonConvexFaces(aas_face_t *face1, aas_face_t *face2, int side1, int side2) { int i, j, edgenum; aas_plane_t *plane1, *plane2; aas_edge_t *edge; plane1 = &aasworld.planes[face1->planenum ^ side1]; plane2 = &aasworld.planes[face2->planenum ^ side2]; //check if one of the points of face1 is at the back of the plane of face2 for (i = 0; i < face1->numedges; i++) { edgenum = abs(aasworld.edgeindex[face1->firstedge + i]); edge = &aasworld.edges[edgenum]; for (j = 0; j < 2; j++) { if (DotProduct(plane2->normal, aasworld.vertexes[edge->v[j]]) - plane2->dist < -0.01) return qtrue; } //end for } //end for for (i = 0; i < face2->numedges; i++) { edgenum = abs(aasworld.edgeindex[face2->firstedge + i]); edge = &aasworld.edges[edgenum]; for (j = 0; j < 2; j++) { if (DotProduct(plane1->normal, aasworld.vertexes[edge->v[j]]) - plane1->dist < -0.01) return qtrue; } //end for } //end for return qfalse; } //end of the function AAS_NonConvexFaces //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== qboolean AAS_CanMergeAreas(int *areanums, int numareas) { int i, j, s, face1num, face2num, side1, side2, fn1, fn2; aas_face_t *face1, *face2; aas_area_t *area1, *area2; for (i = 0; i < numareas; i++) { area1 = &aasworld.areas[areanums[i]]; for (fn1 = 0; fn1 < area1->numfaces; fn1++) { face1num = abs(aasworld.faceindex[area1->firstface + fn1]); face1 = &aasworld.faces[face1num]; side1 = face1->frontarea != areanums[i]; //check if the face isn't a shared one with one of the other areas for (s = 0; s < numareas; s++) { if (s == i) continue; if (face1->frontarea == s || face1->backarea == s) break; } //end for //if the face was a shared one if (s != numareas) continue; // for (j = 0; j < numareas; j++) { if (j == i) continue; area2 = &aasworld.areas[areanums[j]]; for (fn2 = 0; fn2 < area2->numfaces; fn2++) { face2num = abs(aasworld.faceindex[area2->firstface + fn2]); face2 = &aasworld.faces[face2num]; side2 = face2->frontarea != areanums[j]; //check if the face isn't a shared one with one of the other areas for (s = 0; s < numareas; s++) { if (s == j) continue; if (face2->frontarea == s || face2->backarea == s) break; } //end for //if the face was a shared one if (s != numareas) continue; // if (AAS_NonConvexFaces(face1, face2, side1, side2)) return qfalse; } //end for } //end for } //end for } //end for return qtrue; } //end of the function AAS_CanMergeAreas //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== qboolean AAS_NonConvexEdges(aas_edge_t *edge1, aas_edge_t *edge2, int side1, int side2, int planenum) { int i; vec3_t edgevec1, edgevec2, normal1, normal2; float dist1, dist2; aas_plane_t *plane; plane = &aasworld.planes[planenum]; VectorSubtract(aasworld.vertexes[edge1->v[1]], aasworld.vertexes[edge1->v[0]], edgevec1); VectorSubtract(aasworld.vertexes[edge2->v[1]], aasworld.vertexes[edge2->v[0]], edgevec2); if (side1) VectorInverse(edgevec1); if (side2) VectorInverse(edgevec2); // CrossProduct(edgevec1, plane->normal, normal1); dist1 = DotProduct(normal1, aasworld.vertexes[edge1->v[0]]); CrossProduct(edgevec2, plane->normal, normal2); dist2 = DotProduct(normal2, aasworld.vertexes[edge2->v[0]]); for (i = 0; i < 2; i++) { if (DotProduct(aasworld.vertexes[edge1->v[i]], normal2) - dist2 < -0.01) return qfalse; } //end for for (i = 0; i < 2; i++) { if (DotProduct(aasworld.vertexes[edge2->v[i]], normal1) - dist1 < -0.01) return qfalse; } //end for return qtrue; } //end of the function AAS_NonConvexEdges //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== qboolean AAS_CanMergeFaces(int *facenums, int numfaces, int planenum) { int i, j, s, edgenum1, edgenum2, side1, side2, en1, en2, ens; aas_face_t *face1, *face2, *otherface; aas_edge_t *edge1, *edge2; for (i = 0; i < numfaces; i++) { face1 = &aasworld.faces[facenums[i]]; for (en1 = 0; en1 < face1->numedges; en1++) { edgenum1 = aasworld.edgeindex[face1->firstedge + en1]; side1 = (edgenum1 < 0) ^ (face1->planenum != planenum); edgenum1 = abs(edgenum1); edge1 = &aasworld.edges[edgenum1]; //check if the edge is shared with another face for (s = 0; s < numfaces; s++) { if (s == i) continue; otherface = &aasworld.faces[facenums[s]]; for (ens = 0; ens < otherface->numedges; ens++) { if (edgenum1 == abs(aasworld.edgeindex[otherface->firstedge + ens])) break; } //end for if (ens != otherface->numedges) break; } //end for //if the edge was shared if (s != numfaces) continue; // for (j = 0; j < numfaces; j++) { if (j == i) continue; face2 = &aasworld.faces[facenums[j]]; for (en2 = 0; en2 < face2->numedges; en2++) { edgenum2 = aasworld.edgeindex[face2->firstedge + en2]; side2 = (edgenum2 < 0) ^ (face2->planenum != planenum); edgenum2 = abs(edgenum2); edge2 = &aasworld.edges[edgenum2]; //check if the edge is shared with another face for (s = 0; s < numfaces; s++) { if (s == i) continue; otherface = &aasworld.faces[facenums[s]]; for (ens = 0; ens < otherface->numedges; ens++) { if (edgenum2 == abs(aasworld.edgeindex[otherface->firstedge + ens])) break; } //end for if (ens != otherface->numedges) break; } //end for //if the edge was shared if (s != numfaces) continue; // if (AAS_NonConvexEdges(edge1, edge2, side1, side2, planenum)) return qfalse; } //end for } //end for } //end for } //end for return qtrue; } //end of the function AAS_CanMergeFaces*/ //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_ConnectedAreas_r(int *areanums, int numareas, int *connectedareas, int curarea) { int i, j, otherareanum, facenum; aas_area_t *area; aas_face_t *face; connectedareas[curarea] = qtrue; area = &aasworld.areas[areanums[curarea]]; for (i = 0; i < area->numfaces; i++) { facenum = abs(aasworld.faceindex[area->firstface + i]); face = &aasworld.faces[facenum]; //if the face is solid if (face->faceflags & FACE_SOLID) continue; //get the area at the other side of the face if (face->frontarea != areanums[curarea]) otherareanum = face->frontarea; else otherareanum = face->backarea; //check if the face is leading to one of the other areas for (j = 0; j < numareas; j++) { if (areanums[j] == otherareanum) break; } //end for //if the face isn't leading to one of the other areas if (j == numareas) continue; //if the other area is already connected if (connectedareas[j]) continue; //recursively proceed with the other area AAS_ConnectedAreas_r(areanums, numareas, connectedareas, j); } //end for } //end of the function AAS_ConnectedAreas_r //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== qboolean AAS_ConnectedAreas(int *areanums, int numareas) { int connectedareas[MAX_PORTALAREAS], i; Com_Memset(connectedareas, 0, sizeof(connectedareas)); if (numareas < 1) return qfalse; if (numareas == 1) return qtrue; AAS_ConnectedAreas_r(areanums, numareas, connectedareas, 0); for (i = 0; i < numareas; i++) { if (!connectedareas[i]) return qfalse; } //end for return qtrue; } //end of the function AAS_ConnectedAreas //=========================================================================== // gets adjacent areas with less presence types recursively // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_GetAdjacentAreasWithLessPresenceTypes_r(int *areanums, int numareas, int curareanum) { int i, j, presencetype, otherpresencetype, otherareanum, facenum; aas_area_t *area; aas_face_t *face; areanums[numareas++] = curareanum; area = &aasworld.areas[curareanum]; presencetype = aasworld.areasettings[curareanum].presencetype; for (i = 0; i < area->numfaces; i++) { facenum = abs(aasworld.faceindex[area->firstface + i]); face = &aasworld.faces[facenum]; //if the face is solid if (face->faceflags & FACE_SOLID) continue; //the area at the other side of the face if (face->frontarea != curareanum) otherareanum = face->frontarea; else otherareanum = face->backarea; // otherpresencetype = aasworld.areasettings[otherareanum].presencetype; //if the other area has less presence types if ((presencetype & ~otherpresencetype) && !(otherpresencetype & ~presencetype)) { //check if the other area isn't already in the list for (j = 0; j < numareas; j++) { if (otherareanum == areanums[j]) break; } //end for //if the other area isn't already in the list if (j == numareas) { if (numareas >= MAX_PORTALAREAS) { AAS_Error("MAX_PORTALAREAS"); return numareas; } //end if numareas = AAS_GetAdjacentAreasWithLessPresenceTypes_r(areanums, numareas, otherareanum); } //end if } //end if } //end for return numareas; } //end of the function AAS_GetAdjacentAreasWithLessPresenceTypes_r //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_CheckAreaForPossiblePortals(int areanum) { int i, j, k, fen, ben, frontedgenum, backedgenum, facenum; int areanums[MAX_PORTALAREAS], numareas, otherareanum; int numareafrontfaces[MAX_PORTALAREAS], numareabackfaces[MAX_PORTALAREAS]; int frontfacenums[MAX_PORTALAREAS], backfacenums[MAX_PORTALAREAS]; int numfrontfaces, numbackfaces; int frontareanums[MAX_PORTALAREAS], backareanums[MAX_PORTALAREAS]; int numfrontareas, numbackareas; int frontplanenum, backplanenum, faceplanenum; aas_area_t *area; aas_face_t *frontface, *backface, *face; //if it isn't already a portal if (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL) return 0; //it must be a grounded area if (!(aasworld.areasettings[areanum].areaflags & AREA_GROUNDED)) return 0; // Com_Memset(numareafrontfaces, 0, sizeof(numareafrontfaces)); Com_Memset(numareabackfaces, 0, sizeof(numareabackfaces)); numareas = numfrontfaces = numbackfaces = 0; numfrontareas = numbackareas = 0; frontplanenum = backplanenum = -1; //add any adjacent areas with less presence types numareas = AAS_GetAdjacentAreasWithLessPresenceTypes_r(areanums, 0, areanum); // for (i = 0; i < numareas; i++) { area = &aasworld.areas[areanums[i]]; for (j = 0; j < area->numfaces; j++) { facenum = abs(aasworld.faceindex[area->firstface + j]); face = &aasworld.faces[facenum]; //if the face is solid if (face->faceflags & FACE_SOLID) continue; //check if the face is shared with one of the other areas for (k = 0; k < numareas; k++) { if (k == i) continue; if (face->frontarea == areanums[k] || face->backarea == areanums[k]) break; } //end for //if the face is shared if (k != numareas) continue; //the number of the area at the other side of the face if (face->frontarea == areanums[i]) otherareanum = face->backarea; else otherareanum = face->frontarea; //if the other area already is a cluter portal if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) return 0; //number of the plane of the area faceplanenum = face->planenum & ~1; // if (frontplanenum < 0 || faceplanenum == frontplanenum) { frontplanenum = faceplanenum; frontfacenums[numfrontfaces++] = facenum; for (k = 0; k < numfrontareas; k++) { if (frontareanums[k] == otherareanum) break; } //end for if (k == numfrontareas) frontareanums[numfrontareas++] = otherareanum; numareafrontfaces[i]++; } //end if else if (backplanenum < 0 || faceplanenum == backplanenum) { backplanenum = faceplanenum; backfacenums[numbackfaces++] = facenum; for (k = 0; k < numbackareas; k++) { if (backareanums[k] == otherareanum) break; } //end for if (k == numbackareas) backareanums[numbackareas++] = otherareanum; numareabackfaces[i]++; } //end else else { return 0; } //end else } //end for } //end for //every area should have at least one front face and one back face for (i = 0; i < numareas; i++) { if (!numareafrontfaces[i] || !numareabackfaces[i]) return 0; } //end for //the front areas should all be connected if (!AAS_ConnectedAreas(frontareanums, numfrontareas)) return 0; //the back areas should all be connected if (!AAS_ConnectedAreas(backareanums, numbackareas)) return 0; //none of the front faces should have a shared edge with a back face for (i = 0; i < numfrontfaces; i++) { frontface = &aasworld.faces[frontfacenums[i]]; for (fen = 0; fen < frontface->numedges; fen++) { frontedgenum = abs(aasworld.edgeindex[frontface->firstedge + fen]); for (j = 0; j < numbackfaces; j++) { backface = &aasworld.faces[backfacenums[j]]; for (ben = 0; ben < backface->numedges; ben++) { backedgenum = abs(aasworld.edgeindex[backface->firstedge + ben]); if (frontedgenum == backedgenum) break; } //end for if (ben != backface->numedges) break; } //end for if (j != numbackfaces) break; } //end for if (fen != frontface->numedges) break; } //end for if (i != numfrontfaces) return 0; //set the cluster portal contents for (i = 0; i < numareas; i++) { aasworld.areasettings[areanums[i]].contents |= AREACONTENTS_CLUSTERPORTAL; //this area can be used as a route portal aasworld.areasettings[areanums[i]].contents |= AREACONTENTS_ROUTEPORTAL; Log_Write("possible portal: %d\r\n", areanums[i]); } //end for // return numareas; } //end of the function AAS_CheckAreaForPossiblePortals //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_FindPossiblePortals(void) { int i, numpossibleportals; numpossibleportals = 0; for (i = 1; i < aasworld.numareas; i++) { numpossibleportals += AAS_CheckAreaForPossiblePortals(i); } //end for botimport.Print(PRT_MESSAGE, "\r%6d possible portal areas\n", numpossibleportals); } //end of the function AAS_FindPossiblePortals //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_RemoveAllPortals(void) { int i; for (i = 1; i < aasworld.numareas; i++) { aasworld.areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL; } //end for } //end of the function AAS_RemoveAllPortals #if 0 //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_FloodCluster_r(int areanum, int clusternum) { int i, otherareanum; aas_face_t *face; aas_area_t *area; //set cluster mark aasworld.areasettings[areanum].cluster = clusternum; //if the area is a portal //if (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL) return; // area = &aasworld.areas[areanum]; //use area faces to flood into adjacent areas for (i = 0; i < area->numfaces; i++) { face = &aasworld.faces[abs(aasworld.faceindex[area->firstface + i])]; // if (face->frontarea != areanum) otherareanum = face->frontarea; else otherareanum = face->backarea; //if there's no area at the other side if (!otherareanum) continue; //if the area is a portal if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) continue; //if the area is already marked if (aasworld.areasettings[otherareanum].cluster) continue; // AAS_FloodCluster_r(otherareanum, clusternum); } //end for //use the reachabilities to flood into other areas for (i = 0; i < aasworld.areasettings[areanum].numreachableareas; i++) { otherareanum = aasworld.reachability[ aasworld.areasettings[areanum].firstreachablearea + i].areanum; if (!otherareanum) { continue; AAS_Error("reachability %d has zero area\n", aasworld.areasettings[areanum].firstreachablearea + i); } //end if //if the area is a portal if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) continue; //if the area is already marked if (aasworld.areasettings[otherareanum].cluster) continue; // AAS_FloodCluster_r(otherareanum, clusternum); } //end for } //end of the function AAS_FloodCluster_r //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_RemoveTeleporterPortals(void) { int i, j, areanum; for (i = 1; i < aasworld.numareas; i++) { for (j = 0; j < aasworld.areasettings[i].numreachableareas; j++) { areanum = aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].areanum; if (aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].traveltype == TRAVEL_TELEPORT) { aasworld.areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL; aasworld.areasettings[areanum].contents &= ~AREACONTENTS_CLUSTERPORTAL; break; } //end if } //end for } //end for } //end of the function AAS_RemoveTeleporterPortals //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_FloodClusterReachabilities(int clusternum) { int i, j, areanum; for (i = 1; i < aasworld.numareas; i++) { //if this area already has a cluster set if (aasworld.areasettings[i].cluster) continue; //if this area is a cluster portal if (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL) continue; //loop over the reachable areas from this area for (j = 0; j < aasworld.areasettings[i].numreachableareas; j++) { //the reachable area areanum = aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].areanum; //if this area is a cluster portal if (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL) continue; //if this area has a cluster set if (aasworld.areasettings[areanum].cluster == clusternum) { AAS_FloodCluster_r(i, clusternum); i = 0; break; } //end if } //end for } //end for } //end of the function AAS_FloodClusterReachabilities //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_RemoveNotClusterClosingPortals(void) { int i, j, k, facenum, otherareanum, nonclosingportals; aas_area_t *area; aas_face_t *face; AAS_RemoveTeleporterPortals(); // nonclosingportals = 0; for (i = 1; i < aasworld.numareas; i++) { if (!(aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL)) continue; //find a non-portal area adjacent to the portal area and flood //the cluster from there area = &aasworld.areas[i]; for (j = 0; j < area->numfaces; j++) { facenum = abs(aasworld.faceindex[area->firstface + j]); face = &aasworld.faces[facenum]; // if (face->frontarea != i) otherareanum = face->frontarea; else otherareanum = face->backarea; // if (!otherareanum) continue; // if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) { continue; } //end if //reset all cluster fields AAS_RemoveClusterAreas(); // AAS_FloodCluster_r(otherareanum, 1); AAS_FloodClusterReachabilities(1); //check if all adjacent non-portal areas have a cluster set for (k = 0; k < area->numfaces; k++) { facenum = abs(aasworld.faceindex[area->firstface + k]); face = &aasworld.faces[facenum]; // if (face->frontarea != i) otherareanum = face->frontarea; else otherareanum = face->backarea; // if (!otherareanum) continue; // if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) { continue; } //end if // if (!aasworld.areasettings[otherareanum].cluster) break; } //end for //if all adjacent non-portal areas have a cluster set then the portal //didn't seal a cluster if (k >= area->numfaces) { aasworld.areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL; nonclosingportals++; //recheck all the other portals again i = 0; break; } //end if } //end for } //end for botimport.Print(PRT_MESSAGE, "\r%6d non closing portals removed\n", nonclosingportals); } //end of the function AAS_RemoveNotClusterClosingPortals //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_RemoveNotClusterClosingPortals(void) { int i, j, facenum, otherareanum, nonclosingportals, numseperatedclusters; aas_area_t *area; aas_face_t *face; AAS_RemoveTeleporterPortals(); // nonclosingportals = 0; for (i = 1; i < aasworld.numareas; i++) { if (!(aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL)) continue; // numseperatedclusters = 0; //reset all cluster fields AAS_RemoveClusterAreas(); //find a non-portal area adjacent to the portal area and flood //the cluster from there area = &aasworld.areas[i]; for (j = 0; j < area->numfaces; j++) { facenum = abs(aasworld.faceindex[area->firstface + j]); face = &aasworld.faces[facenum]; // if (face->frontarea != i) otherareanum = face->frontarea; else otherareanum = face->backarea; //if not solid at the other side of the face if (!otherareanum) continue; //don't flood into other portals if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) continue; //if the area already has a cluster set if (aasworld.areasettings[otherareanum].cluster) continue; //another cluster is seperated by this portal numseperatedclusters++; //flood the cluster AAS_FloodCluster_r(otherareanum, numseperatedclusters); AAS_FloodClusterReachabilities(numseperatedclusters); } //end for //use the reachabilities to flood into other areas for (j = 0; j < aasworld.areasettings[i].numreachableareas; j++) { otherareanum = aasworld.reachability[ aasworld.areasettings[i].firstreachablearea + j].areanum; //this should never be qtrue but we check anyway if (!otherareanum) continue; //don't flood into other portals if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) continue; //if the area already has a cluster set if (aasworld.areasettings[otherareanum].cluster) continue; //another cluster is seperated by this portal numseperatedclusters++; //flood the cluster AAS_FloodCluster_r(otherareanum, numseperatedclusters); AAS_FloodClusterReachabilities(numseperatedclusters); } //end for //a portal must seperate no more and no less than 2 clusters if (numseperatedclusters != 2) { aasworld.areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL; nonclosingportals++; //recheck all the other portals again i = 0; } //end if } //end for botimport.Print(PRT_MESSAGE, "\r%6d non closing portals removed\n", nonclosingportals); } //end of the function AAS_RemoveNotClusterClosingPortals //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_AddTeleporterPortals(void) { int j, area2num, facenum, otherareanum; char *target, *targetname, *classname; bsp_entity_t *entities, *ent, *dest; vec3_t origin, destorigin, mins, maxs, end; vec3_t bbmins, bbmaxs; aas_area_t *area; aas_face_t *face; aas_trace_t trace; aas_link_t *areas, *link; entities = AAS_ParseBSPEntities(); for (ent = entities; ent; ent = ent->next) { classname = AAS_ValueForBSPEpairKey(ent, "classname"); if (classname && !strcmp(classname, "misc_teleporter")) { if (!AAS_VectorForBSPEpairKey(ent, "origin", origin)) { botimport.Print(PRT_ERROR, "teleporter (%s) without origin\n", target); continue; } //end if // target = AAS_ValueForBSPEpairKey(ent, "target"); if (!target) { botimport.Print(PRT_ERROR, "teleporter (%s) without target\n", target); continue; } //end if for (dest = entities; dest; dest = dest->next) { classname = AAS_ValueForBSPEpairKey(dest, "classname"); if (classname && !strcmp(classname, "misc_teleporter_dest")) { targetname = AAS_ValueForBSPEpairKey(dest, "targetname"); if (targetname && !strcmp(targetname, target)) { break; } //end if } //end if } //end for if (!dest) { botimport.Print(PRT_ERROR, "teleporter without destination (%s)\n", target); continue; } //end if if (!AAS_VectorForBSPEpairKey(dest, "origin", destorigin)) { botimport.Print(PRT_ERROR, "teleporter destination (%s) without origin\n", target); continue; } //end if destorigin[2] += 24; //just for q2e1m2, the dork has put the telepads in the ground VectorCopy(destorigin, end); end[2] -= 100; trace = AAS_TraceClientBBox(destorigin, end, PRESENCE_CROUCH, -1); if (trace.startsolid) { botimport.Print(PRT_ERROR, "teleporter destination (%s) in solid\n", target); continue; } //end if VectorCopy(trace.endpos, destorigin); area2num = AAS_PointAreaNum(destorigin); //reset all cluster fields for (j = 0; j < aasworld.numareas; j++) { aasworld.areasettings[j].cluster = 0; } //end for // VectorSet(mins, -8, -8, 8); VectorSet(maxs, 8, 8, 24); // AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, bbmins, bbmaxs); // VectorAdd(origin, mins, mins); VectorAdd(origin, maxs, maxs); //add bounding box size VectorSubtract(mins, bbmaxs, mins); VectorSubtract(maxs, bbmins, maxs); //link an invalid (-1) entity areas = AAS_AASLinkEntity(mins, maxs, -1); // for (link = areas; link; link = link->next_area) { if (!AAS_AreaGrounded(link->areanum)) continue; //add the teleporter portal mark aasworld.areasettings[link->areanum].contents |= AREACONTENTS_CLUSTERPORTAL | AREACONTENTS_TELEPORTAL; } //end for // for (link = areas; link; link = link->next_area) { if (!AAS_AreaGrounded(link->areanum)) continue; //find a non-portal area adjacent to the portal area and flood //the cluster from there area = &aasworld.areas[link->areanum]; for (j = 0; j < area->numfaces; j++) { facenum = abs(aasworld.faceindex[area->firstface + j]); face = &aasworld.faces[facenum]; // if (face->frontarea != link->areanum) otherareanum = face->frontarea; else otherareanum = face->backarea; // if (!otherareanum) continue; // if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) { continue; } //end if // AAS_FloodCluster_r(otherareanum, 1); } //end for } //end for //if the teleport destination IS in the same cluster if (aasworld.areasettings[area2num].cluster) { for (link = areas; link; link = link->next_area) { if (!AAS_AreaGrounded(link->areanum)) continue; //add the teleporter portal mark aasworld.areasettings[link->areanum].contents &= ~(AREACONTENTS_CLUSTERPORTAL | AREACONTENTS_TELEPORTAL); } //end for } //end if } //end if } //end for AAS_FreeBSPEntities(entities); } //end of the function AAS_AddTeleporterPortals //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_AddTeleporterPortals(void) { int i, j, areanum; for (i = 1; i < aasworld.numareas; i++) { for (j = 0; j < aasworld.areasettings[i].numreachableareas; j++) { if (aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].traveltype != TRAVEL_TELEPORT) continue; areanum = aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].areanum; aasworld.areasettings[areanum].contents |= AREACONTENTS_CLUSTERPORTAL; } //end for } //end for } //end of the function AAS_AddTeleporterPortals #endif //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_TestPortals(void) { int i; aas_portal_t *portal; for (i = 1; i < aasworld.numportals; i++) { portal = &aasworld.portals[i]; if (!portal->frontcluster) { aasworld.areasettings[portal->areanum].contents &= ~AREACONTENTS_CLUSTERPORTAL; Log_Write("portal area %d has no front cluster\r\n", portal->areanum); return qfalse; } //end if if (!portal->backcluster) { aasworld.areasettings[portal->areanum].contents &= ~AREACONTENTS_CLUSTERPORTAL; Log_Write("portal area %d has no back cluster\r\n", portal->areanum); return qfalse; } //end if } //end for return qtrue; } //end of the function //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_CountForcedClusterPortals(void) { int num, i; num = 0; for (i = 1; i < aasworld.numareas; i++) { if (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL) { Log_Write("area %d is a forced portal area\r\n", i); num++; } //end if } //end for botimport.Print(PRT_MESSAGE, "%6d forced portal areas\n", num); } //end of the function AAS_CountForcedClusterPortals //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_CreateViewPortals(void) { int i; for (i = 1; i < aasworld.numareas; i++) { if (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL) { aasworld.areasettings[i].contents |= AREACONTENTS_VIEWPORTAL; } //end if } //end for } //end of the function AAS_CreateViewPortals //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_SetViewPortalsAsClusterPortals(void) { int i; for (i = 1; i < aasworld.numareas; i++) { if (aasworld.areasettings[i].contents & AREACONTENTS_VIEWPORTAL) { aasworld.areasettings[i].contents |= AREACONTENTS_CLUSTERPORTAL; } //end if } //end for } //end of the function AAS_SetViewPortalsAsClusterPortals //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_InitClustering(void) { int i, removedPortalAreas; int n, total, numreachabilityareas; if (!aasworld.loaded) return; //if there are clusters if (aasworld.numclusters >= 1) { #ifndef BSPC //if clustering isn't forced if (!((int)LibVarGetValue("forceclustering")) && !((int)LibVarGetValue("forcereachability"))) return; #endif } //end if //set all view portals as cluster portals in case we re-calculate the reachabilities and clusters (with -reach) AAS_SetViewPortalsAsClusterPortals(); //count the number of forced cluster portals AAS_CountForcedClusterPortals(); //remove all area cluster marks AAS_RemoveClusterAreas(); //find possible cluster portals AAS_FindPossiblePortals(); //craete portals to for the bot view AAS_CreateViewPortals(); //remove all portals that are not closing a cluster //AAS_RemoveNotClusterClosingPortals(); //initialize portal memory if (aasworld.portals) FreeMemory(aasworld.portals); aasworld.portals = (aas_portal_t *) GetClearedMemory(AAS_MAX_PORTALS * sizeof(aas_portal_t)); //initialize portal index memory if (aasworld.portalindex) FreeMemory(aasworld.portalindex); aasworld.portalindex = (aas_portalindex_t *) GetClearedMemory(AAS_MAX_PORTALINDEXSIZE * sizeof(aas_portalindex_t)); //initialize cluster memory if (aasworld.clusters) FreeMemory(aasworld.clusters); aasworld.clusters = (aas_cluster_t *) GetClearedMemory(AAS_MAX_CLUSTERS * sizeof(aas_cluster_t)); // removedPortalAreas = 0; botimport.Print(PRT_MESSAGE, "\r%6d removed portal areas", removedPortalAreas); while(1) { botimport.Print(PRT_MESSAGE, "\r%6d", removedPortalAreas); //initialize the number of portals and clusters aasworld.numportals = 1; //portal 0 is a dummy aasworld.portalindexsize = 0; aasworld.numclusters = 1; //cluster 0 is a dummy //create the portals from the portal areas AAS_CreatePortals(); // removedPortalAreas++; //find the clusters if (!AAS_FindClusters()) continue; //test the portals if (!AAS_TestPortals()) continue; // break; } //end while botimport.Print(PRT_MESSAGE, "\n"); //the AAS file should be saved aasworld.savefile = qtrue; //write the portal areas to the log file for (i = 1; i < aasworld.numportals; i++) { Log_Write("portal %d: area %d\r\n", i, aasworld.portals[i].areanum); } //end for // report cluster info botimport.Print(PRT_MESSAGE, "%6d portals created\n", aasworld.numportals); botimport.Print(PRT_MESSAGE, "%6d clusters created\n", aasworld.numclusters); for (i = 1; i < aasworld.numclusters; i++) { botimport.Print(PRT_MESSAGE, "cluster %d has %d reachability areas\n", i, aasworld.clusters[i].numreachabilityareas); } //end for // report AAS file efficiency numreachabilityareas = 0; total = 0; for (i = 0; i < aasworld.numclusters; i++) { n = aasworld.clusters[i].numreachabilityareas; numreachabilityareas += n; total += n * n; } total += numreachabilityareas * aasworld.numportals; // botimport.Print(PRT_MESSAGE, "%6i total reachability areas\n", numreachabilityareas); botimport.Print(PRT_MESSAGE, "%6i AAS memory/CPU usage (the lower the better)\n", total * 3); } //end of the function AAS_InitClustering ================================================ FILE: src/engine/botlib/be_aas_cluster.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_cluster.h * * desc: AAS * * $Archive: /source/code/botlib/be_aas_cluster.h $ * *****************************************************************************/ #ifdef AASINTERN //initialize the AAS clustering void AAS_InitClustering(void); // void AAS_SetViewPortalsAsClusterPortals(void); #endif //AASINTERN ================================================ FILE: src/engine/botlib/be_aas_debug.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_debug.c * * desc: AAS debug code * * $Archive: /MissionPack/code/botlib/be_aas_debug.c $ * *****************************************************************************/ #include "../../game/q_shared.h" #include "l_memory.h" #include "l_script.h" #include "l_precomp.h" #include "l_struct.h" #include "l_libvar.h" #include "aasfile.h" #include "../../game/botlib.h" #include "../../game/be_aas.h" #include "be_interface.h" #include "be_aas_funcs.h" #include "be_aas_def.h" #define MAX_DEBUGLINES 1024 #define MAX_DEBUGPOLYGONS 8192 int debuglines[MAX_DEBUGLINES]; int debuglinevisible[MAX_DEBUGLINES]; int numdebuglines; static int debugpolygons[MAX_DEBUGPOLYGONS]; //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_ClearShownPolygons(void) { int i; //* for (i = 0; i < MAX_DEBUGPOLYGONS; i++) { if (debugpolygons[i]) botimport.DebugPolygonDelete(debugpolygons[i]); debugpolygons[i] = 0; } //end for //*/ /* for (i = 0; i < MAX_DEBUGPOLYGONS; i++) { botimport.DebugPolygonDelete(i); debugpolygons[i] = 0; } //end for */ } //end of the function AAS_ClearShownPolygons //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_ShowPolygon(int color, int numpoints, vec3_t *points) { int i; for (i = 0; i < MAX_DEBUGPOLYGONS; i++) { if (!debugpolygons[i]) { debugpolygons[i] = botimport.DebugPolygonCreate(color, numpoints, points); break; } //end if } //end for } //end of the function AAS_ShowPolygon //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_ClearShownDebugLines(void) { int i; //make all lines invisible for (i = 0; i < MAX_DEBUGLINES; i++) { if (debuglines[i]) { //botimport.DebugLineShow(debuglines[i], NULL, NULL, LINECOLOR_NONE); botimport.DebugLineDelete(debuglines[i]); debuglines[i] = 0; debuglinevisible[i] = qfalse; } //end if } //end for } //end of the function AAS_ClearShownDebugLines //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_DebugLine(vec3_t start, vec3_t end, int color) { int line; for (line = 0; line < MAX_DEBUGLINES; line++) { if (!debuglines[line]) { debuglines[line] = botimport.DebugLineCreate(); debuglinevisible[line] = qfalse; numdebuglines++; } //end if if (!debuglinevisible[line]) { botimport.DebugLineShow(debuglines[line], start, end, color); debuglinevisible[line] = qtrue; return; } //end else } //end for } //end of the function AAS_DebugLine //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_PermanentLine(vec3_t start, vec3_t end, int color) { int line; line = botimport.DebugLineCreate(); botimport.DebugLineShow(line, start, end, color); } //end of the function AAS_PermenentLine //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_DrawPermanentCross(vec3_t origin, float size, int color) { int i, debugline; vec3_t start, end; for (i = 0; i < 3; i++) { VectorCopy(origin, start); start[i] += size; VectorCopy(origin, end); end[i] -= size; AAS_DebugLine(start, end, color); debugline = botimport.DebugLineCreate(); botimport.DebugLineShow(debugline, start, end, color); } //end for } //end of the function AAS_DrawPermanentCross //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_DrawPlaneCross(vec3_t point, vec3_t normal, float dist, int type, int color) { int n0, n1, n2, j, line, lines[2]; vec3_t start1, end1, start2, end2; //make a cross in the hit plane at the hit point VectorCopy(point, start1); VectorCopy(point, end1); VectorCopy(point, start2); VectorCopy(point, end2); n0 = type % 3; n1 = (type + 1) % 3; n2 = (type + 2) % 3; start1[n1] -= 6; start1[n2] -= 6; end1[n1] += 6; end1[n2] += 6; start2[n1] += 6; start2[n2] -= 6; end2[n1] -= 6; end2[n2] += 6; start1[n0] = (dist - (start1[n1] * normal[n1] + start1[n2] * normal[n2])) / normal[n0]; end1[n0] = (dist - (end1[n1] * normal[n1] + end1[n2] * normal[n2])) / normal[n0]; start2[n0] = (dist - (start2[n1] * normal[n1] + start2[n2] * normal[n2])) / normal[n0]; end2[n0] = (dist - (end2[n1] * normal[n1] + end2[n2] * normal[n2])) / normal[n0]; for (j = 0, line = 0; j < 2 && line < MAX_DEBUGLINES; line++) { if (!debuglines[line]) { debuglines[line] = botimport.DebugLineCreate(); lines[j++] = debuglines[line]; debuglinevisible[line] = qtrue; numdebuglines++; } //end if else if (!debuglinevisible[line]) { lines[j++] = debuglines[line]; debuglinevisible[line] = qtrue; } //end else } //end for botimport.DebugLineShow(lines[0], start1, end1, color); botimport.DebugLineShow(lines[1], start2, end2, color); } //end of the function AAS_DrawPlaneCross //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_ShowBoundingBox(vec3_t origin, vec3_t mins, vec3_t maxs) { vec3_t bboxcorners[8]; int lines[3]; int i, j, line; //upper corners bboxcorners[0][0] = origin[0] + maxs[0]; bboxcorners[0][1] = origin[1] + maxs[1]; bboxcorners[0][2] = origin[2] + maxs[2]; // bboxcorners[1][0] = origin[0] + mins[0]; bboxcorners[1][1] = origin[1] + maxs[1]; bboxcorners[1][2] = origin[2] + maxs[2]; // bboxcorners[2][0] = origin[0] + mins[0]; bboxcorners[2][1] = origin[1] + mins[1]; bboxcorners[2][2] = origin[2] + maxs[2]; // bboxcorners[3][0] = origin[0] + maxs[0]; bboxcorners[3][1] = origin[1] + mins[1]; bboxcorners[3][2] = origin[2] + maxs[2]; //lower corners Com_Memcpy(bboxcorners[4], bboxcorners[0], sizeof(vec3_t) * 4); for (i = 0; i < 4; i++) bboxcorners[4 + i][2] = origin[2] + mins[2]; //draw bounding box for (i = 0; i < 4; i++) { for (j = 0, line = 0; j < 3 && line < MAX_DEBUGLINES; line++) { if (!debuglines[line]) { debuglines[line] = botimport.DebugLineCreate(); lines[j++] = debuglines[line]; debuglinevisible[line] = qtrue; numdebuglines++; } //end if else if (!debuglinevisible[line]) { lines[j++] = debuglines[line]; debuglinevisible[line] = qtrue; } //end else } //end for //top plane botimport.DebugLineShow(lines[0], bboxcorners[i], bboxcorners[(i+1)&3], LINECOLOR_RED); //bottom plane botimport.DebugLineShow(lines[1], bboxcorners[4+i], bboxcorners[4+((i+1)&3)], LINECOLOR_RED); //vertical lines botimport.DebugLineShow(lines[2], bboxcorners[i], bboxcorners[4+i], LINECOLOR_RED); } //end for } //end of the function AAS_ShowBoundingBox //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_ShowFace(int facenum) { int i, color, edgenum; aas_edge_t *edge; aas_face_t *face; aas_plane_t *plane; vec3_t start, end; color = LINECOLOR_YELLOW; //check if face number is in range if (facenum >= aasworld.numfaces) { botimport.Print(PRT_ERROR, "facenum %d out of range\n", facenum); } //end if face = &aasworld.faces[facenum]; //walk through the edges of the face for (i = 0; i < face->numedges; i++) { //edge number edgenum = abs(aasworld.edgeindex[face->firstedge + i]); //check if edge number is in range if (edgenum >= aasworld.numedges) { botimport.Print(PRT_ERROR, "edgenum %d out of range\n", edgenum); } //end if edge = &aasworld.edges[edgenum]; if (color == LINECOLOR_RED) color = LINECOLOR_GREEN; else if (color == LINECOLOR_GREEN) color = LINECOLOR_BLUE; else if (color == LINECOLOR_BLUE) color = LINECOLOR_YELLOW; else color = LINECOLOR_RED; AAS_DebugLine(aasworld.vertexes[edge->v[0]], aasworld.vertexes[edge->v[1]], color); } //end for plane = &aasworld.planes[face->planenum]; edgenum = abs(aasworld.edgeindex[face->firstedge]); edge = &aasworld.edges[edgenum]; VectorCopy(aasworld.vertexes[edge->v[0]], start); VectorMA(start, 20, plane->normal, end); AAS_DebugLine(start, end, LINECOLOR_RED); } //end of the function AAS_ShowFace //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_ShowFacePolygon(int facenum, int color, int flip) { int i, edgenum, numpoints; vec3_t points[128]; aas_edge_t *edge; aas_face_t *face; //check if face number is in range if (facenum >= aasworld.numfaces) { botimport.Print(PRT_ERROR, "facenum %d out of range\n", facenum); } //end if face = &aasworld.faces[facenum]; //walk through the edges of the face numpoints = 0; if (flip) { for (i = face->numedges-1; i >= 0; i--) { //edge number edgenum = aasworld.edgeindex[face->firstedge + i]; edge = &aasworld.edges[abs(edgenum)]; VectorCopy(aasworld.vertexes[edge->v[edgenum < 0]], points[numpoints]); numpoints++; } //end for } //end if else { for (i = 0; i < face->numedges; i++) { //edge number edgenum = aasworld.edgeindex[face->firstedge + i]; edge = &aasworld.edges[abs(edgenum)]; VectorCopy(aasworld.vertexes[edge->v[edgenum < 0]], points[numpoints]); numpoints++; } //end for } //end else AAS_ShowPolygon(color, numpoints, points); } //end of the function AAS_ShowFacePolygon //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_ShowArea(int areanum, int groundfacesonly) { int areaedges[MAX_DEBUGLINES]; int numareaedges, i, j, n, color = 0, line; int facenum, edgenum; aas_area_t *area; aas_face_t *face; aas_edge_t *edge; // numareaedges = 0; // if (areanum < 0 || areanum >= aasworld.numareas) { botimport.Print(PRT_ERROR, "area %d out of range [0, %d]\n", areanum, aasworld.numareas); return; } //end if //pointer to the convex area area = &aasworld.areas[areanum]; //walk through the faces of the area for (i = 0; i < area->numfaces; i++) { facenum = abs(aasworld.faceindex[area->firstface + i]); //check if face number is in range if (facenum >= aasworld.numfaces) { botimport.Print(PRT_ERROR, "facenum %d out of range\n", facenum); } //end if face = &aasworld.faces[facenum]; //ground faces only if (groundfacesonly) { if (!(face->faceflags & (FACE_GROUND | FACE_LADDER))) continue; } //end if //walk through the edges of the face for (j = 0; j < face->numedges; j++) { //edge number edgenum = abs(aasworld.edgeindex[face->firstedge + j]); //check if edge number is in range if (edgenum >= aasworld.numedges) { botimport.Print(PRT_ERROR, "edgenum %d out of range\n", edgenum); } //end if //check if the edge is stored already for (n = 0; n < numareaedges; n++) { if (areaedges[n] == edgenum) break; } //end for if (n == numareaedges && numareaedges < MAX_DEBUGLINES) { areaedges[numareaedges++] = edgenum; } //end if } //end for //AAS_ShowFace(facenum); } //end for //draw all the edges for (n = 0; n < numareaedges; n++) { for (line = 0; line < MAX_DEBUGLINES; line++) { if (!debuglines[line]) { debuglines[line] = botimport.DebugLineCreate(); debuglinevisible[line] = qfalse; numdebuglines++; } //end if if (!debuglinevisible[line]) { break; } //end else } //end for if (line >= MAX_DEBUGLINES) return; edge = &aasworld.edges[areaedges[n]]; if (color == LINECOLOR_RED) color = LINECOLOR_BLUE; else if (color == LINECOLOR_BLUE) color = LINECOLOR_GREEN; else if (color == LINECOLOR_GREEN) color = LINECOLOR_YELLOW; else color = LINECOLOR_RED; botimport.DebugLineShow(debuglines[line], aasworld.vertexes[edge->v[0]], aasworld.vertexes[edge->v[1]], color); debuglinevisible[line] = qtrue; } //end for*/ } //end of the function AAS_ShowArea //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_ShowAreaPolygons(int areanum, int color, int groundfacesonly) { int i, facenum; aas_area_t *area; aas_face_t *face; // if (areanum < 0 || areanum >= aasworld.numareas) { botimport.Print(PRT_ERROR, "area %d out of range [0, %d]\n", areanum, aasworld.numareas); return; } //end if //pointer to the convex area area = &aasworld.areas[areanum]; //walk through the faces of the area for (i = 0; i < area->numfaces; i++) { facenum = abs(aasworld.faceindex[area->firstface + i]); //check if face number is in range if (facenum >= aasworld.numfaces) { botimport.Print(PRT_ERROR, "facenum %d out of range\n", facenum); } //end if face = &aasworld.faces[facenum]; //ground faces only if (groundfacesonly) { if (!(face->faceflags & (FACE_GROUND | FACE_LADDER))) continue; } //end if AAS_ShowFacePolygon(facenum, color, face->frontarea != areanum); } //end for } //end of the function AAS_ShowAreaPolygons //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_DrawCross(vec3_t origin, float size, int color) { int i; vec3_t start, end; for (i = 0; i < 3; i++) { VectorCopy(origin, start); start[i] += size; VectorCopy(origin, end); end[i] -= size; AAS_DebugLine(start, end, color); } //end for } //end of the function AAS_DrawCross //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_PrintTravelType(int traveltype) { #ifdef DEBUG char *str; // switch(traveltype & TRAVELTYPE_MASK) { case TRAVEL_INVALID: str = "TRAVEL_INVALID"; break; case TRAVEL_WALK: str = "TRAVEL_WALK"; break; case TRAVEL_CROUCH: str = "TRAVEL_CROUCH"; break; case TRAVEL_BARRIERJUMP: str = "TRAVEL_BARRIERJUMP"; break; case TRAVEL_JUMP: str = "TRAVEL_JUMP"; break; case TRAVEL_LADDER: str = "TRAVEL_LADDER"; break; case TRAVEL_WALKOFFLEDGE: str = "TRAVEL_WALKOFFLEDGE"; break; case TRAVEL_SWIM: str = "TRAVEL_SWIM"; break; case TRAVEL_WATERJUMP: str = "TRAVEL_WATERJUMP"; break; case TRAVEL_TELEPORT: str = "TRAVEL_TELEPORT"; break; case TRAVEL_ELEVATOR: str = "TRAVEL_ELEVATOR"; break; case TRAVEL_ROCKETJUMP: str = "TRAVEL_ROCKETJUMP"; break; case TRAVEL_BFGJUMP: str = "TRAVEL_BFGJUMP"; break; case TRAVEL_GRAPPLEHOOK: str = "TRAVEL_GRAPPLEHOOK"; break; case TRAVEL_JUMPPAD: str = "TRAVEL_JUMPPAD"; break; case TRAVEL_FUNCBOB: str = "TRAVEL_FUNCBOB"; break; default: str = "UNKNOWN TRAVEL TYPE"; break; } //end switch botimport.Print(PRT_MESSAGE, "%s", str); #endif } //end of the function AAS_PrintTravelType //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_DrawArrow(vec3_t start, vec3_t end, int linecolor, int arrowcolor) { vec3_t dir, cross, p1, p2, up = {0, 0, 1}; float dot; VectorSubtract(end, start, dir); VectorNormalize(dir); dot = DotProduct(dir, up); if (dot > 0.99 || dot < -0.99) VectorSet(cross, 1, 0, 0); else CrossProduct(dir, up, cross); VectorMA(end, -6, dir, p1); VectorCopy(p1, p2); VectorMA(p1, 6, cross, p1); VectorMA(p2, -6, cross, p2); AAS_DebugLine(start, end, linecolor); AAS_DebugLine(p1, end, arrowcolor); AAS_DebugLine(p2, end, arrowcolor); } //end of the function AAS_DrawArrow //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_ShowReachability(aas_reachability_t *reach) { vec3_t dir, cmdmove, velocity; float speed, zvel; aas_clientmove_t move; AAS_ShowAreaPolygons(reach->areanum, 5, qtrue); //AAS_ShowArea(reach->areanum, qtrue); AAS_DrawArrow(reach->start, reach->end, LINECOLOR_BLUE, LINECOLOR_YELLOW); // if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP || (reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_WALKOFFLEDGE) { AAS_HorizontalVelocityForJump(aassettings.phys_jumpvel, reach->start, reach->end, &speed); // VectorSubtract(reach->end, reach->start, dir); dir[2] = 0; VectorNormalize(dir); //set the velocity VectorScale(dir, speed, velocity); //set the command movement VectorClear(cmdmove); cmdmove[2] = aassettings.phys_jumpvel; // AAS_PredictClientMovement(&move, -1, reach->start, PRESENCE_NORMAL, qtrue, velocity, cmdmove, 3, 30, 0.1f, SE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME| SE_ENTERLAVA|SE_HITGROUNDDAMAGE, 0, qtrue); // if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP) { AAS_JumpReachRunStart(reach, dir); AAS_DrawCross(dir, 4, LINECOLOR_BLUE); } //end if } //end if else if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_ROCKETJUMP) { zvel = AAS_RocketJumpZVelocity(reach->start); AAS_HorizontalVelocityForJump(zvel, reach->start, reach->end, &speed); // VectorSubtract(reach->end, reach->start, dir); dir[2] = 0; VectorNormalize(dir); //get command movement VectorScale(dir, speed, cmdmove); VectorSet(velocity, 0, 0, zvel); // AAS_PredictClientMovement(&move, -1, reach->start, PRESENCE_NORMAL, qtrue, velocity, cmdmove, 30, 30, 0.1f, SE_ENTERWATER|SE_ENTERSLIME| SE_ENTERLAVA|SE_HITGROUNDDAMAGE| SE_TOUCHJUMPPAD|SE_HITGROUNDAREA, reach->areanum, qtrue); } //end else if else if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMPPAD) { VectorSet(cmdmove, 0, 0, 0); // VectorSubtract(reach->end, reach->start, dir); dir[2] = 0; VectorNormalize(dir); //set the velocity //NOTE: the edgenum is the horizontal velocity VectorScale(dir, reach->edgenum, velocity); //NOTE: the facenum is the Z velocity velocity[2] = reach->facenum; // AAS_PredictClientMovement(&move, -1, reach->start, PRESENCE_NORMAL, qtrue, velocity, cmdmove, 30, 30, 0.1f, SE_ENTERWATER|SE_ENTERSLIME| SE_ENTERLAVA|SE_HITGROUNDDAMAGE| SE_TOUCHJUMPPAD|SE_HITGROUNDAREA, reach->areanum, qtrue); } //end else if } //end of the function AAS_ShowReachability //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_ShowReachableAreas(int areanum) { aas_areasettings_t *settings; static aas_reachability_t reach; static int index, lastareanum; static float lasttime; if (areanum != lastareanum) { index = 0; lastareanum = areanum; } //end if settings = &aasworld.areasettings[areanum]; // if (!settings->numreachableareas) return; // if (index >= settings->numreachableareas) index = 0; // if (AAS_Time() - lasttime > 1.5) { Com_Memcpy(&reach, &aasworld.reachability[settings->firstreachablearea + index], sizeof(aas_reachability_t)); index++; lasttime = AAS_Time(); AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK); botimport.Print(PRT_MESSAGE, "\n"); } //end if AAS_ShowReachability(&reach); } //end of the function ShowReachableAreas void AAS_FloodAreas_r(int areanum, int cluster, int *done) { int nextareanum, i, facenum; aas_area_t *area; aas_face_t *face; aas_areasettings_t *settings; aas_reachability_t *reach; AAS_ShowAreaPolygons(areanum, 1, qtrue); //pointer to the convex area area = &aasworld.areas[areanum]; settings = &aasworld.areasettings[areanum]; //walk through the faces of the area for (i = 0; i < area->numfaces; i++) { facenum = abs(aasworld.faceindex[area->firstface + i]); face = &aasworld.faces[facenum]; if (face->frontarea == areanum) nextareanum = face->backarea; else nextareanum = face->frontarea; if (!nextareanum) continue; if (done[nextareanum]) continue; done[nextareanum] = qtrue; if (aasworld.areasettings[nextareanum].contents & AREACONTENTS_VIEWPORTAL) continue; if (AAS_AreaCluster(nextareanum) != cluster) continue; AAS_FloodAreas_r(nextareanum, cluster, done); } //end for // for (i = 0; i < settings->numreachableareas; i++) { reach = &aasworld.reachability[settings->firstreachablearea + i]; nextareanum = reach->areanum; if (!nextareanum) continue; if (done[nextareanum]) continue; done[nextareanum] = qtrue; if (aasworld.areasettings[nextareanum].contents & AREACONTENTS_VIEWPORTAL) continue; if (AAS_AreaCluster(nextareanum) != cluster) continue; /* if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_WALKOFFLEDGE) { AAS_DebugLine(reach->start, reach->end, 1); } */ AAS_FloodAreas_r(nextareanum, cluster, done); } } void AAS_FloodAreas(vec3_t origin) { int areanum, cluster, *done; done = (int *) GetClearedMemory(aasworld.numareas * sizeof(int)); areanum = AAS_PointAreaNum(origin); cluster = AAS_AreaCluster(areanum); AAS_FloodAreas_r(areanum, cluster, done); } ================================================ FILE: src/engine/botlib/be_aas_debug.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_debug.h * * desc: AAS * * $Archive: /source/code/botlib/be_aas_debug.h $ * *****************************************************************************/ //clear the shown debug lines void AAS_ClearShownDebugLines(void); // void AAS_ClearShownPolygons(void); //show a debug line void AAS_DebugLine(vec3_t start, vec3_t end, int color); //show a permenent line void AAS_PermanentLine(vec3_t start, vec3_t end, int color); //show a permanent cross void AAS_DrawPermanentCross(vec3_t origin, float size, int color); //draw a cross in the plane void AAS_DrawPlaneCross(vec3_t point, vec3_t normal, float dist, int type, int color); //show a bounding box void AAS_ShowBoundingBox(vec3_t origin, vec3_t mins, vec3_t maxs); //show a face void AAS_ShowFace(int facenum); //show an area void AAS_ShowArea(int areanum, int groundfacesonly); // void AAS_ShowAreaPolygons(int areanum, int color, int groundfacesonly); //draw a cros void AAS_DrawCross(vec3_t origin, float size, int color); //print the travel type void AAS_PrintTravelType(int traveltype); //draw an arrow void AAS_DrawArrow(vec3_t start, vec3_t end, int linecolor, int arrowcolor); //visualize the given reachability void AAS_ShowReachability(struct aas_reachability_s *reach); //show the reachable areas from the given area void AAS_ShowReachableAreas(int areanum); ================================================ FILE: src/engine/botlib/be_aas_def.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_def.h * * desc: AAS * * $Archive: /source/code/botlib/be_aas_def.h $ * *****************************************************************************/ //debugging on #define AAS_DEBUG #define MAX_CLIENTS 64 #define MAX_MODELS 256 // these are sent over the net as 8 bits #define MAX_SOUNDS 256 // so they cannot be blindly increased #define MAX_CONFIGSTRINGS 1024 #define CS_SCORES 32 #define CS_MODELS (CS_SCORES+MAX_CLIENTS) #define CS_SOUNDS (CS_MODELS+MAX_MODELS) #define DF_AASENTNUMBER(x) (x - aasworld.entities) #define DF_NUMBERAASENT(x) (&aasworld.entities[x]) #define DF_AASENTCLIENT(x) (x - aasworld.entities - 1) #define DF_CLIENTAASENT(x) (&aasworld.entities[x + 1]) #ifndef MAX_PATH #define MAX_PATH MAX_QPATH #endif //string index (for model, sound and image index) typedef struct aas_stringindex_s { int numindexes; char **index; } aas_stringindex_t; //structure to link entities to areas and areas to entities typedef struct aas_link_s { int entnum; int areanum; struct aas_link_s *next_ent, *prev_ent; struct aas_link_s *next_area, *prev_area; } aas_link_t; //structure to link entities to leaves and leaves to entities typedef struct bsp_link_s { int entnum; int leafnum; struct bsp_link_s *next_ent, *prev_ent; struct bsp_link_s *next_leaf, *prev_leaf; } bsp_link_t; typedef struct bsp_entdata_s { vec3_t origin; vec3_t angles; vec3_t absmins; vec3_t absmaxs; int solid; int modelnum; } bsp_entdata_t; //entity typedef struct aas_entity_s { //entity info aas_entityinfo_t i; //links into the AAS areas aas_link_t *areas; //links into the BSP leaves bsp_link_t *leaves; } aas_entity_t; typedef struct aas_settings_s { vec3_t phys_gravitydirection; float phys_friction; float phys_stopspeed; float phys_gravity; float phys_waterfriction; float phys_watergravity; float phys_maxvelocity; float phys_maxwalkvelocity; float phys_maxcrouchvelocity; float phys_maxswimvelocity; float phys_walkaccelerate; float phys_airaccelerate; float phys_swimaccelerate; float phys_maxstep; float phys_maxsteepness; float phys_maxwaterjump; float phys_maxbarrier; float phys_jumpvel; float phys_falldelta5; float phys_falldelta10; float rs_waterjump; float rs_teleport; float rs_barrierjump; float rs_startcrouch; float rs_startgrapple; float rs_startwalkoffledge; float rs_startjump; float rs_rocketjump; float rs_bfgjump; float rs_jumppad; float rs_aircontrolledjumppad; float rs_funcbob; float rs_startelevator; float rs_falldamage5; float rs_falldamage10; float rs_maxfallheight; float rs_maxjumpfallheight; } aas_settings_t; #define CACHETYPE_PORTAL 0 #define CACHETYPE_AREA 1 //routing cache typedef struct aas_routingcache_s { byte type; //portal or area cache float time; //last time accessed or updated int size; //size of the routing cache int cluster; //cluster the cache is for int areanum; //area the cache is created for vec3_t origin; //origin within the area float starttraveltime; //travel time to start with int travelflags; //combinations of the travel flags struct aas_routingcache_s *prev, *next; struct aas_routingcache_s *time_prev, *time_next; unsigned char *reachabilities; //reachabilities used for routing unsigned short int traveltimes[1]; //travel time for every area (variable sized) } aas_routingcache_t; //fields for the routing algorithm typedef struct aas_routingupdate_s { int cluster; int areanum; //area number of the update vec3_t start; //start point the area was entered unsigned short int tmptraveltime; //temporary travel time unsigned short int *areatraveltimes; //travel times within the area qboolean inlist; //true if the update is in the list struct aas_routingupdate_s *next; struct aas_routingupdate_s *prev; } aas_routingupdate_t; //reversed reachability link typedef struct aas_reversedlink_s { int linknum; //the aas_areareachability_t int areanum; //reachable from this area struct aas_reversedlink_s *next; //next link } aas_reversedlink_t; //reversed area reachability typedef struct aas_reversedreachability_s { int numlinks; aas_reversedlink_t *first; } aas_reversedreachability_t; //areas a reachability goes through typedef struct aas_reachabilityareas_s { int firstarea, numareas; } aas_reachabilityareas_t; typedef struct aas_s { int loaded; //true when an AAS file is loaded int initialized; //true when AAS has been initialized int savefile; //set true when file should be saved int bspchecksum; //current time float time; int numframes; //name of the aas file char filename[MAX_PATH]; char mapname[MAX_PATH]; //bounding boxes int numbboxes; aas_bbox_t *bboxes; //vertexes int numvertexes; aas_vertex_t *vertexes; //planes int numplanes; aas_plane_t *planes; //edges int numedges; aas_edge_t *edges; //edge index int edgeindexsize; aas_edgeindex_t *edgeindex; //faces int numfaces; aas_face_t *faces; //face index int faceindexsize; aas_faceindex_t *faceindex; //convex areas int numareas; aas_area_t *areas; //convex area settings int numareasettings; aas_areasettings_t *areasettings; //reachablity list int reachabilitysize; aas_reachability_t *reachability; //nodes of the bsp tree int numnodes; aas_node_t *nodes; //cluster portals int numportals; aas_portal_t *portals; //cluster portal index int portalindexsize; aas_portalindex_t *portalindex; //clusters int numclusters; aas_cluster_t *clusters; // int numreachabilityareas; float reachabilitytime; //enities linked in the areas aas_link_t *linkheap; //heap with link structures int linkheapsize; //size of the link heap aas_link_t *freelinks; //first free link aas_link_t **arealinkedentities; //entities linked into areas //entities int maxentities; int maxclients; aas_entity_t *entities; //string indexes char *configstrings[MAX_CONFIGSTRINGS]; int indexessetup; //index to retrieve travel flag for a travel type int travelflagfortype[MAX_TRAVELTYPES]; //travel flags for each area based on contents int *areacontentstravelflags; //routing update aas_routingupdate_t *areaupdate; aas_routingupdate_t *portalupdate; //number of routing updates during a frame (reset every frame) int frameroutingupdates; //reversed reachability links aas_reversedreachability_t *reversedreachability; //travel times within the areas unsigned short ***areatraveltimes; //array of size numclusters with cluster cache aas_routingcache_t ***clusterareacache; aas_routingcache_t **portalcache; //cache list sorted on time aas_routingcache_t *oldestcache; // start of cache list sorted on time aas_routingcache_t *newestcache; // end of cache list sorted on time //maximum travel time through portal areas int *portalmaxtraveltimes; //areas the reachabilities go through int *reachabilityareaindex; aas_reachabilityareas_t *reachabilityareas; } aas_t; #define AASINTERN #ifndef BSPCINCLUDE #include "be_aas_main.h" #include "be_aas_entity.h" #include "be_aas_sample.h" #include "be_aas_cluster.h" #include "be_aas_reach.h" #include "be_aas_route.h" #include "be_aas_routealt.h" #include "be_aas_debug.h" #include "be_aas_file.h" #include "be_aas_optimize.h" #include "be_aas_bsp.h" #include "be_aas_move.h" #endif //BSPCINCLUDE ================================================ FILE: src/engine/botlib/be_aas_entity.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_entity.c * * desc: AAS entities * * $Archive: /MissionPack/code/botlib/be_aas_entity.c $ * *****************************************************************************/ #include "../../game/q_shared.h" #include "l_memory.h" #include "l_script.h" #include "l_precomp.h" #include "l_struct.h" #include "l_utils.h" #include "l_log.h" #include "aasfile.h" #include "../../game/botlib.h" #include "../../game/be_aas.h" #include "be_aas_funcs.h" #include "be_interface.h" #include "be_aas_def.h" #define MASK_SOLID CONTENTS_PLAYERCLIP //FIXME: these might change enum { ET_GENERAL, ET_PLAYER, ET_ITEM, ET_MISSILE, ET_MOVER }; //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_UpdateEntity(int entnum, bot_entitystate_t *state) { int relink; aas_entity_t *ent; vec3_t absmins, absmaxs; if (!aasworld.loaded) { botimport.Print(PRT_MESSAGE, "AAS_UpdateEntity: not loaded\n"); return BLERR_NOAASFILE; } //end if ent = &aasworld.entities[entnum]; if (!state) { //unlink the entity AAS_UnlinkFromAreas(ent->areas); //unlink the entity from the BSP leaves AAS_UnlinkFromBSPLeaves(ent->leaves); // ent->areas = NULL; // ent->leaves = NULL; return BLERR_NOERROR; } ent->i.update_time = AAS_Time() - ent->i.ltime; ent->i.type = state->type; ent->i.flags = state->flags; ent->i.ltime = AAS_Time(); VectorCopy(ent->i.origin, ent->i.lastvisorigin); VectorCopy(state->old_origin, ent->i.old_origin); ent->i.solid = state->solid; ent->i.groundent = state->groundent; ent->i.modelindex = state->modelindex; ent->i.modelindex2 = state->modelindex2; ent->i.frame = state->frame; ent->i.event = state->event; ent->i.eventParm = state->eventParm; ent->i.powerups = state->powerups; ent->i.weapon = state->weapon; ent->i.legsAnim = state->legsAnim; ent->i.torsoAnim = state->torsoAnim; //number of the entity ent->i.number = entnum; //updated so set valid flag ent->i.valid = qtrue; //link everything the first frame if (aasworld.numframes == 1) relink = qtrue; else relink = qfalse; // if (ent->i.solid == SOLID_BSP) { //if the angles of the model changed if (!VectorCompare(state->angles, ent->i.angles)) { VectorCopy(state->angles, ent->i.angles); relink = qtrue; } //end if //get the mins and maxs of the model //FIXME: rotate mins and maxs AAS_BSPModelMinsMaxsOrigin(ent->i.modelindex, ent->i.angles, ent->i.mins, ent->i.maxs, NULL); } //end if else if (ent->i.solid == SOLID_BBOX) { //if the bounding box size changed if (!VectorCompare(state->mins, ent->i.mins) || !VectorCompare(state->maxs, ent->i.maxs)) { VectorCopy(state->mins, ent->i.mins); VectorCopy(state->maxs, ent->i.maxs); relink = qtrue; } //end if VectorCopy(state->angles, ent->i.angles); } //end if //if the origin changed if (!VectorCompare(state->origin, ent->i.origin)) { VectorCopy(state->origin, ent->i.origin); relink = qtrue; } //end if //if the entity should be relinked if (relink) { //don't link the world model if (entnum != ENTITYNUM_WORLD) { //absolute mins and maxs VectorAdd(ent->i.mins, ent->i.origin, absmins); VectorAdd(ent->i.maxs, ent->i.origin, absmaxs); //unlink the entity AAS_UnlinkFromAreas(ent->areas); //relink the entity to the AAS areas (use the larges bbox) ent->areas = AAS_LinkEntityClientBBox(absmins, absmaxs, entnum, PRESENCE_NORMAL); //unlink the entity from the BSP leaves AAS_UnlinkFromBSPLeaves(ent->leaves); //link the entity to the world BSP tree ent->leaves = AAS_BSPLinkEntity(absmins, absmaxs, entnum, 0); } //end if } //end if return BLERR_NOERROR; } //end of the function AAS_UpdateEntity //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_EntityInfo(int entnum, aas_entityinfo_t *info) { if (!aasworld.initialized) { botimport.Print(PRT_FATAL, "AAS_EntityInfo: aasworld not initialized\n"); Com_Memset(info, 0, sizeof(aas_entityinfo_t)); return; } //end if if (entnum < 0 || entnum >= aasworld.maxentities) { botimport.Print(PRT_FATAL, "AAS_EntityInfo: entnum %d out of range\n", entnum); Com_Memset(info, 0, sizeof(aas_entityinfo_t)); return; } //end if Com_Memcpy(info, &aasworld.entities[entnum].i, sizeof(aas_entityinfo_t)); } //end of the function AAS_EntityInfo //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_EntityOrigin(int entnum, vec3_t origin) { if (entnum < 0 || entnum >= aasworld.maxentities) { botimport.Print(PRT_FATAL, "AAS_EntityOrigin: entnum %d out of range\n", entnum); VectorClear(origin); return; } //end if VectorCopy(aasworld.entities[entnum].i.origin, origin); } //end of the function AAS_EntityOrigin //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_EntityModelindex(int entnum) { if (entnum < 0 || entnum >= aasworld.maxentities) { botimport.Print(PRT_FATAL, "AAS_EntityModelindex: entnum %d out of range\n", entnum); return 0; } //end if return aasworld.entities[entnum].i.modelindex; } //end of the function AAS_EntityModelindex //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_EntityType(int entnum) { if (!aasworld.initialized) return 0; if (entnum < 0 || entnum >= aasworld.maxentities) { botimport.Print(PRT_FATAL, "AAS_EntityType: entnum %d out of range\n", entnum); return 0; } //end if return aasworld.entities[entnum].i.type; } //end of the AAS_EntityType //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_EntityModelNum(int entnum) { if (!aasworld.initialized) return 0; if (entnum < 0 || entnum >= aasworld.maxentities) { botimport.Print(PRT_FATAL, "AAS_EntityModelNum: entnum %d out of range\n", entnum); return 0; } //end if return aasworld.entities[entnum].i.modelindex; } //end of the function AAS_EntityModelNum //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_OriginOfMoverWithModelNum(int modelnum, vec3_t origin) { int i; aas_entity_t *ent; for (i = 0; i < aasworld.maxentities; i++) { ent = &aasworld.entities[i]; if (ent->i.type == ET_MOVER) { if (ent->i.modelindex == modelnum) { VectorCopy(ent->i.origin, origin); return qtrue; } //end if } //end if } //end for return qfalse; } //end of the function AAS_OriginOfMoverWithModelNum //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_EntitySize(int entnum, vec3_t mins, vec3_t maxs) { aas_entity_t *ent; if (!aasworld.initialized) return; if (entnum < 0 || entnum >= aasworld.maxentities) { botimport.Print(PRT_FATAL, "AAS_EntitySize: entnum %d out of range\n", entnum); return; } //end if ent = &aasworld.entities[entnum]; VectorCopy(ent->i.mins, mins); VectorCopy(ent->i.maxs, maxs); } //end of the function AAS_EntitySize //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_EntityBSPData(int entnum, bsp_entdata_t *entdata) { aas_entity_t *ent; ent = &aasworld.entities[entnum]; VectorCopy(ent->i.origin, entdata->origin); VectorCopy(ent->i.angles, entdata->angles); VectorAdd(ent->i.origin, ent->i.mins, entdata->absmins); VectorAdd(ent->i.origin, ent->i.maxs, entdata->absmaxs); entdata->solid = ent->i.solid; entdata->modelnum = ent->i.modelindex - 1; } //end of the function AAS_EntityBSPData //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_ResetEntityLinks(void) { int i; for (i = 0; i < aasworld.maxentities; i++) { aasworld.entities[i].areas = NULL; aasworld.entities[i].leaves = NULL; } //end for } //end of the function AAS_ResetEntityLinks //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_InvalidateEntities(void) { int i; for (i = 0; i < aasworld.maxentities; i++) { aasworld.entities[i].i.valid = qfalse; aasworld.entities[i].i.number = i; } //end for } //end of the function AAS_InvalidateEntities //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_UnlinkInvalidEntities(void) { int i; aas_entity_t *ent; for (i = 0; i < aasworld.maxentities; i++) { ent = &aasworld.entities[i]; if (!ent->i.valid) { AAS_UnlinkFromAreas( ent->areas ); ent->areas = NULL; AAS_UnlinkFromBSPLeaves( ent->leaves ); ent->leaves = NULL; } //end for } //end for } //end of the function AAS_UnlinkInvalidEntities //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_NearestEntity(vec3_t origin, int modelindex) { int i, bestentnum; float dist, bestdist; aas_entity_t *ent; vec3_t dir; bestentnum = 0; bestdist = 99999; for (i = 0; i < aasworld.maxentities; i++) { ent = &aasworld.entities[i]; if (ent->i.modelindex != modelindex) continue; VectorSubtract(ent->i.origin, origin, dir); if (fabs(dir[0]) < 40) { if (fabs(dir[1]) < 40) { dist = VectorLength(dir); if (dist < bestdist) { bestdist = dist; bestentnum = i; } //end if } //end if } //end if } //end for return bestentnum; } //end of the function AAS_NearestEntity //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_BestReachableEntityArea(int entnum) { aas_entity_t *ent; ent = &aasworld.entities[entnum]; return AAS_BestReachableLinkArea(ent->areas); } //end of the function AAS_BestReachableEntityArea //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_NextEntity(int entnum) { if (!aasworld.loaded) return 0; if (entnum < 0) entnum = -1; while(++entnum < aasworld.maxentities) { if (aasworld.entities[entnum].i.valid) return entnum; } //end while return 0; } //end of the function AAS_NextEntity ================================================ FILE: src/engine/botlib/be_aas_entity.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_entity.h * * desc: AAS * * $Archive: /source/code/botlib/be_aas_entity.h $ * *****************************************************************************/ #ifdef AASINTERN //invalidates all entity infos void AAS_InvalidateEntities(void); //unlink not updated entities void AAS_UnlinkInvalidEntities(void); //resets the entity AAS and BSP links (sets areas and leaves pointers to NULL) void AAS_ResetEntityLinks(void); //updates an entity int AAS_UpdateEntity(int ent, bot_entitystate_t *state); //gives the entity data used for collision detection void AAS_EntityBSPData(int entnum, bsp_entdata_t *entdata); #endif //AASINTERN //returns the size of the entity bounding box in mins and maxs void AAS_EntitySize(int entnum, vec3_t mins, vec3_t maxs); //returns the BSP model number of the entity int AAS_EntityModelNum(int entnum); //returns the origin of an entity with the given model number int AAS_OriginOfMoverWithModelNum(int modelnum, vec3_t origin); //returns the best reachable area the entity is situated in int AAS_BestReachableEntityArea(int entnum); //returns the info of the given entity void AAS_EntityInfo(int entnum, aas_entityinfo_t *info); //returns the next entity int AAS_NextEntity(int entnum); //returns the origin of the entity void AAS_EntityOrigin(int entnum, vec3_t origin); //returns the entity type int AAS_EntityType(int entnum); //returns the model index of the entity int AAS_EntityModelindex(int entnum); ================================================ FILE: src/engine/botlib/be_aas_file.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_file.c * * desc: AAS file loading/writing * * $Archive: /MissionPack/code/botlib/be_aas_file.c $ * *****************************************************************************/ #include "../../game/q_shared.h" #include "l_memory.h" #include "l_script.h" #include "l_precomp.h" #include "l_struct.h" #include "l_libvar.h" #include "l_utils.h" #include "aasfile.h" #include "../../game/botlib.h" #include "../../game/be_aas.h" #include "be_aas_funcs.h" #include "be_interface.h" #include "be_aas_def.h" //#define AASFILEDEBUG //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_SwapAASData(void) { int i, j; //bounding boxes for (i = 0; i < aasworld.numbboxes; i++) { aasworld.bboxes[i].presencetype = LittleLong(aasworld.bboxes[i].presencetype); aasworld.bboxes[i].flags = LittleLong(aasworld.bboxes[i].flags); for (j = 0; j < 3; j++) { aasworld.bboxes[i].mins[j] = LittleLong(aasworld.bboxes[i].mins[j]); aasworld.bboxes[i].maxs[j] = LittleLong(aasworld.bboxes[i].maxs[j]); } //end for } //end for //vertexes for (i = 0; i < aasworld.numvertexes; i++) { for (j = 0; j < 3; j++) aasworld.vertexes[i][j] = LittleFloat(aasworld.vertexes[i][j]); } //end for //planes for (i = 0; i < aasworld.numplanes; i++) { for (j = 0; j < 3; j++) aasworld.planes[i].normal[j] = LittleFloat(aasworld.planes[i].normal[j]); aasworld.planes[i].dist = LittleFloat(aasworld.planes[i].dist); aasworld.planes[i].type = LittleLong(aasworld.planes[i].type); } //end for //edges for (i = 0; i < aasworld.numedges; i++) { aasworld.edges[i].v[0] = LittleLong(aasworld.edges[i].v[0]); aasworld.edges[i].v[1] = LittleLong(aasworld.edges[i].v[1]); } //end for //edgeindex for (i = 0; i < aasworld.edgeindexsize; i++) { aasworld.edgeindex[i] = LittleLong(aasworld.edgeindex[i]); } //end for //faces for (i = 0; i < aasworld.numfaces; i++) { aasworld.faces[i].planenum = LittleLong(aasworld.faces[i].planenum); aasworld.faces[i].faceflags = LittleLong(aasworld.faces[i].faceflags); aasworld.faces[i].numedges = LittleLong(aasworld.faces[i].numedges); aasworld.faces[i].firstedge = LittleLong(aasworld.faces[i].firstedge); aasworld.faces[i].frontarea = LittleLong(aasworld.faces[i].frontarea); aasworld.faces[i].backarea = LittleLong(aasworld.faces[i].backarea); } //end for //face index for (i = 0; i < aasworld.faceindexsize; i++) { aasworld.faceindex[i] = LittleLong(aasworld.faceindex[i]); } //end for //convex areas for (i = 0; i < aasworld.numareas; i++) { aasworld.areas[i].areanum = LittleLong(aasworld.areas[i].areanum); aasworld.areas[i].numfaces = LittleLong(aasworld.areas[i].numfaces); aasworld.areas[i].firstface = LittleLong(aasworld.areas[i].firstface); for (j = 0; j < 3; j++) { aasworld.areas[i].mins[j] = LittleFloat(aasworld.areas[i].mins[j]); aasworld.areas[i].maxs[j] = LittleFloat(aasworld.areas[i].maxs[j]); aasworld.areas[i].center[j] = LittleFloat(aasworld.areas[i].center[j]); } //end for } //end for //area settings for (i = 0; i < aasworld.numareasettings; i++) { aasworld.areasettings[i].contents = LittleLong(aasworld.areasettings[i].contents); aasworld.areasettings[i].areaflags = LittleLong(aasworld.areasettings[i].areaflags); aasworld.areasettings[i].presencetype = LittleLong(aasworld.areasettings[i].presencetype); aasworld.areasettings[i].cluster = LittleLong(aasworld.areasettings[i].cluster); aasworld.areasettings[i].clusterareanum = LittleLong(aasworld.areasettings[i].clusterareanum); aasworld.areasettings[i].numreachableareas = LittleLong(aasworld.areasettings[i].numreachableareas); aasworld.areasettings[i].firstreachablearea = LittleLong(aasworld.areasettings[i].firstreachablearea); } //end for //area reachability for (i = 0; i < aasworld.reachabilitysize; i++) { aasworld.reachability[i].areanum = LittleLong(aasworld.reachability[i].areanum); aasworld.reachability[i].facenum = LittleLong(aasworld.reachability[i].facenum); aasworld.reachability[i].edgenum = LittleLong(aasworld.reachability[i].edgenum); for (j = 0; j < 3; j++) { aasworld.reachability[i].start[j] = LittleFloat(aasworld.reachability[i].start[j]); aasworld.reachability[i].end[j] = LittleFloat(aasworld.reachability[i].end[j]); } //end for aasworld.reachability[i].traveltype = LittleLong(aasworld.reachability[i].traveltype); aasworld.reachability[i].traveltime = LittleShort(aasworld.reachability[i].traveltime); } //end for //nodes for (i = 0; i < aasworld.numnodes; i++) { aasworld.nodes[i].planenum = LittleLong(aasworld.nodes[i].planenum); aasworld.nodes[i].children[0] = LittleLong(aasworld.nodes[i].children[0]); aasworld.nodes[i].children[1] = LittleLong(aasworld.nodes[i].children[1]); } //end for //cluster portals for (i = 0; i < aasworld.numportals; i++) { aasworld.portals[i].areanum = LittleLong(aasworld.portals[i].areanum); aasworld.portals[i].frontcluster = LittleLong(aasworld.portals[i].frontcluster); aasworld.portals[i].backcluster = LittleLong(aasworld.portals[i].backcluster); aasworld.portals[i].clusterareanum[0] = LittleLong(aasworld.portals[i].clusterareanum[0]); aasworld.portals[i].clusterareanum[1] = LittleLong(aasworld.portals[i].clusterareanum[1]); } //end for //cluster portal index for (i = 0; i < aasworld.portalindexsize; i++) { aasworld.portalindex[i] = LittleLong(aasworld.portalindex[i]); } //end for //cluster for (i = 0; i < aasworld.numclusters; i++) { aasworld.clusters[i].numareas = LittleLong(aasworld.clusters[i].numareas); aasworld.clusters[i].numreachabilityareas = LittleLong(aasworld.clusters[i].numreachabilityareas); aasworld.clusters[i].numportals = LittleLong(aasworld.clusters[i].numportals); aasworld.clusters[i].firstportal = LittleLong(aasworld.clusters[i].firstportal); } //end for } //end of the function AAS_SwapAASData //=========================================================================== // dump the current loaded aas file // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_DumpAASData(void) { aasworld.numbboxes = 0; if (aasworld.bboxes) FreeMemory(aasworld.bboxes); aasworld.bboxes = NULL; aasworld.numvertexes = 0; if (aasworld.vertexes) FreeMemory(aasworld.vertexes); aasworld.vertexes = NULL; aasworld.numplanes = 0; if (aasworld.planes) FreeMemory(aasworld.planes); aasworld.planes = NULL; aasworld.numedges = 0; if (aasworld.edges) FreeMemory(aasworld.edges); aasworld.edges = NULL; aasworld.edgeindexsize = 0; if (aasworld.edgeindex) FreeMemory(aasworld.edgeindex); aasworld.edgeindex = NULL; aasworld.numfaces = 0; if (aasworld.faces) FreeMemory(aasworld.faces); aasworld.faces = NULL; aasworld.faceindexsize = 0; if (aasworld.faceindex) FreeMemory(aasworld.faceindex); aasworld.faceindex = NULL; aasworld.numareas = 0; if (aasworld.areas) FreeMemory(aasworld.areas); aasworld.areas = NULL; aasworld.numareasettings = 0; if (aasworld.areasettings) FreeMemory(aasworld.areasettings); aasworld.areasettings = NULL; aasworld.reachabilitysize = 0; if (aasworld.reachability) FreeMemory(aasworld.reachability); aasworld.reachability = NULL; aasworld.numnodes = 0; if (aasworld.nodes) FreeMemory(aasworld.nodes); aasworld.nodes = NULL; aasworld.numportals = 0; if (aasworld.portals) FreeMemory(aasworld.portals); aasworld.portals = NULL; aasworld.numportals = 0; if (aasworld.portalindex) FreeMemory(aasworld.portalindex); aasworld.portalindex = NULL; aasworld.portalindexsize = 0; if (aasworld.clusters) FreeMemory(aasworld.clusters); aasworld.clusters = NULL; aasworld.numclusters = 0; // aasworld.loaded = qfalse; aasworld.initialized = qfalse; aasworld.savefile = qfalse; } //end of the function AAS_DumpAASData //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== #ifdef AASFILEDEBUG void AAS_FileInfo(void) { int i, n, optimized; botimport.Print(PRT_MESSAGE, "version = %d\n", AASVERSION); botimport.Print(PRT_MESSAGE, "numvertexes = %d\n", aasworld.numvertexes); botimport.Print(PRT_MESSAGE, "numplanes = %d\n", aasworld.numplanes); botimport.Print(PRT_MESSAGE, "numedges = %d\n", aasworld.numedges); botimport.Print(PRT_MESSAGE, "edgeindexsize = %d\n", aasworld.edgeindexsize); botimport.Print(PRT_MESSAGE, "numfaces = %d\n", aasworld.numfaces); botimport.Print(PRT_MESSAGE, "faceindexsize = %d\n", aasworld.faceindexsize); botimport.Print(PRT_MESSAGE, "numareas = %d\n", aasworld.numareas); botimport.Print(PRT_MESSAGE, "numareasettings = %d\n", aasworld.numareasettings); botimport.Print(PRT_MESSAGE, "reachabilitysize = %d\n", aasworld.reachabilitysize); botimport.Print(PRT_MESSAGE, "numnodes = %d\n", aasworld.numnodes); botimport.Print(PRT_MESSAGE, "numportals = %d\n", aasworld.numportals); botimport.Print(PRT_MESSAGE, "portalindexsize = %d\n", aasworld.portalindexsize); botimport.Print(PRT_MESSAGE, "numclusters = %d\n", aasworld.numclusters); // for (n = 0, i = 0; i < aasworld.numareasettings; i++) { if (aasworld.areasettings[i].areaflags & AREA_GROUNDED) n++; } //end for botimport.Print(PRT_MESSAGE, "num grounded areas = %d\n", n); // botimport.Print(PRT_MESSAGE, "planes size %d bytes\n", aasworld.numplanes * sizeof(aas_plane_t)); botimport.Print(PRT_MESSAGE, "areas size %d bytes\n", aasworld.numareas * sizeof(aas_area_t)); botimport.Print(PRT_MESSAGE, "areasettings size %d bytes\n", aasworld.numareasettings * sizeof(aas_areasettings_t)); botimport.Print(PRT_MESSAGE, "nodes size %d bytes\n", aasworld.numnodes * sizeof(aas_node_t)); botimport.Print(PRT_MESSAGE, "reachability size %d bytes\n", aasworld.reachabilitysize * sizeof(aas_reachability_t)); botimport.Print(PRT_MESSAGE, "portals size %d bytes\n", aasworld.numportals * sizeof(aas_portal_t)); botimport.Print(PRT_MESSAGE, "clusters size %d bytes\n", aasworld.numclusters * sizeof(aas_cluster_t)); optimized = aasworld.numplanes * sizeof(aas_plane_t) + aasworld.numareas * sizeof(aas_area_t) + aasworld.numareasettings * sizeof(aas_areasettings_t) + aasworld.numnodes * sizeof(aas_node_t) + aasworld.reachabilitysize * sizeof(aas_reachability_t) + aasworld.numportals * sizeof(aas_portal_t) + aasworld.numclusters * sizeof(aas_cluster_t); botimport.Print(PRT_MESSAGE, "optimzed size %d KB\n", optimized >> 10); } //end of the function AAS_FileInfo #endif //AASFILEDEBUG //=========================================================================== // allocate memory and read a lump of a AAS file // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== char *AAS_LoadAASLump(fileHandle_t fp, int offset, int length, int *lastoffset, int size) { char *buf; // if (!length) { //just alloc a dummy return (char *) GetClearedHunkMemory(size+1); } //end if //seek to the data if (offset != *lastoffset) { botimport.Print(PRT_WARNING, "AAS file not sequentially read\n"); if (botimport.FS_Seek(fp, offset, FS_SEEK_SET)) { AAS_Error("can't seek to aas lump\n"); AAS_DumpAASData(); botimport.FS_FCloseFile(fp); return 0; } //end if } //end if //allocate memory buf = (char *) GetClearedHunkMemory(length+1); //read the data if (length) { botimport.FS_Read(buf, length, fp ); *lastoffset += length; } //end if return buf; } //end of the function AAS_LoadAASLump //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_DData(unsigned char *data, int size) { int i; for (i = 0; i < size; i++) { data[i] ^= (unsigned char) i * 119; } //end for } //end of the function AAS_DData //=========================================================================== // load an aas file // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_LoadAASFile(char *filename) { fileHandle_t fp; aas_header_t header; int offset, length, lastoffset; botimport.Print(PRT_MESSAGE, "trying to load %s\n", filename); //dump current loaded aas file AAS_DumpAASData(); //open the file botimport.FS_FOpenFile( filename, &fp, FS_READ ); if (!fp) { AAS_Error("can't open %s\n", filename); return BLERR_CANNOTOPENAASFILE; } //end if //read the header botimport.FS_Read(&header, sizeof(aas_header_t), fp ); lastoffset = sizeof(aas_header_t); //check header identification header.ident = LittleLong(header.ident); if (header.ident != AASID) { AAS_Error("%s is not an AAS file\n", filename); botimport.FS_FCloseFile(fp); return BLERR_WRONGAASFILEID; } //end if //check the version header.version = LittleLong(header.version); // if (header.version != AASVERSION_OLD && header.version != AASVERSION) { AAS_Error("aas file %s is version %i, not %i\n", filename, header.version, AASVERSION); botimport.FS_FCloseFile(fp); return BLERR_WRONGAASFILEVERSION; } //end if // if (header.version == AASVERSION) { AAS_DData((unsigned char *) &header + 8, sizeof(aas_header_t) - 8); } //end if // aasworld.bspchecksum = atoi(LibVarGetString( "sv_mapChecksum")); if (LittleLong(header.bspchecksum) != aasworld.bspchecksum) { AAS_Error("aas file %s is out of date\n", filename); botimport.FS_FCloseFile(fp); return BLERR_WRONGAASFILEVERSION; } //end if //load the lumps: //bounding boxes offset = LittleLong(header.lumps[AASLUMP_BBOXES].fileofs); length = LittleLong(header.lumps[AASLUMP_BBOXES].filelen); aasworld.bboxes = (aas_bbox_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_bbox_t)); aasworld.numbboxes = length / sizeof(aas_bbox_t); if (aasworld.numbboxes && !aasworld.bboxes) return BLERR_CANNOTREADAASLUMP; //vertexes offset = LittleLong(header.lumps[AASLUMP_VERTEXES].fileofs); length = LittleLong(header.lumps[AASLUMP_VERTEXES].filelen); aasworld.vertexes = (aas_vertex_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_vertex_t)); aasworld.numvertexes = length / sizeof(aas_vertex_t); if (aasworld.numvertexes && !aasworld.vertexes) return BLERR_CANNOTREADAASLUMP; //planes offset = LittleLong(header.lumps[AASLUMP_PLANES].fileofs); length = LittleLong(header.lumps[AASLUMP_PLANES].filelen); aasworld.planes = (aas_plane_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_plane_t)); aasworld.numplanes = length / sizeof(aas_plane_t); if (aasworld.numplanes && !aasworld.planes) return BLERR_CANNOTREADAASLUMP; //edges offset = LittleLong(header.lumps[AASLUMP_EDGES].fileofs); length = LittleLong(header.lumps[AASLUMP_EDGES].filelen); aasworld.edges = (aas_edge_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_edge_t)); aasworld.numedges = length / sizeof(aas_edge_t); if (aasworld.numedges && !aasworld.edges) return BLERR_CANNOTREADAASLUMP; //edgeindex offset = LittleLong(header.lumps[AASLUMP_EDGEINDEX].fileofs); length = LittleLong(header.lumps[AASLUMP_EDGEINDEX].filelen); aasworld.edgeindex = (aas_edgeindex_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_edgeindex_t)); aasworld.edgeindexsize = length / sizeof(aas_edgeindex_t); if (aasworld.edgeindexsize && !aasworld.edgeindex) return BLERR_CANNOTREADAASLUMP; //faces offset = LittleLong(header.lumps[AASLUMP_FACES].fileofs); length = LittleLong(header.lumps[AASLUMP_FACES].filelen); aasworld.faces = (aas_face_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_face_t)); aasworld.numfaces = length / sizeof(aas_face_t); if (aasworld.numfaces && !aasworld.faces) return BLERR_CANNOTREADAASLUMP; //faceindex offset = LittleLong(header.lumps[AASLUMP_FACEINDEX].fileofs); length = LittleLong(header.lumps[AASLUMP_FACEINDEX].filelen); aasworld.faceindex = (aas_faceindex_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_faceindex_t)); aasworld.faceindexsize = length / sizeof(aas_faceindex_t); if (aasworld.faceindexsize && !aasworld.faceindex) return BLERR_CANNOTREADAASLUMP; //convex areas offset = LittleLong(header.lumps[AASLUMP_AREAS].fileofs); length = LittleLong(header.lumps[AASLUMP_AREAS].filelen); aasworld.areas = (aas_area_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_area_t)); aasworld.numareas = length / sizeof(aas_area_t); if (aasworld.numareas && !aasworld.areas) return BLERR_CANNOTREADAASLUMP; //area settings offset = LittleLong(header.lumps[AASLUMP_AREASETTINGS].fileofs); length = LittleLong(header.lumps[AASLUMP_AREASETTINGS].filelen); aasworld.areasettings = (aas_areasettings_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_areasettings_t)); aasworld.numareasettings = length / sizeof(aas_areasettings_t); if (aasworld.numareasettings && !aasworld.areasettings) return BLERR_CANNOTREADAASLUMP; //reachability list offset = LittleLong(header.lumps[AASLUMP_REACHABILITY].fileofs); length = LittleLong(header.lumps[AASLUMP_REACHABILITY].filelen); aasworld.reachability = (aas_reachability_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_reachability_t)); aasworld.reachabilitysize = length / sizeof(aas_reachability_t); if (aasworld.reachabilitysize && !aasworld.reachability) return BLERR_CANNOTREADAASLUMP; //nodes offset = LittleLong(header.lumps[AASLUMP_NODES].fileofs); length = LittleLong(header.lumps[AASLUMP_NODES].filelen); aasworld.nodes = (aas_node_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_node_t)); aasworld.numnodes = length / sizeof(aas_node_t); if (aasworld.numnodes && !aasworld.nodes) return BLERR_CANNOTREADAASLUMP; //cluster portals offset = LittleLong(header.lumps[AASLUMP_PORTALS].fileofs); length = LittleLong(header.lumps[AASLUMP_PORTALS].filelen); aasworld.portals = (aas_portal_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_portal_t)); aasworld.numportals = length / sizeof(aas_portal_t); if (aasworld.numportals && !aasworld.portals) return BLERR_CANNOTREADAASLUMP; //cluster portal index offset = LittleLong(header.lumps[AASLUMP_PORTALINDEX].fileofs); length = LittleLong(header.lumps[AASLUMP_PORTALINDEX].filelen); aasworld.portalindex = (aas_portalindex_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_portalindex_t)); aasworld.portalindexsize = length / sizeof(aas_portalindex_t); if (aasworld.portalindexsize && !aasworld.portalindex) return BLERR_CANNOTREADAASLUMP; //clusters offset = LittleLong(header.lumps[AASLUMP_CLUSTERS].fileofs); length = LittleLong(header.lumps[AASLUMP_CLUSTERS].filelen); aasworld.clusters = (aas_cluster_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_cluster_t)); aasworld.numclusters = length / sizeof(aas_cluster_t); if (aasworld.numclusters && !aasworld.clusters) return BLERR_CANNOTREADAASLUMP; //swap everything AAS_SwapAASData(); //aas file is loaded aasworld.loaded = qtrue; //close the file botimport.FS_FCloseFile(fp); // #ifdef AASFILEDEBUG AAS_FileInfo(); #endif //AASFILEDEBUG // return BLERR_NOERROR; } //end of the function AAS_LoadAASFile //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== static int AAS_WriteAASLump_offset; int AAS_WriteAASLump(fileHandle_t fp, aas_header_t *h, int lumpnum, void *data, int length) { aas_lump_t *lump; lump = &h->lumps[lumpnum]; lump->fileofs = LittleLong(AAS_WriteAASLump_offset); //LittleLong(ftell(fp)); lump->filelen = LittleLong(length); if (length > 0) { botimport.FS_Write(data, length, fp ); } //end if AAS_WriteAASLump_offset += length; return qtrue; } //end of the function AAS_WriteAASLump //=========================================================================== // aas data is useless after writing to file because it is byte swapped // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== qboolean AAS_WriteAASFile(char *filename) { aas_header_t header; fileHandle_t fp; botimport.Print(PRT_MESSAGE, "writing %s\n", filename); //swap the aas data AAS_SwapAASData(); //initialize the file header Com_Memset(&header, 0, sizeof(aas_header_t)); header.ident = LittleLong(AASID); header.version = LittleLong(AASVERSION); header.bspchecksum = LittleLong(aasworld.bspchecksum); //open a new file botimport.FS_FOpenFile( filename, &fp, FS_WRITE ); if (!fp) { botimport.Print(PRT_ERROR, "error opening %s\n", filename); return qfalse; } //end if //write the header botimport.FS_Write(&header, sizeof(aas_header_t), fp); AAS_WriteAASLump_offset = sizeof(aas_header_t); //add the data lumps to the file if (!AAS_WriteAASLump(fp, &header, AASLUMP_BBOXES, aasworld.bboxes, aasworld.numbboxes * sizeof(aas_bbox_t))) return qfalse; if (!AAS_WriteAASLump(fp, &header, AASLUMP_VERTEXES, aasworld.vertexes, aasworld.numvertexes * sizeof(aas_vertex_t))) return qfalse; if (!AAS_WriteAASLump(fp, &header, AASLUMP_PLANES, aasworld.planes, aasworld.numplanes * sizeof(aas_plane_t))) return qfalse; if (!AAS_WriteAASLump(fp, &header, AASLUMP_EDGES, aasworld.edges, aasworld.numedges * sizeof(aas_edge_t))) return qfalse; if (!AAS_WriteAASLump(fp, &header, AASLUMP_EDGEINDEX, aasworld.edgeindex, aasworld.edgeindexsize * sizeof(aas_edgeindex_t))) return qfalse; if (!AAS_WriteAASLump(fp, &header, AASLUMP_FACES, aasworld.faces, aasworld.numfaces * sizeof(aas_face_t))) return qfalse; if (!AAS_WriteAASLump(fp, &header, AASLUMP_FACEINDEX, aasworld.faceindex, aasworld.faceindexsize * sizeof(aas_faceindex_t))) return qfalse; if (!AAS_WriteAASLump(fp, &header, AASLUMP_AREAS, aasworld.areas, aasworld.numareas * sizeof(aas_area_t))) return qfalse; if (!AAS_WriteAASLump(fp, &header, AASLUMP_AREASETTINGS, aasworld.areasettings, aasworld.numareasettings * sizeof(aas_areasettings_t))) return qfalse; if (!AAS_WriteAASLump(fp, &header, AASLUMP_REACHABILITY, aasworld.reachability, aasworld.reachabilitysize * sizeof(aas_reachability_t))) return qfalse; if (!AAS_WriteAASLump(fp, &header, AASLUMP_NODES, aasworld.nodes, aasworld.numnodes * sizeof(aas_node_t))) return qfalse; if (!AAS_WriteAASLump(fp, &header, AASLUMP_PORTALS, aasworld.portals, aasworld.numportals * sizeof(aas_portal_t))) return qfalse; if (!AAS_WriteAASLump(fp, &header, AASLUMP_PORTALINDEX, aasworld.portalindex, aasworld.portalindexsize * sizeof(aas_portalindex_t))) return qfalse; if (!AAS_WriteAASLump(fp, &header, AASLUMP_CLUSTERS, aasworld.clusters, aasworld.numclusters * sizeof(aas_cluster_t))) return qfalse; //rewrite the header with the added lumps botimport.FS_Seek(fp, 0, FS_SEEK_SET); AAS_DData((unsigned char *) &header + 8, sizeof(aas_header_t) - 8); botimport.FS_Write(&header, sizeof(aas_header_t), fp); //close the file botimport.FS_FCloseFile(fp); return qtrue; } //end of the function AAS_WriteAASFile ================================================ FILE: src/engine/botlib/be_aas_file.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_file.h * * desc: AAS * * $Archive: /source/code/botlib/be_aas_file.h $ * *****************************************************************************/ #ifdef AASINTERN //loads the AAS file with the given name int AAS_LoadAASFile(char *filename); //writes an AAS file with the given name qboolean AAS_WriteAASFile(char *filename); //dumps the loaded AAS data void AAS_DumpAASData(void); //print AAS file information void AAS_FileInfo(void); #endif //AASINTERN ================================================ FILE: src/engine/botlib/be_aas_funcs.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_funcs.h * * desc: AAS * * $Archive: /source/code/botlib/be_aas_funcs.h $ * *****************************************************************************/ #ifndef BSPCINCLUDE #include "be_aas_main.h" #include "be_aas_entity.h" #include "be_aas_sample.h" #include "be_aas_cluster.h" #include "be_aas_reach.h" #include "be_aas_route.h" #include "be_aas_routealt.h" #include "be_aas_debug.h" #include "be_aas_file.h" #include "be_aas_optimize.h" #include "be_aas_bsp.h" #include "be_aas_move.h" #endif //BSPCINCLUDE ================================================ FILE: src/engine/botlib/be_aas_main.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_main.c * * desc: AAS * * $Archive: /MissionPack/code/botlib/be_aas_main.c $ * *****************************************************************************/ #include "../../game/q_shared.h" #include "l_memory.h" #include "l_libvar.h" #include "l_utils.h" #include "l_script.h" #include "l_precomp.h" #include "l_struct.h" #include "l_log.h" #include "aasfile.h" #include "../../game/botlib.h" #include "../../game/be_aas.h" #include "be_aas_funcs.h" #include "be_interface.h" #include "be_aas_def.h" aas_t aasworld; libvar_t *saveroutingcache; //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void QDECL AAS_Error(char *fmt, ...) { char str[1024]; va_list arglist; va_start(arglist, fmt); vsprintf(str, fmt, arglist); va_end(arglist); botimport.Print(PRT_FATAL, str); } //end of the function AAS_Error //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== char *AAS_StringFromIndex(char *indexname, char *stringindex[], int numindexes, int index) { if (!aasworld.indexessetup) { botimport.Print(PRT_ERROR, "%s: index %d not setup\n", indexname, index); return ""; } //end if if (index < 0 || index >= numindexes) { botimport.Print(PRT_ERROR, "%s: index %d out of range\n", indexname, index); return ""; } //end if if (!stringindex[index]) { if (index) { botimport.Print(PRT_ERROR, "%s: reference to unused index %d\n", indexname, index); } //end if return ""; } //end if return stringindex[index]; } //end of the function AAS_StringFromIndex //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_IndexFromString(char *indexname, char *stringindex[], int numindexes, char *string) { int i; if (!aasworld.indexessetup) { botimport.Print(PRT_ERROR, "%s: index not setup \"%s\"\n", indexname, string); return 0; } //end if for (i = 0; i < numindexes; i++) { if (!stringindex[i]) continue; if (!Q_stricmp(stringindex[i], string)) return i; } //end for return 0; } //end of the function AAS_IndexFromString //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== char *AAS_ModelFromIndex(int index) { return AAS_StringFromIndex("ModelFromIndex", &aasworld.configstrings[CS_MODELS], MAX_MODELS, index); } //end of the function AAS_ModelFromIndex //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_IndexFromModel(char *modelname) { return AAS_IndexFromString("IndexFromModel", &aasworld.configstrings[CS_MODELS], MAX_MODELS, modelname); } //end of the function AAS_IndexFromModel //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_UpdateStringIndexes(int numconfigstrings, char *configstrings[]) { int i; //set string pointers and copy the strings for (i = 0; i < numconfigstrings; i++) { if (configstrings[i]) { //if (aasworld.configstrings[i]) FreeMemory(aasworld.configstrings[i]); aasworld.configstrings[i] = (char *) GetMemory((int)strlen(configstrings[i]) + 1); strcpy(aasworld.configstrings[i], configstrings[i]); } //end if } //end for aasworld.indexessetup = qtrue; } //end of the function AAS_UpdateStringIndexes //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_Loaded(void) { return aasworld.loaded; } //end of the function AAS_Loaded //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_Initialized(void) { return aasworld.initialized; } //end of the function AAS_Initialized //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_SetInitialized(void) { aasworld.initialized = qtrue; botimport.Print(PRT_MESSAGE, "AAS initialized.\n"); #ifdef DEBUG //create all the routing cache //AAS_CreateAllRoutingCache(); // //AAS_RoutingInfo(); #endif } //end of the function AAS_SetInitialized //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_ContinueInit(float time) { //if no AAS file loaded if (!aasworld.loaded) return; //if AAS is already initialized if (aasworld.initialized) return; //calculate reachability, if not finished return if (AAS_ContinueInitReachability(time)) return; //initialize clustering for the new map AAS_InitClustering(); //if reachability has been calculated and an AAS file should be written //or there is a forced data optimization if (aasworld.savefile || ((int)LibVarGetValue("forcewrite"))) { //optimize the AAS data if ((int)LibVarValue("aasoptimize", "0")) AAS_Optimize(); //save the AAS file if (AAS_WriteAASFile(aasworld.filename)) { botimport.Print(PRT_MESSAGE, "%s written succesfully\n", aasworld.filename); } //end if else { botimport.Print(PRT_ERROR, "couldn't write %s\n", aasworld.filename); } //end else } //end if //initialize the routing AAS_InitRouting(); //at this point AAS is initialized AAS_SetInitialized(); } //end of the function AAS_ContinueInit //=========================================================================== // called at the start of every frame // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_StartFrame(float time) { aasworld.time = time; //unlink all entities that were not updated last frame AAS_UnlinkInvalidEntities(); //invalidate the entities AAS_InvalidateEntities(); //initialize AAS AAS_ContinueInit(time); // aasworld.frameroutingupdates = 0; // if (bot_developer) { if (LibVarGetValue("showcacheupdates")) { AAS_RoutingInfo(); LibVarSet("showcacheupdates", "0"); } //end if if (LibVarGetValue("showmemoryusage")) { PrintUsedMemorySize(); LibVarSet("showmemoryusage", "0"); } //end if if (LibVarGetValue("memorydump")) { PrintMemoryLabels(); LibVarSet("memorydump", "0"); } //end if } //end if // if (saveroutingcache->value) { AAS_WriteRouteCache(); LibVarSet("saveroutingcache", "0"); } //end if // aasworld.numframes++; return BLERR_NOERROR; } //end of the function AAS_StartFrame //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== float AAS_Time(void) { return aasworld.time; } //end of the function AAS_Time //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_ProjectPointOntoVector( vec3_t point, vec3_t vStart, vec3_t vEnd, vec3_t vProj ) { vec3_t pVec, vec; VectorSubtract( point, vStart, pVec ); VectorSubtract( vEnd, vStart, vec ); VectorNormalize( vec ); // project onto the directional vector for this segment VectorMA( vStart, DotProduct( pVec, vec ), vec, vProj ); } //end of the function AAS_ProjectPointOntoVector //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_LoadFiles(const char *mapname) { int errnum; char aasfile[MAX_PATH]; // char bspfile[MAX_PATH]; strcpy(aasworld.mapname, mapname); //NOTE: first reset the entity links into the AAS areas and BSP leaves // the AAS link heap and BSP link heap are reset after respectively the // AAS file and BSP file are loaded AAS_ResetEntityLinks(); // load bsp info AAS_LoadBSPFile(); //load the aas file Com_sprintf(aasfile, MAX_PATH, "maps/%s.aas", mapname); errnum = AAS_LoadAASFile(aasfile); if (errnum != BLERR_NOERROR) return errnum; botimport.Print(PRT_MESSAGE, "loaded %s\n", aasfile); strncpy(aasworld.filename, aasfile, MAX_PATH); return BLERR_NOERROR; } //end of the function AAS_LoadFiles //=========================================================================== // called everytime a map changes // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_LoadMap(const char *mapname) { int errnum; //if no mapname is provided then the string indexes are updated if (!mapname) { return 0; } //end if // aasworld.initialized = qfalse; //NOTE: free the routing caches before loading a new map because // to free the caches the old number of areas, number of clusters // and number of areas in a clusters must be available AAS_FreeRoutingCaches(); //load the map errnum = AAS_LoadFiles(mapname); if (errnum != BLERR_NOERROR) { aasworld.loaded = qfalse; return errnum; } //end if // AAS_InitSettings(); //initialize the AAS link heap for the new map AAS_InitAASLinkHeap(); //initialize the AAS linked entities for the new map AAS_InitAASLinkedEntities(); //initialize reachability for the new map AAS_InitReachability(); //initialize the alternative routing AAS_InitAlternativeRouting(); //everything went ok return 0; } //end of the function AAS_LoadMap //=========================================================================== // called when the library is first loaded // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_Setup(void) { aasworld.maxclients = (int) LibVarValue("maxclients", "128"); aasworld.maxentities = (int) LibVarValue("maxentities", "1024"); // as soon as it's set to 1 the routing cache will be saved saveroutingcache = LibVar("saveroutingcache", "0"); //allocate memory for the entities if (aasworld.entities) FreeMemory(aasworld.entities); aasworld.entities = (aas_entity_t *) GetClearedHunkMemory(aasworld.maxentities * sizeof(aas_entity_t)); //invalidate all the entities AAS_InvalidateEntities(); //force some recalculations //LibVarSet("forceclustering", "1"); //force clustering calculation //LibVarSet("forcereachability", "1"); //force reachability calculation aasworld.numframes = 0; return BLERR_NOERROR; } //end of the function AAS_Setup //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_Shutdown(void) { AAS_ShutdownAlternativeRouting(); // AAS_DumpBSPData(); //free routing caches AAS_FreeRoutingCaches(); //free aas link heap AAS_FreeAASLinkHeap(); //free aas linked entities AAS_FreeAASLinkedEntities(); //free the aas data AAS_DumpAASData(); //free the entities if (aasworld.entities) FreeMemory(aasworld.entities); //clear the aasworld structure Com_Memset(&aasworld, 0, sizeof(aas_t)); //aas has not been initialized aasworld.initialized = qfalse; //NOTE: as soon as a new .bsp file is loaded the .bsp file memory is // freed an reallocated, so there's no need to free that memory here //print shutdown botimport.Print(PRT_MESSAGE, "AAS shutdown.\n"); } //end of the function AAS_Shutdown ================================================ FILE: src/engine/botlib/be_aas_main.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_main.h * * desc: AAS * * $Archive: /source/code/botlib/be_aas_main.h $ * *****************************************************************************/ #ifdef AASINTERN extern aas_t aasworld; //AAS error message void QDECL AAS_Error(char *fmt, ...); //set AAS initialized void AAS_SetInitialized(void); //setup AAS with the given number of entities and clients int AAS_Setup(void); //shutdown AAS void AAS_Shutdown(void); //start a new map int AAS_LoadMap(const char *mapname); //start a new time frame int AAS_StartFrame(float time); #endif //AASINTERN //returns true if AAS is initialized int AAS_Initialized(void); //returns true if the AAS file is loaded int AAS_Loaded(void); //returns the model name from the given index char *AAS_ModelFromIndex(int index); //returns the index from the given model name int AAS_IndexFromModel(char *modelname); //returns the current time float AAS_Time(void); // void AAS_ProjectPointOntoVector( vec3_t point, vec3_t vStart, vec3_t vEnd, vec3_t vProj ); ================================================ FILE: src/engine/botlib/be_aas_move.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_move.c * * desc: AAS * * $Archive: /MissionPack/code/botlib/be_aas_move.c $ * *****************************************************************************/ #include "../../game/q_shared.h" #include "l_memory.h" #include "l_script.h" #include "l_precomp.h" #include "l_struct.h" #include "l_libvar.h" #include "aasfile.h" #include "../../game/botlib.h" #include "../../game/be_aas.h" #include "be_aas_funcs.h" #include "be_aas_def.h" extern botlib_import_t botimport; aas_settings_t aassettings; //#define AAS_MOVE_DEBUG //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_DropToFloor(vec3_t origin, vec3_t mins, vec3_t maxs) { vec3_t end; bsp_trace_t trace; VectorCopy(origin, end); end[2] -= 100; trace = AAS_Trace(origin, mins, maxs, end, 0, CONTENTS_SOLID); if (trace.startsolid) return qfalse; VectorCopy(trace.endpos, origin); return qtrue; } //end of the function AAS_DropToFloor //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_InitSettings(void) { aassettings.phys_gravitydirection[0] = 0; aassettings.phys_gravitydirection[1] = 0; aassettings.phys_gravitydirection[2] = -1; aassettings.phys_friction = LibVarValue("phys_friction", "6"); aassettings.phys_stopspeed = LibVarValue("phys_stopspeed", "100"); aassettings.phys_gravity = LibVarValue("phys_gravity", "800"); aassettings.phys_waterfriction = LibVarValue("phys_waterfriction", "1"); aassettings.phys_watergravity = LibVarValue("phys_watergravity", "400"); aassettings.phys_maxvelocity = LibVarValue("phys_maxvelocity", "320"); aassettings.phys_maxwalkvelocity = LibVarValue("phys_maxwalkvelocity", "320"); aassettings.phys_maxcrouchvelocity = LibVarValue("phys_maxcrouchvelocity", "100"); aassettings.phys_maxswimvelocity = LibVarValue("phys_maxswimvelocity", "150"); aassettings.phys_walkaccelerate = LibVarValue("phys_walkaccelerate", "10"); aassettings.phys_airaccelerate = LibVarValue("phys_airaccelerate", "1"); aassettings.phys_swimaccelerate = LibVarValue("phys_swimaccelerate", "4"); aassettings.phys_maxstep = LibVarValue("phys_maxstep", "19"); aassettings.phys_maxsteepness = LibVarValue("phys_maxsteepness", "0.7"); aassettings.phys_maxwaterjump = LibVarValue("phys_maxwaterjump", "18"); aassettings.phys_maxbarrier = LibVarValue("phys_maxbarrier", "33"); aassettings.phys_jumpvel = LibVarValue("phys_jumpvel", "270"); aassettings.phys_falldelta5 = LibVarValue("phys_falldelta5", "40"); aassettings.phys_falldelta10 = LibVarValue("phys_falldelta10", "60"); aassettings.rs_waterjump = LibVarValue("rs_waterjump", "400"); aassettings.rs_teleport = LibVarValue("rs_teleport", "50"); aassettings.rs_barrierjump = LibVarValue("rs_barrierjump", "100"); aassettings.rs_startcrouch = LibVarValue("rs_startcrouch", "300"); aassettings.rs_startgrapple = LibVarValue("rs_startgrapple", "500"); aassettings.rs_startwalkoffledge = LibVarValue("rs_startwalkoffledge", "70"); aassettings.rs_startjump = LibVarValue("rs_startjump", "300"); aassettings.rs_rocketjump = LibVarValue("rs_rocketjump", "500"); aassettings.rs_bfgjump = LibVarValue("rs_bfgjump", "500"); aassettings.rs_jumppad = LibVarValue("rs_jumppad", "250"); aassettings.rs_aircontrolledjumppad = LibVarValue("rs_aircontrolledjumppad", "300"); aassettings.rs_funcbob = LibVarValue("rs_funcbob", "300"); aassettings.rs_startelevator = LibVarValue("rs_startelevator", "50"); aassettings.rs_falldamage5 = LibVarValue("rs_falldamage5", "300"); aassettings.rs_falldamage10 = LibVarValue("rs_falldamage10", "500"); aassettings.rs_maxfallheight = LibVarValue("rs_maxfallheight", "0"); aassettings.rs_maxjumpfallheight = LibVarValue("rs_maxjumpfallheight", "450"); } //end of the function AAS_InitSettings //=========================================================================== // returns qtrue if the bot is against a ladder // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_AgainstLadder(vec3_t origin) { int areanum, i, facenum, side; vec3_t org; aas_plane_t *plane; aas_face_t *face; aas_area_t *area; VectorCopy(origin, org); areanum = AAS_PointAreaNum(org); if (!areanum) { org[0] += 1; areanum = AAS_PointAreaNum(org); if (!areanum) { org[1] += 1; areanum = AAS_PointAreaNum(org); if (!areanum) { org[0] -= 2; areanum = AAS_PointAreaNum(org); if (!areanum) { org[1] -= 2; areanum = AAS_PointAreaNum(org); } //end if } //end if } //end if } //end if //if in solid... wrrr shouldn't happen if (!areanum) return qfalse; //if not in a ladder area if (!(aasworld.areasettings[areanum].areaflags & AREA_LADDER)) return qfalse; //if a crouch only area if (!(aasworld.areasettings[areanum].presencetype & PRESENCE_NORMAL)) return qfalse; // area = &aasworld.areas[areanum]; for (i = 0; i < area->numfaces; i++) { facenum = aasworld.faceindex[area->firstface + i]; side = facenum < 0; face = &aasworld.faces[abs(facenum)]; //if the face isn't a ladder face if (!(face->faceflags & FACE_LADDER)) continue; //get the plane the face is in plane = &aasworld.planes[face->planenum ^ side]; //if the origin is pretty close to the plane if (fabs(DotProduct(plane->normal, origin) - plane->dist) < 3) { if (AAS_PointInsideFace(abs(facenum), origin, 0.1f)) return qtrue; } //end if } //end for return qfalse; } //end of the function AAS_AgainstLadder //=========================================================================== // returns qtrue if the bot is on the ground // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_OnGround(vec3_t origin, int presencetype, int passent) { aas_trace_t trace; vec3_t end, up = {0, 0, 1}; aas_plane_t *plane; VectorCopy(origin, end); end[2] -= 10; trace = AAS_TraceClientBBox(origin, end, presencetype, passent); //if in solid if (trace.startsolid) return qfalse; //if nothing hit at all if (trace.fraction >= 1.0) return qfalse; //if too far from the hit plane if (origin[2] - trace.endpos[2] > 10) return qfalse; //check if the plane isn't too steep plane = AAS_PlaneFromNum(trace.planenum); if (DotProduct(plane->normal, up) < aassettings.phys_maxsteepness) return qfalse; //the bot is on the ground return qtrue; } //end of the function AAS_OnGround //=========================================================================== // returns qtrue if a bot at the given position is swimming // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_Swimming(vec3_t origin) { vec3_t testorg; VectorCopy(origin, testorg); testorg[2] -= 2; if (AAS_PointContents(testorg) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER)) return qtrue; return qfalse; } //end of the function AAS_Swimming //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== static vec3_t VEC_UP = {0, -1, 0}; static vec3_t MOVEDIR_UP = {0, 0, 1}; static vec3_t VEC_DOWN = {0, -2, 0}; static vec3_t MOVEDIR_DOWN = {0, 0, -1}; void AAS_SetMovedir(vec3_t angles, vec3_t movedir) { if (VectorCompare(angles, VEC_UP)) { VectorCopy(MOVEDIR_UP, movedir); } //end if else if (VectorCompare(angles, VEC_DOWN)) { VectorCopy(MOVEDIR_DOWN, movedir); } //end else if else { AngleVectors(angles, movedir, NULL, NULL); } //end else } //end of the function AAS_SetMovedir //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_JumpReachRunStart(aas_reachability_t *reach, vec3_t runstart) { vec3_t hordir, start, cmdmove; aas_clientmove_t move; // hordir[0] = reach->start[0] - reach->end[0]; hordir[1] = reach->start[1] - reach->end[1]; hordir[2] = 0; VectorNormalize(hordir); //start point VectorCopy(reach->start, start); start[2] += 1; //get command movement VectorScale(hordir, 400, cmdmove); // AAS_PredictClientMovement(&move, -1, start, PRESENCE_NORMAL, qtrue, vec3_origin, cmdmove, 1, 2, 0.1f, SE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA| SE_HITGROUNDDAMAGE|SE_GAP, 0, qfalse); VectorCopy(move.endpos, runstart); //don't enter slime or lava and don't fall from too high if (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE)) { VectorCopy(start, runstart); } //end if } //end of the function AAS_JumpReachRunStart //=========================================================================== // returns the Z velocity when rocket jumping at the origin // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== float AAS_WeaponJumpZVelocity(vec3_t origin, float radiusdamage) { vec3_t kvel, v, start, end, forward, right, viewangles, dir; float mass, knockback, points; vec3_t rocketoffset = {8, 8, -8}; vec3_t botmins = {-16, -16, -24}; vec3_t botmaxs = {16, 16, 32}; bsp_trace_t bsptrace; //look down (90 degrees) viewangles[PITCH] = 90; viewangles[YAW] = 0; viewangles[ROLL] = 0; //get the start point shooting from VectorCopy(origin, start); start[2] += 8; //view offset Z AngleVectors(viewangles, forward, right, NULL); start[0] += forward[0] * rocketoffset[0] + right[0] * rocketoffset[1]; start[1] += forward[1] * rocketoffset[0] + right[1] * rocketoffset[1]; start[2] += forward[2] * rocketoffset[0] + right[2] * rocketoffset[1] + rocketoffset[2]; //end point of the trace VectorMA(start, 500, forward, end); //trace a line to get the impact point bsptrace = AAS_Trace(start, NULL, NULL, end, 1, CONTENTS_SOLID); //calculate the damage the bot will get from the rocket impact VectorAdd(botmins, botmaxs, v); VectorMA(origin, 0.5, v, v); VectorSubtract(bsptrace.endpos, v, v); // points = radiusdamage - 0.5 * VectorLength(v); if (points < 0) points = 0; //the owner of the rocket gets half the damage points *= 0.5; //mass of the bot (p_client.c: PutClientInServer) mass = 200; //knockback is the same as the damage points knockback = points; //direction of the damage (from trace.endpos to bot origin) VectorSubtract(origin, bsptrace.endpos, dir); VectorNormalize(dir); //damage velocity VectorScale(dir, 1600.0 * (float)knockback / mass, kvel); //the rocket jump hack... //rocket impact velocity + jump velocity return kvel[2] + aassettings.phys_jumpvel; } //end of the function AAS_WeaponJumpZVelocity //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== float AAS_RocketJumpZVelocity(vec3_t origin) { //rocket radius damage is 120 (p_weapon.c: Weapon_RocketLauncher_Fire) return AAS_WeaponJumpZVelocity(origin, 120); } //end of the function AAS_RocketJumpZVelocity //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== float AAS_BFGJumpZVelocity(vec3_t origin) { //bfg radius damage is 1000 (p_weapon.c: weapon_bfg_fire) return AAS_WeaponJumpZVelocity(origin, 120); } //end of the function AAS_BFGJumpZVelocity //=========================================================================== // applies ground friction to the given velocity // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_Accelerate(vec3_t velocity, float frametime, vec3_t wishdir, float wishspeed, float accel) { // q2 style int i; float addspeed, accelspeed, currentspeed; currentspeed = DotProduct(velocity, wishdir); addspeed = wishspeed - currentspeed; if (addspeed <= 0) { return; } accelspeed = accel*frametime*wishspeed; if (accelspeed > addspeed) { accelspeed = addspeed; } for (i=0 ; i<3 ; i++) { velocity[i] += accelspeed*wishdir[i]; } } //end of the function AAS_Accelerate //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_AirControl(vec3_t start, vec3_t end, vec3_t velocity, vec3_t cmdmove) { vec3_t dir; VectorSubtract(end, start, dir); } //end of the function AAS_AirControl //=========================================================================== // applies ground friction to the given velocity // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_ApplyFriction(vec3_t vel, float friction, float stopspeed, float frametime) { float speed, control, newspeed; //horizontal speed speed = sqrt(vel[0] * vel[0] + vel[1] * vel[1]); if (speed) { control = speed < stopspeed ? stopspeed : speed; newspeed = speed - frametime * control * friction; if (newspeed < 0) newspeed = 0; newspeed /= speed; vel[0] *= newspeed; vel[1] *= newspeed; } //end if } //end of the function AAS_ApplyFriction //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_ClipToBBox(aas_trace_t *trace, vec3_t start, vec3_t end, int presencetype, vec3_t mins, vec3_t maxs) { int i, j, side; float front, back, frac, planedist; vec3_t bboxmins, bboxmaxs, absmins, absmaxs, dir, mid; AAS_PresenceTypeBoundingBox(presencetype, bboxmins, bboxmaxs); VectorSubtract(mins, bboxmaxs, absmins); VectorSubtract(maxs, bboxmins, absmaxs); // VectorCopy(end, trace->endpos); trace->fraction = 1; for (i = 0; i < 3; i++) { if (start[i] < absmins[i] && end[i] < absmins[i]) return qfalse; if (start[i] > absmaxs[i] && end[i] > absmaxs[i]) return qfalse; } //end for //check bounding box collision VectorSubtract(end, start, dir); frac = 1; for (i = 0; i < 3; i++) { //get plane to test collision with for the current axis direction if (dir[i] > 0) planedist = absmins[i]; else planedist = absmaxs[i]; //calculate collision fraction front = start[i] - planedist; back = end[i] - planedist; frac = front / (front-back); //check if between bounding planes of next axis side = i + 1; if (side > 2) side = 0; mid[side] = start[side] + dir[side] * frac; if (mid[side] > absmins[side] && mid[side] < absmaxs[side]) { //check if between bounding planes of next axis side++; if (side > 2) side = 0; mid[side] = start[side] + dir[side] * frac; if (mid[side] > absmins[side] && mid[side] < absmaxs[side]) { mid[i] = planedist; break; } //end if } //end if } //end for //if there was a collision if (i != 3) { trace->startsolid = qfalse; trace->fraction = frac; trace->ent = 0; trace->planenum = 0; trace->area = 0; trace->lastarea = 0; //trace endpos for (j = 0; j < 3; j++) trace->endpos[j] = start[j] + dir[j] * frac; return qtrue; } //end if return qfalse; } //end of the function AAS_ClipToBBox //=========================================================================== // predicts the movement // assumes regular bounding box sizes // NOTE: out of water jumping is not included // NOTE: grappling hook is not included // // Parameter: origin : origin to start with // presencetype : presence type to start with // velocity : velocity to start with // cmdmove : client command movement // cmdframes : number of frame cmdmove is valid // maxframes : maximum number of predicted frames // frametime : duration of one predicted frame // stopevent : events that stop the prediction // stopareanum : stop as soon as entered this area // Returns: aas_clientmove_t // Changes Globals: - //=========================================================================== int AAS_ClientMovementPrediction(struct aas_clientmove_s *move, int entnum, vec3_t origin, int presencetype, int onground, vec3_t velocity, vec3_t cmdmove, int cmdframes, int maxframes, float frametime, int stopevent, int stopareanum, vec3_t mins, vec3_t maxs, int visualize) { float phys_friction, phys_stopspeed, phys_gravity, phys_waterfriction; float phys_watergravity; float phys_walkaccelerate, phys_airaccelerate, phys_swimaccelerate; float phys_maxwalkvelocity, phys_maxcrouchvelocity, phys_maxswimvelocity; float phys_maxstep, phys_maxsteepness, phys_jumpvel, friction; float gravity, delta, maxvel, wishspeed, accelerate; //float velchange, newvel; int n, i, j, pc, step, swimming, ax, crouch, event, jump_frame, areanum; int areas[20], numareas; vec3_t points[20]; vec3_t org, end, feet, start, stepend, lastorg, wishdir; vec3_t frame_test_vel, old_frame_test_vel, left_test_vel; vec3_t up = {0, 0, 1}; aas_plane_t *plane, *plane2; aas_trace_t trace, steptrace; if (frametime <= 0) frametime = 0.1f; // phys_friction = aassettings.phys_friction; phys_stopspeed = aassettings.phys_stopspeed; phys_gravity = aassettings.phys_gravity; phys_waterfriction = aassettings.phys_waterfriction; phys_watergravity = aassettings.phys_watergravity; phys_maxwalkvelocity = aassettings.phys_maxwalkvelocity;// * frametime; phys_maxcrouchvelocity = aassettings.phys_maxcrouchvelocity;// * frametime; phys_maxswimvelocity = aassettings.phys_maxswimvelocity;// * frametime; phys_walkaccelerate = aassettings.phys_walkaccelerate; phys_airaccelerate = aassettings.phys_airaccelerate; phys_swimaccelerate = aassettings.phys_swimaccelerate; phys_maxstep = aassettings.phys_maxstep; phys_maxsteepness = aassettings.phys_maxsteepness; phys_jumpvel = aassettings.phys_jumpvel * frametime; // Com_Memset(move, 0, sizeof(aas_clientmove_t)); Com_Memset(&trace, 0, sizeof(aas_trace_t)); //start at the current origin VectorCopy(origin, org); org[2] += 0.25; //velocity to test for the first frame VectorScale(velocity, frametime, frame_test_vel); // jump_frame = -1; //predict a maximum of 'maxframes' ahead for (n = 0; n < maxframes; n++) { swimming = AAS_Swimming(org); //get gravity depending on swimming or not gravity = swimming ? phys_watergravity : phys_gravity; //apply gravity at the START of the frame frame_test_vel[2] = frame_test_vel[2] - (gravity * 0.1 * frametime); //if on the ground or swimming if (onground || swimming) { friction = swimming ? phys_friction : phys_waterfriction; //apply friction VectorScale(frame_test_vel, 1/frametime, frame_test_vel); AAS_ApplyFriction(frame_test_vel, friction, phys_stopspeed, frametime); VectorScale(frame_test_vel, frametime, frame_test_vel); } //end if crouch = qfalse; //apply command movement if (n < cmdframes) { ax = 0; maxvel = phys_maxwalkvelocity; accelerate = phys_airaccelerate; VectorCopy(cmdmove, wishdir); if (onground) { if (cmdmove[2] < -300) { crouch = qtrue; maxvel = phys_maxcrouchvelocity; } //end if //if not swimming and upmove is positive then jump if (!swimming && cmdmove[2] > 1) { //jump velocity minus the gravity for one frame + 5 for safety frame_test_vel[2] = phys_jumpvel - (gravity * 0.1 * frametime) + 5; jump_frame = n; //jumping so air accelerate accelerate = phys_airaccelerate; } //end if else { accelerate = phys_walkaccelerate; } //end else ax = 2; } //end if if (swimming) { maxvel = phys_maxswimvelocity; accelerate = phys_swimaccelerate; ax = 3; } //end if else { wishdir[2] = 0; } //end else // wishspeed = VectorNormalize(wishdir); if (wishspeed > maxvel) wishspeed = maxvel; VectorScale(frame_test_vel, 1/frametime, frame_test_vel); AAS_Accelerate(frame_test_vel, frametime, wishdir, wishspeed, accelerate); VectorScale(frame_test_vel, frametime, frame_test_vel); /* for (i = 0; i < ax; i++) { velchange = (cmdmove[i] * frametime) - frame_test_vel[i]; if (velchange > phys_maxacceleration) velchange = phys_maxacceleration; else if (velchange < -phys_maxacceleration) velchange = -phys_maxacceleration; newvel = frame_test_vel[i] + velchange; // if (frame_test_vel[i] <= maxvel && newvel > maxvel) frame_test_vel[i] = maxvel; else if (frame_test_vel[i] >= -maxvel && newvel < -maxvel) frame_test_vel[i] = -maxvel; else frame_test_vel[i] = newvel; } //end for */ } //end if if (crouch) { presencetype = PRESENCE_CROUCH; } //end if else if (presencetype == PRESENCE_CROUCH) { if (AAS_PointPresenceType(org) & PRESENCE_NORMAL) { presencetype = PRESENCE_NORMAL; } //end if } //end else //save the current origin VectorCopy(org, lastorg); //move linear during one frame VectorCopy(frame_test_vel, left_test_vel); j = 0; do { VectorAdd(org, left_test_vel, end); //trace a bounding box trace = AAS_TraceClientBBox(org, end, presencetype, entnum); // //#ifdef AAS_MOVE_DEBUG if (visualize) { if (trace.startsolid) botimport.Print(PRT_MESSAGE, "PredictMovement: start solid\n"); AAS_DebugLine(org, trace.endpos, LINECOLOR_RED); } //end if //#endif //AAS_MOVE_DEBUG // if (stopevent & (SE_ENTERAREA|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER|SE_TOUCHCLUSTERPORTAL)) { numareas = AAS_TraceAreas(org, trace.endpos, areas, points, 20); for (i = 0; i < numareas; i++) { if (stopevent & SE_ENTERAREA) { if (areas[i] == stopareanum) { VectorCopy(points[i], move->endpos); VectorScale(frame_test_vel, 1/frametime, move->velocity); move->endarea = areas[i]; move->trace = trace; move->stopevent = SE_ENTERAREA; move->presencetype = presencetype; move->endcontents = 0; move->time = n * frametime; move->frames = n; return qtrue; } //end if } //end if //NOTE: if not the first frame if ((stopevent & SE_TOUCHJUMPPAD) && n) { if (aasworld.areasettings[areas[i]].contents & AREACONTENTS_JUMPPAD) { VectorCopy(points[i], move->endpos); VectorScale(frame_test_vel, 1/frametime, move->velocity); move->endarea = areas[i]; move->trace = trace; move->stopevent = SE_TOUCHJUMPPAD; move->presencetype = presencetype; move->endcontents = 0; move->time = n * frametime; move->frames = n; return qtrue; } //end if } //end if if (stopevent & SE_TOUCHTELEPORTER) { if (aasworld.areasettings[areas[i]].contents & AREACONTENTS_TELEPORTER) { VectorCopy(points[i], move->endpos); move->endarea = areas[i]; VectorScale(frame_test_vel, 1/frametime, move->velocity); move->trace = trace; move->stopevent = SE_TOUCHTELEPORTER; move->presencetype = presencetype; move->endcontents = 0; move->time = n * frametime; move->frames = n; return qtrue; } //end if } //end if if (stopevent & SE_TOUCHCLUSTERPORTAL) { if (aasworld.areasettings[areas[i]].contents & AREACONTENTS_CLUSTERPORTAL) { VectorCopy(points[i], move->endpos); move->endarea = areas[i]; VectorScale(frame_test_vel, 1/frametime, move->velocity); move->trace = trace; move->stopevent = SE_TOUCHCLUSTERPORTAL; move->presencetype = presencetype; move->endcontents = 0; move->time = n * frametime; move->frames = n; return qtrue; } //end if } //end if } //end for } //end if // if (stopevent & SE_HITBOUNDINGBOX) { if (AAS_ClipToBBox(&trace, org, trace.endpos, presencetype, mins, maxs)) { VectorCopy(trace.endpos, move->endpos); move->endarea = AAS_PointAreaNum(move->endpos); VectorScale(frame_test_vel, 1/frametime, move->velocity); move->trace = trace; move->stopevent = SE_HITBOUNDINGBOX; move->presencetype = presencetype; move->endcontents = 0; move->time = n * frametime; move->frames = n; return qtrue; } //end if } //end if //move the entity to the trace end point VectorCopy(trace.endpos, org); //if there was a collision if (trace.fraction < 1.0) { //get the plane the bounding box collided with plane = AAS_PlaneFromNum(trace.planenum); // if (stopevent & SE_HITGROUNDAREA) { if (DotProduct(plane->normal, up) > phys_maxsteepness) { VectorCopy(org, start); start[2] += 0.5; if (AAS_PointAreaNum(start) == stopareanum) { VectorCopy(start, move->endpos); move->endarea = stopareanum; VectorScale(frame_test_vel, 1/frametime, move->velocity); move->trace = trace; move->stopevent = SE_HITGROUNDAREA; move->presencetype = presencetype; move->endcontents = 0; move->time = n * frametime; move->frames = n; return qtrue; } //end if } //end if } //end if //assume there's no step step = qfalse; //if it is a vertical plane and the bot didn't jump recently if (plane->normal[2] == 0 && (jump_frame < 0 || n - jump_frame > 2)) { //check for a step VectorMA(org, -0.25, plane->normal, start); VectorCopy(start, stepend); start[2] += phys_maxstep; steptrace = AAS_TraceClientBBox(start, stepend, presencetype, entnum); // if (!steptrace.startsolid) { plane2 = AAS_PlaneFromNum(steptrace.planenum); if (DotProduct(plane2->normal, up) > phys_maxsteepness) { VectorSubtract(end, steptrace.endpos, left_test_vel); left_test_vel[2] = 0; frame_test_vel[2] = 0; //#ifdef AAS_MOVE_DEBUG if (visualize) { if (steptrace.endpos[2] - org[2] > 0.125) { VectorCopy(org, start); start[2] = steptrace.endpos[2]; AAS_DebugLine(org, start, LINECOLOR_BLUE); } //end if } //end if //#endif //AAS_MOVE_DEBUG org[2] = steptrace.endpos[2]; step = qtrue; } //end if } //end if } //end if // if (!step) { //velocity left to test for this frame is the projection //of the current test velocity into the hit plane VectorMA(left_test_vel, -DotProduct(left_test_vel, plane->normal), plane->normal, left_test_vel); //store the old velocity for landing check VectorCopy(frame_test_vel, old_frame_test_vel); //test velocity for the next frame is the projection //of the velocity of the current frame into the hit plane VectorMA(frame_test_vel, -DotProduct(frame_test_vel, plane->normal), plane->normal, frame_test_vel); //check for a landing on an almost horizontal floor if (DotProduct(plane->normal, up) > phys_maxsteepness) { onground = qtrue; } //end if if (stopevent & SE_HITGROUNDDAMAGE) { delta = 0; if (old_frame_test_vel[2] < 0 && frame_test_vel[2] > old_frame_test_vel[2] && !onground) { delta = old_frame_test_vel[2]; } //end if else if (onground) { delta = frame_test_vel[2] - old_frame_test_vel[2]; } //end else if (delta) { delta = delta * 10; delta = delta * delta * 0.0001; if (swimming) delta = 0; // never take falling damage if completely underwater /* if (ent->waterlevel == 3) return; if (ent->waterlevel == 2) delta *= 0.25; if (ent->waterlevel == 1) delta *= 0.5; */ if (delta > 40) { VectorCopy(org, move->endpos); move->endarea = AAS_PointAreaNum(org); VectorCopy(frame_test_vel, move->velocity); move->trace = trace; move->stopevent = SE_HITGROUNDDAMAGE; move->presencetype = presencetype; move->endcontents = 0; move->time = n * frametime; move->frames = n; return qtrue; } //end if } //end if } //end if } //end if } //end if //extra check to prevent endless loop if (++j > 20) return qfalse; //while there is a plane hit } while(trace.fraction < 1.0); //if going down if (frame_test_vel[2] <= 10) { //check for a liquid at the feet of the bot VectorCopy(org, feet); feet[2] -= 22; pc = AAS_PointContents(feet); //get event from pc event = SE_NONE; if (pc & CONTENTS_LAVA) event |= SE_ENTERLAVA; if (pc & CONTENTS_SLIME) event |= SE_ENTERSLIME; if (pc & CONTENTS_WATER) event |= SE_ENTERWATER; // areanum = AAS_PointAreaNum(org); if (aasworld.areasettings[areanum].contents & AREACONTENTS_LAVA) event |= SE_ENTERLAVA; if (aasworld.areasettings[areanum].contents & AREACONTENTS_SLIME) event |= SE_ENTERSLIME; if (aasworld.areasettings[areanum].contents & AREACONTENTS_WATER) event |= SE_ENTERWATER; //if in lava or slime if (event & stopevent) { VectorCopy(org, move->endpos); move->endarea = areanum; VectorScale(frame_test_vel, 1/frametime, move->velocity); move->stopevent = event & stopevent; move->presencetype = presencetype; move->endcontents = pc; move->time = n * frametime; move->frames = n; return qtrue; } //end if } //end if // onground = AAS_OnGround(org, presencetype, entnum); //if onground and on the ground for at least one whole frame if (onground) { if (stopevent & SE_HITGROUND) { VectorCopy(org, move->endpos); move->endarea = AAS_PointAreaNum(org); VectorScale(frame_test_vel, 1/frametime, move->velocity); move->trace = trace; move->stopevent = SE_HITGROUND; move->presencetype = presencetype; move->endcontents = 0; move->time = n * frametime; move->frames = n; return qtrue; } //end if } //end if else if (stopevent & SE_LEAVEGROUND) { VectorCopy(org, move->endpos); move->endarea = AAS_PointAreaNum(org); VectorScale(frame_test_vel, 1/frametime, move->velocity); move->trace = trace; move->stopevent = SE_LEAVEGROUND; move->presencetype = presencetype; move->endcontents = 0; move->time = n * frametime; move->frames = n; return qtrue; } //end else if else if (stopevent & SE_GAP) { aas_trace_t gaptrace; VectorCopy(org, start); VectorCopy(start, end); end[2] -= 48 + aassettings.phys_maxbarrier; gaptrace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1); //if solid is found the bot cannot walk any further and will not fall into a gap if (!gaptrace.startsolid) { //if it is a gap (lower than one step height) if (gaptrace.endpos[2] < org[2] - aassettings.phys_maxstep - 1) { if (!(AAS_PointContents(end) & CONTENTS_WATER)) { VectorCopy(lastorg, move->endpos); move->endarea = AAS_PointAreaNum(lastorg); VectorScale(frame_test_vel, 1/frametime, move->velocity); move->trace = trace; move->stopevent = SE_GAP; move->presencetype = presencetype; move->endcontents = 0; move->time = n * frametime; move->frames = n; return qtrue; } //end if } //end if } //end if } //end else if } //end for // VectorCopy(org, move->endpos); move->endarea = AAS_PointAreaNum(org); VectorScale(frame_test_vel, 1/frametime, move->velocity); move->stopevent = SE_NONE; move->presencetype = presencetype; move->endcontents = 0; move->time = n * frametime; move->frames = n; // return qtrue; } //end of the function AAS_ClientMovementPrediction //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_PredictClientMovement(struct aas_clientmove_s *move, int entnum, vec3_t origin, int presencetype, int onground, vec3_t velocity, vec3_t cmdmove, int cmdframes, int maxframes, float frametime, int stopevent, int stopareanum, int visualize) { vec3_t mins, maxs; return AAS_ClientMovementPrediction(move, entnum, origin, presencetype, onground, velocity, cmdmove, cmdframes, maxframes, frametime, stopevent, stopareanum, mins, maxs, visualize); } //end of the function AAS_PredictClientMovement //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_ClientMovementHitBBox(struct aas_clientmove_s *move, int entnum, vec3_t origin, int presencetype, int onground, vec3_t velocity, vec3_t cmdmove, int cmdframes, int maxframes, float frametime, vec3_t mins, vec3_t maxs, int visualize) { return AAS_ClientMovementPrediction(move, entnum, origin, presencetype, onground, velocity, cmdmove, cmdframes, maxframes, frametime, SE_HITBOUNDINGBOX, 0, mins, maxs, visualize); } //end of the function AAS_ClientMovementHitBBox //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_TestMovementPrediction(int entnum, vec3_t origin, vec3_t dir) { vec3_t velocity, cmdmove; aas_clientmove_t move; VectorClear(velocity); if (!AAS_Swimming(origin)) dir[2] = 0; VectorNormalize(dir); VectorScale(dir, 400, cmdmove); cmdmove[2] = 224; AAS_ClearShownDebugLines(); AAS_PredictClientMovement(&move, entnum, origin, PRESENCE_NORMAL, qtrue, velocity, cmdmove, 13, 13, 0.1f, SE_HITGROUND, 0, qtrue);//SE_LEAVEGROUND); if (move.stopevent & SE_LEAVEGROUND) { botimport.Print(PRT_MESSAGE, "leave ground\n"); } //end if } //end of the function TestMovementPrediction //=========================================================================== // calculates the horizontal velocity needed to perform a jump from start // to end // // Parameter: zvel : z velocity for jump // start : start position of jump // end : end position of jump // *speed : returned speed for jump // Returns: qfalse if too high or too far from start to end // Changes Globals: - //=========================================================================== int AAS_HorizontalVelocityForJump(float zvel, vec3_t start, vec3_t end, float *velocity) { float phys_gravity, phys_maxvelocity; float maxjump, height2fall, t, top; vec3_t dir; phys_gravity = aassettings.phys_gravity; phys_maxvelocity = aassettings.phys_maxvelocity; //maximum height a player can jump with the given initial z velocity maxjump = 0.5 * phys_gravity * (zvel / phys_gravity) * (zvel / phys_gravity); //top of the parabolic jump top = start[2] + maxjump; //height the bot will fall from the top height2fall = top - end[2]; //if the goal is to high to jump to if (height2fall < 0) { *velocity = phys_maxvelocity; return 0; } //end if //time a player takes to fall the height t = sqrt(height2fall / (0.5 * phys_gravity)); //direction from start to end VectorSubtract(end, start, dir); // if ( (t + zvel / phys_gravity) == 0.0f ) { *velocity = phys_maxvelocity; return 0; } //calculate horizontal speed *velocity = sqrt(dir[0]*dir[0] + dir[1]*dir[1]) / (t + zvel / phys_gravity); //the horizontal speed must be lower than the max speed if (*velocity > phys_maxvelocity) { *velocity = phys_maxvelocity; return 0; } //end if return 1; } //end of the function AAS_HorizontalVelocityForJump ================================================ FILE: src/engine/botlib/be_aas_move.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_move.h * * desc: AAS * * $Archive: /source/code/botlib/be_aas_move.h $ * *****************************************************************************/ #ifdef AASINTERN extern aas_settings_t aassettings; #endif //AASINTERN //movement prediction int AAS_PredictClientMovement(struct aas_clientmove_s *move, int entnum, vec3_t origin, int presencetype, int onground, vec3_t velocity, vec3_t cmdmove, int cmdframes, int maxframes, float frametime, int stopevent, int stopareanum, int visualize); //predict movement until bounding box is hit int AAS_ClientMovementHitBBox(struct aas_clientmove_s *move, int entnum, vec3_t origin, int presencetype, int onground, vec3_t velocity, vec3_t cmdmove, int cmdframes, int maxframes, float frametime, vec3_t mins, vec3_t maxs, int visualize); //returns true if on the ground at the given origin int AAS_OnGround(vec3_t origin, int presencetype, int passent); //returns true if swimming at the given origin int AAS_Swimming(vec3_t origin); //returns the jump reachability run start point void AAS_JumpReachRunStart(struct aas_reachability_s *reach, vec3_t runstart); //returns true if against a ladder at the given origin int AAS_AgainstLadder(vec3_t origin); //rocket jump Z velocity when rocket-jumping at origin float AAS_RocketJumpZVelocity(vec3_t origin); //bfg jump Z velocity when bfg-jumping at origin float AAS_BFGJumpZVelocity(vec3_t origin); //calculates the horizontal velocity needed for a jump and returns true this velocity could be calculated int AAS_HorizontalVelocityForJump(float zvel, vec3_t start, vec3_t end, float *velocity); // void AAS_SetMovedir(vec3_t angles, vec3_t movedir); // int AAS_DropToFloor(vec3_t origin, vec3_t mins, vec3_t maxs); // void AAS_InitSettings(void); ================================================ FILE: src/engine/botlib/be_aas_optimize.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_optimize.c * * desc: decreases the .aas file size after the reachabilities have * been calculated, just dumps all the faces, edges and vertexes * * $Archive: /MissionPack/code/botlib/be_aas_optimize.c $ * *****************************************************************************/ #include "../../game/q_shared.h" #include "l_libvar.h" #include "l_memory.h" #include "l_script.h" #include "l_precomp.h" #include "l_struct.h" #include "aasfile.h" #include "../../game/botlib.h" #include "../../game/be_aas.h" #include "be_aas_funcs.h" #include "be_interface.h" #include "be_aas_def.h" typedef struct optimized_s { //vertexes int numvertexes; aas_vertex_t *vertexes; //edges int numedges; aas_edge_t *edges; //edge index int edgeindexsize; aas_edgeindex_t *edgeindex; //faces int numfaces; aas_face_t *faces; //face index int faceindexsize; aas_faceindex_t *faceindex; //convex areas int numareas; aas_area_t *areas; // int *vertexoptimizeindex; int *edgeoptimizeindex; int *faceoptimizeindex; } optimized_t; //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_KeepEdge(aas_edge_t *edge) { return 1; } //end of the function AAS_KeepFace //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_OptimizeEdge(optimized_t *optimized, int edgenum) { int i, optedgenum; aas_edge_t *edge, *optedge; edge = &aasworld.edges[abs(edgenum)]; if (!AAS_KeepEdge(edge)) return 0; optedgenum = optimized->edgeoptimizeindex[abs(edgenum)]; if (optedgenum) { //keep the edge reversed sign if (edgenum > 0) return optedgenum; else return -optedgenum; } //end if optedge = &optimized->edges[optimized->numedges]; for (i = 0; i < 2; i++) { if (optimized->vertexoptimizeindex[edge->v[i]]) { optedge->v[i] = optimized->vertexoptimizeindex[edge->v[i]]; } //end if else { VectorCopy(aasworld.vertexes[edge->v[i]], optimized->vertexes[optimized->numvertexes]); optedge->v[i] = optimized->numvertexes; optimized->vertexoptimizeindex[edge->v[i]] = optimized->numvertexes; optimized->numvertexes++; } //end else } //end for optimized->edgeoptimizeindex[abs(edgenum)] = optimized->numedges; optedgenum = optimized->numedges; optimized->numedges++; //keep the edge reversed sign if (edgenum > 0) return optedgenum; else return -optedgenum; } //end of the function AAS_OptimizeEdge //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_KeepFace(aas_face_t *face) { if (!(face->faceflags & FACE_LADDER)) return 0; else return 1; } //end of the function AAS_KeepFace //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_OptimizeFace(optimized_t *optimized, int facenum) { int i, edgenum, optedgenum, optfacenum; aas_face_t *face, *optface; face = &aasworld.faces[abs(facenum)]; if (!AAS_KeepFace(face)) return 0; optfacenum = optimized->faceoptimizeindex[abs(facenum)]; if (optfacenum) { //keep the face side sign if (facenum > 0) return optfacenum; else return -optfacenum; } //end if optface = &optimized->faces[optimized->numfaces]; Com_Memcpy(optface, face, sizeof(aas_face_t)); optface->numedges = 0; optface->firstedge = optimized->edgeindexsize; for (i = 0; i < face->numedges; i++) { edgenum = aasworld.edgeindex[face->firstedge + i]; optedgenum = AAS_OptimizeEdge(optimized, edgenum); if (optedgenum) { optimized->edgeindex[optface->firstedge + optface->numedges] = optedgenum; optface->numedges++; optimized->edgeindexsize++; } //end if } //end for optimized->faceoptimizeindex[abs(facenum)] = optimized->numfaces; optfacenum = optimized->numfaces; optimized->numfaces++; //keep the face side sign if (facenum > 0) return optfacenum; else return -optfacenum; } //end of the function AAS_OptimizeFace //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_OptimizeArea(optimized_t *optimized, int areanum) { int i, facenum, optfacenum; aas_area_t *area, *optarea; area = &aasworld.areas[areanum]; optarea = &optimized->areas[areanum]; Com_Memcpy(optarea, area, sizeof(aas_area_t)); optarea->numfaces = 0; optarea->firstface = optimized->faceindexsize; for (i = 0; i < area->numfaces; i++) { facenum = aasworld.faceindex[area->firstface + i]; optfacenum = AAS_OptimizeFace(optimized, facenum); if (optfacenum) { optimized->faceindex[optarea->firstface + optarea->numfaces] = optfacenum; optarea->numfaces++; optimized->faceindexsize++; } //end if } //end for } //end of the function AAS_OptimizeArea //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_OptimizeAlloc(optimized_t *optimized) { optimized->vertexes = (aas_vertex_t *) GetClearedMemory(aasworld.numvertexes * sizeof(aas_vertex_t)); optimized->numvertexes = 0; optimized->edges = (aas_edge_t *) GetClearedMemory(aasworld.numedges * sizeof(aas_edge_t)); optimized->numedges = 1; //edge zero is a dummy optimized->edgeindex = (aas_edgeindex_t *) GetClearedMemory(aasworld.edgeindexsize * sizeof(aas_edgeindex_t)); optimized->edgeindexsize = 0; optimized->faces = (aas_face_t *) GetClearedMemory(aasworld.numfaces * sizeof(aas_face_t)); optimized->numfaces = 1; //face zero is a dummy optimized->faceindex = (aas_faceindex_t *) GetClearedMemory(aasworld.faceindexsize * sizeof(aas_faceindex_t)); optimized->faceindexsize = 0; optimized->areas = (aas_area_t *) GetClearedMemory(aasworld.numareas * sizeof(aas_area_t)); optimized->numareas = aasworld.numareas; // optimized->vertexoptimizeindex = (int *) GetClearedMemory(aasworld.numvertexes * sizeof(int)); optimized->edgeoptimizeindex = (int *) GetClearedMemory(aasworld.numedges * sizeof(int)); optimized->faceoptimizeindex = (int *) GetClearedMemory(aasworld.numfaces * sizeof(int)); } //end of the function AAS_OptimizeAlloc //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_OptimizeStore(optimized_t *optimized) { //store the optimized vertexes if (aasworld.vertexes) FreeMemory(aasworld.vertexes); aasworld.vertexes = optimized->vertexes; aasworld.numvertexes = optimized->numvertexes; //store the optimized edges if (aasworld.edges) FreeMemory(aasworld.edges); aasworld.edges = optimized->edges; aasworld.numedges = optimized->numedges; //store the optimized edge index if (aasworld.edgeindex) FreeMemory(aasworld.edgeindex); aasworld.edgeindex = optimized->edgeindex; aasworld.edgeindexsize = optimized->edgeindexsize; //store the optimized faces if (aasworld.faces) FreeMemory(aasworld.faces); aasworld.faces = optimized->faces; aasworld.numfaces = optimized->numfaces; //store the optimized face index if (aasworld.faceindex) FreeMemory(aasworld.faceindex); aasworld.faceindex = optimized->faceindex; aasworld.faceindexsize = optimized->faceindexsize; //store the optimized areas if (aasworld.areas) FreeMemory(aasworld.areas); aasworld.areas = optimized->areas; aasworld.numareas = optimized->numareas; //free optimize indexes FreeMemory(optimized->vertexoptimizeindex); FreeMemory(optimized->edgeoptimizeindex); FreeMemory(optimized->faceoptimizeindex); } //end of the function AAS_OptimizeStore //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_Optimize(void) { int i, sign; optimized_t optimized; AAS_OptimizeAlloc(&optimized); for (i = 1; i < aasworld.numareas; i++) { AAS_OptimizeArea(&optimized, i); } //end for //reset the reachability face pointers for (i = 0; i < aasworld.reachabilitysize; i++) { //NOTE: for TRAVEL_ELEVATOR the facenum is the model number of // the elevator if ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_ELEVATOR) continue; //NOTE: for TRAVEL_JUMPPAD the facenum is the Z velocity and the edgenum is the hor velocity if ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMPPAD) continue; //NOTE: for TRAVEL_FUNCBOB the facenum and edgenum contain other coded information if ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_FUNCBOB) continue; // sign = aasworld.reachability[i].facenum; aasworld.reachability[i].facenum = optimized.faceoptimizeindex[abs(aasworld.reachability[i].facenum)]; if (sign < 0) aasworld.reachability[i].facenum = -aasworld.reachability[i].facenum; sign = aasworld.reachability[i].edgenum; aasworld.reachability[i].edgenum = optimized.edgeoptimizeindex[abs(aasworld.reachability[i].edgenum)]; if (sign < 0) aasworld.reachability[i].edgenum = -aasworld.reachability[i].edgenum; } //end for //store the optimized AAS data into aasworld AAS_OptimizeStore(&optimized); //print some nice stuff :) botimport.Print(PRT_MESSAGE, "AAS data optimized.\n"); } //end of the function AAS_Optimize ================================================ FILE: src/engine/botlib/be_aas_optimize.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_optimize.h * * desc: AAS * * $Archive: /source/code/botlib/be_aas_optimize.h $ * *****************************************************************************/ void AAS_Optimize(void); ================================================ FILE: src/engine/botlib/be_aas_reach.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_reach.c * * desc: reachability calculations * * $Archive: /MissionPack/code/botlib/be_aas_reach.c $ * *****************************************************************************/ #include "../../game/q_shared.h" #include "l_log.h" #include "l_memory.h" #include "l_script.h" #include "l_libvar.h" #include "l_precomp.h" #include "l_struct.h" #include "aasfile.h" #include "../../game/botlib.h" #include "../../game/be_aas.h" #include "be_aas_funcs.h" #include "be_aas_def.h" extern int Sys_MilliSeconds(void); extern botlib_import_t botimport; //#define REACH_DEBUG //NOTE: all travel times are in hundreth of a second //maximum number of reachability links #define AAS_MAX_REACHABILITYSIZE 65536 //number of areas reachability is calculated for each frame #define REACHABILITYAREASPERCYCLE 15 //number of units reachability points are placed inside the areas #define INSIDEUNITS 2 #define INSIDEUNITS_WALKEND 5 #define INSIDEUNITS_WALKSTART 0.1 #define INSIDEUNITS_WATERJUMP 15 //area flag used for weapon jumping #define AREA_WEAPONJUMP 8192 //valid area to weapon jump to //number of reachabilities of each type int reach_swim; //swim int reach_equalfloor; //walk on floors with equal height int reach_step; //step up int reach_walk; //walk of step int reach_barrier; //jump up to a barrier int reach_waterjump; //jump out of water int reach_walkoffledge; //walk of a ledge int reach_jump; //jump int reach_ladder; //climb or descent a ladder int reach_teleport; //teleport int reach_elevator; //use an elevator int reach_funcbob; //use a func bob int reach_grapple; //grapple hook int reach_doublejump; //double jump int reach_rampjump; //ramp jump int reach_strafejump; //strafe jump (just normal jump but further) int reach_rocketjump; //rocket jump int reach_bfgjump; //bfg jump int reach_jumppad; //jump pads //if true grapple reachabilities are skipped int calcgrapplereach; //linked reachability typedef struct aas_lreachability_s { int areanum; //number of the reachable area int facenum; //number of the face towards the other area int edgenum; //number of the edge towards the other area vec3_t start; //start point of inter area movement vec3_t end; //end point of inter area movement int traveltype; //type of travel required to get to the area unsigned short int traveltime; //travel time of the inter area movement // struct aas_lreachability_s *next; } aas_lreachability_t; //temporary reachabilities aas_lreachability_t *reachabilityheap; //heap with reachabilities aas_lreachability_t *nextreachability; //next free reachability from the heap aas_lreachability_t **areareachability; //reachability links for every area int numlreachabilities; //=========================================================================== // returns the surface area of the given face // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== float AAS_FaceArea(aas_face_t *face) { int i, edgenum, side; float total; vec_t *v; vec3_t d1, d2, cross; aas_edge_t *edge; edgenum = aasworld.edgeindex[face->firstedge]; side = edgenum < 0; edge = &aasworld.edges[abs(edgenum)]; v = aasworld.vertexes[edge->v[side]]; total = 0; for (i = 1; i < face->numedges - 1; i++) { edgenum = aasworld.edgeindex[face->firstedge + i]; side = edgenum < 0; edge = &aasworld.edges[abs(edgenum)]; VectorSubtract(aasworld.vertexes[edge->v[side]], v, d1); VectorSubtract(aasworld.vertexes[edge->v[!side]], v, d2); CrossProduct(d1, d2, cross); total += 0.5 * VectorLength(cross); } //end for return total; } //end of the function AAS_FaceArea //=========================================================================== // returns the volume of an area // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== float AAS_AreaVolume(int areanum) { int i, edgenum, facenum, side; vec_t d, a, volume; vec3_t corner; aas_plane_t *plane; aas_edge_t *edge; aas_face_t *face; aas_area_t *area; area = &aasworld.areas[areanum]; facenum = aasworld.faceindex[area->firstface]; face = &aasworld.faces[abs(facenum)]; edgenum = aasworld.edgeindex[face->firstedge]; edge = &aasworld.edges[abs(edgenum)]; // VectorCopy(aasworld.vertexes[edge->v[0]], corner); //make tetrahedrons to all other faces volume = 0; for (i = 0; i < area->numfaces; i++) { facenum = abs(aasworld.faceindex[area->firstface + i]); face = &aasworld.faces[facenum]; side = face->backarea != areanum; plane = &aasworld.planes[face->planenum ^ side]; d = -(DotProduct (corner, plane->normal) - plane->dist); a = AAS_FaceArea(face); volume += d * a; } //end for volume /= 3; return volume; } //end of the function AAS_AreaVolume //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_BestReachableLinkArea(aas_link_t *areas) { aas_link_t *link; for (link = areas; link; link = link->next_area) { if (AAS_AreaGrounded(link->areanum) || AAS_AreaSwim(link->areanum)) { return link->areanum; } //end if } //end for // for (link = areas; link; link = link->next_area) { if (link->areanum) return link->areanum; //FIXME: this is a bad idea when the reachability is not yet // calculated when the level items are loaded if (AAS_AreaReachability(link->areanum)) return link->areanum; } //end for return 0; } //end of the function AAS_BestReachableLinkArea //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_GetJumpPadInfo(int ent, vec3_t areastart, vec3_t absmins, vec3_t absmaxs, vec3_t velocity) { int modelnum, ent2; float speed, height, gravity, time, dist, forward; vec3_t origin, angles, teststart, ent2origin; aas_trace_t trace; char model[MAX_EPAIRKEY]; char target[MAX_EPAIRKEY], targetname[MAX_EPAIRKEY]; // AAS_FloatForBSPEpairKey(ent, "speed", &speed); if (!speed) speed = 1000; VectorClear(angles); //get the mins, maxs and origin of the model AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY); if (model[0]) modelnum = atoi(model+1); else modelnum = 0; AAS_BSPModelMinsMaxsOrigin(modelnum, angles, absmins, absmaxs, origin); VectorAdd(origin, absmins, absmins); VectorAdd(origin, absmaxs, absmaxs); VectorAdd(absmins, absmaxs, origin); VectorScale (origin, 0.5, origin); //get the start areas VectorCopy(origin, teststart); teststart[2] += 64; trace = AAS_TraceClientBBox(teststart, origin, PRESENCE_CROUCH, -1); if (trace.startsolid) { botimport.Print(PRT_MESSAGE, "trigger_push start solid\n"); VectorCopy(origin, areastart); } //end if else { VectorCopy(trace.endpos, areastart); } //end else areastart[2] += 0.125; // //AAS_DrawPermanentCross(origin, 4, 4); //get the target entity AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY); for (ent2 = AAS_NextBSPEntity(0); ent2; ent2 = AAS_NextBSPEntity(ent2)) { if (!AAS_ValueForBSPEpairKey(ent2, "targetname", targetname, MAX_EPAIRKEY)) continue; if (!strcmp(targetname, target)) break; } //end for if (!ent2) { botimport.Print(PRT_MESSAGE, "trigger_push without target entity %s\n", target); return qfalse; } //end if AAS_VectorForBSPEpairKey(ent2, "origin", ent2origin); // height = ent2origin[2] - origin[2]; gravity = aassettings.phys_gravity; time = sqrt( height / ( 0.5 * gravity ) ); if (!time) { botimport.Print(PRT_MESSAGE, "trigger_push without time\n"); return qfalse; } //end if // set s.origin2 to the push velocity VectorSubtract ( ent2origin, origin, velocity); dist = VectorNormalize( velocity); forward = dist / time; //FIXME: why multiply by 1.1 forward *= 1.1f; VectorScale(velocity, forward, velocity); velocity[2] = time * gravity; return qtrue; } //end of the function AAS_GetJumpPadInfo //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_BestReachableFromJumpPadArea(vec3_t origin, vec3_t mins, vec3_t maxs) { int area2num, ent, bot_visualizejumppads, bestareanum; float volume, bestareavolume; vec3_t areastart, cmdmove, bboxmins, bboxmaxs; vec3_t absmins, absmaxs, velocity; aas_clientmove_t move; aas_link_t *areas, *link; char classname[MAX_EPAIRKEY]; #ifdef BSPC bot_visualizejumppads = 0; #else bot_visualizejumppads = LibVarValue("bot_visualizejumppads", "0"); #endif VectorAdd(origin, mins, bboxmins); VectorAdd(origin, maxs, bboxmaxs); for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) { if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; if (strcmp(classname, "trigger_push")) continue; // if (!AAS_GetJumpPadInfo(ent, areastart, absmins, absmaxs, velocity)) continue; //get the areas the jump pad brush is in areas = AAS_LinkEntityClientBBox(absmins, absmaxs, -1, PRESENCE_CROUCH); for (link = areas; link; link = link->next_area) { if (AAS_AreaJumpPad(link->areanum)) break; } //end for if (!link) { botimport.Print(PRT_MESSAGE, "trigger_push not in any jump pad area\n"); AAS_UnlinkFromAreas(areas); continue; } //end if // //botimport.Print(PRT_MESSAGE, "found a trigger_push with velocity %f %f %f\n", velocity[0], velocity[1], velocity[2]); // VectorSet(cmdmove, 0, 0, 0); Com_Memset(&move, 0, sizeof(aas_clientmove_t)); area2num = 0; AAS_ClientMovementHitBBox(&move, -1, areastart, PRESENCE_NORMAL, qfalse, velocity, cmdmove, 0, 30, 0.1f, bboxmins, bboxmaxs, bot_visualizejumppads); if (move.frames < 30) { bestareanum = 0; bestareavolume = 0; for (link = areas; link; link = link->next_area) { if (!AAS_AreaJumpPad(link->areanum)) continue; volume = AAS_AreaVolume(link->areanum); if (volume >= bestareavolume) { bestareanum = link->areanum; bestareavolume = volume; } //end if } //end if AAS_UnlinkFromAreas(areas); return bestareanum; } //end if AAS_UnlinkFromAreas(areas); } //end for return 0; } //end of the function AAS_BestReachableFromJumpPadArea //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_BestReachableArea(vec3_t origin, vec3_t mins, vec3_t maxs, vec3_t goalorigin) { int areanum, i, j, k, l; aas_link_t *areas; vec3_t absmins, absmaxs; //vec3_t bbmins, bbmaxs; vec3_t start, end; aas_trace_t trace; if (!aasworld.loaded) { botimport.Print(PRT_ERROR, "AAS_BestReachableArea: aas not loaded\n"); return 0; } //end if //find a point in an area VectorCopy(origin, start); areanum = AAS_PointAreaNum(start); //while no area found fudge around a little for (i = 0; i < 5 && !areanum; i++) { for (j = 0; j < 5 && !areanum; j++) { for (k = -1; k <= 1 && !areanum; k++) { for (l = -1; l <= 1 && !areanum; l++) { VectorCopy(origin, start); start[0] += (float) j * 4 * k; start[1] += (float) j * 4 * l; start[2] += (float) i * 4; areanum = AAS_PointAreaNum(start); } //end for } //end for } //end for } //end for //if an area was found if (areanum) { //drop client bbox down and try again VectorCopy(start, end); start[2] += 0.25; end[2] -= 50; trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1); if (!trace.startsolid) { areanum = AAS_PointAreaNum(trace.endpos); VectorCopy(trace.endpos, goalorigin); //FIXME: cannot enable next line right now because the reachability // does not have to be calculated when the level items are loaded //if the origin is in an area with reachability //if (AAS_AreaReachability(areanum)) return areanum; if (areanum) return areanum; } //end if else { //it can very well happen that the AAS_PointAreaNum function tells that //a point is in an area and that starting a AAS_TraceClientBBox from that //point will return trace.startsolid qtrue #if 0 if (AAS_PointAreaNum(start)) { Log_Write("point %f %f %f in area %d but trace startsolid", start[0], start[1], start[2], areanum); AAS_DrawPermanentCross(start, 4, LINECOLOR_RED); } //end if botimport.Print(PRT_MESSAGE, "AAS_BestReachableArea: start solid\n"); #endif VectorCopy(start, goalorigin); return areanum; } //end else } //end if // //AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, bbmins, bbmaxs); //NOTE: the goal origin does not have to be in the goal area // because the bot will have to move towards the item origin anyway VectorCopy(origin, goalorigin); // VectorAdd(origin, mins, absmins); VectorAdd(origin, maxs, absmaxs); //add bounding box size //VectorSubtract(absmins, bbmaxs, absmins); //VectorSubtract(absmaxs, bbmins, absmaxs); //link an invalid (-1) entity areas = AAS_LinkEntityClientBBox(absmins, absmaxs, -1, PRESENCE_CROUCH); //get the reachable link arae areanum = AAS_BestReachableLinkArea(areas); //unlink the invalid entity AAS_UnlinkFromAreas(areas); // return areanum; } //end of the function AAS_BestReachableArea //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_SetupReachabilityHeap(void) { int i; reachabilityheap = (aas_lreachability_t *) GetClearedMemory( AAS_MAX_REACHABILITYSIZE * sizeof(aas_lreachability_t)); for (i = 0; i < AAS_MAX_REACHABILITYSIZE-1; i++) { reachabilityheap[i].next = &reachabilityheap[i+1]; } //end for reachabilityheap[AAS_MAX_REACHABILITYSIZE-1].next = NULL; nextreachability = reachabilityheap; numlreachabilities = 0; } //end of the function AAS_InitReachabilityHeap //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_ShutDownReachabilityHeap(void) { FreeMemory(reachabilityheap); numlreachabilities = 0; } //end of the function AAS_ShutDownReachabilityHeap //=========================================================================== // returns a reachability link // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== aas_lreachability_t *AAS_AllocReachability(void) { aas_lreachability_t *r; if (!nextreachability) return NULL; //make sure the error message only shows up once if (!nextreachability->next) AAS_Error("AAS_MAX_REACHABILITYSIZE"); // r = nextreachability; nextreachability = nextreachability->next; numlreachabilities++; return r; } //end of the function AAS_AllocReachability //=========================================================================== // frees a reachability link // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_FreeReachability(aas_lreachability_t *lreach) { Com_Memset(lreach, 0, sizeof(aas_lreachability_t)); lreach->next = nextreachability; nextreachability = lreach; numlreachabilities--; } //end of the function AAS_FreeReachability //=========================================================================== // returns qtrue if the area has reachability links // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_AreaReachability(int areanum) { if (areanum < 0 || areanum >= aasworld.numareas) { AAS_Error("AAS_AreaReachability: areanum %d out of range", areanum); return 0; } //end if return aasworld.areasettings[areanum].numreachableareas; } //end of the function AAS_AreaReachability //=========================================================================== // returns the surface area of all ground faces together of the area // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== float AAS_AreaGroundFaceArea(int areanum) { int i; float total; aas_area_t *area; aas_face_t *face; total = 0; area = &aasworld.areas[areanum]; for (i = 0; i < area->numfaces; i++) { face = &aasworld.faces[abs(aasworld.faceindex[area->firstface + i])]; if (!(face->faceflags & FACE_GROUND)) continue; // total += AAS_FaceArea(face); } //end for return total; } //end of the function AAS_AreaGroundFaceArea //=========================================================================== // returns the center of a face // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_FaceCenter(int facenum, vec3_t center) { int i; float scale; aas_face_t *face; aas_edge_t *edge; face = &aasworld.faces[facenum]; VectorClear(center); for (i = 0; i < face->numedges; i++) { edge = &aasworld.edges[abs(aasworld.edgeindex[face->firstedge + i])]; VectorAdd(center, aasworld.vertexes[edge->v[0]], center); VectorAdd(center, aasworld.vertexes[edge->v[1]], center); } //end for scale = 0.5 / face->numedges; VectorScale(center, scale, center); } //end of the function AAS_FaceCenter //=========================================================================== // returns the maximum distance a player can fall before being damaged // damage = deltavelocity*deltavelocity * 0.0001 // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_FallDamageDistance(void) { float maxzvelocity, gravity, t; maxzvelocity = sqrt(30.f * 10000.f); gravity = aassettings.phys_gravity; t = maxzvelocity / gravity; return 0.5 * gravity * t * t; } //end of the function AAS_FallDamageDistance //=========================================================================== // distance = 0.5 * gravity * t * t // vel = t * gravity // damage = vel * vel * 0.0001 // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== float AAS_FallDelta(float distance) { float t, delta, gravity; gravity = aassettings.phys_gravity; t = sqrt(fabs(distance) * 2 / gravity); delta = t * gravity; return delta * delta * 0.0001; } //end of the function AAS_FallDelta //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== float AAS_MaxJumpHeight(float phys_jumpvel) { float phys_gravity; phys_gravity = aassettings.phys_gravity; //maximum height a player can jump with the given initial z velocity return 0.5 * phys_gravity * (phys_jumpvel / phys_gravity) * (phys_jumpvel / phys_gravity); } //end of the function MaxJumpHeight //=========================================================================== // returns true if a player can only crouch in the area // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== float AAS_MaxJumpDistance(float phys_jumpvel) { float phys_gravity, phys_maxvelocity, t; phys_gravity = aassettings.phys_gravity; phys_maxvelocity = aassettings.phys_maxvelocity; //time a player takes to fall the height t = sqrt(aassettings.rs_maxjumpfallheight / (0.5 * phys_gravity)); //maximum distance return phys_maxvelocity * (t + phys_jumpvel / phys_gravity); } //end of the function AAS_MaxJumpDistance //=========================================================================== // returns true if a player can only crouch in the area // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_AreaCrouch(int areanum) { if (!(aasworld.areasettings[areanum].presencetype & PRESENCE_NORMAL)) return qtrue; else return qfalse; } //end of the function AAS_AreaCrouch //=========================================================================== // returns qtrue if it is possible to swim in the area // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_AreaSwim(int areanum) { if (aasworld.areasettings[areanum].areaflags & AREA_LIQUID) return qtrue; else return qfalse; } //end of the function AAS_AreaSwim //=========================================================================== // returns qtrue if the area contains a liquid // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_AreaLiquid(int areanum) { if (aasworld.areasettings[areanum].areaflags & AREA_LIQUID) return qtrue; else return qfalse; } //end of the function AAS_AreaLiquid //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_AreaLava(int areanum) { return (aasworld.areasettings[areanum].contents & AREACONTENTS_LAVA); } //end of the function AAS_AreaLava //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_AreaSlime(int areanum) { return (aasworld.areasettings[areanum].contents & AREACONTENTS_SLIME); } //end of the function AAS_AreaSlime //=========================================================================== // returns qtrue if the area contains ground faces // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_AreaGrounded(int areanum) { return (aasworld.areasettings[areanum].areaflags & AREA_GROUNDED); } //end of the function AAS_AreaGround //=========================================================================== // returns true if the area contains ladder faces // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_AreaLadder(int areanum) { return (aasworld.areasettings[areanum].areaflags & AREA_LADDER); } //end of the function AAS_AreaLadder //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_AreaJumpPad(int areanum) { return (aasworld.areasettings[areanum].contents & AREACONTENTS_JUMPPAD); } //end of the function AAS_AreaJumpPad //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_AreaTeleporter(int areanum) { return (aasworld.areasettings[areanum].contents & AREACONTENTS_TELEPORTER); } //end of the function AAS_AreaTeleporter //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_AreaClusterPortal(int areanum) { return (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL); } //end of the function AAS_AreaClusterPortal //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_AreaDoNotEnter(int areanum) { return (aasworld.areasettings[areanum].contents & AREACONTENTS_DONOTENTER); } //end of the function AAS_AreaDoNotEnter //=========================================================================== // returns the time it takes perform a barrier jump // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== unsigned short int AAS_BarrierJumpTravelTime(void) { return aassettings.phys_jumpvel / (aassettings.phys_gravity * 0.1); } //end op the function AAS_BarrierJumpTravelTime //=========================================================================== // returns true if there already exists a reachability from area1 to area2 // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== qboolean AAS_ReachabilityExists(int area1num, int area2num) { aas_lreachability_t *r; for (r = areareachability[area1num]; r; r = r->next) { if (r->areanum == area2num) return qtrue; } //end for return qfalse; } //end of the function AAS_ReachabilityExists //=========================================================================== // returns true if there is a solid just after the end point when going // from start to end // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_NearbySolidOrGap(vec3_t start, vec3_t end) { vec3_t dir, testpoint; int areanum; VectorSubtract(end, start, dir); dir[2] = 0; VectorNormalize(dir); VectorMA(end, 48, dir, testpoint); areanum = AAS_PointAreaNum(testpoint); if (!areanum) { testpoint[2] += 16; areanum = AAS_PointAreaNum(testpoint); if (!areanum) return qtrue; } //end if VectorMA(end, 64, dir, testpoint); areanum = AAS_PointAreaNum(testpoint); if (areanum) { if (!AAS_AreaSwim(areanum) && !AAS_AreaGrounded(areanum)) return qtrue; } //end if return qfalse; } //end of the function AAS_SolidGapTime //=========================================================================== // searches for swim reachabilities between adjacent areas // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_Reachability_Swim(int area1num, int area2num) { int i, j, face1num, face2num, side1; aas_area_t *area1, *area2; aas_areasettings_t *areasettings; aas_lreachability_t *lreach; aas_face_t *face1; aas_plane_t *plane; vec3_t start; if (!AAS_AreaSwim(area1num) || !AAS_AreaSwim(area2num)) return qfalse; //if the second area is crouch only if (!(aasworld.areasettings[area2num].presencetype & PRESENCE_NORMAL)) return qfalse; area1 = &aasworld.areas[area1num]; area2 = &aasworld.areas[area2num]; //if the areas are not near anough for (i = 0; i < 3; i++) { if (area1->mins[i] > area2->maxs[i] + 10) return qfalse; if (area1->maxs[i] < area2->mins[i] - 10) return qfalse; } //end for //find a shared face and create a reachability link for (i = 0; i < area1->numfaces; i++) { face1num = aasworld.faceindex[area1->firstface + i]; side1 = face1num < 0; face1num = abs(face1num); // for (j = 0; j < area2->numfaces; j++) { face2num = abs(aasworld.faceindex[area2->firstface + j]); // if (face1num == face2num) { AAS_FaceCenter(face1num, start); // if (AAS_PointContents(start) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER)) { // face1 = &aasworld.faces[face1num]; areasettings = &aasworld.areasettings[area1num]; //create a new reachability link lreach = AAS_AllocReachability(); if (!lreach) return qfalse; lreach->areanum = area2num; lreach->facenum = face1num; lreach->edgenum = 0; VectorCopy(start, lreach->start); plane = &aasworld.planes[face1->planenum ^ side1]; VectorMA(lreach->start, -INSIDEUNITS, plane->normal, lreach->end); lreach->traveltype = TRAVEL_SWIM; lreach->traveltime = 1; //if the volume of the area is rather small if (AAS_AreaVolume(area2num) < 800) lreach->traveltime += 200; //if (!(AAS_PointContents(start) & MASK_WATER)) lreach->traveltime += 500; //link the reachability lreach->next = areareachability[area1num]; areareachability[area1num] = lreach; reach_swim++; return qtrue; } //end if } //end if } //end for } //end for return qfalse; } //end of the function AAS_Reachability_Swim //=========================================================================== // searches for reachabilities between adjacent areas with equal floor // heights // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_Reachability_EqualFloorHeight(int area1num, int area2num) { int i, j, edgenum, edgenum1, edgenum2, foundreach, side; float height, bestheight, length, bestlength; vec3_t dir, start, end, normal, invgravity, gravitydirection = {0, 0, -1}; vec3_t edgevec; aas_area_t *area1, *area2; aas_face_t *face1, *face2; aas_edge_t *edge; aas_plane_t *plane2; aas_lreachability_t lr, *lreach; if (!AAS_AreaGrounded(area1num) || !AAS_AreaGrounded(area2num)) return qfalse; area1 = &aasworld.areas[area1num]; area2 = &aasworld.areas[area2num]; //if the areas are not near anough in the x-y direction for (i = 0; i < 2; i++) { if (area1->mins[i] > area2->maxs[i] + 10) return qfalse; if (area1->maxs[i] < area2->mins[i] - 10) return qfalse; } //end for //if area 2 is too high above area 1 if (area2->mins[2] > area1->maxs[2]) return qfalse; // VectorCopy(gravitydirection, invgravity); VectorInverse(invgravity); // bestheight = 99999; bestlength = 0; foundreach = qfalse; Com_Memset(&lr, 0, sizeof(aas_lreachability_t)); //make the compiler happy // //check if the areas have ground faces with a common edge //if existing use the lowest common edge for a reachability link for (i = 0; i < area1->numfaces; i++) { face1 = &aasworld.faces[abs(aasworld.faceindex[area1->firstface + i])]; if (!(face1->faceflags & FACE_GROUND)) continue; // for (j = 0; j < area2->numfaces; j++) { face2 = &aasworld.faces[abs(aasworld.faceindex[area2->firstface + j])]; if (!(face2->faceflags & FACE_GROUND)) continue; //if there is a common edge for (edgenum1 = 0; edgenum1 < face1->numedges; edgenum1++) { for (edgenum2 = 0; edgenum2 < face2->numedges; edgenum2++) { if (abs(aasworld.edgeindex[face1->firstedge + edgenum1]) != abs(aasworld.edgeindex[face2->firstedge + edgenum2])) continue; edgenum = aasworld.edgeindex[face1->firstedge + edgenum1]; side = edgenum < 0; edge = &aasworld.edges[abs(edgenum)]; //get the length of the edge VectorSubtract(aasworld.vertexes[edge->v[1]], aasworld.vertexes[edge->v[0]], dir); length = VectorLength(dir); //get the start point VectorAdd(aasworld.vertexes[edge->v[0]], aasworld.vertexes[edge->v[1]], start); VectorScale(start, 0.5, start); VectorCopy(start, end); //get the end point several units inside area2 //and the start point several units inside area1 //NOTE: normal is pointing into area2 because the //face edges are stored counter clockwise VectorSubtract(aasworld.vertexes[edge->v[side]], aasworld.vertexes[edge->v[!side]], edgevec); plane2 = &aasworld.planes[face2->planenum]; CrossProduct(edgevec, plane2->normal, normal); VectorNormalize(normal); // //VectorMA(start, -1, normal, start); VectorMA(end, INSIDEUNITS_WALKEND, normal, end); VectorMA(start, INSIDEUNITS_WALKSTART, normal, start); end[2] += 0.125; // height = DotProduct(invgravity, start); //NOTE: if there's nearby solid or a gap area after this area //disabled this crap //if (AAS_NearbySolidOrGap(start, end)) height += 200; //NOTE: disabled because it disables reachabilities to very small areas //if (AAS_PointAreaNum(end) != area2num) continue; //get the longest lowest edge if (height < bestheight || (height < bestheight + 1 && length > bestlength)) { bestheight = height; bestlength = length; //create a new reachability link lr.areanum = area2num; lr.facenum = 0; lr.edgenum = edgenum; VectorCopy(start, lr.start); VectorCopy(end, lr.end); lr.traveltype = TRAVEL_WALK; lr.traveltime = 1; foundreach = qtrue; } //end if } //end for } //end for } //end for } //end for if (foundreach) { //create a new reachability link lreach = AAS_AllocReachability(); if (!lreach) return qfalse; lreach->areanum = lr.areanum; lreach->facenum = lr.facenum; lreach->edgenum = lr.edgenum; VectorCopy(lr.start, lreach->start); VectorCopy(lr.end, lreach->end); lreach->traveltype = lr.traveltype; lreach->traveltime = lr.traveltime; lreach->next = areareachability[area1num]; areareachability[area1num] = lreach; //if going into a crouch area if (!AAS_AreaCrouch(area1num) && AAS_AreaCrouch(area2num)) { lreach->traveltime += aassettings.rs_startcrouch; } //end if /* //NOTE: if there's nearby solid or a gap area after this area if (!AAS_NearbySolidOrGap(lreach->start, lreach->end)) { lreach->traveltime += 100; } //end if */ //avoid rather small areas //if (AAS_AreaGroundFaceArea(lreach->areanum) < 500) lreach->traveltime += 100; // reach_equalfloor++; return qtrue; } //end if return qfalse; } //end of the function AAS_Reachability_EqualFloorHeight //=========================================================================== // searches step, barrier, waterjump and walk off ledge reachabilities // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_Reachability_Step_Barrier_WaterJump_WalkOffLedge(int area1num, int area2num) { int i, j, k, l, edge1num, edge2num, areas[10], numareas; int ground_bestarea2groundedgenum, ground_foundreach; int water_bestarea2groundedgenum, water_foundreach; int side1, area1swim, faceside1, groundface1num; float dist, dist1, dist2, diff, invgravitydot, ortdot; float x1, x2, x3, x4, y1, y2, y3, y4, tmp, y; float length, ground_bestlength, water_bestlength, ground_bestdist, water_bestdist; vec3_t v1, v2, v3, v4, tmpv, p1area1, p1area2, p2area1, p2area2; vec3_t normal, ort, edgevec, start, end, dir; vec3_t ground_beststart, ground_bestend, ground_bestnormal; vec3_t water_beststart, water_bestend, water_bestnormal; vec3_t invgravity = {0, 0, 1}; vec3_t testpoint; aas_plane_t *plane; aas_area_t *area1, *area2; aas_face_t *groundface1, *groundface2, *ground_bestface1, *water_bestface1; aas_edge_t *edge1, *edge2; aas_lreachability_t *lreach; aas_trace_t trace; //must be able to walk or swim in the first area if (!AAS_AreaGrounded(area1num) && !AAS_AreaSwim(area1num)) return qfalse; // if (!AAS_AreaGrounded(area2num) && !AAS_AreaSwim(area2num)) return qfalse; // area1 = &aasworld.areas[area1num]; area2 = &aasworld.areas[area2num]; //if the first area contains a liquid area1swim = AAS_AreaSwim(area1num); //if the areas are not near anough in the x-y direction for (i = 0; i < 2; i++) { if (area1->mins[i] > area2->maxs[i] + 10) return qfalse; if (area1->maxs[i] < area2->mins[i] - 10) return qfalse; } //end for // ground_foundreach = qfalse; ground_bestdist = 99999; ground_bestlength = 0; ground_bestarea2groundedgenum = 0; // water_foundreach = qfalse; water_bestdist = 99999; water_bestlength = 0; water_bestarea2groundedgenum = 0; // for (i = 0; i < area1->numfaces; i++) { groundface1num = aasworld.faceindex[area1->firstface + i]; faceside1 = groundface1num < 0; groundface1 = &aasworld.faces[abs(groundface1num)]; //if this isn't a ground face if (!(groundface1->faceflags & FACE_GROUND)) { //if we can swim in the first area if (area1swim) { //face plane must be more or less horizontal plane = &aasworld.planes[groundface1->planenum ^ (!faceside1)]; if (DotProduct(plane->normal, invgravity) < 0.7) continue; } //end if else { //if we can't swim in the area it must be a ground face continue; } //end else } //end if // for (k = 0; k < groundface1->numedges; k++) { edge1num = aasworld.edgeindex[groundface1->firstedge + k]; side1 = (edge1num < 0); //NOTE: for water faces we must take the side area 1 is // on into account because the face is shared and doesn't // have to be oriented correctly if (!(groundface1->faceflags & FACE_GROUND)) side1 = (side1 == faceside1); edge1num = abs(edge1num); edge1 = &aasworld.edges[edge1num]; //vertexes of the edge VectorCopy(aasworld.vertexes[edge1->v[!side1]], v1); VectorCopy(aasworld.vertexes[edge1->v[side1]], v2); //get a vertical plane through the edge //NOTE: normal is pointing into area 2 because the //face edges are stored counter clockwise VectorSubtract(v2, v1, edgevec); CrossProduct(edgevec, invgravity, normal); VectorNormalize(normal); dist = DotProduct(normal, v1); //check the faces from the second area for (j = 0; j < area2->numfaces; j++) { groundface2 = &aasworld.faces[abs(aasworld.faceindex[area2->firstface + j])]; //must be a ground face if (!(groundface2->faceflags & FACE_GROUND)) continue; //check the edges of this ground face for (l = 0; l < groundface2->numedges; l++) { edge2num = abs(aasworld.edgeindex[groundface2->firstedge + l]); edge2 = &aasworld.edges[edge2num]; //vertexes of the edge VectorCopy(aasworld.vertexes[edge2->v[0]], v3); VectorCopy(aasworld.vertexes[edge2->v[1]], v4); //check the distance between the two points and the vertical plane //through the edge of area1 diff = DotProduct(normal, v3) - dist; if (diff < -0.1 || diff > 0.1) continue; diff = DotProduct(normal, v4) - dist; if (diff < -0.1 || diff > 0.1) continue; // //project the two ground edges into the step side plane //and calculate the shortest distance between the two //edges if they overlap in the direction orthogonal to //the gravity direction CrossProduct(invgravity, normal, ort); invgravitydot = DotProduct(invgravity, invgravity); ortdot = DotProduct(ort, ort); //projection into the step plane //NOTE: since gravity is vertical this is just the z coordinate y1 = v1[2];//DotProduct(v1, invgravity) / invgravitydot; y2 = v2[2];//DotProduct(v2, invgravity) / invgravitydot; y3 = v3[2];//DotProduct(v3, invgravity) / invgravitydot; y4 = v4[2];//DotProduct(v4, invgravity) / invgravitydot; // x1 = DotProduct(v1, ort) / ortdot; x2 = DotProduct(v2, ort) / ortdot; x3 = DotProduct(v3, ort) / ortdot; x4 = DotProduct(v4, ort) / ortdot; // if (x1 > x2) { tmp = x1; x1 = x2; x2 = tmp; tmp = y1; y1 = y2; y2 = tmp; VectorCopy(v1, tmpv); VectorCopy(v2, v1); VectorCopy(tmpv, v2); } //end if if (x3 > x4) { tmp = x3; x3 = x4; x4 = tmp; tmp = y3; y3 = y4; y4 = tmp; VectorCopy(v3, tmpv); VectorCopy(v4, v3); VectorCopy(tmpv, v4); } //end if //if the two projected edge lines have no overlap if (x2 <= x3 || x4 <= x1) { // Log_Write("lines no overlap: from area %d to %d\r\n", area1num, area2num); continue; } //end if //if the two lines fully overlap if ((x1 - 0.5 < x3 && x4 < x2 + 0.5) && (x3 - 0.5 < x1 && x2 < x4 + 0.5)) { dist1 = y3 - y1; dist2 = y4 - y2; VectorCopy(v1, p1area1); VectorCopy(v2, p2area1); VectorCopy(v3, p1area2); VectorCopy(v4, p2area2); } //end if else { //if the points are equal if (x1 > x3 - 0.1 && x1 < x3 + 0.1) { dist1 = y3 - y1; VectorCopy(v1, p1area1); VectorCopy(v3, p1area2); } //end if else if (x1 < x3) { y = y1 + (x3 - x1) * (y2 - y1) / (x2 - x1); dist1 = y3 - y; VectorCopy(v3, p1area1); p1area1[2] = y; VectorCopy(v3, p1area2); } //end if else { y = y3 + (x1 - x3) * (y4 - y3) / (x4 - x3); dist1 = y - y1; VectorCopy(v1, p1area1); VectorCopy(v1, p1area2); p1area2[2] = y; } //end if //if the points are equal if (x2 > x4 - 0.1 && x2 < x4 + 0.1) { dist2 = y4 - y2; VectorCopy(v2, p2area1); VectorCopy(v4, p2area2); } //end if else if (x2 < x4) { y = y3 + (x2 - x3) * (y4 - y3) / (x4 - x3); dist2 = y - y2; VectorCopy(v2, p2area1); VectorCopy(v2, p2area2); p2area2[2] = y; } //end if else { y = y1 + (x4 - x1) * (y2 - y1) / (x2 - x1); dist2 = y4 - y; VectorCopy(v4, p2area1); p2area1[2] = y; VectorCopy(v4, p2area2); } //end else } //end else //if both distances are pretty much equal //then we take the middle of the points if (dist1 > dist2 - 1 && dist1 < dist2 + 1) { dist = dist1; VectorAdd(p1area1, p2area1, start); VectorScale(start, 0.5, start); VectorAdd(p1area2, p2area2, end); VectorScale(end, 0.5, end); } //end if else if (dist1 < dist2) { dist = dist1; VectorCopy(p1area1, start); VectorCopy(p1area2, end); } //end else if else { dist = dist2; VectorCopy(p2area1, start); VectorCopy(p2area2, end); } //end else //get the length of the overlapping part of the edges of the two areas VectorSubtract(p2area2, p1area2, dir); length = VectorLength(dir); // if (groundface1->faceflags & FACE_GROUND) { //if the vertical distance is smaller if (dist < ground_bestdist || //or the vertical distance is pretty much the same //but the overlapping part of the edges is longer (dist < ground_bestdist + 1 && length > ground_bestlength)) { ground_bestdist = dist; ground_bestlength = length; ground_foundreach = qtrue; ground_bestarea2groundedgenum = edge1num; ground_bestface1 = groundface1; //best point towards area1 VectorCopy(start, ground_beststart); //normal is pointing into area2 VectorCopy(normal, ground_bestnormal); //best point towards area2 VectorCopy(end, ground_bestend); } //end if } //end if else { //if the vertical distance is smaller if (dist < water_bestdist || //or the vertical distance is pretty much the same //but the overlapping part of the edges is longer (dist < water_bestdist + 1 && length > water_bestlength)) { water_bestdist = dist; water_bestlength = length; water_foundreach = qtrue; water_bestarea2groundedgenum = edge1num; water_bestface1 = groundface1; //best point towards area1 VectorCopy(start, water_beststart); //normal is pointing into area2 VectorCopy(normal, water_bestnormal); //best point towards area2 VectorCopy(end, water_bestend); } //end if } //end else } //end for } //end for } //end for } //end for // // NOTE: swim reachabilities are already filtered out // // Steps // // --------- // | step height -> TRAVEL_WALK //--------| // // --------- //~~~~~~~~| step height and low water -> TRAVEL_WALK //--------| // //~~~~~~~~~~~~~~~~~~ // --------- // | step height and low water up to the step -> TRAVEL_WALK //--------| // //check for a step reachability if (ground_foundreach) { //if area2 is higher but lower than the maximum step height //NOTE: ground_bestdist >= 0 also catches equal floor reachabilities if (ground_bestdist >= 0 && ground_bestdist < aassettings.phys_maxstep) { //create walk reachability from area1 to area2 lreach = AAS_AllocReachability(); if (!lreach) return qfalse; lreach->areanum = area2num; lreach->facenum = 0; lreach->edgenum = ground_bestarea2groundedgenum; VectorMA(ground_beststart, INSIDEUNITS_WALKSTART, ground_bestnormal, lreach->start); VectorMA(ground_bestend, INSIDEUNITS_WALKEND, ground_bestnormal, lreach->end); lreach->traveltype = TRAVEL_WALK; lreach->traveltime = 0;//1; //if going into a crouch area if (!AAS_AreaCrouch(area1num) && AAS_AreaCrouch(area2num)) { lreach->traveltime += aassettings.rs_startcrouch; } //end if lreach->next = areareachability[area1num]; areareachability[area1num] = lreach; //NOTE: if there's nearby solid or a gap area after this area /* if (!AAS_NearbySolidOrGap(lreach->start, lreach->end)) { lreach->traveltime += 100; } //end if */ //avoid rather small areas //if (AAS_AreaGroundFaceArea(lreach->areanum) < 500) lreach->traveltime += 100; // reach_step++; return qtrue; } //end if } //end if // // Water Jumps // // --------- // | //~~~~~~~~| // | // | higher than step height and water up to waterjump height -> TRAVEL_WATERJUMP //--------| // //~~~~~~~~~~~~~~~~~~ // --------- // | // | // | // | higher than step height and low water up to the step -> TRAVEL_WATERJUMP //--------| // //check for a waterjump reachability if (water_foundreach) { //get a test point a little bit towards area1 VectorMA(water_bestend, -INSIDEUNITS, water_bestnormal, testpoint); //go down the maximum waterjump height testpoint[2] -= aassettings.phys_maxwaterjump; //if there IS water the sv_maxwaterjump height below the bestend point if (aasworld.areasettings[AAS_PointAreaNum(testpoint)].areaflags & AREA_LIQUID) { //don't create rediculous water jump reachabilities from areas very far below //the water surface if (water_bestdist < aassettings.phys_maxwaterjump + 24) { //waterjumping from or towards a crouch only area is not possible in Quake2 if ((aasworld.areasettings[area1num].presencetype & PRESENCE_NORMAL) && (aasworld.areasettings[area2num].presencetype & PRESENCE_NORMAL)) { //create water jump reachability from area1 to area2 lreach = AAS_AllocReachability(); if (!lreach) return qfalse; lreach->areanum = area2num; lreach->facenum = 0; lreach->edgenum = water_bestarea2groundedgenum; VectorCopy(water_beststart, lreach->start); VectorMA(water_bestend, INSIDEUNITS_WATERJUMP, water_bestnormal, lreach->end); lreach->traveltype = TRAVEL_WATERJUMP; lreach->traveltime = aassettings.rs_waterjump; lreach->next = areareachability[area1num]; areareachability[area1num] = lreach; //we've got another waterjump reachability reach_waterjump++; return qtrue; } //end if } //end if } //end if } //end if // // Barrier Jumps // // --------- // | // | // | // | higher than step height lower than barrier height -> TRAVEL_BARRIERJUMP //--------| // // --------- // | // | // | //~~~~~~~~| higher than step height lower than barrier height //--------| and a thin layer of water in the area to jump from -> TRAVEL_BARRIERJUMP // //check for a barrier jump reachability if (ground_foundreach) { //if area2 is higher but lower than the maximum barrier jump height if (ground_bestdist > 0 && ground_bestdist < aassettings.phys_maxbarrier) { //if no water in area1 or a very thin layer of water on the ground if (!water_foundreach || (ground_bestdist - water_bestdist < 16)) { //cannot perform a barrier jump towards or from a crouch area in Quake2 if (!AAS_AreaCrouch(area1num) && !AAS_AreaCrouch(area2num)) { //create barrier jump reachability from area1 to area2 lreach = AAS_AllocReachability(); if (!lreach) return qfalse; lreach->areanum = area2num; lreach->facenum = 0; lreach->edgenum = ground_bestarea2groundedgenum; VectorMA(ground_beststart, INSIDEUNITS_WALKSTART, ground_bestnormal, lreach->start); VectorMA(ground_bestend, INSIDEUNITS_WALKEND, ground_bestnormal, lreach->end); lreach->traveltype = TRAVEL_BARRIERJUMP; lreach->traveltime = aassettings.rs_barrierjump;//AAS_BarrierJumpTravelTime(); lreach->next = areareachability[area1num]; areareachability[area1num] = lreach; //we've got another barrierjump reachability reach_barrier++; return qtrue; } //end if } //end if } //end if } //end if // // Walk and Walk Off Ledge // //--------| // | can walk or step back -> TRAVEL_WALK // --------- // //--------| // | // | // | // | cannot walk/step back -> TRAVEL_WALKOFFLEDGE // --------- // //--------| // | // |~~~~~~~~ // | // | cannot step back but can waterjump back -> TRAVEL_WALKOFFLEDGE // --------- FIXME: create TRAVEL_WALK reach?? // //check for a walk or walk off ledge reachability if (ground_foundreach) { if (ground_bestdist < 0) { if (ground_bestdist > -aassettings.phys_maxstep) { //create walk reachability from area1 to area2 lreach = AAS_AllocReachability(); if (!lreach) return qfalse; lreach->areanum = area2num; lreach->facenum = 0; lreach->edgenum = ground_bestarea2groundedgenum; VectorMA(ground_beststart, INSIDEUNITS_WALKSTART, ground_bestnormal, lreach->start); VectorMA(ground_bestend, INSIDEUNITS_WALKEND, ground_bestnormal, lreach->end); lreach->traveltype = TRAVEL_WALK; lreach->traveltime = 1; lreach->next = areareachability[area1num]; areareachability[area1num] = lreach; //we've got another walk reachability reach_walk++; return qtrue; } //end if // if no maximum fall height set or less than the max if (!aassettings.rs_maxfallheight || fabs(ground_bestdist) < aassettings.rs_maxfallheight) { //trace a bounding box vertically to check for solids VectorMA(ground_bestend, INSIDEUNITS, ground_bestnormal, ground_bestend); VectorCopy(ground_bestend, start); start[2] = ground_beststart[2]; VectorCopy(ground_bestend, end); end[2] += 4; trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1); //if no solids were found if (!trace.startsolid && trace.fraction >= 1.0) { //the trace end point must be in the goal area trace.endpos[2] += 1; if (AAS_PointAreaNum(trace.endpos) == area2num) { //if not going through a cluster portal numareas = AAS_TraceAreas(start, end, areas, NULL, sizeof(areas) / sizeof(int)); for (i = 0; i < numareas; i++) if (AAS_AreaClusterPortal(areas[i])) break; if (i >= numareas) { //create a walk off ledge reachability from area1 to area2 lreach = AAS_AllocReachability(); if (!lreach) return qfalse; lreach->areanum = area2num; lreach->facenum = 0; lreach->edgenum = ground_bestarea2groundedgenum; VectorCopy(ground_beststart, lreach->start); VectorCopy(ground_bestend, lreach->end); lreach->traveltype = TRAVEL_WALKOFFLEDGE; lreach->traveltime = aassettings.rs_startwalkoffledge + fabs(ground_bestdist) * 50 / aassettings.phys_gravity; //if falling from too high and not falling into water if (!AAS_AreaSwim(area2num) && !AAS_AreaJumpPad(area2num)) { if (AAS_FallDelta(ground_bestdist) > aassettings.phys_falldelta5) { lreach->traveltime += aassettings.rs_falldamage5; } //end if if (AAS_FallDelta(ground_bestdist) > aassettings.phys_falldelta10) { lreach->traveltime += aassettings.rs_falldamage10; } //end if } //end if lreach->next = areareachability[area1num]; areareachability[area1num] = lreach; // reach_walkoffledge++; //NOTE: don't create a weapon (rl, bfg) jump reachability here //because it interferes with other reachabilities //like the ladder reachability return qtrue; } //end if } //end if } //end if } //end if } //end else } //end if return qfalse; } //end of the function AAS_Reachability_Step_Barrier_WaterJump_WalkOffLedge //=========================================================================== // returns the distance between the two vectors // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== float VectorDistance(vec3_t v1, vec3_t v2) { vec3_t dir; VectorSubtract(v2, v1, dir); return VectorLength(dir); } //end of the function VectorDistance //=========================================================================== // returns true if the first vector is between the last two vectors // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int VectorBetweenVectors(vec3_t v, vec3_t v1, vec3_t v2) { vec3_t dir1, dir2; VectorSubtract(v, v1, dir1); VectorSubtract(v, v2, dir2); return (DotProduct(dir1, dir2) <= 0); } //end of the function VectorBetweenVectors //=========================================================================== // returns the mid point between the two vectors // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void VectorMiddle(vec3_t v1, vec3_t v2, vec3_t middle) { VectorAdd(v1, v2, middle); VectorScale(middle, 0.5, middle); } //end of the function VectorMiddle //=========================================================================== // calculate a range of points closest to each other on both edges // // Parameter: beststart1 start of the range of points on edge v1-v2 // beststart2 end of the range of points on edge v1-v2 // bestend1 start of the range of points on edge v3-v4 // bestend2 end of the range of points on edge v3-v4 // bestdist best distance so far // Returns: - // Changes Globals: - //=========================================================================== /* float AAS_ClosestEdgePoints(vec3_t v1, vec3_t v2, vec3_t v3, vec3_t v4, aas_plane_t *plane1, aas_plane_t *plane2, vec3_t beststart, vec3_t bestend, float bestdist) { vec3_t dir1, dir2, p1, p2, p3, p4; float a1, a2, b1, b2, dist; int founddist; //edge vectors VectorSubtract(v2, v1, dir1); VectorSubtract(v4, v3, dir2); //get the horizontal directions dir1[2] = 0; dir2[2] = 0; // // p1 = point on an edge vector of area2 closest to v1 // p2 = point on an edge vector of area2 closest to v2 // p3 = point on an edge vector of area1 closest to v3 // p4 = point on an edge vector of area1 closest to v4 // if (dir2[0]) { a2 = dir2[1] / dir2[0]; b2 = v3[1] - a2 * v3[0]; //point on the edge vector of area2 closest to v1 p1[0] = (DotProduct(v1, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0]; p1[1] = a2 * p1[0] + b2; //point on the edge vector of area2 closest to v2 p2[0] = (DotProduct(v2, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0]; p2[1] = a2 * p2[0] + b2; } //end if else { //point on the edge vector of area2 closest to v1 p1[0] = v3[0]; p1[1] = v1[1]; //point on the edge vector of area2 closest to v2 p2[0] = v3[0]; p2[1] = v2[1]; } //end else // if (dir1[0]) { // a1 = dir1[1] / dir1[0]; b1 = v1[1] - a1 * v1[0]; //point on the edge vector of area1 closest to v3 p3[0] = (DotProduct(v3, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0]; p3[1] = a1 * p3[0] + b1; //point on the edge vector of area1 closest to v4 p4[0] = (DotProduct(v4, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0]; p4[1] = a1 * p4[0] + b1; } //end if else { //point on the edge vector of area1 closest to v3 p3[0] = v1[0]; p3[1] = v3[1]; //point on the edge vector of area1 closest to v4 p4[0] = v1[0]; p4[1] = v4[1]; } //end else //start with zero z-coordinates p1[2] = 0; p2[2] = 0; p3[2] = 0; p4[2] = 0; //calculate the z-coordinates from the ground planes p1[2] = (plane2->dist - DotProduct(plane2->normal, p1)) / plane2->normal[2]; p2[2] = (plane2->dist - DotProduct(plane2->normal, p2)) / plane2->normal[2]; p3[2] = (plane1->dist - DotProduct(plane1->normal, p3)) / plane1->normal[2]; p4[2] = (plane1->dist - DotProduct(plane1->normal, p4)) / plane1->normal[2]; // founddist = qfalse; // if (VectorBetweenVectors(p1, v3, v4)) { dist = VectorDistance(v1, p1); if (dist > bestdist - 0.5 && dist < bestdist + 0.5) { VectorMiddle(beststart, v1, beststart); VectorMiddle(bestend, p1, bestend); } //end if else if (dist < bestdist) { bestdist = dist; VectorCopy(v1, beststart); VectorCopy(p1, bestend); } //end if founddist = qtrue; } //end if if (VectorBetweenVectors(p2, v3, v4)) { dist = VectorDistance(v2, p2); if (dist > bestdist - 0.5 && dist < bestdist + 0.5) { VectorMiddle(beststart, v2, beststart); VectorMiddle(bestend, p2, bestend); } //end if else if (dist < bestdist) { bestdist = dist; VectorCopy(v2, beststart); VectorCopy(p2, bestend); } //end if founddist = qtrue; } //end else if if (VectorBetweenVectors(p3, v1, v2)) { dist = VectorDistance(v3, p3); if (dist > bestdist - 0.5 && dist < bestdist + 0.5) { VectorMiddle(beststart, p3, beststart); VectorMiddle(bestend, v3, bestend); } //end if else if (dist < bestdist) { bestdist = dist; VectorCopy(p3, beststart); VectorCopy(v3, bestend); } //end if founddist = qtrue; } //end else if if (VectorBetweenVectors(p4, v1, v2)) { dist = VectorDistance(v4, p4); if (dist > bestdist - 0.5 && dist < bestdist + 0.5) { VectorMiddle(beststart, p4, beststart); VectorMiddle(bestend, v4, bestend); } //end if else if (dist < bestdist) { bestdist = dist; VectorCopy(p4, beststart); VectorCopy(v4, bestend); } //end if founddist = qtrue; } //end else if //if no shortest distance was found the shortest distance //is between one of the vertexes of edge1 and one of edge2 if (!founddist) { dist = VectorDistance(v1, v3); if (dist < bestdist) { bestdist = dist; VectorCopy(v1, beststart); VectorCopy(v3, bestend); } //end if dist = VectorDistance(v1, v4); if (dist < bestdist) { bestdist = dist; VectorCopy(v1, beststart); VectorCopy(v4, bestend); } //end if dist = VectorDistance(v2, v3); if (dist < bestdist) { bestdist = dist; VectorCopy(v2, beststart); VectorCopy(v3, bestend); } //end if dist = VectorDistance(v2, v4); if (dist < bestdist) { bestdist = dist; VectorCopy(v2, beststart); VectorCopy(v4, bestend); } //end if } //end if return bestdist; } //end of the function AAS_ClosestEdgePoints*/ float AAS_ClosestEdgePoints(vec3_t v1, vec3_t v2, vec3_t v3, vec3_t v4, aas_plane_t *plane1, aas_plane_t *plane2, vec3_t beststart1, vec3_t bestend1, vec3_t beststart2, vec3_t bestend2, float bestdist) { vec3_t dir1, dir2, p1, p2, p3, p4; float a1, a2, b1, b2, dist, dist1, dist2; int founddist; //edge vectors VectorSubtract(v2, v1, dir1); VectorSubtract(v4, v3, dir2); //get the horizontal directions dir1[2] = 0; dir2[2] = 0; // // p1 = point on an edge vector of area2 closest to v1 // p2 = point on an edge vector of area2 closest to v2 // p3 = point on an edge vector of area1 closest to v3 // p4 = point on an edge vector of area1 closest to v4 // if (dir2[0]) { a2 = dir2[1] / dir2[0]; b2 = v3[1] - a2 * v3[0]; //point on the edge vector of area2 closest to v1 p1[0] = (DotProduct(v1, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0]; p1[1] = a2 * p1[0] + b2; //point on the edge vector of area2 closest to v2 p2[0] = (DotProduct(v2, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0]; p2[1] = a2 * p2[0] + b2; } //end if else { //point on the edge vector of area2 closest to v1 p1[0] = v3[0]; p1[1] = v1[1]; //point on the edge vector of area2 closest to v2 p2[0] = v3[0]; p2[1] = v2[1]; } //end else // if (dir1[0]) { // a1 = dir1[1] / dir1[0]; b1 = v1[1] - a1 * v1[0]; //point on the edge vector of area1 closest to v3 p3[0] = (DotProduct(v3, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0]; p3[1] = a1 * p3[0] + b1; //point on the edge vector of area1 closest to v4 p4[0] = (DotProduct(v4, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0]; p4[1] = a1 * p4[0] + b1; } //end if else { //point on the edge vector of area1 closest to v3 p3[0] = v1[0]; p3[1] = v3[1]; //point on the edge vector of area1 closest to v4 p4[0] = v1[0]; p4[1] = v4[1]; } //end else //start with zero z-coordinates p1[2] = 0; p2[2] = 0; p3[2] = 0; p4[2] = 0; //calculate the z-coordinates from the ground planes p1[2] = (plane2->dist - DotProduct(plane2->normal, p1)) / plane2->normal[2]; p2[2] = (plane2->dist - DotProduct(plane2->normal, p2)) / plane2->normal[2]; p3[2] = (plane1->dist - DotProduct(plane1->normal, p3)) / plane1->normal[2]; p4[2] = (plane1->dist - DotProduct(plane1->normal, p4)) / plane1->normal[2]; // founddist = qfalse; // if (VectorBetweenVectors(p1, v3, v4)) { dist = VectorDistance(v1, p1); if (dist > bestdist - 0.5 && dist < bestdist + 0.5) { dist1 = VectorDistance(beststart1, v1); dist2 = VectorDistance(beststart2, v1); if (dist1 > dist2) { if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(v1, beststart2); } //end if else { if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(v1, beststart1); } //end else dist1 = VectorDistance(bestend1, p1); dist2 = VectorDistance(bestend2, p1); if (dist1 > dist2) { if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(p1, bestend2); } //end if else { if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(p1, bestend1); } //end else } //end if else if (dist < bestdist) { bestdist = dist; VectorCopy(v1, beststart1); VectorCopy(v1, beststart2); VectorCopy(p1, bestend1); VectorCopy(p1, bestend2); } //end if founddist = qtrue; } //end if if (VectorBetweenVectors(p2, v3, v4)) { dist = VectorDistance(v2, p2); if (dist > bestdist - 0.5 && dist < bestdist + 0.5) { dist1 = VectorDistance(beststart1, v2); dist2 = VectorDistance(beststart2, v2); if (dist1 > dist2) { if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(v2, beststart2); } //end if else { if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(v2, beststart1); } //end else dist1 = VectorDistance(bestend1, p2); dist2 = VectorDistance(bestend2, p2); if (dist1 > dist2) { if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(p2, bestend2); } //end if else { if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(p2, bestend1); } //end else } //end if else if (dist < bestdist) { bestdist = dist; VectorCopy(v2, beststart1); VectorCopy(v2, beststart2); VectorCopy(p2, bestend1); VectorCopy(p2, bestend2); } //end if founddist = qtrue; } //end else if if (VectorBetweenVectors(p3, v1, v2)) { dist = VectorDistance(v3, p3); if (dist > bestdist - 0.5 && dist < bestdist + 0.5) { dist1 = VectorDistance(beststart1, p3); dist2 = VectorDistance(beststart2, p3); if (dist1 > dist2) { if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(p3, beststart2); } //end if else { if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(p3, beststart1); } //end else dist1 = VectorDistance(bestend1, v3); dist2 = VectorDistance(bestend2, v3); if (dist1 > dist2) { if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(v3, bestend2); } //end if else { if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(v3, bestend1); } //end else } //end if else if (dist < bestdist) { bestdist = dist; VectorCopy(p3, beststart1); VectorCopy(p3, beststart2); VectorCopy(v3, bestend1); VectorCopy(v3, bestend2); } //end if founddist = qtrue; } //end else if if (VectorBetweenVectors(p4, v1, v2)) { dist = VectorDistance(v4, p4); if (dist > bestdist - 0.5 && dist < bestdist + 0.5) { dist1 = VectorDistance(beststart1, p4); dist2 = VectorDistance(beststart2, p4); if (dist1 > dist2) { if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(p4, beststart2); } //end if else { if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(p4, beststart1); } //end else dist1 = VectorDistance(bestend1, v4); dist2 = VectorDistance(bestend2, v4); if (dist1 > dist2) { if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(v4, bestend2); } //end if else { if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(v4, bestend1); } //end else } //end if else if (dist < bestdist) { bestdist = dist; VectorCopy(p4, beststart1); VectorCopy(p4, beststart2); VectorCopy(v4, bestend1); VectorCopy(v4, bestend2); } //end if founddist = qtrue; } //end else if //if no shortest distance was found the shortest distance //is between one of the vertexes of edge1 and one of edge2 if (!founddist) { dist = VectorDistance(v1, v3); if (dist < bestdist) { bestdist = dist; VectorCopy(v1, beststart1); VectorCopy(v1, beststart2); VectorCopy(v3, bestend1); VectorCopy(v3, bestend2); } //end if dist = VectorDistance(v1, v4); if (dist < bestdist) { bestdist = dist; VectorCopy(v1, beststart1); VectorCopy(v1, beststart2); VectorCopy(v4, bestend1); VectorCopy(v4, bestend2); } //end if dist = VectorDistance(v2, v3); if (dist < bestdist) { bestdist = dist; VectorCopy(v2, beststart1); VectorCopy(v2, beststart2); VectorCopy(v3, bestend1); VectorCopy(v3, bestend2); } //end if dist = VectorDistance(v2, v4); if (dist < bestdist) { bestdist = dist; VectorCopy(v2, beststart1); VectorCopy(v2, beststart2); VectorCopy(v4, bestend1); VectorCopy(v4, bestend2); } //end if } //end if return bestdist; } //end of the function AAS_ClosestEdgePoints //=========================================================================== // creates possible jump reachabilities between the areas // // The two closest points on the ground of the areas are calculated // One of the points will be on an edge of a ground face of area1 and // one on an edge of a ground face of area2. // If there is a range of closest points the point in the middle of this range // is selected. // Between these two points there must be one or more gaps. // If the gaps exist a potential jump is predicted. // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_Reachability_Jump(int area1num, int area2num) { int i, j, k, l, face1num, face2num, edge1num, edge2num, traveltype; int stopevent, areas[10], numareas; float phys_jumpvel, maxjumpdistance, maxjumpheight, height, bestdist, speed; vec_t *v1, *v2, *v3, *v4; vec3_t beststart, beststart2, bestend, bestend2; vec3_t teststart, testend, dir, velocity, cmdmove, up = {0, 0, 1}, sidewards; aas_area_t *area1, *area2; aas_face_t *face1, *face2; aas_edge_t *edge1, *edge2; aas_plane_t *plane1, *plane2, *plane; aas_trace_t trace; aas_clientmove_t move; aas_lreachability_t *lreach; if (!AAS_AreaGrounded(area1num) || !AAS_AreaGrounded(area2num)) return qfalse; //cannot jump from or to a crouch area if (AAS_AreaCrouch(area1num) || AAS_AreaCrouch(area2num)) return qfalse; // area1 = &aasworld.areas[area1num]; area2 = &aasworld.areas[area2num]; // phys_jumpvel = aassettings.phys_jumpvel; //maximum distance a player can jump maxjumpdistance = 2 * AAS_MaxJumpDistance(phys_jumpvel); //maximum height a player can jump with the given initial z velocity maxjumpheight = AAS_MaxJumpHeight(phys_jumpvel); //if the areas are not near anough in the x-y direction for (i = 0; i < 2; i++) { if (area1->mins[i] > area2->maxs[i] + maxjumpdistance) return qfalse; if (area1->maxs[i] < area2->mins[i] - maxjumpdistance) return qfalse; } //end for //if area2 is way to high to jump up to if (area2->mins[2] > area1->maxs[2] + maxjumpheight) return qfalse; // bestdist = 999999; // for (i = 0; i < area1->numfaces; i++) { face1num = aasworld.faceindex[area1->firstface + i]; face1 = &aasworld.faces[abs(face1num)]; //if not a ground face if (!(face1->faceflags & FACE_GROUND)) continue; // for (j = 0; j < area2->numfaces; j++) { face2num = aasworld.faceindex[area2->firstface + j]; face2 = &aasworld.faces[abs(face2num)]; //if not a ground face if (!(face2->faceflags & FACE_GROUND)) continue; // for (k = 0; k < face1->numedges; k++) { edge1num = abs(aasworld.edgeindex[face1->firstedge + k]); edge1 = &aasworld.edges[edge1num]; for (l = 0; l < face2->numedges; l++) { edge2num = abs(aasworld.edgeindex[face2->firstedge + l]); edge2 = &aasworld.edges[edge2num]; //calculate the minimum distance between the two edges v1 = aasworld.vertexes[edge1->v[0]]; v2 = aasworld.vertexes[edge1->v[1]]; v3 = aasworld.vertexes[edge2->v[0]]; v4 = aasworld.vertexes[edge2->v[1]]; //get the ground planes plane1 = &aasworld.planes[face1->planenum]; plane2 = &aasworld.planes[face2->planenum]; // bestdist = AAS_ClosestEdgePoints(v1, v2, v3, v4, plane1, plane2, beststart, bestend, beststart2, bestend2, bestdist); } //end for } //end for } //end for } //end for VectorMiddle(beststart, beststart2, beststart); VectorMiddle(bestend, bestend2, bestend); if (bestdist > 4 && bestdist < maxjumpdistance) { // Log_Write("shortest distance between %d and %d is %f\r\n", area1num, area2num, bestdist); // if very close and almost no height difference then the bot can walk if (bestdist <= 48 && fabs(beststart[2] - bestend[2]) < 8) { speed = 400; traveltype = TRAVEL_WALKOFFLEDGE; } //end if else if (AAS_HorizontalVelocityForJump(0, beststart, bestend, &speed)) { //FIXME: why multiply with 1.2??? speed *= 1.2f; traveltype = TRAVEL_WALKOFFLEDGE; } //end else if else { //get the horizontal speed for the jump, if it isn't possible to calculate this //speed (the jump is not possible) then there's no jump reachability created if (!AAS_HorizontalVelocityForJump(phys_jumpvel, beststart, bestend, &speed)) return qfalse; speed *= 1.05f; traveltype = TRAVEL_JUMP; // //NOTE: test if the horizontal distance isn't too small VectorSubtract(bestend, beststart, dir); dir[2] = 0; if (VectorLength(dir) < 10) return qfalse; } //end if // VectorSubtract(bestend, beststart, dir); VectorNormalize(dir); VectorMA(beststart, 1, dir, teststart); // VectorCopy(teststart, testend); testend[2] -= 100; trace = AAS_TraceClientBBox(teststart, testend, PRESENCE_NORMAL, -1); // if (trace.startsolid) return qfalse; if (trace.fraction < 1) { plane = &aasworld.planes[trace.planenum]; // if the bot can stand on the surface if (DotProduct(plane->normal, up) >= 0.7) { // if no lava or slime below if (!(AAS_PointContents(trace.endpos) & (CONTENTS_LAVA|CONTENTS_SLIME))) { if (teststart[2] - trace.endpos[2] <= aassettings.phys_maxbarrier) return qfalse; } //end if } //end if } //end if // VectorMA(bestend, -1, dir, teststart); // VectorCopy(teststart, testend); testend[2] -= 100; trace = AAS_TraceClientBBox(teststart, testend, PRESENCE_NORMAL, -1); // if (trace.startsolid) return qfalse; if (trace.fraction < 1) { plane = &aasworld.planes[trace.planenum]; // if the bot can stand on the surface if (DotProduct(plane->normal, up) >= 0.7) { // if no lava or slime below if (!(AAS_PointContents(trace.endpos) & (CONTENTS_LAVA|CONTENTS_SLIME))) { if (teststart[2] - trace.endpos[2] <= aassettings.phys_maxbarrier) return qfalse; } //end if } //end if } //end if // // get command movement VectorClear(cmdmove); if ((traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP) cmdmove[2] = aassettings.phys_jumpvel; else cmdmove[2] = 0; // VectorSubtract(bestend, beststart, dir); dir[2] = 0; VectorNormalize(dir); CrossProduct(dir, up, sidewards); // stopevent = SE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE; if (!AAS_AreaClusterPortal(area1num) && !AAS_AreaClusterPortal(area2num)) stopevent |= SE_TOUCHCLUSTERPORTAL; // for (i = 0; i < 3; i++) { // if (i == 1) VectorAdd(testend, sidewards, testend); else if (i == 2) VectorSubtract(bestend, sidewards, testend); else VectorCopy(bestend, testend); VectorSubtract(testend, beststart, dir); dir[2] = 0; VectorNormalize(dir); VectorScale(dir, speed, velocity); // AAS_PredictClientMovement(&move, -1, beststart, PRESENCE_NORMAL, qtrue, velocity, cmdmove, 3, 30, 0.1f, stopevent, 0, qfalse); // if prediction time wasn't enough to fully predict the movement if (move.frames >= 30) return qfalse; // don't enter slime or lava and don't fall from too high if (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA)) return qfalse; // never jump or fall through a cluster portal if (move.stopevent & SE_TOUCHCLUSTERPORTAL) return qfalse; //the end position should be in area2, also test a little bit back //because the predicted jump could have rushed through the area VectorMA(move.endpos, -64, dir, teststart); teststart[2] += 1; numareas = AAS_TraceAreas(move.endpos, teststart, areas, NULL, sizeof(areas) / sizeof(int)); for (j = 0; j < numareas; j++) { if (areas[j] == area2num) break; } //end for if (j < numareas) break; } if (i >= 3) return qfalse; // #ifdef REACH_DEBUG //create the reachability Log_Write("jump reachability between %d and %d\r\n", area1num, area2num); #endif //REACH_DEBUG //create a new reachability link lreach = AAS_AllocReachability(); if (!lreach) return qfalse; lreach->areanum = area2num; lreach->facenum = 0; lreach->edgenum = 0; VectorCopy(beststart, lreach->start); VectorCopy(bestend, lreach->end); lreach->traveltype = traveltype; VectorSubtract(bestend, beststart, dir); height = dir[2]; dir[2] = 0; if ((traveltype & TRAVELTYPE_MASK) == TRAVEL_WALKOFFLEDGE && height > VectorLength(dir)) { lreach->traveltime = aassettings.rs_startwalkoffledge + height * 50 / aassettings.phys_gravity; } else { lreach->traveltime = aassettings.rs_startjump + VectorDistance(bestend, beststart) * 240 / aassettings.phys_maxwalkvelocity; } //end if // if (!AAS_AreaJumpPad(area2num)) { if (AAS_FallDelta(beststart[2] - bestend[2]) > aassettings.phys_falldelta5) { lreach->traveltime += aassettings.rs_falldamage5; } //end if else if (AAS_FallDelta(beststart[2] - bestend[2]) > aassettings.phys_falldelta10) { lreach->traveltime += aassettings.rs_falldamage10; } //end if } //end if lreach->next = areareachability[area1num]; areareachability[area1num] = lreach; // if ((traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP) reach_jump++; else reach_walkoffledge++; } //end if return qfalse; } //end of the function AAS_Reachability_Jump //=========================================================================== // create a possible ladder reachability from area1 to area2 // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_Reachability_Ladder(int area1num, int area2num) { int i, j, k, l, edge1num, edge2num, sharededgenum, lowestedgenum; int face1num, face2num, ladderface1num, ladderface2num; int ladderface1vertical, ladderface2vertical, firstv; float face1area, face2area, bestface1area, bestface2area; float phys_jumpvel, maxjumpheight; vec3_t area1point, area2point, v1, v2, up = {0, 0, 1}; vec3_t mid, lowestpoint, start, end, sharededgevec, dir; aas_area_t *area1, *area2; aas_face_t *face1, *face2, *ladderface1, *ladderface2; aas_plane_t *plane1, *plane2; aas_edge_t *sharededge, *edge1; aas_lreachability_t *lreach; aas_trace_t trace; if (!AAS_AreaLadder(area1num) || !AAS_AreaLadder(area2num)) return qfalse; // phys_jumpvel = aassettings.phys_jumpvel; //maximum height a player can jump with the given initial z velocity maxjumpheight = AAS_MaxJumpHeight(phys_jumpvel); area1 = &aasworld.areas[area1num]; area2 = &aasworld.areas[area2num]; // ladderface1 = NULL; ladderface2 = NULL; ladderface1num = 0; //make compiler happy ladderface2num = 0; //make compiler happy bestface1area = -9999; bestface2area = -9999; sharededgenum = 0; //make compiler happy lowestedgenum = 0; //make compiler happy // for (i = 0; i < area1->numfaces; i++) { face1num = aasworld.faceindex[area1->firstface + i]; face1 = &aasworld.faces[abs(face1num)]; //if not a ladder face if (!(face1->faceflags & FACE_LADDER)) continue; // for (j = 0; j < area2->numfaces; j++) { face2num = aasworld.faceindex[area2->firstface + j]; face2 = &aasworld.faces[abs(face2num)]; //if not a ladder face if (!(face2->faceflags & FACE_LADDER)) continue; //check if the faces share an edge for (k = 0; k < face1->numedges; k++) { edge1num = aasworld.edgeindex[face1->firstedge + k]; for (l = 0; l < face2->numedges; l++) { edge2num = aasworld.edgeindex[face2->firstedge + l]; if (abs(edge1num) == abs(edge2num)) { //get the face with the largest area face1area = AAS_FaceArea(face1); face2area = AAS_FaceArea(face2); if (face1area > bestface1area && face2area > bestface2area) { bestface1area = face1area; bestface2area = face2area; ladderface1 = face1; ladderface2 = face2; ladderface1num = face1num; ladderface2num = face2num; sharededgenum = edge1num; } //end if break; } //end if } //end for if (l != face2->numedges) break; } //end for } //end for } //end for // if (ladderface1 && ladderface2) { //get the middle of the shared edge sharededge = &aasworld.edges[abs(sharededgenum)]; firstv = sharededgenum < 0; // VectorCopy(aasworld.vertexes[sharededge->v[firstv]], v1); VectorCopy(aasworld.vertexes[sharededge->v[!firstv]], v2); VectorAdd(v1, v2, area1point); VectorScale(area1point, 0.5, area1point); VectorCopy(area1point, area2point); // //if the face plane in area 1 is pretty much vertical plane1 = &aasworld.planes[ladderface1->planenum ^ (ladderface1num < 0)]; plane2 = &aasworld.planes[ladderface2->planenum ^ (ladderface2num < 0)]; // //get the points really into the areas VectorSubtract(v2, v1, sharededgevec); CrossProduct(plane1->normal, sharededgevec, dir); VectorNormalize(dir); //NOTE: 32 because that's larger than 16 (bot bbox x,y) VectorMA(area1point, -32, dir, area1point); VectorMA(area2point, 32, dir, area2point); // ladderface1vertical = fabs(DotProduct(plane1->normal, up)) < 0.1; ladderface2vertical = fabs(DotProduct(plane2->normal, up)) < 0.1; //there's only reachability between vertical ladder faces if (!ladderface1vertical && !ladderface2vertical) return qfalse; //if both vertical ladder faces if (ladderface1vertical && ladderface2vertical //and the ladder faces do not make a sharp corner && DotProduct(plane1->normal, plane2->normal) > 0.7 //and the shared edge is not too vertical && fabs(DotProduct(sharededgevec, up)) < 0.7) { //create a new reachability link lreach = AAS_AllocReachability(); if (!lreach) return qfalse; lreach->areanum = area2num; lreach->facenum = ladderface1num; lreach->edgenum = abs(sharededgenum); VectorCopy(area1point, lreach->start); //VectorCopy(area2point, lreach->end); VectorMA(area2point, -3, plane1->normal, lreach->end); lreach->traveltype = TRAVEL_LADDER; lreach->traveltime = 10; lreach->next = areareachability[area1num]; areareachability[area1num] = lreach; // reach_ladder++; //create a new reachability link lreach = AAS_AllocReachability(); if (!lreach) return qfalse; lreach->areanum = area1num; lreach->facenum = ladderface2num; lreach->edgenum = abs(sharededgenum); VectorCopy(area2point, lreach->start); //VectorCopy(area1point, lreach->end); VectorMA(area1point, -3, plane1->normal, lreach->end); lreach->traveltype = TRAVEL_LADDER; lreach->traveltime = 10; lreach->next = areareachability[area2num]; areareachability[area2num] = lreach; // reach_ladder++; // return qtrue; } //end if //if the second ladder face is also a ground face //create ladder end (just ladder) reachability and //walk off a ladder (ledge) reachability if (ladderface1vertical && (ladderface2->faceflags & FACE_GROUND)) { //create a new reachability link lreach = AAS_AllocReachability(); if (!lreach) return qfalse; lreach->areanum = area2num; lreach->facenum = ladderface1num; lreach->edgenum = abs(sharededgenum); VectorCopy(area1point, lreach->start); VectorCopy(area2point, lreach->end); lreach->end[2] += 16; VectorMA(lreach->end, -15, plane1->normal, lreach->end); lreach->traveltype = TRAVEL_LADDER; lreach->traveltime = 10; lreach->next = areareachability[area1num]; areareachability[area1num] = lreach; // reach_ladder++; //create a new reachability link lreach = AAS_AllocReachability(); if (!lreach) return qfalse; lreach->areanum = area1num; lreach->facenum = ladderface2num; lreach->edgenum = abs(sharededgenum); VectorCopy(area2point, lreach->start); VectorCopy(area1point, lreach->end); lreach->traveltype = TRAVEL_WALKOFFLEDGE; lreach->traveltime = 10; lreach->next = areareachability[area2num]; areareachability[area2num] = lreach; // reach_walkoffledge++; // return qtrue; } //end if // if (ladderface1vertical) { //find lowest edge of the ladder face lowestpoint[2] = 99999; for (i = 0; i < ladderface1->numedges; i++) { edge1num = abs(aasworld.edgeindex[ladderface1->firstedge + i]); edge1 = &aasworld.edges[edge1num]; // VectorCopy(aasworld.vertexes[edge1->v[0]], v1); VectorCopy(aasworld.vertexes[edge1->v[1]], v2); // VectorAdd(v1, v2, mid); VectorScale(mid, 0.5, mid); // if (mid[2] < lowestpoint[2]) { VectorCopy(mid, lowestpoint); lowestedgenum = edge1num; } //end if } //end for // plane1 = &aasworld.planes[ladderface1->planenum]; //trace down in the middle of this edge VectorMA(lowestpoint, 5, plane1->normal, start); VectorCopy(start, end); start[2] += 5; end[2] -= 100; //trace without entity collision trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1); // // #ifdef REACH_DEBUG if (trace.startsolid) { Log_Write("trace from area %d started in solid\r\n", area1num); } //end if #endif //REACH_DEBUG // trace.endpos[2] += 1; area2num = AAS_PointAreaNum(trace.endpos); // area2 = &aasworld.areas[area2num]; for (i = 0; i < area2->numfaces; i++) { face2num = aasworld.faceindex[area2->firstface + i]; face2 = &aasworld.faces[abs(face2num)]; // if (face2->faceflags & FACE_LADDER) { plane2 = &aasworld.planes[face2->planenum]; if (fabs(DotProduct(plane2->normal, up)) < 0.1) break; } //end if } //end for //if from another area without vertical ladder faces if (i >= area2->numfaces && area2num != area1num && //the reachabilities shouldn't exist already !AAS_ReachabilityExists(area1num, area2num) && !AAS_ReachabilityExists(area2num, area1num)) { //if the height is jumpable if (start[2] - trace.endpos[2] < maxjumpheight) { //create a new reachability link lreach = AAS_AllocReachability(); if (!lreach) return qfalse; lreach->areanum = area2num; lreach->facenum = ladderface1num; lreach->edgenum = lowestedgenum; VectorCopy(lowestpoint, lreach->start); VectorCopy(trace.endpos, lreach->end); lreach->traveltype = TRAVEL_LADDER; lreach->traveltime = 10; lreach->next = areareachability[area1num]; areareachability[area1num] = lreach; // reach_ladder++; //create a new reachability link lreach = AAS_AllocReachability(); if (!lreach) return qfalse; lreach->areanum = area1num; lreach->facenum = ladderface1num; lreach->edgenum = lowestedgenum; VectorCopy(trace.endpos, lreach->start); //get the end point a little bit into the ladder VectorMA(lowestpoint, -5, plane1->normal, lreach->end); //get the end point a little higher lreach->end[2] += 10; lreach->traveltype = TRAVEL_JUMP; lreach->traveltime = 10; lreach->next = areareachability[area2num]; areareachability[area2num] = lreach; // reach_jump++; // return qtrue; #ifdef REACH_DEBUG Log_Write("jump up to ladder reach between %d and %d\r\n", area2num, area1num); #endif //REACH_DEBUG } //end if #ifdef REACH_DEBUG else Log_Write("jump too high between area %d and %d\r\n", area2num, area1num); #endif //REACH_DEBUG } //end if /*//if slime or lava below the ladder //try jump reachability from far towards the ladder if (aasworld.areasettings[area2num].contents & (AREACONTENTS_SLIME | AREACONTENTS_LAVA)) { for (i = 20; i <= 120; i += 20) { //trace down in the middle of this edge VectorMA(lowestpoint, i, plane1->normal, start); VectorCopy(start, end); start[2] += 5; end[2] -= 100; //trace without entity collision trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1); // if (trace.startsolid) break; trace.endpos[2] += 1; area2num = AAS_PointAreaNum(trace.endpos); if (area2num == area1num) continue; // if (start[2] - trace.endpos[2] > maxjumpheight) continue; if (aasworld.areasettings[area2num].contents & (AREACONTENTS_SLIME | AREACONTENTS_LAVA)) continue; // //create a new reachability link lreach = AAS_AllocReachability(); if (!lreach) return qfalse; lreach->areanum = area1num; lreach->facenum = ladderface1num; lreach->edgenum = lowestedgenum; VectorCopy(trace.endpos, lreach->start); VectorCopy(lowestpoint, lreach->end); lreach->end[2] += 5; lreach->traveltype = TRAVEL_JUMP; lreach->traveltime = 10; lreach->next = areareachability[area2num]; areareachability[area2num] = lreach; // reach_jump++; // Log_Write("jump far to ladder reach between %d and %d\r\n", area2num, area1num); // break; } //end for } //end if*/ } //end if } //end if return qfalse; } //end of the function AAS_Reachability_Ladder //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_TravelFlagsForTeam(int ent) { int notteam; if (!AAS_IntForBSPEpairKey(ent, "bot_notteam", ¬team)) return 0; if (notteam == 1) return TRAVELFLAG_NOTTEAM1; if (notteam == 2) return TRAVELFLAG_NOTTEAM2; return 0; } //end of the function AAS_TravelFlagsForTeam //=========================================================================== // create possible teleporter reachabilities // this is very game dependent.... :( // // classname = trigger_multiple or trigger_teleport // target = "t1" // // classname = target_teleporter // targetname = "t1" // target = "t2" // // classname = misc_teleporter_dest // targetname = "t2" // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_Reachability_Teleport(void) { int area1num, area2num; char target[MAX_EPAIRKEY], targetname[MAX_EPAIRKEY]; char classname[MAX_EPAIRKEY], model[MAX_EPAIRKEY]; int ent, dest; float angle; vec3_t origin, destorigin, mins, maxs, end, angles; vec3_t mid, velocity, cmdmove; aas_lreachability_t *lreach; aas_clientmove_t move; aas_trace_t trace; aas_link_t *areas, *link; for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) { if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; if (!strcmp(classname, "trigger_multiple")) { AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY); //#ifdef REACH_DEBUG botimport.Print(PRT_MESSAGE, "trigger_multiple model = \"%s\"\n", model); //#endif REACH_DEBUG VectorClear(angles); AAS_BSPModelMinsMaxsOrigin(atoi(model+1), angles, mins, maxs, origin); // if (!AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY)) { botimport.Print(PRT_ERROR, "trigger_multiple at %1.0f %1.0f %1.0f without target\n", origin[0], origin[1], origin[2]); continue; } //end if for (dest = AAS_NextBSPEntity(0); dest; dest = AAS_NextBSPEntity(dest)) { if (!AAS_ValueForBSPEpairKey(dest, "classname", classname, MAX_EPAIRKEY)) continue; if (!strcmp(classname, "target_teleporter")) { if (!AAS_ValueForBSPEpairKey(dest, "targetname", targetname, MAX_EPAIRKEY)) continue; if (!strcmp(targetname, target)) { break; } //end if } //end if } //end for if (!dest) { continue; } //end if if (!AAS_ValueForBSPEpairKey(dest, "target", target, MAX_EPAIRKEY)) { botimport.Print(PRT_ERROR, "target_teleporter without target\n"); continue; } //end if } //end else else if (!strcmp(classname, "trigger_teleport")) { AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY); //#ifdef REACH_DEBUG botimport.Print(PRT_MESSAGE, "trigger_teleport model = \"%s\"\n", model); //#endif REACH_DEBUG VectorClear(angles); AAS_BSPModelMinsMaxsOrigin(atoi(model+1), angles, mins, maxs, origin); // if (!AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY)) { botimport.Print(PRT_ERROR, "trigger_teleport at %1.0f %1.0f %1.0f without target\n", origin[0], origin[1], origin[2]); continue; } //end if } //end if else { continue; } //end else // for (dest = AAS_NextBSPEntity(0); dest; dest = AAS_NextBSPEntity(dest)) { //classname should be misc_teleporter_dest //but I've also seen target_position and actually any //entity could be used... burp if (AAS_ValueForBSPEpairKey(dest, "targetname", targetname, MAX_EPAIRKEY)) { if (!strcmp(targetname, target)) { break; } //end if } //end if } //end for if (!dest) { botimport.Print(PRT_ERROR, "teleporter without misc_teleporter_dest (%s)\n", target); continue; } //end if if (!AAS_VectorForBSPEpairKey(dest, "origin", destorigin)) { botimport.Print(PRT_ERROR, "teleporter destination (%s) without origin\n", target); continue; } //end if // area2num = AAS_PointAreaNum(destorigin); //if not teleported into a teleporter or into a jumppad if (!AAS_AreaTeleporter(area2num) && !AAS_AreaJumpPad(area2num)) { VectorCopy(destorigin, end); end[2] -= 64; trace = AAS_TraceClientBBox(destorigin, end, PRESENCE_CROUCH, -1); if (trace.startsolid) { botimport.Print(PRT_ERROR, "teleporter destination (%s) in solid\n", target); continue; } //end if area2num = AAS_PointAreaNum(trace.endpos); // /* if (!AAS_AreaTeleporter(area2num) && !AAS_AreaJumpPad(area2num) && !AAS_AreaGrounded(area2num)) { VectorCopy(trace.endpos, destorigin); } else*/ { //predict where you'll end up AAS_FloatForBSPEpairKey(dest, "angle", &angle); if (angle) { VectorSet(angles, 0, angle, 0); AngleVectors(angles, velocity, NULL, NULL); VectorScale(velocity, 400, velocity); } //end if else { VectorClear(velocity); } //end else VectorClear(cmdmove); AAS_PredictClientMovement(&move, -1, destorigin, PRESENCE_NORMAL, qfalse, velocity, cmdmove, 0, 30, 0.1f, SE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME| SE_ENTERLAVA|SE_HITGROUNDDAMAGE|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER, 0, qfalse); //qtrue); area2num = AAS_PointAreaNum(move.endpos); if (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA)) { botimport.Print(PRT_WARNING, "teleported into slime or lava at dest %s\n", target); } //end if VectorCopy(move.endpos, destorigin); } //end else } //end if // //botimport.Print(PRT_MESSAGE, "teleporter brush origin at %f %f %f\n", origin[0], origin[1], origin[2]); //botimport.Print(PRT_MESSAGE, "teleporter brush mins = %f %f %f\n", mins[0], mins[1], mins[2]); //botimport.Print(PRT_MESSAGE, "teleporter brush maxs = %f %f %f\n", maxs[0], maxs[1], maxs[2]); VectorAdd(origin, mins, mins); VectorAdd(origin, maxs, maxs); // VectorAdd(mins, maxs, mid); VectorScale(mid, 0.5, mid); //link an invalid (-1) entity areas = AAS_LinkEntityClientBBox(mins, maxs, -1, PRESENCE_CROUCH); if (!areas) botimport.Print(PRT_MESSAGE, "trigger_multiple not in any area\n"); // for (link = areas; link; link = link->next_area) { //if (!AAS_AreaGrounded(link->areanum)) continue; if (!AAS_AreaTeleporter(link->areanum)) continue; // area1num = link->areanum; //create a new reachability link lreach = AAS_AllocReachability(); if (!lreach) break; lreach->areanum = area2num; lreach->facenum = 0; lreach->edgenum = 0; VectorCopy(mid, lreach->start); VectorCopy(destorigin, lreach->end); lreach->traveltype = TRAVEL_TELEPORT; lreach->traveltype |= AAS_TravelFlagsForTeam(ent); lreach->traveltime = aassettings.rs_teleport; lreach->next = areareachability[area1num]; areareachability[area1num] = lreach; // reach_teleport++; } //end for //unlink the invalid entity AAS_UnlinkFromAreas(areas); } //end for } //end of the function AAS_Reachability_Teleport //=========================================================================== // create possible elevator (func_plat) reachabilities // this is very game dependent.... :( // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_Reachability_Elevator(void) { int area1num, area2num, modelnum, i, j, k, l, n, p; float lip, height, speed; char model[MAX_EPAIRKEY], classname[MAX_EPAIRKEY]; int ent; vec3_t mins, maxs, origin, angles = {0, 0, 0}; vec3_t pos1, pos2, mids, platbottom, plattop; vec3_t bottomorg, toporg, start, end, dir; vec_t xvals[8], yvals[8], xvals_top[8], yvals_top[8]; aas_lreachability_t *lreach; aas_trace_t trace; #ifdef REACH_DEBUG Log_Write("AAS_Reachability_Elevator\r\n"); #endif //REACH_DEBUG for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) { if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; if (!strcmp(classname, "func_plat")) { #ifdef REACH_DEBUG Log_Write("found func plat\r\n"); #endif //REACH_DEBUG if (!AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY)) { botimport.Print(PRT_ERROR, "func_plat without model\n"); continue; } //end if //get the model number, and skip the leading * modelnum = atoi(model+1); if (modelnum <= 0) { botimport.Print(PRT_ERROR, "func_plat with invalid model number\n"); continue; } //end if //get the mins, maxs and origin of the model //NOTE: the origin is usually (0,0,0) and the mins and maxs // are the absolute mins and maxs AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin); // AAS_VectorForBSPEpairKey(ent, "origin", origin); //pos1 is the top position, pos2 is the bottom VectorCopy(origin, pos1); VectorCopy(origin, pos2); //get the lip of the plat AAS_FloatForBSPEpairKey(ent, "lip", &lip); if (!lip) lip = 8; //get the movement height of the plat AAS_FloatForBSPEpairKey(ent, "height", &height); if (!height) height = (maxs[2] - mins[2]) - lip; //get the speed of the plat AAS_FloatForBSPEpairKey(ent, "speed", &speed); if (!speed) speed = 200; //get bottom position below pos1 pos2[2] -= height; // //get a point just above the plat in the bottom position VectorAdd(mins, maxs, mids); VectorMA(pos2, 0.5, mids, platbottom); platbottom[2] = maxs[2] - (pos1[2] - pos2[2]) + 2; //get a point just above the plat in the top position VectorAdd(mins, maxs, mids); VectorMA(pos2, 0.5, mids, plattop); plattop[2] = maxs[2] + 2; // /*if (!area1num) { Log_Write("no grounded area near plat bottom\r\n"); continue; } //end if*/ //get the mins and maxs a little larger for (i = 0; i < 3; i++) { mins[i] -= 1; maxs[i] += 1; } //end for // //botimport.Print(PRT_MESSAGE, "platbottom[2] = %1.1f plattop[2] = %1.1f\n", platbottom[2], plattop[2]); // VectorAdd(mins, maxs, mids); VectorScale(mids, 0.5, mids); // xvals[0] = mins[0]; xvals[1] = mids[0]; xvals[2] = maxs[0]; xvals[3] = mids[0]; yvals[0] = mids[1]; yvals[1] = maxs[1]; yvals[2] = mids[1]; yvals[3] = mins[1]; // xvals[4] = mins[0]; xvals[5] = maxs[0]; xvals[6] = maxs[0]; xvals[7] = mins[0]; yvals[4] = maxs[1]; yvals[5] = maxs[1]; yvals[6] = mins[1]; yvals[7] = mins[1]; //find adjacent areas around the bottom of the plat for (i = 0; i < 9; i++) { if (i < 8) //check at the sides of the plat { bottomorg[0] = origin[0] + xvals[i]; bottomorg[1] = origin[1] + yvals[i]; bottomorg[2] = platbottom[2] + 16; //get a grounded or swim area near the plat in the bottom position area1num = AAS_PointAreaNum(bottomorg); for (k = 0; k < 16; k++) { if (area1num) { if (AAS_AreaGrounded(area1num) || AAS_AreaSwim(area1num)) break; } //end if bottomorg[2] += 4; area1num = AAS_PointAreaNum(bottomorg); } //end if //if in solid if (k >= 16) { continue; } //end if } //end if else //at the middle of the plat { VectorCopy(plattop, bottomorg); bottomorg[2] += 24; area1num = AAS_PointAreaNum(bottomorg); if (!area1num) continue; VectorCopy(platbottom, bottomorg); bottomorg[2] += 24; } //end else //look at adjacent areas around the top of the plat //make larger steps to outside the plat everytime for (n = 0; n < 3; n++) { for (k = 0; k < 3; k++) { mins[k] -= 4; maxs[k] += 4; } //end for xvals_top[0] = mins[0]; xvals_top[1] = mids[0]; xvals_top[2] = maxs[0]; xvals_top[3] = mids[0]; yvals_top[0] = mids[1]; yvals_top[1] = maxs[1]; yvals_top[2] = mids[1]; yvals_top[3] = mins[1]; // xvals_top[4] = mins[0]; xvals_top[5] = maxs[0]; xvals_top[6] = maxs[0]; xvals_top[7] = mins[0]; yvals_top[4] = maxs[1]; yvals_top[5] = maxs[1]; yvals_top[6] = mins[1]; yvals_top[7] = mins[1]; // for (j = 0; j < 8; j++) { toporg[0] = origin[0] + xvals_top[j]; toporg[1] = origin[1] + yvals_top[j]; toporg[2] = plattop[2] + 16; //get a grounded or swim area near the plat in the top position area2num = AAS_PointAreaNum(toporg); for (l = 0; l < 16; l++) { if (area2num) { if (AAS_AreaGrounded(area2num) || AAS_AreaSwim(area2num)) { VectorCopy(plattop, start); start[2] += 32; VectorCopy(toporg, end); end[2] += 1; trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1); if (trace.fraction >= 1) break; } //end if } //end if toporg[2] += 4; area2num = AAS_PointAreaNum(toporg); } //end if //if in solid if (l >= 16) continue; //never create a reachability in the same area if (area2num == area1num) continue; //if the area isn't grounded if (!AAS_AreaGrounded(area2num)) continue; //if there already exists reachability between the areas if (AAS_ReachabilityExists(area1num, area2num)) continue; //if the reachability start is within the elevator bounding box VectorSubtract(bottomorg, platbottom, dir); VectorNormalize(dir); dir[0] = bottomorg[0] + 24 * dir[0]; dir[1] = bottomorg[1] + 24 * dir[1]; dir[2] = bottomorg[2]; // for (p = 0; p < 3; p++) if (dir[p] < origin[p] + mins[p] || dir[p] > origin[p] + maxs[p]) break; if (p >= 3) continue; //create a new reachability link lreach = AAS_AllocReachability(); if (!lreach) continue; lreach->areanum = area2num; //the facenum is the model number lreach->facenum = modelnum; //the edgenum is the height lreach->edgenum = (int) height; // VectorCopy(dir, lreach->start); VectorCopy(toporg, lreach->end); lreach->traveltype = TRAVEL_ELEVATOR; lreach->traveltype |= AAS_TravelFlagsForTeam(ent); lreach->traveltime = aassettings.rs_startelevator + height * 100 / speed; lreach->next = areareachability[area1num]; areareachability[area1num] = lreach; //don't go any further to the outside n = 9999; // #ifdef REACH_DEBUG Log_Write("elevator reach from %d to %d\r\n", area1num, area2num); #endif //REACH_DEBUG // reach_elevator++; } //end for } //end for } //end for } //end if } //end for } //end of the function AAS_Reachability_Elevator //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== aas_lreachability_t *AAS_FindFaceReachabilities(vec3_t *facepoints, int numpoints, aas_plane_t *plane, int towardsface) { int i, j, k, l; int facenum, edgenum, bestfacenum; float *v1, *v2, *v3, *v4; float bestdist, speed, hordist, dist; vec3_t beststart, beststart2, bestend, bestend2, tmp, hordir, testpoint; aas_lreachability_t *lreach, *lreachabilities; aas_area_t *area; aas_face_t *face; aas_edge_t *edge; aas_plane_t *faceplane, *bestfaceplane; // lreachabilities = NULL; bestfacenum = 0; bestfaceplane = NULL; // for (i = 1; i < aasworld.numareas; i++) { area = &aasworld.areas[i]; // get the shortest distance between one of the func_bob start edges and // one of the face edges of area1 bestdist = 999999; for (j = 0; j < area->numfaces; j++) { facenum = aasworld.faceindex[area->firstface + j]; face = &aasworld.faces[abs(facenum)]; //if not a ground face if (!(face->faceflags & FACE_GROUND)) continue; //get the ground planes faceplane = &aasworld.planes[face->planenum]; // for (k = 0; k < face->numedges; k++) { edgenum = abs(aasworld.edgeindex[face->firstedge + k]); edge = &aasworld.edges[edgenum]; //calculate the minimum distance between the two edges v1 = aasworld.vertexes[edge->v[0]]; v2 = aasworld.vertexes[edge->v[1]]; // for (l = 0; l < numpoints; l++) { v3 = facepoints[l]; v4 = facepoints[(l+1) % numpoints]; dist = AAS_ClosestEdgePoints(v1, v2, v3, v4, faceplane, plane, beststart, bestend, beststart2, bestend2, bestdist); if (dist < bestdist) { bestfacenum = facenum; bestfaceplane = faceplane; bestdist = dist; } //end if } //end for } //end for } //end for // if (bestdist > 192) continue; // VectorMiddle(beststart, beststart2, beststart); VectorMiddle(bestend, bestend2, bestend); // if (!towardsface) { VectorCopy(beststart, tmp); VectorCopy(bestend, beststart); VectorCopy(tmp, bestend); } //end if // VectorSubtract(bestend, beststart, hordir); hordir[2] = 0; hordist = VectorLength(hordir); // if (hordist > 2 * AAS_MaxJumpDistance(aassettings.phys_jumpvel)) continue; //the end point should not be significantly higher than the start point if (bestend[2] - 32 > beststart[2]) continue; //don't fall down too far if (bestend[2] < beststart[2] - 128) continue; //the distance should not be too far if (hordist > 32) { //check for walk off ledge if (!AAS_HorizontalVelocityForJump(0, beststart, bestend, &speed)) continue; } //end if // beststart[2] += 1; bestend[2] += 1; // if (towardsface) VectorCopy(bestend, testpoint); else VectorCopy(beststart, testpoint); testpoint[2] = 0; testpoint[2] = (bestfaceplane->dist - DotProduct(bestfaceplane->normal, testpoint)) / bestfaceplane->normal[2]; // if (!AAS_PointInsideFace(bestfacenum, testpoint, 0.1f)) { //if the faces are not overlapping then only go down if (bestend[2] - 16 > beststart[2]) continue; } //end if lreach = AAS_AllocReachability(); if (!lreach) return lreachabilities; lreach->areanum = i; lreach->facenum = 0; lreach->edgenum = 0; VectorCopy(beststart, lreach->start); VectorCopy(bestend, lreach->end); lreach->traveltype = 0; lreach->traveltime = 0; lreach->next = lreachabilities; lreachabilities = lreach; #ifndef BSPC if (towardsface) AAS_PermanentLine(lreach->start, lreach->end, 1); else AAS_PermanentLine(lreach->start, lreach->end, 2); #endif } //end for return lreachabilities; } //end of the function AAS_FindFaceReachabilities //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_Reachability_FuncBobbing(void) { int ent, spawnflags, modelnum, axis; int i, numareas, areas[10]; char classname[MAX_EPAIRKEY], model[MAX_EPAIRKEY]; vec3_t origin, move_end, move_start, move_start_top, move_end_top; vec3_t mins, maxs, angles = {0, 0, 0}; vec3_t start_edgeverts[4], end_edgeverts[4], mid; vec3_t org, start, end, dir, points[10]; float height; aas_plane_t start_plane, end_plane; aas_lreachability_t *startreach, *endreach, *nextstartreach, *nextendreach, *lreach; aas_lreachability_t *firststartreach, *firstendreach; for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) { if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; if (strcmp(classname, "func_bobbing")) continue; AAS_FloatForBSPEpairKey(ent, "height", &height); if (!height) height = 32; // if (!AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY)) { botimport.Print(PRT_ERROR, "func_bobbing without model\n"); continue; } //end if //get the model number, and skip the leading * modelnum = atoi(model+1); if (modelnum <= 0) { botimport.Print(PRT_ERROR, "func_bobbing with invalid model number\n"); continue; } //end if //if the entity has an origin set then use it if (!AAS_VectorForBSPEpairKey(ent, "origin", origin)) VectorSet(origin, 0, 0, 0); // AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, NULL); // VectorAdd(mins, origin, mins); VectorAdd(maxs, origin, maxs); // VectorAdd(mins, maxs, mid); VectorScale(mid, 0.5, mid); VectorCopy(mid, origin); // VectorCopy(origin, move_end); VectorCopy(origin, move_start); // AAS_IntForBSPEpairKey(ent, "spawnflags", &spawnflags); // set the axis of bobbing if (spawnflags & 1) axis = 0; else if (spawnflags & 2) axis = 1; else axis = 2; // move_start[axis] -= height; move_end[axis] += height; // Log_Write("funcbob model %d, start = {%1.1f, %1.1f, %1.1f} end = {%1.1f, %1.1f, %1.1f}\n", modelnum, move_start[0], move_start[1], move_start[2], move_end[0], move_end[1], move_end[2]); // #ifndef BSPC /* AAS_DrawPermanentCross(move_start, 4, 1); AAS_DrawPermanentCross(move_end, 4, 2); */ #endif // for (i = 0; i < 4; i++) { VectorCopy(move_start, start_edgeverts[i]); start_edgeverts[i][2] += maxs[2] - mid[2]; //+ bbox maxs z start_edgeverts[i][2] += 24; //+ player origin to ground dist } //end for start_edgeverts[0][0] += maxs[0] - mid[0]; start_edgeverts[0][1] += maxs[1] - mid[1]; start_edgeverts[1][0] += maxs[0] - mid[0]; start_edgeverts[1][1] += mins[1] - mid[1]; start_edgeverts[2][0] += mins[0] - mid[0]; start_edgeverts[2][1] += mins[1] - mid[1]; start_edgeverts[3][0] += mins[0] - mid[0]; start_edgeverts[3][1] += maxs[1] - mid[1]; // start_plane.dist = start_edgeverts[0][2]; VectorSet(start_plane.normal, 0, 0, 1); // for (i = 0; i < 4; i++) { VectorCopy(move_end, end_edgeverts[i]); end_edgeverts[i][2] += maxs[2] - mid[2]; //+ bbox maxs z end_edgeverts[i][2] += 24; //+ player origin to ground dist } //end for end_edgeverts[0][0] += maxs[0] - mid[0]; end_edgeverts[0][1] += maxs[1] - mid[1]; end_edgeverts[1][0] += maxs[0] - mid[0]; end_edgeverts[1][1] += mins[1] - mid[1]; end_edgeverts[2][0] += mins[0] - mid[0]; end_edgeverts[2][1] += mins[1] - mid[1]; end_edgeverts[3][0] += mins[0] - mid[0]; end_edgeverts[3][1] += maxs[1] - mid[1]; // end_plane.dist = end_edgeverts[0][2]; VectorSet(end_plane.normal, 0, 0, 1); // #ifndef BSPC #if 0 for (i = 0; i < 4; i++) { AAS_PermanentLine(start_edgeverts[i], start_edgeverts[(i+1)%4], 1); AAS_PermanentLine(end_edgeverts[i], end_edgeverts[(i+1)%4], 1); } //end for #endif #endif VectorCopy(move_start, move_start_top); move_start_top[2] += maxs[2] - mid[2] + 24; //+ bbox maxs z VectorCopy(move_end, move_end_top); move_end_top[2] += maxs[2] - mid[2] + 24; //+ bbox maxs z // if (!AAS_PointAreaNum(move_start_top)) continue; if (!AAS_PointAreaNum(move_end_top)) continue; // for (i = 0; i < 2; i++) { firststartreach = firstendreach = NULL; // if (i == 0) { firststartreach = AAS_FindFaceReachabilities(start_edgeverts, 4, &start_plane, qtrue); firstendreach = AAS_FindFaceReachabilities(end_edgeverts, 4, &end_plane, qfalse); } //end if else { firststartreach = AAS_FindFaceReachabilities(end_edgeverts, 4, &end_plane, qtrue); firstendreach = AAS_FindFaceReachabilities(start_edgeverts, 4, &start_plane, qfalse); } //end else // //create reachabilities from start to end for (startreach = firststartreach; startreach; startreach = nextstartreach) { nextstartreach = startreach->next; // //trace = AAS_TraceClientBBox(startreach->start, move_start_top, PRESENCE_NORMAL, -1); //if (trace.fraction < 1) continue; // for (endreach = firstendreach; endreach; endreach = nextendreach) { nextendreach = endreach->next; // //trace = AAS_TraceClientBBox(endreach->end, move_end_top, PRESENCE_NORMAL, -1); //if (trace.fraction < 1) continue; // Log_Write("funcbob reach from area %d to %d\n", startreach->areanum, endreach->areanum); // // if (i == 0) VectorCopy(move_start_top, org); else VectorCopy(move_end_top, org); VectorSubtract(startreach->start, org, dir); dir[2] = 0; VectorNormalize(dir); VectorCopy(startreach->start, start); VectorMA(startreach->start, 1, dir, start); start[2] += 1; VectorMA(startreach->start, 16, dir, end); end[2] += 1; // numareas = AAS_TraceAreas(start, end, areas, points, 10); if (numareas <= 0) continue; if (numareas > 1) VectorCopy(points[1], startreach->start); else VectorCopy(end, startreach->start); // if (!AAS_PointAreaNum(startreach->start)) continue; if (!AAS_PointAreaNum(endreach->end)) continue; // lreach = AAS_AllocReachability(); lreach->areanum = endreach->areanum; if (i == 0) lreach->edgenum = ((int)move_start[axis] << 16) | ((int) move_end[axis] & 0x0000ffff); else lreach->edgenum = ((int)move_end[axis] << 16) | ((int) move_start[axis] & 0x0000ffff); lreach->facenum = (spawnflags << 16) | modelnum; VectorCopy(startreach->start, lreach->start); VectorCopy(endreach->end, lreach->end); #ifndef BSPC // AAS_DrawArrow(lreach->start, lreach->end, LINECOLOR_BLUE, LINECOLOR_YELLOW); // AAS_PermanentLine(lreach->start, lreach->end, 1); #endif lreach->traveltype = TRAVEL_FUNCBOB; lreach->traveltype |= AAS_TravelFlagsForTeam(ent); lreach->traveltime = aassettings.rs_funcbob; reach_funcbob++; lreach->next = areareachability[startreach->areanum]; areareachability[startreach->areanum] = lreach; // } //end for } //end for for (startreach = firststartreach; startreach; startreach = nextstartreach) { nextstartreach = startreach->next; AAS_FreeReachability(startreach); } //end for for (endreach = firstendreach; endreach; endreach = nextendreach) { nextendreach = endreach->next; AAS_FreeReachability(endreach); } //end for //only go up with func_bobbing entities that go up and down if (!(spawnflags & 1) && !(spawnflags & 2)) break; } //end for } //end for } //end of the function AAS_Reachability_FuncBobbing //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_Reachability_JumpPad(void) { int face2num, i, ret, area2num, visualize, ent, bot_visualizejumppads; //int modelnum, ent2; //float dist, time, height, gravity, forward; float speed, zvel, hordist; aas_face_t *face2; aas_area_t *area2; aas_lreachability_t *lreach; vec3_t areastart, facecenter, dir, cmdmove; vec3_t velocity, absmins, absmaxs; //vec3_t origin, ent2origin, angles, teststart; aas_clientmove_t move; //aas_trace_t trace; aas_link_t *areas, *link; //char target[MAX_EPAIRKEY], targetname[MAX_EPAIRKEY], model[MAX_EPAIRKEY]; char classname[MAX_EPAIRKEY]; #ifdef BSPC bot_visualizejumppads = 0; #else bot_visualizejumppads = LibVarValue("bot_visualizejumppads", "0"); #endif for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) { if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; if (strcmp(classname, "trigger_push")) continue; // if (!AAS_GetJumpPadInfo(ent, areastart, absmins, absmaxs, velocity)) continue; /* // AAS_FloatForBSPEpairKey(ent, "speed", &speed); if (!speed) speed = 1000; // AAS_VectorForBSPEpairKey(ent, "angles", angles); // AAS_SetMovedir(angles, velocity); // VectorScale(velocity, speed, velocity); VectorClear(angles); //get the mins, maxs and origin of the model AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY); if (model[0]) modelnum = atoi(model+1); else modelnum = 0; AAS_BSPModelMinsMaxsOrigin(modelnum, angles, absmins, absmaxs, origin); VectorAdd(origin, absmins, absmins); VectorAdd(origin, absmaxs, absmaxs); // #ifdef REACH_DEBUG botimport.Print(PRT_MESSAGE, "absmins = %f %f %f\n", absmins[0], absmins[1], absmins[2]); botimport.Print(PRT_MESSAGE, "absmaxs = %f %f %f\n", absmaxs[0], absmaxs[1], absmaxs[2]); #endif REACH_DEBUG VectorAdd(absmins, absmaxs, origin); VectorScale (origin, 0.5, origin); //get the start areas VectorCopy(origin, teststart); teststart[2] += 64; trace = AAS_TraceClientBBox(teststart, origin, PRESENCE_CROUCH, -1); if (trace.startsolid) { botimport.Print(PRT_MESSAGE, "trigger_push start solid\n"); VectorCopy(origin, areastart); } //end if else { VectorCopy(trace.endpos, areastart); } //end else areastart[2] += 0.125; // //AAS_DrawPermanentCross(origin, 4, 4); //get the target entity AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY); for (ent2 = AAS_NextBSPEntity(0); ent2; ent2 = AAS_NextBSPEntity(ent2)) { if (!AAS_ValueForBSPEpairKey(ent2, "targetname", targetname, MAX_EPAIRKEY)) continue; if (!strcmp(targetname, target)) break; } //end for if (!ent2) { botimport.Print(PRT_MESSAGE, "trigger_push without target entity %s\n", target); continue; } //end if AAS_VectorForBSPEpairKey(ent2, "origin", ent2origin); // height = ent2origin[2] - origin[2]; gravity = aassettings.sv_gravity; time = sqrt( height / ( 0.5 * gravity ) ); if (!time) { botimport.Print(PRT_MESSAGE, "trigger_push without time\n"); continue; } //end if // set s.origin2 to the push velocity VectorSubtract ( ent2origin, origin, velocity); dist = VectorNormalize( velocity); forward = dist / time; //FIXME: why multiply by 1.1 forward *= 1.1; VectorScale(velocity, forward, velocity); velocity[2] = time * gravity; */ //get the areas the jump pad brush is in areas = AAS_LinkEntityClientBBox(absmins, absmaxs, -1, PRESENCE_CROUCH); /* for (link = areas; link; link = link->next_area) { if (link->areanum == 563) { ret = qfalse; } } */ for (link = areas; link; link = link->next_area) { if (AAS_AreaJumpPad(link->areanum)) break; } //end for if (!link) { botimport.Print(PRT_MESSAGE, "trigger_push not in any jump pad area\n"); AAS_UnlinkFromAreas(areas); continue; } //end if // botimport.Print(PRT_MESSAGE, "found a trigger_push with velocity %f %f %f\n", velocity[0], velocity[1], velocity[2]); //if there is a horizontal velocity check for a reachability without air control if (velocity[0] || velocity[1]) { VectorSet(cmdmove, 0, 0, 0); //VectorCopy(velocity, cmdmove); //cmdmove[2] = 0; Com_Memset(&move, 0, sizeof(aas_clientmove_t)); area2num = 0; for (i = 0; i < 20; i++) { AAS_PredictClientMovement(&move, -1, areastart, PRESENCE_NORMAL, qfalse, velocity, cmdmove, 0, 30, 0.1f, SE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME| SE_ENTERLAVA|SE_HITGROUNDDAMAGE|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER, 0, bot_visualizejumppads); area2num = move.endarea; for (link = areas; link; link = link->next_area) { if (!AAS_AreaJumpPad(link->areanum)) continue; if (link->areanum == area2num) break; } //end if if (!link) break; VectorCopy(move.endpos, areastart); VectorCopy(move.velocity, velocity); } //end for if (area2num && i < 20) { for (link = areas; link; link = link->next_area) { if (!AAS_AreaJumpPad(link->areanum)) continue; if (AAS_ReachabilityExists(link->areanum, area2num)) continue; //create a rocket or bfg jump reachability from area1 to area2 lreach = AAS_AllocReachability(); if (!lreach) { AAS_UnlinkFromAreas(areas); return; } //end if lreach->areanum = area2num; //NOTE: the facenum is the Z velocity lreach->facenum = velocity[2]; //NOTE: the edgenum is the horizontal velocity lreach->edgenum = sqrt(velocity[0] * velocity[0] + velocity[1] * velocity[1]); VectorCopy(areastart, lreach->start); VectorCopy(move.endpos, lreach->end); lreach->traveltype = TRAVEL_JUMPPAD; lreach->traveltype |= AAS_TravelFlagsForTeam(ent); lreach->traveltime = aassettings.rs_jumppad; lreach->next = areareachability[link->areanum]; areareachability[link->areanum] = lreach; // reach_jumppad++; } //end for } //end if } //end if // if (fabs(velocity[0]) > 100 || fabs(velocity[1]) > 100) continue; //check for areas we can reach with air control for (area2num = 1; area2num < aasworld.numareas; area2num++) { visualize = qfalse; /* if (area2num == 3568) { for (link = areas; link; link = link->next_area) { if (link->areanum == 3380) { visualize = qtrue; botimport.Print(PRT_MESSAGE, "bah\n"); } //end if } //end for } //end if*/ //never try to go back to one of the original jumppad areas //and don't create reachabilities if they already exist for (link = areas; link; link = link->next_area) { if (AAS_ReachabilityExists(link->areanum, area2num)) break; if (AAS_AreaJumpPad(link->areanum)) { if (link->areanum == area2num) break; } //end if } //end if if (link) continue; // area2 = &aasworld.areas[area2num]; for (i = 0; i < area2->numfaces; i++) { face2num = aasworld.faceindex[area2->firstface + i]; face2 = &aasworld.faces[abs(face2num)]; //if it is not a ground face if (!(face2->faceflags & FACE_GROUND)) continue; //get the center of the face AAS_FaceCenter(face2num, facecenter); //only go higher up if (facecenter[2] < areastart[2]) continue; //get the jumppad jump z velocity zvel = velocity[2]; //get the horizontal speed for the jump, if it isn't possible to calculate this //speed ret = AAS_HorizontalVelocityForJump(zvel, areastart, facecenter, &speed); if (ret && speed < 150) { //direction towards the face center VectorSubtract(facecenter, areastart, dir); dir[2] = 0; hordist = VectorNormalize(dir); //if (hordist < 1.6 * facecenter[2] - areastart[2]) { //get command movement VectorScale(dir, speed, cmdmove); // AAS_PredictClientMovement(&move, -1, areastart, PRESENCE_NORMAL, qfalse, velocity, cmdmove, 30, 30, 0.1f, SE_ENTERWATER|SE_ENTERSLIME| SE_ENTERLAVA|SE_HITGROUNDDAMAGE| SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER|SE_HITGROUNDAREA, area2num, visualize); //if prediction time wasn't enough to fully predict the movement //don't enter slime or lava and don't fall from too high if (move.frames < 30 && !(move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE)) && (move.stopevent & (SE_HITGROUNDAREA|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER))) { //never go back to the same jumppad for (link = areas; link; link = link->next_area) { if (link->areanum == move.endarea) break; } if (!link) { for (link = areas; link; link = link->next_area) { if (!AAS_AreaJumpPad(link->areanum)) continue; if (AAS_ReachabilityExists(link->areanum, area2num)) continue; //create a jumppad reachability from area1 to area2 lreach = AAS_AllocReachability(); if (!lreach) { AAS_UnlinkFromAreas(areas); return; } //end if lreach->areanum = move.endarea; //NOTE: the facenum is the Z velocity lreach->facenum = velocity[2]; //NOTE: the edgenum is the horizontal velocity lreach->edgenum = sqrt(cmdmove[0] * cmdmove[0] + cmdmove[1] * cmdmove[1]); VectorCopy(areastart, lreach->start); VectorCopy(facecenter, lreach->end); lreach->traveltype = TRAVEL_JUMPPAD; lreach->traveltype |= AAS_TravelFlagsForTeam(ent); lreach->traveltime = aassettings.rs_aircontrolledjumppad; lreach->next = areareachability[link->areanum]; areareachability[link->areanum] = lreach; // reach_jumppad++; } //end for } } //end if } //end if } //end for } //end for } //end for AAS_UnlinkFromAreas(areas); } //end for } //end of the function AAS_Reachability_JumpPad //=========================================================================== // never point at ground faces // always a higher and pretty far area // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_Reachability_Grapple(int area1num, int area2num) { int face2num, i, j, areanum, numareas, areas[20]; float mingrappleangle, z, hordist; bsp_trace_t bsptrace; aas_trace_t trace; aas_face_t *face2; aas_area_t *area1, *area2; aas_lreachability_t *lreach; vec3_t areastart, facecenter, start, end, dir, down = {0, 0, -1}; vec_t *v; //only grapple when on the ground or swimming if (!AAS_AreaGrounded(area1num) && !AAS_AreaSwim(area1num)) return qfalse; //don't grapple from a crouch area if (!(AAS_AreaPresenceType(area1num) & PRESENCE_NORMAL)) return qfalse; //NOTE: disabled area swim it doesn't work right if (AAS_AreaSwim(area1num)) return qfalse; // area1 = &aasworld.areas[area1num]; area2 = &aasworld.areas[area2num]; //don't grapple towards way lower areas if (area2->maxs[2] < area1->mins[2]) return qfalse; // VectorCopy(aasworld.areas[area1num].center, start); //if not a swim area if (!AAS_AreaSwim(area1num)) { if (!AAS_PointAreaNum(start)) Log_Write("area %d center %f %f %f in solid?\r\n", area1num, start[0], start[1], start[2]); VectorCopy(start, end); end[2] -= 1000; trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1); if (trace.startsolid) return qfalse; VectorCopy(trace.endpos, areastart); } //end if else { if (!(AAS_PointContents(start) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER))) return qfalse; } //end else // //start is now the start point // for (i = 0; i < area2->numfaces; i++) { face2num = aasworld.faceindex[area2->firstface + i]; face2 = &aasworld.faces[abs(face2num)]; //if it is not a solid face if (!(face2->faceflags & FACE_SOLID)) continue; //direction towards the first vertex of the face v = aasworld.vertexes[aasworld.edges[abs(aasworld.edgeindex[face2->firstedge])].v[0]]; VectorSubtract(v, areastart, dir); //if the face plane is facing away if (DotProduct(aasworld.planes[face2->planenum].normal, dir) > 0) continue; //get the center of the face AAS_FaceCenter(face2num, facecenter); //only go higher up with the grapple if (facecenter[2] < areastart[2] + 64) continue; //only use vertical faces or downward facing faces if (DotProduct(aasworld.planes[face2->planenum].normal, down) < 0) continue; //direction towards the face center VectorSubtract(facecenter, areastart, dir); // z = dir[2]; dir[2] = 0; hordist = VectorLength(dir); if (!hordist) continue; //if too far if (hordist > 2000) continue; //check the minimal angle of the movement mingrappleangle = 15; //15 degrees if (z / hordist < tan(2 * M_PI * mingrappleangle / 360)) continue; // VectorCopy(facecenter, start); VectorMA(facecenter, -500, aasworld.planes[face2->planenum].normal, end); // bsptrace = AAS_Trace(start, NULL, NULL, end, 0, CONTENTS_SOLID); //the grapple won't stick to the sky and the grapple point should be near the AAS wall if ((bsptrace.surface.flags & SURF_SKY) || (bsptrace.fraction * 500 > 32)) continue; //trace a full bounding box from the area center on the ground to //the center of the face VectorSubtract(facecenter, areastart, dir); VectorNormalize(dir); VectorMA(areastart, 4, dir, start); VectorCopy(bsptrace.endpos, end); trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1); VectorSubtract(trace.endpos, facecenter, dir); if (VectorLength(dir) > 24) continue; // VectorCopy(trace.endpos, start); VectorCopy(trace.endpos, end); end[2] -= AAS_FallDamageDistance(); trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1); if (trace.fraction >= 1) continue; //area to end in areanum = AAS_PointAreaNum(trace.endpos); //if not in lava or slime if (aasworld.areasettings[areanum].contents & (AREACONTENTS_SLIME|AREACONTENTS_LAVA)) { continue; } //end if //do not go the the source area if (areanum == area1num) continue; //don't create reachabilities if they already exist if (AAS_ReachabilityExists(area1num, areanum)) continue; //only end in areas we can stand if (!AAS_AreaGrounded(areanum)) continue; //never go through cluster portals!! numareas = AAS_TraceAreas(areastart, bsptrace.endpos, areas, NULL, 20); if (numareas >= 20) continue; for (j = 0; j < numareas; j++) { if (aasworld.areasettings[areas[j]].contents & AREACONTENTS_CLUSTERPORTAL) break; } //end for if (j < numareas) continue; //create a new reachability link lreach = AAS_AllocReachability(); if (!lreach) return qfalse; lreach->areanum = areanum; lreach->facenum = face2num; lreach->edgenum = 0; VectorCopy(areastart, lreach->start); //VectorCopy(facecenter, lreach->end); VectorCopy(bsptrace.endpos, lreach->end); lreach->traveltype = TRAVEL_GRAPPLEHOOK; VectorSubtract(lreach->end, lreach->start, dir); lreach->traveltime = aassettings.rs_startgrapple + VectorLength(dir) * 0.25; lreach->next = areareachability[area1num]; areareachability[area1num] = lreach; // reach_grapple++; } //end for // return qfalse; } //end of the function AAS_Reachability_Grapple //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_SetWeaponJumpAreaFlags(void) { int ent, i; vec3_t mins = {-15, -15, -15}, maxs = {15, 15, 15}; vec3_t origin; int areanum, weaponjumpareas, spawnflags; char classname[MAX_EPAIRKEY]; weaponjumpareas = 0; for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) { if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; if ( !strcmp(classname, "item_armor_body") || !strcmp(classname, "item_armor_combat") || !strcmp(classname, "item_health_mega") || !strcmp(classname, "weapon_grenadelauncher") || !strcmp(classname, "weapon_rocketlauncher") || !strcmp(classname, "weapon_lightning") || !strcmp(classname, "weapon_plasmagun") || !strcmp(classname, "weapon_railgun") || !strcmp(classname, "weapon_bfg") || !strcmp(classname, "item_quad") || !strcmp(classname, "item_regen") || !strcmp(classname, "item_invulnerability")) { if (AAS_VectorForBSPEpairKey(ent, "origin", origin)) { spawnflags = 0; AAS_IntForBSPEpairKey(ent, "spawnflags", &spawnflags); //if not a stationary item if (!(spawnflags & 1)) { if (!AAS_DropToFloor(origin, mins, maxs)) { botimport.Print(PRT_MESSAGE, "%s in solid at (%1.1f %1.1f %1.1f)\n", classname, origin[0], origin[1], origin[2]); } //end if } //end if //areanum = AAS_PointAreaNum(origin); areanum = AAS_BestReachableArea(origin, mins, maxs, origin); //the bot may rocket jump towards this area aasworld.areasettings[areanum].areaflags |= AREA_WEAPONJUMP; // //if (!AAS_AreaGrounded(areanum)) // botimport.Print(PRT_MESSAGE, "area not grounded\n"); // weaponjumpareas++; } //end if } //end if } //end for for (i = 1; i < aasworld.numareas; i++) { if (aasworld.areasettings[i].contents & AREACONTENTS_JUMPPAD) { aasworld.areasettings[i].areaflags |= AREA_WEAPONJUMP; weaponjumpareas++; } //end if } //end for botimport.Print(PRT_MESSAGE, "%d weapon jump areas\n", weaponjumpareas); } //end of the function AAS_SetWeaponJumpAreaFlags //=========================================================================== // create a possible weapon jump reachability from area1 to area2 // // check if there's a cool item in the second area // check if area1 is lower than area2 // check if the bot can rocketjump from area1 to area2 // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_Reachability_WeaponJump(int area1num, int area2num) { int face2num, i, n, ret, visualize; float speed, zvel, hordist; aas_face_t *face2; aas_area_t *area1, *area2; aas_lreachability_t *lreach; vec3_t areastart, facecenter, start, end, dir, cmdmove;// teststart; vec3_t velocity; aas_clientmove_t move; aas_trace_t trace; visualize = qfalse; // if (area1num == 4436 && area2num == 4318) // { // visualize = qtrue; // } if (!AAS_AreaGrounded(area1num) || AAS_AreaSwim(area1num)) return qfalse; if (!AAS_AreaGrounded(area2num)) return qfalse; //NOTE: only weapon jump towards areas with an interesting item in it?? if (!(aasworld.areasettings[area2num].areaflags & AREA_WEAPONJUMP)) return qfalse; // area1 = &aasworld.areas[area1num]; area2 = &aasworld.areas[area2num]; //don't weapon jump towards way lower areas if (area2->maxs[2] < area1->mins[2]) return qfalse; // VectorCopy(aasworld.areas[area1num].center, start); //if not a swim area if (!AAS_PointAreaNum(start)) Log_Write("area %d center %f %f %f in solid?\r\n", area1num, start[0], start[1], start[2]); VectorCopy(start, end); end[2] -= 1000; trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1); if (trace.startsolid) return qfalse; VectorCopy(trace.endpos, areastart); // //areastart is now the start point // for (i = 0; i < area2->numfaces; i++) { face2num = aasworld.faceindex[area2->firstface + i]; face2 = &aasworld.faces[abs(face2num)]; //if it is not a solid face if (!(face2->faceflags & FACE_GROUND)) continue; //get the center of the face AAS_FaceCenter(face2num, facecenter); //only go higher up with weapon jumps if (facecenter[2] < areastart[2] + 64) continue; //NOTE: set to 2 to allow bfg jump reachabilities for (n = 0; n < 1/*2*/; n++) { //get the rocket jump z velocity if (n) zvel = AAS_BFGJumpZVelocity(areastart); else zvel = AAS_RocketJumpZVelocity(areastart); //get the horizontal speed for the jump, if it isn't possible to calculate this //speed (the jump is not possible) then there's no jump reachability created ret = AAS_HorizontalVelocityForJump(zvel, areastart, facecenter, &speed); if (ret && speed < 300) { //direction towards the face center VectorSubtract(facecenter, areastart, dir); dir[2] = 0; hordist = VectorNormalize(dir); //if (hordist < 1.6 * (facecenter[2] - areastart[2])) { //get command movement VectorScale(dir, speed, cmdmove); VectorSet(velocity, 0, 0, zvel); /* //get command movement VectorScale(dir, speed, velocity); velocity[2] = zvel; VectorSet(cmdmove, 0, 0, 0); */ // AAS_PredictClientMovement(&move, -1, areastart, PRESENCE_NORMAL, qtrue, velocity, cmdmove, 30, 30, 0.1f, SE_ENTERWATER|SE_ENTERSLIME| SE_ENTERLAVA|SE_HITGROUNDDAMAGE| SE_TOUCHJUMPPAD|SE_HITGROUND|SE_HITGROUNDAREA, area2num, visualize); //if prediction time wasn't enough to fully predict the movement //don't enter slime or lava and don't fall from too high if (move.frames < 30 && !(move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE)) && (move.stopevent & (SE_HITGROUNDAREA|SE_TOUCHJUMPPAD))) { //create a rocket or bfg jump reachability from area1 to area2 lreach = AAS_AllocReachability(); if (!lreach) return qfalse; lreach->areanum = area2num; lreach->facenum = 0; lreach->edgenum = 0; VectorCopy(areastart, lreach->start); VectorCopy(facecenter, lreach->end); if (n) { lreach->traveltype = TRAVEL_BFGJUMP; lreach->traveltime = aassettings.rs_bfgjump; } //end if else { lreach->traveltype = TRAVEL_ROCKETJUMP; lreach->traveltime = aassettings.rs_rocketjump; } //end else lreach->next = areareachability[area1num]; areareachability[area1num] = lreach; // reach_rocketjump++; return qtrue; } //end if } //end if } //end if } //end for } //end for // return qfalse; } //end of the function AAS_Reachability_WeaponJump //=========================================================================== // calculates additional walk off ledge reachabilities for the given area // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_Reachability_WalkOffLedge(int areanum) { int i, j, k, l, m, n, p, areas[10], numareas; int face1num, face2num, face3num, edge1num, edge2num, edge3num; int otherareanum, gap, reachareanum, side; aas_area_t *area, *area2; aas_face_t *face1, *face2, *face3; aas_edge_t *edge; aas_plane_t *plane; vec_t *v1, *v2; vec3_t sharededgevec, mid, dir, testend; aas_lreachability_t *lreach; aas_trace_t trace; if (!AAS_AreaGrounded(areanum) || AAS_AreaSwim(areanum)) return; // area = &aasworld.areas[areanum]; // for (i = 0; i < area->numfaces; i++) { face1num = aasworld.faceindex[area->firstface + i]; face1 = &aasworld.faces[abs(face1num)]; //face 1 must be a ground face if (!(face1->faceflags & FACE_GROUND)) continue; //go through all the edges of this ground face for (k = 0; k < face1->numedges; k++) { edge1num = aasworld.edgeindex[face1->firstedge + k]; //find another not ground face using this same edge for (j = 0; j < area->numfaces; j++) { face2num = aasworld.faceindex[area->firstface + j]; face2 = &aasworld.faces[abs(face2num)]; //face 2 may not be a ground face if (face2->faceflags & FACE_GROUND) continue; //compare all the edges for (l = 0; l < face2->numedges; l++) { edge2num = aasworld.edgeindex[face2->firstedge + l]; if (abs(edge1num) == abs(edge2num)) { //get the area at the other side of the face if (face2->frontarea == areanum) otherareanum = face2->backarea; else otherareanum = face2->frontarea; // area2 = &aasworld.areas[otherareanum]; //if the other area is grounded! if (aasworld.areasettings[otherareanum].areaflags & AREA_GROUNDED) { //check for a possible gap gap = qfalse; for (n = 0; n < area2->numfaces; n++) { face3num = aasworld.faceindex[area2->firstface + n]; //may not be the shared face of the two areas if (abs(face3num) == abs(face2num)) continue; // face3 = &aasworld.faces[abs(face3num)]; //find an edge shared by all three faces for (m = 0; m < face3->numedges; m++) { edge3num = aasworld.edgeindex[face3->firstedge + m]; //but the edge should be shared by all three faces if (abs(edge3num) == abs(edge1num)) { if (!(face3->faceflags & FACE_SOLID)) { gap = qtrue; break; } //end if // if (face3->faceflags & FACE_GROUND) { gap = qfalse; break; } //end if //FIXME: there are more situations to be handled gap = qtrue; break; } //end if } //end for if (m < face3->numedges) break; } //end for if (!gap) break; } //end if //check for a walk off ledge reachability edge = &aasworld.edges[abs(edge1num)]; side = edge1num < 0; // v1 = aasworld.vertexes[edge->v[side]]; v2 = aasworld.vertexes[edge->v[!side]]; // plane = &aasworld.planes[face1->planenum]; //get the points really into the areas VectorSubtract(v2, v1, sharededgevec); CrossProduct(plane->normal, sharededgevec, dir); VectorNormalize(dir); // VectorAdd(v1, v2, mid); VectorScale(mid, 0.5, mid); VectorMA(mid, 8, dir, mid); // VectorCopy(mid, testend); testend[2] -= 1000; trace = AAS_TraceClientBBox(mid, testend, PRESENCE_CROUCH, -1); // if (trace.startsolid) { //Log_Write("area %d: trace.startsolid\r\n", areanum); break; } //end if reachareanum = AAS_PointAreaNum(trace.endpos); if (reachareanum == areanum) { //Log_Write("area %d: same area\r\n", areanum); break; } //end if if (AAS_ReachabilityExists(areanum, reachareanum)) { //Log_Write("area %d: reachability already exists\r\n", areanum); break; } //end if if (!AAS_AreaGrounded(reachareanum) && !AAS_AreaSwim(reachareanum)) { //Log_Write("area %d, reach area %d: not grounded and not swim\r\n", areanum, reachareanum); break; } //end if // if (aasworld.areasettings[reachareanum].contents & (AREACONTENTS_SLIME | AREACONTENTS_LAVA)) { //Log_Write("area %d, reach area %d: lava or slime\r\n", areanum, reachareanum); break; } //end if //if not going through a cluster portal numareas = AAS_TraceAreas(mid, testend, areas, NULL, sizeof(areas) / sizeof(int)); for (p = 0; p < numareas; p++) if (AAS_AreaClusterPortal(areas[p])) break; if (p < numareas) break; // if a maximum fall height is set and the bot would fall down further if (aassettings.rs_maxfallheight && fabs(mid[2] - trace.endpos[2]) > aassettings.rs_maxfallheight) break; // lreach = AAS_AllocReachability(); if (!lreach) break; lreach->areanum = reachareanum; lreach->facenum = 0; lreach->edgenum = edge1num; VectorCopy(mid, lreach->start); VectorCopy(trace.endpos, lreach->end); lreach->traveltype = TRAVEL_WALKOFFLEDGE; lreach->traveltime = aassettings.rs_startwalkoffledge + fabs(mid[2] - trace.endpos[2]) * 50 / aassettings.phys_gravity; if (!AAS_AreaSwim(reachareanum) && !AAS_AreaJumpPad(reachareanum)) { if (AAS_FallDelta(mid[2] - trace.endpos[2]) > aassettings.phys_falldelta5) { lreach->traveltime += aassettings.rs_falldamage5; } //end if else if (AAS_FallDelta(mid[2] - trace.endpos[2]) > aassettings.phys_falldelta10) { lreach->traveltime += aassettings.rs_falldamage10; } //end if } //end if lreach->next = areareachability[areanum]; areareachability[areanum] = lreach; //we've got another walk off ledge reachability reach_walkoffledge++; } //end if } //end for } //end for } //end for } //end for } //end of the function AAS_Reachability_WalkOffLedge //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_StoreReachability(void) { int i; aas_areasettings_t *areasettings; aas_lreachability_t *lreach; aas_reachability_t *reach; if (aasworld.reachability) FreeMemory(aasworld.reachability); aasworld.reachability = (aas_reachability_t *) GetClearedMemory((numlreachabilities + 10) * sizeof(aas_reachability_t)); aasworld.reachabilitysize = 1; for (i = 0; i < aasworld.numareas; i++) { areasettings = &aasworld.areasettings[i]; areasettings->firstreachablearea = aasworld.reachabilitysize; areasettings->numreachableareas = 0; for (lreach = areareachability[i]; lreach; lreach = lreach->next) { reach = &aasworld.reachability[areasettings->firstreachablearea + areasettings->numreachableareas]; reach->areanum = lreach->areanum; reach->facenum = lreach->facenum; reach->edgenum = lreach->edgenum; VectorCopy(lreach->start, reach->start); VectorCopy(lreach->end, reach->end); reach->traveltype = lreach->traveltype; reach->traveltime = lreach->traveltime; // areasettings->numreachableareas++; } //end for aasworld.reachabilitysize += areasettings->numreachableareas; } //end for } //end of the function AAS_StoreReachability //=========================================================================== // // TRAVEL_WALK 100% equal floor height + steps // TRAVEL_CROUCH 100% // TRAVEL_BARRIERJUMP 100% // TRAVEL_JUMP 80% // TRAVEL_LADDER 100% + fall down from ladder + jump up to ladder // TRAVEL_WALKOFFLEDGE 90% walk off very steep walls? // TRAVEL_SWIM 100% // TRAVEL_WATERJUMP 100% // TRAVEL_TELEPORT 100% // TRAVEL_ELEVATOR 100% // TRAVEL_GRAPPLEHOOK 100% // TRAVEL_DOUBLEJUMP 0% // TRAVEL_RAMPJUMP 0% // TRAVEL_STRAFEJUMP 0% // TRAVEL_ROCKETJUMP 100% (currently limited towards areas with items) // TRAVEL_BFGJUMP 0% (currently disabled) // TRAVEL_JUMPPAD 100% // TRAVEL_FUNCBOB 100% // // Parameter: - // Returns: true if NOT finished // Changes Globals: - //=========================================================================== int AAS_ContinueInitReachability(float time) { int i, j, todo, start_time; static float framereachability, reachability_delay; static int lastpercentage; if (!aasworld.loaded) return qfalse; //if reachability is calculated for all areas if (aasworld.numreachabilityareas >= aasworld.numareas + 2) return qfalse; //if starting with area 1 (area 0 is a dummy) if (aasworld.numreachabilityareas == 1) { botimport.Print(PRT_MESSAGE, "calculating reachability...\n"); lastpercentage = 0; framereachability = 2000; reachability_delay = 1000; } //end if //number of areas to calculate reachability for this cycle todo = aasworld.numreachabilityareas + (int) framereachability; start_time = Sys_MilliSeconds(); //loop over the areas for (i = aasworld.numreachabilityareas; i < aasworld.numareas && i < todo; i++) { aasworld.numreachabilityareas++; //only create jumppad reachabilities from jumppad areas if (aasworld.areasettings[i].contents & AREACONTENTS_JUMPPAD) { continue; } //end if //loop over the areas for (j = 1; j < aasworld.numareas; j++) { if (i == j) continue; //never create reachabilities from teleporter or jumppad areas to regular areas if (aasworld.areasettings[i].contents & (AREACONTENTS_TELEPORTER|AREACONTENTS_JUMPPAD)) { if (!(aasworld.areasettings[j].contents & (AREACONTENTS_TELEPORTER|AREACONTENTS_JUMPPAD))) { continue; } //end if } //end if //if there already is a reachability link from area i to j if (AAS_ReachabilityExists(i, j)) continue; //check for a swim reachability if (AAS_Reachability_Swim(i, j)) continue; //check for a simple walk on equal floor height reachability if (AAS_Reachability_EqualFloorHeight(i, j)) continue; //check for step, barrier, waterjump and walk off ledge reachabilities if (AAS_Reachability_Step_Barrier_WaterJump_WalkOffLedge(i, j)) continue; //check for ladder reachabilities if (AAS_Reachability_Ladder(i, j)) continue; //check for a jump reachability if (AAS_Reachability_Jump(i, j)) continue; } //end for //never create these reachabilities from teleporter or jumppad areas if (aasworld.areasettings[i].contents & (AREACONTENTS_TELEPORTER|AREACONTENTS_JUMPPAD)) { continue; } //end if //loop over the areas for (j = 1; j < aasworld.numareas; j++) { if (i == j) continue; // if (AAS_ReachabilityExists(i, j)) continue; //check for a grapple hook reachability if (calcgrapplereach) AAS_Reachability_Grapple(i, j); //check for a weapon jump reachability AAS_Reachability_WeaponJump(i, j); } //end for //if the calculation took more time than the max reachability delay if (Sys_MilliSeconds() - start_time > (int) reachability_delay) break; // if (aasworld.numreachabilityareas * 1000 / aasworld.numareas > lastpercentage) break; } //end for // if (aasworld.numreachabilityareas == aasworld.numareas) { botimport.Print(PRT_MESSAGE, "\r%6.1f%%", (float) 100.0); botimport.Print(PRT_MESSAGE, "\nplease wait while storing reachability...\n"); aasworld.numreachabilityareas++; } //end if //if this is the last step in the reachability calculations else if (aasworld.numreachabilityareas == aasworld.numareas + 1) { //create additional walk off ledge reachabilities for every area for (i = 1; i < aasworld.numareas; i++) { //only create jumppad reachabilities from jumppad areas if (aasworld.areasettings[i].contents & AREACONTENTS_JUMPPAD) { continue; } //end if AAS_Reachability_WalkOffLedge(i); } //end for //create jump pad reachabilities AAS_Reachability_JumpPad(); //create teleporter reachabilities AAS_Reachability_Teleport(); //create elevator (func_plat) reachabilities AAS_Reachability_Elevator(); //create func_bobbing reachabilities AAS_Reachability_FuncBobbing(); // #ifdef DEBUG botimport.Print(PRT_MESSAGE, "%6d reach swim\n", reach_swim); botimport.Print(PRT_MESSAGE, "%6d reach equal floor\n", reach_equalfloor); botimport.Print(PRT_MESSAGE, "%6d reach step\n", reach_step); botimport.Print(PRT_MESSAGE, "%6d reach barrier\n", reach_barrier); botimport.Print(PRT_MESSAGE, "%6d reach waterjump\n", reach_waterjump); botimport.Print(PRT_MESSAGE, "%6d reach walkoffledge\n", reach_walkoffledge); botimport.Print(PRT_MESSAGE, "%6d reach jump\n", reach_jump); botimport.Print(PRT_MESSAGE, "%6d reach ladder\n", reach_ladder); botimport.Print(PRT_MESSAGE, "%6d reach walk\n", reach_walk); botimport.Print(PRT_MESSAGE, "%6d reach teleport\n", reach_teleport); botimport.Print(PRT_MESSAGE, "%6d reach funcbob\n", reach_funcbob); botimport.Print(PRT_MESSAGE, "%6d reach elevator\n", reach_elevator); botimport.Print(PRT_MESSAGE, "%6d reach grapple\n", reach_grapple); botimport.Print(PRT_MESSAGE, "%6d reach rocketjump\n", reach_rocketjump); botimport.Print(PRT_MESSAGE, "%6d reach jumppad\n", reach_jumppad); #endif //*/ //store all the reachabilities AAS_StoreReachability(); //free the reachability link heap AAS_ShutDownReachabilityHeap(); // FreeMemory(areareachability); // aasworld.numreachabilityareas++; // botimport.Print(PRT_MESSAGE, "calculating clusters...\n"); } //end if else { lastpercentage = aasworld.numreachabilityareas * 1000 / aasworld.numareas; botimport.Print(PRT_MESSAGE, "\r%6.1f%%", (float) lastpercentage / 10); } //end else //not yet finished return qtrue; } //end of the function AAS_ContinueInitReachability //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_InitReachability(void) { if (!aasworld.loaded) return; if (aasworld.reachabilitysize) { #ifndef BSPC if (!((int)LibVarGetValue("forcereachability"))) { aasworld.numreachabilityareas = aasworld.numareas + 2; return; } //end if #else aasworld.numreachabilityareas = aasworld.numareas + 2; return; #endif //BSPC } //end if #ifndef BSPC calcgrapplereach = LibVarGetValue("grapplereach"); #endif aasworld.savefile = qtrue; //start with area 1 because area zero is a dummy aasworld.numreachabilityareas = 1; ////aasworld.numreachabilityareas = aasworld.numareas + 1; //only calculate entity reachabilities //setup the heap with reachability links AAS_SetupReachabilityHeap(); //allocate area reachability link array areareachability = (aas_lreachability_t **) GetClearedMemory( aasworld.numareas * sizeof(aas_lreachability_t *)); // AAS_SetWeaponJumpAreaFlags(); } //end of the function AAS_InitReachable ================================================ FILE: src/engine/botlib/be_aas_reach.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_reach.h * * desc: AAS * * $Archive: /source/code/botlib/be_aas_reach.h $ * *****************************************************************************/ #ifdef AASINTERN //initialize calculating the reachabilities void AAS_InitReachability(void); //continue calculating the reachabilities int AAS_ContinueInitReachability(float time); // int AAS_BestReachableLinkArea(aas_link_t *areas); #endif //AASINTERN //returns true if the are has reachabilities to other areas int AAS_AreaReachability(int areanum); //returns the best reachable area and goal origin for a bounding box at the given origin int AAS_BestReachableArea(vec3_t origin, vec3_t mins, vec3_t maxs, vec3_t goalorigin); //returns the best jumppad area from which the bbox at origin is reachable int AAS_BestReachableFromJumpPadArea(vec3_t origin, vec3_t mins, vec3_t maxs); //returns the next reachability using the given model int AAS_NextModelReachability(int num, int modelnum); //returns the total area of the ground faces of the given area float AAS_AreaGroundFaceArea(int areanum); //returns true if the area is crouch only int AAS_AreaCrouch(int areanum); //returns true if a player can swim in this area int AAS_AreaSwim(int areanum); //returns true if the area is filled with a liquid int AAS_AreaLiquid(int areanum); //returns true if the area contains lava int AAS_AreaLava(int areanum); //returns true if the area contains slime int AAS_AreaSlime(int areanum); //returns true if the area has one or more ground faces int AAS_AreaGrounded(int areanum); //returns true if the area has one or more ladder faces int AAS_AreaLadder(int areanum); //returns true if the area is a jump pad int AAS_AreaJumpPad(int areanum); //returns true if the area is donotenter int AAS_AreaDoNotEnter(int areanum); ================================================ FILE: src/engine/botlib/be_aas_route.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_route.c * * desc: AAS * * $Archive: /MissionPack/code/botlib/be_aas_route.c $ * *****************************************************************************/ #include "../../game/q_shared.h" #include "l_utils.h" #include "l_memory.h" #include "l_log.h" #include "l_crc.h" #include "l_libvar.h" #include "l_script.h" #include "l_precomp.h" #include "l_struct.h" #include "aasfile.h" #include "../../game/botlib.h" #include "../../game/be_aas.h" #include "be_aas_funcs.h" #include "be_interface.h" #include "be_aas_def.h" #define ROUTING_DEBUG //travel time in hundreths of a second = distance * 100 / speed #define DISTANCEFACTOR_CROUCH 1.3f //crouch speed = 100 #define DISTANCEFACTOR_SWIM 1 //should be 0.66, swim speed = 150 #define DISTANCEFACTOR_WALK 0.33f //walk speed = 300 //cache refresh time #define CACHE_REFRESHTIME 15.0f //15 seconds refresh time //maximum number of routing updates each frame #define MAX_FRAMEROUTINGUPDATES 10 /* area routing cache: stores the distances within one cluster to a specific goal area this goal area is in this same cluster and could be a cluster portal for every cluster there's a list with routing cache for every area in that cluster (including the portals of that cluster) area cache stores aasworld.clusters[?].numreachabilityareas travel times portal routing cache: stores the distances of all portals to a specific goal area this goal area could be in any cluster and could also be a cluster portal for every area (aasworld.numareas) the portal cache stores aasworld.numportals travel times */ #ifdef ROUTING_DEBUG int numareacacheupdates; int numportalcacheupdates; #endif //ROUTING_DEBUG int routingcachesize; int max_routingcachesize; //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== #ifdef ROUTING_DEBUG void AAS_RoutingInfo(void) { botimport.Print(PRT_MESSAGE, "%d area cache updates\n", numareacacheupdates); botimport.Print(PRT_MESSAGE, "%d portal cache updates\n", numportalcacheupdates); botimport.Print(PRT_MESSAGE, "%d bytes routing cache\n", routingcachesize); } //end of the function AAS_RoutingInfo #endif //ROUTING_DEBUG //=========================================================================== // returns the number of the area in the cluster // assumes the given area is in the given cluster or a portal of the cluster // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== __inline int AAS_ClusterAreaNum(int cluster, int areanum) { int side, areacluster; areacluster = aasworld.areasettings[areanum].cluster; if (areacluster > 0) return aasworld.areasettings[areanum].clusterareanum; else { /*#ifdef ROUTING_DEBUG if (aasworld.portals[-areacluster].frontcluster != cluster && aasworld.portals[-areacluster].backcluster != cluster) { botimport.Print(PRT_ERROR, "portal %d: does not belong to cluster %d\n" , -areacluster, cluster); } //end if #endif //ROUTING_DEBUG*/ side = aasworld.portals[-areacluster].frontcluster != cluster; return aasworld.portals[-areacluster].clusterareanum[side]; } //end else } //end of the function AAS_ClusterAreaNum //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_InitTravelFlagFromType(void) { int i; for (i = 0; i < MAX_TRAVELTYPES; i++) { aasworld.travelflagfortype[i] = TFL_INVALID; } //end for aasworld.travelflagfortype[TRAVEL_INVALID] = TFL_INVALID; aasworld.travelflagfortype[TRAVEL_WALK] = TFL_WALK; aasworld.travelflagfortype[TRAVEL_CROUCH] = TFL_CROUCH; aasworld.travelflagfortype[TRAVEL_BARRIERJUMP] = TFL_BARRIERJUMP; aasworld.travelflagfortype[TRAVEL_JUMP] = TFL_JUMP; aasworld.travelflagfortype[TRAVEL_LADDER] = TFL_LADDER; aasworld.travelflagfortype[TRAVEL_WALKOFFLEDGE] = TFL_WALKOFFLEDGE; aasworld.travelflagfortype[TRAVEL_SWIM] = TFL_SWIM; aasworld.travelflagfortype[TRAVEL_WATERJUMP] = TFL_WATERJUMP; aasworld.travelflagfortype[TRAVEL_TELEPORT] = TFL_TELEPORT; aasworld.travelflagfortype[TRAVEL_ELEVATOR] = TFL_ELEVATOR; aasworld.travelflagfortype[TRAVEL_ROCKETJUMP] = TFL_ROCKETJUMP; aasworld.travelflagfortype[TRAVEL_BFGJUMP] = TFL_BFGJUMP; aasworld.travelflagfortype[TRAVEL_GRAPPLEHOOK] = TFL_GRAPPLEHOOK; aasworld.travelflagfortype[TRAVEL_DOUBLEJUMP] = TFL_DOUBLEJUMP; aasworld.travelflagfortype[TRAVEL_RAMPJUMP] = TFL_RAMPJUMP; aasworld.travelflagfortype[TRAVEL_STRAFEJUMP] = TFL_STRAFEJUMP; aasworld.travelflagfortype[TRAVEL_JUMPPAD] = TFL_JUMPPAD; aasworld.travelflagfortype[TRAVEL_FUNCBOB] = TFL_FUNCBOB; } //end of the function AAS_InitTravelFlagFromType //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== __inline int AAS_TravelFlagForType_inline(int traveltype) { int tfl; tfl = 0; if (tfl & TRAVELFLAG_NOTTEAM1) tfl |= TFL_NOTTEAM1; if (tfl & TRAVELFLAG_NOTTEAM2) tfl |= TFL_NOTTEAM2; traveltype &= TRAVELTYPE_MASK; if (traveltype < 0 || traveltype >= MAX_TRAVELTYPES) return TFL_INVALID; tfl |= aasworld.travelflagfortype[traveltype]; return tfl; } //end of the function AAS_TravelFlagForType_inline //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_TravelFlagForType(int traveltype) { return AAS_TravelFlagForType_inline(traveltype); } //end of the function AAS_TravelFlagForType_inline //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_UnlinkCache(aas_routingcache_t *cache) { if (cache->time_next) cache->time_next->time_prev = cache->time_prev; else aasworld.newestcache = cache->time_prev; if (cache->time_prev) cache->time_prev->time_next = cache->time_next; else aasworld.oldestcache = cache->time_next; cache->time_next = NULL; cache->time_prev = NULL; } //end of the function AAS_UnlinkCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_LinkCache(aas_routingcache_t *cache) { if (aasworld.newestcache) { aasworld.newestcache->time_next = cache; cache->time_prev = aasworld.newestcache; } //end if else { aasworld.oldestcache = cache; cache->time_prev = NULL; } //end else cache->time_next = NULL; aasworld.newestcache = cache; } //end of the function AAS_LinkCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_FreeRoutingCache(aas_routingcache_t *cache) { AAS_UnlinkCache(cache); routingcachesize -= cache->size; FreeMemory(cache); } //end of the function AAS_FreeRoutingCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_RemoveRoutingCacheInCluster( int clusternum ) { int i; aas_routingcache_t *cache, *nextcache; aas_cluster_t *cluster; if (!aasworld.clusterareacache) return; cluster = &aasworld.clusters[clusternum]; for (i = 0; i < cluster->numareas; i++) { for (cache = aasworld.clusterareacache[clusternum][i]; cache; cache = nextcache) { nextcache = cache->next; AAS_FreeRoutingCache(cache); } //end for aasworld.clusterareacache[clusternum][i] = NULL; } //end for } //end of the function AAS_RemoveRoutingCacheInCluster //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_RemoveRoutingCacheUsingArea( int areanum ) { int i, clusternum; aas_routingcache_t *cache, *nextcache; clusternum = aasworld.areasettings[areanum].cluster; if (clusternum > 0) { //remove all the cache in the cluster the area is in AAS_RemoveRoutingCacheInCluster( clusternum ); } //end if else { // if this is a portal remove all cache in both the front and back cluster AAS_RemoveRoutingCacheInCluster( aasworld.portals[-clusternum].frontcluster ); AAS_RemoveRoutingCacheInCluster( aasworld.portals[-clusternum].backcluster ); } //end else // remove all portal cache for (i = 0; i < aasworld.numareas; i++) { //refresh portal cache for (cache = aasworld.portalcache[i]; cache; cache = nextcache) { nextcache = cache->next; AAS_FreeRoutingCache(cache); } //end for aasworld.portalcache[i] = NULL; } //end for } //end of the function AAS_RemoveRoutingCacheUsingArea //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_EnableRoutingArea(int areanum, int enable) { int flags; if (areanum <= 0 || areanum >= aasworld.numareas) { if (bot_developer) { botimport.Print(PRT_ERROR, "AAS_EnableRoutingArea: areanum %d out of range\n", areanum); } //end if return 0; } //end if flags = aasworld.areasettings[areanum].areaflags & AREA_DISABLED; if (enable < 0) return !flags; if (enable) aasworld.areasettings[areanum].areaflags &= ~AREA_DISABLED; else aasworld.areasettings[areanum].areaflags |= AREA_DISABLED; // if the status of the area changed if ( (flags & AREA_DISABLED) != (aasworld.areasettings[areanum].areaflags & AREA_DISABLED) ) { //remove all routing cache involving this area AAS_RemoveRoutingCacheUsingArea( areanum ); } //end if return !flags; } //end of the function AAS_EnableRoutingArea //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== __inline float AAS_RoutingTime(void) { return AAS_Time(); } //end of the function AAS_RoutingTime //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_GetAreaContentsTravelFlags(int areanum) { int contents, tfl; contents = aasworld.areasettings[areanum].contents; tfl = 0; if (contents & AREACONTENTS_WATER) tfl |= TFL_WATER; else if (contents & AREACONTENTS_SLIME) tfl |= TFL_SLIME; else if (contents & AREACONTENTS_LAVA) tfl |= TFL_LAVA; else tfl |= TFL_AIR; if (contents & AREACONTENTS_DONOTENTER) tfl |= TFL_DONOTENTER; if (contents & AREACONTENTS_NOTTEAM1) tfl |= TFL_NOTTEAM1; if (contents & AREACONTENTS_NOTTEAM2) tfl |= TFL_NOTTEAM2; if (aasworld.areasettings[areanum].areaflags & AREA_BRIDGE) tfl |= TFL_BRIDGE; return tfl; } //end of the function AAS_GetAreaContentsTravelFlags //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== __inline int AAS_AreaContentsTravelFlags_inline(int areanum) { return aasworld.areacontentstravelflags[areanum]; } //end of the function AAS_AreaContentsTravelFlags //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_AreaContentsTravelFlags(int areanum) { return aasworld.areacontentstravelflags[areanum]; } //end of the function AAS_AreaContentsTravelFlags //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_InitAreaContentsTravelFlags(void) { int i; if (aasworld.areacontentstravelflags) FreeMemory(aasworld.areacontentstravelflags); aasworld.areacontentstravelflags = (int *) GetClearedMemory(aasworld.numareas * sizeof(int)); // for (i = 0; i < aasworld.numareas; i++) { aasworld.areacontentstravelflags[i] = AAS_GetAreaContentsTravelFlags(i); } } //end of the function AAS_InitAreaContentsTravelFlags //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_CreateReversedReachability(void) { int i, n; aas_reversedlink_t *revlink; aas_reachability_t *reach; aas_areasettings_t *settings; char *ptr; #ifdef DEBUG int starttime; starttime = Sys_MilliSeconds(); #endif //free reversed links that have already been created if (aasworld.reversedreachability) FreeMemory(aasworld.reversedreachability); //allocate memory for the reversed reachability links ptr = (char *) GetClearedMemory(aasworld.numareas * sizeof(aas_reversedreachability_t) + aasworld.reachabilitysize * sizeof(aas_reversedlink_t)); // aasworld.reversedreachability = (aas_reversedreachability_t *) ptr; //pointer to the memory for the reversed links ptr += aasworld.numareas * sizeof(aas_reversedreachability_t); //check all reachabilities of all areas for (i = 1; i < aasworld.numareas; i++) { //settings of the area settings = &aasworld.areasettings[i]; // if (settings->numreachableareas >= 128) botimport.Print(PRT_WARNING, "area %d has more than 128 reachabilities\n", i); //create reversed links for the reachabilities for (n = 0; n < settings->numreachableareas && n < 128; n++) { //reachability link reach = &aasworld.reachability[settings->firstreachablearea + n]; // revlink = (aas_reversedlink_t *) ptr; ptr += sizeof(aas_reversedlink_t); // revlink->areanum = i; revlink->linknum = settings->firstreachablearea + n; revlink->next = aasworld.reversedreachability[reach->areanum].first; aasworld.reversedreachability[reach->areanum].first = revlink; aasworld.reversedreachability[reach->areanum].numlinks++; } //end for } //end for #ifdef DEBUG botimport.Print(PRT_MESSAGE, "reversed reachability %d msec\n", Sys_MilliSeconds() - starttime); #endif } //end of the function AAS_CreateReversedReachability //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== unsigned short int AAS_AreaTravelTime(int areanum, vec3_t start, vec3_t end) { int intdist; float dist; vec3_t dir; VectorSubtract(start, end, dir); dist = VectorLength(dir); //if crouch only area if (AAS_AreaCrouch(areanum)) dist *= DISTANCEFACTOR_CROUCH; //if swim area else if (AAS_AreaSwim(areanum)) dist *= DISTANCEFACTOR_SWIM; //normal walk area else dist *= DISTANCEFACTOR_WALK; // intdist = (int) dist; //make sure the distance isn't zero if (intdist <= 0) intdist = 1; return intdist; } //end of the function AAS_AreaTravelTime //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_CalculateAreaTravelTimes(void) { int i, l, n, size; char *ptr; vec3_t end; aas_reversedreachability_t *revreach; aas_reversedlink_t *revlink; aas_reachability_t *reach; aas_areasettings_t *settings; int starttime; starttime = Sys_MilliSeconds(); //if there are still area travel times, free the memory if (aasworld.areatraveltimes) FreeMemory(aasworld.areatraveltimes); //get the total size of all the area travel times size = aasworld.numareas * sizeof(unsigned short **); for (i = 0; i < aasworld.numareas; i++) { revreach = &aasworld.reversedreachability[i]; //settings of the area settings = &aasworld.areasettings[i]; // size += settings->numreachableareas * sizeof(unsigned short *); // size += settings->numreachableareas * revreach->numlinks * sizeof(unsigned short); } //end for //allocate memory for the area travel times ptr = (char *) GetClearedMemory(size); aasworld.areatraveltimes = (unsigned short ***) ptr; ptr += aasworld.numareas * sizeof(unsigned short **); //calcluate the travel times for all the areas for (i = 0; i < aasworld.numareas; i++) { //reversed reachabilities of this area revreach = &aasworld.reversedreachability[i]; //settings of the area settings = &aasworld.areasettings[i]; // aasworld.areatraveltimes[i] = (unsigned short **) ptr; ptr += settings->numreachableareas * sizeof(unsigned short *); // for (l = 0; l < settings->numreachableareas; l++) { aasworld.areatraveltimes[i][l] = (unsigned short *) ptr; ptr += revreach->numlinks * sizeof(unsigned short); //reachability link reach = &aasworld.reachability[settings->firstreachablearea + l]; // for (n = 0, revlink = revreach->first; revlink; revlink = revlink->next, n++) { VectorCopy(aasworld.reachability[revlink->linknum].end, end); // aasworld.areatraveltimes[i][l][n] = AAS_AreaTravelTime(i, end, reach->start); } //end for } //end for } //end for #ifdef DEBUG botimport.Print(PRT_MESSAGE, "area travel times %d msec\n", Sys_MilliSeconds() - starttime); #endif } //end of the function AAS_CalculateAreaTravelTimes //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_PortalMaxTravelTime(int portalnum) { int l, n, t, maxt; aas_portal_t *portal; aas_reversedreachability_t *revreach; aas_reversedlink_t *revlink; aas_areasettings_t *settings; portal = &aasworld.portals[portalnum]; //reversed reachabilities of this portal area revreach = &aasworld.reversedreachability[portal->areanum]; //settings of the portal area settings = &aasworld.areasettings[portal->areanum]; // maxt = 0; for (l = 0; l < settings->numreachableareas; l++) { for (n = 0, revlink = revreach->first; revlink; revlink = revlink->next, n++) { t = aasworld.areatraveltimes[portal->areanum][l][n]; if (t > maxt) { maxt = t; } //end if } //end for } //end for return maxt; } //end of the function AAS_PortalMaxTravelTime //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_InitPortalMaxTravelTimes(void) { int i; if (aasworld.portalmaxtraveltimes) FreeMemory(aasworld.portalmaxtraveltimes); aasworld.portalmaxtraveltimes = (int *) GetClearedMemory(aasworld.numportals * sizeof(int)); for (i = 0; i < aasworld.numportals; i++) { aasworld.portalmaxtraveltimes[i] = AAS_PortalMaxTravelTime(i); //botimport.Print(PRT_MESSAGE, "portal %d max tt = %d\n", i, aasworld.portalmaxtraveltimes[i]); } //end for } //end of the function AAS_InitPortalMaxTravelTimes //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== /* int AAS_FreeOldestCache(void) { int i, j, bestcluster, bestarea, freed; float besttime; aas_routingcache_t *cache, *bestcache; freed = qfalse; besttime = 999999999; bestcache = NULL; bestcluster = 0; bestarea = 0; //refresh cluster cache for (i = 0; i < aasworld.numclusters; i++) { for (j = 0; j < aasworld.clusters[i].numareas; j++) { for (cache = aasworld.clusterareacache[i][j]; cache; cache = cache->next) { //never remove cache leading towards a portal if (aasworld.areasettings[cache->areanum].cluster < 0) continue; //if this cache is older than the cache we found so far if (cache->time < besttime) { bestcache = cache; bestcluster = i; bestarea = j; besttime = cache->time; } //end if } //end for } //end for } //end for if (bestcache) { cache = bestcache; if (cache->prev) cache->prev->next = cache->next; else aasworld.clusterareacache[bestcluster][bestarea] = cache->next; if (cache->next) cache->next->prev = cache->prev; AAS_FreeRoutingCache(cache); freed = qtrue; } //end if besttime = 999999999; bestcache = NULL; bestarea = 0; for (i = 0; i < aasworld.numareas; i++) { //refresh portal cache for (cache = aasworld.portalcache[i]; cache; cache = cache->next) { if (cache->time < besttime) { bestcache = cache; bestarea = i; besttime = cache->time; } //end if } //end for } //end for if (bestcache) { cache = bestcache; if (cache->prev) cache->prev->next = cache->next; else aasworld.portalcache[bestarea] = cache->next; if (cache->next) cache->next->prev = cache->prev; AAS_FreeRoutingCache(cache); freed = qtrue; } //end if return freed; } //end of the function AAS_FreeOldestCache */ //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_FreeOldestCache(void) { int clusterareanum; aas_routingcache_t *cache; for (cache = aasworld.oldestcache; cache; cache = cache->time_next) { // never free area cache leading towards a portal if (cache->type == CACHETYPE_AREA && aasworld.areasettings[cache->areanum].cluster < 0) { continue; } break; } if (cache) { // unlink the cache if (cache->type == CACHETYPE_AREA) { //number of the area in the cluster clusterareanum = AAS_ClusterAreaNum(cache->cluster, cache->areanum); // unlink from cluster area cache if (cache->prev) cache->prev->next = cache->next; else aasworld.clusterareacache[cache->cluster][clusterareanum] = cache->next; if (cache->next) cache->next->prev = cache->prev; } else { // unlink from portal cache if (cache->prev) cache->prev->next = cache->next; else aasworld.portalcache[cache->areanum] = cache->next; if (cache->next) cache->next->prev = cache->prev; } AAS_FreeRoutingCache(cache); return qtrue; } return qfalse; } //end of the function AAS_FreeOldestCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== aas_routingcache_t *AAS_AllocRoutingCache(int numtraveltimes) { aas_routingcache_t *cache; int size; // size = sizeof(aas_routingcache_t) + numtraveltimes * sizeof(unsigned short int) + numtraveltimes * sizeof(unsigned char); // routingcachesize += size; // cache = (aas_routingcache_t *) GetClearedMemory(size); cache->reachabilities = (unsigned char *) cache + sizeof(aas_routingcache_t) + numtraveltimes * sizeof(unsigned short int); cache->size = size; return cache; } //end of the function AAS_AllocRoutingCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_FreeAllClusterAreaCache(void) { int i, j; aas_routingcache_t *cache, *nextcache; aas_cluster_t *cluster; //free all cluster cache if existing if (!aasworld.clusterareacache) return; //free caches for (i = 0; i < aasworld.numclusters; i++) { cluster = &aasworld.clusters[i]; for (j = 0; j < cluster->numareas; j++) { for (cache = aasworld.clusterareacache[i][j]; cache; cache = nextcache) { nextcache = cache->next; AAS_FreeRoutingCache(cache); } //end for aasworld.clusterareacache[i][j] = NULL; } //end for } //end for //free the cluster cache array FreeMemory(aasworld.clusterareacache); aasworld.clusterareacache = NULL; } //end of the function AAS_FreeAllClusterAreaCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_InitClusterAreaCache(void) { int i, size; char *ptr; // for (size = 0, i = 0; i < aasworld.numclusters; i++) { size += aasworld.clusters[i].numareas; } //end for //two dimensional array with pointers for every cluster to routing cache //for every area in that cluster ptr = (char *) GetClearedMemory( aasworld.numclusters * sizeof(aas_routingcache_t **) + size * sizeof(aas_routingcache_t *)); aasworld.clusterareacache = (aas_routingcache_t ***) ptr; ptr += aasworld.numclusters * sizeof(aas_routingcache_t **); for (i = 0; i < aasworld.numclusters; i++) { aasworld.clusterareacache[i] = (aas_routingcache_t **) ptr; ptr += aasworld.clusters[i].numareas * sizeof(aas_routingcache_t *); } //end for } //end of the function AAS_InitClusterAreaCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_FreeAllPortalCache(void) { int i; aas_routingcache_t *cache, *nextcache; //free all portal cache if existing if (!aasworld.portalcache) return; //free portal caches for (i = 0; i < aasworld.numareas; i++) { for (cache = aasworld.portalcache[i]; cache; cache = nextcache) { nextcache = cache->next; AAS_FreeRoutingCache(cache); } //end for aasworld.portalcache[i] = NULL; } //end for FreeMemory(aasworld.portalcache); aasworld.portalcache = NULL; } //end of the function AAS_FreeAllPortalCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_InitPortalCache(void) { // aasworld.portalcache = (aas_routingcache_t **) GetClearedMemory( aasworld.numareas * sizeof(aas_routingcache_t *)); } //end of the function AAS_InitPortalCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_InitRoutingUpdate(void) { int i, maxreachabilityareas; //free routing update fields if already existing if (aasworld.areaupdate) FreeMemory(aasworld.areaupdate); // maxreachabilityareas = 0; for (i = 0; i < aasworld.numclusters; i++) { if (aasworld.clusters[i].numreachabilityareas > maxreachabilityareas) { maxreachabilityareas = aasworld.clusters[i].numreachabilityareas; } //end if } //end for //allocate memory for the routing update fields aasworld.areaupdate = (aas_routingupdate_t *) GetClearedMemory( maxreachabilityareas * sizeof(aas_routingupdate_t)); // if (aasworld.portalupdate) FreeMemory(aasworld.portalupdate); //allocate memory for the portal update fields aasworld.portalupdate = (aas_routingupdate_t *) GetClearedMemory( (aasworld.numportals+1) * sizeof(aas_routingupdate_t)); } //end of the function AAS_InitRoutingUpdate //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_CreateAllRoutingCache(void) { int i, j, t; aasworld.initialized = qtrue; botimport.Print(PRT_MESSAGE, "AAS_CreateAllRoutingCache\n"); for (i = 1; i < aasworld.numareas; i++) { if (!AAS_AreaReachability(i)) continue; for (j = 1; j < aasworld.numareas; j++) { if (i == j) continue; if (!AAS_AreaReachability(j)) continue; t = AAS_AreaTravelTimeToGoalArea(i, aasworld.areas[i].center, j, TFL_DEFAULT); //Log_Write("traveltime from %d to %d is %d", i, j, t); } //end for } //end for aasworld.initialized = qfalse; } //end of the function AAS_CreateAllRoutingCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== //the route cache header //this header is followed by numportalcache + numareacache aas_routingcache_t //structures that store routing cache typedef struct routecacheheader_s { int ident; int version; int numareas; int numclusters; int areacrc; int clustercrc; int numportalcache; int numareacache; } routecacheheader_t; #define RCID (('C'<<24)+('R'<<16)+('E'<<8)+'M') #define RCVERSION 2 //void AAS_DecompressVis(byte *in, int numareas, byte *decompressed); //int AAS_CompressVis(byte *vis, int numareas, byte *dest); void AAS_WriteRouteCache(void) { int i, j, numportalcache, numareacache, totalsize; aas_routingcache_t *cache; aas_cluster_t *cluster; fileHandle_t fp; char filename[MAX_QPATH]; routecacheheader_t routecacheheader; numportalcache = 0; for (i = 0; i < aasworld.numareas; i++) { for (cache = aasworld.portalcache[i]; cache; cache = cache->next) { numportalcache++; } //end for } //end for numareacache = 0; for (i = 0; i < aasworld.numclusters; i++) { cluster = &aasworld.clusters[i]; for (j = 0; j < cluster->numareas; j++) { for (cache = aasworld.clusterareacache[i][j]; cache; cache = cache->next) { numareacache++; } //end for } //end for } //end for // open the file for writing Com_sprintf(filename, MAX_QPATH, "maps/%s.rcd", aasworld.mapname); botimport.FS_FOpenFile( filename, &fp, FS_WRITE ); if (!fp) { AAS_Error("Unable to open file: %s\n", filename); return; } //end if //create the header routecacheheader.ident = RCID; routecacheheader.version = RCVERSION; routecacheheader.numareas = aasworld.numareas; routecacheheader.numclusters = aasworld.numclusters; routecacheheader.areacrc = CRC_ProcessString( (unsigned char *)aasworld.areas, sizeof(aas_area_t) * aasworld.numareas ); routecacheheader.clustercrc = CRC_ProcessString( (unsigned char *)aasworld.clusters, sizeof(aas_cluster_t) * aasworld.numclusters ); routecacheheader.numportalcache = numportalcache; routecacheheader.numareacache = numareacache; //write the header botimport.FS_Write(&routecacheheader, sizeof(routecacheheader_t), fp); // totalsize = 0; //write all the cache for (i = 0; i < aasworld.numareas; i++) { for (cache = aasworld.portalcache[i]; cache; cache = cache->next) { botimport.FS_Write(cache, cache->size, fp); totalsize += cache->size; } //end for } //end for for (i = 0; i < aasworld.numclusters; i++) { cluster = &aasworld.clusters[i]; for (j = 0; j < cluster->numareas; j++) { for (cache = aasworld.clusterareacache[i][j]; cache; cache = cache->next) { botimport.FS_Write(cache, cache->size, fp); totalsize += cache->size; } //end for } //end for } //end for // write the visareas /* for (i = 0; i < aasworld.numareas; i++) { if (!aasworld.areavisibility[i]) { size = 0; botimport.FS_Write(&size, sizeof(int), fp); continue; } AAS_DecompressVis( aasworld.areavisibility[i], aasworld.numareas, aasworld.decompressedvis ); size = AAS_CompressVis( aasworld.decompressedvis, aasworld.numareas, aasworld.decompressedvis ); botimport.FS_Write(&size, sizeof(int), fp); botimport.FS_Write(aasworld.decompressedvis, size, fp); } */ // botimport.FS_FCloseFile(fp); botimport.Print(PRT_MESSAGE, "\nroute cache written to %s\n", filename); botimport.Print(PRT_MESSAGE, "written %d bytes of routing cache\n", totalsize); } //end of the function AAS_WriteRouteCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== aas_routingcache_t *AAS_ReadCache(fileHandle_t fp) { int size; aas_routingcache_t *cache; botimport.FS_Read(&size, sizeof(size), fp); cache = (aas_routingcache_t *) GetMemory(size); cache->size = size; botimport.FS_Read((unsigned char *)cache + sizeof(size), size - sizeof(size), fp); cache->reachabilities = (unsigned char *) cache + sizeof(aas_routingcache_t) - sizeof(unsigned short) + (size - sizeof(aas_routingcache_t) + sizeof(unsigned short)) / 3 * 2; return cache; } //end of the function AAS_ReadCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_ReadRouteCache(void) { int i, clusterareanum;//, size; fileHandle_t fp; char filename[MAX_QPATH]; routecacheheader_t routecacheheader; aas_routingcache_t *cache; Com_sprintf(filename, MAX_QPATH, "maps/%s.rcd", aasworld.mapname); botimport.FS_FOpenFile( filename, &fp, FS_READ ); if (!fp) { return qfalse; } //end if botimport.FS_Read(&routecacheheader, sizeof(routecacheheader_t), fp ); if (routecacheheader.ident != RCID) { AAS_Error("%s is not a route cache dump\n"); return qfalse; } //end if if (routecacheheader.version != RCVERSION) { AAS_Error("route cache dump has wrong version %d, should be %d", routecacheheader.version, RCVERSION); return qfalse; } //end if if (routecacheheader.numareas != aasworld.numareas) { //AAS_Error("route cache dump has wrong number of areas\n"); return qfalse; } //end if if (routecacheheader.numclusters != aasworld.numclusters) { //AAS_Error("route cache dump has wrong number of clusters\n"); return qfalse; } //end if if (routecacheheader.areacrc != CRC_ProcessString( (unsigned char *)aasworld.areas, sizeof(aas_area_t) * aasworld.numareas )) { //AAS_Error("route cache dump area CRC incorrect\n"); return qfalse; } //end if if (routecacheheader.clustercrc != CRC_ProcessString( (unsigned char *)aasworld.clusters, sizeof(aas_cluster_t) * aasworld.numclusters )) { //AAS_Error("route cache dump cluster CRC incorrect\n"); return qfalse; } //end if //read all the portal cache for (i = 0; i < routecacheheader.numportalcache; i++) { cache = AAS_ReadCache(fp); cache->next = aasworld.portalcache[cache->areanum]; cache->prev = NULL; if (aasworld.portalcache[cache->areanum]) aasworld.portalcache[cache->areanum]->prev = cache; aasworld.portalcache[cache->areanum] = cache; } //end for //read all the cluster area cache for (i = 0; i < routecacheheader.numareacache; i++) { cache = AAS_ReadCache(fp); clusterareanum = AAS_ClusterAreaNum(cache->cluster, cache->areanum); cache->next = aasworld.clusterareacache[cache->cluster][clusterareanum]; cache->prev = NULL; if (aasworld.clusterareacache[cache->cluster][clusterareanum]) aasworld.clusterareacache[cache->cluster][clusterareanum]->prev = cache; aasworld.clusterareacache[cache->cluster][clusterareanum] = cache; } //end for // read the visareas /* aasworld.areavisibility = (byte **) GetClearedMemory(aasworld.numareas * sizeof(byte *)); aasworld.decompressedvis = (byte *) GetClearedMemory(aasworld.numareas * sizeof(byte)); for (i = 0; i < aasworld.numareas; i++) { botimport.FS_Read(&size, sizeof(size), fp ); if (size) { aasworld.areavisibility[i] = (byte *) GetMemory(size); botimport.FS_Read(aasworld.areavisibility[i], size, fp ); } } */ // botimport.FS_FCloseFile(fp); return qtrue; } //end of the function AAS_ReadRouteCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== #define MAX_REACHABILITYPASSAREAS 32 void AAS_InitReachabilityAreas(void) { int i, j, numareas, areas[MAX_REACHABILITYPASSAREAS]; int numreachareas; aas_reachability_t *reach; vec3_t start, end; if (aasworld.reachabilityareas) FreeMemory(aasworld.reachabilityareas); if (aasworld.reachabilityareaindex) FreeMemory(aasworld.reachabilityareaindex); aasworld.reachabilityareas = (aas_reachabilityareas_t *) GetClearedMemory(aasworld.reachabilitysize * sizeof(aas_reachabilityareas_t)); aasworld.reachabilityareaindex = (int *) GetClearedMemory(aasworld.reachabilitysize * MAX_REACHABILITYPASSAREAS * sizeof(int)); numreachareas = 0; for (i = 0; i < aasworld.reachabilitysize; i++) { reach = &aasworld.reachability[i]; numareas = 0; switch(reach->traveltype & TRAVELTYPE_MASK) { //trace areas from start to end case TRAVEL_BARRIERJUMP: case TRAVEL_WATERJUMP: VectorCopy(reach->start, end); end[2] = reach->end[2]; numareas = AAS_TraceAreas(reach->start, end, areas, NULL, MAX_REACHABILITYPASSAREAS); break; case TRAVEL_WALKOFFLEDGE: VectorCopy(reach->end, start); start[2] = reach->start[2]; numareas = AAS_TraceAreas(start, reach->end, areas, NULL, MAX_REACHABILITYPASSAREAS); break; case TRAVEL_GRAPPLEHOOK: numareas = AAS_TraceAreas(reach->start, reach->end, areas, NULL, MAX_REACHABILITYPASSAREAS); break; //trace arch case TRAVEL_JUMP: break; case TRAVEL_ROCKETJUMP: break; case TRAVEL_BFGJUMP: break; case TRAVEL_JUMPPAD: break; //trace from reach->start to entity center, along entity movement //and from entity center to reach->end case TRAVEL_ELEVATOR: break; case TRAVEL_FUNCBOB: break; //no areas in between case TRAVEL_WALK: break; case TRAVEL_CROUCH: break; case TRAVEL_LADDER: break; case TRAVEL_SWIM: break; case TRAVEL_TELEPORT: break; default: break; } //end switch aasworld.reachabilityareas[i].firstarea = numreachareas; aasworld.reachabilityareas[i].numareas = numareas; for (j = 0; j < numareas; j++) { aasworld.reachabilityareaindex[numreachareas++] = areas[j]; } //end for } //end for } //end of the function AAS_InitReachabilityAreas //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_InitRouting(void) { AAS_InitTravelFlagFromType(); // AAS_InitAreaContentsTravelFlags(); //initialize the routing update fields AAS_InitRoutingUpdate(); //create reversed reachability links used by the routing update algorithm AAS_CreateReversedReachability(); //initialize the cluster cache AAS_InitClusterAreaCache(); //initialize portal cache AAS_InitPortalCache(); //initialize the area travel times AAS_CalculateAreaTravelTimes(); //calculate the maximum travel times through portals AAS_InitPortalMaxTravelTimes(); //get the areas reachabilities go through AAS_InitReachabilityAreas(); // #ifdef ROUTING_DEBUG numareacacheupdates = 0; numportalcacheupdates = 0; #endif //ROUTING_DEBUG // routingcachesize = 0; max_routingcachesize = 1024 * (int) LibVarValue("max_routingcache", "4096"); // read any routing cache if available AAS_ReadRouteCache(); } //end of the function AAS_InitRouting //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_FreeRoutingCaches(void) { // free all the existing cluster area cache AAS_FreeAllClusterAreaCache(); // free all the existing portal cache AAS_FreeAllPortalCache(); // free cached travel times within areas if (aasworld.areatraveltimes) FreeMemory(aasworld.areatraveltimes); aasworld.areatraveltimes = NULL; // free cached maximum travel time through cluster portals if (aasworld.portalmaxtraveltimes) FreeMemory(aasworld.portalmaxtraveltimes); aasworld.portalmaxtraveltimes = NULL; // free reversed reachability links if (aasworld.reversedreachability) FreeMemory(aasworld.reversedreachability); aasworld.reversedreachability = NULL; // free routing algorithm memory if (aasworld.areaupdate) FreeMemory(aasworld.areaupdate); aasworld.areaupdate = NULL; if (aasworld.portalupdate) FreeMemory(aasworld.portalupdate); aasworld.portalupdate = NULL; // free lists with areas the reachabilities go through if (aasworld.reachabilityareas) FreeMemory(aasworld.reachabilityareas); aasworld.reachabilityareas = NULL; // free the reachability area index if (aasworld.reachabilityareaindex) FreeMemory(aasworld.reachabilityareaindex); aasworld.reachabilityareaindex = NULL; // free area contents travel flags look up table if (aasworld.areacontentstravelflags) FreeMemory(aasworld.areacontentstravelflags); aasworld.areacontentstravelflags = NULL; } //end of the function AAS_FreeRoutingCaches //=========================================================================== // update the given routing cache // // Parameter: areacache : routing cache to update // Returns: - // Changes Globals: - //=========================================================================== void AAS_UpdateAreaRoutingCache(aas_routingcache_t *areacache) { int i, nextareanum, cluster, badtravelflags, clusterareanum, linknum; int numreachabilityareas; unsigned short int t, startareatraveltimes[128]; //NOTE: not more than 128 reachabilities per area allowed aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate; aas_reachability_t *reach; aas_reversedreachability_t *revreach; aas_reversedlink_t *revlink; #ifdef ROUTING_DEBUG numareacacheupdates++; #endif //ROUTING_DEBUG //number of reachability areas within this cluster numreachabilityareas = aasworld.clusters[areacache->cluster].numreachabilityareas; // aasworld.frameroutingupdates++; //clear the routing update fields // Com_Memset(aasworld.areaupdate, 0, aasworld.numareas * sizeof(aas_routingupdate_t)); // badtravelflags = ~areacache->travelflags; // clusterareanum = AAS_ClusterAreaNum(areacache->cluster, areacache->areanum); if (clusterareanum >= numreachabilityareas) return; // Com_Memset(startareatraveltimes, 0, sizeof(startareatraveltimes)); // curupdate = &aasworld.areaupdate[clusterareanum]; curupdate->areanum = areacache->areanum; //VectorCopy(areacache->origin, curupdate->start); curupdate->areatraveltimes = startareatraveltimes; curupdate->tmptraveltime = areacache->starttraveltime; // areacache->traveltimes[clusterareanum] = areacache->starttraveltime; //put the area to start with in the current read list curupdate->next = NULL; curupdate->prev = NULL; updateliststart = curupdate; updatelistend = curupdate; //while there are updates in the current list while (updateliststart) { curupdate = updateliststart; // if (curupdate->next) curupdate->next->prev = NULL; else updatelistend = NULL; updateliststart = curupdate->next; // curupdate->inlist = qfalse; //check all reversed reachability links revreach = &aasworld.reversedreachability[curupdate->areanum]; // for (i = 0, revlink = revreach->first; revlink; revlink = revlink->next, i++) { linknum = revlink->linknum; reach = &aasworld.reachability[linknum]; //if there is used an undesired travel type if (AAS_TravelFlagForType_inline(reach->traveltype) & badtravelflags) continue; //if not allowed to enter the next area if (aasworld.areasettings[reach->areanum].areaflags & AREA_DISABLED) continue; //if the next area has a not allowed travel flag if (AAS_AreaContentsTravelFlags_inline(reach->areanum) & badtravelflags) continue; //number of the area the reversed reachability leads to nextareanum = revlink->areanum; //get the cluster number of the area cluster = aasworld.areasettings[nextareanum].cluster; //don't leave the cluster if (cluster > 0 && cluster != areacache->cluster) continue; //get the number of the area in the cluster clusterareanum = AAS_ClusterAreaNum(areacache->cluster, nextareanum); if (clusterareanum >= numreachabilityareas) continue; //time already travelled plus the traveltime through //the current area plus the travel time from the reachability t = curupdate->tmptraveltime + //AAS_AreaTravelTime(curupdate->areanum, curupdate->start, reach->end) + curupdate->areatraveltimes[i] + reach->traveltime; // if (!areacache->traveltimes[clusterareanum] || areacache->traveltimes[clusterareanum] > t) { areacache->traveltimes[clusterareanum] = t; areacache->reachabilities[clusterareanum] = linknum - aasworld.areasettings[nextareanum].firstreachablearea; nextupdate = &aasworld.areaupdate[clusterareanum]; nextupdate->areanum = nextareanum; nextupdate->tmptraveltime = t; //VectorCopy(reach->start, nextupdate->start); nextupdate->areatraveltimes = aasworld.areatraveltimes[nextareanum][linknum - aasworld.areasettings[nextareanum].firstreachablearea]; if (!nextupdate->inlist) { // we add the update to the end of the list // we could also use a B+ tree to have a real sorted list // on travel time which makes for faster routing updates nextupdate->next = NULL; nextupdate->prev = updatelistend; if (updatelistend) updatelistend->next = nextupdate; else updateliststart = nextupdate; updatelistend = nextupdate; nextupdate->inlist = qtrue; } //end if } //end if } //end for } //end while } //end of the function AAS_UpdateAreaRoutingCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== aas_routingcache_t *AAS_GetAreaRoutingCache(int clusternum, int areanum, int travelflags) { int clusterareanum; aas_routingcache_t *cache, *clustercache; //number of the area in the cluster clusterareanum = AAS_ClusterAreaNum(clusternum, areanum); //pointer to the cache for the area in the cluster clustercache = aasworld.clusterareacache[clusternum][clusterareanum]; //find the cache without undesired travel flags for (cache = clustercache; cache; cache = cache->next) { //if there aren't used any undesired travel types for the cache if (cache->travelflags == travelflags) break; } //end for //if there was no cache if (!cache) { cache = AAS_AllocRoutingCache(aasworld.clusters[clusternum].numreachabilityareas); cache->cluster = clusternum; cache->areanum = areanum; VectorCopy(aasworld.areas[areanum].center, cache->origin); cache->starttraveltime = 1; cache->travelflags = travelflags; cache->prev = NULL; cache->next = clustercache; if (clustercache) clustercache->prev = cache; aasworld.clusterareacache[clusternum][clusterareanum] = cache; AAS_UpdateAreaRoutingCache(cache); } //end if else { AAS_UnlinkCache(cache); } //end else //the cache has been accessed cache->time = AAS_RoutingTime(); cache->type = CACHETYPE_AREA; AAS_LinkCache(cache); return cache; } //end of the function AAS_GetAreaRoutingCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_UpdatePortalRoutingCache(aas_routingcache_t *portalcache) { int i, portalnum, clusterareanum, clusternum; unsigned short int t; aas_portal_t *portal; aas_cluster_t *cluster; aas_routingcache_t *cache; aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate; #ifdef ROUTING_DEBUG numportalcacheupdates++; #endif //ROUTING_DEBUG //clear the routing update fields // Com_Memset(aasworld.portalupdate, 0, (aasworld.numportals+1) * sizeof(aas_routingupdate_t)); // curupdate = &aasworld.portalupdate[aasworld.numportals]; curupdate->cluster = portalcache->cluster; curupdate->areanum = portalcache->areanum; curupdate->tmptraveltime = portalcache->starttraveltime; //if the start area is a cluster portal, store the travel time for that portal clusternum = aasworld.areasettings[portalcache->areanum].cluster; if (clusternum < 0) { portalcache->traveltimes[-clusternum] = portalcache->starttraveltime; } //end if //put the area to start with in the current read list curupdate->next = NULL; curupdate->prev = NULL; updateliststart = curupdate; updatelistend = curupdate; //while there are updates in the current list while (updateliststart) { curupdate = updateliststart; //remove the current update from the list if (curupdate->next) curupdate->next->prev = NULL; else updatelistend = NULL; updateliststart = curupdate->next; //current update is removed from the list curupdate->inlist = qfalse; // cluster = &aasworld.clusters[curupdate->cluster]; // cache = AAS_GetAreaRoutingCache(curupdate->cluster, curupdate->areanum, portalcache->travelflags); //take all portals of the cluster for (i = 0; i < cluster->numportals; i++) { portalnum = aasworld.portalindex[cluster->firstportal + i]; portal = &aasworld.portals[portalnum]; //if this is the portal of the current update continue if (portal->areanum == curupdate->areanum) continue; // clusterareanum = AAS_ClusterAreaNum(curupdate->cluster, portal->areanum); if (clusterareanum >= cluster->numreachabilityareas) continue; // t = cache->traveltimes[clusterareanum]; if (!t) continue; t += curupdate->tmptraveltime; // if (!portalcache->traveltimes[portalnum] || portalcache->traveltimes[portalnum] > t) { portalcache->traveltimes[portalnum] = t; nextupdate = &aasworld.portalupdate[portalnum]; if (portal->frontcluster == curupdate->cluster) { nextupdate->cluster = portal->backcluster; } //end if else { nextupdate->cluster = portal->frontcluster; } //end else nextupdate->areanum = portal->areanum; //add travel time through the actual portal area for the next update nextupdate->tmptraveltime = t + aasworld.portalmaxtraveltimes[portalnum]; if (!nextupdate->inlist) { // we add the update to the end of the list // we could also use a B+ tree to have a real sorted list // on travel time which makes for faster routing updates nextupdate->next = NULL; nextupdate->prev = updatelistend; if (updatelistend) updatelistend->next = nextupdate; else updateliststart = nextupdate; updatelistend = nextupdate; nextupdate->inlist = qtrue; } //end if } //end if } //end for } //end while } //end of the function AAS_UpdatePortalRoutingCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== aas_routingcache_t *AAS_GetPortalRoutingCache(int clusternum, int areanum, int travelflags) { aas_routingcache_t *cache; //find the cached portal routing if existing for (cache = aasworld.portalcache[areanum]; cache; cache = cache->next) { if (cache->travelflags == travelflags) break; } //end for //if the portal routing isn't cached if (!cache) { cache = AAS_AllocRoutingCache(aasworld.numportals); cache->cluster = clusternum; cache->areanum = areanum; VectorCopy(aasworld.areas[areanum].center, cache->origin); cache->starttraveltime = 1; cache->travelflags = travelflags; //add the cache to the cache list cache->prev = NULL; cache->next = aasworld.portalcache[areanum]; if (aasworld.portalcache[areanum]) aasworld.portalcache[areanum]->prev = cache; aasworld.portalcache[areanum] = cache; //update the cache AAS_UpdatePortalRoutingCache(cache); } //end if else { AAS_UnlinkCache(cache); } //end else //the cache has been accessed cache->time = AAS_RoutingTime(); cache->type = CACHETYPE_PORTAL; AAS_LinkCache(cache); return cache; } //end of the function AAS_GetPortalRoutingCache //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_AreaRouteToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags, int *traveltime, int *reachnum) { int clusternum, goalclusternum, portalnum, i, clusterareanum, bestreachnum; unsigned short int t, besttime; aas_portal_t *portal; aas_cluster_t *cluster; aas_routingcache_t *areacache, *portalcache; aas_reachability_t *reach; if (!aasworld.initialized) return qfalse; if (areanum == goalareanum) { *traveltime = 1; *reachnum = 0; return qtrue; } // if (areanum <= 0 || areanum >= aasworld.numareas) { if (bot_developer) { botimport.Print(PRT_ERROR, "AAS_AreaTravelTimeToGoalArea: areanum %d out of range\n", areanum); } //end if return qfalse; } //end if if (goalareanum <= 0 || goalareanum >= aasworld.numareas) { if (bot_developer) { botimport.Print(PRT_ERROR, "AAS_AreaTravelTimeToGoalArea: goalareanum %d out of range\n", goalareanum); } //end if return qfalse; } //end if // make sure the routing cache doesn't grow to large while(AvailableMemory() < 1 * 1024 * 1024) { if (!AAS_FreeOldestCache()) break; } // if (AAS_AreaDoNotEnter(areanum) || AAS_AreaDoNotEnter(goalareanum)) { travelflags |= TFL_DONOTENTER; } //end if //NOTE: the number of routing updates is limited per frame /* if (aasworld.frameroutingupdates > MAX_FRAMEROUTINGUPDATES) { #ifdef DEBUG //Log_Write("WARNING: AAS_AreaTravelTimeToGoalArea: frame routing updates overflowed"); #endif return 0; } //end if */ // clusternum = aasworld.areasettings[areanum].cluster; goalclusternum = aasworld.areasettings[goalareanum].cluster; //check if the area is a portal of the goal area cluster if (clusternum < 0 && goalclusternum > 0) { portal = &aasworld.portals[-clusternum]; if (portal->frontcluster == goalclusternum || portal->backcluster == goalclusternum) { clusternum = goalclusternum; } //end if } //end if //check if the goalarea is a portal of the area cluster else if (clusternum > 0 && goalclusternum < 0) { portal = &aasworld.portals[-goalclusternum]; if (portal->frontcluster == clusternum || portal->backcluster == clusternum) { goalclusternum = clusternum; } //end if } //end if //if both areas are in the same cluster //NOTE: there might be a shorter route via another cluster!!! but we don't care if (clusternum > 0 && goalclusternum > 0 && clusternum == goalclusternum) { // areacache = AAS_GetAreaRoutingCache(clusternum, goalareanum, travelflags); //the number of the area in the cluster clusterareanum = AAS_ClusterAreaNum(clusternum, areanum); //the cluster the area is in cluster = &aasworld.clusters[clusternum]; //if the area is NOT a reachability area if (clusterareanum >= cluster->numreachabilityareas) return 0; //if it is possible to travel to the goal area through this cluster if (areacache->traveltimes[clusterareanum] != 0) { *reachnum = aasworld.areasettings[areanum].firstreachablearea + areacache->reachabilities[clusterareanum]; if (!origin) { *traveltime = areacache->traveltimes[clusterareanum]; return qtrue; } reach = &aasworld.reachability[*reachnum]; *traveltime = areacache->traveltimes[clusterareanum] + AAS_AreaTravelTime(areanum, origin, reach->start); // return qtrue; } //end if } //end if // clusternum = aasworld.areasettings[areanum].cluster; goalclusternum = aasworld.areasettings[goalareanum].cluster; //if the goal area is a portal if (goalclusternum < 0) { //just assume the goal area is part of the front cluster portal = &aasworld.portals[-goalclusternum]; goalclusternum = portal->frontcluster; } //end if //get the portal routing cache portalcache = AAS_GetPortalRoutingCache(goalclusternum, goalareanum, travelflags); //if the area is a cluster portal, read directly from the portal cache if (clusternum < 0) { *traveltime = portalcache->traveltimes[-clusternum]; *reachnum = aasworld.areasettings[areanum].firstreachablearea + portalcache->reachabilities[-clusternum]; return qtrue; } //end if // besttime = 0; bestreachnum = -1; //the cluster the area is in cluster = &aasworld.clusters[clusternum]; //find the portal of the area cluster leading towards the goal area for (i = 0; i < cluster->numportals; i++) { portalnum = aasworld.portalindex[cluster->firstportal + i]; //if the goal area isn't reachable from the portal if (!portalcache->traveltimes[portalnum]) continue; // portal = &aasworld.portals[portalnum]; //get the cache of the portal area areacache = AAS_GetAreaRoutingCache(clusternum, portal->areanum, travelflags); //current area inside the current cluster clusterareanum = AAS_ClusterAreaNum(clusternum, areanum); //if the area is NOT a reachability area if (clusterareanum >= cluster->numreachabilityareas) continue; //if the portal is NOT reachable from this area if (!areacache->traveltimes[clusterareanum]) continue; //total travel time is the travel time the portal area is from //the goal area plus the travel time towards the portal area t = portalcache->traveltimes[portalnum] + areacache->traveltimes[clusterareanum]; //FIXME: add the exact travel time through the actual portal area //NOTE: for now we just add the largest travel time through the portal area // because we can't directly calculate the exact travel time // to be more specific we don't know which reachability was used to travel // into the portal area t += aasworld.portalmaxtraveltimes[portalnum]; // if (origin) { *reachnum = aasworld.areasettings[areanum].firstreachablearea + areacache->reachabilities[clusterareanum]; reach = aasworld.reachability + *reachnum; t += AAS_AreaTravelTime(areanum, origin, reach->start); } //end if //if the time is better than the one already found if (!besttime || t < besttime) { bestreachnum = *reachnum; besttime = t; } //end if } //end for if (bestreachnum < 0) { return qfalse; } *reachnum = bestreachnum; *traveltime = besttime; return qtrue; } //end of the function AAS_AreaRouteToGoalArea //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_AreaTravelTimeToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags) { int traveltime, reachnum; if (AAS_AreaRouteToGoalArea(areanum, origin, goalareanum, travelflags, &traveltime, &reachnum)) { return traveltime; } return 0; } //end of the function AAS_AreaTravelTimeToGoalArea //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_AreaReachabilityToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags) { int traveltime, reachnum; if (AAS_AreaRouteToGoalArea(areanum, origin, goalareanum, travelflags, &traveltime, &reachnum)) { return reachnum; } return 0; } //end of the function AAS_AreaReachabilityToGoalArea //=========================================================================== // predict the route and stop on one of the stop events // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_PredictRoute(struct aas_predictroute_s *route, int areanum, vec3_t origin, int goalareanum, int travelflags, int maxareas, int maxtime, int stopevent, int stopcontents, int stoptfl, int stopareanum) { int curareanum, reachnum, i, j, testareanum; vec3_t curorigin; aas_reachability_t *reach; aas_reachabilityareas_t *reachareas; //init output route->stopevent = RSE_NONE; route->endarea = goalareanum; route->endcontents = 0; route->endtravelflags = 0; VectorCopy(origin, route->endpos); route->time = 0; curareanum = areanum; VectorCopy(origin, curorigin); for (i = 0; curareanum != goalareanum && (!maxareas || i < maxareas) && i < aasworld.numareas; i++) { reachnum = AAS_AreaReachabilityToGoalArea(curareanum, curorigin, goalareanum, travelflags); if (!reachnum) { route->stopevent = RSE_NOROUTE; return qfalse; } //end if reach = &aasworld.reachability[reachnum]; // if (stopevent & RSE_USETRAVELTYPE) { if (AAS_TravelFlagForType_inline(reach->traveltype) & stoptfl) { route->stopevent = RSE_USETRAVELTYPE; route->endarea = curareanum; route->endcontents = aasworld.areasettings[curareanum].contents; route->endtravelflags = AAS_TravelFlagForType_inline(reach->traveltype); VectorCopy(reach->start, route->endpos); return qtrue; } //end if if (AAS_AreaContentsTravelFlags_inline(reach->areanum) & stoptfl) { route->stopevent = RSE_USETRAVELTYPE; route->endarea = reach->areanum; route->endcontents = aasworld.areasettings[reach->areanum].contents; route->endtravelflags = AAS_AreaContentsTravelFlags_inline(reach->areanum); VectorCopy(reach->end, route->endpos); route->time += AAS_AreaTravelTime(areanum, origin, reach->start); route->time += reach->traveltime; return qtrue; } //end if } //end if reachareas = &aasworld.reachabilityareas[reachnum]; for (j = 0; j < reachareas->numareas + 1; j++) { if (j >= reachareas->numareas) testareanum = reach->areanum; else testareanum = aasworld.reachabilityareaindex[reachareas->firstarea + j]; if (stopevent & RSE_ENTERCONTENTS) { if (aasworld.areasettings[testareanum].contents & stopcontents) { route->stopevent = RSE_ENTERCONTENTS; route->endarea = testareanum; route->endcontents = aasworld.areasettings[testareanum].contents; VectorCopy(reach->end, route->endpos); route->time += AAS_AreaTravelTime(areanum, origin, reach->start); route->time += reach->traveltime; return qtrue; } //end if } //end if if (stopevent & RSE_ENTERAREA) { if (testareanum == stopareanum) { route->stopevent = RSE_ENTERAREA; route->endarea = testareanum; route->endcontents = aasworld.areasettings[testareanum].contents; VectorCopy(reach->start, route->endpos); return qtrue; } //end if } //end if } //end for route->time += AAS_AreaTravelTime(areanum, origin, reach->start); route->time += reach->traveltime; route->endarea = reach->areanum; route->endcontents = aasworld.areasettings[reach->areanum].contents; route->endtravelflags = AAS_TravelFlagForType_inline(reach->traveltype); VectorCopy(reach->end, route->endpos); // curareanum = reach->areanum; VectorCopy(reach->end, curorigin); // if (maxtime && route->time > maxtime) break; } //end while if (curareanum != goalareanum) return qfalse; return qtrue; } //end of the function AAS_PredictRoute //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_BridgeWalkable(int areanum) { return qfalse; } //end of the function AAS_BridgeWalkable //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_ReachabilityFromNum(int num, struct aas_reachability_s *reach) { if (!aasworld.initialized) { Com_Memset(reach, 0, sizeof(aas_reachability_t)); return; } //end if if (num < 0 || num >= aasworld.reachabilitysize) { Com_Memset(reach, 0, sizeof(aas_reachability_t)); return; } //end if Com_Memcpy(reach, &aasworld.reachability[num], sizeof(aas_reachability_t));; } //end of the function AAS_ReachabilityFromNum //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_NextAreaReachability(int areanum, int reachnum) { aas_areasettings_t *settings; if (!aasworld.initialized) return 0; if (areanum <= 0 || areanum >= aasworld.numareas) { botimport.Print(PRT_ERROR, "AAS_NextAreaReachability: areanum %d out of range\n", areanum); return 0; } //end if settings = &aasworld.areasettings[areanum]; if (!reachnum) { return settings->firstreachablearea; } //end if if (reachnum < settings->firstreachablearea) { botimport.Print(PRT_FATAL, "AAS_NextAreaReachability: reachnum < settings->firstreachableara"); return 0; } //end if reachnum++; if (reachnum >= settings->firstreachablearea + settings->numreachableareas) { return 0; } //end if return reachnum; } //end of the function AAS_NextAreaReachability //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_NextModelReachability(int num, int modelnum) { int i; if (num <= 0) num = 1; else if (num >= aasworld.reachabilitysize) return 0; else num++; // for (i = num; i < aasworld.reachabilitysize; i++) { if ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_ELEVATOR) { if (aasworld.reachability[i].facenum == modelnum) return i; } //end if else if ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_FUNCBOB) { if ((aasworld.reachability[i].facenum & 0x0000FFFF) == modelnum) return i; } //end if } //end for return 0; } //end of the function AAS_NextModelReachability //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_RandomGoalArea(int areanum, int travelflags, int *goalareanum, vec3_t goalorigin) { int i, n, t; vec3_t start, end; aas_trace_t trace; //if the area has no reachabilities if (!AAS_AreaReachability(areanum)) return qfalse; // n = aasworld.numareas * random(); for (i = 0; i < aasworld.numareas; i++) { if (n <= 0) n = 1; if (n >= aasworld.numareas) n = 1; if (AAS_AreaReachability(n)) { t = AAS_AreaTravelTimeToGoalArea(areanum, aasworld.areas[areanum].center, n, travelflags); //if the goal is reachable if (t > 0) { if (AAS_AreaSwim(n)) { *goalareanum = n; VectorCopy(aasworld.areas[n].center, goalorigin); //botimport.Print(PRT_MESSAGE, "found random goal area %d\n", *goalareanum); return qtrue; } //end if VectorCopy(aasworld.areas[n].center, start); if (!AAS_PointAreaNum(start)) Log_Write("area %d center %f %f %f in solid?", n, start[0], start[1], start[2]); VectorCopy(start, end); end[2] -= 300; trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1); if (!trace.startsolid && trace.fraction < 1 && AAS_PointAreaNum(trace.endpos) == n) { if (AAS_AreaGroundFaceArea(n) > 300) { *goalareanum = n; VectorCopy(trace.endpos, goalorigin); //botimport.Print(PRT_MESSAGE, "found random goal area %d\n", *goalareanum); return qtrue; } //end if } //end if } //end if } //end if n++; } //end for return qfalse; } //end of the function AAS_RandomGoalArea //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_AreaVisible(int srcarea, int destarea) { return qfalse; } //end of the function AAS_AreaVisible //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== float DistancePointToLine(vec3_t v1, vec3_t v2, vec3_t point) { vec3_t vec, p2; AAS_ProjectPointOntoVector(point, v1, v2, p2); VectorSubtract(point, p2, vec); return VectorLength(vec); } //end of the function DistancePointToLine //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_NearestHideArea(int srcnum, vec3_t origin, int areanum, int enemynum, vec3_t enemyorigin, int enemyareanum, int travelflags) { int i, j, nextareanum, badtravelflags, numreach, bestarea; unsigned short int t, besttraveltime; static unsigned short int *hidetraveltimes; aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate; aas_reachability_t *reach; float dist1, dist2; vec3_t v1, v2, p; qboolean startVisible; // if (!hidetraveltimes) { hidetraveltimes = (unsigned short int *) GetClearedMemory(aasworld.numareas * sizeof(unsigned short int)); } //end if else { Com_Memset(hidetraveltimes, 0, aasworld.numareas * sizeof(unsigned short int)); } //end else besttraveltime = 0; bestarea = 0; //assume visible startVisible = qtrue; // badtravelflags = ~travelflags; // curupdate = &aasworld.areaupdate[areanum]; curupdate->areanum = areanum; VectorCopy(origin, curupdate->start); curupdate->areatraveltimes = aasworld.areatraveltimes[areanum][0]; curupdate->tmptraveltime = 0; //put the area to start with in the current read list curupdate->next = NULL; curupdate->prev = NULL; updateliststart = curupdate; updatelistend = curupdate; //while there are updates in the list while (updateliststart) { curupdate = updateliststart; // if (curupdate->next) curupdate->next->prev = NULL; else updatelistend = NULL; updateliststart = curupdate->next; // curupdate->inlist = qfalse; //check all reversed reachability links numreach = aasworld.areasettings[curupdate->areanum].numreachableareas; reach = &aasworld.reachability[aasworld.areasettings[curupdate->areanum].firstreachablearea]; // for (i = 0; i < numreach; i++, reach++) { //if an undesired travel type is used if (AAS_TravelFlagForType_inline(reach->traveltype) & badtravelflags) continue; // if (AAS_AreaContentsTravelFlags_inline(reach->areanum) & badtravelflags) continue; //number of the area the reachability leads to nextareanum = reach->areanum; // if this moves us into the enemies area, skip it if (nextareanum == enemyareanum) continue; //time already travelled plus the traveltime through //the current area plus the travel time from the reachability t = curupdate->tmptraveltime + AAS_AreaTravelTime(curupdate->areanum, curupdate->start, reach->start) + reach->traveltime; //avoid going near the enemy AAS_ProjectPointOntoVector(enemyorigin, curupdate->start, reach->end, p); for (j = 0; j < 3; j++) if ((p[j] > curupdate->start[j] && p[j] > reach->end[j]) || (p[j] < curupdate->start[j] && p[j] < reach->end[j])) break; if (j < 3) { VectorSubtract(enemyorigin, reach->end, v2); } //end if else { VectorSubtract(enemyorigin, p, v2); } //end else dist2 = VectorLength(v2); //never go through the enemy if (dist2 < 40) continue; // VectorSubtract(enemyorigin, curupdate->start, v1); dist1 = VectorLength(v1); // if (dist2 < dist1) { t += (dist1 - dist2) * 10; } // if we weren't visible when starting, make sure we don't move into their view if (!startVisible && AAS_AreaVisible(enemyareanum, nextareanum)) { continue; } // if (besttraveltime && t >= besttraveltime) continue; // if (!hidetraveltimes[nextareanum] || hidetraveltimes[nextareanum] > t) { //if the nextarea is not visible from the enemy area if (!AAS_AreaVisible(enemyareanum, nextareanum)) { besttraveltime = t; bestarea = nextareanum; } //end if hidetraveltimes[nextareanum] = t; nextupdate = &aasworld.areaupdate[nextareanum]; nextupdate->areanum = nextareanum; nextupdate->tmptraveltime = t; //remember where we entered this area VectorCopy(reach->end, nextupdate->start); //if this update is not in the list yet if (!nextupdate->inlist) { //add the new update to the end of the list nextupdate->next = NULL; nextupdate->prev = updatelistend; if (updatelistend) updatelistend->next = nextupdate; else updateliststart = nextupdate; updatelistend = nextupdate; nextupdate->inlist = qtrue; } //end if } //end if } //end for } //end while return bestarea; } //end of the function AAS_NearestHideArea ================================================ FILE: src/engine/botlib/be_aas_route.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_route.h * * desc: AAS * * $Archive: /source/code/botlib/be_aas_route.h $ * *****************************************************************************/ #ifdef AASINTERN //initialize the AAS routing void AAS_InitRouting(void); //free the AAS routing caches void AAS_FreeRoutingCaches(void); //returns the travel time from start to end in the given area unsigned short int AAS_AreaTravelTime(int areanum, vec3_t start, vec3_t end); // void AAS_CreateAllRoutingCache(void); void AAS_WriteRouteCache(void); // void AAS_RoutingInfo(void); #endif //AASINTERN //returns the travel flag for the given travel type int AAS_TravelFlagForType(int traveltype); //return the travel flag(s) for traveling through this area int AAS_AreaContentsTravelFlags(int areanum); //returns the index of the next reachability for the given area int AAS_NextAreaReachability(int areanum, int reachnum); //returns the reachability with the given index void AAS_ReachabilityFromNum(int num, struct aas_reachability_s *reach); //returns a random goal area and goal origin int AAS_RandomGoalArea(int areanum, int travelflags, int *goalareanum, vec3_t goalorigin); //enable or disable an area for routing int AAS_EnableRoutingArea(int areanum, int enable); //returns the travel time within the given area from start to end unsigned short int AAS_AreaTravelTime(int areanum, vec3_t start, vec3_t end); //returns the travel time from the area to the goal area using the given travel flags int AAS_AreaTravelTimeToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags); //predict a route up to a stop event int AAS_PredictRoute(struct aas_predictroute_s *route, int areanum, vec3_t origin, int goalareanum, int travelflags, int maxareas, int maxtime, int stopevent, int stopcontents, int stoptfl, int stopareanum); ================================================ FILE: src/engine/botlib/be_aas_routealt.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_routealt.c * * desc: AAS * * $Archive: /MissionPack/code/botlib/be_aas_routealt.c $ * *****************************************************************************/ #include "../../game/q_shared.h" #include "l_utils.h" #include "l_memory.h" #include "l_log.h" #include "l_script.h" #include "l_precomp.h" #include "l_struct.h" #include "aasfile.h" #include "../../game/botlib.h" #include "../../game/be_aas.h" #include "be_aas_funcs.h" #include "be_interface.h" #include "be_aas_def.h" #define ENABLE_ALTROUTING //#define ALTROUTE_DEBUG typedef struct midrangearea_s { int valid; unsigned short starttime; unsigned short goaltime; } midrangearea_t; midrangearea_t *midrangeareas; int *clusterareas; int numclusterareas; //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_AltRoutingFloodCluster_r(int areanum) { int i, otherareanum; aas_area_t *area; aas_face_t *face; //add the current area to the areas of the current cluster clusterareas[numclusterareas] = areanum; numclusterareas++; //remove the area from the mid range areas midrangeareas[areanum].valid = qfalse; //flood to other areas through the faces of this area area = &aasworld.areas[areanum]; for (i = 0; i < area->numfaces; i++) { face = &aasworld.faces[abs(aasworld.faceindex[area->firstface + i])]; //get the area at the other side of the face if (face->frontarea == areanum) otherareanum = face->backarea; else otherareanum = face->frontarea; //if there is an area at the other side of this face if (!otherareanum) continue; //if the other area is not a midrange area if (!midrangeareas[otherareanum].valid) continue; // AAS_AltRoutingFloodCluster_r(otherareanum); } //end for } //end of the function AAS_AltRoutingFloodCluster_r //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_AlternativeRouteGoals(vec3_t start, int startareanum, vec3_t goal, int goalareanum, int travelflags, aas_altroutegoal_t *altroutegoals, int maxaltroutegoals, int type) { #ifndef ENABLE_ALTROUTING return 0; #else int i, j, bestareanum; int numaltroutegoals, nummidrangeareas; int starttime, goaltime, goaltraveltime; float dist, bestdist; vec3_t mid, dir; #ifdef ALTROUTE_DEBUG int startmillisecs; startmillisecs = Sys_MilliSeconds(); #endif if (!startareanum || !goalareanum) return 0; //travel time towards the goal area goaltraveltime = AAS_AreaTravelTimeToGoalArea(startareanum, start, goalareanum, travelflags); //clear the midrange areas Com_Memset(midrangeareas, 0, aasworld.numareas * sizeof(midrangearea_t)); numaltroutegoals = 0; // nummidrangeareas = 0; // for (i = 1; i < aasworld.numareas; i++) { // if (!(type & ALTROUTEGOAL_ALL)) { if (!(type & ALTROUTEGOAL_CLUSTERPORTALS && (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL))) { if (!(type & ALTROUTEGOAL_VIEWPORTALS && (aasworld.areasettings[i].contents & AREACONTENTS_VIEWPORTAL))) { continue; } //end if } //end if } //end if //if the area has no reachabilities if (!AAS_AreaReachability(i)) continue; //tavel time from the area to the start area starttime = AAS_AreaTravelTimeToGoalArea(startareanum, start, i, travelflags); if (!starttime) continue; //if the travel time from the start to the area is greater than the shortest goal travel time if (starttime > (float) 1.1 * goaltraveltime) continue; //travel time from the area to the goal area goaltime = AAS_AreaTravelTimeToGoalArea(i, NULL, goalareanum, travelflags); if (!goaltime) continue; //if the travel time from the area to the goal is greater than the shortest goal travel time if (goaltime > (float) 0.8 * goaltraveltime) continue; //this is a mid range area midrangeareas[i].valid = qtrue; midrangeareas[i].starttime = starttime; midrangeareas[i].goaltime = goaltime; Log_Write("%d midrange area %d", nummidrangeareas, i); nummidrangeareas++; } //end for // for (i = 1; i < aasworld.numareas; i++) { if (!midrangeareas[i].valid) continue; //get the areas in one cluster numclusterareas = 0; AAS_AltRoutingFloodCluster_r(i); //now we've got a cluster with areas through which an alternative route could go //get the 'center' of the cluster VectorClear(mid); for (j = 0; j < numclusterareas; j++) { VectorAdd(mid, aasworld.areas[clusterareas[j]].center, mid); } //end for VectorScale(mid, 1.0 / numclusterareas, mid); //get the area closest to the center of the cluster bestdist = 999999; bestareanum = 0; for (j = 0; j < numclusterareas; j++) { VectorSubtract(mid, aasworld.areas[clusterareas[j]].center, dir); dist = VectorLength(dir); if (dist < bestdist) { bestdist = dist; bestareanum = clusterareas[j]; } //end if } //end for //now we've got an area for an alternative route //FIXME: add alternative goal origin VectorCopy(aasworld.areas[bestareanum].center, altroutegoals[numaltroutegoals].origin); altroutegoals[numaltroutegoals].areanum = bestareanum; altroutegoals[numaltroutegoals].starttraveltime = midrangeareas[bestareanum].starttime; altroutegoals[numaltroutegoals].goaltraveltime = midrangeareas[bestareanum].goaltime; altroutegoals[numaltroutegoals].extratraveltime = (midrangeareas[bestareanum].starttime + midrangeareas[bestareanum].goaltime) - goaltraveltime; numaltroutegoals++; // #ifdef ALTROUTE_DEBUG AAS_ShowAreaPolygons(bestareanum, 1, qtrue); #endif //don't return more than the maximum alternative route goals if (numaltroutegoals >= maxaltroutegoals) break; } //end for #ifdef ALTROUTE_DEBUG botimport.Print(PRT_MESSAGE, "alternative route goals in %d msec\n", Sys_MilliSeconds() - startmillisecs); #endif return numaltroutegoals; #endif } //end of the function AAS_AlternativeRouteGoals //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_InitAlternativeRouting(void) { #ifdef ENABLE_ALTROUTING if (midrangeareas) FreeMemory(midrangeareas); midrangeareas = (midrangearea_t *) GetMemory(aasworld.numareas * sizeof(midrangearea_t)); if (clusterareas) FreeMemory(clusterareas); clusterareas = (int *) GetMemory(aasworld.numareas * sizeof(int)); #endif } //end of the function AAS_InitAlternativeRouting //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_ShutdownAlternativeRouting(void) { #ifdef ENABLE_ALTROUTING if (midrangeareas) FreeMemory(midrangeareas); midrangeareas = NULL; if (clusterareas) FreeMemory(clusterareas); clusterareas = NULL; numclusterareas = 0; #endif } //end of the function AAS_ShutdownAlternativeRouting ================================================ FILE: src/engine/botlib/be_aas_routealt.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_routealt.h * * desc: AAS * * $Archive: /source/code/botlib/be_aas_routealt.h $ * *****************************************************************************/ #ifdef AASINTERN void AAS_InitAlternativeRouting(void); void AAS_ShutdownAlternativeRouting(void); #endif //AASINTERN int AAS_AlternativeRouteGoals(vec3_t start, int startareanum, vec3_t goal, int goalareanum, int travelflags, aas_altroutegoal_t *altroutegoals, int maxaltroutegoals, int type); ================================================ FILE: src/engine/botlib/be_aas_sample.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_sample.c * * desc: AAS environment sampling * * $Archive: /MissionPack/code/botlib/be_aas_sample.c $ * *****************************************************************************/ #include "../../game/q_shared.h" #include "l_memory.h" #include "l_script.h" #include "l_precomp.h" #include "l_struct.h" #ifndef BSPC #include "l_libvar.h" #endif #include "aasfile.h" #include "../../game/botlib.h" #include "../../game/be_aas.h" #include "be_interface.h" #include "be_aas_funcs.h" #include "be_aas_def.h" extern botlib_import_t botimport; //#define AAS_SAMPLE_DEBUG #define BBOX_NORMAL_EPSILON 0.001 #define ON_EPSILON 0 //0.0005 #define TRACEPLANE_EPSILON 0.125 typedef struct aas_tracestack_s { vec3_t start; //start point of the piece of line to trace vec3_t end; //end point of the piece of line to trace int planenum; //last plane used as splitter int nodenum; //node found after splitting with planenum } aas_tracestack_t; int numaaslinks; //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_PresenceTypeBoundingBox(int presencetype, vec3_t mins, vec3_t maxs) { int index; //bounding box size for each presence type vec3_t boxmins[3] = {{0, 0, 0}, {-15, -15, -24}, {-15, -15, -24}}; vec3_t boxmaxs[3] = {{0, 0, 0}, { 15, 15, 32}, { 15, 15, 8}}; if (presencetype == PRESENCE_NORMAL) index = 1; else if (presencetype == PRESENCE_CROUCH) index = 2; else { botimport.Print(PRT_FATAL, "AAS_PresenceTypeBoundingBox: unknown presence type\n"); index = 2; } //end if VectorCopy(boxmins[index], mins); VectorCopy(boxmaxs[index], maxs); } //end of the function AAS_PresenceTypeBoundingBox //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_InitAASLinkHeap(void) { int i, max_aaslinks; max_aaslinks = aasworld.linkheapsize; //if there's no link heap present if (!aasworld.linkheap) { #ifdef BSPC max_aaslinks = 6144; #else max_aaslinks = (int) LibVarValue("max_aaslinks", "6144"); #endif if (max_aaslinks < 0) max_aaslinks = 0; aasworld.linkheapsize = max_aaslinks; aasworld.linkheap = (aas_link_t *) GetHunkMemory(max_aaslinks * sizeof(aas_link_t)); } //end if //link the links on the heap aasworld.linkheap[0].prev_ent = NULL; aasworld.linkheap[0].next_ent = &aasworld.linkheap[1]; for (i = 1; i < max_aaslinks-1; i++) { aasworld.linkheap[i].prev_ent = &aasworld.linkheap[i - 1]; aasworld.linkheap[i].next_ent = &aasworld.linkheap[i + 1]; } //end for aasworld.linkheap[max_aaslinks-1].prev_ent = &aasworld.linkheap[max_aaslinks-2]; aasworld.linkheap[max_aaslinks-1].next_ent = NULL; //pointer to the first free link aasworld.freelinks = &aasworld.linkheap[0]; // numaaslinks = max_aaslinks; } //end of the function AAS_InitAASLinkHeap //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_FreeAASLinkHeap(void) { if (aasworld.linkheap) FreeMemory(aasworld.linkheap); aasworld.linkheap = NULL; aasworld.linkheapsize = 0; } //end of the function AAS_FreeAASLinkHeap //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== aas_link_t *AAS_AllocAASLink(void) { aas_link_t *link; link = aasworld.freelinks; if (!link) { #ifndef BSPC if (bot_developer) #endif { botimport.Print(PRT_FATAL, "empty aas link heap\n"); } //end if return NULL; } //end if if (aasworld.freelinks) aasworld.freelinks = aasworld.freelinks->next_ent; if (aasworld.freelinks) aasworld.freelinks->prev_ent = NULL; numaaslinks--; return link; } //end of the function AAS_AllocAASLink //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_DeAllocAASLink(aas_link_t *link) { if (aasworld.freelinks) aasworld.freelinks->prev_ent = link; link->prev_ent = NULL; link->next_ent = aasworld.freelinks; link->prev_area = NULL; link->next_area = NULL; aasworld.freelinks = link; numaaslinks++; } //end of the function AAS_DeAllocAASLink //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_InitAASLinkedEntities(void) { if (!aasworld.loaded) return; if (aasworld.arealinkedentities) FreeMemory(aasworld.arealinkedentities); aasworld.arealinkedentities = (aas_link_t **) GetClearedHunkMemory( aasworld.numareas * sizeof(aas_link_t *)); } //end of the function AAS_InitAASLinkedEntities //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_FreeAASLinkedEntities(void) { if (aasworld.arealinkedentities) FreeMemory(aasworld.arealinkedentities); aasworld.arealinkedentities = NULL; } //end of the function AAS_InitAASLinkedEntities //=========================================================================== // returns the AAS area the point is in // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_PointAreaNum(vec3_t point) { int nodenum; vec_t dist; aas_node_t *node; aas_plane_t *plane; if (!aasworld.loaded) { botimport.Print(PRT_ERROR, "AAS_PointAreaNum: aas not loaded\n"); return 0; } //end if //start with node 1 because node zero is a dummy used for solid leafs nodenum = 1; while (nodenum > 0) { // botimport.Print(PRT_MESSAGE, "[%d]", nodenum); #ifdef AAS_SAMPLE_DEBUG if (nodenum >= aasworld.numnodes) { botimport.Print(PRT_ERROR, "nodenum = %d >= aasworld.numnodes = %d\n", nodenum, aasworld.numnodes); return 0; } //end if #endif //AAS_SAMPLE_DEBUG node = &aasworld.nodes[nodenum]; #ifdef AAS_SAMPLE_DEBUG if (node->planenum < 0 || node->planenum >= aasworld.numplanes) { botimport.Print(PRT_ERROR, "node->planenum = %d >= aasworld.numplanes = %d\n", node->planenum, aasworld.numplanes); return 0; } //end if #endif //AAS_SAMPLE_DEBUG plane = &aasworld.planes[node->planenum]; dist = DotProduct(point, plane->normal) - plane->dist; if (dist > 0) nodenum = node->children[0]; else nodenum = node->children[1]; } //end while if (!nodenum) { #ifdef AAS_SAMPLE_DEBUG botimport.Print(PRT_MESSAGE, "in solid\n"); #endif //AAS_SAMPLE_DEBUG return 0; } //end if return -nodenum; } //end of the function AAS_PointAreaNum //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_PointReachabilityAreaIndex( vec3_t origin ) { int areanum, cluster, i, index; if (!aasworld.initialized) return 0; if ( !origin ) { index = 0; for (i = 0; i < aasworld.numclusters; i++) { index += aasworld.clusters[i].numreachabilityareas; } //end for return index; } //end if areanum = AAS_PointAreaNum( origin ); if ( !areanum || !AAS_AreaReachability(areanum) ) return 0; cluster = aasworld.areasettings[areanum].cluster; areanum = aasworld.areasettings[areanum].clusterareanum; if (cluster < 0) { cluster = aasworld.portals[-cluster].frontcluster; areanum = aasworld.portals[-cluster].clusterareanum[0]; } //end if index = 0; for (i = 0; i < cluster; i++) { index += aasworld.clusters[i].numreachabilityareas; } //end for index += areanum; return index; } //end of the function AAS_PointReachabilityAreaIndex //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_AreaCluster(int areanum) { if (areanum <= 0 || areanum >= aasworld.numareas) { botimport.Print(PRT_ERROR, "AAS_AreaCluster: invalid area number\n"); return 0; } //end if return aasworld.areasettings[areanum].cluster; } //end of the function AAS_AreaCluster //=========================================================================== // returns the presence types of the given area // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_AreaPresenceType(int areanum) { if (!aasworld.loaded) return 0; if (areanum <= 0 || areanum >= aasworld.numareas) { botimport.Print(PRT_ERROR, "AAS_AreaPresenceType: invalid area number\n"); return 0; } //end if return aasworld.areasettings[areanum].presencetype; } //end of the function AAS_AreaPresenceType //=========================================================================== // returns the presence type at the given point // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_PointPresenceType(vec3_t point) { int areanum; if (!aasworld.loaded) return 0; areanum = AAS_PointAreaNum(point); if (!areanum) return PRESENCE_NONE; return aasworld.areasettings[areanum].presencetype; } //end of the function AAS_PointPresenceType //=========================================================================== // calculates the minimum distance between the origin of the box and the // given plane when both will collide on the given side of the plane // // normal = normal vector of plane to calculate distance from // mins = minimums of box relative to origin // maxs = maximums of box relative to origin // side = side of the plane we want to calculate the distance from // 0 normal vector side // 1 not normal vector side // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== vec_t AAS_BoxOriginDistanceFromPlane(vec3_t normal, vec3_t mins, vec3_t maxs, int side) { vec3_t v1, v2; int i; //swap maxs and mins when on the other side of the plane if (side) { //get a point of the box that would be one of the first //to collide with the plane for (i = 0; i < 3; i++) { if (normal[i] > BBOX_NORMAL_EPSILON) v1[i] = maxs[i]; else if (normal[i] < -BBOX_NORMAL_EPSILON) v1[i] = mins[i]; else v1[i] = 0; } //end for } //end if else { //get a point of the box that would be one of the first //to collide with the plane for (i = 0; i < 3; i++) { if (normal[i] > BBOX_NORMAL_EPSILON) v1[i] = mins[i]; else if (normal[i] < -BBOX_NORMAL_EPSILON) v1[i] = maxs[i]; else v1[i] = 0; } //end for } //end else // VectorCopy(normal, v2); VectorInverse(v2); // VectorNegate(normal, v2); return DotProduct(v1, v2); } //end of the function AAS_BoxOriginDistanceFromPlane //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== qboolean AAS_AreaEntityCollision(int areanum, vec3_t start, vec3_t end, int presencetype, int passent, aas_trace_t *trace) { int collision; vec3_t boxmins, boxmaxs; aas_link_t *link; bsp_trace_t bsptrace; AAS_PresenceTypeBoundingBox(presencetype, boxmins, boxmaxs); Com_Memset(&bsptrace, 0, sizeof(bsp_trace_t)); //make compiler happy //assume no collision bsptrace.fraction = 1; collision = qfalse; for (link = aasworld.arealinkedentities[areanum]; link; link = link->next_ent) { //ignore the pass entity if (link->entnum == passent) continue; // if (AAS_EntityCollision(link->entnum, start, boxmins, boxmaxs, end, CONTENTS_SOLID|CONTENTS_PLAYERCLIP, &bsptrace)) { collision = qtrue; } //end if } //end for if (collision) { trace->startsolid = bsptrace.startsolid; trace->ent = bsptrace.ent; VectorCopy(bsptrace.endpos, trace->endpos); trace->area = 0; trace->planenum = 0; return qtrue; } //end if return qfalse; } //end of the function AAS_AreaEntityCollision //=========================================================================== // recursive subdivision of the line by the BSP tree. // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== aas_trace_t AAS_TraceClientBBox(vec3_t start, vec3_t end, int presencetype, int passent) { int side, nodenum, tmpplanenum; float front, back, frac; vec3_t cur_start, cur_end, cur_mid, v1, v2; aas_tracestack_t tracestack[127]; aas_tracestack_t *tstack_p; aas_node_t *aasnode; aas_plane_t *plane; aas_trace_t trace; //clear the trace structure Com_Memset(&trace, 0, sizeof(aas_trace_t)); if (!aasworld.loaded) return trace; tstack_p = tracestack; //we start with the whole line on the stack VectorCopy(start, tstack_p->start); VectorCopy(end, tstack_p->end); tstack_p->planenum = 0; //start with node 1 because node zero is a dummy for a solid leaf tstack_p->nodenum = 1; //starting at the root of the tree tstack_p++; while (1) { //pop up the stack tstack_p--; //if the trace stack is empty (ended up with a piece of the //line to be traced in an area) if (tstack_p < tracestack) { tstack_p++; //nothing was hit trace.startsolid = qfalse; trace.fraction = 1.0; //endpos is the end of the line VectorCopy(end, trace.endpos); //nothing hit trace.ent = 0; trace.area = 0; trace.planenum = 0; return trace; } //end if //number of the current node to test the line against nodenum = tstack_p->nodenum; //if it is an area if (nodenum < 0) { #ifdef AAS_SAMPLE_DEBUG if (-nodenum > aasworld.numareasettings) { botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: -nodenum out of range\n"); return trace; } //end if #endif //AAS_SAMPLE_DEBUG //botimport.Print(PRT_MESSAGE, "areanum = %d, must be %d\n", -nodenum, AAS_PointAreaNum(start)); //if can't enter the area because it hasn't got the right presence type if (!(aasworld.areasettings[-nodenum].presencetype & presencetype)) { //if the start point is still the initial start point //NOTE: no need for epsilons because the points will be //exactly the same when they're both the start point if (tstack_p->start[0] == start[0] && tstack_p->start[1] == start[1] && tstack_p->start[2] == start[2]) { trace.startsolid = qtrue; trace.fraction = 0.0; VectorClear(v1); } //end if else { trace.startsolid = qfalse; VectorSubtract(end, start, v1); VectorSubtract(tstack_p->start, start, v2); trace.fraction = VectorLength(v2) / VectorNormalize(v1); VectorMA(tstack_p->start, -0.125, v1, tstack_p->start); } //end else VectorCopy(tstack_p->start, trace.endpos); trace.ent = 0; trace.area = -nodenum; // VectorSubtract(end, start, v1); trace.planenum = tstack_p->planenum; //always take the plane with normal facing towards the trace start plane = &aasworld.planes[trace.planenum]; if (DotProduct(v1, plane->normal) > 0) trace.planenum ^= 1; return trace; } //end if else { if (passent >= 0) { if (AAS_AreaEntityCollision(-nodenum, tstack_p->start, tstack_p->end, presencetype, passent, &trace)) { if (!trace.startsolid) { VectorSubtract(end, start, v1); VectorSubtract(trace.endpos, start, v2); trace.fraction = VectorLength(v2) / VectorLength(v1); } //end if return trace; } //end if } //end if } //end else trace.lastarea = -nodenum; continue; } //end if //if it is a solid leaf if (!nodenum) { //if the start point is still the initial start point //NOTE: no need for epsilons because the points will be //exactly the same when they're both the start point if (tstack_p->start[0] == start[0] && tstack_p->start[1] == start[1] && tstack_p->start[2] == start[2]) { trace.startsolid = qtrue; trace.fraction = 0.0; VectorClear(v1); } //end if else { trace.startsolid = qfalse; VectorSubtract(end, start, v1); VectorSubtract(tstack_p->start, start, v2); trace.fraction = VectorLength(v2) / VectorNormalize(v1); VectorMA(tstack_p->start, -0.125, v1, tstack_p->start); } //end else VectorCopy(tstack_p->start, trace.endpos); trace.ent = 0; trace.area = 0; //hit solid leaf // VectorSubtract(end, start, v1); trace.planenum = tstack_p->planenum; //always take the plane with normal facing towards the trace start plane = &aasworld.planes[trace.planenum]; if (DotProduct(v1, plane->normal) > 0) trace.planenum ^= 1; return trace; } //end if #ifdef AAS_SAMPLE_DEBUG if (nodenum > aasworld.numnodes) { botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: nodenum out of range\n"); return trace; } //end if #endif //AAS_SAMPLE_DEBUG //the node to test against aasnode = &aasworld.nodes[nodenum]; //start point of current line to test against node VectorCopy(tstack_p->start, cur_start); //end point of the current line to test against node VectorCopy(tstack_p->end, cur_end); //the current node plane plane = &aasworld.planes[aasnode->planenum]; front = DotProduct(cur_start, plane->normal) - plane->dist; back = DotProduct(cur_end, plane->normal) - plane->dist; // bk010221 - old location of FPE hack and divide by zero expression //if the whole to be traced line is totally at the front of this node //only go down the tree with the front child if ((front >= -ON_EPSILON && back >= -ON_EPSILON)) { //keep the current start and end point on the stack //and go down the tree with the front child tstack_p->nodenum = aasnode->children[0]; tstack_p++; if (tstack_p >= &tracestack[127]) { botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n"); return trace; } //end if } //end if //if the whole to be traced line is totally at the back of this node //only go down the tree with the back child else if ((front < ON_EPSILON && back < ON_EPSILON)) { //keep the current start and end point on the stack //and go down the tree with the back child tstack_p->nodenum = aasnode->children[1]; tstack_p++; if (tstack_p >= &tracestack[127]) { botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n"); return trace; } //end if } //end if //go down the tree both at the front and back of the node else { tmpplanenum = tstack_p->planenum; // bk010221 - new location of divide by zero (see above) if ( front == back ) front -= 0.001f; // bk0101022 - hack/FPE //calculate the hitpoint with the node (split point of the line) //put the crosspoint TRACEPLANE_EPSILON pixels on the near side if (front < 0) frac = (front + TRACEPLANE_EPSILON)/(front-back); else frac = (front - TRACEPLANE_EPSILON)/(front-back); // bk010221 // if (frac < 0) frac = 0.001f; //0 else if (frac > 1) frac = 0.999f; //1 //frac = front / (front-back); // cur_mid[0] = cur_start[0] + (cur_end[0] - cur_start[0]) * frac; cur_mid[1] = cur_start[1] + (cur_end[1] - cur_start[1]) * frac; cur_mid[2] = cur_start[2] + (cur_end[2] - cur_start[2]) * frac; // AAS_DrawPlaneCross(cur_mid, plane->normal, plane->dist, plane->type, LINECOLOR_RED); //side the front part of the line is on side = front < 0; //first put the end part of the line on the stack (back side) VectorCopy(cur_mid, tstack_p->start); //not necesary to store because still on stack //VectorCopy(cur_end, tstack_p->end); tstack_p->planenum = aasnode->planenum; tstack_p->nodenum = aasnode->children[!side]; tstack_p++; if (tstack_p >= &tracestack[127]) { botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n"); return trace; } //end if //now put the part near the start of the line on the stack so we will //continue with thats part first. This way we'll find the first //hit of the bbox VectorCopy(cur_start, tstack_p->start); VectorCopy(cur_mid, tstack_p->end); tstack_p->planenum = tmpplanenum; tstack_p->nodenum = aasnode->children[side]; tstack_p++; if (tstack_p >= &tracestack[127]) { botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n"); return trace; } //end if } //end else } //end while // return trace; } //end of the function AAS_TraceClientBBox //=========================================================================== // recursive subdivision of the line by the BSP tree. // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_TraceAreas(vec3_t start, vec3_t end, int *areas, vec3_t *points, int maxareas) { int side, nodenum, tmpplanenum; int numareas; float front, back, frac; vec3_t cur_start, cur_end, cur_mid; aas_tracestack_t tracestack[127]; aas_tracestack_t *tstack_p; aas_node_t *aasnode; aas_plane_t *plane; numareas = 0; areas[0] = 0; if (!aasworld.loaded) return numareas; tstack_p = tracestack; //we start with the whole line on the stack VectorCopy(start, tstack_p->start); VectorCopy(end, tstack_p->end); tstack_p->planenum = 0; //start with node 1 because node zero is a dummy for a solid leaf tstack_p->nodenum = 1; //starting at the root of the tree tstack_p++; while (1) { //pop up the stack tstack_p--; //if the trace stack is empty (ended up with a piece of the //line to be traced in an area) if (tstack_p < tracestack) { return numareas; } //end if //number of the current node to test the line against nodenum = tstack_p->nodenum; //if it is an area if (nodenum < 0) { #ifdef AAS_SAMPLE_DEBUG if (-nodenum > aasworld.numareasettings) { botimport.Print(PRT_ERROR, "AAS_TraceAreas: -nodenum = %d out of range\n", -nodenum); return numareas; } //end if #endif //AAS_SAMPLE_DEBUG //botimport.Print(PRT_MESSAGE, "areanum = %d, must be %d\n", -nodenum, AAS_PointAreaNum(start)); areas[numareas] = -nodenum; if (points) VectorCopy(tstack_p->start, points[numareas]); numareas++; if (numareas >= maxareas) return numareas; continue; } //end if //if it is a solid leaf if (!nodenum) { continue; } //end if #ifdef AAS_SAMPLE_DEBUG if (nodenum > aasworld.numnodes) { botimport.Print(PRT_ERROR, "AAS_TraceAreas: nodenum out of range\n"); return numareas; } //end if #endif //AAS_SAMPLE_DEBUG //the node to test against aasnode = &aasworld.nodes[nodenum]; //start point of current line to test against node VectorCopy(tstack_p->start, cur_start); //end point of the current line to test against node VectorCopy(tstack_p->end, cur_end); //the current node plane plane = &aasworld.planes[aasnode->planenum]; front = DotProduct(cur_start, plane->normal) - plane->dist; back = DotProduct(cur_end, plane->normal) - plane->dist; //if the whole to be traced line is totally at the front of this node //only go down the tree with the front child if (front > 0 && back > 0) { //keep the current start and end point on the stack //and go down the tree with the front child tstack_p->nodenum = aasnode->children[0]; tstack_p++; if (tstack_p >= &tracestack[127]) { botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n"); return numareas; } //end if } //end if //if the whole to be traced line is totally at the back of this node //only go down the tree with the back child else if (front <= 0 && back <= 0) { //keep the current start and end point on the stack //and go down the tree with the back child tstack_p->nodenum = aasnode->children[1]; tstack_p++; if (tstack_p >= &tracestack[127]) { botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n"); return numareas; } //end if } //end if //go down the tree both at the front and back of the node else { tmpplanenum = tstack_p->planenum; //calculate the hitpoint with the node (split point of the line) //put the crosspoint TRACEPLANE_EPSILON pixels on the near side if (front < 0) frac = (front)/(front-back); else frac = (front)/(front-back); if (frac < 0) frac = 0; else if (frac > 1) frac = 1; //frac = front / (front-back); // cur_mid[0] = cur_start[0] + (cur_end[0] - cur_start[0]) * frac; cur_mid[1] = cur_start[1] + (cur_end[1] - cur_start[1]) * frac; cur_mid[2] = cur_start[2] + (cur_end[2] - cur_start[2]) * frac; // AAS_DrawPlaneCross(cur_mid, plane->normal, plane->dist, plane->type, LINECOLOR_RED); //side the front part of the line is on side = front < 0; //first put the end part of the line on the stack (back side) VectorCopy(cur_mid, tstack_p->start); //not necesary to store because still on stack //VectorCopy(cur_end, tstack_p->end); tstack_p->planenum = aasnode->planenum; tstack_p->nodenum = aasnode->children[!side]; tstack_p++; if (tstack_p >= &tracestack[127]) { botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n"); return numareas; } //end if //now put the part near the start of the line on the stack so we will //continue with thats part first. This way we'll find the first //hit of the bbox VectorCopy(cur_start, tstack_p->start); VectorCopy(cur_mid, tstack_p->end); tstack_p->planenum = tmpplanenum; tstack_p->nodenum = aasnode->children[side]; tstack_p++; if (tstack_p >= &tracestack[127]) { botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n"); return numareas; } //end if } //end else } //end while // return numareas; } //end of the function AAS_TraceAreas //=========================================================================== // a simple cross product // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== // void AAS_OrthogonalToVectors(vec3_t v1, vec3_t v2, vec3_t res) #define AAS_OrthogonalToVectors(v1, v2, res) \ (res)[0] = ((v1)[1] * (v2)[2]) - ((v1)[2] * (v2)[1]);\ (res)[1] = ((v1)[2] * (v2)[0]) - ((v1)[0] * (v2)[2]);\ (res)[2] = ((v1)[0] * (v2)[1]) - ((v1)[1] * (v2)[0]); //=========================================================================== // tests if the given point is within the face boundaries // // Parameter: face : face to test if the point is in it // pnormal : normal of the plane to use for the face // point : point to test if inside face boundaries // Returns: qtrue if the point is within the face boundaries // Changes Globals: - //=========================================================================== qboolean AAS_InsideFace(aas_face_t *face, vec3_t pnormal, vec3_t point, float epsilon) { int i, firstvertex, edgenum; vec3_t v0; vec3_t edgevec, pointvec, sepnormal; aas_edge_t *edge; #ifdef AAS_SAMPLE_DEBUG int lastvertex = 0; #endif //AAS_SAMPLE_DEBUG if (!aasworld.loaded) return qfalse; for (i = 0; i < face->numedges; i++) { edgenum = aasworld.edgeindex[face->firstedge + i]; edge = &aasworld.edges[abs(edgenum)]; //get the first vertex of the edge firstvertex = edgenum < 0; VectorCopy(aasworld.vertexes[edge->v[firstvertex]], v0); //edge vector VectorSubtract(aasworld.vertexes[edge->v[!firstvertex]], v0, edgevec); // #ifdef AAS_SAMPLE_DEBUG if (lastvertex && lastvertex != edge->v[firstvertex]) { botimport.Print(PRT_MESSAGE, "winding not counter clockwise\n"); } //end if lastvertex = edge->v[!firstvertex]; #endif //AAS_SAMPLE_DEBUG //vector from first edge point to point possible in face VectorSubtract(point, v0, pointvec); //get a vector pointing inside the face orthogonal to both the //edge vector and the normal vector of the plane the face is in //this vector defines a plane through the origin (first vertex of //edge) and through both the edge vector and the normal vector //of the plane AAS_OrthogonalToVectors(edgevec, pnormal, sepnormal); //check on wich side of the above plane the point is //this is done by checking the sign of the dot product of the //vector orthogonal vector from above and the vector from the //origin (first vertex of edge) to the point //if the dotproduct is smaller than zero the point is outside the face if (DotProduct(pointvec, sepnormal) < -epsilon) return qfalse; } //end for return qtrue; } //end of the function AAS_InsideFace //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== qboolean AAS_PointInsideFace(int facenum, vec3_t point, float epsilon) { int i, firstvertex, edgenum; vec_t *v1, *v2; vec3_t edgevec, pointvec, sepnormal; aas_edge_t *edge; aas_plane_t *plane; aas_face_t *face; if (!aasworld.loaded) return qfalse; face = &aasworld.faces[facenum]; plane = &aasworld.planes[face->planenum]; // for (i = 0; i < face->numedges; i++) { edgenum = aasworld.edgeindex[face->firstedge + i]; edge = &aasworld.edges[abs(edgenum)]; //get the first vertex of the edge firstvertex = edgenum < 0; v1 = aasworld.vertexes[edge->v[firstvertex]]; v2 = aasworld.vertexes[edge->v[!firstvertex]]; //edge vector VectorSubtract(v2, v1, edgevec); //vector from first edge point to point possible in face VectorSubtract(point, v1, pointvec); // CrossProduct(edgevec, plane->normal, sepnormal); // if (DotProduct(pointvec, sepnormal) < -epsilon) return qfalse; } //end for return qtrue; } //end of the function AAS_PointInsideFace //=========================================================================== // returns the ground face the given point is above in the given area // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== aas_face_t *AAS_AreaGroundFace(int areanum, vec3_t point) { int i, facenum; vec3_t up = {0, 0, 1}; vec3_t normal; aas_area_t *area; aas_face_t *face; if (!aasworld.loaded) return NULL; area = &aasworld.areas[areanum]; for (i = 0; i < area->numfaces; i++) { facenum = aasworld.faceindex[area->firstface + i]; face = &aasworld.faces[abs(facenum)]; //if this is a ground face if (face->faceflags & FACE_GROUND) { //get the up or down normal if (aasworld.planes[face->planenum].normal[2] < 0) VectorNegate(up, normal); else VectorCopy(up, normal); //check if the point is in the face if (AAS_InsideFace(face, normal, point, 0.01f)) return face; } //end if } //end for return NULL; } //end of the function AAS_AreaGroundFace //=========================================================================== // returns the face the trace end position is situated in // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_FacePlane(int facenum, vec3_t normal, float *dist) { aas_plane_t *plane; plane = &aasworld.planes[aasworld.faces[facenum].planenum]; VectorCopy(plane->normal, normal); *dist = plane->dist; } //end of the function AAS_FacePlane //=========================================================================== // returns the face the trace end position is situated in // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== aas_face_t *AAS_TraceEndFace(aas_trace_t *trace) { int i, facenum; aas_area_t *area; aas_face_t *face, *firstface = NULL; if (!aasworld.loaded) return NULL; //if started in solid no face was hit if (trace->startsolid) return NULL; //trace->lastarea is the last area the trace was in area = &aasworld.areas[trace->lastarea]; //check which face the trace.endpos was in for (i = 0; i < area->numfaces; i++) { facenum = aasworld.faceindex[area->firstface + i]; face = &aasworld.faces[abs(facenum)]; //if the face is in the same plane as the trace end point if ((face->planenum & ~1) == (trace->planenum & ~1)) { //firstface is used for optimization, if theres only one //face in the plane then it has to be the good one //if there are more faces in the same plane then always //check the one with the fewest edges first /* if (firstface) { if (firstface->numedges < face->numedges) { if (AAS_InsideFace(firstface, aasworld.planes[face->planenum].normal, trace->endpos)) { return firstface; } //end if firstface = face; } //end if else { if (AAS_InsideFace(face, aasworld.planes[face->planenum].normal, trace->endpos)) { return face; } //end if } //end else } //end if else { firstface = face; } //end else*/ if (AAS_InsideFace(face, aasworld.planes[face->planenum].normal, trace->endpos, 0.01f)) return face; } //end if } //end for return firstface; } //end of the function AAS_TraceEndFace //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_BoxOnPlaneSide2(vec3_t absmins, vec3_t absmaxs, aas_plane_t *p) { int i, sides; float dist1, dist2; vec3_t corners[2]; for (i = 0; i < 3; i++) { if (p->normal[i] < 0) { corners[0][i] = absmins[i]; corners[1][i] = absmaxs[i]; } //end if else { corners[1][i] = absmins[i]; corners[0][i] = absmaxs[i]; } //end else } //end for dist1 = DotProduct(p->normal, corners[0]) - p->dist; dist2 = DotProduct(p->normal, corners[1]) - p->dist; sides = 0; if (dist1 >= 0) sides = 1; if (dist2 < 0) sides |= 2; return sides; } //end of the function AAS_BoxOnPlaneSide2 //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== //int AAS_BoxOnPlaneSide(vec3_t absmins, vec3_t absmaxs, aas_plane_t *p) #define AAS_BoxOnPlaneSide(absmins, absmaxs, p) (\ ( (p)->type < 3) ?\ (\ ( (p)->dist <= (absmins)[(p)->type]) ?\ (\ 1\ )\ :\ (\ ( (p)->dist >= (absmaxs)[(p)->type]) ?\ (\ 2\ )\ :\ (\ 3\ )\ )\ )\ :\ (\ AAS_BoxOnPlaneSide2((absmins), (absmaxs), (p))\ )\ ) //end of the function AAS_BoxOnPlaneSide //=========================================================================== // remove the links to this entity from all areas // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_UnlinkFromAreas(aas_link_t *areas) { aas_link_t *link, *nextlink; for (link = areas; link; link = nextlink) { //next area the entity is linked in nextlink = link->next_area; //remove the entity from the linked list of this area if (link->prev_ent) link->prev_ent->next_ent = link->next_ent; else aasworld.arealinkedentities[link->areanum] = link->next_ent; if (link->next_ent) link->next_ent->prev_ent = link->prev_ent; //deallocate the link structure AAS_DeAllocAASLink(link); } //end for } //end of the function AAS_UnlinkFromAreas //=========================================================================== // link the entity to the areas the bounding box is totally or partly // situated in. This is done with recursion down the tree using the // bounding box to test for plane sides // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== typedef struct { int nodenum; //node found after splitting } aas_linkstack_t; aas_link_t *AAS_AASLinkEntity(vec3_t absmins, vec3_t absmaxs, int entnum) { int side, nodenum; aas_linkstack_t linkstack[128]; aas_linkstack_t *lstack_p; aas_node_t *aasnode; aas_plane_t *plane; aas_link_t *link, *areas; if (!aasworld.loaded) { botimport.Print(PRT_ERROR, "AAS_LinkEntity: aas not loaded\n"); return NULL; } //end if areas = NULL; // lstack_p = linkstack; //we start with the whole line on the stack //start with node 1 because node zero is a dummy used for solid leafs lstack_p->nodenum = 1; //starting at the root of the tree lstack_p++; while (1) { //pop up the stack lstack_p--; //if the trace stack is empty (ended up with a piece of the //line to be traced in an area) if (lstack_p < linkstack) break; //number of the current node to test the line against nodenum = lstack_p->nodenum; //if it is an area if (nodenum < 0) { //NOTE: the entity might have already been linked into this area // because several node children can point to the same area for (link = aasworld.arealinkedentities[-nodenum]; link; link = link->next_ent) { if (link->entnum == entnum) break; } //end for if (link) continue; // link = AAS_AllocAASLink(); if (!link) return areas; link->entnum = entnum; link->areanum = -nodenum; //put the link into the double linked area list of the entity link->prev_area = NULL; link->next_area = areas; if (areas) areas->prev_area = link; areas = link; //put the link into the double linked entity list of the area link->prev_ent = NULL; link->next_ent = aasworld.arealinkedentities[-nodenum]; if (aasworld.arealinkedentities[-nodenum]) aasworld.arealinkedentities[-nodenum]->prev_ent = link; aasworld.arealinkedentities[-nodenum] = link; // continue; } //end if //if solid leaf if (!nodenum) continue; //the node to test against aasnode = &aasworld.nodes[nodenum]; //the current node plane plane = &aasworld.planes[aasnode->planenum]; //get the side(s) the box is situated relative to the plane side = AAS_BoxOnPlaneSide2(absmins, absmaxs, plane); //if on the front side of the node if (side & 1) { lstack_p->nodenum = aasnode->children[0]; lstack_p++; } //end if if (lstack_p >= &linkstack[127]) { botimport.Print(PRT_ERROR, "AAS_LinkEntity: stack overflow\n"); break; } //end if //if on the back side of the node if (side & 2) { lstack_p->nodenum = aasnode->children[1]; lstack_p++; } //end if if (lstack_p >= &linkstack[127]) { botimport.Print(PRT_ERROR, "AAS_LinkEntity: stack overflow\n"); break; } //end if } //end while return areas; } //end of the function AAS_AASLinkEntity //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== aas_link_t *AAS_LinkEntityClientBBox(vec3_t absmins, vec3_t absmaxs, int entnum, int presencetype) { vec3_t mins, maxs; vec3_t newabsmins, newabsmaxs; AAS_PresenceTypeBoundingBox(presencetype, mins, maxs); VectorSubtract(absmins, maxs, newabsmins); VectorSubtract(absmaxs, mins, newabsmaxs); //relink the entity return AAS_AASLinkEntity(newabsmins, newabsmaxs, entnum); } //end of the function AAS_LinkEntityClientBBox //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_BBoxAreas(vec3_t absmins, vec3_t absmaxs, int *areas, int maxareas) { aas_link_t *linkedareas, *link; int num; linkedareas = AAS_AASLinkEntity(absmins, absmaxs, -1); num = 0; for (link = linkedareas; link; link = link->next_area) { areas[num] = link->areanum; num++; if (num >= maxareas) break; } //end for AAS_UnlinkFromAreas(linkedareas); return num; } //end of the function AAS_BBoxAreas //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_AreaInfo( int areanum, aas_areainfo_t *info ) { aas_areasettings_t *settings; if (!info) return 0; if (areanum <= 0 || areanum >= aasworld.numareas) { botimport.Print(PRT_ERROR, "AAS_AreaInfo: areanum %d out of range\n", areanum); return 0; } //end if settings = &aasworld.areasettings[areanum]; info->cluster = settings->cluster; info->contents = settings->contents; info->flags = settings->areaflags; info->presencetype = settings->presencetype; VectorCopy(aasworld.areas[areanum].mins, info->mins); VectorCopy(aasworld.areas[areanum].maxs, info->maxs); VectorCopy(aasworld.areas[areanum].center, info->center); return sizeof(aas_areainfo_t); } //end of the function AAS_AreaInfo //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== aas_plane_t *AAS_PlaneFromNum(int planenum) { if (!aasworld.loaded) return 0; return &aasworld.planes[planenum]; } //end of the function AAS_PlaneFromNum ================================================ FILE: src/engine/botlib/be_aas_sample.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_aas_sample.h * * desc: AAS * * $Archive: /source/code/botlib/be_aas_sample.h $ * *****************************************************************************/ #ifdef AASINTERN void AAS_InitAASLinkHeap(void); void AAS_InitAASLinkedEntities(void); void AAS_FreeAASLinkHeap(void); void AAS_FreeAASLinkedEntities(void); aas_face_t *AAS_AreaGroundFace(int areanum, vec3_t point); aas_face_t *AAS_TraceEndFace(aas_trace_t *trace); aas_plane_t *AAS_PlaneFromNum(int planenum); aas_link_t *AAS_AASLinkEntity(vec3_t absmins, vec3_t absmaxs, int entnum); aas_link_t *AAS_LinkEntityClientBBox(vec3_t absmins, vec3_t absmaxs, int entnum, int presencetype); qboolean AAS_PointInsideFace(int facenum, vec3_t point, float epsilon); qboolean AAS_InsideFace(aas_face_t *face, vec3_t pnormal, vec3_t point, float epsilon); void AAS_UnlinkFromAreas(aas_link_t *areas); #endif //AASINTERN //returns the mins and maxs of the bounding box for the given presence type void AAS_PresenceTypeBoundingBox(int presencetype, vec3_t mins, vec3_t maxs); //returns the cluster the area is in (negative portal number if the area is a portal) int AAS_AreaCluster(int areanum); //returns the presence type(s) of the area int AAS_AreaPresenceType(int areanum); //returns the presence type(s) at the given point int AAS_PointPresenceType(vec3_t point); //returns the result of the trace of a client bbox aas_trace_t AAS_TraceClientBBox(vec3_t start, vec3_t end, int presencetype, int passent); //stores the areas the trace went through and returns the number of passed areas int AAS_TraceAreas(vec3_t start, vec3_t end, int *areas, vec3_t *points, int maxareas); //returns the areas the bounding box is in int AAS_BBoxAreas(vec3_t absmins, vec3_t absmaxs, int *areas, int maxareas); //return area information int AAS_AreaInfo( int areanum, aas_areainfo_t *info ); //returns the area the point is in int AAS_PointAreaNum(vec3_t point); // int AAS_PointReachabilityAreaIndex( vec3_t point ); //returns the plane the given face is in void AAS_FacePlane(int facenum, vec3_t normal, float *dist); ================================================ FILE: src/engine/botlib/be_ai_char.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_ai_char.c * * desc: bot characters * * $Archive: /MissionPack/code/botlib/be_ai_char.c $ * *****************************************************************************/ #include "../../game/q_shared.h" #include "l_log.h" #include "l_memory.h" #include "l_utils.h" #include "l_script.h" #include "l_precomp.h" #include "l_struct.h" #include "l_libvar.h" #include "aasfile.h" #include "../../game/botlib.h" #include "../../game/be_aas.h" #include "be_aas_funcs.h" #include "be_interface.h" #include "../../game/be_ai_char.h" #define MAX_CHARACTERISTICS 80 #define CT_INTEGER 1 #define CT_FLOAT 2 #define CT_STRING 3 #define DEFAULT_CHARACTER "bots/default_c.c" //characteristic value union cvalue { int integer; float _float; char *string; }; //a characteristic typedef struct bot_characteristic_s { char type; //characteristic type union cvalue value; //characteristic value } bot_characteristic_t; //a bot character typedef struct bot_character_s { char filename[MAX_QPATH]; float skill; bot_characteristic_t c[1]; //variable sized } bot_character_t; bot_character_t *botcharacters[MAX_CLIENTS + 1]; //======================================================================== // // Parameter: - // Returns: - // Changes Globals: - //======================================================================== bot_character_t *BotCharacterFromHandle(int handle) { if (handle <= 0 || handle > MAX_CLIENTS) { botimport.Print(PRT_FATAL, "character handle %d out of range\n", handle); return NULL; } //end if if (!botcharacters[handle]) { botimport.Print(PRT_FATAL, "invalid character %d\n", handle); return NULL; } //end if return botcharacters[handle]; } //end of the function BotCharacterFromHandle //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotDumpCharacter(bot_character_t *ch) { int i; Log_Write("%s", ch->filename); Log_Write("skill %d\n", ch->skill); Log_Write("{\n"); for (i = 0; i < MAX_CHARACTERISTICS; i++) { switch(ch->c[i].type) { case CT_INTEGER: Log_Write(" %4d %d\n", i, ch->c[i].value.integer); break; case CT_FLOAT: Log_Write(" %4d %f\n", i, ch->c[i].value._float); break; case CT_STRING: Log_Write(" %4d %s\n", i, ch->c[i].value.string); break; } //end case } //end for Log_Write("}\n"); } //end of the function BotDumpCharacter //======================================================================== // // Parameter: - // Returns: - // Changes Globals: - //======================================================================== void BotFreeCharacterStrings(bot_character_t *ch) { int i; for (i = 0; i < MAX_CHARACTERISTICS; i++) { if (ch->c[i].type == CT_STRING) { FreeMemory(ch->c[i].value.string); } //end if } //end for } //end of the function BotFreeCharacterStrings //======================================================================== // // Parameter: - // Returns: - // Changes Globals: - //======================================================================== void BotFreeCharacter2(int handle) { if (handle <= 0 || handle > MAX_CLIENTS) { botimport.Print(PRT_FATAL, "character handle %d out of range\n", handle); return; } //end if if (!botcharacters[handle]) { botimport.Print(PRT_FATAL, "invalid character %d\n", handle); return; } //end if BotFreeCharacterStrings(botcharacters[handle]); FreeMemory(botcharacters[handle]); botcharacters[handle] = NULL; } //end of the function BotFreeCharacter2 //======================================================================== // // Parameter: - // Returns: - // Changes Globals: - //======================================================================== void BotFreeCharacter(int handle) { if (!LibVarGetValue("bot_reloadcharacters")) return; BotFreeCharacter2(handle); } //end of the function BotFreeCharacter //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotDefaultCharacteristics(bot_character_t *ch, bot_character_t *defaultch) { int i; for (i = 0; i < MAX_CHARACTERISTICS; i++) { if (ch->c[i].type) continue; // if (defaultch->c[i].type == CT_FLOAT) { ch->c[i].type = CT_FLOAT; ch->c[i].value._float = defaultch->c[i].value._float; } //end if else if (defaultch->c[i].type == CT_INTEGER) { ch->c[i].type = CT_INTEGER; ch->c[i].value.integer = defaultch->c[i].value.integer; } //end else if else if (defaultch->c[i].type == CT_STRING) { ch->c[i].type = CT_STRING; ch->c[i].value.string = (char *) GetMemory((int)strlen(defaultch->c[i].value.string)+1); strcpy(ch->c[i].value.string, defaultch->c[i].value.string); } //end else if } //end for } //end of the function BotDefaultCharacteristics //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bot_character_t *BotLoadCharacterFromFile(char *charfile, int skill) { int indent, index, foundcharacter; bot_character_t *ch; source_t *source; token_t token; foundcharacter = qfalse; //a bot character is parsed in two phases PC_SetBaseFolder(BOTFILESBASEFOLDER); source = LoadSourceFile(charfile); if (!source) { botimport.Print(PRT_ERROR, "counldn't load %s\n", charfile); return NULL; } //end if ch = (bot_character_t *) GetClearedMemory(sizeof(bot_character_t) + MAX_CHARACTERISTICS * sizeof(bot_characteristic_t)); strcpy(ch->filename, charfile); while(PC_ReadToken(source, &token)) { if (!strcmp(token.string, "skill")) { if (!PC_ExpectTokenType(source, TT_NUMBER, 0, &token)) { FreeSource(source); BotFreeCharacterStrings(ch); FreeMemory(ch); return NULL; } //end if if (!PC_ExpectTokenString(source, "{")) { FreeSource(source); BotFreeCharacterStrings(ch); FreeMemory(ch); return NULL; } //end if //if it's the correct skill if (skill < 0 || (int)token.intvalue == skill) { foundcharacter = qtrue; ch->skill = token.intvalue; while(PC_ExpectAnyToken(source, &token)) { if (!strcmp(token.string, "}")) break; if (token.type != TT_NUMBER || !(token.subtype & TT_INTEGER)) { SourceError(source, "expected integer index, found %s\n", token.string); FreeSource(source); BotFreeCharacterStrings(ch); FreeMemory(ch); return NULL; } //end if index = token.intvalue; if (index < 0 || index > MAX_CHARACTERISTICS) { SourceError(source, "characteristic index out of range [0, %d]\n", MAX_CHARACTERISTICS); FreeSource(source); BotFreeCharacterStrings(ch); FreeMemory(ch); return NULL; } //end if if (ch->c[index].type) { SourceError(source, "characteristic %d already initialized\n", index); FreeSource(source); BotFreeCharacterStrings(ch); FreeMemory(ch); return NULL; } //end if if (!PC_ExpectAnyToken(source, &token)) { FreeSource(source); BotFreeCharacterStrings(ch); FreeMemory(ch); return NULL; } //end if if (token.type == TT_NUMBER) { if (token.subtype & TT_FLOAT) { ch->c[index].value._float = token.floatvalue; ch->c[index].type = CT_FLOAT; } //end if else { ch->c[index].value.integer = token.intvalue; ch->c[index].type = CT_INTEGER; } //end else } //end if else if (token.type == TT_STRING) { StripDoubleQuotes(token.string); ch->c[index].value.string = (char*)GetMemory((int)strlen(token.string)+1); strcpy(ch->c[index].value.string, token.string); ch->c[index].type = CT_STRING; } //end else if else { SourceError(source, "expected integer, float or string, found %s\n", token.string); FreeSource(source); BotFreeCharacterStrings(ch); FreeMemory(ch); return NULL; } //end else } //end if break; } //end if else { indent = 1; while(indent) { if (!PC_ExpectAnyToken(source, &token)) { FreeSource(source); BotFreeCharacterStrings(ch); FreeMemory(ch); return NULL; } //end if if (!strcmp(token.string, "{")) indent++; else if (!strcmp(token.string, "}")) indent--; } //end while } //end else } //end if else { SourceError(source, "unknown definition %s\n", token.string); FreeSource(source); BotFreeCharacterStrings(ch); FreeMemory(ch); return NULL; } //end else } //end while FreeSource(source); // if (!foundcharacter) { BotFreeCharacterStrings(ch); FreeMemory(ch); return NULL; } //end if return ch; } //end of the function BotLoadCharacterFromFile //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotFindCachedCharacter(char *charfile, float skill) { int handle; for (handle = 1; handle <= MAX_CLIENTS; handle++) { if ( !botcharacters[handle] ) continue; if ( strcmp( botcharacters[handle]->filename, charfile ) == 0 && (skill < 0 || fabs(botcharacters[handle]->skill - skill) < 0.01) ) { return handle; } //end if } //end for return 0; } //end of the function BotFindCachedCharacter //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotLoadCachedCharacter(char *charfile, float skill, int reload) { int handle, cachedhandle, intskill; bot_character_t *ch = NULL; #ifdef DEBUG int starttime; starttime = Sys_MilliSeconds(); #endif //DEBUG //find a free spot for a character for (handle = 1; handle <= MAX_CLIENTS; handle++) { if (!botcharacters[handle]) break; } //end for if (handle > MAX_CLIENTS) return 0; //try to load a cached character with the given skill if (!reload) { cachedhandle = BotFindCachedCharacter(charfile, skill); if (cachedhandle) { botimport.Print(PRT_MESSAGE, "loaded cached skill %f from %s\n", skill, charfile); return cachedhandle; } //end if } //end else // intskill = (int) (skill + 0.5); //try to load the character with the given skill ch = BotLoadCharacterFromFile(charfile, intskill); if (ch) { botcharacters[handle] = ch; // botimport.Print(PRT_MESSAGE, "loaded skill %d from %s\n", intskill, charfile); #ifdef DEBUG if (bot_developer) { botimport.Print(PRT_MESSAGE, "skill %d loaded in %d msec from %s\n", intskill, Sys_MilliSeconds() - starttime, charfile); } //end if #endif //DEBUG return handle; } //end if // botimport.Print(PRT_WARNING, "couldn't find skill %d in %s\n", intskill, charfile); // if (!reload) { //try to load a cached default character with the given skill cachedhandle = BotFindCachedCharacter(DEFAULT_CHARACTER, skill); if (cachedhandle) { botimport.Print(PRT_MESSAGE, "loaded cached default skill %d from %s\n", intskill, charfile); return cachedhandle; } //end if } //end if //try to load the default character with the given skill ch = BotLoadCharacterFromFile(DEFAULT_CHARACTER, intskill); if (ch) { botcharacters[handle] = ch; botimport.Print(PRT_MESSAGE, "loaded default skill %d from %s\n", intskill, charfile); return handle; } //end if // if (!reload) { //try to load a cached character with any skill cachedhandle = BotFindCachedCharacter(charfile, -1); if (cachedhandle) { botimport.Print(PRT_MESSAGE, "loaded cached skill %f from %s\n", botcharacters[cachedhandle]->skill, charfile); return cachedhandle; } //end if } //end if //try to load a character with any skill ch = BotLoadCharacterFromFile(charfile, -1); if (ch) { botcharacters[handle] = ch; botimport.Print(PRT_MESSAGE, "loaded skill %f from %s\n", ch->skill, charfile); return handle; } //end if // if (!reload) { //try to load a cached character with any skill cachedhandle = BotFindCachedCharacter(DEFAULT_CHARACTER, -1); if (cachedhandle) { botimport.Print(PRT_MESSAGE, "loaded cached default skill %f from %s\n", botcharacters[cachedhandle]->skill, charfile); return cachedhandle; } //end if } //end if //try to load a character with any skill ch = BotLoadCharacterFromFile(DEFAULT_CHARACTER, -1); if (ch) { botcharacters[handle] = ch; botimport.Print(PRT_MESSAGE, "loaded default skill %f from %s\n", ch->skill, charfile); return handle; } //end if // botimport.Print(PRT_WARNING, "couldn't load any skill from %s\n", charfile); //couldn't load any character return 0; } //end of the function BotLoadCachedCharacter //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotLoadCharacterSkill(char *charfile, float skill) { int ch, defaultch; defaultch = BotLoadCachedCharacter(DEFAULT_CHARACTER, skill, qfalse); ch = BotLoadCachedCharacter(charfile, skill, LibVarGetValue("bot_reloadcharacters")); if (defaultch && ch) { BotDefaultCharacteristics(botcharacters[ch], botcharacters[defaultch]); } //end if return ch; } //end of the function BotLoadCharacterSkill //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotInterpolateCharacters(int handle1, int handle2, float desiredskill) { bot_character_t *ch1, *ch2, *out; int i, handle; float scale; ch1 = BotCharacterFromHandle(handle1); ch2 = BotCharacterFromHandle(handle2); if (!ch1 || !ch2) return 0; //find a free spot for a character for (handle = 1; handle <= MAX_CLIENTS; handle++) { if (!botcharacters[handle]) break; } //end for if (handle > MAX_CLIENTS) return 0; out = (bot_character_t *) GetClearedMemory(sizeof(bot_character_t) + MAX_CHARACTERISTICS * sizeof(bot_characteristic_t)); out->skill = desiredskill; strcpy(out->filename, ch1->filename); botcharacters[handle] = out; scale = (float) (desiredskill - ch1->skill) / (ch2->skill - ch1->skill); for (i = 0; i < MAX_CHARACTERISTICS; i++) { // if (ch1->c[i].type == CT_FLOAT && ch2->c[i].type == CT_FLOAT) { out->c[i].type = CT_FLOAT; out->c[i].value._float = ch1->c[i].value._float + (ch2->c[i].value._float - ch1->c[i].value._float) * scale; } //end if else if (ch1->c[i].type == CT_INTEGER) { out->c[i].type = CT_INTEGER; out->c[i].value.integer = ch1->c[i].value.integer; } //end else if else if (ch1->c[i].type == CT_STRING) { out->c[i].type = CT_STRING; out->c[i].value.string = (char *) GetMemory((int)strlen(ch1->c[i].value.string)+1); strcpy(out->c[i].value.string, ch1->c[i].value.string); } //end else if } //end for return handle; } //end of the function BotInterpolateCharacters //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotLoadCharacter(char *charfile, float skill) { int firstskill, secondskill, handle; //make sure the skill is in the valid range if (skill < 1.0) skill = 1.0; else if (skill > 5.0) skill = 5.0; //skill 1, 4 and 5 should be available in the character files if (skill == 1.0 || skill == 4.0 || skill == 5.0) { return BotLoadCharacterSkill(charfile, skill); } //end if //check if there's a cached skill handle = BotFindCachedCharacter(charfile, skill); if (handle) { botimport.Print(PRT_MESSAGE, "loaded cached skill %f from %s\n", skill, charfile); return handle; } //end if if (skill < 4.0) { //load skill 1 and 4 firstskill = BotLoadCharacterSkill(charfile, 1); if (!firstskill) return 0; secondskill = BotLoadCharacterSkill(charfile, 4); if (!secondskill) return firstskill; } //end if else { //load skill 4 and 5 firstskill = BotLoadCharacterSkill(charfile, 4); if (!firstskill) return 0; secondskill = BotLoadCharacterSkill(charfile, 5); if (!secondskill) return firstskill; } //end else //interpolate between the two skills handle = BotInterpolateCharacters(firstskill, secondskill, skill); if (!handle) return 0; //write the character to the log file BotDumpCharacter(botcharacters[handle]); // return handle; } //end of the function BotLoadCharacter //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int CheckCharacteristicIndex(int character, int index) { bot_character_t *ch; ch = BotCharacterFromHandle(character); if (!ch) return qfalse; if (index < 0 || index >= MAX_CHARACTERISTICS) { botimport.Print(PRT_ERROR, "characteristic %d does not exist\n", index); return qfalse; } //end if if (!ch->c[index].type) { botimport.Print(PRT_ERROR, "characteristic %d is not initialized\n", index); return qfalse; } //end if return qtrue; } //end of the function CheckCharacteristicIndex //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== float Characteristic_Float(int character, int index) { bot_character_t *ch; ch = BotCharacterFromHandle(character); if (!ch) return 0; //check if the index is in range if (!CheckCharacteristicIndex(character, index)) return 0; //an integer will be converted to a float if (ch->c[index].type == CT_INTEGER) { return (float) ch->c[index].value.integer; } //end if //floats are just returned else if (ch->c[index].type == CT_FLOAT) { return ch->c[index].value._float; } //end else if //cannot convert a string pointer to a float else { botimport.Print(PRT_ERROR, "characteristic %d is not a float\n", index); return 0; } //end else if // return 0; } //end of the function Characteristic_Float //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== float Characteristic_BFloat(int character, int index, float min, float max) { float value; bot_character_t *ch; ch = BotCharacterFromHandle(character); if (!ch) return 0; if (min > max) { botimport.Print(PRT_ERROR, "cannot bound characteristic %d between %f and %f\n", index, min, max); return 0; } //end if value = Characteristic_Float(character, index); if (value < min) return min; if (value > max) return max; return value; } //end of the function Characteristic_BFloat //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int Characteristic_Integer(int character, int index) { bot_character_t *ch; ch = BotCharacterFromHandle(character); if (!ch) return 0; //check if the index is in range if (!CheckCharacteristicIndex(character, index)) return 0; //an integer will just be returned if (ch->c[index].type == CT_INTEGER) { return ch->c[index].value.integer; } //end if //floats are casted to integers else if (ch->c[index].type == CT_FLOAT) { return (int) ch->c[index].value._float; } //end else if else { botimport.Print(PRT_ERROR, "characteristic %d is not a integer\n", index); return 0; } //end else if // return 0; } //end of the function Characteristic_Integer //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int Characteristic_BInteger(int character, int index, int min, int max) { int value; bot_character_t *ch; ch = BotCharacterFromHandle(character); if (!ch) return 0; if (min > max) { botimport.Print(PRT_ERROR, "cannot bound characteristic %d between %d and %d\n", index, min, max); return 0; } //end if value = Characteristic_Integer(character, index); if (value < min) return min; if (value > max) return max; return value; } //end of the function Characteristic_BInteger //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void Characteristic_String(int character, int index, char *buf, int size) { bot_character_t *ch; ch = BotCharacterFromHandle(character); if (!ch) return; //check if the index is in range if (!CheckCharacteristicIndex(character, index)) return; //an integer will be converted to a float if (ch->c[index].type == CT_STRING) { strncpy(buf, ch->c[index].value.string, size-1); buf[size-1] = '\0'; return; } //end if else { botimport.Print(PRT_ERROR, "characteristic %d is not a string\n", index); return; } //end else if return; } //end of the function Characteristic_String //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotShutdownCharacters(void) { int handle; for (handle = 1; handle <= MAX_CLIENTS; handle++) { if (botcharacters[handle]) { BotFreeCharacter2(handle); } //end if } //end for } //end of the function BotShutdownCharacters ================================================ FILE: src/engine/botlib/be_ai_chat.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_ai_chat.c * * desc: bot chat AI * * $Archive: /MissionPack/code/botlib/be_ai_chat.c $ * *****************************************************************************/ #include "../../game/q_shared.h" #include "l_memory.h" #include "l_libvar.h" #include "l_script.h" #include "l_precomp.h" #include "l_struct.h" #include "l_utils.h" #include "l_log.h" #include "aasfile.h" #include "../../game/botlib.h" #include "../../game/be_aas.h" #include "be_aas_funcs.h" #include "be_interface.h" #include "../../game/be_ea.h" #include "../../game/be_ai_chat.h" //escape character #define ESCAPE_CHAR 0x01 //'_' // // "hi ", people, " ", 0, " entered the game" //becomes: // "hi _rpeople_ _v0_ entered the game" // //match piece types #define MT_VARIABLE 1 //variable match piece #define MT_STRING 2 //string match piece //reply chat key flags #define RCKFL_AND 1 //key must be present #define RCKFL_NOT 2 //key must be absent #define RCKFL_NAME 4 //name of bot must be present #define RCKFL_STRING 8 //key is a string #define RCKFL_VARIABLES 16 //key is a match template #define RCKFL_BOTNAMES 32 //key is a series of botnames #define RCKFL_GENDERFEMALE 64 //bot must be female #define RCKFL_GENDERMALE 128 //bot must be male #define RCKFL_GENDERLESS 256 //bot must be genderless //time to ignore a chat message after using it #define CHATMESSAGE_RECENTTIME 20 //the actuall chat messages typedef struct bot_chatmessage_s { char *chatmessage; //chat message string float time; //last time used struct bot_chatmessage_s *next; //next chat message in a list } bot_chatmessage_t; //bot chat type with chat lines typedef struct bot_chattype_s { char name[MAX_CHATTYPE_NAME]; int numchatmessages; bot_chatmessage_t *firstchatmessage; struct bot_chattype_s *next; } bot_chattype_t; //bot chat lines typedef struct bot_chat_s { bot_chattype_t *types; } bot_chat_t; //random string typedef struct bot_randomstring_s { char *string; struct bot_randomstring_s *next; } bot_randomstring_t; //list with random strings typedef struct bot_randomlist_s { char *string; int numstrings; bot_randomstring_t *firstrandomstring; struct bot_randomlist_s *next; } bot_randomlist_t; //synonym typedef struct bot_synonym_s { char *string; float weight; struct bot_synonym_s *next; } bot_synonym_t; //list with synonyms typedef struct bot_synonymlist_s { unsigned long int context; float totalweight; bot_synonym_t *firstsynonym; struct bot_synonymlist_s *next; } bot_synonymlist_t; //fixed match string typedef struct bot_matchstring_s { char *string; struct bot_matchstring_s *next; } bot_matchstring_t; //piece of a match template typedef struct bot_matchpiece_s { int type; bot_matchstring_t *firststring; int variable; struct bot_matchpiece_s *next; } bot_matchpiece_t; //match template typedef struct bot_matchtemplate_s { unsigned long int context; int type; int subtype; bot_matchpiece_t *first; struct bot_matchtemplate_s *next; } bot_matchtemplate_t; //reply chat key typedef struct bot_replychatkey_s { int flags; char *string; bot_matchpiece_t *match; struct bot_replychatkey_s *next; } bot_replychatkey_t; //reply chat typedef struct bot_replychat_s { bot_replychatkey_t *keys; float priority; int numchatmessages; bot_chatmessage_t *firstchatmessage; struct bot_replychat_s *next; } bot_replychat_t; //string list typedef struct bot_stringlist_s { char *string; struct bot_stringlist_s *next; } bot_stringlist_t; //chat state of a bot typedef struct bot_chatstate_s { int gender; //0=it, 1=female, 2=male int client; //client number char name[32]; //name of the bot char chatmessage[MAX_MESSAGE_SIZE]; int handle; //the console messages visible to the bot bot_consolemessage_t *firstmessage; //first message is the first typed message bot_consolemessage_t *lastmessage; //last message is the last typed message, bottom of console //number of console messages stored in the state int numconsolemessages; //the bot chat lines bot_chat_t *chat; } bot_chatstate_t; typedef struct { bot_chat_t *chat; char filename[MAX_QPATH]; char chatname[MAX_QPATH]; } bot_ichatdata_t; bot_ichatdata_t *ichatdata[MAX_CLIENTS]; bot_chatstate_t *botchatstates[MAX_CLIENTS+1]; //console message heap bot_consolemessage_t *consolemessageheap = NULL; bot_consolemessage_t *freeconsolemessages = NULL; //list with match strings bot_matchtemplate_t *matchtemplates = NULL; //list with synonyms bot_synonymlist_t *synonyms = NULL; //list with random strings bot_randomlist_t *randomstrings = NULL; //reply chats bot_replychat_t *replychats = NULL; //======================================================================== // // Parameter: - // Returns: - // Changes Globals: - //======================================================================== bot_chatstate_t *BotChatStateFromHandle(int handle) { if (handle <= 0 || handle > MAX_CLIENTS) { botimport.Print(PRT_FATAL, "chat state handle %d out of range\n", handle); return NULL; } //end if if (!botchatstates[handle]) { botimport.Print(PRT_FATAL, "invalid chat state %d\n", handle); return NULL; } //end if return botchatstates[handle]; } //end of the function BotChatStateFromHandle //=========================================================================== // initialize the heap with unused console messages // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void InitConsoleMessageHeap(void) { int i, max_messages; if (consolemessageheap) FreeMemory(consolemessageheap); // max_messages = (int) LibVarValue("max_messages", "1024"); consolemessageheap = (bot_consolemessage_t *) GetClearedHunkMemory(max_messages * sizeof(bot_consolemessage_t)); consolemessageheap[0].prev = NULL; consolemessageheap[0].next = &consolemessageheap[1]; for (i = 1; i < max_messages-1; i++) { consolemessageheap[i].prev = &consolemessageheap[i - 1]; consolemessageheap[i].next = &consolemessageheap[i + 1]; } //end for consolemessageheap[max_messages-1].prev = &consolemessageheap[max_messages-2]; consolemessageheap[max_messages-1].next = NULL; //pointer to the free console messages freeconsolemessages = consolemessageheap; } //end of the function InitConsoleMessageHeap //=========================================================================== // allocate one console message from the heap // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bot_consolemessage_t *AllocConsoleMessage(void) { bot_consolemessage_t *message; message = freeconsolemessages; if (freeconsolemessages) freeconsolemessages = freeconsolemessages->next; if (freeconsolemessages) freeconsolemessages->prev = NULL; return message; } //end of the function AllocConsoleMessage //=========================================================================== // deallocate one console message from the heap // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void FreeConsoleMessage(bot_consolemessage_t *message) { if (freeconsolemessages) freeconsolemessages->prev = message; message->prev = NULL; message->next = freeconsolemessages; freeconsolemessages = message; } //end of the function FreeConsoleMessage //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotRemoveConsoleMessage(int chatstate, int handle) { bot_consolemessage_t *m, *nextm; bot_chatstate_t *cs; cs = BotChatStateFromHandle(chatstate); if (!cs) return; for (m = cs->firstmessage; m; m = nextm) { nextm = m->next; if (m->handle == handle) { if (m->next) m->next->prev = m->prev; else cs->lastmessage = m->prev; if (m->prev) m->prev->next = m->next; else cs->firstmessage = m->next; FreeConsoleMessage(m); cs->numconsolemessages--; break; } //end if } //end for } //end of the function BotRemoveConsoleMessage //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotQueueConsoleMessage(int chatstate, int type, char *message) { bot_consolemessage_t *m; bot_chatstate_t *cs; cs = BotChatStateFromHandle(chatstate); if (!cs) return; m = AllocConsoleMessage(); if (!m) { botimport.Print(PRT_ERROR, "empty console message heap\n"); return; } //end if cs->handle++; if (cs->handle <= 0 || cs->handle > 8192) cs->handle = 1; m->handle = cs->handle; m->time = AAS_Time(); m->type = type; strncpy(m->message, message, MAX_MESSAGE_SIZE); m->next = NULL; if (cs->lastmessage) { cs->lastmessage->next = m; m->prev = cs->lastmessage; cs->lastmessage = m; } //end if else { cs->lastmessage = m; cs->firstmessage = m; m->prev = NULL; } //end if cs->numconsolemessages++; } //end of the function BotQueueConsoleMessage //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotNextConsoleMessage(int chatstate, bot_consolemessage_t *cm) { bot_chatstate_t *cs; cs = BotChatStateFromHandle(chatstate); if (!cs) return 0; if (cs->firstmessage) { Com_Memcpy(cm, cs->firstmessage, sizeof(bot_consolemessage_t)); cm->next = cm->prev = NULL; return cm->handle; } //end if return 0; } //end of the function BotConsoleMessage //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotNumConsoleMessages(int chatstate) { bot_chatstate_t *cs; cs = BotChatStateFromHandle(chatstate); if (!cs) return 0; return cs->numconsolemessages; } //end of the function BotNumConsoleMessages //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int IsWhiteSpace(char c) { if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '(' || c == ')' || c == '?' || c == ':' || c == '\''|| c == '/' || c == ',' || c == '.' || c == '[' || c == ']' || c == '-' || c == '_' || c == '+' || c == '=') return qfalse; return qtrue; } //end of the function IsWhiteSpace //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotRemoveTildes(char *message) { int i; //remove all tildes from the chat message for (i = 0; message[i]; i++) { if (message[i] == '~') { memmove(&message[i], &message[i+1], (int)strlen(&message[i+1])+1); } //end if } //end for } //end of the function BotRemoveTildes //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void UnifyWhiteSpaces(char *string) { char *ptr, *oldptr; for (ptr = oldptr = string; *ptr; oldptr = ptr) { while(*ptr && IsWhiteSpace(*ptr)) ptr++; if (ptr > oldptr) { //if not at the start and not at the end of the string //write only one space if (oldptr > string && *ptr) *oldptr++ = ' '; //remove all other white spaces if (ptr > oldptr) memmove(oldptr, ptr, (int)strlen(ptr)+1); } //end if while(*ptr && !IsWhiteSpace(*ptr)) ptr++; } //end while } //end of the function UnifyWhiteSpaces //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int StringContains(char *str1, char *str2, int casesensitive) { int len, i, j, index; if (str1 == NULL || str2 == NULL) return -1; len = (int)strlen(str1) - (int)strlen(str2); index = 0; for (i = 0; i <= len; i++, str1++, index++) { for (j = 0; str2[j]; j++) { if (casesensitive) { if (str1[j] != str2[j]) break; } //end if else { if (toupper(str1[j]) != toupper(str2[j])) break; } //end else } //end for if (!str2[j]) return index; } //end for return -1; } //end of the function StringContains //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== char *StringContainsWord(char *str1, char *str2, int casesensitive) { int len, i, j; len = (int)strlen(str1) - (int)strlen(str2); for (i = 0; i <= len; i++, str1++) { //if not at the start of the string if (i) { //skip to the start of the next word while(*str1 && *str1 != ' ' && *str1 != '.' && *str1 != ',' && *str1 != '!') str1++; if (!*str1) break; str1++; } //end for //compare the word for (j = 0; str2[j]; j++) { if (casesensitive) { if (str1[j] != str2[j]) break; } //end if else { if (toupper(str1[j]) != toupper(str2[j])) break; } //end else } //end for //if there was a word match if (!str2[j]) { //if the first string has an end of word if (!str1[j] || str1[j] == ' ' || str1[j] == '.' || str1[j] == ',' || str1[j] == '!') return str1; } //end if } //end for return NULL; } //end of the function StringContainsWord //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void StringReplaceWords(char *string, char *synonym, char *replacement) { char *str, *str2; //find the synonym in the string str = StringContainsWord(string, synonym, qfalse); //if the synonym occured in the string while(str) { //if the synonym isn't part of the replacement which is already in the string //usefull for abreviations str2 = StringContainsWord(string, replacement, qfalse); while(str2) { if (str2 <= str && str < str2 + (int)strlen(replacement)) break; str2 = StringContainsWord(str2+1, replacement, qfalse); } //end while if (!str2) { memmove(str + (int)strlen(replacement), str+(int)strlen(synonym), (int)strlen(str+(int)strlen(synonym))+1); //append the synonum replacement Com_Memcpy(str, replacement, (int)strlen(replacement)); } //end if //find the next synonym in the string str = StringContainsWord(str+(int)strlen(replacement), synonym, qfalse); } //end if } //end of the function StringReplaceWords //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotDumpSynonymList(bot_synonymlist_t *synlist) { FILE *fp; bot_synonymlist_t *syn; bot_synonym_t *synonym; fp = Log_FilePointer(); if (!fp) return; for (syn = synlist; syn; syn = syn->next) { fprintf(fp, "%ld : [", syn->context); for (synonym = syn->firstsynonym; synonym; synonym = synonym->next) { fprintf(fp, "(\"%s\", %1.2f)", synonym->string, synonym->weight); if (synonym->next) fprintf(fp, ", "); } //end for fprintf(fp, "]\n"); } //end for } //end of the function BotDumpSynonymList //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bot_synonymlist_t *BotLoadSynonyms(char *filename) { int pass, size, contextlevel, numsynonyms; unsigned long int context, contextstack[32]; char *ptr = NULL; source_t *source; token_t token; bot_synonymlist_t *synlist, *lastsyn, *syn; bot_synonym_t *synonym, *lastsynonym; size = 0; synlist = NULL; //make compiler happy syn = NULL; //make compiler happy synonym = NULL; //make compiler happy //the synonyms are parsed in two phases for (pass = 0; pass < 2; pass++) { // if (pass && size) ptr = (char *) GetClearedHunkMemory(size); // PC_SetBaseFolder(BOTFILESBASEFOLDER); source = LoadSourceFile(filename); if (!source) { botimport.Print(PRT_ERROR, "counldn't load %s\n", filename); return NULL; } //end if // context = 0; contextlevel = 0; synlist = NULL; //list synonyms lastsyn = NULL; //last synonym in the list // while(PC_ReadToken(source, &token)) { if (token.type == TT_NUMBER) { context |= token.intvalue; contextstack[contextlevel] = token.intvalue; contextlevel++; if (contextlevel >= 32) { SourceError(source, "more than 32 context levels"); FreeSource(source); return NULL; } //end if if (!PC_ExpectTokenString(source, "{")) { FreeSource(source); return NULL; } //end if } //end if else if (token.type == TT_PUNCTUATION) { if (!strcmp(token.string, "}")) { contextlevel--; if (contextlevel < 0) { SourceError(source, "too many }"); FreeSource(source); return NULL; } //end if context &= ~contextstack[contextlevel]; } //end if else if (!strcmp(token.string, "[")) { size += sizeof(bot_synonymlist_t); if (pass) { syn = (bot_synonymlist_t *) ptr; ptr += sizeof(bot_synonymlist_t); syn->context = context; syn->firstsynonym = NULL; syn->next = NULL; if (lastsyn) lastsyn->next = syn; else synlist = syn; lastsyn = syn; } //end if numsynonyms = 0; lastsynonym = NULL; while(1) { if (!PC_ExpectTokenString(source, "(") || !PC_ExpectTokenType(source, TT_STRING, 0, &token)) { FreeSource(source); return NULL; } //end if StripDoubleQuotes(token.string); if ((int)strlen(token.string) <= 0) { SourceError(source, "empty string", token.string); FreeSource(source); return NULL; } //end if size += sizeof(bot_synonym_t) + (int)strlen(token.string) + 1; if (pass) { synonym = (bot_synonym_t *) ptr; ptr += sizeof(bot_synonym_t); synonym->string = ptr; ptr += (int)strlen(token.string) + 1; strcpy(synonym->string, token.string); // if (lastsynonym) lastsynonym->next = synonym; else syn->firstsynonym = synonym; lastsynonym = synonym; } //end if numsynonyms++; if (!PC_ExpectTokenString(source, ",") || !PC_ExpectTokenType(source, TT_NUMBER, 0, &token) || !PC_ExpectTokenString(source, ")")) { FreeSource(source); return NULL; } //end if if (pass) { synonym->weight = token.floatvalue; syn->totalweight += synonym->weight; } //end if if (PC_CheckTokenString(source, "]")) break; if (!PC_ExpectTokenString(source, ",")) { FreeSource(source); return NULL; } //end if } //end while if (numsynonyms < 2) { SourceError(source, "synonym must have at least two entries\n"); FreeSource(source); return NULL; } //end if } //end else else { SourceError(source, "unexpected %s", token.string); FreeSource(source); return NULL; } //end if } //end else if } //end while // FreeSource(source); // if (contextlevel > 0) { SourceError(source, "missing }"); return NULL; } //end if } //end for botimport.Print(PRT_MESSAGE, "loaded %s\n", filename); // //BotDumpSynonymList(synlist); // return synlist; } //end of the function BotLoadSynonyms //=========================================================================== // replace all the synonyms in the string // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotReplaceSynonyms(char *string, unsigned long int context) { bot_synonymlist_t *syn; bot_synonym_t *synonym; for (syn = synonyms; syn; syn = syn->next) { if (!(syn->context & context)) continue; for (synonym = syn->firstsynonym->next; synonym; synonym = synonym->next) { StringReplaceWords(string, synonym->string, syn->firstsynonym->string); } //end for } //end for } //end of the function BotReplaceSynonyms //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotReplaceWeightedSynonyms(char *string, unsigned long int context) { bot_synonymlist_t *syn; bot_synonym_t *synonym, *replacement; float weight, curweight; for (syn = synonyms; syn; syn = syn->next) { if (!(syn->context & context)) continue; //choose a weighted random replacement synonym weight = random() * syn->totalweight; if (!weight) continue; curweight = 0; for (replacement = syn->firstsynonym; replacement; replacement = replacement->next) { curweight += replacement->weight; if (weight < curweight) break; } //end for if (!replacement) continue; //replace all synonyms with the replacement for (synonym = syn->firstsynonym; synonym; synonym = synonym->next) { if (synonym == replacement) continue; StringReplaceWords(string, synonym->string, replacement->string); } //end for } //end for } //end of the function BotReplaceWeightedSynonyms //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotReplaceReplySynonyms(char *string, unsigned long int context) { char *str1, *str2, *replacement; bot_synonymlist_t *syn; bot_synonym_t *synonym; for (str1 = string; *str1; ) { //go to the start of the next word while(*str1 && *str1 <= ' ') str1++; if (!*str1) break; // for (syn = synonyms; syn; syn = syn->next) { if (!(syn->context & context)) continue; for (synonym = syn->firstsynonym->next; synonym; synonym = synonym->next) { str2 = synonym->string; //if the synonym is not at the front of the string continue str2 = StringContainsWord(str1, synonym->string, qfalse); if (!str2 || str2 != str1) continue; // replacement = syn->firstsynonym->string; //if the replacement IS in front of the string continue str2 = StringContainsWord(str1, replacement, qfalse); if (str2 && str2 == str1) continue; // memmove(str1 + (int)strlen(replacement), str1+(int)strlen(synonym->string), (int)strlen(str1+(int)strlen(synonym->string)) + 1); //append the synonum replacement Com_Memcpy(str1, replacement, (int)strlen(replacement)); // break; } //end for //if a synonym has been replaced if (synonym) break; } //end for //skip over this word while(*str1 && *str1 > ' ') str1++; if (!*str1) break; } //end while } //end of the function BotReplaceReplySynonyms //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotLoadChatMessage(source_t *source, char *chatmessagestring) { char *ptr; token_t token; ptr = chatmessagestring; *ptr = 0; // while(1) { if (!PC_ExpectAnyToken(source, &token)) return qfalse; //fixed string if (token.type == TT_STRING) { StripDoubleQuotes(token.string); if ((int)strlen(ptr) + (int)strlen(token.string) + 1 > MAX_MESSAGE_SIZE) { SourceError(source, "chat message too long\n"); return qfalse; } //end if strcat(ptr, token.string); } //end else if //variable string else if (token.type == TT_NUMBER && (token.subtype & TT_INTEGER)) { if ((int)strlen(ptr) + 7 > MAX_MESSAGE_SIZE) { SourceError(source, "chat message too long\n"); return qfalse; } //end if sprintf(&ptr[(int)strlen(ptr)], "%cv%ld%c", ESCAPE_CHAR, token.intvalue, ESCAPE_CHAR); } //end if //random string else if (token.type == TT_NAME) { if ((int)strlen(ptr) + 7 > MAX_MESSAGE_SIZE) { SourceError(source, "chat message too long\n"); return qfalse; } //end if sprintf(&ptr[(int)strlen(ptr)], "%cr%s%c", ESCAPE_CHAR, token.string, ESCAPE_CHAR); } //end else if else { SourceError(source, "unknown message component %s\n", token.string); return qfalse; } //end else if (PC_CheckTokenString(source, ";")) break; if (!PC_ExpectTokenString(source, ",")) return qfalse; } //end while // return qtrue; } //end of the function BotLoadChatMessage //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotDumpRandomStringList(bot_randomlist_t *randomlist) { FILE *fp; bot_randomlist_t *random; bot_randomstring_t *rs; fp = Log_FilePointer(); if (!fp) return; for (random = randomlist; random; random = random->next) { fprintf(fp, "%s = {", random->string); for (rs = random->firstrandomstring; rs; rs = rs->next) { fprintf(fp, "\"%s\"", rs->string); if (rs->next) fprintf(fp, ", "); else fprintf(fp, "}\n"); } //end for } //end for } //end of the function BotDumpRandomStringList //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bot_randomlist_t *BotLoadRandomStrings(char *filename) { int pass, size; char *ptr = NULL, chatmessagestring[MAX_MESSAGE_SIZE]; source_t *source; token_t token; bot_randomlist_t *randomlist, *lastrandom, *random; bot_randomstring_t *randomstring; #ifdef DEBUG int starttime = Sys_MilliSeconds(); #endif //DEBUG size = 0; randomlist = NULL; random = NULL; //the synonyms are parsed in two phases for (pass = 0; pass < 2; pass++) { // if (pass && size) ptr = (char *) GetClearedHunkMemory(size); // PC_SetBaseFolder(BOTFILESBASEFOLDER); source = LoadSourceFile(filename); if (!source) { botimport.Print(PRT_ERROR, "counldn't load %s\n", filename); return NULL; } //end if // randomlist = NULL; //list lastrandom = NULL; //last // while(PC_ReadToken(source, &token)) { if (token.type != TT_NAME) { SourceError(source, "unknown random %s", token.string); FreeSource(source); return NULL; } //end if size += sizeof(bot_randomlist_t) + (int)strlen(token.string) + 1; if (pass) { random = (bot_randomlist_t *) ptr; ptr += sizeof(bot_randomlist_t); random->string = ptr; ptr += (int)strlen(token.string) + 1; strcpy(random->string, token.string); random->firstrandomstring = NULL; random->numstrings = 0; // if (lastrandom) lastrandom->next = random; else randomlist = random; lastrandom = random; } //end if if (!PC_ExpectTokenString(source, "=") || !PC_ExpectTokenString(source, "{")) { FreeSource(source); return NULL; } //end if while(!PC_CheckTokenString(source, "}")) { if (!BotLoadChatMessage(source, chatmessagestring)) { FreeSource(source); return NULL; } //end if size += sizeof(bot_randomstring_t) + (int)strlen(chatmessagestring) + 1; if (pass) { randomstring = (bot_randomstring_t *) ptr; ptr += sizeof(bot_randomstring_t); randomstring->string = ptr; ptr += (int)strlen(chatmessagestring) + 1; strcpy(randomstring->string, chatmessagestring); // random->numstrings++; randomstring->next = random->firstrandomstring; random->firstrandomstring = randomstring; } //end if } //end while } //end while //free the source after one pass FreeSource(source); } //end for botimport.Print(PRT_MESSAGE, "loaded %s\n", filename); // #ifdef DEBUG botimport.Print(PRT_MESSAGE, "random strings %d msec\n", Sys_MilliSeconds() - starttime); //BotDumpRandomStringList(randomlist); #endif //DEBUG // return randomlist; } //end of the function BotLoadRandomStrings //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== char *RandomString(char *name) { bot_randomlist_t *random; bot_randomstring_t *rs; int i; for (random = randomstrings; random; random = random->next) { if (!strcmp(random->string, name)) { i = random() * random->numstrings; for (rs = random->firstrandomstring; rs; rs = rs->next) { if (--i < 0) break; } //end for if (rs) { return rs->string; } //end if } //end for } //end for return NULL; } //end of the function RandomString //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotDumpMatchTemplates(bot_matchtemplate_t *matches) { FILE *fp; bot_matchtemplate_t *mt; bot_matchpiece_t *mp; bot_matchstring_t *ms; fp = Log_FilePointer(); if (!fp) return; for (mt = matches; mt; mt = mt->next) { fprintf(fp, "{ " ); for (mp = mt->first; mp; mp = mp->next) { if (mp->type == MT_STRING) { for (ms = mp->firststring; ms; ms = ms->next) { fprintf(fp, "\"%s\"", ms->string); if (ms->next) fprintf(fp, "|"); } //end for } //end if else if (mp->type == MT_VARIABLE) { fprintf(fp, "%d", mp->variable); } //end else if if (mp->next) fprintf(fp, ", "); } //end for fprintf(fp, " = (%d, %d);}\n", mt->type, mt->subtype); } //end for } //end of the function BotDumpMatchTemplates //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotFreeMatchPieces(bot_matchpiece_t *matchpieces) { bot_matchpiece_t *mp, *nextmp; bot_matchstring_t *ms, *nextms; for (mp = matchpieces; mp; mp = nextmp) { nextmp = mp->next; if (mp->type == MT_STRING) { for (ms = mp->firststring; ms; ms = nextms) { nextms = ms->next; FreeMemory(ms); } //end for } //end if FreeMemory(mp); } //end for } //end of the function BotFreeMatchPieces //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bot_matchpiece_t *BotLoadMatchPieces(source_t *source, char *endtoken) { int lastwasvariable, emptystring; token_t token; bot_matchpiece_t *matchpiece, *firstpiece, *lastpiece; bot_matchstring_t *matchstring, *lastmatchstring; firstpiece = NULL; lastpiece = NULL; // lastwasvariable = qfalse; // while(PC_ReadToken(source, &token)) { if (token.type == TT_NUMBER && (token.subtype & TT_INTEGER)) { if (token.intvalue < 0 || token.intvalue >= MAX_MATCHVARIABLES) { SourceError(source, "can't have more than %d match variables\n", MAX_MATCHVARIABLES); FreeSource(source); BotFreeMatchPieces(firstpiece); return NULL; } //end if if (lastwasvariable) { SourceError(source, "not allowed to have adjacent variables\n"); FreeSource(source); BotFreeMatchPieces(firstpiece); return NULL; } //end if lastwasvariable = qtrue; // matchpiece = (bot_matchpiece_t *) GetClearedHunkMemory(sizeof(bot_matchpiece_t)); matchpiece->type = MT_VARIABLE; matchpiece->variable = token.intvalue; matchpiece->next = NULL; if (lastpiece) lastpiece->next = matchpiece; else firstpiece = matchpiece; lastpiece = matchpiece; } //end if else if (token.type == TT_STRING) { // matchpiece = (bot_matchpiece_t *) GetClearedHunkMemory(sizeof(bot_matchpiece_t)); matchpiece->firststring = NULL; matchpiece->type = MT_STRING; matchpiece->variable = 0; matchpiece->next = NULL; if (lastpiece) lastpiece->next = matchpiece; else firstpiece = matchpiece; lastpiece = matchpiece; // lastmatchstring = NULL; emptystring = qfalse; // do { if (matchpiece->firststring) { if (!PC_ExpectTokenType(source, TT_STRING, 0, &token)) { FreeSource(source); BotFreeMatchPieces(firstpiece); return NULL; } //end if } //end if StripDoubleQuotes(token.string); matchstring = (bot_matchstring_t *) GetClearedHunkMemory(sizeof(bot_matchstring_t) + (int)strlen(token.string) + 1); matchstring->string = (char *) matchstring + sizeof(bot_matchstring_t); strcpy(matchstring->string, token.string); if (!(int)strlen(token.string)) emptystring = qtrue; matchstring->next = NULL; if (lastmatchstring) lastmatchstring->next = matchstring; else matchpiece->firststring = matchstring; lastmatchstring = matchstring; } while(PC_CheckTokenString(source, "|")); //if there was no empty string found if (!emptystring) lastwasvariable = qfalse; } //end if else { SourceError(source, "invalid token %s\n", token.string); FreeSource(source); BotFreeMatchPieces(firstpiece); return NULL; } //end else if (PC_CheckTokenString(source, endtoken)) break; if (!PC_ExpectTokenString(source, ",")) { FreeSource(source); BotFreeMatchPieces(firstpiece); return NULL; } //end if } //end while return firstpiece; } //end of the function BotLoadMatchPieces //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotFreeMatchTemplates(bot_matchtemplate_t *mt) { bot_matchtemplate_t *nextmt; for (; mt; mt = nextmt) { nextmt = mt->next; BotFreeMatchPieces(mt->first); FreeMemory(mt); } //end for } //end of the function BotFreeMatchTemplates //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bot_matchtemplate_t *BotLoadMatchTemplates(char *matchfile) { source_t *source; token_t token; bot_matchtemplate_t *matchtemplate, *matches, *lastmatch; unsigned long int context; PC_SetBaseFolder(BOTFILESBASEFOLDER); source = LoadSourceFile(matchfile); if (!source) { botimport.Print(PRT_ERROR, "counldn't load %s\n", matchfile); return NULL; } //end if // matches = NULL; //list with matches lastmatch = NULL; //last match in the list while(PC_ReadToken(source, &token)) { if (token.type != TT_NUMBER || !(token.subtype & TT_INTEGER)) { SourceError(source, "expected integer, found %s\n", token.string); BotFreeMatchTemplates(matches); FreeSource(source); return NULL; } //end if //the context context = token.intvalue; // if (!PC_ExpectTokenString(source, "{")) { BotFreeMatchTemplates(matches); FreeSource(source); return NULL; } //end if // while(PC_ReadToken(source, &token)) { if (!strcmp(token.string, "}")) break; // PC_UnreadLastToken(source); // matchtemplate = (bot_matchtemplate_t *) GetClearedHunkMemory(sizeof(bot_matchtemplate_t)); matchtemplate->context = context; matchtemplate->next = NULL; //add the match template to the list if (lastmatch) lastmatch->next = matchtemplate; else matches = matchtemplate; lastmatch = matchtemplate; //load the match template matchtemplate->first = BotLoadMatchPieces(source, "="); if (!matchtemplate->first) { BotFreeMatchTemplates(matches); return NULL; } //end if //read the match type if (!PC_ExpectTokenString(source, "(") || !PC_ExpectTokenType(source, TT_NUMBER, TT_INTEGER, &token)) { BotFreeMatchTemplates(matches); FreeSource(source); return NULL; } //end if matchtemplate->type = token.intvalue; //read the match subtype if (!PC_ExpectTokenString(source, ",") || !PC_ExpectTokenType(source, TT_NUMBER, TT_INTEGER, &token)) { BotFreeMatchTemplates(matches); FreeSource(source); return NULL; } //end if matchtemplate->subtype = token.intvalue; //read trailing punctuations if (!PC_ExpectTokenString(source, ")") || !PC_ExpectTokenString(source, ";")) { BotFreeMatchTemplates(matches); FreeSource(source); return NULL; } //end if } //end while } //end while //free the source FreeSource(source); botimport.Print(PRT_MESSAGE, "loaded %s\n", matchfile); // //BotDumpMatchTemplates(matches); // return matches; } //end of the function BotLoadMatchTemplates //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int StringsMatch(bot_matchpiece_t *pieces, bot_match_t *match) { int lastvariable, index; char *strptr, *newstrptr; bot_matchpiece_t *mp; bot_matchstring_t *ms; //no last variable lastvariable = -1; //pointer to the string to compare the match string with strptr = match->string; //Log_Write("match: %s", strptr); //compare the string with the current match string for (mp = pieces; mp; mp = mp->next) { //if it is a piece of string if (mp->type == MT_STRING) { newstrptr = NULL; for (ms = mp->firststring; ms; ms = ms->next) { if (!(int)strlen(ms->string)) { newstrptr = strptr; break; } //end if //Log_Write("MT_STRING: %s", mp->string); index = StringContains(strptr, ms->string, qfalse); if (index >= 0) { newstrptr = strptr + index; if (lastvariable >= 0) { match->variables[lastvariable].length = (newstrptr - match->string) - match->variables[lastvariable].offset; //newstrptr - match->variables[lastvariable].ptr; lastvariable = -1; break; } //end if else if (index == 0) { break; } //end else newstrptr = NULL; } //end if } //end for if (!newstrptr) return qfalse; strptr = newstrptr + (int)strlen(ms->string); } //end if //if it is a variable piece of string else if (mp->type == MT_VARIABLE) { //Log_Write("MT_VARIABLE"); match->variables[mp->variable].offset = strptr - match->string; lastvariable = mp->variable; } //end else if } //end for //if a match was found if (!mp && (lastvariable >= 0 || !(int)strlen(strptr))) { //if the last piece was a variable string if (lastvariable >= 0) { assert( match->variables[lastvariable].offset >= 0 ); // bk001204 match->variables[lastvariable].length = (int)strlen(&match->string[ (int) match->variables[lastvariable].offset]); } //end if return qtrue; } //end if return qfalse; } //end of the function StringsMatch //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotFindMatch(char *str, bot_match_t *match, unsigned long int context) { int i; bot_matchtemplate_t *ms; strncpy(match->string, str, MAX_MESSAGE_SIZE); //remove any trailing enters while((int)strlen(match->string) && match->string[(int)strlen(match->string)-1] == '\n') { match->string[(int)strlen(match->string)-1] = '\0'; } //end while //compare the string with all the match strings for (ms = matchtemplates; ms; ms = ms->next) { if (!(ms->context & context)) continue; //reset the match variable offsets for (i = 0; i < MAX_MATCHVARIABLES; i++) match->variables[i].offset = -1; // if (StringsMatch(ms->first, match)) { match->type = ms->type; match->subtype = ms->subtype; return qtrue; } //end if } //end for return qfalse; } //end of the function BotFindMatch //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotMatchVariable(bot_match_t *match, int variable, char *buf, int size) { if (variable < 0 || variable >= MAX_MATCHVARIABLES) { botimport.Print(PRT_FATAL, "BotMatchVariable: variable out of range\n"); strcpy(buf, ""); return; } //end if if (match->variables[variable].offset >= 0) { if (match->variables[variable].length < size) size = match->variables[variable].length+1; assert( match->variables[variable].offset >= 0 ); // bk001204 strncpy(buf, &match->string[ (int) match->variables[variable].offset], size-1); buf[size-1] = '\0'; } //end if else { strcpy(buf, ""); } //end else return; } //end of the function BotMatchVariable //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bot_stringlist_t *BotFindStringInList(bot_stringlist_t *list, char *string) { bot_stringlist_t *s; for (s = list; s; s = s->next) { if (!strcmp(s->string, string)) return s; } //end for return NULL; } //end of the function BotFindStringInList //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bot_stringlist_t *BotCheckChatMessageIntegrety(char *message, bot_stringlist_t *stringlist) { int i; char *msgptr; char temp[MAX_MESSAGE_SIZE]; bot_stringlist_t *s; msgptr = message; // while(*msgptr) { if (*msgptr == ESCAPE_CHAR) { msgptr++; switch(*msgptr) { case 'v': //variable { //step over the 'v' msgptr++; while(*msgptr && *msgptr != ESCAPE_CHAR) msgptr++; //step over the trailing escape char if (*msgptr) msgptr++; break; } //end case case 'r': //random { //step over the 'r' msgptr++; for (i = 0; (*msgptr && *msgptr != ESCAPE_CHAR); i++) { temp[i] = *msgptr++; } //end while temp[i] = '\0'; //step over the trailing escape char if (*msgptr) msgptr++; //find the random keyword if (!RandomString(temp)) { if (!BotFindStringInList(stringlist, temp)) { Log_Write("%s = {\"%s\"} //MISSING RANDOM\r\n", temp, temp); s = (bot_stringlist_t*) GetClearedMemory(sizeof(bot_stringlist_t) + (int)strlen(temp) + 1); s->string = (char *) s + sizeof(bot_stringlist_t); strcpy(s->string, temp); s->next = stringlist; stringlist = s; } //end if } //end if break; } //end case default: { botimport.Print(PRT_FATAL, "BotCheckChatMessageIntegrety: message \"%s\" invalid escape char\n", message); break; } //end default } //end switch } //end if else { msgptr++; } //end else } //end while return stringlist; } //end of the function BotCheckChatMessageIntegrety //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotCheckInitialChatIntegrety(bot_chat_t *chat) { bot_chattype_t *t; bot_chatmessage_t *cm; bot_stringlist_t *stringlist, *s, *nexts; stringlist = NULL; for (t = chat->types; t; t = t->next) { for (cm = t->firstchatmessage; cm; cm = cm->next) { stringlist = BotCheckChatMessageIntegrety(cm->chatmessage, stringlist); } //end for } //end for for (s = stringlist; s; s = nexts) { nexts = s->next; FreeMemory(s); } //end for } //end of the function BotCheckInitialChatIntegrety //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotCheckReplyChatIntegrety(bot_replychat_t *replychat) { bot_replychat_t *rp; bot_chatmessage_t *cm; bot_stringlist_t *stringlist, *s, *nexts; stringlist = NULL; for (rp = replychat; rp; rp = rp->next) { for (cm = rp->firstchatmessage; cm; cm = cm->next) { stringlist = BotCheckChatMessageIntegrety(cm->chatmessage, stringlist); } //end for } //end for for (s = stringlist; s; s = nexts) { nexts = s->next; FreeMemory(s); } //end for } //end of the function BotCheckReplyChatIntegrety //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotDumpReplyChat(bot_replychat_t *replychat) { FILE *fp; bot_replychat_t *rp; bot_replychatkey_t *key; bot_chatmessage_t *cm; bot_matchpiece_t *mp; fp = Log_FilePointer(); if (!fp) return; fprintf(fp, "BotDumpReplyChat:\n"); for (rp = replychat; rp; rp = rp->next) { fprintf(fp, "["); for (key = rp->keys; key; key = key->next) { if (key->flags & RCKFL_AND) fprintf(fp, "&"); else if (key->flags & RCKFL_NOT) fprintf(fp, "!"); // if (key->flags & RCKFL_NAME) fprintf(fp, "name"); else if (key->flags & RCKFL_GENDERFEMALE) fprintf(fp, "female"); else if (key->flags & RCKFL_GENDERMALE) fprintf(fp, "male"); else if (key->flags & RCKFL_GENDERLESS) fprintf(fp, "it"); else if (key->flags & RCKFL_VARIABLES) { fprintf(fp, "("); for (mp = key->match; mp; mp = mp->next) { if (mp->type == MT_STRING) fprintf(fp, "\"%s\"", mp->firststring->string); else fprintf(fp, "%d", mp->variable); if (mp->next) fprintf(fp, ", "); } //end for fprintf(fp, ")"); } //end if else if (key->flags & RCKFL_STRING) { fprintf(fp, "\"%s\"", key->string); } //end if if (key->next) fprintf(fp, ", "); else fprintf(fp, "] = %1.0f\n", rp->priority); } //end for fprintf(fp, "{\n"); for (cm = rp->firstchatmessage; cm; cm = cm->next) { fprintf(fp, "\t\"%s\";\n", cm->chatmessage); } //end for fprintf(fp, "}\n"); } //end for } //end of the function BotDumpReplyChat //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotFreeReplyChat(bot_replychat_t *replychat) { bot_replychat_t *rp, *nextrp; bot_replychatkey_t *key, *nextkey; bot_chatmessage_t *cm, *nextcm; for (rp = replychat; rp; rp = nextrp) { nextrp = rp->next; for (key = rp->keys; key; key = nextkey) { nextkey = key->next; if (key->match) BotFreeMatchPieces(key->match); if (key->string) FreeMemory(key->string); FreeMemory(key); } //end for for (cm = rp->firstchatmessage; cm; cm = nextcm) { nextcm = cm->next; FreeMemory(cm); } //end for FreeMemory(rp); } //end for } //end of the function BotFreeReplyChat //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotCheckValidReplyChatKeySet(source_t *source, bot_replychatkey_t *keys) { int allprefixed, hasvariableskey, hasstringkey; bot_matchpiece_t *m; bot_matchstring_t *ms; bot_replychatkey_t *key, *key2; // allprefixed = qtrue; hasvariableskey = hasstringkey = qfalse; for (key = keys; key; key = key->next) { if (!(key->flags & (RCKFL_AND|RCKFL_NOT))) { allprefixed = qfalse; if (key->flags & RCKFL_VARIABLES) { for (m = key->match; m; m = m->next) { if (m->type == MT_VARIABLE) hasvariableskey = qtrue; } //end for } //end if else if (key->flags & RCKFL_STRING) { hasstringkey = qtrue; } //end else if } //end if else if ((key->flags & RCKFL_AND) && (key->flags & RCKFL_STRING)) { for (key2 = keys; key2; key2 = key2->next) { if (key2 == key) continue; if (key2->flags & RCKFL_NOT) continue; if (key2->flags & RCKFL_VARIABLES) { for (m = key2->match; m; m = m->next) { if (m->type == MT_STRING) { for (ms = m->firststring; ms; ms = ms->next) { if (StringContains(ms->string, key->string, qfalse) != -1) { break; } //end if } //end for if (ms) break; } //end if else if (m->type == MT_VARIABLE) { break; } //end if } //end for if (!m) { SourceWarning(source, "one of the match templates does not " "leave space for the key %s with the & prefix", key->string); } //end if } //end if } //end for } //end else if ((key->flags & RCKFL_NOT) && (key->flags & RCKFL_STRING)) { for (key2 = keys; key2; key2 = key2->next) { if (key2 == key) continue; if (key2->flags & RCKFL_NOT) continue; if (key2->flags & RCKFL_STRING) { if (StringContains(key2->string, key->string, qfalse) != -1) { SourceWarning(source, "the key %s with prefix ! is inside the key %s", key->string, key2->string); } //end if } //end if else if (key2->flags & RCKFL_VARIABLES) { for (m = key2->match; m; m = m->next) { if (m->type == MT_STRING) { for (ms = m->firststring; ms; ms = ms->next) { if (StringContains(ms->string, key->string, qfalse) != -1) { SourceWarning(source, "the key %s with prefix ! is inside " "the match template string %s", key->string, ms->string); } //end if } //end for } //end if } //end for } //end else if } //end for } //end if } //end for if (allprefixed) SourceWarning(source, "all keys have a & or ! prefix"); if (hasvariableskey && hasstringkey) { SourceWarning(source, "variables from the match template(s) could be " "invalid when outputting one of the chat messages"); } //end if } //end of the function BotCheckValidReplyChatKeySet //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bot_replychat_t *BotLoadReplyChat(char *filename) { char chatmessagestring[MAX_MESSAGE_SIZE]; char namebuffer[MAX_MESSAGE_SIZE]; source_t *source; token_t token; bot_chatmessage_t *chatmessage = NULL; bot_replychat_t *replychat, *replychatlist; bot_replychatkey_t *key; PC_SetBaseFolder(BOTFILESBASEFOLDER); source = LoadSourceFile(filename); if (!source) { botimport.Print(PRT_ERROR, "counldn't load %s\n", filename); return NULL; } //end if // replychatlist = NULL; // while(PC_ReadToken(source, &token)) { if (strcmp(token.string, "[")) { SourceError(source, "expected [, found %s", token.string); BotFreeReplyChat(replychatlist); FreeSource(source); return NULL; } //end if // replychat = (bot_replychat_t*) GetClearedHunkMemory(sizeof(bot_replychat_t)); replychat->keys = NULL; replychat->next = replychatlist; replychatlist = replychat; //read the keys, there must be at least one key do { //allocate a key key = (bot_replychatkey_t *) GetClearedHunkMemory(sizeof(bot_replychatkey_t)); key->flags = 0; key->string = NULL; key->match = NULL; key->next = replychat->keys; replychat->keys = key; //check for MUST BE PRESENT and MUST BE ABSENT keys if (PC_CheckTokenString(source, "&")) key->flags |= RCKFL_AND; else if (PC_CheckTokenString(source, "!")) key->flags |= RCKFL_NOT; //special keys if (PC_CheckTokenString(source, "name")) key->flags |= RCKFL_NAME; else if (PC_CheckTokenString(source, "female")) key->flags |= RCKFL_GENDERFEMALE; else if (PC_CheckTokenString(source, "male")) key->flags |= RCKFL_GENDERMALE; else if (PC_CheckTokenString(source, "it")) key->flags |= RCKFL_GENDERLESS; else if (PC_CheckTokenString(source, "(")) //match key { key->flags |= RCKFL_VARIABLES; key->match = BotLoadMatchPieces(source, ")"); if (!key->match) { BotFreeReplyChat(replychatlist); return NULL; } //end if } //end else if else if (PC_CheckTokenString(source, "<")) //bot names { key->flags |= RCKFL_BOTNAMES; strcpy(namebuffer, ""); do { if (!PC_ExpectTokenType(source, TT_STRING, 0, &token)) { BotFreeReplyChat(replychatlist); FreeSource(source); return NULL; } //end if StripDoubleQuotes(token.string); if ((int)strlen(namebuffer)) strcat(namebuffer, "\\"); strcat(namebuffer, token.string); } while(PC_CheckTokenString(source, ",")); if (!PC_ExpectTokenString(source, ">")) { BotFreeReplyChat(replychatlist); FreeSource(source); return NULL; } //end if key->string = (char *) GetClearedHunkMemory((int)strlen(namebuffer) + 1); strcpy(key->string, namebuffer); } //end else if else //normal string key { key->flags |= RCKFL_STRING; if (!PC_ExpectTokenType(source, TT_STRING, 0, &token)) { BotFreeReplyChat(replychatlist); FreeSource(source); return NULL; } //end if StripDoubleQuotes(token.string); key->string = (char *) GetClearedHunkMemory((int)strlen(token.string) + 1); strcpy(key->string, token.string); } //end else // PC_CheckTokenString(source, ","); } while(!PC_CheckTokenString(source, "]")); // BotCheckValidReplyChatKeySet(source, replychat->keys); //read the = sign and the priority if (!PC_ExpectTokenString(source, "=") || !PC_ExpectTokenType(source, TT_NUMBER, 0, &token)) { BotFreeReplyChat(replychatlist); FreeSource(source); return NULL; } //end if replychat->priority = token.floatvalue; //read the leading { if (!PC_ExpectTokenString(source, "{")) { BotFreeReplyChat(replychatlist); FreeSource(source); return NULL; } //end if replychat->numchatmessages = 0; //while the trailing } is not found while(!PC_CheckTokenString(source, "}")) { if (!BotLoadChatMessage(source, chatmessagestring)) { BotFreeReplyChat(replychatlist); FreeSource(source); return NULL; } //end if chatmessage = (bot_chatmessage_t *) GetClearedHunkMemory(sizeof(bot_chatmessage_t) + (int)strlen(chatmessagestring) + 1); chatmessage->chatmessage = (char *) chatmessage + sizeof(bot_chatmessage_t); strcpy(chatmessage->chatmessage, chatmessagestring); chatmessage->time = -2*CHATMESSAGE_RECENTTIME; chatmessage->next = replychat->firstchatmessage; //add the chat message to the reply chat replychat->firstchatmessage = chatmessage; replychat->numchatmessages++; } //end while } //end while FreeSource(source); botimport.Print(PRT_MESSAGE, "loaded %s\n", filename); // //BotDumpReplyChat(replychatlist); if (bot_developer) { BotCheckReplyChatIntegrety(replychatlist); } //end if // if (!replychatlist) botimport.Print(PRT_MESSAGE, "no rchats\n"); // return replychatlist; } //end of the function BotLoadReplyChat //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotDumpInitialChat(bot_chat_t *chat) { bot_chattype_t *t; bot_chatmessage_t *m; Log_Write("{"); for (t = chat->types; t; t = t->next) { Log_Write(" type \"%s\"", t->name); Log_Write(" {"); Log_Write(" numchatmessages = %d", t->numchatmessages); for (m = t->firstchatmessage; m; m = m->next) { Log_Write(" \"%s\"", m->chatmessage); } //end for Log_Write(" }"); } //end for Log_Write("}"); } //end of the function BotDumpInitialChat //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bot_chat_t *BotLoadInitialChat(char *chatfile, char *chatname) { int pass, foundchat, indent, size; char *ptr = NULL; char chatmessagestring[MAX_MESSAGE_SIZE]; source_t *source; token_t token; bot_chat_t *chat = NULL; bot_chattype_t *chattype = NULL; bot_chatmessage_t *chatmessage = NULL; #ifdef DEBUG int starttime; starttime = Sys_MilliSeconds(); #endif //DEBUG // size = 0; foundchat = qfalse; //a bot chat is parsed in two phases for (pass = 0; pass < 2; pass++) { //allocate memory if (pass && size) ptr = (char *) GetClearedMemory(size); //load the source file PC_SetBaseFolder(BOTFILESBASEFOLDER); source = LoadSourceFile(chatfile); if (!source) { botimport.Print(PRT_ERROR, "counldn't load %s\n", chatfile); return NULL; } //end if //chat structure if (pass) { chat = (bot_chat_t *) ptr; ptr += sizeof(bot_chat_t); } //end if size = sizeof(bot_chat_t); // while(PC_ReadToken(source, &token)) { if (!strcmp(token.string, "chat")) { if (!PC_ExpectTokenType(source, TT_STRING, 0, &token)) { FreeSource(source); return NULL; } //end if StripDoubleQuotes(token.string); //after the chat name we expect a opening brace if (!PC_ExpectTokenString(source, "{")) { FreeSource(source); return NULL; } //end if //if the chat name is found if (!Q_stricmp(token.string, chatname)) { foundchat = qtrue; //read the chat types while(1) { if (!PC_ExpectAnyToken(source, &token)) { FreeSource(source); return NULL; } //end if if (!strcmp(token.string, "}")) break; if (strcmp(token.string, "type")) { SourceError(source, "expected type found %s\n", token.string); FreeSource(source); return NULL; } //end if //expect the chat type name if (!PC_ExpectTokenType(source, TT_STRING, 0, &token) || !PC_ExpectTokenString(source, "{")) { FreeSource(source); return NULL; } //end if StripDoubleQuotes(token.string); if (pass) { chattype = (bot_chattype_t *) ptr; strncpy(chattype->name, token.string, MAX_CHATTYPE_NAME); chattype->firstchatmessage = NULL; //add the chat type to the chat chattype->next = chat->types; chat->types = chattype; // ptr += sizeof(bot_chattype_t); } //end if size += sizeof(bot_chattype_t); //read the chat messages while(!PC_CheckTokenString(source, "}")) { if (!BotLoadChatMessage(source, chatmessagestring)) { FreeSource(source); return NULL; } //end if if (pass) { chatmessage = (bot_chatmessage_t *) ptr; chatmessage->time = -2*CHATMESSAGE_RECENTTIME; //put the chat message in the list chatmessage->next = chattype->firstchatmessage; chattype->firstchatmessage = chatmessage; //store the chat message ptr += sizeof(bot_chatmessage_t); chatmessage->chatmessage = ptr; strcpy(chatmessage->chatmessage, chatmessagestring); ptr += (int)strlen(chatmessagestring) + 1; //the number of chat messages increased chattype->numchatmessages++; } //end if size += sizeof(bot_chatmessage_t) + (int)strlen(chatmessagestring) + 1; } //end if } //end while } //end if else //skip the bot chat { indent = 1; while(indent) { if (!PC_ExpectAnyToken(source, &token)) { FreeSource(source); return NULL; } //end if if (!strcmp(token.string, "{")) indent++; else if (!strcmp(token.string, "}")) indent--; } //end while } //end else } //end if else { SourceError(source, "unknown definition %s\n", token.string); FreeSource(source); return NULL; } //end else } //end while //free the source FreeSource(source); //if the requested character is not found if (!foundchat) { botimport.Print(PRT_ERROR, "couldn't find chat %s in %s\n", chatname, chatfile); return NULL; } //end if } //end for // botimport.Print(PRT_MESSAGE, "loaded %s from %s\n", chatname, chatfile); // //BotDumpInitialChat(chat); if (bot_developer) { BotCheckInitialChatIntegrety(chat); } //end if #ifdef DEBUG botimport.Print(PRT_MESSAGE, "initial chats loaded in %d msec\n", Sys_MilliSeconds() - starttime); #endif //DEBUG //character was read succesfully return chat; } //end of the function BotLoadInitialChat //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotFreeChatFile(int chatstate) { bot_chatstate_t *cs; cs = BotChatStateFromHandle(chatstate); if (!cs) return; if (cs->chat) FreeMemory(cs->chat); cs->chat = NULL; } //end of the function BotFreeChatFile //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotLoadChatFile(int chatstate, char *chatfile, char *chatname) { bot_chatstate_t *cs; int n, avail = 0; cs = BotChatStateFromHandle(chatstate); if (!cs) return BLERR_CANNOTLOADICHAT; BotFreeChatFile(chatstate); if (!LibVarGetValue("bot_reloadcharacters")) { avail = -1; for( n = 0; n < MAX_CLIENTS; n++ ) { if( !ichatdata[n] ) { if( avail == -1 ) { avail = n; } continue; } if( strcmp( chatfile, ichatdata[n]->filename ) != 0 ) { continue; } if( strcmp( chatname, ichatdata[n]->chatname ) != 0 ) { continue; } cs->chat = ichatdata[n]->chat; // botimport.Print( PRT_MESSAGE, "retained %s from %s\n", chatname, chatfile ); return BLERR_NOERROR; } if( avail == -1 ) { botimport.Print(PRT_FATAL, "ichatdata table full; couldn't load chat %s from %s\n", chatname, chatfile); return BLERR_CANNOTLOADICHAT; } } cs->chat = BotLoadInitialChat(chatfile, chatname); if (!cs->chat) { botimport.Print(PRT_FATAL, "couldn't load chat %s from %s\n", chatname, chatfile); return BLERR_CANNOTLOADICHAT; } //end if if (!LibVarGetValue("bot_reloadcharacters")) { ichatdata[avail] = (bot_ichatdata_t*) GetClearedMemory( sizeof(bot_ichatdata_t) ); ichatdata[avail]->chat = cs->chat; Q_strncpyz( ichatdata[avail]->chatname, chatname, sizeof(ichatdata[avail]->chatname) ); Q_strncpyz( ichatdata[avail]->filename, chatfile, sizeof(ichatdata[avail]->filename) ); } //end if return BLERR_NOERROR; } //end of the function BotLoadChatFile //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotExpandChatMessage(char *outmessage, char *message, unsigned long mcontext, bot_match_t *match, unsigned long vcontext, int reply) { int num, len, i, expansion; char *outputbuf, *ptr, *msgptr; char temp[MAX_MESSAGE_SIZE]; expansion = qfalse; msgptr = message; outputbuf = outmessage; len = 0; // while(*msgptr) { if (*msgptr == ESCAPE_CHAR) { msgptr++; switch(*msgptr) { case 'v': //variable { msgptr++; num = 0; while(*msgptr && *msgptr != ESCAPE_CHAR) { num = num * 10 + (*msgptr++) - '0'; } //end while //step over the trailing escape char if (*msgptr) msgptr++; if (num > MAX_MATCHVARIABLES) { botimport.Print(PRT_ERROR, "BotConstructChat: message %s variable %d out of range\n", message, num); return qfalse; } //end if if (match->variables[num].offset >= 0) { assert( match->variables[num].offset >= 0 ); // bk001204 ptr = &match->string[ (int) match->variables[num].offset]; for (i = 0; i < match->variables[num].length; i++) { temp[i] = ptr[i]; } //end for temp[i] = 0; //if it's a reply message if (reply) { //replace the reply synonyms in the variables BotReplaceReplySynonyms(temp, vcontext); } //end if else { //replace synonyms in the variable context BotReplaceSynonyms(temp, vcontext); } //end else // if (len + (int)strlen(temp) >= MAX_MESSAGE_SIZE) { botimport.Print(PRT_ERROR, "BotConstructChat: message %s too long\n", message); return qfalse; } //end if strcpy(&outputbuf[len], temp); len += (int)strlen(temp); } //end if break; } //end case case 'r': //random { msgptr++; for (i = 0; (*msgptr && *msgptr != ESCAPE_CHAR); i++) { temp[i] = *msgptr++; } //end while temp[i] = '\0'; //step over the trailing escape char if (*msgptr) msgptr++; //find the random keyword ptr = RandomString(temp); if (!ptr) { botimport.Print(PRT_ERROR, "BotConstructChat: unknown random string %s\n", temp); return qfalse; } //end if if (len + (int)strlen(ptr) >= MAX_MESSAGE_SIZE) { botimport.Print(PRT_ERROR, "BotConstructChat: message \"%s\" too long\n", message); return qfalse; } //end if strcpy(&outputbuf[len], ptr); len += (int)strlen(ptr); expansion = qtrue; break; } //end case default: { botimport.Print(PRT_FATAL, "BotConstructChat: message \"%s\" invalid escape char\n", message); break; } //end default } //end switch } //end if else { outputbuf[len++] = *msgptr++; if (len >= MAX_MESSAGE_SIZE) { botimport.Print(PRT_ERROR, "BotConstructChat: message \"%s\" too long\n", message); break; } //end if } //end else } //end while outputbuf[len] = '\0'; //replace synonyms weighted in the message context BotReplaceWeightedSynonyms(outputbuf, mcontext); //return true if a random was expanded return expansion; } //end of the function BotExpandChatMessage //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotConstructChatMessage(bot_chatstate_t *chatstate, char *message, unsigned long mcontext, bot_match_t *match, unsigned long vcontext, int reply) { int i; char srcmessage[MAX_MESSAGE_SIZE]; strcpy(srcmessage, message); for (i = 0; i < 10; i++) { if (!BotExpandChatMessage(chatstate->chatmessage, srcmessage, mcontext, match, vcontext, reply)) { break; } //end if strcpy(srcmessage, chatstate->chatmessage); } //end for if (i >= 10) { botimport.Print(PRT_WARNING, "too many expansions in chat message\n"); botimport.Print(PRT_WARNING, "%s\n", chatstate->chatmessage); } //end if } //end of the function BotConstructChatMessage //=========================================================================== // randomly chooses one of the chat message of the given type // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== char *BotChooseInitialChatMessage(bot_chatstate_t *cs, char *type) { int n, numchatmessages; float besttime; bot_chattype_t *t; bot_chatmessage_t *m, *bestchatmessage; bot_chat_t *chat; chat = cs->chat; for (t = chat->types; t; t = t->next) { if (!Q_stricmp(t->name, type)) { numchatmessages = 0; for (m = t->firstchatmessage; m; m = m->next) { if (m->time > AAS_Time()) continue; numchatmessages++; } //end if //if all chat messages have been used recently if (numchatmessages <= 0) { besttime = 0; bestchatmessage = NULL; for (m = t->firstchatmessage; m; m = m->next) { if (!besttime || m->time < besttime) { bestchatmessage = m; besttime = m->time; } //end if } //end for if (bestchatmessage) return bestchatmessage->chatmessage; } //end if else //choose a chat message randomly { n = random() * numchatmessages; for (m = t->firstchatmessage; m; m = m->next) { if (m->time > AAS_Time()) continue; if (--n < 0) { m->time = AAS_Time() + CHATMESSAGE_RECENTTIME; return m->chatmessage; } //end if } //end for } //end else return NULL; } //end if } //end for return NULL; } //end of the function BotChooseInitialChatMessage //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotNumInitialChats(int chatstate, char *type) { bot_chatstate_t *cs; bot_chattype_t *t; cs = BotChatStateFromHandle(chatstate); if (!cs) return 0; for (t = cs->chat->types; t; t = t->next) { if (!Q_stricmp(t->name, type)) { if (LibVarGetValue("bot_testichat")) { botimport.Print(PRT_MESSAGE, "%s has %d chat lines\n", type, t->numchatmessages); botimport.Print(PRT_MESSAGE, "-------------------\n"); } return t->numchatmessages; } //end if } //end for return 0; } //end of the function BotNumInitialChats //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotInitialChat(int chatstate, char *type, int mcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7) { char *message; int index; bot_match_t match; bot_chatstate_t *cs; cs = BotChatStateFromHandle(chatstate); if (!cs) return; //if no chat file is loaded if (!cs->chat) return; //choose a chat message randomly of the given type message = BotChooseInitialChatMessage(cs, type); //if there's no message of the given type if (!message) { #ifdef DEBUG botimport.Print(PRT_MESSAGE, "no chat messages of type %s\n", type); #endif //DEBUG return; } //end if // Com_Memset(&match, 0, sizeof(match)); index = 0; if( var0 ) { strcat(match.string, var0); match.variables[0].offset = index; match.variables[0].length = (int)strlen(var0); index += (int)strlen(var0); } if( var1 ) { strcat(match.string, var1); match.variables[1].offset = index; match.variables[1].length = (int)strlen(var1); index += (int)strlen(var1); } if( var2 ) { strcat(match.string, var2); match.variables[2].offset = index; match.variables[2].length = (int)strlen(var2); index += (int)strlen(var2); } if( var3 ) { strcat(match.string, var3); match.variables[3].offset = index; match.variables[3].length = (int)strlen(var3); index += (int)strlen(var3); } if( var4 ) { strcat(match.string, var4); match.variables[4].offset = index; match.variables[4].length = (int)strlen(var4); index += (int)strlen(var4); } if( var5 ) { strcat(match.string, var5); match.variables[5].offset = index; match.variables[5].length = (int)strlen(var5); index += (int)strlen(var5); } if( var6 ) { strcat(match.string, var6); match.variables[6].offset = index; match.variables[6].length = (int)strlen(var6); index += (int)strlen(var6); } if( var7 ) { strcat(match.string, var7); match.variables[7].offset = index; match.variables[7].length = (int)strlen(var7); index += (int)strlen(var7); } // BotConstructChatMessage(cs, message, mcontext, &match, 0, qfalse); } //end of the function BotInitialChat //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotPrintReplyChatKeys(bot_replychat_t *replychat) { bot_replychatkey_t *key; bot_matchpiece_t *mp; botimport.Print(PRT_MESSAGE, "["); for (key = replychat->keys; key; key = key->next) { if (key->flags & RCKFL_AND) botimport.Print(PRT_MESSAGE, "&"); else if (key->flags & RCKFL_NOT) botimport.Print(PRT_MESSAGE, "!"); // if (key->flags & RCKFL_NAME) botimport.Print(PRT_MESSAGE, "name"); else if (key->flags & RCKFL_GENDERFEMALE) botimport.Print(PRT_MESSAGE, "female"); else if (key->flags & RCKFL_GENDERMALE) botimport.Print(PRT_MESSAGE, "male"); else if (key->flags & RCKFL_GENDERLESS) botimport.Print(PRT_MESSAGE, "it"); else if (key->flags & RCKFL_VARIABLES) { botimport.Print(PRT_MESSAGE, "("); for (mp = key->match; mp; mp = mp->next) { if (mp->type == MT_STRING) botimport.Print(PRT_MESSAGE, "\"%s\"", mp->firststring->string); else botimport.Print(PRT_MESSAGE, "%d", mp->variable); if (mp->next) botimport.Print(PRT_MESSAGE, ", "); } //end for botimport.Print(PRT_MESSAGE, ")"); } //end if else if (key->flags & RCKFL_STRING) { botimport.Print(PRT_MESSAGE, "\"%s\"", key->string); } //end if if (key->next) botimport.Print(PRT_MESSAGE, ", "); else botimport.Print(PRT_MESSAGE, "] = %1.0f\n", replychat->priority); } //end for botimport.Print(PRT_MESSAGE, "{\n"); } //end of the function BotPrintReplyChatKeys //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotReplyChat(int chatstate, char *message, int mcontext, int vcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7) { bot_replychat_t *rchat, *bestrchat; bot_replychatkey_t *key; bot_chatmessage_t *m, *bestchatmessage; bot_match_t match, bestmatch; int bestpriority, num, found, res, numchatmessages, index; bot_chatstate_t *cs; cs = BotChatStateFromHandle(chatstate); if (!cs) return qfalse; Com_Memset(&match, 0, sizeof(bot_match_t)); strcpy(match.string, message); bestpriority = -1; bestchatmessage = NULL; bestrchat = NULL; //go through all the reply chats for (rchat = replychats; rchat; rchat = rchat->next) { found = qfalse; for (key = rchat->keys; key; key = key->next) { res = qfalse; //get the match result if (key->flags & RCKFL_NAME) res = (StringContains(message, cs->name, qfalse) != -1); else if (key->flags & RCKFL_BOTNAMES) res = (StringContains(key->string, cs->name, qfalse) != -1); else if (key->flags & RCKFL_GENDERFEMALE) res = (cs->gender == CHAT_GENDERFEMALE); else if (key->flags & RCKFL_GENDERMALE) res = (cs->gender == CHAT_GENDERMALE); else if (key->flags & RCKFL_GENDERLESS) res = (cs->gender == CHAT_GENDERLESS); else if (key->flags & RCKFL_VARIABLES) res = StringsMatch(key->match, &match); else if (key->flags & RCKFL_STRING) res = (StringContainsWord(message, key->string, qfalse) != NULL); //if the key must be present if (key->flags & RCKFL_AND) { if (!res) { found = qfalse; break; } //end if } //end else if //if the key must be absent else if (key->flags & RCKFL_NOT) { if (res) { found = qfalse; break; } //end if } //end if else if (res) { found = qtrue; } //end else } //end for // if (found) { if (rchat->priority > bestpriority) { numchatmessages = 0; for (m = rchat->firstchatmessage; m; m = m->next) { if (m->time > AAS_Time()) continue; numchatmessages++; } //end if num = random() * numchatmessages; for (m = rchat->firstchatmessage; m; m = m->next) { if (--num < 0) break; if (m->time > AAS_Time()) continue; } //end for //if the reply chat has a message if (m) { Com_Memcpy(&bestmatch, &match, sizeof(bot_match_t)); bestchatmessage = m; bestrchat = rchat; bestpriority = rchat->priority; } //end if } //end if } //end if } //end for if (bestchatmessage) { index = (int)strlen(bestmatch.string); if( var0 ) { strcat(bestmatch.string, var0); bestmatch.variables[0].offset = index; bestmatch.variables[0].length = (int)strlen(var0); index += (int)strlen(var0); } if( var1 ) { strcat(bestmatch.string, var1); bestmatch.variables[1].offset = index; bestmatch.variables[1].length = (int)strlen(var1); index += (int)strlen(var1); } if( var2 ) { strcat(bestmatch.string, var2); bestmatch.variables[2].offset = index; bestmatch.variables[2].length = (int)strlen(var2); index += (int)strlen(var2); } if( var3 ) { strcat(bestmatch.string, var3); bestmatch.variables[3].offset = index; bestmatch.variables[3].length = (int)strlen(var3); index += (int)strlen(var3); } if( var4 ) { strcat(bestmatch.string, var4); bestmatch.variables[4].offset = index; bestmatch.variables[4].length = (int)strlen(var4); index += (int)strlen(var4); } if( var5 ) { strcat(bestmatch.string, var5); bestmatch.variables[5].offset = index; bestmatch.variables[5].length = (int)strlen(var5); index += (int)strlen(var5); } if( var6 ) { strcat(bestmatch.string, var6); bestmatch.variables[6].offset = index; bestmatch.variables[6].length = (int)strlen(var6); index += (int)strlen(var6); } if( var7 ) { strcat(bestmatch.string, var7); bestmatch.variables[7].offset = index; bestmatch.variables[7].length = (int)strlen(var7); index += (int)strlen(var7); } if (LibVarGetValue("bot_testrchat")) { for (m = bestrchat->firstchatmessage; m; m = m->next) { BotConstructChatMessage(cs, m->chatmessage, mcontext, &bestmatch, vcontext, qtrue); BotRemoveTildes(cs->chatmessage); botimport.Print(PRT_MESSAGE, "%s\n", cs->chatmessage); } //end if } //end if else { bestchatmessage->time = AAS_Time() + CHATMESSAGE_RECENTTIME; BotConstructChatMessage(cs, bestchatmessage->chatmessage, mcontext, &bestmatch, vcontext, qtrue); } //end else return qtrue; } //end if return qfalse; } //end of the function BotReplyChat //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotChatLength(int chatstate) { bot_chatstate_t *cs; cs = BotChatStateFromHandle(chatstate); if (!cs) return 0; return (int)strlen(cs->chatmessage); } //end of the function BotChatLength //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotEnterChat(int chatstate, int clientto, int sendto) { bot_chatstate_t *cs; cs = BotChatStateFromHandle(chatstate); if (!cs) return; if ((int)strlen(cs->chatmessage)) { BotRemoveTildes(cs->chatmessage); if (LibVarGetValue("bot_testichat")) { botimport.Print(PRT_MESSAGE, "%s\n", cs->chatmessage); } else { switch(sendto) { case CHAT_TEAM: EA_Command(cs->client, va("say_team %s", cs->chatmessage)); break; case CHAT_TELL: EA_Command(cs->client, va("tell %d %s", clientto, cs->chatmessage)); break; default: //CHAT_ALL EA_Command(cs->client, va("say %s", cs->chatmessage)); break; } } //clear the chat message from the state strcpy(cs->chatmessage, ""); } //end if } //end of the function BotEnterChat //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotGetChatMessage(int chatstate, char *buf, int size) { bot_chatstate_t *cs; cs = BotChatStateFromHandle(chatstate); if (!cs) return; BotRemoveTildes(cs->chatmessage); strncpy(buf, cs->chatmessage, size-1); buf[size-1] = '\0'; //clear the chat message from the state strcpy(cs->chatmessage, ""); } //end of the function BotGetChatMessage //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotSetChatGender(int chatstate, int gender) { bot_chatstate_t *cs; cs = BotChatStateFromHandle(chatstate); if (!cs) return; switch(gender) { case CHAT_GENDERFEMALE: cs->gender = CHAT_GENDERFEMALE; break; case CHAT_GENDERMALE: cs->gender = CHAT_GENDERMALE; break; default: cs->gender = CHAT_GENDERLESS; break; } //end switch } //end of the function BotSetChatGender //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotSetChatName(int chatstate, char *name, int client) { bot_chatstate_t *cs; cs = BotChatStateFromHandle(chatstate); if (!cs) return; cs->client = client; Com_Memset(cs->name, 0, sizeof(cs->name)); strncpy(cs->name, name, sizeof(cs->name)); cs->name[sizeof(cs->name)-1] = '\0'; } //end of the function BotSetChatName //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotResetChatAI(void) { bot_replychat_t *rchat; bot_chatmessage_t *m; for (rchat = replychats; rchat; rchat = rchat->next) { for (m = rchat->firstchatmessage; m; m = m->next) { m->time = 0; } //end for } //end for } //end of the function BotResetChatAI //======================================================================== // // Parameter: - // Returns: - // Changes Globals: - //======================================================================== int BotAllocChatState(void) { int i; for (i = 1; i <= MAX_CLIENTS; i++) { if (!botchatstates[i]) { botchatstates[i] = (bot_chatstate_t*) GetClearedMemory(sizeof(bot_chatstate_t)); return i; } //end if } //end for return 0; } //end of the function BotAllocChatState //======================================================================== // // Parameter: - // Returns: - // Changes Globals: - //======================================================================== void BotFreeChatState(int handle) { bot_chatstate_t *cs; bot_consolemessage_t m; int h; if (handle <= 0 || handle > MAX_CLIENTS) { botimport.Print(PRT_FATAL, "chat state handle %d out of range\n", handle); return; } //end if if (!botchatstates[handle]) { botimport.Print(PRT_FATAL, "invalid chat state %d\n", handle); return; } //end if cs = botchatstates[handle]; if (LibVarGetValue("bot_reloadcharacters")) { BotFreeChatFile(handle); } //end if //free all the console messages left in the chat state for (h = BotNextConsoleMessage(handle, &m); h; h = BotNextConsoleMessage(handle, &m)) { //remove the console message BotRemoveConsoleMessage(handle, h); } //end for FreeMemory(botchatstates[handle]); botchatstates[handle] = NULL; } //end of the function BotFreeChatState //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotSetupChatAI(void) { char *file; #ifdef DEBUG int starttime = Sys_MilliSeconds(); #endif //DEBUG file = LibVarString("synfile", "syn.c"); synonyms = BotLoadSynonyms(file); file = LibVarString("rndfile", "rnd.c"); randomstrings = BotLoadRandomStrings(file); file = LibVarString("matchfile", "match.c"); matchtemplates = BotLoadMatchTemplates(file); // if (!LibVarValue("nochat", "0")) { file = LibVarString("rchatfile", "rchat.c"); replychats = BotLoadReplyChat(file); } //end if InitConsoleMessageHeap(); #ifdef DEBUG botimport.Print(PRT_MESSAGE, "setup chat AI %d msec\n", Sys_MilliSeconds() - starttime); #endif //DEBUG return BLERR_NOERROR; } //end of the function BotSetupChatAI //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotShutdownChatAI(void) { int i; //free all remaining chat states for(i = 0; i < MAX_CLIENTS; i++) { if (botchatstates[i]) { BotFreeChatState(i); } //end if } //end for //free all cached chats for(i = 0; i < MAX_CLIENTS; i++) { if (ichatdata[i]) { FreeMemory(ichatdata[i]->chat); FreeMemory(ichatdata[i]); ichatdata[i] = NULL; } //end if } //end for if (consolemessageheap) FreeMemory(consolemessageheap); consolemessageheap = NULL; if (matchtemplates) BotFreeMatchTemplates(matchtemplates); matchtemplates = NULL; if (randomstrings) FreeMemory(randomstrings); randomstrings = NULL; if (synonyms) FreeMemory(synonyms); synonyms = NULL; if (replychats) BotFreeReplyChat(replychats); replychats = NULL; } //end of the function BotShutdownChatAI ================================================ FILE: src/engine/botlib/be_ai_gen.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_ai_gen.c * * desc: genetic selection * * $Archive: /MissionPack/code/botlib/be_ai_gen.c $ * *****************************************************************************/ #include "../../game/q_shared.h" #include "l_memory.h" #include "l_log.h" #include "l_utils.h" #include "l_script.h" #include "l_precomp.h" #include "l_struct.h" #include "aasfile.h" #include "../../game/botlib.h" #include "../../game/be_aas.h" #include "be_aas_funcs.h" #include "be_interface.h" #include "../../game/be_ai_gen.h" //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int GeneticSelection(int numranks, float *rankings) { float sum, select; int i, index; sum = 0; for (i = 0; i < numranks; i++) { if (rankings[i] < 0) continue; sum += rankings[i]; } //end for if (sum > 0) { //select a bot where the ones with the higest rankings have //the highest chance of being selected select = random() * sum; for (i = 0; i < numranks; i++) { if (rankings[i] < 0) continue; sum -= rankings[i]; if (sum <= 0) return i; } //end for } //end if //select a bot randomly index = random() * numranks; for (i = 0; i < numranks; i++) { if (rankings[index] >= 0) return index; index = (index + 1) % numranks; } //end for return 0; } //end of the function GeneticSelection //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int GeneticParentsAndChildSelection(int numranks, float *ranks, int *parent1, int *parent2, int *child) { float rankings[256], max; int i; if (numranks > 256) { botimport.Print(PRT_WARNING, "GeneticParentsAndChildSelection: too many bots\n"); *parent1 = *parent2 = *child = 0; return qfalse; } //end if for (max = 0, i = 0; i < numranks; i++) { if (ranks[i] < 0) continue; max++; } //end for if (max < 3) { botimport.Print(PRT_WARNING, "GeneticParentsAndChildSelection: too few valid bots\n"); *parent1 = *parent2 = *child = 0; return qfalse; } //end if Com_Memcpy(rankings, ranks, sizeof(float) * numranks); //select first parent *parent1 = GeneticSelection(numranks, rankings); rankings[*parent1] = -1; //select second parent *parent2 = GeneticSelection(numranks, rankings); rankings[*parent2] = -1; //reverse the rankings max = 0; for (i = 0; i < numranks; i++) { if (rankings[i] < 0) continue; if (rankings[i] > max) max = rankings[i]; } //end for for (i = 0; i < numranks; i++) { if (rankings[i] < 0) continue; rankings[i] = max - rankings[i]; } //end for //select child *child = GeneticSelection(numranks, rankings); return qtrue; } //end of the function GeneticParentsAndChildSelection ================================================ FILE: src/engine/botlib/be_ai_goal.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_ai_goal.c * * desc: goal AI * * $Archive: /MissionPack/code/botlib/be_ai_goal.c $ * *****************************************************************************/ #include "../../game/q_shared.h" #include "l_utils.h" #include "l_libvar.h" #include "l_memory.h" #include "l_log.h" #include "l_script.h" #include "l_precomp.h" #include "l_struct.h" #include "aasfile.h" #include "../../game/botlib.h" #include "../../game/be_aas.h" #include "be_aas_funcs.h" #include "be_interface.h" #include "be_ai_weight.h" #include "../../game/be_ai_goal.h" #include "../../game/be_ai_move.h" //#define DEBUG_AI_GOAL #ifdef RANDOMIZE #define UNDECIDEDFUZZY #endif //RANDOMIZE #define DROPPEDWEIGHT //minimum avoid goal time #define AVOID_MINIMUM_TIME 10 //default avoid goal time #define AVOID_DEFAULT_TIME 30 //avoid dropped goal time #define AVOID_DROPPED_TIME 10 // #define TRAVELTIME_SCALE 0.01 //item flags #define IFL_NOTFREE 1 //not in free for all #define IFL_NOTTEAM 2 //not in team play #define IFL_NOTSINGLE 4 //not in single player #define IFL_NOTBOT 8 //bot should never go for this #define IFL_ROAM 16 //bot roam goal //location in the map "target_location" typedef struct maplocation_s { vec3_t origin; int areanum; char name[MAX_EPAIRKEY]; struct maplocation_s *next; } maplocation_t; //camp spots "info_camp" typedef struct campspot_s { vec3_t origin; int areanum; char name[MAX_EPAIRKEY]; float range; float weight; float wait; float random; struct campspot_s *next; } campspot_t; //FIXME: these are game specific typedef enum { GT_FFA, // free for all GT_TOURNAMENT, // one on one tournament GT_SINGLE_PLAYER, // single player tournament //-- team games go after this -- GT_TEAM, // team deathmatch GT_CTF, // capture the flag GT_MAX_GAME_TYPE } gametype_t; typedef struct levelitem_s { int number; //number of the level item int iteminfo; //index into the item info int flags; //item flags float weight; //fixed roam weight vec3_t origin; //origin of the item int goalareanum; //area the item is in vec3_t goalorigin; //goal origin within the area int entitynum; //entity number float timeout; //item is removed after this time struct levelitem_s *prev, *next; } levelitem_t; typedef struct iteminfo_s { char classname[32]; //classname of the item char name[MAX_STRINGFIELD]; //name of the item char model[MAX_STRINGFIELD]; //model of the item int modelindex; //model index int type; //item type int index; //index in the inventory float respawntime; //respawn time vec3_t mins; //mins of the item vec3_t maxs; //maxs of the item int number; //number of the item info } iteminfo_t; #define ITEMINFO_OFS(x) (int)(intptr_t)&(((iteminfo_t *)0)->x) fielddef_t iteminfo_fields[] = { {"name", ITEMINFO_OFS(name), FT_STRING}, {"model", ITEMINFO_OFS(model), FT_STRING}, {"modelindex", ITEMINFO_OFS(modelindex), FT_INT}, {"type", ITEMINFO_OFS(type), FT_INT}, {"index", ITEMINFO_OFS(index), FT_INT}, {"respawntime", ITEMINFO_OFS(respawntime), FT_FLOAT}, {"mins", ITEMINFO_OFS(mins), FT_FLOAT|FT_ARRAY, 3}, {"maxs", ITEMINFO_OFS(maxs), FT_FLOAT|FT_ARRAY, 3}, {0, 0, 0} }; structdef_t iteminfo_struct = { sizeof(iteminfo_t), iteminfo_fields }; typedef struct itemconfig_s { int numiteminfo; iteminfo_t *iteminfo; } itemconfig_t; //goal state typedef struct bot_goalstate_s { struct weightconfig_s *itemweightconfig; //weight config int *itemweightindex; //index from item to weight // int client; //client using this goal state int lastreachabilityarea; //last area with reachabilities the bot was in // bot_goal_t goalstack[MAX_GOALSTACK]; //goal stack int goalstacktop; //the top of the goal stack // int avoidgoals[MAX_AVOIDGOALS]; //goals to avoid float avoidgoaltimes[MAX_AVOIDGOALS]; //times to avoid the goals } bot_goalstate_t; bot_goalstate_t *botgoalstates[MAX_CLIENTS + 1]; // bk001206 - FIXME: init? //item configuration itemconfig_t *itemconfig = NULL; // bk001206 - init //level items levelitem_t *levelitemheap = NULL; // bk001206 - init levelitem_t *freelevelitems = NULL; // bk001206 - init levelitem_t *levelitems = NULL; // bk001206 - init int numlevelitems = 0; //map locations maplocation_t *maplocations = NULL; // bk001206 - init //camp spots campspot_t *campspots = NULL; // bk001206 - init //the game type int g_gametype = 0; // bk001206 - init //additional dropped item weight libvar_t *droppedweight = NULL; // bk001206 - init //======================================================================== // // Parameter: - // Returns: - // Changes Globals: - //======================================================================== bot_goalstate_t *BotGoalStateFromHandle(int handle) { if (handle <= 0 || handle > MAX_CLIENTS) { botimport.Print(PRT_FATAL, "goal state handle %d out of range\n", handle); return NULL; } //end if if (!botgoalstates[handle]) { botimport.Print(PRT_FATAL, "invalid goal state %d\n", handle); return NULL; } //end if return botgoalstates[handle]; } //end of the function BotGoalStateFromHandle //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotInterbreedGoalFuzzyLogic(int parent1, int parent2, int child) { bot_goalstate_t *p1, *p2, *c; p1 = BotGoalStateFromHandle(parent1); p2 = BotGoalStateFromHandle(parent2); c = BotGoalStateFromHandle(child); InterbreedWeightConfigs(p1->itemweightconfig, p2->itemweightconfig, c->itemweightconfig); } //end of the function BotInterbreedingGoalFuzzyLogic //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotSaveGoalFuzzyLogic(int goalstate, char *filename) { bot_goalstate_t *gs; gs = BotGoalStateFromHandle(goalstate); //WriteWeightConfig(filename, gs->itemweightconfig); } //end of the function BotSaveGoalFuzzyLogic //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotMutateGoalFuzzyLogic(int goalstate, float range) { bot_goalstate_t *gs; gs = BotGoalStateFromHandle(goalstate); EvolveWeightConfig(gs->itemweightconfig); } //end of the function BotMutateGoalFuzzyLogic //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== itemconfig_t *LoadItemConfig(char *filename) { int max_iteminfo; token_t token; char path[MAX_PATH]; source_t *source; itemconfig_t *ic; iteminfo_t *ii; max_iteminfo = (int) LibVarValue("max_iteminfo", "256"); if (max_iteminfo < 0) { botimport.Print(PRT_ERROR, "max_iteminfo = %d\n", max_iteminfo); max_iteminfo = 256; LibVarSet( "max_iteminfo", "256" ); } strncpy( path, filename, MAX_PATH ); PC_SetBaseFolder(BOTFILESBASEFOLDER); source = LoadSourceFile( path ); if( !source ) { botimport.Print( PRT_ERROR, "counldn't load %s\n", path ); return NULL; } //end if //initialize item config ic = (itemconfig_t *) GetClearedHunkMemory(sizeof(itemconfig_t) + max_iteminfo * sizeof(iteminfo_t)); ic->iteminfo = (iteminfo_t *) ((char *) ic + sizeof(itemconfig_t)); ic->numiteminfo = 0; //parse the item config file while(PC_ReadToken(source, &token)) { if (!strcmp(token.string, "iteminfo")) { if (ic->numiteminfo >= max_iteminfo) { SourceError(source, "more than %d item info defined\n", max_iteminfo); FreeMemory(ic); FreeSource(source); return NULL; } //end if ii = &ic->iteminfo[ic->numiteminfo]; Com_Memset(ii, 0, sizeof(iteminfo_t)); if (!PC_ExpectTokenType(source, TT_STRING, 0, &token)) { FreeMemory(ic); FreeMemory(source); return NULL; } //end if StripDoubleQuotes(token.string); strncpy(ii->classname, token.string, sizeof(ii->classname)-1); if (!ReadStructure(source, &iteminfo_struct, (char *) ii)) { FreeMemory(ic); FreeSource(source); return NULL; } //end if ii->number = ic->numiteminfo; ic->numiteminfo++; } //end if else { SourceError(source, "unknown definition %s\n", token.string); FreeMemory(ic); FreeSource(source); return NULL; } //end else } //end while FreeSource(source); // if (!ic->numiteminfo) botimport.Print(PRT_WARNING, "no item info loaded\n"); botimport.Print(PRT_MESSAGE, "loaded %s\n", path); return ic; } //end of the function LoadItemConfig //=========================================================================== // index to find the weight function of an iteminfo // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int *ItemWeightIndex(weightconfig_t *iwc, itemconfig_t *ic) { int *index, i; //initialize item weight index index = (int *) GetClearedMemory(sizeof(int) * ic->numiteminfo); for (i = 0; i < ic->numiteminfo; i++) { index[i] = FindFuzzyWeight(iwc, ic->iteminfo[i].classname); if (index[i] < 0) { Log_Write("item info %d \"%s\" has no fuzzy weight\r\n", i, ic->iteminfo[i].classname); } //end if } //end for return index; } //end of the function ItemWeightIndex //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void InitLevelItemHeap(void) { int i, max_levelitems; if (levelitemheap) FreeMemory(levelitemheap); max_levelitems = (int) LibVarValue("max_levelitems", "256"); levelitemheap = (levelitem_t *) GetClearedMemory(max_levelitems * sizeof(levelitem_t)); for (i = 0; i < max_levelitems-1; i++) { levelitemheap[i].next = &levelitemheap[i + 1]; } //end for levelitemheap[max_levelitems-1].next = NULL; // freelevelitems = levelitemheap; } //end of the function InitLevelItemHeap //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== levelitem_t *AllocLevelItem(void) { levelitem_t *li; li = freelevelitems; if (!li) { botimport.Print(PRT_FATAL, "out of level items\n"); return NULL; } //end if // freelevelitems = freelevelitems->next; Com_Memset(li, 0, sizeof(levelitem_t)); return li; } //end of the function AllocLevelItem //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void FreeLevelItem(levelitem_t *li) { li->next = freelevelitems; freelevelitems = li; } //end of the function FreeLevelItem //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AddLevelItemToList(levelitem_t *li) { if (levelitems) levelitems->prev = li; li->prev = NULL; li->next = levelitems; levelitems = li; } //end of the function AddLevelItemToList //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void RemoveLevelItemFromList(levelitem_t *li) { if (li->prev) li->prev->next = li->next; else levelitems = li->next; if (li->next) li->next->prev = li->prev; } //end of the function RemoveLevelItemFromList //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotFreeInfoEntities(void) { maplocation_t *ml, *nextml; campspot_t *cs, *nextcs; for (ml = maplocations; ml; ml = nextml) { nextml = ml->next; FreeMemory(ml); } //end for maplocations = NULL; for (cs = campspots; cs; cs = nextcs) { nextcs = cs->next; FreeMemory(cs); } //end for campspots = NULL; } //end of the function BotFreeInfoEntities //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotInitInfoEntities(void) { char classname[MAX_EPAIRKEY]; maplocation_t *ml; campspot_t *cs; int ent, numlocations, numcampspots; BotFreeInfoEntities(); // numlocations = 0; numcampspots = 0; for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) { if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; //map locations if (!strcmp(classname, "target_location")) { ml = (maplocation_t *) GetClearedMemory(sizeof(maplocation_t)); AAS_VectorForBSPEpairKey(ent, "origin", ml->origin); AAS_ValueForBSPEpairKey(ent, "message", ml->name, sizeof(ml->name)); ml->areanum = AAS_PointAreaNum(ml->origin); ml->next = maplocations; maplocations = ml; numlocations++; } //end if //camp spots else if (!strcmp(classname, "info_camp")) { cs = (campspot_t *) GetClearedMemory(sizeof(campspot_t)); AAS_VectorForBSPEpairKey(ent, "origin", cs->origin); //cs->origin[2] += 16; AAS_ValueForBSPEpairKey(ent, "message", cs->name, sizeof(cs->name)); AAS_FloatForBSPEpairKey(ent, "range", &cs->range); AAS_FloatForBSPEpairKey(ent, "weight", &cs->weight); AAS_FloatForBSPEpairKey(ent, "wait", &cs->wait); AAS_FloatForBSPEpairKey(ent, "random", &cs->random); cs->areanum = AAS_PointAreaNum(cs->origin); if (!cs->areanum) { botimport.Print(PRT_MESSAGE, "camp spot at %1.1f %1.1f %1.1f in solid\n", cs->origin[0], cs->origin[1], cs->origin[2]); FreeMemory(cs); continue; } //end if cs->next = campspots; campspots = cs; //AAS_DrawPermanentCross(cs->origin, 4, LINECOLOR_YELLOW); numcampspots++; } //end else if } //end for if (bot_developer) { botimport.Print(PRT_MESSAGE, "%d map locations\n", numlocations); botimport.Print(PRT_MESSAGE, "%d camp spots\n", numcampspots); } //end if } //end of the function BotInitInfoEntities //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotInitLevelItems(void) { int i, spawnflags, value; char classname[MAX_EPAIRKEY]; vec3_t origin, end; int ent, goalareanum; itemconfig_t *ic; levelitem_t *li; bsp_trace_t trace; //initialize the map locations and camp spots BotInitInfoEntities(); //initialize the level item heap InitLevelItemHeap(); levelitems = NULL; numlevelitems = 0; // ic = itemconfig; if (!ic) return; //if there's no AAS file loaded if (!AAS_Loaded()) return; //update the modelindexes of the item info for (i = 0; i < ic->numiteminfo; i++) { //ic->iteminfo[i].modelindex = AAS_IndexFromModel(ic->iteminfo[i].model); if (!ic->iteminfo[i].modelindex) { Log_Write("item %s has modelindex 0", ic->iteminfo[i].classname); } //end if } //end for for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) { if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; // spawnflags = 0; AAS_IntForBSPEpairKey(ent, "spawnflags", &spawnflags); // for (i = 0; i < ic->numiteminfo; i++) { if (!strcmp(classname, ic->iteminfo[i].classname)) break; } //end for if (i >= ic->numiteminfo) { Log_Write("entity %s unknown item\r\n", classname); continue; } //end if //get the origin of the item if (!AAS_VectorForBSPEpairKey(ent, "origin", origin)) { botimport.Print(PRT_ERROR, "item %s without origin\n", classname); continue; } //end else // goalareanum = 0; //if it is a floating item if (spawnflags & 1) { //if the item is not floating in water if (!(AAS_PointContents(origin) & CONTENTS_WATER)) { VectorCopy(origin, end); end[2] -= 32; trace = AAS_Trace(origin, ic->iteminfo[i].mins, ic->iteminfo[i].maxs, end, -1, CONTENTS_SOLID|CONTENTS_PLAYERCLIP); //if the item not near the ground if (trace.fraction >= 1) { //if the item is not reachable from a jumppad goalareanum = AAS_BestReachableFromJumpPadArea(origin, ic->iteminfo[i].mins, ic->iteminfo[i].maxs); Log_Write("item %s reachable from jumppad area %d\r\n", ic->iteminfo[i].classname, goalareanum); //botimport.Print(PRT_MESSAGE, "item %s reachable from jumppad area %d\r\n", ic->iteminfo[i].classname, goalareanum); if (!goalareanum) continue; } //end if } //end if } //end if li = AllocLevelItem(); if (!li) return; // li->number = ++numlevelitems; li->timeout = 0; li->entitynum = 0; // li->flags = 0; AAS_IntForBSPEpairKey(ent, "notfree", &value); if (value) li->flags |= IFL_NOTFREE; AAS_IntForBSPEpairKey(ent, "notteam", &value); if (value) li->flags |= IFL_NOTTEAM; AAS_IntForBSPEpairKey(ent, "notsingle", &value); if (value) li->flags |= IFL_NOTSINGLE; AAS_IntForBSPEpairKey(ent, "notbot", &value); if (value) li->flags |= IFL_NOTBOT; if (!strcmp(classname, "item_botroam")) { li->flags |= IFL_ROAM; AAS_FloatForBSPEpairKey(ent, "weight", &li->weight); } //end if //if not a stationary item if (!(spawnflags & 1)) { if (!AAS_DropToFloor(origin, ic->iteminfo[i].mins, ic->iteminfo[i].maxs)) { botimport.Print(PRT_MESSAGE, "%s in solid at (%1.1f %1.1f %1.1f)\n", classname, origin[0], origin[1], origin[2]); } //end if } //end if //item info of the level item li->iteminfo = i; //origin of the item VectorCopy(origin, li->origin); // if (goalareanum) { li->goalareanum = goalareanum; VectorCopy(origin, li->goalorigin); } //end if else { //get the item goal area and goal origin li->goalareanum = AAS_BestReachableArea(origin, ic->iteminfo[i].mins, ic->iteminfo[i].maxs, li->goalorigin); if (!li->goalareanum) { botimport.Print(PRT_MESSAGE, "%s not reachable for bots at (%1.1f %1.1f %1.1f)\n", classname, origin[0], origin[1], origin[2]); } //end if } //end else // AddLevelItemToList(li); } //end for botimport.Print(PRT_MESSAGE, "found %d level items\n", numlevelitems); } //end of the function BotInitLevelItems //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotGoalName(int number, char *name, int size) { levelitem_t *li; if (!itemconfig) return; // for (li = levelitems; li; li = li->next) { if (li->number == number) { strncpy(name, itemconfig->iteminfo[li->iteminfo].name, size-1); name[size-1] = '\0'; return; } //end for } //end for strcpy(name, ""); return; } //end of the function BotGoalName //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotResetAvoidGoals(int goalstate) { bot_goalstate_t *gs; gs = BotGoalStateFromHandle(goalstate); if (!gs) return; Com_Memset(gs->avoidgoals, 0, MAX_AVOIDGOALS * sizeof(int)); Com_Memset(gs->avoidgoaltimes, 0, MAX_AVOIDGOALS * sizeof(float)); } //end of the function BotResetAvoidGoals //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotDumpAvoidGoals(int goalstate) { int i; bot_goalstate_t *gs; char name[32]; gs = BotGoalStateFromHandle(goalstate); if (!gs) return; for (i = 0; i < MAX_AVOIDGOALS; i++) { if (gs->avoidgoaltimes[i] >= AAS_Time()) { BotGoalName(gs->avoidgoals[i], name, 32); Log_Write("avoid goal %s, number %d for %f seconds", name, gs->avoidgoals[i], gs->avoidgoaltimes[i] - AAS_Time()); } //end if } //end for } //end of the function BotDumpAvoidGoals //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotAddToAvoidGoals(bot_goalstate_t *gs, int number, float avoidtime) { int i; for (i = 0; i < MAX_AVOIDGOALS; i++) { //if the avoid goal is already stored if (gs->avoidgoals[i] == number) { gs->avoidgoals[i] = number; gs->avoidgoaltimes[i] = AAS_Time() + avoidtime; return; } //end if } //end for for (i = 0; i < MAX_AVOIDGOALS; i++) { //if this avoid goal has expired if (gs->avoidgoaltimes[i] < AAS_Time()) { gs->avoidgoals[i] = number; gs->avoidgoaltimes[i] = AAS_Time() + avoidtime; return; } //end if } //end for } //end of the function BotAddToAvoidGoals //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotRemoveFromAvoidGoals(int goalstate, int number) { int i; bot_goalstate_t *gs; gs = BotGoalStateFromHandle(goalstate); if (!gs) return; //don't use the goals the bot wants to avoid for (i = 0; i < MAX_AVOIDGOALS; i++) { if (gs->avoidgoals[i] == number && gs->avoidgoaltimes[i] >= AAS_Time()) { gs->avoidgoaltimes[i] = 0; return; } //end if } //end for } //end of the function BotRemoveFromAvoidGoals //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== float BotAvoidGoalTime(int goalstate, int number) { int i; bot_goalstate_t *gs; gs = BotGoalStateFromHandle(goalstate); if (!gs) return 0; //don't use the goals the bot wants to avoid for (i = 0; i < MAX_AVOIDGOALS; i++) { if (gs->avoidgoals[i] == number && gs->avoidgoaltimes[i] >= AAS_Time()) { return gs->avoidgoaltimes[i] - AAS_Time(); } //end if } //end for return 0; } //end of the function BotAvoidGoalTime //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotSetAvoidGoalTime(int goalstate, int number, float avoidtime) { bot_goalstate_t *gs; levelitem_t *li; gs = BotGoalStateFromHandle(goalstate); if (!gs) return; if (avoidtime < 0) { if (!itemconfig) return; // for (li = levelitems; li; li = li->next) { if (li->number == number) { avoidtime = itemconfig->iteminfo[li->iteminfo].respawntime; if (!avoidtime) avoidtime = AVOID_DEFAULT_TIME; if (avoidtime < AVOID_MINIMUM_TIME) avoidtime = AVOID_MINIMUM_TIME; BotAddToAvoidGoals(gs, number, avoidtime); return; } //end for } //end for return; } //end if else { BotAddToAvoidGoals(gs, number, avoidtime); } //end else } //end of the function BotSetAvoidGoalTime //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotGetLevelItemGoal(int index, char *name, bot_goal_t *goal) { levelitem_t *li; if (!itemconfig) return -1; li = levelitems; if (index >= 0) { for (; li; li = li->next) { if (li->number == index) { li = li->next; break; } //end if } //end for } //end for for (; li; li = li->next) { // if (g_gametype == GT_SINGLE_PLAYER) { if (li->flags & IFL_NOTSINGLE) continue; } else if (g_gametype >= GT_TEAM) { if (li->flags & IFL_NOTTEAM) continue; } else { if (li->flags & IFL_NOTFREE) continue; } if (li->flags & IFL_NOTBOT) continue; // if (!Q_stricmp(name, itemconfig->iteminfo[li->iteminfo].name)) { goal->areanum = li->goalareanum; VectorCopy(li->goalorigin, goal->origin); goal->entitynum = li->entitynum; VectorCopy(itemconfig->iteminfo[li->iteminfo].mins, goal->mins); VectorCopy(itemconfig->iteminfo[li->iteminfo].maxs, goal->maxs); goal->number = li->number; goal->flags = GFL_ITEM; if (li->timeout) goal->flags |= GFL_DROPPED; //botimport.Print(PRT_MESSAGE, "found li %s\n", itemconfig->iteminfo[li->iteminfo].name); return li->number; } //end if } //end for return -1; } //end of the function BotGetLevelItemGoal //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotGetMapLocationGoal(char *name, bot_goal_t *goal) { maplocation_t *ml; vec3_t mins = {-8, -8, -8}, maxs = {8, 8, 8}; for (ml = maplocations; ml; ml = ml->next) { if (!Q_stricmp(ml->name, name)) { goal->areanum = ml->areanum; VectorCopy(ml->origin, goal->origin); goal->entitynum = 0; VectorCopy(mins, goal->mins); VectorCopy(maxs, goal->maxs); return qtrue; } //end if } //end for return qfalse; } //end of the function BotGetMapLocationGoal //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotGetNextCampSpotGoal(int num, bot_goal_t *goal) { int i; campspot_t *cs; vec3_t mins = {-8, -8, -8}, maxs = {8, 8, 8}; if (num < 0) num = 0; i = num; for (cs = campspots; cs; cs = cs->next) { if (--i < 0) { goal->areanum = cs->areanum; VectorCopy(cs->origin, goal->origin); goal->entitynum = 0; VectorCopy(mins, goal->mins); VectorCopy(maxs, goal->maxs); return num+1; } //end if } //end for return 0; } //end of the function BotGetNextCampSpotGoal //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotFindEntityForLevelItem(levelitem_t *li) { int ent, modelindex; itemconfig_t *ic; aas_entityinfo_t entinfo; vec3_t dir; ic = itemconfig; if (!itemconfig) return; for (ent = AAS_NextEntity(0); ent; ent = AAS_NextEntity(ent)) { //get the model index of the entity modelindex = AAS_EntityModelindex(ent); // if (!modelindex) continue; //get info about the entity AAS_EntityInfo(ent, &entinfo); //if the entity is still moving if (entinfo.origin[0] != entinfo.lastvisorigin[0] || entinfo.origin[1] != entinfo.lastvisorigin[1] || entinfo.origin[2] != entinfo.lastvisorigin[2]) continue; // if (ic->iteminfo[li->iteminfo].modelindex == modelindex) { //check if the entity is very close VectorSubtract(li->origin, entinfo.origin, dir); if (VectorLength(dir) < 30) { //found an entity for this level item li->entitynum = ent; } //end if } //end if } //end for } //end of the function BotFindEntityForLevelItem //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== //NOTE: enum entityType_t in bg_public.h #define ET_ITEM 2 void BotUpdateEntityItems(void) { int ent, i, modelindex; vec3_t dir; levelitem_t *li, *nextli; aas_entityinfo_t entinfo; itemconfig_t *ic; //timeout current entity items if necessary for (li = levelitems; li; li = nextli) { nextli = li->next; //if it is a item that will time out if (li->timeout) { //timeout the item if (li->timeout < AAS_Time()) { RemoveLevelItemFromList(li); FreeLevelItem(li); } //end if } //end if } //end for //find new entity items ic = itemconfig; if (!itemconfig) return; // for (ent = AAS_NextEntity(0); ent; ent = AAS_NextEntity(ent)) { if (AAS_EntityType(ent) != ET_ITEM) continue; //get the model index of the entity modelindex = AAS_EntityModelindex(ent); // if (!modelindex) continue; //get info about the entity AAS_EntityInfo(ent, &entinfo); //FIXME: don't do this //skip all floating items for now //if (entinfo.groundent != ENTITYNUM_WORLD) continue; //if the entity is still moving if (entinfo.origin[0] != entinfo.lastvisorigin[0] || entinfo.origin[1] != entinfo.lastvisorigin[1] || entinfo.origin[2] != entinfo.lastvisorigin[2]) continue; //check if the entity is already stored as a level item for (li = levelitems; li; li = li->next) { //if the level item is linked to an entity if (li->entitynum && li->entitynum == ent) { //the entity is re-used if the models are different if (ic->iteminfo[li->iteminfo].modelindex != modelindex) { //remove this level item RemoveLevelItemFromList(li); FreeLevelItem(li); li = NULL; break; } //end if else { if (entinfo.origin[0] != li->origin[0] || entinfo.origin[1] != li->origin[1] || entinfo.origin[2] != li->origin[2]) { VectorCopy(entinfo.origin, li->origin); //also update the goal area number li->goalareanum = AAS_BestReachableArea(li->origin, ic->iteminfo[li->iteminfo].mins, ic->iteminfo[li->iteminfo].maxs, li->goalorigin); } //end if break; } //end else } //end if } //end for if (li) continue; //try to link the entity to a level item for (li = levelitems; li; li = li->next) { //if this level item is already linked if (li->entitynum) continue; // if (g_gametype == GT_SINGLE_PLAYER) { if (li->flags & IFL_NOTSINGLE) continue; } else if (g_gametype >= GT_TEAM) { if (li->flags & IFL_NOTTEAM) continue; } else { if (li->flags & IFL_NOTFREE) continue; } //if the model of the level item and the entity are the same if (ic->iteminfo[li->iteminfo].modelindex == modelindex) { //check if the entity is very close VectorSubtract(li->origin, entinfo.origin, dir); if (VectorLength(dir) < 30) { //found an entity for this level item li->entitynum = ent; //if the origin is different if (entinfo.origin[0] != li->origin[0] || entinfo.origin[1] != li->origin[1] || entinfo.origin[2] != li->origin[2]) { //update the level item origin VectorCopy(entinfo.origin, li->origin); //also update the goal area number li->goalareanum = AAS_BestReachableArea(li->origin, ic->iteminfo[li->iteminfo].mins, ic->iteminfo[li->iteminfo].maxs, li->goalorigin); } //end if #ifdef DEBUG Log_Write("linked item %s to an entity", ic->iteminfo[li->iteminfo].classname); #endif //DEBUG break; } //end if } //end else } //end for if (li) continue; //check if the model is from a known item for (i = 0; i < ic->numiteminfo; i++) { if (ic->iteminfo[i].modelindex == modelindex) { break; } //end if } //end for //if the model is not from a known item if (i >= ic->numiteminfo) continue; //allocate a new level item li = AllocLevelItem(); // if (!li) continue; //entity number of the level item li->entitynum = ent; //number for the level item li->number = numlevelitems + ent; //set the item info index for the level item li->iteminfo = i; //origin of the item VectorCopy(entinfo.origin, li->origin); //get the item goal area and goal origin li->goalareanum = AAS_BestReachableArea(li->origin, ic->iteminfo[i].mins, ic->iteminfo[i].maxs, li->goalorigin); //never go for items dropped into jumppads if (AAS_AreaJumpPad(li->goalareanum)) { FreeLevelItem(li); continue; } //end if //time this item out after 30 seconds //dropped items disappear after 30 seconds li->timeout = AAS_Time() + 30; //add the level item to the list AddLevelItemToList(li); //botimport.Print(PRT_MESSAGE, "found new level item %s\n", ic->iteminfo[i].classname); } //end for /* for (li = levelitems; li; li = li->next) { if (!li->entitynum) { BotFindEntityForLevelItem(li); } //end if } //end for*/ } //end of the function BotUpdateEntityItems //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotDumpGoalStack(int goalstate) { int i; bot_goalstate_t *gs; char name[32]; gs = BotGoalStateFromHandle(goalstate); if (!gs) return; for (i = 1; i <= gs->goalstacktop; i++) { BotGoalName(gs->goalstack[i].number, name, 32); Log_Write("%d: %s", i, name); } //end for } //end of the function BotDumpGoalStack //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotPushGoal(int goalstate, bot_goal_t *goal) { bot_goalstate_t *gs; gs = BotGoalStateFromHandle(goalstate); if (!gs) return; if (gs->goalstacktop >= MAX_GOALSTACK-1) { botimport.Print(PRT_ERROR, "goal heap overflow\n"); BotDumpGoalStack(goalstate); return; } //end if gs->goalstacktop++; Com_Memcpy(&gs->goalstack[gs->goalstacktop], goal, sizeof(bot_goal_t)); } //end of the function BotPushGoal //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotPopGoal(int goalstate) { bot_goalstate_t *gs; gs = BotGoalStateFromHandle(goalstate); if (!gs) return; if (gs->goalstacktop > 0) gs->goalstacktop--; } //end of the function BotPopGoal //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotEmptyGoalStack(int goalstate) { bot_goalstate_t *gs; gs = BotGoalStateFromHandle(goalstate); if (!gs) return; gs->goalstacktop = 0; } //end of the function BotEmptyGoalStack //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotGetTopGoal(int goalstate, bot_goal_t *goal) { bot_goalstate_t *gs; gs = BotGoalStateFromHandle(goalstate); if (!gs) return qfalse; if (!gs->goalstacktop) return qfalse; Com_Memcpy(goal, &gs->goalstack[gs->goalstacktop], sizeof(bot_goal_t)); return qtrue; } //end of the function BotGetTopGoal //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotGetSecondGoal(int goalstate, bot_goal_t *goal) { bot_goalstate_t *gs; gs = BotGoalStateFromHandle(goalstate); if (!gs) return qfalse; if (gs->goalstacktop <= 1) return qfalse; Com_Memcpy(goal, &gs->goalstack[gs->goalstacktop-1], sizeof(bot_goal_t)); return qtrue; } //end of the function BotGetSecondGoal //=========================================================================== // pops a new long term goal on the goal stack in the goalstate // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotChooseLTGItem(int goalstate, vec3_t origin, int *inventory, int travelflags) { int areanum, t, weightnum; float weight, bestweight, avoidtime; iteminfo_t *iteminfo; itemconfig_t *ic; levelitem_t *li, *bestitem; bot_goal_t goal; bot_goalstate_t *gs; gs = BotGoalStateFromHandle(goalstate); if (!gs) return qfalse; if (!gs->itemweightconfig) return qfalse; //get the area the bot is in areanum = BotReachabilityArea(origin, gs->client); //if the bot is in solid or if the area the bot is in has no reachability links if (!areanum || !AAS_AreaReachability(areanum)) { //use the last valid area the bot was in areanum = gs->lastreachabilityarea; } //end if //remember the last area with reachabilities the bot was in gs->lastreachabilityarea = areanum; //if still in solid if (!areanum) return qfalse; //the item configuration ic = itemconfig; if (!itemconfig) return qfalse; //best weight and item so far bestweight = 0; bestitem = NULL; Com_Memset(&goal, 0, sizeof(bot_goal_t)); //go through the items in the level for (li = levelitems; li; li = li->next) { if (g_gametype == GT_SINGLE_PLAYER) { if (li->flags & IFL_NOTSINGLE) continue; } else if (g_gametype >= GT_TEAM) { if (li->flags & IFL_NOTTEAM) continue; } else { if (li->flags & IFL_NOTFREE) continue; } if (li->flags & IFL_NOTBOT) continue; //if the item is not in a possible goal area if (!li->goalareanum) continue; //FIXME: is this a good thing? added this for items that never spawned into the game (f.i. CTF flags in obelisk) if (!li->entitynum && !(li->flags & IFL_ROAM)) continue; //get the fuzzy weight function for this item iteminfo = &ic->iteminfo[li->iteminfo]; weightnum = gs->itemweightindex[iteminfo->number]; if (weightnum < 0) continue; #ifdef UNDECIDEDFUZZY weight = FuzzyWeightUndecided(inventory, gs->itemweightconfig, weightnum); #else weight = FuzzyWeight(inventory, gs->itemweightconfig, weightnum); #endif //UNDECIDEDFUZZY #ifdef DROPPEDWEIGHT //HACK: to make dropped items more attractive if (li->timeout) weight += droppedweight->value; #endif //DROPPEDWEIGHT //use weight scale for item_botroam if (li->flags & IFL_ROAM) weight *= li->weight; // if (weight > 0) { //get the travel time towards the goal area t = AAS_AreaTravelTimeToGoalArea(areanum, origin, li->goalareanum, travelflags); //if the goal is reachable if (t > 0) { //if this item won't respawn before we get there avoidtime = BotAvoidGoalTime(goalstate, li->number); if (avoidtime - t * 0.009 > 0) continue; // weight /= (float) t * TRAVELTIME_SCALE; // if (weight > bestweight) { bestweight = weight; bestitem = li; } //end if } //end if } //end if } //end for //if no goal item found if (!bestitem) { /* //if not in lava or slime if (!AAS_AreaLava(areanum) && !AAS_AreaSlime(areanum)) { if (AAS_RandomGoalArea(areanum, travelflags, &goal.areanum, goal.origin)) { VectorSet(goal.mins, -15, -15, -15); VectorSet(goal.maxs, 15, 15, 15); goal.entitynum = 0; goal.number = 0; goal.flags = GFL_ROAM; goal.iteminfo = 0; //push the goal on the stack BotPushGoal(goalstate, &goal); // #ifdef DEBUG botimport.Print(PRT_MESSAGE, "chosen roam goal area %d\n", goal.areanum); #endif //DEBUG return qtrue; } //end if } //end if */ return qfalse; } //end if //create a bot goal for this item iteminfo = &ic->iteminfo[bestitem->iteminfo]; VectorCopy(bestitem->goalorigin, goal.origin); VectorCopy(iteminfo->mins, goal.mins); VectorCopy(iteminfo->maxs, goal.maxs); goal.areanum = bestitem->goalareanum; goal.entitynum = bestitem->entitynum; goal.number = bestitem->number; goal.flags = GFL_ITEM; if (bestitem->timeout) goal.flags |= GFL_DROPPED; if (bestitem->flags & IFL_ROAM) goal.flags |= GFL_ROAM; goal.iteminfo = bestitem->iteminfo; //if it's a dropped item if (bestitem->timeout) { avoidtime = AVOID_DROPPED_TIME; } //end if else { avoidtime = iteminfo->respawntime; if (!avoidtime) avoidtime = AVOID_DEFAULT_TIME; if (avoidtime < AVOID_MINIMUM_TIME) avoidtime = AVOID_MINIMUM_TIME; } //end else //add the chosen goal to the goals to avoid for a while BotAddToAvoidGoals(gs, bestitem->number, avoidtime); //push the goal on the stack BotPushGoal(goalstate, &goal); // return qtrue; } //end of the function BotChooseLTGItem //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotChooseNBGItem(int goalstate, vec3_t origin, int *inventory, int travelflags, bot_goal_t *ltg, float maxtime) { int areanum, t, weightnum, ltg_time; float weight, bestweight, avoidtime; iteminfo_t *iteminfo; itemconfig_t *ic; levelitem_t *li, *bestitem; bot_goal_t goal; bot_goalstate_t *gs; gs = BotGoalStateFromHandle(goalstate); if (!gs) return qfalse; if (!gs->itemweightconfig) return qfalse; //get the area the bot is in areanum = BotReachabilityArea(origin, gs->client); //if the bot is in solid or if the area the bot is in has no reachability links if (!areanum || !AAS_AreaReachability(areanum)) { //use the last valid area the bot was in areanum = gs->lastreachabilityarea; } //end if //remember the last area with reachabilities the bot was in gs->lastreachabilityarea = areanum; //if still in solid if (!areanum) return qfalse; // if (ltg) ltg_time = AAS_AreaTravelTimeToGoalArea(areanum, origin, ltg->areanum, travelflags); else ltg_time = 99999; //the item configuration ic = itemconfig; if (!itemconfig) return qfalse; //best weight and item so far bestweight = 0; bestitem = NULL; Com_Memset(&goal, 0, sizeof(bot_goal_t)); //go through the items in the level for (li = levelitems; li; li = li->next) { if (g_gametype == GT_SINGLE_PLAYER) { if (li->flags & IFL_NOTSINGLE) continue; } else if (g_gametype >= GT_TEAM) { if (li->flags & IFL_NOTTEAM) continue; } else { if (li->flags & IFL_NOTFREE) continue; } if (li->flags & IFL_NOTBOT) continue; //if the item is in a possible goal area if (!li->goalareanum) continue; //FIXME: is this a good thing? added this for items that never spawned into the game (f.i. CTF flags in obelisk) if (!li->entitynum && !(li->flags & IFL_ROAM)) continue; //get the fuzzy weight function for this item iteminfo = &ic->iteminfo[li->iteminfo]; weightnum = gs->itemweightindex[iteminfo->number]; if (weightnum < 0) continue; // #ifdef UNDECIDEDFUZZY weight = FuzzyWeightUndecided(inventory, gs->itemweightconfig, weightnum); #else weight = FuzzyWeight(inventory, gs->itemweightconfig, weightnum); #endif //UNDECIDEDFUZZY #ifdef DROPPEDWEIGHT //HACK: to make dropped items more attractive if (li->timeout) weight += droppedweight->value; #endif //DROPPEDWEIGHT //use weight scale for item_botroam if (li->flags & IFL_ROAM) weight *= li->weight; // if (weight > 0) { //get the travel time towards the goal area t = AAS_AreaTravelTimeToGoalArea(areanum, origin, li->goalareanum, travelflags); //if the goal is reachable if (t > 0 && t < maxtime) { //if this item won't respawn before we get there avoidtime = BotAvoidGoalTime(goalstate, li->number); if (avoidtime - t * 0.009 > 0) continue; // weight /= (float) t * TRAVELTIME_SCALE; // if (weight > bestweight) { t = 0; if (ltg && !li->timeout) { //get the travel time from the goal to the long term goal t = AAS_AreaTravelTimeToGoalArea(li->goalareanum, li->goalorigin, ltg->areanum, travelflags); } //end if //if the travel back is possible and doesn't take too long if (t <= ltg_time) { bestweight = weight; bestitem = li; } //end if } //end if } //end if } //end if } //end for //if no goal item found if (!bestitem) return qfalse; //create a bot goal for this item iteminfo = &ic->iteminfo[bestitem->iteminfo]; VectorCopy(bestitem->goalorigin, goal.origin); VectorCopy(iteminfo->mins, goal.mins); VectorCopy(iteminfo->maxs, goal.maxs); goal.areanum = bestitem->goalareanum; goal.entitynum = bestitem->entitynum; goal.number = bestitem->number; goal.flags = GFL_ITEM; if (bestitem->timeout) goal.flags |= GFL_DROPPED; if (bestitem->flags & IFL_ROAM) goal.flags |= GFL_ROAM; goal.iteminfo = bestitem->iteminfo; //if it's a dropped item if (bestitem->timeout) { avoidtime = AVOID_DROPPED_TIME; } //end if else { avoidtime = iteminfo->respawntime; if (!avoidtime) avoidtime = AVOID_DEFAULT_TIME; if (avoidtime < AVOID_MINIMUM_TIME) avoidtime = AVOID_MINIMUM_TIME; } //end else //add the chosen goal to the goals to avoid for a while BotAddToAvoidGoals(gs, bestitem->number, avoidtime); //push the goal on the stack BotPushGoal(goalstate, &goal); // return qtrue; } //end of the function BotChooseNBGItem //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotTouchingGoal(vec3_t origin, bot_goal_t *goal) { int i; vec3_t boxmins, boxmaxs; vec3_t absmins, absmaxs; vec3_t safety_maxs = {0, 0, 0}; //{4, 4, 10}; vec3_t safety_mins = {0, 0, 0}; //{-4, -4, 0}; AAS_PresenceTypeBoundingBox(PRESENCE_NORMAL, boxmins, boxmaxs); VectorSubtract(goal->mins, boxmaxs, absmins); VectorSubtract(goal->maxs, boxmins, absmaxs); VectorAdd(absmins, goal->origin, absmins); VectorAdd(absmaxs, goal->origin, absmaxs); //make the box a little smaller for safety VectorSubtract(absmaxs, safety_maxs, absmaxs); VectorSubtract(absmins, safety_mins, absmins); for (i = 0; i < 3; i++) { if (origin[i] < absmins[i] || origin[i] > absmaxs[i]) return qfalse; } //end for return qtrue; } //end of the function BotTouchingGoal //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotItemGoalInVisButNotVisible(int viewer, vec3_t eye, vec3_t viewangles, bot_goal_t *goal) { aas_entityinfo_t entinfo; bsp_trace_t trace; vec3_t middle; if (!(goal->flags & GFL_ITEM)) return qfalse; // VectorAdd(goal->mins, goal->mins, middle); VectorScale(middle, 0.5, middle); VectorAdd(goal->origin, middle, middle); // trace = AAS_Trace(eye, NULL, NULL, middle, viewer, CONTENTS_SOLID); //if the goal middle point is visible if (trace.fraction >= 1) { //the goal entity number doesn't have to be valid //just assume it's valid if (goal->entitynum <= 0) return qfalse; // //if the entity data isn't valid AAS_EntityInfo(goal->entitynum, &entinfo); //NOTE: for some wacko reason entities are sometimes // not updated //if (!entinfo.valid) return qtrue; if (entinfo.ltime < AAS_Time() - 0.5) return qtrue; } //end if return qfalse; } //end of the function BotItemGoalInVisButNotVisible //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotResetGoalState(int goalstate) { bot_goalstate_t *gs; gs = BotGoalStateFromHandle(goalstate); if (!gs) return; Com_Memset(gs->goalstack, 0, MAX_GOALSTACK * sizeof(bot_goal_t)); gs->goalstacktop = 0; BotResetAvoidGoals(goalstate); } //end of the function BotResetGoalState //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotLoadItemWeights(int goalstate, char *filename) { bot_goalstate_t *gs; gs = BotGoalStateFromHandle(goalstate); if (!gs) return BLERR_CANNOTLOADITEMWEIGHTS; //load the weight configuration gs->itemweightconfig = ReadWeightConfig(filename); if (!gs->itemweightconfig) { botimport.Print(PRT_FATAL, "couldn't load weights\n"); return BLERR_CANNOTLOADITEMWEIGHTS; } //end if //if there's no item configuration if (!itemconfig) return BLERR_CANNOTLOADITEMWEIGHTS; //create the item weight index gs->itemweightindex = ItemWeightIndex(gs->itemweightconfig, itemconfig); //everything went ok return BLERR_NOERROR; } //end of the function BotLoadItemWeights //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotFreeItemWeights(int goalstate) { bot_goalstate_t *gs; gs = BotGoalStateFromHandle(goalstate); if (!gs) return; if (gs->itemweightconfig) FreeWeightConfig(gs->itemweightconfig); if (gs->itemweightindex) FreeMemory(gs->itemweightindex); } //end of the function BotFreeItemWeights //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotAllocGoalState(int client) { int i; for (i = 1; i <= MAX_CLIENTS; i++) { if (!botgoalstates[i]) { botgoalstates[i] = (bot_goalstate_t*) GetClearedMemory(sizeof(bot_goalstate_t)); botgoalstates[i]->client = client; return i; } //end if } //end for return 0; } //end of the function BotAllocGoalState //======================================================================== // // Parameter: - // Returns: - // Changes Globals: - //======================================================================== void BotFreeGoalState(int handle) { if (handle <= 0 || handle > MAX_CLIENTS) { botimport.Print(PRT_FATAL, "goal state handle %d out of range\n", handle); return; } //end if if (!botgoalstates[handle]) { botimport.Print(PRT_FATAL, "invalid goal state handle %d\n", handle); return; } //end if BotFreeItemWeights(handle); FreeMemory(botgoalstates[handle]); botgoalstates[handle] = NULL; } //end of the function BotFreeGoalState //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotSetupGoalAI(void) { char *filename; //check if teamplay is on g_gametype = LibVarValue("g_gametype", "0"); //item configuration file filename = LibVarString("itemconfig", "items.c"); //load the item configuration itemconfig = LoadItemConfig(filename); if (!itemconfig) { botimport.Print(PRT_FATAL, "couldn't load item config\n"); return BLERR_CANNOTLOADITEMCONFIG; } //end if // droppedweight = LibVar("droppedweight", "1000"); //everything went ok return BLERR_NOERROR; } //end of the function BotSetupGoalAI //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotShutdownGoalAI(void) { int i; if (itemconfig) FreeMemory(itemconfig); itemconfig = NULL; if (levelitemheap) FreeMemory(levelitemheap); levelitemheap = NULL; freelevelitems = NULL; levelitems = NULL; numlevelitems = 0; BotFreeInfoEntities(); for (i = 1; i <= MAX_CLIENTS; i++) { if (botgoalstates[i]) { BotFreeGoalState(i); } //end if } //end for } //end of the function BotShutdownGoalAI ================================================ FILE: src/engine/botlib/be_ai_move.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_ai_move.c * * desc: bot movement AI * * $Archive: /MissionPack/code/botlib/be_ai_move.c $ * *****************************************************************************/ #include "../../game/q_shared.h" #include "l_memory.h" #include "l_libvar.h" #include "l_utils.h" #include "l_script.h" #include "l_precomp.h" #include "l_struct.h" #include "aasfile.h" #include "../../game/botlib.h" #include "../../game/be_aas.h" #include "be_aas_funcs.h" #include "be_interface.h" #include "../../game/be_ea.h" #include "../../game/be_ai_goal.h" #include "../../game/be_ai_move.h" //#define DEBUG_AI_MOVE //#define DEBUG_ELEVATOR //#define DEBUG_GRAPPLE // bk001204 - redundant bot_avoidspot_t, see ../game/be_ai_move.h //movement state //NOTE: the moveflags MFL_ONGROUND, MFL_TELEPORTED, MFL_WATERJUMP and // MFL_GRAPPLEPULL must be set outside the movement code typedef struct bot_movestate_s { //input vars (all set outside the movement code) vec3_t origin; //origin of the bot vec3_t velocity; //velocity of the bot vec3_t viewoffset; //view offset int entitynum; //entity number of the bot int client; //client number of the bot float thinktime; //time the bot thinks int presencetype; //presencetype of the bot vec3_t viewangles; //view angles of the bot //state vars int areanum; //area the bot is in int lastareanum; //last area the bot was in int lastgoalareanum; //last goal area number int lastreachnum; //last reachability number vec3_t lastorigin; //origin previous cycle int reachareanum; //area number of the reachabilty int moveflags; //movement flags int jumpreach; //set when jumped float grapplevisible_time; //last time the grapple was visible float lastgrappledist; //last distance to the grapple end float reachability_time; //time to use current reachability int avoidreach[MAX_AVOIDREACH]; //reachabilities to avoid float avoidreachtimes[MAX_AVOIDREACH]; //times to avoid the reachabilities int avoidreachtries[MAX_AVOIDREACH]; //number of tries before avoiding // bot_avoidspot_t avoidspots[MAX_AVOIDSPOTS]; //spots to avoid int numavoidspots; } bot_movestate_t; //used to avoid reachability links for some time after being used #define AVOIDREACH #define AVOIDREACH_TIME 6 //avoid links for 6 seconds after use #define AVOIDREACH_TRIES 4 //prediction times #define PREDICTIONTIME_JUMP 3 //in seconds #define PREDICTIONTIME_MOVE 2 //in seconds //weapon indexes for weapon jumping #define WEAPONINDEX_ROCKET_LAUNCHER 5 #define WEAPONINDEX_BFG 9 #define MODELTYPE_FUNC_PLAT 1 #define MODELTYPE_FUNC_BOB 2 #define MODELTYPE_FUNC_DOOR 3 #define MODELTYPE_FUNC_STATIC 4 libvar_t *sv_maxstep; libvar_t *sv_maxbarrier; libvar_t *sv_gravity; libvar_t *weapindex_rocketlauncher; libvar_t *weapindex_bfg10k; libvar_t *weapindex_grapple; libvar_t *entitytypemissile; libvar_t *offhandgrapple; libvar_t *cmd_grappleoff; libvar_t *cmd_grappleon; //type of model, func_plat or func_bobbing int modeltypes[MAX_MODELS]; bot_movestate_t *botmovestates[MAX_CLIENTS+1]; //======================================================================== // // Parameter: - // Returns: - // Changes Globals: - //======================================================================== int BotAllocMoveState(void) { int i; for (i = 1; i <= MAX_CLIENTS; i++) { if (!botmovestates[i]) { botmovestates[i] = (bot_movestate_t*) GetClearedMemory(sizeof(bot_movestate_t)); return i; } //end if } //end for return 0; } //end of the function BotAllocMoveState //======================================================================== // // Parameter: - // Returns: - // Changes Globals: - //======================================================================== void BotFreeMoveState(int handle) { if (handle <= 0 || handle > MAX_CLIENTS) { botimport.Print(PRT_FATAL, "move state handle %d out of range\n", handle); return; } //end if if (!botmovestates[handle]) { botimport.Print(PRT_FATAL, "invalid move state %d\n", handle); return; } //end if FreeMemory(botmovestates[handle]); botmovestates[handle] = NULL; } //end of the function BotFreeMoveState //======================================================================== // // Parameter: - // Returns: - // Changes Globals: - //======================================================================== bot_movestate_t *BotMoveStateFromHandle(int handle) { if (handle <= 0 || handle > MAX_CLIENTS) { botimport.Print(PRT_FATAL, "move state handle %d out of range\n", handle); return NULL; } //end if if (!botmovestates[handle]) { botimport.Print(PRT_FATAL, "invalid move state %d\n", handle); return NULL; } //end if return botmovestates[handle]; } //end of the function BotMoveStateFromHandle //======================================================================== // // Parameter: - // Returns: - // Changes Globals: - //======================================================================== void BotInitMoveState(int handle, bot_initmove_t *initmove) { bot_movestate_t *ms; ms = BotMoveStateFromHandle(handle); if (!ms) return; VectorCopy(initmove->origin, ms->origin); VectorCopy(initmove->velocity, ms->velocity); VectorCopy(initmove->viewoffset, ms->viewoffset); ms->entitynum = initmove->entitynum; ms->client = initmove->client; ms->thinktime = initmove->thinktime; ms->presencetype = initmove->presencetype; VectorCopy(initmove->viewangles, ms->viewangles); // ms->moveflags &= ~MFL_ONGROUND; if (initmove->or_moveflags & MFL_ONGROUND) ms->moveflags |= MFL_ONGROUND; ms->moveflags &= ~MFL_TELEPORTED; if (initmove->or_moveflags & MFL_TELEPORTED) ms->moveflags |= MFL_TELEPORTED; ms->moveflags &= ~MFL_WATERJUMP; if (initmove->or_moveflags & MFL_WATERJUMP) ms->moveflags |= MFL_WATERJUMP; ms->moveflags &= ~MFL_WALK; if (initmove->or_moveflags & MFL_WALK) ms->moveflags |= MFL_WALK; ms->moveflags &= ~MFL_GRAPPLEPULL; if (initmove->or_moveflags & MFL_GRAPPLEPULL) ms->moveflags |= MFL_GRAPPLEPULL; } //end of the function BotInitMoveState //======================================================================== // // Parameter: - // Returns: - // Changes Globals: - //======================================================================== float AngleDiff(float ang1, float ang2) { float diff; diff = ang1 - ang2; if (ang1 > ang2) { if (diff > 180.0) diff -= 360.0; } //end if else { if (diff < -180.0) diff += 360.0; } //end else return diff; } //end of the function AngleDiff //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotFuzzyPointReachabilityArea(vec3_t origin) { int firstareanum, j, x, y, z; int areas[10], numareas, areanum, bestareanum; float dist, bestdist; vec3_t points[10], v, end; firstareanum = 0; areanum = AAS_PointAreaNum(origin); if (areanum) { firstareanum = areanum; if (AAS_AreaReachability(areanum)) return areanum; } //end if VectorCopy(origin, end); end[2] += 4; numareas = AAS_TraceAreas(origin, end, areas, points, 10); for (j = 0; j < numareas; j++) { if (AAS_AreaReachability(areas[j])) return areas[j]; } //end for bestdist = 999999; bestareanum = 0; for (z = 1; z >= -1; z -= 1) { for (x = 1; x >= -1; x -= 1) { for (y = 1; y >= -1; y -= 1) { VectorCopy(origin, end); end[0] += x * 8; end[1] += y * 8; end[2] += z * 12; numareas = AAS_TraceAreas(origin, end, areas, points, 10); for (j = 0; j < numareas; j++) { if (AAS_AreaReachability(areas[j])) { VectorSubtract(points[j], origin, v); dist = VectorLength(v); if (dist < bestdist) { bestareanum = areas[j]; bestdist = dist; } //end if } //end if if (!firstareanum) firstareanum = areas[j]; } //end for } //end for } //end for if (bestareanum) return bestareanum; } //end for return firstareanum; } //end of the function BotFuzzyPointReachabilityArea //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotReachabilityArea(vec3_t origin, int client) { int modelnum, modeltype, reachnum, areanum; aas_reachability_t reach; vec3_t org, end, mins, maxs, up = {0, 0, 1}; bsp_trace_t bsptrace; aas_trace_t trace; //check if the bot is standing on something AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, mins, maxs); VectorMA(origin, -3, up, end); bsptrace = AAS_Trace(origin, mins, maxs, end, client, CONTENTS_SOLID|CONTENTS_PLAYERCLIP); if (!bsptrace.startsolid && bsptrace.fraction < 1 && bsptrace.ent != ENTITYNUM_NONE) { //if standing on the world the bot should be in a valid area if (bsptrace.ent == ENTITYNUM_WORLD) { return BotFuzzyPointReachabilityArea(origin); } //end if modelnum = AAS_EntityModelindex(bsptrace.ent); modeltype = modeltypes[modelnum]; //if standing on a func_plat or func_bobbing then the bot is assumed to be //in the area the reachability points to if (modeltype == MODELTYPE_FUNC_PLAT || modeltype == MODELTYPE_FUNC_BOB) { reachnum = AAS_NextModelReachability(0, modelnum); if (reachnum) { AAS_ReachabilityFromNum(reachnum, &reach); return reach.areanum; } //end if } //end else if //if the bot is swimming the bot should be in a valid area if (AAS_Swimming(origin)) { return BotFuzzyPointReachabilityArea(origin); } //end if // areanum = BotFuzzyPointReachabilityArea(origin); //if the bot is in an area with reachabilities if (areanum && AAS_AreaReachability(areanum)) return areanum; //trace down till the ground is hit because the bot is standing on some other entity VectorCopy(origin, org); VectorCopy(org, end); end[2] -= 800; trace = AAS_TraceClientBBox(org, end, PRESENCE_CROUCH, -1); if (!trace.startsolid) { VectorCopy(trace.endpos, org); } //end if // return BotFuzzyPointReachabilityArea(org); } //end if // return BotFuzzyPointReachabilityArea(origin); } //end of the function BotReachabilityArea //=========================================================================== // returns the reachability area the bot is in // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== /* int BotReachabilityArea(vec3_t origin, int testground) { int firstareanum, i, j, x, y, z; int areas[10], numareas, areanum, bestareanum; float dist, bestdist; vec3_t org, end, points[10], v; aas_trace_t trace; firstareanum = 0; for (i = 0; i < 2; i++) { VectorCopy(origin, org); //if test at the ground (used when bot is standing on an entity) if (i > 0) { VectorCopy(origin, end); end[2] -= 800; trace = AAS_TraceClientBBox(origin, end, PRESENCE_CROUCH, -1); if (!trace.startsolid) { VectorCopy(trace.endpos, org); } //end if } //end if firstareanum = 0; areanum = AAS_PointAreaNum(org); if (areanum) { firstareanum = areanum; if (AAS_AreaReachability(areanum)) return areanum; } //end if bestdist = 999999; bestareanum = 0; for (z = 1; z >= -1; z -= 1) { for (x = 1; x >= -1; x -= 1) { for (y = 1; y >= -1; y -= 1) { VectorCopy(org, end); end[0] += x * 8; end[1] += y * 8; end[2] += z * 12; numareas = AAS_TraceAreas(org, end, areas, points, 10); for (j = 0; j < numareas; j++) { if (AAS_AreaReachability(areas[j])) { VectorSubtract(points[j], org, v); dist = VectorLength(v); if (dist < bestdist) { bestareanum = areas[j]; bestdist = dist; } //end if } //end if } //end for } //end for } //end for if (bestareanum) return bestareanum; } //end for if (!testground) break; } //end for //#ifdef DEBUG //botimport.Print(PRT_MESSAGE, "no reachability area\n"); //#endif //DEBUG return firstareanum; } //end of the function BotReachabilityArea*/ //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotOnMover(vec3_t origin, int entnum, aas_reachability_t *reach) { int i, modelnum; vec3_t mins, maxs, modelorigin, org, end; vec3_t angles = {0, 0, 0}; vec3_t boxmins = {-16, -16, -8}, boxmaxs = {16, 16, 8}; bsp_trace_t trace; modelnum = reach->facenum & 0x0000FFFF; //get some bsp model info AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, NULL); // if (!AAS_OriginOfMoverWithModelNum(modelnum, modelorigin)) { botimport.Print(PRT_MESSAGE, "no entity with model %d\n", modelnum); return qfalse; } //end if // for (i = 0; i < 2; i++) { if (origin[i] > modelorigin[i] + maxs[i] + 16) return qfalse; if (origin[i] < modelorigin[i] + mins[i] - 16) return qfalse; } //end for // VectorCopy(origin, org); org[2] += 24; VectorCopy(origin, end); end[2] -= 48; // trace = AAS_Trace(org, boxmins, boxmaxs, end, entnum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP); if (!trace.startsolid && !trace.allsolid) { //NOTE: the reachability face number is the model number of the elevator if (trace.ent != ENTITYNUM_NONE && AAS_EntityModelNum(trace.ent) == modelnum) { return qtrue; } //end if } //end if return qfalse; } //end of the function BotOnMover //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int MoverDown(aas_reachability_t *reach) { int modelnum; vec3_t mins, maxs, origin; vec3_t angles = {0, 0, 0}; modelnum = reach->facenum & 0x0000FFFF; //get some bsp model info AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin); // if (!AAS_OriginOfMoverWithModelNum(modelnum, origin)) { botimport.Print(PRT_MESSAGE, "no entity with model %d\n", modelnum); return qfalse; } //end if //if the top of the plat is below the reachability start point if (origin[2] + maxs[2] < reach->start[2]) return qtrue; return qfalse; } //end of the function MoverDown //======================================================================== // // Parameter: - // Returns: - // Changes Globals: - //======================================================================== void BotSetBrushModelTypes(void) { int ent, modelnum; char classname[MAX_EPAIRKEY], model[MAX_EPAIRKEY]; Com_Memset(modeltypes, 0, MAX_MODELS * sizeof(int)); // for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) { if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; if (!AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY)) continue; if (model[0]) modelnum = atoi(model+1); else modelnum = 0; if (modelnum < 0 || modelnum > MAX_MODELS) { botimport.Print(PRT_MESSAGE, "entity %s model number out of range\n", classname); continue; } //end if if (!Q_stricmp(classname, "func_bobbing")) modeltypes[modelnum] = MODELTYPE_FUNC_BOB; else if (!Q_stricmp(classname, "func_plat")) modeltypes[modelnum] = MODELTYPE_FUNC_PLAT; else if (!Q_stricmp(classname, "func_door")) modeltypes[modelnum] = MODELTYPE_FUNC_DOOR; else if (!Q_stricmp(classname, "func_static")) modeltypes[modelnum] = MODELTYPE_FUNC_STATIC; } //end for } //end of the function BotSetBrushModelTypes //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotOnTopOfEntity(bot_movestate_t *ms) { vec3_t mins, maxs, end, up = {0, 0, 1}; bsp_trace_t trace; AAS_PresenceTypeBoundingBox(ms->presencetype, mins, maxs); VectorMA(ms->origin, -3, up, end); trace = AAS_Trace(ms->origin, mins, maxs, end, ms->entitynum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP); if (!trace.startsolid && (trace.ent != ENTITYNUM_WORLD && trace.ent != ENTITYNUM_NONE) ) { return trace.ent; } //end if return -1; } //end of the function BotOnTopOfEntity //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotValidTravel(vec3_t origin, aas_reachability_t *reach, int travelflags) { //if the reachability uses an unwanted travel type if (AAS_TravelFlagForType(reach->traveltype) & ~travelflags) return qfalse; //don't go into areas with bad travel types if (AAS_AreaContentsTravelFlags(reach->areanum) & ~travelflags) return qfalse; return qtrue; } //end of the function BotValidTravel //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotAddToAvoidReach(bot_movestate_t *ms, int number, float avoidtime) { int i; for (i = 0; i < MAX_AVOIDREACH; i++) { if (ms->avoidreach[i] == number) { if (ms->avoidreachtimes[i] > AAS_Time()) ms->avoidreachtries[i]++; else ms->avoidreachtries[i] = 1; ms->avoidreachtimes[i] = AAS_Time() + avoidtime; return; } //end if } //end for //add the reachability to the reachabilities to avoid for a while for (i = 0; i < MAX_AVOIDREACH; i++) { if (ms->avoidreachtimes[i] < AAS_Time()) { ms->avoidreach[i] = number; ms->avoidreachtimes[i] = AAS_Time() + avoidtime; ms->avoidreachtries[i] = 1; return; } //end if } //end for } //end of the function BotAddToAvoidReach //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== float DistanceFromLineSquared(vec3_t p, vec3_t lp1, vec3_t lp2) { vec3_t proj, dir; int j; AAS_ProjectPointOntoVector(p, lp1, lp2, proj); for (j = 0; j < 3; j++) if ((proj[j] > lp1[j] && proj[j] > lp2[j]) || (proj[j] < lp1[j] && proj[j] < lp2[j])) break; if (j < 3) { if (fabs(proj[j] - lp1[j]) < fabs(proj[j] - lp2[j])) VectorSubtract(p, lp1, dir); else VectorSubtract(p, lp2, dir); return VectorLengthSquared(dir); } VectorSubtract(p, proj, dir); return VectorLengthSquared(dir); } //end of the function DistanceFromLineSquared //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== float VectorDistanceSquared(vec3_t p1, vec3_t p2) { vec3_t dir; VectorSubtract(p2, p1, dir); return VectorLengthSquared(dir); } //end of the function VectorDistanceSquared //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotAvoidSpots(vec3_t origin, aas_reachability_t *reach, bot_avoidspot_t *avoidspots, int numavoidspots) { int checkbetween, i, type; float squareddist, squaredradius; switch(reach->traveltype & TRAVELTYPE_MASK) { case TRAVEL_WALK: checkbetween = qtrue; break; case TRAVEL_CROUCH: checkbetween = qtrue; break; case TRAVEL_BARRIERJUMP: checkbetween = qtrue; break; case TRAVEL_LADDER: checkbetween = qtrue; break; case TRAVEL_WALKOFFLEDGE: checkbetween = qfalse; break; case TRAVEL_JUMP: checkbetween = qfalse; break; case TRAVEL_SWIM: checkbetween = qtrue; break; case TRAVEL_WATERJUMP: checkbetween = qtrue; break; case TRAVEL_TELEPORT: checkbetween = qfalse; break; case TRAVEL_ELEVATOR: checkbetween = qfalse; break; case TRAVEL_GRAPPLEHOOK: checkbetween = qfalse; break; case TRAVEL_ROCKETJUMP: checkbetween = qfalse; break; case TRAVEL_BFGJUMP: checkbetween = qfalse; break; case TRAVEL_JUMPPAD: checkbetween = qfalse; break; case TRAVEL_FUNCBOB: checkbetween = qfalse; break; default: checkbetween = qtrue; break; } //end switch type = AVOID_CLEAR; for (i = 0; i < numavoidspots; i++) { squaredradius = Square(avoidspots[i].radius); squareddist = DistanceFromLineSquared(avoidspots[i].origin, origin, reach->start); // if moving towards the avoid spot if (squareddist < squaredradius && VectorDistanceSquared(avoidspots[i].origin, origin) > squareddist) { type = avoidspots[i].type; } //end if else if (checkbetween) { squareddist = DistanceFromLineSquared(avoidspots[i].origin, reach->start, reach->end); // if moving towards the avoid spot if (squareddist < squaredradius && VectorDistanceSquared(avoidspots[i].origin, reach->start) > squareddist) { type = avoidspots[i].type; } //end if } //end if else { VectorDistanceSquared(avoidspots[i].origin, reach->end); // if the reachability leads closer to the avoid spot if (squareddist < squaredradius && VectorDistanceSquared(avoidspots[i].origin, reach->start) > squareddist) { type = avoidspots[i].type; } //end if } //end else if (type == AVOID_ALWAYS) return type; } //end for return type; } //end of the function BotAvoidSpots //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotAddAvoidSpot(int movestate, vec3_t origin, float radius, int type) { bot_movestate_t *ms; ms = BotMoveStateFromHandle(movestate); if (!ms) return; if (type == AVOID_CLEAR) { ms->numavoidspots = 0; return; } //end if if (ms->numavoidspots >= MAX_AVOIDSPOTS) return; VectorCopy(origin, ms->avoidspots[ms->numavoidspots].origin); ms->avoidspots[ms->numavoidspots].radius = radius; ms->avoidspots[ms->numavoidspots].type = type; ms->numavoidspots++; } //end of the function BotAddAvoidSpot //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotGetReachabilityToGoal(vec3_t origin, int areanum, int lastgoalareanum, int lastareanum, int *avoidreach, float *avoidreachtimes, int *avoidreachtries, bot_goal_t *goal, int travelflags, int movetravelflags, struct bot_avoidspot_s *avoidspots, int numavoidspots, int *flags) { int i, t, besttime, bestreachnum, reachnum; aas_reachability_t reach; //if not in a valid area if (!areanum) return 0; // if (AAS_AreaDoNotEnter(areanum) || AAS_AreaDoNotEnter(goal->areanum)) { travelflags |= TFL_DONOTENTER; movetravelflags |= TFL_DONOTENTER; } //end if //use the routing to find the next area to go to besttime = 0; bestreachnum = 0; // for (reachnum = AAS_NextAreaReachability(areanum, 0); reachnum; reachnum = AAS_NextAreaReachability(areanum, reachnum)) { #ifdef AVOIDREACH //check if it isn't an reachability to avoid for (i = 0; i < MAX_AVOIDREACH; i++) { if (avoidreach[i] == reachnum && avoidreachtimes[i] >= AAS_Time()) break; } //end for if (i != MAX_AVOIDREACH && avoidreachtries[i] > AVOIDREACH_TRIES) { #ifdef DEBUG if (bot_developer) { botimport.Print(PRT_MESSAGE, "avoiding reachability %d\n", avoidreach[i]); } //end if #endif //DEBUG continue; } //end if #endif //AVOIDREACH //get the reachability from the number AAS_ReachabilityFromNum(reachnum, &reach); //NOTE: do not go back to the previous area if the goal didn't change //NOTE: is this actually avoidance of local routing minima between two areas??? if (lastgoalareanum == goal->areanum && reach.areanum == lastareanum) continue; //if (AAS_AreaContentsTravelFlags(reach.areanum) & ~travelflags) continue; //if the travel isn't valid if (!BotValidTravel(origin, &reach, movetravelflags)) continue; //get the travel time t = AAS_AreaTravelTimeToGoalArea(reach.areanum, reach.end, goal->areanum, travelflags); //if the goal area isn't reachable from the reachable area if (!t) continue; //if the bot should not use this reachability to avoid bad spots if (BotAvoidSpots(origin, &reach, avoidspots, numavoidspots)) { if (flags) { *flags |= MOVERESULT_BLOCKEDBYAVOIDSPOT; } continue; } //add the travel time towards the area t += reach.traveltime;// + AAS_AreaTravelTime(areanum, origin, reach.start); //if the travel time is better than the ones already found if (!besttime || t < besttime) { besttime = t; bestreachnum = reachnum; } //end if } //end for // return bestreachnum; } //end of the function BotGetReachabilityToGoal //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotAddToTarget(vec3_t start, vec3_t end, float maxdist, float *dist, vec3_t target) { vec3_t dir; float curdist; VectorSubtract(end, start, dir); curdist = VectorNormalize(dir); if (*dist + curdist < maxdist) { VectorCopy(end, target); *dist += curdist; return qfalse; } //end if else { VectorMA(start, maxdist - *dist, dir, target); *dist = maxdist; return qtrue; } //end else } //end of the function BotAddToTarget int BotMovementViewTarget(int movestate, bot_goal_t *goal, int travelflags, float lookahead, vec3_t target) { aas_reachability_t reach; int reachnum, lastareanum; bot_movestate_t *ms; vec3_t end; float dist; ms = BotMoveStateFromHandle(movestate); if (!ms) return qfalse; reachnum = 0; //if the bot has no goal or no last reachability if (!ms->lastreachnum || !goal) return qfalse; reachnum = ms->lastreachnum; VectorCopy(ms->origin, end); lastareanum = ms->lastareanum; dist = 0; while(reachnum && dist < lookahead) { AAS_ReachabilityFromNum(reachnum, &reach); if (BotAddToTarget(end, reach.start, lookahead, &dist, target)) return qtrue; //never look beyond teleporters if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_TELEPORT) return qtrue; //never look beyond the weapon jump point if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_ROCKETJUMP) return qtrue; if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_BFGJUMP) return qtrue; //don't add jump pad distances if ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_JUMPPAD && (reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_ELEVATOR && (reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_FUNCBOB) { if (BotAddToTarget(reach.start, reach.end, lookahead, &dist, target)) return qtrue; } //end if reachnum = BotGetReachabilityToGoal(reach.end, reach.areanum, ms->lastgoalareanum, lastareanum, ms->avoidreach, ms->avoidreachtimes, ms->avoidreachtries, goal, travelflags, travelflags, NULL, 0, NULL); VectorCopy(reach.end, end); lastareanum = reach.areanum; if (lastareanum == goal->areanum) { BotAddToTarget(reach.end, goal->origin, lookahead, &dist, target); return qtrue; } //end if } //end while // return qfalse; } //end of the function BotMovementViewTarget //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotVisible(int ent, vec3_t eye, vec3_t target) { bsp_trace_t trace; trace = AAS_Trace(eye, NULL, NULL, target, ent, CONTENTS_SOLID|CONTENTS_PLAYERCLIP); if (trace.fraction >= 1) return qtrue; return qfalse; } //end of the function BotVisible //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotPredictVisiblePosition(vec3_t origin, int areanum, bot_goal_t *goal, int travelflags, vec3_t target) { aas_reachability_t reach; int reachnum, lastgoalareanum, lastareanum, i; int avoidreach[MAX_AVOIDREACH]; float avoidreachtimes[MAX_AVOIDREACH]; int avoidreachtries[MAX_AVOIDREACH]; vec3_t end; //if the bot has no goal or no last reachability if (!goal) return qfalse; //if the areanum is not valid if (!areanum) return qfalse; //if the goal areanum is not valid if (!goal->areanum) return qfalse; Com_Memset(avoidreach, 0, MAX_AVOIDREACH * sizeof(int)); lastgoalareanum = goal->areanum; lastareanum = areanum; VectorCopy(origin, end); //only do 20 hops for (i = 0; i < 20 && (areanum != goal->areanum); i++) { // reachnum = BotGetReachabilityToGoal(end, areanum, lastgoalareanum, lastareanum, avoidreach, avoidreachtimes, avoidreachtries, goal, travelflags, travelflags, NULL, 0, NULL); if (!reachnum) return qfalse; AAS_ReachabilityFromNum(reachnum, &reach); // if (BotVisible(goal->entitynum, goal->origin, reach.start)) { VectorCopy(reach.start, target); return qtrue; } //end if // if (BotVisible(goal->entitynum, goal->origin, reach.end)) { VectorCopy(reach.end, target); return qtrue; } //end if // if (reach.areanum == goal->areanum) { VectorCopy(reach.end, target); return qtrue; } //end if // lastareanum = areanum; areanum = reach.areanum; VectorCopy(reach.end, end); // } //end while // return qfalse; } //end of the function BotPredictVisiblePosition //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void MoverBottomCenter(aas_reachability_t *reach, vec3_t bottomcenter) { int modelnum; vec3_t mins, maxs, origin, mids; vec3_t angles = {0, 0, 0}; modelnum = reach->facenum & 0x0000FFFF; //get some bsp model info AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin); // if (!AAS_OriginOfMoverWithModelNum(modelnum, origin)) { botimport.Print(PRT_MESSAGE, "no entity with model %d\n", modelnum); } //end if //get a point just above the plat in the bottom position VectorAdd(mins, maxs, mids); VectorMA(origin, 0.5, mids, bottomcenter); bottomcenter[2] = reach->start[2]; } //end of the function MoverBottomCenter //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== float BotGapDistance(vec3_t origin, vec3_t hordir, int entnum) { float dist, startz; vec3_t start, end; aas_trace_t trace; //do gap checking startz = origin[2]; //this enables walking down stairs more fluidly { VectorCopy(origin, start); VectorCopy(origin, end); end[2] -= 60; trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, entnum); if (trace.fraction >= 1) return 1; startz = trace.endpos[2] + 1; } // for (dist = 8; dist <= 100; dist += 8) { VectorMA(origin, dist, hordir, start); start[2] = startz + 24; VectorCopy(start, end); end[2] -= 48 + sv_maxbarrier->value; trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, entnum); //if solid is found the bot can't walk any further and fall into a gap if (!trace.startsolid) { //if it is a gap if (trace.endpos[2] < startz - sv_maxstep->value - 8) { VectorCopy(trace.endpos, end); end[2] -= 20; if (AAS_PointContents(end) & CONTENTS_WATER) break; //if a gap is found slow down //botimport.Print(PRT_MESSAGE, "gap at %f\n", dist); return dist; } //end if startz = trace.endpos[2]; } //end if } //end for return 0; } //end of the function BotGapDistance //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotCheckBarrierJump(bot_movestate_t *ms, vec3_t dir, float speed) { vec3_t start, hordir, end; aas_trace_t trace; VectorCopy(ms->origin, end); end[2] += sv_maxbarrier->value; //trace right up trace = AAS_TraceClientBBox(ms->origin, end, PRESENCE_NORMAL, ms->entitynum); //this shouldn't happen... but we check anyway if (trace.startsolid) return qfalse; //if very low ceiling it isn't possible to jump up to a barrier if (trace.endpos[2] - ms->origin[2] < sv_maxstep->value) return qfalse; // hordir[0] = dir[0]; hordir[1] = dir[1]; hordir[2] = 0; VectorNormalize(hordir); VectorMA(ms->origin, ms->thinktime * speed * 0.5, hordir, end); VectorCopy(trace.endpos, start); end[2] = trace.endpos[2]; //trace from previous trace end pos horizontally in the move direction trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, ms->entitynum); //again this shouldn't happen if (trace.startsolid) return qfalse; // VectorCopy(trace.endpos, start); VectorCopy(trace.endpos, end); end[2] = ms->origin[2]; //trace down from the previous trace end pos trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, ms->entitynum); //if solid if (trace.startsolid) return qfalse; //if no obstacle at all if (trace.fraction >= 1.0) return qfalse; //if less than the maximum step height if (trace.endpos[2] - ms->origin[2] < sv_maxstep->value) return qfalse; // EA_Jump(ms->client); EA_Move(ms->client, hordir, speed); ms->moveflags |= MFL_BARRIERJUMP; //there is a barrier return qtrue; } //end of the function BotCheckBarrierJump //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotSwimInDirection(bot_movestate_t *ms, vec3_t dir, float speed, int type) { vec3_t normdir; VectorCopy(dir, normdir); VectorNormalize(normdir); EA_Move(ms->client, normdir, speed); return qtrue; } //end of the function BotSwimInDirection //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotWalkInDirection(bot_movestate_t *ms, vec3_t dir, float speed, int type) { vec3_t hordir, cmdmove, velocity, tmpdir, origin; int presencetype, maxframes, cmdframes, stopevent; aas_clientmove_t move; float dist; if (AAS_OnGround(ms->origin, ms->presencetype, ms->entitynum)) ms->moveflags |= MFL_ONGROUND; //if the bot is on the ground if (ms->moveflags & MFL_ONGROUND) { //if there is a barrier the bot can jump on if (BotCheckBarrierJump(ms, dir, speed)) return qtrue; //remove barrier jump flag ms->moveflags &= ~MFL_BARRIERJUMP; //get the presence type for the movement if ((type & MOVE_CROUCH) && !(type & MOVE_JUMP)) presencetype = PRESENCE_CROUCH; else presencetype = PRESENCE_NORMAL; //horizontal direction hordir[0] = dir[0]; hordir[1] = dir[1]; hordir[2] = 0; VectorNormalize(hordir); //if the bot is not supposed to jump if (!(type & MOVE_JUMP)) { //if there is a gap, try to jump over it if (BotGapDistance(ms->origin, hordir, ms->entitynum) > 0) type |= MOVE_JUMP; } //end if //get command movement VectorScale(hordir, speed, cmdmove); VectorCopy(ms->velocity, velocity); // if (type & MOVE_JUMP) { //botimport.Print(PRT_MESSAGE, "trying jump\n"); cmdmove[2] = 400; maxframes = PREDICTIONTIME_JUMP / 0.1; cmdframes = 1; stopevent = SE_HITGROUND|SE_HITGROUNDDAMAGE| SE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA; } //end if else { maxframes = 2; cmdframes = 2; stopevent = SE_HITGROUNDDAMAGE| SE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA; } //end else //AAS_ClearShownDebugLines(); // VectorCopy(ms->origin, origin); origin[2] += 0.5; AAS_PredictClientMovement(&move, ms->entitynum, origin, presencetype, qtrue, velocity, cmdmove, cmdframes, maxframes, 0.1f, stopevent, 0, qfalse);//qtrue); //if prediction time wasn't enough to fully predict the movement if (move.frames >= maxframes && (type & MOVE_JUMP)) { //botimport.Print(PRT_MESSAGE, "client %d: max prediction frames\n", ms->client); return qfalse; } //end if //don't enter slime or lava and don't fall from too high if (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE)) { //botimport.Print(PRT_MESSAGE, "client %d: would be hurt ", ms->client); //if (move.stopevent & SE_ENTERSLIME) botimport.Print(PRT_MESSAGE, "slime\n"); //if (move.stopevent & SE_ENTERLAVA) botimport.Print(PRT_MESSAGE, "lava\n"); //if (move.stopevent & SE_HITGROUNDDAMAGE) botimport.Print(PRT_MESSAGE, "hitground\n"); return qfalse; } //end if //if ground was hit if (move.stopevent & SE_HITGROUND) { //check for nearby gap VectorNormalize2(move.velocity, tmpdir); dist = BotGapDistance(move.endpos, tmpdir, ms->entitynum); if (dist > 0) return qfalse; // dist = BotGapDistance(move.endpos, hordir, ms->entitynum); if (dist > 0) return qfalse; } //end if //get horizontal movement tmpdir[0] = move.endpos[0] - ms->origin[0]; tmpdir[1] = move.endpos[1] - ms->origin[1]; tmpdir[2] = 0; // //AAS_DrawCross(move.endpos, 4, LINECOLOR_BLUE); //the bot is blocked by something if (VectorLength(tmpdir) < speed * ms->thinktime * 0.5) return qfalse; //perform the movement if (type & MOVE_JUMP) EA_Jump(ms->client); if (type & MOVE_CROUCH) EA_Crouch(ms->client); EA_Move(ms->client, hordir, speed); //movement was succesfull return qtrue; } //end if else { if (ms->moveflags & MFL_BARRIERJUMP) { //if near the top or going down if (ms->velocity[2] < 50) { EA_Move(ms->client, dir, speed); } //end if } //end if //FIXME: do air control to avoid hazards return qtrue; } //end else } //end of the function BotWalkInDirection //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotMoveInDirection(int movestate, vec3_t dir, float speed, int type) { bot_movestate_t *ms; ms = BotMoveStateFromHandle(movestate); if (!ms) return qfalse; //if swimming if (AAS_Swimming(ms->origin)) { return BotSwimInDirection(ms, dir, speed, type); } //end if else { return BotWalkInDirection(ms, dir, speed, type); } //end else } //end of the function BotMoveInDirection //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int Intersection(vec2_t p1, vec2_t p2, vec2_t p3, vec2_t p4, vec2_t out) { float x1, dx1, dy1, x2, dx2, dy2, d; dx1 = p2[0] - p1[0]; dy1 = p2[1] - p1[1]; dx2 = p4[0] - p3[0]; dy2 = p4[1] - p3[1]; d = dy1 * dx2 - dx1 * dy2; if (d != 0) { x1 = p1[1] * dx1 - p1[0] * dy1; x2 = p3[1] * dx2 - p3[0] * dy2; out[0] = (int) ((dx1 * x2 - dx2 * x1) / d); out[1] = (int) ((dy1 * x2 - dy2 * x1) / d); return qtrue; } //end if else { return qfalse; } //end else } //end of the function Intersection //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotCheckBlocked(bot_movestate_t *ms, vec3_t dir, int checkbottom, bot_moveresult_t *result) { vec3_t mins, maxs, end, up = {0, 0, 1}; bsp_trace_t trace; //test for entities obstructing the bot's path AAS_PresenceTypeBoundingBox(ms->presencetype, mins, maxs); // if (fabs(DotProduct(dir, up)) < 0.7) { mins[2] += sv_maxstep->value; //if the bot can step on maxs[2] -= 10; //a little lower to avoid low ceiling } //end if VectorMA(ms->origin, 3, dir, end); trace = AAS_Trace(ms->origin, mins, maxs, end, ms->entitynum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP|CONTENTS_BODY); //if not started in solid and not hitting the world entity if (!trace.startsolid && (trace.ent != ENTITYNUM_WORLD && trace.ent != ENTITYNUM_NONE) ) { result->blocked = qtrue; result->blockentity = trace.ent; #ifdef DEBUG //botimport.Print(PRT_MESSAGE, "%d: BotCheckBlocked: I'm blocked\n", ms->client); #endif //DEBUG } //end if //if not in an area with reachability else if (checkbottom && !AAS_AreaReachability(ms->areanum)) { //check if the bot is standing on something AAS_PresenceTypeBoundingBox(ms->presencetype, mins, maxs); VectorMA(ms->origin, -3, up, end); trace = AAS_Trace(ms->origin, mins, maxs, end, ms->entitynum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP); if (!trace.startsolid && (trace.ent != ENTITYNUM_WORLD && trace.ent != ENTITYNUM_NONE) ) { result->blocked = qtrue; result->blockentity = trace.ent; result->flags |= MOVERESULT_ONTOPOFOBSTACLE; #ifdef DEBUG //botimport.Print(PRT_MESSAGE, "%d: BotCheckBlocked: I'm blocked\n", ms->client); #endif //DEBUG } //end if } //end else } //end of the function BotCheckBlocked //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotClearMoveResult(bot_moveresult_t *moveresult) { moveresult->failure = qfalse; moveresult->type = 0; moveresult->blocked = qfalse; moveresult->blockentity = 0; moveresult->traveltype = 0; moveresult->flags = 0; } //end of the function BotClearMoveResult //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bot_moveresult_t BotTravel_Walk(bot_movestate_t *ms, aas_reachability_t *reach) { float dist, speed; vec3_t hordir; bot_moveresult_t result; BotClearMoveResult(&result); //first walk straight to the reachability start hordir[0] = reach->start[0] - ms->origin[0]; hordir[1] = reach->start[1] - ms->origin[1]; hordir[2] = 0; dist = VectorNormalize(hordir); // BotCheckBlocked(ms, hordir, qtrue, &result); // if (dist < 10) { //walk straight to the reachability end hordir[0] = reach->end[0] - ms->origin[0]; hordir[1] = reach->end[1] - ms->origin[1]; hordir[2] = 0; dist = VectorNormalize(hordir); } //end if //if going towards a crouch area if (!(AAS_AreaPresenceType(reach->areanum) & PRESENCE_NORMAL)) { //if pretty close to the reachable area if (dist < 20) EA_Crouch(ms->client); } //end if // dist = BotGapDistance(ms->origin, hordir, ms->entitynum); // if (ms->moveflags & MFL_WALK) { if (dist > 0) speed = 200 - (180 - 1 * dist); else speed = 200; EA_Walk(ms->client); } //end if else { if (dist > 0) speed = 400 - (360 - 2 * dist); else speed = 400; } //end else //elemantary action move in direction EA_Move(ms->client, hordir, speed); VectorCopy(hordir, result.movedir); // return result; } //end of the function BotTravel_Walk //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bot_moveresult_t BotFinishTravel_Walk(bot_movestate_t *ms, aas_reachability_t *reach) { vec3_t hordir; float dist, speed; bot_moveresult_t result; BotClearMoveResult(&result); //if not on the ground and changed areas... don't walk back!! //(doesn't seem to help) /* ms->areanum = BotFuzzyPointReachabilityArea(ms->origin); if (ms->areanum == reach->areanum) { #ifdef DEBUG botimport.Print(PRT_MESSAGE, "BotFinishTravel_Walk: already in reach area\n"); #endif //DEBUG return result; } //end if*/ //go straight to the reachability end hordir[0] = reach->end[0] - ms->origin[0]; hordir[1] = reach->end[1] - ms->origin[1]; hordir[2] = 0; dist = VectorNormalize(hordir); // if (dist > 100) dist = 100; speed = 400 - (400 - 3 * dist); // EA_Move(ms->client, hordir, speed); VectorCopy(hordir, result.movedir); // return result; } //end of the function BotFinishTravel_Walk //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bot_moveresult_t BotTravel_Crouch(bot_movestate_t *ms, aas_reachability_t *reach) { float speed; vec3_t hordir; bot_moveresult_t result; BotClearMoveResult(&result); // speed = 400; //walk straight to reachability end hordir[0] = reach->end[0] - ms->origin[0]; hordir[1] = reach->end[1] - ms->origin[1]; hordir[2] = 0; VectorNormalize(hordir); // BotCheckBlocked(ms, hordir, qtrue, &result); //elemantary actions EA_Crouch(ms->client); EA_Move(ms->client, hordir, speed); // VectorCopy(hordir, result.movedir); // return result; } //end of the function BotTravel_Crouch //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bot_moveresult_t BotTravel_BarrierJump(bot_movestate_t *ms, aas_reachability_t *reach) { float dist, speed; vec3_t hordir; bot_moveresult_t result; BotClearMoveResult(&result); //walk straight to reachability start hordir[0] = reach->start[0] - ms->origin[0]; hordir[1] = reach->start[1] - ms->origin[1]; hordir[2] = 0; dist = VectorNormalize(hordir); // BotCheckBlocked(ms, hordir, qtrue, &result); //if pretty close to the barrier if (dist < 9) { EA_Jump(ms->client); } //end if else { if (dist > 60) dist = 60; speed = 360 - (360 - 6 * dist); EA_Move(ms->client, hordir, speed); } //end else VectorCopy(hordir, result.movedir); // return result; } //end of the function BotTravel_BarrierJump //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bot_moveresult_t BotFinishTravel_BarrierJump(bot_movestate_t *ms, aas_reachability_t *reach) { float dist; vec3_t hordir; bot_moveresult_t result; BotClearMoveResult(&result); //if near the top or going down if (ms->velocity[2] < 250) { hordir[0] = reach->end[0] - ms->origin[0]; hordir[1] = reach->end[1] - ms->origin[1]; hordir[2] = 0; dist = VectorNormalize(hordir); // BotCheckBlocked(ms, hordir, qtrue, &result); // EA_Move(ms->client, hordir, 400); VectorCopy(hordir, result.movedir); } //end if // return result; } //end of the function BotFinishTravel_BarrierJump //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bot_moveresult_t BotTravel_Swim(bot_movestate_t *ms, aas_reachability_t *reach) { vec3_t dir; bot_moveresult_t result; BotClearMoveResult(&result); //swim straight to reachability end VectorSubtract(reach->start, ms->origin, dir); VectorNormalize(dir); // BotCheckBlocked(ms, dir, qtrue, &result); //elemantary actions EA_Move(ms->client, dir, 400); // VectorCopy(dir, result.movedir); Vector2Angles(dir, result.ideal_viewangles); result.flags |= MOVERESULT_SWIMVIEW; // return result; } //end of the function BotTravel_Swim //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bot_moveresult_t BotTravel_WaterJump(bot_movestate_t *ms, aas_reachability_t *reach) { vec3_t dir, hordir; float dist; bot_moveresult_t result; BotClearMoveResult(&result); //swim straight to reachability end VectorSubtract(reach->end, ms->origin, dir); VectorCopy(dir, hordir); hordir[2] = 0; dir[2] += 15 + crandom() * 40; //botimport.Print(PRT_MESSAGE, "BotTravel_WaterJump: dir[2] = %f\n", dir[2]); VectorNormalize(dir); dist = VectorNormalize(hordir); //elemantary actions //EA_Move(ms->client, dir, 400); EA_MoveForward(ms->client); //move up if close to the actual out of water jump spot if (dist < 40) EA_MoveUp(ms->client); //set the ideal view angles Vector2Angles(dir, result.ideal_viewangles); result.flags |= MOVERESULT_MOVEMENTVIEW; // VectorCopy(dir, result.movedir); // return result; } //end of the function BotTravel_WaterJump //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bot_moveresult_t BotFinishTravel_WaterJump(bot_movestate_t *ms, aas_reachability_t *reach) { vec3_t dir, pnt; float dist; bot_moveresult_t result; //botimport.Print(PRT_MESSAGE, "BotFinishTravel_WaterJump\n"); BotClearMoveResult(&result); //if waterjumping there's nothing to do if (ms->moveflags & MFL_WATERJUMP) return result; //if not touching any water anymore don't do anything //otherwise the bot sometimes keeps jumping? VectorCopy(ms->origin, pnt); pnt[2] -= 32; //extra for q2dm4 near red armor/mega health if (!(AAS_PointContents(pnt) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER))) return result; //swim straight to reachability end VectorSubtract(reach->end, ms->origin, dir); dir[0] += crandom() * 10; dir[1] += crandom() * 10; dir[2] += 70 + crandom() * 10; dist = VectorNormalize(dir); //elemantary actions EA_Move(ms->client, dir, 400); //set the ideal view angles Vector2Angles(dir, result.ideal_viewangles); result.flags |= MOVERESULT_MOVEMENTVIEW; // VectorCopy(dir, result.movedir); // return result; } //end of the function BotFinishTravel_WaterJump //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bot_moveresult_t BotTravel_WalkOffLedge(bot_movestate_t *ms, aas_reachability_t *reach) { vec3_t hordir, dir; float dist, speed, reachhordist; bot_moveresult_t result; BotClearMoveResult(&result); //check if the bot is blocked by anything VectorSubtract(reach->start, ms->origin, dir); VectorNormalize(dir); BotCheckBlocked(ms, dir, qtrue, &result); //if the reachability start and end are practially above each other VectorSubtract(reach->end, reach->start, dir); dir[2] = 0; reachhordist = VectorLength(dir); //walk straight to the reachability start hordir[0] = reach->start[0] - ms->origin[0]; hordir[1] = reach->start[1] - ms->origin[1]; hordir[2] = 0; dist = VectorNormalize(hordir); //if pretty close to the start focus on the reachability end if (dist < 48) { hordir[0] = reach->end[0] - ms->origin[0]; hordir[1] = reach->end[1] - ms->origin[1]; hordir[2] = 0; VectorNormalize(hordir); // if (reachhordist < 20) { speed = 100; } //end if else if (!AAS_HorizontalVelocityForJump(0, reach->start, reach->end, &speed)) { speed = 400; } //end if } //end if else { if (reachhordist < 20) { if (dist > 64) dist = 64; speed = 400 - (256 - 4 * dist); } //end if else { speed = 400; } //end else } //end else // BotCheckBlocked(ms, hordir, qtrue, &result); //elemantary action EA_Move(ms->client, hordir, speed); VectorCopy(hordir, result.movedir); // return result; } //end of the function BotTravel_WalkOffLedge //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotAirControl(vec3_t origin, vec3_t velocity, vec3_t goal, vec3_t dir, float *speed) { vec3_t org, vel; float dist; int i; VectorCopy(origin, org); VectorScale(velocity, 0.1, vel); for (i = 0; i < 50; i++) { vel[2] -= sv_gravity->value * 0.01; //if going down and next position would be below the goal if (vel[2] < 0 && org[2] + vel[2] < goal[2]) { VectorScale(vel, (goal[2] - org[2]) / vel[2], vel); VectorAdd(org, vel, org); VectorSubtract(goal, org, dir); dist = VectorNormalize(dir); if (dist > 32) dist = 32; *speed = 400 - (400 - 13 * dist); return qtrue; } //end if else { VectorAdd(org, vel, org); } //end else } //end for VectorSet(dir, 0, 0, 0); *speed = 400; return qfalse; } //end of the function BotAirControl //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bot_moveresult_t BotFinishTravel_WalkOffLedge(bot_movestate_t *ms, aas_reachability_t *reach) { vec3_t dir, hordir, end, v; float dist, speed; bot_moveresult_t result; BotClearMoveResult(&result); // VectorSubtract(reach->end, ms->origin, dir); BotCheckBlocked(ms, dir, qtrue, &result); // VectorSubtract(reach->end, ms->origin, v); v[2] = 0; dist = VectorNormalize(v); if (dist > 16) VectorMA(reach->end, 16, v, end); else VectorCopy(reach->end, end); // if (!BotAirControl(ms->origin, ms->velocity, end, hordir, &speed)) { //go straight to the reachability end VectorCopy(dir, hordir); hordir[2] = 0; // dist = VectorNormalize(hordir); speed = 400; } //end if // EA_Move(ms->client, hordir, speed); VectorCopy(hordir, result.movedir); // return result; } //end of the function BotFinishTravel_WalkOffLedge //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== /* bot_moveresult_t BotTravel_Jump(bot_movestate_t *ms, aas_reachability_t *reach) { vec3_t hordir; float dist, gapdist, speed, horspeed, sv_jumpvel; bot_moveresult_t result; BotClearMoveResult(&result); // sv_jumpvel = botlibglobals.sv_jumpvel->value; //walk straight to the reachability start hordir[0] = reach->start[0] - ms->origin[0]; hordir[1] = reach->start[1] - ms->origin[1]; hordir[2] = 0; dist = VectorNormalize(hordir); // speed = 350; // gapdist = BotGapDistance(ms, hordir, ms->entitynum); //if pretty close to the start focus on the reachability end if (dist < 50 || (gapdist && gapdist < 50)) { //NOTE: using max speed (400) works best //if (AAS_HorizontalVelocityForJump(sv_jumpvel, ms->origin, reach->end, &horspeed)) //{ // speed = horspeed * 400 / botlibglobals.sv_maxwalkvelocity->value; //} //end if hordir[0] = reach->end[0] - ms->origin[0]; hordir[1] = reach->end[1] - ms->origin[1]; VectorNormalize(hordir); //elemantary action jump EA_Jump(ms->client); // ms->jumpreach = ms->lastreachnum; speed = 600; } //end if else { if (AAS_HorizontalVelocityForJump(sv_jumpvel, reach->start, reach->end, &horspeed)) { speed = horspeed * 400 / botlibglobals.sv_maxwalkvelocity->value; } //end if } //end else //elemantary action EA_Move(ms->client, hordir, speed); VectorCopy(hordir, result.movedir); // return result; } //end of the function BotTravel_Jump*/ /* bot_moveresult_t BotTravel_Jump(bot_movestate_t *ms, aas_reachability_t *reach) { vec3_t hordir, dir1, dir2, mins, maxs, start, end; float dist1, dist2, speed; bot_moveresult_t result; bsp_trace_t trace; BotClearMoveResult(&result); // hordir[0] = reach->start[0] - reach->end[0]; hordir[1] = reach->start[1] - reach->end[1]; hordir[2] = 0; VectorNormalize(hordir); // VectorCopy(reach->start, start); start[2] += 1; //minus back the bouding box size plus 16 VectorMA(reach->start, 80, hordir, end); // AAS_PresenceTypeBoundingBox(PRESENCE_NORMAL, mins, maxs); //check for solids trace = AAS_Trace(start, mins, maxs, end, ms->entitynum, MASK_PLAYERSOLID); if (trace.startsolid) VectorCopy(start, trace.endpos); //check for a gap for (dist1 = 0; dist1 < 80; dist1 += 10) { VectorMA(start, dist1+10, hordir, end); end[2] += 1; if (AAS_PointAreaNum(end) != ms->reachareanum) break; } //end for if (dist1 < 80) VectorMA(reach->start, dist1, hordir, trace.endpos); // dist1 = BotGapDistance(start, hordir, ms->entitynum); // if (dist1 && dist1 <= trace.fraction * 80) VectorMA(reach->start, dist1-20, hordir, trace.endpos); // VectorSubtract(ms->origin, reach->start, dir1); dir1[2] = 0; dist1 = VectorNormalize(dir1); VectorSubtract(ms->origin, trace.endpos, dir2); dir2[2] = 0; dist2 = VectorNormalize(dir2); //if just before the reachability start if (DotProduct(dir1, dir2) < -0.8 || dist2 < 5) { //botimport.Print(PRT_MESSAGE, "between jump start and run to point\n"); hordir[0] = reach->end[0] - ms->origin[0]; hordir[1] = reach->end[1] - ms->origin[1]; hordir[2] = 0; VectorNormalize(hordir); //elemantary action jump if (dist1 < 24) EA_Jump(ms->client); else if (dist1 < 32) EA_DelayedJump(ms->client); EA_Move(ms->client, hordir, 600); // ms->jumpreach = ms->lastreachnum; } //end if else { //botimport.Print(PRT_MESSAGE, "going towards run to point\n"); hordir[0] = trace.endpos[0] - ms->origin[0]; hordir[1] = trace.endpos[1] - ms->origin[1]; hordir[2] = 0; VectorNormalize(hordir); // if (dist2 > 80) dist2 = 80; speed = 400 - (400 - 5 * dist2); EA_Move(ms->client, hordir, speed); } //end else VectorCopy(hordir, result.movedir); // return result; } //end of the function BotTravel_Jump*/ //* bot_moveresult_t BotTravel_Jump(bot_movestate_t *ms, aas_reachability_t *reach) { vec3_t hordir, dir1, dir2, start, end, runstart; // vec3_t runstart, dir1, dir2, hordir; float dist1, dist2, speed; bot_moveresult_t result; BotClearMoveResult(&result); // AAS_JumpReachRunStart(reach, runstart); //* hordir[0] = runstart[0] - reach->start[0]; hordir[1] = runstart[1] - reach->start[1]; hordir[2] = 0; VectorNormalize(hordir); // VectorCopy(reach->start, start); start[2] += 1; VectorMA(reach->start, 80, hordir, runstart); //check for a gap for (dist1 = 0; dist1 < 80; dist1 += 10) { VectorMA(start, dist1+10, hordir, end); end[2] += 1; if (AAS_PointAreaNum(end) != ms->reachareanum) break; } //end for if (dist1 < 80) VectorMA(reach->start, dist1, hordir, runstart); // VectorSubtract(ms->origin, reach->start, dir1); dir1[2] = 0; dist1 = VectorNormalize(dir1); VectorSubtract(ms->origin, runstart, dir2); dir2[2] = 0; dist2 = VectorNormalize(dir2); //if just before the reachability start if (DotProduct(dir1, dir2) < -0.8 || dist2 < 5) { // botimport.Print(PRT_MESSAGE, "between jump start and run start point\n"); hordir[0] = reach->end[0] - ms->origin[0]; hordir[1] = reach->end[1] - ms->origin[1]; hordir[2] = 0; VectorNormalize(hordir); //elemantary action jump if (dist1 < 24) EA_Jump(ms->client); else if (dist1 < 32) EA_DelayedJump(ms->client); EA_Move(ms->client, hordir, 600); // ms->jumpreach = ms->lastreachnum; } //end if else { // botimport.Print(PRT_MESSAGE, "going towards run start point\n"); hordir[0] = runstart[0] - ms->origin[0]; hordir[1] = runstart[1] - ms->origin[1]; hordir[2] = 0; VectorNormalize(hordir); // if (dist2 > 80) dist2 = 80; speed = 400 - (400 - 5 * dist2); EA_Move(ms->client, hordir, speed); } //end else VectorCopy(hordir, result.movedir); // return result; } //end of the function BotTravel_Jump*/ //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bot_moveresult_t BotFinishTravel_Jump(bot_movestate_t *ms, aas_reachability_t *reach) { vec3_t hordir, hordir2; float speed, dist; bot_moveresult_t result; BotClearMoveResult(&result); //if not jumped yet if (!ms->jumpreach) return result; //go straight to the reachability end hordir[0] = reach->end[0] - ms->origin[0]; hordir[1] = reach->end[1] - ms->origin[1]; hordir[2] = 0; dist = VectorNormalize(hordir); // hordir2[0] = reach->end[0] - reach->start[0]; hordir2[1] = reach->end[1] - reach->start[1]; hordir2[2] = 0; VectorNormalize(hordir2); // if (DotProduct(hordir, hordir2) < -0.5 && dist < 24) return result; //always use max speed when traveling through the air speed = 800; // EA_Move(ms->client, hordir, speed); VectorCopy(hordir, result.movedir); // return result; } //end of the function BotFinishTravel_Jump //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bot_moveresult_t BotTravel_Ladder(bot_movestate_t *ms, aas_reachability_t *reach) { //float dist, speed; vec3_t dir, viewdir;//, hordir; vec3_t origin = {0, 0, 0}; // vec3_t up = {0, 0, 1}; bot_moveresult_t result; BotClearMoveResult(&result); // // if ((ms->moveflags & MFL_AGAINSTLADDER)) //NOTE: not a good idea for ladders starting in water // || !(ms->moveflags & MFL_ONGROUND)) { //botimport.Print(PRT_MESSAGE, "against ladder or not on ground\n"); VectorSubtract(reach->end, ms->origin, dir); VectorNormalize(dir); //set the ideal view angles, facing the ladder up or down viewdir[0] = dir[0]; viewdir[1] = dir[1]; viewdir[2] = 3 * dir[2]; Vector2Angles(viewdir, result.ideal_viewangles); //elemantary action EA_Move(ms->client, origin, 0); EA_MoveForward(ms->client); //set movement view flag so the AI can see the view is focussed result.flags |= MOVERESULT_MOVEMENTVIEW; } //end if /* else { //botimport.Print(PRT_MESSAGE, "moving towards ladder\n"); VectorSubtract(reach->end, ms->origin, dir); //make sure the horizontal movement is large anough VectorCopy(dir, hordir); hordir[2] = 0; dist = VectorNormalize(hordir); // dir[0] = hordir[0]; dir[1] = hordir[1]; if (dir[2] > 0) dir[2] = 1; else dir[2] = -1; if (dist > 50) dist = 50; speed = 400 - (200 - 4 * dist); EA_Move(ms->client, dir, speed); } //end else*/ //save the movement direction VectorCopy(dir, result.movedir); // return result; } //end of the function BotTravel_Ladder //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bot_moveresult_t BotTravel_Teleport(bot_movestate_t *ms, aas_reachability_t *reach) { vec3_t hordir; float dist; bot_moveresult_t result; BotClearMoveResult(&result); //if the bot is being teleported if (ms->moveflags & MFL_TELEPORTED) return result; //walk straight to center of the teleporter VectorSubtract(reach->start, ms->origin, hordir); if (!(ms->moveflags & MFL_SWIMMING)) hordir[2] = 0; dist = VectorNormalize(hordir); // BotCheckBlocked(ms, hordir, qtrue, &result); if (dist < 30) EA_Move(ms->client, hordir, 200); else EA_Move(ms->client, hordir, 400); if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW; VectorCopy(hordir, result.movedir); return result; } //end of the function BotTravel_Teleport //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bot_moveresult_t BotTravel_Elevator(bot_movestate_t *ms, aas_reachability_t *reach) { vec3_t dir, dir1, dir2, hordir, bottomcenter; float dist, dist1, dist2, speed; bot_moveresult_t result; BotClearMoveResult(&result); //if standing on the plat if (BotOnMover(ms->origin, ms->entitynum, reach)) { #ifdef DEBUG_ELEVATOR botimport.Print(PRT_MESSAGE, "bot on elevator\n"); #endif //DEBUG_ELEVATOR //if vertically not too far from the end point if (fabs(ms->origin[2] - reach->end[2]) < sv_maxbarrier->value) { #ifdef DEBUG_ELEVATOR botimport.Print(PRT_MESSAGE, "bot moving to end\n"); #endif //DEBUG_ELEVATOR //move to the end point VectorSubtract(reach->end, ms->origin, hordir); hordir[2] = 0; VectorNormalize(hordir); if (!BotCheckBarrierJump(ms, hordir, 100)) { EA_Move(ms->client, hordir, 400); } //end if VectorCopy(hordir, result.movedir); } //end else //if not really close to the center of the elevator else { MoverBottomCenter(reach, bottomcenter); VectorSubtract(bottomcenter, ms->origin, hordir); hordir[2] = 0; dist = VectorNormalize(hordir); // if (dist > 10) { #ifdef DEBUG_ELEVATOR botimport.Print(PRT_MESSAGE, "bot moving to center\n"); #endif //DEBUG_ELEVATOR //move to the center of the plat if (dist > 100) dist = 100; speed = 400 - (400 - 4 * dist); // EA_Move(ms->client, hordir, speed); VectorCopy(hordir, result.movedir); } //end if } //end else } //end if else { #ifdef DEBUG_ELEVATOR botimport.Print(PRT_MESSAGE, "bot not on elevator\n"); #endif //DEBUG_ELEVATOR //if very near the reachability end VectorSubtract(reach->end, ms->origin, dir); dist = VectorLength(dir); if (dist < 64) { if (dist > 60) dist = 60; speed = 360 - (360 - 6 * dist); // if ((ms->moveflags & MFL_SWIMMING) || !BotCheckBarrierJump(ms, dir, 50)) { if (speed > 5) EA_Move(ms->client, dir, speed); } //end if VectorCopy(dir, result.movedir); // if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW; //stop using this reachability ms->reachability_time = 0; return result; } //end if //get direction and distance to reachability start VectorSubtract(reach->start, ms->origin, dir1); if (!(ms->moveflags & MFL_SWIMMING)) dir1[2] = 0; dist1 = VectorNormalize(dir1); //if the elevator isn't down if (!MoverDown(reach)) { #ifdef DEBUG_ELEVATOR botimport.Print(PRT_MESSAGE, "elevator not down\n"); #endif //DEBUG_ELEVATOR dist = dist1; VectorCopy(dir1, dir); // BotCheckBlocked(ms, dir, qfalse, &result); // if (dist > 60) dist = 60; speed = 360 - (360 - 6 * dist); // if (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50)) { if (speed > 5) EA_Move(ms->client, dir, speed); } //end if VectorCopy(dir, result.movedir); // if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW; //this isn't a failure... just wait till the elevator comes down result.type = RESULTTYPE_ELEVATORUP; result.flags |= MOVERESULT_WAITING; return result; } //end if //get direction and distance to elevator bottom center MoverBottomCenter(reach, bottomcenter); VectorSubtract(bottomcenter, ms->origin, dir2); if (!(ms->moveflags & MFL_SWIMMING)) dir2[2] = 0; dist2 = VectorNormalize(dir2); //if very close to the reachability start or //closer to the elevator center or //between reachability start and elevator center if (dist1 < 20 || dist2 < dist1 || DotProduct(dir1, dir2) < 0) { #ifdef DEBUG_ELEVATOR botimport.Print(PRT_MESSAGE, "bot moving to center\n"); #endif //DEBUG_ELEVATOR dist = dist2; VectorCopy(dir2, dir); } //end if else //closer to the reachability start { #ifdef DEBUG_ELEVATOR botimport.Print(PRT_MESSAGE, "bot moving to start\n"); #endif //DEBUG_ELEVATOR dist = dist1; VectorCopy(dir1, dir); } //end else // BotCheckBlocked(ms, dir, qfalse, &result); // if (dist > 60) dist = 60; speed = 400 - (400 - 6 * dist); // if (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50)) { EA_Move(ms->client, dir, speed); } //end if VectorCopy(dir, result.movedir); // if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW; } //end else return result; } //end of the function BotTravel_Elevator //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bot_moveresult_t BotFinishTravel_Elevator(bot_movestate_t *ms, aas_reachability_t *reach) { vec3_t bottomcenter, bottomdir, topdir; bot_moveresult_t result; BotClearMoveResult(&result); // MoverBottomCenter(reach, bottomcenter); VectorSubtract(bottomcenter, ms->origin, bottomdir); // VectorSubtract(reach->end, ms->origin, topdir); // if (fabs(bottomdir[2]) < fabs(topdir[2])) { VectorNormalize(bottomdir); EA_Move(ms->client, bottomdir, 300); } //end if else { VectorNormalize(topdir); EA_Move(ms->client, topdir, 300); } //end else return result; } //end of the function BotFinishTravel_Elevator //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotFuncBobStartEnd(aas_reachability_t *reach, vec3_t start, vec3_t end, vec3_t origin) { int spawnflags, modelnum; vec3_t mins, maxs, mid, angles = {0, 0, 0}; int num0, num1; modelnum = reach->facenum & 0x0000FFFF; if (!AAS_OriginOfMoverWithModelNum(modelnum, origin)) { botimport.Print(PRT_MESSAGE, "BotFuncBobStartEnd: no entity with model %d\n", modelnum); VectorSet(start, 0, 0, 0); VectorSet(end, 0, 0, 0); return; } //end if AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, NULL); VectorAdd(mins, maxs, mid); VectorScale(mid, 0.5, mid); VectorCopy(mid, start); VectorCopy(mid, end); spawnflags = reach->facenum >> 16; num0 = reach->edgenum >> 16; if (num0 > 0x00007FFF) num0 |= 0xFFFF0000; num1 = reach->edgenum & 0x0000FFFF; if (num1 > 0x00007FFF) num1 |= 0xFFFF0000; if (spawnflags & 1) { start[0] = num0; end[0] = num1; // origin[0] += mid[0]; origin[1] = mid[1]; origin[2] = mid[2]; } //end if else if (spawnflags & 2) { start[1] = num0; end[1] = num1; // origin[0] = mid[0]; origin[1] += mid[1]; origin[2] = mid[2]; } //end else if else { start[2] = num0; end[2] = num1; // origin[0] = mid[0]; origin[1] = mid[1]; origin[2] += mid[2]; } //end else } //end of the function BotFuncBobStartEnd //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bot_moveresult_t BotTravel_FuncBobbing(bot_movestate_t *ms, aas_reachability_t *reach) { vec3_t dir, dir1, dir2, hordir, bottomcenter, bob_start, bob_end, bob_origin; float dist, dist1, dist2, speed; bot_moveresult_t result; BotClearMoveResult(&result); // BotFuncBobStartEnd(reach, bob_start, bob_end, bob_origin); //if standing ontop of the func_bobbing if (BotOnMover(ms->origin, ms->entitynum, reach)) { #ifdef DEBUG_FUNCBOB botimport.Print(PRT_MESSAGE, "bot on func_bobbing\n"); #endif //if near end point of reachability VectorSubtract(bob_origin, bob_end, dir); if (VectorLength(dir) < 24) { #ifdef DEBUG_FUNCBOB botimport.Print(PRT_MESSAGE, "bot moving to reachability end\n"); #endif //move to the end point VectorSubtract(reach->end, ms->origin, hordir); hordir[2] = 0; VectorNormalize(hordir); if (!BotCheckBarrierJump(ms, hordir, 100)) { EA_Move(ms->client, hordir, 400); } //end if VectorCopy(hordir, result.movedir); } //end else //if not really close to the center of the elevator else { MoverBottomCenter(reach, bottomcenter); VectorSubtract(bottomcenter, ms->origin, hordir); hordir[2] = 0; dist = VectorNormalize(hordir); // if (dist > 10) { #ifdef DEBUG_FUNCBOB botimport.Print(PRT_MESSAGE, "bot moving to func_bobbing center\n"); #endif //move to the center of the plat if (dist > 100) dist = 100; speed = 400 - (400 - 4 * dist); // EA_Move(ms->client, hordir, speed); VectorCopy(hordir, result.movedir); } //end if } //end else } //end if else { #ifdef DEBUG_FUNCBOB botimport.Print(PRT_MESSAGE, "bot not ontop of func_bobbing\n"); #endif //if very near the reachability end VectorSubtract(reach->end, ms->origin, dir); dist = VectorLength(dir); if (dist < 64) { #ifdef DEBUG_FUNCBOB botimport.Print(PRT_MESSAGE, "bot moving to end\n"); #endif if (dist > 60) dist = 60; speed = 360 - (360 - 6 * dist); //if swimming or no barrier jump if ((ms->moveflags & MFL_SWIMMING) || !BotCheckBarrierJump(ms, dir, 50)) { if (speed > 5) EA_Move(ms->client, dir, speed); } //end if VectorCopy(dir, result.movedir); // if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW; //stop using this reachability ms->reachability_time = 0; return result; } //end if //get direction and distance to reachability start VectorSubtract(reach->start, ms->origin, dir1); if (!(ms->moveflags & MFL_SWIMMING)) dir1[2] = 0; dist1 = VectorNormalize(dir1); //if func_bobbing is Not it's start position VectorSubtract(bob_origin, bob_start, dir); if (VectorLength(dir) > 16) { #ifdef DEBUG_FUNCBOB botimport.Print(PRT_MESSAGE, "func_bobbing not at start\n"); #endif dist = dist1; VectorCopy(dir1, dir); // BotCheckBlocked(ms, dir, qfalse, &result); // if (dist > 60) dist = 60; speed = 360 - (360 - 6 * dist); // if (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50)) { if (speed > 5) EA_Move(ms->client, dir, speed); } //end if VectorCopy(dir, result.movedir); // if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW; //this isn't a failure... just wait till the func_bobbing arrives result.type = RESULTTYPE_WAITFORFUNCBOBBING; result.flags |= MOVERESULT_WAITING; return result; } //end if //get direction and distance to func_bob bottom center MoverBottomCenter(reach, bottomcenter); VectorSubtract(bottomcenter, ms->origin, dir2); if (!(ms->moveflags & MFL_SWIMMING)) dir2[2] = 0; dist2 = VectorNormalize(dir2); //if very close to the reachability start or //closer to the elevator center or //between reachability start and func_bobbing center if (dist1 < 20 || dist2 < dist1 || DotProduct(dir1, dir2) < 0) { #ifdef DEBUG_FUNCBOB botimport.Print(PRT_MESSAGE, "bot moving to func_bobbing center\n"); #endif dist = dist2; VectorCopy(dir2, dir); } //end if else //closer to the reachability start { #ifdef DEBUG_FUNCBOB botimport.Print(PRT_MESSAGE, "bot moving to reachability start\n"); #endif dist = dist1; VectorCopy(dir1, dir); } //end else // BotCheckBlocked(ms, dir, qfalse, &result); // if (dist > 60) dist = 60; speed = 400 - (400 - 6 * dist); // if (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50)) { EA_Move(ms->client, dir, speed); } //end if VectorCopy(dir, result.movedir); // if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW; } //end else return result; } //end of the function BotTravel_FuncBobbing //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bot_moveresult_t BotFinishTravel_FuncBobbing(bot_movestate_t *ms, aas_reachability_t *reach) { vec3_t bob_origin, bob_start, bob_end, dir, hordir, bottomcenter; bot_moveresult_t result; float dist, speed; BotClearMoveResult(&result); // BotFuncBobStartEnd(reach, bob_start, bob_end, bob_origin); // VectorSubtract(bob_origin, bob_end, dir); dist = VectorLength(dir); //if the func_bobbing is near the end if (dist < 16) { VectorSubtract(reach->end, ms->origin, hordir); if (!(ms->moveflags & MFL_SWIMMING)) hordir[2] = 0; dist = VectorNormalize(hordir); // if (dist > 60) dist = 60; speed = 360 - (360 - 6 * dist); // if (speed > 5) EA_Move(ms->client, dir, speed); VectorCopy(dir, result.movedir); // if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW; } //end if else { MoverBottomCenter(reach, bottomcenter); VectorSubtract(bottomcenter, ms->origin, hordir); if (!(ms->moveflags & MFL_SWIMMING)) hordir[2] = 0; dist = VectorNormalize(hordir); // if (dist > 5) { //move to the center of the plat if (dist > 100) dist = 100; speed = 400 - (400 - 4 * dist); // EA_Move(ms->client, hordir, speed); VectorCopy(hordir, result.movedir); } //end if } //end else return result; } //end of the function BotFinishTravel_FuncBobbing //=========================================================================== // 0 no valid grapple hook visible // 1 the grapple hook is still flying // 2 the grapple hooked into a wall // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int GrappleState(bot_movestate_t *ms, aas_reachability_t *reach) { int i; aas_entityinfo_t entinfo; //if the grapple hook is pulling if (ms->moveflags & MFL_GRAPPLEPULL) return 2; //check for a visible grapple missile entity //or visible grapple entity for (i = AAS_NextEntity(0); i; i = AAS_NextEntity(i)) { if (AAS_EntityType(i) == (int) entitytypemissile->value) { AAS_EntityInfo(i, &entinfo); if (entinfo.weapon == (int) weapindex_grapple->value) { return 1; } //end if } //end if } //end for //no valid grapple at all return 0; } //end of the function GrappleState //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotResetGrapple(bot_movestate_t *ms) { aas_reachability_t reach; AAS_ReachabilityFromNum(ms->lastreachnum, &reach); //if not using the grapple hook reachability anymore if ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_GRAPPLEHOOK) { if ((ms->moveflags & MFL_ACTIVEGRAPPLE) || ms->grapplevisible_time) { if (offhandgrapple->value) EA_Command(ms->client, cmd_grappleoff->string); ms->moveflags &= ~MFL_ACTIVEGRAPPLE; ms->grapplevisible_time = 0; #ifdef DEBUG_GRAPPLE botimport.Print(PRT_MESSAGE, "reset grapple\n"); #endif //DEBUG_GRAPPLE } //end if } //end if } //end of the function BotResetGrapple //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bot_moveresult_t BotTravel_Grapple(bot_movestate_t *ms, aas_reachability_t *reach) { bot_moveresult_t result; float dist, speed; vec3_t dir, viewdir, org; int state, areanum; bsp_trace_t trace; #ifdef DEBUG_GRAPPLE static int debugline; if (!debugline) debugline = botimport.DebugLineCreate(); botimport.DebugLineShow(debugline, reach->start, reach->end, LINECOLOR_BLUE); #endif //DEBUG_GRAPPLE BotClearMoveResult(&result); // if (ms->moveflags & MFL_GRAPPLERESET) { if (offhandgrapple->value) EA_Command(ms->client, cmd_grappleoff->string); ms->moveflags &= ~MFL_ACTIVEGRAPPLE; return result; } //end if // if (!(int) offhandgrapple->value) { result.weapon = weapindex_grapple->value; result.flags |= MOVERESULT_MOVEMENTWEAPON; } //end if // if (ms->moveflags & MFL_ACTIVEGRAPPLE) { #ifdef DEBUG_GRAPPLE botimport.Print(PRT_MESSAGE, "BotTravel_Grapple: active grapple\n"); #endif //DEBUG_GRAPPLE // state = GrappleState(ms, reach); // VectorSubtract(reach->end, ms->origin, dir); dir[2] = 0; dist = VectorLength(dir); //if very close to the grapple end or the grappled is hooked and //the bot doesn't get any closer if (state && dist < 48) { if (ms->lastgrappledist - dist < 1) { #ifdef DEBUG_GRAPPLE botimport.Print(PRT_ERROR, "grapple normal end\n"); #endif //DEBUG_GRAPPLE if (offhandgrapple->value) EA_Command(ms->client, cmd_grappleoff->string); ms->moveflags &= ~MFL_ACTIVEGRAPPLE; ms->moveflags |= MFL_GRAPPLERESET; ms->reachability_time = 0; //end the reachability return result; } //end if } //end if //if no valid grapple at all, or the grapple hooked and the bot //isn't moving anymore else if (!state || (state == 2 && dist > ms->lastgrappledist - 2)) { if (ms->grapplevisible_time < AAS_Time() - 0.4) { #ifdef DEBUG_GRAPPLE botimport.Print(PRT_ERROR, "grapple not visible\n"); #endif //DEBUG_GRAPPLE if (offhandgrapple->value) EA_Command(ms->client, cmd_grappleoff->string); ms->moveflags &= ~MFL_ACTIVEGRAPPLE; ms->moveflags |= MFL_GRAPPLERESET; ms->reachability_time = 0; //end the reachability return result; } //end if } //end if else { ms->grapplevisible_time = AAS_Time(); } //end else // if (!(int) offhandgrapple->value) { EA_Attack(ms->client); } //end if //remember the current grapple distance ms->lastgrappledist = dist; } //end if else { #ifdef DEBUG_GRAPPLE botimport.Print(PRT_MESSAGE, "BotTravel_Grapple: inactive grapple\n"); #endif //DEBUG_GRAPPLE // ms->grapplevisible_time = AAS_Time(); // VectorSubtract(reach->start, ms->origin, dir); if (!(ms->moveflags & MFL_SWIMMING)) dir[2] = 0; VectorAdd(ms->origin, ms->viewoffset, org); VectorSubtract(reach->end, org, viewdir); // dist = VectorNormalize(dir); Vector2Angles(viewdir, result.ideal_viewangles); result.flags |= MOVERESULT_MOVEMENTVIEW; // if (dist < 5 && fabs(AngleDiff(result.ideal_viewangles[0], ms->viewangles[0])) < 2 && fabs(AngleDiff(result.ideal_viewangles[1], ms->viewangles[1])) < 2) { #ifdef DEBUG_GRAPPLE botimport.Print(PRT_MESSAGE, "BotTravel_Grapple: activating grapple\n"); #endif //DEBUG_GRAPPLE //check if the grapple missile path is clear VectorAdd(ms->origin, ms->viewoffset, org); trace = AAS_Trace(org, NULL, NULL, reach->end, ms->entitynum, CONTENTS_SOLID); VectorSubtract(reach->end, trace.endpos, dir); if (VectorLength(dir) > 16) { result.failure = qtrue; return result; } //end if //activate the grapple if (offhandgrapple->value) { EA_Command(ms->client, cmd_grappleon->string); } //end if else { EA_Attack(ms->client); } //end else ms->moveflags |= MFL_ACTIVEGRAPPLE; ms->lastgrappledist = 999999; } //end if else { if (dist < 70) speed = 300 - (300 - 4 * dist); else speed = 400; // BotCheckBlocked(ms, dir, qtrue, &result); //elemantary action move in direction EA_Move(ms->client, dir, speed); VectorCopy(dir, result.movedir); } //end else //if in another area before actually grappling areanum = AAS_PointAreaNum(ms->origin); if (areanum && areanum != ms->reachareanum) ms->reachability_time = 0; } //end else return result; } //end of the function BotTravel_Grapple //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bot_moveresult_t BotTravel_RocketJump(bot_movestate_t *ms, aas_reachability_t *reach) { vec3_t hordir; float dist, speed; bot_moveresult_t result; //botimport.Print(PRT_MESSAGE, "BotTravel_RocketJump: bah\n"); BotClearMoveResult(&result); // hordir[0] = reach->start[0] - ms->origin[0]; hordir[1] = reach->start[1] - ms->origin[1]; hordir[2] = 0; // dist = VectorNormalize(hordir); //look in the movement direction Vector2Angles(hordir, result.ideal_viewangles); //look straight down result.ideal_viewangles[PITCH] = 90; // if (dist < 5 && fabs(AngleDiff(result.ideal_viewangles[0], ms->viewangles[0])) < 5 && fabs(AngleDiff(result.ideal_viewangles[1], ms->viewangles[1])) < 5) { //botimport.Print(PRT_MESSAGE, "between jump start and run start point\n"); hordir[0] = reach->end[0] - ms->origin[0]; hordir[1] = reach->end[1] - ms->origin[1]; hordir[2] = 0; VectorNormalize(hordir); //elemantary action jump EA_Jump(ms->client); EA_Attack(ms->client); EA_Move(ms->client, hordir, 800); // ms->jumpreach = ms->lastreachnum; } //end if else { if (dist > 80) dist = 80; speed = 400 - (400 - 5 * dist); EA_Move(ms->client, hordir, speed); } //end else //look in the movement direction Vector2Angles(hordir, result.ideal_viewangles); //look straight down result.ideal_viewangles[PITCH] = 90; //set the view angles directly EA_View(ms->client, result.ideal_viewangles); //view is important for the movment result.flags |= MOVERESULT_MOVEMENTVIEWSET; //select the rocket launcher EA_SelectWeapon(ms->client, (int) weapindex_rocketlauncher->value); //weapon is used for movement result.weapon = (int) weapindex_rocketlauncher->value; result.flags |= MOVERESULT_MOVEMENTWEAPON; // VectorCopy(hordir, result.movedir); // return result; } //end of the function BotTravel_RocketJump //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bot_moveresult_t BotTravel_BFGJump(bot_movestate_t *ms, aas_reachability_t *reach) { vec3_t hordir; float dist, speed; bot_moveresult_t result; //botimport.Print(PRT_MESSAGE, "BotTravel_BFGJump: bah\n"); BotClearMoveResult(&result); // hordir[0] = reach->start[0] - ms->origin[0]; hordir[1] = reach->start[1] - ms->origin[1]; hordir[2] = 0; // dist = VectorNormalize(hordir); // if (dist < 5 && fabs(AngleDiff(result.ideal_viewangles[0], ms->viewangles[0])) < 5 && fabs(AngleDiff(result.ideal_viewangles[1], ms->viewangles[1])) < 5) { //botimport.Print(PRT_MESSAGE, "between jump start and run start point\n"); hordir[0] = reach->end[0] - ms->origin[0]; hordir[1] = reach->end[1] - ms->origin[1]; hordir[2] = 0; VectorNormalize(hordir); //elemantary action jump EA_Jump(ms->client); EA_Attack(ms->client); EA_Move(ms->client, hordir, 800); // ms->jumpreach = ms->lastreachnum; } //end if else { if (dist > 80) dist = 80; speed = 400 - (400 - 5 * dist); EA_Move(ms->client, hordir, speed); } //end else //look in the movement direction Vector2Angles(hordir, result.ideal_viewangles); //look straight down result.ideal_viewangles[PITCH] = 90; //set the view angles directly EA_View(ms->client, result.ideal_viewangles); //view is important for the movment result.flags |= MOVERESULT_MOVEMENTVIEWSET; //select the rocket launcher EA_SelectWeapon(ms->client, (int) weapindex_bfg10k->value); //weapon is used for movement result.weapon = (int) weapindex_bfg10k->value; result.flags |= MOVERESULT_MOVEMENTWEAPON; // VectorCopy(hordir, result.movedir); // return result; } //end of the function BotTravel_BFGJump //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bot_moveresult_t BotFinishTravel_WeaponJump(bot_movestate_t *ms, aas_reachability_t *reach) { vec3_t hordir; float speed; bot_moveresult_t result; BotClearMoveResult(&result); //if not jumped yet if (!ms->jumpreach) return result; /* //go straight to the reachability end hordir[0] = reach->end[0] - ms->origin[0]; hordir[1] = reach->end[1] - ms->origin[1]; hordir[2] = 0; VectorNormalize(hordir); //always use max speed when traveling through the air EA_Move(ms->client, hordir, 800); VectorCopy(hordir, result.movedir); */ // if (!BotAirControl(ms->origin, ms->velocity, reach->end, hordir, &speed)) { //go straight to the reachability end VectorSubtract(reach->end, ms->origin, hordir); hordir[2] = 0; VectorNormalize(hordir); speed = 400; } //end if // EA_Move(ms->client, hordir, speed); VectorCopy(hordir, result.movedir); // return result; } //end of the function BotFinishTravel_WeaponJump //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bot_moveresult_t BotTravel_JumpPad(bot_movestate_t *ms, aas_reachability_t *reach) { float dist, speed; vec3_t hordir; bot_moveresult_t result; BotClearMoveResult(&result); //first walk straight to the reachability start hordir[0] = reach->start[0] - ms->origin[0]; hordir[1] = reach->start[1] - ms->origin[1]; hordir[2] = 0; dist = VectorNormalize(hordir); // BotCheckBlocked(ms, hordir, qtrue, &result); speed = 400; //elemantary action move in direction EA_Move(ms->client, hordir, speed); VectorCopy(hordir, result.movedir); // return result; } //end of the function BotTravel_JumpPad //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bot_moveresult_t BotFinishTravel_JumpPad(bot_movestate_t *ms, aas_reachability_t *reach) { float speed; vec3_t hordir; bot_moveresult_t result; BotClearMoveResult(&result); if (!BotAirControl(ms->origin, ms->velocity, reach->end, hordir, &speed)) { hordir[0] = reach->end[0] - ms->origin[0]; hordir[1] = reach->end[1] - ms->origin[1]; hordir[2] = 0; VectorNormalize(hordir); speed = 400; } //end if BotCheckBlocked(ms, hordir, qtrue, &result); //elemantary action move in direction EA_Move(ms->client, hordir, speed); VectorCopy(hordir, result.movedir); // return result; } //end of the function BotFinishTravel_JumpPad //=========================================================================== // time before the reachability times out // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotReachabilityTime(aas_reachability_t *reach) { switch(reach->traveltype & TRAVELTYPE_MASK) { case TRAVEL_WALK: return 5; case TRAVEL_CROUCH: return 5; case TRAVEL_BARRIERJUMP: return 5; case TRAVEL_LADDER: return 6; case TRAVEL_WALKOFFLEDGE: return 5; case TRAVEL_JUMP: return 5; case TRAVEL_SWIM: return 5; case TRAVEL_WATERJUMP: return 5; case TRAVEL_TELEPORT: return 5; case TRAVEL_ELEVATOR: return 10; case TRAVEL_GRAPPLEHOOK: return 8; case TRAVEL_ROCKETJUMP: return 6; case TRAVEL_BFGJUMP: return 6; case TRAVEL_JUMPPAD: return 10; case TRAVEL_FUNCBOB: return 10; default: { botimport.Print(PRT_ERROR, "travel type %d not implemented yet\n", reach->traveltype); return 8; } //end case } //end switch } //end of the function BotReachabilityTime //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bot_moveresult_t BotMoveInGoalArea(bot_movestate_t *ms, bot_goal_t *goal) { bot_moveresult_t result; vec3_t dir; float dist, speed; #ifdef DEBUG //botimport.Print(PRT_MESSAGE, "%s: moving straight to goal\n", ClientName(ms->entitynum-1)); //AAS_ClearShownDebugLines(); //AAS_DebugLine(ms->origin, goal->origin, LINECOLOR_RED); #endif //DEBUG BotClearMoveResult(&result); //walk straight to the goal origin dir[0] = goal->origin[0] - ms->origin[0]; dir[1] = goal->origin[1] - ms->origin[1]; if (ms->moveflags & MFL_SWIMMING) { dir[2] = goal->origin[2] - ms->origin[2]; result.traveltype = TRAVEL_SWIM; } //end if else { dir[2] = 0; result.traveltype = TRAVEL_WALK; } //endif // dist = VectorNormalize(dir); if (dist > 100) dist = 100; speed = 400 - (400 - 4 * dist); if (speed < 10) speed = 0; // BotCheckBlocked(ms, dir, qtrue, &result); //elemantary action move in direction EA_Move(ms->client, dir, speed); VectorCopy(dir, result.movedir); // if (ms->moveflags & MFL_SWIMMING) { Vector2Angles(dir, result.ideal_viewangles); result.flags |= MOVERESULT_SWIMVIEW; } //end if //if (!debugline) debugline = botimport.DebugLineCreate(); //botimport.DebugLineShow(debugline, ms->origin, goal->origin, LINECOLOR_BLUE); // ms->lastreachnum = 0; ms->lastareanum = 0; ms->lastgoalareanum = goal->areanum; VectorCopy(ms->origin, ms->lastorigin); // return result; } //end of the function BotMoveInGoalArea //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotMoveToGoal(bot_moveresult_t *result, int movestate, bot_goal_t *goal, int travelflags) { int reachnum, lastreachnum, foundjumppad, ent, resultflags; aas_reachability_t reach, lastreach; bot_movestate_t *ms; //vec3_t mins, maxs, up = {0, 0, 1}; //bsp_trace_t trace; //static int debugline; BotClearMoveResult(result); // ms = BotMoveStateFromHandle(movestate); if (!ms) return; //reset the grapple before testing if the bot has a valid goal //because the bot could loose all it's goals when stuck to a wall BotResetGrapple(ms); // if (!goal) { #ifdef DEBUG botimport.Print(PRT_MESSAGE, "client %d: movetogoal -> no goal\n", ms->client); #endif //DEBUG result->failure = qtrue; return; } //end if //botimport.Print(PRT_MESSAGE, "numavoidreach = %d\n", ms->numavoidreach); //remove some of the move flags ms->moveflags &= ~(MFL_SWIMMING|MFL_AGAINSTLADDER); //set some of the move flags //NOTE: the MFL_ONGROUND flag is also set in the higher AI if (AAS_OnGround(ms->origin, ms->presencetype, ms->entitynum)) ms->moveflags |= MFL_ONGROUND; // if (ms->moveflags & MFL_ONGROUND) { int modeltype, modelnum; ent = BotOnTopOfEntity(ms); if (ent != -1) { modelnum = AAS_EntityModelindex(ent); if (modelnum >= 0 && modelnum < MAX_MODELS) { modeltype = modeltypes[modelnum]; if (modeltype == MODELTYPE_FUNC_PLAT) { AAS_ReachabilityFromNum(ms->lastreachnum, &reach); //if the bot is Not using the elevator if ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_ELEVATOR || //NOTE: the face number is the plat model number (reach.facenum & 0x0000FFFF) != modelnum) { reachnum = AAS_NextModelReachability(0, modelnum); if (reachnum) { //botimport.Print(PRT_MESSAGE, "client %d: accidentally ended up on func_plat\n", ms->client); AAS_ReachabilityFromNum(reachnum, &reach); ms->lastreachnum = reachnum; ms->reachability_time = AAS_Time() + BotReachabilityTime(&reach); } //end if else { if (bot_developer) { botimport.Print(PRT_MESSAGE, "client %d: on func_plat without reachability\n", ms->client); } //end if result->blocked = qtrue; result->blockentity = ent; result->flags |= MOVERESULT_ONTOPOFOBSTACLE; return; } //end else } //end if result->flags |= MOVERESULT_ONTOPOF_ELEVATOR; } //end if else if (modeltype == MODELTYPE_FUNC_BOB) { AAS_ReachabilityFromNum(ms->lastreachnum, &reach); //if the bot is Not using the func bobbing if ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_FUNCBOB || //NOTE: the face number is the func_bobbing model number (reach.facenum & 0x0000FFFF) != modelnum) { reachnum = AAS_NextModelReachability(0, modelnum); if (reachnum) { //botimport.Print(PRT_MESSAGE, "client %d: accidentally ended up on func_bobbing\n", ms->client); AAS_ReachabilityFromNum(reachnum, &reach); ms->lastreachnum = reachnum; ms->reachability_time = AAS_Time() + BotReachabilityTime(&reach); } //end if else { if (bot_developer) { botimport.Print(PRT_MESSAGE, "client %d: on func_bobbing without reachability\n", ms->client); } //end if result->blocked = qtrue; result->blockentity = ent; result->flags |= MOVERESULT_ONTOPOFOBSTACLE; return; } //end else } //end if result->flags |= MOVERESULT_ONTOPOF_FUNCBOB; } //end if else if (modeltype == MODELTYPE_FUNC_STATIC || modeltype == MODELTYPE_FUNC_DOOR) { // check if ontop of a door bridge ? ms->areanum = BotFuzzyPointReachabilityArea(ms->origin); // if not in a reachability area if (!AAS_AreaReachability(ms->areanum)) { result->blocked = qtrue; result->blockentity = ent; result->flags |= MOVERESULT_ONTOPOFOBSTACLE; return; } //end if } //end else if else { result->blocked = qtrue; result->blockentity = ent; result->flags |= MOVERESULT_ONTOPOFOBSTACLE; return; } //end else } //end if } //end if } //end if //if swimming if (AAS_Swimming(ms->origin)) ms->moveflags |= MFL_SWIMMING; //if against a ladder if (AAS_AgainstLadder(ms->origin)) ms->moveflags |= MFL_AGAINSTLADDER; //if the bot is on the ground, swimming or against a ladder if (ms->moveflags & (MFL_ONGROUND|MFL_SWIMMING|MFL_AGAINSTLADDER)) { //botimport.Print(PRT_MESSAGE, "%s: onground, swimming or against ladder\n", ClientName(ms->entitynum-1)); // AAS_ReachabilityFromNum(ms->lastreachnum, &lastreach); //reachability area the bot is in //ms->areanum = BotReachabilityArea(ms->origin, ((lastreach.traveltype & TRAVELTYPE_MASK) != TRAVEL_ELEVATOR)); ms->areanum = BotFuzzyPointReachabilityArea(ms->origin); // if ( !ms->areanum ) { result->failure = qtrue; result->blocked = qtrue; result->blockentity = 0; result->type = RESULTTYPE_INSOLIDAREA; return; } //end if //if the bot is in the goal area if (ms->areanum == goal->areanum) { *result = BotMoveInGoalArea(ms, goal); return; } //end if //assume we can use the reachability from the last frame reachnum = ms->lastreachnum; //if there is a last reachability if (reachnum) { AAS_ReachabilityFromNum(reachnum, &reach); //check if the reachability is still valid if (!(AAS_TravelFlagForType(reach.traveltype) & travelflags)) { reachnum = 0; } //end if //special grapple hook case else if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_GRAPPLEHOOK) { if (ms->reachability_time < AAS_Time() || (ms->moveflags & MFL_GRAPPLERESET)) { reachnum = 0; } //end if } //end if //special elevator case else if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_ELEVATOR || (reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_FUNCBOB) { if ((result->flags & MOVERESULT_ONTOPOF_FUNCBOB) || (result->flags & MOVERESULT_ONTOPOF_FUNCBOB)) { ms->reachability_time = AAS_Time() + 5; } //end if //if the bot was going for an elevator and reached the reachability area if (ms->areanum == reach.areanum || ms->reachability_time < AAS_Time()) { reachnum = 0; } //end if } //end if else { #ifdef DEBUG if (bot_developer) { if (ms->reachability_time < AAS_Time()) { botimport.Print(PRT_MESSAGE, "client %d: reachability timeout in ", ms->client); AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK); botimport.Print(PRT_MESSAGE, "\n"); } //end if /* if (ms->lastareanum != ms->areanum) { botimport.Print(PRT_MESSAGE, "changed from area %d to %d\n", ms->lastareanum, ms->areanum); } //end if*/ } //end if #endif //DEBUG //if the goal area changed or the reachability timed out //or the area changed if (ms->lastgoalareanum != goal->areanum || ms->reachability_time < AAS_Time() || ms->lastareanum != ms->areanum) { reachnum = 0; //botimport.Print(PRT_MESSAGE, "area change or timeout\n"); } //end else if } //end else } //end if resultflags = 0; //if the bot needs a new reachability if (!reachnum) { //if the area has no reachability links if (!AAS_AreaReachability(ms->areanum)) { #ifdef DEBUG if (bot_developer) { botimport.Print(PRT_MESSAGE, "area %d no reachability\n", ms->areanum); } //end if #endif //DEBUG } //end if //get a new reachability leading towards the goal reachnum = BotGetReachabilityToGoal(ms->origin, ms->areanum, ms->lastgoalareanum, ms->lastareanum, ms->avoidreach, ms->avoidreachtimes, ms->avoidreachtries, goal, travelflags, travelflags, ms->avoidspots, ms->numavoidspots, &resultflags); //the area number the reachability starts in ms->reachareanum = ms->areanum; //reset some state variables ms->jumpreach = 0; //for TRAVEL_JUMP ms->moveflags &= ~MFL_GRAPPLERESET; //for TRAVEL_GRAPPLEHOOK //if there is a reachability to the goal if (reachnum) { AAS_ReachabilityFromNum(reachnum, &reach); //set a timeout for this reachability ms->reachability_time = AAS_Time() + BotReachabilityTime(&reach); // #ifdef AVOIDREACH //add the reachability to the reachabilities to avoid for a while BotAddToAvoidReach(ms, reachnum, AVOIDREACH_TIME); #endif //AVOIDREACH } //end if #ifdef DEBUG else if (bot_developer) { botimport.Print(PRT_MESSAGE, "goal not reachable\n"); Com_Memset(&reach, 0, sizeof(aas_reachability_t)); //make compiler happy } //end else if (bot_developer) { //if still going for the same goal if (ms->lastgoalareanum == goal->areanum) { if (ms->lastareanum == reach.areanum) { botimport.Print(PRT_MESSAGE, "same goal, going back to previous area\n"); } //end if } //end if } //end if #endif //DEBUG } //end else // ms->lastreachnum = reachnum; ms->lastgoalareanum = goal->areanum; ms->lastareanum = ms->areanum; //if the bot has a reachability if (reachnum) { //get the reachability from the number AAS_ReachabilityFromNum(reachnum, &reach); result->traveltype = reach.traveltype; // #ifdef DEBUG_AI_MOVE AAS_ClearShownDebugLines(); AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK); AAS_ShowReachability(&reach); #endif //DEBUG_AI_MOVE // #ifdef DEBUG //botimport.Print(PRT_MESSAGE, "client %d: ", ms->client); //AAS_PrintTravelType(reach.traveltype); //botimport.Print(PRT_MESSAGE, "\n"); #endif //DEBUG switch(reach.traveltype & TRAVELTYPE_MASK) { case TRAVEL_WALK: *result = BotTravel_Walk(ms, &reach); break; case TRAVEL_CROUCH: *result = BotTravel_Crouch(ms, &reach); break; case TRAVEL_BARRIERJUMP: *result = BotTravel_BarrierJump(ms, &reach); break; case TRAVEL_LADDER: *result = BotTravel_Ladder(ms, &reach); break; case TRAVEL_WALKOFFLEDGE: *result = BotTravel_WalkOffLedge(ms, &reach); break; case TRAVEL_JUMP: *result = BotTravel_Jump(ms, &reach); break; case TRAVEL_SWIM: *result = BotTravel_Swim(ms, &reach); break; case TRAVEL_WATERJUMP: *result = BotTravel_WaterJump(ms, &reach); break; case TRAVEL_TELEPORT: *result = BotTravel_Teleport(ms, &reach); break; case TRAVEL_ELEVATOR: *result = BotTravel_Elevator(ms, &reach); break; case TRAVEL_GRAPPLEHOOK: *result = BotTravel_Grapple(ms, &reach); break; case TRAVEL_ROCKETJUMP: *result = BotTravel_RocketJump(ms, &reach); break; case TRAVEL_BFGJUMP: *result = BotTravel_BFGJump(ms, &reach); break; case TRAVEL_JUMPPAD: *result = BotTravel_JumpPad(ms, &reach); break; case TRAVEL_FUNCBOB: *result = BotTravel_FuncBobbing(ms, &reach); break; default: { botimport.Print(PRT_FATAL, "travel type %d not implemented yet\n", (reach.traveltype & TRAVELTYPE_MASK)); break; } //end case } //end switch result->traveltype = reach.traveltype; result->flags |= resultflags; } //end if else { result->failure = qtrue; result->flags |= resultflags; Com_Memset(&reach, 0, sizeof(aas_reachability_t)); } //end else #ifdef DEBUG if (bot_developer) { if (result->failure) { botimport.Print(PRT_MESSAGE, "client %d: movement failure in ", ms->client); AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK); botimport.Print(PRT_MESSAGE, "\n"); } //end if } //end if #endif //DEBUG } //end if else { int i, numareas, areas[16]; vec3_t end; //special handling of jump pads when the bot uses a jump pad without knowing it foundjumppad = qfalse; VectorMA(ms->origin, -2 * ms->thinktime, ms->velocity, end); numareas = AAS_TraceAreas(ms->origin, end, areas, NULL, 16); for (i = numareas-1; i >= 0; i--) { if (AAS_AreaJumpPad(areas[i])) { //botimport.Print(PRT_MESSAGE, "client %d used a jumppad without knowing, area %d\n", ms->client, areas[i]); foundjumppad = qtrue; lastreachnum = BotGetReachabilityToGoal(end, areas[i], ms->lastgoalareanum, ms->lastareanum, ms->avoidreach, ms->avoidreachtimes, ms->avoidreachtries, goal, travelflags, TFL_JUMPPAD, ms->avoidspots, ms->numavoidspots, NULL); if (lastreachnum) { ms->lastreachnum = lastreachnum; ms->lastareanum = areas[i]; //botimport.Print(PRT_MESSAGE, "found jumppad reachability\n"); break; } //end if else { for (lastreachnum = AAS_NextAreaReachability(areas[i], 0); lastreachnum; lastreachnum = AAS_NextAreaReachability(areas[i], lastreachnum)) { //get the reachability from the number AAS_ReachabilityFromNum(lastreachnum, &reach); if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMPPAD) { ms->lastreachnum = lastreachnum; ms->lastareanum = areas[i]; //botimport.Print(PRT_MESSAGE, "found jumppad reachability hard!!\n"); break; } //end if } //end for if (lastreachnum) break; } //end else } //end if } //end for if (bot_developer) { //if a jumppad is found with the trace but no reachability is found if (foundjumppad && !ms->lastreachnum) { botimport.Print(PRT_MESSAGE, "client %d didn't find jumppad reachability\n", ms->client); } //end if } //end if // if (ms->lastreachnum) { //botimport.Print(PRT_MESSAGE, "%s: NOT onground, swimming or against ladder\n", ClientName(ms->entitynum-1)); AAS_ReachabilityFromNum(ms->lastreachnum, &reach); result->traveltype = reach.traveltype; #ifdef DEBUG //botimport.Print(PRT_MESSAGE, "client %d finish: ", ms->client); //AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK); //botimport.Print(PRT_MESSAGE, "\n"); #endif //DEBUG // switch(reach.traveltype & TRAVELTYPE_MASK) { case TRAVEL_WALK: *result = BotTravel_Walk(ms, &reach); break;//BotFinishTravel_Walk(ms, &reach); break; case TRAVEL_CROUCH: /*do nothing*/ break; case TRAVEL_BARRIERJUMP: *result = BotFinishTravel_BarrierJump(ms, &reach); break; case TRAVEL_LADDER: *result = BotTravel_Ladder(ms, &reach); break; case TRAVEL_WALKOFFLEDGE: *result = BotFinishTravel_WalkOffLedge(ms, &reach); break; case TRAVEL_JUMP: *result = BotFinishTravel_Jump(ms, &reach); break; case TRAVEL_SWIM: *result = BotTravel_Swim(ms, &reach); break; case TRAVEL_WATERJUMP: *result = BotFinishTravel_WaterJump(ms, &reach); break; case TRAVEL_TELEPORT: /*do nothing*/ break; case TRAVEL_ELEVATOR: *result = BotFinishTravel_Elevator(ms, &reach); break; case TRAVEL_GRAPPLEHOOK: *result = BotTravel_Grapple(ms, &reach); break; case TRAVEL_ROCKETJUMP: case TRAVEL_BFGJUMP: *result = BotFinishTravel_WeaponJump(ms, &reach); break; case TRAVEL_JUMPPAD: *result = BotFinishTravel_JumpPad(ms, &reach); break; case TRAVEL_FUNCBOB: *result = BotFinishTravel_FuncBobbing(ms, &reach); break; default: { botimport.Print(PRT_FATAL, "(last) travel type %d not implemented yet\n", (reach.traveltype & TRAVELTYPE_MASK)); break; } //end case } //end switch result->traveltype = reach.traveltype; #ifdef DEBUG if (bot_developer) { if (result->failure) { botimport.Print(PRT_MESSAGE, "client %d: movement failure in finish ", ms->client); AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK); botimport.Print(PRT_MESSAGE, "\n"); } //end if } //end if #endif //DEBUG } //end if } //end else //FIXME: is it right to do this here? if (result->blocked) ms->reachability_time -= 10 * ms->thinktime; //copy the last origin VectorCopy(ms->origin, ms->lastorigin); //return the movement result return; } //end of the function BotMoveToGoal //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotResetAvoidReach(int movestate) { bot_movestate_t *ms; ms = BotMoveStateFromHandle(movestate); if (!ms) return; Com_Memset(ms->avoidreach, 0, MAX_AVOIDREACH * sizeof(int)); Com_Memset(ms->avoidreachtimes, 0, MAX_AVOIDREACH * sizeof(float)); Com_Memset(ms->avoidreachtries, 0, MAX_AVOIDREACH * sizeof(int)); } //end of the function BotResetAvoidReach //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotResetLastAvoidReach(int movestate) { int i, latest; float latesttime; bot_movestate_t *ms; ms = BotMoveStateFromHandle(movestate); if (!ms) return; latesttime = 0; latest = 0; for (i = 0; i < MAX_AVOIDREACH; i++) { if (ms->avoidreachtimes[i] > latesttime) { latesttime = ms->avoidreachtimes[i]; latest = i; } //end if } //end for if (latesttime) { ms->avoidreachtimes[latest] = 0; if (ms->avoidreachtries[i] > 0) ms->avoidreachtries[latest]--; } //end if } //end of the function BotResetLastAvoidReach //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotResetMoveState(int movestate) { bot_movestate_t *ms; ms = BotMoveStateFromHandle(movestate); if (!ms) return; Com_Memset(ms, 0, sizeof(bot_movestate_t)); } //end of the function BotResetMoveState //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotSetupMoveAI(void) { BotSetBrushModelTypes(); sv_maxstep = LibVar("sv_step", "18"); sv_maxbarrier = LibVar("sv_maxbarrier", "32"); sv_gravity = LibVar("sv_gravity", "800"); weapindex_rocketlauncher = LibVar("weapindex_rocketlauncher", "5"); weapindex_bfg10k = LibVar("weapindex_bfg10k", "9"); weapindex_grapple = LibVar("weapindex_grapple", "10"); entitytypemissile = LibVar("entitytypemissile", "3"); offhandgrapple = LibVar("offhandgrapple", "0"); cmd_grappleon = LibVar("cmd_grappleon", "grappleon"); cmd_grappleoff = LibVar("cmd_grappleoff", "grappleoff"); return BLERR_NOERROR; } //end of the function BotSetupMoveAI //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotShutdownMoveAI(void) { int i; for (i = 1; i <= MAX_CLIENTS; i++) { if (botmovestates[i]) { FreeMemory(botmovestates[i]); botmovestates[i] = NULL; } //end if } //end for } //end of the function BotShutdownMoveAI ================================================ FILE: src/engine/botlib/be_ai_weap.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_ai_weap.c * * desc: weapon AI * * $Archive: /MissionPack/code/botlib/be_ai_weap.c $ * *****************************************************************************/ #include "../../game/q_shared.h" #include "l_libvar.h" #include "l_log.h" #include "l_memory.h" #include "l_utils.h" #include "l_script.h" #include "l_precomp.h" #include "l_struct.h" #include "aasfile.h" #include "../../game/botlib.h" #include "../../game/be_aas.h" #include "be_aas_funcs.h" #include "be_interface.h" #include "be_ai_weight.h" //fuzzy weights #include "../../game/be_ai_weap.h" //#define DEBUG_AI_WEAP //structure field offsets #define WEAPON_OFS(x) (int)(intptr_t)&(((weaponinfo_t *)0)->x) #define PROJECTILE_OFS(x) (int)(intptr_t)&(((projectileinfo_t *)0)->x) //weapon definition // bk001212 - static static fielddef_t weaponinfo_fields[] = { {"number", WEAPON_OFS(number), FT_INT}, //weapon number {"name", WEAPON_OFS(name), FT_STRING}, //name of the weapon {"level", WEAPON_OFS(level), FT_INT}, {"model", WEAPON_OFS(model), FT_STRING}, //model of the weapon {"weaponindex", WEAPON_OFS(weaponindex), FT_INT}, //index of weapon in inventory {"flags", WEAPON_OFS(flags), FT_INT}, //special flags {"projectile", WEAPON_OFS(projectile), FT_STRING}, //projectile used by the weapon {"numprojectiles", WEAPON_OFS(numprojectiles), FT_INT}, //number of projectiles {"hspread", WEAPON_OFS(hspread), FT_FLOAT}, //horizontal spread of projectiles (degrees from middle) {"vspread", WEAPON_OFS(vspread), FT_FLOAT}, //vertical spread of projectiles (degrees from middle) {"speed", WEAPON_OFS(speed), FT_FLOAT}, //speed of the projectile (0 = instant hit) {"acceleration", WEAPON_OFS(acceleration), FT_FLOAT}, //"acceleration" * time (in seconds) + "speed" = projectile speed {"recoil", WEAPON_OFS(recoil), FT_FLOAT|FT_ARRAY, 3}, //amount of recoil the player gets from the weapon {"offset", WEAPON_OFS(offset), FT_FLOAT|FT_ARRAY, 3}, //projectile start offset relative to eye and view angles {"angleoffset", WEAPON_OFS(angleoffset), FT_FLOAT|FT_ARRAY, 3},//offset of the shoot angles relative to the view angles {"extrazvelocity", WEAPON_OFS(extrazvelocity), FT_FLOAT},//extra z velocity the projectile gets {"ammoamount", WEAPON_OFS(ammoamount), FT_INT}, //ammo amount used per shot {"ammoindex", WEAPON_OFS(ammoindex), FT_INT}, //index of ammo in inventory {"activate", WEAPON_OFS(activate), FT_FLOAT}, //time it takes to select the weapon {"reload", WEAPON_OFS(reload), FT_FLOAT}, //time it takes to reload the weapon {"spinup", WEAPON_OFS(spinup), FT_FLOAT}, //time it takes before first shot {"spindown", WEAPON_OFS(spindown), FT_FLOAT}, //time it takes before weapon stops firing {NULL, 0, 0, 0} }; //projectile definition static fielddef_t projectileinfo_fields[] = { {"name", PROJECTILE_OFS(name), FT_STRING}, //name of the projectile {"model", WEAPON_OFS(model), FT_STRING}, //model of the projectile {"flags", PROJECTILE_OFS(flags), FT_INT}, //special flags {"gravity", PROJECTILE_OFS(gravity), FT_FLOAT}, //amount of gravity applied to the projectile [0,1] {"damage", PROJECTILE_OFS(damage), FT_INT}, //damage of the projectile {"radius", PROJECTILE_OFS(radius), FT_FLOAT}, //radius of damage {"visdamage", PROJECTILE_OFS(visdamage), FT_INT}, //damage of the projectile to visible entities {"damagetype", PROJECTILE_OFS(damagetype), FT_INT}, //type of damage (combination of the DAMAGETYPE_? flags) {"healthinc", PROJECTILE_OFS(healthinc), FT_INT}, //health increase the owner gets {"push", PROJECTILE_OFS(push), FT_FLOAT}, //amount a player is pushed away from the projectile impact {"detonation", PROJECTILE_OFS(detonation), FT_FLOAT}, //time before projectile explodes after fire pressed {"bounce", PROJECTILE_OFS(bounce), FT_FLOAT}, //amount the projectile bounces {"bouncefric", PROJECTILE_OFS(bouncefric), FT_FLOAT}, //amount the bounce decreases per bounce {"bouncestop", PROJECTILE_OFS(bouncestop), FT_FLOAT}, //minimum bounce value before bouncing stops //recurive projectile definition?? {NULL, 0, 0, 0} }; static structdef_t weaponinfo_struct = { sizeof(weaponinfo_t), weaponinfo_fields }; static structdef_t projectileinfo_struct = { sizeof(projectileinfo_t), projectileinfo_fields }; //weapon configuration: set of weapons with projectiles typedef struct weaponconfig_s { int numweapons; int numprojectiles; projectileinfo_t *projectileinfo; weaponinfo_t *weaponinfo; } weaponconfig_t; //the bot weapon state typedef struct bot_weaponstate_s { struct weightconfig_s *weaponweightconfig; //weapon weight configuration int *weaponweightindex; //weapon weight index } bot_weaponstate_t; static bot_weaponstate_t *botweaponstates[MAX_CLIENTS+1]; static weaponconfig_t *weaponconfig; //======================================================================== // // Parameter: - // Returns: - // Changes Globals: - //======================================================================== int BotValidWeaponNumber(int weaponnum) { if (weaponnum <= 0 || weaponnum > weaponconfig->numweapons) { botimport.Print(PRT_ERROR, "weapon number out of range\n"); return qfalse; } //end if return qtrue; } //end of the function BotValidWeaponNumber //======================================================================== // // Parameter: - // Returns: - // Changes Globals: - //======================================================================== bot_weaponstate_t *BotWeaponStateFromHandle(int handle) { if (handle <= 0 || handle > MAX_CLIENTS) { botimport.Print(PRT_FATAL, "move state handle %d out of range\n", handle); return NULL; } //end if if (!botweaponstates[handle]) { botimport.Print(PRT_FATAL, "invalid move state %d\n", handle); return NULL; } //end if return botweaponstates[handle]; } //end of the function BotWeaponStateFromHandle //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== #ifdef DEBUG_AI_WEAP void DumpWeaponConfig(weaponconfig_t *wc) { FILE *fp; int i; fp = Log_FileStruct(); if (!fp) return; for (i = 0; i < wc->numprojectiles; i++) { WriteStructure(fp, &projectileinfo_struct, (char *) &wc->projectileinfo[i]); Log_Flush(); } //end for for (i = 0; i < wc->numweapons; i++) { WriteStructure(fp, &weaponinfo_struct, (char *) &wc->weaponinfo[i]); Log_Flush(); } //end for } //end of the function DumpWeaponConfig #endif //DEBUG_AI_WEAP //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== weaponconfig_t *LoadWeaponConfig(char *filename) { int max_weaponinfo, max_projectileinfo; token_t token; char path[MAX_PATH]; int i, j; source_t *source; weaponconfig_t *wc; weaponinfo_t weaponinfo; max_weaponinfo = (int) LibVarValue("max_weaponinfo", "32"); if (max_weaponinfo < 0) { botimport.Print(PRT_ERROR, "max_weaponinfo = %d\n", max_weaponinfo); max_weaponinfo = 32; LibVarSet("max_weaponinfo", "32"); } //end if max_projectileinfo = (int) LibVarValue("max_projectileinfo", "32"); if (max_projectileinfo < 0) { botimport.Print(PRT_ERROR, "max_projectileinfo = %d\n", max_projectileinfo); max_projectileinfo = 32; LibVarSet("max_projectileinfo", "32"); } //end if strncpy(path, filename, MAX_PATH); PC_SetBaseFolder(BOTFILESBASEFOLDER); source = LoadSourceFile(path); if (!source) { botimport.Print(PRT_ERROR, "counldn't load %s\n", path); return NULL; } //end if //initialize weapon config wc = (weaponconfig_t *) GetClearedHunkMemory(sizeof(weaponconfig_t) + max_weaponinfo * sizeof(weaponinfo_t) + max_projectileinfo * sizeof(projectileinfo_t)); wc->weaponinfo = (weaponinfo_t *) ((char *) wc + sizeof(weaponconfig_t)); wc->projectileinfo = (projectileinfo_t *) ((char *) wc->weaponinfo + max_weaponinfo * sizeof(weaponinfo_t)); wc->numweapons = max_weaponinfo; wc->numprojectiles = 0; //parse the source file while(PC_ReadToken(source, &token)) { if (!strcmp(token.string, "weaponinfo")) { Com_Memset(&weaponinfo, 0, sizeof(weaponinfo_t)); if (!ReadStructure(source, &weaponinfo_struct, (char *) &weaponinfo)) { FreeMemory(wc); FreeSource(source); return NULL; } //end if if (weaponinfo.number < 0 || weaponinfo.number >= max_weaponinfo) { botimport.Print(PRT_ERROR, "weapon info number %d out of range in %s\n", weaponinfo.number, path); FreeMemory(wc); FreeSource(source); return NULL; } //end if Com_Memcpy(&wc->weaponinfo[weaponinfo.number], &weaponinfo, sizeof(weaponinfo_t)); wc->weaponinfo[weaponinfo.number].valid = qtrue; } //end if else if (!strcmp(token.string, "projectileinfo")) { if (wc->numprojectiles >= max_projectileinfo) { botimport.Print(PRT_ERROR, "more than %d projectiles defined in %s\n", max_projectileinfo, path); FreeMemory(wc); FreeSource(source); return NULL; } //end if Com_Memset(&wc->projectileinfo[wc->numprojectiles], 0, sizeof(projectileinfo_t)); if (!ReadStructure(source, &projectileinfo_struct, (char *) &wc->projectileinfo[wc->numprojectiles])) { FreeMemory(wc); FreeSource(source); return NULL; } //end if wc->numprojectiles++; } //end if else { botimport.Print(PRT_ERROR, "unknown definition %s in %s\n", token.string, path); FreeMemory(wc); FreeSource(source); return NULL; } //end else } //end while FreeSource(source); //fix up weapons for (i = 0; i < wc->numweapons; i++) { if (!wc->weaponinfo[i].valid) continue; if (!wc->weaponinfo[i].name[0]) { botimport.Print(PRT_ERROR, "weapon %d has no name in %s\n", i, path); FreeMemory(wc); return NULL; } //end if if (!wc->weaponinfo[i].projectile[0]) { botimport.Print(PRT_ERROR, "weapon %s has no projectile in %s\n", wc->weaponinfo[i].name, path); FreeMemory(wc); return NULL; } //end if //find the projectile info and copy it to the weapon info for (j = 0; j < wc->numprojectiles; j++) { if (!strcmp(wc->projectileinfo[j].name, wc->weaponinfo[i].projectile)) { Com_Memcpy(&wc->weaponinfo[i].proj, &wc->projectileinfo[j], sizeof(projectileinfo_t)); break; } //end if } //end for if (j == wc->numprojectiles) { botimport.Print(PRT_ERROR, "weapon %s uses undefined projectile in %s\n", wc->weaponinfo[i].name, path); FreeMemory(wc); return NULL; } //end if } //end for if (!wc->numweapons) botimport.Print(PRT_WARNING, "no weapon info loaded\n"); botimport.Print(PRT_MESSAGE, "loaded %s\n", path); return wc; } //end of the function LoadWeaponConfig //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int *WeaponWeightIndex(weightconfig_t *wwc, weaponconfig_t *wc) { int *index, i; //initialize item weight index index = (int *) GetClearedMemory(sizeof(int) * wc->numweapons); for (i = 0; i < wc->numweapons; i++) { index[i] = FindFuzzyWeight(wwc, wc->weaponinfo[i].name); } //end for return index; } //end of the function WeaponWeightIndex //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotFreeWeaponWeights(int weaponstate) { bot_weaponstate_t *ws; ws = BotWeaponStateFromHandle(weaponstate); if (!ws) return; if (ws->weaponweightconfig) FreeWeightConfig(ws->weaponweightconfig); if (ws->weaponweightindex) FreeMemory(ws->weaponweightindex); } //end of the function BotFreeWeaponWeights //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotLoadWeaponWeights(int weaponstate, char *filename) { bot_weaponstate_t *ws; ws = BotWeaponStateFromHandle(weaponstate); if (!ws) return BLERR_CANNOTLOADWEAPONWEIGHTS; BotFreeWeaponWeights(weaponstate); // ws->weaponweightconfig = ReadWeightConfig(filename); if (!ws->weaponweightconfig) { botimport.Print(PRT_FATAL, "couldn't load weapon config %s\n", filename); return BLERR_CANNOTLOADWEAPONWEIGHTS; } //end if if (!weaponconfig) return BLERR_CANNOTLOADWEAPONCONFIG; ws->weaponweightindex = WeaponWeightIndex(ws->weaponweightconfig, weaponconfig); return BLERR_NOERROR; } //end of the function BotLoadWeaponWeights //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotGetWeaponInfo(int weaponstate, int weapon, weaponinfo_t *weaponinfo) { bot_weaponstate_t *ws; if (!BotValidWeaponNumber(weapon)) return; ws = BotWeaponStateFromHandle(weaponstate); if (!ws) return; if (!weaponconfig) return; Com_Memcpy(weaponinfo, &weaponconfig->weaponinfo[weapon], sizeof(weaponinfo_t)); } //end of the function BotGetWeaponInfo //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotChooseBestFightWeapon(int weaponstate, int *inventory) { int i, index, bestweapon; float weight, bestweight; weaponconfig_t *wc; bot_weaponstate_t *ws; ws = BotWeaponStateFromHandle(weaponstate); if (!ws) return 0; wc = weaponconfig; if (!weaponconfig) return 0; //if the bot has no weapon weight configuration if (!ws->weaponweightconfig) return 0; bestweight = 0; bestweapon = 0; for (i = 0; i < wc->numweapons; i++) { if (!wc->weaponinfo[i].valid) continue; index = ws->weaponweightindex[i]; if (index < 0) continue; weight = FuzzyWeight(inventory, ws->weaponweightconfig, index); if (weight > bestweight) { bestweight = weight; bestweapon = i; } //end if } //end for return bestweapon; } //end of the function BotChooseBestFightWeapon //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotResetWeaponState(int weaponstate) { struct weightconfig_s *weaponweightconfig; int *weaponweightindex; bot_weaponstate_t *ws; ws = BotWeaponStateFromHandle(weaponstate); if (!ws) return; weaponweightconfig = ws->weaponweightconfig; weaponweightindex = ws->weaponweightindex; //Com_Memset(ws, 0, sizeof(bot_weaponstate_t)); ws->weaponweightconfig = weaponweightconfig; ws->weaponweightindex = weaponweightindex; } //end of the function BotResetWeaponState //======================================================================== // // Parameter: - // Returns: - // Changes Globals: - //======================================================================== int BotAllocWeaponState(void) { int i; for (i = 1; i <= MAX_CLIENTS; i++) { if (!botweaponstates[i]) { botweaponstates[i] = (bot_weaponstate_t*) GetClearedMemory(sizeof(bot_weaponstate_t)); return i; } //end if } //end for return 0; } //end of the function BotAllocWeaponState //======================================================================== // // Parameter: - // Returns: - // Changes Globals: - //======================================================================== void BotFreeWeaponState(int handle) { if (handle <= 0 || handle > MAX_CLIENTS) { botimport.Print(PRT_FATAL, "move state handle %d out of range\n", handle); return; } //end if if (!botweaponstates[handle]) { botimport.Print(PRT_FATAL, "invalid move state %d\n", handle); return; } //end if BotFreeWeaponWeights(handle); FreeMemory(botweaponstates[handle]); botweaponstates[handle] = NULL; } //end of the function BotFreeWeaponState //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotSetupWeaponAI(void) { char *file; file = LibVarString("weaponconfig", "weapons.c"); weaponconfig = LoadWeaponConfig(file); if (!weaponconfig) { botimport.Print(PRT_FATAL, "couldn't load the weapon config\n"); return BLERR_CANNOTLOADWEAPONCONFIG; } //end if #ifdef DEBUG_AI_WEAP DumpWeaponConfig(weaponconfig); #endif //DEBUG_AI_WEAP // return BLERR_NOERROR; } //end of the function BotSetupWeaponAI //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotShutdownWeaponAI(void) { int i; if (weaponconfig) FreeMemory(weaponconfig); weaponconfig = NULL; for (i = 1; i <= MAX_CLIENTS; i++) { if (botweaponstates[i]) { BotFreeWeaponState(i); } //end if } //end for } //end of the function BotShutdownWeaponAI ================================================ FILE: src/engine/botlib/be_ai_weight.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_ai_weight.c * * desc: fuzzy logic * * $Archive: /MissionPack/code/botlib/be_ai_weight.c $ * *****************************************************************************/ #include "../../game/q_shared.h" #include "l_memory.h" #include "l_log.h" #include "l_utils.h" #include "l_script.h" #include "l_precomp.h" #include "l_struct.h" #include "l_libvar.h" #include "aasfile.h" #include "../../game/botlib.h" #include "../../game/be_aas.h" #include "be_aas_funcs.h" #include "be_interface.h" #include "be_ai_weight.h" #define MAX_INVENTORYVALUE 999999 #define EVALUATERECURSIVELY #define MAX_WEIGHT_FILES 128 weightconfig_t *weightFileList[MAX_WEIGHT_FILES]; //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int ReadValue(source_t *source, float *value) { token_t token; if (!PC_ExpectAnyToken(source, &token)) return qfalse; if (!strcmp(token.string, "-")) { SourceWarning(source, "negative value set to zero\n"); if (!PC_ExpectTokenType(source, TT_NUMBER, 0, &token)) return qfalse; } //end if if (token.type != TT_NUMBER) { SourceError(source, "invalid return value %s\n", token.string); return qfalse; } //end if *value = token.floatvalue; return qtrue; } //end of the function ReadValue //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int ReadFuzzyWeight(source_t *source, fuzzyseperator_t *fs) { if (PC_CheckTokenString(source, "balance")) { fs->type = WT_BALANCE; if (!PC_ExpectTokenString(source, "(")) return qfalse; if (!ReadValue(source, &fs->weight)) return qfalse; if (!PC_ExpectTokenString(source, ",")) return qfalse; if (!ReadValue(source, &fs->minweight)) return qfalse; if (!PC_ExpectTokenString(source, ",")) return qfalse; if (!ReadValue(source, &fs->maxweight)) return qfalse; if (!PC_ExpectTokenString(source, ")")) return qfalse; } //end if else { fs->type = 0; if (!ReadValue(source, &fs->weight)) return qfalse; fs->minweight = fs->weight; fs->maxweight = fs->weight; } //end if if (!PC_ExpectTokenString(source, ";")) return qfalse; return qtrue; } //end of the function ReadFuzzyWeight //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void FreeFuzzySeperators_r(fuzzyseperator_t *fs) { if (!fs) return; if (fs->child) FreeFuzzySeperators_r(fs->child); if (fs->next) FreeFuzzySeperators_r(fs->next); FreeMemory(fs); } //end of the function FreeFuzzySeperators //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void FreeWeightConfig2(weightconfig_t *config) { int i; for (i = 0; i < config->numweights; i++) { FreeFuzzySeperators_r(config->weights[i].firstseperator); if (config->weights[i].name) FreeMemory(config->weights[i].name); } //end for FreeMemory(config); } //end of the function FreeWeightConfig2 //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void FreeWeightConfig(weightconfig_t *config) { if (!LibVarGetValue("bot_reloadcharacters")) return; FreeWeightConfig2(config); } //end of the function FreeWeightConfig //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== fuzzyseperator_t *ReadFuzzySeperators_r(source_t *source) { int newindent, index, def, founddefault; token_t token; fuzzyseperator_t *fs, *lastfs, *firstfs; founddefault = qfalse; firstfs = NULL; lastfs = NULL; if (!PC_ExpectTokenString(source, "(")) return NULL; if (!PC_ExpectTokenType(source, TT_NUMBER, TT_INTEGER, &token)) return NULL; index = token.intvalue; if (!PC_ExpectTokenString(source, ")")) return NULL; if (!PC_ExpectTokenString(source, "{")) return NULL; if (!PC_ExpectAnyToken(source, &token)) return NULL; do { def = !strcmp(token.string, "default"); if (def || !strcmp(token.string, "case")) { fs = (fuzzyseperator_t *) GetClearedMemory(sizeof(fuzzyseperator_t)); fs->index = index; if (lastfs) lastfs->next = fs; else firstfs = fs; lastfs = fs; if (def) { if (founddefault) { SourceError(source, "switch already has a default\n"); FreeFuzzySeperators_r(firstfs); return NULL; } //end if fs->value = MAX_INVENTORYVALUE; founddefault = qtrue; } //end if else { if (!PC_ExpectTokenType(source, TT_NUMBER, TT_INTEGER, &token)) { FreeFuzzySeperators_r(firstfs); return NULL; } //end if fs->value = token.intvalue; } //end else if (!PC_ExpectTokenString(source, ":") || !PC_ExpectAnyToken(source, &token)) { FreeFuzzySeperators_r(firstfs); return NULL; } //end if newindent = qfalse; if (!strcmp(token.string, "{")) { newindent = qtrue; if (!PC_ExpectAnyToken(source, &token)) { FreeFuzzySeperators_r(firstfs); return NULL; } //end if } //end if if (!strcmp(token.string, "return")) { if (!ReadFuzzyWeight(source, fs)) { FreeFuzzySeperators_r(firstfs); return NULL; } //end if } //end if else if (!strcmp(token.string, "switch")) { fs->child = ReadFuzzySeperators_r(source); if (!fs->child) { FreeFuzzySeperators_r(firstfs); return NULL; } //end if } //end else if else { SourceError(source, "invalid name %s\n", token.string); return NULL; } //end else if (newindent) { if (!PC_ExpectTokenString(source, "}")) { FreeFuzzySeperators_r(firstfs); return NULL; } //end if } //end if } //end if else { FreeFuzzySeperators_r(firstfs); SourceError(source, "invalid name %s\n", token.string); return NULL; } //end else if (!PC_ExpectAnyToken(source, &token)) { FreeFuzzySeperators_r(firstfs); return NULL; } //end if } while(strcmp(token.string, "}")); // if (!founddefault) { SourceWarning(source, "switch without default\n"); fs = (fuzzyseperator_t *) GetClearedMemory(sizeof(fuzzyseperator_t)); fs->index = index; fs->value = MAX_INVENTORYVALUE; fs->weight = 0; fs->next = NULL; fs->child = NULL; if (lastfs) lastfs->next = fs; else firstfs = fs; lastfs = fs; } //end if // return firstfs; } //end of the function ReadFuzzySeperators_r //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== weightconfig_t *ReadWeightConfig(char *filename) { int newindent, avail = 0, n; token_t token; source_t *source; fuzzyseperator_t *fs; weightconfig_t *config = NULL; #ifdef DEBUG int starttime; starttime = Sys_MilliSeconds(); #endif //DEBUG if (!LibVarGetValue("bot_reloadcharacters")) { avail = -1; for( n = 0; n < MAX_WEIGHT_FILES; n++ ) { config = weightFileList[n]; if( !config ) { if( avail == -1 ) { avail = n; } //end if continue; } //end if if( strcmp( filename, config->filename ) == 0 ) { //botimport.Print( PRT_MESSAGE, "retained %s\n", filename ); return config; } //end if } //end for if( avail == -1 ) { botimport.Print( PRT_ERROR, "weightFileList was full trying to load %s\n", filename ); return NULL; } //end if } //end if PC_SetBaseFolder(BOTFILESBASEFOLDER); source = LoadSourceFile(filename); if (!source) { botimport.Print(PRT_ERROR, "counldn't load %s\n", filename); return NULL; } //end if // config = (weightconfig_t *) GetClearedMemory(sizeof(weightconfig_t)); config->numweights = 0; Q_strncpyz( config->filename, filename, sizeof(config->filename) ); //parse the item config file while(PC_ReadToken(source, &token)) { if (!strcmp(token.string, "weight")) { if (config->numweights >= MAX_WEIGHTS) { SourceWarning(source, "too many fuzzy weights\n"); break; } //end if if (!PC_ExpectTokenType(source, TT_STRING, 0, &token)) { FreeWeightConfig(config); FreeSource(source); return NULL; } //end if StripDoubleQuotes(token.string); config->weights[config->numweights].name = (char *) GetClearedMemory((int)strlen(token.string) + 1); strcpy(config->weights[config->numweights].name, token.string); if (!PC_ExpectAnyToken(source, &token)) { FreeWeightConfig(config); FreeSource(source); return NULL; } //end if newindent = qfalse; if (!strcmp(token.string, "{")) { newindent = qtrue; if (!PC_ExpectAnyToken(source, &token)) { FreeWeightConfig(config); FreeSource(source); return NULL; } //end if } //end if if (!strcmp(token.string, "switch")) { fs = ReadFuzzySeperators_r(source); if (!fs) { FreeWeightConfig(config); FreeSource(source); return NULL; } //end if config->weights[config->numweights].firstseperator = fs; } //end if else if (!strcmp(token.string, "return")) { fs = (fuzzyseperator_t *) GetClearedMemory(sizeof(fuzzyseperator_t)); fs->index = 0; fs->value = MAX_INVENTORYVALUE; fs->next = NULL; fs->child = NULL; if (!ReadFuzzyWeight(source, fs)) { FreeMemory(fs); FreeWeightConfig(config); FreeSource(source); return NULL; } //end if config->weights[config->numweights].firstseperator = fs; } //end else if else { SourceError(source, "invalid name %s\n", token.string); FreeWeightConfig(config); FreeSource(source); return NULL; } //end else if (newindent) { if (!PC_ExpectTokenString(source, "}")) { FreeWeightConfig(config); FreeSource(source); return NULL; } //end if } //end if config->numweights++; } //end if else { SourceError(source, "invalid name %s\n", token.string); FreeWeightConfig(config); FreeSource(source); return NULL; } //end else } //end while //free the source at the end of a pass FreeSource(source); //if the file was located in a pak file botimport.Print(PRT_MESSAGE, "loaded %s\n", filename); #ifdef DEBUG if (bot_developer) { botimport.Print(PRT_MESSAGE, "weights loaded in %d msec\n", Sys_MilliSeconds() - starttime); } //end if #endif //DEBUG // if (!LibVarGetValue("bot_reloadcharacters")) { weightFileList[avail] = config; } //end if // return config; } //end of the function ReadWeightConfig #if 0 //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== qboolean WriteFuzzyWeight(FILE *fp, fuzzyseperator_t *fs) { if (fs->type == WT_BALANCE) { if (fprintf(fp, " return balance(") < 0) return qfalse; if (!WriteFloat(fp, fs->weight)) return qfalse; if (fprintf(fp, ",") < 0) return qfalse; if (!WriteFloat(fp, fs->minweight)) return qfalse; if (fprintf(fp, ",") < 0) return qfalse; if (!WriteFloat(fp, fs->maxweight)) return qfalse; if (fprintf(fp, ");\n") < 0) return qfalse; } //end if else { if (fprintf(fp, " return ") < 0) return qfalse; if (!WriteFloat(fp, fs->weight)) return qfalse; if (fprintf(fp, ";\n") < 0) return qfalse; } //end else return qtrue; } //end of the function WriteFuzzyWeight //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== qboolean WriteFuzzySeperators_r(FILE *fp, fuzzyseperator_t *fs, int indent) { if (!WriteIndent(fp, indent)) return qfalse; if (fprintf(fp, "switch(%d)\n", fs->index) < 0) return qfalse; if (!WriteIndent(fp, indent)) return qfalse; if (fprintf(fp, "{\n") < 0) return qfalse; indent++; do { if (!WriteIndent(fp, indent)) return qfalse; if (fs->next) { if (fprintf(fp, "case %d:", fs->value) < 0) return qfalse; } //end if else { if (fprintf(fp, "default:") < 0) return qfalse; } //end else if (fs->child) { if (fprintf(fp, "\n") < 0) return qfalse; if (!WriteIndent(fp, indent)) return qfalse; if (fprintf(fp, "{\n") < 0) return qfalse; if (!WriteFuzzySeperators_r(fp, fs->child, indent + 1)) return qfalse; if (!WriteIndent(fp, indent)) return qfalse; if (fs->next) { if (fprintf(fp, "} //end case\n") < 0) return qfalse; } //end if else { if (fprintf(fp, "} //end default\n") < 0) return qfalse; } //end else } //end if else { if (!WriteFuzzyWeight(fp, fs)) return qfalse; } //end else fs = fs->next; } while(fs); indent--; if (!WriteIndent(fp, indent)) return qfalse; if (fprintf(fp, "} //end switch\n") < 0) return qfalse; return qtrue; } //end of the function WriteItemFuzzyWeights_r //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== qboolean WriteWeightConfig(char *filename, weightconfig_t *config) { int i; FILE *fp; weight_t *ifw; fp = fopen(filename, "wb"); if (!fp) return qfalse; for (i = 0; i < config->numweights; i++) { ifw = &config->weights[i]; if (fprintf(fp, "\nweight \"%s\"\n", ifw->name) < 0) return qfalse; if (fprintf(fp, "{\n") < 0) return qfalse; if (ifw->firstseperator->index > 0) { if (!WriteFuzzySeperators_r(fp, ifw->firstseperator, 1)) return qfalse; } //end if else { if (!WriteIndent(fp, 1)) return qfalse; if (!WriteFuzzyWeight(fp, ifw->firstseperator)) return qfalse; } //end else if (fprintf(fp, "} //end weight\n") < 0) return qfalse; } //end for fclose(fp); return qtrue; } //end of the function WriteWeightConfig #endif //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int FindFuzzyWeight(weightconfig_t *wc, char *name) { int i; for (i = 0; i < wc->numweights; i++) { if (!strcmp(wc->weights[i].name, name)) { return i; } //end if } //end if return -1; } //end of the function FindFuzzyWeight //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== float FuzzyWeight_r(int *inventory, fuzzyseperator_t *fs) { float scale, w1, w2; if (inventory[fs->index] < fs->value) { if (fs->child) return FuzzyWeight_r(inventory, fs->child); else return fs->weight; } //end if else if (fs->next) { if (inventory[fs->index] < fs->next->value) { //first weight if (fs->child) w1 = FuzzyWeight_r(inventory, fs->child); else w1 = fs->weight; //second weight if (fs->next->child) w2 = FuzzyWeight_r(inventory, fs->next->child); else w2 = fs->next->weight; //the scale factor scale = (inventory[fs->index] - fs->value) / (fs->next->value - fs->value); //scale between the two weights return scale * w1 + (1 - scale) * w2; } //end if return FuzzyWeight_r(inventory, fs->next); } //end else if return fs->weight; } //end of the function FuzzyWeight_r //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== float FuzzyWeightUndecided_r(int *inventory, fuzzyseperator_t *fs) { float scale, w1, w2; if (inventory[fs->index] < fs->value) { if (fs->child) return FuzzyWeightUndecided_r(inventory, fs->child); else return fs->minweight + random() * (fs->maxweight - fs->minweight); } //end if else if (fs->next) { if (inventory[fs->index] < fs->next->value) { //first weight if (fs->child) w1 = FuzzyWeightUndecided_r(inventory, fs->child); else w1 = fs->minweight + random() * (fs->maxweight - fs->minweight); //second weight if (fs->next->child) w2 = FuzzyWeight_r(inventory, fs->next->child); else w2 = fs->next->minweight + random() * (fs->next->maxweight - fs->next->minweight); //the scale factor scale = (inventory[fs->index] - fs->value) / (fs->next->value - fs->value); //scale between the two weights return scale * w1 + (1 - scale) * w2; } //end if return FuzzyWeightUndecided_r(inventory, fs->next); } //end else if return fs->weight; } //end of the function FuzzyWeightUndecided_r //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== float FuzzyWeight(int *inventory, weightconfig_t *wc, int weightnum) { #ifdef EVALUATERECURSIVELY return FuzzyWeight_r(inventory, wc->weights[weightnum].firstseperator); #else fuzzyseperator_t *s; s = wc->weights[weightnum].firstseperator; if (!s) return 0; while(1) { if (inventory[s->index] < s->value) { if (s->child) s = s->child; else return s->weight; } //end if else { if (s->next) s = s->next; else return s->weight; } //end else } //end if return 0; #endif } //end of the function FuzzyWeight //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== float FuzzyWeightUndecided(int *inventory, weightconfig_t *wc, int weightnum) { #ifdef EVALUATERECURSIVELY return FuzzyWeightUndecided_r(inventory, wc->weights[weightnum].firstseperator); #else fuzzyseperator_t *s; s = wc->weights[weightnum].firstseperator; if (!s) return 0; while(1) { if (inventory[s->index] < s->value) { if (s->child) s = s->child; else return s->minweight + random() * (s->maxweight - s->minweight); } //end if else { if (s->next) s = s->next; else return s->minweight + random() * (s->maxweight - s->minweight); } //end else } //end if return 0; #endif } //end of the function FuzzyWeightUndecided //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void EvolveFuzzySeperator_r(fuzzyseperator_t *fs) { if (fs->child) { EvolveFuzzySeperator_r(fs->child); } //end if else if (fs->type == WT_BALANCE) { //every once in a while an evolution leap occurs, mutation if (random() < 0.01) fs->weight += crandom() * (fs->maxweight - fs->minweight); else fs->weight += crandom() * (fs->maxweight - fs->minweight) * 0.5; //modify bounds if necesary because of mutation if (fs->weight < fs->minweight) fs->minweight = fs->weight; else if (fs->weight > fs->maxweight) fs->maxweight = fs->weight; } //end else if if (fs->next) EvolveFuzzySeperator_r(fs->next); } //end of the function EvolveFuzzySeperator_r //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void EvolveWeightConfig(weightconfig_t *config) { int i; for (i = 0; i < config->numweights; i++) { EvolveFuzzySeperator_r(config->weights[i].firstseperator); } //end for } //end of the function EvolveWeightConfig //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void ScaleFuzzySeperator_r(fuzzyseperator_t *fs, float scale) { if (fs->child) { ScaleFuzzySeperator_r(fs->child, scale); } //end if else if (fs->type == WT_BALANCE) { // fs->weight = (fs->maxweight + fs->minweight) * scale; //get the weight between bounds if (fs->weight < fs->minweight) fs->weight = fs->minweight; else if (fs->weight > fs->maxweight) fs->weight = fs->maxweight; } //end else if if (fs->next) ScaleFuzzySeperator_r(fs->next, scale); } //end of the function ScaleFuzzySeperator_r //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void ScaleWeight(weightconfig_t *config, char *name, float scale) { int i; if (scale < 0) scale = 0; else if (scale > 1) scale = 1; for (i = 0; i < config->numweights; i++) { if (!strcmp(name, config->weights[i].name)) { ScaleFuzzySeperator_r(config->weights[i].firstseperator, scale); break; } //end if } //end for } //end of the function ScaleWeight //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void ScaleFuzzySeperatorBalanceRange_r(fuzzyseperator_t *fs, float scale) { if (fs->child) { ScaleFuzzySeperatorBalanceRange_r(fs->child, scale); } //end if else if (fs->type == WT_BALANCE) { float mid = (fs->minweight + fs->maxweight) * 0.5; //get the weight between bounds fs->maxweight = mid + (fs->maxweight - mid) * scale; fs->minweight = mid + (fs->minweight - mid) * scale; if (fs->maxweight < fs->minweight) { fs->maxweight = fs->minweight; } //end if } //end else if if (fs->next) ScaleFuzzySeperatorBalanceRange_r(fs->next, scale); } //end of the function ScaleFuzzySeperatorBalanceRange_r //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void ScaleFuzzyBalanceRange(weightconfig_t *config, float scale) { int i; if (scale < 0) scale = 0; else if (scale > 100) scale = 100; for (i = 0; i < config->numweights; i++) { ScaleFuzzySeperatorBalanceRange_r(config->weights[i].firstseperator, scale); } //end for } //end of the function ScaleFuzzyBalanceRange //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int InterbreedFuzzySeperator_r(fuzzyseperator_t *fs1, fuzzyseperator_t *fs2, fuzzyseperator_t *fsout) { if (fs1->child) { if (!fs2->child || !fsout->child) { botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal child\n"); return qfalse; } //end if if (!InterbreedFuzzySeperator_r(fs2->child, fs2->child, fsout->child)) { return qfalse; } //end if } //end if else if (fs1->type == WT_BALANCE) { if (fs2->type != WT_BALANCE || fsout->type != WT_BALANCE) { botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal balance\n"); return qfalse; } //end if fsout->weight = (fs1->weight + fs2->weight) / 2; if (fsout->weight > fsout->maxweight) fsout->maxweight = fsout->weight; if (fsout->weight > fsout->minweight) fsout->minweight = fsout->weight; } //end else if if (fs1->next) { if (!fs2->next || !fsout->next) { botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal next\n"); return qfalse; } //end if if (!InterbreedFuzzySeperator_r(fs1->next, fs2->next, fsout->next)) { return qfalse; } //end if } //end if return qtrue; } //end of the function InterbreedFuzzySeperator_r //=========================================================================== // config1 and config2 are interbreeded and stored in configout // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void InterbreedWeightConfigs(weightconfig_t *config1, weightconfig_t *config2, weightconfig_t *configout) { int i; if (config1->numweights != config2->numweights || config1->numweights != configout->numweights) { botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal numweights\n"); return; } //end if for (i = 0; i < config1->numweights; i++) { InterbreedFuzzySeperator_r(config1->weights[i].firstseperator, config2->weights[i].firstseperator, configout->weights[i].firstseperator); } //end for } //end of the function InterbreedWeightConfigs //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotShutdownWeights(void) { int i; for( i = 0; i < MAX_WEIGHT_FILES; i++ ) { if (weightFileList[i]) { FreeWeightConfig2(weightFileList[i]); weightFileList[i] = NULL; } //end if } //end for } //end of the function BotShutdownWeights ================================================ FILE: src/engine/botlib/be_ai_weight.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_ai_weight.h * * desc: fuzzy weights * * $Archive: /source/code/botlib/be_ai_weight.h $ * *****************************************************************************/ #define WT_BALANCE 1 #define MAX_WEIGHTS 128 //fuzzy seperator typedef struct fuzzyseperator_s { int index; int value; int type; float weight; float minweight; float maxweight; struct fuzzyseperator_s *child; struct fuzzyseperator_s *next; } fuzzyseperator_t; //fuzzy weight typedef struct weight_s { char *name; struct fuzzyseperator_s *firstseperator; } weight_t; //weight configuration typedef struct weightconfig_s { int numweights; weight_t weights[MAX_WEIGHTS]; char filename[MAX_QPATH]; } weightconfig_t; //reads a weight configuration weightconfig_t *ReadWeightConfig(char *filename); //free a weight configuration void FreeWeightConfig(weightconfig_t *config); //writes a weight configuration, returns true if successfull qboolean WriteWeightConfig(char *filename, weightconfig_t *config); //find the fuzzy weight with the given name int FindFuzzyWeight(weightconfig_t *wc, char *name); //returns the fuzzy weight for the given inventory and weight float FuzzyWeight(int *inventory, weightconfig_t *wc, int weightnum); float FuzzyWeightUndecided(int *inventory, weightconfig_t *wc, int weightnum); //scales the weight with the given name void ScaleWeight(weightconfig_t *config, char *name, float scale); //scale the balance range void ScaleBalanceRange(weightconfig_t *config, float scale); //evolves the weight configuration void EvolveWeightConfig(weightconfig_t *config); //interbreed the weight configurations and stores the interbreeded one in configout void InterbreedWeightConfigs(weightconfig_t *config1, weightconfig_t *config2, weightconfig_t *configout); //frees cached weight configurations void BotShutdownWeights(void); ================================================ FILE: src/engine/botlib/be_ea.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_ea.c * * desc: elementary actions * * $Archive: /MissionPack/code/botlib/be_ea.c $ * *****************************************************************************/ #include "../../game/q_shared.h" #include "l_memory.h" #include "l_script.h" #include "l_precomp.h" #include "l_struct.h" #include "../../game/botlib.h" #include "be_interface.h" #define MAX_USERMOVE 400 #define MAX_COMMANDARGUMENTS 10 #define ACTION_JUMPEDLASTFRAME 128 bot_input_t *botinputs; //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void EA_Say(int client, char *str) { botimport.BotClientCommand(client, va("say %s", str) ); } //end of the function EA_Say //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void EA_SayTeam(int client, char *str) { botimport.BotClientCommand(client, va("say_team %s", str)); } //end of the function EA_SayTeam //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void EA_Tell(int client, int clientto, char *str) { botimport.BotClientCommand(client, va("tell %d, %s", clientto, str)); } //end of the function EA_SayTeam //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void EA_UseItem(int client, char *it) { botimport.BotClientCommand(client, va("use %s", it)); } //end of the function EA_UseItem //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void EA_DropItem(int client, char *it) { botimport.BotClientCommand(client, va("drop %s", it)); } //end of the function EA_DropItem //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void EA_UseInv(int client, char *inv) { botimport.BotClientCommand(client, va("invuse %s", inv)); } //end of the function EA_UseInv //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void EA_DropInv(int client, char *inv) { botimport.BotClientCommand(client, va("invdrop %s", inv)); } //end of the function EA_DropInv //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void EA_Gesture(int client) { bot_input_t *bi; bi = &botinputs[client]; bi->actionflags |= ACTION_GESTURE; } //end of the function EA_Gesture //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void EA_Command(int client, char *command) { botimport.BotClientCommand(client, command); } //end of the function EA_Command //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void EA_SelectWeapon(int client, int weapon) { bot_input_t *bi; bi = &botinputs[client]; bi->weapon = weapon; } //end of the function EA_SelectWeapon //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void EA_Attack(int client) { bot_input_t *bi; bi = &botinputs[client]; bi->actionflags |= ACTION_ATTACK; } //end of the function EA_Attack //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void EA_Talk(int client) { bot_input_t *bi; bi = &botinputs[client]; bi->actionflags |= ACTION_TALK; } //end of the function EA_Talk //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void EA_Use(int client) { bot_input_t *bi; bi = &botinputs[client]; bi->actionflags |= ACTION_USE; } //end of the function EA_Use //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void EA_Respawn(int client) { bot_input_t *bi; bi = &botinputs[client]; bi->actionflags |= ACTION_RESPAWN; } //end of the function EA_Respawn //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void EA_Jump(int client) { bot_input_t *bi; bi = &botinputs[client]; if (bi->actionflags & ACTION_JUMPEDLASTFRAME) { bi->actionflags &= ~ACTION_JUMP; } //end if else { bi->actionflags |= ACTION_JUMP; } //end if } //end of the function EA_Jump //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void EA_DelayedJump(int client) { bot_input_t *bi; bi = &botinputs[client]; if (bi->actionflags & ACTION_JUMPEDLASTFRAME) { bi->actionflags &= ~ACTION_DELAYEDJUMP; } //end if else { bi->actionflags |= ACTION_DELAYEDJUMP; } //end if } //end of the function EA_DelayedJump //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void EA_Crouch(int client) { bot_input_t *bi; bi = &botinputs[client]; bi->actionflags |= ACTION_CROUCH; } //end of the function EA_Crouch //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void EA_Walk(int client) { bot_input_t *bi; bi = &botinputs[client]; bi->actionflags |= ACTION_WALK; } //end of the function EA_Walk //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void EA_Action(int client, int action) { bot_input_t *bi; bi = &botinputs[client]; bi->actionflags |= action; } //end of function EA_Action //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void EA_MoveUp(int client) { bot_input_t *bi; bi = &botinputs[client]; bi->actionflags |= ACTION_MOVEUP; } //end of the function EA_MoveUp //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void EA_MoveDown(int client) { bot_input_t *bi; bi = &botinputs[client]; bi->actionflags |= ACTION_MOVEDOWN; } //end of the function EA_MoveDown //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void EA_MoveForward(int client) { bot_input_t *bi; bi = &botinputs[client]; bi->actionflags |= ACTION_MOVEFORWARD; } //end of the function EA_MoveForward //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void EA_MoveBack(int client) { bot_input_t *bi; bi = &botinputs[client]; bi->actionflags |= ACTION_MOVEBACK; } //end of the function EA_MoveBack //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void EA_MoveLeft(int client) { bot_input_t *bi; bi = &botinputs[client]; bi->actionflags |= ACTION_MOVELEFT; } //end of the function EA_MoveLeft //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void EA_MoveRight(int client) { bot_input_t *bi; bi = &botinputs[client]; bi->actionflags |= ACTION_MOVERIGHT; } //end of the function EA_MoveRight //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void EA_Move(int client, vec3_t dir, float speed) { bot_input_t *bi; bi = &botinputs[client]; VectorCopy(dir, bi->dir); //cap speed if (speed > MAX_USERMOVE) speed = MAX_USERMOVE; else if (speed < -MAX_USERMOVE) speed = -MAX_USERMOVE; bi->speed = speed; } //end of the function EA_Move //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void EA_View(int client, vec3_t viewangles) { bot_input_t *bi; bi = &botinputs[client]; VectorCopy(viewangles, bi->viewangles); } //end of the function EA_View //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void EA_EndRegular(int client, float thinktime) { /* bot_input_t *bi; int jumped = qfalse; bi = &botinputs[client]; bi->actionflags &= ~ACTION_JUMPEDLASTFRAME; bi->thinktime = thinktime; botimport.BotInput(client, bi); bi->thinktime = 0; VectorClear(bi->dir); bi->speed = 0; jumped = bi->actionflags & ACTION_JUMP; bi->actionflags = 0; if (jumped) bi->actionflags |= ACTION_JUMPEDLASTFRAME; */ } //end of the function EA_EndRegular //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void EA_GetInput(int client, float thinktime, bot_input_t *input) { bot_input_t *bi; // int jumped = qfalse; bi = &botinputs[client]; // bi->actionflags &= ~ACTION_JUMPEDLASTFRAME; bi->thinktime = thinktime; Com_Memcpy(input, bi, sizeof(bot_input_t)); /* bi->thinktime = 0; VectorClear(bi->dir); bi->speed = 0; jumped = bi->actionflags & ACTION_JUMP; bi->actionflags = 0; if (jumped) bi->actionflags |= ACTION_JUMPEDLASTFRAME; */ } //end of the function EA_GetInput //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void EA_ResetInput(int client) { bot_input_t *bi; int jumped = qfalse; bi = &botinputs[client]; bi->actionflags &= ~ACTION_JUMPEDLASTFRAME; bi->thinktime = 0; VectorClear(bi->dir); bi->speed = 0; jumped = bi->actionflags & ACTION_JUMP; bi->actionflags = 0; if (jumped) bi->actionflags |= ACTION_JUMPEDLASTFRAME; } //end of the function EA_ResetInput //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int EA_Setup(void) { //initialize the bot inputs botinputs = (bot_input_t *) GetClearedHunkMemory( botlibglobals.maxclients * sizeof(bot_input_t)); return BLERR_NOERROR; } //end of the function EA_Setup //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void EA_Shutdown(void) { FreeMemory(botinputs); botinputs = NULL; } //end of the function EA_Shutdown ================================================ FILE: src/engine/botlib/be_interface.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_interface.c // bk010221 - FIXME - DEAD code elimination * * desc: bot library interface * * $Archive: /MissionPack/code/botlib/be_interface.c $ * *****************************************************************************/ #include "../../game/q_shared.h" #include "l_memory.h" #include "l_log.h" #include "l_libvar.h" #include "l_script.h" #include "l_precomp.h" #include "l_struct.h" #include "aasfile.h" #include "../../game/botlib.h" #include "../../game/be_aas.h" #include "be_aas_funcs.h" #include "be_aas_def.h" #include "be_interface.h" #include "../../game/be_ea.h" #include "be_ai_weight.h" #include "../../game/be_ai_goal.h" #include "../../game/be_ai_move.h" #include "../../game/be_ai_weap.h" #include "../../game/be_ai_chat.h" #include "../../game/be_ai_char.h" #include "../../game/be_ai_gen.h" //library globals in a structure botlib_globals_t botlibglobals; botlib_export_t be_botlib_export; botlib_import_t botimport; // int bot_developer; //qtrue if the library is setup int botlibsetup = qfalse; //=========================================================================== // // several functions used by the exported functions // //=========================================================================== //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int Sys_MilliSeconds(void) { return clock() * 1000 / CLOCKS_PER_SEC; } //end of the function Sys_MilliSeconds //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== qboolean ValidClientNumber(int num, char *str) { if (num < 0 || num > botlibglobals.maxclients) { //weird: the disabled stuff results in a crash botimport.Print(PRT_ERROR, "%s: invalid client number %d, [0, %d]\n", str, num, botlibglobals.maxclients); return qfalse; } //end if return qtrue; } //end of the function BotValidateClientNumber //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== qboolean ValidEntityNumber(int num, char *str) { if (num < 0 || num > botlibglobals.maxentities) { botimport.Print(PRT_ERROR, "%s: invalid entity number %d, [0, %d]\n", str, num, botlibglobals.maxentities); return qfalse; } //end if return qtrue; } //end of the function BotValidateClientNumber //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== qboolean BotLibSetup(char *str) { if (!botlibglobals.botlibsetup) { botimport.Print(PRT_ERROR, "%s: bot library used before being setup\n", str); return qfalse; } //end if return qtrue; } //end of the function BotLibSetup //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int Export_BotLibSetup(void) { int errnum; bot_developer = LibVarGetValue("bot_developer"); memset( &botlibglobals, 0, sizeof(botlibglobals) ); // bk001207 - init //initialize byte swapping (litte endian etc.) // Swap_Init(); // Log_Open("botlib.log"); // botimport.Print(PRT_MESSAGE, "------- BotLib Initialization -------\n"); // botlibglobals.maxclients = (int) LibVarValue("maxclients", "128"); botlibglobals.maxentities = (int) LibVarValue("maxentities", "1024"); errnum = AAS_Setup(); //be_aas_main.c if (errnum != BLERR_NOERROR) return errnum; errnum = EA_Setup(); //be_ea.c if (errnum != BLERR_NOERROR) return errnum; errnum = BotSetupWeaponAI(); //be_ai_weap.c if (errnum != BLERR_NOERROR)return errnum; errnum = BotSetupGoalAI(); //be_ai_goal.c if (errnum != BLERR_NOERROR) return errnum; errnum = BotSetupChatAI(); //be_ai_chat.c if (errnum != BLERR_NOERROR) return errnum; errnum = BotSetupMoveAI(); //be_ai_move.c if (errnum != BLERR_NOERROR) return errnum; botlibsetup = qtrue; botlibglobals.botlibsetup = qtrue; return BLERR_NOERROR; } //end of the function Export_BotLibSetup //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int Export_BotLibShutdown(void) { if (!BotLibSetup("BotLibShutdown")) return BLERR_LIBRARYNOTSETUP; #ifndef DEMO //DumpFileCRCs(); #endif //DEMO // BotShutdownChatAI(); //be_ai_chat.c BotShutdownMoveAI(); //be_ai_move.c BotShutdownGoalAI(); //be_ai_goal.c BotShutdownWeaponAI(); //be_ai_weap.c BotShutdownWeights(); //be_ai_weight.c BotShutdownCharacters(); //be_ai_char.c //shud down aas AAS_Shutdown(); //shut down bot elemantary actions EA_Shutdown(); //free all libvars LibVarDeAllocAll(); //remove all global defines from the pre compiler PC_RemoveAllGlobalDefines(); //dump all allocated memory // DumpMemory(); #ifdef DEBUG PrintMemoryLabels(); #endif //shut down library log file Log_Shutdown(); // botlibsetup = qfalse; botlibglobals.botlibsetup = qfalse; // print any files still open PC_CheckOpenSourceHandles(); // return BLERR_NOERROR; } //end of the function Export_BotLibShutdown //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int Export_BotLibVarSet(char *var_name, char *value) { LibVarSet(var_name, value); return BLERR_NOERROR; } //end of the function Export_BotLibVarSet //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int Export_BotLibVarGet(char *var_name, char *value, int size) { char *varvalue; varvalue = LibVarGetString(var_name); strncpy(value, varvalue, size-1); value[size-1] = '\0'; return BLERR_NOERROR; } //end of the function Export_BotLibVarGet //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int Export_BotLibStartFrame(float time) { if (!BotLibSetup("BotStartFrame")) return BLERR_LIBRARYNOTSETUP; return AAS_StartFrame(time); } //end of the function Export_BotLibStartFrame //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int Export_BotLibLoadMap(const char *mapname) { #ifdef DEBUG int starttime = Sys_MilliSeconds(); #endif int errnum; if (!BotLibSetup("BotLoadMap")) return BLERR_LIBRARYNOTSETUP; // botimport.Print(PRT_MESSAGE, "------------ Map Loading ------------\n"); //startup AAS for the current map, model and sound index errnum = AAS_LoadMap(mapname); if (errnum != BLERR_NOERROR) return errnum; //initialize the items in the level BotInitLevelItems(); //be_ai_goal.h BotSetBrushModelTypes(); //be_ai_move.h // botimport.Print(PRT_MESSAGE, "-------------------------------------\n"); #ifdef DEBUG botimport.Print(PRT_MESSAGE, "map loaded in %d msec\n", Sys_MilliSeconds() - starttime); #endif // return BLERR_NOERROR; } //end of the function Export_BotLibLoadMap //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int Export_BotLibUpdateEntity(int ent, bot_entitystate_t *state) { if (!BotLibSetup("BotUpdateEntity")) return BLERR_LIBRARYNOTSETUP; if (!ValidEntityNumber(ent, "BotUpdateEntity")) return BLERR_INVALIDENTITYNUMBER; return AAS_UpdateEntity(ent, state); } //end of the function Export_BotLibUpdateEntity //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_TestMovementPrediction(int entnum, vec3_t origin, vec3_t dir); void ElevatorBottomCenter(aas_reachability_t *reach, vec3_t bottomcenter); int BotGetReachabilityToGoal(vec3_t origin, int areanum, int lastgoalareanum, int lastareanum, int *avoidreach, float *avoidreachtimes, int *avoidreachtries, bot_goal_t *goal, int travelflags, int movetravelflags, struct bot_avoidspot_s *avoidspots, int numavoidspots, int *flags); int AAS_PointLight(vec3_t origin, int *red, int *green, int *blue); int AAS_TraceAreas(vec3_t start, vec3_t end, int *areas, vec3_t *points, int maxareas); int AAS_Reachability_WeaponJump(int area1num, int area2num); int BotFuzzyPointReachabilityArea(vec3_t origin); float BotGapDistance(vec3_t origin, vec3_t hordir, int entnum); void AAS_FloodAreas(vec3_t origin); int BotExportTest(int parm0, char *parm1, vec3_t parm2, vec3_t parm3) { // return AAS_PointLight(parm2, NULL, NULL, NULL); #ifdef DEBUG static int area = -1; static int line[2]; int newarea, i, highlightarea, flood; // int reachnum; vec3_t eye, forward, right, end, origin; // vec3_t bottomcenter; // aas_trace_t trace; // aas_face_t *face; // aas_entity_t *ent; // bsp_trace_t bsptrace; // aas_reachability_t reach; // bot_goal_t goal; // clock_t start_time, end_time; vec3_t mins = {-16, -16, -24}; vec3_t maxs = {16, 16, 32}; // int areas[10], numareas; //return 0; if (!aasworld.loaded) return 0; /* if (parm0 & 1) { AAS_ClearShownPolygons(); AAS_FloodAreas(parm2); } //end if return 0; */ for (i = 0; i < 2; i++) if (!line[i]) line[i] = botimport.DebugLineCreate(); // AAS_ClearShownDebugLines(); //if (AAS_AgainstLadder(parm2)) botimport.Print(PRT_MESSAGE, "against ladder\n"); //BotOnGround(parm2, PRESENCE_NORMAL, 1, &newarea, &newarea); //botimport.Print(PRT_MESSAGE, "%f %f %f\n", parm2[0], parm2[1], parm2[2]); //* highlightarea = LibVarGetValue("bot_highlightarea"); if (highlightarea > 0) { newarea = highlightarea; } //end if else { VectorCopy(parm2, origin); origin[2] += 0.5; //newarea = AAS_PointAreaNum(origin); newarea = BotFuzzyPointReachabilityArea(origin); } //end else botimport.Print(PRT_MESSAGE, "\rtravel time to goal (%d) = %d ", botlibglobals.goalareanum, AAS_AreaTravelTimeToGoalArea(newarea, origin, botlibglobals.goalareanum, TFL_DEFAULT)); //newarea = BotReachabilityArea(origin, qtrue); if (newarea != area) { botimport.Print(PRT_MESSAGE, "origin = %f, %f, %f\n", origin[0], origin[1], origin[2]); area = newarea; botimport.Print(PRT_MESSAGE, "new area %d, cluster %d, presence type %d\n", area, AAS_AreaCluster(area), AAS_PointPresenceType(origin)); botimport.Print(PRT_MESSAGE, "area contents: "); if (aasworld.areasettings[area].contents & AREACONTENTS_WATER) { botimport.Print(PRT_MESSAGE, "water &"); } //end if if (aasworld.areasettings[area].contents & AREACONTENTS_LAVA) { botimport.Print(PRT_MESSAGE, "lava &"); } //end if if (aasworld.areasettings[area].contents & AREACONTENTS_SLIME) { botimport.Print(PRT_MESSAGE, "slime &"); } //end if if (aasworld.areasettings[area].contents & AREACONTENTS_JUMPPAD) { botimport.Print(PRT_MESSAGE, "jump pad &"); } //end if if (aasworld.areasettings[area].contents & AREACONTENTS_CLUSTERPORTAL) { botimport.Print(PRT_MESSAGE, "cluster portal &"); } //end if if (aasworld.areasettings[area].contents & AREACONTENTS_VIEWPORTAL) { botimport.Print(PRT_MESSAGE, "view portal &"); } //end if if (aasworld.areasettings[area].contents & AREACONTENTS_DONOTENTER) { botimport.Print(PRT_MESSAGE, "do not enter &"); } //end if if (aasworld.areasettings[area].contents & AREACONTENTS_MOVER) { botimport.Print(PRT_MESSAGE, "mover &"); } //end if if (!aasworld.areasettings[area].contents) { botimport.Print(PRT_MESSAGE, "empty"); } //end if botimport.Print(PRT_MESSAGE, "\n"); botimport.Print(PRT_MESSAGE, "travel time to goal (%d) = %d\n", botlibglobals.goalareanum, AAS_AreaTravelTimeToGoalArea(newarea, origin, botlibglobals.goalareanum, TFL_DEFAULT|TFL_ROCKETJUMP)); /* VectorCopy(origin, end); end[2] += 5; numareas = AAS_TraceAreas(origin, end, areas, NULL, 10); AAS_TraceClientBBox(origin, end, PRESENCE_CROUCH, -1); botimport.Print(PRT_MESSAGE, "num areas = %d, area = %d\n", numareas, areas[0]); */ /* botlibglobals.goalareanum = newarea; VectorCopy(parm2, botlibglobals.goalorigin); botimport.Print(PRT_MESSAGE, "new goal %2.1f %2.1f %2.1f area %d\n", origin[0], origin[1], origin[2], newarea); */ } //end if //* flood = LibVarGetValue("bot_flood"); if (parm0 & 1) { if (flood) { AAS_ClearShownPolygons(); AAS_ClearShownDebugLines(); AAS_FloodAreas(parm2); } else { botlibglobals.goalareanum = newarea; VectorCopy(parm2, botlibglobals.goalorigin); botimport.Print(PRT_MESSAGE, "new goal %2.1f %2.1f %2.1f area %d\n", origin[0], origin[1], origin[2], newarea); } } //end if*/ if (flood) return 0; // if (parm0 & BUTTON_USE) // { // botlibglobals.runai = !botlibglobals.runai; // if (botlibglobals.runai) botimport.Print(PRT_MESSAGE, "started AI\n"); // else botimport.Print(PRT_MESSAGE, "stopped AI\n"); //* / /* goal.areanum = botlibglobals.goalareanum; reachnum = BotGetReachabilityToGoal(parm2, newarea, 1, ms.avoidreach, ms.avoidreachtimes, &goal, TFL_DEFAULT); if (!reachnum) { botimport.Print(PRT_MESSAGE, "goal not reachable\n"); } //end if else { AAS_ReachabilityFromNum(reachnum, &reach); AAS_ClearShownDebugLines(); AAS_ShowArea(area, qtrue); AAS_ShowArea(reach.areanum, qtrue); AAS_DrawCross(reach.start, 6, LINECOLOR_BLUE); AAS_DrawCross(reach.end, 6, LINECOLOR_RED); // if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_ELEVATOR) { ElevatorBottomCenter(&reach, bottomcenter); AAS_DrawCross(bottomcenter, 10, LINECOLOR_GREEN); } //end if } //end else*/ // botimport.Print(PRT_MESSAGE, "travel time to goal = %d\n", // AAS_AreaTravelTimeToGoalArea(area, origin, botlibglobals.goalareanum, TFL_DEFAULT)); // botimport.Print(PRT_MESSAGE, "test rj from 703 to 716\n"); // AAS_Reachability_WeaponJump(703, 716); // } //end if*/ /* face = AAS_AreaGroundFace(newarea, parm2); if (face) { AAS_ShowFace(face - aasworld.faces); } //end if*/ /* AAS_ClearShownDebugLines(); AAS_ShowArea(newarea, parm0 & BUTTON_USE); AAS_ShowReachableAreas(area); */ AAS_ClearShownPolygons(); AAS_ClearShownDebugLines(); AAS_ShowAreaPolygons(newarea, 1, parm0 & 4); if (parm0 & 2) AAS_ShowReachableAreas(area); else { static int lastgoalareanum, lastareanum; static int avoidreach[MAX_AVOIDREACH]; static float avoidreachtimes[MAX_AVOIDREACH]; static int avoidreachtries[MAX_AVOIDREACH]; int reachnum, resultFlags; bot_goal_t goal; aas_reachability_t reach; /* goal.areanum = botlibglobals.goalareanum; VectorCopy(botlibglobals.goalorigin, goal.origin); reachnum = BotGetReachabilityToGoal(origin, newarea, lastgoalareanum, lastareanum, avoidreach, avoidreachtimes, avoidreachtries, &goal, TFL_DEFAULT|TFL_FUNCBOB|TFL_ROCKETJUMP, TFL_DEFAULT|TFL_FUNCBOB|TFL_ROCKETJUMP, NULL, 0, &resultFlags); AAS_ReachabilityFromNum(reachnum, &reach); AAS_ShowReachability(&reach); */ int curarea; vec3_t curorigin; goal.areanum = botlibglobals.goalareanum; VectorCopy(botlibglobals.goalorigin, goal.origin); VectorCopy(origin, curorigin); curarea = newarea; for ( i = 0; i < 100; i++ ) { if ( curarea == goal.areanum ) { break; } reachnum = BotGetReachabilityToGoal(curorigin, curarea, lastgoalareanum, lastareanum, avoidreach, avoidreachtimes, avoidreachtries, &goal, TFL_DEFAULT|TFL_FUNCBOB|TFL_ROCKETJUMP, TFL_DEFAULT|TFL_FUNCBOB|TFL_ROCKETJUMP, NULL, 0, &resultFlags); AAS_ReachabilityFromNum(reachnum, &reach); AAS_ShowReachability(&reach); VectorCopy(reach.end, origin); lastareanum = curarea; curarea = reach.areanum; } } //end else VectorClear(forward); //BotGapDistance(origin, forward, 0); /* if (parm0 & BUTTON_USE) { botimport.Print(PRT_MESSAGE, "test rj from 703 to 716\n"); AAS_Reachability_WeaponJump(703, 716); } //end if*/ AngleVectors(parm3, forward, right, NULL); //get the eye 16 units to the right of the origin VectorMA(parm2, 8, right, eye); //get the eye 24 units up eye[2] += 24; //get the end point for the line to be traced VectorMA(eye, 800, forward, end); // AAS_TestMovementPrediction(1, parm2, forward); /* //trace the line to find the hit point trace = AAS_TraceClientBBox(eye, end, PRESENCE_NORMAL, 1); if (!line[0]) line[0] = botimport.DebugLineCreate(); botimport.DebugLineShow(line[0], eye, trace.endpos, LINECOLOR_BLUE); // AAS_ClearShownDebugLines(); if (trace.ent) { ent = &aasworld.entities[trace.ent]; AAS_ShowBoundingBox(ent->origin, ent->mins, ent->maxs); } //end if */ /* start_time = clock(); for (i = 0; i < 2000; i++) { AAS_Trace2(eye, mins, maxs, end, 1, MASK_PLAYERSOLID); // AAS_TraceClientBBox(eye, end, PRESENCE_NORMAL, 1); } //end for end_time = clock(); botimport.Print(PRT_MESSAGE, "me %lu clocks, %lu CLOCKS_PER_SEC\n", end_time - start_time, CLOCKS_PER_SEC); start_time = clock(); for (i = 0; i < 2000; i++) { AAS_Trace(eye, mins, maxs, end, 1, MASK_PLAYERSOLID); } //end for end_time = clock(); botimport.Print(PRT_MESSAGE, "id %lu clocks, %lu CLOCKS_PER_SEC\n", end_time - start_time, CLOCKS_PER_SEC); */ // TTimo: nested comments are BAD for gcc -Werror, use #if 0 instead.. #if 0 AAS_ClearShownDebugLines(); //bsptrace = AAS_Trace(eye, NULL, NULL, end, 1, MASK_PLAYERSOLID); bsptrace = AAS_Trace(eye, mins, maxs, end, 1, MASK_PLAYERSOLID); if (!line[0]) line[0] = botimport.DebugLineCreate(); botimport.DebugLineShow(line[0], eye, bsptrace.endpos, LINECOLOR_YELLOW); if (bsptrace.fraction < 1.0) { face = AAS_TraceEndFace(&trace); if (face) { AAS_ShowFace(face - aasworld.faces); } //end if AAS_DrawPlaneCross(bsptrace.endpos, bsptrace.plane.normal, bsptrace.plane.dist + bsptrace.exp_dist, bsptrace.plane.type, LINECOLOR_GREEN); if (trace.ent) { ent = &aasworld.entities[trace.ent]; AAS_ShowBoundingBox(ent->origin, ent->mins, ent->maxs); } //end if } //end if //bsptrace = AAS_Trace2(eye, NULL, NULL, end, 1, MASK_PLAYERSOLID); bsptrace = AAS_Trace2(eye, mins, maxs, end, 1, MASK_PLAYERSOLID); botimport.DebugLineShow(line[1], eye, bsptrace.endpos, LINECOLOR_BLUE); if (bsptrace.fraction < 1.0) { AAS_DrawPlaneCross(bsptrace.endpos, bsptrace.plane.normal, bsptrace.plane.dist,// + bsptrace.exp_dist, bsptrace.plane.type, LINECOLOR_RED); if (bsptrace.ent) { ent = &aasworld.entities[bsptrace.ent]; AAS_ShowBoundingBox(ent->origin, ent->mins, ent->maxs); } //end if } //end if #endif #endif return 0; } //end of the function BotExportTest /* ============ Init_AAS_Export ============ */ static void Init_AAS_Export( aas_export_t *aas ) { //-------------------------------------------- // be_aas_entity.c //-------------------------------------------- aas->AAS_EntityInfo = AAS_EntityInfo; //-------------------------------------------- // be_aas_main.c //-------------------------------------------- aas->AAS_Initialized = AAS_Initialized; aas->AAS_PresenceTypeBoundingBox = AAS_PresenceTypeBoundingBox; aas->AAS_Time = AAS_Time; //-------------------------------------------- // be_aas_sample.c //-------------------------------------------- aas->AAS_PointAreaNum = AAS_PointAreaNum; aas->AAS_PointReachabilityAreaIndex = AAS_PointReachabilityAreaIndex; aas->AAS_TraceAreas = AAS_TraceAreas; aas->AAS_BBoxAreas = AAS_BBoxAreas; aas->AAS_AreaInfo = AAS_AreaInfo; //-------------------------------------------- // be_aas_bspq3.c //-------------------------------------------- aas->AAS_PointContents = AAS_PointContents; aas->AAS_NextBSPEntity = AAS_NextBSPEntity; aas->AAS_ValueForBSPEpairKey = AAS_ValueForBSPEpairKey; aas->AAS_VectorForBSPEpairKey = AAS_VectorForBSPEpairKey; aas->AAS_FloatForBSPEpairKey = AAS_FloatForBSPEpairKey; aas->AAS_IntForBSPEpairKey = AAS_IntForBSPEpairKey; //-------------------------------------------- // be_aas_reach.c //-------------------------------------------- aas->AAS_AreaReachability = AAS_AreaReachability; //-------------------------------------------- // be_aas_route.c //-------------------------------------------- aas->AAS_AreaTravelTimeToGoalArea = AAS_AreaTravelTimeToGoalArea; aas->AAS_EnableRoutingArea = AAS_EnableRoutingArea; aas->AAS_PredictRoute = AAS_PredictRoute; //-------------------------------------------- // be_aas_altroute.c //-------------------------------------------- aas->AAS_AlternativeRouteGoals = AAS_AlternativeRouteGoals; //-------------------------------------------- // be_aas_move.c //-------------------------------------------- aas->AAS_Swimming = AAS_Swimming; aas->AAS_PredictClientMovement = AAS_PredictClientMovement; } /* ============ Init_EA_Export ============ */ static void Init_EA_Export( ea_export_t *ea ) { //ClientCommand elementary actions ea->EA_Command = EA_Command; ea->EA_Say = EA_Say; ea->EA_SayTeam = EA_SayTeam; ea->EA_Action = EA_Action; ea->EA_Gesture = EA_Gesture; ea->EA_Talk = EA_Talk; ea->EA_Attack = EA_Attack; ea->EA_Use = EA_Use; ea->EA_Respawn = EA_Respawn; ea->EA_Crouch = EA_Crouch; ea->EA_MoveUp = EA_MoveUp; ea->EA_MoveDown = EA_MoveDown; ea->EA_MoveForward = EA_MoveForward; ea->EA_MoveBack = EA_MoveBack; ea->EA_MoveLeft = EA_MoveLeft; ea->EA_MoveRight = EA_MoveRight; ea->EA_SelectWeapon = EA_SelectWeapon; ea->EA_Jump = EA_Jump; ea->EA_DelayedJump = EA_DelayedJump; ea->EA_Move = EA_Move; ea->EA_View = EA_View; ea->EA_GetInput = EA_GetInput; ea->EA_EndRegular = EA_EndRegular; ea->EA_ResetInput = EA_ResetInput; } /* ============ Init_AI_Export ============ */ static void Init_AI_Export( ai_export_t *ai ) { //----------------------------------- // be_ai_char.h //----------------------------------- ai->BotLoadCharacter = BotLoadCharacter; ai->BotFreeCharacter = BotFreeCharacter; ai->Characteristic_Float = Characteristic_Float; ai->Characteristic_BFloat = Characteristic_BFloat; ai->Characteristic_Integer = Characteristic_Integer; ai->Characteristic_BInteger = Characteristic_BInteger; ai->Characteristic_String = Characteristic_String; //----------------------------------- // be_ai_chat.h //----------------------------------- ai->BotAllocChatState = BotAllocChatState; ai->BotFreeChatState = BotFreeChatState; ai->BotQueueConsoleMessage = BotQueueConsoleMessage; ai->BotRemoveConsoleMessage = BotRemoveConsoleMessage; ai->BotNextConsoleMessage = BotNextConsoleMessage; ai->BotNumConsoleMessages = BotNumConsoleMessages; ai->BotInitialChat = BotInitialChat; ai->BotNumInitialChats = BotNumInitialChats; ai->BotReplyChat = BotReplyChat; ai->BotChatLength = BotChatLength; ai->BotEnterChat = BotEnterChat; ai->BotGetChatMessage = BotGetChatMessage; ai->StringContains = StringContains; ai->BotFindMatch = BotFindMatch; ai->BotMatchVariable = BotMatchVariable; ai->UnifyWhiteSpaces = UnifyWhiteSpaces; ai->BotReplaceSynonyms = BotReplaceSynonyms; ai->BotLoadChatFile = BotLoadChatFile; ai->BotSetChatGender = BotSetChatGender; ai->BotSetChatName = BotSetChatName; //----------------------------------- // be_ai_goal.h //----------------------------------- ai->BotResetGoalState = BotResetGoalState; ai->BotResetAvoidGoals = BotResetAvoidGoals; ai->BotRemoveFromAvoidGoals = BotRemoveFromAvoidGoals; ai->BotPushGoal = BotPushGoal; ai->BotPopGoal = BotPopGoal; ai->BotEmptyGoalStack = BotEmptyGoalStack; ai->BotDumpAvoidGoals = BotDumpAvoidGoals; ai->BotDumpGoalStack = BotDumpGoalStack; ai->BotGoalName = BotGoalName; ai->BotGetTopGoal = BotGetTopGoal; ai->BotGetSecondGoal = BotGetSecondGoal; ai->BotChooseLTGItem = BotChooseLTGItem; ai->BotChooseNBGItem = BotChooseNBGItem; ai->BotTouchingGoal = BotTouchingGoal; ai->BotItemGoalInVisButNotVisible = BotItemGoalInVisButNotVisible; ai->BotGetLevelItemGoal = BotGetLevelItemGoal; ai->BotGetNextCampSpotGoal = BotGetNextCampSpotGoal; ai->BotGetMapLocationGoal = BotGetMapLocationGoal; ai->BotAvoidGoalTime = BotAvoidGoalTime; ai->BotSetAvoidGoalTime = BotSetAvoidGoalTime; ai->BotInitLevelItems = BotInitLevelItems; ai->BotUpdateEntityItems = BotUpdateEntityItems; ai->BotLoadItemWeights = BotLoadItemWeights; ai->BotFreeItemWeights = BotFreeItemWeights; ai->BotInterbreedGoalFuzzyLogic = BotInterbreedGoalFuzzyLogic; ai->BotSaveGoalFuzzyLogic = BotSaveGoalFuzzyLogic; ai->BotMutateGoalFuzzyLogic = BotMutateGoalFuzzyLogic; ai->BotAllocGoalState = BotAllocGoalState; ai->BotFreeGoalState = BotFreeGoalState; //----------------------------------- // be_ai_move.h //----------------------------------- ai->BotResetMoveState = BotResetMoveState; ai->BotMoveToGoal = BotMoveToGoal; ai->BotMoveInDirection = BotMoveInDirection; ai->BotResetAvoidReach = BotResetAvoidReach; ai->BotResetLastAvoidReach = BotResetLastAvoidReach; ai->BotReachabilityArea = BotReachabilityArea; ai->BotMovementViewTarget = BotMovementViewTarget; ai->BotPredictVisiblePosition = BotPredictVisiblePosition; ai->BotAllocMoveState = BotAllocMoveState; ai->BotFreeMoveState = BotFreeMoveState; ai->BotInitMoveState = BotInitMoveState; ai->BotAddAvoidSpot = BotAddAvoidSpot; //----------------------------------- // be_ai_weap.h //----------------------------------- ai->BotChooseBestFightWeapon = BotChooseBestFightWeapon; ai->BotGetWeaponInfo = BotGetWeaponInfo; ai->BotLoadWeaponWeights = BotLoadWeaponWeights; ai->BotAllocWeaponState = BotAllocWeaponState; ai->BotFreeWeaponState = BotFreeWeaponState; ai->BotResetWeaponState = BotResetWeaponState; //----------------------------------- // be_ai_gen.h //----------------------------------- ai->GeneticParentsAndChildSelection = GeneticParentsAndChildSelection; } /* ============ GetBotLibAPI ============ */ botlib_export_t *GetBotLibAPI(int apiVersion, botlib_import_t *import) { assert(import); // bk001129 - this wasn't set for baseq3/ botimport = *import; assert(botimport.Print); // bk001129 - pars pro toto Com_Memset( &be_botlib_export, 0, sizeof( be_botlib_export ) ); if ( apiVersion != BOTLIB_API_VERSION ) { botimport.Print( PRT_ERROR, "Mismatched BOTLIB_API_VERSION: expected %i, got %i\n", BOTLIB_API_VERSION, apiVersion ); return NULL; } Init_AAS_Export(&be_botlib_export.aas); Init_EA_Export(&be_botlib_export.ea); Init_AI_Export(&be_botlib_export.ai); be_botlib_export.BotLibSetup = Export_BotLibSetup; be_botlib_export.BotLibShutdown = Export_BotLibShutdown; be_botlib_export.BotLibVarSet = Export_BotLibVarSet; be_botlib_export.BotLibVarGet = Export_BotLibVarGet; be_botlib_export.PC_AddGlobalDefine = PC_AddGlobalDefine; be_botlib_export.PC_LoadSourceHandle = PC_LoadSourceHandle; be_botlib_export.PC_FreeSourceHandle = PC_FreeSourceHandle; be_botlib_export.PC_ReadTokenHandle = PC_ReadTokenHandle; be_botlib_export.PC_SourceFileAndLine = PC_SourceFileAndLine; be_botlib_export.BotLibStartFrame = Export_BotLibStartFrame; be_botlib_export.BotLibLoadMap = Export_BotLibLoadMap; be_botlib_export.BotLibUpdateEntity = Export_BotLibUpdateEntity; be_botlib_export.Test = BotExportTest; return &be_botlib_export; } ================================================ FILE: src/engine/botlib/be_interface.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: be_interface.h * * desc: botlib interface * * $Archive: /source/code/botlib/be_interface.h $ * *****************************************************************************/ //#define DEBUG //debug code #define RANDOMIZE //randomize bot behaviour //FIXME: get rid of this global structure typedef struct botlib_globals_s { int botlibsetup; //true when the bot library has been setup int maxentities; //maximum number of entities int maxclients; //maximum number of clients float time; //the global time #ifdef DEBUG qboolean debug; //true if debug is on int goalareanum; vec3_t goalorigin; int runai; #endif } botlib_globals_t; extern botlib_globals_t botlibglobals; extern botlib_import_t botimport; extern int bot_developer; //true if developer is on // int Sys_MilliSeconds(void); ================================================ FILE: src/engine/botlib/l_crc.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: l_crc.c * * desc: CRC calculation * * $Archive: /MissionPack/CODE/botlib/l_crc.c $ * *****************************************************************************/ #include #include #include #include "../../game/q_shared.h" #include "../../game/botlib.h" #include "be_interface.h" //for botimport.Print // FIXME: byte swap? // this is a 16 bit, non-reflected CRC using the polynomial 0x1021 // and the initial and final xor values shown below... in other words, the // CCITT standard CRC used by XMODEM #define CRC_INIT_VALUE 0xffff #define CRC_XOR_VALUE 0x0000 unsigned short crctable[257] = { 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 }; //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void CRC_Init(unsigned short *crcvalue) { *crcvalue = CRC_INIT_VALUE; } //end of the function CRC_Init //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void CRC_ProcessByte(unsigned short *crcvalue, byte data) { *crcvalue = (*crcvalue << 8) ^ crctable[(*crcvalue >> 8) ^ data]; } //end of the function CRC_ProcessByte //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== unsigned short CRC_Value(unsigned short crcvalue) { return crcvalue ^ CRC_XOR_VALUE; } //end of the function CRC_Value //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== unsigned short CRC_ProcessString(unsigned char *data, int length) { unsigned short crcvalue; int i, ind; CRC_Init(&crcvalue); for (i = 0; i < length; i++) { ind = (crcvalue >> 8) ^ data[i]; if (ind < 0 || ind > 256) ind = 0; crcvalue = (crcvalue << 8) ^ crctable[ind]; } //end for return CRC_Value(crcvalue); } //end of the function CRC_ProcessString //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void CRC_ContinueProcessString(unsigned short *crc, char *data, int length) { int i; for (i = 0; i < length; i++) { *crc = (*crc << 8) ^ crctable[(*crc >> 8) ^ data[i]]; } //end for } //end of the function CRC_ProcessString ================================================ FILE: src/engine/botlib/l_crc.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ typedef unsigned short crc_t; void CRC_Init(unsigned short *crcvalue); void CRC_ProcessByte(unsigned short *crcvalue, byte data); unsigned short CRC_Value(unsigned short crcvalue); unsigned short CRC_ProcessString(unsigned char *data, int length); void CRC_ContinueProcessString(unsigned short *crc, char *data, int length); ================================================ FILE: src/engine/botlib/l_libvar.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: l_libvar.c * * desc: bot library variables * * $Archive: /MissionPack/code/botlib/l_libvar.c $ * *****************************************************************************/ #include "../../game/q_shared.h" #include "l_memory.h" #include "l_libvar.h" //list with library variables libvar_t *libvarlist; //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== float LibVarStringValue(char *string) { int dotfound = 0; float value = 0; while(*string) { if (*string < '0' || *string > '9') { if (dotfound || *string != '.') { return 0; } //end if else { dotfound = 10; string++; } //end if } //end if if (dotfound) { value = value + (float) (*string - '0') / (float) dotfound; dotfound *= 10; } //end if else { value = value * 10.0 + (float) (*string - '0'); } //end else string++; } //end while return value; } //end of the function LibVarStringValue //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== libvar_t *LibVarAlloc(char *var_name) { libvar_t *v; v = (libvar_t *) GetMemory(sizeof(libvar_t) + (int)strlen(var_name) + 1); Com_Memset(v, 0, sizeof(libvar_t)); v->name = (char *) v + sizeof(libvar_t); strcpy(v->name, var_name); //add the variable in the list v->next = libvarlist; libvarlist = v; return v; } //end of the function LibVarAlloc //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void LibVarDeAlloc(libvar_t *v) { if (v->string) FreeMemory(v->string); FreeMemory(v); } //end of the function LibVarDeAlloc //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void LibVarDeAllocAll(void) { libvar_t *v; for (v = libvarlist; v; v = libvarlist) { libvarlist = libvarlist->next; LibVarDeAlloc(v); } //end for libvarlist = NULL; } //end of the function LibVarDeAllocAll //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== libvar_t *LibVarGet(char *var_name) { libvar_t *v; for (v = libvarlist; v; v = v->next) { if (!Q_stricmp(v->name, var_name)) { return v; } //end if } //end for return NULL; } //end of the function LibVarGet //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== char *LibVarGetString(char *var_name) { libvar_t *v; v = LibVarGet(var_name); if (v) { return v->string; } //end if else { return ""; } //end else } //end of the function LibVarGetString //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== float LibVarGetValue(char *var_name) { libvar_t *v; v = LibVarGet(var_name); if (v) { return v->value; } //end if else { return 0; } //end else } //end of the function LibVarGetValue //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== libvar_t *LibVar(char *var_name, char *value) { libvar_t *v; v = LibVarGet(var_name); if (v) return v; //create new variable v = LibVarAlloc(var_name); //variable string v->string = (char *) GetMemory((int)strlen(value) + 1); strcpy(v->string, value); //the value v->value = LibVarStringValue(v->string); //variable is modified v->modified = qtrue; // return v; } //end of the function LibVar //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== char *LibVarString(char *var_name, char *value) { libvar_t *v; v = LibVar(var_name, value); return v->string; } //end of the function LibVarString //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== float LibVarValue(char *var_name, char *value) { libvar_t *v; v = LibVar(var_name, value); return v->value; } //end of the function LibVarValue //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void LibVarSet(char *var_name, char *value) { libvar_t *v; v = LibVarGet(var_name); if (v) { FreeMemory(v->string); } //end if else { v = LibVarAlloc(var_name); } //end else //variable string v->string = (char *) GetMemory((int)strlen(value) + 1); strcpy(v->string, value); //the value v->value = LibVarStringValue(v->string); //variable is modified v->modified = qtrue; } //end of the function LibVarSet //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== qboolean LibVarChanged(char *var_name) { libvar_t *v; v = LibVarGet(var_name); if (v) { return v->modified; } //end if else { return qfalse; } //end else } //end of the function LibVarChanged //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void LibVarSetNotModified(char *var_name) { libvar_t *v; v = LibVarGet(var_name); if (v) { v->modified = qfalse; } //end if } //end of the function LibVarSetNotModified ================================================ FILE: src/engine/botlib/l_libvar.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: l_libvar.h * * desc: botlib vars * * $Archive: /source/code/botlib/l_libvar.h $ * *****************************************************************************/ //library variable typedef struct libvar_s { char *name; char *string; int flags; qboolean modified; // set each time the cvar is changed float value; struct libvar_s *next; } libvar_t; //removes all library variables void LibVarDeAllocAll(void); //gets the library variable with the given name libvar_t *LibVarGet(char *var_name); //gets the string of the library variable with the given name char *LibVarGetString(char *var_name); //gets the value of the library variable with the given name float LibVarGetValue(char *var_name); //creates the library variable if not existing already and returns it libvar_t *LibVar(char *var_name, char *value); //creates the library variable if not existing already and returns the value float LibVarValue(char *var_name, char *value); //creates the library variable if not existing already and returns the value string char *LibVarString(char *var_name, char *value); //sets the library variable void LibVarSet(char *var_name, char *value); //returns true if the library variable has been modified qboolean LibVarChanged(char *var_name); //sets the library variable to unmodified void LibVarSetNotModified(char *var_name); ================================================ FILE: src/engine/botlib/l_log.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: l_log.c * * desc: log file * * $Archive: /MissionPack/CODE/botlib/l_log.c $ * *****************************************************************************/ #include #include #include #include "../../game/q_shared.h" #include "../../game/botlib.h" #include "be_interface.h" //for botimport.Print #include "l_libvar.h" #define MAX_LOGFILENAMESIZE 1024 typedef struct logfile_s { char filename[MAX_LOGFILENAMESIZE]; FILE *fp; int numwrites; } logfile_t; static logfile_t logfile; //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void Log_Open(char *filename) { if (!LibVarValue("log", "0")) return; if (!filename || !strlen(filename)) { botimport.Print(PRT_MESSAGE, "openlog \n"); return; } //end if if (logfile.fp) { botimport.Print(PRT_ERROR, "log file %s is already opened\n", logfile.filename); return; } //end if logfile.fp = fopen(filename, "wb"); if (!logfile.fp) { botimport.Print(PRT_ERROR, "can't open the log file %s\n", filename); return; } //end if strncpy(logfile.filename, filename, MAX_LOGFILENAMESIZE); botimport.Print(PRT_MESSAGE, "Opened log %s\n", logfile.filename); } //end of the function Log_Create //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void Log_Close(void) { if (!logfile.fp) return; if (fclose(logfile.fp)) { botimport.Print(PRT_ERROR, "can't close log file %s\n", logfile.filename); return; } //end if logfile.fp = NULL; botimport.Print(PRT_MESSAGE, "Closed log %s\n", logfile.filename); } //end of the function Log_Close //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void Log_Shutdown(void) { if (logfile.fp) Log_Close(); } //end of the function Log_Shutdown //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void QDECL Log_Write(char *fmt, ...) { va_list ap; if (!logfile.fp) return; va_start(ap, fmt); vfprintf(logfile.fp, fmt, ap); va_end(ap); //fprintf(logfile.fp, "\r\n"); fflush(logfile.fp); } //end of the function Log_Write //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void QDECL Log_WriteTimeStamped(char *fmt, ...) { va_list ap; if (!logfile.fp) return; fprintf(logfile.fp, "%d %02d:%02d:%02d:%02d ", logfile.numwrites, (int) (botlibglobals.time / 60 / 60), (int) (botlibglobals.time / 60), (int) (botlibglobals.time), (int) ((int) (botlibglobals.time * 100)) - ((int) botlibglobals.time) * 100); va_start(ap, fmt); vfprintf(logfile.fp, fmt, ap); va_end(ap); fprintf(logfile.fp, "\r\n"); logfile.numwrites++; fflush(logfile.fp); } //end of the function Log_Write //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== FILE *Log_FilePointer(void) { return logfile.fp; } //end of the function Log_FilePointer //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void Log_Flush(void) { if (logfile.fp) fflush(logfile.fp); } //end of the function Log_Flush ================================================ FILE: src/engine/botlib/l_log.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: l_log.h * * desc: log file * * $Archive: /source/code/botlib/l_log.h $ * *****************************************************************************/ //open a log file void Log_Open(char *filename); //close the current log file void Log_Close(void); //close log file if present void Log_Shutdown(void); //write to the current opened log file void QDECL Log_Write(char *fmt, ...); //write to the current opened log file with a time stamp void QDECL Log_WriteTimeStamped(char *fmt, ...); //returns a pointer to the log file FILE *Log_FilePointer(void); //flush log file void Log_Flush(void); ================================================ FILE: src/engine/botlib/l_memory.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: l_memory.c * * desc: memory allocation * * $Archive: /MissionPack/code/botlib/l_memory.c $ * *****************************************************************************/ #include "../../game/q_shared.h" #include "../../game/botlib.h" #include "l_log.h" #include "be_interface.h" //#define MEMDEBUG //#define MEMORYMANEGER #define MEM_ID 0x12345678l #define HUNK_ID 0x87654321l int allocatedmemory; int totalmemorysize; int numblocks; #ifdef MEMORYMANEGER typedef struct memoryblock_s { unsigned long int id; void *ptr; int size; #ifdef MEMDEBUG char *label; char *file; int line; #endif //MEMDEBUG struct memoryblock_s *prev, *next; } memoryblock_t; memoryblock_t *memory; //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void LinkMemoryBlock(memoryblock_t *block) { block->prev = NULL; block->next = memory; if (memory) memory->prev = block; memory = block; } //end of the function LinkMemoryBlock //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void UnlinkMemoryBlock(memoryblock_t *block) { if (block->prev) block->prev->next = block->next; else memory = block->next; if (block->next) block->next->prev = block->prev; } //end of the function UnlinkMemoryBlock //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== #ifdef MEMDEBUG void *GetMemoryDebug(unsigned long size, char *label, char *file, int line) #else void *GetMemory(unsigned long size) #endif //MEMDEBUG { void *ptr; memoryblock_t *block; assert(botimport.GetMemory); // bk001129 - was NULL'ed ptr = botimport.GetMemory(size + sizeof(memoryblock_t)); block = (memoryblock_t *) ptr; block->id = MEM_ID; block->ptr = (char *) ptr + sizeof(memoryblock_t); block->size = size + sizeof(memoryblock_t); #ifdef MEMDEBUG block->label = label; block->file = file; block->line = line; #endif //MEMDEBUG LinkMemoryBlock(block); allocatedmemory += block->size; totalmemorysize += block->size + sizeof(memoryblock_t); numblocks++; return block->ptr; } //end of the function GetMemoryDebug //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== #ifdef MEMDEBUG void *GetClearedMemoryDebug(unsigned long size, char *label, char *file, int line) #else void *GetClearedMemory(unsigned long size) #endif //MEMDEBUG { void *ptr; #ifdef MEMDEBUG ptr = GetMemoryDebug(size, label, file, line); #else ptr = GetMemory(size); #endif //MEMDEBUG Com_Memset(ptr, 0, size); return ptr; } //end of the function GetClearedMemory //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== #ifdef MEMDEBUG void *GetHunkMemoryDebug(unsigned long size, char *label, char *file, int line) #else void *GetHunkMemory(unsigned long size) #endif //MEMDEBUG { void *ptr; memoryblock_t *block; ptr = botimport.HunkAlloc(size + sizeof(memoryblock_t)); block = (memoryblock_t *) ptr; block->id = HUNK_ID; block->ptr = (char *) ptr + sizeof(memoryblock_t); block->size = size + sizeof(memoryblock_t); #ifdef MEMDEBUG block->label = label; block->file = file; block->line = line; #endif //MEMDEBUG LinkMemoryBlock(block); allocatedmemory += block->size; totalmemorysize += block->size + sizeof(memoryblock_t); numblocks++; return block->ptr; } //end of the function GetHunkMemoryDebug //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== #ifdef MEMDEBUG void *GetClearedHunkMemoryDebug(unsigned long size, char *label, char *file, int line) #else void *GetClearedHunkMemory(unsigned long size) #endif //MEMDEBUG { void *ptr; #ifdef MEMDEBUG ptr = GetHunkMemoryDebug(size, label, file, line); #else ptr = GetHunkMemory(size); #endif //MEMDEBUG Com_Memset(ptr, 0, size); return ptr; } //end of the function GetClearedHunkMemory //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== memoryblock_t *BlockFromPointer(void *ptr, char *str) { memoryblock_t *block; if (!ptr) { #ifdef MEMDEBUG //char *crash = (char *) NULL; //crash[0] = 1; botimport.Print(PRT_FATAL, "%s: NULL pointer\n", str); #endif // MEMDEBUG return NULL; } //end if block = (memoryblock_t *) ((char *) ptr - sizeof(memoryblock_t)); if (block->id != MEM_ID && block->id != HUNK_ID) { botimport.Print(PRT_FATAL, "%s: invalid memory block\n", str); return NULL; } //end if if (block->ptr != ptr) { botimport.Print(PRT_FATAL, "%s: memory block pointer invalid\n", str); return NULL; } //end if return block; } //end of the function BlockFromPointer //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void FreeMemory(void *ptr) { memoryblock_t *block; block = BlockFromPointer(ptr, "FreeMemory"); if (!block) return; UnlinkMemoryBlock(block); allocatedmemory -= block->size; totalmemorysize -= block->size + sizeof(memoryblock_t); numblocks--; // if (block->id == MEM_ID) { botimport.FreeMemory(block); } //end if } //end of the function FreeMemory //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AvailableMemory(void) { return botimport.AvailableMemory(); } //end of the function AvailableMemory //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int MemoryByteSize(void *ptr) { memoryblock_t *block; block = BlockFromPointer(ptr, "MemoryByteSize"); if (!block) return 0; return block->size; } //end of the function MemoryByteSize //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void PrintUsedMemorySize(void) { botimport.Print(PRT_MESSAGE, "total allocated memory: %d KB\n", allocatedmemory >> 10); botimport.Print(PRT_MESSAGE, "total botlib memory: %d KB\n", totalmemorysize >> 10); botimport.Print(PRT_MESSAGE, "total memory blocks: %d\n", numblocks); } //end of the function PrintUsedMemorySize //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void PrintMemoryLabels(void) { memoryblock_t *block; int i; PrintUsedMemorySize(); i = 0; Log_Write("============= Botlib memory log ==============\r\n"); Log_Write("\r\n"); for (block = memory; block; block = block->next) { #ifdef MEMDEBUG if (block->id == HUNK_ID) { Log_Write("%6d, hunk %p, %8d: %24s line %6d: %s\r\n", i, block->ptr, block->size, block->file, block->line, block->label); } //end if else { Log_Write("%6d, %p, %8d: %24s line %6d: %s\r\n", i, block->ptr, block->size, block->file, block->line, block->label); } //end else #endif //MEMDEBUG i++; } //end for } //end of the function PrintMemoryLabels //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void DumpMemory(void) { memoryblock_t *block; for (block = memory; block; block = memory) { FreeMemory(block->ptr); } //end for totalmemorysize = 0; allocatedmemory = 0; } //end of the function DumpMemory #else //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== #ifdef MEMDEBUG void *GetMemoryDebug(unsigned long size, char *label, char *file, int line) #else void *GetMemory(unsigned long size) #endif //MEMDEBUG { void *ptr; unsigned long int *memid; ptr = botimport.GetMemory(size + sizeof(unsigned long int)); if (!ptr) return NULL; memid = (unsigned long int *) ptr; *memid = MEM_ID; return (unsigned long int *) ((char *) ptr + sizeof(unsigned long int)); } //end of the function GetMemory //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== #ifdef MEMDEBUG void *GetClearedMemoryDebug(unsigned long size, char *label, char *file, int line) #else void *GetClearedMemory(unsigned long size) #endif //MEMDEBUG { void *ptr; #ifdef MEMDEBUG ptr = GetMemoryDebug(size, label, file, line); #else ptr = GetMemory(size); #endif //MEMDEBUG Com_Memset(ptr, 0, size); return ptr; } //end of the function GetClearedMemory //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== #ifdef MEMDEBUG void *GetHunkMemoryDebug(unsigned long size, char *label, char *file, int line) #else void *GetHunkMemory(unsigned long size) #endif //MEMDEBUG { void *ptr; unsigned long int *memid; ptr = botimport.HunkAlloc(size + sizeof(unsigned long int)); if (!ptr) return NULL; memid = (unsigned long int *) ptr; *memid = HUNK_ID; return (unsigned long int *) ((char *) ptr + sizeof(unsigned long int)); } //end of the function GetHunkMemory //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== #ifdef MEMDEBUG void *GetClearedHunkMemoryDebug(unsigned long size, char *label, char *file, int line) #else void *GetClearedHunkMemory(unsigned long size) #endif //MEMDEBUG { void *ptr; #ifdef MEMDEBUG ptr = GetHunkMemoryDebug(size, label, file, line); #else ptr = GetHunkMemory(size); #endif //MEMDEBUG Com_Memset(ptr, 0, size); return ptr; } //end of the function GetClearedHunkMemory //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void FreeMemory(void *ptr) { unsigned long int *memid; memid = (unsigned long int *) ((char *) ptr - sizeof(unsigned long int)); if (*memid == MEM_ID) { botimport.FreeMemory(memid); } //end if } //end of the function FreeMemory //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AvailableMemory(void) { return botimport.AvailableMemory(); } //end of the function AvailableMemory //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void PrintUsedMemorySize(void) { } //end of the function PrintUsedMemorySize //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void PrintMemoryLabels(void) { } //end of the function PrintMemoryLabels #endif ================================================ FILE: src/engine/botlib/l_memory.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: l_memory.h * * desc: memory management * * $Archive: /source/code/botlib/l_memory.h $ * *****************************************************************************/ //#define MEMDEBUG #ifdef MEMDEBUG #define GetMemory(size) GetMemoryDebug(size, #size, __FILE__, __LINE__); #define GetClearedMemory(size) GetClearedMemoryDebug(size, #size, __FILE__, __LINE__); //allocate a memory block of the given size void *GetMemoryDebug(unsigned long size, char *label, char *file, int line); //allocate a memory block of the given size and clear it void *GetClearedMemoryDebug(unsigned long size, char *label, char *file, int line); // #define GetHunkMemory(size) GetHunkMemoryDebug(size, #size, __FILE__, __LINE__); #define GetClearedHunkMemory(size) GetClearedHunkMemoryDebug(size, #size, __FILE__, __LINE__); //allocate a memory block of the given size void *GetHunkMemoryDebug(unsigned long size, char *label, char *file, int line); //allocate a memory block of the given size and clear it void *GetClearedHunkMemoryDebug(unsigned long size, char *label, char *file, int line); #else //allocate a memory block of the given size void *GetMemory(unsigned long size); //allocate a memory block of the given size and clear it void *GetClearedMemory(unsigned long size); // #ifdef BSPC #define GetHunkMemory GetMemory #define GetClearedHunkMemory GetClearedMemory #else //allocate a memory block of the given size void *GetHunkMemory(unsigned long size); //allocate a memory block of the given size and clear it void *GetClearedHunkMemory(unsigned long size); #endif #endif //free the given memory block void FreeMemory(void *ptr); //returns the amount available memory int AvailableMemory(void); //prints the total used memory size void PrintUsedMemorySize(void); //print all memory blocks with label void PrintMemoryLabels(void); //returns the size of the memory block in bytes int MemoryByteSize(void *ptr); //free all allocated memory void DumpMemory(void); ================================================ FILE: src/engine/botlib/l_precomp.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: l_precomp.c * * desc: pre compiler * * $Archive: /MissionPack/code/botlib/l_precomp.c $ * *****************************************************************************/ //Notes: fix: PC_StringizeTokens //#define SCREWUP //#define BOTLIB //#define QUAKE //#define QUAKEC //#define MEQCC #ifdef SCREWUP #include #include #include #include #include #include #include "l_memory.h" #include "l_script.h" #include "l_precomp.h" typedef enum {qfalse, qtrue} qboolean; #endif //SCREWUP #ifdef BOTLIB #include "../../game/q_shared.h" #include "../../game/botlib.h" #include "be_interface.h" #include "l_memory.h" #include "l_script.h" #include "l_precomp.h" #include "l_log.h" #endif //BOTLIB #ifdef MEQCC #include "qcc.h" #include "time.h" //time & ctime #include "math.h" //fabs #include "l_memory.h" #include "l_script.h" #include "l_precomp.h" #include "l_log.h" #define qtrue true #define qfalse false #endif //MEQCC #ifdef BSPC //include files for usage in the BSP Converter #include "../bspc/qbsp.h" #include "../bspc/l_log.h" #include "../bspc/l_mem.h" #include "l_precomp.h" #define qtrue true #define qfalse false #define Q_stricmp stricmp #endif //BSPC #if defined(QUAKE) && !defined(BSPC) #include "l_utils.h" #endif //QUAKE //#define DEBUG_EVAL #define MAX_DEFINEPARMS 128 #define DEFINEHASHING 1 //directive name with parse function typedef struct directive_s { char *name; int (*func)(source_t *source); } directive_t; #define DEFINEHASHSIZE 1024 #define TOKEN_HEAP_SIZE 4096 int numtokens; /* int tokenheapinitialized; //true when the token heap is initialized token_t token_heap[TOKEN_HEAP_SIZE]; //heap with tokens token_t *freetokens; //free tokens from the heap */ //list with global defines added to every source loaded define_t *globaldefines; //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ void QDECL SourceError(source_t *source, char *str, ...) { char text[1024]; va_list ap; va_start(ap, str); vsprintf(text, str, ap); va_end(ap); #ifdef BOTLIB botimport.Print(PRT_ERROR, "file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text); #endif //BOTLIB #ifdef MEQCC printf("error: file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text); #endif //MEQCC #ifdef BSPC Log_Print("error: file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text); #endif //BSPC } //end of the function SourceError //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void QDECL SourceWarning(source_t *source, char *str, ...) { char text[1024]; va_list ap; va_start(ap, str); vsprintf(text, str, ap); va_end(ap); #ifdef BOTLIB botimport.Print(PRT_WARNING, "file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text); #endif //BOTLIB #ifdef MEQCC printf("warning: file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text); #endif //MEQCC #ifdef BSPC Log_Print("warning: file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text); #endif //BSPC } //end of the function ScriptWarning //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ void PC_PushIndent(source_t *source, int type, int skip) { indent_t *indent; indent = (indent_t *) GetMemory(sizeof(indent_t)); indent->type = type; indent->script = source->scriptstack; indent->skip = (skip != 0); source->skip += indent->skip; indent->next = source->indentstack; source->indentstack = indent; } //end of the function PC_PushIndent //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ void PC_PopIndent(source_t *source, int *type, int *skip) { indent_t *indent; *type = 0; *skip = 0; indent = source->indentstack; if (!indent) return; //must be an indent from the current script if (source->indentstack->script != source->scriptstack) return; *type = indent->type; *skip = indent->skip; source->indentstack = source->indentstack->next; source->skip -= indent->skip; FreeMemory(indent); } //end of the function PC_PopIndent //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ void PC_PushScript(source_t *source, script_t *script) { script_t *s; for (s = source->scriptstack; s; s = s->next) { if (!Q_stricmp(s->filename, script->filename)) { SourceError(source, "%s recursively included", script->filename); return; } //end if } //end for //push the script on the script stack script->next = source->scriptstack; source->scriptstack = script; } //end of the function PC_PushScript //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ void PC_InitTokenHeap(void) { /* int i; if (tokenheapinitialized) return; freetokens = NULL; for (i = 0; i < TOKEN_HEAP_SIZE; i++) { token_heap[i].next = freetokens; freetokens = &token_heap[i]; } //end for tokenheapinitialized = qtrue; */ } //end of the function PC_InitTokenHeap //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ token_t *PC_CopyToken(token_t *token) { token_t *t; // t = (token_t *) malloc(sizeof(token_t)); t = (token_t *) GetMemory(sizeof(token_t)); // t = freetokens; if (!t) { #ifdef BSPC Error("out of token space\n"); #else Com_Error(ERR_FATAL, "out of token space\n"); #endif return NULL; } //end if // freetokens = freetokens->next; Com_Memcpy(t, token, sizeof(token_t)); t->next = NULL; numtokens++; return t; } //end of the function PC_CopyToken //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ void PC_FreeToken(token_t *token) { //free(token); FreeMemory(token); // token->next = freetokens; // freetokens = token; numtokens--; } //end of the function PC_FreeToken //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_ReadSourceToken(source_t *source, token_t *token) { token_t *t; script_t *script; int type, skip; //if there's no token already available while(!source->tokens) { //if there's a token to read from the script if (PS_ReadToken(source->scriptstack, token)) return qtrue; //if at the end of the script if (EndOfScript(source->scriptstack)) { //remove all indents of the script while(source->indentstack && source->indentstack->script == source->scriptstack) { SourceWarning(source, "missing #endif"); PC_PopIndent(source, &type, &skip); } //end if } //end if //if this was the initial script if (!source->scriptstack->next) return qfalse; //remove the script and return to the last one script = source->scriptstack; source->scriptstack = source->scriptstack->next; FreeScript(script); } //end while //copy the already available token Com_Memcpy(token, source->tokens, sizeof(token_t)); //free the read token t = source->tokens; source->tokens = source->tokens->next; PC_FreeToken(t); return qtrue; } //end of the function PC_ReadSourceToken //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_UnreadSourceToken(source_t *source, token_t *token) { token_t *t; t = PC_CopyToken(token); t->next = source->tokens; source->tokens = t; return qtrue; } //end of the function PC_UnreadSourceToken //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_ReadDefineParms(source_t *source, define_t *define, token_t **parms, int maxparms) { token_t token, *t, *last; int i, done, lastcomma, numparms, indent; if (!PC_ReadSourceToken(source, &token)) { SourceError(source, "define %s missing parms", define->name); return qfalse; } //end if // if (define->numparms > maxparms) { SourceError(source, "define with more than %d parameters", maxparms); return qfalse; } //end if // for (i = 0; i < define->numparms; i++) parms[i] = NULL; //if no leading "(" if (strcmp(token.string, "(")) { PC_UnreadSourceToken(source, &token); SourceError(source, "define %s missing parms", define->name); return qfalse; } //end if //read the define parameters for (done = 0, numparms = 0, indent = 0; !done;) { if (numparms >= maxparms) { SourceError(source, "define %s with too many parms", define->name); return qfalse; } //end if if (numparms >= define->numparms) { SourceWarning(source, "define %s has too many parms", define->name); return qfalse; } //end if parms[numparms] = NULL; lastcomma = 1; last = NULL; while(!done) { // if (!PC_ReadSourceToken(source, &token)) { SourceError(source, "define %s incomplete", define->name); return qfalse; } //end if // if (!strcmp(token.string, ",")) { if (indent <= 0) { if (lastcomma) SourceWarning(source, "too many comma's"); lastcomma = 1; break; } //end if } //end if lastcomma = 0; // if (!strcmp(token.string, "(")) { indent++; continue; } //end if else if (!strcmp(token.string, ")")) { if (--indent <= 0) { if (!parms[define->numparms-1]) { SourceWarning(source, "too few define parms"); } //end if done = 1; break; } //end if } //end if // if (numparms < define->numparms) { // t = PC_CopyToken(&token); t->next = NULL; if (last) last->next = t; else parms[numparms] = t; last = t; } //end if } //end while numparms++; } //end for return qtrue; } //end of the function PC_ReadDefineParms //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_StringizeTokens(token_t *tokens, token_t *token) { token_t *t; token->type = TT_STRING; token->whitespace_p = NULL; token->endwhitespace_p = NULL; token->string[0] = '\0'; strcat(token->string, "\""); for (t = tokens; t; t = t->next) { strncat(token->string, t->string, MAX_TOKEN - (int)strlen(token->string)); } //end for strncat(token->string, "\"", MAX_TOKEN - (int)strlen(token->string)); return qtrue; } //end of the function PC_StringizeTokens //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_MergeTokens(token_t *t1, token_t *t2) { //merging of a name with a name or number if (t1->type == TT_NAME && (t2->type == TT_NAME || t2->type == TT_NUMBER)) { strcat(t1->string, t2->string); return qtrue; } //end if //merging of two strings if (t1->type == TT_STRING && t2->type == TT_STRING) { //remove trailing double quote t1->string[strlen(t1->string)-1] = '\0'; //concat without leading double quote strcat(t1->string, &t2->string[1]); return qtrue; } //end if //FIXME: merging of two number of the same sub type return qfalse; } //end of the function PC_MergeTokens //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ /* void PC_PrintDefine(define_t *define) { printf("define->name = %s\n", define->name); printf("define->flags = %d\n", define->flags); printf("define->builtin = %d\n", define->builtin); printf("define->numparms = %d\n", define->numparms); // token_t *parms; //define parameters // token_t *tokens; //macro tokens (possibly containing parm tokens) // struct define_s *next; //next defined macro in a list } //end of the function PC_PrintDefine*/ #if DEFINEHASHING //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ void PC_PrintDefineHashTable(define_t **definehash) { int i; define_t *d; for (i = 0; i < DEFINEHASHSIZE; i++) { Log_Write("%4d:", i); for (d = definehash[i]; d; d = d->hashnext) { Log_Write(" %s", d->name); } //end for Log_Write("\n"); } //end for } //end of the function PC_PrintDefineHashTable //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ //char primes[16] = {1, 3, 5, 7, 11, 13, 17, 19, 23, 27, 29, 31, 37, 41, 43, 47}; int PC_NameHash(char *name) { int register hash, i; hash = 0; for (i = 0; name[i] != '\0'; i++) { hash += name[i] * (119 + i); //hash += (name[i] << 7) + i; //hash += (name[i] << (i&15)); } //end while hash = (hash ^ (hash >> 10) ^ (hash >> 20)) & (DEFINEHASHSIZE-1); return hash; } //end of the function PC_NameHash //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ void PC_AddDefineToHash(define_t *define, define_t **definehash) { int hash; hash = PC_NameHash(define->name); define->hashnext = definehash[hash]; definehash[hash] = define; } //end of the function PC_AddDefineToHash //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ define_t *PC_FindHashedDefine(define_t **definehash, char *name) { define_t *d; int hash; hash = PC_NameHash(name); for (d = definehash[hash]; d; d = d->hashnext) { if (!strcmp(d->name, name)) return d; } //end for return NULL; } //end of the function PC_FindHashedDefine #endif //DEFINEHASHING //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ define_t *PC_FindDefine(define_t *defines, char *name) { define_t *d; for (d = defines; d; d = d->next) { if (!strcmp(d->name, name)) return d; } //end for return NULL; } //end of the function PC_FindDefine //============================================================================ // // Parameter: - // Returns: number of the parm // if no parm found with the given name -1 is returned // Changes Globals: - //============================================================================ int PC_FindDefineParm(define_t *define, char *name) { token_t *p; int i; i = 0; for (p = define->parms; p; p = p->next) { if (!strcmp(p->string, name)) return i; i++; } //end for return -1; } //end of the function PC_FindDefineParm //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ void PC_FreeDefine(define_t *define) { token_t *t, *next; //free the define parameters for (t = define->parms; t; t = next) { next = t->next; PC_FreeToken(t); } //end for //free the define tokens for (t = define->tokens; t; t = next) { next = t->next; PC_FreeToken(t); } //end for //free the define FreeMemory(define); } //end of the function PC_FreeDefine //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ void PC_AddBuiltinDefines(source_t *source) { int i; define_t *define; struct builtinStruct { char *string; int builtin; builtinStruct(char* istring, int ibuiltin) : string(istring), builtin(ibuiltin) {} } builtin[] = { // bk001204 - brackets { "__LINE__", BUILTIN_LINE }, { "__FILE__", BUILTIN_FILE }, { "__DATE__", BUILTIN_DATE }, { "__TIME__", BUILTIN_TIME }, // { "__STDC__", BUILTIN_STDC }, { NULL, 0 } }; for (i = 0; builtin[i].string; i++) { define = (define_t *) GetMemory(sizeof(define_t) + (int)strlen(builtin[i].string) + 1); Com_Memset(define, 0, sizeof(define_t)); define->name = (char *) define + sizeof(define_t); strcpy(define->name, builtin[i].string); define->flags |= DEFINE_FIXED; define->builtin = builtin[i].builtin; //add the define to the source #if DEFINEHASHING PC_AddDefineToHash(define, source->definehash); #else define->next = source->defines; source->defines = define; #endif //DEFINEHASHING } //end for } //end of the function PC_AddBuiltinDefines //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_ExpandBuiltinDefine(source_t *source, token_t *deftoken, define_t *define, token_t **firsttoken, token_t **lasttoken) { token_t *token; time_t t; char *curtime; token = PC_CopyToken(deftoken); switch(define->builtin) { case BUILTIN_LINE: { sprintf(token->string, "%d", deftoken->line); #ifdef NUMBERVALUE token->intvalue = deftoken->line; token->floatvalue = deftoken->line; #endif //NUMBERVALUE token->type = TT_NUMBER; token->subtype = TT_DECIMAL | TT_INTEGER; *firsttoken = token; *lasttoken = token; break; } //end case case BUILTIN_FILE: { strcpy(token->string, source->scriptstack->filename); token->type = TT_NAME; token->subtype = (int)strlen(token->string); *firsttoken = token; *lasttoken = token; break; } //end case case BUILTIN_DATE: { t = time(NULL); curtime = ctime((const time_t*)&t); strcpy(token->string, "\""); strncat(token->string, curtime+4, 7); strncat(token->string+7, curtime+20, 4); strcat(token->string, "\""); free(curtime); token->type = TT_NAME; token->subtype = (int)strlen(token->string); *firsttoken = token; *lasttoken = token; break; } //end case case BUILTIN_TIME: { t = time(NULL); curtime = ctime((const time_t*)&t); strcpy(token->string, "\""); strncat(token->string, curtime+11, 8); strcat(token->string, "\""); free(curtime); token->type = TT_NAME; token->subtype = (int)strlen(token->string); *firsttoken = token; *lasttoken = token; break; } //end case case BUILTIN_STDC: default: { *firsttoken = NULL; *lasttoken = NULL; break; } //end case } //end switch return qtrue; } //end of the function PC_ExpandBuiltinDefine //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_ExpandDefine(source_t *source, token_t *deftoken, define_t *define, token_t **firsttoken, token_t **lasttoken) { token_t *parms[MAX_DEFINEPARMS], *dt, *pt, *t; token_t *t1, *t2, *first, *last, *nextpt, token; int parmnum, i; //if it is a builtin define if (define->builtin) { return PC_ExpandBuiltinDefine(source, deftoken, define, firsttoken, lasttoken); } //end if //if the define has parameters if (define->numparms) { if (!PC_ReadDefineParms(source, define, parms, MAX_DEFINEPARMS)) return qfalse; #ifdef DEBUG_EVAL for (i = 0; i < define->numparms; i++) { Log_Write("define parms %d:", i); for (pt = parms[i]; pt; pt = pt->next) { Log_Write("%s", pt->string); } //end for } //end for #endif //DEBUG_EVAL } //end if //empty list at first first = NULL; last = NULL; //create a list with tokens of the expanded define for (dt = define->tokens; dt; dt = dt->next) { parmnum = -1; //if the token is a name, it could be a define parameter if (dt->type == TT_NAME) { parmnum = PC_FindDefineParm(define, dt->string); } //end if //if it is a define parameter if (parmnum >= 0) { for (pt = parms[parmnum]; pt; pt = pt->next) { t = PC_CopyToken(pt); //add the token to the list t->next = NULL; if (last) last->next = t; else first = t; last = t; } //end for } //end if else { //if stringizing operator if (dt->string[0] == '#' && dt->string[1] == '\0') { //the stringizing operator must be followed by a define parameter if (dt->next) parmnum = PC_FindDefineParm(define, dt->next->string); else parmnum = -1; // if (parmnum >= 0) { //step over the stringizing operator dt = dt->next; //stringize the define parameter tokens if (!PC_StringizeTokens(parms[parmnum], &token)) { SourceError(source, "can't stringize tokens"); return qfalse; } //end if t = PC_CopyToken(&token); } //end if else { SourceWarning(source, "stringizing operator without define parameter"); continue; } //end if } //end if else { t = PC_CopyToken(dt); } //end else //add the token to the list t->next = NULL; if (last) last->next = t; else first = t; last = t; } //end else } //end for //check for the merging operator for (t = first; t; ) { if (t->next) { //if the merging operator if (t->next->string[0] == '#' && t->next->string[1] == '#') { t1 = t; t2 = t->next->next; if (t2) { if (!PC_MergeTokens(t1, t2)) { SourceError(source, "can't merge %s with %s", t1->string, t2->string); return qfalse; } //end if PC_FreeToken(t1->next); t1->next = t2->next; if (t2 == last) last = t1; PC_FreeToken(t2); continue; } //end if } //end if } //end if t = t->next; } //end for //store the first and last token of the list *firsttoken = first; *lasttoken = last; //free all the parameter tokens for (i = 0; i < define->numparms; i++) { for (pt = parms[i]; pt; pt = nextpt) { nextpt = pt->next; PC_FreeToken(pt); } //end for } //end for // return qtrue; } //end of the function PC_ExpandDefine //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_ExpandDefineIntoSource(source_t *source, token_t *deftoken, define_t *define) { token_t *firsttoken, *lasttoken; if (!PC_ExpandDefine(source, deftoken, define, &firsttoken, &lasttoken)) return qfalse; if (firsttoken && lasttoken) { lasttoken->next = source->tokens; source->tokens = firsttoken; return qtrue; } //end if return qfalse; } //end of the function PC_ExpandDefineIntoSource //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ void PC_ConvertPath(char *path) { char *ptr; //remove double path seperators for (ptr = path; *ptr;) { if ((*ptr == '\\' || *ptr == '/') && (*(ptr+1) == '\\' || *(ptr+1) == '/')) { strcpy(ptr, ptr+1); } //end if else { ptr++; } //end else } //end while //set OS dependent path seperators for (ptr = path; *ptr;) { if (*ptr == '/' || *ptr == '\\') *ptr = PATHSEPERATOR_CHAR; ptr++; } //end while } //end of the function PC_ConvertPath //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_Directive_include(source_t *source) { script_t *script; token_t token; char path[MAX_PATH]; #ifdef QUAKE foundfile_t file; #endif //QUAKE if (source->skip > 0) return qtrue; // if (!PC_ReadSourceToken(source, &token)) { SourceError(source, "#include without file name"); return qfalse; } //end if if (token.linescrossed > 0) { SourceError(source, "#include without file name"); return qfalse; } //end if if (token.type == TT_STRING) { StripDoubleQuotes(token.string); PC_ConvertPath(token.string); script = LoadScriptFile(token.string); if (!script) { strcpy(path, source->includepath); strcat(path, token.string); script = LoadScriptFile(path); } //end if } //end if else if (token.type == TT_PUNCTUATION && *token.string == '<') { strcpy(path, source->includepath); while(PC_ReadSourceToken(source, &token)) { if (token.linescrossed > 0) { PC_UnreadSourceToken(source, &token); break; } //end if if (token.type == TT_PUNCTUATION && *token.string == '>') break; strncat(path, token.string, MAX_PATH); } //end while if (*token.string != '>') { SourceWarning(source, "#include missing trailing >"); } //end if if (!strlen(path)) { SourceError(source, "#include without file name between < >"); return qfalse; } //end if PC_ConvertPath(path); script = LoadScriptFile(path); } //end if else { SourceError(source, "#include without file name"); return qfalse; } //end else #ifdef QUAKE if (!script) { Com_Memset(&file, 0, sizeof(foundfile_t)); script = LoadScriptFile(path); if (script) strncpy(script->filename, path, MAX_PATH); } //end if #endif //QUAKE if (!script) { #ifdef SCREWUP SourceWarning(source, "file %s not found", path); return qtrue; #else SourceError(source, "file %s not found", path); return qfalse; #endif //SCREWUP } //end if PC_PushScript(source, script); return qtrue; } //end of the function PC_Directive_include //============================================================================ // reads a token from the current line, continues reading on the next // line only if a backslash '\' is encountered. // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_ReadLine(source_t *source, token_t *token) { int crossline; crossline = 0; do { if (!PC_ReadSourceToken(source, token)) return qfalse; if (token->linescrossed > crossline) { PC_UnreadSourceToken(source, token); return qfalse; } //end if crossline = 1; } while(!strcmp(token->string, "\\")); return qtrue; } //end of the function PC_ReadLine //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_WhiteSpaceBeforeToken(token_t *token) { return token->endwhitespace_p - token->whitespace_p > 0; } //end of the function PC_WhiteSpaceBeforeToken //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ void PC_ClearTokenWhiteSpace(token_t *token) { token->whitespace_p = NULL; token->endwhitespace_p = NULL; token->linescrossed = 0; } //end of the function PC_ClearTokenWhiteSpace //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_Directive_undef(source_t *source) { token_t token; define_t *define, *lastdefine; int hash; if (source->skip > 0) return qtrue; // if (!PC_ReadLine(source, &token)) { SourceError(source, "undef without name"); return qfalse; } //end if if (token.type != TT_NAME) { PC_UnreadSourceToken(source, &token); SourceError(source, "expected name, found %s", token.string); return qfalse; } //end if #if DEFINEHASHING hash = PC_NameHash(token.string); for (lastdefine = NULL, define = source->definehash[hash]; define; define = define->hashnext) { if (!strcmp(define->name, token.string)) { if (define->flags & DEFINE_FIXED) { SourceWarning(source, "can't undef %s", token.string); } //end if else { if (lastdefine) lastdefine->hashnext = define->hashnext; else source->definehash[hash] = define->hashnext; PC_FreeDefine(define); } //end else break; } //end if lastdefine = define; } //end for #else //DEFINEHASHING for (lastdefine = NULL, define = source->defines; define; define = define->next) { if (!strcmp(define->name, token.string)) { if (define->flags & DEFINE_FIXED) { SourceWarning(source, "can't undef %s", token.string); } //end if else { if (lastdefine) lastdefine->next = define->next; else source->defines = define->next; PC_FreeDefine(define); } //end else break; } //end if lastdefine = define; } //end for #endif //DEFINEHASHING return qtrue; } //end of the function PC_Directive_undef //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_Directive_define(source_t *source) { token_t token, *t, *last; define_t *define; if (source->skip > 0) return qtrue; // if (!PC_ReadLine(source, &token)) { SourceError(source, "#define without name"); return qfalse; } //end if if (token.type != TT_NAME) { PC_UnreadSourceToken(source, &token); SourceError(source, "expected name after #define, found %s", token.string); return qfalse; } //end if //check if the define already exists #if DEFINEHASHING define = PC_FindHashedDefine(source->definehash, token.string); #else define = PC_FindDefine(source->defines, token.string); #endif //DEFINEHASHING if (define) { if (define->flags & DEFINE_FIXED) { SourceError(source, "can't redefine %s", token.string); return qfalse; } //end if SourceWarning(source, "redefinition of %s", token.string); //unread the define name before executing the #undef directive PC_UnreadSourceToken(source, &token); if (!PC_Directive_undef(source)) return qfalse; //if the define was not removed (define->flags & DEFINE_FIXED) #if DEFINEHASHING define = PC_FindHashedDefine(source->definehash, token.string); #else define = PC_FindDefine(source->defines, token.string); #endif //DEFINEHASHING } //end if //allocate define define = (define_t *) GetMemory(sizeof(define_t) + (int)strlen(token.string) + 1); Com_Memset(define, 0, sizeof(define_t)); define->name = (char *) define + sizeof(define_t); strcpy(define->name, token.string); //add the define to the source #if DEFINEHASHING PC_AddDefineToHash(define, source->definehash); #else //DEFINEHASHING define->next = source->defines; source->defines = define; #endif //DEFINEHASHING //if nothing is defined, just return if (!PC_ReadLine(source, &token)) return qtrue; //if it is a define with parameters if (!PC_WhiteSpaceBeforeToken(&token) && !strcmp(token.string, "(")) { //read the define parameters last = NULL; if (!PC_CheckTokenString(source, ")")) { while(1) { if (!PC_ReadLine(source, &token)) { SourceError(source, "expected define parameter"); return qfalse; } //end if //if it isn't a name if (token.type != TT_NAME) { SourceError(source, "invalid define parameter"); return qfalse; } //end if // if (PC_FindDefineParm(define, token.string) >= 0) { SourceError(source, "two the same define parameters"); return qfalse; } //end if //add the define parm t = PC_CopyToken(&token); PC_ClearTokenWhiteSpace(t); t->next = NULL; if (last) last->next = t; else define->parms = t; last = t; define->numparms++; //read next token if (!PC_ReadLine(source, &token)) { SourceError(source, "define parameters not terminated"); return qfalse; } //end if // if (!strcmp(token.string, ")")) break; //then it must be a comma if (strcmp(token.string, ",")) { SourceError(source, "define not terminated"); return qfalse; } //end if } //end while } //end if if (!PC_ReadLine(source, &token)) return qtrue; } //end if //read the defined stuff last = NULL; do { t = PC_CopyToken(&token); if (t->type == TT_NAME && !strcmp(t->string, define->name)) { SourceError(source, "recursive define (removed recursion)"); continue; } //end if PC_ClearTokenWhiteSpace(t); t->next = NULL; if (last) last->next = t; else define->tokens = t; last = t; } while(PC_ReadLine(source, &token)); // if (last) { //check for merge operators at the beginning or end if (!strcmp(define->tokens->string, "##") || !strcmp(last->string, "##")) { SourceError(source, "define with misplaced ##"); return qfalse; } //end if } //end if return qtrue; } //end of the function PC_Directive_define //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ define_t *PC_DefineFromString(char *string) { script_t *script; source_t src; token_t *t; int res, i; define_t *def; PC_InitTokenHeap(); script = LoadScriptMemory(string, (int)strlen(string), "*extern"); //create a new source Com_Memset(&src, 0, sizeof(source_t)); strncpy(src.filename, "*extern", MAX_PATH); src.scriptstack = script; #if DEFINEHASHING src.definehash = (define_t**) GetClearedMemory(DEFINEHASHSIZE * sizeof(define_t *)); #endif //DEFINEHASHING //create a define from the source res = PC_Directive_define(&src); //free any tokens if left for (t = src.tokens; t; t = src.tokens) { src.tokens = src.tokens->next; PC_FreeToken(t); } //end for #ifdef DEFINEHASHING def = NULL; for (i = 0; i < DEFINEHASHSIZE; i++) { if (src.definehash[i]) { def = src.definehash[i]; break; } //end if } //end for #else def = src.defines; #endif //DEFINEHASHING // #if DEFINEHASHING FreeMemory(src.definehash); #endif //DEFINEHASHING // FreeScript(script); //if the define was created succesfully if (res > 0) return def; //free the define is created if (src.defines) PC_FreeDefine(def); // return NULL; } //end of the function PC_DefineFromString //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_AddDefine(source_t *source, char *string) { define_t *define; define = PC_DefineFromString(string); if (!define) return qfalse; #if DEFINEHASHING PC_AddDefineToHash(define, source->definehash); #else //DEFINEHASHING define->next = source->defines; source->defines = define; #endif //DEFINEHASHING return qtrue; } //end of the function PC_AddDefine //============================================================================ // add a globals define that will be added to all opened sources // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_AddGlobalDefine(char *string) { define_t *define; define = PC_DefineFromString(string); if (!define) return qfalse; define->next = globaldefines; globaldefines = define; return qtrue; } //end of the function PC_AddGlobalDefine //============================================================================ // remove the given global define // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_RemoveGlobalDefine(char *name) { define_t *define; define = PC_FindDefine(globaldefines, name); if (define) { PC_FreeDefine(define); return qtrue; } //end if return qfalse; } //end of the function PC_RemoveGlobalDefine //============================================================================ // remove all globals defines // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ void PC_RemoveAllGlobalDefines(void) { define_t *define; for (define = globaldefines; define; define = globaldefines) { globaldefines = globaldefines->next; PC_FreeDefine(define); } //end for } //end of the function PC_RemoveAllGlobalDefines //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ define_t *PC_CopyDefine(source_t *source, define_t *define) { define_t *newdefine; token_t *token, *newtoken, *lasttoken; newdefine = (define_t *) GetMemory(sizeof(define_t) + (int)strlen(define->name) + 1); //copy the define name newdefine->name = (char *) newdefine + sizeof(define_t); strcpy(newdefine->name, define->name); newdefine->flags = define->flags; newdefine->builtin = define->builtin; newdefine->numparms = define->numparms; //the define is not linked newdefine->next = NULL; newdefine->hashnext = NULL; //copy the define tokens newdefine->tokens = NULL; for (lasttoken = NULL, token = define->tokens; token; token = token->next) { newtoken = PC_CopyToken(token); newtoken->next = NULL; if (lasttoken) lasttoken->next = newtoken; else newdefine->tokens = newtoken; lasttoken = newtoken; } //end for //copy the define parameters newdefine->parms = NULL; for (lasttoken = NULL, token = define->parms; token; token = token->next) { newtoken = PC_CopyToken(token); newtoken->next = NULL; if (lasttoken) lasttoken->next = newtoken; else newdefine->parms = newtoken; lasttoken = newtoken; } //end for return newdefine; } //end of the function PC_CopyDefine //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ void PC_AddGlobalDefinesToSource(source_t *source) { define_t *define, *newdefine; for (define = globaldefines; define; define = define->next) { newdefine = PC_CopyDefine(source, define); #if DEFINEHASHING PC_AddDefineToHash(newdefine, source->definehash); #else //DEFINEHASHING newdefine->next = source->defines; source->defines = newdefine; #endif //DEFINEHASHING } //end for } //end of the function PC_AddGlobalDefinesToSource //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_Directive_if_def(source_t *source, int type) { token_t token; define_t *d; int skip; if (!PC_ReadLine(source, &token)) { SourceError(source, "#ifdef without name"); return qfalse; } //end if if (token.type != TT_NAME) { PC_UnreadSourceToken(source, &token); SourceError(source, "expected name after #ifdef, found %s", token.string); return qfalse; } //end if #if DEFINEHASHING d = PC_FindHashedDefine(source->definehash, token.string); #else d = PC_FindDefine(source->defines, token.string); #endif //DEFINEHASHING skip = (type == INDENT_IFDEF) == (d == NULL); PC_PushIndent(source, type, skip); return qtrue; } //end of the function PC_Directiveif_def //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_Directive_ifdef(source_t *source) { return PC_Directive_if_def(source, INDENT_IFDEF); } //end of the function PC_Directive_ifdef //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_Directive_ifndef(source_t *source) { return PC_Directive_if_def(source, INDENT_IFNDEF); } //end of the function PC_Directive_ifndef //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_Directive_else(source_t *source) { int type, skip; PC_PopIndent(source, &type, &skip); if (!type) { SourceError(source, "misplaced #else"); return qfalse; } //end if if (type == INDENT_ELSE) { SourceError(source, "#else after #else"); return qfalse; } //end if PC_PushIndent(source, INDENT_ELSE, !skip); return qtrue; } //end of the function PC_Directive_else //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_Directive_endif(source_t *source) { int type, skip; PC_PopIndent(source, &type, &skip); if (!type) { SourceError(source, "misplaced #endif"); return qfalse; } //end if return qtrue; } //end of the function PC_Directive_endif //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ typedef struct operator_s { int operatorCode; int priority; int parentheses; struct operator_s *prev, *next; } operator_t; typedef struct value_s { signed long int intvalue; double floatvalue; int parentheses; struct value_s *prev, *next; } value_t; int PC_OperatorPriority(int op) { switch(op) { case P_MUL: return 15; case P_DIV: return 15; case P_MOD: return 15; case P_ADD: return 14; case P_SUB: return 14; case P_LOGIC_AND: return 7; case P_LOGIC_OR: return 6; case P_LOGIC_GEQ: return 12; case P_LOGIC_LEQ: return 12; case P_LOGIC_EQ: return 11; case P_LOGIC_UNEQ: return 11; case P_LOGIC_NOT: return 16; case P_LOGIC_GREATER: return 12; case P_LOGIC_LESS: return 12; case P_RSHIFT: return 13; case P_LSHIFT: return 13; case P_BIN_AND: return 10; case P_BIN_OR: return 8; case P_BIN_XOR: return 9; case P_BIN_NOT: return 16; case P_COLON: return 5; case P_QUESTIONMARK: return 5; } //end switch return qfalse; } //end of the function PC_OperatorPriority //#define AllocValue() GetClearedMemory(sizeof(value_t)); //#define FreeValue(val) FreeMemory(val) //#define AllocOperator(op) op = (operator_t *) GetClearedMemory(sizeof(operator_t)); //#define FreeOperator(op) FreeMemory(op); #define MAX_VALUES 64 #define MAX_OPERATORS 64 #define AllocValue(val) \ if (numvalues >= MAX_VALUES) { \ SourceError(source, "out of value space\n"); \ error = 1; \ break; \ } \ else \ val = &value_heap[numvalues++]; #define FreeValue(val) // #define AllocOperator(op) \ if (numoperators >= MAX_OPERATORS) { \ SourceError(source, "out of operator space\n"); \ error = 1; \ break; \ } \ else \ op = &operator_heap[numoperators++]; #define FreeOperator(op) int PC_EvaluateTokens(source_t *source, token_t *tokens, signed long int *intvalue, double *floatvalue, int integer) { operator_t *o, *firstoperator, *lastoperator; value_t *v, *firstvalue, *lastvalue, *v1, *v2; token_t *t; int brace = 0; int parentheses = 0; int error = 0; int lastwasvalue = 0; int negativevalue = 0; int questmarkintvalue = 0; double questmarkfloatvalue = 0; int gotquestmarkvalue = qfalse; int lastoperatortype = 0; // operator_t operator_heap[MAX_OPERATORS]; int numoperators = 0; value_t value_heap[MAX_VALUES]; int numvalues = 0; firstoperator = lastoperator = NULL; firstvalue = lastvalue = NULL; if (intvalue) *intvalue = 0; if (floatvalue) *floatvalue = 0; for (t = tokens; t; t = t->next) { switch(t->type) { case TT_NAME: { if (lastwasvalue || negativevalue) { SourceError(source, "syntax error in #if/#elif"); error = 1; break; } //end if if (strcmp(t->string, "defined")) { SourceError(source, "undefined name %s in #if/#elif", t->string); error = 1; break; } //end if t = t->next; if (!strcmp(t->string, "(")) { brace = qtrue; t = t->next; } //end if if (!t || t->type != TT_NAME) { SourceError(source, "defined without name in #if/#elif"); error = 1; break; } //end if //v = (value_t *) GetClearedMemory(sizeof(value_t)); AllocValue(v); #if DEFINEHASHING if (PC_FindHashedDefine(source->definehash, t->string)) #else if (PC_FindDefine(source->defines, t->string)) #endif //DEFINEHASHING { v->intvalue = 1; v->floatvalue = 1; } //end if else { v->intvalue = 0; v->floatvalue = 0; } //end else v->parentheses = parentheses; v->next = NULL; v->prev = lastvalue; if (lastvalue) lastvalue->next = v; else firstvalue = v; lastvalue = v; if (brace) { t = t->next; if (!t || strcmp(t->string, ")")) { SourceError(source, "defined without ) in #if/#elif"); error = 1; break; } //end if } //end if brace = qfalse; // defined() creates a value lastwasvalue = 1; break; } //end case case TT_NUMBER: { if (lastwasvalue) { SourceError(source, "syntax error in #if/#elif"); error = 1; break; } //end if //v = (value_t *) GetClearedMemory(sizeof(value_t)); AllocValue(v); if (negativevalue) { v->intvalue = - (signed int) t->intvalue; v->floatvalue = - t->floatvalue; } //end if else { v->intvalue = t->intvalue; v->floatvalue = t->floatvalue; } //end else v->parentheses = parentheses; v->next = NULL; v->prev = lastvalue; if (lastvalue) lastvalue->next = v; else firstvalue = v; lastvalue = v; //last token was a value lastwasvalue = 1; // negativevalue = 0; break; } //end case case TT_PUNCTUATION: { if (negativevalue) { SourceError(source, "misplaced minus sign in #if/#elif"); error = 1; break; } //end if if (t->subtype == P_PARENTHESESOPEN) { parentheses++; break; } //end if else if (t->subtype == P_PARENTHESESCLOSE) { parentheses--; if (parentheses < 0) { SourceError(source, "too many ) in #if/#elsif"); error = 1; } //end if break; } //end else if //check for invalid operators on floating point values if (!integer) { if (t->subtype == P_BIN_NOT || t->subtype == P_MOD || t->subtype == P_RSHIFT || t->subtype == P_LSHIFT || t->subtype == P_BIN_AND || t->subtype == P_BIN_OR || t->subtype == P_BIN_XOR) { SourceError(source, "illigal operator %s on floating point operands\n", t->string); error = 1; break; } //end if } //end if switch(t->subtype) { case P_LOGIC_NOT: case P_BIN_NOT: { if (lastwasvalue) { SourceError(source, "! or ~ after value in #if/#elif"); error = 1; break; } //end if break; } //end case case P_INC: case P_DEC: { SourceError(source, "++ or -- used in #if/#elif"); break; } //end case case P_SUB: { if (!lastwasvalue) { negativevalue = 1; break; } //end if } //end case case P_MUL: case P_DIV: case P_MOD: case P_ADD: case P_LOGIC_AND: case P_LOGIC_OR: case P_LOGIC_GEQ: case P_LOGIC_LEQ: case P_LOGIC_EQ: case P_LOGIC_UNEQ: case P_LOGIC_GREATER: case P_LOGIC_LESS: case P_RSHIFT: case P_LSHIFT: case P_BIN_AND: case P_BIN_OR: case P_BIN_XOR: case P_COLON: case P_QUESTIONMARK: { if (!lastwasvalue) { SourceError(source, "operator %s after operator in #if/#elif", t->string); error = 1; break; } //end if break; } //end case default: { SourceError(source, "invalid operator %s in #if/#elif", t->string); error = 1; break; } //end default } //end switch if (!error && !negativevalue) { //o = (operator_t *) GetClearedMemory(sizeof(operator_t)); AllocOperator(o); o->operatorCode = t->subtype; o->priority = PC_OperatorPriority(t->subtype); o->parentheses = parentheses; o->next = NULL; o->prev = lastoperator; if (lastoperator) lastoperator->next = o; else firstoperator = o; lastoperator = o; lastwasvalue = 0; } //end if break; } //end case default: { SourceError(source, "unknown %s in #if/#elif", t->string); error = 1; break; } //end default } //end switch if (error) break; } //end for if (!error) { if (!lastwasvalue) { SourceError(source, "trailing operator in #if/#elif"); error = 1; } //end if else if (parentheses) { SourceError(source, "too many ( in #if/#elif"); error = 1; } //end else if } //end if // gotquestmarkvalue = qfalse; questmarkintvalue = 0; questmarkfloatvalue = 0; //while there are operators while(!error && firstoperator) { v = firstvalue; for (o = firstoperator; o->next; o = o->next) { //if the current operator is nested deeper in parentheses //than the next operator if (o->parentheses > o->next->parentheses) break; //if the current and next operator are nested equally deep in parentheses if (o->parentheses == o->next->parentheses) { //if the priority of the current operator is equal or higher //than the priority of the next operator if (o->priority >= o->next->priority) break; } //end if //if the arity of the operator isn't equal to 1 if (o->operatorCode != P_LOGIC_NOT && o->operatorCode != P_BIN_NOT) v = v->next; //if there's no value or no next value if (!v) { SourceError(source, "mising values in #if/#elif"); error = 1; break; } //end if } //end for if (error) break; v1 = v; v2 = v->next; #ifdef DEBUG_EVAL if (integer) { Log_Write("operator %s, value1 = %d", PunctuationFromNum(source->scriptstack, o->operator), v1->intvalue); if (v2) Log_Write("value2 = %d", v2->intvalue); } //end if else { Log_Write("operator %s, value1 = %f", PunctuationFromNum(source->scriptstack, o->operator), v1->floatvalue); if (v2) Log_Write("value2 = %f", v2->floatvalue); } //end else #endif //DEBUG_EVAL switch(o->operatorCode) { case P_LOGIC_NOT: v1->intvalue = !v1->intvalue; v1->floatvalue = !v1->floatvalue; break; case P_BIN_NOT: v1->intvalue = ~v1->intvalue; break; case P_MUL: v1->intvalue *= v2->intvalue; v1->floatvalue *= v2->floatvalue; break; case P_DIV: if (!v2->intvalue || !v2->floatvalue) { SourceError(source, "divide by zero in #if/#elif\n"); error = 1; break; } v1->intvalue /= v2->intvalue; v1->floatvalue /= v2->floatvalue; break; case P_MOD: if (!v2->intvalue) { SourceError(source, "divide by zero in #if/#elif\n"); error = 1; break; } v1->intvalue %= v2->intvalue; break; case P_ADD: v1->intvalue += v2->intvalue; v1->floatvalue += v2->floatvalue; break; case P_SUB: v1->intvalue -= v2->intvalue; v1->floatvalue -= v2->floatvalue; break; case P_LOGIC_AND: v1->intvalue = v1->intvalue && v2->intvalue; v1->floatvalue = v1->floatvalue && v2->floatvalue; break; case P_LOGIC_OR: v1->intvalue = v1->intvalue || v2->intvalue; v1->floatvalue = v1->floatvalue || v2->floatvalue; break; case P_LOGIC_GEQ: v1->intvalue = v1->intvalue >= v2->intvalue; v1->floatvalue = v1->floatvalue >= v2->floatvalue; break; case P_LOGIC_LEQ: v1->intvalue = v1->intvalue <= v2->intvalue; v1->floatvalue = v1->floatvalue <= v2->floatvalue; break; case P_LOGIC_EQ: v1->intvalue = v1->intvalue == v2->intvalue; v1->floatvalue = v1->floatvalue == v2->floatvalue; break; case P_LOGIC_UNEQ: v1->intvalue = v1->intvalue != v2->intvalue; v1->floatvalue = v1->floatvalue != v2->floatvalue; break; case P_LOGIC_GREATER: v1->intvalue = v1->intvalue > v2->intvalue; v1->floatvalue = v1->floatvalue > v2->floatvalue; break; case P_LOGIC_LESS: v1->intvalue = v1->intvalue < v2->intvalue; v1->floatvalue = v1->floatvalue < v2->floatvalue; break; case P_RSHIFT: v1->intvalue >>= v2->intvalue; break; case P_LSHIFT: v1->intvalue <<= v2->intvalue; break; case P_BIN_AND: v1->intvalue &= v2->intvalue; break; case P_BIN_OR: v1->intvalue |= v2->intvalue; break; case P_BIN_XOR: v1->intvalue ^= v2->intvalue; break; case P_COLON: { if (!gotquestmarkvalue) { SourceError(source, ": without ? in #if/#elif"); error = 1; break; } //end if if (integer) { if (!questmarkintvalue) v1->intvalue = v2->intvalue; } //end if else { if (!questmarkfloatvalue) v1->floatvalue = v2->floatvalue; } //end else gotquestmarkvalue = qfalse; break; } //end case case P_QUESTIONMARK: { if (gotquestmarkvalue) { SourceError(source, "? after ? in #if/#elif"); error = 1; break; } //end if questmarkintvalue = v1->intvalue; questmarkfloatvalue = v1->floatvalue; gotquestmarkvalue = qtrue; break; } //end if } //end switch #ifdef DEBUG_EVAL if (integer) Log_Write("result value = %d", v1->intvalue); else Log_Write("result value = %f", v1->floatvalue); #endif //DEBUG_EVAL if (error) break; lastoperatortype = o->operatorCode; //if not an operator with arity 1 if (o->operatorCode != P_LOGIC_NOT && o->operatorCode != P_BIN_NOT) { //remove the second value if not question mark operator if (o->operatorCode != P_QUESTIONMARK) v = v->next; // if (v->prev) v->prev->next = v->next; else firstvalue = v->next; if (v->next) v->next->prev = v->prev; else lastvalue = v->prev; //FreeMemory(v); FreeValue(v); } //end if //remove the operator if (o->prev) o->prev->next = o->next; else firstoperator = o->next; if (o->next) o->next->prev = o->prev; else lastoperator = o->prev; //FreeMemory(o); FreeOperator(o); } //end while if (firstvalue) { if (intvalue) *intvalue = firstvalue->intvalue; if (floatvalue) *floatvalue = firstvalue->floatvalue; } //end if for (o = firstoperator; o; o = lastoperator) { lastoperator = o->next; //FreeMemory(o); FreeOperator(o); } //end for for (v = firstvalue; v; v = lastvalue) { lastvalue = v->next; //FreeMemory(v); FreeValue(v); } //end for if (!error) return qtrue; if (intvalue) *intvalue = 0; if (floatvalue) *floatvalue = 0; return qfalse; } //end of the function PC_EvaluateTokens //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_Evaluate(source_t *source, signed long int *intvalue, double *floatvalue, int integer) { token_t token, *firsttoken, *lasttoken; token_t *t, *nexttoken; define_t *define; int defined = qfalse; if (intvalue) *intvalue = 0; if (floatvalue) *floatvalue = 0; // if (!PC_ReadLine(source, &token)) { SourceError(source, "no value after #if/#elif"); return qfalse; } //end if firsttoken = NULL; lasttoken = NULL; do { //if the token is a name if (token.type == TT_NAME) { if (defined) { defined = qfalse; t = PC_CopyToken(&token); t->next = NULL; if (lasttoken) lasttoken->next = t; else firsttoken = t; lasttoken = t; } //end if else if (!strcmp(token.string, "defined")) { defined = qtrue; t = PC_CopyToken(&token); t->next = NULL; if (lasttoken) lasttoken->next = t; else firsttoken = t; lasttoken = t; } //end if else { //then it must be a define #if DEFINEHASHING define = PC_FindHashedDefine(source->definehash, token.string); #else define = PC_FindDefine(source->defines, token.string); #endif //DEFINEHASHING if (!define) { SourceError(source, "can't evaluate %s, not defined", token.string); return qfalse; } //end if if (!PC_ExpandDefineIntoSource(source, &token, define)) return qfalse; } //end else } //end if //if the token is a number or a punctuation else if (token.type == TT_NUMBER || token.type == TT_PUNCTUATION) { t = PC_CopyToken(&token); t->next = NULL; if (lasttoken) lasttoken->next = t; else firsttoken = t; lasttoken = t; } //end else else //can't evaluate the token { SourceError(source, "can't evaluate %s", token.string); return qfalse; } //end else } while(PC_ReadLine(source, &token)); // if (!PC_EvaluateTokens(source, firsttoken, intvalue, floatvalue, integer)) return qfalse; // #ifdef DEBUG_EVAL Log_Write("eval:"); #endif //DEBUG_EVAL for (t = firsttoken; t; t = nexttoken) { #ifdef DEBUG_EVAL Log_Write(" %s", t->string); #endif //DEBUG_EVAL nexttoken = t->next; PC_FreeToken(t); } //end for #ifdef DEBUG_EVAL if (integer) Log_Write("eval result: %d", *intvalue); else Log_Write("eval result: %f", *floatvalue); #endif //DEBUG_EVAL // return qtrue; } //end of the function PC_Evaluate //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_DollarEvaluate(source_t *source, signed long int *intvalue, double *floatvalue, int integer) { int indent, defined = qfalse; token_t token, *firsttoken, *lasttoken; token_t *t, *nexttoken; define_t *define; if (intvalue) *intvalue = 0; if (floatvalue) *floatvalue = 0; // if (!PC_ReadSourceToken(source, &token)) { SourceError(source, "no leading ( after $evalint/$evalfloat"); return qfalse; } //end if if (!PC_ReadSourceToken(source, &token)) { SourceError(source, "nothing to evaluate"); return qfalse; } //end if indent = 1; firsttoken = NULL; lasttoken = NULL; do { //if the token is a name if (token.type == TT_NAME) { if (defined) { defined = qfalse; t = PC_CopyToken(&token); t->next = NULL; if (lasttoken) lasttoken->next = t; else firsttoken = t; lasttoken = t; } //end if else if (!strcmp(token.string, "defined")) { defined = qtrue; t = PC_CopyToken(&token); t->next = NULL; if (lasttoken) lasttoken->next = t; else firsttoken = t; lasttoken = t; } //end if else { //then it must be a define #if DEFINEHASHING define = PC_FindHashedDefine(source->definehash, token.string); #else define = PC_FindDefine(source->defines, token.string); #endif //DEFINEHASHING if (!define) { SourceError(source, "can't evaluate %s, not defined", token.string); return qfalse; } //end if if (!PC_ExpandDefineIntoSource(source, &token, define)) return qfalse; } //end else } //end if //if the token is a number or a punctuation else if (token.type == TT_NUMBER || token.type == TT_PUNCTUATION) { if (*token.string == '(') indent++; else if (*token.string == ')') indent--; if (indent <= 0) break; t = PC_CopyToken(&token); t->next = NULL; if (lasttoken) lasttoken->next = t; else firsttoken = t; lasttoken = t; } //end else else //can't evaluate the token { SourceError(source, "can't evaluate %s", token.string); return qfalse; } //end else } while(PC_ReadSourceToken(source, &token)); // if (!PC_EvaluateTokens(source, firsttoken, intvalue, floatvalue, integer)) return qfalse; // #ifdef DEBUG_EVAL Log_Write("$eval:"); #endif //DEBUG_EVAL for (t = firsttoken; t; t = nexttoken) { #ifdef DEBUG_EVAL Log_Write(" %s", t->string); #endif //DEBUG_EVAL nexttoken = t->next; PC_FreeToken(t); } //end for #ifdef DEBUG_EVAL if (integer) Log_Write("$eval result: %d", *intvalue); else Log_Write("$eval result: %f", *floatvalue); #endif //DEBUG_EVAL // return qtrue; } //end of the function PC_DollarEvaluate //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_Directive_elif(source_t *source) { signed long int value; int type, skip; PC_PopIndent(source, &type, &skip); if (!type || type == INDENT_ELSE) { SourceError(source, "misplaced #elif"); return qfalse; } //end if if (!PC_Evaluate(source, &value, NULL, qtrue)) return qfalse; skip = (value == 0); PC_PushIndent(source, INDENT_ELIF, skip); return qtrue; } //end of the function PC_Directive_elif //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_Directive_if(source_t *source) { signed long int value; int skip; if (!PC_Evaluate(source, &value, NULL, qtrue)) return qfalse; skip = (value == 0); PC_PushIndent(source, INDENT_IF, skip); return qtrue; } //end of the function PC_Directive //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_Directive_line(source_t *source) { SourceError(source, "#line directive not supported"); return qfalse; } //end of the function PC_Directive_line //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_Directive_error(source_t *source) { token_t token; strcpy(token.string, ""); PC_ReadSourceToken(source, &token); SourceError(source, "#error directive: %s", token.string); return qfalse; } //end of the function PC_Directive_error //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_Directive_pragma(source_t *source) { token_t token; SourceWarning(source, "#pragma directive not supported"); while(PC_ReadLine(source, &token)) ; return qtrue; } //end of the function PC_Directive_pragma //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ void UnreadSignToken(source_t *source) { token_t token; token.line = source->scriptstack->line; token.whitespace_p = source->scriptstack->script_p; token.endwhitespace_p = source->scriptstack->script_p; token.linescrossed = 0; strcpy(token.string, "-"); token.type = TT_PUNCTUATION; token.subtype = P_SUB; PC_UnreadSourceToken(source, &token); } //end of the function UnreadSignToken //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_Directive_eval(source_t *source) { signed long int value; token_t token; if (!PC_Evaluate(source, &value, NULL, qtrue)) return qfalse; // token.line = source->scriptstack->line; token.whitespace_p = source->scriptstack->script_p; token.endwhitespace_p = source->scriptstack->script_p; token.linescrossed = 0; sprintf(token.string, "%d", abs(value)); token.type = TT_NUMBER; token.subtype = TT_INTEGER|TT_LONG|TT_DECIMAL; PC_UnreadSourceToken(source, &token); if (value < 0) UnreadSignToken(source); return qtrue; } //end of the function PC_Directive_eval //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_Directive_evalfloat(source_t *source) { double value; token_t token; if (!PC_Evaluate(source, NULL, &value, qfalse)) return qfalse; token.line = source->scriptstack->line; token.whitespace_p = source->scriptstack->script_p; token.endwhitespace_p = source->scriptstack->script_p; token.linescrossed = 0; sprintf(token.string, "%1.2f", fabs(value)); token.type = TT_NUMBER; token.subtype = TT_FLOAT|TT_LONG|TT_DECIMAL; PC_UnreadSourceToken(source, &token); if (value < 0) UnreadSignToken(source); return qtrue; } //end of the function PC_Directive_evalfloat //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ directive_t directives[20] = { {"if", PC_Directive_if}, {"ifdef", PC_Directive_ifdef}, {"ifndef", PC_Directive_ifndef}, {"elif", PC_Directive_elif}, {"else", PC_Directive_else}, {"endif", PC_Directive_endif}, {"include", PC_Directive_include}, {"define", PC_Directive_define}, {"undef", PC_Directive_undef}, {"line", PC_Directive_line}, {"error", PC_Directive_error}, {"pragma", PC_Directive_pragma}, {"eval", PC_Directive_eval}, {"evalfloat", PC_Directive_evalfloat}, {NULL, NULL} }; int PC_ReadDirective(source_t *source) { token_t token; int i; //read the directive name if (!PC_ReadSourceToken(source, &token)) { SourceError(source, "found # without name"); return qfalse; } //end if //directive name must be on the same line if (token.linescrossed > 0) { PC_UnreadSourceToken(source, &token); SourceError(source, "found # at end of line"); return qfalse; } //end if //if if is a name if (token.type == TT_NAME) { //find the precompiler directive for (i = 0; directives[i].name; i++) { if (!strcmp(directives[i].name, token.string)) { return directives[i].func(source); } //end if } //end for } //end if SourceError(source, "unknown precompiler directive %s", token.string); return qfalse; } //end of the function PC_ReadDirective //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_DollarDirective_evalint(source_t *source) { signed long int value; token_t token; if (!PC_DollarEvaluate(source, &value, NULL, qtrue)) return qfalse; // token.line = source->scriptstack->line; token.whitespace_p = source->scriptstack->script_p; token.endwhitespace_p = source->scriptstack->script_p; token.linescrossed = 0; sprintf(token.string, "%d", abs(value)); token.type = TT_NUMBER; token.subtype = TT_INTEGER|TT_LONG|TT_DECIMAL; #ifdef NUMBERVALUE token.intvalue = value; token.floatvalue = value; #endif //NUMBERVALUE PC_UnreadSourceToken(source, &token); if (value < 0) UnreadSignToken(source); return qtrue; } //end of the function PC_DollarDirective_evalint //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_DollarDirective_evalfloat(source_t *source) { double value; token_t token; if (!PC_DollarEvaluate(source, NULL, &value, qfalse)) return qfalse; token.line = source->scriptstack->line; token.whitespace_p = source->scriptstack->script_p; token.endwhitespace_p = source->scriptstack->script_p; token.linescrossed = 0; sprintf(token.string, "%1.2f", fabs(value)); token.type = TT_NUMBER; token.subtype = TT_FLOAT|TT_LONG|TT_DECIMAL; #ifdef NUMBERVALUE token.intvalue = (unsigned long) value; token.floatvalue = value; #endif //NUMBERVALUE PC_UnreadSourceToken(source, &token); if (value < 0) UnreadSignToken(source); return qtrue; } //end of the function PC_DollarDirective_evalfloat //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ directive_t dollardirectives[20] = { {"evalint", PC_DollarDirective_evalint}, {"evalfloat", PC_DollarDirective_evalfloat}, {NULL, NULL} }; int PC_ReadDollarDirective(source_t *source) { token_t token; int i; //read the directive name if (!PC_ReadSourceToken(source, &token)) { SourceError(source, "found $ without name"); return qfalse; } //end if //directive name must be on the same line if (token.linescrossed > 0) { PC_UnreadSourceToken(source, &token); SourceError(source, "found $ at end of line"); return qfalse; } //end if //if if is a name if (token.type == TT_NAME) { //find the precompiler directive for (i = 0; dollardirectives[i].name; i++) { if (!strcmp(dollardirectives[i].name, token.string)) { return dollardirectives[i].func(source); } //end if } //end for } //end if PC_UnreadSourceToken(source, &token); SourceError(source, "unknown precompiler directive %s", token.string); return qfalse; } //end of the function PC_ReadDirective #ifdef QUAKEC //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int BuiltinFunction(source_t *source) { token_t token; if (!PC_ReadSourceToken(source, &token)) return qfalse; if (token.type == TT_NUMBER) { PC_UnreadSourceToken(source, &token); return qtrue; } //end if else { PC_UnreadSourceToken(source, &token); return qfalse; } //end else } //end of the function BuiltinFunction //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int QuakeCMacro(source_t *source) { int i; token_t token; if (!PC_ReadSourceToken(source, &token)) return qtrue; if (token.type != TT_NAME) { PC_UnreadSourceToken(source, &token); return qtrue; } //end if //find the precompiler directive for (i = 0; dollardirectives[i].name; i++) { if (!strcmp(dollardirectives[i].name, token.string)) { PC_UnreadSourceToken(source, &token); return qfalse; } //end if } //end for PC_UnreadSourceToken(source, &token); return qtrue; } //end of the function QuakeCMacro #endif //QUAKEC //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_ReadToken(source_t *source, token_t *token) { define_t *define; while(1) { if (!PC_ReadSourceToken(source, token)) return qfalse; //check for precompiler directives if (token->type == TT_PUNCTUATION && *token->string == '#') { #ifdef QUAKEC if (!BuiltinFunction(source)) #endif //QUAKC { //read the precompiler directive if (!PC_ReadDirective(source)) return qfalse; continue; } //end if } //end if if (token->type == TT_PUNCTUATION && *token->string == '$') { #ifdef QUAKEC if (!QuakeCMacro(source)) #endif //QUAKEC { //read the precompiler directive if (!PC_ReadDollarDirective(source)) return qfalse; continue; } //end if } //end if // recursively concatenate strings that are behind each other still resolving defines if (token->type == TT_STRING) { token_t newtoken; if (PC_ReadToken(source, &newtoken)) { if (newtoken.type == TT_STRING) { token->string[strlen(token->string)-1] = '\0'; if (strlen(token->string) + (int)strlen(newtoken.string+1) + 1 >= MAX_TOKEN) { SourceError(source, "string longer than MAX_TOKEN %d\n", MAX_TOKEN); return qfalse; } strcat(token->string, newtoken.string+1); } else { PC_UnreadToken(source, &newtoken); } } } //end if //if skipping source because of conditional compilation if (source->skip) continue; //if the token is a name if (token->type == TT_NAME) { //check if the name is a define macro #if DEFINEHASHING define = PC_FindHashedDefine(source->definehash, token->string); #else define = PC_FindDefine(source->defines, token->string); #endif //DEFINEHASHING //if it is a define macro if (define) { //expand the defined macro if (!PC_ExpandDefineIntoSource(source, token, define)) return qfalse; continue; } //end if } //end if //copy token for unreading Com_Memcpy(&source->token, token, sizeof(token_t)); //found a token return qtrue; } //end while } //end of the function PC_ReadToken //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_ExpectTokenString(source_t *source, char *string) { token_t token; if (!PC_ReadToken(source, &token)) { SourceError(source, "couldn't find expected %s", string); return qfalse; } //end if if (strcmp(token.string, string)) { SourceError(source, "expected %s, found %s", string, token.string); return qfalse; } //end if return qtrue; } //end of the function PC_ExpectTokenString //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_ExpectTokenType(source_t *source, int type, int subtype, token_t *token) { char str[MAX_TOKEN]; if (!PC_ReadToken(source, token)) { SourceError(source, "couldn't read expected token"); return qfalse; } //end if if (token->type != type) { strcpy(str, ""); if (type == TT_STRING) strcpy(str, "string"); if (type == TT_LITERAL) strcpy(str, "literal"); if (type == TT_NUMBER) strcpy(str, "number"); if (type == TT_NAME) strcpy(str, "name"); if (type == TT_PUNCTUATION) strcpy(str, "punctuation"); SourceError(source, "expected a %s, found %s", str, token->string); return qfalse; } //end if if (token->type == TT_NUMBER) { if ((token->subtype & subtype) != subtype) { if (subtype & TT_DECIMAL) strcpy(str, "decimal"); if (subtype & TT_HEX) strcpy(str, "hex"); if (subtype & TT_OCTAL) strcpy(str, "octal"); if (subtype & TT_BINARY) strcpy(str, "binary"); if (subtype & TT_LONG) strcat(str, " long"); if (subtype & TT_UNSIGNED) strcat(str, " unsigned"); if (subtype & TT_FLOAT) strcat(str, " float"); if (subtype & TT_INTEGER) strcat(str, " integer"); SourceError(source, "expected %s, found %s", str, token->string); return qfalse; } //end if } //end if else if (token->type == TT_PUNCTUATION) { if (token->subtype != subtype) { SourceError(source, "found %s", token->string); return qfalse; } //end if } //end else if return qtrue; } //end of the function PC_ExpectTokenType //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_ExpectAnyToken(source_t *source, token_t *token) { if (!PC_ReadToken(source, token)) { SourceError(source, "couldn't read expected token"); return qfalse; } //end if else { return qtrue; } //end else } //end of the function PC_ExpectAnyToken //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_CheckTokenString(source_t *source, char *string) { token_t tok; if (!PC_ReadToken(source, &tok)) return qfalse; //if the token is available if (!strcmp(tok.string, string)) return qtrue; // PC_UnreadSourceToken(source, &tok); return qfalse; } //end of the function PC_CheckTokenString //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_CheckTokenType(source_t *source, int type, int subtype, token_t *token) { token_t tok; if (!PC_ReadToken(source, &tok)) return qfalse; //if the type matches if (tok.type == type && (tok.subtype & subtype) == subtype) { Com_Memcpy(token, &tok, sizeof(token_t)); return qtrue; } //end if // PC_UnreadSourceToken(source, &tok); return qfalse; } //end of the function PC_CheckTokenType //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_SkipUntilString(source_t *source, char *string) { token_t token; while(PC_ReadToken(source, &token)) { if (!strcmp(token.string, string)) return qtrue; } //end while return qfalse; } //end of the function PC_SkipUntilString //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ void PC_UnreadLastToken(source_t *source) { PC_UnreadSourceToken(source, &source->token); } //end of the function PC_UnreadLastToken //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ void PC_UnreadToken(source_t *source, token_t *token) { PC_UnreadSourceToken(source, token); } //end of the function PC_UnreadToken //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ void PC_SetIncludePath(source_t *source, char *path) { strncpy(source->includepath, path, MAX_PATH); //add trailing path seperator if (source->includepath[strlen(source->includepath)-1] != '\\' && source->includepath[strlen(source->includepath)-1] != '/') { strcat(source->includepath, PATHSEPERATOR_STR); } //end if } //end of the function PC_SetIncludePath //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ void PC_SetPunctuations(source_t *source, punctuation_t *p) { source->punctuations = p; } //end of the function PC_SetPunctuations //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ source_t *LoadSourceFile(const char *filename) { source_t *source; script_t *script; PC_InitTokenHeap(); script = LoadScriptFile(filename); if (!script) return NULL; script->next = NULL; source = (source_t *) GetMemory(sizeof(source_t)); Com_Memset(source, 0, sizeof(source_t)); strncpy(source->filename, filename, MAX_PATH); source->scriptstack = script; source->tokens = NULL; source->defines = NULL; source->indentstack = NULL; source->skip = 0; #if DEFINEHASHING source->definehash = (define_t**) GetClearedMemory(DEFINEHASHSIZE * sizeof(define_t *)); #endif //DEFINEHASHING PC_AddGlobalDefinesToSource(source); return source; } //end of the function LoadSourceFile //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ source_t *LoadSourceMemory(char *ptr, int length, char *name) { source_t *source; script_t *script; PC_InitTokenHeap(); script = LoadScriptMemory(ptr, length, name); if (!script) return NULL; script->next = NULL; source = (source_t *) GetMemory(sizeof(source_t)); Com_Memset(source, 0, sizeof(source_t)); strncpy(source->filename, name, MAX_PATH); source->scriptstack = script; source->tokens = NULL; source->defines = NULL; source->indentstack = NULL; source->skip = 0; #if DEFINEHASHING source->definehash = (define_t**) GetClearedMemory(DEFINEHASHSIZE * sizeof(define_t *)); #endif //DEFINEHASHING PC_AddGlobalDefinesToSource(source); return source; } //end of the function LoadSourceMemory //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ void FreeSource(source_t *source) { script_t *script; token_t *token; define_t *define; indent_t *indent; int i; //PC_PrintDefineHashTable(source->definehash); //free all the scripts while(source->scriptstack) { script = source->scriptstack; source->scriptstack = source->scriptstack->next; FreeScript(script); } //end for //free all the tokens while(source->tokens) { token = source->tokens; source->tokens = source->tokens->next; PC_FreeToken(token); } //end for #if DEFINEHASHING for (i = 0; i < DEFINEHASHSIZE; i++) { while(source->definehash[i]) { define = source->definehash[i]; source->definehash[i] = source->definehash[i]->hashnext; PC_FreeDefine(define); } //end while } //end for #else //DEFINEHASHING //free all defines while(source->defines) { define = source->defines; source->defines = source->defines->next; PC_FreeDefine(define); } //end for #endif //DEFINEHASHING //free all indents while(source->indentstack) { indent = source->indentstack; source->indentstack = source->indentstack->next; FreeMemory(indent); } //end for #if DEFINEHASHING // if (source->definehash) FreeMemory(source->definehash); #endif //DEFINEHASHING //free the source itself FreeMemory(source); } //end of the function FreeSource //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ #define MAX_SOURCEFILES 64 source_t *sourceFiles[MAX_SOURCEFILES]; int PC_LoadSourceHandle(const char *filename) { source_t *source; int i; for (i = 1; i < MAX_SOURCEFILES; i++) { if (!sourceFiles[i]) break; } //end for if (i >= MAX_SOURCEFILES) return 0; PS_SetBaseFolder(""); source = LoadSourceFile(filename); if (!source) return 0; sourceFiles[i] = source; return i; } //end of the function PC_LoadSourceHandle //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_FreeSourceHandle(int handle) { if (handle < 1 || handle >= MAX_SOURCEFILES) return qfalse; if (!sourceFiles[handle]) return qfalse; FreeSource(sourceFiles[handle]); sourceFiles[handle] = NULL; return qtrue; } //end of the function PC_FreeSourceHandle //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_ReadTokenHandle(int handle, pc_token_t *pc_token) { token_t token; int ret; if (handle < 1 || handle >= MAX_SOURCEFILES) return 0; if (!sourceFiles[handle]) return 0; ret = PC_ReadToken(sourceFiles[handle], &token); strcpy(pc_token->string, token.string); pc_token->type = token.type; pc_token->subtype = token.subtype; pc_token->intvalue = token.intvalue; pc_token->floatvalue = token.floatvalue; if (pc_token->type == TT_STRING) StripDoubleQuotes(pc_token->string); return ret; } //end of the function PC_ReadTokenHandle //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PC_SourceFileAndLine(int handle, char *filename, int *line) { if (handle < 1 || handle >= MAX_SOURCEFILES) return qfalse; if (!sourceFiles[handle]) return qfalse; strcpy(filename, sourceFiles[handle]->filename); if (sourceFiles[handle]->scriptstack) *line = sourceFiles[handle]->scriptstack->line; else *line = 0; return qtrue; } //end of the function PC_SourceFileAndLine //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ void PC_SetBaseFolder(char *path) { PS_SetBaseFolder(path); } //end of the function PC_SetBaseFolder //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ void PC_CheckOpenSourceHandles(void) { int i; for (i = 1; i < MAX_SOURCEFILES; i++) { if (sourceFiles[i]) { #ifdef BOTLIB botimport.Print(PRT_ERROR, "file %s still open in precompiler\n", sourceFiles[i]->scriptstack->filename); #endif //BOTLIB } //end if } //end for } //end of the function PC_CheckOpenSourceHandles ================================================ FILE: src/engine/botlib/l_precomp.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: l_precomp.h * * desc: pre compiler * * $Archive: /source/code/botlib/l_precomp.h $ * *****************************************************************************/ #ifndef MAX_PATH #define MAX_PATH MAX_QPATH #endif #ifndef PATH_SEPERATORSTR #if defined(WIN32)|defined(_WIN32)|defined(__NT__)|defined(__WINDOWS__)|defined(__WINDOWS_386__) #define PATHSEPERATOR_STR "\\" #else #define PATHSEPERATOR_STR "/" #endif #endif #ifndef PATH_SEPERATORCHAR #if defined(WIN32)|defined(_WIN32)|defined(__NT__)|defined(__WINDOWS__)|defined(__WINDOWS_386__) #define PATHSEPERATOR_CHAR '\\' #else #define PATHSEPERATOR_CHAR '/' #endif #endif #if defined(BSPC) && !defined(QDECL) #define QDECL #endif #define DEFINE_FIXED 0x0001 #define BUILTIN_LINE 1 #define BUILTIN_FILE 2 #define BUILTIN_DATE 3 #define BUILTIN_TIME 4 #define BUILTIN_STDC 5 #define INDENT_IF 0x0001 #define INDENT_ELSE 0x0002 #define INDENT_ELIF 0x0004 #define INDENT_IFDEF 0x0008 #define INDENT_IFNDEF 0x0010 //macro definitions typedef struct define_s { char *name; //define name int flags; //define flags int builtin; // > 0 if builtin define int numparms; //number of define parameters token_t *parms; //define parameters token_t *tokens; //macro tokens (possibly containing parm tokens) struct define_s *next; //next defined macro in a list struct define_s *hashnext; //next define in the hash chain } define_t; //indents //used for conditional compilation directives: //#if, #else, #elif, #ifdef, #ifndef typedef struct indent_s { int type; //indent type int skip; //true if skipping current indent script_t *script; //script the indent was in struct indent_s *next; //next indent on the indent stack } indent_t; //source file typedef struct source_s { char filename[1024]; //file name of the script char includepath[1024]; //path to include files punctuation_t *punctuations; //punctuations to use script_t *scriptstack; //stack with scripts of the source token_t *tokens; //tokens to read first define_t *defines; //list with macro definitions define_t **definehash; //hash chain with defines indent_t *indentstack; //stack with indents int skip; // > 0 if skipping conditional code token_t token; //last read token } source_t; //read a token from the source int PC_ReadToken(source_t *source, token_t *token); //expect a certain token int PC_ExpectTokenString(source_t *source, char *string); //expect a certain token type int PC_ExpectTokenType(source_t *source, int type, int subtype, token_t *token); //expect a token int PC_ExpectAnyToken(source_t *source, token_t *token); //returns true when the token is available int PC_CheckTokenString(source_t *source, char *string); //returns true an reads the token when a token with the given type is available int PC_CheckTokenType(source_t *source, int type, int subtype, token_t *token); //skip tokens until the given token string is read int PC_SkipUntilString(source_t *source, char *string); //unread the last token read from the script void PC_UnreadLastToken(source_t *source); //unread the given token void PC_UnreadToken(source_t *source, token_t *token); //read a token only if on the same line, lines are concatenated with a slash int PC_ReadLine(source_t *source, token_t *token); //returns true if there was a white space in front of the token int PC_WhiteSpaceBeforeToken(token_t *token); //add a define to the source int PC_AddDefine(source_t *source, char *string); //add a globals define that will be added to all opened sources int PC_AddGlobalDefine(char *string); //remove the given global define int PC_RemoveGlobalDefine(char *name); //remove all globals defines void PC_RemoveAllGlobalDefines(void); //add builtin defines void PC_AddBuiltinDefines(source_t *source); //set the source include path void PC_SetIncludePath(source_t *source, char *path); //set the punction set void PC_SetPunctuations(source_t *source, punctuation_t *p); //set the base folder to load files from void PC_SetBaseFolder(char *path); //load a source file source_t *LoadSourceFile(const char *filename); //load a source from memory source_t *LoadSourceMemory(char *ptr, int length, char *name); //free the given source void FreeSource(source_t *source); //print a source error void QDECL SourceError(source_t *source, char *str, ...); //print a source warning void QDECL SourceWarning(source_t *source, char *str, ...); #ifdef BSPC // some of BSPC source does include game/q_shared.h and some does not // we define pc_token_s pc_token_t if needed (yes, it's ugly) #ifndef __Q_SHARED_H #define MAX_TOKENLENGTH 1024 typedef struct pc_token_s { int type; int subtype; int intvalue; float floatvalue; char string[MAX_TOKENLENGTH]; } pc_token_t; #endif //!_Q_SHARED_H #endif //BSPC // int PC_LoadSourceHandle(const char *filename); int PC_FreeSourceHandle(int handle); int PC_ReadTokenHandle(int handle, pc_token_t *pc_token); int PC_SourceFileAndLine(int handle, char *filename, int *line); void PC_CheckOpenSourceHandles(void); ================================================ FILE: src/engine/botlib/l_script.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: l_script.c * * desc: lexicographical parser * * $Archive: /MissionPack/code/botlib/l_script.c $ * *****************************************************************************/ //#define SCREWUP //#define BOTLIB //#define MEQCC //#define BSPC #ifdef SCREWUP #include #include #include #include #include #include "l_memory.h" #include "l_script.h" typedef enum {qfalse, qtrue} qboolean; #endif //SCREWUP #ifdef BOTLIB //include files for usage in the bot library #include "../../game/q_shared.h" #include "../../game/botlib.h" #include "be_interface.h" #include "l_script.h" #include "l_memory.h" #include "l_log.h" #include "l_libvar.h" #endif //BOTLIB #ifdef MEQCC //include files for usage in MrElusive's QuakeC Compiler #include "qcc.h" #include "l_script.h" #include "l_memory.h" #include "l_log.h" #define qtrue true #define qfalse false #endif //MEQCC #ifdef BSPC //include files for usage in the BSP Converter #include "../bspc/qbsp.h" #include "../bspc/l_log.h" #include "../bspc/l_mem.h" #define qtrue true #define qfalse false #endif //BSPC #define PUNCTABLE //longer punctuations first punctuation_t default_punctuations[] = { //binary operators {">>=",P_RSHIFT_ASSIGN, NULL}, {"<<=",P_LSHIFT_ASSIGN, NULL}, // {"...",P_PARMS, NULL}, //define merge operator {"##",P_PRECOMPMERGE, NULL}, //logic operators {"&&",P_LOGIC_AND, NULL}, {"||",P_LOGIC_OR, NULL}, {">=",P_LOGIC_GEQ, NULL}, {"<=",P_LOGIC_LEQ, NULL}, {"==",P_LOGIC_EQ, NULL}, {"!=",P_LOGIC_UNEQ, NULL}, //arithmatic operators {"*=",P_MUL_ASSIGN, NULL}, {"/=",P_DIV_ASSIGN, NULL}, {"%=",P_MOD_ASSIGN, NULL}, {"+=",P_ADD_ASSIGN, NULL}, {"-=",P_SUB_ASSIGN, NULL}, {"++",P_INC, NULL}, {"--",P_DEC, NULL}, //binary operators {"&=",P_BIN_AND_ASSIGN, NULL}, {"|=",P_BIN_OR_ASSIGN, NULL}, {"^=",P_BIN_XOR_ASSIGN, NULL}, {">>",P_RSHIFT, NULL}, {"<<",P_LSHIFT, NULL}, //reference operators {"->",P_POINTERREF, NULL}, //C++ {"::",P_CPP1, NULL}, {".*",P_CPP2, NULL}, //arithmatic operators {"*",P_MUL, NULL}, {"/",P_DIV, NULL}, {"%",P_MOD, NULL}, {"+",P_ADD, NULL}, {"-",P_SUB, NULL}, {"=",P_ASSIGN, NULL}, //binary operators {"&",P_BIN_AND, NULL}, {"|",P_BIN_OR, NULL}, {"^",P_BIN_XOR, NULL}, {"~",P_BIN_NOT, NULL}, //logic operators {"!",P_LOGIC_NOT, NULL}, {">",P_LOGIC_GREATER, NULL}, {"<",P_LOGIC_LESS, NULL}, //reference operator {".",P_REF, NULL}, //seperators {",",P_COMMA, NULL}, {";",P_SEMICOLON, NULL}, //label indication {":",P_COLON, NULL}, //if statement {"?",P_QUESTIONMARK, NULL}, //embracements {"(",P_PARENTHESESOPEN, NULL}, {")",P_PARENTHESESCLOSE, NULL}, {"{",P_BRACEOPEN, NULL}, {"}",P_BRACECLOSE, NULL}, {"[",P_SQBRACKETOPEN, NULL}, {"]",P_SQBRACKETCLOSE, NULL}, // {"\\",P_BACKSLASH, NULL}, //precompiler operator {"#",P_PRECOMP, NULL}, #ifdef DOLLAR {"$",P_DOLLAR, NULL}, #endif //DOLLAR {NULL, 0} }; #ifdef BSPC char basefolder[MAX_PATH]; #else char basefolder[MAX_QPATH]; #endif //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void PS_CreatePunctuationTable(script_t *script, punctuation_t *punctuations) { int i; punctuation_t *p, *lastp, *newp; //get memory for the table if (!script->punctuationtable) script->punctuationtable = (punctuation_t **) GetMemory(256 * sizeof(punctuation_t *)); Com_Memset(script->punctuationtable, 0, 256 * sizeof(punctuation_t *)); //add the punctuations in the list to the punctuation table for (i = 0; punctuations[i].p; i++) { newp = &punctuations[i]; lastp = NULL; //sort the punctuations in this table entry on length (longer punctuations first) for (p = script->punctuationtable[(unsigned int) newp->p[0]]; p; p = p->next) { if (strlen(p->p) < (int)strlen(newp->p)) { newp->next = p; if (lastp) lastp->next = newp; else script->punctuationtable[(unsigned int) newp->p[0]] = newp; break; } //end if lastp = p; } //end for if (!p) { newp->next = NULL; if (lastp) lastp->next = newp; else script->punctuationtable[(unsigned int) newp->p[0]] = newp; } //end if } //end for } //end of the function PS_CreatePunctuationTable //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== char *PunctuationFromNum(script_t *script, int num) { int i; for (i = 0; script->punctuations[i].p; i++) { if (script->punctuations[i].n == num) return script->punctuations[i].p; } //end for return "unkown punctuation"; } //end of the function PunctuationFromNum //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void QDECL ScriptError(script_t *script, char *str, ...) { char text[1024]; va_list ap; if (script->flags & SCFL_NOERRORS) return; va_start(ap, str); vsprintf(text, str, ap); va_end(ap); #ifdef BOTLIB botimport.Print(PRT_ERROR, "file %s, line %d: %s\n", script->filename, script->line, text); #endif //BOTLIB #ifdef MEQCC printf("error: file %s, line %d: %s\n", script->filename, script->line, text); #endif //MEQCC #ifdef BSPC Log_Print("error: file %s, line %d: %s\n", script->filename, script->line, text); #endif //BSPC } //end of the function ScriptError //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void QDECL ScriptWarning(script_t *script, char *str, ...) { char text[1024]; va_list ap; if (script->flags & SCFL_NOWARNINGS) return; va_start(ap, str); vsprintf(text, str, ap); va_end(ap); #ifdef BOTLIB botimport.Print(PRT_WARNING, "file %s, line %d: %s\n", script->filename, script->line, text); #endif //BOTLIB #ifdef MEQCC printf("warning: file %s, line %d: %s\n", script->filename, script->line, text); #endif //MEQCC #ifdef BSPC Log_Print("warning: file %s, line %d: %s\n", script->filename, script->line, text); #endif //BSPC } //end of the function ScriptWarning //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void SetScriptPunctuations(script_t *script, punctuation_t *p) { #ifdef PUNCTABLE if (p) PS_CreatePunctuationTable(script, p); else PS_CreatePunctuationTable(script, default_punctuations); #endif //PUNCTABLE if (p) script->punctuations = p; else script->punctuations = default_punctuations; } //end of the function SetScriptPunctuations //============================================================================ // Reads spaces, tabs, C-like comments etc. // When a newline character is found the scripts line counter is increased. // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PS_ReadWhiteSpace(script_t *script) { while(1) { //skip white space while(*script->script_p <= ' ') { if (!*script->script_p) return 0; if (*script->script_p == '\n') script->line++; script->script_p++; } //end while //skip comments if (*script->script_p == '/') { //comments // if (*(script->script_p+1) == '/') { script->script_p++; do { script->script_p++; if (!*script->script_p) return 0; } //end do while(*script->script_p != '\n'); script->line++; script->script_p++; if (!*script->script_p) return 0; continue; } //end if //comments /* */ else if (*(script->script_p+1) == '*') { script->script_p++; do { script->script_p++; if (!*script->script_p) return 0; if (*script->script_p == '\n') script->line++; } //end do while(!(*script->script_p == '*' && *(script->script_p+1) == '/')); script->script_p++; if (!*script->script_p) return 0; script->script_p++; if (!*script->script_p) return 0; continue; } //end if } //end if break; } //end while return 1; } //end of the function PS_ReadWhiteSpace //============================================================================ // Reads an escape character. // // Parameter: script : script to read from // ch : place to store the read escape character // Returns: - // Changes Globals: - //============================================================================ int PS_ReadEscapeCharacter(script_t *script, char *ch) { int c, val, i; //step over the leading '\\' script->script_p++; //determine the escape character switch(*script->script_p) { case '\\': c = '\\'; break; case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case 'v': c = '\v'; break; case 'b': c = '\b'; break; case 'f': c = '\f'; break; case 'a': c = '\a'; break; case '\'': c = '\''; break; case '\"': c = '\"'; break; case '\?': c = '\?'; break; case 'x': { script->script_p++; for (i = 0, val = 0; ; i++, script->script_p++) { c = *script->script_p; if (c >= '0' && c <= '9') c = c - '0'; else if (c >= 'A' && c <= 'Z') c = c - 'A' + 10; else if (c >= 'a' && c <= 'z') c = c - 'a' + 10; else break; val = (val << 4) + c; } //end for script->script_p--; if (val > 0xFF) { ScriptWarning(script, "too large value in escape character"); val = 0xFF; } //end if c = val; break; } //end case default: //NOTE: decimal ASCII code, NOT octal { if (*script->script_p < '0' || *script->script_p > '9') ScriptError(script, "unknown escape char"); for (i = 0, val = 0; ; i++, script->script_p++) { c = *script->script_p; if (c >= '0' && c <= '9') c = c - '0'; else break; val = val * 10 + c; } //end for script->script_p--; if (val > 0xFF) { ScriptWarning(script, "too large value in escape character"); val = 0xFF; } //end if c = val; break; } //end default } //end switch //step over the escape character or the last digit of the number script->script_p++; //store the escape character *ch = c; //succesfully read escape character return 1; } //end of the function PS_ReadEscapeCharacter //============================================================================ // Reads C-like string. Escape characters are interpretted. // Quotes are included with the string. // Reads two strings with a white space between them as one string. // // Parameter: script : script to read from // token : buffer to store the string // Returns: qtrue when a string was read succesfully // Changes Globals: - //============================================================================ int PS_ReadString(script_t *script, token_t *token, int quote) { int len, tmpline; char *tmpscript_p; if (quote == '\"') token->type = TT_STRING; else token->type = TT_LITERAL; len = 0; //leading quote token->string[len++] = *script->script_p++; // while(1) { //minus 2 because trailing double quote and zero have to be appended if (len >= MAX_TOKEN - 2) { ScriptError(script, "string longer than MAX_TOKEN = %d", MAX_TOKEN); return 0; } //end if //if there is an escape character and //if escape characters inside a string are allowed if (*script->script_p == '\\' && !(script->flags & SCFL_NOSTRINGESCAPECHARS)) { if (!PS_ReadEscapeCharacter(script, &token->string[len])) { token->string[len] = 0; return 0; } //end if len++; } //end if //if a trailing quote else if (*script->script_p == quote) { //step over the double quote script->script_p++; //if white spaces in a string are not allowed if (script->flags & SCFL_NOSTRINGWHITESPACES) break; // tmpscript_p = script->script_p; tmpline = script->line; //read unusefull stuff between possible two following strings if (!PS_ReadWhiteSpace(script)) { script->script_p = tmpscript_p; script->line = tmpline; break; } //end if //if there's no leading double qoute if (*script->script_p != quote) { script->script_p = tmpscript_p; script->line = tmpline; break; } //end if //step over the new leading double quote script->script_p++; } //end if else { if (*script->script_p == '\0') { token->string[len] = 0; ScriptError(script, "missing trailing quote"); return 0; } //end if if (*script->script_p == '\n') { token->string[len] = 0; ScriptError(script, "newline inside string %s", token->string); return 0; } //end if token->string[len++] = *script->script_p++; } //end else } //end while //trailing quote token->string[len++] = quote; //end string with a zero token->string[len] = '\0'; //the sub type is the length of the string token->subtype = len; return 1; } //end of the function PS_ReadString //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PS_ReadName(script_t *script, token_t *token) { int len = 0; char c; token->type = TT_NAME; do { token->string[len++] = *script->script_p++; if (len >= MAX_TOKEN) { ScriptError(script, "name longer than MAX_TOKEN = %d", MAX_TOKEN); return 0; } //end if c = *script->script_p; } while ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'); token->string[len] = '\0'; //the sub type is the length of the name token->subtype = len; return 1; } //end of the function PS_ReadName //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ void NumberValue(char *string, int subtype, unsigned long int *intvalue, long double *floatvalue) { unsigned long int dotfound = 0; *intvalue = 0; *floatvalue = 0; //floating point number if (subtype & TT_FLOAT) { while(*string) { if (*string == '.') { if (dotfound) return; dotfound = 10; string++; } //end if if (dotfound) { *floatvalue = *floatvalue + (long double) (*string - '0') / (long double) dotfound; dotfound *= 10; } //end if else { *floatvalue = *floatvalue * 10.0 + (long double) (*string - '0'); } //end else string++; } //end while *intvalue = (unsigned long) *floatvalue; } //end if else if (subtype & TT_DECIMAL) { while(*string) *intvalue = *intvalue * 10 + (*string++ - '0'); *floatvalue = *intvalue; } //end else if else if (subtype & TT_HEX) { //step over the leading 0x or 0X string += 2; while(*string) { *intvalue <<= 4; if (*string >= 'a' && *string <= 'f') *intvalue += *string - 'a' + 10; else if (*string >= 'A' && *string <= 'F') *intvalue += *string - 'A' + 10; else *intvalue += *string - '0'; string++; } //end while *floatvalue = *intvalue; } //end else if else if (subtype & TT_OCTAL) { //step over the first zero string += 1; while(*string) *intvalue = (*intvalue << 3) + (*string++ - '0'); *floatvalue = *intvalue; } //end else if else if (subtype & TT_BINARY) { //step over the leading 0b or 0B string += 2; while(*string) *intvalue = (*intvalue << 1) + (*string++ - '0'); *floatvalue = *intvalue; } //end else if } //end of the function NumberValue //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PS_ReadNumber(script_t *script, token_t *token) { int len = 0, i; int octal, dot; char c; // unsigned long int intvalue = 0; // long double floatvalue = 0; token->type = TT_NUMBER; //check for a hexadecimal number if (*script->script_p == '0' && (*(script->script_p + 1) == 'x' || *(script->script_p + 1) == 'X')) { token->string[len++] = *script->script_p++; token->string[len++] = *script->script_p++; c = *script->script_p; //hexadecimal while((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'A')) { token->string[len++] = *script->script_p++; if (len >= MAX_TOKEN) { ScriptError(script, "hexadecimal number longer than MAX_TOKEN = %d", MAX_TOKEN); return 0; } //end if c = *script->script_p; } //end while token->subtype |= TT_HEX; } //end if #ifdef BINARYNUMBERS //check for a binary number else if (*script->script_p == '0' && (*(script->script_p + 1) == 'b' || *(script->script_p + 1) == 'B')) { token->string[len++] = *script->script_p++; token->string[len++] = *script->script_p++; c = *script->script_p; //binary while(c == '0' || c == '1') { token->string[len++] = *script->script_p++; if (len >= MAX_TOKEN) { ScriptError(script, "binary number longer than MAX_TOKEN = %d", MAX_TOKEN); return 0; } //end if c = *script->script_p; } //end while token->subtype |= TT_BINARY; } //end if #endif //BINARYNUMBERS else //decimal or octal integer or floating point number { octal = qfalse; dot = qfalse; if (*script->script_p == '0') octal = qtrue; while(1) { c = *script->script_p; if (c == '.') dot = qtrue; else if (c == '8' || c == '9') octal = qfalse; else if (c < '0' || c > '9') break; token->string[len++] = *script->script_p++; if (len >= MAX_TOKEN - 1) { ScriptError(script, "number longer than MAX_TOKEN = %d", MAX_TOKEN); return 0; } //end if } //end while if (octal) token->subtype |= TT_OCTAL; else token->subtype |= TT_DECIMAL; if (dot) token->subtype |= TT_FLOAT; } //end else for (i = 0; i < 2; i++) { c = *script->script_p; //check for a LONG number if ( (c == 'l' || c == 'L') // bk001204 - brackets && !(token->subtype & TT_LONG)) { script->script_p++; token->subtype |= TT_LONG; } //end if //check for an UNSIGNED number else if ( (c == 'u' || c == 'U') // bk001204 - brackets && !(token->subtype & (TT_UNSIGNED | TT_FLOAT))) { script->script_p++; token->subtype |= TT_UNSIGNED; } //end if } //end for token->string[len] = '\0'; #ifdef NUMBERVALUE NumberValue(token->string, token->subtype, &token->intvalue, &token->floatvalue); #endif //NUMBERVALUE if (!(token->subtype & TT_FLOAT)) token->subtype |= TT_INTEGER; return 1; } //end of the function PS_ReadNumber //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PS_ReadLiteral(script_t *script, token_t *token) { token->type = TT_LITERAL; //first quote token->string[0] = *script->script_p++; //check for end of file if (!*script->script_p) { ScriptError(script, "end of file before trailing \'"); return 0; } //end if //if it is an escape character if (*script->script_p == '\\') { if (!PS_ReadEscapeCharacter(script, &token->string[1])) return 0; } //end if else { token->string[1] = *script->script_p++; } //end else //check for trailing quote if (*script->script_p != '\'') { ScriptWarning(script, "too many characters in literal, ignored"); while(*script->script_p && *script->script_p != '\'' && *script->script_p != '\n') { script->script_p++; } //end while if (*script->script_p == '\'') script->script_p++; } //end if //store the trailing quote token->string[2] = *script->script_p++; //store trailing zero to end the string token->string[3] = '\0'; //the sub type is the integer literal value token->subtype = token->string[1]; // return 1; } //end of the function PS_ReadLiteral //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PS_ReadPunctuation(script_t *script, token_t *token) { int len; char *p; punctuation_t *punc; #ifdef PUNCTABLE for (punc = script->punctuationtable[(unsigned int)*script->script_p]; punc; punc = punc->next) { #else int i; for (i = 0; script->punctuations[i].p; i++) { punc = &script->punctuations[i]; #endif //PUNCTABLE p = punc->p; len = (int)strlen(p); //if the script contains at least as much characters as the punctuation if (script->script_p + len <= script->end_p) { //if the script contains the punctuation if (!strncmp(script->script_p, p, len)) { strncpy(token->string, p, MAX_TOKEN); script->script_p += len; token->type = TT_PUNCTUATION; //sub type is the number of the punctuation token->subtype = punc->n; return 1; } //end if } //end if } //end for return 0; } //end of the function PS_ReadPunctuation //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PS_ReadPrimitive(script_t *script, token_t *token) { int len; len = 0; while(*script->script_p > ' ' && *script->script_p != ';') { if (len >= MAX_TOKEN) { ScriptError(script, "primitive token longer than MAX_TOKEN = %d", MAX_TOKEN); return 0; } //end if token->string[len++] = *script->script_p++; } //end while token->string[len] = 0; //copy the token into the script structure Com_Memcpy(&script->token, token, sizeof(token_t)); //primitive reading successfull return 1; } //end of the function PS_ReadPrimitive //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PS_ReadToken(script_t *script, token_t *token) { //if there is a token available (from UnreadToken) if (script->tokenavailable) { script->tokenavailable = 0; Com_Memcpy(token, &script->token, sizeof(token_t)); return 1; } //end if //save script pointer script->lastscript_p = script->script_p; //save line counter script->lastline = script->line; //clear the token stuff Com_Memset(token, 0, sizeof(token_t)); //start of the white space script->whitespace_p = script->script_p; token->whitespace_p = script->script_p; //read unusefull stuff if (!PS_ReadWhiteSpace(script)) return 0; //end of the white space script->endwhitespace_p = script->script_p; token->endwhitespace_p = script->script_p; //line the token is on token->line = script->line; //number of lines crossed before token token->linescrossed = script->line - script->lastline; //if there is a leading double quote if (*script->script_p == '\"') { if (!PS_ReadString(script, token, '\"')) return 0; } //end if //if an literal else if (*script->script_p == '\'') { //if (!PS_ReadLiteral(script, token)) return 0; if (!PS_ReadString(script, token, '\'')) return 0; } //end if //if there is a number else if ((*script->script_p >= '0' && *script->script_p <= '9') || (*script->script_p == '.' && (*(script->script_p + 1) >= '0' && *(script->script_p + 1) <= '9'))) { if (!PS_ReadNumber(script, token)) return 0; } //end if //if this is a primitive script else if (script->flags & SCFL_PRIMITIVE) { return PS_ReadPrimitive(script, token); } //end else if //if there is a name else if ((*script->script_p >= 'a' && *script->script_p <= 'z') || (*script->script_p >= 'A' && *script->script_p <= 'Z') || *script->script_p == '_') { if (!PS_ReadName(script, token)) return 0; } //end if //check for punctuations else if (!PS_ReadPunctuation(script, token)) { ScriptError(script, "can't read token"); return 0; } //end if //copy the token into the script structure Com_Memcpy(&script->token, token, sizeof(token_t)); //succesfully read a token return 1; } //end of the function PS_ReadToken //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PS_ExpectTokenString(script_t *script, char *string) { token_t token; if (!PS_ReadToken(script, &token)) { ScriptError(script, "couldn't find expected %s", string); return 0; } //end if if (strcmp(token.string, string)) { ScriptError(script, "expected %s, found %s", string, token.string); return 0; } //end if return 1; } //end of the function PS_ExpectToken //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PS_ExpectTokenType(script_t *script, int type, int subtype, token_t *token) { char str[MAX_TOKEN]; if (!PS_ReadToken(script, token)) { ScriptError(script, "couldn't read expected token"); return 0; } //end if if (token->type != type) { if (type == TT_STRING) strcpy(str, "string"); if (type == TT_LITERAL) strcpy(str, "literal"); if (type == TT_NUMBER) strcpy(str, "number"); if (type == TT_NAME) strcpy(str, "name"); if (type == TT_PUNCTUATION) strcpy(str, "punctuation"); ScriptError(script, "expected a %s, found %s", str, token->string); return 0; } //end if if (token->type == TT_NUMBER) { if ((token->subtype & subtype) != subtype) { if (subtype & TT_DECIMAL) strcpy(str, "decimal"); if (subtype & TT_HEX) strcpy(str, "hex"); if (subtype & TT_OCTAL) strcpy(str, "octal"); if (subtype & TT_BINARY) strcpy(str, "binary"); if (subtype & TT_LONG) strcat(str, " long"); if (subtype & TT_UNSIGNED) strcat(str, " unsigned"); if (subtype & TT_FLOAT) strcat(str, " float"); if (subtype & TT_INTEGER) strcat(str, " integer"); ScriptError(script, "expected %s, found %s", str, token->string); return 0; } //end if } //end if else if (token->type == TT_PUNCTUATION) { if (subtype < 0) { ScriptError(script, "BUG: wrong punctuation subtype"); return 0; } //end if if (token->subtype != subtype) { ScriptError(script, "expected %s, found %s", script->punctuations[subtype], token->string); return 0; } //end if } //end else if return 1; } //end of the function PS_ExpectTokenType //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PS_ExpectAnyToken(script_t *script, token_t *token) { if (!PS_ReadToken(script, token)) { ScriptError(script, "couldn't read expected token"); return 0; } //end if else { return 1; } //end else } //end of the function PS_ExpectAnyToken //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PS_CheckTokenString(script_t *script, char *string) { token_t tok; if (!PS_ReadToken(script, &tok)) return 0; //if the token is available if (!strcmp(tok.string, string)) return 1; //token not available script->script_p = script->lastscript_p; return 0; } //end of the function PS_CheckTokenString //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PS_CheckTokenType(script_t *script, int type, int subtype, token_t *token) { token_t tok; if (!PS_ReadToken(script, &tok)) return 0; //if the type matches if (tok.type == type && (tok.subtype & subtype) == subtype) { Com_Memcpy(token, &tok, sizeof(token_t)); return 1; } //end if //token is not available script->script_p = script->lastscript_p; return 0; } //end of the function PS_CheckTokenType //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int PS_SkipUntilString(script_t *script, char *string) { token_t token; while(PS_ReadToken(script, &token)) { if (!strcmp(token.string, string)) return 1; } //end while return 0; } //end of the function PS_SkipUntilString //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ void PS_UnreadLastToken(script_t *script) { script->tokenavailable = 1; } //end of the function UnreadLastToken //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ void PS_UnreadToken(script_t *script, token_t *token) { Com_Memcpy(&script->token, token, sizeof(token_t)); script->tokenavailable = 1; } //end of the function UnreadToken //============================================================================ // returns the next character of the read white space, returns NULL if none // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ char PS_NextWhiteSpaceChar(script_t *script) { if (script->whitespace_p != script->endwhitespace_p) { return *script->whitespace_p++; } //end if else { return 0; } //end else } //end of the function PS_NextWhiteSpaceChar //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ void StripDoubleQuotes(char *string) { if (*string == '\"') { strcpy(string, string+1); } //end if if (string[strlen(string)-1] == '\"') { string[strlen(string)-1] = '\0'; } //end if } //end of the function StripDoubleQuotes //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ void StripSingleQuotes(char *string) { if (*string == '\'') { strcpy(string, string+1); } //end if if (string[strlen(string)-1] == '\'') { string[strlen(string)-1] = '\0'; } //end if } //end of the function StripSingleQuotes //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ long double ReadSignedFloat(script_t *script) { token_t token; long double sign = 1; PS_ExpectAnyToken(script, &token); if (!strcmp(token.string, "-")) { sign = -1; PS_ExpectTokenType(script, TT_NUMBER, 0, &token); } //end if else if (token.type != TT_NUMBER) { ScriptError(script, "expected float value, found %s\n", token.string); } //end else if return sign * token.floatvalue; } //end of the function ReadSignedFloat //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ signed long int ReadSignedInt(script_t *script) { token_t token; signed long int sign = 1; PS_ExpectAnyToken(script, &token); if (!strcmp(token.string, "-")) { sign = -1; PS_ExpectTokenType(script, TT_NUMBER, TT_INTEGER, &token); } //end if else if (token.type != TT_NUMBER || token.subtype == TT_FLOAT) { ScriptError(script, "expected integer value, found %s\n", token.string); } //end else if return sign * token.intvalue; } //end of the function ReadSignedInt //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ void SetScriptFlags(script_t *script, int flags) { script->flags = flags; } //end of the function SetScriptFlags //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int GetScriptFlags(script_t *script) { return script->flags; } //end of the function GetScriptFlags //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ void ResetScript(script_t *script) { //pointer in script buffer script->script_p = script->buffer; //pointer in script buffer before reading token script->lastscript_p = script->buffer; //begin of white space script->whitespace_p = NULL; //end of white space script->endwhitespace_p = NULL; //set if there's a token available in script->token script->tokenavailable = 0; // script->line = 1; script->lastline = 1; //clear the saved token Com_Memset(&script->token, 0, sizeof(token_t)); } //end of the function ResetScript //============================================================================ // returns true if at the end of the script // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int EndOfScript(script_t *script) { return script->script_p >= script->end_p; } //end of the function EndOfScript //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int NumLinesCrossed(script_t *script) { return script->line - script->lastline; } //end of the function NumLinesCrossed //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int ScriptSkipTo(script_t *script, char *value) { int len; char firstchar; firstchar = *value; len = (int)strlen(value); do { if (!PS_ReadWhiteSpace(script)) return 0; if (*script->script_p == firstchar) { if (!strncmp(script->script_p, value, len)) { return 1; } //end if } //end if script->script_p++; } while(1); } //end of the function ScriptSkipTo #ifndef BOTLIB //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ int FileLength(FILE *fp) { int pos; int end; pos = ftell(fp); fseek(fp, 0, SEEK_END); end = ftell(fp); fseek(fp, pos, SEEK_SET); return end; } //end of the function FileLength #endif //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ script_t *LoadScriptFile(const char *filename) { #ifdef BOTLIB fileHandle_t fp; char pathname[MAX_QPATH]; #else FILE *fp; #endif int length; void *buffer; script_t *script; #ifdef BOTLIB if (strlen(basefolder)) Com_sprintf(pathname, sizeof(pathname), "%s/%s", basefolder, filename); else Com_sprintf(pathname, sizeof(pathname), "%s", filename); length = botimport.FS_FOpenFile( pathname, &fp, FS_READ ); if (!fp) return NULL; #else fp = fopen(filename, "rb"); if (!fp) return NULL; length = FileLength(fp); #endif buffer = GetClearedMemory(sizeof(script_t) + length + 1); script = (script_t *) buffer; Com_Memset(script, 0, sizeof(script_t)); strcpy(script->filename, filename); script->buffer = (char *) buffer + sizeof(script_t); script->buffer[length] = 0; script->length = length; //pointer in script buffer script->script_p = script->buffer; //pointer in script buffer before reading token script->lastscript_p = script->buffer; //pointer to end of script buffer script->end_p = &script->buffer[length]; //set if there's a token available in script->token script->tokenavailable = 0; // script->line = 1; script->lastline = 1; // SetScriptPunctuations(script, NULL); // #ifdef BOTLIB botimport.FS_Read(script->buffer, length, fp); botimport.FS_FCloseFile(fp); #else if (fread(script->buffer, length, 1, fp) != 1) { FreeMemory(buffer); script = NULL; } //end if fclose(fp); #endif // script->length = COM_Compress(script->buffer); return script; } //end of the function LoadScriptFile //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ script_t *LoadScriptMemory(char *ptr, int length, char *name) { void *buffer; script_t *script; buffer = GetClearedMemory(sizeof(script_t) + length + 1); script = (script_t *) buffer; Com_Memset(script, 0, sizeof(script_t)); strcpy(script->filename, name); script->buffer = (char *) buffer + sizeof(script_t); script->buffer[length] = 0; script->length = length; //pointer in script buffer script->script_p = script->buffer; //pointer in script buffer before reading token script->lastscript_p = script->buffer; //pointer to end of script buffer script->end_p = &script->buffer[length]; //set if there's a token available in script->token script->tokenavailable = 0; // script->line = 1; script->lastline = 1; // SetScriptPunctuations(script, NULL); // Com_Memcpy(script->buffer, ptr, length); // return script; } //end of the function LoadScriptMemory //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ void FreeScript(script_t *script) { #ifdef PUNCTABLE if (script->punctuationtable) FreeMemory(script->punctuationtable); #endif //PUNCTABLE FreeMemory(script); } //end of the function FreeScript //============================================================================ // // Parameter: - // Returns: - // Changes Globals: - //============================================================================ void PS_SetBaseFolder(char *path) { #ifdef BSPC sprintf(basefolder, path); #else Com_sprintf(basefolder, sizeof(basefolder), path); #endif } //end of the function PS_SetBaseFolder ================================================ FILE: src/engine/botlib/l_script.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: l_script.h * * desc: lexicographical parser * * $Archive: /source/code/botlib/l_script.h $ * *****************************************************************************/ //undef if binary numbers of the form 0b... or 0B... are not allowed #define BINARYNUMBERS //undef if not using the token.intvalue and token.floatvalue #define NUMBERVALUE //use dollar sign also as punctuation #define DOLLAR //maximum token length #define MAX_TOKEN 1024 #if defined(BSPC) && !defined(QDECL) #define QDECL #endif //script flags #define SCFL_NOERRORS 0x0001 #define SCFL_NOWARNINGS 0x0002 #define SCFL_NOSTRINGWHITESPACES 0x0004 #define SCFL_NOSTRINGESCAPECHARS 0x0008 #define SCFL_PRIMITIVE 0x0010 #define SCFL_NOBINARYNUMBERS 0x0020 #define SCFL_NONUMBERVALUES 0x0040 //token types #define TT_STRING 1 // string #define TT_LITERAL 2 // literal #define TT_NUMBER 3 // number #define TT_NAME 4 // name #define TT_PUNCTUATION 5 // punctuation //string sub type //--------------- // the length of the string //literal sub type //---------------- // the ASCII code of the literal //number sub type //--------------- #define TT_DECIMAL 0x0008 // decimal number #define TT_HEX 0x0100 // hexadecimal number #define TT_OCTAL 0x0200 // octal number #ifdef BINARYNUMBERS #define TT_BINARY 0x0400 // binary number #endif //BINARYNUMBERS #define TT_FLOAT 0x0800 // floating point number #define TT_INTEGER 0x1000 // integer number #define TT_LONG 0x2000 // long number #define TT_UNSIGNED 0x4000 // unsigned number //punctuation sub type //-------------------- #define P_RSHIFT_ASSIGN 1 #define P_LSHIFT_ASSIGN 2 #define P_PARMS 3 #define P_PRECOMPMERGE 4 #define P_LOGIC_AND 5 #define P_LOGIC_OR 6 #define P_LOGIC_GEQ 7 #define P_LOGIC_LEQ 8 #define P_LOGIC_EQ 9 #define P_LOGIC_UNEQ 10 #define P_MUL_ASSIGN 11 #define P_DIV_ASSIGN 12 #define P_MOD_ASSIGN 13 #define P_ADD_ASSIGN 14 #define P_SUB_ASSIGN 15 #define P_INC 16 #define P_DEC 17 #define P_BIN_AND_ASSIGN 18 #define P_BIN_OR_ASSIGN 19 #define P_BIN_XOR_ASSIGN 20 #define P_RSHIFT 21 #define P_LSHIFT 22 #define P_POINTERREF 23 #define P_CPP1 24 #define P_CPP2 25 #define P_MUL 26 #define P_DIV 27 #define P_MOD 28 #define P_ADD 29 #define P_SUB 30 #define P_ASSIGN 31 #define P_BIN_AND 32 #define P_BIN_OR 33 #define P_BIN_XOR 34 #define P_BIN_NOT 35 #define P_LOGIC_NOT 36 #define P_LOGIC_GREATER 37 #define P_LOGIC_LESS 38 #define P_REF 39 #define P_COMMA 40 #define P_SEMICOLON 41 #define P_COLON 42 #define P_QUESTIONMARK 43 #define P_PARENTHESESOPEN 44 #define P_PARENTHESESCLOSE 45 #define P_BRACEOPEN 46 #define P_BRACECLOSE 47 #define P_SQBRACKETOPEN 48 #define P_SQBRACKETCLOSE 49 #define P_BACKSLASH 50 #define P_PRECOMP 51 #define P_DOLLAR 52 //name sub type //------------- // the length of the name //punctuation typedef struct punctuation_s { char *p; //punctuation character(s) int n; //punctuation indication struct punctuation_s *next; //next punctuation } punctuation_t; //token typedef struct token_s { char string[MAX_TOKEN]; //available token int type; //last read token type int subtype; //last read token sub type #ifdef NUMBERVALUE unsigned long int intvalue; //integer value long double floatvalue; //floating point value #endif //NUMBERVALUE char *whitespace_p; //start of white space before token char *endwhitespace_p; //start of white space before token int line; //line the token was on int linescrossed; //lines crossed in white space struct token_s *next; //next token in chain } token_t; //script file typedef struct script_s { char filename[1024]; //file name of the script char *buffer; //buffer containing the script char *script_p; //current pointer in the script char *end_p; //pointer to the end of the script char *lastscript_p; //script pointer before reading token char *whitespace_p; //begin of the white space char *endwhitespace_p; //end of the white space int length; //length of the script in bytes int line; //current line in script int lastline; //line before reading token int tokenavailable; //set by UnreadLastToken int flags; //several script flags punctuation_t *punctuations; //the punctuations used in the script punctuation_t **punctuationtable; token_t token; //available token struct script_s *next; //next script in a chain } script_t; //read a token from the script int PS_ReadToken(script_t *script, token_t *token); //expect a certain token int PS_ExpectTokenString(script_t *script, char *string); //expect a certain token type int PS_ExpectTokenType(script_t *script, int type, int subtype, token_t *token); //expect a token int PS_ExpectAnyToken(script_t *script, token_t *token); //returns true when the token is available int PS_CheckTokenString(script_t *script, char *string); //returns true an reads the token when a token with the given type is available int PS_CheckTokenType(script_t *script, int type, int subtype, token_t *token); //skip tokens until the given token string is read int PS_SkipUntilString(script_t *script, char *string); //unread the last token read from the script void PS_UnreadLastToken(script_t *script); //unread the given token void PS_UnreadToken(script_t *script, token_t *token); //returns the next character of the read white space, returns NULL if none char PS_NextWhiteSpaceChar(script_t *script); //remove any leading and trailing double quotes from the token void StripDoubleQuotes(char *string); //remove any leading and trailing single quotes from the token void StripSingleQuotes(char *string); //read a possible signed integer signed long int ReadSignedInt(script_t *script); //read a possible signed floating point number long double ReadSignedFloat(script_t *script); //set an array with punctuations, NULL restores default C/C++ set void SetScriptPunctuations(script_t *script, punctuation_t *p); //set script flags void SetScriptFlags(script_t *script, int flags); //get script flags int GetScriptFlags(script_t *script); //reset a script void ResetScript(script_t *script); //returns true if at the end of the script int EndOfScript(script_t *script); //returns a pointer to the punctuation with the given number char *PunctuationFromNum(script_t *script, int num); //load a script from the given file at the given offset with the given length script_t *LoadScriptFile(const char *filename); //load a script from the given memory with the given length script_t *LoadScriptMemory(char *ptr, int length, char *name); //free a script void FreeScript(script_t *script); //set the base folder to load files from void PS_SetBaseFolder(char *path); //print a script error with filename and line number void QDECL ScriptError(script_t *script, char *str, ...); //print a script warning with filename and line number void QDECL ScriptWarning(script_t *script, char *str, ...); ================================================ FILE: src/engine/botlib/l_struct.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: l_struct.c * * desc: structure reading / writing * * $Archive: /MissionPack/CODE/botlib/l_struct.c $ * *****************************************************************************/ #ifdef BOTLIB #include "../../game/q_shared.h" #include "../../game/botlib.h" //for the include of be_interface.h #include "l_script.h" #include "l_precomp.h" #include "l_struct.h" #include "l_utils.h" #include "be_interface.h" #endif //BOTLIB #ifdef BSPC //include files for usage in the BSP Converter #include "../bspc/qbsp.h" #include "../bspc/l_log.h" #include "../bspc/l_mem.h" #include "l_precomp.h" #include "l_struct.h" #define qtrue true #define qfalse false #endif //BSPC //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== fielddef_t *FindField(fielddef_t *defs, char *name) { int i; for (i = 0; defs[i].name; i++) { if (!strcmp(defs[i].name, name)) return &defs[i]; } //end for return NULL; } //end of the function FindField //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== qboolean ReadNumber(source_t *source, fielddef_t *fd, void *p) { token_t token; int negative = qfalse; long int intval, intmin = 0, intmax = 0; double floatval; if (!PC_ExpectAnyToken(source, &token)) return (qboolean) 0; //check for minus sign if (token.type == TT_PUNCTUATION) { if (fd->type & FT_UNSIGNED) { SourceError(source, "expected unsigned value, found %s", token.string); return (qboolean) 0; } //end if //if not a minus sign if (strcmp(token.string, "-")) { SourceError(source, "unexpected punctuation %s", token.string); return (qboolean) 0; } //end if negative = qtrue; //read the number if (!PC_ExpectAnyToken(source, &token)) return (qboolean) 0; } //end if //check if it is a number if (token.type != TT_NUMBER) { SourceError(source, "expected number, found %s", token.string); return (qboolean) 0; } //end if //check for a float value if (token.subtype & TT_FLOAT) { if ((fd->type & FT_TYPE) != FT_FLOAT) { SourceError(source, "unexpected float"); return (qboolean) 0; } //end if floatval = token.floatvalue; if (negative) floatval = -floatval; if (fd->type & FT_BOUNDED) { if (floatval < fd->floatmin || floatval > fd->floatmax) { SourceError(source, "float out of range [%f, %f]", fd->floatmin, fd->floatmax); return (qboolean) 0; } //end if } //end if *(float *) p = (float) floatval; return (qboolean) 1; } //end if // intval = token.intvalue; if (negative) intval = -intval; //check bounds if ((fd->type & FT_TYPE) == FT_CHAR) { if (fd->type & FT_UNSIGNED) {intmin = 0; intmax = 255;} else {intmin = -128; intmax = 127;} } //end if if ((fd->type & FT_TYPE) == FT_INT) { if (fd->type & FT_UNSIGNED) {intmin = 0; intmax = 65535;} else {intmin = -32768; intmax = 32767;} } //end else if if ((fd->type & FT_TYPE) == FT_CHAR || (fd->type & FT_TYPE) == FT_INT) { if (fd->type & FT_BOUNDED) { intmin = Maximum(intmin, fd->floatmin); intmax = Minimum(intmax, fd->floatmax); } //end if if (intval < intmin || intval > intmax) { SourceError(source, "value %d out of range [%d, %d]", intval, intmin, intmax); return (qboolean) 0; } //end if } //end if else if ((fd->type & FT_TYPE) == FT_FLOAT) { if (fd->type & FT_BOUNDED) { if (intval < fd->floatmin || intval > fd->floatmax) { SourceError(source, "value %d out of range [%f, %f]", intval, fd->floatmin, fd->floatmax); return (qboolean) 0; } //end if } //end if } //end else if //store the value if ((fd->type & FT_TYPE) == FT_CHAR) { if (fd->type & FT_UNSIGNED) *(unsigned char *) p = (unsigned char) intval; else *(char *) p = (char) intval; } //end if else if ((fd->type & FT_TYPE) == FT_INT) { if (fd->type & FT_UNSIGNED) *(unsigned int *) p = (unsigned int) intval; else *(int *) p = (int) intval; } //end else else if ((fd->type & FT_TYPE) == FT_FLOAT) { *(float *) p = (float) intval; } //end else return (qboolean) 1; } //end of the function ReadNumber //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== qboolean ReadChar(source_t *source, fielddef_t *fd, void *p) { token_t token; if (!PC_ExpectAnyToken(source, &token)) return (qboolean) 0; //take literals into account if (token.type == TT_LITERAL) { StripSingleQuotes(token.string); *(char *) p = token.string[0]; } //end if else { PC_UnreadLastToken(source); if (!ReadNumber(source, fd, p)) return (qboolean) 0; } //end if return (qboolean) 1; } //end of the function ReadChar //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int ReadString(source_t *source, fielddef_t *fd, void *p) { token_t token; if (!PC_ExpectTokenType(source, TT_STRING, 0, &token)) return 0; //remove the double quotes StripDoubleQuotes(token.string); //copy the string strncpy((char *) p, token.string, MAX_STRINGFIELD); //make sure the string is closed with a zero ((char *)p)[MAX_STRINGFIELD-1] = '\0'; // return 1; } //end of the function ReadString //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int ReadStructure(source_t *source, structdef_t *def, char *structure) { token_t token; fielddef_t *fd; void *p; int num; if (!PC_ExpectTokenString(source, "{")) return 0; while(1) { if (!PC_ExpectAnyToken(source, &token)) return qfalse; //if end of structure if (!strcmp(token.string, "}")) break; //find the field with the name fd = FindField(def->fields, token.string); if (!fd) { SourceError(source, "unknown structure field %s", token.string); return qfalse; } //end if if (fd->type & FT_ARRAY) { num = fd->maxarray; if (!PC_ExpectTokenString(source, "{")) return qfalse; } //end if else { num = 1; } //end else p = (void *)(structure + fd->offset); while (num-- > 0) { if (fd->type & FT_ARRAY) { if (PC_CheckTokenString(source, "}")) break; } //end if switch(fd->type & FT_TYPE) { case FT_CHAR: { if (!ReadChar(source, fd, p)) return qfalse; p = (char *) p + sizeof(char); break; } //end case case FT_INT: { if (!ReadNumber(source, fd, p)) return qfalse; p = (char *) p + sizeof(int); break; } //end case case FT_FLOAT: { if (!ReadNumber(source, fd, p)) return qfalse; p = (char *) p + sizeof(float); break; } //end case case FT_STRING: { if (!ReadString(source, fd, p)) return qfalse; p = (char *) p + MAX_STRINGFIELD; break; } //end case case FT_STRUCT: { if (!fd->substruct) { SourceError(source, "BUG: no sub structure defined"); return qfalse; } //end if ReadStructure(source, fd->substruct, (char *) p); p = (char *) p + fd->substruct->size; break; } //end case } //end switch if (fd->type & FT_ARRAY) { if (!PC_ExpectAnyToken(source, &token)) return qfalse; if (!strcmp(token.string, "}")) break; if (strcmp(token.string, ",")) { SourceError(source, "expected a comma, found %s", token.string); return qfalse; } //end if } //end if } //end while } //end while return qtrue; } //end of the function ReadStructure //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int WriteIndent(FILE *fp, int indent) { while(indent-- > 0) { if (fprintf(fp, "\t") < 0) return qfalse; } //end while return qtrue; } //end of the function WriteIndent //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int WriteFloat(FILE *fp, float value) { char buf[128]; int l; sprintf(buf, "%f", value); l = (int)strlen(buf); //strip any trailing zeros while(l-- > 1) { if (buf[l] != '0' && buf[l] != '.') break; if (buf[l] == '.') { buf[l] = 0; break; } //end if buf[l] = 0; } //end while //write the float to file if (fprintf(fp, "%s", buf) < 0) return 0; return 1; } //end of the function WriteFloat //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int WriteStructWithIndent(FILE *fp, structdef_t *def, char *structure, int indent) { int i, num; void *p; fielddef_t *fd; if (!WriteIndent(fp, indent)) return qfalse; if (fprintf(fp, "{\r\n") < 0) return qfalse; indent++; for (i = 0; def->fields[i].name; i++) { fd = &def->fields[i]; if (!WriteIndent(fp, indent)) return qfalse; if (fprintf(fp, "%s\t", fd->name) < 0) return qfalse; p = (void *)(structure + fd->offset); if (fd->type & FT_ARRAY) { num = fd->maxarray; if (fprintf(fp, "{") < 0) return qfalse; } //end if else { num = 1; } //end else while(num-- > 0) { switch(fd->type & FT_TYPE) { case FT_CHAR: { if (fprintf(fp, "%d", *(char *) p) < 0) return qfalse; p = (char *) p + sizeof(char); break; } //end case case FT_INT: { if (fprintf(fp, "%d", *(int *) p) < 0) return qfalse; p = (char *) p + sizeof(int); break; } //end case case FT_FLOAT: { if (!WriteFloat(fp, *(float *)p)) return qfalse; p = (char *) p + sizeof(float); break; } //end case case FT_STRING: { if (fprintf(fp, "\"%s\"", (char *) p) < 0) return qfalse; p = (char *) p + MAX_STRINGFIELD; break; } //end case case FT_STRUCT: { if (!WriteStructWithIndent(fp, fd->substruct, structure, indent)) return qfalse; p = (char *) p + fd->substruct->size; break; } //end case } //end switch if (fd->type & FT_ARRAY) { if (num > 0) { if (fprintf(fp, ",") < 0) return qfalse; } //end if else { if (fprintf(fp, "}") < 0) return qfalse; } //end else } //end if } //end while if (fprintf(fp, "\r\n") < 0) return qfalse; } //end for indent--; if (!WriteIndent(fp, indent)) return qfalse; if (fprintf(fp, "}\r\n") < 0) return qfalse; return qtrue; } //end of the function WriteStructWithIndent //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int WriteStructure(FILE *fp, structdef_t *def, char *structure) { return WriteStructWithIndent(fp, def, structure, 0); } //end of the function WriteStructure ================================================ FILE: src/engine/botlib/l_struct.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: l_struct.h * * desc: structure reading/writing * * $Archive: /source/code/botlib/l_struct.h $ * *****************************************************************************/ #define MAX_STRINGFIELD 80 //field types #define FT_CHAR 1 // char #define FT_INT 2 // int #define FT_FLOAT 3 // float #define FT_STRING 4 // char [MAX_STRINGFIELD] #define FT_STRUCT 6 // struct (sub structure) //type only mask #define FT_TYPE 0x00FF // only type, clear subtype //sub types #define FT_ARRAY 0x0100 // array of type #define FT_BOUNDED 0x0200 // bounded value #define FT_UNSIGNED 0x0400 //structure field definition typedef struct fielddef_s { char *name; //name of the field int offset; //offset in the structure int type; //type of the field //type specific fields int maxarray; //maximum array size float floatmin, floatmax; //float min and max struct structdef_s *substruct; //sub structure } fielddef_t; //structure definition typedef struct structdef_s { int size; fielddef_t *fields; } structdef_t; //read a structure from a script int ReadStructure(source_t *source, structdef_t *def, char *structure); //write a structure to a file int WriteStructure(FILE *fp, structdef_t *def, char *structure); //writes indents int WriteIndent(FILE *fp, int indent); //writes a float without traling zeros int WriteFloat(FILE *fp, float value); ================================================ FILE: src/engine/botlib/l_utils.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: l_util.h * * desc: utils * * $Archive: /source/code/botlib/l_util.h $ * *****************************************************************************/ #define Vector2Angles(v,a) vectoangles(v,a) #define MAX_PATH MAX_QPATH #define Maximum(x,y) (x > y ? x : y) #define Minimum(x,y) (x < y ? x : y) ================================================ FILE: src/engine/client/cl_cgame.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // cl_cgame.c -- client system interaction with client game #include "client.h" #include "../../game/botlib.h" extern botlib_export_t *botlib_export; extern qboolean loadCamera(const char *name); extern void startCamera(int time); extern qboolean getCameraInfo(int time, vec3_t *origin, vec3_t *angles); /* ==================== CL_GetGameState ==================== */ void CL_GetGameState( gameState_t *gs ) { *gs = cl.gameState; } /* ==================== CL_GetGlconfig ==================== */ void CL_GetGlconfig( glconfig_t *glconfig ) { *glconfig = cls.glconfig; } /* ==================== CL_GetUserCmd ==================== */ qboolean CL_GetUserCmd( int cmdNumber, usercmd_t *ucmd ) { // cmds[cmdNumber] is the last properly generated command // can't return anything that we haven't created yet if ( cmdNumber > cl.cmdNumber ) { Com_Error( ERR_DROP, "CL_GetUserCmd: %i >= %i", cmdNumber, cl.cmdNumber ); } // the usercmd has been overwritten in the wrapping // buffer because it is too far out of date if ( cmdNumber <= cl.cmdNumber - CMD_BACKUP ) { return qfalse; } *ucmd = cl.cmds[ cmdNumber & CMD_MASK ]; return qtrue; } int CL_GetCurrentCmdNumber( void ) { return cl.cmdNumber; } /* ==================== CL_GetParseEntityState ==================== */ qboolean CL_GetParseEntityState( int parseEntityNumber, entityState_t *state ) { // can't return anything that hasn't been parsed yet if ( parseEntityNumber >= cl.parseEntitiesNum ) { Com_Error( ERR_DROP, "CL_GetParseEntityState: %i >= %i", parseEntityNumber, cl.parseEntitiesNum ); } // can't return anything that has been overwritten in the circular buffer if ( parseEntityNumber <= cl.parseEntitiesNum - MAX_PARSE_ENTITIES ) { return qfalse; } *state = cl.parseEntities[ parseEntityNumber & ( MAX_PARSE_ENTITIES - 1 ) ]; return qtrue; } /* ==================== CL_GetCurrentSnapshotNumber ==================== */ void CL_GetCurrentSnapshotNumber( int *snapshotNumber, int *serverTime ) { *snapshotNumber = cl.snap.messageNum; *serverTime = cl.snap.serverTime; } /* ==================== CL_GetSnapshot ==================== */ qboolean CL_GetSnapshot( int snapshotNumber, snapshot_t *snapshot ) { clSnapshot_t *clSnap; int i, count; if ( snapshotNumber > cl.snap.messageNum ) { Com_Error( ERR_DROP, "CL_GetSnapshot: snapshotNumber > cl.snapshot.messageNum" ); } // if the frame has fallen out of the circular buffer, we can't return it if ( cl.snap.messageNum - snapshotNumber >= PACKET_BACKUP ) { return qfalse; } // if the frame is not valid, we can't return it clSnap = &cl.snapshots[snapshotNumber & PACKET_MASK]; if ( !clSnap->valid ) { return qfalse; } // if the entities in the frame have fallen out of their // circular buffer, we can't return it if ( cl.parseEntitiesNum - clSnap->parseEntitiesNum >= MAX_PARSE_ENTITIES ) { return qfalse; } // write the snapshot snapshot->snapFlags = clSnap->snapFlags; snapshot->serverCommandSequence = clSnap->serverCommandNum; snapshot->ping = clSnap->ping; snapshot->serverTime = clSnap->serverTime; Com_Memcpy( snapshot->areamask, clSnap->areamask, sizeof( snapshot->areamask ) ); snapshot->ps = clSnap->ps; count = clSnap->numEntities; if ( count > MAX_ENTITIES_IN_SNAPSHOT ) { Com_DPrintf( "CL_GetSnapshot: truncated %i entities to %i\n", count, MAX_ENTITIES_IN_SNAPSHOT ); count = MAX_ENTITIES_IN_SNAPSHOT; } snapshot->numEntities = count; for ( i = 0 ; i < count ; i++ ) { snapshot->entities[i] = cl.parseEntities[ ( clSnap->parseEntitiesNum + i ) & (MAX_PARSE_ENTITIES-1) ]; } // FIXME: configstring changes and server commands!!! return qtrue; } /* ===================== CL_SetUserCmdValue ===================== */ void CL_SetUserCmdValue( int userCmdValue, float sensitivityScale ) { cl.cgameUserCmdValue = userCmdValue; cl.cgameSensitivity = sensitivityScale; } /* ===================== CL_AddCgameCommand ===================== */ void CL_AddCgameCommand( const char *cmdName ) { Cmd_AddCommand( cmdName, NULL ); } /* ===================== CL_CgameError ===================== */ void CL_CgameError( const char *string ) { Com_Error( ERR_DROP, "%s", string ); } /* ===================== CL_ConfigstringModified ===================== */ void CL_ConfigstringModified( void ) { char *old, *s; int i, index; char *dup; gameState_t oldGs; int len; index = atoi( Cmd_Argv(1) ); if ( index < 0 || index >= MAX_CONFIGSTRINGS ) { Com_Error( ERR_DROP, "configstring > MAX_CONFIGSTRINGS" ); } // get everything after "cs " s = Cmd_ArgsFrom(2); old = cl.gameState.stringData + cl.gameState.stringOffsets[ index ]; if ( !strcmp( old, s ) ) { return; // unchanged } // build the new gameState_t oldGs = cl.gameState; Com_Memset( &cl.gameState, 0, sizeof( cl.gameState ) ); // leave the first 0 for uninitialized strings cl.gameState.dataCount = 1; for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) { if ( i == index ) { dup = s; } else { dup = oldGs.stringData + oldGs.stringOffsets[ i ]; } if ( !dup[0] ) { continue; // leave with the default empty string } len = (int)strlen( dup ); if ( len + 1 + cl.gameState.dataCount > MAX_GAMESTATE_CHARS ) { Com_Error( ERR_DROP, "MAX_GAMESTATE_CHARS exceeded" ); } // append it to the gameState string buffer cl.gameState.stringOffsets[ i ] = cl.gameState.dataCount; Com_Memcpy( cl.gameState.stringData + cl.gameState.dataCount, dup, len + 1 ); cl.gameState.dataCount += len + 1; } if ( index == CS_SYSTEMINFO ) { // parse serverId and other cvars CL_SystemInfoChanged(); } } /* =================== CL_GetServerCommand Set up argc/argv for the given command =================== */ qboolean CL_GetServerCommand( int serverCommandNumber ) { char *s; char *cmd; static char bigConfigString[BIG_INFO_STRING]; int argc; // if we have irretrievably lost a reliable command, drop the connection if ( serverCommandNumber <= clc.serverCommandSequence - MAX_RELIABLE_COMMANDS ) { // when a demo record was started after the client got a whole bunch of // reliable commands then the client never got those first reliable commands if ( clc.demoplaying ) return qfalse; Com_Error( ERR_DROP, "CL_GetServerCommand: a reliable command was cycled out" ); return qfalse; } if ( serverCommandNumber > clc.serverCommandSequence ) { Com_Error( ERR_DROP, "CL_GetServerCommand: requested a command not received" ); return qfalse; } s = clc.serverCommands[ serverCommandNumber & ( MAX_RELIABLE_COMMANDS - 1 ) ]; clc.lastExecutedServerCommand = serverCommandNumber; Com_DPrintf( "serverCommand: %i : %s\n", serverCommandNumber, s ); rescan: Cmd_TokenizeString( s ); cmd = Cmd_Argv(0); argc = Cmd_Argc(); if ( !strcmp( cmd, "disconnect" ) ) { // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=552 // allow server to indicate why they were disconnected if ( argc >= 2 ) Com_Error (ERR_SERVERDISCONNECT, va( "Server Disconnected - %s", Cmd_Argv( 1 ) ) ); else Com_Error (ERR_SERVERDISCONNECT,"Server disconnected\n"); } if ( !strcmp( cmd, "bcs0" ) ) { Com_sprintf( bigConfigString, BIG_INFO_STRING, "cs %s \"%s", Cmd_Argv(1), Cmd_Argv(2) ); return qfalse; } if ( !strcmp( cmd, "bcs1" ) ) { s = Cmd_Argv(2); if( (int)strlen(bigConfigString) + (int)strlen(s) >= BIG_INFO_STRING ) { Com_Error( ERR_DROP, "bcs exceeded BIG_INFO_STRING" ); } strcat( bigConfigString, s ); return qfalse; } if ( !strcmp( cmd, "bcs2" ) ) { s = Cmd_Argv(2); if( (int)strlen(bigConfigString) + (int)strlen(s) + 1 >= BIG_INFO_STRING ) { Com_Error( ERR_DROP, "bcs exceeded BIG_INFO_STRING" ); } strcat( bigConfigString, s ); strcat( bigConfigString, "\"" ); s = bigConfigString; goto rescan; } if ( !strcmp( cmd, "cs" ) ) { CL_ConfigstringModified(); // reparse the string, because CL_ConfigstringModified may have done another Cmd_TokenizeString() Cmd_TokenizeString( s ); return qtrue; } if ( !strcmp( cmd, "map_restart" ) ) { // clear notify lines and outgoing commands before passing // the restart to the cgame Con_ClearNotify(); Com_Memset( cl.cmds, 0, sizeof( cl.cmds ) ); return qtrue; } // the clientLevelShot command is used during development // to generate 128*128 screenshots from the intermission // point of levels for the menu system to use // we pass it along to the cgame to make apropriate adjustments, // but we also clear the console and notify lines here if ( !strcmp( cmd, "clientLevelShot" ) ) { // don't do it if we aren't running the server locally, // otherwise malicious remote servers could overwrite // the existing thumbnails if ( !com_sv_running->integer ) { return qfalse; } // close the console Con_Close(); // take a special screenshot next frame Cbuf_AddText( "wait ; wait ; wait ; wait ; screenshot levelshot\n" ); return qtrue; } // we may want to put a "connect to other server" command here // cgame can now act on the command return qtrue; } /* ==================== CL_CM_LoadMap Just adds default parameters that cgame doesn't need to know about ==================== */ void CL_CM_LoadMap( const char *mapname ) { int checksum; CM_LoadMap( mapname, qtrue, &checksum ); } /* ==================== CL_ShutdonwCGame ==================== */ void CL_ShutdownCGame( void ) { cls.keyCatchers &= ~KEYCATCH_CGAME; cls.cgameStarted = qfalse; if ( !cgvm ) { return; } VM_Call( cgvm, CG_SHUTDOWN ); VM_Free( cgvm ); cgvm = NULL; } static int FloatAsInt( float f ) { int temp; *(float *)&temp = f; return temp; } /* ==================== CL_CgameSystemCalls The cgame module is making a system call ==================== */ #define VMA(x) VM_ArgPtr(args[x]) #define VMF(x) (*(float*)&args[x]) intptr_t CL_CgameSystemCalls( intptr_t *args ) { switch( args[0] ) { case CG_PRINT: Com_Printf( "%s", VMA(1) ); return 0; case CG_ERROR: Com_Error( ERR_DROP, "%s", VMA(1) ); return 0; case CG_MILLISECONDS: return Sys_Milliseconds(); case CG_CVAR_REGISTER: Cvar_Register( (vmCvar_t*) VMA(1), (const char*) VMA(2), (const char*) VMA(3), args[4] ); return 0; case CG_CVAR_UPDATE: Cvar_Update( (vmCvar_t*) VMA(1) ); return 0; case CG_CVAR_SET: Cvar_Set( (const char*) VMA(1), (const char*) VMA(2) ); return 0; case CG_CVAR_VARIABLESTRINGBUFFER: Cvar_VariableStringBuffer( (const char*) VMA(1), (char*) VMA(2), args[3] ); return 0; case CG_ARGC: return Cmd_Argc(); case CG_ARGV: Cmd_ArgvBuffer( args[1], (char*) VMA(2), args[3] ); return 0; case CG_ARGS: Cmd_ArgsBuffer( (char*) VMA(1), args[2] ); return 0; case CG_FS_FOPENFILE: return FS_FOpenFileByMode( (const char*) VMA(1), (fileHandle_t*) VMA(2), (fsMode_t) args[3] ); case CG_FS_READ: FS_Read2( VMA(1), args[2], args[3] ); return 0; case CG_FS_WRITE: FS_Write( VMA(1), args[2], args[3] ); return 0; case CG_FS_FCLOSEFILE: FS_FCloseFile( args[1] ); return 0; case CG_FS_SEEK: return FS_Seek( args[1], args[2], args[3] ); case CG_SENDCONSOLECOMMAND: Cbuf_AddText( (const char*) VMA(1) ); return 0; case CG_ADDCOMMAND: CL_AddCgameCommand( (const char*) VMA(1) ); return 0; case CG_REMOVECOMMAND: Cmd_RemoveCommand( (const char*) VMA(1) ); return 0; case CG_SENDCLIENTCOMMAND: CL_AddReliableCommand( (const char*) VMA(1) ); return 0; case CG_UPDATESCREEN: // this is used during lengthy level loading, so pump message loop // Com_EventLoop(); // FIXME: if a server restarts here, BAD THINGS HAPPEN! // We can't call Com_EventLoop here, a restart will crash and this _does_ happen // if there is a map change while we are downloading at pk3. // ZOID SCR_UpdateScreen(); return 0; case CG_CM_LOADMAP: CL_CM_LoadMap( (const char*) VMA(1) ); return 0; case CG_CM_NUMINLINEMODELS: return CM_NumInlineModels(); case CG_CM_INLINEMODEL: return CM_InlineModel( args[1] ); case CG_CM_TEMPBOXMODEL: return CM_TempBoxModel((const vec_t*)VMA(1), (const vec_t*) VMA(2), /*int capsule*/ qfalse); case CG_CM_TEMPCAPSULEMODEL: return CM_TempBoxModel((const vec_t*)VMA(1), (const vec_t*) VMA(2), /*int capsule*/ qtrue); case CG_CM_POINTCONTENTS: return CM_PointContents((const vec_t*) VMA(1), args[2]); case CG_CM_TRANSFORMEDPOINTCONTENTS: return CM_TransformedPointContents((const vec_t*)VMA(1), args[2], (const vec_t*)VMA(3), (const vec_t*) VMA(4)); case CG_CM_BOXTRACE: CM_BoxTrace((trace_t*)VMA(1), (const vec_t*)VMA(2), (const vec_t*) VMA(3), (vec_t*) VMA(4), (vec_t*) VMA(5), args[6], args[7], /*int capsule*/ qfalse); return 0; case CG_CM_CAPSULETRACE: CM_BoxTrace((trace_t*)VMA(1), (const vec_t*)VMA(2), (const vec_t*) VMA(3), (vec_t*) VMA(4), (vec_t*) VMA(5), args[6], args[7], /*int capsule*/ qtrue); return 0; case CG_CM_TRANSFORMEDBOXTRACE: CM_TransformedBoxTrace((trace_t*)VMA(1), (const vec_t*)VMA(2), (const vec_t*)VMA(3), (vec_t*)VMA(4), (vec_t*)VMA(5), args[6], args[7], (const vec_t*)VMA(8), (const vec_t*) VMA(9), /*int capsule*/ qfalse); return 0; case CG_CM_TRANSFORMEDCAPSULETRACE: CM_TransformedBoxTrace((trace_t*)VMA(1), (const vec_t*)VMA(2), (const vec_t*)VMA(3), (vec_t*)VMA(4), (vec_t*)VMA(5), args[6], args[7], (const vec_t*)VMA(8), (const vec_t*) VMA(9), /*int capsule*/ qtrue); return 0; case CG_CM_MARKFRAGMENTS: return re.MarkFragments(args[1], (const vec3_t*)VMA(2), (const vec_t*) VMA(3), args[4], (vec_t*) VMA(5), args[6], (markFragment_t*) VMA(7)); case CG_S_STARTSOUND: S_StartSound( (vec_t*) VMA(1), args[2], args[3], args[4] ); return 0; case CG_S_STARTLOCALSOUND: S_StartLocalSound( args[1], args[2] ); return 0; case CG_S_CLEARLOOPINGSOUNDS: S_ClearLoopingSounds((qboolean) args[1]); return 0; case CG_S_ADDLOOPINGSOUND: S_AddLoopingSound(args[1], (const vec_t*)VMA(2), (const vec_t*) VMA(3), args[4]); return 0; case CG_S_ADDREALLOOPINGSOUND: S_AddRealLoopingSound(args[1], (const vec_t*)VMA(2), (const vec_t*) VMA(3), args[4]); return 0; case CG_S_STOPLOOPINGSOUND: S_StopLoopingSound( args[1] ); return 0; case CG_S_UPDATEENTITYPOSITION: S_UpdateEntityPosition(args[1], (const vec_t*) VMA(2)); return 0; case CG_S_RESPATIALIZE: S_Respatialize(args[1], (const vec_t*) VMA(2), (vec3_t*) VMA(3), args[4]); return 0; case CG_S_REGISTERSOUND: return S_RegisterSound( (const char*) VMA(1), (qboolean) args[2] ); case CG_S_STARTBACKGROUNDTRACK: S_StartBackgroundTrack( (const char*) VMA(1), (const char*) VMA(2) ); return 0; case CG_R_LOADWORLDMAP: re.LoadWorld( (const char*) VMA(1) ); return 0; case CG_R_REGISTERMODEL: return re.RegisterModel( (const char*) VMA(1) ); case CG_R_REGISTERSKIN: return re.RegisterSkin( (const char*) VMA(1) ); case CG_R_REGISTERSHADER: return re.RegisterShader( (const char*) VMA(1) ); case CG_R_REGISTERSHADERNOMIP: return re.RegisterShaderNoMip( (const char*) VMA(1) ); case CG_R_REGISTERFONT: re.RegisterFont( (const char*) VMA(1), args[2], (fontInfo_t*) VMA(3)); case CG_R_CLEARSCENE: re.ClearScene(); return 0; case CG_R_ADDREFENTITYTOSCENE: re.AddRefEntityToScene( (const refEntity_t*) VMA(1) ); return 0; case CG_R_ADDPOLYTOSCENE: re.AddPolyToScene( args[1], args[2], (const polyVert_t*) VMA(3), 1 ); return 0; case CG_R_ADDPOLYSTOSCENE: re.AddPolyToScene( args[1], args[2], (const polyVert_t*) VMA(3), args[4] ); return 0; case CG_R_LIGHTFORPOINT: return re.LightForPoint( (vec_t*) VMA(1), (vec_t*) VMA(2), (vec_t*) VMA(3), (vec_t*) VMA(4) ); case CG_R_ADDLIGHTTOSCENE: re.AddLightToScene((const vec_t*) VMA(1), VMF(2), VMF(3), VMF(4), VMF(5)); return 0; case CG_R_ADDADDITIVELIGHTTOSCENE: re.AddAdditiveLightToScene((const vec_t*) VMA(1), VMF(2), VMF(3), VMF(4), VMF(5)); return 0; case CG_R_RENDERSCENE: re.RenderScene( (const refdef_t*) VMA(1) ); return 0; case CG_R_SETCOLOR: re.SetColor( (const float*) VMA(1) ); return 0; case CG_R_DRAWSTRETCHPIC: re.DrawStretchPic( VMF(1), VMF(2), VMF(3), VMF(4), VMF(5), VMF(6), VMF(7), VMF(8), args[9] ); return 0; case CG_R_MODELBOUNDS: re.ModelBounds( args[1], (vec_t*) VMA(2), (vec_t*) VMA(3) ); return 0; case CG_R_LERPTAG: return re.LerpTag( (orientation_t*) VMA(1), args[2], args[3], args[4], VMF(5), (const char*) VMA(6) ); case CG_GETGLCONFIG: CL_GetGlconfig( (glconfig_t*) VMA(1) ); return 0; case CG_GETGAMESTATE: CL_GetGameState( (gameState_t*) VMA(1) ); return 0; case CG_GETCURRENTSNAPSHOTNUMBER: CL_GetCurrentSnapshotNumber( (int*) VMA(1), (int*) VMA(2) ); return 0; case CG_GETSNAPSHOT: return CL_GetSnapshot( args[1], (snapshot_t*) VMA(2) ); case CG_GETSERVERCOMMAND: return CL_GetServerCommand( args[1] ); case CG_GETCURRENTCMDNUMBER: return CL_GetCurrentCmdNumber(); case CG_GETUSERCMD: return CL_GetUserCmd( args[1], (usercmd_t*) VMA(2) ); case CG_SETUSERCMDVALUE: CL_SetUserCmdValue( args[1], VMF(2) ); return 0; case CG_MEMORY_REMAINING: return Hunk_MemoryRemaining(); case CG_KEY_ISDOWN: return Key_IsDown( args[1] ); case CG_KEY_GETCATCHER: return Key_GetCatcher(); case CG_KEY_SETCATCHER: Key_SetCatcher( args[1] ); return 0; case CG_KEY_GETKEY: return Key_GetKey( (const char*) VMA(1) ); case CG_MEMSET: Com_Memset( VMA(1), args[2], args[3] ); return 0; case CG_MEMCPY: Com_Memcpy( VMA(1), VMA(2), args[3] ); return 0; case CG_STRNCPY: strncpy( (char*)VMA(1), (const char*)VMA(2), args[3] ); return args[1]; case CG_SIN: return FloatAsInt( sin( VMF(1) ) ); case CG_COS: return FloatAsInt( cos( VMF(1) ) ); case CG_ATAN2: return FloatAsInt( atan2( VMF(1), VMF(2) ) ); case CG_SQRT: return FloatAsInt( sqrt( VMF(1) ) ); case CG_FLOOR: return FloatAsInt( floor( VMF(1) ) ); case CG_CEIL: return FloatAsInt( ceil( VMF(1) ) ); case CG_ACOS: return FloatAsInt( Q_acos( VMF(1) ) ); case CG_PC_ADD_GLOBAL_DEFINE: return botlib_export->PC_AddGlobalDefine( (char*) VMA(1) ); case CG_PC_LOAD_SOURCE: return botlib_export->PC_LoadSourceHandle( (const char*) VMA(1) ); case CG_PC_FREE_SOURCE: return botlib_export->PC_FreeSourceHandle( args[1] ); case CG_PC_READ_TOKEN: return botlib_export->PC_ReadTokenHandle( args[1], (pc_token_t*) VMA(2) ); case CG_PC_SOURCE_FILE_AND_LINE: return botlib_export->PC_SourceFileAndLine( args[1], (char*) VMA(2), (int*) VMA(3) ); case CG_S_STOPBACKGROUNDTRACK: S_StopBackgroundTrack(); return 0; case CG_REAL_TIME: return Com_RealTime( (qtime_t*) VMA(1) ); case CG_SNAPVECTOR: Sys_SnapVector( (float*) VMA(1) ); return 0; case CG_CIN_PLAYCINEMATIC: return CIN_PlayCinematic((const char*) VMA(1), args[2], args[3], args[4], args[5], args[6]); case CG_CIN_STOPCINEMATIC: return CIN_StopCinematic(args[1]); case CG_CIN_RUNCINEMATIC: return CIN_RunCinematic(args[1]); case CG_CIN_DRAWCINEMATIC: CIN_DrawCinematic(args[1]); return 0; case CG_CIN_SETEXTENTS: CIN_SetExtents(args[1], args[2], args[3], args[4], args[5]); return 0; case CG_R_REMAP_SHADER: re.RemapShader( (const char*) VMA(1), (const char*) VMA(2), (const char*) VMA(3) ); return 0; /* case CG_LOADCAMERA: return loadCamera(VMA(1)); case CG_STARTCAMERA: startCamera(args[1]); return 0; case CG_GETCAMERAINFO: return getCameraInfo(args[1], VMA(2), VMA(3)); */ case CG_GET_ENTITY_TOKEN: return re.GetEntityToken( (char*) VMA(1), args[2] ); case CG_R_INPVS: return re.inPVS((const vec_t*)VMA(1), (const vec_t*) VMA(2)); default: assert(0); // bk010102 Com_Error( ERR_DROP, "Bad cgame system trap: %i", args[0] ); } return 0; } /* ==================== CL_InitCGame Should only be called by CL_StartHunkUsers ==================== */ void CL_InitCGame( void ) { const char *info; const char *mapname; int t1, t2; vmInterpret_t interpret; t1 = Sys_Milliseconds(); // put away the console Con_Close(); // find the current mapname info = cl.gameState.stringData + cl.gameState.stringOffsets[ CS_SERVERINFO ]; mapname = Info_ValueForKey( info, "mapname" ); Com_sprintf( cl.mapname, sizeof( cl.mapname ), "maps/%s.bsp", mapname ); // load the dll or bytecode if ( cl_connectedToPureServer != 0 ) { // if sv_pure is set we only allow qvms to be loaded interpret = VMI_BYTECODE; } else { interpret = (vmInterpret_t) (int) Cvar_VariableValue( "vm_cgame" ); } cgvm = VM_Create( "cgame", CL_CgameSystemCalls, interpret ); if ( !cgvm ) { Com_Error( ERR_DROP, "VM_Create on cgame failed" ); } cls.state = CA_LOADING; // init for this gamestate // use the lastExecutedServerCommand instead of the serverCommandSequence // otherwise server commands sent just before a gamestate are dropped VM_Call( cgvm, CG_INIT, clc.serverMessageSequence, clc.lastExecutedServerCommand, clc.clientNum ); // we will send a usercmd this frame, which // will cause the server to send us the first snapshot cls.state = CA_PRIMED; t2 = Sys_Milliseconds(); Com_Printf( "CL_InitCGame: %5.2f seconds\n", (t2-t1)/1000.0 ); // have the renderer touch all its images, so they are present // on the card even if the driver does deferred loading re.EndRegistration(); // make sure everything is paged in if (!Sys_LowPhysicalMemory()) { Com_TouchMemory(); } // clear anything that got printed Con_ClearNotify (); } /* ==================== CL_GameCommand See if the current console command is claimed by the cgame ==================== */ qboolean CL_GameCommand( void ) { if ( !cgvm ) { return qfalse; } return (qboolean) VM_Call( cgvm, CG_CONSOLE_COMMAND ); } /* ===================== CL_CGameRendering ===================== */ void CL_CGameRendering( stereoFrame_t stereo ) { VM_Call( cgvm, CG_DRAW_ACTIVE_FRAME, cl.serverTime, stereo, clc.demoplaying ); VM_Debug( 0 ); } /* ================= CL_AdjustTimeDelta Adjust the clients view of server time. We attempt to have cl.serverTime exactly equal the server's view of time plus the timeNudge, but with variable latencies over the internet it will often need to drift a bit to match conditions. Our ideal time would be to have the adjusted time approach, but not pass, the very latest snapshot. Adjustments are only made when a new snapshot arrives with a rational latency, which keeps the adjustment process framerate independent and prevents massive overadjustment during times of significant packet loss or bursted delayed packets. ================= */ #define RESET_TIME 500 void CL_AdjustTimeDelta( void ) { int resetTime; int newDelta; int deltaDelta; cl.newSnapshots = qfalse; // the delta never drifts when replaying a demo if ( clc.demoplaying ) { return; } // if the current time is WAY off, just correct to the current value if ( com_sv_running->integer ) { resetTime = 100; } else { resetTime = RESET_TIME; } newDelta = cl.snap.serverTime - cls.realtime; deltaDelta = abs( newDelta - cl.serverTimeDelta ); if ( deltaDelta > RESET_TIME ) { cl.serverTimeDelta = newDelta; cl.oldServerTime = cl.snap.serverTime; // FIXME: is this a problem for cgame? cl.serverTime = cl.snap.serverTime; if ( cl_showTimeDelta->integer ) { Com_Printf( " " ); } } else if ( deltaDelta > 100 ) { // fast adjust, cut the difference in half if ( cl_showTimeDelta->integer ) { Com_Printf( " " ); } cl.serverTimeDelta = ( cl.serverTimeDelta + newDelta ) >> 1; } else { // slow drift adjust, only move 1 or 2 msec // if any of the frames between this and the previous snapshot // had to be extrapolated, nudge our sense of time back a little // the granularity of +1 / -2 is too high for timescale modified frametimes if ( com_timescale->value == 0 || com_timescale->value == 1 ) { if ( cl.extrapolatedSnapshot ) { cl.extrapolatedSnapshot = qfalse; cl.serverTimeDelta -= 2; } else { // otherwise, move our sense of time forward to minimize total latency cl.serverTimeDelta++; } } } if ( cl_showTimeDelta->integer ) { Com_Printf( "%i ", cl.serverTimeDelta ); } } /* ================== CL_FirstSnapshot ================== */ void CL_FirstSnapshot( void ) { // ignore snapshots that don't have entities if ( cl.snap.snapFlags & SNAPFLAG_NOT_ACTIVE ) { return; } cls.state = CA_ACTIVE; // set the timedelta so we are exactly on this first frame cl.serverTimeDelta = cl.snap.serverTime - cls.realtime; cl.oldServerTime = cl.snap.serverTime; clc.timeDemoBaseTime = cl.snap.serverTime; // if this is the first frame of active play, // execute the contents of activeAction now // this is to allow scripting a timedemo to start right // after loading if ( cl_activeAction->string[0] ) { Cbuf_AddText( cl_activeAction->string ); Cvar_Set( "activeAction", "" ); } Sys_BeginProfiling(); } /* ================== CL_SetCGameTime ================== */ void CL_SetCGameTime( void ) { // getting a valid frame message ends the connection process if ( cls.state != CA_ACTIVE ) { if ( cls.state != CA_PRIMED ) { return; } if ( clc.demoplaying ) { // we shouldn't get the first snapshot on the same frame // as the gamestate, because it causes a bad time skip if ( !clc.firstDemoFrameSkipped ) { clc.firstDemoFrameSkipped = qtrue; return; } CL_ReadDemoMessage(); } if ( cl.newSnapshots ) { cl.newSnapshots = qfalse; CL_FirstSnapshot(); } if ( cls.state != CA_ACTIVE ) { return; } } // if we have gotten to this point, cl.snap is guaranteed to be valid if ( !cl.snap.valid ) { Com_Error( ERR_DROP, "CL_SetCGameTime: !cl.snap.valid" ); } // allow pause in single player if ( sv_paused->integer && cl_paused->integer && com_sv_running->integer ) { // paused return; } if ( cl.snap.serverTime < cl.oldFrameServerTime ) { Com_Error( ERR_DROP, "cl.snap.serverTime < cl.oldFrameServerTime" ); } cl.oldFrameServerTime = cl.snap.serverTime; // get our current view of time if ( clc.demoplaying && cl_freezeDemo->integer ) { // cl_freezeDemo is used to lock a demo in place for single frame advances } else { // cl_timeNudge is a user adjustable cvar that allows more // or less latency to be added in the interest of better // smoothness or better responsiveness. int tn; tn = cl_timeNudge->integer; if (tn<-30) { tn = -30; } else if (tn>30) { tn = 30; } cl.serverTime = cls.realtime + cl.serverTimeDelta - tn; // guarantee that time will never flow backwards, even if // serverTimeDelta made an adjustment or cl_timeNudge was changed if ( cl.serverTime < cl.oldServerTime ) { cl.serverTime = cl.oldServerTime; } cl.oldServerTime = cl.serverTime; // note if we are almost past the latest frame (without timeNudge), // so we will try and adjust back a bit when the next snapshot arrives if ( cls.realtime + cl.serverTimeDelta >= cl.snap.serverTime - 5 ) { cl.extrapolatedSnapshot = qtrue; } } // if we have gotten new snapshots, drift serverTimeDelta // don't do this every frame, or a period of packet loss would // make a huge adjustment if ( cl.newSnapshots ) { CL_AdjustTimeDelta(); } if ( !clc.demoplaying ) { return; } // if we are playing a demo back, we can just keep reading // messages from the demo file until the cgame definately // has valid snapshots to interpolate between // a timedemo will always use a deterministic set of time samples // no matter what speed machine it is run on, // while a normal demo may have different time samples // each time it is played back if ( cl_timedemo->integer ) { if (!clc.timeDemoStart) { clc.timeDemoStart = Sys_Milliseconds(); } clc.timeDemoFrames++; cl.serverTime = clc.timeDemoBaseTime + clc.timeDemoFrames * 50; } while ( cl.serverTime >= cl.snap.serverTime ) { // feed another messag, which should change // the contents of cl.snap CL_ReadDemoMessage(); if ( cls.state != CA_ACTIVE ) { return; // end of demo } } } ================================================ FILE: src/engine/client/cl_cin.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: cl_cin.c * * desc: video and cinematic playback * * $Archive: /MissionPack/code/client/cl_cin.c $ * * cl_glconfig.hwtype trtypes 3dfx/ragepro need 256x256 * *****************************************************************************/ #include "client.h" #include "snd_local.h" #define MAXSIZE 8 #define MINSIZE 4 #define DEFAULT_CIN_WIDTH 512 #define DEFAULT_CIN_HEIGHT 512 #define ROQ_QUAD 0x1000 #define ROQ_QUAD_INFO 0x1001 #define ROQ_CODEBOOK 0x1002 #define ROQ_QUAD_VQ 0x1011 #define ROQ_QUAD_JPEG 0x1012 #define ROQ_QUAD_HANG 0x1013 #define ROQ_PACKET 0x1030 #define ZA_SOUND_MONO 0x1020 #define ZA_SOUND_STEREO 0x1021 #define MAX_VIDEO_HANDLES 16 extern int s_paintedtime; extern int s_rawend; static void RoQ_init( void ); /****************************************************************************** * * Class: trFMV * * Description: RoQ/RnR manipulation routines * not entirely complete for first run * ******************************************************************************/ static long ROQ_YY_tab[256]; static long ROQ_UB_tab[256]; static long ROQ_UG_tab[256]; static long ROQ_VG_tab[256]; static long ROQ_VR_tab[256]; static unsigned short vq2[256*16*4]; static unsigned short vq4[256*64*4]; static unsigned short vq8[256*256*4]; typedef struct { byte linbuf[DEFAULT_CIN_WIDTH*DEFAULT_CIN_HEIGHT*4*2]; byte file[65536]; short sqrTable[256]; int mcomp[256]; byte *qStatus[2][32768]; long oldXOff, oldYOff, oldysize, oldxsize; int currentHandle; } cinematics_t; typedef struct { char fileName[MAX_OSPATH]; int CIN_WIDTH, CIN_HEIGHT; int xpos, ypos, width, height; qboolean looping, holdAtEnd, dirty, alterGameState, silent, shader; fileHandle_t iFile; e_status status; unsigned int startTime; unsigned int lastTime; long tfps; long RoQPlayed; long ROQSize; unsigned int RoQFrameSize; long onQuad; long numQuads; long samplesPerLine; unsigned int roq_id; long screenDelta; void ( *VQ0)(byte *status, void *qdata ); void ( *VQ1)(byte *status, void *qdata ); void ( *VQNormal)(byte *status, void *qdata ); void ( *VQBuffer)(byte *status, void *qdata ); long samplesPerPixel; // defaults to 2 byte* gray; unsigned int xsize, ysize, maxsize, minsize; qboolean half, smootheddouble, inMemory; long normalBuffer0; long roq_flags; long roqF0; long roqF1; long t[2]; long roqFPS; int playonwalls; byte* buf; long drawX, drawY; } cin_cache; static cinematics_t cin; static cin_cache cinTable[MAX_VIDEO_HANDLES]; static int currentHandle = -1; static int CL_handle = -1; extern int s_soundtime; // sample PAIRS extern int s_paintedtime; // sample PAIRS void CIN_CloseAllVideos(void) { int i; for ( i = 0 ; i < MAX_VIDEO_HANDLES ; i++ ) { if (cinTable[i].fileName[0] != 0 ) { CIN_StopCinematic(i); } } } static int CIN_HandleForVideo(void) { int i; for ( i = 0 ; i < MAX_VIDEO_HANDLES ; i++ ) { if ( cinTable[i].fileName[0] == 0 ) { return i; } } Com_Error( ERR_DROP, "CIN_HandleForVideo: none free" ); return -1; } extern int CL_ScaledMilliseconds(void); //----------------------------------------------------------------------------- // RllSetupTable // // Allocates and initializes the square table. // // Parameters: None // // Returns: Nothing //----------------------------------------------------------------------------- static void RllSetupTable() { int z; for (z=0;z<128;z++) { cin.sqrTable[z] = (short)(z*z); cin.sqrTable[z+128] = (short)(-cin.sqrTable[z]); } } //----------------------------------------------------------------------------- // RllDecodeMonoToMono // // Decode mono source data into a mono buffer. // // Parameters: from -> buffer holding encoded data // to -> buffer to hold decoded data // size = number of bytes of input (= # of shorts of output) // signedOutput = 0 for unsigned output, non-zero for signed output // flag = flags from asset header // // Returns: Number of samples placed in output buffer //----------------------------------------------------------------------------- long RllDecodeMonoToMono(unsigned char *from,short *to,unsigned int size,char signedOutput ,unsigned short flag) { unsigned int z; int prev; if (signedOutput) prev = flag - 0x8000; else prev = flag; for (z=0;z buffer holding encoded data // to -> buffer to hold decoded data // size = number of bytes of input (= 1/4 # of bytes of output) // signedOutput = 0 for unsigned output, non-zero for signed output // flag = flags from asset header // // Returns: Number of samples placed in output buffer //----------------------------------------------------------------------------- long RllDecodeMonoToStereo(unsigned char *from,short *to,unsigned int size,char signedOutput,unsigned short flag) { unsigned int z; int prev; if (signedOutput) prev = flag - 0x8000; else prev = flag; for (z = 0; z < size; z++) { prev = (short)(prev + cin.sqrTable[from[z]]); to[z*2+0] = to[z*2+1] = (short)(prev); } return size; // * 2 * sizeof(short)); } //----------------------------------------------------------------------------- // RllDecodeStereoToStereo // // Decode stereo source data into a stereo buffer. // // Parameters: from -> buffer holding encoded data // to -> buffer to hold decoded data // size = number of bytes of input (= 1/2 # of bytes of output) // signedOutput = 0 for unsigned output, non-zero for signed output // flag = flags from asset header // // Returns: Number of samples placed in output buffer //----------------------------------------------------------------------------- long RllDecodeStereoToStereo(unsigned char *from,short *to,unsigned int size,char signedOutput, unsigned short flag) { unsigned int z; unsigned char *zz = from; int prevL, prevR; if (signedOutput) { prevL = (flag & 0xff00) - 0x8000; prevR = ((flag & 0x00ff) << 8) - 0x8000; } else { prevL = flag & 0xff00; prevR = (flag & 0x00ff) << 8; } for (z=0;z>1); //*sizeof(short)); } //----------------------------------------------------------------------------- // RllDecodeStereoToMono // // Decode stereo source data into a mono buffer. // // Parameters: from -> buffer holding encoded data // to -> buffer to hold decoded data // size = number of bytes of input (= # of bytes of output) // signedOutput = 0 for unsigned output, non-zero for signed output // flag = flags from asset header // // Returns: Number of samples placed in output buffer //----------------------------------------------------------------------------- long RllDecodeStereoToMono(unsigned char *from,short *to,unsigned int size,char signedOutput, unsigned short flag) { unsigned int z; int prevL,prevR; if (signedOutput) { prevL = (flag & 0xff00) - 0x8000; prevR = ((flag & 0x00ff) << 8) -0x8000; } else { prevL = flag & 0xff00; prevR = (flag & 0x00ff) << 8; } for (z=0;z>3; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; dsrc += dspl; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; dsrc += dspl; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; dsrc += dspl; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; dsrc += dspl; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; dsrc += dspl; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; dsrc += dspl; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; dsrc += dspl; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; } /****************************************************************************** * * Function: * * Description: * ******************************************************************************/ static void move4_32( byte *src, byte *dst, int spl ) { double *dsrc, *ddst; int dspl; dsrc = (double *)src; ddst = (double *)dst; dspl = spl>>3; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; dsrc += dspl; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; dsrc += dspl; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; dsrc += dspl; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; } /****************************************************************************** * * Function: * * Description: * ******************************************************************************/ static void blit8_32( byte *src, byte *dst, int spl ) { double *dsrc, *ddst; int dspl; dsrc = (double *)src; ddst = (double *)dst; dspl = spl>>3; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; dsrc += 4; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; dsrc += 4; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; dsrc += 4; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; dsrc += 4; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; dsrc += 4; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; dsrc += 4; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; dsrc += 4; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; } /****************************************************************************** * * Function: * * Description: * ******************************************************************************/ #define movs double static void blit4_32( byte *src, byte *dst, int spl ) { movs *dsrc, *ddst; int dspl; dsrc = (movs *)src; ddst = (movs *)dst; dspl = spl>>3; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; dsrc += 2; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; dsrc += 2; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; dsrc += 2; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; } /****************************************************************************** * * Function: * * Description: * ******************************************************************************/ static void blit2_32( byte *src, byte *dst, int spl ) { double *dsrc, *ddst; int dspl; dsrc = (double *)src; ddst = (double *)dst; dspl = spl>>3; ddst[0] = dsrc[0]; ddst[dspl] = dsrc[1]; } /****************************************************************************** * * Function: * * Description: * ******************************************************************************/ static void blitVQQuad32fs( byte **status, unsigned char *data ) { unsigned short newd, celdata, code; unsigned int index, i; int spl; newd = 0; celdata = 0; index = 0; spl = cinTable[currentHandle].samplesPerLine; do { if (!newd) { newd = 7; celdata = data[0] + data[1]*256; data += 2; } else { newd--; } code = (unsigned short)(celdata&0xc000); celdata <<= 2; switch (code) { case 0x8000: // vq code blit8_32( (byte *)&vq8[(*data)*128], status[index], spl ); data++; index += 5; break; case 0xc000: // drop index++; // skip 8x8 for(i=0;i<4;i++) { if (!newd) { newd = 7; celdata = data[0] + data[1]*256; data += 2; } else { newd--; } code = (unsigned short)(celdata&0xc000); celdata <<= 2; switch (code) { // code in top two bits of code case 0x8000: // 4x4 vq code blit4_32( (byte *)&vq4[(*data)*32], status[index], spl ); data++; break; case 0xc000: // 2x2 vq code blit2_32( (byte *)&vq2[(*data)*8], status[index], spl ); data++; blit2_32( (byte *)&vq2[(*data)*8], status[index]+8, spl ); data++; blit2_32( (byte *)&vq2[(*data)*8], status[index]+spl*2, spl ); data++; blit2_32( (byte *)&vq2[(*data)*8], status[index]+spl*2+8, spl ); data++; break; case 0x4000: // motion compensation move4_32( status[index] + cin.mcomp[(*data)], status[index], spl ); data++; break; } index++; } break; case 0x4000: // motion compensation move8_32( status[index] + cin.mcomp[(*data)], status[index], spl ); data++; index += 5; break; case 0x0000: index += 5; break; } } while ( status[index] != NULL ); } /****************************************************************************** * * Function: * * Description: * ******************************************************************************/ static void ROQ_GenYUVTables( void ) { float t_ub,t_vr,t_ug,t_vg; long i; t_ub = (1.77200f/2.0f) * (float)(1<<6) + 0.5f; t_vr = (1.40200f/2.0f) * (float)(1<<6) + 0.5f; t_ug = (0.34414f/2.0f) * (float)(1<<6) + 0.5f; t_vg = (0.71414f/2.0f) * (float)(1<<6) + 0.5f; for(i=0;i<256;i++) { float x = (float)(2 * i - 255); ROQ_UB_tab[i] = (long)( ( t_ub * x) + (1<<5)); ROQ_VR_tab[i] = (long)( ( t_vr * x) + (1<<5)); ROQ_UG_tab[i] = (long)( (-t_ug * x) ); ROQ_VG_tab[i] = (long)( (-t_vg * x) + (1<<5)); ROQ_YY_tab[i] = (long)( (i << 6) | (i >> 2) ); } } #define VQ2TO4(a,b,c,d) { \ *c++ = a[0]; \ *d++ = a[0]; \ *d++ = a[0]; \ *c++ = a[1]; \ *d++ = a[1]; \ *d++ = a[1]; \ *c++ = b[0]; \ *d++ = b[0]; \ *d++ = b[0]; \ *c++ = b[1]; \ *d++ = b[1]; \ *d++ = b[1]; \ *d++ = a[0]; \ *d++ = a[0]; \ *d++ = a[1]; \ *d++ = a[1]; \ *d++ = b[0]; \ *d++ = b[0]; \ *d++ = b[1]; \ *d++ = b[1]; \ a += 2; b += 2; } #define VQ2TO2(a,b,c,d) { \ *c++ = *a; \ *d++ = *a; \ *d++ = *a; \ *c++ = *b; \ *d++ = *b; \ *d++ = *b; \ *d++ = *a; \ *d++ = *a; \ *d++ = *b; \ *d++ = *b; \ a++; b++; } /****************************************************************************** * * Function: * * Description: * ******************************************************************************/ static unsigned short yuv_to_rgb( long y, long u, long v ) { long r,g,b,YY = (long)(ROQ_YY_tab[(y)]); r = (YY + ROQ_VR_tab[v]) >> 9; g = (YY + ROQ_UG_tab[u] + ROQ_VG_tab[v]) >> 8; b = (YY + ROQ_UB_tab[u]) >> 9; if (r<0) r = 0; if (g<0) g = 0; if (b<0) b = 0; if (r > 31) r = 31; if (g > 63) g = 63; if (b > 31) b = 31; return (unsigned short)((r<<11)+(g<<5)+(b)); } /****************************************************************************** * * Function: * * Description: * ******************************************************************************/ #if defined(MACOS_X) static inline unsigned int yuv_to_rgb24( long y, long u, long v ) { long r,g,b,YY; YY = (long)(ROQ_YY_tab[(y)]); r = (YY + ROQ_VR_tab[v]) >> 6; g = (YY + ROQ_UG_tab[u] + ROQ_VG_tab[v]) >> 6; b = (YY + ROQ_UB_tab[u]) >> 6; if (r<0) r = 0; if (g<0) g = 0; if (b<0) b = 0; if (r > 255) r = 255; if (g > 255) g = 255; if (b > 255) b = 255; return ((r<<24)|(g<<16)|(b<<8))|(255); //+(255<<24)); } #else static unsigned int yuv_to_rgb24( long y, long u, long v ) { long r,g,b,YY = (long)(ROQ_YY_tab[(y)]); r = (YY + ROQ_VR_tab[v]) >> 6; g = (YY + ROQ_UG_tab[u] + ROQ_VG_tab[v]) >> 6; b = (YY + ROQ_UB_tab[u]) >> 6; if (r<0) r = 0; if (g<0) g = 0; if (b<0) b = 0; if (r > 255) r = 255; if (g > 255) g = 255; if (b > 255) b = 255; return LittleLong ((r)|(g<<8)|(b<<16)|(255<<24)); } #endif /****************************************************************************** * * Function: * * Description: * ******************************************************************************/ static void decodeCodeBook( byte *input, unsigned short roq_flags ) { long i, j, two, four; unsigned short *aptr, *bptr, *cptr, *dptr; long y0,y1,y2,y3,cr,cb; byte *bbptr, *baptr, *bcptr, *bdptr; unsigned int *iaptr, *ibptr, *icptr, *idptr; if (!roq_flags) { two = four = 256; } else { two = roq_flags>>8; if (!two) two = 256; four = roq_flags&0xff; } four *= 2; bptr = (unsigned short *)vq2; if (!cinTable[currentHandle].half) { if (!cinTable[currentHandle].smootheddouble) { // // normal height // if (cinTable[currentHandle].samplesPerPixel==2) { for(i=0;i cinTable[currentHandle].CIN_WIDTH) bigx = cinTable[currentHandle].CIN_WIDTH; if (bigy > cinTable[currentHandle].CIN_HEIGHT) bigy = cinTable[currentHandle].CIN_HEIGHT; if ( (startX >= lowx) && (startX+quadSize) <= (bigx) && (startY+quadSize) <= (bigy) && (startY >= lowy) && quadSize <= MAXSIZE) { useY = startY; scroff = cin.linbuf + (useY+((cinTable[currentHandle].CIN_HEIGHT-bigy)>>1)+yOff)*(cinTable[currentHandle].samplesPerLine) + (((startX+xOff))*cinTable[currentHandle].samplesPerPixel); cin.qStatus[0][cinTable[currentHandle].onQuad ] = scroff; cin.qStatus[1][cinTable[currentHandle].onQuad++] = scroff+offset; } if ( quadSize != MINSIZE ) { quadSize >>= 1; recurseQuad( startX, startY , quadSize, xOff, yOff ); recurseQuad( startX+quadSize, startY , quadSize, xOff, yOff ); recurseQuad( startX, startY+quadSize , quadSize, xOff, yOff ); recurseQuad( startX+quadSize, startY+quadSize , quadSize, xOff, yOff ); } } /****************************************************************************** * * Function: * * Description: * ******************************************************************************/ static void setupQuad( long xOff, long yOff ) { long numQuadCels, i,x,y; byte *temp; if (xOff == cin.oldXOff && yOff == cin.oldYOff && (long)cinTable[currentHandle].ysize == cin.oldysize && (long)cinTable[currentHandle].xsize == cin.oldxsize) { return; } cin.oldXOff = xOff; cin.oldYOff = yOff; cin.oldysize = cinTable[currentHandle].ysize; cin.oldxsize = cinTable[currentHandle].xsize; numQuadCels = (cinTable[currentHandle].CIN_WIDTH*cinTable[currentHandle].CIN_HEIGHT) / (16); numQuadCels += numQuadCels/4 + numQuadCels/16; numQuadCels += 64; // for overflow numQuadCels = (cinTable[currentHandle].xsize*cinTable[currentHandle].ysize) / (16); numQuadCels += numQuadCels/4; numQuadCels += 64; // for overflow cinTable[currentHandle].onQuad = 0; for(y=0;y<(long)cinTable[currentHandle].ysize;y+=16) for(x=0;x<(long)cinTable[currentHandle].xsize;x+=16) recurseQuad( x, y, 16, xOff, yOff ); temp = NULL; for(i=(numQuadCels-64);i= cinTable[currentHandle].ROQSize ) { if (cinTable[currentHandle].holdAtEnd==qfalse) { if (cinTable[currentHandle].looping) { RoQReset(); } else { cinTable[currentHandle].status = FMV_EOF; } } else { cinTable[currentHandle].status = FMV_IDLE; } return; } framedata = cin.file; // // new frame is ready // redump: switch(cinTable[currentHandle].roq_id) { case ROQ_QUAD_VQ: if ((cinTable[currentHandle].numQuads&1)) { cinTable[currentHandle].normalBuffer0 = cinTable[currentHandle].t[1]; RoQPrepMcomp( cinTable[currentHandle].roqF0, cinTable[currentHandle].roqF1 ); cinTable[currentHandle].VQ1( (byte *)cin.qStatus[1], framedata); cinTable[currentHandle].buf = cin.linbuf + cinTable[currentHandle].screenDelta; } else { cinTable[currentHandle].normalBuffer0 = cinTable[currentHandle].t[0]; RoQPrepMcomp( cinTable[currentHandle].roqF0, cinTable[currentHandle].roqF1 ); cinTable[currentHandle].VQ0( (byte *)cin.qStatus[0], framedata ); cinTable[currentHandle].buf = cin.linbuf; } if (cinTable[currentHandle].numQuads == 0) { // first frame Com_Memcpy(cin.linbuf+cinTable[currentHandle].screenDelta, cin.linbuf, cinTable[currentHandle].samplesPerLine*cinTable[currentHandle].ysize); } cinTable[currentHandle].numQuads++; cinTable[currentHandle].dirty = qtrue; break; case ROQ_CODEBOOK: decodeCodeBook( framedata, (unsigned short)cinTable[currentHandle].roq_flags ); break; case ZA_SOUND_MONO: if (!cinTable[currentHandle].silent) { ssize = RllDecodeMonoToStereo( framedata, sbuf, cinTable[currentHandle].RoQFrameSize, 0, (unsigned short)cinTable[currentHandle].roq_flags); S_RawSamples( ssize, 22050, 2, 1, (byte *)sbuf, 1.0f ); } break; case ZA_SOUND_STEREO: if (!cinTable[currentHandle].silent) { if (cinTable[currentHandle].numQuads == -1) { S_Update(); s_rawend = s_soundtime; } ssize = RllDecodeStereoToStereo( framedata, sbuf, cinTable[currentHandle].RoQFrameSize, 0, (unsigned short)cinTable[currentHandle].roq_flags); S_RawSamples( ssize, 22050, 2, 2, (byte *)sbuf, 1.0f ); } break; case ROQ_QUAD_INFO: if (cinTable[currentHandle].numQuads == -1) { readQuadInfo( framedata ); setupQuad( 0, 0 ); // we need to use CL_ScaledMilliseconds because of the smp mode calls from the renderer cinTable[currentHandle].startTime = cinTable[currentHandle].lastTime = CL_ScaledMilliseconds()*com_timescale->value; } if (cinTable[currentHandle].numQuads != 1) cinTable[currentHandle].numQuads = 0; break; case ROQ_PACKET: cinTable[currentHandle].inMemory = (qboolean) cinTable[currentHandle].roq_flags; cinTable[currentHandle].RoQFrameSize = 0; // for header break; case ROQ_QUAD_HANG: cinTable[currentHandle].RoQFrameSize = 0; break; case ROQ_QUAD_JPEG: break; default: cinTable[currentHandle].status = FMV_EOF; break; } // // read in next frame data // if ( cinTable[currentHandle].RoQPlayed >= cinTable[currentHandle].ROQSize ) { if (cinTable[currentHandle].holdAtEnd==qfalse) { if (cinTable[currentHandle].looping) { RoQReset(); } else { cinTable[currentHandle].status = FMV_EOF; } } else { cinTable[currentHandle].status = FMV_IDLE; } return; } framedata += cinTable[currentHandle].RoQFrameSize; cinTable[currentHandle].roq_id = framedata[0] + framedata[1]*256; cinTable[currentHandle].RoQFrameSize = framedata[2] + framedata[3]*256 + framedata[4]*65536; cinTable[currentHandle].roq_flags = framedata[6] + framedata[7]*256; cinTable[currentHandle].roqF0 = (char)framedata[7]; cinTable[currentHandle].roqF1 = (char)framedata[6]; if (cinTable[currentHandle].RoQFrameSize>65536||cinTable[currentHandle].roq_id==0x1084) { Com_DPrintf("roq_size>65536||roq_id==0x1084\n"); cinTable[currentHandle].status = FMV_EOF; if (cinTable[currentHandle].looping) { RoQReset(); } return; } if (cinTable[currentHandle].inMemory && (cinTable[currentHandle].status != FMV_EOF)) { ((int&)cinTable[currentHandle].inMemory)--; framedata += 8; goto redump; } // // one more frame hits the dust // // assert(cinTable[currentHandle].RoQFrameSize <= 65536); // r = Sys_StreamedRead( cin.file, cinTable[currentHandle].RoQFrameSize+8, 1, cinTable[currentHandle].iFile ); cinTable[currentHandle].RoQPlayed += cinTable[currentHandle].RoQFrameSize+8; } /****************************************************************************** * * Function: * * Description: * ******************************************************************************/ static void RoQ_init( void ) { // we need to use CL_ScaledMilliseconds because of the smp mode calls from the renderer cinTable[currentHandle].startTime = cinTable[currentHandle].lastTime = CL_ScaledMilliseconds()*com_timescale->value; cinTable[currentHandle].RoQPlayed = 24; /* get frame rate */ cinTable[currentHandle].roqFPS = cin.file[ 6] + cin.file[ 7]*256; if (!cinTable[currentHandle].roqFPS) cinTable[currentHandle].roqFPS = 30; cinTable[currentHandle].numQuads = -1; cinTable[currentHandle].roq_id = cin.file[ 8] + cin.file[ 9]*256; cinTable[currentHandle].RoQFrameSize = cin.file[10] + cin.file[11]*256 + cin.file[12]*65536; cinTable[currentHandle].roq_flags = cin.file[14] + cin.file[15]*256; if (cinTable[currentHandle].RoQFrameSize > 65536 || !cinTable[currentHandle].RoQFrameSize) { return; } } /****************************************************************************** * * Function: * * Description: * ******************************************************************************/ static void RoQShutdown( void ) { const char *s; if (!cinTable[currentHandle].buf) { return; } if ( cinTable[currentHandle].status == FMV_IDLE ) { return; } Com_DPrintf("finished cinematic\n"); cinTable[currentHandle].status = FMV_IDLE; if (cinTable[currentHandle].iFile) { Sys_EndStreamedFile( cinTable[currentHandle].iFile ); FS_FCloseFile( cinTable[currentHandle].iFile ); cinTable[currentHandle].iFile = 0; } if (cinTable[currentHandle].alterGameState) { cls.state = CA_DISCONNECTED; // we can't just do a vstr nextmap, because // if we are aborting the intro cinematic with // a devmap command, nextmap would be valid by // the time it was referenced s = Cvar_VariableString( "nextmap" ); if ( s[0] ) { Cbuf_ExecuteText( EXEC_APPEND, va("%s\n", s) ); Cvar_Set( "nextmap", "" ); } CL_handle = -1; } cinTable[currentHandle].fileName[0] = 0; currentHandle = -1; } /* ================== SCR_StopCinematic ================== */ e_status CIN_StopCinematic(int handle) { if (handle < 0 || handle>= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF) return FMV_EOF; currentHandle = handle; Com_DPrintf("trFMV::stop(), closing %s\n", cinTable[currentHandle].fileName); if (!cinTable[currentHandle].buf) { return FMV_EOF; } if (cinTable[currentHandle].alterGameState) { if ( cls.state != CA_CINEMATIC ) { return cinTable[currentHandle].status; } } cinTable[currentHandle].status = FMV_EOF; RoQShutdown(); return FMV_EOF; } /* ================== SCR_RunCinematic Fetch and decompress the pending frame ================== */ e_status CIN_RunCinematic (int handle) { // bk001204 - init int start = 0; int thisTime = 0; if (handle < 0 || handle>= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF) return FMV_EOF; if (cin.currentHandle != handle) { currentHandle = handle; cin.currentHandle = currentHandle; cinTable[currentHandle].status = FMV_EOF; RoQReset(); } if (cinTable[handle].playonwalls < -1) { return cinTable[handle].status; } currentHandle = handle; if (cinTable[currentHandle].alterGameState) { if ( cls.state != CA_CINEMATIC ) { return cinTable[currentHandle].status; } } if (cinTable[currentHandle].status == FMV_IDLE) { return cinTable[currentHandle].status; } // we need to use CL_ScaledMilliseconds because of the smp mode calls from the renderer thisTime = CL_ScaledMilliseconds()*com_timescale->value; if (cinTable[currentHandle].shader && (abs((int)(thisTime - cinTable[currentHandle].lastTime)))>100) { cinTable[currentHandle].startTime += thisTime - cinTable[currentHandle].lastTime; } // we need to use CL_ScaledMilliseconds because of the smp mode calls from the renderer cinTable[currentHandle].tfps = ((((CL_ScaledMilliseconds()*com_timescale->value) - cinTable[currentHandle].startTime)*3)/100); start = cinTable[currentHandle].startTime; while( (cinTable[currentHandle].tfps != cinTable[currentHandle].numQuads) && (cinTable[currentHandle].status == FMV_PLAY) ) { RoQInterrupt(); if (start != (int)cinTable[currentHandle].startTime) { // we need to use CL_ScaledMilliseconds because of the smp mode calls from the renderer cinTable[currentHandle].tfps = ((((CL_ScaledMilliseconds()*com_timescale->value) - cinTable[currentHandle].startTime)*3)/100); start = cinTable[currentHandle].startTime; } } cinTable[currentHandle].lastTime = thisTime; if (cinTable[currentHandle].status == FMV_LOOPED) { cinTable[currentHandle].status = FMV_PLAY; } if (cinTable[currentHandle].status == FMV_EOF) { if (cinTable[currentHandle].looping) { RoQReset(); } else { RoQShutdown(); } } return cinTable[currentHandle].status; } /* ================== CL_PlayCinematic ================== */ int CIN_PlayCinematic( const char *arg, int x, int y, int w, int h, int systemBits ) { unsigned short RoQID; char name[MAX_OSPATH]; int i; if (strstr(arg, "/") == NULL && strstr(arg, "\\") == NULL) { Com_sprintf (name, sizeof(name), "video/%s", arg); } else { Com_sprintf (name, sizeof(name), "%s", arg); } if (!(systemBits & CIN_system)) { for ( i = 0 ; i < MAX_VIDEO_HANDLES ; i++ ) { if (!strcmp(cinTable[i].fileName, name) ) { return i; } } } Com_DPrintf("SCR_PlayCinematic( %s )\n", arg); Com_Memset(&cin, 0, sizeof(cinematics_t) ); currentHandle = CIN_HandleForVideo(); cin.currentHandle = currentHandle; strcpy(cinTable[currentHandle].fileName, name); cinTable[currentHandle].ROQSize = 0; cinTable[currentHandle].ROQSize = FS_FOpenFileRead (cinTable[currentHandle].fileName, &cinTable[currentHandle].iFile, qtrue); if (cinTable[currentHandle].ROQSize<=0) { Com_DPrintf("play(%s), ROQSize<=0\n", arg); cinTable[currentHandle].fileName[0] = 0; return -1; } CIN_SetExtents(currentHandle, x, y, w, h); CIN_SetLooping(currentHandle, (qboolean) ((systemBits & CIN_loop)!=0)); cinTable[currentHandle].CIN_HEIGHT = DEFAULT_CIN_HEIGHT; cinTable[currentHandle].CIN_WIDTH = DEFAULT_CIN_WIDTH; cinTable[currentHandle].holdAtEnd = (qboolean) ((systemBits & CIN_hold) != 0); cinTable[currentHandle].alterGameState = (qboolean) ((systemBits & CIN_system) != 0); cinTable[currentHandle].playonwalls = 1; cinTable[currentHandle].silent = (qboolean) ((systemBits & CIN_silent) != 0); cinTable[currentHandle].shader = (qboolean) ((systemBits & CIN_shader) != 0); if (cinTable[currentHandle].alterGameState) { // close the menu if ( uivm ) { VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_NONE ); } } else { cinTable[currentHandle].playonwalls = cl_inGameVideo->integer; } initRoQ(); FS_Read (cin.file, 16, cinTable[currentHandle].iFile); RoQID = (unsigned short)(cin.file[0]) + (unsigned short)(cin.file[1])*256; if (RoQID == 0x1084) { RoQ_init(); // FS_Read (cin.file, cinTable[currentHandle].RoQFrameSize+8, cinTable[currentHandle].iFile); // let the background thread start reading ahead Sys_BeginStreamedFile( cinTable[currentHandle].iFile, 0x10000 ); cinTable[currentHandle].status = FMV_PLAY; Com_DPrintf("trFMV::play(), playing %s\n", arg); if (cinTable[currentHandle].alterGameState) { cls.state = CA_CINEMATIC; } Con_Close(); s_rawend = s_soundtime; return currentHandle; } Com_DPrintf("trFMV::play(), invalid RoQ ID\n"); RoQShutdown(); return -1; } void CIN_SetExtents (int handle, int x, int y, int w, int h) { if (handle < 0 || handle>= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF) return; cinTable[handle].xpos = x; cinTable[handle].ypos = y; cinTable[handle].width = w; cinTable[handle].height = h; cinTable[handle].dirty = qtrue; } void CIN_SetLooping(int handle, qboolean loop) { if (handle < 0 || handle>= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF) return; cinTable[handle].looping = loop; } /* ================== SCR_DrawCinematic ================== */ void CIN_DrawCinematic (int handle) { float x, y, w, h; byte *buf; if (handle < 0 || handle>= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF) return; if (!cinTable[handle].buf) { return; } x = cinTable[handle].xpos; y = cinTable[handle].ypos; w = cinTable[handle].width; h = cinTable[handle].height; buf = cinTable[handle].buf; SCR_AdjustFrom640( &x, &y, &w, &h ); if (cinTable[handle].dirty && (cinTable[handle].CIN_WIDTH != cinTable[handle].drawX || cinTable[handle].CIN_HEIGHT != cinTable[handle].drawY)) { int ix, iy, *buf2, *buf3, xm, ym, ll; xm = cinTable[handle].CIN_WIDTH/256; ym = cinTable[handle].CIN_HEIGHT/256; ll = 8; if (cinTable[handle].CIN_WIDTH==512) { ll = 9; } buf3 = (int*)buf; buf2 = (int*) Hunk_AllocateTempMemory( 256*256*4 ); if (xm==2 && ym==2) { byte *bc2, *bc3; int ic, iiy; bc2 = (byte *)buf2; bc3 = (byte *)buf3; for (iy = 0; iy<256; iy++) { iiy = iy<<12; for (ix = 0; ix<2048; ix+=8) { for(ic = ix;ic<(ix+4);ic++) { *bc2=(bc3[iiy+ic]+bc3[iiy+4+ic]+bc3[iiy+2048+ic]+bc3[iiy+2048+4+ic])>>2; bc2++; } } } } else if (xm==2 && ym==1) { byte *bc2, *bc3; int ic, iiy; bc2 = (byte *)buf2; bc3 = (byte *)buf3; for (iy = 0; iy<256; iy++) { iiy = iy<<11; for (ix = 0; ix<2048; ix+=8) { for(ic = ix;ic<(ix+4);ic++) { *bc2=(bc3[iiy+ic]+bc3[iiy+4+ic])>>1; bc2++; } } } } else { for (iy = 0; iy<256; iy++) { for (ix = 0; ix<256; ix++) { buf2[(iy<<8)+ix] = buf3[((iy*ym)<= 0) { do { SCR_RunCinematic(); } while (cinTable[currentHandle].buf == NULL && cinTable[currentHandle].status == FMV_PLAY); // wait for first frame (load codebook and sound) } } void SCR_DrawCinematic (void) { if (CL_handle >= 0 && CL_handle < MAX_VIDEO_HANDLES) { CIN_DrawCinematic(CL_handle); } } void SCR_RunCinematic (void) { if (CL_handle >= 0 && CL_handle < MAX_VIDEO_HANDLES) { CIN_RunCinematic(CL_handle); } } void SCR_StopCinematic(void) { if (CL_handle >= 0 && CL_handle < MAX_VIDEO_HANDLES) { CIN_StopCinematic(CL_handle); S_StopAllSounds (); CL_handle = -1; } } void CIN_UploadCinematic(int handle) { if (handle >= 0 && handle < MAX_VIDEO_HANDLES) { if (!cinTable[handle].buf) { return; } if (cinTable[handle].playonwalls <= 0 && cinTable[handle].dirty) { if (cinTable[handle].playonwalls == 0) { cinTable[handle].playonwalls = -1; } else { if (cinTable[handle].playonwalls == -1) { cinTable[handle].playonwalls = -2; } else { cinTable[handle].dirty = qfalse; } } } re.UploadCinematic( 256, 256, 256, 256, cinTable[handle].buf, handle, cinTable[handle].dirty); if (cl_inGameVideo->integer == 0 && cinTable[handle].playonwalls == 1) { cinTable[handle].playonwalls--; } } } ================================================ FILE: src/engine/client/cl_console.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // console.c #include "client.h" int g_console_field_width = 78; #define NUM_CON_TIMES 4 #define CON_TEXTSIZE 32768 typedef struct { qboolean initialized; short text[CON_TEXTSIZE]; int current; // line where next message will be printed int x; // offset in current line for next print int display; // bottom of console displays this line int linewidth; // characters across screen int totallines; // total lines in console scrollback float xadjust; // for wide aspect screens float displayFrac; // aproaches finalFrac at scr_conspeed float finalFrac; // 0.0 to 1.0 lines of console to display int vislines; // in scanlines int times[NUM_CON_TIMES]; // cls.realtime time the line was generated // for transparent notify lines vec4_t color; } console_t; extern console_t con; console_t con; cvar_t *con_conspeed; cvar_t *con_notifytime; #define DEFAULT_CONSOLE_WIDTH 78 vec4_t console_color = {1.0, 1.0, 1.0, 1.0}; /* ================ Con_ToggleConsole_f ================ */ void Con_ToggleConsole_f (void) { // closing a full screen console restarts the demo loop if ( cls.state == CA_DISCONNECTED && cls.keyCatchers == KEYCATCH_CONSOLE ) { CL_StartDemoLoop(); return; } Field_Clear( &g_consoleField ); g_consoleField.widthInChars = g_console_field_width; Con_ClearNotify (); cls.keyCatchers ^= KEYCATCH_CONSOLE; } /* ================ Con_MessageMode_f ================ */ void Con_MessageMode_f (void) { chat_playerNum = -1; chat_team = qfalse; Field_Clear( &chatField ); chatField.widthInChars = 30; cls.keyCatchers ^= KEYCATCH_MESSAGE; } /* ================ Con_MessageMode2_f ================ */ void Con_MessageMode2_f (void) { chat_playerNum = -1; chat_team = qtrue; Field_Clear( &chatField ); chatField.widthInChars = 25; cls.keyCatchers ^= KEYCATCH_MESSAGE; } /* ================ Con_MessageMode3_f ================ */ void Con_MessageMode3_f (void) { chat_playerNum = VM_Call( cgvm, CG_CROSSHAIR_PLAYER ); if ( chat_playerNum < 0 || chat_playerNum >= MAX_CLIENTS ) { chat_playerNum = -1; return; } chat_team = qfalse; Field_Clear( &chatField ); chatField.widthInChars = 30; cls.keyCatchers ^= KEYCATCH_MESSAGE; } /* ================ Con_MessageMode4_f ================ */ void Con_MessageMode4_f (void) { chat_playerNum = VM_Call( cgvm, CG_LAST_ATTACKER ); if ( chat_playerNum < 0 || chat_playerNum >= MAX_CLIENTS ) { chat_playerNum = -1; return; } chat_team = qfalse; Field_Clear( &chatField ); chatField.widthInChars = 30; cls.keyCatchers ^= KEYCATCH_MESSAGE; } /* ================ Con_Clear_f ================ */ void Con_Clear_f (void) { int i; for ( i = 0 ; i < CON_TEXTSIZE ; i++ ) { con.text[i] = (ColorIndex(COLOR_WHITE)<<8) | ' '; } Con_Bottom(); // go to end } /* ================ Con_Dump_f Save the console contents out to a file ================ */ void Con_Dump_f (void) { int l, x, i; short *line; fileHandle_t f; char buffer[1024]; if (Cmd_Argc() != 2) { Com_Printf ("usage: condump \n"); return; } Com_Printf ("Dumped console text to %s.\n", Cmd_Argv(1) ); f = FS_FOpenFileWrite( Cmd_Argv( 1 ) ); if (!f) { Com_Printf ("ERROR: couldn't open.\n"); return; } // skip empty lines for (l = con.current - con.totallines + 1 ; l <= con.current ; l++) { line = con.text + (l%con.totallines)*con.linewidth; for (x=0 ; x=0 ; x--) { if (buffer[x] == ' ') buffer[x] = 0; else break; } strcat( buffer, "\n" ); FS_Write(buffer, (int)strlen(buffer), f); } FS_FCloseFile( f ); } /* ================ Con_ClearNotify ================ */ void Con_ClearNotify( void ) { int i; for ( i = 0 ; i < NUM_CON_TIMES ; i++ ) { con.times[i] = 0; } } /* ================ Con_CheckResize If the line width has changed, reformat the buffer. ================ */ void Con_CheckResize (void) { int i, j, width, oldwidth, oldtotallines, numlines, numchars; MAC_STATIC short tbuf[CON_TEXTSIZE]; width = (SCREEN_WIDTH / SMALLCHAR_WIDTH) - 2; if (width == con.linewidth) return; if (width < 1) // video hasn't been initialized yet { width = DEFAULT_CONSOLE_WIDTH; con.linewidth = width; con.totallines = CON_TEXTSIZE / con.linewidth; for(i=0; i= 0) { if (skipnotify) con.times[con.current % NUM_CON_TIMES] = 0; else con.times[con.current % NUM_CON_TIMES] = cls.realtime; } con.x = 0; if (con.display == con.current) con.display++; con.current++; for(i=0; iinteger ) { return; } if (!con.initialized) { con.color[0] = con.color[1] = con.color[2] = con.color[3] = 1.0f; con.linewidth = -1; Con_CheckResize (); con.initialized = qtrue; } color = ColorIndex(COLOR_WHITE); while ( (c = *txt) != 0 ) { if ( Q_IsColorString( txt ) ) { color = ColorIndex( *(txt+1) ); txt += 2; continue; } // count word length for (l=0 ; l< con.linewidth ; l++) { if ( txt[l] <= ' ') { break; } } // word wrap if (l != con.linewidth && (con.x + l >= con.linewidth) ) { Con_Linefeed(skipnotify); } txt++; switch (c) { case '\n': Con_Linefeed (skipnotify); break; case '\r': con.x = 0; break; default: // display character and advance y = con.current % con.totallines; con.text[y*con.linewidth+con.x] = (color << 8) | c; con.x++; if (con.x >= con.linewidth) { Con_Linefeed(skipnotify); con.x = 0; } break; } } // mark time for transparent overlay if (con.current >= 0) { // NERVE - SMF if ( skipnotify ) { prev = con.current % NUM_CON_TIMES - 1; if ( prev < 0 ) prev = NUM_CON_TIMES - 1; con.times[prev] = 0; } else // -NERVE - SMF con.times[con.current % NUM_CON_TIMES] = cls.realtime; } } /* ============================================================================== DRAWING ============================================================================== */ /* ================ Con_DrawInput Draw the editline after a ] prompt ================ */ void Con_DrawInput (void) { int y; if ( cls.state != CA_DISCONNECTED && !(cls.keyCatchers & KEYCATCH_CONSOLE ) ) { return; } y = con.vislines - ( SMALLCHAR_HEIGHT * 2 ); re.SetColor( con.color ); SCR_DrawSmallChar( con.xadjust + 1 * SMALLCHAR_WIDTH, y, ']' ); Field_Draw( &g_consoleField, con.xadjust + 2 * SMALLCHAR_WIDTH, y, SCREEN_WIDTH - 3 * SMALLCHAR_WIDTH, qtrue ); } /* ================ Con_DrawNotify Draws the last few lines of output transparently over the game top ================ */ void Con_DrawNotify (void) { int x, v; short *text; int i; int time; int skip; int currentColor; currentColor = 7; re.SetColor( g_color_table[currentColor] ); v = 0; for (i= con.current-NUM_CON_TIMES+1 ; i<=con.current ; i++) { if (i < 0) continue; time = con.times[i % NUM_CON_TIMES]; if (time == 0) continue; time = cls.realtime - time; if (time > con_notifytime->value*1000) continue; text = con.text + (i % con.totallines)*con.linewidth; if (cl.snap.ps.pm_type != PM_INTERMISSION && cls.keyCatchers & (KEYCATCH_UI | KEYCATCH_CGAME) ) { continue; } for (x = 0 ; x < con.linewidth ; x++) { if ( ( text[x] & 0xff ) == ' ' ) { continue; } if ( ( (text[x]>>8)&7 ) != currentColor ) { currentColor = (text[x]>>8)&7; re.SetColor( g_color_table[currentColor] ); } SCR_DrawSmallChar( cl_conXOffset->integer + con.xadjust + (x+1)*SMALLCHAR_WIDTH, v, text[x] & 0xff ); } v += SMALLCHAR_HEIGHT; } re.SetColor( NULL ); if (cls.keyCatchers & (KEYCATCH_UI | KEYCATCH_CGAME) ) { return; } // draw the chat line if ( cls.keyCatchers & KEYCATCH_MESSAGE ) { if (chat_team) { SCR_DrawBigString (8, v, "say_team:", 1.0f ); skip = 11; } else { SCR_DrawBigString (8, v, "say:", 1.0f ); skip = 5; } Field_BigDraw( &chatField, skip * BIGCHAR_WIDTH, v, SCREEN_WIDTH - ( skip + 1 ) * BIGCHAR_WIDTH, qtrue ); v += BIGCHAR_HEIGHT; } } /* ================ Con_DrawSolidConsole Draws the console with the solid background ================ */ void Con_DrawSolidConsole( float frac ) { int i, x, y; int rows; short *text; int row; int lines; // qhandle_t conShader; int currentColor; vec4_t color; lines = cls.glconfig.vidHeight * frac; if (lines <= 0) return; if (lines > cls.glconfig.vidHeight ) lines = cls.glconfig.vidHeight; // on wide screens, we will center the text con.xadjust = 0; SCR_AdjustFrom640( &con.xadjust, NULL, NULL, NULL ); // draw the background y = frac * SCREEN_HEIGHT - 2; if ( y < 1 ) { y = 0; } else { SCR_DrawPic( 0, 0, SCREEN_WIDTH, y, cls.consoleShader ); } color[0] = 1; color[1] = 0; color[2] = 0; color[3] = 1; SCR_FillRect( 0, y, SCREEN_WIDTH, 2, color ); // draw the version number re.SetColor( g_color_table[ColorIndex(COLOR_RED)] ); i = (int)strlen( Q3_VERSION ); for (x=0 ; x= con.totallines) { // past scrollback wrap point continue; } text = con.text + (row % con.totallines)*con.linewidth; for (x=0 ; x>8)&7 ) != currentColor ) { currentColor = (text[x]>>8)&7; re.SetColor( g_color_table[currentColor] ); } SCR_DrawSmallChar( con.xadjust + (x+1)*SMALLCHAR_WIDTH, y, text[x] & 0xff ); } } // draw the input prompt, user text, and cursor if desired Con_DrawInput (); re.SetColor( NULL ); } /* ================== Con_DrawConsole ================== */ void Con_DrawConsole( void ) { // check for console width changes from a vid mode change Con_CheckResize (); // if disconnected, render console full screen if ( cls.state == CA_DISCONNECTED ) { if ( !( cls.keyCatchers & (KEYCATCH_UI | KEYCATCH_CGAME)) ) { Con_DrawSolidConsole( 1.0 ); return; } } if ( con.displayFrac ) { Con_DrawSolidConsole( con.displayFrac ); } else { // draw notify lines if ( cls.state == CA_ACTIVE ) { Con_DrawNotify (); } } } //================================================================ /* ================== Con_RunConsole Scroll it up or down ================== */ void Con_RunConsole (void) { // decide on the destination height of the console if ( cls.keyCatchers & KEYCATCH_CONSOLE ) con.finalFrac = 0.5; // half screen else con.finalFrac = 0; // none visible // scroll towards the destination height if (con.finalFrac < con.displayFrac) { con.displayFrac -= con_conspeed->value*cls.realFrametime*0.001; if (con.finalFrac > con.displayFrac) con.displayFrac = con.finalFrac; } else if (con.finalFrac > con.displayFrac) { con.displayFrac += con_conspeed->value*cls.realFrametime*0.001; if (con.finalFrac < con.displayFrac) con.displayFrac = con.finalFrac; } } void Con_PageUp( void ) { con.display -= 2; if ( con.current - con.display >= con.totallines ) { con.display = con.current - con.totallines + 1; } } void Con_PageDown( void ) { con.display += 2; if (con.display > con.current) { con.display = con.current; } } void Con_Top( void ) { con.display = con.totallines; if ( con.current - con.display >= con.totallines ) { con.display = con.current - con.totallines + 1; } } void Con_Bottom( void ) { con.display = con.current; } void Con_Close( void ) { if ( !com_cl_running->integer ) { return; } Field_Clear( &g_consoleField ); Con_ClearNotify (); cls.keyCatchers &= ~KEYCATCH_CONSOLE; con.finalFrac = 0; // none visible con.displayFrac = 0; } ================================================ FILE: src/engine/client/cl_input.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // cl.input.c -- builds an intended movement command to send to the server #include "client.h" unsigned frame_msec; int old_com_frameTime; /* =============================================================================== KEY BUTTONS Continuous button event tracking is complicated by the fact that two different input sources (say, mouse button 1 and the control key) can both press the same button, but the button should only be released when both of the pressing key have been released. When a key event issues a button command (+forward, +attack, etc), it appends its key number as argv(1) so it can be matched up with the release. argv(2) will be set to the time the event happened, which allows exact control even at low framerates when the down and up events may both get qued at the same time. =============================================================================== */ kbutton_t in_left, in_right, in_forward, in_back; kbutton_t in_lookup, in_lookdown, in_moveleft, in_moveright; kbutton_t in_strafe, in_speed; kbutton_t in_up, in_down; kbutton_t in_buttons[16]; qboolean in_mlooking; void IN_MLookDown( void ) { in_mlooking = qtrue; } void IN_MLookUp( void ) { in_mlooking = qfalse; if ( !cl_freelook->integer ) { IN_CenterView (); } } void IN_KeyDown( kbutton_t *b ) { int k; char *c; c = Cmd_Argv(1); if ( c[0] ) { k = atoi(c); } else { k = -1; // typed manually at the console for continuous down } if ( k == b->down[0] || k == b->down[1] ) { return; // repeating key } if ( !b->down[0] ) { b->down[0] = k; } else if ( !b->down[1] ) { b->down[1] = k; } else { Com_Printf ("Three keys down for a button!\n"); return; } if ( b->active ) { return; // still down } // save timestamp for partial frame summing c = Cmd_Argv(2); b->downtime = atoi(c); b->active = qtrue; b->wasPressed = qtrue; } void IN_KeyUp( kbutton_t *b ) { int k; char *c; unsigned uptime; c = Cmd_Argv(1); if ( c[0] ) { k = atoi(c); } else { // typed manually at the console, assume for unsticking, so clear all b->down[0] = b->down[1] = 0; b->active = qfalse; return; } if ( b->down[0] == k ) { b->down[0] = 0; } else if ( b->down[1] == k ) { b->down[1] = 0; } else { return; // key up without coresponding down (menu pass through) } if ( b->down[0] || b->down[1] ) { return; // some other key is still holding it down } b->active = qfalse; // save timestamp for partial frame summing c = Cmd_Argv(2); uptime = atoi(c); if ( uptime ) { b->msec += uptime - b->downtime; } else { b->msec += frame_msec / 2; } b->active = qfalse; } /* =============== CL_KeyState Returns the fraction of the frame that the key was down =============== */ float CL_KeyState( kbutton_t *key ) { float val; int msec; msec = key->msec; key->msec = 0; if ( key->active ) { // still down if ( !key->downtime ) { msec = com_frameTime; } else { msec += com_frameTime - key->downtime; } key->downtime = com_frameTime; } #if 0 if (msec) { Com_Printf ("%i ", msec); } #endif val = (float)msec / frame_msec; if ( val < 0 ) { val = 0; } if ( val > 1 ) { val = 1; } return val; } void IN_UpDown(void) {IN_KeyDown(&in_up);} void IN_UpUp(void) {IN_KeyUp(&in_up);} void IN_DownDown(void) {IN_KeyDown(&in_down);} void IN_DownUp(void) {IN_KeyUp(&in_down);} void IN_LeftDown(void) {IN_KeyDown(&in_left);} void IN_LeftUp(void) {IN_KeyUp(&in_left);} void IN_RightDown(void) {IN_KeyDown(&in_right);} void IN_RightUp(void) {IN_KeyUp(&in_right);} void IN_ForwardDown(void) {IN_KeyDown(&in_forward);} void IN_ForwardUp(void) {IN_KeyUp(&in_forward);} void IN_BackDown(void) {IN_KeyDown(&in_back);} void IN_BackUp(void) {IN_KeyUp(&in_back);} void IN_LookupDown(void) {IN_KeyDown(&in_lookup);} void IN_LookupUp(void) {IN_KeyUp(&in_lookup);} void IN_LookdownDown(void) {IN_KeyDown(&in_lookdown);} void IN_LookdownUp(void) {IN_KeyUp(&in_lookdown);} void IN_MoveleftDown(void) {IN_KeyDown(&in_moveleft);} void IN_MoveleftUp(void) {IN_KeyUp(&in_moveleft);} void IN_MoverightDown(void) {IN_KeyDown(&in_moveright);} void IN_MoverightUp(void) {IN_KeyUp(&in_moveright);} void IN_SpeedDown(void) {IN_KeyDown(&in_speed);} void IN_SpeedUp(void) {IN_KeyUp(&in_speed);} void IN_StrafeDown(void) {IN_KeyDown(&in_strafe);} void IN_StrafeUp(void) {IN_KeyUp(&in_strafe);} void IN_Button0Down(void) {IN_KeyDown(&in_buttons[0]);} void IN_Button0Up(void) {IN_KeyUp(&in_buttons[0]);} void IN_Button1Down(void) {IN_KeyDown(&in_buttons[1]);} void IN_Button1Up(void) {IN_KeyUp(&in_buttons[1]);} void IN_Button2Down(void) {IN_KeyDown(&in_buttons[2]);} void IN_Button2Up(void) {IN_KeyUp(&in_buttons[2]);} void IN_Button3Down(void) {IN_KeyDown(&in_buttons[3]);} void IN_Button3Up(void) {IN_KeyUp(&in_buttons[3]);} void IN_Button4Down(void) {IN_KeyDown(&in_buttons[4]);} void IN_Button4Up(void) {IN_KeyUp(&in_buttons[4]);} void IN_Button5Down(void) {IN_KeyDown(&in_buttons[5]);} void IN_Button5Up(void) {IN_KeyUp(&in_buttons[5]);} void IN_Button6Down(void) {IN_KeyDown(&in_buttons[6]);} void IN_Button6Up(void) {IN_KeyUp(&in_buttons[6]);} void IN_Button7Down(void) {IN_KeyDown(&in_buttons[7]);} void IN_Button7Up(void) {IN_KeyUp(&in_buttons[7]);} void IN_Button8Down(void) {IN_KeyDown(&in_buttons[8]);} void IN_Button8Up(void) {IN_KeyUp(&in_buttons[8]);} void IN_Button9Down(void) {IN_KeyDown(&in_buttons[9]);} void IN_Button9Up(void) {IN_KeyUp(&in_buttons[9]);} void IN_Button10Down(void) {IN_KeyDown(&in_buttons[10]);} void IN_Button10Up(void) {IN_KeyUp(&in_buttons[10]);} void IN_Button11Down(void) {IN_KeyDown(&in_buttons[11]);} void IN_Button11Up(void) {IN_KeyUp(&in_buttons[11]);} void IN_Button12Down(void) {IN_KeyDown(&in_buttons[12]);} void IN_Button12Up(void) {IN_KeyUp(&in_buttons[12]);} void IN_Button13Down(void) {IN_KeyDown(&in_buttons[13]);} void IN_Button13Up(void) {IN_KeyUp(&in_buttons[13]);} void IN_Button14Down(void) {IN_KeyDown(&in_buttons[14]);} void IN_Button14Up(void) {IN_KeyUp(&in_buttons[14]);} void IN_Button15Down(void) {IN_KeyDown(&in_buttons[15]);} void IN_Button15Up(void) {IN_KeyUp(&in_buttons[15]);} void IN_ButtonDown (void) { IN_KeyDown(&in_buttons[1]);} void IN_ButtonUp (void) { IN_KeyUp(&in_buttons[1]);} void IN_CenterView (void) { cl.viewangles[PITCH] = -SHORT2ANGLE(cl.snap.ps.delta_angles[PITCH]); } //========================================================================== cvar_t *cl_upspeed; cvar_t *cl_forwardspeed; cvar_t *cl_sidespeed; cvar_t *cl_yawspeed; cvar_t *cl_pitchspeed; cvar_t *cl_run; cvar_t *cl_anglespeedkey; /* ================ CL_AdjustAngles Moves the local angle positions ================ */ void CL_AdjustAngles( void ) { float speed; if ( in_speed.active ) { speed = 0.001 * cls.frametime * cl_anglespeedkey->value; } else { speed = 0.001 * cls.frametime; } if ( !in_strafe.active ) { cl.viewangles[YAW] -= speed*cl_yawspeed->value*CL_KeyState (&in_right); cl.viewangles[YAW] += speed*cl_yawspeed->value*CL_KeyState (&in_left); } cl.viewangles[PITCH] -= speed*cl_pitchspeed->value * CL_KeyState (&in_lookup); cl.viewangles[PITCH] += speed*cl_pitchspeed->value * CL_KeyState (&in_lookdown); } /* ================ CL_KeyMove Sets the usercmd_t based on key states ================ */ void CL_KeyMove( usercmd_t *cmd ) { int movespeed; int forward, side, up; // // adjust for speed key / running // the walking flag is to keep animations consistant // even during acceleration and develeration // if ( in_speed.active ^ cl_run->integer ) { movespeed = 127; cmd->buttons &= ~BUTTON_WALKING; } else { cmd->buttons |= BUTTON_WALKING; movespeed = 64; } forward = 0; side = 0; up = 0; if ( in_strafe.active ) { side += movespeed * CL_KeyState (&in_right); side -= movespeed * CL_KeyState (&in_left); } side += movespeed * CL_KeyState (&in_moveright); side -= movespeed * CL_KeyState (&in_moveleft); up += movespeed * CL_KeyState (&in_up); up -= movespeed * CL_KeyState (&in_down); forward += movespeed * CL_KeyState (&in_forward); forward -= movespeed * CL_KeyState (&in_back); cmd->forwardmove = ClampChar( forward ); cmd->rightmove = ClampChar( side ); cmd->upmove = ClampChar( up ); } /* ================= CL_MouseEvent ================= */ void CL_MouseEvent( int dx, int dy, int time ) { if ( cls.keyCatchers & KEYCATCH_UI ) { VM_Call( uivm, UI_MOUSE_EVENT, dx, dy ); } else if (cls.keyCatchers & KEYCATCH_CGAME) { VM_Call (cgvm, CG_MOUSE_EVENT, dx, dy); } else { cl.mouseDx[cl.mouseIndex] += dx; cl.mouseDy[cl.mouseIndex] += dy; } } /* ================= CL_JoystickEvent Joystick values stay set until changed ================= */ void CL_JoystickEvent( int axis, int value, int time ) { if ( axis < 0 || axis >= MAX_JOYSTICK_AXIS ) { Com_Error( ERR_DROP, "CL_JoystickEvent: bad axis %i", axis ); } cl.joystickAxis[axis] = value; } /* ================= CL_JoystickMove ================= */ void CL_JoystickMove( usercmd_t *cmd ) { int movespeed; float anglespeed; if ( in_speed.active ^ cl_run->integer ) { movespeed = 2; } else { movespeed = 1; cmd->buttons |= BUTTON_WALKING; } if ( in_speed.active ) { anglespeed = 0.001 * cls.frametime * cl_anglespeedkey->value; } else { anglespeed = 0.001 * cls.frametime; } if ( !in_strafe.active ) { cl.viewangles[YAW] += anglespeed * cl_yawspeed->value * cl.joystickAxis[AXIS_SIDE]; } else { cmd->rightmove = ClampChar( cmd->rightmove + cl.joystickAxis[AXIS_SIDE] ); } if ( in_mlooking ) { cl.viewangles[PITCH] += anglespeed * cl_pitchspeed->value * cl.joystickAxis[AXIS_FORWARD]; } else { cmd->forwardmove = ClampChar( cmd->forwardmove + cl.joystickAxis[AXIS_FORWARD] ); } cmd->upmove = ClampChar( cmd->upmove + cl.joystickAxis[AXIS_UP] ); } /* ================= CL_MouseMove ================= */ void CL_MouseMove( usercmd_t *cmd ) { float mx, my; float accelSensitivity; float rate; // allow mouse smoothing if ( m_filter->integer ) { mx = ( cl.mouseDx[0] + cl.mouseDx[1] ) * 0.5; my = ( cl.mouseDy[0] + cl.mouseDy[1] ) * 0.5; } else { mx = cl.mouseDx[cl.mouseIndex]; my = cl.mouseDy[cl.mouseIndex]; } cl.mouseIndex ^= 1; cl.mouseDx[cl.mouseIndex] = 0; cl.mouseDy[cl.mouseIndex] = 0; rate = sqrt( mx * mx + my * my ) / (float)frame_msec; accelSensitivity = cl_sensitivity->value + rate * cl_mouseAccel->value; // scale by FOV accelSensitivity *= cl.cgameSensitivity; if ( rate && cl_showMouseRate->integer ) { Com_Printf( "%f : %f\n", rate, accelSensitivity ); } mx *= accelSensitivity; my *= accelSensitivity; if (!mx && !my) { return; } // add mouse X/Y movement to cmd if ( in_strafe.active ) { cmd->rightmove = ClampChar( cmd->rightmove + m_side->value * mx ); } else { cl.viewangles[YAW] -= m_yaw->value * mx; } if ( (in_mlooking || cl_freelook->integer) && !in_strafe.active ) { cl.viewangles[PITCH] += m_pitch->value * my; } else { cmd->forwardmove = ClampChar( cmd->forwardmove - m_forward->value * my ); } } /* ============== CL_CmdButtons ============== */ void CL_CmdButtons( usercmd_t *cmd ) { int i; // // figure button bits // send a button bit even if the key was pressed and released in // less than a frame // for (i = 0 ; i < 15 ; i++) { if ( in_buttons[i].active || in_buttons[i].wasPressed ) { cmd->buttons |= 1 << i; } in_buttons[i].wasPressed = qfalse; } if ( cls.keyCatchers ) { cmd->buttons |= BUTTON_TALK; } // allow the game to know if any key at all is // currently pressed, even if it isn't bound to anything if ( anykeydown && !cls.keyCatchers ) { cmd->buttons |= BUTTON_ANY; } } /* ============== CL_FinishMove ============== */ void CL_FinishMove( usercmd_t *cmd ) { int i; // copy the state that the cgame is currently sending cmd->weapon = cl.cgameUserCmdValue; // send the current server time so the amount of movement // can be determined without allowing cheating cmd->serverTime = cl.serverTime; for (i=0 ; i<3 ; i++) { cmd->angles[i] = ANGLE2SHORT(cl.viewangles[i]); } } /* ================= CL_CreateCmd ================= */ usercmd_t CL_CreateCmd( void ) { usercmd_t cmd; vec3_t oldAngles; VectorCopy( cl.viewangles, oldAngles ); // keyboard angle adjustment CL_AdjustAngles (); Com_Memset( &cmd, 0, sizeof( cmd ) ); CL_CmdButtons( &cmd ); // get basic movement from keyboard CL_KeyMove( &cmd ); // get basic movement from mouse CL_MouseMove( &cmd ); // get basic movement from joystick CL_JoystickMove( &cmd ); // check to make sure the angles haven't wrapped if ( cl.viewangles[PITCH] - oldAngles[PITCH] > 90 ) { cl.viewangles[PITCH] = oldAngles[PITCH] + 90; } else if ( oldAngles[PITCH] - cl.viewangles[PITCH] > 90 ) { cl.viewangles[PITCH] = oldAngles[PITCH] - 90; } // store out the final values CL_FinishMove( &cmd ); // draw debug graphs of turning for mouse testing if ( cl_debugMove->integer ) { if ( cl_debugMove->integer == 1 ) { SCR_DebugGraph( fabs(cl.viewangles[YAW] - oldAngles[YAW]), 0 ); } if ( cl_debugMove->integer == 2 ) { SCR_DebugGraph( fabs(cl.viewangles[PITCH] - oldAngles[PITCH]), 0 ); } } return cmd; } /* ================= CL_CreateNewCommands Create a new usercmd_t structure for this frame ================= */ void CL_CreateNewCommands( void ) { usercmd_t *cmd; int cmdNum; // no need to create usercmds until we have a gamestate if ( cls.state < CA_PRIMED ) { return; } frame_msec = com_frameTime - old_com_frameTime; // if running less than 5fps, truncate the extra time to prevent // unexpected moves after a hitch if ( frame_msec > 200 ) { frame_msec = 200; } old_com_frameTime = com_frameTime; // generate a command for this frame cl.cmdNumber++; cmdNum = cl.cmdNumber & CMD_MASK; cl.cmds[cmdNum] = CL_CreateCmd (); cmd = &cl.cmds[cmdNum]; } /* ================= CL_ReadyToSendPacket Returns qfalse if we are over the maxpackets limit and should choke back the bandwidth a bit by not sending a packet this frame. All the commands will still get delivered in the next packet, but saving a header and getting more delta compression will reduce total bandwidth. ================= */ qboolean CL_ReadyToSendPacket( void ) { int oldPacketNum; int delta; // don't send anything if playing back a demo if ( clc.demoplaying || cls.state == CA_CINEMATIC ) { return qfalse; } // If we are downloading, we send no less than 50ms between packets if ( *clc.downloadTempName && cls.realtime - clc.lastPacketSentTime < 50 ) { return qfalse; } // if we don't have a valid gamestate yet, only send // one packet a second if ( cls.state != CA_ACTIVE && cls.state != CA_PRIMED && !*clc.downloadTempName && cls.realtime - clc.lastPacketSentTime < 1000 ) { return qfalse; } // send every frame for loopbacks if ( clc.netchan.remoteAddress.type == NA_LOOPBACK ) { return qtrue; } // send every frame for LAN if ( Sys_IsLANAddress( clc.netchan.remoteAddress ) ) { return qtrue; } // check for exceeding cl_maxpackets if ( cl_maxpackets->integer < 15 ) { Cvar_Set( "cl_maxpackets", "15" ); } else if ( cl_maxpackets->integer > 125 ) { Cvar_Set( "cl_maxpackets", "125" ); } oldPacketNum = (clc.netchan.outgoingSequence - 1) & PACKET_MASK; delta = cls.realtime - cl.outPackets[ oldPacketNum ].p_realtime; if ( delta < 1000 / cl_maxpackets->integer ) { // the accumulated commands will go out in the next packet return qfalse; } return qtrue; } /* =================== CL_WritePacket Create and send the command packet to the server Including both the reliable commands and the usercmds During normal gameplay, a client packet will contain something like: 4 sequence number 2 qport 4 serverid 4 acknowledged sequence number 4 clc.serverCommandSequence 1 clc_move or clc_moveNoDelta 1 command count =================== */ void CL_WritePacket( void ) { msg_t buf; byte data[MAX_MSGLEN]; int i, j; usercmd_t *cmd, *oldcmd; usercmd_t nullcmd; int packetNum; int oldPacketNum; int count, key; // don't send anything if playing back a demo if ( clc.demoplaying || cls.state == CA_CINEMATIC ) { return; } Com_Memset( &nullcmd, 0, sizeof(nullcmd) ); oldcmd = &nullcmd; MSG_Init( &buf, data, sizeof(data) ); MSG_Bitstream( &buf ); // write the current serverId so the server // can tell if this is from the current gameState MSG_WriteLong( &buf, cl.serverId ); // write the last message we received, which can // be used for delta compression, and is also used // to tell if we dropped a gamestate MSG_WriteLong( &buf, clc.serverMessageSequence ); // write the last reliable message we received MSG_WriteLong( &buf, clc.serverCommandSequence ); // write any unacknowledged clientCommands for ( i = clc.reliableAcknowledge + 1 ; i <= clc.reliableSequence ; i++ ) { MSG_WriteByte( &buf, clc_clientCommand ); MSG_WriteLong( &buf, i ); MSG_WriteString( &buf, clc.reliableCommands[ i & (MAX_RELIABLE_COMMANDS-1) ] ); } // we want to send all the usercmds that were generated in the last // few packet, so even if a couple packets are dropped in a row, // all the cmds will make it to the server if ( cl_packetdup->integer < 0 ) { Cvar_Set( "cl_packetdup", "0" ); } else if ( cl_packetdup->integer > 5 ) { Cvar_Set( "cl_packetdup", "5" ); } oldPacketNum = (clc.netchan.outgoingSequence - 1 - cl_packetdup->integer) & PACKET_MASK; count = cl.cmdNumber - cl.outPackets[ oldPacketNum ].p_cmdNumber; if ( count > MAX_PACKET_USERCMDS ) { count = MAX_PACKET_USERCMDS; Com_Printf("MAX_PACKET_USERCMDS\n"); } if ( count >= 1 ) { if ( cl_showSend->integer ) { Com_Printf( "(%i)", count ); } // begin a client move command if ( cl_nodelta->integer || !cl.snap.valid || clc.demowaiting || clc.serverMessageSequence != cl.snap.messageNum ) { MSG_WriteByte (&buf, clc_moveNoDelta); } else { MSG_WriteByte (&buf, clc_move); } // write the command count MSG_WriteByte( &buf, count ); // use the checksum feed in the key key = clc.checksumFeed; // also use the message acknowledge key ^= clc.serverMessageSequence; // also use the last acknowledged server command in the key key ^= Com_HashKey(clc.serverCommands[ clc.serverCommandSequence & (MAX_RELIABLE_COMMANDS-1) ], 32); // write all the commands, including the predicted command for ( i = 0 ; i < count ; i++ ) { j = (cl.cmdNumber - count + i + 1) & CMD_MASK; cmd = &cl.cmds[j]; MSG_WriteDeltaUsercmdKey (&buf, key, oldcmd, cmd); oldcmd = cmd; } } // // deliver the message // packetNum = clc.netchan.outgoingSequence & PACKET_MASK; cl.outPackets[ packetNum ].p_realtime = cls.realtime; cl.outPackets[ packetNum ].p_serverTime = oldcmd->serverTime; cl.outPackets[ packetNum ].p_cmdNumber = cl.cmdNumber; clc.lastPacketSentTime = cls.realtime; if ( cl_showSend->integer ) { Com_Printf( "%i ", buf.cursize ); } CL_Netchan_Transmit (&clc.netchan, &buf); // clients never really should have messages large enough // to fragment, but in case they do, fire them all off // at once // TTimo: this causes a packet burst, which is bad karma for winsock // added a WARNING message, we'll see if there are legit situations where this happens while ( clc.netchan.unsentFragments ) { Com_DPrintf( "WARNING: #462 unsent fragments (not supposed to happen!)\n" ); CL_Netchan_TransmitNextFragment( &clc.netchan ); } } /* ================= CL_SendCmd Called every frame to builds and sends a command packet to the server. ================= */ void CL_SendCmd( void ) { // don't send any message if not connected if ( cls.state < CA_CONNECTED ) { return; } // don't send commands if paused if ( com_sv_running->integer && sv_paused->integer && cl_paused->integer ) { return; } // we create commands even if a demo is playing, CL_CreateNewCommands(); // don't send a packet if the last packet was sent too recently if ( !CL_ReadyToSendPacket() ) { if ( cl_showSend->integer ) { Com_Printf( ". " ); } return; } CL_WritePacket(); } /* ============ CL_InitInput ============ */ void CL_InitInput( void ) { Cmd_AddCommand ("centerview",IN_CenterView); Cmd_AddCommand ("+moveup",IN_UpDown); Cmd_AddCommand ("-moveup",IN_UpUp); Cmd_AddCommand ("+movedown",IN_DownDown); Cmd_AddCommand ("-movedown",IN_DownUp); Cmd_AddCommand ("+left",IN_LeftDown); Cmd_AddCommand ("-left",IN_LeftUp); Cmd_AddCommand ("+right",IN_RightDown); Cmd_AddCommand ("-right",IN_RightUp); Cmd_AddCommand ("+forward",IN_ForwardDown); Cmd_AddCommand ("-forward",IN_ForwardUp); Cmd_AddCommand ("+back",IN_BackDown); Cmd_AddCommand ("-back",IN_BackUp); Cmd_AddCommand ("+lookup", IN_LookupDown); Cmd_AddCommand ("-lookup", IN_LookupUp); Cmd_AddCommand ("+lookdown", IN_LookdownDown); Cmd_AddCommand ("-lookdown", IN_LookdownUp); Cmd_AddCommand ("+strafe", IN_StrafeDown); Cmd_AddCommand ("-strafe", IN_StrafeUp); Cmd_AddCommand ("+moveleft", IN_MoveleftDown); Cmd_AddCommand ("-moveleft", IN_MoveleftUp); Cmd_AddCommand ("+moveright", IN_MoverightDown); Cmd_AddCommand ("-moveright", IN_MoverightUp); Cmd_AddCommand ("+speed", IN_SpeedDown); Cmd_AddCommand ("-speed", IN_SpeedUp); Cmd_AddCommand ("+attack", IN_Button0Down); Cmd_AddCommand ("-attack", IN_Button0Up); Cmd_AddCommand ("+button0", IN_Button0Down); Cmd_AddCommand ("-button0", IN_Button0Up); Cmd_AddCommand ("+button1", IN_Button1Down); Cmd_AddCommand ("-button1", IN_Button1Up); Cmd_AddCommand ("+button2", IN_Button2Down); Cmd_AddCommand ("-button2", IN_Button2Up); Cmd_AddCommand ("+button3", IN_Button3Down); Cmd_AddCommand ("-button3", IN_Button3Up); Cmd_AddCommand ("+button4", IN_Button4Down); Cmd_AddCommand ("-button4", IN_Button4Up); Cmd_AddCommand ("+button5", IN_Button5Down); Cmd_AddCommand ("-button5", IN_Button5Up); Cmd_AddCommand ("+button6", IN_Button6Down); Cmd_AddCommand ("-button6", IN_Button6Up); Cmd_AddCommand ("+button7", IN_Button7Down); Cmd_AddCommand ("-button7", IN_Button7Up); Cmd_AddCommand ("+button8", IN_Button8Down); Cmd_AddCommand ("-button8", IN_Button8Up); Cmd_AddCommand ("+button9", IN_Button9Down); Cmd_AddCommand ("-button9", IN_Button9Up); Cmd_AddCommand ("+button10", IN_Button10Down); Cmd_AddCommand ("-button10", IN_Button10Up); Cmd_AddCommand ("+button11", IN_Button11Down); Cmd_AddCommand ("-button11", IN_Button11Up); Cmd_AddCommand ("+button12", IN_Button12Down); Cmd_AddCommand ("-button12", IN_Button12Up); Cmd_AddCommand ("+button13", IN_Button13Down); Cmd_AddCommand ("-button13", IN_Button13Up); Cmd_AddCommand ("+button14", IN_Button14Down); Cmd_AddCommand ("-button14", IN_Button14Up); Cmd_AddCommand ("+mlook", IN_MLookDown); Cmd_AddCommand ("-mlook", IN_MLookUp); cl_nodelta = Cvar_Get ("cl_nodelta", "0", 0); cl_debugMove = Cvar_Get ("cl_debugMove", "0", 0); } ================================================ FILE: src/engine/client/cl_keys.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "client.h" /* key up events are sent even if in console mode */ field_t historyEditLines[COMMAND_HISTORY]; int nextHistoryLine; // the last line in the history buffer, not masked int historyLine; // the line being displayed from history buffer // will be <= nextHistoryLine field_t g_consoleField; field_t chatField; qboolean chat_team; int chat_playerNum; qboolean key_overstrikeMode; qboolean anykeydown; qkey_t keys[MAX_KEYS]; typedef struct { char *name; int keynum; } keyname_t; // names not in this list can either be lowercase ascii, or '0xnn' hex sequences keyname_t keynames[] = { {"TAB", K_TAB}, {"ENTER", K_ENTER}, {"ESCAPE", K_ESCAPE}, {"SPACE", K_SPACE}, {"BACKSPACE", K_BACKSPACE}, {"UPARROW", K_UPARROW}, {"DOWNARROW", K_DOWNARROW}, {"LEFTARROW", K_LEFTARROW}, {"RIGHTARROW", K_RIGHTARROW}, {"ALT", K_ALT}, {"CTRL", K_CTRL}, {"SHIFT", K_SHIFT}, {"COMMAND", K_COMMAND}, {"CAPSLOCK", K_CAPSLOCK}, {"F1", K_F1}, {"F2", K_F2}, {"F3", K_F3}, {"F4", K_F4}, {"F5", K_F5}, {"F6", K_F6}, {"F7", K_F7}, {"F8", K_F8}, {"F9", K_F9}, {"F10", K_F10}, {"F11", K_F11}, {"F12", K_F12}, {"INS", K_INS}, {"DEL", K_DEL}, {"PGDN", K_PGDN}, {"PGUP", K_PGUP}, {"HOME", K_HOME}, {"END", K_END}, {"MOUSE1", K_MOUSE1}, {"MOUSE2", K_MOUSE2}, {"MOUSE3", K_MOUSE3}, {"MOUSE4", K_MOUSE4}, {"MOUSE5", K_MOUSE5}, {"MWHEELUP", K_MWHEELUP }, {"MWHEELDOWN", K_MWHEELDOWN }, {"JOY1", K_JOY1}, {"JOY2", K_JOY2}, {"JOY3", K_JOY3}, {"JOY4", K_JOY4}, {"JOY5", K_JOY5}, {"JOY6", K_JOY6}, {"JOY7", K_JOY7}, {"JOY8", K_JOY8}, {"JOY9", K_JOY9}, {"JOY10", K_JOY10}, {"JOY11", K_JOY11}, {"JOY12", K_JOY12}, {"JOY13", K_JOY13}, {"JOY14", K_JOY14}, {"JOY15", K_JOY15}, {"JOY16", K_JOY16}, {"JOY17", K_JOY17}, {"JOY18", K_JOY18}, {"JOY19", K_JOY19}, {"JOY20", K_JOY20}, {"JOY21", K_JOY21}, {"JOY22", K_JOY22}, {"JOY23", K_JOY23}, {"JOY24", K_JOY24}, {"JOY25", K_JOY25}, {"JOY26", K_JOY26}, {"JOY27", K_JOY27}, {"JOY28", K_JOY28}, {"JOY29", K_JOY29}, {"JOY30", K_JOY30}, {"JOY31", K_JOY31}, {"JOY32", K_JOY32}, {"AUX1", K_AUX1}, {"AUX2", K_AUX2}, {"AUX3", K_AUX3}, {"AUX4", K_AUX4}, {"AUX5", K_AUX5}, {"AUX6", K_AUX6}, {"AUX7", K_AUX7}, {"AUX8", K_AUX8}, {"AUX9", K_AUX9}, {"AUX10", K_AUX10}, {"AUX11", K_AUX11}, {"AUX12", K_AUX12}, {"AUX13", K_AUX13}, {"AUX14", K_AUX14}, {"AUX15", K_AUX15}, {"AUX16", K_AUX16}, {"KP_HOME", K_KP_HOME }, {"KP_UPARROW", K_KP_UPARROW }, {"KP_PGUP", K_KP_PGUP }, {"KP_LEFTARROW", K_KP_LEFTARROW }, {"KP_5", K_KP_5 }, {"KP_RIGHTARROW", K_KP_RIGHTARROW }, {"KP_END", K_KP_END }, {"KP_DOWNARROW", K_KP_DOWNARROW }, {"KP_PGDN", K_KP_PGDN }, {"KP_ENTER", K_KP_ENTER }, {"KP_INS", K_KP_INS }, {"KP_DEL", K_KP_DEL }, {"KP_SLASH", K_KP_SLASH }, {"KP_MINUS", K_KP_MINUS }, {"KP_PLUS", K_KP_PLUS }, {"KP_NUMLOCK", K_KP_NUMLOCK }, {"KP_STAR", K_KP_STAR }, {"KP_EQUALS", K_KP_EQUALS }, {"PAUSE", K_PAUSE}, {"SEMICOLON", ';'}, // because a raw semicolon seperates commands {NULL,0} }; /* ============================================================================= EDIT FIELDS ============================================================================= */ /* =================== Field_Draw Handles horizontal scrolling and cursor blinking x, y, amd width are in pixels =================== */ void Field_VariableSizeDraw( field_t *edit, int x, int y, int width, int size, qboolean showCursor ) { int len; int drawLen; int prestep; int cursorChar; char str[MAX_STRING_CHARS]; int i; drawLen = edit->widthInChars; len = (int)strlen( edit->buffer ) + 1; // guarantee that cursor will be visible if ( len <= drawLen ) { prestep = 0; } else { if ( edit->scroll + drawLen > len ) { edit->scroll = len - drawLen; if ( edit->scroll < 0 ) { edit->scroll = 0; } } prestep = edit->scroll; /* if ( edit->cursor < len - drawLen ) { prestep = edit->cursor; // cursor at start } else { prestep = len - drawLen; } */ } if ( prestep + drawLen > len ) { drawLen = len - prestep; } // extract characters from the field at if ( drawLen >= MAX_STRING_CHARS ) { Com_Error( ERR_DROP, "drawLen >= MAX_STRING_CHARS" ); } Com_Memcpy( str, edit->buffer + prestep, drawLen ); str[ drawLen ] = 0; // draw it if ( size == SMALLCHAR_WIDTH ) { float color[4]; color[0] = color[1] = color[2] = color[3] = 1.0; SCR_DrawSmallStringExt( x, y, str, color, qfalse ); } else { // draw big string with drop shadow SCR_DrawBigString( x, y, str, 1.0 ); } // draw the cursor if ( !showCursor ) { return; } if ( (int)( cls.realtime >> 8 ) & 1 ) { return; // off blink } if ( key_overstrikeMode ) { cursorChar = 11; } else { cursorChar = 10; } i = drawLen - ( Q_PrintStrlen( str ) + 1 ); if ( size == SMALLCHAR_WIDTH ) { SCR_DrawSmallChar( x + ( edit->cursor - prestep - i ) * size, y, cursorChar ); } else { str[0] = cursorChar; str[1] = 0; SCR_DrawBigString( x + ( edit->cursor - prestep - i ) * size, y, str, 1.0 ); } } void Field_Draw( field_t *edit, int x, int y, int width, qboolean showCursor ) { Field_VariableSizeDraw( edit, x, y, width, SMALLCHAR_WIDTH, showCursor ); } void Field_BigDraw( field_t *edit, int x, int y, int width, qboolean showCursor ) { Field_VariableSizeDraw( edit, x, y, width, BIGCHAR_WIDTH, showCursor ); } /* ================ Field_Paste ================ */ void Field_Paste( field_t *edit ) { char *cbd; int pasteLen, i; cbd = Sys_GetClipboardData(); if ( !cbd ) { return; } // send as if typed, so insert / overstrike works properly pasteLen = (int)strlen( cbd ); for ( i = 0 ; i < pasteLen ; i++ ) { Field_CharEvent( edit, cbd[i] ); } Z_Free( cbd ); } /* ================= Field_KeyDownEvent Performs the basic line editing functions for the console, in-game talk, and menu fields Key events are used for non-printable characters, others are gotten from char events. ================= */ void Field_KeyDownEvent( field_t *edit, int key ) { int len; // shift-insert is paste if ( ( ( key == K_INS ) || ( key == K_KP_INS ) ) && keys[K_SHIFT].down ) { Field_Paste( edit ); return; } len = (int)strlen( edit->buffer ); if ( key == K_DEL ) { if ( edit->cursor < len ) { memmove( edit->buffer + edit->cursor, edit->buffer + edit->cursor + 1, len - edit->cursor ); } return; } if ( key == K_RIGHTARROW ) { if ( edit->cursor < len ) { edit->cursor++; } if ( edit->cursor >= edit->scroll + edit->widthInChars && edit->cursor <= len ) { edit->scroll++; } return; } if ( key == K_LEFTARROW ) { if ( edit->cursor > 0 ) { edit->cursor--; } if ( edit->cursor < edit->scroll ) { edit->scroll--; } return; } if ( key == K_HOME || ( tolower(key) == 'a' && keys[K_CTRL].down ) ) { edit->cursor = 0; return; } if ( key == K_END || ( tolower(key) == 'e' && keys[K_CTRL].down ) ) { edit->cursor = len; return; } if ( key == K_INS ) { key_overstrikeMode = (qboolean) !key_overstrikeMode; return; } } /* ================== Field_CharEvent ================== */ void Field_CharEvent( field_t *edit, int ch ) { int len; if ( ch == 'v' - 'a' + 1 ) { // ctrl-v is paste Field_Paste( edit ); return; } if ( ch == 'c' - 'a' + 1 ) { // ctrl-c clears the field Field_Clear( edit ); return; } len = (int)strlen( edit->buffer ); if ( ch == 'h' - 'a' + 1 ) { // ctrl-h is backspace if ( edit->cursor > 0 ) { memmove( edit->buffer + edit->cursor - 1, edit->buffer + edit->cursor, len + 1 - edit->cursor ); edit->cursor--; if ( edit->cursor < edit->scroll ) { edit->scroll--; } } return; } if ( ch == 'a' - 'a' + 1 ) { // ctrl-a is home edit->cursor = 0; edit->scroll = 0; return; } if ( ch == 'e' - 'a' + 1 ) { // ctrl-e is end edit->cursor = len; edit->scroll = edit->cursor - edit->widthInChars; return; } // // ignore any other non printable chars // if ( ch < 32 ) { return; } if ( key_overstrikeMode ) { if ( edit->cursor == MAX_EDIT_LINE - 1 ) return; edit->buffer[edit->cursor] = ch; edit->cursor++; } else { // insert mode if ( len == MAX_EDIT_LINE - 1 ) { return; // all full } memmove( edit->buffer + edit->cursor + 1, edit->buffer + edit->cursor, len + 1 - edit->cursor ); edit->buffer[edit->cursor] = ch; edit->cursor++; } if ( edit->cursor >= edit->widthInChars ) { edit->scroll++; } if ( edit->cursor == len + 1) { edit->buffer[edit->cursor] = 0; } } /* ============================================================================= CONSOLE LINE EDITING ============================================================================== */ /* ==================== Console_Key Handles history and console scrollback ==================== */ void Console_Key (int key) { // ctrl-L clears screen if ( key == 'l' && keys[K_CTRL].down ) { Cbuf_AddText ("clear\n"); return; } // enter finishes the line if ( key == K_ENTER || key == K_KP_ENTER ) { // if not in the game explicitly prepent a slash if needed if ( cls.state != CA_ACTIVE && g_consoleField.buffer[0] != '\\' && g_consoleField.buffer[0] != '/' ) { char temp[MAX_STRING_CHARS]; Q_strncpyz( temp, g_consoleField.buffer, sizeof( temp ) ); Com_sprintf( g_consoleField.buffer, sizeof( g_consoleField.buffer ), "\\%s", temp ); g_consoleField.cursor++; } Com_Printf ( "]%s\n", g_consoleField.buffer ); // leading slash is an explicit command if ( g_consoleField.buffer[0] == '\\' || g_consoleField.buffer[0] == '/' ) { Cbuf_AddText( g_consoleField.buffer+1 ); // valid command Cbuf_AddText ("\n"); } else { // other text will be chat messages if ( !g_consoleField.buffer[0] ) { return; // empty lines just scroll the console without adding to history } else { Cbuf_AddText ("cmd say "); Cbuf_AddText( g_consoleField.buffer ); Cbuf_AddText ("\n"); } } // copy line to history buffer historyEditLines[nextHistoryLine % COMMAND_HISTORY] = g_consoleField; nextHistoryLine++; historyLine = nextHistoryLine; Field_Clear( &g_consoleField ); g_consoleField.widthInChars = g_console_field_width; if ( cls.state == CA_DISCONNECTED ) { SCR_UpdateScreen (); // force an update, because the command } // may take some time return; } // command completion if (key == K_TAB) { Field_CompleteCommand(&g_consoleField); return; } // command history (ctrl-p ctrl-n for unix style) if ( (key == K_MWHEELUP && keys[K_SHIFT].down) || ( key == K_UPARROW ) || ( key == K_KP_UPARROW ) || ( ( tolower(key) == 'p' ) && keys[K_CTRL].down ) ) { if ( nextHistoryLine - historyLine < COMMAND_HISTORY && historyLine > 0 ) { historyLine--; } g_consoleField = historyEditLines[ historyLine % COMMAND_HISTORY ]; return; } if ( (key == K_MWHEELDOWN && keys[K_SHIFT].down) || ( key == K_DOWNARROW ) || ( key == K_KP_DOWNARROW ) || ( ( tolower(key) == 'n' ) && keys[K_CTRL].down ) ) { if (historyLine == nextHistoryLine) return; historyLine++; g_consoleField = historyEditLines[ historyLine % COMMAND_HISTORY ]; return; } // console scrolling if ( key == K_PGUP ) { Con_PageUp(); return; } if ( key == K_PGDN) { Con_PageDown(); return; } if ( key == K_MWHEELUP) { //----(SA) added some mousewheel functionality to the console Con_PageUp(); if(keys[K_CTRL].down) { // hold to accelerate scrolling Con_PageUp(); Con_PageUp(); } return; } if ( key == K_MWHEELDOWN) { //----(SA) added some mousewheel functionality to the console Con_PageDown(); if(keys[K_CTRL].down) { // hold to accelerate scrolling Con_PageDown(); Con_PageDown(); } return; } // ctrl-home = top of console if ( key == K_HOME && keys[K_CTRL].down ) { Con_Top(); return; } // ctrl-end = bottom of console if ( key == K_END && keys[K_CTRL].down ) { Con_Bottom(); return; } // pass to the normal editline routine Field_KeyDownEvent( &g_consoleField, key ); } //============================================================================ /* ================ Message_Key In game talk message ================ */ void Message_Key( int key ) { char buffer[MAX_STRING_CHARS]; if (key == K_ESCAPE) { cls.keyCatchers &= ~KEYCATCH_MESSAGE; Field_Clear( &chatField ); return; } if ( key == K_ENTER || key == K_KP_ENTER ) { if ( chatField.buffer[0] && cls.state == CA_ACTIVE ) { if (chat_playerNum != -1 ) Com_sprintf( buffer, sizeof( buffer ), "tell %i \"%s\"\n", chat_playerNum, chatField.buffer ); else if (chat_team) Com_sprintf( buffer, sizeof( buffer ), "say_team \"%s\"\n", chatField.buffer ); else Com_sprintf( buffer, sizeof( buffer ), "say \"%s\"\n", chatField.buffer ); CL_AddReliableCommand( buffer ); } cls.keyCatchers &= ~KEYCATCH_MESSAGE; Field_Clear( &chatField ); return; } Field_KeyDownEvent( &chatField, key ); } //============================================================================ qboolean Key_GetOverstrikeMode( void ) { return key_overstrikeMode; } void Key_SetOverstrikeMode( qboolean state ) { key_overstrikeMode = state; } /* =================== Key_IsDown =================== */ qboolean Key_IsDown( int keynum ) { if ( keynum == -1 ) { return qfalse; } return keys[keynum].down; } /* =================== Key_StringToKeynum Returns a key number to be used to index keys[] by looking at the given string. Single ascii characters return themselves, while the K_* names are matched up. 0x11 will be interpreted as raw hex, which will allow new controlers to be configured even if they don't have defined names. =================== */ int Key_StringToKeynum( char *str ) { keyname_t *kn; if ( !str || !str[0] ) { return -1; } if ( !str[1] ) { return str[0]; } // check for hex code if ( str[0] == '0' && str[1] == 'x' && (int)strlen( str ) == 4) { int n1, n2; n1 = str[2]; if ( n1 >= '0' && n1 <= '9' ) { n1 -= '0'; } else if ( n1 >= 'a' && n1 <= 'f' ) { n1 = n1 - 'a' + 10; } else { n1 = 0; } n2 = str[3]; if ( n2 >= '0' && n2 <= '9' ) { n2 -= '0'; } else if ( n2 >= 'a' && n2 <= 'f' ) { n2 = n2 - 'a' + 10; } else { n2 = 0; } return n1 * 16 + n2; } // scan for a text match for ( kn=keynames ; kn->name ; kn++ ) { if ( !Q_stricmp( str,kn->name ) ) return kn->keynum; } return -1; } /* =================== Key_KeynumToString Returns a string (either a single ascii char, a K_* name, or a 0x11 hex string) for the given keynum. =================== */ char *Key_KeynumToString( int keynum ) { keyname_t *kn; static char tinystr[5]; int i, j; if ( keynum == -1 ) { return ""; } if ( keynum < 0 || keynum > 255 ) { return ""; } // check for printable ascii (don't use quote) if ( keynum > 32 && keynum < 127 && keynum != '"' && keynum != ';' ) { tinystr[0] = keynum; tinystr[1] = 0; return tinystr; } // check for a key string for ( kn=keynames ; kn->name ; kn++ ) { if (keynum == kn->keynum) { return kn->name; } } // make a hex string i = keynum >> 4; j = keynum & 15; tinystr[0] = '0'; tinystr[1] = 'x'; tinystr[2] = i > 9 ? i - 10 + 'a' : i + '0'; tinystr[3] = j > 9 ? j - 10 + 'a' : j + '0'; tinystr[4] = 0; return tinystr; } /* =================== Key_SetBinding =================== */ void Key_SetBinding( int keynum, const char *binding ) { if ( keynum == -1 ) { return; } // free old bindings if ( keys[ keynum ].binding ) { Z_Free( keys[ keynum ].binding ); } // allocate memory for new binding keys[keynum].binding = CopyString( binding ); // consider this like modifying an archived cvar, so the // file write will be triggered at the next oportunity cvar_modifiedFlags |= CVAR_ARCHIVE; } /* =================== Key_GetBinding =================== */ char *Key_GetBinding( int keynum ) { if ( keynum == -1 ) { return ""; } return keys[ keynum ].binding; } /* =================== Key_GetKey =================== */ int Key_GetKey(const char *binding) { int i; if (binding) { for (i=0 ; i<256 ; i++) { if (keys[i].binding && Q_stricmp(binding, keys[i].binding) == 0) { return i; } } } return -1; } /* =================== Key_Unbind_f =================== */ void Key_Unbind_f (void) { int b; if (Cmd_Argc() != 2) { Com_Printf ("unbind : remove commands from a key\n"); return; } b = Key_StringToKeynum (Cmd_Argv(1)); if (b==-1) { Com_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1)); return; } Key_SetBinding (b, ""); } /* =================== Key_Unbindall_f =================== */ void Key_Unbindall_f (void) { int i; for (i=0 ; i<256 ; i++) if (keys[i].binding) Key_SetBinding (i, ""); } /* =================== Key_Bind_f =================== */ void Key_Bind_f (void) { int i, c, b; char cmd[1024]; c = Cmd_Argc(); if (c < 2) { Com_Printf ("bind [command] : attach a command to a key\n"); return; } b = Key_StringToKeynum (Cmd_Argv(1)); if (b==-1) { Com_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1)); return; } if (c == 2) { if (keys[b].binding) Com_Printf ("\"%s\" = \"%s\"\n", Cmd_Argv(1), keys[b].binding ); else Com_Printf ("\"%s\" is not bound\n", Cmd_Argv(1) ); return; } // copy the rest of the command line cmd[0] = 0; // start out with a null string for (i=2 ; i< c ; i++) { strcat (cmd, Cmd_Argv(i)); if (i != (c-1)) strcat (cmd, " "); } Key_SetBinding (b, cmd); } /* ============ Key_WriteBindings Writes lines containing "bind key value" ============ */ void Key_WriteBindings( fileHandle_t f ) { int i; FS_Printf (f, "unbindall\n" ); for (i=0 ; i<256 ; i++) { if (keys[i].binding && keys[i].binding[0] ) { FS_Printf (f, "bind %s \"%s\"\n", Key_KeynumToString(i), keys[i].binding); } } } /* ============ Key_Bindlist_f ============ */ void Key_Bindlist_f( void ) { int i; for ( i = 0 ; i < 256 ; i++ ) { if ( keys[i].binding && keys[i].binding[0] ) { Com_Printf( "%s \"%s\"\n", Key_KeynumToString(i), keys[i].binding ); } } } /* =================== CL_InitKeyCommands =================== */ void CL_InitKeyCommands( void ) { // register our functions Cmd_AddCommand ("bind",Key_Bind_f); Cmd_AddCommand ("unbind",Key_Unbind_f); Cmd_AddCommand ("unbindall",Key_Unbindall_f); Cmd_AddCommand ("bindlist",Key_Bindlist_f); } /* =================== CL_AddKeyUpCommands =================== */ void CL_AddKeyUpCommands( int key, char *kb ) { int i; char button[1024], *buttonPtr; char cmd[1024]; qboolean keyevent; if ( !kb ) { return; } keyevent = qfalse; buttonPtr = button; for ( i = 0; ; i++ ) { if ( kb[i] == ';' || !kb[i] ) { *buttonPtr = '\0'; if ( button[0] == '+') { // button commands add keynum and time as parms so that multiple // sources can be discriminated and subframe corrected Com_sprintf (cmd, sizeof(cmd), "-%s %i %i\n", button+1, key, time); Cbuf_AddText (cmd); keyevent = qtrue; } else { if (keyevent) { // down-only command Cbuf_AddText (button); Cbuf_AddText ("\n"); } } buttonPtr = button; while ( (kb[i] <= ' ' || kb[i] == ';') && kb[i] != 0 ) { i++; } } *buttonPtr++ = kb[i]; if ( !kb[i] ) { break; } } } /* =================== CL_KeyEvent Called by the system for both key up and key down events =================== */ void CL_KeyEvent (int key, qboolean down, unsigned time) { char *kb; char cmd[1024]; // update auto-repeat status and BUTTON_ANY status keys[key].down = down; if (down) { keys[key].repeats++; if ( keys[key].repeats == 1) { ((int&)anykeydown)++; } } else { keys[key].repeats = 0; ((int&)anykeydown)--; if (anykeydown < 0) { (int&)anykeydown = 0; } } #ifdef __linux__ if (key == K_ENTER) { if (down) { if (keys[K_ALT].down) { Key_ClearStates(); if (Cvar_VariableValue("r_fullscreen") == 0) { Com_Printf("Switching to fullscreen rendering\n"); Cvar_Set("r_fullscreen", "1"); } else { Com_Printf("Switching to windowed rendering\n"); Cvar_Set("r_fullscreen", "0"); } Cbuf_ExecuteText( EXEC_APPEND, "vid_restart\n"); return; } } } #endif // console key is hardcoded, so the user can never unbind it if (key == '`' || key == '~') { if (!down) { return; } Con_ToggleConsole_f (); return; } // keys can still be used for bound actions if ( down && ( key < 128 || key == K_MOUSE1 ) && ( clc.demoplaying || cls.state == CA_CINEMATIC ) && !cls.keyCatchers) { if (Cvar_VariableValue ("com_cameraMode") == 0) { Cvar_Set ("nextdemo",""); key = K_ESCAPE; } } // escape is always handled special if ( key == K_ESCAPE && down ) { if ( cls.keyCatchers & KEYCATCH_MESSAGE ) { // clear message mode Message_Key( key ); return; } // escape always gets out of CGAME stuff if (cls.keyCatchers & KEYCATCH_CGAME) { cls.keyCatchers &= ~KEYCATCH_CGAME; VM_Call (cgvm, CG_EVENT_HANDLING, CGAME_EVENT_NONE); return; } if ( !( cls.keyCatchers & KEYCATCH_UI ) ) { if ( cls.state == CA_ACTIVE && !clc.demoplaying ) { VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_INGAME ); } else { CL_Disconnect_f(); S_StopAllSounds(); VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_MAIN ); } return; } VM_Call( uivm, UI_KEY_EVENT, key, down ); return; } // // key up events only perform actions if the game key binding is // a button command (leading + sign). These will be processed even in // console mode and menu mode, to keep the character from continuing // an action started before a mode switch. // if (!down) { kb = keys[key].binding; CL_AddKeyUpCommands( key, kb ); if ( cls.keyCatchers & KEYCATCH_UI && uivm ) { VM_Call( uivm, UI_KEY_EVENT, key, down ); } else if ( cls.keyCatchers & KEYCATCH_CGAME && cgvm ) { VM_Call( cgvm, CG_KEY_EVENT, key, down ); } return; } // distribute the key down event to the apropriate handler if ( cls.keyCatchers & KEYCATCH_CONSOLE ) { Console_Key( key ); } else if ( cls.keyCatchers & KEYCATCH_UI ) { if ( uivm ) { VM_Call( uivm, UI_KEY_EVENT, key, down ); } } else if ( cls.keyCatchers & KEYCATCH_CGAME ) { if ( cgvm ) { VM_Call( cgvm, CG_KEY_EVENT, key, down ); } } else if ( cls.keyCatchers & KEYCATCH_MESSAGE ) { Message_Key( key ); } else if ( cls.state == CA_DISCONNECTED ) { Console_Key( key ); } else { // send the bound action kb = keys[key].binding; if ( !kb ) { if (key >= 200) { Com_Printf ("%s is unbound, use controls menu to set.\n" , Key_KeynumToString( key ) ); } } else if (kb[0] == '+') { int i; char button[1024], *buttonPtr; buttonPtr = button; for ( i = 0; ; i++ ) { if ( kb[i] == ';' || !kb[i] ) { *buttonPtr = '\0'; if ( button[0] == '+') { // button commands add keynum and time as parms so that multiple // sources can be discriminated and subframe corrected Com_sprintf (cmd, sizeof(cmd), "%s %i %i\n", button, key, time); Cbuf_AddText (cmd); } else { // down-only command Cbuf_AddText (button); Cbuf_AddText ("\n"); } buttonPtr = button; while ( (kb[i] <= ' ' || kb[i] == ';') && kb[i] != 0 ) { i++; } } *buttonPtr++ = kb[i]; if ( !kb[i] ) { break; } } } else { // down-only command Cbuf_AddText (kb); Cbuf_AddText ("\n"); } } } /* =================== CL_CharEvent Normal keyboard characters, already shifted / capslocked / etc =================== */ void CL_CharEvent( int key ) { // the console key should never be used as a char if ( key == '`' || key == '~' ) { return; } // distribute the key down event to the apropriate handler if ( cls.keyCatchers & KEYCATCH_CONSOLE ) { Field_CharEvent( &g_consoleField, key ); } else if ( cls.keyCatchers & KEYCATCH_UI ) { VM_Call( uivm, UI_KEY_EVENT, key | K_CHAR_FLAG, qtrue ); } else if ( cls.keyCatchers & KEYCATCH_MESSAGE ) { Field_CharEvent( &chatField, key ); } else if ( cls.state == CA_DISCONNECTED ) { Field_CharEvent( &g_consoleField, key ); } } /* =================== Key_ClearStates =================== */ void Key_ClearStates (void) { int i; anykeydown = qfalse; for ( i=0 ; i < MAX_KEYS ; i++ ) { if ( keys[i].down ) { CL_KeyEvent( i, qfalse, 0 ); } keys[i].down = (qboolean) 0; keys[i].repeats = 0; } } ================================================ FILE: src/engine/client/cl_main.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // cl_main.c -- client main loop #include "client.h" #include cvar_t *cl_nodelta; cvar_t *cl_debugMove; cvar_t *cl_noprint; cvar_t *cl_motd; cvar_t *rcon_client_password; cvar_t *rconAddress; cvar_t *cl_timeout; cvar_t *cl_maxpackets; cvar_t *cl_packetdup; cvar_t *cl_timeNudge; cvar_t *cl_showTimeDelta; cvar_t *cl_freezeDemo; cvar_t *cl_shownet; cvar_t *cl_showSend; cvar_t *cl_timedemo; cvar_t *cl_avidemo; cvar_t *cl_forceavidemo; cvar_t *cl_freelook; cvar_t *cl_sensitivity; cvar_t *cl_mouseAccel; cvar_t *cl_showMouseRate; cvar_t *m_pitch; cvar_t *m_yaw; cvar_t *m_forward; cvar_t *m_side; cvar_t *m_filter; cvar_t *cl_activeAction; cvar_t *cl_motdString; cvar_t *cl_allowDownload; cvar_t *cl_conXOffset; cvar_t *cl_inGameVideo; cvar_t *cl_serverStatusResendTime; cvar_t *cl_trn; clientActive_t cl; clientConnection_t clc; clientStatic_t cls; vm_t *cgvm; // Structure containing functions exported from refresh DLL refexport_t re; ping_t cl_pinglist[MAX_PINGREQUESTS]; typedef struct serverStatus_s { char string[BIG_INFO_STRING]; netadr_t address; int time, startTime; qboolean pending; qboolean print; qboolean retrieved; } serverStatus_t; serverStatus_t cl_serverStatusList[MAX_SERVERSTATUSREQUESTS]; int serverStatusCount; #if defined __USEA3D && defined __A3D_GEOM void hA3Dg_ExportRenderGeom (refexport_t *incoming_re); #endif extern void SV_BotFrame( int time ); void CL_CheckForResend( void ); void CL_ShowIP_f(void); void CL_ServerStatus_f(void); void CL_ServerStatusResponse( netadr_t from, msg_t *msg ); /* =============== CL_CDDialog Called by Com_Error when a cd is needed =============== */ void CL_CDDialog( void ) { cls.cddialog = qtrue; // start it next frame } /* ======================================================================= CLIENT RELIABLE COMMAND COMMUNICATION ======================================================================= */ /* ====================== CL_AddReliableCommand The given command will be transmitted to the server, and is gauranteed to not have future usercmd_t executed before it is executed ====================== */ void CL_AddReliableCommand( const char *cmd ) { int index; // if we would be losing an old command that hasn't been acknowledged, // we must drop the connection if ( clc.reliableSequence - clc.reliableAcknowledge > MAX_RELIABLE_COMMANDS ) { Com_Error( ERR_DROP, "Client command overflow" ); } clc.reliableSequence++; index = clc.reliableSequence & ( MAX_RELIABLE_COMMANDS - 1 ); Q_strncpyz( clc.reliableCommands[ index ], cmd, sizeof( clc.reliableCommands[ index ] ) ); } /* ====================== CL_ChangeReliableCommand ====================== */ void CL_ChangeReliableCommand( void ) { int r, index, l; r = clc.reliableSequence - (random() * 5); index = clc.reliableSequence & ( MAX_RELIABLE_COMMANDS - 1 ); l = (int)strlen(clc.reliableCommands[ index ]); if ( l >= MAX_STRING_CHARS - 1 ) { l = MAX_STRING_CHARS - 2; } clc.reliableCommands[ index ][ l ] = '\n'; clc.reliableCommands[ index ][ l+1 ] = '\0'; } /* ======================================================================= CLIENT SIDE DEMO RECORDING ======================================================================= */ /* ==================== CL_WriteDemoMessage Dumps the current net message, prefixed by the length ==================== */ void CL_WriteDemoMessage ( msg_t *msg, int headerBytes ) { int len, swlen; // write the packet sequence len = clc.serverMessageSequence; swlen = LittleLong( len ); FS_Write (&swlen, 4, clc.demofile); // skip the packet sequencing information len = msg->cursize - headerBytes; swlen = LittleLong(len); FS_Write (&swlen, 4, clc.demofile); FS_Write ( msg->data + headerBytes, len, clc.demofile ); } /* ==================== CL_StopRecording_f stop recording a demo ==================== */ void CL_StopRecord_f( void ) { int len; if ( !clc.demorecording ) { Com_Printf ("Not recording a demo.\n"); return; } // finish up len = -1; FS_Write (&len, 4, clc.demofile); FS_Write (&len, 4, clc.demofile); FS_FCloseFile (clc.demofile); clc.demofile = 0; clc.demorecording = qfalse; clc.spDemoRecording = qfalse; Com_Printf ("Stopped demo.\n"); } /* ================== CL_DemoFilename ================== */ void CL_DemoFilename( int number, char *fileName ) { int a,b,c,d; if ( number < 0 || number > 9999 ) { Com_sprintf( fileName, MAX_QPATH, "demo9999.tga" ); return; } a = number / 1000; number -= a*1000; b = number / 100; number -= b*100; c = number / 10; number -= c*10; d = number; Com_sprintf( fileName, MAX_QPATH, "demo%i%i%i%i" , a, b, c, d ); } /* ==================== CL_Record_f record Begins recording a demo from the current position ==================== */ static char demoName[MAX_QPATH]; // compiler bug workaround void CL_Record_f( void ) { char name[MAX_OSPATH]; byte bufData[MAX_MSGLEN]; msg_t buf; int i; int len; entityState_t *ent; entityState_t nullstate; char *s; if ( Cmd_Argc() > 2 ) { Com_Printf ("record \n"); return; } if ( clc.demorecording ) { if (!clc.spDemoRecording) { Com_Printf ("Already recording.\n"); } return; } if ( cls.state != CA_ACTIVE ) { Com_Printf ("You must be in a level to record.\n"); return; } // sync 0 doesn't prevent recording, so not forcing it off .. everyone does g_sync 1 ; record ; g_sync 0 .. if ( !Cvar_VariableValue( "g_synchronousClients" ) ) { Com_Printf (S_COLOR_YELLOW "WARNING: You should set 'g_synchronousClients 1' for smoother demo recording\n"); } if ( Cmd_Argc() == 2 ) { s = Cmd_Argv(1); Q_strncpyz( demoName, s, sizeof( demoName ) ); Com_sprintf (name, sizeof(name), "demos/%s.dm_%d", demoName, PROTOCOL_VERSION ); } else { int number; // scan for a free demo name for ( number = 0 ; number <= 9999 ; number++ ) { CL_DemoFilename( number, demoName ); Com_sprintf (name, sizeof(name), "demos/%s.dm_%d", demoName, PROTOCOL_VERSION ); len = FS_ReadFile( name, NULL ); if ( len <= 0 ) { break; // file doesn't exist } } } // open the demo file Com_Printf ("recording to %s.\n", name); clc.demofile = FS_FOpenFileWrite( name ); if ( !clc.demofile ) { Com_Printf ("ERROR: couldn't open.\n"); return; } clc.demorecording = qtrue; if (Cvar_VariableValue("ui_recordSPDemo")) { clc.spDemoRecording = qtrue; } else { clc.spDemoRecording = qfalse; } Q_strncpyz( clc.demoName, demoName, sizeof( clc.demoName ) ); // don't start saving messages until a non-delta compressed message is received clc.demowaiting = qtrue; // write out the gamestate message MSG_Init (&buf, bufData, sizeof(bufData)); MSG_Bitstream(&buf); // NOTE, MRE: all server->client messages now acknowledge MSG_WriteLong( &buf, clc.reliableSequence ); MSG_WriteByte (&buf, svc_gamestate); MSG_WriteLong (&buf, clc.serverCommandSequence ); // configstrings for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) { if ( !cl.gameState.stringOffsets[i] ) { continue; } s = cl.gameState.stringData + cl.gameState.stringOffsets[i]; MSG_WriteByte (&buf, svc_configstring); MSG_WriteShort (&buf, i); MSG_WriteBigString (&buf, s); } // baselines Com_Memset (&nullstate, 0, sizeof(nullstate)); for ( i = 0; i < MAX_GENTITIES ; i++ ) { ent = &cl.entityBaselines[i]; if ( !ent->number ) { continue; } MSG_WriteByte (&buf, svc_baseline); MSG_WriteDeltaEntity (&buf, &nullstate, ent, qtrue ); } MSG_WriteByte( &buf, svc_EOF ); // finished writing the gamestate stuff // write the client num MSG_WriteLong(&buf, clc.clientNum); // write the checksum feed MSG_WriteLong(&buf, clc.checksumFeed); // finished writing the client packet MSG_WriteByte( &buf, svc_EOF ); // write it to the demo file len = LittleLong( clc.serverMessageSequence - 1 ); FS_Write (&len, 4, clc.demofile); len = LittleLong (buf.cursize); FS_Write (&len, 4, clc.demofile); FS_Write (buf.data, buf.cursize, clc.demofile); // the rest of the demo file will be copied from net messages } /* ======================================================================= CLIENT SIDE DEMO PLAYBACK ======================================================================= */ /* ================= CL_DemoCompleted ================= */ void CL_DemoCompleted( void ) { if (cl_timedemo && cl_timedemo->integer) { int time; time = Sys_Milliseconds() - clc.timeDemoStart; if ( time > 0 ) { Com_Printf ("%i frames, %3.1f seconds: %3.1f fps\n", clc.timeDemoFrames, time/1000.0, clc.timeDemoFrames*1000.0 / time); } } CL_Disconnect( qtrue ); CL_NextDemo(); } /* ================= CL_ReadDemoMessage ================= */ void CL_ReadDemoMessage( void ) { int r; msg_t buf; byte bufData[ MAX_MSGLEN ]; int s; if ( !clc.demofile ) { CL_DemoCompleted (); return; } // get the sequence number r = FS_Read( &s, 4, clc.demofile); if ( r != 4 ) { CL_DemoCompleted (); return; } clc.serverMessageSequence = LittleLong( s ); // init the message MSG_Init( &buf, bufData, sizeof( bufData ) ); // get the length r = FS_Read (&buf.cursize, 4, clc.demofile); if ( r != 4 ) { CL_DemoCompleted (); return; } buf.cursize = LittleLong( buf.cursize ); if ( buf.cursize == -1 ) { CL_DemoCompleted (); return; } if ( buf.cursize > buf.maxsize ) { Com_Error (ERR_DROP, "CL_ReadDemoMessage: demoMsglen > MAX_MSGLEN"); } r = FS_Read( buf.data, buf.cursize, clc.demofile ); if ( r != buf.cursize ) { Com_Printf( "Demo file was truncated.\n"); CL_DemoCompleted (); return; } clc.lastPacketTime = cls.realtime; buf.readcount = 0; CL_ParseServerMessage( &buf ); } /* ==================== CL_WalkDemoExt ==================== */ static void CL_WalkDemoExt(char *arg, char *name, int *demofile) { int i = 0; *demofile = 0; while(demo_protocols[i]) { Com_sprintf (name, MAX_OSPATH, "demos/%s.dm_%d", arg, demo_protocols[i]); FS_FOpenFileRead( name, demofile, qtrue ); if (*demofile) { Com_Printf("Demo file: %s\n", name); break; } else Com_Printf("Not found: %s\n", name); i++; } } /* ==================== CL_PlayDemo_f demo ==================== */ void CL_PlayDemo_f( void ) { char name[MAX_OSPATH]; char *arg, *ext_test; int protocol, i; char retry[MAX_OSPATH]; if (Cmd_Argc() != 2) { Com_Printf ("playdemo \n"); return; } // make sure a local server is killed Cvar_Set( "sv_killserver", "1" ); CL_Disconnect( qtrue ); // open the demo file arg = Cmd_Argv(1); // check for an extension .dm_?? (?? is protocol) ext_test = arg + (int)strlen(arg) - 6; if ((strlen(arg) > 6) && (ext_test[0] == '.') && ((ext_test[1] == 'd') || (ext_test[1] == 'D')) && ((ext_test[2] == 'm') || (ext_test[2] == 'M')) && (ext_test[3] == '_')) { protocol = atoi(ext_test+4); i=0; while(demo_protocols[i]) { if (demo_protocols[i] == protocol) break; i++; } if (demo_protocols[i]) { Com_sprintf (name, sizeof(name), "demos/%s", arg); FS_FOpenFileRead( name, &clc.demofile, qtrue ); } else { Com_Printf("Protocol %d not supported for demos\n", protocol); Q_strncpyz(retry, arg, sizeof(retry)); retry[strlen(retry)-6] = 0; CL_WalkDemoExt( retry, name, &clc.demofile ); } } else { CL_WalkDemoExt( arg, name, &clc.demofile ); } if (!clc.demofile) { Com_Error( ERR_DROP, "couldn't open %s", name); return; } Q_strncpyz( clc.demoName, Cmd_Argv(1), sizeof( clc.demoName ) ); Con_Close(); cls.state = CA_CONNECTED; clc.demoplaying = qtrue; Q_strncpyz( cls.servername, Cmd_Argv(1), sizeof( cls.servername ) ); // read demo messages until connected while ( cls.state >= CA_CONNECTED && cls.state < CA_PRIMED ) { CL_ReadDemoMessage(); } // don't get the first snapshot this frame, to prevent the long // time from the gamestate load from messing causing a time skip clc.firstDemoFrameSkipped = qfalse; } /* ==================== CL_StartDemoLoop Closing the main menu will restart the demo loop ==================== */ void CL_StartDemoLoop( void ) { // start the demo loop again Cbuf_AddText ("d1\n"); cls.keyCatchers = 0; } /* ================== CL_NextDemo Called when a demo or cinematic finishes If the "nextdemo" cvar is set, that command will be issued ================== */ void CL_NextDemo( void ) { char v[MAX_STRING_CHARS]; Q_strncpyz( v, Cvar_VariableString ("nextdemo"), sizeof(v) ); v[MAX_STRING_CHARS-1] = 0; Com_DPrintf("CL_NextDemo: %s\n", v ); if (!v[0]) { return; } Cvar_Set ("nextdemo",""); Cbuf_AddText (v); Cbuf_AddText ("\n"); Cbuf_Execute(); } //====================================================================== /* ===================== CL_ShutdownAll ===================== */ void CL_ShutdownAll(void) { // clear sounds S_DisableSounds(); // shutdown CGame CL_ShutdownCGame(); // shutdown UI CL_ShutdownUI(); // shutdown the renderer if ( re.Shutdown ) { re.Shutdown( qfalse ); // don't destroy window or context } cls.uiStarted = qfalse; cls.cgameStarted = qfalse; cls.rendererStarted = qfalse; cls.soundRegistered = qfalse; } /* ================= CL_FlushMemory Called by CL_MapLoading, CL_Connect_f, CL_PlayDemo_f, and CL_ParseGamestate the only ways a client gets into a game Also called by Com_Error ================= */ void CL_FlushMemory( void ) { // shutdown all the client stuff CL_ShutdownAll(); // if not running a server clear the whole hunk if ( !com_sv_running->integer ) { // clear the whole hunk Hunk_Clear(); // clear collision map data CM_ClearMap(); } else { // clear all the client data on the hunk Hunk_ClearToMark(); } CL_StartHunkUsers(); } /* ===================== CL_MapLoading A local server is starting to load a map, so update the screen to let the user know about it, then dump all client memory on the hunk from cgame, ui, and renderer ===================== */ void CL_MapLoading( void ) { if ( !com_cl_running->integer ) { return; } Con_Close(); cls.keyCatchers = 0; // if we are already connected to the local host, stay connected if ( cls.state >= CA_CONNECTED && !Q_stricmp( cls.servername, "localhost" ) ) { cls.state = CA_CONNECTED; // so the connect screen is drawn Com_Memset( cls.updateInfoString, 0, sizeof( cls.updateInfoString ) ); Com_Memset( clc.serverMessage, 0, sizeof( clc.serverMessage ) ); Com_Memset( &cl.gameState, 0, sizeof( cl.gameState ) ); clc.lastPacketSentTime = -9999; SCR_UpdateScreen(); } else { // clear nextmap so the cinematic shutdown doesn't execute it Cvar_Set( "nextmap", "" ); CL_Disconnect( qtrue ); Q_strncpyz( cls.servername, "localhost", sizeof(cls.servername) ); cls.state = CA_CHALLENGING; // so the connect screen is drawn cls.keyCatchers = 0; SCR_UpdateScreen(); clc.connectTime = -RETRANSMIT_TIMEOUT; NET_StringToAdr( cls.servername, &clc.serverAddress); // we don't need a challenge on the localhost CL_CheckForResend(); } } /* ===================== CL_ClearState Called before parsing a gamestate ===================== */ void CL_ClearState (void) { // S_StopAllSounds(); Com_Memset( &cl, 0, sizeof( cl ) ); } /* ===================== CL_Disconnect Called when a connection, demo, or cinematic is being terminated. Goes from a connected state to either a menu state or a console state Sends a disconnect message to the server This is also called on Com_Error and Com_Quit, so it shouldn't cause any errors ===================== */ void CL_Disconnect( qboolean showMainMenu ) { if ( !com_cl_running || !com_cl_running->integer ) { return; } // shutting down the client so enter full screen ui mode Cvar_Set("r_uiFullScreen", "1"); if ( clc.demorecording ) { CL_StopRecord_f (); } if (clc.download) { FS_FCloseFile( clc.download ); clc.download = 0; } *clc.downloadTempName = *clc.downloadName = 0; Cvar_Set( "cl_downloadName", "" ); if ( clc.demofile ) { FS_FCloseFile( clc.demofile ); clc.demofile = 0; } if ( uivm && showMainMenu ) { VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_NONE ); } SCR_StopCinematic (); S_ClearSoundBuffer(); // send a disconnect message to the server // send it a few times in case one is dropped if ( cls.state >= CA_CONNECTED ) { CL_AddReliableCommand( "disconnect" ); CL_WritePacket(); CL_WritePacket(); CL_WritePacket(); } CL_ClearState (); // wipe the client connection Com_Memset( &clc, 0, sizeof( clc ) ); cls.state = CA_DISCONNECTED; // allow cheats locally Cvar_Set( "sv_cheats", "1" ); // not connected to a pure server anymore cl_connectedToPureServer = qfalse; } /* =================== CL_ForwardCommandToServer adds the current command line as a clientCommand things like godmode, noclip, etc, are commands directed to the server, so when they are typed in at the console, they will need to be forwarded. =================== */ void CL_ForwardCommandToServer( const char *string ) { char *cmd; cmd = Cmd_Argv(0); // ignore key up commands if ( cmd[0] == '-' ) { return; } if ( clc.demoplaying || cls.state < CA_CONNECTED || cmd[0] == '+' ) { Com_Printf ("Unknown command \"%s\"\n", cmd); return; } if ( Cmd_Argc() > 1 ) { CL_AddReliableCommand( string ); } else { CL_AddReliableCommand( cmd ); } } /* =================== CL_RequestMotd =================== */ void CL_RequestMotd( void ) { char info[MAX_INFO_STRING]; if ( !cl_motd->integer ) { return; } Com_Printf( "Resolving %s\n", UPDATE_SERVER_NAME ); if ( !NET_StringToAdr( UPDATE_SERVER_NAME, &cls.updateServer ) ) { Com_Printf( "Couldn't resolve address\n" ); return; } cls.updateServer.port = BigShort( PORT_UPDATE ); Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", UPDATE_SERVER_NAME, cls.updateServer.ip[0], cls.updateServer.ip[1], cls.updateServer.ip[2], cls.updateServer.ip[3], BigShort( cls.updateServer.port ) ); info[0] = 0; // NOTE TTimo xoring against Com_Milliseconds, otherwise we may not have a true randomization // only srand I could catch before here is tr_noise.c l:26 srand(1001) // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=382 // NOTE: the Com_Milliseconds xoring only affects the lower 16-bit word, // but I decided it was enough randomization Com_sprintf( cls.updateChallenge, sizeof( cls.updateChallenge ), "%i", ((rand() << 16) ^ rand()) ^ Com_Milliseconds()); Info_SetValueForKey( info, "challenge", cls.updateChallenge ); Info_SetValueForKey( info, "renderer", cls.glconfig.renderer_string ); Info_SetValueForKey( info, "version", com_version->string ); NET_OutOfBandPrint( NS_CLIENT, cls.updateServer, "getmotd \"%s\"\n", info ); } /* =================== CL_RequestAuthorization Authorization server protocol ----------------------------- All commands are text in Q3 out of band packets (leading 0xff 0xff 0xff 0xff). Whenever the client tries to get a challenge from the server it wants to connect to, it also blindly fires off a packet to the authorize server: getKeyAuthorize cdkey may be "demo" #OLD The authorize server returns a: #OLD #OLD keyAthorize #OLD #OLD A client will be accepted if the cdkey is valid and it has not been used by any other IP #OLD address in the last 15 minutes. The server sends a: getIpAuthorize The authorize server returns a: ipAuthorize A client will be accepted if a valid cdkey was sent by that ip (only) in the last 15 minutes. If no response is received from the authorize server after two tries, the client will be let in anyway. =================== */ void CL_RequestAuthorization( void ) { char nums[64]; int i, j, l; cvar_t *fs; if ( !cls.authorizeServer.port ) { Com_Printf( "Resolving %s\n", AUTHORIZE_SERVER_NAME ); if ( !NET_StringToAdr( AUTHORIZE_SERVER_NAME, &cls.authorizeServer ) ) { Com_Printf( "Couldn't resolve address\n" ); return; } cls.authorizeServer.port = BigShort( PORT_AUTHORIZE ); Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", AUTHORIZE_SERVER_NAME, cls.authorizeServer.ip[0], cls.authorizeServer.ip[1], cls.authorizeServer.ip[2], cls.authorizeServer.ip[3], BigShort( cls.authorizeServer.port ) ); } if ( cls.authorizeServer.type == NA_BAD ) { return; } if ( Cvar_VariableValue( "fs_restrict" ) ) { Q_strncpyz( nums, "demota", sizeof( nums ) ); } else { // only grab the alphanumeric values from the cdkey, to avoid any dashes or spaces j = 0; l = (int)strlen( cl_cdkey ); if ( l > 32 ) { l = 32; } for ( i = 0 ; i < l ; i++ ) { if ( ( cl_cdkey[i] >= '0' && cl_cdkey[i] <= '9' ) || ( cl_cdkey[i] >= 'a' && cl_cdkey[i] <= 'z' ) || ( cl_cdkey[i] >= 'A' && cl_cdkey[i] <= 'Z' ) ) { nums[j] = cl_cdkey[i]; j++; } } nums[j] = 0; } fs = Cvar_Get ("cl_anonymous", "0", CVAR_INIT|CVAR_SYSTEMINFO ); NET_OutOfBandPrint(NS_CLIENT, cls.authorizeServer, va("getKeyAuthorize %i %s", fs->integer, nums) ); } /* ====================================================================== CONSOLE COMMANDS ====================================================================== */ /* ================== CL_ForwardToServer_f ================== */ void CL_ForwardToServer_f( void ) { if ( cls.state != CA_ACTIVE || clc.demoplaying ) { Com_Printf ("Not connected to a server.\n"); return; } // don't forward the first argument if ( Cmd_Argc() > 1 ) { CL_AddReliableCommand( Cmd_Args() ); } } /* ================== CL_Setenv_f Mostly for controlling voodoo environment variables ================== */ void CL_Setenv_f( void ) { int argc = Cmd_Argc(); if ( argc > 2 ) { char buffer[1024]; int i; strcpy( buffer, Cmd_Argv(1) ); strcat( buffer, "=" ); for ( i = 2; i < argc; i++ ) { strcat( buffer, Cmd_Argv( i ) ); strcat( buffer, " " ); } putenv( buffer ); } else if ( argc == 2 ) { char *env = getenv( Cmd_Argv(1) ); if ( env ) { Com_Printf( "%s=%s\n", Cmd_Argv(1), env ); } else { Com_Printf( "%s undefined\n", Cmd_Argv(1), env ); } } } /* ================== CL_Disconnect_f ================== */ void CL_Disconnect_f( void ) { SCR_StopCinematic(); Cvar_Set("ui_singlePlayerActive", "0"); if ( cls.state != CA_DISCONNECTED && cls.state != CA_CINEMATIC ) { Com_Error (ERR_DISCONNECT, "Disconnected from server"); } } /* ================ CL_Reconnect_f ================ */ void CL_Reconnect_f( void ) { if ( !strlen( cls.servername ) || !strcmp( cls.servername, "localhost" ) ) { Com_Printf( "Can't reconnect to localhost.\n" ); return; } Cvar_Set("ui_singlePlayerActive", "0"); Cbuf_AddText( va("connect %s\n", cls.servername ) ); } /* ================ CL_Connect_f ================ */ void CL_Connect_f( void ) { char *server; if ( Cmd_Argc() != 2 ) { Com_Printf( "usage: connect [server]\n"); return; } Cvar_Set("ui_singlePlayerActive", "0"); // fire a message off to the motd server CL_RequestMotd(); // clear any previous "server full" type messages clc.serverMessage[0] = 0; server = Cmd_Argv (1); if ( com_sv_running->integer && !strcmp( server, "localhost" ) ) { // if running a local server, kill it SV_Shutdown( "Server quit\n" ); } // make sure a local server is killed Cvar_Set( "sv_killserver", "1" ); SV_Frame( 0 ); CL_Disconnect( qtrue ); Con_Close(); /* MrE: 2000-09-13: now called in CL_DownloadsComplete CL_FlushMemory( ); */ Q_strncpyz( cls.servername, server, sizeof(cls.servername) ); if (!NET_StringToAdr( cls.servername, &clc.serverAddress) ) { Com_Printf ("Bad server address\n"); cls.state = CA_DISCONNECTED; return; } if (clc.serverAddress.port == 0) { clc.serverAddress.port = BigShort( PORT_SERVER ); } Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", cls.servername, clc.serverAddress.ip[0], clc.serverAddress.ip[1], clc.serverAddress.ip[2], clc.serverAddress.ip[3], BigShort( clc.serverAddress.port ) ); // if we aren't playing on a lan, we need to authenticate // with the cd key if ( NET_IsLocalAddress( clc.serverAddress ) ) { cls.state = CA_CHALLENGING; } else { cls.state = CA_CONNECTING; } cls.keyCatchers = 0; clc.connectTime = -99999; // CL_CheckForResend() will fire immediately clc.connectPacketCount = 0; // server connection string Cvar_Set( "cl_currentServerAddress", server ); } /* ===================== CL_Rcon_f Send the rest of the command line over as an unconnected command. ===================== */ void CL_Rcon_f( void ) { char message[1024]; netadr_t to; if ( !rcon_client_password->string ) { Com_Printf ("You must set 'rconpassword' before\n" "issuing an rcon command.\n"); return; } message[0] = -1; message[1] = -1; message[2] = -1; message[3] = -1; message[4] = 0; strcat (message, "rcon "); strcat (message, rcon_client_password->string); strcat (message, " "); // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=543 strcat (message, Cmd_Cmd()+5); if ( cls.state >= CA_CONNECTED ) { to = clc.netchan.remoteAddress; } else { if (!strlen(rconAddress->string)) { Com_Printf ("You must either be connected,\n" "or set the 'rconAddress' cvar\n" "to issue rcon commands\n"); return; } NET_StringToAdr (rconAddress->string, &to); if (to.port == 0) { to.port = BigShort (PORT_SERVER); } } NET_SendPacket (NS_CLIENT, (int)strlen(message)+1, message, to); } /* ================= CL_SendPureChecksums ================= */ void CL_SendPureChecksums( void ) { const char *pChecksums; char cMsg[MAX_INFO_VALUE]; int i; // if we are pure we need to send back a command with our referenced pk3 checksums pChecksums = FS_ReferencedPakPureChecksums(); // "cp" // "Yf" Com_sprintf(cMsg, sizeof(cMsg), "Yf "); Q_strcat(cMsg, sizeof(cMsg), va("%d ", cl.serverId) ); Q_strcat(cMsg, sizeof(cMsg), pChecksums); for (i = 0; i < 2; i++) { cMsg[i] += 10; } CL_AddReliableCommand( cMsg ); } /* ================= CL_ResetPureClientAtServer ================= */ void CL_ResetPureClientAtServer( void ) { CL_AddReliableCommand( va("vdr") ); } /* ================= CL_Vid_Restart_f Restart the video subsystem we also have to reload the UI and CGame because the renderer doesn't know what graphics to reload ================= */ void CL_Vid_Restart_f( void ) { // don't let them loop during the restart S_StopAllSounds(); // shutdown the UI CL_ShutdownUI(); // shutdown the CGame CL_ShutdownCGame(); // shutdown the renderer and clear the renderer interface CL_ShutdownRef(); // client is no longer pure untill new checksums are sent CL_ResetPureClientAtServer(); // clear pak references FS_ClearPakReferences( FS_UI_REF | FS_CGAME_REF ); // reinitialize the filesystem if the game directory or checksum has changed FS_ConditionalRestart( clc.checksumFeed ); cls.rendererStarted = qfalse; cls.uiStarted = qfalse; cls.cgameStarted = qfalse; cls.soundRegistered = qfalse; // unpause so the cgame definately gets a snapshot and renders a frame Cvar_Set( "cl_paused", "0" ); // if not running a server clear the whole hunk if ( !com_sv_running->integer ) { // clear the whole hunk Hunk_Clear(); } else { // clear all the client data on the hunk Hunk_ClearToMark(); } // initialize the renderer interface CL_InitRef(); // startup all the client stuff CL_StartHunkUsers(); // start the cgame if connected if ( cls.state > CA_CONNECTED && cls.state != CA_CINEMATIC ) { cls.cgameStarted = qtrue; CL_InitCGame(); // send pure checksums CL_SendPureChecksums(); } } /* ================= CL_Snd_Restart_f Restart the sound subsystem The cgame and game must also be forced to restart because handles will be invalid ================= */ void CL_Snd_Restart_f( void ) { S_Shutdown(); S_Init(); CL_Vid_Restart_f(); } /* ================== CL_PK3List_f ================== */ void CL_OpenedPK3List_f( void ) { Com_Printf("Opened PK3 Names: %s\n", FS_LoadedPakNames()); } /* ================== CL_PureList_f ================== */ void CL_ReferencedPK3List_f( void ) { Com_Printf("Referenced PK3 Names: %s\n", FS_ReferencedPakNames()); } /* ================== CL_Configstrings_f ================== */ void CL_Configstrings_f( void ) { int i; int ofs; if ( cls.state != CA_ACTIVE ) { Com_Printf( "Not connected to a server.\n"); return; } for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) { ofs = cl.gameState.stringOffsets[ i ]; if ( !ofs ) { continue; } Com_Printf( "%4i: %s\n", i, cl.gameState.stringData + ofs ); } } /* ============== CL_Clientinfo_f ============== */ void CL_Clientinfo_f( void ) { Com_Printf( "--------- Client Information ---------\n" ); Com_Printf( "state: %i\n", cls.state ); Com_Printf( "Server: %s\n", cls.servername ); Com_Printf ("User info settings:\n"); Info_Print( Cvar_InfoString( CVAR_USERINFO ) ); Com_Printf( "--------------------------------------\n" ); } //==================================================================== /* ================= CL_DownloadsComplete Called when all downloading has been completed ================= */ void CL_DownloadsComplete( void ) { // if we downloaded files we need to restart the file system if (clc.downloadRestart) { clc.downloadRestart = qfalse; FS_Restart(clc.checksumFeed); // We possibly downloaded a pak, restart the file system to load it // inform the server so we get new gamestate info CL_AddReliableCommand( "donedl" ); // by sending the donedl command we request a new gamestate // so we don't want to load stuff yet return; } // let the client game init and load data cls.state = CA_LOADING; // Pump the loop, this may change gamestate! Com_EventLoop(); // if the gamestate was changed by calling Com_EventLoop // then we loaded everything already and we don't want to do it again. if ( cls.state != CA_LOADING ) { return; } // starting to load a map so we get out of full screen ui mode Cvar_Set("r_uiFullScreen", "0"); // flush client memory and start loading stuff // this will also (re)load the UI // if this is a local client then only the client part of the hunk // will be cleared, note that this is done after the hunk mark has been set CL_FlushMemory(); // initialize the CGame cls.cgameStarted = qtrue; CL_InitCGame(); // set pure checksums CL_SendPureChecksums(); CL_WritePacket(); CL_WritePacket(); CL_WritePacket(); } /* ================= CL_BeginDownload Requests a file to download from the server. Stores it in the current game directory. ================= */ void CL_BeginDownload( const char *localName, const char *remoteName ) { Com_DPrintf("***** CL_BeginDownload *****\n" "Localname: %s\n" "Remotename: %s\n" "****************************\n", localName, remoteName); Q_strncpyz ( clc.downloadName, localName, sizeof(clc.downloadName) ); Com_sprintf( clc.downloadTempName, sizeof(clc.downloadTempName), "%s.tmp", localName ); // Set so UI gets access to it Cvar_Set( "cl_downloadName", remoteName ); Cvar_Set( "cl_downloadSize", "0" ); Cvar_Set( "cl_downloadCount", "0" ); Cvar_SetValue( "cl_downloadTime", cls.realtime ); clc.downloadBlock = 0; // Starting new file clc.downloadCount = 0; CL_AddReliableCommand( va("download %s", remoteName) ); } /* ================= CL_NextDownload A download completed or failed ================= */ void CL_NextDownload(void) { char *s; char *remoteName, *localName; // We are looking to start a download here if (*clc.downloadList) { s = clc.downloadList; // format is: // @remotename@localname@remotename@localname, etc. if (*s == '@') s++; remoteName = s; if ( (s = strchr(s, '@')) == NULL ) { CL_DownloadsComplete(); return; } *s++ = 0; localName = s; if ( (s = strchr(s, '@')) != NULL ) *s++ = 0; else s = localName + (int)strlen(localName); // point at the nul byte CL_BeginDownload( localName, remoteName ); clc.downloadRestart = qtrue; // move over the rest memmove( clc.downloadList, s, (int)strlen(s) + 1); return; } CL_DownloadsComplete(); } /* ================= CL_InitDownloads After receiving a valid game state, we valid the cgame and local zip files here and determine if we need to download them ================= */ void CL_InitDownloads(void) { char missingfiles[1024]; if ( !cl_allowDownload->integer ) { // autodownload is disabled on the client // but it's possible that some referenced files on the server are missing if (FS_ComparePaks( missingfiles, sizeof( missingfiles ), qfalse ) ) { // NOTE TTimo I would rather have that printed as a modal message box // but at this point while joining the game we don't know wether we will successfully join or not Com_Printf( "\nWARNING: You are missing some files referenced by the server:\n%s" "You might not be able to join the game\n" "Go to the setting menu to turn on autodownload, or get the file elsewhere\n\n", missingfiles ); } } else if ( FS_ComparePaks( clc.downloadList, sizeof( clc.downloadList ) , qtrue ) ) { Com_Printf("Need paks: %s\n", clc.downloadList ); if ( *clc.downloadList ) { // if autodownloading is not enabled on the server cls.state = CA_CONNECTED; CL_NextDownload(); return; } } CL_DownloadsComplete(); } /* ================= CL_CheckForResend Resend a connect message if the last one has timed out ================= */ void CL_CheckForResend( void ) { int port, i; char info[MAX_INFO_STRING]; char data[MAX_INFO_STRING]; // don't send anything if playing back a demo if ( clc.demoplaying ) { return; } // resend if we haven't gotten a reply yet if ( cls.state != CA_CONNECTING && cls.state != CA_CHALLENGING ) { return; } if ( cls.realtime - clc.connectTime < RETRANSMIT_TIMEOUT ) { return; } clc.connectTime = cls.realtime; // for retransmit requests clc.connectPacketCount++; switch ( cls.state ) { case CA_CONNECTING: // requesting a challenge if ( !Sys_IsLANAddress( clc.serverAddress ) ) { CL_RequestAuthorization(); } NET_OutOfBandPrint(NS_CLIENT, clc.serverAddress, "getchallenge"); break; case CA_CHALLENGING: // sending back the challenge port = Cvar_VariableValue ("net_qport"); Q_strncpyz( info, Cvar_InfoString( CVAR_USERINFO ), sizeof( info ) ); Info_SetValueForKey( info, "protocol", va("%i", PROTOCOL_VERSION ) ); Info_SetValueForKey( info, "qport", va("%i", port ) ); Info_SetValueForKey( info, "challenge", va("%i", clc.challenge ) ); strcpy(data, "connect "); // TTimo adding " " around the userinfo string to avoid truncated userinfo on the server // (Com_TokenizeString tokenizes around spaces) data[8] = '"'; for(i=0;iadr.type = NA_IP; server->adr.ip[0] = address->ip[0]; server->adr.ip[1] = address->ip[1]; server->adr.ip[2] = address->ip[2]; server->adr.ip[3] = address->ip[3]; server->adr.port = address->port; server->clients = 0; server->hostName[0] = '\0'; server->mapName[0] = '\0'; server->maxClients = 0; server->maxPing = 0; server->minPing = 0; server->ping = -1; server->game[0] = '\0'; server->gameType = 0; server->netType = 0; } #define MAX_SERVERSPERPACKET 256 /* =================== CL_ServersResponsePacket =================== */ void CL_ServersResponsePacket( netadr_t from, msg_t *msg ) { int i, count, max, total; serverAddress_t addresses[MAX_SERVERSPERPACKET]; int numservers; byte* buffptr; byte* buffend; Com_Printf("CL_ServersResponsePacket\n"); if (cls.numglobalservers == -1) { // state to detect lack of servers or lack of response cls.numglobalservers = 0; cls.numGlobalServerAddresses = 0; } if (cls.nummplayerservers == -1) { cls.nummplayerservers = 0; } // parse through server response string numservers = 0; buffptr = msg->data; buffend = buffptr + msg->cursize; while (buffptr+1 < buffend) { // advance to initial token do { if (*buffptr++ == '\\') break; } while (buffptr < buffend); if ( buffptr >= buffend - 6 ) { break; } // parse out ip addresses[numservers].ip[0] = *buffptr++; addresses[numservers].ip[1] = *buffptr++; addresses[numservers].ip[2] = *buffptr++; addresses[numservers].ip[3] = *buffptr++; // parse out port addresses[numservers].port = (*buffptr++)<<8; addresses[numservers].port += *buffptr++; addresses[numservers].port = BigShort( addresses[numservers].port ); // syntax check if (*buffptr != '\\') { break; } Com_DPrintf( "server: %d ip: %d.%d.%d.%d:%d\n",numservers, addresses[numservers].ip[0], addresses[numservers].ip[1], addresses[numservers].ip[2], addresses[numservers].ip[3], addresses[numservers].port ); numservers++; if (numservers >= MAX_SERVERSPERPACKET) { break; } // parse out EOT if (buffptr[1] == 'E' && buffptr[2] == 'O' && buffptr[3] == 'T') { break; } } if (cls.masterNum == 0) { count = cls.numglobalservers; max = MAX_GLOBAL_SERVERS; } else { count = cls.nummplayerservers; max = MAX_OTHER_SERVERS; } for (i = 0; i < numservers && count < max; i++) { // build net address serverInfo_t *server = (cls.masterNum == 0) ? &cls.globalServers[count] : &cls.mplayerServers[count]; CL_InitServerInfo( server, &addresses[i] ); // advance to next slot count++; } // if getting the global list if (cls.masterNum == 0) { if ( cls.numGlobalServerAddresses < MAX_GLOBAL_SERVERS ) { // if we couldn't store the servers in the main list anymore for (; i < numservers && count >= max; i++) { serverAddress_t *addr; // just store the addresses in an additional list addr = &cls.globalServerAddresses[cls.numGlobalServerAddresses++]; addr->ip[0] = addresses[i].ip[0]; addr->ip[1] = addresses[i].ip[1]; addr->ip[2] = addresses[i].ip[2]; addr->ip[3] = addresses[i].ip[3]; addr->port = addresses[i].port; } } } if (cls.masterNum == 0) { cls.numglobalservers = count; total = count + cls.numGlobalServerAddresses; } else { cls.nummplayerservers = count; total = count; } Com_Printf("%d servers parsed (total %d)\n", numservers, total); } /* ================= CL_ConnectionlessPacket Responses to broadcasts, etc ================= */ void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) { char *s; char *c; MSG_BeginReadingOOB( msg ); MSG_ReadLong( msg ); // skip the -1 s = MSG_ReadStringLine( msg ); Cmd_TokenizeString( s ); c = Cmd_Argv(0); Com_DPrintf ("CL packet %s: %s\n", NET_AdrToString(from), c); // challenge from the server we are connecting to if ( !Q_stricmp(c, "challengeResponse") ) { if ( cls.state != CA_CONNECTING ) { Com_Printf( "Unwanted challenge response received. Ignored.\n" ); } else { // start sending challenge repsonse instead of challenge request packets clc.challenge = atoi(Cmd_Argv(1)); cls.state = CA_CHALLENGING; clc.connectPacketCount = 0; clc.connectTime = -99999; // take this address as the new server address. This allows // a server proxy to hand off connections to multiple servers clc.serverAddress = from; Com_DPrintf ("challengeResponse: %d\n", clc.challenge); } return; } // server connection if ( !Q_stricmp(c, "connectResponse") ) { if ( cls.state >= CA_CONNECTED ) { Com_Printf ("Dup connect received. Ignored.\n"); return; } if ( cls.state != CA_CHALLENGING ) { Com_Printf ("connectResponse packet while not connecting. Ignored.\n"); return; } if ( !NET_CompareBaseAdr( from, clc.serverAddress ) ) { Com_Printf( "connectResponse from a different address. Ignored.\n" ); Com_Printf( "%s should have been %s\n", NET_AdrToString( from ), NET_AdrToString( clc.serverAddress ) ); return; } Netchan_Setup (NS_CLIENT, &clc.netchan, from, Cvar_VariableValue( "net_qport" ) ); cls.state = CA_CONNECTED; clc.lastPacketSentTime = -9999; // send first packet immediately return; } // server responding to an info broadcast if ( !Q_stricmp(c, "infoResponse") ) { CL_ServerInfoPacket( from, msg ); return; } // server responding to a get playerlist if ( !Q_stricmp(c, "statusResponse") ) { CL_ServerStatusResponse( from, msg ); return; } // a disconnect message from the server, which will happen if the server // dropped the connection but it is still getting packets from us if (!Q_stricmp(c, "disconnect")) { CL_DisconnectPacket( from ); return; } // echo request from server if ( !Q_stricmp(c, "echo") ) { NET_OutOfBandPrint( NS_CLIENT, from, "%s", Cmd_Argv(1) ); return; } // cd check if ( !Q_stricmp(c, "keyAuthorize") ) { // we don't use these now, so dump them on the floor return; } // global MOTD from id if ( !Q_stricmp(c, "motd") ) { CL_MotdPacket( from ); return; } // echo request from server if ( !Q_stricmp(c, "print") ) { s = MSG_ReadString( msg ); Q_strncpyz( clc.serverMessage, s, sizeof( clc.serverMessage ) ); Com_Printf( "%s", s ); return; } // echo request from server if ( !Q_strncmp(c, "getserversResponse", 18) ) { CL_ServersResponsePacket( from, msg ); return; } Com_DPrintf ("Unknown connectionless packet command.\n"); } /* ================= CL_PacketEvent A packet has arrived from the main event loop ================= */ void CL_PacketEvent( netadr_t from, msg_t *msg ) { int headerBytes; clc.lastPacketTime = cls.realtime; if ( msg->cursize >= 4 && *(int *)msg->data == -1 ) { CL_ConnectionlessPacket( from, msg ); return; } if ( cls.state < CA_CONNECTED ) { return; // can't be a valid sequenced packet } if ( msg->cursize < 4 ) { Com_Printf ("%s: Runt packet\n",NET_AdrToString( from )); return; } // // packet from server // if ( !NET_CompareAdr( from, clc.netchan.remoteAddress ) ) { Com_DPrintf ("%s:sequenced packet without connection\n" ,NET_AdrToString( from ) ); // FIXME: send a client disconnect? return; } if (!CL_Netchan_Process( &clc.netchan, msg) ) { return; // out of order, duplicated, etc } // the header is different lengths for reliable and unreliable messages headerBytes = msg->readcount; // track the last message received so it can be returned in // client messages, allowing the server to detect a dropped // gamestate clc.serverMessageSequence = LittleLong( *(int *)msg->data ); clc.lastPacketTime = cls.realtime; CL_ParseServerMessage( msg ); // // we don't know if it is ok to save a demo message until // after we have parsed the frame // if ( clc.demorecording && !clc.demowaiting ) { CL_WriteDemoMessage( msg, headerBytes ); } } /* ================== CL_CheckTimeout ================== */ void CL_CheckTimeout( void ) { // // check timeout // if ( ( !cl_paused->integer || !sv_paused->integer ) && cls.state >= CA_CONNECTED && cls.state != CA_CINEMATIC && cls.realtime - clc.lastPacketTime > cl_timeout->value*1000) { if (++cl.timeoutcount > 5) { // timeoutcount saves debugger Com_Printf ("\nServer connection timed out.\n"); CL_Disconnect( qtrue ); return; } } else { cl.timeoutcount = 0; } } //============================================================================ /* ================== CL_CheckUserinfo ================== */ void CL_CheckUserinfo( void ) { // don't add reliable commands when not yet connected if ( cls.state < CA_CHALLENGING ) { return; } // don't overflow the reliable command buffer when paused if ( cl_paused->integer ) { return; } // send a reliable userinfo update if needed if ( cvar_modifiedFlags & CVAR_USERINFO ) { cvar_modifiedFlags &= ~CVAR_USERINFO; CL_AddReliableCommand( va("userinfo \"%s\"", Cvar_InfoString( CVAR_USERINFO ) ) ); } } /* ================== CL_Frame ================== */ void CL_Frame ( int msec ) { if ( !com_cl_running->integer ) { return; } if ( cls.cddialog ) { // bring up the cd error dialog if needed cls.cddialog = qfalse; VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_NEED_CD ); } else if ( cls.state == CA_DISCONNECTED && !( cls.keyCatchers & KEYCATCH_UI ) && !com_sv_running->integer ) { // if disconnected, bring up the menu S_StopAllSounds(); VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_MAIN ); } // if recording an avi, lock to a fixed fps if ( cl_avidemo->integer && msec) { // save the current screen if ( cls.state == CA_ACTIVE || cl_forceavidemo->integer) { Cbuf_ExecuteText( EXEC_NOW, "screenshot silent\n" ); } // fixed time for next frame' msec = (1000 / cl_avidemo->integer) * com_timescale->value; if (msec == 0) { msec = 1; } } // save the msec before checking pause cls.realFrametime = msec; // decide the simulation time cls.frametime = msec; cls.realtime += cls.frametime; if ( cl_timegraph->integer ) { SCR_DebugGraph ( cls.realFrametime * 0.25, 0 ); } // see if we need to update any userinfo CL_CheckUserinfo(); // if we haven't gotten a packet in a long time, // drop the connection CL_CheckTimeout(); // send intentions now CL_SendCmd(); // resend a connection request if necessary CL_CheckForResend(); // decide on the serverTime to render CL_SetCGameTime(); // update the screen SCR_UpdateScreen(); // update audio S_Update(); // advance local effects for next frame SCR_RunCinematic(); Con_RunConsole(); cls.framecount++; } //============================================================================ /* ================ CL_RefPrintf DLL glue ================ */ void QDECL CL_RefPrintf( int print_level, const char *fmt, ...) { va_list argptr; char msg[MAXPRINTMSG]; va_start (argptr,fmt); Q_vsnprintf (msg, sizeof(msg), fmt, argptr); va_end (argptr); if ( print_level == PRINT_ALL ) { Com_Printf ("%s", msg); } else if ( print_level == PRINT_WARNING ) { Com_Printf (S_COLOR_YELLOW "%s", msg); // yellow } else if ( print_level == PRINT_DEVELOPER ) { Com_DPrintf (S_COLOR_RED "%s", msg); // red } } /* ============ CL_ShutdownRef ============ */ void CL_ShutdownRef( void ) { if ( !re.Shutdown ) { return; } re.Shutdown( qtrue ); Com_Memset( &re, 0, sizeof( re ) ); } /* ============ CL_InitRenderer ============ */ void CL_InitRenderer( void ) { // this sets up the renderer and calls R_Init re.BeginRegistration( &cls.glconfig ); // load character sets cls.charSetShader = re.RegisterShader( "gfx/2d/bigchars" ); cls.whiteShader = re.RegisterShader( "white" ); cls.consoleShader = re.RegisterShader( "console" ); g_console_field_width = cls.glconfig.vidWidth / SMALLCHAR_WIDTH - 2; g_consoleField.widthInChars = g_console_field_width; } /* ============================ CL_StartHunkUsers After the server has cleared the hunk, these will need to be restarted This is the only place that any of these functions are called from ============================ */ void CL_StartHunkUsers( void ) { if (!com_cl_running) { return; } if ( !com_cl_running->integer ) { return; } if ( !cls.rendererStarted ) { cls.rendererStarted = qtrue; CL_InitRenderer(); } if ( !cls.soundStarted ) { cls.soundStarted = qtrue; S_Init(); } if ( !cls.soundRegistered ) { cls.soundRegistered = qtrue; S_BeginRegistration(); } if ( !cls.uiStarted ) { cls.uiStarted = qtrue; CL_InitUI(); } } /* ============ CL_RefMalloc ============ */ void *CL_RefMalloc( int size ) { return Z_TagMalloc( size, TAG_RENDERER ); } int CL_ScaledMilliseconds(void) { return Sys_Milliseconds()*com_timescale->value; } /* ============ CL_InitRef ============ */ void CL_InitRef( void ) { refimport_t ri; refexport_t *ret; Com_Printf( "----- Initializing Renderer ----\n" ); ri.Cmd_AddCommand = Cmd_AddCommand; ri.Cmd_RemoveCommand = Cmd_RemoveCommand; ri.Cmd_Argc = Cmd_Argc; ri.Cmd_Argv = Cmd_Argv; ri.Cmd_ExecuteText = Cbuf_ExecuteText; ri.Printf = CL_RefPrintf; ri.Error = Com_Error; ri.Milliseconds = CL_ScaledMilliseconds; ri.Malloc = CL_RefMalloc; ri.Free = Z_Free; #ifdef HUNK_DEBUG ri.Hunk_AllocDebug = Hunk_AllocDebug; #else ri.Hunk_Alloc = Hunk_Alloc; #endif ri.Hunk_AllocateTempMemory = Hunk_AllocateTempMemory; ri.Hunk_FreeTempMemory = Hunk_FreeTempMemory; ri.CM_DrawDebugSurface = CM_DrawDebugSurface; ri.FS_ReadFile = FS_ReadFile; ri.FS_FreeFile = FS_FreeFile; ri.FS_WriteFile = FS_WriteFile; ri.FS_FreeFileList = FS_FreeFileList; ri.FS_ListFiles = FS_ListFiles; ri.FS_FileIsInPAK = FS_FileIsInPAK; ri.FS_FileExists = FS_FileExists; ri.Cvar_Get = Cvar_Get; ri.Cvar_Set = Cvar_Set; // cinematic stuff ri.CIN_UploadCinematic = CIN_UploadCinematic; ri.CIN_PlayCinematic = CIN_PlayCinematic; ri.CIN_RunCinematic = CIN_RunCinematic; ret = GetRefAPI( REF_API_VERSION, &ri ); #if defined __USEA3D && defined __A3D_GEOM hA3Dg_ExportRenderGeom (ret); #endif Com_Printf( "-------------------------------\n"); if ( !ret ) { Com_Error (ERR_FATAL, "Couldn't initialize refresh" ); } re = *ret; // unpause so the cgame definately gets a snapshot and renders a frame Cvar_Set( "cl_paused", "0" ); } //=========================================================================================== void CL_SetModel_f( void ) { char *arg; char name[256]; arg = Cmd_Argv( 1 ); if (arg[0]) { Cvar_Set( "model", arg ); Cvar_Set( "headmodel", arg ); } else { Cvar_VariableStringBuffer( "model", name, sizeof(name) ); Com_Printf("model is set to %s\n", name); } } /* ==================== CL_Init ==================== */ void CL_Init( void ) { Com_Printf( "----- Client Initialization -----\n" ); Con_Init (); CL_ClearState (); cls.state = CA_DISCONNECTED; // no longer CA_UNINITIALIZED cls.realtime = 0; CL_InitInput (); // // register our variables // cl_noprint = Cvar_Get( "cl_noprint", "0", 0 ); cl_motd = Cvar_Get ("cl_motd", "1", 0); cl_timeout = Cvar_Get ("cl_timeout", "200", 0); cl_timeNudge = Cvar_Get ("cl_timeNudge", "0", CVAR_TEMP ); cl_shownet = Cvar_Get ("cl_shownet", "0", CVAR_TEMP ); cl_showSend = Cvar_Get ("cl_showSend", "0", CVAR_TEMP ); cl_showTimeDelta = Cvar_Get ("cl_showTimeDelta", "0", CVAR_TEMP ); cl_freezeDemo = Cvar_Get ("cl_freezeDemo", "0", CVAR_TEMP ); rcon_client_password = Cvar_Get ("rconPassword", "", CVAR_TEMP ); cl_activeAction = Cvar_Get( "activeAction", "", CVAR_TEMP ); cl_timedemo = Cvar_Get ("timedemo", "0", 0); cl_avidemo = Cvar_Get ("cl_avidemo", "0", 0); cl_forceavidemo = Cvar_Get ("cl_forceavidemo", "0", 0); rconAddress = Cvar_Get ("rconAddress", "", 0); cl_yawspeed = Cvar_Get ("cl_yawspeed", "140", CVAR_ARCHIVE); cl_pitchspeed = Cvar_Get ("cl_pitchspeed", "140", CVAR_ARCHIVE); cl_anglespeedkey = Cvar_Get ("cl_anglespeedkey", "1.5", 0); cl_maxpackets = Cvar_Get ("cl_maxpackets", "30", CVAR_ARCHIVE ); cl_packetdup = Cvar_Get ("cl_packetdup", "1", CVAR_ARCHIVE ); cl_run = Cvar_Get ("cl_run", "1", CVAR_ARCHIVE); cl_sensitivity = Cvar_Get ("sensitivity", "5", CVAR_ARCHIVE); cl_mouseAccel = Cvar_Get ("cl_mouseAccel", "0", CVAR_ARCHIVE); cl_freelook = Cvar_Get( "cl_freelook", "1", CVAR_ARCHIVE ); cl_showMouseRate = Cvar_Get ("cl_showmouserate", "0", 0); cl_allowDownload = Cvar_Get ("cl_allowDownload", "0", CVAR_ARCHIVE); cl_conXOffset = Cvar_Get ("cl_conXOffset", "0", 0); #ifdef MACOS_X // In game video is REALLY slow in Mac OS X right now due to driver slowness cl_inGameVideo = Cvar_Get ("r_inGameVideo", "0", CVAR_ARCHIVE); #else cl_inGameVideo = Cvar_Get ("r_inGameVideo", "1", CVAR_ARCHIVE); #endif cl_serverStatusResendTime = Cvar_Get ("cl_serverStatusResendTime", "750", 0); // init autoswitch so the ui will have it correctly even // if the cgame hasn't been started Cvar_Get ("cg_autoswitch", "1", CVAR_ARCHIVE); m_pitch = Cvar_Get ("m_pitch", "0.022", CVAR_ARCHIVE); m_yaw = Cvar_Get ("m_yaw", "0.022", CVAR_ARCHIVE); m_forward = Cvar_Get ("m_forward", "0.25", CVAR_ARCHIVE); m_side = Cvar_Get ("m_side", "0.25", CVAR_ARCHIVE); #ifdef MACOS_X // Input is jittery on OS X w/o this m_filter = Cvar_Get ("m_filter", "1", CVAR_ARCHIVE); #else m_filter = Cvar_Get ("m_filter", "0", CVAR_ARCHIVE); #endif cl_motdString = Cvar_Get( "cl_motdString", "", CVAR_ROM ); Cvar_Get( "cl_maxPing", "800", CVAR_ARCHIVE ); // userinfo Cvar_Get ("name", "UnnamedPlayer", CVAR_USERINFO | CVAR_ARCHIVE ); Cvar_Get ("rate", "3000", CVAR_USERINFO | CVAR_ARCHIVE ); Cvar_Get ("snaps", "20", CVAR_USERINFO | CVAR_ARCHIVE ); Cvar_Get ("model", "sarge", CVAR_USERINFO | CVAR_ARCHIVE ); Cvar_Get ("headmodel", "sarge", CVAR_USERINFO | CVAR_ARCHIVE ); Cvar_Get ("team_model", "james", CVAR_USERINFO | CVAR_ARCHIVE ); Cvar_Get ("team_headmodel", "*james", CVAR_USERINFO | CVAR_ARCHIVE ); Cvar_Get ("g_redTeam", "Stroggs", CVAR_SERVERINFO | CVAR_ARCHIVE); Cvar_Get ("g_blueTeam", "Pagans", CVAR_SERVERINFO | CVAR_ARCHIVE); Cvar_Get ("color1", "4", CVAR_USERINFO | CVAR_ARCHIVE ); Cvar_Get ("color2", "5", CVAR_USERINFO | CVAR_ARCHIVE ); Cvar_Get ("handicap", "100", CVAR_USERINFO | CVAR_ARCHIVE ); Cvar_Get ("teamtask", "0", CVAR_USERINFO ); Cvar_Get ("sex", "male", CVAR_USERINFO | CVAR_ARCHIVE ); Cvar_Get ("cl_anonymous", "0", CVAR_USERINFO | CVAR_ARCHIVE ); Cvar_Get ("password", "", CVAR_USERINFO); Cvar_Get ("cg_predictItems", "1", CVAR_USERINFO | CVAR_ARCHIVE ); // cgame might not be initialized before menu is used Cvar_Get ("cg_viewsize", "100", CVAR_ARCHIVE ); // // register our commands // Cmd_AddCommand ("cmd", CL_ForwardToServer_f); Cmd_AddCommand ("configstrings", CL_Configstrings_f); Cmd_AddCommand ("clientinfo", CL_Clientinfo_f); Cmd_AddCommand ("snd_restart", CL_Snd_Restart_f); Cmd_AddCommand ("vid_restart", CL_Vid_Restart_f); Cmd_AddCommand ("disconnect", CL_Disconnect_f); Cmd_AddCommand ("record", CL_Record_f); Cmd_AddCommand ("demo", CL_PlayDemo_f); Cmd_AddCommand ("cinematic", CL_PlayCinematic_f); Cmd_AddCommand ("stoprecord", CL_StopRecord_f); Cmd_AddCommand ("connect", CL_Connect_f); Cmd_AddCommand ("reconnect", CL_Reconnect_f); Cmd_AddCommand ("localservers", CL_LocalServers_f); Cmd_AddCommand ("globalservers", CL_GlobalServers_f); Cmd_AddCommand ("rcon", CL_Rcon_f); Cmd_AddCommand ("setenv", CL_Setenv_f ); Cmd_AddCommand ("ping", CL_Ping_f ); Cmd_AddCommand ("serverstatus", CL_ServerStatus_f ); Cmd_AddCommand ("showip", CL_ShowIP_f ); Cmd_AddCommand ("fs_openedList", CL_OpenedPK3List_f ); Cmd_AddCommand ("fs_referencedList", CL_ReferencedPK3List_f ); Cmd_AddCommand ("model", CL_SetModel_f ); CL_InitRef(); SCR_Init (); Cbuf_Execute (); Cvar_Set( "cl_running", "1" ); Com_Printf( "----- Client Initialization Complete -----\n" ); } /* =============== CL_Shutdown =============== */ void CL_Shutdown( void ) { static qboolean recursive = qfalse; Com_Printf( "----- CL_Shutdown -----\n" ); if ( recursive ) { printf ("recursive shutdown\n"); return; } recursive = qtrue; CL_Disconnect( qtrue ); S_Shutdown(); CL_ShutdownRef(); CL_ShutdownUI(); Cmd_RemoveCommand ("cmd"); Cmd_RemoveCommand ("configstrings"); Cmd_RemoveCommand ("userinfo"); Cmd_RemoveCommand ("snd_restart"); Cmd_RemoveCommand ("vid_restart"); Cmd_RemoveCommand ("disconnect"); Cmd_RemoveCommand ("record"); Cmd_RemoveCommand ("demo"); Cmd_RemoveCommand ("cinematic"); Cmd_RemoveCommand ("stoprecord"); Cmd_RemoveCommand ("connect"); Cmd_RemoveCommand ("localservers"); Cmd_RemoveCommand ("globalservers"); Cmd_RemoveCommand ("rcon"); Cmd_RemoveCommand ("setenv"); Cmd_RemoveCommand ("ping"); Cmd_RemoveCommand ("serverstatus"); Cmd_RemoveCommand ("showip"); Cmd_RemoveCommand ("model"); Cvar_Set( "cl_running", "0" ); recursive = qfalse; Com_Memset( &cls, 0, sizeof( cls ) ); Com_Printf( "-----------------------\n" ); } static void CL_SetServerInfo(serverInfo_t *server, const char *info, int ping) { if (server) { if (info) { server->clients = atoi(Info_ValueForKey(info, "clients")); Q_strncpyz(server->hostName,Info_ValueForKey(info, "hostname"), MAX_NAME_LENGTH); Q_strncpyz(server->mapName, Info_ValueForKey(info, "mapname"), MAX_NAME_LENGTH); server->maxClients = atoi(Info_ValueForKey(info, "sv_maxclients")); Q_strncpyz(server->game,Info_ValueForKey(info, "game"), MAX_NAME_LENGTH); server->gameType = atoi(Info_ValueForKey(info, "gametype")); server->netType = atoi(Info_ValueForKey(info, "nettype")); server->minPing = atoi(Info_ValueForKey(info, "minping")); server->maxPing = atoi(Info_ValueForKey(info, "maxping")); server->punkbuster = atoi(Info_ValueForKey(info, "punkbuster")); } server->ping = ping; } } static void CL_SetServerInfoByAddress(netadr_t from, const char *info, int ping) { int i; for (i = 0; i < MAX_OTHER_SERVERS; i++) { if (NET_CompareAdr(from, cls.localServers[i].adr)) { CL_SetServerInfo(&cls.localServers[i], info, ping); } } for (i = 0; i < MAX_OTHER_SERVERS; i++) { if (NET_CompareAdr(from, cls.mplayerServers[i].adr)) { CL_SetServerInfo(&cls.mplayerServers[i], info, ping); } } for (i = 0; i < MAX_GLOBAL_SERVERS; i++) { if (NET_CompareAdr(from, cls.globalServers[i].adr)) { CL_SetServerInfo(&cls.globalServers[i], info, ping); } } for (i = 0; i < MAX_OTHER_SERVERS; i++) { if (NET_CompareAdr(from, cls.favoriteServers[i].adr)) { CL_SetServerInfo(&cls.favoriteServers[i], info, ping); } } } /* =================== CL_ServerInfoPacket =================== */ void CL_ServerInfoPacket( netadr_t from, msg_t *msg ) { int i, type; char info[MAX_INFO_STRING]; char* str; char *infoString; int prot; infoString = MSG_ReadString( msg ); // if this isn't the correct protocol version, ignore it prot = atoi( Info_ValueForKey( infoString, "protocol" ) ); if ( prot != PROTOCOL_VERSION ) { Com_DPrintf( "Different protocol info packet: %s\n", infoString ); return; } // iterate servers waiting for ping response for (i=0; iretrieved = qtrue; return qfalse; } // if this server status request has the same address if ( NET_CompareAdr( to, serverStatus->address) ) { // if we recieved an response for this server status request if (!serverStatus->pending) { Q_strncpyz(serverStatusString, serverStatus->string, maxLen); serverStatus->retrieved = qtrue; serverStatus->startTime = 0; return qtrue; } // resend the request regularly else if ( serverStatus->startTime < Com_Milliseconds() - cl_serverStatusResendTime->integer ) { serverStatus->print = qfalse; serverStatus->pending = qtrue; serverStatus->retrieved = qfalse; serverStatus->time = 0; serverStatus->startTime = Com_Milliseconds(); NET_OutOfBandPrint( NS_CLIENT, to, "getstatus" ); return qfalse; } } // if retrieved else if ( serverStatus->retrieved ) { serverStatus->address = to; serverStatus->print = qfalse; serverStatus->pending = qtrue; serverStatus->retrieved = qfalse; serverStatus->startTime = Com_Milliseconds(); serverStatus->time = 0; NET_OutOfBandPrint( NS_CLIENT, to, "getstatus" ); return qfalse; } return qfalse; } /* =================== CL_ServerStatusResponse =================== */ void CL_ServerStatusResponse( netadr_t from, msg_t *msg ) { char *s; char info[MAX_INFO_STRING]; int i, l, score, ping; int len; serverStatus_t *serverStatus; serverStatus = NULL; for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) { if ( NET_CompareAdr( from, cl_serverStatusList[i].address ) ) { serverStatus = &cl_serverStatusList[i]; break; } } // if we didn't request this server status if (!serverStatus) { return; } s = MSG_ReadStringLine( msg ); len = 0; Com_sprintf(&serverStatus->string[len], sizeof(serverStatus->string)-len, "%s", s); if (serverStatus->print) { Com_Printf("Server settings:\n"); // print cvars while (*s) { for (i = 0; i < 2 && *s; i++) { if (*s == '\\') s++; l = 0; while (*s) { info[l++] = *s; if (l >= MAX_INFO_STRING-1) break; s++; if (*s == '\\') { break; } } info[l] = '\0'; if (i) { Com_Printf("%s\n", info); } else { Com_Printf("%-24s", info); } } } } len = (int)strlen(serverStatus->string); Com_sprintf(&serverStatus->string[len], sizeof(serverStatus->string)-len, "\\"); if (serverStatus->print) { Com_Printf("\nPlayers:\n"); Com_Printf("num: score: ping: name:\n"); } for (i = 0, s = MSG_ReadStringLine( msg ); *s; s = MSG_ReadStringLine( msg ), i++) { len = (int)strlen(serverStatus->string); Com_sprintf(&serverStatus->string[len], sizeof(serverStatus->string)-len, "\\%s", s); if (serverStatus->print) { score = ping = 0; sscanf(s, "%d %d", &score, &ping); s = strchr(s, ' '); if (s) s = strchr(s+1, ' '); if (s) s++; else s = "unknown"; Com_Printf("%-2d %-3d %-3d %s\n", i, score, ping, s ); } } len = (int)strlen(serverStatus->string); Com_sprintf(&serverStatus->string[len], sizeof(serverStatus->string)-len, "\\"); serverStatus->time = Com_Milliseconds(); serverStatus->address = from; serverStatus->pending = qfalse; if (serverStatus->print) { serverStatus->retrieved = qtrue; } } /* ================== CL_LocalServers_f ================== */ void CL_LocalServers_f( void ) { char *message; int i, j; netadr_t to; Com_Printf( "Scanning for servers on the local network...\n"); // reset the list, waiting for response cls.numlocalservers = 0; cls.pingUpdateSource = AS_LOCAL; for (i = 0; i < MAX_OTHER_SERVERS; i++) { qboolean b = cls.localServers[i].visible; Com_Memset(&cls.localServers[i], 0, sizeof(cls.localServers[i])); cls.localServers[i].visible = b; } Com_Memset( &to, 0, sizeof( to ) ); // The 'xxx' in the message is a challenge that will be echoed back // by the server. We don't care about that here, but master servers // can use that to prevent spoofed server responses from invalid ip message = "\377\377\377\377getinfo xxx"; // send each message twice in case one is dropped for ( i = 0 ; i < 2 ; i++ ) { // send a broadcast packet on each server port // we support multiple server ports so a single machine // can nicely run multiple servers for ( j = 0 ; j < NUM_SERVER_PORTS ; j++ ) { to.port = BigShort( (short)(PORT_SERVER + j) ); to.type = NA_BROADCAST; NET_SendPacket( NS_CLIENT, (int)strlen( message ), message, to ); to.type = NA_BROADCAST_IPX; NET_SendPacket( NS_CLIENT, (int)strlen( message ), message, to ); } } } /* ================== CL_GlobalServers_f ================== */ void CL_GlobalServers_f( void ) { netadr_t to; int i; int count; char *buffptr; char command[1024]; if ( Cmd_Argc() < 3) { Com_Printf( "usage: globalservers [keywords]\n"); return; } cls.masterNum = atoi( Cmd_Argv(1) ); Com_Printf( "Requesting servers from the master...\n"); // reset the list, waiting for response // -1 is used to distinguish a "no response" if( cls.masterNum == 1 ) { NET_StringToAdr( MASTER_SERVER_NAME, &to ); cls.nummplayerservers = -1; cls.pingUpdateSource = AS_MPLAYER; } else { NET_StringToAdr( MASTER_SERVER_NAME, &to ); cls.numglobalservers = -1; cls.pingUpdateSource = AS_GLOBAL; } to.type = NA_IP; to.port = BigShort(PORT_MASTER); sprintf( command, "getservers %s", Cmd_Argv(2) ); // tack on keywords buffptr = command + (int)strlen( command ); count = Cmd_Argc(); for (i=3; i= MAX_PINGREQUESTS) return; cl_pinglist[n].adr.port = 0; } /* ================== CL_GetPingQueueCount ================== */ int CL_GetPingQueueCount( void ) { int i; int count; ping_t* pingptr; count = 0; pingptr = cl_pinglist; for (i=0; iadr.port) { count++; } } return (count); } /* ================== CL_GetFreePing ================== */ ping_t* CL_GetFreePing( void ) { ping_t* pingptr; ping_t* best; int oldest; int i; int time; pingptr = cl_pinglist; for (i=0; iadr.port) { if (!pingptr->time) { if (cls.realtime - pingptr->start < 500) { // still waiting for response continue; } } else if (pingptr->time < 500) { // results have not been queried continue; } } // clear it pingptr->adr.port = 0; return (pingptr); } // use oldest entry pingptr = cl_pinglist; best = cl_pinglist; oldest = INT_MIN; for (i=0; istart; if (time > oldest) { oldest = time; best = pingptr; } } return (best); } /* ================== CL_Ping_f ================== */ void CL_Ping_f( void ) { netadr_t to; ping_t* pingptr; char* server; if ( Cmd_Argc() != 2 ) { Com_Printf( "usage: ping [server]\n"); return; } Com_Memset( &to, 0, sizeof(netadr_t) ); server = Cmd_Argv(1); if ( !NET_StringToAdr( server, &to ) ) { return; } pingptr = CL_GetFreePing(); memcpy( &pingptr->adr, &to, sizeof (netadr_t) ); pingptr->start = cls.realtime; pingptr->time = 0; CL_SetServerInfoByAddress(pingptr->adr, NULL, 0); NET_OutOfBandPrint( NS_CLIENT, to, "getinfo xxx" ); } /* ================== CL_UpdateVisiblePings_f ================== */ qboolean CL_UpdateVisiblePings_f(int source) { int slots, i; char buff[MAX_STRING_CHARS]; int pingTime; int max; qboolean status = qfalse; if (source < 0 || source > AS_FAVORITES) { return qfalse; } cls.pingUpdateSource = source; slots = CL_GetPingQueueCount(); if (slots < MAX_PINGREQUESTS) { serverInfo_t *server = NULL; max = (source == AS_GLOBAL) ? MAX_GLOBAL_SERVERS : MAX_OTHER_SERVERS; switch (source) { case AS_LOCAL : server = &cls.localServers[0]; max = cls.numlocalservers; break; case AS_MPLAYER : server = &cls.mplayerServers[0]; max = cls.nummplayerservers; break; case AS_GLOBAL : server = &cls.globalServers[0]; max = cls.numglobalservers; break; case AS_FAVORITES : server = &cls.favoriteServers[0]; max = cls.numfavoriteservers; break; } for (i = 0; i < max; i++) { if (server[i].visible) { if (server[i].ping == -1) { int j; if (slots >= MAX_PINGREQUESTS) { break; } for (j = 0; j < MAX_PINGREQUESTS; j++) { if (!cl_pinglist[j].adr.port) { continue; } if (NET_CompareAdr( cl_pinglist[j].adr, server[i].adr)) { // already on the list break; } } if (j >= MAX_PINGREQUESTS) { status = qtrue; for (j = 0; j < MAX_PINGREQUESTS; j++) { if (!cl_pinglist[j].adr.port) { break; } } memcpy(&cl_pinglist[j].adr, &server[i].adr, sizeof(netadr_t)); cl_pinglist[j].start = cls.realtime; cl_pinglist[j].time = 0; NET_OutOfBandPrint( NS_CLIENT, cl_pinglist[j].adr, "getinfo xxx" ); slots++; } } // if the server has a ping higher than cl_maxPing or // the ping packet got lost else if (server[i].ping == 0) { // if we are updating global servers if (source == AS_GLOBAL) { // if ( cls.numGlobalServerAddresses > 0 ) { // overwrite this server with one from the additional global servers cls.numGlobalServerAddresses--; CL_InitServerInfo(&server[i], &cls.globalServerAddresses[cls.numGlobalServerAddresses]); // NOTE: the server[i].visible flag stays untouched } } } } } } if (slots) { status = qtrue; } for (i = 0; i < MAX_PINGREQUESTS; i++) { if (!cl_pinglist[i].adr.port) { continue; } CL_GetPing( i, buff, MAX_STRING_CHARS, &pingTime ); if (pingTime != 0) { CL_ClearPing(i); status = qtrue; } } return status; } /* ================== CL_ServerStatus_f ================== */ void CL_ServerStatus_f(void) { netadr_t to; char *server; serverStatus_t *serverStatus; Com_Memset( &to, 0, sizeof(netadr_t) ); if ( Cmd_Argc() != 2 ) { if ( cls.state != CA_ACTIVE || clc.demoplaying ) { Com_Printf ("Not connected to a server.\n"); Com_Printf( "Usage: serverstatus [server]\n"); return; } server = cls.servername; } else { server = Cmd_Argv(1); } if ( !NET_StringToAdr( server, &to ) ) { return; } NET_OutOfBandPrint( NS_CLIENT, to, "getstatus" ); serverStatus = CL_GetServerStatus( to ); serverStatus->address = to; serverStatus->print = qtrue; serverStatus->pending = qtrue; } /* ================== CL_ShowIP_f ================== */ void CL_ShowIP_f(void) { Sys_ShowIP(); } /* ================= bool CL_CDKeyValidate ================= */ qboolean CL_CDKeyValidate( const char *key, const char *checksum ) { char ch; byte sum; char chs[3]; int i, len; len = (int)strlen(key); if( len != CDKEY_LEN ) { return qfalse; } if( checksum && (int)strlen( checksum ) != CDCHKSUM_LEN ) { return qfalse; } sum = 0; // for loop gets rid of conditional assignment warning for (i = 0; i < len; i++) { ch = *key++; if (ch>='a' && ch<='z') { ch -= 32; } switch( ch ) { case '2': case '3': case '7': case 'A': case 'B': case 'C': case 'D': case 'G': case 'H': case 'J': case 'L': case 'P': case 'R': case 'S': case 'T': case 'W': sum += ch; continue; default: return qfalse; } } sprintf(chs, "%02x", sum); if (checksum && !Q_stricmp(chs, checksum)) { return qtrue; } if (!checksum) { return qtrue; } return qfalse; } ================================================ FILE: src/engine/client/cl_net_chan.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "../../game/q_shared.h" #include "../qcommon/qcommon.h" #include "client.h" /* ============== CL_Netchan_Encode // first 12 bytes of the data are always: long serverId; long messageAcknowledge; long reliableAcknowledge; ============== */ static void CL_Netchan_Encode( msg_t *msg ) { int serverId, messageAcknowledge, reliableAcknowledge; int i, index, srdc, sbit, soob; byte key, *string; if ( msg->cursize <= CL_ENCODE_START ) { return; } srdc = msg->readcount; sbit = msg->bit; soob = msg->oob; msg->bit = 0; msg->readcount = 0; msg->oob = (qboolean) 0; serverId = MSG_ReadLong(msg); messageAcknowledge = MSG_ReadLong(msg); reliableAcknowledge = MSG_ReadLong(msg); msg->oob = (qboolean) soob; msg->bit = sbit; msg->readcount = srdc; string = (byte *)clc.serverCommands[ reliableAcknowledge & (MAX_RELIABLE_COMMANDS-1) ]; index = 0; // key = clc.challenge ^ serverId ^ messageAcknowledge; for (i = CL_ENCODE_START; i < msg->cursize; i++) { // modify the key with the last received now acknowledged server command if (!string[index]) index = 0; if (string[index] > 127 || string[index] == '%') { key ^= '.' << (i & 1); } else { key ^= string[index] << (i & 1); } index++; // encode the data with this key *(msg->data + i) = (*(msg->data + i)) ^ key; } } /* ============== CL_Netchan_Decode // first four bytes of the data are always: long reliableAcknowledge; ============== */ static void CL_Netchan_Decode( msg_t *msg ) { long reliableAcknowledge, i, index; byte key, *string; int srdc, sbit, soob; srdc = msg->readcount; sbit = msg->bit; soob = msg->oob; msg->oob = (qboolean) 0; reliableAcknowledge = MSG_ReadLong(msg); msg->oob = (qboolean) soob; msg->bit = sbit; msg->readcount = srdc; string = (byte*) clc.reliableCommands[ reliableAcknowledge & (MAX_RELIABLE_COMMANDS-1) ]; index = 0; // xor the client challenge with the netchan sequence number (need something that changes every message) key = clc.challenge ^ LittleLong( *(unsigned *)msg->data ); for (i = msg->readcount + CL_DECODE_START; i < msg->cursize; i++) { // modify the key with the last sent and with this message acknowledged client command if (!string[index]) index = 0; if (string[index] > 127 || string[index] == '%') { key ^= '.' << (i & 1); } else { key ^= string[index] << (i & 1); } index++; // decode the data with this key *(msg->data + i) = *(msg->data + i) ^ key; } } /* ================= CL_Netchan_TransmitNextFragment ================= */ void CL_Netchan_TransmitNextFragment( netchan_t *chan ) { Netchan_TransmitNextFragment( chan ); } /* =============== CL_Netchan_Transmit ================ */ void CL_Netchan_Transmit( netchan_t *chan, msg_t* msg ) { MSG_WriteByte( msg, clc_EOF ); CL_Netchan_Encode( msg ); Netchan_Transmit( chan, msg->cursize, msg->data ); } extern int oldsize; int newsize = 0; /* ================= CL_Netchan_Process ================= */ qboolean CL_Netchan_Process( netchan_t *chan, msg_t *msg ) { int ret; ret = Netchan_Process( chan, msg ); if (!ret) return qfalse; CL_Netchan_Decode( msg ); newsize += msg->cursize; return qtrue; } ================================================ FILE: src/engine/client/cl_parse.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // cl_parse.c -- parse a message received from the server #include "client.h" char *svc_strings[256] = { "svc_bad", "svc_nop", "svc_gamestate", "svc_configstring", "svc_baseline", "svc_serverCommand", "svc_download", "svc_snapshot" }; void SHOWNET( msg_t *msg, char *s) { if ( cl_shownet->integer >= 2) { Com_Printf ("%3i:%s\n", msg->readcount-1, s); } } /* ========================================================================= MESSAGE PARSING ========================================================================= */ /* ================== CL_DeltaEntity Parses deltas from the given base and adds the resulting entity to the current frame ================== */ void CL_DeltaEntity (msg_t *msg, clSnapshot_t *frame, int newnum, entityState_t *old, qboolean unchanged) { entityState_t *state; // save the parsed entity state into the big circular buffer so // it can be used as the source for a later delta state = &cl.parseEntities[cl.parseEntitiesNum & (MAX_PARSE_ENTITIES-1)]; if ( unchanged ) { *state = *old; } else { MSG_ReadDeltaEntity( msg, old, state, newnum ); } if ( state->number == (MAX_GENTITIES-1) ) { return; // entity was delta removed } cl.parseEntitiesNum++; frame->numEntities++; } /* ================== CL_ParsePacketEntities ================== */ void CL_ParsePacketEntities( msg_t *msg, clSnapshot_t *oldframe, clSnapshot_t *newframe) { int newnum; entityState_t *oldstate; int oldindex, oldnum; newframe->parseEntitiesNum = cl.parseEntitiesNum; newframe->numEntities = 0; // delta from the entities present in oldframe oldindex = 0; oldstate = NULL; if (!oldframe) { oldnum = 99999; } else { if ( oldindex >= oldframe->numEntities ) { oldnum = 99999; } else { oldstate = &cl.parseEntities[ (oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)]; oldnum = oldstate->number; } } while ( 1 ) { // read the entity index number newnum = MSG_ReadBits( msg, GENTITYNUM_BITS ); if ( newnum == (MAX_GENTITIES-1) ) { break; } if ( msg->readcount > msg->cursize ) { Com_Error (ERR_DROP,"CL_ParsePacketEntities: end of message"); } while ( oldnum < newnum ) { // one or more entities from the old packet are unchanged if ( cl_shownet->integer == 3 ) { Com_Printf ("%3i: unchanged: %i\n", msg->readcount, oldnum); } CL_DeltaEntity( msg, newframe, oldnum, oldstate, qtrue ); oldindex++; if ( oldindex >= oldframe->numEntities ) { oldnum = 99999; } else { oldstate = &cl.parseEntities[ (oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)]; oldnum = oldstate->number; } } if (oldnum == newnum) { // delta from previous state if ( cl_shownet->integer == 3 ) { Com_Printf ("%3i: delta: %i\n", msg->readcount, newnum); } CL_DeltaEntity( msg, newframe, newnum, oldstate, qfalse ); oldindex++; if ( oldindex >= oldframe->numEntities ) { oldnum = 99999; } else { oldstate = &cl.parseEntities[ (oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)]; oldnum = oldstate->number; } continue; } if ( oldnum > newnum ) { // delta from baseline if ( cl_shownet->integer == 3 ) { Com_Printf ("%3i: baseline: %i\n", msg->readcount, newnum); } CL_DeltaEntity( msg, newframe, newnum, &cl.entityBaselines[newnum], qfalse ); continue; } } // any remaining entities in the old frame are copied over while ( oldnum != 99999 ) { // one or more entities from the old packet are unchanged if ( cl_shownet->integer == 3 ) { Com_Printf ("%3i: unchanged: %i\n", msg->readcount, oldnum); } CL_DeltaEntity( msg, newframe, oldnum, oldstate, qtrue ); oldindex++; if ( oldindex >= oldframe->numEntities ) { oldnum = 99999; } else { oldstate = &cl.parseEntities[ (oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)]; oldnum = oldstate->number; } } } /* ================ CL_ParseSnapshot If the snapshot is parsed properly, it will be copied to cl.snap and saved in cl.snapshots[]. If the snapshot is invalid for any reason, no changes to the state will be made at all. ================ */ void CL_ParseSnapshot( msg_t *msg ) { int len; clSnapshot_t *old; clSnapshot_t newSnap; int deltaNum; int oldMessageNum; int i, packetNum; // get the reliable sequence acknowledge number // NOTE: now sent with all server to client messages //clc.reliableAcknowledge = MSG_ReadLong( msg ); // read in the new snapshot to a temporary buffer // we will only copy to cl.snap if it is valid Com_Memset (&newSnap, 0, sizeof(newSnap)); // we will have read any new server commands in this // message before we got to svc_snapshot newSnap.serverCommandNum = clc.serverCommandSequence; newSnap.serverTime = MSG_ReadLong( msg ); newSnap.messageNum = clc.serverMessageSequence; deltaNum = MSG_ReadByte( msg ); if ( !deltaNum ) { newSnap.deltaNum = -1; } else { newSnap.deltaNum = newSnap.messageNum - deltaNum; } newSnap.snapFlags = MSG_ReadByte( msg ); // If the frame is delta compressed from data that we // no longer have available, we must suck up the rest of // the frame, but not use it, then ask for a non-compressed // message if ( newSnap.deltaNum <= 0 ) { newSnap.valid = qtrue; // uncompressed frame old = NULL; clc.demowaiting = qfalse; // we can start recording now } else { old = &cl.snapshots[newSnap.deltaNum & PACKET_MASK]; if ( !old->valid ) { // should never happen Com_Printf ("Delta from invalid frame (not supposed to happen!).\n"); } else if ( old->messageNum != newSnap.deltaNum ) { // The frame that the server did the delta from // is too old, so we can't reconstruct it properly. Com_Printf ("Delta frame too old.\n"); } else if ( cl.parseEntitiesNum - old->parseEntitiesNum > MAX_PARSE_ENTITIES-128 ) { Com_Printf ("Delta parseEntitiesNum too old.\n"); } else { newSnap.valid = qtrue; // valid delta parse } } // read areamask len = MSG_ReadByte( msg ); MSG_ReadData( msg, &newSnap.areamask, len); // read playerinfo SHOWNET( msg, "playerstate" ); if ( old ) { MSG_ReadDeltaPlayerstate( msg, &old->ps, &newSnap.ps ); } else { MSG_ReadDeltaPlayerstate( msg, NULL, &newSnap.ps ); } // read packet entities SHOWNET( msg, "packet entities" ); CL_ParsePacketEntities( msg, old, &newSnap ); // if not valid, dump the entire thing now that it has // been properly read if ( !newSnap.valid ) { return; } // clear the valid flags of any snapshots between the last // received and this one, so if there was a dropped packet // it won't look like something valid to delta from next // time we wrap around in the buffer oldMessageNum = cl.snap.messageNum + 1; if ( newSnap.messageNum - oldMessageNum >= PACKET_BACKUP ) { oldMessageNum = newSnap.messageNum - ( PACKET_BACKUP - 1 ); } for ( ; oldMessageNum < newSnap.messageNum ; oldMessageNum++ ) { cl.snapshots[oldMessageNum & PACKET_MASK].valid = qfalse; } // copy to the current good spot cl.snap = newSnap; cl.snap.ping = 999; // calculate ping time for ( i = 0 ; i < PACKET_BACKUP ; i++ ) { packetNum = ( clc.netchan.outgoingSequence - 1 - i ) & PACKET_MASK; if ( cl.snap.ps.commandTime >= cl.outPackets[ packetNum ].p_serverTime ) { cl.snap.ping = cls.realtime - cl.outPackets[ packetNum ].p_realtime; break; } } // save the frame off in the backup array for later delta comparisons cl.snapshots[cl.snap.messageNum & PACKET_MASK] = cl.snap; if (cl_shownet->integer == 3) { Com_Printf( " snapshot:%i delta:%i ping:%i\n", cl.snap.messageNum, cl.snap.deltaNum, cl.snap.ping ); } cl.newSnapshots = qtrue; } //===================================================================== int cl_connectedToPureServer; /* ================== CL_SystemInfoChanged The systeminfo configstring has been changed, so parse new information out of it. This will happen at every gamestate, and possibly during gameplay. ================== */ void CL_SystemInfoChanged( void ) { char *systemInfo; const char *s, *t; char key[BIG_INFO_KEY]; char value[BIG_INFO_VALUE]; qboolean gameSet; systemInfo = cl.gameState.stringData + cl.gameState.stringOffsets[ CS_SYSTEMINFO ]; // NOTE TTimo: // when the serverId changes, any further messages we send to the server will use this new serverId // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=475 // in some cases, outdated cp commands might get sent with this news serverId cl.serverId = atoi( Info_ValueForKey( systemInfo, "sv_serverid" ) ); // don't set any vars when playing a demo if ( clc.demoplaying ) { return; } s = Info_ValueForKey( systemInfo, "sv_cheats" ); if ( atoi(s) == 0 ) { Cvar_SetCheatState(); } // check pure server string s = Info_ValueForKey( systemInfo, "sv_paks" ); t = Info_ValueForKey( systemInfo, "sv_pakNames" ); FS_PureServerSetLoadedPaks( s, t ); s = Info_ValueForKey( systemInfo, "sv_referencedPaks" ); t = Info_ValueForKey( systemInfo, "sv_referencedPakNames" ); FS_PureServerSetReferencedPaks( s, t ); gameSet = qfalse; // scan through all the variables in the systeminfo and locally set cvars to match s = systemInfo; while ( s ) { Info_NextPair( &s, key, value ); if ( !key[0] ) { break; } // ehw! if ( !Q_stricmp( key, "fs_game" ) ) { gameSet = qtrue; } Cvar_Set( key, value ); } // if game folder should not be set and it is set at the client side if ( !gameSet && *Cvar_VariableString("fs_game") ) { Cvar_Set( "fs_game", "" ); } cl_connectedToPureServer = Cvar_VariableValue( "sv_pure" ); } /* ================== CL_ParseGamestate ================== */ void CL_ParseGamestate( msg_t *msg ) { int i; entityState_t *es; int newnum; entityState_t nullstate; int cmd; char *s; Con_Close(); clc.connectPacketCount = 0; // wipe local client state CL_ClearState(); // a gamestate always marks a server command sequence clc.serverCommandSequence = MSG_ReadLong( msg ); // parse all the configstrings and baselines cl.gameState.dataCount = 1; // leave a 0 at the beginning for uninitialized configstrings while ( 1 ) { cmd = MSG_ReadByte( msg ); if ( cmd == svc_EOF ) { break; } if ( cmd == svc_configstring ) { int len; i = MSG_ReadShort( msg ); if ( i < 0 || i >= MAX_CONFIGSTRINGS ) { Com_Error( ERR_DROP, "configstring > MAX_CONFIGSTRINGS" ); } s = MSG_ReadBigString( msg ); len = (int)strlen( s ); if ( len + 1 + cl.gameState.dataCount > MAX_GAMESTATE_CHARS ) { Com_Error( ERR_DROP, "MAX_GAMESTATE_CHARS exceeded" ); } // append it to the gameState string buffer cl.gameState.stringOffsets[ i ] = cl.gameState.dataCount; Com_Memcpy( cl.gameState.stringData + cl.gameState.dataCount, s, len + 1 ); cl.gameState.dataCount += len + 1; } else if ( cmd == svc_baseline ) { newnum = MSG_ReadBits( msg, GENTITYNUM_BITS ); if ( newnum < 0 || newnum >= MAX_GENTITIES ) { Com_Error( ERR_DROP, "Baseline number out of range: %i", newnum ); } Com_Memset (&nullstate, 0, sizeof(nullstate)); es = &cl.entityBaselines[ newnum ]; MSG_ReadDeltaEntity( msg, &nullstate, es, newnum ); } else { Com_Error( ERR_DROP, "CL_ParseGamestate: bad command byte" ); } } clc.clientNum = MSG_ReadLong(msg); // read the checksum feed clc.checksumFeed = MSG_ReadLong( msg ); // parse serverId and other cvars CL_SystemInfoChanged(); // reinitialize the filesystem if the game directory has changed FS_ConditionalRestart( clc.checksumFeed ); // This used to call CL_StartHunkUsers, but now we enter the download state before loading the // cgame CL_InitDownloads(); // make sure the game starts Cvar_Set( "cl_paused", "0" ); } //===================================================================== /* ===================== CL_ParseDownload A download message has been received from the server ===================== */ void CL_ParseDownload ( msg_t *msg ) { int size; unsigned char data[MAX_MSGLEN]; int block; // read the data block = MSG_ReadShort ( msg ); if ( !block ) { // block zero is special, contains file size clc.downloadSize = MSG_ReadLong ( msg ); Cvar_SetValue( "cl_downloadSize", clc.downloadSize ); if (clc.downloadSize < 0) { Com_Error(ERR_DROP, MSG_ReadString( msg ) ); return; } } size = MSG_ReadShort ( msg ); if (size > 0) MSG_ReadData( msg, data, size ); if (clc.downloadBlock != block) { Com_DPrintf( "CL_ParseDownload: Expected block %d, got %d\n", clc.downloadBlock, block); return; } // open the file if not opened yet if (!clc.download) { if (!*clc.downloadTempName) { Com_Printf("Server sending download, but no download was requested\n"); CL_AddReliableCommand( "stopdl" ); return; } clc.download = FS_SV_FOpenFileWrite( clc.downloadTempName ); if (!clc.download) { Com_Printf( "Could not create %s\n", clc.downloadTempName ); CL_AddReliableCommand( "stopdl" ); CL_NextDownload(); return; } } if (size) FS_Write( data, size, clc.download ); CL_AddReliableCommand( va("nextdl %d", clc.downloadBlock) ); clc.downloadBlock++; clc.downloadCount += size; // So UI gets access to it Cvar_SetValue( "cl_downloadCount", clc.downloadCount ); if (!size) { // A zero length block means EOF if (clc.download) { FS_FCloseFile( clc.download ); clc.download = 0; // rename the file FS_SV_Rename ( clc.downloadTempName, clc.downloadName ); } *clc.downloadTempName = *clc.downloadName = 0; Cvar_Set( "cl_downloadName", "" ); // send intentions now // We need this because without it, we would hold the last nextdl and then start // loading right away. If we take a while to load, the server is happily trying // to send us that last block over and over. // Write it twice to help make sure we acknowledge the download CL_WritePacket(); CL_WritePacket(); // get another file if needed CL_NextDownload (); } } /* ===================== CL_ParseCommandString Command strings are just saved off until cgame asks for them when it transitions a snapshot ===================== */ void CL_ParseCommandString( msg_t *msg ) { char *s; int seq; int index; seq = MSG_ReadLong( msg ); s = MSG_ReadString( msg ); // see if we have already executed stored it off if ( clc.serverCommandSequence >= seq ) { return; } clc.serverCommandSequence = seq; index = seq & (MAX_RELIABLE_COMMANDS-1); Q_strncpyz( clc.serverCommands[ index ], s, sizeof( clc.serverCommands[ index ] ) ); } /* ===================== CL_ParseServerMessage ===================== */ void CL_ParseServerMessage( msg_t *msg ) { int cmd; if ( cl_shownet->integer == 1 ) { Com_Printf ("%i ",msg->cursize); } else if ( cl_shownet->integer >= 2 ) { Com_Printf ("------------------\n"); } MSG_Bitstream(msg); // get the reliable sequence acknowledge number clc.reliableAcknowledge = MSG_ReadLong( msg ); // if ( clc.reliableAcknowledge < clc.reliableSequence - MAX_RELIABLE_COMMANDS ) { clc.reliableAcknowledge = clc.reliableSequence; } // // parse the message // while ( 1 ) { if ( msg->readcount > msg->cursize ) { Com_Error (ERR_DROP,"CL_ParseServerMessage: read past end of server message"); break; } cmd = MSG_ReadByte( msg ); if ( cmd == svc_EOF) { SHOWNET( msg, "END OF MESSAGE" ); break; } if ( cl_shownet->integer >= 2 ) { if ( !svc_strings[cmd] ) { Com_Printf( "%3i:BAD CMD %i\n", msg->readcount-1, cmd ); } else { SHOWNET( msg, svc_strings[cmd] ); } } // other commands switch ( cmd ) { default: Com_Error (ERR_DROP,"CL_ParseServerMessage: Illegible server message\n"); break; case svc_nop: break; case svc_serverCommand: CL_ParseCommandString( msg ); break; case svc_gamestate: CL_ParseGamestate( msg ); break; case svc_snapshot: CL_ParseSnapshot( msg ); break; case svc_download: CL_ParseDownload( msg ); break; } } } ================================================ FILE: src/engine/client/cl_scrn.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // cl_scrn.c -- master for refresh, status bar, console, chat, notify, etc #include "client.h" qboolean scr_initialized; // ready to draw cvar_t *cl_timegraph; cvar_t *cl_debuggraph; cvar_t *cl_graphheight; cvar_t *cl_graphscale; cvar_t *cl_graphshift; /* ================ SCR_DrawNamedPic Coordinates are 640*480 virtual values ================= */ void SCR_DrawNamedPic( float x, float y, float width, float height, const char *picname ) { qhandle_t hShader; assert( width != 0 ); hShader = re.RegisterShader( picname ); SCR_AdjustFrom640( &x, &y, &width, &height ); re.DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader ); } /* ================ SCR_AdjustFrom640 Adjusted for resolution and screen aspect ratio ================ */ void SCR_AdjustFrom640( float *x, float *y, float *w, float *h ) { float xscale; float yscale; #if 0 // adjust for wide screens if ( cls.glconfig.vidWidth * 480 > cls.glconfig.vidHeight * 640 ) { *x += 0.5 * ( cls.glconfig.vidWidth - ( cls.glconfig.vidHeight * 640 / 480 ) ); } #endif // scale for screen sizes xscale = cls.glconfig.vidWidth / 640.0; yscale = cls.glconfig.vidHeight / 480.0; if ( x ) { *x *= xscale; } if ( y ) { *y *= yscale; } if ( w ) { *w *= xscale; } if ( h ) { *h *= yscale; } } /* ================ SCR_FillRect Coordinates are 640*480 virtual values ================= */ void SCR_FillRect( float x, float y, float width, float height, const float *color ) { re.SetColor( color ); SCR_AdjustFrom640( &x, &y, &width, &height ); re.DrawStretchPic( x, y, width, height, 0, 0, 0, 0, cls.whiteShader ); re.SetColor( NULL ); } /* ================ SCR_DrawPic Coordinates are 640*480 virtual values ================= */ void SCR_DrawPic( float x, float y, float width, float height, qhandle_t hShader ) { SCR_AdjustFrom640( &x, &y, &width, &height ); re.DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader ); } /* ** SCR_DrawChar ** chars are drawn at 640*480 virtual screen size */ static void SCR_DrawChar( int x, int y, float size, int ch ) { int row, col; float frow, fcol; float ax, ay, aw, ah; ch &= 255; if ( ch == ' ' ) { return; } if ( y < -size ) { return; } ax = x; ay = y; aw = size; ah = size; SCR_AdjustFrom640( &ax, &ay, &aw, &ah ); row = ch>>4; col = ch&15; frow = row*0.0625; fcol = col*0.0625; size = 0.0625; re.DrawStretchPic( ax, ay, aw, ah, fcol, frow, fcol + size, frow + size, cls.charSetShader ); } /* ** SCR_DrawSmallChar ** small chars are drawn at native screen resolution */ void SCR_DrawSmallChar( int x, int y, int ch ) { int row, col; float frow, fcol; float size; ch &= 255; if ( ch == ' ' ) { return; } if ( y < -SMALLCHAR_HEIGHT ) { return; } row = ch>>4; col = ch&15; frow = row*0.0625; fcol = col*0.0625; size = 0.0625; re.DrawStretchPic( x, y, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, fcol, frow, fcol + size, frow + size, cls.charSetShader ); } /* ================== SCR_DrawBigString[Color] Draws a multi-colored string with a drop shadow, optionally forcing to a fixed color. Coordinates are at 640 by 480 virtual resolution ================== */ void SCR_DrawStringExt( int x, int y, float size, const char *string, float *setColor, qboolean forceColor ) { vec4_t color; const char *s; int xx; // draw the drop shadow color[0] = color[1] = color[2] = 0; color[3] = setColor[3]; re.SetColor( color ); s = string; xx = x; while ( *s ) { if ( Q_IsColorString( s ) ) { s += 2; continue; } SCR_DrawChar( xx+2, y+2, size, *s ); xx += size; s++; } // draw the colored text s = string; xx = x; re.SetColor( setColor ); while ( *s ) { if ( Q_IsColorString( s ) ) { if ( !forceColor ) { Com_Memcpy( color, g_color_table[ColorIndex(*(s+1))], sizeof( color ) ); color[3] = setColor[3]; re.SetColor( color ); } s += 2; continue; } SCR_DrawChar( xx, y, size, *s ); xx += size; s++; } re.SetColor( NULL ); } void SCR_DrawBigString( int x, int y, const char *s, float alpha ) { float color[4]; color[0] = color[1] = color[2] = 1.0; color[3] = alpha; SCR_DrawStringExt( x, y, BIGCHAR_WIDTH, s, color, qfalse ); } void SCR_DrawBigStringColor( int x, int y, const char *s, vec4_t color ) { SCR_DrawStringExt( x, y, BIGCHAR_WIDTH, s, color, qtrue ); } /* ================== SCR_DrawSmallString[Color] Draws a multi-colored string with a drop shadow, optionally forcing to a fixed color. Coordinates are at 640 by 480 virtual resolution ================== */ void SCR_DrawSmallStringExt( int x, int y, const char *string, float *setColor, qboolean forceColor ) { vec4_t color; const char *s; int xx; // draw the colored text s = string; xx = x; re.SetColor( setColor ); while ( *s ) { if ( Q_IsColorString( s ) ) { if ( !forceColor ) { Com_Memcpy( color, g_color_table[ColorIndex(*(s+1))], sizeof( color ) ); color[3] = setColor[3]; re.SetColor( color ); } s += 2; continue; } SCR_DrawSmallChar( xx, y, *s ); xx += SMALLCHAR_WIDTH; s++; } re.SetColor( NULL ); } /* ** SCR_Strlen -- skips color escape codes */ static int SCR_Strlen( const char *str ) { const char *s = str; int count = 0; while ( *s ) { if ( Q_IsColorString( s ) ) { s += 2; } else { count++; s++; } } return count; } /* ** SCR_GetBigStringWidth */ int SCR_GetBigStringWidth( const char *str ) { return SCR_Strlen( str ) * 16; } //=============================================================================== /* ================= SCR_DrawDemoRecording ================= */ void SCR_DrawDemoRecording( void ) { char string[1024]; int pos; if ( !clc.demorecording ) { return; } if ( clc.spDemoRecording ) { return; } pos = FS_FTell( clc.demofile ); sprintf( string, "RECORDING %s: %ik", clc.demoName, pos / 1024 ); SCR_DrawStringExt( 320 - (int)strlen( string ) * 4, 20, 8, string, g_color_table[7], qtrue ); } /* =============================================================================== DEBUG GRAPH =============================================================================== */ typedef struct { float value; int color; } graphsamp_t; static int current; static graphsamp_t values[1024]; /* ============== SCR_DebugGraph ============== */ void SCR_DebugGraph (float value, int color) { values[current&1023].value = value; values[current&1023].color = color; current++; } /* ============== SCR_DrawDebugGraph ============== */ void SCR_DrawDebugGraph (void) { int a, x, y, w, i, h; float v; int color; // // draw the graph // w = cls.glconfig.vidWidth; x = 0; y = cls.glconfig.vidHeight; re.SetColor( g_color_table[0] ); re.DrawStretchPic(x, y - cl_graphheight->integer, w, cl_graphheight->integer, 0, 0, 0, 0, cls.whiteShader ); re.SetColor( NULL ); for (a=0 ; ainteger + cl_graphshift->integer; if (v < 0) v += cl_graphheight->integer * (1+(int)(-v / cl_graphheight->integer)); h = (int)v % cl_graphheight->integer; re.DrawStretchPic( x+w-1-a, y - h, 1, h, 0, 0, 0, 0, cls.whiteShader ); } } //============================================================================= /* ================== SCR_Init ================== */ void SCR_Init( void ) { cl_timegraph = Cvar_Get ("timegraph", "0", CVAR_CHEAT); cl_debuggraph = Cvar_Get ("debuggraph", "0", CVAR_CHEAT); cl_graphheight = Cvar_Get ("graphheight", "32", CVAR_CHEAT); cl_graphscale = Cvar_Get ("graphscale", "1", CVAR_CHEAT); cl_graphshift = Cvar_Get ("graphshift", "0", CVAR_CHEAT); scr_initialized = qtrue; } //======================================================= /* ================== SCR_DrawScreenField This will be called twice if rendering in stereo mode ================== */ void SCR_DrawScreenField( stereoFrame_t stereoFrame ) { re.BeginFrame( stereoFrame ); // wide aspect ratio screens need to have the sides cleared // unless they are displaying game renderings if ( cls.state != CA_ACTIVE ) { if ( cls.glconfig.vidWidth * 480 > cls.glconfig.vidHeight * 640 ) { re.SetColor( g_color_table[0] ); re.DrawStretchPic( 0, 0, cls.glconfig.vidWidth, cls.glconfig.vidHeight, 0, 0, 0, 0, cls.whiteShader ); re.SetColor( NULL ); } } if ( !uivm ) { Com_DPrintf("draw screen without UI loaded\n"); return; } // if the menu is going to cover the entire screen, we // don't need to render anything under it if ( !VM_Call( uivm, UI_IS_FULLSCREEN )) { switch( cls.state ) { default: Com_Error( ERR_FATAL, "SCR_DrawScreenField: bad cls.state" ); break; case CA_CINEMATIC: SCR_DrawCinematic(); break; case CA_DISCONNECTED: // force menu up S_StopAllSounds(); VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_MAIN ); break; case CA_CONNECTING: case CA_CHALLENGING: case CA_CONNECTED: // connecting clients will only show the connection dialog // refresh to update the time VM_Call( uivm, UI_REFRESH, cls.realtime ); VM_Call( uivm, UI_DRAW_CONNECT_SCREEN, qfalse ); break; case CA_LOADING: case CA_PRIMED: // draw the game information screen and loading progress CL_CGameRendering( stereoFrame ); // also draw the connection information, so it doesn't // flash away too briefly on local or lan games // refresh to update the time VM_Call( uivm, UI_REFRESH, cls.realtime ); VM_Call( uivm, UI_DRAW_CONNECT_SCREEN, qtrue ); break; case CA_ACTIVE: CL_CGameRendering( stereoFrame ); SCR_DrawDemoRecording(); break; } } // the menu draws next if ( cls.keyCatchers & KEYCATCH_UI && uivm ) { VM_Call( uivm, UI_REFRESH, cls.realtime ); } // console draws next Con_DrawConsole (); // debug graph can be drawn on top of anything if ( cl_debuggraph->integer || cl_timegraph->integer || cl_debugMove->integer ) { SCR_DrawDebugGraph (); } } /* ================== SCR_UpdateScreen This is called every frame, and can also be called explicitly to flush text to the screen. ================== */ void SCR_UpdateScreen( void ) { static int recursive; if ( !scr_initialized ) { return; // not initialized yet } if ( ++recursive > 2 ) { Com_Error( ERR_FATAL, "SCR_UpdateScreen: recursively called" ); } recursive = 1; // if running in stereo, we need to draw the frame twice if ( cls.glconfig.stereoEnabled ) { SCR_DrawScreenField( STEREO_LEFT ); SCR_DrawScreenField( STEREO_RIGHT ); } else { SCR_DrawScreenField( STEREO_CENTER ); } if ( com_speeds->integer ) { re.EndFrame( &time_frontend, &time_backend ); } else { re.EndFrame( NULL, NULL ); } recursive = 0; } ================================================ FILE: src/engine/client/cl_ui.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "client.h" #include "../../game/botlib.h" extern botlib_export_t *botlib_export; vm_t *uivm; /* ==================== GetClientState ==================== */ static void GetClientState( uiClientState_t *state ) { state->connectPacketCount = clc.connectPacketCount; state->connState = cls.state; Q_strncpyz( state->servername, cls.servername, sizeof( state->servername ) ); Q_strncpyz( state->updateInfoString, cls.updateInfoString, sizeof( state->updateInfoString ) ); Q_strncpyz( state->messageString, clc.serverMessage, sizeof( state->messageString ) ); state->clientNum = cl.snap.ps.clientNum; } /* ==================== LAN_LoadCachedServers ==================== */ void LAN_LoadCachedServers( ) { int size; fileHandle_t fileIn; cls.numglobalservers = cls.nummplayerservers = cls.numfavoriteservers = 0; cls.numGlobalServerAddresses = 0; if (FS_SV_FOpenFileRead("servercache.dat", &fileIn)) { FS_Read(&cls.numglobalservers, sizeof(int), fileIn); FS_Read(&cls.nummplayerservers, sizeof(int), fileIn); FS_Read(&cls.numfavoriteservers, sizeof(int), fileIn); FS_Read(&size, sizeof(int), fileIn); if (size == sizeof(cls.globalServers) + sizeof(cls.favoriteServers) + sizeof(cls.mplayerServers)) { FS_Read(&cls.globalServers, sizeof(cls.globalServers), fileIn); FS_Read(&cls.mplayerServers, sizeof(cls.mplayerServers), fileIn); FS_Read(&cls.favoriteServers, sizeof(cls.favoriteServers), fileIn); } else { cls.numglobalservers = cls.nummplayerservers = cls.numfavoriteservers = 0; cls.numGlobalServerAddresses = 0; } FS_FCloseFile(fileIn); } } /* ==================== LAN_SaveServersToCache ==================== */ void LAN_SaveServersToCache( ) { int size; fileHandle_t fileOut = FS_SV_FOpenFileWrite("servercache.dat"); FS_Write(&cls.numglobalservers, sizeof(int), fileOut); FS_Write(&cls.nummplayerservers, sizeof(int), fileOut); FS_Write(&cls.numfavoriteservers, sizeof(int), fileOut); size = sizeof(cls.globalServers) + sizeof(cls.favoriteServers) + sizeof(cls.mplayerServers); FS_Write(&size, sizeof(int), fileOut); FS_Write(&cls.globalServers, sizeof(cls.globalServers), fileOut); FS_Write(&cls.mplayerServers, sizeof(cls.mplayerServers), fileOut); FS_Write(&cls.favoriteServers, sizeof(cls.favoriteServers), fileOut); FS_FCloseFile(fileOut); } /* ==================== LAN_ResetPings ==================== */ static void LAN_ResetPings(int source) { int count,i; serverInfo_t *servers = NULL; count = 0; switch (source) { case AS_LOCAL : servers = &cls.localServers[0]; count = MAX_OTHER_SERVERS; break; case AS_MPLAYER : servers = &cls.mplayerServers[0]; count = MAX_OTHER_SERVERS; break; case AS_GLOBAL : servers = &cls.globalServers[0]; count = MAX_GLOBAL_SERVERS; break; case AS_FAVORITES : servers = &cls.favoriteServers[0]; count = MAX_OTHER_SERVERS; break; } if (servers) { for (i = 0; i < count; i++) { servers[i].ping = -1; } } } /* ==================== LAN_AddServer ==================== */ static int LAN_AddServer(int source, const char *name, const char *address) { int max, *count, i; netadr_t adr; serverInfo_t *servers = NULL; max = MAX_OTHER_SERVERS; count = 0; switch (source) { case AS_LOCAL : count = &cls.numlocalservers; servers = &cls.localServers[0]; break; case AS_MPLAYER : count = &cls.nummplayerservers; servers = &cls.mplayerServers[0]; break; case AS_GLOBAL : max = MAX_GLOBAL_SERVERS; count = &cls.numglobalservers; servers = &cls.globalServers[0]; break; case AS_FAVORITES : count = &cls.numfavoriteservers; servers = &cls.favoriteServers[0]; break; } if (servers && *count < max) { NET_StringToAdr( address, &adr ); for ( i = 0; i < *count; i++ ) { if (NET_CompareAdr(servers[i].adr, adr)) { break; } } if (i >= *count) { servers[*count].adr = adr; Q_strncpyz(servers[*count].hostName, name, sizeof(servers[*count].hostName)); servers[*count].visible = qtrue; (*count)++; return 1; } return 0; } return -1; } /* ==================== LAN_RemoveServer ==================== */ static void LAN_RemoveServer(int source, const char *addr) { int *count, i; serverInfo_t *servers = NULL; count = 0; switch (source) { case AS_LOCAL : count = &cls.numlocalservers; servers = &cls.localServers[0]; break; case AS_MPLAYER : count = &cls.nummplayerservers; servers = &cls.mplayerServers[0]; break; case AS_GLOBAL : count = &cls.numglobalservers; servers = &cls.globalServers[0]; break; case AS_FAVORITES : count = &cls.numfavoriteservers; servers = &cls.favoriteServers[0]; break; } if (servers) { netadr_t comp; NET_StringToAdr( addr, &comp ); for (i = 0; i < *count; i++) { if (NET_CompareAdr( comp, servers[i].adr)) { int j = i; while (j < *count - 1) { Com_Memcpy(&servers[j], &servers[j+1], sizeof(servers[j])); j++; } (*count)--; break; } } } } /* ==================== LAN_GetServerCount ==================== */ static int LAN_GetServerCount( int source ) { switch (source) { case AS_LOCAL : return cls.numlocalservers; break; case AS_MPLAYER : return cls.nummplayerservers; break; case AS_GLOBAL : return cls.numglobalservers; break; case AS_FAVORITES : return cls.numfavoriteservers; break; } return 0; } /* ==================== LAN_GetLocalServerAddressString ==================== */ static void LAN_GetServerAddressString( int source, int n, char *buf, int buflen ) { switch (source) { case AS_LOCAL : if (n >= 0 && n < MAX_OTHER_SERVERS) { Q_strncpyz(buf, NET_AdrToString( cls.localServers[n].adr) , buflen ); return; } break; case AS_MPLAYER : if (n >= 0 && n < MAX_OTHER_SERVERS) { Q_strncpyz(buf, NET_AdrToString( cls.mplayerServers[n].adr) , buflen ); return; } break; case AS_GLOBAL : if (n >= 0 && n < MAX_GLOBAL_SERVERS) { Q_strncpyz(buf, NET_AdrToString( cls.globalServers[n].adr) , buflen ); return; } break; case AS_FAVORITES : if (n >= 0 && n < MAX_OTHER_SERVERS) { Q_strncpyz(buf, NET_AdrToString( cls.favoriteServers[n].adr) , buflen ); return; } break; } buf[0] = '\0'; } /* ==================== LAN_GetServerInfo ==================== */ static void LAN_GetServerInfo( int source, int n, char *buf, int buflen ) { char info[MAX_STRING_CHARS]; serverInfo_t *server = NULL; info[0] = '\0'; switch (source) { case AS_LOCAL : if (n >= 0 && n < MAX_OTHER_SERVERS) { server = &cls.localServers[n]; } break; case AS_MPLAYER : if (n >= 0 && n < MAX_OTHER_SERVERS) { server = &cls.mplayerServers[n]; } break; case AS_GLOBAL : if (n >= 0 && n < MAX_GLOBAL_SERVERS) { server = &cls.globalServers[n]; } break; case AS_FAVORITES : if (n >= 0 && n < MAX_OTHER_SERVERS) { server = &cls.favoriteServers[n]; } break; } if (server && buf) { buf[0] = '\0'; Info_SetValueForKey( info, "hostname", server->hostName); Info_SetValueForKey( info, "mapname", server->mapName); Info_SetValueForKey( info, "clients", va("%i",server->clients)); Info_SetValueForKey( info, "sv_maxclients", va("%i",server->maxClients)); Info_SetValueForKey( info, "ping", va("%i",server->ping)); Info_SetValueForKey( info, "minping", va("%i",server->minPing)); Info_SetValueForKey( info, "maxping", va("%i",server->maxPing)); Info_SetValueForKey( info, "game", server->game); Info_SetValueForKey( info, "gametype", va("%i",server->gameType)); Info_SetValueForKey( info, "nettype", va("%i",server->netType)); Info_SetValueForKey( info, "addr", NET_AdrToString(server->adr)); Info_SetValueForKey( info, "punkbuster", va("%i", server->punkbuster)); Q_strncpyz(buf, info, buflen); } else { if (buf) { buf[0] = '\0'; } } } /* ==================== LAN_GetServerPing ==================== */ static int LAN_GetServerPing( int source, int n ) { serverInfo_t *server = NULL; switch (source) { case AS_LOCAL : if (n >= 0 && n < MAX_OTHER_SERVERS) { server = &cls.localServers[n]; } break; case AS_MPLAYER : if (n >= 0 && n < MAX_OTHER_SERVERS) { server = &cls.mplayerServers[n]; } break; case AS_GLOBAL : if (n >= 0 && n < MAX_GLOBAL_SERVERS) { server = &cls.globalServers[n]; } break; case AS_FAVORITES : if (n >= 0 && n < MAX_OTHER_SERVERS) { server = &cls.favoriteServers[n]; } break; } if (server) { return server->ping; } return -1; } /* ==================== LAN_GetServerPtr ==================== */ static serverInfo_t *LAN_GetServerPtr( int source, int n ) { switch (source) { case AS_LOCAL : if (n >= 0 && n < MAX_OTHER_SERVERS) { return &cls.localServers[n]; } break; case AS_MPLAYER : if (n >= 0 && n < MAX_OTHER_SERVERS) { return &cls.mplayerServers[n]; } break; case AS_GLOBAL : if (n >= 0 && n < MAX_GLOBAL_SERVERS) { return &cls.globalServers[n]; } break; case AS_FAVORITES : if (n >= 0 && n < MAX_OTHER_SERVERS) { return &cls.favoriteServers[n]; } break; } return NULL; } /* ==================== LAN_CompareServers ==================== */ static int LAN_CompareServers( int source, int sortKey, int sortDir, int s1, int s2 ) { int res; serverInfo_t *server1, *server2; server1 = LAN_GetServerPtr(source, s1); server2 = LAN_GetServerPtr(source, s2); if (!server1 || !server2) { return 0; } res = 0; switch( sortKey ) { case SORT_HOST: res = Q_stricmp( server1->hostName, server2->hostName ); break; case SORT_MAP: res = Q_stricmp( server1->mapName, server2->mapName ); break; case SORT_CLIENTS: if (server1->clients < server2->clients) { res = -1; } else if (server1->clients > server2->clients) { res = 1; } else { res = 0; } break; case SORT_GAME: if (server1->gameType < server2->gameType) { res = -1; } else if (server1->gameType > server2->gameType) { res = 1; } else { res = 0; } break; case SORT_PING: if (server1->ping < server2->ping) { res = -1; } else if (server1->ping > server2->ping) { res = 1; } else { res = 0; } break; } if (sortDir) { if (res < 0) return 1; if (res > 0) return -1; return 0; } return res; } /* ==================== LAN_GetPingQueueCount ==================== */ static int LAN_GetPingQueueCount( void ) { return (CL_GetPingQueueCount()); } /* ==================== LAN_ClearPing ==================== */ static void LAN_ClearPing( int n ) { CL_ClearPing( n ); } /* ==================== LAN_GetPing ==================== */ static void LAN_GetPing( int n, char *buf, int buflen, int *pingtime ) { CL_GetPing( n, buf, buflen, pingtime ); } /* ==================== LAN_GetPingInfo ==================== */ static void LAN_GetPingInfo( int n, char *buf, int buflen ) { CL_GetPingInfo( n, buf, buflen ); } /* ==================== LAN_MarkServerVisible ==================== */ static void LAN_MarkServerVisible(int source, int n, qboolean visible ) { if (n == -1) { int count = MAX_OTHER_SERVERS; serverInfo_t *server = NULL; switch (source) { case AS_LOCAL : server = &cls.localServers[0]; break; case AS_MPLAYER : server = &cls.mplayerServers[0]; break; case AS_GLOBAL : server = &cls.globalServers[0]; count = MAX_GLOBAL_SERVERS; break; case AS_FAVORITES : server = &cls.favoriteServers[0]; break; } if (server) { for (n = 0; n < count; n++) { server[n].visible = visible; } } } else { switch (source) { case AS_LOCAL : if (n >= 0 && n < MAX_OTHER_SERVERS) { cls.localServers[n].visible = visible; } break; case AS_MPLAYER : if (n >= 0 && n < MAX_OTHER_SERVERS) { cls.mplayerServers[n].visible = visible; } break; case AS_GLOBAL : if (n >= 0 && n < MAX_GLOBAL_SERVERS) { cls.globalServers[n].visible = visible; } break; case AS_FAVORITES : if (n >= 0 && n < MAX_OTHER_SERVERS) { cls.favoriteServers[n].visible = visible; } break; } } } /* ======================= LAN_ServerIsVisible ======================= */ static int LAN_ServerIsVisible(int source, int n ) { switch (source) { case AS_LOCAL : if (n >= 0 && n < MAX_OTHER_SERVERS) { return cls.localServers[n].visible; } break; case AS_MPLAYER : if (n >= 0 && n < MAX_OTHER_SERVERS) { return cls.mplayerServers[n].visible; } break; case AS_GLOBAL : if (n >= 0 && n < MAX_GLOBAL_SERVERS) { return cls.globalServers[n].visible; } break; case AS_FAVORITES : if (n >= 0 && n < MAX_OTHER_SERVERS) { return cls.favoriteServers[n].visible; } break; } return qfalse; } /* ======================= LAN_UpdateVisiblePings ======================= */ qboolean LAN_UpdateVisiblePings(int source ) { return CL_UpdateVisiblePings_f(source); } /* ==================== LAN_GetServerStatus ==================== */ int LAN_GetServerStatus( char *serverAddress, char *serverStatus, int maxLen ) { return CL_ServerStatus( serverAddress, serverStatus, maxLen ); } /* ==================== CL_GetGlConfig ==================== */ static void CL_GetGlconfig( glconfig_t *config ) { *config = cls.glconfig; } /* ==================== GetClipboardData ==================== */ static void GetClipboardData( char *buf, int buflen ) { char *cbd; cbd = Sys_GetClipboardData(); if ( !cbd ) { *buf = 0; return; } Q_strncpyz( buf, cbd, buflen ); Z_Free( cbd ); } /* ==================== Key_KeynumToStringBuf ==================== */ static void Key_KeynumToStringBuf( int keynum, char *buf, int buflen ) { Q_strncpyz( buf, Key_KeynumToString( keynum ), buflen ); } /* ==================== Key_GetBindingBuf ==================== */ static void Key_GetBindingBuf( int keynum, char *buf, int buflen ) { char *value; value = Key_GetBinding( keynum ); if ( value ) { Q_strncpyz( buf, value, buflen ); } else { *buf = 0; } } /* ==================== Key_GetCatcher ==================== */ int Key_GetCatcher( void ) { return cls.keyCatchers; } /* ==================== Ket_SetCatcher ==================== */ void Key_SetCatcher( int catcher ) { cls.keyCatchers = catcher; } /* ==================== CLUI_GetCDKey ==================== */ static void CLUI_GetCDKey( char *buf, int buflen ) { cvar_t *fs; fs = Cvar_Get ("fs_game", "", CVAR_INIT|CVAR_SYSTEMINFO ); if (UI_usesUniqueCDKey() && fs && fs->string[0] != 0) { Com_Memcpy( buf, &cl_cdkey[16], 16); buf[16] = 0; } else { Com_Memcpy( buf, cl_cdkey, 16); buf[16] = 0; } } /* ==================== CLUI_SetCDKey ==================== */ static void CLUI_SetCDKey( char *buf ) { cvar_t *fs; fs = Cvar_Get ("fs_game", "", CVAR_INIT|CVAR_SYSTEMINFO ); if (UI_usesUniqueCDKey() && fs && fs->string[0] != 0) { Com_Memcpy( &cl_cdkey[16], buf, 16 ); cl_cdkey[32] = 0; // set the flag so the fle will be written at the next opportunity cvar_modifiedFlags |= CVAR_ARCHIVE; } else { Com_Memcpy( cl_cdkey, buf, 16 ); // set the flag so the fle will be written at the next opportunity cvar_modifiedFlags |= CVAR_ARCHIVE; } } /* ==================== GetConfigString ==================== */ static int GetConfigString(int index, char *buf, int size) { int offset; if (index < 0 || index >= MAX_CONFIGSTRINGS) return qfalse; offset = cl.gameState.stringOffsets[index]; if (!offset) { if( size ) { buf[0] = 0; } return qfalse; } Q_strncpyz( buf, cl.gameState.stringData+offset, size); return qtrue; } /* ==================== FloatAsInt ==================== */ static int FloatAsInt( float f ) { int temp; *(float *)&temp = f; return temp; } void *VM_ArgPtr( intptr_t intValue ); #define VMA(x) VM_ArgPtr(args[x]) #define VMF(x) (*(float*)&args[x]) /* ==================== CL_UISystemCalls The ui module is making a system call ==================== */ intptr_t CL_UISystemCalls( intptr_t *args ) { switch( args[0] ) { case UI_ERROR: Com_Error( ERR_DROP, "%s", VMA(1) ); return 0; case UI_PRINT: Com_Printf( "%s", VMA(1) ); return 0; case UI_MILLISECONDS: return Sys_Milliseconds(); case UI_CVAR_REGISTER: Cvar_Register( (vmCvar_t*) VMA(1), (const char*) VMA(2), (const char*) VMA(3), args[4] ); return 0; case UI_CVAR_UPDATE: Cvar_Update( (vmCvar_t*) VMA(1) ); return 0; case UI_CVAR_SET: Cvar_Set( (const char*) VMA(1), (const char*) VMA(2) ); return 0; case UI_CVAR_VARIABLEVALUE: return FloatAsInt( Cvar_VariableValue( (const char*) VMA(1) ) ); case UI_CVAR_VARIABLESTRINGBUFFER: Cvar_VariableStringBuffer( (const char*) VMA(1), (char*) VMA(2), args[3] ); return 0; case UI_CVAR_SETVALUE: Cvar_SetValue( (const char*) VMA(1), VMF(2) ); return 0; case UI_CVAR_RESET: Cvar_Reset( (const char*) VMA(1) ); return 0; case UI_CVAR_CREATE: Cvar_Get( (const char*) VMA(1), (const char*) VMA(2), args[3] ); return 0; case UI_CVAR_INFOSTRINGBUFFER: Cvar_InfoStringBuffer( args[1], (char*) VMA(2), args[3] ); return 0; case UI_ARGC: return Cmd_Argc(); case UI_ARGV: Cmd_ArgvBuffer( args[1], (char*) VMA(2), args[3] ); return 0; case UI_CMD_EXECUTETEXT: Cbuf_ExecuteText( args[1], (char*) VMA(2) ); return 0; case UI_FS_FOPENFILE: return FS_FOpenFileByMode( (const char*) VMA(1), (fileHandle_t*) VMA(2), (fsMode_t) args[3] ); case UI_FS_READ: FS_Read2( VMA(1), args[2], args[3] ); return 0; case UI_FS_WRITE: FS_Write( VMA(1), args[2], args[3] ); return 0; case UI_FS_FCLOSEFILE: FS_FCloseFile( args[1] ); return 0; case UI_FS_GETFILELIST: return FS_GetFileList( (const char*) VMA(1), (const char*) VMA(2), (char*) VMA(3), args[4] ); case UI_FS_SEEK: return FS_Seek( args[1], args[2], args[3] ); case UI_R_REGISTERMODEL: return re.RegisterModel( (const char*) VMA(1) ); case UI_R_REGISTERSKIN: return re.RegisterSkin( (const char*) VMA(1) ); case UI_R_REGISTERSHADERNOMIP: return re.RegisterShaderNoMip( (const char*) VMA(1) ); case UI_R_CLEARSCENE: re.ClearScene(); return 0; case UI_R_ADDREFENTITYTOSCENE: re.AddRefEntityToScene( (const refEntity_t*) VMA(1) ); return 0; case UI_R_ADDPOLYTOSCENE: re.AddPolyToScene( args[1], args[2], (const polyVert_t*) VMA(3), 1 ); return 0; case UI_R_ADDLIGHTTOSCENE: re.AddLightToScene( (const vec_t*) VMA(1), VMF(2), VMF(3), VMF(4), VMF(5) ); return 0; case UI_R_RENDERSCENE: re.RenderScene( (const refdef_t*) VMA(1) ); return 0; case UI_R_SETCOLOR: re.SetColor( (const float*) VMA(1) ); return 0; case UI_R_DRAWSTRETCHPIC: re.DrawStretchPic( VMF(1), VMF(2), VMF(3), VMF(4), VMF(5), VMF(6), VMF(7), VMF(8), args[9] ); return 0; case UI_R_MODELBOUNDS: re.ModelBounds( args[1], (vec_t*) VMA(2), (vec_t*) VMA(3) ); return 0; case UI_UPDATESCREEN: SCR_UpdateScreen(); return 0; case UI_CM_LERPTAG: re.LerpTag( (orientation_t*) VMA(1), args[2], args[3], args[4], VMF(5), (const char*) VMA(6) ); return 0; case UI_S_REGISTERSOUND: return S_RegisterSound( (const char*) VMA(1), (qboolean) args[2] ); case UI_S_STARTLOCALSOUND: S_StartLocalSound( args[1], args[2] ); return 0; case UI_KEY_KEYNUMTOSTRINGBUF: Key_KeynumToStringBuf( args[1], (char*) VMA(2), args[3] ); return 0; case UI_KEY_GETBINDINGBUF: Key_GetBindingBuf( args[1], (char*) VMA(2), args[3] ); return 0; case UI_KEY_SETBINDING: Key_SetBinding( args[1], (const char*) VMA(2) ); return 0; case UI_KEY_ISDOWN: return Key_IsDown( args[1] ); case UI_KEY_GETOVERSTRIKEMODE: return Key_GetOverstrikeMode(); case UI_KEY_SETOVERSTRIKEMODE: Key_SetOverstrikeMode( (qboolean) args[1] ); return 0; case UI_KEY_CLEARSTATES: Key_ClearStates(); return 0; case UI_KEY_GETCATCHER: return Key_GetCatcher(); case UI_KEY_SETCATCHER: Key_SetCatcher( args[1] ); return 0; case UI_GETCLIPBOARDDATA: GetClipboardData( (char*) VMA(1), args[2] ); return 0; case UI_GETCLIENTSTATE: GetClientState( (uiClientState_t*) VMA(1) ); return 0; case UI_GETGLCONFIG: CL_GetGlconfig( (glconfig_t*) VMA(1) ); return 0; case UI_GETCONFIGSTRING: return GetConfigString( args[1], (char*) VMA(2), args[3] ); case UI_LAN_LOADCACHEDSERVERS: LAN_LoadCachedServers(); return 0; case UI_LAN_SAVECACHEDSERVERS: LAN_SaveServersToCache(); return 0; case UI_LAN_ADDSERVER: return LAN_AddServer(args[1], (const char*) VMA(2), (const char*) VMA(3)); case UI_LAN_REMOVESERVER: LAN_RemoveServer(args[1], (const char*) VMA(2)); return 0; case UI_LAN_GETPINGQUEUECOUNT: return LAN_GetPingQueueCount(); case UI_LAN_CLEARPING: LAN_ClearPing( args[1] ); return 0; case UI_LAN_GETPING: LAN_GetPing( args[1], (char*) VMA(2), args[3], (int*) VMA(4) ); return 0; case UI_LAN_GETPINGINFO: LAN_GetPingInfo( args[1], (char*) VMA(2), args[3] ); return 0; case UI_LAN_GETSERVERCOUNT: return LAN_GetServerCount(args[1]); case UI_LAN_GETSERVERADDRESSSTRING: LAN_GetServerAddressString( args[1], args[2], (char*) VMA(3), args[4] ); return 0; case UI_LAN_GETSERVERINFO: LAN_GetServerInfo( args[1], args[2], (char*) VMA(3), args[4] ); return 0; case UI_LAN_GETSERVERPING: return LAN_GetServerPing( args[1], args[2] ); case UI_LAN_MARKSERVERVISIBLE: LAN_MarkServerVisible( args[1], args[2], (qboolean) args[3] ); return 0; case UI_LAN_SERVERISVISIBLE: return LAN_ServerIsVisible( args[1], args[2] ); case UI_LAN_UPDATEVISIBLEPINGS: return LAN_UpdateVisiblePings( args[1] ); case UI_LAN_RESETPINGS: LAN_ResetPings( args[1] ); return 0; case UI_LAN_SERVERSTATUS: return LAN_GetServerStatus( (char*) VMA(1), (char*) VMA(2), args[3] ); case UI_LAN_COMPARESERVERS: return LAN_CompareServers( args[1], args[2], args[3], args[4], args[5] ); case UI_MEMORY_REMAINING: return Hunk_MemoryRemaining(); case UI_GET_CDKEY: CLUI_GetCDKey( (char*) VMA(1), args[2] ); return 0; case UI_SET_CDKEY: CLUI_SetCDKey( (char*) VMA(1) ); return 0; case UI_SET_PBCLSTATUS: return 0; case UI_R_REGISTERFONT: re.RegisterFont( (const char*) VMA(1), args[2], (fontInfo_t*) VMA(3)); return 0; case UI_MEMSET: Com_Memset( VMA(1), args[2], args[3] ); return 0; case UI_MEMCPY: Com_Memcpy( VMA(1), VMA(2), args[3] ); return 0; case UI_STRNCPY: strncpy( (char*)VMA(1), (const char*)VMA(2), args[3] ); return args[1]; case UI_SIN: return FloatAsInt( sin( VMF(1) ) ); case UI_COS: return FloatAsInt( cos( VMF(1) ) ); case UI_ATAN2: return FloatAsInt( atan2( VMF(1), VMF(2) ) ); case UI_SQRT: return FloatAsInt( sqrt( VMF(1) ) ); case UI_FLOOR: return FloatAsInt( floor( VMF(1) ) ); case UI_CEIL: return FloatAsInt( ceil( VMF(1) ) ); case UI_PC_ADD_GLOBAL_DEFINE: return botlib_export->PC_AddGlobalDefine( (char*) VMA(1) ); case UI_PC_LOAD_SOURCE: return botlib_export->PC_LoadSourceHandle( (const char*) VMA(1) ); case UI_PC_FREE_SOURCE: return botlib_export->PC_FreeSourceHandle( args[1] ); case UI_PC_READ_TOKEN: return botlib_export->PC_ReadTokenHandle( args[1], (pc_token_t*) VMA(2) ); case UI_PC_SOURCE_FILE_AND_LINE: return botlib_export->PC_SourceFileAndLine( args[1], (char*) VMA(2), (int*) VMA(3) ); case UI_S_STOPBACKGROUNDTRACK: S_StopBackgroundTrack(); return 0; case UI_S_STARTBACKGROUNDTRACK: S_StartBackgroundTrack( (const char*) VMA(1), (const char*) VMA(2)); return 0; case UI_REAL_TIME: return Com_RealTime( (qtime_t*) VMA(1) ); case UI_CIN_PLAYCINEMATIC: Com_DPrintf("UI_CIN_PlayCinematic\n"); return CIN_PlayCinematic( (const char*) VMA(1), args[2], args[3], args[4], args[5], args[6]); case UI_CIN_STOPCINEMATIC: return CIN_StopCinematic(args[1]); case UI_CIN_RUNCINEMATIC: return CIN_RunCinematic(args[1]); case UI_CIN_DRAWCINEMATIC: CIN_DrawCinematic(args[1]); return 0; case UI_CIN_SETEXTENTS: CIN_SetExtents(args[1], args[2], args[3], args[4], args[5]); return 0; case UI_R_REMAP_SHADER: re.RemapShader( (const char*) VMA(1), (const char*) VMA(2), (const char*) VMA(3) ); return 0; case UI_VERIFY_CDKEY: return CL_CDKeyValidate( (const char*) VMA(1), (const char*) VMA(2)); default: Com_Error( ERR_DROP, "Bad UI system trap: %i", args[0] ); } return 0; } /* ==================== CL_ShutdownUI ==================== */ void CL_ShutdownUI( void ) { cls.keyCatchers &= ~KEYCATCH_UI; cls.uiStarted = qfalse; if ( !uivm ) { return; } VM_Call( uivm, UI_SHUTDOWN ); VM_Free( uivm ); uivm = NULL; } /* ==================== CL_InitUI ==================== */ #define UI_OLD_API_VERSION 4 void CL_InitUI( void ) { int v; vmInterpret_t interpret; // load the dll or bytecode if ( cl_connectedToPureServer != 0 ) { // if sv_pure is set we only allow qvms to be loaded interpret = VMI_BYTECODE; } else { interpret = (vmInterpret_t) (int) Cvar_VariableValue( "vm_ui" ); } uivm = VM_Create( "ui", CL_UISystemCalls, interpret ); if ( !uivm ) { Com_Error( ERR_FATAL, "VM_Create on UI failed" ); } // sanity check v = VM_Call( uivm, UI_GETAPIVERSION ); if (v == UI_OLD_API_VERSION) { // Com_Printf(S_COLOR_YELLOW "WARNING: loading old Quake III Arena User Interface version %d\n", v ); // init for this gamestate VM_Call( uivm, UI_INIT, (cls.state >= CA_AUTHORIZING && cls.state < CA_ACTIVE)); } else if (v != UI_API_VERSION) { Com_Error( ERR_DROP, "User Interface is version %d, expected %d", v, UI_API_VERSION ); cls.uiStarted = qfalse; } else { // init for this gamestate VM_Call( uivm, UI_INIT, (cls.state >= CA_AUTHORIZING && cls.state < CA_ACTIVE) ); } } qboolean UI_usesUniqueCDKey() { if (uivm) { return (qboolean) (VM_Call( uivm, UI_HASUNIQUECDKEY) == qtrue); } else { return qfalse; } } /* ==================== UI_GameCommand See if the current console command is claimed by the ui ==================== */ qboolean UI_GameCommand( void ) { if ( !uivm ) { return qfalse; } return (qboolean) VM_Call( uivm, UI_CONSOLE_COMMAND, cls.realtime ); } ================================================ FILE: src/engine/client/client.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // client.h -- primary header for client #include "../../game/q_shared.h" #include "../qcommon/qcommon.h" #include "../renderer/tr_public.h" #include "../../q3_ui/ui_public.h" #include "keys.h" #include "snd_public.h" #include "../../cgame/cg_public.h" #include "../../game/bg_public.h" #define RETRANSMIT_TIMEOUT 3000 // time between connection packet retransmits // snapshots are a view of the server at a given time typedef struct { qboolean valid; // cleared if delta parsing was invalid int snapFlags; // rate delayed and dropped commands int serverTime; // server time the message is valid for (in msec) int messageNum; // copied from netchan->incoming_sequence int deltaNum; // messageNum the delta is from int ping; // time from when cmdNum-1 was sent to time packet was reeceived byte areamask[MAX_MAP_AREA_BYTES]; // portalarea visibility bits int cmdNum; // the next cmdNum the server is expecting playerState_t ps; // complete information about the current player at this time int numEntities; // all of the entities that need to be presented int parseEntitiesNum; // at the time of this snapshot int serverCommandNum; // execute all commands up to this before // making the snapshot current } clSnapshot_t; /* ============================================================================= the clientActive_t structure is wiped completely at every new gamestate_t, potentially several times during an established connection ============================================================================= */ typedef struct { int p_cmdNumber; // cl.cmdNumber when packet was sent int p_serverTime; // usercmd->serverTime when packet was sent int p_realtime; // cls.realtime when packet was sent } outPacket_t; // the parseEntities array must be large enough to hold PACKET_BACKUP frames of // entities, so that when a delta compressed message arives from the server // it can be un-deltad from the original #define MAX_PARSE_ENTITIES 2048 extern int g_console_field_width; typedef struct { int timeoutcount; // it requres several frames in a timeout condition // to disconnect, preventing debugging breaks from // causing immediate disconnects on continue clSnapshot_t snap; // latest received from server int serverTime; // may be paused during play int oldServerTime; // to prevent time from flowing bakcwards int oldFrameServerTime; // to check tournament restarts int serverTimeDelta; // cl.serverTime = cls.realtime + cl.serverTimeDelta // this value changes as net lag varies qboolean extrapolatedSnapshot; // set if any cgame frame has been forced to extrapolate // cleared when CL_AdjustTimeDelta looks at it qboolean newSnapshots; // set on parse of any valid packet gameState_t gameState; // configstrings char mapname[MAX_QPATH]; // extracted from CS_SERVERINFO int parseEntitiesNum; // index (not anded off) into cl_parse_entities[] int mouseDx[2], mouseDy[2]; // added to by mouse events int mouseIndex; int joystickAxis[MAX_JOYSTICK_AXIS]; // set by joystick events // cgame communicates a few values to the client system int cgameUserCmdValue; // current weapon to add to usercmd_t float cgameSensitivity; // cmds[cmdNumber] is the predicted command, [cmdNumber-1] is the last // properly generated command usercmd_t cmds[CMD_BACKUP]; // each mesage will send several old cmds int cmdNumber; // incremented each frame, because multiple // frames may need to be packed into a single packet outPacket_t outPackets[PACKET_BACKUP]; // information about each packet we have sent out // the client maintains its own idea of view angles, which are // sent to the server each frame. It is cleared to 0 upon entering each level. // the server sends a delta each frame which is added to the locally // tracked view angles to account for standing on rotating objects, // and teleport direction changes vec3_t viewangles; int serverId; // included in each client message so the server // can tell if it is for a prior map_restart // big stuff at end of structure so most offsets are 15 bits or less clSnapshot_t snapshots[PACKET_BACKUP]; entityState_t entityBaselines[MAX_GENTITIES]; // for delta compression when not in previous frame entityState_t parseEntities[MAX_PARSE_ENTITIES]; } clientActive_t; extern clientActive_t cl; /* ============================================================================= the clientConnection_t structure is wiped when disconnecting from a server, either to go to a full screen console, play a demo, or connect to a different server A connection can be to either a server through the network layer or a demo through a file. ============================================================================= */ typedef struct { int clientNum; int lastPacketSentTime; // for retransmits during connection int lastPacketTime; // for timeouts netadr_t serverAddress; int connectTime; // for connection retransmits int connectPacketCount; // for display on connection dialog char serverMessage[MAX_STRING_TOKENS]; // for display on connection dialog int challenge; // from the server to use for connecting int checksumFeed; // from the server for checksum calculations // these are our reliable messages that go to the server int reliableSequence; int reliableAcknowledge; // the last one the server has executed char reliableCommands[MAX_RELIABLE_COMMANDS][MAX_STRING_CHARS]; // server message (unreliable) and command (reliable) sequence // numbers are NOT cleared at level changes, but continue to // increase as long as the connection is valid // message sequence is used by both the network layer and the // delta compression layer int serverMessageSequence; // reliable messages received from server int serverCommandSequence; int lastExecutedServerCommand; // last server command grabbed or executed with CL_GetServerCommand char serverCommands[MAX_RELIABLE_COMMANDS][MAX_STRING_CHARS]; // file transfer from server fileHandle_t download; char downloadTempName[MAX_OSPATH]; char downloadName[MAX_OSPATH]; int downloadNumber; int downloadBlock; // block we are waiting for int downloadCount; // how many bytes we got int downloadSize; // how many bytes we got char downloadList[MAX_INFO_STRING]; // list of paks we need to download qboolean downloadRestart; // if true, we need to do another FS_Restart because we downloaded a pak // demo information char demoName[MAX_QPATH]; qboolean spDemoRecording; qboolean demorecording; qboolean demoplaying; qboolean demowaiting; // don't record until a non-delta message is received qboolean firstDemoFrameSkipped; fileHandle_t demofile; int timeDemoFrames; // counter of rendered frames int timeDemoStart; // cls.realtime before first frame int timeDemoBaseTime; // each frame will be at this time + frameNum * 50 // big stuff at end of structure so most offsets are 15 bits or less netchan_t netchan; } clientConnection_t; extern clientConnection_t clc; /* ================================================================== the clientStatic_t structure is never wiped, and is used even when no client connection is active at all ================================================================== */ typedef struct { netadr_t adr; int start; int time; char info[MAX_INFO_STRING]; } ping_t; typedef struct { netadr_t adr; char hostName[MAX_NAME_LENGTH]; char mapName[MAX_NAME_LENGTH]; char game[MAX_NAME_LENGTH]; int netType; int gameType; int clients; int maxClients; int minPing; int maxPing; int ping; qboolean visible; int punkbuster; } serverInfo_t; typedef struct { byte ip[4]; unsigned short port; } serverAddress_t; typedef struct { connstate_t state; // connection status int keyCatchers; // bit flags qboolean cddialog; // bring up the cd needed dialog next frame char servername[MAX_OSPATH]; // name of server from original connect (used by reconnect) // when the server clears the hunk, all of these must be restarted qboolean rendererStarted; qboolean soundStarted; qboolean soundRegistered; qboolean uiStarted; qboolean cgameStarted; int framecount; int frametime; // msec since last frame int realtime; // ignores pause int realFrametime; // ignoring pause, so console always works int numlocalservers; serverInfo_t localServers[MAX_OTHER_SERVERS]; int numglobalservers; serverInfo_t globalServers[MAX_GLOBAL_SERVERS]; // additional global servers int numGlobalServerAddresses; serverAddress_t globalServerAddresses[MAX_GLOBAL_SERVERS]; int numfavoriteservers; serverInfo_t favoriteServers[MAX_OTHER_SERVERS]; int nummplayerservers; serverInfo_t mplayerServers[MAX_OTHER_SERVERS]; int pingUpdateSource; // source currently pinging or updating int masterNum; // update server info netadr_t updateServer; char updateChallenge[MAX_TOKEN_CHARS]; char updateInfoString[MAX_INFO_STRING]; netadr_t authorizeServer; // rendering info glconfig_t glconfig; qhandle_t charSetShader; qhandle_t whiteShader; qhandle_t consoleShader; } clientStatic_t; extern clientStatic_t cls; //============================================================================= extern vm_t *cgvm; // interface to cgame dll or vm extern vm_t *uivm; // interface to ui dll or vm extern refexport_t re; // interface to refresh .dll // // cvars // extern cvar_t *cl_nodelta; extern cvar_t *cl_debugMove; extern cvar_t *cl_noprint; extern cvar_t *cl_timegraph; extern cvar_t *cl_maxpackets; extern cvar_t *cl_packetdup; extern cvar_t *cl_shownet; extern cvar_t *cl_showSend; extern cvar_t *cl_timeNudge; extern cvar_t *cl_showTimeDelta; extern cvar_t *cl_freezeDemo; extern cvar_t *cl_yawspeed; extern cvar_t *cl_pitchspeed; extern cvar_t *cl_run; extern cvar_t *cl_anglespeedkey; extern cvar_t *cl_sensitivity; extern cvar_t *cl_freelook; extern cvar_t *cl_mouseAccel; extern cvar_t *cl_showMouseRate; extern cvar_t *m_pitch; extern cvar_t *m_yaw; extern cvar_t *m_forward; extern cvar_t *m_side; extern cvar_t *m_filter; extern cvar_t *cl_timedemo; extern cvar_t *cl_activeAction; extern cvar_t *cl_allowDownload; extern cvar_t *cl_conXOffset; extern cvar_t *cl_inGameVideo; //================================================= // // cl_main // void CL_Init (void); void CL_FlushMemory(void); void CL_ShutdownAll(void); void CL_AddReliableCommand( const char *cmd ); void CL_StartHunkUsers( void ); void CL_Disconnect_f (void); void CL_Vid_Restart_f( void ); void CL_Snd_Restart_f (void); void CL_StartDemoLoop( void ); void CL_NextDemo( void ); void CL_ReadDemoMessage( void ); void CL_InitDownloads(void); void CL_NextDownload(void); void CL_GetPing( int n, char *buf, int buflen, int *pingtime ); void CL_GetPingInfo( int n, char *buf, int buflen ); void CL_ClearPing( int n ); int CL_GetPingQueueCount( void ); void CL_ShutdownRef( void ); void CL_InitRef( void ); qboolean CL_CDKeyValidate( const char *key, const char *checksum ); int CL_ServerStatus( char *serverAddress, char *serverStatusString, int maxLen ); // // cl_input // typedef struct { int down[2]; // key nums holding it down unsigned downtime; // msec timestamp unsigned msec; // msec down this frame if both a down and up happened qboolean active; // current state qboolean wasPressed; // set when down, not cleared when up } kbutton_t; extern kbutton_t in_mlook, in_klook; extern kbutton_t in_strafe; extern kbutton_t in_speed; void CL_InitInput (void); void CL_SendCmd (void); void CL_ClearState (void); void CL_ReadPackets (void); void CL_WritePacket( void ); void IN_CenterView (void); void CL_VerifyCode( void ); float CL_KeyState (kbutton_t *key); char *Key_KeynumToString (int keynum); // // cl_parse.c // extern int cl_connectedToPureServer; void CL_SystemInfoChanged( void ); void CL_ParseServerMessage( msg_t *msg ); //==================================================================== void CL_ServerInfoPacket( netadr_t from, msg_t *msg ); void CL_LocalServers_f( void ); void CL_GlobalServers_f( void ); void CL_FavoriteServers_f( void ); void CL_Ping_f( void ); qboolean CL_UpdateVisiblePings_f( int source ); // // console // void Con_DrawCharacter (int cx, int line, int num); void Con_CheckResize (void); void Con_Init (void); void Con_Clear_f (void); void Con_ToggleConsole_f (void); void Con_DrawNotify (void); void Con_ClearNotify (void); void Con_RunConsole (void); void Con_DrawConsole (void); void Con_PageUp( void ); void Con_PageDown( void ); void Con_Top( void ); void Con_Bottom( void ); void Con_Close( void ); // // cl_scrn.c // void SCR_Init (void); void SCR_UpdateScreen (void); void SCR_DebugGraph (float value, int color); int SCR_GetBigStringWidth( const char *str ); // returns in virtual 640x480 coordinates void SCR_AdjustFrom640( float *x, float *y, float *w, float *h ); void SCR_FillRect( float x, float y, float width, float height, const float *color ); void SCR_DrawPic( float x, float y, float width, float height, qhandle_t hShader ); void SCR_DrawNamedPic( float x, float y, float width, float height, const char *picname ); void SCR_DrawBigString( int x, int y, const char *s, float alpha ); // draws a string with embedded color control characters with fade void SCR_DrawBigStringColor( int x, int y, const char *s, vec4_t color ); // ignores embedded color control characters void SCR_DrawSmallStringExt( int x, int y, const char *string, float *setColor, qboolean forceColor ); void SCR_DrawSmallChar( int x, int y, int ch ); // // cl_cin.c // void CL_PlayCinematic_f( void ); void SCR_DrawCinematic (void); void SCR_RunCinematic (void); void SCR_StopCinematic (void); int CIN_PlayCinematic( const char *arg0, int xpos, int ypos, int width, int height, int bits); e_status CIN_StopCinematic(int handle); e_status CIN_RunCinematic (int handle); void CIN_DrawCinematic (int handle); void CIN_SetExtents (int handle, int x, int y, int w, int h); void CIN_SetLooping (int handle, qboolean loop); void CIN_UploadCinematic(int handle); void CIN_CloseAllVideos(void); // // cl_cgame.c // void CL_InitCGame( void ); void CL_ShutdownCGame( void ); qboolean CL_GameCommand( void ); void CL_CGameRendering( stereoFrame_t stereo ); void CL_SetCGameTime( void ); void CL_FirstSnapshot( void ); void CL_ShaderStateChanged(void); // // cl_ui.c // void CL_InitUI( void ); void CL_ShutdownUI( void ); int Key_GetCatcher( void ); void Key_SetCatcher( int catcher ); void LAN_LoadCachedServers(); void LAN_SaveServersToCache(); // // cl_net_chan.c // void CL_Netchan_Transmit( netchan_t *chan, msg_t* msg); //int length, const byte *data ); void CL_Netchan_TransmitNextFragment( netchan_t *chan ); qboolean CL_Netchan_Process( netchan_t *chan, msg_t *msg ); ================================================ FILE: src/engine/client/keys.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "../../q3_ui/keycodes.h" #define MAX_KEYS 256 typedef struct { qboolean down; int repeats; // if > 1, it is autorepeating char *binding; } qkey_t; extern qboolean key_overstrikeMode; extern qkey_t keys[MAX_KEYS]; // NOTE TTimo the declaration of field_t and Field_Clear is now in qcommon/qcommon.h void Field_KeyDownEvent( field_t *edit, int key ); void Field_CharEvent( field_t *edit, int ch ); void Field_Draw( field_t *edit, int x, int y, int width, qboolean showCursor ); void Field_BigDraw( field_t *edit, int x, int y, int width, qboolean showCursor ); #define COMMAND_HISTORY 32 extern field_t historyEditLines[COMMAND_HISTORY]; extern field_t g_consoleField; extern field_t chatField; extern qboolean anykeydown; extern qboolean chat_team; extern int chat_playerNum; void Key_WriteBindings( fileHandle_t f ); void Key_SetBinding( int keynum, const char *binding ); char *Key_GetBinding( int keynum ); qboolean Key_IsDown( int keynum ); qboolean Key_GetOverstrikeMode( void ); void Key_SetOverstrikeMode( qboolean state ); void Key_ClearStates( void ); int Key_GetKey(const char *binding); ================================================ FILE: src/engine/client/snd_adpcm.c ================================================ /*********************************************************** Copyright 1992 by Stichting Mathematisch Centrum, Amsterdam, The Netherlands. All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the names of Stichting Mathematisch Centrum or CWI not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ******************************************************************/ /* ** Intel/DVI ADPCM coder/decoder. ** ** The algorithm for this coder was taken from the IMA Compatability Project ** proceedings, Vol 2, Number 2; May 1992. ** ** Version 1.2, 18-Dec-92. */ #include "snd_local.h" /* Intel ADPCM step variation table */ static int indexTable[16] = { -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8, }; static int stepsizeTable[89] = { 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 }; void S_AdpcmEncode( short indata[], char outdata[], int len, struct adpcm_state *state ) { short *inp; /* Input buffer pointer */ signed char *outp; /* output buffer pointer */ int val; /* Current input sample value */ int sign; /* Current adpcm sign bit */ int delta; /* Current adpcm output value */ int diff; /* Difference between val and sample */ int step; /* Stepsize */ int valpred; /* Predicted output value */ int vpdiff; /* Current change to valpred */ int index; /* Current step change index */ int outputbuffer; /* place to keep previous 4-bit value */ int bufferstep; /* toggle between outputbuffer/output */ outp = (signed char *)outdata; inp = indata; valpred = state->sample; index = state->index; step = stepsizeTable[index]; outputbuffer = 0; // quiet a compiler warning bufferstep = 1; for ( ; len > 0 ; len-- ) { val = *inp++; /* Step 1 - compute difference with previous value */ diff = val - valpred; sign = (diff < 0) ? 8 : 0; if ( sign ) diff = (-diff); /* Step 2 - Divide and clamp */ /* Note: ** This code *approximately* computes: ** delta = diff*4/step; ** vpdiff = (delta+0.5)*step/4; ** but in shift step bits are dropped. The net result of this is ** that even if you have fast mul/div hardware you cannot put it to ** good use since the fixup would be too expensive. */ delta = 0; vpdiff = (step >> 3); if ( diff >= step ) { delta = 4; diff -= step; vpdiff += step; } step >>= 1; if ( diff >= step ) { delta |= 2; diff -= step; vpdiff += step; } step >>= 1; if ( diff >= step ) { delta |= 1; vpdiff += step; } /* Step 3 - Update previous value */ if ( sign ) valpred -= vpdiff; else valpred += vpdiff; /* Step 4 - Clamp previous value to 16 bits */ if ( valpred > 32767 ) valpred = 32767; else if ( valpred < -32768 ) valpred = -32768; /* Step 5 - Assemble value, update index and step values */ delta |= sign; index += indexTable[delta]; if ( index < 0 ) index = 0; if ( index > 88 ) index = 88; step = stepsizeTable[index]; /* Step 6 - Output value */ if ( bufferstep ) { outputbuffer = (delta << 4) & 0xf0; } else { *outp++ = (delta & 0x0f) | outputbuffer; } bufferstep = !bufferstep; } /* Output last step, if needed */ if ( !bufferstep ) *outp++ = outputbuffer; state->sample = valpred; state->index = index; } /* static */ void S_AdpcmDecode( const char indata[], short *outdata, int len, struct adpcm_state *state ) { signed char *inp; /* Input buffer pointer */ int outp; /* output buffer pointer */ int sign; /* Current adpcm sign bit */ int delta; /* Current adpcm output value */ int step; /* Stepsize */ int valpred; /* Predicted value */ int vpdiff; /* Current change to valpred */ int index; /* Current step change index */ int inputbuffer; /* place to keep next 4-bit value */ int bufferstep; /* toggle between inputbuffer/input */ outp = 0; inp = (signed char *)indata; valpred = state->sample; index = state->index; step = stepsizeTable[index]; bufferstep = 0; inputbuffer = 0; // quiet a compiler warning for ( ; len > 0 ; len-- ) { /* Step 1 - get the delta value */ if ( bufferstep ) { delta = inputbuffer & 0xf; } else { inputbuffer = *inp++; delta = (inputbuffer >> 4) & 0xf; } bufferstep = !bufferstep; /* Step 2 - Find new index value (for later) */ index += indexTable[delta]; if ( index < 0 ) index = 0; if ( index > 88 ) index = 88; /* Step 3 - Separate sign and magnitude */ sign = delta & 8; delta = delta & 7; /* Step 4 - Compute difference and new predicted value */ /* ** Computes 'vpdiff = (delta+0.5)*step/4', but see comment ** in adpcm_coder. */ vpdiff = step >> 3; if ( delta & 4 ) vpdiff += step; if ( delta & 2 ) vpdiff += step>>1; if ( delta & 1 ) vpdiff += step>>2; if ( sign ) valpred -= vpdiff; else valpred += vpdiff; /* Step 5 - clamp output value */ if ( valpred > 32767 ) valpred = 32767; else if ( valpred < -32768 ) valpred = -32768; /* Step 6 - Update step value */ step = stepsizeTable[index]; /* Step 7 - Output value */ outdata[outp] = valpred; outp++; } state->sample = valpred; state->index = index; } /* ==================== S_AdpcmMemoryNeeded Returns the amount of memory (in bytes) needed to store the samples in out internal adpcm format ==================== */ int S_AdpcmMemoryNeeded( const wavinfo_t *info ) { float scale; int scaledSampleCount; int sampleMemory; int blockCount; int headerMemory; // determine scale to convert from input sampling rate to desired sampling rate scale = (float)info->rate / dma.speed; // calc number of samples at playback sampling rate scaledSampleCount = info->samples / scale; // calc memory need to store those samples using ADPCM at 4 bits per sample sampleMemory = scaledSampleCount / 2; // calc number of sample blocks needed of PAINTBUFFER_SIZE blockCount = scaledSampleCount / PAINTBUFFER_SIZE; if( scaledSampleCount % PAINTBUFFER_SIZE ) { blockCount++; } // calc memory needed to store the block headers headerMemory = blockCount * sizeof(adpcm_state_t); return sampleMemory + headerMemory; } /* ==================== S_AdpcmGetSamples ==================== */ void S_AdpcmGetSamples(sndBuffer *chunk, short *to) { adpcm_state_t state; byte *out; // get the starting state from the block header state.index = chunk->adpcm.index; state.sample = chunk->adpcm.sample; out = (byte *)chunk->sndChunk; // get samples S_AdpcmDecode( (const char*) out, to, SND_CHUNK_SIZE_BYTE*2, &state ); } /* ==================== S_AdpcmEncodeSound ==================== */ void S_AdpcmEncodeSound( sfx_t *sfx, short *samples ) { adpcm_state_t state; int inOffset; int count; int n; sndBuffer *newchunk, *chunk; byte *out; inOffset = 0; count = sfx->soundLength; state.index = 0; state.sample = samples[0]; chunk = NULL; while( count ) { n = count; if( n > SND_CHUNK_SIZE_BYTE*2 ) { n = SND_CHUNK_SIZE_BYTE*2; } newchunk = SND_malloc(); if (sfx->soundData == NULL) { sfx->soundData = newchunk; } else { chunk->next = newchunk; } chunk = newchunk; // output the header chunk->adpcm.index = state.index; chunk->adpcm.sample = state.sample; out = (byte *)chunk->sndChunk; // encode the samples S_AdpcmEncode( samples + inOffset, (char*) out, n, &state ); inOffset += n; count -= n; } } ================================================ FILE: src/engine/client/snd_dma.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: snd_dma.c * * desc: main control for any streaming sound output device * * $Archive: /MissionPack/code/client/snd_dma.c $ * *****************************************************************************/ #include "snd_local.h" #include "client.h" void S_Play_f(void); void S_SoundList_f(void); void S_Music_f(void); void S_Update_(); void S_StopAllSounds(void); void S_UpdateBackgroundTrack( void ); static fileHandle_t s_backgroundFile; static wavinfo_t s_backgroundInfo; //int s_nextWavChunk; static int s_backgroundSamples; static char s_backgroundLoop[MAX_QPATH]; //static char s_backgroundMusic[MAX_QPATH]; //TTimo: unused // ======================================================================= // Internal sound data & structures // ======================================================================= // only begin attenuating sound volumes when outside the FULLVOLUME range #define SOUND_FULLVOLUME 80 #define SOUND_ATTENUATE 0.0008f channel_t s_channels[MAX_CHANNELS]; channel_t loop_channels[MAX_CHANNELS]; int numLoopChannels; static int s_soundStarted; static qboolean s_soundMuted; dma_t dma; static int listener_number; static vec3_t listener_origin; static vec3_t listener_axis[3]; int s_soundtime; // sample PAIRS int s_paintedtime; // sample PAIRS // MAX_SFX may be larger than MAX_SOUNDS because // of custom player sounds #define MAX_SFX 4096 sfx_t s_knownSfx[MAX_SFX]; int s_numSfx = 0; #define LOOP_HASH 128 static sfx_t *sfxHash[LOOP_HASH]; cvar_t *s_volume; cvar_t *s_testsound; cvar_t *s_khz; cvar_t *s_show; cvar_t *s_mixahead; cvar_t *s_mixPreStep; cvar_t *s_musicVolume; cvar_t *s_separation; cvar_t *s_doppler; static loopSound_t loopSounds[MAX_GENTITIES]; static channel_t *freelist = NULL; int s_rawend; portable_samplepair_t s_rawsamples[MAX_RAW_SAMPLES]; // ==================================================================== // User-setable variables // ==================================================================== void S_SoundInfo_f(void) { Com_Printf("----- Sound Info -----\n" ); if (!s_soundStarted) { Com_Printf ("sound system not started\n"); } else { if ( s_soundMuted ) { Com_Printf ("sound system is muted\n"); } Com_Printf("%5d stereo\n", dma.channels - 1); Com_Printf("%5d samples\n", dma.samples); Com_Printf("%5d samplebits\n", dma.samplebits); Com_Printf("%5d submission_chunk\n", dma.submission_chunk); Com_Printf("%5d speed\n", dma.speed); Com_Printf("0x%x dma buffer\n", dma.buffer); if ( s_backgroundFile ) { Com_Printf("Background file: %s\n", s_backgroundLoop ); } else { Com_Printf("No background file.\n" ); } } Com_Printf("----------------------\n" ); } /* ================ S_Init ================ */ void S_Init( void ) { cvar_t *cv; qboolean r; Com_Printf("\n------- sound initialization -------\n"); s_volume = Cvar_Get ("s_volume", "0.8", CVAR_ARCHIVE); s_musicVolume = Cvar_Get ("s_musicvolume", "0.25", CVAR_ARCHIVE); s_separation = Cvar_Get ("s_separation", "0.5", CVAR_ARCHIVE); s_doppler = Cvar_Get ("s_doppler", "1", CVAR_ARCHIVE); s_khz = Cvar_Get ("s_khz", "22", CVAR_ARCHIVE); s_mixahead = Cvar_Get ("s_mixahead", "0.2", CVAR_ARCHIVE); s_mixPreStep = Cvar_Get ("s_mixPreStep", "0.05", CVAR_ARCHIVE); s_show = Cvar_Get ("s_show", "0", CVAR_CHEAT); s_testsound = Cvar_Get ("s_testsound", "0", CVAR_CHEAT); cv = Cvar_Get ("s_initsound", "1", 0); if ( !cv->integer ) { Com_Printf ("not initializing.\n"); Com_Printf("------------------------------------\n"); return; } Cmd_AddCommand("play", S_Play_f); Cmd_AddCommand("music", S_Music_f); Cmd_AddCommand("s_list", S_SoundList_f); Cmd_AddCommand("s_info", S_SoundInfo_f); Cmd_AddCommand("s_stop", S_StopAllSounds); r = SNDDMA_Init(); Com_Printf("------------------------------------\n"); if ( r ) { s_soundStarted = 1; s_soundMuted = (qboolean) 1; // s_numSfx = 0; Com_Memset(sfxHash, 0, sizeof(sfx_t *)*LOOP_HASH); s_soundtime = 0; s_paintedtime = 0; S_StopAllSounds (); S_SoundInfo_f(); } } void S_ChannelFree(channel_t *v) { v->thesfx = NULL; *(channel_t **)v = freelist; freelist = (channel_t*)v; } channel_t* S_ChannelMalloc() { channel_t *v; if (freelist == NULL) { return NULL; } v = freelist; freelist = *(channel_t **)freelist; v->allocTime = Com_Milliseconds(); return v; } void S_ChannelSetup() { channel_t *p, *q; // clear all the sounds so they don't Com_Memset( s_channels, 0, sizeof( s_channels ) ); p = s_channels;; q = p + MAX_CHANNELS; while (--q > p) { *(channel_t **)q = q-1; } *(channel_t **)q = NULL; freelist = p + MAX_CHANNELS - 1; Com_DPrintf("Channel memory manager started\n"); } // ======================================================================= // Shutdown sound engine // ======================================================================= void S_Shutdown( void ) { if ( !s_soundStarted ) { return; } SNDDMA_Shutdown(); s_soundStarted = 0; Cmd_RemoveCommand("play"); Cmd_RemoveCommand("music"); Cmd_RemoveCommand("stopsound"); Cmd_RemoveCommand("soundlist"); Cmd_RemoveCommand("soundinfo"); } // ======================================================================= // Load a sound // ======================================================================= /* ================ return a hash value for the sfx name ================ */ static long S_HashSFXName(const char *name) { int i; long hash; char letter; hash = 0; i = 0; while (name[i] != '\0') { letter = tolower(name[i]); if (letter =='.') break; // don't include extension if (letter =='\\') letter = '/'; // damn path names hash+=(long)(letter)*(i+119); i++; } hash &= (LOOP_HASH-1); return hash; } /* ================== S_FindName Will allocate a new sfx if it isn't found ================== */ static sfx_t *S_FindName( const char *name ) { int i; int hash; sfx_t *sfx; if (!name) { Com_Error (ERR_FATAL, "S_FindName: NULL\n"); } if (!name[0]) { Com_Error (ERR_FATAL, "S_FindName: empty name\n"); } if (strlen(name) >= MAX_QPATH) { Com_Error (ERR_FATAL, "Sound name too long: %s", name); } hash = S_HashSFXName(name); sfx = sfxHash[hash]; // see if already loaded while (sfx) { if (!Q_stricmp(sfx->soundName, name) ) { return sfx; } sfx = sfx->next; } // find a free sfx for (i=0 ; i < s_numSfx ; i++) { if (!s_knownSfx[i].soundName[0]) { break; } } if (i == s_numSfx) { if (s_numSfx == MAX_SFX) { Com_Error (ERR_FATAL, "S_FindName: out of sfx_t"); } s_numSfx++; } sfx = &s_knownSfx[i]; Com_Memset (sfx, 0, sizeof(*sfx)); strcpy (sfx->soundName, name); sfx->next = sfxHash[hash]; sfxHash[hash] = sfx; return sfx; } /* ================= S_DefaultSound ================= */ void S_DefaultSound( sfx_t *sfx ) { int i; sfx->soundLength = 512; sfx->soundData = SND_malloc(); sfx->soundData->next = NULL; for ( i = 0 ; i < sfx->soundLength ; i++ ) { sfx->soundData->sndChunk[i] = i; } } /* =================== S_DisableSounds Disables sounds until the next S_BeginRegistration. This is called when the hunk is cleared and the sounds are no longer valid. =================== */ void S_DisableSounds( void ) { S_StopAllSounds(); s_soundMuted = qtrue; } /* ===================== S_BeginRegistration ===================== */ void S_BeginRegistration( void ) { s_soundMuted = qfalse; // we can play again if (s_numSfx == 0) { SND_setup(); s_numSfx = 0; Com_Memset( s_knownSfx, 0, sizeof( s_knownSfx ) ); Com_Memset(sfxHash, 0, sizeof(sfx_t *)*LOOP_HASH); S_RegisterSound("sound/feedback/hit.wav", qfalse); // changed to a sound in baseq3 } } /* ================== S_RegisterSound Creates a default buzz sound if the file can't be loaded ================== */ sfxHandle_t S_RegisterSound( const char *name, qboolean compressed ) { sfx_t *sfx; compressed = qfalse; if (!s_soundStarted) { return 0; } if ( (int)strlen( name ) >= MAX_QPATH ) { Com_Printf( "Sound name exceeds MAX_QPATH\n" ); return 0; } sfx = S_FindName( name ); if ( sfx->soundData ) { if ( sfx->defaultSound ) { Com_Printf( S_COLOR_YELLOW "WARNING: could not find %s - using default\n", sfx->soundName ); return 0; } return sfx - s_knownSfx; } sfx->inMemory = qfalse; sfx->soundCompressed = compressed; S_memoryLoad(sfx); if ( sfx->defaultSound ) { Com_Printf( S_COLOR_YELLOW "WARNING: could not find %s - using default\n", sfx->soundName ); return 0; } return sfx - s_knownSfx; } void S_memoryLoad(sfx_t *sfx) { // load the sound file if ( !S_LoadSound ( sfx ) ) { // Com_Printf( S_COLOR_YELLOW "WARNING: couldn't load sound: %s\n", sfx->soundName ); sfx->defaultSound = qtrue; } sfx->inMemory = qtrue; } //============================================================================= /* ================= S_SpatializeOrigin Used for spatializing s_channels ================= */ void S_SpatializeOrigin (vec3_t origin, int master_vol, int *left_vol, int *right_vol) { vec_t dot; vec_t dist; vec_t lscale, rscale, scale; vec3_t source_vec; vec3_t vec; const float dist_mult = SOUND_ATTENUATE; // calculate stereo seperation and distance attenuation VectorSubtract(origin, listener_origin, source_vec); dist = VectorNormalize(source_vec); dist -= SOUND_FULLVOLUME; if (dist < 0) dist = 0; // close enough to be at full volume dist *= dist_mult; // different attenuation levels VectorRotate( source_vec, listener_axis, vec ); dot = -vec[1]; if (dma.channels == 1) { // no attenuation = no spatialization rscale = 1.0; lscale = 1.0; } else { rscale = 0.5 * (1.0 + dot); lscale = 0.5 * (1.0 - dot); //rscale = s_separation->value + ( 1.0 - s_separation->value ) * dot; //lscale = s_separation->value - ( 1.0 - s_separation->value ) * dot; if ( rscale < 0 ) { rscale = 0; } if ( lscale < 0 ) { lscale = 0; } } // add in distance effect scale = (1.0 - dist) * rscale; *right_vol = (master_vol * scale); if (*right_vol < 0) *right_vol = 0; scale = (1.0 - dist) * lscale; *left_vol = (master_vol * scale); if (*left_vol < 0) *left_vol = 0; } // ======================================================================= // Start a sound effect // ======================================================================= /* ==================== S_StartSound Validates the parms and ques the sound up if pos is NULL, the sound will be dynamically sourced from the entity Entchannel 0 will never override a playing sound ==================== */ void S_StartSound(vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfxHandle ) { channel_t *ch; sfx_t *sfx; int i, oldest, chosen, time; int inplay, allowed; if ( !s_soundStarted || s_soundMuted ) { return; } if ( !origin && ( entityNum < 0 || entityNum > MAX_GENTITIES ) ) { Com_Error( ERR_DROP, "S_StartSound: bad entitynum %i", entityNum ); } if ( sfxHandle < 0 || sfxHandle >= s_numSfx ) { Com_Printf( S_COLOR_YELLOW, "S_StartSound: handle %i out of range\n", sfxHandle ); return; } sfx = &s_knownSfx[ sfxHandle ]; if (sfx->inMemory == qfalse) { S_memoryLoad(sfx); } if ( s_show->integer == 1 ) { Com_Printf( "%i : %s\n", s_paintedtime, sfx->soundName ); } time = Com_Milliseconds(); // Com_Printf("playing %s\n", sfx->soundName); // pick a channel to play on allowed = 4; if (entityNum == listener_number) { allowed = 8; } ch = s_channels; inplay = 0; for ( i = 0; i < MAX_CHANNELS ; i++, ch++ ) { if (ch[i].entnum == entityNum && ch[i].thesfx == sfx) { if (time - ch[i].allocTime < 50) { // if (Cvar_VariableValue( "cg_showmiss" )) { // Com_Printf("double sound start\n"); // } return; } inplay++; } } if (inplay>allowed) { return; } sfx->lastTimeUsed = time; ch = S_ChannelMalloc(); // entityNum, entchannel); if (!ch) { ch = s_channels; oldest = sfx->lastTimeUsed; chosen = -1; for ( i = 0 ; i < MAX_CHANNELS ; i++, ch++ ) { if (ch->entnum != listener_number && ch->entnum == entityNum && ch->allocTimeentchannel != CHAN_ANNOUNCER) { oldest = ch->allocTime; chosen = i; } } if (chosen == -1) { ch = s_channels; for ( i = 0 ; i < MAX_CHANNELS ; i++, ch++ ) { if (ch->entnum != listener_number && ch->allocTimeentchannel != CHAN_ANNOUNCER) { oldest = ch->allocTime; chosen = i; } } if (chosen == -1) { if (ch->entnum == listener_number) { for ( i = 0 ; i < MAX_CHANNELS ; i++, ch++ ) { if (ch->allocTimeallocTime; chosen = i; } } } if (chosen == -1) { Com_Printf("dropping sound\n"); return; } } } ch = &s_channels[chosen]; ch->allocTime = sfx->lastTimeUsed; } if (origin) { VectorCopy (origin, ch->origin); ch->fixed_origin = qtrue; } else { ch->fixed_origin = qfalse; } ch->master_vol = 127; ch->entnum = entityNum; ch->thesfx = sfx; ch->startSample = START_SAMPLE_IMMEDIATE; ch->entchannel = entchannel; ch->leftvol = ch->master_vol; // these will get calced at next spatialize ch->rightvol = ch->master_vol; // unless the game isn't running ch->doppler = qfalse; } /* ================== S_StartLocalSound ================== */ void S_StartLocalSound( sfxHandle_t sfxHandle, int channelNum ) { if ( !s_soundStarted || s_soundMuted ) { return; } if ( sfxHandle < 0 || sfxHandle >= s_numSfx ) { Com_Printf( S_COLOR_YELLOW, "S_StartLocalSound: handle %i out of range\n", sfxHandle ); return; } S_StartSound (NULL, listener_number, channelNum, sfxHandle ); } /* ================== S_ClearSoundBuffer If we are about to perform file access, clear the buffer so sound doesn't stutter. ================== */ void S_ClearSoundBuffer( void ) { int clear; if (!s_soundStarted) return; // stop looping sounds Com_Memset(loopSounds, 0, MAX_GENTITIES*sizeof(loopSound_t)); Com_Memset(loop_channels, 0, MAX_CHANNELS*sizeof(channel_t)); numLoopChannels = 0; S_ChannelSetup(); s_rawend = 0; if (dma.samplebits == 8) clear = 0x80; else clear = 0; SNDDMA_BeginPainting (); if (dma.buffer) // TTimo: due to a particular bug workaround in linux sound code, // have to optionally use a custom C implementation of Com_Memset // not affecting win32, we have #define Snd_Memset Com_Memset // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=371 Snd_Memset(dma.buffer, clear, dma.samples * dma.samplebits/8); SNDDMA_Submit (); } /* ================== S_StopAllSounds ================== */ void S_StopAllSounds(void) { if ( !s_soundStarted ) { return; } // stop the background music S_StopBackgroundTrack(); S_ClearSoundBuffer (); } /* ============================================================== continuous looping sounds are added each frame ============================================================== */ void S_StopLoopingSound(int entityNum) { loopSounds[entityNum].active = qfalse; // loopSounds[entityNum].sfx = 0; loopSounds[entityNum].kill = qfalse; } /* ================== S_ClearLoopingSounds ================== */ void S_ClearLoopingSounds( qboolean killall ) { int i; for ( i = 0 ; i < MAX_GENTITIES ; i++) { if (killall || loopSounds[i].kill == qtrue || (loopSounds[i].sfx && loopSounds[i].sfx->soundLength == 0)) { loopSounds[i].kill = qfalse; S_StopLoopingSound(i); } } numLoopChannels = 0; } /* ================== S_AddLoopingSound Called during entity generation for a frame Include velocity in case I get around to doing doppler... ================== */ void S_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfxHandle ) { sfx_t *sfx; if ( !s_soundStarted || s_soundMuted ) { return; } if ( sfxHandle < 0 || sfxHandle >= s_numSfx ) { Com_Printf( S_COLOR_YELLOW, "S_AddLoopingSound: handle %i out of range\n", sfxHandle ); return; } sfx = &s_knownSfx[ sfxHandle ]; if (sfx->inMemory == qfalse) { S_memoryLoad(sfx); } if ( !sfx->soundLength ) { Com_Error( ERR_DROP, "%s has length 0", sfx->soundName ); } VectorCopy( origin, loopSounds[entityNum].origin ); VectorCopy( velocity, loopSounds[entityNum].velocity ); loopSounds[entityNum].active = qtrue; loopSounds[entityNum].kill = qtrue; loopSounds[entityNum].doppler = qfalse; loopSounds[entityNum].oldDopplerScale = 1.0; loopSounds[entityNum].dopplerScale = 1.0; loopSounds[entityNum].sfx = sfx; if (s_doppler->integer && VectorLengthSquared(velocity)>0.0) { vec3_t out; float lena, lenb; loopSounds[entityNum].doppler = qtrue; lena = DistanceSquared(loopSounds[listener_number].origin, loopSounds[entityNum].origin); VectorAdd(loopSounds[entityNum].origin, loopSounds[entityNum].velocity, out); lenb = DistanceSquared(loopSounds[listener_number].origin, out); if ((loopSounds[entityNum].framenum+1) != cls.framecount) { loopSounds[entityNum].oldDopplerScale = 1.0; } else { loopSounds[entityNum].oldDopplerScale = loopSounds[entityNum].dopplerScale; } loopSounds[entityNum].dopplerScale = lenb/(lena*100); if (loopSounds[entityNum].dopplerScale<=1.0) { loopSounds[entityNum].doppler = qfalse; // don't bother doing the math } } loopSounds[entityNum].framenum = cls.framecount; } /* ================== S_AddLoopingSound Called during entity generation for a frame Include velocity in case I get around to doing doppler... ================== */ void S_AddRealLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfxHandle ) { sfx_t *sfx; if ( !s_soundStarted || s_soundMuted ) { return; } if ( sfxHandle < 0 || sfxHandle >= s_numSfx ) { Com_Printf( S_COLOR_YELLOW, "S_AddRealLoopingSound: handle %i out of range\n", sfxHandle ); return; } sfx = &s_knownSfx[ sfxHandle ]; if (sfx->inMemory == qfalse) { S_memoryLoad(sfx); } if ( !sfx->soundLength ) { Com_Error( ERR_DROP, "%s has length 0", sfx->soundName ); } VectorCopy( origin, loopSounds[entityNum].origin ); VectorCopy( velocity, loopSounds[entityNum].velocity ); loopSounds[entityNum].sfx = sfx; loopSounds[entityNum].active = qtrue; loopSounds[entityNum].kill = qfalse; loopSounds[entityNum].doppler = qfalse; } /* ================== S_AddLoopSounds Spatialize all of the looping sounds. All sounds are on the same cycle, so any duplicates can just sum up the channel multipliers. ================== */ void S_AddLoopSounds (void) { int i, j, time; int left_total, right_total, left, right; channel_t *ch; loopSound_t *loop, *loop2; static int loopFrame; numLoopChannels = 0; time = Com_Milliseconds(); loopFrame++; for ( i = 0 ; i < MAX_GENTITIES ; i++) { loop = &loopSounds[i]; if ( !loop->active || loop->mergeFrame == loopFrame ) { continue; // already merged into an earlier sound } if (loop->kill) { S_SpatializeOrigin( loop->origin, 127, &left_total, &right_total); // 3d } else { S_SpatializeOrigin( loop->origin, 90, &left_total, &right_total); // sphere } loop->sfx->lastTimeUsed = time; for (j=(i+1); j< MAX_GENTITIES ; j++) { loop2 = &loopSounds[j]; if ( !loop2->active || loop2->doppler || loop2->sfx != loop->sfx) { continue; } loop2->mergeFrame = loopFrame; if (loop2->kill) { S_SpatializeOrigin( loop2->origin, 127, &left, &right); // 3d } else { S_SpatializeOrigin( loop2->origin, 90, &left, &right); // sphere } loop2->sfx->lastTimeUsed = time; left_total += left; right_total += right; } if (left_total == 0 && right_total == 0) { continue; // not audible } // allocate a channel ch = &loop_channels[numLoopChannels]; if (left_total > 255) { left_total = 255; } if (right_total > 255) { right_total = 255; } ch->master_vol = 127; ch->leftvol = left_total; ch->rightvol = right_total; ch->thesfx = loop->sfx; ch->doppler = loop->doppler; ch->dopplerScale = loop->dopplerScale; ch->oldDopplerScale = loop->oldDopplerScale; numLoopChannels++; if (numLoopChannels == MAX_CHANNELS) { return; } } } //============================================================================= /* ================= S_ByteSwapRawSamples If raw data has been loaded in little endien binary form, this must be done. If raw data was calculated, as with ADPCM, this should not be called. ================= */ void S_ByteSwapRawSamples( int samples, int width, int s_channels, const byte *data ) { int i; if ( width != 2 ) { return; } if ( LittleShort( 256 ) == 256 ) { return; } if ( s_channels == 2 ) { samples <<= 1; } for ( i = 0 ; i < samples ; i++ ) { ((short *)data)[i] = LittleShort( ((short *)data)[i] ); } } portable_samplepair_t *S_GetRawSamplePointer() { return s_rawsamples; } /* ============ S_RawSamples Music streaming ============ */ void S_RawSamples( int samples, int rate, int width, int s_channels, const byte *data, float volume ) { int i; int src, dst; float scale; int intVolume; if ( !s_soundStarted || s_soundMuted ) { return; } intVolume = 256 * volume; if ( s_rawend < s_soundtime ) { Com_DPrintf( "S_RawSamples: resetting minimum: %i < %i\n", s_rawend, s_soundtime ); s_rawend = s_soundtime; } scale = (float)rate / dma.speed; //Com_Printf ("%i < %i < %i\n", s_soundtime, s_paintedtime, s_rawend); if (s_channels == 2 && width == 2) { if (scale == 1.0) { // optimized case for (i=0 ; i= samples) break; dst = s_rawend&(MAX_RAW_SAMPLES-1); s_rawend++; s_rawsamples[dst].left = ((short *)data)[src*2] * intVolume; s_rawsamples[dst].right = ((short *)data)[src*2+1] * intVolume; } } } else if (s_channels == 1 && width == 2) { for (i=0 ; ; i++) { src = i*scale; if (src >= samples) break; dst = s_rawend&(MAX_RAW_SAMPLES-1); s_rawend++; s_rawsamples[dst].left = ((short *)data)[src] * intVolume; s_rawsamples[dst].right = ((short *)data)[src] * intVolume; } } else if (s_channels == 2 && width == 1) { intVolume *= 256; for (i=0 ; ; i++) { src = i*scale; if (src >= samples) break; dst = s_rawend&(MAX_RAW_SAMPLES-1); s_rawend++; s_rawsamples[dst].left = ((char *)data)[src*2] * intVolume; s_rawsamples[dst].right = ((char *)data)[src*2+1] * intVolume; } } else if (s_channels == 1 && width == 1) { intVolume *= 256; for (i=0 ; ; i++) { src = i*scale; if (src >= samples) break; dst = s_rawend&(MAX_RAW_SAMPLES-1); s_rawend++; s_rawsamples[dst].left = (((byte *)data)[src]-128) * intVolume; s_rawsamples[dst].right = (((byte *)data)[src]-128) * intVolume; } } if ( s_rawend > s_soundtime + MAX_RAW_SAMPLES ) { Com_DPrintf( "S_RawSamples: overflowed %i > %i\n", s_rawend, s_soundtime ); } } //============================================================================= /* ===================== S_UpdateEntityPosition let the sound system know where an entity currently is ====================== */ void S_UpdateEntityPosition( int entityNum, const vec3_t origin ) { if ( entityNum < 0 || entityNum > MAX_GENTITIES ) { Com_Error( ERR_DROP, "S_UpdateEntityPosition: bad entitynum %i", entityNum ); } VectorCopy( origin, loopSounds[entityNum].origin ); } /* ============ S_Respatialize Change the volumes of all the playing sounds for changes in their positions ============ */ void S_Respatialize( int entityNum, const vec3_t head, vec3_t axis[3], int inwater ) { int i; channel_t *ch; vec3_t origin; if ( !s_soundStarted || s_soundMuted ) { return; } listener_number = entityNum; VectorCopy(head, listener_origin); VectorCopy(axis[0], listener_axis[0]); VectorCopy(axis[1], listener_axis[1]); VectorCopy(axis[2], listener_axis[2]); // update spatialization for dynamic sounds ch = s_channels; for ( i = 0 ; i < MAX_CHANNELS ; i++, ch++ ) { if ( !ch->thesfx ) { continue; } // anything coming from the view entity will always be full volume if (ch->entnum == listener_number) { ch->leftvol = ch->master_vol; ch->rightvol = ch->master_vol; } else { if (ch->fixed_origin) { VectorCopy( ch->origin, origin ); } else { VectorCopy( loopSounds[ ch->entnum ].origin, origin ); } S_SpatializeOrigin (origin, ch->master_vol, &ch->leftvol, &ch->rightvol); } } // add loopsounds S_AddLoopSounds (); } /* ======================== S_ScanChannelStarts Returns qtrue if any new sounds were started since the last mix ======================== */ qboolean S_ScanChannelStarts( void ) { channel_t *ch; int i; qboolean newSamples; newSamples = qfalse; ch = s_channels; for (i=0; ithesfx ) { continue; } // if this channel was just started this frame, // set the sample count to it begins mixing // into the very first sample if ( ch->startSample == START_SAMPLE_IMMEDIATE ) { ch->startSample = s_paintedtime; newSamples = qtrue; continue; } // if it is completely finished by now, clear it if ( ch->startSample + (ch->thesfx->soundLength) <= s_paintedtime ) { S_ChannelFree(ch); } } return newSamples; } /* ============ S_Update Called once each time through the main loop ============ */ void S_Update( void ) { int i; int total; channel_t *ch; if ( !s_soundStarted || s_soundMuted ) { Com_DPrintf ("not started or muted\n"); return; } // // debugging output // if ( s_show->integer == 2 ) { total = 0; ch = s_channels; for (i=0 ; ithesfx && (ch->leftvol || ch->rightvol) ) { Com_Printf ("%f %f %s\n", ch->leftvol, ch->rightvol, ch->thesfx->soundName); total++; } } Com_Printf ("----(%i)---- painted: %i\n", total, s_paintedtime); } // add raw data from streamed samples S_UpdateBackgroundTrack(); // mix some sound S_Update_(); } void S_GetSoundtime(void) { int samplepos; static int buffers; static int oldsamplepos; int fullsamples; fullsamples = dma.samples / dma.channels; // it is possible to miscount buffers if it has wrapped twice between // calls to S_Update. Oh well. samplepos = SNDDMA_GetDMAPos(); if (samplepos < oldsamplepos) { buffers++; // buffer wrapped if (s_paintedtime > 0x40000000) { // time to chop things off to avoid 32 bit limits buffers = 0; s_paintedtime = fullsamples; S_StopAllSounds (); } } oldsamplepos = samplepos; s_soundtime = buffers*fullsamples + samplepos/dma.channels; #if 0 // check to make sure that we haven't overshot if (s_paintedtime < s_soundtime) { Com_DPrintf ("S_Update_ : overflow\n"); s_paintedtime = s_soundtime; } #endif if ( dma.submission_chunk < 256 ) { s_paintedtime = s_soundtime + s_mixPreStep->value * dma.speed; } else { s_paintedtime = s_soundtime + dma.submission_chunk; } } void S_Update_(void) { unsigned endtime; int samps; static float lastTime = 0.0f; float ma, op; float thisTime, sane; static int ot = -1; if ( !s_soundStarted || s_soundMuted ) { return; } thisTime = Com_Milliseconds(); // Updates s_soundtime S_GetSoundtime(); if (s_soundtime == ot) { return; } ot = s_soundtime; // clear any sound effects that end before the current time, // and start any new sounds S_ScanChannelStarts(); sane = thisTime - lastTime; if (sane<11) { sane = 11; // 85hz } ma = s_mixahead->value * dma.speed; op = s_mixPreStep->value + sane*dma.speed*0.01; if (op < ma) { ma = op; } // mix ahead of current position endtime = s_soundtime + ma; // mix to an even submission block size endtime = (endtime + dma.submission_chunk-1) & ~(dma.submission_chunk-1); // never mix more than the complete buffer samps = dma.samples >> (dma.channels-1); if (endtime - s_soundtime > samps) endtime = s_soundtime + samps; SNDDMA_BeginPainting (); S_PaintChannels (endtime); SNDDMA_Submit (); lastTime = thisTime; } /* =============================================================================== console functions =============================================================================== */ void S_Play_f( void ) { int i; sfxHandle_t h; char name[256]; i = 1; while ( i [loopfile]\n"); return; } } void S_SoundList_f( void ) { int i; sfx_t *sfx; int size, total; char type[4][16]; char mem[2][16]; strcpy(type[0], "16bit"); strcpy(type[1], "adpcm"); strcpy(type[2], "daub4"); strcpy(type[3], "mulaw"); strcpy(mem[0], "paged out"); strcpy(mem[1], "resident "); total = 0; for (sfx=s_knownSfx, i=0 ; isoundLength; total += size; Com_Printf("%6i[%s] : %s[%s]\n", size, type[sfx->soundCompressionMethod], sfx->soundName, mem[sfx->inMemory] ); } Com_Printf ("Total resident: %i\n", total); S_DisplayFreeMemory(); } /* =============================================================================== background music functions =============================================================================== */ int FGetLittleLong( fileHandle_t f ) { int v; FS_Read( &v, sizeof(v), f ); return LittleLong( v); } int FGetLittleShort( fileHandle_t f ) { short v; FS_Read( &v, sizeof(v), f ); return LittleShort( v); } // returns the length of the data in the chunk, or 0 if not found int S_FindWavChunk( fileHandle_t f, char *chunk ) { char name[5]; int len; int r; name[4] = 0; len = 0; r = FS_Read( name, 4, f ); if ( r != 4 ) { return 0; } len = FGetLittleLong( f ); if ( len < 0 || len > 0xfffffff ) { len = 0; return 0; } len = (len + 1 ) & ~1; // pad to word boundary // s_nextWavChunk += len + 8; if ( strcmp( name, chunk ) ) { return 0; } return len; } /* ====================== S_StopBackgroundTrack ====================== */ void S_StopBackgroundTrack( void ) { if ( !s_backgroundFile ) { return; } Sys_EndStreamedFile( s_backgroundFile ); FS_FCloseFile( s_backgroundFile ); s_backgroundFile = 0; s_rawend = 0; } /* ====================== S_StartBackgroundTrack ====================== */ void S_StartBackgroundTrack( const char *intro, const char *loop ){ int len; char dump[16]; char name[MAX_QPATH]; if ( !intro ) { intro = ""; } if ( !loop || !loop[0] ) { loop = intro; } Com_DPrintf( "S_StartBackgroundTrack( %s, %s )\n", intro, loop ); Q_strncpyz( name, intro, sizeof( name ) - 4 ); COM_DefaultExtension( name, sizeof( name ), ".wav" ); if ( !intro[0] ) { return; } Q_strncpyz( s_backgroundLoop, loop, sizeof( s_backgroundLoop ) ); // close the background track, but DON'T reset s_rawend // if restarting the same back ground track if ( s_backgroundFile ) { Sys_EndStreamedFile( s_backgroundFile ); FS_FCloseFile( s_backgroundFile ); s_backgroundFile = 0; } // // open up a wav file and get all the info // FS_FOpenFileRead( name, &s_backgroundFile, qtrue ); if ( !s_backgroundFile ) { Com_Printf( S_COLOR_YELLOW "WARNING: couldn't open music file %s\n", name ); return; } // skip the riff wav header FS_Read(dump, 12, s_backgroundFile); if ( !S_FindWavChunk( s_backgroundFile, "fmt " ) ) { Com_Printf( "No fmt chunk in %s\n", name ); FS_FCloseFile( s_backgroundFile ); s_backgroundFile = 0; return; } // save name for soundinfo s_backgroundInfo.format = FGetLittleShort( s_backgroundFile ); s_backgroundInfo.channels = FGetLittleShort( s_backgroundFile ); s_backgroundInfo.rate = FGetLittleLong( s_backgroundFile ); FGetLittleLong( s_backgroundFile ); FGetLittleShort( s_backgroundFile ); s_backgroundInfo.width = FGetLittleShort( s_backgroundFile ) / 8; if ( s_backgroundInfo.format != WAV_FORMAT_PCM ) { FS_FCloseFile( s_backgroundFile ); s_backgroundFile = 0; Com_Printf("Not a microsoft PCM format wav: %s\n", name); return; } if ( s_backgroundInfo.channels != 2 || s_backgroundInfo.rate != 22050 ) { Com_Printf(S_COLOR_YELLOW "WARNING: music file %s is not 22k stereo\n", name ); } if ( ( len = S_FindWavChunk( s_backgroundFile, "data" ) ) == 0 ) { FS_FCloseFile( s_backgroundFile ); s_backgroundFile = 0; Com_Printf("No data chunk in %s\n", name); return; } s_backgroundInfo.samples = len / (s_backgroundInfo.width * s_backgroundInfo.channels); s_backgroundSamples = s_backgroundInfo.samples; // // start the background streaming // Sys_BeginStreamedFile( s_backgroundFile, 0x10000 ); } /* ====================== S_UpdateBackgroundTrack ====================== */ void S_UpdateBackgroundTrack( void ) { int bufferSamples; int fileSamples; byte raw[30000]; // just enough to fit in a mac stack frame int fileBytes; int r; static float musicVolume = 0.5f; if ( !s_backgroundFile ) { return; } // graeme see if this is OK musicVolume = (musicVolume + (s_musicVolume->value * 2))/4.0f; // don't bother playing anything if musicvolume is 0 if ( musicVolume <= 0 ) { return; } // see how many samples should be copied into the raw buffer if ( s_rawend < s_soundtime ) { s_rawend = s_soundtime; } while ( s_rawend < s_soundtime + MAX_RAW_SAMPLES ) { bufferSamples = MAX_RAW_SAMPLES - (s_rawend - s_soundtime); // decide how much data needs to be read from the file fileSamples = bufferSamples * s_backgroundInfo.rate / dma.speed; // don't try and read past the end of the file if ( fileSamples > s_backgroundSamples ) { fileSamples = s_backgroundSamples; } // our max buffer size fileBytes = fileSamples * (s_backgroundInfo.width * s_backgroundInfo.channels); if ( fileBytes > sizeof(raw) ) { fileBytes = sizeof(raw); fileSamples = fileBytes / (s_backgroundInfo.width * s_backgroundInfo.channels); } r = Sys_StreamedRead( raw, 1, fileBytes, s_backgroundFile ); if ( r != fileBytes ) { Com_Printf("StreamedRead failure on music track\n"); S_StopBackgroundTrack(); return; } // byte swap if needed S_ByteSwapRawSamples( fileSamples, s_backgroundInfo.width, s_backgroundInfo.channels, raw ); // add to raw buffer S_RawSamples( fileSamples, s_backgroundInfo.rate, s_backgroundInfo.width, s_backgroundInfo.channels, raw, musicVolume ); s_backgroundSamples -= fileSamples; if ( !s_backgroundSamples ) { // loop if (s_backgroundLoop[0]) { Sys_EndStreamedFile( s_backgroundFile ); FS_FCloseFile( s_backgroundFile ); s_backgroundFile = 0; S_StartBackgroundTrack( s_backgroundLoop, s_backgroundLoop ); if ( !s_backgroundFile ) { return; // loop failed to restart } } else { s_backgroundFile = 0; return; } } } } /* ====================== S_FreeOldestSound ====================== */ void S_FreeOldestSound() { int i, oldest, used; sfx_t *sfx; sndBuffer *buffer, *nbuffer; oldest = Com_Milliseconds(); used = 0; for (i=1 ; i < s_numSfx ; i++) { sfx = &s_knownSfx[i]; if (sfx->inMemory && sfx->lastTimeUsedlastTimeUsed; } } sfx = &s_knownSfx[used]; Com_DPrintf("S_FreeOldestSound: freeing sound %s\n", sfx->soundName); buffer = sfx->soundData; while(buffer != NULL) { nbuffer = buffer->next; SND_free(buffer); buffer = nbuffer; } sfx->inMemory = qfalse; sfx->soundData = NULL; } ================================================ FILE: src/engine/client/snd_local.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // snd_local.h -- private sound definations #include "../../game/q_shared.h" #include "../qcommon/qcommon.h" #include "snd_public.h" #define PAINTBUFFER_SIZE 4096 // this is in samples #define SND_CHUNK_SIZE 1024 // samples #define SND_CHUNK_SIZE_FLOAT (SND_CHUNK_SIZE/2) // floats #define SND_CHUNK_SIZE_BYTE (SND_CHUNK_SIZE*2) // floats typedef struct { int left; // the final values will be clamped to +/- 0x00ffff00 and shifted down int right; } portable_samplepair_t; typedef struct adpcm_state { short sample; /* Previous output value */ char index; /* Index into stepsize table */ } adpcm_state_t; typedef struct sndBuffer_s { short sndChunk[SND_CHUNK_SIZE]; struct sndBuffer_s *next; int size; adpcm_state_t adpcm; } sndBuffer; typedef struct sfx_s { sndBuffer *soundData; qboolean defaultSound; // couldn't be loaded, so use buzz qboolean inMemory; // not in Memory qboolean soundCompressed; // not in Memory int soundCompressionMethod; int soundLength; char soundName[MAX_QPATH]; int lastTimeUsed; struct sfx_s *next; } sfx_t; typedef struct { int channels; int samples; // mono samples in buffer int submission_chunk; // don't mix less than this # int samplebits; int speed; byte *buffer; } dma_t; #define START_SAMPLE_IMMEDIATE 0x7fffffff typedef struct loopSound_s { vec3_t origin; vec3_t velocity; sfx_t *sfx; int mergeFrame; qboolean active; qboolean kill; qboolean doppler; float dopplerScale; float oldDopplerScale; int framenum; } loopSound_t; typedef struct { int allocTime; int startSample; // START_SAMPLE_IMMEDIATE = set immediately on next mix int entnum; // to allow overriding a specific sound int entchannel; // to allow overriding a specific sound int leftvol; // 0-255 volume after spatialization int rightvol; // 0-255 volume after spatialization int master_vol; // 0-255 volume before spatialization float dopplerScale; float oldDopplerScale; vec3_t origin; // only use if fixed_origin is set qboolean fixed_origin; // use origin instead of fetching entnum's origin sfx_t *thesfx; // sfx structure qboolean doppler; } channel_t; #define WAV_FORMAT_PCM 1 typedef struct { int format; int rate; int width; int channels; int samples; int dataofs; // chunk starts this many bytes from file start } wavinfo_t; /* ==================================================================== SYSTEM SPECIFIC FUNCTIONS ==================================================================== */ // initializes cycling through a DMA buffer and returns information on it qboolean SNDDMA_Init(void); // gets the current DMA position int SNDDMA_GetDMAPos(void); // shutdown the DMA xfer. void SNDDMA_Shutdown(void); void SNDDMA_BeginPainting (void); void SNDDMA_Submit(void); //==================================================================== #define MAX_CHANNELS 96 extern channel_t s_channels[MAX_CHANNELS]; extern channel_t loop_channels[MAX_CHANNELS]; extern int numLoopChannels; extern int s_paintedtime; extern int s_rawend; extern vec3_t listener_forward; extern vec3_t listener_right; extern vec3_t listener_up; extern dma_t dma; #define MAX_RAW_SAMPLES 16384 extern portable_samplepair_t s_rawsamples[MAX_RAW_SAMPLES]; extern cvar_t *s_volume; extern cvar_t *s_nosound; extern cvar_t *s_khz; extern cvar_t *s_show; extern cvar_t *s_mixahead; extern cvar_t *s_testsound; extern cvar_t *s_separation; qboolean S_LoadSound( sfx_t *sfx ); void SND_free(sndBuffer *v); sndBuffer* SND_malloc(); void SND_setup(); void S_PaintChannels(int endtime); void S_memoryLoad(sfx_t *sfx); portable_samplepair_t *S_GetRawSamplePointer(); // adpcm functions int S_AdpcmMemoryNeeded( const wavinfo_t *info ); void S_AdpcmEncodeSound( sfx_t *sfx, short *samples ); void S_AdpcmGetSamples(sndBuffer *chunk, short *to); // wavelet function #define SENTINEL_MULAW_ZERO_RUN 127 #define SENTINEL_MULAW_FOUR_BIT_RUN 126 void S_FreeOldestSound(); #define NXStream byte void encodeWavelet(sfx_t *sfx, short *packets); void decodeWavelet( sndBuffer *stream, short *packets); void encodeMuLaw( sfx_t *sfx, short *packets); extern short mulawToShort[256]; extern short *sfxScratchBuffer; extern sfx_t *sfxScratchPointer; extern int sfxScratchIndex; ================================================ FILE: src/engine/client/snd_mem.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: snd_mem.c * * desc: sound caching * * $Archive: /MissionPack/code/client/snd_mem.c $ * *****************************************************************************/ #include "snd_local.h" #define DEF_COMSOUNDMEGS "8" /* =============================================================================== memory management =============================================================================== */ static sndBuffer *buffer = NULL; static sndBuffer *freelist = NULL; static int inUse = 0; static int totalInUse = 0; short *sfxScratchBuffer = NULL; sfx_t *sfxScratchPointer = NULL; int sfxScratchIndex = 0; void SND_free(sndBuffer *v) { *(sndBuffer **)v = freelist; freelist = (sndBuffer*)v; inUse += sizeof(sndBuffer); } sndBuffer* SND_malloc() { sndBuffer *v; redo: if (freelist == NULL) { S_FreeOldestSound(); goto redo; } inUse -= sizeof(sndBuffer); totalInUse += sizeof(sndBuffer); v = freelist; freelist = *(sndBuffer **)freelist; v->next = NULL; return v; } void SND_setup() { sndBuffer *p, *q; cvar_t *cv; int scs; cv = Cvar_Get( "com_soundMegs", DEF_COMSOUNDMEGS, CVAR_LATCH | CVAR_ARCHIVE ); scs = (cv->integer*1536); buffer = (sndBuffer*) malloc(scs*sizeof(sndBuffer)); // allocate the stack based hunk allocator sfxScratchBuffer = (short*) malloc(SND_CHUNK_SIZE * sizeof(short) * 4); //Hunk_Alloc(SND_CHUNK_SIZE * sizeof(short) * 4); sfxScratchPointer = NULL; inUse = scs*sizeof(sndBuffer); p = buffer;; q = p + scs; while (--q > p) *(sndBuffer **)q = q-1; *(sndBuffer **)q = NULL; freelist = p + scs - 1; Com_Printf("Sound memory manager started\n"); } /* =============================================================================== WAV loading =============================================================================== */ static byte *data_p; static byte *iff_end; static byte *last_chunk; static byte *iff_data; static int iff_chunk_len; static short GetLittleShort(void) { short val = 0; val = *data_p; val = val + (*(data_p+1)<<8); data_p += 2; return val; } static int GetLittleLong(void) { int val = 0; val = *data_p; val = val + (*(data_p+1)<<8); val = val + (*(data_p+2)<<16); val = val + (*(data_p+3)<<24); data_p += 4; return val; } static void FindNextChunk(char *name) { while (1) { data_p=last_chunk; if (data_p >= iff_end) { // didn't find the chunk data_p = NULL; return; } data_p += 4; iff_chunk_len = GetLittleLong(); if (iff_chunk_len < 0) { data_p = NULL; return; } data_p -= 8; last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 ); if (!strncmp((char *)data_p, name, 4)) return; } } static void FindChunk(char *name) { last_chunk = iff_data; FindNextChunk (name); } /* ============ GetWavinfo ============ */ static wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength) { wavinfo_t info; Com_Memset (&info, 0, sizeof(info)); if (!wav) return info; iff_data = wav; iff_end = wav + wavlength; // find "RIFF" chunk FindChunk("RIFF"); if (!(data_p && !strncmp((char *)data_p+8, "WAVE", 4))) { Com_Printf("Missing RIFF/WAVE chunks\n"); return info; } // get "fmt " chunk iff_data = data_p + 12; // DumpChunks (); FindChunk("fmt "); if (!data_p) { Com_Printf("Missing fmt chunk\n"); return info; } data_p += 8; info.format = GetLittleShort(); info.channels = GetLittleShort(); info.rate = GetLittleLong(); data_p += 4+2; info.width = GetLittleShort() / 8; if (info.format != 1) { Com_Printf("Microsoft PCM format only\n"); return info; } // find data chunk FindChunk("data"); if (!data_p) { Com_Printf("Missing data chunk\n"); return info; } data_p += 4; info.samples = GetLittleLong () / info.width; info.dataofs = data_p - wav; return info; } /* ================ ResampleSfx resample / decimate to the current source rate ================ */ static void ResampleSfx( sfx_t *sfx, int inrate, int inwidth, byte *data, qboolean compressed ) { int outcount; int srcsample; float stepscale; int i; int sample, samplefrac, fracstep; int part; sndBuffer *chunk; stepscale = (float)inrate / dma.speed; // this is usually 0.5, 1, or 2 outcount = sfx->soundLength / stepscale; sfx->soundLength = outcount; samplefrac = 0; fracstep = stepscale * 256; chunk = sfx->soundData; for (i=0 ; i> 8; samplefrac += fracstep; if( inwidth == 2 ) { sample = LittleShort ( ((short *)data)[srcsample] ); } else { sample = (int)( (unsigned char)(data[srcsample]) - 128) << 8; } part = (i&(SND_CHUNK_SIZE-1)); if (part == 0) { sndBuffer *newchunk; newchunk = SND_malloc(); if (chunk == NULL) { sfx->soundData = newchunk; } else { chunk->next = newchunk; } chunk = newchunk; } chunk->sndChunk[part] = sample; } } /* ================ ResampleSfx resample / decimate to the current source rate ================ */ static int ResampleSfxRaw( short *sfx, int inrate, int inwidth, int samples, byte *data ) { int outcount; int srcsample; float stepscale; int i; int sample, samplefrac, fracstep; stepscale = (float)inrate / dma.speed; // this is usually 0.5, 1, or 2 outcount = samples / stepscale; samplefrac = 0; fracstep = stepscale * 256; for (i=0 ; i> 8; samplefrac += fracstep; if( inwidth == 2 ) { sample = LittleShort ( ((short *)data)[srcsample] ); } else { sample = (int)( (unsigned char)(data[srcsample]) - 128) << 8; } sfx[i] = sample; } return outcount; } //============================================================================= /* ============== S_LoadSound The filename may be different than sfx->name in the case of a forced fallback of a player specific sound ============== */ qboolean S_LoadSound( sfx_t *sfx ) { byte *data; short *samples; wavinfo_t info; int size; // player specific sounds are never directly loaded if ( sfx->soundName[0] == '*') { return qfalse; } // load it in size = FS_ReadFile( sfx->soundName, (void **)&data ); if ( !data ) { return qfalse; } info = GetWavinfo( sfx->soundName, data, size ); if ( info.channels != 1 ) { Com_Printf ("%s is a stereo wav file\n", sfx->soundName); FS_FreeFile (data); return qfalse; } if ( info.width == 1 ) { Com_DPrintf(S_COLOR_YELLOW "WARNING: %s is a 8 bit wav file\n", sfx->soundName); } if ( info.rate != 22050 ) { Com_DPrintf(S_COLOR_YELLOW "WARNING: %s is not a 22kHz wav file\n", sfx->soundName); } samples = (short*) Hunk_AllocateTempMemory(info.samples * sizeof(short) * 2); sfx->lastTimeUsed = Com_Milliseconds()+1; // each of these compression schemes works just fine // but the 16bit quality is much nicer and with a local // install assured we can rely upon the sound memory // manager to do the right thing for us and page // sound in as needed if( sfx->soundCompressed == qtrue) { sfx->soundCompressionMethod = 1; sfx->soundData = NULL; sfx->soundLength = ResampleSfxRaw( samples, info.rate, info.width, info.samples, (data + info.dataofs) ); S_AdpcmEncodeSound(sfx, samples); #if 0 } else if (info.samples>(SND_CHUNK_SIZE*16) && info.width >1) { sfx->soundCompressionMethod = 3; sfx->soundData = NULL; sfx->soundLength = ResampleSfxRaw( samples, info.rate, info.width, info.samples, (data + info.dataofs) ); encodeMuLaw( sfx, samples); } else if (info.samples>(SND_CHUNK_SIZE*6400) && info.width >1) { sfx->soundCompressionMethod = 2; sfx->soundData = NULL; sfx->soundLength = ResampleSfxRaw( samples, info.rate, info.width, info.samples, (data + info.dataofs) ); encodeWavelet( sfx, samples); #endif } else { sfx->soundCompressionMethod = 0; sfx->soundLength = info.samples; sfx->soundData = NULL; ResampleSfx( sfx, info.rate, info.width, data + info.dataofs, qfalse ); } Hunk_FreeTempMemory(samples); FS_FreeFile( data ); return qtrue; } void S_DisplayFreeMemory() { Com_Printf("%d bytes free sound buffer memory, %d total used\n", inUse, totalInUse); } ================================================ FILE: src/engine/client/snd_mix.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // snd_mix.c -- portable code to mix sounds for snd_dma.c #include "snd_local.h" static portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE]; static int snd_vol; // bk001119 - these not static, required by unix/snd_mixa.s int* snd_p; int snd_linear_count; short* snd_out; #if !( (defined __linux__ || defined __FreeBSD__ ) && (defined __i386__) ) // rb010123 #if !id386 void S_WriteLinearBlastStereo16 (void) { int i; int val; for (i=0 ; i>8; if (val > 0x7fff) snd_out[i] = 0x7fff; else if (val < -32768) snd_out[i] = -32768; else snd_out[i] = val; val = snd_p[i+1]>>8; if (val > 0x7fff) snd_out[i+1] = 0x7fff; else if (val < -32768) snd_out[i+1] = -32768; else snd_out[i+1] = val; } } #else __declspec( naked ) void S_WriteLinearBlastStereo16 (void) { __asm { push edi push ebx mov ecx,ds:dword ptr[snd_linear_count] mov ebx,ds:dword ptr[snd_p] mov edi,ds:dword ptr[snd_out] LWLBLoopTop: mov eax,ds:dword ptr[-8+ebx+ecx*4] sar eax,8 cmp eax,07FFFh jg LClampHigh cmp eax,0FFFF8000h jnl LClampDone mov eax,0FFFF8000h jmp LClampDone LClampHigh: mov eax,07FFFh LClampDone: mov edx,ds:dword ptr[-4+ebx+ecx*4] sar edx,8 cmp edx,07FFFh jg LClampHigh2 cmp edx,0FFFF8000h jnl LClampDone2 mov edx,0FFFF8000h jmp LClampDone2 LClampHigh2: mov edx,07FFFh LClampDone2: shl edx,16 and eax,0FFFFh or edx,eax mov ds:dword ptr[-4+edi+ecx*2],edx sub ecx,2 jnz LWLBLoopTop pop ebx pop edi ret } } #endif #else // forward declare, implementation somewhere else void S_WriteLinearBlastStereo16 (void); #endif void S_TransferStereo16 (unsigned long *pbuf, int endtime) { int lpos; int ls_paintedtime; snd_p = (int *) paintbuffer; ls_paintedtime = s_paintedtime; while (ls_paintedtime < endtime) { // handle recirculating buffer issues lpos = ls_paintedtime & ((dma.samples>>1)-1); snd_out = (short *) pbuf + (lpos<<1); snd_linear_count = (dma.samples>>1) - lpos; if (ls_paintedtime + snd_linear_count > endtime) snd_linear_count = endtime - ls_paintedtime; snd_linear_count <<= 1; // write a linear blast of samples S_WriteLinearBlastStereo16 (); snd_p += snd_linear_count; ls_paintedtime += (snd_linear_count>>1); } } /* =================== S_TransferPaintBuffer =================== */ void S_TransferPaintBuffer(int endtime) { int out_idx; int count; int out_mask; int *p; int step; int val; unsigned long *pbuf; pbuf = (unsigned long *)dma.buffer; if ( s_testsound->integer ) { int i; int count; // write a fixed sine wave count = (endtime - s_paintedtime); for (i=0 ; i> 8; p+= step; if (val > 0x7fff) val = 0x7fff; else if (val < -32768) val = -32768; out[out_idx] = val; out_idx = (out_idx + 1) & out_mask; } } else if (dma.samplebits == 8) { unsigned char *out = (unsigned char *) pbuf; while (count--) { val = *p >> 8; p+= step; if (val > 0x7fff) val = 0x7fff; else if (val < -32768) val = -32768; out[out_idx] = (val>>8) + 128; out_idx = (out_idx + 1) & out_mask; } } } } /* =============================================================================== CHANNEL MIXING =============================================================================== */ static void S_PaintChannelFrom16( channel_t *ch, const sfx_t *sc, int count, int sampleOffset, int bufferOffset ) { int data, aoff, boff; int leftvol, rightvol; int i, j; portable_samplepair_t *samp; sndBuffer *chunk; short *samples; float ooff, fdata, fdiv, fleftvol, frightvol; samp = &paintbuffer[ bufferOffset ]; if (ch->doppler) { sampleOffset = sampleOffset*ch->oldDopplerScale; } chunk = sc->soundData; while (sampleOffset>=SND_CHUNK_SIZE) { chunk = chunk->next; sampleOffset -= SND_CHUNK_SIZE; if (!chunk) { chunk = sc->soundData; } } if (!ch->doppler || ch->dopplerScale==1.0f) { #if idppc_altivec vector signed short volume_vec; vector unsigned int volume_shift; int vectorCount, samplesLeft, chunkSamplesLeft; #endif leftvol = ch->leftvol*snd_vol; rightvol = ch->rightvol*snd_vol; samples = chunk->sndChunk; #if idppc_altivec ((short *)&volume_vec)[0] = leftvol; ((short *)&volume_vec)[1] = leftvol; ((short *)&volume_vec)[4] = leftvol; ((short *)&volume_vec)[5] = leftvol; ((short *)&volume_vec)[2] = rightvol; ((short *)&volume_vec)[3] = rightvol; ((short *)&volume_vec)[6] = rightvol; ((short *)&volume_vec)[7] = rightvol; volume_shift = vec_splat_u32(8); i = 0; while(i < count) { /* Try to align destination to 16-byte boundary */ while(i < count && (((unsigned long)&samp[i] & 0x1f) || ((count-i) < 8) || ((SND_CHUNK_SIZE - sampleOffset) < 8))) { data = samples[sampleOffset++]; samp[i].left += (data * leftvol)>>8; samp[i].right += (data * rightvol)>>8; if (sampleOffset == SND_CHUNK_SIZE) { chunk = chunk->next; samples = chunk->sndChunk; sampleOffset = 0; } i++; } /* Destination is now aligned. Process as many 8-sample chunks as we can before we run out of room from the current sound chunk. We do 8 per loop to avoid extra source data reads. */ samplesLeft = count - i; chunkSamplesLeft = SND_CHUNK_SIZE - sampleOffset; if(samplesLeft > chunkSamplesLeft) samplesLeft = chunkSamplesLeft; vectorCount = samplesLeft / 8; if(vectorCount) { vector unsigned char tmp; vector short s0, s1, sampleData0, sampleData1; vector short samples0, samples1; vector signed int left0, right0; vector signed int merge0, merge1; vector signed int d0, d1, d2, d3; vector unsigned char samplePermute0 = (vector unsigned char)(0, 1, 4, 5, 0, 1, 4, 5, 2, 3, 6, 7, 2, 3, 6, 7); vector unsigned char samplePermute1 = (vector unsigned char)(8, 9, 12, 13, 8, 9, 12, 13, 10, 11, 14, 15, 10, 11, 14, 15); vector unsigned char loadPermute0, loadPermute1; // Rather than permute the vectors after we load them to do the sample // replication and rearrangement, we permute the alignment vector so // we do everything in one step below and avoid data shuffling. tmp = vec_lvsl(0,&samples[sampleOffset]); loadPermute0 = vec_perm(tmp,tmp,samplePermute0); loadPermute1 = vec_perm(tmp,tmp,samplePermute1); s0 = *(vector short *)&samples[sampleOffset]; while(vectorCount) { /* Load up source (16-bit) sample data */ s1 = *(vector short *)&samples[sampleOffset+7]; /* Load up destination sample data */ d0 = *(vector signed int *)&samp[i]; d1 = *(vector signed int *)&samp[i+2]; d2 = *(vector signed int *)&samp[i+4]; d3 = *(vector signed int *)&samp[i+6]; sampleData0 = vec_perm(s0,s1,loadPermute0); sampleData1 = vec_perm(s0,s1,loadPermute1); merge0 = vec_mule(sampleData0,volume_vec); merge0 = vec_sra(merge0,volume_shift); /* Shift down to proper range */ merge1 = vec_mulo(sampleData0,volume_vec); merge1 = vec_sra(merge1,volume_shift); d0 = vec_add(merge0,d0); d1 = vec_add(merge1,d1); merge0 = vec_mule(sampleData1,volume_vec); merge0 = vec_sra(merge0,volume_shift); /* Shift down to proper range */ merge1 = vec_mulo(sampleData1,volume_vec); merge1 = vec_sra(merge1,volume_shift); d2 = vec_add(merge0,d2); d3 = vec_add(merge1,d3); /* Store destination sample data */ *(vector signed int *)&samp[i] = d0; *(vector signed int *)&samp[i+2] = d1; *(vector signed int *)&samp[i+4] = d2; *(vector signed int *)&samp[i+6] = d3; i += 8; vectorCount--; s0 = s1; sampleOffset += 8; } if (sampleOffset == SND_CHUNK_SIZE) { chunk = chunk->next; samples = chunk->sndChunk; sampleOffset = 0; } } } #else for ( i=0 ; i>8; samp[i].right += (data * rightvol)>>8; if (sampleOffset == SND_CHUNK_SIZE) { chunk = chunk->next; samples = chunk->sndChunk; sampleOffset = 0; } } #endif } else { fleftvol = ch->leftvol*snd_vol; frightvol = ch->rightvol*snd_vol; ooff = sampleOffset; samples = chunk->sndChunk; for ( i=0 ; idopplerScale; boff = ooff; fdata = 0; for (j=aoff; jnext; if (!chunk) { chunk = sc->soundData; } samples = chunk->sndChunk; ooff -= SND_CHUNK_SIZE; } fdata += samples[j&(SND_CHUNK_SIZE-1)]; } fdiv = 256 * (boff-aoff); samp[i].left += (fdata * fleftvol)/fdiv; samp[i].right += (fdata * frightvol)/fdiv; } } } void S_PaintChannelFromWavelet( channel_t *ch, sfx_t *sc, int count, int sampleOffset, int bufferOffset ) { int data; int leftvol, rightvol; int i; portable_samplepair_t *samp; sndBuffer *chunk; short *samples; leftvol = ch->leftvol*snd_vol; rightvol = ch->rightvol*snd_vol; i = 0; samp = &paintbuffer[ bufferOffset ]; chunk = sc->soundData; while (sampleOffset>=(SND_CHUNK_SIZE_FLOAT*4)) { chunk = chunk->next; sampleOffset -= (SND_CHUNK_SIZE_FLOAT*4); i++; } if (i!=sfxScratchIndex || sfxScratchPointer != sc) { S_AdpcmGetSamples( chunk, sfxScratchBuffer ); sfxScratchIndex = i; sfxScratchPointer = sc; } samples = sfxScratchBuffer; for ( i=0 ; i>8; samp[i].right += (data * rightvol)>>8; if (sampleOffset == SND_CHUNK_SIZE*2) { chunk = chunk->next; decodeWavelet(chunk, sfxScratchBuffer); sfxScratchIndex++; sampleOffset = 0; } } } void S_PaintChannelFromADPCM( channel_t *ch, sfx_t *sc, int count, int sampleOffset, int bufferOffset ) { int data; int leftvol, rightvol; int i; portable_samplepair_t *samp; sndBuffer *chunk; short *samples; leftvol = ch->leftvol*snd_vol; rightvol = ch->rightvol*snd_vol; i = 0; samp = &paintbuffer[ bufferOffset ]; chunk = sc->soundData; if (ch->doppler) { sampleOffset = sampleOffset*ch->oldDopplerScale; } while (sampleOffset>=(SND_CHUNK_SIZE*4)) { chunk = chunk->next; sampleOffset -= (SND_CHUNK_SIZE*4); i++; } if (i!=sfxScratchIndex || sfxScratchPointer != sc) { S_AdpcmGetSamples( chunk, sfxScratchBuffer ); sfxScratchIndex = i; sfxScratchPointer = sc; } samples = sfxScratchBuffer; for ( i=0 ; i>8; samp[i].right += (data * rightvol)>>8; if (sampleOffset == SND_CHUNK_SIZE*4) { chunk = chunk->next; S_AdpcmGetSamples( chunk, sfxScratchBuffer); sampleOffset = 0; sfxScratchIndex++; } } } void S_PaintChannelFromMuLaw( channel_t *ch, sfx_t *sc, int count, int sampleOffset, int bufferOffset ) { int data; int leftvol, rightvol; int i; portable_samplepair_t *samp; sndBuffer *chunk; byte *samples; float ooff; leftvol = ch->leftvol*snd_vol; rightvol = ch->rightvol*snd_vol; samp = &paintbuffer[ bufferOffset ]; chunk = sc->soundData; while (sampleOffset>=(SND_CHUNK_SIZE*2)) { chunk = chunk->next; sampleOffset -= (SND_CHUNK_SIZE*2); if (!chunk) { chunk = sc->soundData; } } if (!ch->doppler) { samples = (byte *)chunk->sndChunk + sampleOffset; for ( i=0 ; i>8; samp[i].right += (data * rightvol)>>8; samples++; if (samples == (byte *)chunk->sndChunk+(SND_CHUNK_SIZE*2)) { chunk = chunk->next; samples = (byte *)chunk->sndChunk; } } } else { ooff = sampleOffset; samples = (byte *)chunk->sndChunk; for ( i=0 ; idopplerScale; samp[i].left += (data * leftvol)>>8; samp[i].right += (data * rightvol)>>8; if (ooff >= SND_CHUNK_SIZE*2) { chunk = chunk->next; if (!chunk) { chunk = sc->soundData; } samples = (byte *)chunk->sndChunk; ooff = 0.0; } } } } /* =================== S_PaintChannels =================== */ void S_PaintChannels( int endtime ) { int i; int end; channel_t *ch; sfx_t *sc; int ltime, count; int sampleOffset; snd_vol = s_volume->value*255; //Com_Printf ("%i to %i\n", s_paintedtime, endtime); while ( s_paintedtime < endtime ) { // if paintbuffer is smaller than DMA buffer // we may need to fill it multiple times end = endtime; if ( endtime - s_paintedtime > PAINTBUFFER_SIZE ) { end = s_paintedtime + PAINTBUFFER_SIZE; } // clear the paint buffer to either music or zeros if ( s_rawend < s_paintedtime ) { if ( s_rawend ) { //Com_DPrintf ("background sound underrun\n"); } Com_Memset(paintbuffer, 0, (end - s_paintedtime) * sizeof(portable_samplepair_t)); } else { // copy from the streaming sound source int s; int stop; stop = (end < s_rawend) ? end : s_rawend; for ( i = s_paintedtime ; i < stop ; i++ ) { s = i&(MAX_RAW_SAMPLES-1); paintbuffer[i-s_paintedtime] = s_rawsamples[s]; } // if (i != end) // Com_Printf ("partial stream\n"); // else // Com_Printf ("full stream\n"); for ( ; i < end ; i++ ) { paintbuffer[i-s_paintedtime].left = paintbuffer[i-s_paintedtime].right = 0; } } // paint in the channels. ch = s_channels; for ( i = 0; i < MAX_CHANNELS ; i++, ch++ ) { if ( !ch->thesfx || (ch->leftvol<0.25 && ch->rightvol<0.25 )) { continue; } ltime = s_paintedtime; sc = ch->thesfx; sampleOffset = ltime - ch->startSample; count = end - ltime; if ( sampleOffset + count > sc->soundLength ) { count = sc->soundLength - sampleOffset; } if ( count > 0 ) { if( sc->soundCompressionMethod == 1) { S_PaintChannelFromADPCM (ch, sc, count, sampleOffset, ltime - s_paintedtime); } else if( sc->soundCompressionMethod == 2) { S_PaintChannelFromWavelet (ch, sc, count, sampleOffset, ltime - s_paintedtime); } else if( sc->soundCompressionMethod == 3) { S_PaintChannelFromMuLaw (ch, sc, count, sampleOffset, ltime - s_paintedtime); } else { S_PaintChannelFrom16 (ch, sc, count, sampleOffset, ltime - s_paintedtime); } } } // paint in the looped channels. ch = loop_channels; for ( i = 0; i < numLoopChannels ; i++, ch++ ) { if ( !ch->thesfx || (!ch->leftvol && !ch->rightvol )) { continue; } ltime = s_paintedtime; sc = ch->thesfx; if (sc->soundData==NULL || sc->soundLength==0) { continue; } // we might have to make two passes if it // is a looping sound effect and the end of // the sample is hit do { sampleOffset = (ltime % sc->soundLength); count = end - ltime; if ( sampleOffset + count > sc->soundLength ) { count = sc->soundLength - sampleOffset; } if ( count > 0 ) { if( sc->soundCompressionMethod == 1) { S_PaintChannelFromADPCM (ch, sc, count, sampleOffset, ltime - s_paintedtime); } else if( sc->soundCompressionMethod == 2) { S_PaintChannelFromWavelet (ch, sc, count, sampleOffset, ltime - s_paintedtime); } else if( sc->soundCompressionMethod == 3) { S_PaintChannelFromMuLaw (ch, sc, count, sampleOffset, ltime - s_paintedtime); } else { S_PaintChannelFrom16 (ch, sc, count, sampleOffset, ltime - s_paintedtime); } ltime += count; } } while ( ltime < end); } // transfer out according to DMA format S_TransferPaintBuffer( end ); s_paintedtime = end; } } ================================================ FILE: src/engine/client/snd_public.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ void S_Init( void ); void S_Shutdown( void ); // if origin is NULL, the sound will be dynamically sourced from the entity void S_StartSound( vec3_t origin, int entnum, int entchannel, sfxHandle_t sfx ); void S_StartLocalSound( sfxHandle_t sfx, int channelNum ); void S_StartBackgroundTrack( const char *intro, const char *loop ); void S_StopBackgroundTrack( void ); // cinematics and voice-over-network will send raw samples // 1.0 volume will be direct output of source samples void S_RawSamples (int samples, int rate, int width, int channels, const byte *data, float volume); // stop all sounds and the background track void S_StopAllSounds( void ); // all continuous looping sounds must be added before calling S_Update void S_ClearLoopingSounds( qboolean killall ); void S_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx ); void S_AddRealLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx ); void S_StopLoopingSound(int entityNum ); // recompute the reletive volumes for all running sounds // reletive to the given entityNum / orientation void S_Respatialize( int entityNum, const vec3_t origin, vec3_t axis[3], int inwater ); // let the sound system know where an entity currently is void S_UpdateEntityPosition( int entityNum, const vec3_t origin ); void S_Update( void ); void S_DisableSounds( void ); void S_BeginRegistration( void ); // RegisterSound will allways return a valid sample, even if it // has to create a placeholder. This prevents continuous filesystem // checks for missing files sfxHandle_t S_RegisterSound( const char *sample, qboolean compressed ); void S_DisplayFreeMemory(void); void S_ClearSoundBuffer( void ); void SNDDMA_Activate( void ); void S_UpdateBackgroundTrack( void ); ================================================ FILE: src/engine/client/snd_wavelet.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "snd_local.h" long myftol( float f ); #define C0 0.4829629131445341 #define C1 0.8365163037378079 #define C2 0.2241438680420134 #define C3 -0.1294095225512604 void daub4(float b[], unsigned long n, int isign) { float wksp[4097]; float *a=b-1; // numerical recipies so a[1] = b[0] unsigned long nh,nh1,i,j; if (n < 4) return; nh1=(nh=n >> 1)+1; if (isign >= 0) { for (i=1,j=1;j<=n-3;j+=2,i++) { wksp[i] = C0*a[j]+C1*a[j+1]+C2*a[j+2]+C3*a[j+3]; wksp[i+nh] = C3*a[j]-C2*a[j+1]+C1*a[j+2]-C0*a[j+3]; } wksp[i ] = C0*a[n-1]+C1*a[n]+C2*a[1]+C3*a[2]; wksp[i+nh] = C3*a[n-1]-C2*a[n]+C1*a[1]-C0*a[2]; } else { wksp[1] = C2*a[nh]+C1*a[n]+C0*a[1]+C3*a[nh1]; wksp[2] = C3*a[nh]-C0*a[n]+C1*a[1]-C2*a[nh1]; for (i=1,j=3;i= 0) { for (nn=n;nn>=inverseStartLength;nn>>=1) daub4(a,nn,isign); } else { for (nn=inverseStartLength;nn<=n;nn<<=1) daub4(a,nn,isign); } } /* The number of bits required by each value */ static unsigned char numBits[] = { 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, }; byte MuLawEncode(short s) { unsigned long adjusted; byte sign, exponent, mantissa; sign = (s<0)?0:0x80; if (s<0) s=-s; adjusted = (long)s << (16-sizeof(short)*8); adjusted += 128L + 4L; if (adjusted > 32767) adjusted = 32767; exponent = numBits[(adjusted>>7)&0xff] - 1; mantissa = (adjusted>>(exponent+3))&0xf; return ~(sign | (exponent<<4) | mantissa); } short MuLawDecode(byte uLaw) { signed long adjusted; byte exponent, mantissa; uLaw = ~uLaw; exponent = (uLaw>>4) & 0x7; mantissa = (uLaw&0xf) + 16; adjusted = (mantissa << (exponent +3)) - 128 - 4; return (uLaw & 0x80)? adjusted : -adjusted; } short mulawToShort[256]; static qboolean madeTable = qfalse; static int NXStreamCount; void NXPutc(NXStream *stream, char out) { stream[NXStreamCount++] = out; } void encodeWavelet( sfx_t *sfx, short *packets) { float wksp[4097], temp; int i, samples, size; sndBuffer *newchunk, *chunk; byte *out; if (!madeTable) { for (i=0;i<256;i++) { mulawToShort[i] = (float)MuLawDecode((byte)i); } madeTable = qtrue; } chunk = NULL; samples = sfx->soundLength; while(samples>0) { size = samples; if (size>(SND_CHUNK_SIZE*2)) { size = (SND_CHUNK_SIZE*2); } if (size<4) { size = 4; } newchunk = SND_malloc(); if (sfx->soundData == NULL) { sfx->soundData = newchunk; } else { chunk->next = newchunk; } chunk = newchunk; for(i=0; isndChunk; for(i=0;i 32767) temp = 32767; else if (temp<-32768) temp = -32768; out[i] = MuLawEncode((short)temp); } chunk->size = size; samples -= size; } } void decodeWavelet(sndBuffer *chunk, short *to) { float wksp[4097]; int i; byte *out; int size = chunk->size; out = (byte *)chunk->sndChunk; for(i=0;isoundLength; grade = 0; while(samples>0) { size = samples; if (size>(SND_CHUNK_SIZE*2)) { size = (SND_CHUNK_SIZE*2); } newchunk = SND_malloc(); if (sfx->soundData == NULL) { sfx->soundData = newchunk; } else { chunk->next = newchunk; } chunk = newchunk; out = (byte *)chunk->sndChunk; for(i=0; i32767) { poop = 32767; } else if (poop<-32768) { poop = -32768; } out[i] = MuLawEncode((short)poop); grade = poop - mulawToShort[out[i]]; packets++; } chunk->size = size; samples -= size; } } void decodeMuLaw(sndBuffer *chunk, short *to) { int i; byte *out; int size = chunk->size; out = (byte *)chunk->sndChunk; for(i=0;i #include "../renderer/tr_local.h" #include "../qcommon/qcommon.h" #include "win_local.h" static unsigned short s_oldHardwareGamma[3][256]; /* ** WG_CheckHardwareGamma ** ** Determines if the underlying hardware supports the Win32 gamma correction API. */ void WG_CheckHardwareGamma( void ) { HDC hDC; glConfig.deviceSupportsGamma = qfalse; if ( !r_ignorehwgamma->integer ) { hDC = GetDC( GetDesktopWindow() ); glConfig.deviceSupportsGamma = (qboolean) GetDeviceGammaRamp( hDC, s_oldHardwareGamma ); ReleaseDC( GetDesktopWindow(), hDC ); if ( glConfig.deviceSupportsGamma ) { // // do a sanity check on the gamma values // if ( ( HIBYTE( s_oldHardwareGamma[0][255] ) <= HIBYTE( s_oldHardwareGamma[0][0] ) ) || ( HIBYTE( s_oldHardwareGamma[1][255] ) <= HIBYTE( s_oldHardwareGamma[1][0] ) ) || ( HIBYTE( s_oldHardwareGamma[2][255] ) <= HIBYTE( s_oldHardwareGamma[2][0] ) ) ) { glConfig.deviceSupportsGamma = qfalse; ri.Printf( PRINT_WARNING, "WARNING: device has broken gamma support, generated gamma.dat\n" ); } // // make sure that we didn't have a prior crash in the game, and if so we need to // restore the gamma values to at least a linear value // if ( ( HIBYTE( s_oldHardwareGamma[0][181] ) == 255 ) ) { int g; ri.Printf( PRINT_WARNING, "WARNING: suspicious gamma tables, using linear ramp for restoration\n" ); for ( g = 0; g < 255; g++ ) { s_oldHardwareGamma[0][g] = g << 8; s_oldHardwareGamma[1][g] = g << 8; s_oldHardwareGamma[2][g] = g << 8; } } } } } /* ** GLimp_SetGamma ** ** This routine should only be called if glConfig.deviceSupportsGamma is TRUE */ void GLimp_SetGamma( unsigned char mapping[256]) { unsigned short table[3][256]; int i, j; int ret; if ( !glConfig.deviceSupportsGamma || r_ignorehwgamma->integer ) { return; } for ( i = 0; i < 256; i++ ) { unsigned short value = (((unsigned short)mapping[i]) << 8) | mapping[i]; table[0][i] = value; table[1][i] = value; table[2][i] = value; } // Win2K puts this odd restriction on gamma ramps... Com_DPrintf( "performing W2K gamma clamp.\n" ); for ( j = 0 ; j < 3 ; j++ ) { for ( i = 0 ; i < 128 ; i++ ) { if ( table[j][i] > ( (128+i) << 8 ) ) { table[j][i] = (128+i) << 8; } } if ( table[j][127] > 254<<8 ) { table[j][127] = 254<<8; } } // enforce constantly increasing for ( j = 0 ; j < 3 ; j++ ) { for ( i = 1 ; i < 256 ; i++ ) { if ( table[j][i] < table[j][i-1] ) { table[j][i] = table[j][i-1]; } } } HDC hdc = GetDC(NULL); ret = SetDeviceGammaRamp( hdc, table ); if ( !ret ) { Com_Printf( "SetDeviceGammaRamp failed.\n" ); } ReleaseDC(NULL, hdc); } /* ** WG_RestoreGamma */ void WG_RestoreGamma( void ) { if ( glConfig.deviceSupportsGamma ) { HDC hDC; hDC = GetDC( GetDesktopWindow() ); SetDeviceGammaRamp( hDC, s_oldHardwareGamma ); ReleaseDC( GetDesktopWindow(), hDC ); } } void GLimp_RestoreGamma() { WG_RestoreGamma(); } ================================================ FILE: src/engine/platform/win_glimp.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /* ** WIN_GLIMP.C ** ** This file contains ALL Win32 specific stuff having to do with the ** OpenGL/Vulkan refresh. When a port is being made the following functions ** must be implemented by the port: ** ** GLimp_EndFrame ** GLimp_Init ** GLimp_LogComment ** GLimp_Shutdown ** ** vk_imp_init ** vk_imp_shutdown ** vk_imp_create_surface */ #include "../renderer/tr_local.h" #include "resource.h" #include "win_local.h" #define MAIN_WINDOW_CLASS_NAME "Quake 3: Arena" #define TWIN_WINDOW_CLASS_NAME "Quake 3: Arena [Twin]" static bool s_main_window_class_registered = false; static bool s_twin_window_class_registered = false; static HDC gl_hdc; // handle to device context static HGLRC gl_hglrc; // handle to GL rendering context static HINSTANCE vk_library_handle; // HINSTANCE for the Vulkan library FILE* log_fp; qboolean QGL_Init(const char *dllname); void QGL_Shutdown(); void QGL_EnableLogging(qboolean enable); void WG_CheckHardwareGamma(); void WG_RestoreGamma(); static int GetDesktopCaps(int index) { HDC hdc = GetDC(GetDesktopWindow()); int value = GetDeviceCaps(hdc, index); ReleaseDC(GetDesktopWindow(), hdc); return value; } static int GetDesktopColorDepth() { return GetDesktopCaps(BITSPIXEL); } static int GetDesktopWidth() { return GetDesktopCaps(HORZRES); } static int GetDesktopHeight() { return GetDesktopCaps(VERTRES); } /* ** ChoosePFD ** ** Helper function that replaces ChoosePixelFormat. */ static int GLW_ChoosePixelFormat(HDC hDC, const PIXELFORMATDESCRIPTOR *pPFD) { const int MAX_PFDS = 512; PIXELFORMATDESCRIPTOR pfds[MAX_PFDS+1]; ri.Printf(PRINT_ALL, "...GLW_ChoosePFD( %d, %d, %d )\n", (int) pPFD->cColorBits, (int) pPFD->cDepthBits, (int) pPFD->cStencilBits); // count number of PFDs int maxPFD = DescribePixelFormat(hDC, 1, sizeof( PIXELFORMATDESCRIPTOR ), &pfds[0]); if (maxPFD > MAX_PFDS) { ri.Printf(PRINT_WARNING, "...numPFDs > MAX_PFDS (%d > %d)\n", maxPFD, MAX_PFDS); maxPFD = MAX_PFDS; } ri.Printf(PRINT_ALL, "...%d PFDs found\n", maxPFD); // grab information for (int i = 1; i <= maxPFD; i++) { DescribePixelFormat(hDC, i, sizeof(PIXELFORMATDESCRIPTOR), &pfds[i]); } // look for a best match int bestMatch = 0; for (int i = 1; i <= maxPFD; i++ ) { if (bestMatch != 0 && (pfds[bestMatch].dwFlags & PFD_STEREO) == (pPFD->dwFlags & PFD_STEREO) && pfds[bestMatch].cColorBits == pPFD->cColorBits && pfds[bestMatch].cDepthBits == pPFD->cDepthBits && pfds[bestMatch].cStencilBits == pPFD->cStencilBits) { break; } // // make sure this has hardware acceleration // if ((pfds[i].dwFlags & PFD_GENERIC_FORMAT) != 0) { if (r_verbose->integer) { ri.Printf( PRINT_ALL, "...PFD %d rejected, software acceleration\n", i ); } continue; } // verify pixel type if (pfds[i].iPixelType != PFD_TYPE_RGBA) { if (r_verbose->integer) { ri.Printf( PRINT_ALL, "...PFD %d rejected, not RGBA\n", i ); } continue; } // verify proper flags if ((pfds[i].dwFlags & pPFD->dwFlags) != pPFD->dwFlags) { if (r_verbose->integer) { ri.Printf( PRINT_ALL, "...PFD %d rejected, improper flags (%x instead of %x)\n", i, pfds[i].dwFlags, pPFD->dwFlags ); } continue; } // verify enough bits if (pfds[i].cDepthBits < 15) { continue; } if ((pfds[i].cStencilBits < 4) && (pPFD->cStencilBits > 0)) { continue; } if (!bestMatch) { bestMatch = i; continue; } // // selection criteria (in order of priority): // // PFD_STEREO // colorBits // depthBits // stencilBits // bool same_stereo = (pfds[i].dwFlags & PFD_STEREO) == (pfds[bestMatch].dwFlags & PFD_STEREO); bool better_stereo = (pfds[i].dwFlags & PFD_STEREO) == (pPFD->dwFlags & PFD_STEREO) && (pfds[bestMatch].dwFlags & PFD_STEREO) != (pPFD->dwFlags & PFD_STEREO); bool same_color = pfds[i].cColorBits == pfds[bestMatch].cColorBits; bool better_color = (pfds[bestMatch].cColorBits >= pPFD->cColorBits) ? pfds[i].cColorBits >= pPFD->cColorBits && pfds[i].cColorBits < pfds[bestMatch].cColorBits : pfds[i].cColorBits > pfds[bestMatch].cColorBits; bool same_depth = pfds[i].cDepthBits == pfds[bestMatch].cDepthBits; bool better_depth = (pfds[bestMatch].cDepthBits >= pPFD->cDepthBits) ? pfds[i].cDepthBits >= pPFD->cDepthBits && pfds[i].cDepthBits < pfds[bestMatch].cDepthBits : pfds[i].cDepthBits > pfds[bestMatch].cDepthBits; bool better_stencil; if (pPFD->cStencilBits == 0) better_stencil = pfds[i].cStencilBits == 0 && pfds[bestMatch].cStencilBits != 0; else better_stencil = (pfds[bestMatch].cStencilBits >= pPFD->cStencilBits) ? pfds[i].cStencilBits >= pPFD->cStencilBits && pfds[i].cStencilBits < pfds[bestMatch].cStencilBits : pfds[i].cStencilBits > pfds[bestMatch].cStencilBits; if (better_stereo) bestMatch = i; else if (same_stereo) { if (better_color) bestMatch = i; else if (same_color) { if (better_depth) bestMatch = i; else if (same_depth) { if (better_stencil) bestMatch = i; } } } } if ( !bestMatch ) return 0; if ((pfds[bestMatch].dwFlags & PFD_GENERIC_FORMAT) != 0 || (pfds[bestMatch].dwFlags & PFD_GENERIC_ACCELERATED) != 0) { ri.Printf(PRINT_ALL, "...no hardware acceleration found\n"); return 0; } ri.Printf(PRINT_ALL, "...hardware acceleration found\n"); return bestMatch; } static bool GLW_SetPixelFormat(HDC hdc, PIXELFORMATDESCRIPTOR *pPFD, int colorbits, int depthbits, int stencilbits, qboolean stereo) { const PIXELFORMATDESCRIPTOR pfd_base = { sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd 1, // version number PFD_DRAW_TO_WINDOW | // support window PFD_SUPPORT_OPENGL | // support OpenGL PFD_DOUBLEBUFFER, // double buffered PFD_TYPE_RGBA, // RGBA type 0, // color bits 0, 0, 0, 0, 0, 0, // color bits ignored 0, // no alpha buffer 0, // shift bit ignored 0, // no accumulation buffer 0, 0, 0, 0, // accum bits ignored 0, // z-buffer bits 0, // stencil buffer bits 0, // no auxiliary buffer PFD_MAIN_PLANE, // main layer 0, // reserved 0, 0, 0 // layer masks ignored }; *pPFD = pfd_base; pPFD->cColorBits = (BYTE)colorbits; pPFD->cDepthBits = (BYTE)depthbits; pPFD->cStencilBits = (BYTE)stencilbits; if (stereo) { ri.Printf(PRINT_ALL, "...attempting to use stereo\n"); pPFD->dwFlags |= PFD_STEREO; } int pixelformat = GLW_ChoosePixelFormat(hdc, pPFD); if (pixelformat == 0) { ri.Printf( PRINT_ALL, "...GLW_ChoosePixelFormat failed\n"); return false; } ri.Printf( PRINT_ALL, "...PIXELFORMAT %d selected\n", pixelformat ); DescribePixelFormat(hdc, pixelformat, sizeof( *pPFD ), pPFD); if (SetPixelFormat(hdc, pixelformat, pPFD ) == FALSE) { ri.Printf (PRINT_ALL, "...SetPixelFormat failed\n", hdc); return false; } return true; } // Sets pixel format and creates opengl context for the given window. static qboolean GLW_InitDriver(HWND hwnd) { ri.Printf( PRINT_ALL, "Initializing OpenGL driver\n" ); // // get a DC for our window // ri.Printf(PRINT_ALL, "...getting DC: "); gl_hdc = GetDC(hwnd); if (gl_hdc == NULL) { ri.Printf(PRINT_ALL, "failed\n"); return qfalse; } ri.Printf(PRINT_ALL, "succeeded\n"); // // set pixel format // int colorbits = GetDesktopColorDepth(); int depthbits = (r_depthbits->integer == 0) ? 24 : r_depthbits->integer; int stencilbits = r_stencilbits->integer; PIXELFORMATDESCRIPTOR pfd; if (!GLW_SetPixelFormat(gl_hdc, &pfd, colorbits, depthbits, stencilbits, (qboolean)r_stereo->integer)) { ReleaseDC(hwnd, gl_hdc); gl_hdc = NULL; ri.Printf(PRINT_ALL, "...failed to find an appropriate PIXELFORMAT\n"); return qfalse; } // report if stereo is desired but unavailable if (!(pfd.dwFlags & PFD_STEREO) && (r_stereo->integer != 0)) { ri.Printf(PRINT_ALL, "...failed to select stereo pixel format\n"); } // // startup the OpenGL subsystem by creating a context and making it current // ri.Printf(PRINT_ALL, "...creating GL context: "); gl_hglrc = qwglCreateContext(gl_hdc); if (gl_hglrc == NULL) { ReleaseDC(hwnd, gl_hdc); gl_hdc = NULL; ri.Printf(PRINT_ALL, "failed\n"); return qfalse; } ri.Printf(PRINT_ALL, "succeeded\n"); ri.Printf(PRINT_ALL, "...making context current: "); if (!qwglMakeCurrent(gl_hdc, gl_hglrc)) { qwglDeleteContext(gl_hglrc); gl_hglrc = NULL; ReleaseDC(hwnd, gl_hdc); gl_hdc = NULL; ri.Printf(PRINT_ALL, "failed\n"); return qfalse; } ri.Printf(PRINT_ALL, "succeeded\n"); glConfig.colorBits = ( int ) pfd.cColorBits; glConfig.depthBits = ( int ) pfd.cDepthBits; glConfig.stencilBits = ( int ) pfd.cStencilBits; glConfig.stereoEnabled = (pfd.dwFlags & PFD_STEREO) ? qtrue : qfalse; return qtrue; } static HWND create_main_window(int width, int height, qboolean fullscreen) { // // register the window class if necessary // if (!s_main_window_class_registered) { cvar_t* cv = ri.Cvar_Get( "win_wndproc", "", 0 ); WNDPROC wndproc; sscanf(cv->string, "%p", (void **)&wndproc); WNDCLASS wc; memset( &wc, 0, sizeof( wc ) ); wc.style = 0; wc.lpfnWndProc = wndproc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = g_wv.hInstance; wc.hIcon = LoadIcon( g_wv.hInstance, MAKEINTRESOURCE(IDI_ICON1)); wc.hCursor = LoadCursor (NULL,IDC_ARROW); wc.hbrBackground = (HBRUSH) (void *)COLOR_GRAYTEXT; wc.lpszMenuName = 0; wc.lpszClassName = MAIN_WINDOW_CLASS_NAME; if ( !RegisterClass( &wc ) ) { ri.Error( ERR_FATAL, "create_main_window: could not register window class" ); } s_main_window_class_registered = true; ri.Printf( PRINT_ALL, "...registered window class\n" ); } // // compute width and height // RECT r; r.left = 0; r.top = 0; r.right = width; r.bottom = height; int stylebits; if ( fullscreen ) { stylebits = WS_POPUP|WS_VISIBLE|WS_SYSMENU; } else { stylebits = WS_OVERLAPPED | WS_BORDER | WS_CAPTION | WS_VISIBLE | WS_SYSMENU; AdjustWindowRect (&r, stylebits, FALSE); } int w = r.right - r.left; int h = r.bottom - r.top; int x, y; if ( fullscreen ) { x = 0; y = 0; } else { cvar_t* vid_xpos = ri.Cvar_Get ("vid_xpos", "", 0); cvar_t* vid_ypos = ri.Cvar_Get ("vid_ypos", "", 0); x = vid_xpos->integer; y = vid_ypos->integer; // adjust window coordinates if necessary // so that the window is completely on screen if ( x < 0 ) x = 0; if ( y < 0 ) y = 0; int desktop_width = GetDesktopWidth(); int desktop_height = GetDesktopHeight(); if (w < desktop_width && h < desktop_height) { if ( x + w > desktop_width ) x = ( desktop_width - w ); if ( y + h > desktop_height ) y = ( desktop_height - h ); } } char window_name[1024]; if (r_twinMode->integer == 0) { strcpy(window_name, MAIN_WINDOW_CLASS_NAME); } else { const char* api_name = "invalid-render-api"; if (get_render_api() == RENDER_API_GL) api_name = "OpenGL"; else if (get_render_api() == RENDER_API_VK) api_name = "Vulkan"; else if (get_render_api() == RENDER_API_DX) api_name = "DX12"; sprintf(window_name, "%s [%s]", MAIN_WINDOW_CLASS_NAME, api_name); } HWND hwnd = CreateWindowEx( 0, MAIN_WINDOW_CLASS_NAME, window_name, stylebits, x, y, w, h, NULL, NULL, g_wv.hInstance, NULL); if (!hwnd) { ri.Error (ERR_FATAL, "create_main_window() - Couldn't create window"); } ShowWindow(hwnd, SW_SHOW); UpdateWindow(hwnd); ri.Printf(PRINT_ALL, "...created window@%d,%d (%dx%d)\n", x, y, w, h); return hwnd; } static HWND create_twin_window(int width, int height, RenderApi render_api) { // // register the window class if necessary // if (!s_twin_window_class_registered) { WNDCLASS wc; memset( &wc, 0, sizeof( wc ) ); wc.style = 0; wc.lpfnWndProc = DefWindowProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = g_wv.hInstance; wc.hIcon = LoadIcon( g_wv.hInstance, MAKEINTRESOURCE(IDI_ICON1)); wc.hCursor = LoadCursor (NULL,IDC_ARROW); wc.hbrBackground = (HBRUSH) (void *)COLOR_GRAYTEXT; wc.lpszMenuName = 0; wc.lpszClassName = TWIN_WINDOW_CLASS_NAME; if ( !RegisterClass( &wc ) ) { ri.Error( ERR_FATAL, "create_twin_window: could not register window class" ); } s_twin_window_class_registered = true; ri.Printf( PRINT_ALL, "...registered twin window class\n" ); } // // compute width and height // RECT r; r.left = 0; r.top = 0; r.right = width; r.bottom = height; int stylebits = WS_OVERLAPPED | WS_BORDER | WS_CAPTION | WS_VISIBLE | WS_SYSMENU; AdjustWindowRect (&r, stylebits, FALSE); int w = r.right - r.left; int h = r.bottom - r.top; cvar_t* vid_xpos = ri.Cvar_Get ("vid_xpos", "", 0); cvar_t* vid_ypos = ri.Cvar_Get ("vid_ypos", "", 0); int x, y; bool first_twin_window = (get_render_api() != RENDER_API_GL && render_api == RENDER_API_GL) || (get_render_api() == RENDER_API_GL && render_api == RENDER_API_VK); if (first_twin_window) { x = vid_xpos->integer + width + 5; y = vid_ypos->integer; } else { x = vid_xpos->integer + 2*width + 10; y = vid_ypos->integer; } int desktop_width = GetDesktopWidth(); int desktop_height = GetDesktopHeight(); if (x < 0) x = 0; else if (x >= desktop_width - 20) x = desktop_width - 20; if (y < 0) y = 0; else if (y >= desktop_height - 20) y = desktop_height - 20; char window_name[1024]; const char* api_name = "invalid-render-api"; if (render_api == RENDER_API_GL) api_name = "OpenGL"; else if (render_api == RENDER_API_VK) api_name = "Vulkan"; else if (render_api == RENDER_API_DX) api_name = "DX12"; sprintf(window_name, "%s [%s]", MAIN_WINDOW_CLASS_NAME, api_name); HWND hwnd = CreateWindowEx( 0, TWIN_WINDOW_CLASS_NAME, window_name, stylebits, x, y, w, h, NULL, NULL, g_wv.hInstance, NULL); if (!hwnd) { ri.Error (ERR_FATAL, "create_twin_window() - Couldn't create window"); } ShowWindow(hwnd, SW_SHOW); UpdateWindow(hwnd); ri.Printf(PRINT_ALL, "...created twin window@%d,%d (%dx%d)\n", x, y, w, h); return hwnd; } static void SetMode(int mode, qboolean fullscreen) { if (fullscreen) { ri.Printf( PRINT_ALL, "...setting fullscreen mode:"); glConfig.vidWidth = GetDesktopWidth(); glConfig.vidHeight = GetDesktopHeight(); glConfig.windowAspect = 1.0f; } else { ri.Printf( PRINT_ALL, "...setting mode %d:", mode ); if (!R_GetModeInfo(&glConfig.vidWidth, &glConfig.vidHeight, &glConfig.windowAspect, mode)) { ri.Printf( PRINT_ALL, " invalid mode\n" ); ri.Error(ERR_FATAL, "SetMode - could not set the given mode (%d)\n", mode); } // Ensure that window size does not exceed desktop size. // CreateWindow Win32 API does not allow to create windows larger than desktop. int desktop_width = GetDesktopWidth(); int desktop_height = GetDesktopHeight(); if (glConfig.vidWidth > desktop_width || glConfig.vidHeight > desktop_height) { int default_mode = 4; ri.Printf(PRINT_WARNING, "\nMode %d specifies width that is larger than desktop width: using default mode %d\n", mode, default_mode); ri.Printf( PRINT_ALL, "...setting mode %d:", default_mode ); if (!R_GetModeInfo(&glConfig.vidWidth, &glConfig.vidHeight, &glConfig.windowAspect, default_mode)) { ri.Printf( PRINT_ALL, " invalid mode\n" ); ri.Error(ERR_FATAL, "SetMode - could not set the given mode (%d)\n", default_mode); } } } glConfig.isFullscreen = fullscreen; ri.Printf( PRINT_ALL, " %d %d %s\n", glConfig.vidWidth, glConfig.vidHeight, fullscreen ? "FS" : "W"); } /* ** GLW_InitExtensions */ static void GLW_InitExtensions( void ) { ri.Printf( PRINT_ALL, "Initializing OpenGL extensions\n" ); // GL_S3_s3tc glConfig.textureCompression = TC_NONE; if ( strstr( glConfig.extensions_string, "GL_S3_s3tc" ) ) { if ( r_ext_compressed_textures->integer ) { glConfig.textureCompression = TC_S3TC; ri.Printf( PRINT_ALL, "...using GL_S3_s3tc\n" ); } else { glConfig.textureCompression = TC_NONE; ri.Printf( PRINT_ALL, "...ignoring GL_S3_s3tc\n" ); } } else { ri.Printf( PRINT_ALL, "...GL_S3_s3tc not found\n" ); } // GL_EXT_texture_env_add glConfig.textureEnvAddAvailable = qfalse; if ( strstr( glConfig.extensions_string, "EXT_texture_env_add" ) ) { if ( r_ext_texture_env_add->integer ) { glConfig.textureEnvAddAvailable = qtrue; ri.Printf( PRINT_ALL, "...using GL_EXT_texture_env_add\n" ); } else { glConfig.textureEnvAddAvailable = qfalse; ri.Printf( PRINT_ALL, "...ignoring GL_EXT_texture_env_add\n" ); } } else { ri.Printf( PRINT_ALL, "...GL_EXT_texture_env_add not found\n" ); } // WGL_EXT_swap_control qwglSwapIntervalEXT = ( BOOL (WINAPI *)(int)) qwglGetProcAddress( "wglSwapIntervalEXT" ); if ( qwglSwapIntervalEXT ) { ri.Printf( PRINT_ALL, "...using WGL_EXT_swap_control\n" ); r_swapInterval->modified = qtrue; // force a set next frame } else { ri.Printf( PRINT_ALL, "...WGL_EXT_swap_control not found\n" ); } // GL_ARB_multitexture { if (!strstr(glConfig.extensions_string, "GL_ARB_multitexture")) ri.Error(ERR_FATAL, "GL_ARB_multitexture not found"); qglActiveTextureARB = ( void (APIENTRY * ) (GLenum target) ) qwglGetProcAddress("glActiveTextureARB"); qglClientActiveTextureARB = ( void (APIENTRY * ) (GLenum target) ) qwglGetProcAddress("glClientActiveTextureARB"); if (!qglActiveTextureARB || !qglClientActiveTextureARB) ri.Error(ERR_FATAL, "GL_ARB_multitexture: could not initialize function pointers"); qglGetIntegerv(GL_MAX_ACTIVE_TEXTURES_ARB, &glConfig.maxActiveTextures); if (glConfig.maxActiveTextures < 2) ri.Error(ERR_FATAL, "GL_ARB_multitexture: < 2 texture units"); ri.Printf(PRINT_ALL, "...using GL_ARB_multitexture\n"); } // GL_EXT_compiled_vertex_array qglLockArraysEXT = NULL; qglUnlockArraysEXT = NULL; if ( strstr( glConfig.extensions_string, "GL_EXT_compiled_vertex_array" ) ) { if ( r_ext_compiled_vertex_array->integer ) { ri.Printf( PRINT_ALL, "...using GL_EXT_compiled_vertex_array\n" ); qglLockArraysEXT = ( void ( APIENTRY * )( int, int ) ) qwglGetProcAddress( "glLockArraysEXT" ); qglUnlockArraysEXT = ( void ( APIENTRY * )( void ) ) qwglGetProcAddress( "glUnlockArraysEXT" ); if (!qglLockArraysEXT || !qglUnlockArraysEXT) { ri.Error (ERR_FATAL, "bad getprocaddress"); } } else { ri.Printf( PRINT_ALL, "...ignoring GL_EXT_compiled_vertex_array\n" ); } } else { ri.Printf( PRINT_ALL, "...GL_EXT_compiled_vertex_array not found\n" ); } } /* ** GLimp_EndFrame */ void GLimp_EndFrame (void) { if (!gl_active) return; // // swapinterval stuff // if ( r_swapInterval->modified ) { r_swapInterval->modified = qfalse; if ( !glConfig.stereoEnabled ) { // why? if ( qwglSwapIntervalEXT ) { qwglSwapIntervalEXT( r_swapInterval->integer ); } } } // don't flip if drawing to front buffer if ( Q_stricmp( r_drawBuffer->string, "GL_FRONT" ) != 0 ) { SwapBuffers( gl_hdc ); } // check logging QGL_EnableLogging( (qboolean) r_logFile->integer ); } /* ** GLimp_Init ** ** This is the platform specific OpenGL initialization function. It ** is responsible for loading OpenGL, initializing it, setting ** extensions, creating a window of the appropriate size, doing ** fullscreen manipulations, etc. Its overall responsibility is ** to make sure that a functional OpenGL subsystem is operating ** when it returns to the ref. */ void GLimp_Init( void ) { ri.Printf( PRINT_ALL, "Initializing OpenGL subsystem\n" ); // load appropriate DLL and initialize subsystem // // load the driver and bind our function pointers to it // if (!QGL_Init(r_glDriver->string)) { if (Q_stricmp(r_glDriver->string, OPENGL_DRIVER_NAME)) { ri.Printf(PRINT_ALL, "...attempting to load default driver: %s\n", OPENGL_DRIVER_NAME); if (!QGL_Init(OPENGL_DRIVER_NAME)) { ri.Error(ERR_FATAL, "QGL_Init - could not load OpenGL driver\n"); } ri.Cvar_Set( "r_glDriver", OPENGL_DRIVER_NAME ); r_glDriver->modified = qfalse; } } SetMode(r_mode->integer, (qboolean)r_fullscreen->integer); if (get_render_api() == RENDER_API_GL) { g_wv.hWnd_opengl = create_main_window(glConfig.vidWidth, glConfig.vidHeight, (qboolean)r_fullscreen->integer); g_wv.hWnd = g_wv.hWnd_opengl; SetForegroundWindow(g_wv.hWnd); SetFocus(g_wv.hWnd); WG_CheckHardwareGamma(); } else { g_wv.hWnd_opengl = create_twin_window(glConfig.vidWidth, glConfig.vidHeight, RENDER_API_GL); } if (!GLW_InitDriver(g_wv.hWnd_opengl)) { ri.Error(ERR_FATAL, "GLW_InitDriver - could not initialize OpenGL subsystem\n"); } // get our config strings Q_strncpyz( glConfig.vendor_string, (const char*) qglGetString (GL_VENDOR), sizeof( glConfig.vendor_string ) ); Q_strncpyz(glConfig.renderer_string, (const char*)qglGetString(GL_RENDERER), sizeof(glConfig.renderer_string)); Q_strncpyz(glConfig.version_string, (const char*)qglGetString(GL_VERSION), sizeof(glConfig.version_string)); Q_strncpyz(glConfig.extensions_string, (const char*)qglGetString(GL_EXTENSIONS), sizeof(glConfig.extensions_string)); GLW_InitExtensions(); } /* ** GLimp_Shutdown ** ** This routine does all OS specific shutdown procedures for the OpenGL ** subsystem. */ void GLimp_Shutdown( void ) { const char *success[] = { "failed", "success" }; ri.Printf(PRINT_ALL, "Shutting down OpenGL subsystem\n"); if (qwglMakeCurrent) { int retVal = qwglMakeCurrent(NULL, NULL) != 0; ri.Printf(PRINT_ALL, "...wglMakeCurrent( NULL, NULL ): %s\n", success[retVal]); } if (gl_hglrc) { int retVal = qwglDeleteContext(gl_hglrc) != 0; ri.Printf(PRINT_ALL, "...deleting GL context: %s\n", success[retVal]); gl_hglrc = NULL; } if (gl_hdc) { int retVal = ReleaseDC(g_wv.hWnd_opengl, gl_hdc) != 0; ri.Printf(PRINT_ALL, "...releasing DC: %s\n", success[retVal]); gl_hdc = NULL; } // destroy window if (g_wv.hWnd_opengl) { ri.Printf(PRINT_ALL, "...destroying opengl window\n"); ShowWindow(g_wv.hWnd_opengl, SW_HIDE); DestroyWindow(g_wv.hWnd_opengl); if (g_wv.hWnd == g_wv.hWnd_opengl) { g_wv.hWnd = NULL; } g_wv.hWnd_opengl = NULL; } QGL_Shutdown(); WG_RestoreGamma(); gl_active = false; memset(&glConfig, 0, sizeof(glConfig)); memset(&glState, 0, sizeof(glState)); if (log_fp) { fclose(log_fp); log_fp = 0; } } void GLimp_LogComment( char *comment ) { if ( log_fp ) { fprintf( log_fp, "%s", comment ); } } void vk_imp_init() { ri.Printf(PRINT_ALL, "Initializing Vulkan subsystem\n"); // This will set qgl pointers to no-op placeholders. if (!gl_active) { QGL_Init(nullptr); qglActiveTextureARB = [] (GLenum) {}; qglClientActiveTextureARB = [](GLenum) {}; } // Load Vulkan DLL. const char* dll_name = "vulkan-1.dll"; ri.Printf(PRINT_ALL, "...calling LoadLibrary('%s'): ", dll_name); vk_library_handle = LoadLibrary(dll_name); if (vk_library_handle == NULL) { ri.Printf(PRINT_ALL, "failed\n"); ri.Error(ERR_FATAL, "vk_imp_init - could not load %s\n", dll_name); } ri.Printf( PRINT_ALL, "succeeded\n" ); vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)GetProcAddress(vk_library_handle, "vkGetInstanceProcAddr"); // Create window. SetMode(r_mode->integer, (qboolean)r_fullscreen->integer); if (get_render_api() == RENDER_API_VK) { g_wv.hWnd_vulkan = create_main_window(glConfig.vidWidth, glConfig.vidHeight, (qboolean)r_fullscreen->integer); g_wv.hWnd = g_wv.hWnd_vulkan; SetForegroundWindow(g_wv.hWnd); SetFocus(g_wv.hWnd); WG_CheckHardwareGamma(); } else { g_wv.hWnd_vulkan = create_twin_window(glConfig.vidWidth, glConfig.vidHeight, RENDER_API_VK); } } void vk_imp_shutdown() { ri.Printf(PRINT_ALL, "Shutting down Vulkan subsystem\n"); if (g_wv.hWnd_vulkan) { ri.Printf(PRINT_ALL, "...destroying Vulkan window\n"); DestroyWindow(g_wv.hWnd_vulkan); if (g_wv.hWnd == g_wv.hWnd_vulkan) { g_wv.hWnd = NULL; } g_wv.hWnd_vulkan = NULL; } if (vk_library_handle != NULL) { ri.Printf(PRINT_ALL, "...unloading Vulkan DLL\n"); FreeLibrary(vk_library_handle); vk_library_handle = NULL; } vkGetInstanceProcAddr = nullptr; // For vulkan mode we still have qgl pointers initialized with placeholder values. // Reset them the same way as we do in opengl mode. QGL_Shutdown(); WG_RestoreGamma(); memset(&glConfig, 0, sizeof(glConfig)); memset(&glState, 0, sizeof(glState)); if (log_fp) { fclose(log_fp); log_fp = 0; } } void vk_imp_create_surface() { VkWin32SurfaceCreateInfoKHR desc; desc.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; desc.pNext = nullptr; desc.flags = 0; desc.hinstance = ::GetModuleHandle(nullptr); desc.hwnd = g_wv.hWnd_vulkan; VK_CHECK(vkCreateWin32SurfaceKHR(vk.instance, &desc, nullptr, &vk.surface)); } void dx_imp_init() { ri.Printf(PRINT_ALL, "Initializing DX12 subsystem\n"); // This will set qgl pointers to no-op placeholders. if (!gl_active) { QGL_Init(nullptr); qglActiveTextureARB = [] (GLenum) {}; qglClientActiveTextureARB = [](GLenum) {}; } // Create window. SetMode(r_mode->integer, (qboolean)r_fullscreen->integer); if (get_render_api() == RENDER_API_DX) { g_wv.hWnd_dx = create_main_window(glConfig.vidWidth, glConfig.vidHeight, (qboolean)r_fullscreen->integer); g_wv.hWnd = g_wv.hWnd_dx; SetForegroundWindow(g_wv.hWnd); SetFocus(g_wv.hWnd); WG_CheckHardwareGamma(); } else { g_wv.hWnd_dx = create_twin_window(glConfig.vidWidth, glConfig.vidHeight, RENDER_API_DX); } } void dx_imp_shutdown() { ri.Printf(PRINT_ALL, "Shutting down DX12 subsystem\n"); if (g_wv.hWnd_dx) { ri.Printf(PRINT_ALL, "...destroying DX12 window\n"); DestroyWindow(g_wv.hWnd_dx); if (g_wv.hWnd == g_wv.hWnd_dx) { g_wv.hWnd = NULL; } g_wv.hWnd_dx = NULL; } // For DX12 mode we still have qgl pointers initialized with placeholder values. // Reset them the same way as we do in opengl mode. QGL_Shutdown(); WG_RestoreGamma(); memset(&glConfig, 0, sizeof(glConfig)); memset(&glState, 0, sizeof(glState)); if (log_fp) { fclose(log_fp); log_fp = 0; } } /* =========================================================== SMP acceleration =========================================================== */ HANDLE renderCommandsEvent; HANDLE renderCompletedEvent; HANDLE renderActiveEvent; void (*glimpRenderThread)( void ); void GLimp_RenderThreadWrapper( void ) { glimpRenderThread(); // unbind the context before we die qwglMakeCurrent( gl_hdc, NULL ); } /* ======================= GLimp_SpawnRenderThread ======================= */ HANDLE renderThreadHandle; int renderThreadId; qboolean GLimp_SpawnRenderThread( void (*function)( void ) ) { renderCommandsEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); renderCompletedEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); renderActiveEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); glimpRenderThread = function; renderThreadHandle = CreateThread( NULL, // LPSECURITY_ATTRIBUTES lpsa, 0, // DWORD cbStack, (LPTHREAD_START_ROUTINE)GLimp_RenderThreadWrapper, // LPTHREAD_START_ROUTINE lpStartAddr, 0, // LPVOID lpvThreadParm, 0, // DWORD fdwCreate, (LPDWORD)&renderThreadId ); if ( !renderThreadHandle ) { return qfalse; } return qtrue; } static void *smpData; static int wglErrors; void *GLimp_RendererSleep( void ) { void *data; if ( !qwglMakeCurrent( gl_hdc, NULL ) ) { wglErrors++; } ResetEvent( renderActiveEvent ); // after this, the front end can exit GLimp_FrontEndSleep SetEvent( renderCompletedEvent ); WaitForSingleObject( renderCommandsEvent, INFINITE ); if ( !qwglMakeCurrent( gl_hdc, gl_hglrc ) ) { wglErrors++; } ResetEvent( renderCompletedEvent ); ResetEvent( renderCommandsEvent ); data = smpData; // after this, the main thread can exit GLimp_WakeRenderer SetEvent( renderActiveEvent ); return data; } void GLimp_FrontEndSleep( void ) { WaitForSingleObject( renderCompletedEvent, INFINITE ); if ( !qwglMakeCurrent( gl_hdc, gl_hglrc ) ) { wglErrors++; } } void GLimp_WakeRenderer( void *data ) { smpData = data; if ( !qwglMakeCurrent( gl_hdc, NULL ) ) { wglErrors++; } // after this, the renderer can continue through GLimp_RendererSleep SetEvent( renderCommandsEvent ); WaitForSingleObject( renderActiveEvent, INFINITE ); } ================================================ FILE: src/engine/platform/win_input.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // win_input.c -- win32 mouse and joystick code // 02/21/97 JCB Added extended DirectInput code to support external controllers. #include "../client/client.h" #include "win_local.h" typedef struct { int oldButtonState; qboolean mouseActive; qboolean mouseInitialized; } WinMouseVars_t; static WinMouseVars_t s_wmv; static int window_center_x, window_center_y; cvar_t *in_mouse; cvar_t *in_logitechbug; qboolean in_appactive; /* ============================================================ WIN32 MOUSE CONTROL ============================================================ */ /* ================ IN_InitWin32Mouse ================ */ void IN_InitWin32Mouse( void ) { } /* ================ IN_ShutdownWin32Mouse ================ */ void IN_ShutdownWin32Mouse( void ) { } /* ================ IN_ActivateWin32Mouse ================ */ void IN_ActivateWin32Mouse( void ) { int width, height; RECT window_rect; width = GetSystemMetrics (SM_CXSCREEN); height = GetSystemMetrics (SM_CYSCREEN); GetWindowRect ( g_wv.hWnd, &window_rect); if (window_rect.left < 0) window_rect.left = 0; if (window_rect.top < 0) window_rect.top = 0; if (window_rect.right >= width) window_rect.right = width-1; if (window_rect.bottom >= height-1) window_rect.bottom = height-1; window_center_x = (window_rect.right + window_rect.left)/2; window_center_y = (window_rect.top + window_rect.bottom)/2; SetCursorPos (window_center_x, window_center_y); SetCapture ( g_wv.hWnd ); ClipCursor (&window_rect); while (ShowCursor (FALSE) >= 0) ; } /* ================ IN_DeactivateWin32Mouse ================ */ void IN_DeactivateWin32Mouse( void ) { ClipCursor (NULL); ReleaseCapture (); while (ShowCursor (TRUE) < 0) ; } /* ================ IN_Win32Mouse ================ */ void IN_Win32Mouse( int *mx, int *my ) { POINT current_pos; // find mouse movement GetCursorPos (¤t_pos); // force the mouse to the center, so there's room to move SetCursorPos (window_center_x, window_center_y); *mx = current_pos.x - window_center_x; *my = current_pos.y - window_center_y; } /* ============================================================ MOUSE CONTROL ============================================================ */ /* =========== IN_ActivateMouse Called when the window gains focus or changes in some way =========== */ void IN_ActivateMouse( void ) { if (!s_wmv.mouseInitialized ) { return; } if ( !in_mouse->integer ) { s_wmv.mouseActive = qfalse; return; } if ( s_wmv.mouseActive ) { return; } s_wmv.mouseActive = qtrue; IN_ActivateWin32Mouse(); } /* =========== IN_DeactivateMouse Called when the window loses focus =========== */ void IN_DeactivateMouse( void ) { if (!s_wmv.mouseInitialized ) { return; } if (!s_wmv.mouseActive ) { return; } s_wmv.mouseActive = qfalse; IN_DeactivateWin32Mouse(); } /* =========== IN_StartupMouse =========== */ void IN_StartupMouse( void ) { s_wmv.mouseInitialized = qfalse; if ( in_mouse->integer == 0 ) { Com_Printf ("Mouse control not active.\n"); return; } s_wmv.mouseInitialized = qtrue; IN_InitWin32Mouse(); Com_Printf ("Win32 mouse input initialized.\n"); } /* =========== IN_MouseEvent =========== */ void IN_MouseEvent (int mstate) { int i; if ( !s_wmv.mouseInitialized ) return; // perform button actions for (i = 0 ; i < 3 ; i++ ) { if ( (mstate & (1<modified = qfalse; } /* =========== IN_Shutdown =========== */ void IN_Shutdown( void ) { IN_DeactivateMouse(); } /* =========== IN_Init =========== */ void IN_Init( void ) { in_mouse = Cvar_Get ("in_mouse", "1", CVAR_ARCHIVE|CVAR_LATCH); in_logitechbug = Cvar_Get ("in_logitechbug", "0", CVAR_ARCHIVE); IN_Startup(); } /* =========== IN_Activate Called when the main window gains or loses focus. The window may have been destroyed and recreated between a deactivate and an activate. =========== */ void IN_Activate (qboolean active) { in_appactive = active; if ( !active ) { IN_DeactivateMouse(); } } /* ================== IN_Frame Called every frame, even if not generating commands ================== */ void IN_Frame (void) { if ( !s_wmv.mouseInitialized ) { return; } if ( cls.keyCatchers & KEYCATCH_CONSOLE ) { // temporarily deactivate if not in the game and // running on the desktop // voodoo always counts as full screen if (Cvar_VariableValue ("r_fullscreen") == 0 ) { IN_DeactivateMouse (); return; } } if ( !in_appactive ) { IN_DeactivateMouse (); return; } IN_ActivateMouse(); // post events to the system que IN_MouseMove(); } ================================================ FILE: src/engine/platform/win_local.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // win_local.h: Win32-specific Quake3 header file #pragma once #if defined (_MSC_VER) && (_MSC_VER >= 1200) #pragma warning(disable : 4201) #pragma warning( push ) #endif #include #if defined (_MSC_VER) && (_MSC_VER >= 1200) #pragma warning( pop ) #endif #define DIRECTSOUND_VERSION 0x0300 #include #include #include void IN_MouseEvent (int mstate); void Sys_QueEvent( int time, sysEventType_t type, int value, int value2, int ptrLength, void *ptr ); void Sys_CreateConsole( void ); void Sys_DestroyConsole( void ); char *Sys_ConsoleInput (void); qboolean Sys_GetPacket ( netadr_t *net_from, msg_t *net_message ); // Input subsystem void IN_Init (void); void IN_Shutdown (void); void IN_Activate (qboolean active); void IN_Frame (void); // window procedure LONG WINAPI MainWndProc ( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); void Conbuf_AppendText( const char *msg ); void SNDDMA_Activate( void ); int SNDDMA_InitDS (); typedef struct { HINSTANCE reflib_library; // Handle to refresh DLL qboolean reflib_active; HWND hWnd; // main window, refers to one of the hWnd_XXX listed below HWND hWnd_opengl; HWND hWnd_vulkan; HWND hWnd_dx; HINSTANCE hInstance; qboolean activeApp; qboolean isMinimized; OSVERSIONINFO osversion; // when we get a windows message, we store the time off so keyboard processing // can know the exact time of an event unsigned sysMsgTime; } WinVars_t; extern WinVars_t g_wv; ================================================ FILE: src/engine/platform/win_main.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // win_main.c #include "../client/client.h" #include "../qcommon/qcommon.h" #include "win_local.h" #include "resource.h" #include #include #include #include #include #include #include #define CD_BASEDIR "quake3" #define CD_EXE "quake3.exe" #define CD_BASEDIR_LINUX "bin\\x86\\glibc-2.1" #define CD_EXE_LINUX "quake3" #define MEM_THRESHOLD 96*1024*1024 static char sys_cmdline[MAX_STRING_CHARS]; // define this to use alternate spanking method // I found out that the regular way doesn't work on my box for some reason // see the associated spank.sh script #define ALT_SPANK #ifdef ALT_SPANK #include #include int fh = 0; void Spk_Open(char *name) { fh = open( name, O_TRUNC | O_CREAT | O_WRONLY, S_IREAD | S_IWRITE ); }; void Spk_Close() { if (!fh) return; close( fh ); fh = 0; } void Spk_Printf (const char *text, ...) { va_list argptr; char buf[32768]; if (!fh) return; va_start (argptr,text); vsprintf (buf, text, argptr); write(fh, buf, (int)strlen(buf)); _commit(fh); va_end (argptr); }; #endif /* ================== Sys_LowPhysicalMemory() ================== */ qboolean Sys_LowPhysicalMemory() { MEMORYSTATUS stat; GlobalMemoryStatus (&stat); return (stat.dwTotalPhys <= MEM_THRESHOLD) ? qtrue : qfalse; } /* ================== Sys_BeginProfiling ================== */ void Sys_BeginProfiling( void ) { // this is just used on the mac build } /* ============= Sys_Error Show the early console as an error dialog ============= */ void QDECL Sys_Error( const char *error, ... ) { va_list argptr; char text[4096]; MSG msg; va_start (argptr, error); vsprintf (text, error, argptr); va_end (argptr); Conbuf_AppendText( text ); Conbuf_AppendText( "\n" ); Sys_SetErrorText( text ); Sys_ShowConsole( 1, qtrue ); timeEndPeriod( 1 ); IN_Shutdown(); // wait for the user to quit while ( 1 ) { if (!GetMessage (&msg, NULL, 0, 0)) Com_Quit_f (); TranslateMessage (&msg); DispatchMessage (&msg); } Sys_DestroyConsole(); exit (1); } /* ============== Sys_Quit ============== */ void Sys_Quit( void ) { timeEndPeriod( 1 ); IN_Shutdown(); Sys_DestroyConsole(); exit (0); } /* ============== Sys_Print ============== */ void Sys_Print( const char *msg ) { Conbuf_AppendText( msg ); } /* ============== Sys_Mkdir ============== */ void Sys_Mkdir( const char *path ) { _mkdir (path); } /* ============== Sys_Cwd ============== */ char *Sys_Cwd( void ) { static char cwd[MAX_OSPATH]; _getcwd( cwd, sizeof( cwd ) - 1 ); cwd[MAX_OSPATH-1] = 0; return cwd; } /* ============== Sys_DefaultCDPath ============== */ char *Sys_DefaultCDPath( void ) { return ""; } /* ============== Sys_DefaultBasePath ============== */ char *Sys_DefaultBasePath( void ) { return Sys_Cwd(); } /* ============================================================== DIRECTORY SCANNING ============================================================== */ #define MAX_FOUND_FILES 0x1000 void Sys_ListFilteredFiles( const char *basedir, char *subdirs, char *filter, char **list, int *numfiles ) { char search[MAX_OSPATH], newsubdirs[MAX_OSPATH]; char filename[MAX_OSPATH]; int findhandle; struct _finddata_t findinfo; if ( *numfiles >= MAX_FOUND_FILES - 1 ) { return; } if (strlen(subdirs)) { Com_sprintf( search, sizeof(search), "%s\\%s\\*", basedir, subdirs ); } else { Com_sprintf( search, sizeof(search), "%s\\*", basedir ); } findhandle = _findfirst (search, &findinfo); if (findhandle == -1) { return; } do { if (findinfo.attrib & _A_SUBDIR) { if (Q_stricmp(findinfo.name, ".") && Q_stricmp(findinfo.name, "..")) { if (strlen(subdirs)) { Com_sprintf( newsubdirs, sizeof(newsubdirs), "%s\\%s", subdirs, findinfo.name); } else { Com_sprintf( newsubdirs, sizeof(newsubdirs), "%s", findinfo.name); } Sys_ListFilteredFiles( basedir, newsubdirs, filter, list, numfiles ); } } if ( *numfiles >= MAX_FOUND_FILES - 1 ) { break; } Com_sprintf( filename, sizeof(filename), "%s\\%s", subdirs, findinfo.name ); if (!Com_FilterPath( filter, filename, qfalse )) continue; list[ *numfiles ] = CopyString( filename ); (*numfiles)++; } while ( _findnext (findhandle, &findinfo) != -1 ); _findclose (findhandle); } static qboolean strgtr(const char *s0, const char *s1) { int l0, l1, i; l0 = (int)strlen(s0); l1 = (int)strlen(s1); if (l1 s0[i]) { return qtrue; } if (s1[i] < s0[i]) { return qfalse; } } return qfalse; } char **Sys_ListFiles( const char *directory, const char *extension, char *filter, int *numfiles, qboolean wantsubs ) { char search[MAX_OSPATH]; int nfiles; char **listCopy; char *list[MAX_FOUND_FILES]; struct _finddata_t findinfo; intptr_t findhandle; int flag; int i; if (filter) { nfiles = 0; Sys_ListFilteredFiles( directory, "", filter, list, &nfiles ); list[ nfiles ] = 0; *numfiles = nfiles; if (!nfiles) return NULL; listCopy = (char**) Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ) ); for ( i = 0 ; i < nfiles ; i++ ) { listCopy[i] = list[i]; } listCopy[i] = NULL; return listCopy; } if ( !extension) { extension = ""; } // passing a slash as extension will find directories if ( extension[0] == '/' && extension[1] == 0 ) { extension = ""; flag = 0; } else { flag = _A_SUBDIR; } Com_sprintf( search, sizeof(search), "%s\\*%s", directory, extension ); // search nfiles = 0; findhandle = _findfirst (search, &findinfo); if (findhandle == -1) { *numfiles = 0; return NULL; } do { if ( (!wantsubs && flag ^ ( findinfo.attrib & _A_SUBDIR )) || (wantsubs && findinfo.attrib & _A_SUBDIR) ) { if ( nfiles == MAX_FOUND_FILES - 1 ) { break; } list[ nfiles ] = CopyString( findinfo.name ); nfiles++; } } while ( _findnext (findhandle, &findinfo) != -1 ); list[ nfiles ] = 0; _findclose (findhandle); // return a copy of the list *numfiles = nfiles; if ( !nfiles ) { return NULL; } listCopy = (char**) Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ) ); for ( i = 0 ; i < nfiles ; i++ ) { listCopy[i] = list[i]; } listCopy[i] = NULL; do { flag = 0; for(i=1; i (5 * 60000)) && !Cvar_VariableIntegerValue( "dedicated" ) && !Cvar_VariableIntegerValue( "com_blindlyLoadDLLs" ) ) { if (FS_FileExists(filename)) { lastWarning = timestamp; ret = MessageBoxEx( NULL, "You are about to load a .DLL executable that\n" "has not been verified for use with Quake III Arena.\n" "This type of file can compromise the security of\n" "your computer.\n\n" "Select 'OK' if you choose to load it anyway.", "Security Warning", MB_OKCANCEL | MB_ICONEXCLAMATION | MB_DEFBUTTON2 | MB_TOPMOST | MB_SETFOREGROUND, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ) ); if( ret != IDOK ) { return NULL; } } } #endif #ifndef NDEBUG libHandle = LoadLibrary( filename ); if (libHandle) Com_Printf("LoadLibrary '%s' ok\n", filename); else Com_Printf("LoadLibrary '%s' failed\n", filename); if ( !libHandle ) { #endif basepath = Cvar_VariableString( "fs_basepath" ); cdpath = Cvar_VariableString( "fs_cdpath" ); gamedir = Cvar_VariableString( "fs_game" ); fn = FS_BuildOSPath( basepath, gamedir, filename ); libHandle = LoadLibrary( fn ); #ifndef NDEBUG if (libHandle) Com_Printf("LoadLibrary '%s' ok\n", fn); else Com_Printf("LoadLibrary '%s' failed\n", fn); #endif if ( !libHandle ) { if( cdpath[0] ) { fn = FS_BuildOSPath( cdpath, gamedir, filename ); libHandle = LoadLibrary( fn ); #ifndef NDEBUG if (libHandle) Com_Printf("LoadLibrary '%s' ok\n", fn); else Com_Printf("LoadLibrary '%s' failed\n", fn); #endif } if ( !libHandle ) { return NULL; } } #ifndef NDEBUG } #endif dllEntry = ( void (QDECL *)( intptr_t (QDECL *)( intptr_t, ... ) ) )GetProcAddress( libHandle, "dllEntry" ); *entryPoint = (intptr_t (QDECL *)(int,...))GetProcAddress( libHandle, "vmMain" ); if ( !*entryPoint || !dllEntry ) { FreeLibrary( libHandle ); return NULL; } dllEntry( systemcalls ); if ( libHandle ) Q_strncpyz ( fqpath , filename , MAX_QPATH ) ; // added 7/20/02 by T.Ray return libHandle; } /* ======================================================================== BACKGROUND FILE STREAMING ======================================================================== */ #if 1 void Sys_InitStreamThread( void ) { } void Sys_ShutdownStreamThread( void ) { } void Sys_BeginStreamedFile( fileHandle_t f, int readAhead ) { } void Sys_EndStreamedFile( fileHandle_t f ) { } int Sys_StreamedRead( void *buffer, int size, int count, fileHandle_t f ) { return FS_Read( buffer, size * count, f ); } void Sys_StreamSeek( fileHandle_t f, int offset, int origin ) { FS_Seek( f, offset, origin ); } #else typedef struct { fileHandle_t file; byte *buffer; qboolean eof; qboolean active; int bufferSize; int streamPosition; // next byte to be returned by Sys_StreamRead int threadPosition; // next byte to be read from file } streamsIO_t; typedef struct { HANDLE threadHandle; int threadId; CRITICAL_SECTION crit; streamsIO_t sIO[MAX_FILE_HANDLES]; } streamState_t; streamState_t stream; /* =============== Sys_StreamThread A thread will be sitting in this loop forever ================ */ void Sys_StreamThread( void ) { int buffer; int count; int readCount; int bufferPoint; int r, i; while (1) { Sleep( 10 ); // EnterCriticalSection (&stream.crit); for (i=1;i 0 ) { available = stream.sIO[f].threadPosition - stream.sIO[f].streamPosition; if ( !available ) { if ( stream.sIO[f].eof ) { break; } if ( sleepCount == 1 ) { Com_DPrintf( "Sys_StreamedRead: waiting\n" ); } if ( ++sleepCount > 100 ) { Com_Error( ERR_FATAL, "Sys_StreamedRead: thread has died"); } Sleep( 10 ); continue; } EnterCriticalSection( &stream.crit ); bufferPoint = stream.sIO[f].streamPosition % stream.sIO[f].bufferSize; bufferCount = stream.sIO[f].bufferSize - bufferPoint; copy = available < bufferCount ? available : bufferCount; if ( copy > remaining ) { copy = remaining; } memcpy( dest, stream.sIO[f].buffer + bufferPoint, copy ); stream.sIO[f].streamPosition += copy; dest += copy; remaining -= copy; LeaveCriticalSection( &stream.crit ); } return (count * size - remaining) / size; } /* =============== Sys_StreamSeek ================ */ void Sys_StreamSeek( fileHandle_t f, int offset, int origin ) { // halt the thread EnterCriticalSection( &stream.crit ); // clear to that point FS_Seek( f, offset, origin ); stream.sIO[f].streamPosition = 0; stream.sIO[f].threadPosition = 0; stream.sIO[f].eof = qfalse; // let the thread start running at the new position LeaveCriticalSection( &stream.crit ); } #endif /* ======================================================================== EVENT LOOP ======================================================================== */ #define MAX_QUED_EVENTS 256 #define MASK_QUED_EVENTS ( MAX_QUED_EVENTS - 1 ) sysEvent_t eventQue[MAX_QUED_EVENTS]; int eventHead, eventTail; byte sys_packetReceived[MAX_MSGLEN]; /* ================ Sys_QueEvent A time of 0 will get the current time Ptr should either be null, or point to a block of data that can be freed by the game later. ================ */ void Sys_QueEvent( int time, sysEventType_t type, int value, int value2, int ptrLength, void *ptr ) { sysEvent_t *ev; ev = &eventQue[ eventHead & MASK_QUED_EVENTS ]; if ( eventHead - eventTail >= MAX_QUED_EVENTS ) { Com_Printf("Sys_QueEvent: overflow\n"); // we are discarding an event, but don't leak memory if ( ev->evPtr ) { Z_Free( ev->evPtr ); } eventTail++; } eventHead++; if ( time == 0 ) { time = Sys_Milliseconds(); } ev->evTime = time; ev->evType = type; ev->evValue = value; ev->evValue2 = value2; ev->evPtrLength = ptrLength; ev->evPtr = ptr; } /* ================ Sys_GetEvent ================ */ sysEvent_t Sys_GetEvent( void ) { MSG msg; sysEvent_t ev; char *s; msg_t netmsg; netadr_t adr; // return if we have data if ( eventHead > eventTail ) { eventTail++; return eventQue[ ( eventTail - 1 ) & MASK_QUED_EVENTS ]; } // pump the message loop while (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) { if ( !GetMessage (&msg, NULL, 0, 0) ) { Com_Quit_f(); } // save the msg time, because wndprocs don't have access to the timestamp g_wv.sysMsgTime = msg.time; TranslateMessage (&msg); DispatchMessage (&msg); } // check for console commands s = Sys_ConsoleInput(); if ( s ) { char *b; int len; len = (int)strlen( s ) + 1; b = (char*)Z_Malloc( len ); Q_strncpyz( b, s, len-1 ); Sys_QueEvent( 0, SE_CONSOLE, 0, 0, len, b ); } // check for network packets MSG_Init( &netmsg, sys_packetReceived, sizeof( sys_packetReceived ) ); if ( Sys_GetPacket ( &adr, &netmsg ) ) { netadr_t *buf; int len; // copy out to a seperate buffer for qeueing // the readcount stepahead is for SOCKS support len = sizeof( netadr_t ) + netmsg.cursize - netmsg.readcount; buf = (netadr_t*) Z_Malloc( len ); *buf = adr; memcpy( buf+1, &netmsg.data[netmsg.readcount], netmsg.cursize - netmsg.readcount ); Sys_QueEvent( 0, SE_PACKET, 0, 0, len, buf ); } // return if we have data if ( eventHead > eventTail ) { eventTail++; return eventQue[ ( eventTail - 1 ) & MASK_QUED_EVENTS ]; } // create an empty event to return memset( &ev, 0, sizeof( ev ) ); ev.evTime = timeGetTime(); return ev; } //================================================================ /* ================= Sys_In_Restart_f Restart the input subsystem ================= */ void Sys_In_Restart_f( void ) { IN_Shutdown(); IN_Init(); } /* ================= Sys_Net_Restart_f Restart the network subsystem ================= */ void Sys_Net_Restart_f( void ) { NET_Restart(); } /* ================ Sys_Init Called after the common systems (cvars, files, etc) are initialized ================ */ #define OSR2_BUILD_NUMBER 1111 #define WIN98_BUILD_NUMBER 1998 void Sys_Init( void ) { int cpuid; // make sure the timer is high precision, otherwise // NT gets 18ms resolution timeBeginPeriod( 1 ); Cmd_AddCommand ("in_restart", Sys_In_Restart_f); Cmd_AddCommand ("net_restart", Sys_Net_Restart_f); g_wv.osversion.dwOSVersionInfoSize = sizeof( g_wv.osversion ); if (!GetVersionEx (&g_wv.osversion)) Sys_Error ("Couldn't get OS info"); if (g_wv.osversion.dwMajorVersion < 4) Sys_Error ("Quake3 requires Windows version 4 or greater"); if (g_wv.osversion.dwPlatformId == VER_PLATFORM_WIN32s) Sys_Error ("Quake3 doesn't run on Win32s"); if ( g_wv.osversion.dwPlatformId == VER_PLATFORM_WIN32_NT ) { Cvar_Set( "arch", "winnt" ); } else if ( g_wv.osversion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ) { if ( LOWORD( g_wv.osversion.dwBuildNumber ) >= WIN98_BUILD_NUMBER ) { Cvar_Set( "arch", "win98" ); } else if ( LOWORD( g_wv.osversion.dwBuildNumber ) >= OSR2_BUILD_NUMBER ) { Cvar_Set( "arch", "win95 osr2.x" ); } else { Cvar_Set( "arch", "win95" ); } } else { Cvar_Set( "arch", "unknown Windows variant" ); } // save out a couple things in rom cvars for the renderer to access Cvar_Get( "win_wndproc", va("%p", MainWndProc), CVAR_ROM ); // // figure out our CPU // Cvar_Get( "sys_cpustring", "detect", 0 ); if ( !Q_stricmp( Cvar_VariableString( "sys_cpustring"), "detect" ) ) { Com_Printf( "...detecting CPU, found " ); cpuid = Sys_GetProcessorId(); switch ( cpuid ) { case CPUID_GENERIC: Cvar_Set( "sys_cpustring", "generic" ); break; default: Com_Error( ERR_FATAL, "Unknown cpu type %d\n", cpuid ); break; } } else { Com_Printf( "...forcing CPU type to " ); if ( !Q_stricmp( Cvar_VariableString( "sys_cpustring" ), "generic" ) ) { cpuid = CPUID_GENERIC; } else { Com_Printf( "WARNING: unknown sys_cpustring '%s'\n", Cvar_VariableString( "sys_cpustring" ) ); cpuid = CPUID_GENERIC; } } Cvar_SetValue( "sys_cpuid", cpuid ); Com_Printf( "%s\n", Cvar_VariableString( "sys_cpustring" ) ); Cvar_Set( "username", Sys_GetCurrentUser() ); IN_Init(); // FIXME: not in dedicated? } //======================================================================= int totalMsec, countMsec; /* ================== WinMain ================== */ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { char cwd[MAX_OSPATH]; int startTime, endTime; // should never get a previous instance in Win32 if ( hPrevInstance ) { return 0; } g_wv.hInstance = hInstance; Q_strncpyz( sys_cmdline, lpCmdLine, sizeof( sys_cmdline ) ); // done before Com/Sys_Init since we need this for error output Sys_CreateConsole(); // no abort/retry/fail errors SetErrorMode( SEM_FAILCRITICALERRORS ); // get the initial time base Sys_Milliseconds(); #if 0 // if we find the CD, add a +set cddir xxx command line Sys_ScanForCD(); #endif Sys_InitStreamThread(); Com_Init( sys_cmdline ); NET_Init(); _getcwd (cwd, sizeof(cwd)); Com_Printf("Working directory: %s\n", cwd); // hide the early console since we've reached the point where we // have a working graphics subsystems if ( !com_dedicated->integer && !com_viewlog->integer ) { Sys_ShowConsole( 0, qfalse ); } // main game loop while( 1 ) { // if not running as a game client, sleep a bit if ( g_wv.isMinimized || ( com_dedicated && com_dedicated->integer ) ) { Sleep( 5 ); } // set low precision every frame, because some system calls // reset it arbitrarily // _controlfp( _PC_24, _MCW_PC ); // _controlfp( -1, _MCW_EM ); // no exceptions, even if some crappy // syscall turns them back on! startTime = Sys_Milliseconds(); // make sure mouse and joystick are only called once a frame IN_Frame(); // run the game Com_Frame(); endTime = Sys_Milliseconds(); totalMsec += endTime - startTime; countMsec++; } // never gets here } ================================================ FILE: src/engine/platform/win_net.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // net_wins.c #include "../../game/q_shared.h" #include "../qcommon/qcommon.h" #include "win_local.h" static WSADATA winsockdata; static qboolean winsockInitialized = qfalse; static qboolean usingSocks = qfalse; static qboolean networkingEnabled = qfalse; static cvar_t *net_noudp; static cvar_t *net_noipx; static cvar_t *net_socksEnabled; static cvar_t *net_socksServer; static cvar_t *net_socksPort; static cvar_t *net_socksUsername; static cvar_t *net_socksPassword; static struct sockaddr socksRelayAddr; static SOCKET ip_socket; static SOCKET socks_socket; static SOCKET ipx_socket; #define MAX_IPS 16 static int numIP; static byte localIP[MAX_IPS][4]; //============================================================================= /* ==================== NET_ErrorString ==================== */ char *NET_ErrorString( void ) { int code; code = WSAGetLastError(); switch( code ) { case WSAEINTR: return "WSAEINTR"; case WSAEBADF: return "WSAEBADF"; case WSAEACCES: return "WSAEACCES"; case WSAEDISCON: return "WSAEDISCON"; case WSAEFAULT: return "WSAEFAULT"; case WSAEINVAL: return "WSAEINVAL"; case WSAEMFILE: return "WSAEMFILE"; case WSAEWOULDBLOCK: return "WSAEWOULDBLOCK"; case WSAEINPROGRESS: return "WSAEINPROGRESS"; case WSAEALREADY: return "WSAEALREADY"; case WSAENOTSOCK: return "WSAENOTSOCK"; case WSAEDESTADDRREQ: return "WSAEDESTADDRREQ"; case WSAEMSGSIZE: return "WSAEMSGSIZE"; case WSAEPROTOTYPE: return "WSAEPROTOTYPE"; case WSAENOPROTOOPT: return "WSAENOPROTOOPT"; case WSAEPROTONOSUPPORT: return "WSAEPROTONOSUPPORT"; case WSAESOCKTNOSUPPORT: return "WSAESOCKTNOSUPPORT"; case WSAEOPNOTSUPP: return "WSAEOPNOTSUPP"; case WSAEPFNOSUPPORT: return "WSAEPFNOSUPPORT"; case WSAEAFNOSUPPORT: return "WSAEAFNOSUPPORT"; case WSAEADDRINUSE: return "WSAEADDRINUSE"; case WSAEADDRNOTAVAIL: return "WSAEADDRNOTAVAIL"; case WSAENETDOWN: return "WSAENETDOWN"; case WSAENETUNREACH: return "WSAENETUNREACH"; case WSAENETRESET: return "WSAENETRESET"; case WSAECONNABORTED: return "WSWSAECONNABORTEDAEINTR"; case WSAECONNRESET: return "WSAECONNRESET"; case WSAENOBUFS: return "WSAENOBUFS"; case WSAEISCONN: return "WSAEISCONN"; case WSAENOTCONN: return "WSAENOTCONN"; case WSAESHUTDOWN: return "WSAESHUTDOWN"; case WSAETOOMANYREFS: return "WSAETOOMANYREFS"; case WSAETIMEDOUT: return "WSAETIMEDOUT"; case WSAECONNREFUSED: return "WSAECONNREFUSED"; case WSAELOOP: return "WSAELOOP"; case WSAENAMETOOLONG: return "WSAENAMETOOLONG"; case WSAEHOSTDOWN: return "WSAEHOSTDOWN"; case WSASYSNOTREADY: return "WSASYSNOTREADY"; case WSAVERNOTSUPPORTED: return "WSAVERNOTSUPPORTED"; case WSANOTINITIALISED: return "WSANOTINITIALISED"; case WSAHOST_NOT_FOUND: return "WSAHOST_NOT_FOUND"; case WSATRY_AGAIN: return "WSATRY_AGAIN"; case WSANO_RECOVERY: return "WSANO_RECOVERY"; case WSANO_DATA: return "WSANO_DATA"; default: return "NO ERROR"; } } void NetadrToSockadr( netadr_t *a, struct sockaddr *s ) { memset( s, 0, sizeof(*s) ); if( a->type == NA_BROADCAST ) { ((struct sockaddr_in *)s)->sin_family = AF_INET; ((struct sockaddr_in *)s)->sin_port = a->port; ((struct sockaddr_in *)s)->sin_addr.s_addr = INADDR_BROADCAST; } else if( a->type == NA_IP ) { ((struct sockaddr_in *)s)->sin_family = AF_INET; ((struct sockaddr_in *)s)->sin_addr.s_addr = *(int *)&a->ip; ((struct sockaddr_in *)s)->sin_port = a->port; } else if( a->type == NA_IPX ) { ((struct sockaddr_ipx *)s)->sa_family = AF_IPX; memcpy( ((struct sockaddr_ipx *)s)->sa_netnum, &a->ipx[0], 4 ); memcpy( ((struct sockaddr_ipx *)s)->sa_nodenum, &a->ipx[4], 6 ); ((struct sockaddr_ipx *)s)->sa_socket = a->port; } else if( a->type == NA_BROADCAST_IPX ) { ((struct sockaddr_ipx *)s)->sa_family = AF_IPX; memset( ((struct sockaddr_ipx *)s)->sa_netnum, 0, 4 ); memset( ((struct sockaddr_ipx *)s)->sa_nodenum, 0xff, 6 ); ((struct sockaddr_ipx *)s)->sa_socket = a->port; } } void SockadrToNetadr( struct sockaddr *s, netadr_t *a ) { if (s->sa_family == AF_INET) { a->type = NA_IP; *(int *)&a->ip = ((struct sockaddr_in *)s)->sin_addr.s_addr; a->port = ((struct sockaddr_in *)s)->sin_port; } else if( s->sa_family == AF_IPX ) { a->type = NA_IPX; memcpy( &a->ipx[0], ((struct sockaddr_ipx *)s)->sa_netnum, 4 ); memcpy( &a->ipx[4], ((struct sockaddr_ipx *)s)->sa_nodenum, 6 ); a->port = ((struct sockaddr_ipx *)s)->sa_socket; } } /* ============= Sys_StringToAdr idnewt 192.246.40.70 12121212.121212121212 ============= */ #define DO(src,dest) \ copy[0] = s[src]; \ copy[1] = s[src + 1]; \ sscanf (copy, "%x", &val); \ ((struct sockaddr_ipx *)sadr)->dest = val qboolean Sys_StringToSockaddr( const char *s, struct sockaddr *sadr ) { struct hostent *h; int val; char copy[MAX_STRING_CHARS]; memset( sadr, 0, sizeof( *sadr ) ); // check for an IPX address if( ( (int)strlen( s ) == 21 ) && ( s[8] == '.' ) ) { ((struct sockaddr_ipx *)sadr)->sa_family = AF_IPX; ((struct sockaddr_ipx *)sadr)->sa_socket = 0; copy[2] = 0; DO(0, sa_netnum[0]); DO(2, sa_netnum[1]); DO(4, sa_netnum[2]); DO(6, sa_netnum[3]); DO(9, sa_nodenum[0]); DO(11, sa_nodenum[1]); DO(13, sa_nodenum[2]); DO(15, sa_nodenum[3]); DO(17, sa_nodenum[4]); DO(19, sa_nodenum[5]); } else { ((struct sockaddr_in *)sadr)->sin_family = AF_INET; ((struct sockaddr_in *)sadr)->sin_port = 0; if( s[0] >= '0' && s[0] <= '9' ) { *(int *)&((struct sockaddr_in *)sadr)->sin_addr = inet_addr(s); } else { if( ( h = gethostbyname( s ) ) == 0 ) { return (qboolean) 0; } *(int *)&((struct sockaddr_in *)sadr)->sin_addr = *(int *)h->h_addr_list[0]; } } return qtrue; } #undef DO /* ============= Sys_StringToAdr idnewt 192.246.40.70 ============= */ qboolean Sys_StringToAdr( const char *s, netadr_t *a ) { struct sockaddr sadr; if ( !Sys_StringToSockaddr( s, &sadr ) ) { return qfalse; } SockadrToNetadr( &sadr, a ); return qtrue; } //============================================================================= /* ================== Sys_GetPacket Never called by the game logic, just the system event queing ================== */ int recvfromCount; qboolean Sys_GetPacket( netadr_t *net_from, msg_t *net_message ) { int ret; struct sockaddr from; int fromlen; int net_socket; int protocol; int err; for( protocol = 0 ; protocol < 2 ; protocol++ ) { if( protocol == 0 ) { net_socket = ip_socket; } else { net_socket = ipx_socket; } if( !net_socket ) { continue; } fromlen = sizeof(from); recvfromCount++; // performance check ret = recvfrom( net_socket, (char*) net_message->data, net_message->maxsize, 0, (struct sockaddr *)&from, &fromlen ); if (ret == SOCKET_ERROR) { err = WSAGetLastError(); if( err == WSAEWOULDBLOCK || err == WSAECONNRESET ) { continue; } Com_Printf( "NET_GetPacket: %s\n", NET_ErrorString() ); continue; } if ( net_socket == ip_socket ) { memset( ((struct sockaddr_in *)&from)->sin_zero, 0, 8 ); } if ( usingSocks && net_socket == ip_socket && memcmp( &from, &socksRelayAddr, fromlen ) == 0 ) { if ( ret < 10 || net_message->data[0] != 0 || net_message->data[1] != 0 || net_message->data[2] != 0 || net_message->data[3] != 1 ) { continue; } net_from->type = NA_IP; net_from->ip[0] = net_message->data[4]; net_from->ip[1] = net_message->data[5]; net_from->ip[2] = net_message->data[6]; net_from->ip[3] = net_message->data[7]; net_from->port = *(short *)&net_message->data[8]; net_message->readcount = 10; } else { SockadrToNetadr( &from, net_from ); net_message->readcount = 0; } if( ret == net_message->maxsize ) { Com_Printf( "Oversize packet from %s\n", NET_AdrToString (*net_from) ); continue; } net_message->cursize = ret; return qtrue; } return qfalse; } //============================================================================= static char socksBuf[4096]; /* ================== Sys_SendPacket ================== */ void Sys_SendPacket( int length, const void *data, netadr_t to ) { int ret; struct sockaddr addr; SOCKET net_socket; if( to.type == NA_BROADCAST ) { net_socket = ip_socket; } else if( to.type == NA_IP ) { net_socket = ip_socket; } else if( to.type == NA_IPX ) { net_socket = ipx_socket; } else if( to.type == NA_BROADCAST_IPX ) { net_socket = ipx_socket; } else { Com_Error( ERR_FATAL, "Sys_SendPacket: bad address type" ); return; } if( !net_socket ) { return; } NetadrToSockadr( &to, &addr ); if( usingSocks && to.type == NA_IP ) { socksBuf[0] = 0; // reserved socksBuf[1] = 0; socksBuf[2] = 0; // fragment (not fragmented) socksBuf[3] = 1; // address type: IPV4 *(int *)&socksBuf[4] = ((struct sockaddr_in *)&addr)->sin_addr.s_addr; *(short *)&socksBuf[8] = ((struct sockaddr_in *)&addr)->sin_port; memcpy( &socksBuf[10], data, length ); ret = sendto( net_socket, socksBuf, length+10, 0, &socksRelayAddr, sizeof(socksRelayAddr) ); } else { ret = sendto( net_socket, (const char*) data, length, 0, &addr, sizeof(addr) ); } if( ret == SOCKET_ERROR ) { int err = WSAGetLastError(); // wouldblock is silent if( err == WSAEWOULDBLOCK ) { return; } // some PPP links do not allow broadcasts and return an error if( ( err == WSAEADDRNOTAVAIL ) && ( ( to.type == NA_BROADCAST ) || ( to.type == NA_BROADCAST_IPX ) ) ) { return; } Com_Printf( "NET_SendPacket: %s\n", NET_ErrorString() ); } } //============================================================================= /* ================== Sys_IsLANAddress LAN clients will have their rate var ignored ================== */ qboolean Sys_IsLANAddress( netadr_t adr ) { int i; if( adr.type == NA_LOOPBACK ) { return qtrue; } if( adr.type == NA_IPX ) { return qtrue; } if( adr.type != NA_IP ) { return qfalse; } // choose which comparison to use based on the class of the address being tested // any local adresses of a different class than the address being tested will fail based on the first byte if( adr.ip[0] == 127 && adr.ip[1] == 0 && adr.ip[2] == 0 && adr.ip[3] == 1 ) { return qtrue; } // Class A if( (adr.ip[0] & 0x80) == 0x00 ) { for ( i = 0 ; i < numIP ; i++ ) { if( adr.ip[0] == localIP[i][0] ) { return qtrue; } } // the RFC1918 class a block will pass the above test return qfalse; } // Class B if( (adr.ip[0] & 0xc0) == 0x80 ) { for ( i = 0 ; i < numIP ; i++ ) { if( adr.ip[0] == localIP[i][0] && adr.ip[1] == localIP[i][1] ) { return qtrue; } // also check against the RFC1918 class b blocks if( adr.ip[0] == 172 && localIP[i][0] == 172 && (adr.ip[1] & 0xf0) == 16 && (localIP[i][1] & 0xf0) == 16 ) { return qtrue; } } return qfalse; } // Class C for ( i = 0 ; i < numIP ; i++ ) { if( adr.ip[0] == localIP[i][0] && adr.ip[1] == localIP[i][1] && adr.ip[2] == localIP[i][2] ) { return qtrue; } // also check against the RFC1918 class c blocks if( adr.ip[0] == 192 && localIP[i][0] == 192 && adr.ip[1] == 168 && localIP[i][1] == 168 ) { return qtrue; } } return qfalse; } /* ================== Sys_ShowIP ================== */ void Sys_ShowIP(void) { int i; for (i = 0; i < numIP; i++) { Com_Printf( "IP: %i.%i.%i.%i\n", localIP[i][0], localIP[i][1], localIP[i][2], localIP[i][3] ); } } //============================================================================= /* ==================== NET_IPSocket ==================== */ int NET_IPSocket( char *net_interface, int port ) { SOCKET newsocket; struct sockaddr_in address; u_long _true = qtrue; int i = 1; int err; if( net_interface ) { Com_Printf( "Opening IP socket: %s:%i\n", net_interface, port ); } else { Com_Printf( "Opening IP socket: localhost:%i\n", port ); } if( ( newsocket = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ) ) == INVALID_SOCKET ) { err = WSAGetLastError(); if( err != WSAEAFNOSUPPORT ) { Com_Printf( "WARNING: UDP_OpenSocket: socket: %s\n", NET_ErrorString() ); } return 0; } // make it non-blocking if( ioctlsocket( newsocket, FIONBIO, &(u_long&)_true ) == SOCKET_ERROR ) { Com_Printf( "WARNING: UDP_OpenSocket: ioctl FIONBIO: %s\n", NET_ErrorString() ); return 0; } // make it broadcast capable if( setsockopt( newsocket, SOL_SOCKET, SO_BROADCAST, (char *)&i, sizeof(i) ) == SOCKET_ERROR ) { Com_Printf( "WARNING: UDP_OpenSocket: setsockopt SO_BROADCAST: %s\n", NET_ErrorString() ); return 0; } if( !net_interface || !net_interface[0] || !Q_stricmp(net_interface, "localhost") ) { address.sin_addr.s_addr = INADDR_ANY; } else { Sys_StringToSockaddr( net_interface, (struct sockaddr *)&address ); } if( port == PORT_ANY ) { address.sin_port = 0; } else { address.sin_port = htons( (short)port ); } address.sin_family = AF_INET; if( bind( newsocket, (const sockaddr*) (void *)&address, sizeof(address) ) == SOCKET_ERROR ) { Com_Printf( "WARNING: UDP_OpenSocket: bind: %s\n", NET_ErrorString() ); closesocket( newsocket ); return 0; } return newsocket; } /* ==================== NET_OpenSocks ==================== */ void NET_OpenSocks( int port ) { struct sockaddr_in address; int err; struct hostent *h; int len; qboolean rfc1929; unsigned char buf[64]; usingSocks = qfalse; Com_Printf( "Opening connection to SOCKS server.\n" ); if ( ( socks_socket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ) ) == INVALID_SOCKET ) { err = WSAGetLastError(); Com_Printf( "WARNING: NET_OpenSocks: socket: %s\n", NET_ErrorString() ); return; } h = gethostbyname( net_socksServer->string ); if ( h == NULL ) { err = WSAGetLastError(); Com_Printf( "WARNING: NET_OpenSocks: gethostbyname: %s\n", NET_ErrorString() ); return; } if ( h->h_addrtype != AF_INET ) { Com_Printf( "WARNING: NET_OpenSocks: gethostbyname: address type was not AF_INET\n" ); return; } address.sin_family = AF_INET; address.sin_addr.s_addr = *(int *)h->h_addr_list[0]; address.sin_port = htons( (short)net_socksPort->integer ); if ( connect( socks_socket, (struct sockaddr *)&address, sizeof( address ) ) == SOCKET_ERROR ) { err = WSAGetLastError(); Com_Printf( "NET_OpenSocks: connect: %s\n", NET_ErrorString() ); return; } // send socks authentication handshake if ( *net_socksUsername->string || *net_socksPassword->string ) { rfc1929 = qtrue; } else { rfc1929 = qfalse; } buf[0] = 5; // SOCKS version // method count if ( rfc1929 ) { buf[1] = 2; len = 4; } else { buf[1] = 1; len = 3; } buf[2] = 0; // method #1 - method id #00: no authentication if ( rfc1929 ) { buf[2] = 2; // method #2 - method id #02: username/password } if ( send( socks_socket, (const char*) buf, len, 0 ) == SOCKET_ERROR ) { err = WSAGetLastError(); Com_Printf( "NET_OpenSocks: send: %s\n", NET_ErrorString() ); return; } // get the response len = recv( socks_socket, (char*) buf, 64, 0 ); if ( len == SOCKET_ERROR ) { err = WSAGetLastError(); Com_Printf( "NET_OpenSocks: recv: %s\n", NET_ErrorString() ); return; } if ( len != 2 || buf[0] != 5 ) { Com_Printf( "NET_OpenSocks: bad response\n" ); return; } switch( buf[1] ) { case 0: // no authentication break; case 2: // username/password authentication break; default: Com_Printf( "NET_OpenSocks: request denied\n" ); return; } // do username/password authentication if needed if ( buf[1] == 2 ) { int ulen; int plen; // build the request ulen = (int)strlen( net_socksUsername->string ); plen = (int)strlen( net_socksPassword->string ); buf[0] = 1; // username/password authentication version buf[1] = ulen; if ( ulen ) { memcpy( &buf[2], net_socksUsername->string, ulen ); } buf[2 + ulen] = plen; if ( plen ) { memcpy( &buf[3 + ulen], net_socksPassword->string, plen ); } // send it if ( send( socks_socket, (const char*) buf, 3 + ulen + plen, 0 ) == SOCKET_ERROR ) { err = WSAGetLastError(); Com_Printf( "NET_OpenSocks: send: %s\n", NET_ErrorString() ); return; } // get the response len = recv( socks_socket, (char*) buf, 64, 0 ); if ( len == SOCKET_ERROR ) { err = WSAGetLastError(); Com_Printf( "NET_OpenSocks: recv: %s\n", NET_ErrorString() ); return; } if ( len != 2 || buf[0] != 1 ) { Com_Printf( "NET_OpenSocks: bad response\n" ); return; } if ( buf[1] != 0 ) { Com_Printf( "NET_OpenSocks: authentication failed\n" ); return; } } // send the UDP associate request buf[0] = 5; // SOCKS version buf[1] = 3; // command: UDP associate buf[2] = 0; // reserved buf[3] = 1; // address type: IPV4 *(int *)&buf[4] = INADDR_ANY; *(short *)&buf[8] = htons( (short)port ); // port if ( send( socks_socket, (const char*) buf, 10, 0 ) == SOCKET_ERROR ) { err = WSAGetLastError(); Com_Printf( "NET_OpenSocks: send: %s\n", NET_ErrorString() ); return; } // get the response len = recv( socks_socket, (char*) buf, 64, 0 ); if( len == SOCKET_ERROR ) { err = WSAGetLastError(); Com_Printf( "NET_OpenSocks: recv: %s\n", NET_ErrorString() ); return; } if( len < 2 || buf[0] != 5 ) { Com_Printf( "NET_OpenSocks: bad response\n" ); return; } // check completion code if( buf[1] != 0 ) { Com_Printf( "NET_OpenSocks: request denied: %i\n", buf[1] ); return; } if( buf[3] != 1 ) { Com_Printf( "NET_OpenSocks: relay address is not IPV4: %i\n", buf[3] ); return; } ((struct sockaddr_in *)&socksRelayAddr)->sin_family = AF_INET; ((struct sockaddr_in *)&socksRelayAddr)->sin_addr.s_addr = *(int *)&buf[4]; ((struct sockaddr_in *)&socksRelayAddr)->sin_port = *(short *)&buf[8]; memset( ((struct sockaddr_in *)&socksRelayAddr)->sin_zero, 0, 8 ); usingSocks = qtrue; } /* ===================== NET_GetLocalAddress ===================== */ void NET_GetLocalAddress( void ) { char hostname[256]; struct hostent *hostInfo; int error; char *p; int ip; int n; if( gethostname( hostname, 256 ) == SOCKET_ERROR ) { error = WSAGetLastError(); return; } hostInfo = gethostbyname( hostname ); if( !hostInfo ) { error = WSAGetLastError(); return; } Com_Printf( "Hostname: %s\n", hostInfo->h_name ); n = 0; while( ( p = hostInfo->h_aliases[n++] ) != NULL ) { Com_Printf( "Alias: %s\n", p ); } if ( hostInfo->h_addrtype != AF_INET ) { return; } numIP = 0; while( ( p = hostInfo->h_addr_list[numIP] ) != NULL && numIP < MAX_IPS ) { ip = ntohl( *(int *)p ); localIP[ numIP ][0] = p[0]; localIP[ numIP ][1] = p[1]; localIP[ numIP ][2] = p[2]; localIP[ numIP ][3] = p[3]; Com_Printf( "IP: %i.%i.%i.%i\n", ( ip >> 24 ) & 0xff, ( ip >> 16 ) & 0xff, ( ip >> 8 ) & 0xff, ip & 0xff ); numIP++; } } /* ==================== NET_OpenIP ==================== */ void NET_OpenIP( void ) { cvar_t *ip; int port; int i; ip = Cvar_Get( "net_ip", "localhost", CVAR_LATCH ); port = Cvar_Get( "net_port", va( "%i", PORT_SERVER ), CVAR_LATCH )->integer; // automatically scan for a valid port, so multiple // dedicated servers can be started without requiring // a different net_port for each one for( i = 0 ; i < 10 ; i++ ) { ip_socket = NET_IPSocket( ip->string, port + i ); if ( ip_socket ) { Cvar_SetValue( "net_port", port + i ); if ( net_socksEnabled->integer ) { NET_OpenSocks( port + i ); } NET_GetLocalAddress(); return; } } Com_Printf( "WARNING: Couldn't allocate IP port\n"); } /* ==================== NET_IPXSocket ==================== */ int NET_IPXSocket( int port ) { SOCKET newsocket; struct sockaddr_ipx address; int _true = 1; int err; if( ( newsocket = socket( AF_IPX, SOCK_DGRAM, NSPROTO_IPX ) ) == INVALID_SOCKET ) { err = WSAGetLastError(); if (err != WSAEAFNOSUPPORT) { Com_Printf( "WARNING: IPX_Socket: socket: %s\n", NET_ErrorString() ); } return 0; } // make it non-blocking if( ioctlsocket( newsocket, FIONBIO, &(u_long&)_true ) == SOCKET_ERROR ) { Com_Printf( "WARNING: IPX_Socket: ioctl FIONBIO: %s\n", NET_ErrorString() ); return 0; } // make it broadcast capable if( setsockopt( newsocket, SOL_SOCKET, SO_BROADCAST, (char *)&_true, sizeof( _true ) ) == SOCKET_ERROR ) { Com_Printf( "WARNING: IPX_Socket: setsockopt SO_BROADCAST: %s\n", NET_ErrorString() ); return 0; } address.sa_family = AF_IPX; memset( address.sa_netnum, 0, 4 ); memset( address.sa_nodenum, 0, 6 ); if( port == PORT_ANY ) { address.sa_socket = 0; } else { address.sa_socket = htons( (short)port ); } if( bind( newsocket, (const sockaddr*) (void *)&address, sizeof(address) ) == SOCKET_ERROR ) { Com_Printf( "WARNING: IPX_Socket: bind: %s\n", NET_ErrorString() ); closesocket( newsocket ); return 0; } return newsocket; } /* ==================== NET_OpenIPX ==================== */ void NET_OpenIPX( void ) { int port; port = Cvar_Get( "net_port", va( "%i", PORT_SERVER ), CVAR_LATCH )->integer; ipx_socket = NET_IPXSocket( port ); } //=================================================================== /* ==================== NET_GetCvars ==================== */ static qboolean NET_GetCvars( void ) { qboolean modified; modified = qfalse; if( net_noudp && net_noudp->modified ) { modified = qtrue; } net_noudp = Cvar_Get( "net_noudp", "0", CVAR_LATCH | CVAR_ARCHIVE ); if( net_noipx && net_noipx->modified ) { modified = qtrue; } net_noipx = Cvar_Get( "net_noipx", "0", CVAR_LATCH | CVAR_ARCHIVE ); if( net_socksEnabled && net_socksEnabled->modified ) { modified = qtrue; } net_socksEnabled = Cvar_Get( "net_socksEnabled", "0", CVAR_LATCH | CVAR_ARCHIVE ); if( net_socksServer && net_socksServer->modified ) { modified = qtrue; } net_socksServer = Cvar_Get( "net_socksServer", "", CVAR_LATCH | CVAR_ARCHIVE ); if( net_socksPort && net_socksPort->modified ) { modified = qtrue; } net_socksPort = Cvar_Get( "net_socksPort", "1080", CVAR_LATCH | CVAR_ARCHIVE ); if( net_socksUsername && net_socksUsername->modified ) { modified = qtrue; } net_socksUsername = Cvar_Get( "net_socksUsername", "", CVAR_LATCH | CVAR_ARCHIVE ); if( net_socksPassword && net_socksPassword->modified ) { modified = qtrue; } net_socksPassword = Cvar_Get( "net_socksPassword", "", CVAR_LATCH | CVAR_ARCHIVE ); return modified; } /* ==================== NET_Config ==================== */ void NET_Config( qboolean enableNetworking ) { qboolean modified; qboolean stop; qboolean start; // get any latched changes to cvars modified = NET_GetCvars(); if( net_noudp->integer && net_noipx->integer ) { enableNetworking = qfalse; } // if enable state is the same and no cvars were modified, we have nothing to do if( enableNetworking == networkingEnabled && !modified ) { return; } if( enableNetworking == networkingEnabled ) { if( enableNetworking ) { stop = qtrue; start = qtrue; } else { stop = qfalse; start = qfalse; } } else { if( enableNetworking ) { stop = qfalse; start = qtrue; } else { stop = qtrue; start = qfalse; } networkingEnabled = enableNetworking; } if( stop ) { if ( ip_socket && ip_socket != INVALID_SOCKET ) { closesocket( ip_socket ); ip_socket = 0; } if ( socks_socket && socks_socket != INVALID_SOCKET ) { closesocket( socks_socket ); socks_socket = 0; } if ( ipx_socket && ipx_socket != INVALID_SOCKET ) { closesocket( ipx_socket ); ipx_socket = 0; } } if( start ) { if (! net_noudp->integer ) { NET_OpenIP(); } if (! net_noipx->integer ) { NET_OpenIPX(); } } } /* ==================== NET_Init ==================== */ void NET_Init( void ) { int r; r = WSAStartup( MAKEWORD( 1, 1 ), &winsockdata ); if( r ) { Com_Printf( "WARNING: Winsock initialization failed, returned %d\n", r ); return; } winsockInitialized = qtrue; Com_Printf( "Winsock Initialized\n" ); // this is really just to get the cvars registered NET_GetCvars(); //FIXME testing! NET_Config( qtrue ); } /* ==================== NET_Shutdown ==================== */ void NET_Shutdown( void ) { if ( !winsockInitialized ) { return; } NET_Config( qfalse ); WSACleanup(); winsockInitialized = qfalse; } /* ==================== NET_Sleep sleeps msec or until net socket is ready ==================== */ void NET_Sleep( int msec ) { } /* ==================== NET_Restart_f ==================== */ void NET_Restart( void ) { NET_Config( networkingEnabled ); } ================================================ FILE: src/engine/platform/win_qgl.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /* ** QGL_WIN.C ** ** This file implements the operating system binding of GL to QGL function ** pointers. When doing a port of Quake3 you must implement the following ** two functions: ** ** QGL_Init() - loads libraries, assigns function pointers, etc. ** QGL_Shutdown() - unloads libraries, NULLs function pointers */ #include "../renderer/tr_local.h" extern FILE* log_fp; static HINSTANCE hinstOpenGL; // HINSTANCE for the OpenGL library void QGL_EnableLogging( qboolean enable ); HGLRC ( WINAPI * qwglCreateContext)(HDC); BOOL ( WINAPI * qwglDeleteContext)(HGLRC); PROC ( WINAPI * qwglGetProcAddress)(LPCSTR); BOOL ( WINAPI * qwglMakeCurrent)(HDC, HGLRC); int ( WINAPI * qwglSwapIntervalEXT)( int interval ); void ( APIENTRY * qglAlphaFunc )(GLenum func, GLclampf ref); void ( APIENTRY * qglBegin )(GLenum mode); void ( APIENTRY * qglBindTexture )(GLenum target, GLuint texture); void ( APIENTRY * qglBlendFunc )(GLenum sfactor, GLenum dfactor); void ( APIENTRY * qglClear )(GLbitfield mask); void ( APIENTRY * qglClearColor )(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); void ( APIENTRY * qglClipPlane )(GLenum plane, const GLdouble *equation); void ( APIENTRY * qglColor3f )(GLfloat red, GLfloat green, GLfloat blue); void ( APIENTRY * qglColorMask )(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); void ( APIENTRY * qglColorPointer )(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); void ( APIENTRY * qglCullFace )(GLenum mode); void ( APIENTRY * qglDeleteTextures )(GLsizei n, const GLuint *textures); void ( APIENTRY * qglDepthFunc )(GLenum func); void ( APIENTRY * qglDepthMask )(GLboolean flag); void ( APIENTRY * qglDepthRange )(GLclampd zNear, GLclampd zFar); void ( APIENTRY * qglDisable )(GLenum cap); void ( APIENTRY * qglDisableClientState )(GLenum array); void ( APIENTRY * qglDrawBuffer )(GLenum mode); void ( APIENTRY * qglDrawElements )(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices); void ( APIENTRY * qglEnable )(GLenum cap); void ( APIENTRY * qglEnableClientState )(GLenum array); void ( APIENTRY * qglEnd )(void); void ( APIENTRY * qglFinish )(void); GLenum ( APIENTRY * qglGetError )(void); void ( APIENTRY * qglGetIntegerv )(GLenum pname, GLint *params); const GLubyte * ( APIENTRY * qglGetString )(GLenum name); void ( APIENTRY * qglLineWidth )(GLfloat width); void ( APIENTRY * qglLoadIdentity )(void); void ( APIENTRY * qglLoadMatrixf )(const GLfloat *m); void ( APIENTRY * qglMatrixMode )(GLenum mode); void ( APIENTRY * qglOrtho )(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); void ( APIENTRY * qglPolygonMode )(GLenum face, GLenum mode); void ( APIENTRY * qglPolygonOffset )(GLfloat factor, GLfloat units); void ( APIENTRY * qglPopMatrix )(void); void ( APIENTRY * qglPushMatrix )(void); void ( APIENTRY * qglReadPixels )(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels); void ( APIENTRY * qglScissor )(GLint x, GLint y, GLsizei width, GLsizei height); void ( APIENTRY * qglStencilFunc )(GLenum func, GLint ref, GLuint mask); void ( APIENTRY * qglStencilOp )(GLenum fail, GLenum zfail, GLenum zpass); void ( APIENTRY * qglTexCoord2f )(GLfloat s, GLfloat t); void ( APIENTRY * qglTexCoord2fv )(const GLfloat *v); void ( APIENTRY * qglTexCoordPointer )(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); void ( APIENTRY * qglTexEnvf )(GLenum target, GLenum pname, GLfloat param); void ( APIENTRY * qglTexImage2D )(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels); void ( APIENTRY * qglTexParameterf )(GLenum target, GLenum pname, GLfloat param); void ( APIENTRY * qglTexParameterfv )(GLenum target, GLenum pname, const GLfloat *params); void ( APIENTRY * qglTexSubImage2D )(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); void ( APIENTRY * qglVertex2f )(GLfloat x, GLfloat y); void ( APIENTRY * qglVertex3f )(GLfloat x, GLfloat y, GLfloat z); void ( APIENTRY * qglVertex3fv )(const GLfloat *v); void ( APIENTRY * qglVertexPointer )(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); void ( APIENTRY * qglViewport )(GLint x, GLint y, GLsizei width, GLsizei height); static void ( APIENTRY * dllAlphaFunc )(GLenum func, GLclampf ref); static void ( APIENTRY * dllBegin )(GLenum mode); static void ( APIENTRY * dllBindTexture )(GLenum target, GLuint texture); static void ( APIENTRY * dllBlendFunc )(GLenum sfactor, GLenum dfactor); static void ( APIENTRY * dllClear )(GLbitfield mask); static void ( APIENTRY * dllClearColor )(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); static void ( APIENTRY * dllClipPlane )(GLenum plane, const GLdouble *equation); static void ( APIENTRY * dllColor3f )(GLfloat red, GLfloat green, GLfloat blue); static void ( APIENTRY * dllColorMask )(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); static void ( APIENTRY * dllColorPointer )(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); static void ( APIENTRY * dllCullFace )(GLenum mode); static void ( APIENTRY * dllDeleteTextures )(GLsizei n, const GLuint *textures); static void ( APIENTRY * dllDepthFunc )(GLenum func); static void ( APIENTRY * dllDepthMask )(GLboolean flag); static void ( APIENTRY * dllDepthRange )(GLclampd zNear, GLclampd zFar); static void ( APIENTRY * dllDisable )(GLenum cap); static void ( APIENTRY * dllDisableClientState )(GLenum array); static void ( APIENTRY * dllDrawBuffer )(GLenum mode); static void ( APIENTRY * dllDrawElements )(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices); static void ( APIENTRY * dllEnable )(GLenum cap); static void ( APIENTRY * dllEnableClientState )(GLenum array); static void ( APIENTRY * dllEnd )(void); static void ( APIENTRY * dllFinish )(void); GLenum ( APIENTRY * dllGetError )(void); static void ( APIENTRY * dllGetIntegerv )(GLenum pname, GLint *params); const GLubyte * ( APIENTRY * dllGetString )(GLenum name); static void ( APIENTRY * dllLineWidth )(GLfloat width); static void ( APIENTRY * dllLoadIdentity )(void); static void ( APIENTRY * dllLoadMatrixf )(const GLfloat *m); static void ( APIENTRY * dllMatrixMode )(GLenum mode); static void ( APIENTRY * dllOrtho )(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); static void ( APIENTRY * dllPolygonMode )(GLenum face, GLenum mode); static void ( APIENTRY * dllPolygonOffset )(GLfloat factor, GLfloat units); static void ( APIENTRY * dllPopMatrix )(void); static void ( APIENTRY * dllPushMatrix )(void); static void ( APIENTRY * dllReadPixels )(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels); static void ( APIENTRY * dllScissor )(GLint x, GLint y, GLsizei width, GLsizei height); static void ( APIENTRY * dllStencilFunc )(GLenum func, GLint ref, GLuint mask); static void ( APIENTRY * dllStencilOp )(GLenum fail, GLenum zfail, GLenum zpass); static void ( APIENTRY * dllTexCoord2f )(GLfloat s, GLfloat t); static void ( APIENTRY * dllTexCoord2fv )(const GLfloat *v); static void ( APIENTRY * dllTexCoordPointer )(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); static void ( APIENTRY * dllTexEnvf )(GLenum target, GLenum pname, GLfloat param); static void ( APIENTRY * dllTexImage2D )(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels); static void ( APIENTRY * dllTexParameterf )(GLenum target, GLenum pname, GLfloat param); static void ( APIENTRY * dllTexParameterfv )(GLenum target, GLenum pname, const GLfloat *params); static void ( APIENTRY * dllTexSubImage2D )(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); static void ( APIENTRY * dllVertex2f )(GLfloat x, GLfloat y); static void ( APIENTRY * dllVertex3f )(GLfloat x, GLfloat y, GLfloat z); static void ( APIENTRY * dllVertex3fv )(const GLfloat *v); static void ( APIENTRY * dllVertexPointer )(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); static void ( APIENTRY * dllViewport )(GLint x, GLint y, GLsizei width, GLsizei height); // // Placeholder functions to replace OpenGL calls when Vulkan renderer is active. // static HGLRC nowglCreateContext(HDC) { return NULL;} static BOOL nowglDeleteContext(HGLRC) { return FALSE; } static PROC nowglGetProcAddress(LPCSTR) { return NULL; } static BOOL nowglMakeCurrent(HDC, HGLRC) { return FALSE; } static int nowglSwapIntervalEXT( int interval ) { return -1; } static void noglActiveTextureARB ( GLenum texture ) {} static void noglClientActiveTextureARB ( GLenum texture ) {} static void noglLockArraysEXT (GLint, GLint) {} static void noglUnlockArraysEXT (void) {} static void noglAlphaFunc(GLenum func, GLclampf ref) {} static void noglBegin(GLenum mode) {} static void noglBindTexture(GLenum target, GLuint texture) {} static void noglBlendFunc(GLenum sfactor, GLenum dfactor) {} static void noglClear(GLbitfield mask) {} static void noglClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) {} static void noglClipPlane(GLenum plane, const GLdouble *equation) {} static void noglColor3f(GLfloat red, GLfloat green, GLfloat blue) {} static void noglColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) {} static void noglColorPointer(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer) {} static void noglCullFace(GLenum mode) {} static void noglDeleteTextures(GLsizei n, const GLuint *textures) {} static void noglDepthFunc(GLenum func) {} static void noglDepthMask(GLboolean flag) {} static void noglDepthRange(GLclampd zNear, GLclampd zFar) {} static void noglDisable(GLenum cap) {} static void noglDisableClientState(GLenum array) {} static void noglDrawBuffer(GLenum mode) {} static void noglDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices) {} static void noglEnable(GLenum cap) {} static void noglEnableClientState(GLenum array) {} static void noglEnd(void) {} static void noglFinish(void) {} static GLenum noglGetError(void) { return GL_NO_ERROR; } static void noglGetIntegerv(GLenum pname, GLint *params) {} static const GLubyte* noglGetString(GLenum name) { static char* s = ""; return (GLubyte*)s;} static void noglLineWidth(GLfloat width) {} static void noglLoadIdentity(void) {} static void noglLoadMatrixf(const GLfloat *m) {} static void noglMatrixMode(GLenum mode) {} static void noglOrtho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar) {} static void noglPolygonMode(GLenum face, GLenum mode) {} static void noglPolygonOffset(GLfloat factor, GLfloat units) {} static void noglPopMatrix(void) {} static void noglPushMatrix(void) {} static void noglReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels) {} static void noglScissor(GLint x, GLint y, GLsizei width, GLsizei height) {} static void noglStencilFunc(GLenum func, GLint ref, GLuint mask) {} static void noglStencilOp(GLenum fail, GLenum zfail, GLenum zpass) {} static void noglTexCoord2f(GLfloat s, GLfloat t) {} static void noglTexCoord2fv(const GLfloat *v) {} static void noglTexCoordPointer(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer) {} static void noglTexEnvf(GLenum target, GLenum pname, GLfloat param) {} static void noglTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels) {} static void noglTexParameterf(GLenum target, GLenum pname, GLfloat param) {} static void noglTexParameterfv(GLenum target, GLenum pname, const GLfloat *params) {} static void noglTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels) {} static void noglVertex2f(GLfloat x, GLfloat y) {} static void noglVertex3f(GLfloat x, GLfloat y, GLfloat z) {} static void noglVertex3fv(const GLfloat *v) {} static void noglVertexPointer(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer) {} static void noglViewport(GLint x, GLint y, GLsizei width, GLsizei height) {} static const char * BooleanToString( GLboolean b ) { if ( b == GL_FALSE ) return "GL_FALSE"; else if ( b == GL_TRUE ) return "GL_TRUE"; else return "OUT OF RANGE FOR BOOLEAN"; } static const char * FuncToString( GLenum f ) { switch ( f ) { case GL_ALWAYS: return "GL_ALWAYS"; case GL_NEVER: return "GL_NEVER"; case GL_LEQUAL: return "GL_LEQUAL"; case GL_LESS: return "GL_LESS"; case GL_EQUAL: return "GL_EQUAL"; case GL_GREATER: return "GL_GREATER"; case GL_GEQUAL: return "GL_GEQUAL"; case GL_NOTEQUAL: return "GL_NOTEQUAL"; default: return "!!! UNKNOWN !!!"; } } static const char * PrimToString( GLenum mode ) { static char prim[1024]; if ( mode == GL_TRIANGLES ) strcpy( prim, "GL_TRIANGLES" ); else if ( mode == GL_TRIANGLE_STRIP ) strcpy( prim, "GL_TRIANGLE_STRIP" ); else if ( mode == GL_TRIANGLE_FAN ) strcpy( prim, "GL_TRIANGLE_FAN" ); else if ( mode == GL_QUADS ) strcpy( prim, "GL_QUADS" ); else if ( mode == GL_QUAD_STRIP ) strcpy( prim, "GL_QUAD_STRIP" ); else if ( mode == GL_POLYGON ) strcpy( prim, "GL_POLYGON" ); else if ( mode == GL_POINTS ) strcpy( prim, "GL_POINTS" ); else if ( mode == GL_LINES ) strcpy( prim, "GL_LINES" ); else if ( mode == GL_LINE_STRIP ) strcpy( prim, "GL_LINE_STRIP" ); else if ( mode == GL_LINE_LOOP ) strcpy( prim, "GL_LINE_LOOP" ); else sprintf( prim, "0x%x", mode ); return prim; } static const char * CapToString( GLenum cap ) { static char buffer[1024]; switch ( cap ) { case GL_TEXTURE_2D: return "GL_TEXTURE_2D"; case GL_BLEND: return "GL_BLEND"; case GL_DEPTH_TEST: return "GL_DEPTH_TEST"; case GL_CULL_FACE: return "GL_CULL_FACE"; case GL_CLIP_PLANE0: return "GL_CLIP_PLANE0"; case GL_COLOR_ARRAY: return "GL_COLOR_ARRAY"; case GL_TEXTURE_COORD_ARRAY: return "GL_TEXTURE_COORD_ARRAY"; case GL_VERTEX_ARRAY: return "GL_VERTEX_ARRAY"; case GL_ALPHA_TEST: return "GL_ALPHA_TEST"; case GL_STENCIL_TEST: return "GL_STENCIL_TEST"; default: sprintf( buffer, "0x%x", cap ); } return buffer; } static const char * TypeToString( GLenum t ) { switch ( t ) { case GL_BYTE: return "GL_BYTE"; case GL_UNSIGNED_BYTE: return "GL_UNSIGNED_BYTE"; case GL_SHORT: return "GL_SHORT"; case GL_UNSIGNED_SHORT: return "GL_UNSIGNED_SHORT"; case GL_INT: return "GL_INT"; case GL_UNSIGNED_INT: return "GL_UNSIGNED_INT"; case GL_FLOAT: return "GL_FLOAT"; case GL_DOUBLE: return "GL_DOUBLE"; default: return "!!! UNKNOWN !!!"; } } static void APIENTRY logAlphaFunc(GLenum func, GLclampf ref) { fprintf( log_fp, "glAlphaFunc( 0x%x, %f )\n", func, ref ); dllAlphaFunc( func, ref ); } static void APIENTRY logBegin(GLenum mode) { fprintf( log_fp, "glBegin( %s )\n", PrimToString( mode )); dllBegin( mode ); } static void APIENTRY logBindTexture(GLenum target, GLuint texture) { fprintf( log_fp, "glBindTexture( 0x%x, %u )\n", target, texture ); dllBindTexture( target, texture ); } static void BlendToName( char *n, GLenum f ) { switch ( f ) { case GL_ONE: strcpy( n, "GL_ONE" ); break; case GL_ZERO: strcpy( n, "GL_ZERO" ); break; case GL_SRC_ALPHA: strcpy( n, "GL_SRC_ALPHA" ); break; case GL_ONE_MINUS_SRC_ALPHA: strcpy( n, "GL_ONE_MINUS_SRC_ALPHA" ); break; case GL_DST_COLOR: strcpy( n, "GL_DST_COLOR" ); break; case GL_ONE_MINUS_DST_COLOR: strcpy( n, "GL_ONE_MINUS_DST_COLOR" ); break; case GL_DST_ALPHA: strcpy( n, "GL_DST_ALPHA" ); break; default: sprintf( n, "0x%x", f ); } } static void APIENTRY logBlendFunc(GLenum sfactor, GLenum dfactor) { char sf[128], df[128]; BlendToName( sf, sfactor ); BlendToName( df, dfactor ); fprintf( log_fp, "glBlendFunc( %s, %s )\n", sf, df ); dllBlendFunc( sfactor, dfactor ); } static void APIENTRY logClear(GLbitfield mask) { fprintf( log_fp, "glClear( 0x%x = ", mask ); if ( mask & GL_COLOR_BUFFER_BIT ) fprintf( log_fp, "GL_COLOR_BUFFER_BIT " ); if ( mask & GL_DEPTH_BUFFER_BIT ) fprintf( log_fp, "GL_DEPTH_BUFFER_BIT " ); if ( mask & GL_STENCIL_BUFFER_BIT ) fprintf( log_fp, "GL_STENCIL_BUFFER_BIT " ); if ( mask & GL_ACCUM_BUFFER_BIT ) fprintf( log_fp, "GL_ACCUM_BUFFER_BIT " ); fprintf( log_fp, ")\n" ); dllClear( mask ); } static void APIENTRY logClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) { fprintf( log_fp, "glClearColor\n" ); dllClearColor( red, green, blue, alpha ); } static void APIENTRY logClipPlane(GLenum plane, const GLdouble *equation) { fprintf( log_fp, "glClipPlane\n" ); dllClipPlane( plane, equation ); } static void APIENTRY logColor3f(GLfloat red, GLfloat green, GLfloat blue) { fprintf( log_fp, "glColor3f\n" ); dllColor3f( red, green, blue ); } #define SIG( x ) fprintf( log_fp, x "\n" ) static void APIENTRY logColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) { SIG( "glColorMask" ); dllColorMask( red, green, blue, alpha ); } static void APIENTRY logColorPointer(GLint size, GLenum type, GLsizei stride, const void *pointer) { fprintf( log_fp, "glColorPointer( %d, %s, %d, MEM )\n", size, TypeToString( type ), stride ); dllColorPointer( size, type, stride, pointer ); } static void APIENTRY logCullFace(GLenum mode) { fprintf( log_fp, "glCullFace( %s )\n", ( mode == GL_FRONT ) ? "GL_FRONT" : "GL_BACK" ); dllCullFace( mode ); } static void APIENTRY logDeleteTextures(GLsizei n, const GLuint *textures) { SIG( "glDeleteTextures" ); dllDeleteTextures( n, textures ); } static void APIENTRY logDepthFunc(GLenum func) { fprintf( log_fp, "glDepthFunc( %s )\n", FuncToString( func ) ); dllDepthFunc( func ); } static void APIENTRY logDepthMask(GLboolean flag) { fprintf( log_fp, "glDepthMask( %s )\n", BooleanToString( flag ) ); dllDepthMask( flag ); } static void APIENTRY logDepthRange(GLclampd zNear, GLclampd zFar) { fprintf( log_fp, "glDepthRange( %f, %f )\n", ( float ) zNear, ( float ) zFar ); dllDepthRange( zNear, zFar ); } static void APIENTRY logDisable(GLenum cap) { fprintf( log_fp, "glDisable( %s )\n", CapToString( cap ) ); dllDisable( cap ); } static void APIENTRY logDisableClientState(GLenum array) { fprintf( log_fp, "glDisableClientState( %s )\n", CapToString( array ) ); dllDisableClientState( array ); } static void APIENTRY logDrawBuffer(GLenum mode) { SIG( "glDrawBuffer" ); dllDrawBuffer( mode ); } static void APIENTRY logDrawElements(GLenum mode, GLsizei count, GLenum type, const void *indices) { fprintf( log_fp, "glDrawElements( %s, %d, %s, MEM )\n", PrimToString( mode ), count, TypeToString( type ) ); dllDrawElements( mode, count, type, indices ); } static void APIENTRY logEnable(GLenum cap) { fprintf( log_fp, "glEnable( %s )\n", CapToString( cap ) ); dllEnable( cap ); } static void APIENTRY logEnableClientState(GLenum array) { fprintf( log_fp, "glEnableClientState( %s )\n", CapToString( array ) ); dllEnableClientState( array ); } static void APIENTRY logEnd(void) { SIG( "glEnd" ); dllEnd(); } static void APIENTRY logFinish(void) { SIG( "glFinish" ); dllFinish(); } static GLenum APIENTRY logGetError(void) { SIG( "glGetError" ); return dllGetError(); } static void APIENTRY logGetIntegerv(GLenum pname, GLint *params) { SIG( "glGetIntegerv" ); dllGetIntegerv( pname, params ); } static const GLubyte * APIENTRY logGetString(GLenum name) { SIG( "glGetString" ); return dllGetString( name ); } static void APIENTRY logLineWidth(GLfloat width) { SIG( "glLineWidth" ); dllLineWidth( width ); } static void APIENTRY logLoadIdentity(void) { SIG( "glLoadIdentity" ); dllLoadIdentity(); } static void APIENTRY logLoadMatrixf(const GLfloat *m) { SIG( "glLoadMatrixf" ); dllLoadMatrixf( m ); } static void APIENTRY logMatrixMode(GLenum mode) { SIG( "glMatrixMode" ); dllMatrixMode( mode ); } static void APIENTRY logOrtho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar) { SIG( "glOrtho" ); dllOrtho( left, right, bottom, top, zNear, zFar ); } static void APIENTRY logPolygonMode(GLenum face, GLenum mode) { fprintf( log_fp, "glPolygonMode( 0x%x, 0x%x )\n", face, mode ); dllPolygonMode( face, mode ); } static void APIENTRY logPolygonOffset(GLfloat factor, GLfloat units) { SIG( "glPolygonOffset" ); dllPolygonOffset( factor, units ); } static void APIENTRY logPopMatrix(void) { SIG( "glPopMatrix" ); dllPopMatrix(); } static void APIENTRY logPushMatrix(void) { SIG( "glPushMatrix" ); dllPushMatrix(); } static void APIENTRY logReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels) { SIG( "glReadPixels" ); dllReadPixels( x, y, width, height, format, type, pixels ); } static void APIENTRY logScissor(GLint x, GLint y, GLsizei width, GLsizei height) { fprintf( log_fp, "glScissor( %d, %d, %d, %d )\n", x, y, width, height ); dllScissor( x, y, width, height ); } static void APIENTRY logStencilFunc(GLenum func, GLint ref, GLuint mask) { SIG( "glStencilFunc" ); dllStencilFunc( func, ref, mask ); } static void APIENTRY logStencilOp(GLenum fail, GLenum zfail, GLenum zpass) { SIG( "glStencilOp" ); dllStencilOp( fail, zfail, zpass ); } static void APIENTRY logTexCoord2f(GLfloat s, GLfloat t) { SIG( "glTexCoord2f" ); dllTexCoord2f( s, t ); } static void APIENTRY logTexCoord2fv(const GLfloat *v) { SIG( "glTexCoord2fv" ); dllTexCoord2fv( v ); } static void APIENTRY logTexCoordPointer(GLint size, GLenum type, GLsizei stride, const void *pointer) { fprintf( log_fp, "glTexCoordPointer( %d, %s, %d, MEM )\n", size, TypeToString( type ), stride ); dllTexCoordPointer( size, type, stride, pointer ); } static void APIENTRY logTexEnvf(GLenum target, GLenum pname, GLfloat param) { fprintf( log_fp, "glTexEnvf( 0x%x, 0x%x, %f )\n", target, pname, param ); dllTexEnvf( target, pname, param ); } static void APIENTRY logTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels) { SIG( "glTexImage2D" ); dllTexImage2D( target, level, internalformat, width, height, border, format, type, pixels ); } static void APIENTRY logTexParameterf(GLenum target, GLenum pname, GLfloat param) { fprintf( log_fp, "glTexParameterf( 0x%x, 0x%x, %f )\n", target, pname, param ); dllTexParameterf( target, pname, param ); } static void APIENTRY logTexParameterfv(GLenum target, GLenum pname, const GLfloat *params) { SIG( "glTexParameterfv" ); dllTexParameterfv( target, pname, params ); } static void APIENTRY logTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels) { SIG( "glTexSubImage2D" ); dllTexSubImage2D( target, level, xoffset, yoffset, width, height, format, type, pixels ); } static void APIENTRY logVertex2f(GLfloat x, GLfloat y) { SIG( "glVertex2f" ); dllVertex2f( x, y ); } static void APIENTRY logVertex3f(GLfloat x, GLfloat y, GLfloat z) { SIG( "glVertex3f" ); dllVertex3f( x, y, z ); } static void APIENTRY logVertex3fv(const GLfloat *v) { SIG( "glVertex3fv" ); dllVertex3fv( v ); } static void APIENTRY logVertexPointer(GLint size, GLenum type, GLsizei stride, const void *pointer) { fprintf( log_fp, "glVertexPointer( %d, %s, %d, MEM )\n", size, TypeToString( type ), stride ); dllVertexPointer( size, type, stride, pointer ); } static void APIENTRY logViewport(GLint x, GLint y, GLsizei width, GLsizei height) { fprintf( log_fp, "glViewport( %d, %d, %d, %d )\n", x, y, width, height ); dllViewport( x, y, width, height ); } /* ** QGL_Shutdown ** ** Unloads the specified DLL then nulls out all the proc pointers. This ** is only called during a hard shutdown of the OGL subsystem (e.g. vid_restart). */ void QGL_Shutdown( void ) { if ( hinstOpenGL ) { ri.Printf( PRINT_ALL, "...shutting down QGL\n" ); ri.Printf( PRINT_ALL, "...unloading OpenGL DLL\n" ); FreeLibrary( hinstOpenGL ); hinstOpenGL = NULL; } qglAlphaFunc = NULL; qglBegin = NULL; qglBindTexture = NULL; qglBlendFunc = NULL; qglClear = NULL; qglClearColor = NULL; qglClipPlane = NULL; qglColor3f = NULL; qglColorMask = NULL; qglColorPointer = NULL; qglCullFace = NULL; qglDeleteTextures = NULL; qglDepthFunc = NULL; qglDepthMask = NULL; qglDepthRange = NULL; qglDisable = NULL; qglDisableClientState = NULL; qglDrawBuffer = NULL; qglDrawElements = NULL; qglEnable = NULL; qglEnableClientState = NULL; qglEnd = NULL; qglFinish = NULL; qglGetError = NULL; qglGetIntegerv = NULL; qglGetString = NULL; qglLineWidth = NULL; qglLoadIdentity = NULL; qglLoadMatrixf = NULL; qglMatrixMode = NULL; qglOrtho = NULL; qglPolygonMode = NULL; qglPolygonOffset = NULL; qglPopMatrix = NULL; qglPushMatrix = NULL; qglReadPixels = NULL; qglScissor = NULL; qglStencilFunc = NULL; qglStencilOp = NULL; qglTexCoord2f = NULL; qglTexCoord2fv = NULL; qglTexCoordPointer = NULL; qglTexEnvf = NULL; qglTexImage2D = NULL; qglTexParameterf = NULL; qglTexParameterfv = NULL; qglTexSubImage2D = NULL; qglVertex2f = NULL; qglVertex3f = NULL; qglVertex3fv = NULL; qglVertexPointer = NULL; qglViewport = NULL; qwglCreateContext = NULL; qwglDeleteContext = NULL; qwglGetProcAddress = NULL; qwglMakeCurrent = NULL; } # pragma warning (disable : 4113 4133 4047 ) # define GPA( a ) (dllname ? (void*)GetProcAddress(hinstOpenGL, #a) : (void*)(&no ## a)) /* ** QGL_Init ** ** This is responsible for binding our qgl function pointers to ** the appropriate GL stuff. In Windows this means doing a ** LoadLibrary and a bunch of calls to GetProcAddress. On other ** operating systems we need to do the right thing, whatever that ** might be. */ qboolean QGL_Init( const char *dllname ) { if (dllname != nullptr) { assert( hinstOpenGL == 0 ); ri.Printf( PRINT_ALL, "...initializing QGL\n" ); ri.Printf( PRINT_ALL, "...calling LoadLibrary('%s'): ", dllname ); if ( ( hinstOpenGL = LoadLibrary( dllname ) ) == 0 ) { ri.Printf( PRINT_ALL, "failed\n" ); return qfalse; } ri.Printf( PRINT_ALL, "succeeded\n" ); } qglAlphaFunc = dllAlphaFunc = (decltype(dllAlphaFunc))GPA(glAlphaFunc); qglBegin = dllBegin = (decltype(dllBegin))GPA(glBegin); qglBindTexture = dllBindTexture = (decltype(dllBindTexture))GPA(glBindTexture); qglBlendFunc = dllBlendFunc = (decltype(dllBlendFunc))GPA(glBlendFunc); qglClear = dllClear = (decltype(dllClear))GPA(glClear); qglClearColor = dllClearColor = (decltype(dllClearColor))GPA(glClearColor); qglClipPlane = dllClipPlane = (decltype(dllClipPlane))GPA(glClipPlane); qglColor3f = dllColor3f = (decltype(dllColor3f))GPA(glColor3f); qglColorMask = dllColorMask = (decltype(dllColorMask))GPA(glColorMask); qglColorPointer = dllColorPointer = (decltype(dllColorPointer))GPA(glColorPointer); qglCullFace = dllCullFace = (decltype(dllCullFace))GPA(glCullFace); qglDeleteTextures = dllDeleteTextures = (decltype(dllDeleteTextures))GPA(glDeleteTextures); qglDepthFunc = dllDepthFunc = (decltype(dllDepthFunc))GPA(glDepthFunc); qglDepthMask = dllDepthMask = (decltype(dllDepthMask))GPA(glDepthMask); qglDepthRange = dllDepthRange = (decltype(dllDepthRange))GPA(glDepthRange); qglDisable = dllDisable = (decltype(dllDisable))GPA(glDisable); qglDisableClientState = dllDisableClientState = (decltype(dllDisableClientState))GPA(glDisableClientState); qglDrawBuffer = dllDrawBuffer = (decltype(dllDrawBuffer))GPA(glDrawBuffer); qglDrawElements = dllDrawElements = (decltype(dllDrawElements))GPA(glDrawElements); qglEnable = dllEnable = (decltype(dllEnable))GPA(glEnable); qglEnableClientState = dllEnableClientState = (decltype(dllEnableClientState))GPA(glEnableClientState); qglEnd = dllEnd = (decltype(dllEnd))GPA(glEnd); qglFinish = dllFinish = (decltype(dllFinish))GPA(glFinish); qglGetError = dllGetError = ( GLenum (__stdcall * )(void) ) GPA( glGetError ); qglGetIntegerv = dllGetIntegerv = (decltype(dllGetIntegerv))GPA(glGetIntegerv); qglGetString = dllGetString = (decltype(dllGetString))GPA(glGetString); qglLineWidth = dllLineWidth = (decltype(dllLineWidth))GPA(glLineWidth); qglLoadIdentity = dllLoadIdentity = (decltype(dllLoadIdentity))GPA(glLoadIdentity); qglLoadMatrixf = dllLoadMatrixf = (decltype(dllLoadMatrixf))GPA(glLoadMatrixf); qglMatrixMode = dllMatrixMode = (decltype(dllMatrixMode))GPA(glMatrixMode); qglOrtho = dllOrtho = (decltype(dllOrtho))GPA(glOrtho); qglPolygonMode = dllPolygonMode = (decltype(dllPolygonMode))GPA(glPolygonMode); qglPolygonOffset = dllPolygonOffset = (decltype(dllPolygonOffset))GPA(glPolygonOffset); qglPopMatrix = dllPopMatrix = (decltype(dllPopMatrix))GPA(glPopMatrix); qglPushMatrix = dllPushMatrix = (decltype(dllPushMatrix))GPA(glPushMatrix); qglReadPixels = dllReadPixels = (decltype(dllReadPixels))GPA(glReadPixels); qglScissor = dllScissor = (decltype(dllScissor))GPA(glScissor); qglStencilFunc = dllStencilFunc = (decltype(dllStencilFunc))GPA(glStencilFunc); qglStencilOp = dllStencilOp = (decltype(dllStencilOp))GPA(glStencilOp); qglTexCoord2f = dllTexCoord2f = (decltype(dllTexCoord2f))GPA(glTexCoord2f); qglTexCoord2fv = dllTexCoord2fv = (decltype(dllTexCoord2fv))GPA(glTexCoord2fv); qglTexCoordPointer = dllTexCoordPointer = (decltype(dllTexCoordPointer))GPA(glTexCoordPointer); qglTexEnvf = dllTexEnvf = (decltype(dllTexEnvf))GPA(glTexEnvf); qglTexImage2D = dllTexImage2D = (decltype(dllTexImage2D))GPA(glTexImage2D); qglTexParameterf = dllTexParameterf = (decltype(dllTexParameterf))GPA(glTexParameterf); qglTexParameterfv = dllTexParameterfv = (decltype(dllTexParameterfv))GPA(glTexParameterfv); qglTexSubImage2D = dllTexSubImage2D = (decltype(dllTexSubImage2D))GPA(glTexSubImage2D); qglVertex2f = dllVertex2f = (decltype(dllVertex2f))GPA(glVertex2f); qglVertex3f = dllVertex3f = (decltype(dllVertex3f))GPA(glVertex3f); qglVertex3fv = dllVertex3fv = (decltype(dllVertex3fv))GPA(glVertex3fv); qglVertexPointer = dllVertexPointer = (decltype(dllVertexPointer))GPA(glVertexPointer); qglViewport = dllViewport = (decltype(dllViewport))GPA(glViewport); qwglCreateContext = (decltype(qwglCreateContext))GPA(wglCreateContext); qwglDeleteContext = (decltype(qwglDeleteContext))GPA(wglDeleteContext); qwglGetProcAddress = (decltype(qwglGetProcAddress))GPA(wglGetProcAddress); qwglMakeCurrent = (decltype(qwglMakeCurrent))GPA(wglMakeCurrent); qwglSwapIntervalEXT = 0; qglActiveTextureARB = 0; qglClientActiveTextureARB = 0; qglLockArraysEXT = 0; qglUnlockArraysEXT = 0; if (dllname != nullptr) { // check logging QGL_EnableLogging( (qboolean) r_logFile->integer ); } return qtrue; } void QGL_EnableLogging( qboolean enable ) { static qboolean isEnabled; // return if we're already active if ( isEnabled && enable ) { // decrement log counter and stop if it has reached 0 ri.Cvar_Set( "r_logFile", va("%d", r_logFile->integer - 1 ) ); if ( r_logFile->integer ) { return; } enable = qfalse; } // return if we're already disabled if ( !enable && !isEnabled ) return; isEnabled = enable; if ( enable ) { if ( !log_fp ) { struct tm *newtime; time_t aclock; char buffer[1024]; cvar_t *basedir; time( &aclock ); newtime = localtime( &aclock ); asctime( newtime ); basedir = ri.Cvar_Get( "fs_basepath", "", 0 ); Com_sprintf( buffer, sizeof(buffer), "%s/gl.log", basedir->string ); log_fp = fopen( buffer, "wt" ); fprintf( log_fp, "%s\n", asctime( newtime ) ); } qglAlphaFunc = logAlphaFunc; qglBegin = logBegin; qglBindTexture = logBindTexture; qglBlendFunc = logBlendFunc; qglClear = logClear; qglClearColor = logClearColor; qglClipPlane = logClipPlane; qglColor3f = logColor3f; qglColorMask = logColorMask; qglColorPointer = logColorPointer; qglCullFace = logCullFace; qglDeleteTextures = logDeleteTextures ; qglDepthFunc = logDepthFunc ; qglDepthMask = logDepthMask ; qglDepthRange = logDepthRange ; qglDisable = logDisable ; qglDisableClientState = logDisableClientState ; qglDrawBuffer = logDrawBuffer ; qglDrawElements = logDrawElements ; qglEnable = logEnable ; qglEnableClientState = logEnableClientState ; qglEnd = logEnd ; qglFinish = logFinish ; qglGetError = logGetError ; qglGetIntegerv = logGetIntegerv ; qglGetString = logGetString ; qglLineWidth = logLineWidth ; qglLoadIdentity = logLoadIdentity ; qglLoadMatrixf = logLoadMatrixf ; qglMatrixMode = logMatrixMode ; qglOrtho = logOrtho ; qglPolygonMode = logPolygonMode ; qglPolygonOffset = logPolygonOffset ; qglPopMatrix = logPopMatrix ; qglPushMatrix = logPushMatrix ; qglReadPixels = logReadPixels ; qglScissor = logScissor ; qglStencilFunc = logStencilFunc ; qglStencilOp = logStencilOp ; qglTexCoord2f = logTexCoord2f ; qglTexCoord2fv = logTexCoord2fv ; qglTexCoordPointer = logTexCoordPointer ; qglTexEnvf = logTexEnvf ; qglTexImage2D = logTexImage2D ; qglTexParameterf = logTexParameterf ; qglTexParameterfv = logTexParameterfv ; qglTexSubImage2D = logTexSubImage2D ; qglVertex2f = logVertex2f ; qglVertex3f = logVertex3f ; qglVertex3fv = logVertex3fv ; qglVertexPointer = logVertexPointer ; qglViewport = logViewport ; } else { if ( log_fp ) { fprintf( log_fp, "*** CLOSING LOG ***\n" ); fclose( log_fp ); log_fp = NULL; } qglAlphaFunc = dllAlphaFunc; qglBegin = dllBegin; qglBindTexture = dllBindTexture; qglBlendFunc = dllBlendFunc; qglClear = dllClear; qglClearColor = dllClearColor; qglClipPlane = dllClipPlane; qglColor3f = dllColor3f; qglColorMask = dllColorMask; qglColorPointer = dllColorPointer; qglCullFace = dllCullFace; qglDeleteTextures = dllDeleteTextures ; qglDepthFunc = dllDepthFunc ; qglDepthMask = dllDepthMask ; qglDepthRange = dllDepthRange ; qglDisable = dllDisable ; qglDisableClientState = dllDisableClientState ; qglDrawBuffer = dllDrawBuffer ; qglDrawElements = dllDrawElements ; qglEnable = dllEnable ; qglEnableClientState = dllEnableClientState ; qglEnd = dllEnd ; qglFinish = dllFinish ; qglGetError = dllGetError ; qglGetIntegerv = dllGetIntegerv ; qglGetString = dllGetString ; qglLineWidth = dllLineWidth ; qglLoadIdentity = dllLoadIdentity ; qglLoadMatrixf = dllLoadMatrixf ; qglMatrixMode = dllMatrixMode ; qglOrtho = dllOrtho ; qglPolygonMode = dllPolygonMode ; qglPolygonOffset = dllPolygonOffset ; qglPopMatrix = dllPopMatrix ; qglPushMatrix = dllPushMatrix ; qglReadPixels = dllReadPixels ; qglScissor = dllScissor ; qglStencilFunc = dllStencilFunc ; qglStencilOp = dllStencilOp ; qglTexCoord2f = dllTexCoord2f ; qglTexCoord2fv = dllTexCoord2fv ; qglTexCoordPointer = dllTexCoordPointer ; qglTexEnvf = dllTexEnvf ; qglTexImage2D = dllTexImage2D ; qglTexParameterf = dllTexParameterf ; qglTexParameterfv = dllTexParameterfv ; qglTexSubImage2D = dllTexSubImage2D ; qglVertex2f = dllVertex2f ; qglVertex3f = dllVertex3f ; qglVertex3fv = dllVertex3fv ; qglVertexPointer = dllVertexPointer ; qglViewport = dllViewport ; } } #pragma warning (default : 4113 4133 4047 ) ================================================ FILE: src/engine/platform/win_shared.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "../../game/q_shared.h" #include "../qcommon/qcommon.h" #include "win_local.h" #include #include #include #include #include #include #include #include #include /* ================ Sys_Milliseconds ================ */ int sys_timeBase; int Sys_Milliseconds (void) { int sys_curtime; static qboolean initialized = qfalse; if (!initialized) { sys_timeBase = timeGetTime(); initialized = qtrue; } sys_curtime = timeGetTime() - sys_timeBase; return sys_curtime; } /* ================ Sys_SnapVector ================ */ void Sys_SnapVector( float *v ) { v[0] = (int)v[0]; v[1] = (int)v[1]; v[2] = (int)v[2]; } /* ** ** Disable all optimizations temporarily so this code works correctly! ** */ #pragma optimize( "", off ) /* ** -------------------------------------------------------------------------------- ** ** PROCESSOR STUFF ** ** -------------------------------------------------------------------------------- */ int Sys_GetProcessorId( void ) { return CPUID_GENERIC; } /* ** ** Re-enable optimizations back to what they were ** */ #pragma optimize( "", on ) //============================================ char *Sys_GetCurrentUser( void ) { static char s_userName[1024]; unsigned long size = sizeof( s_userName ); if ( !GetUserName( s_userName, &size ) ) strcpy( s_userName, "player" ); if ( !s_userName[0] ) { strcpy( s_userName, "player" ); } return s_userName; } char *Sys_DefaultHomePath(void) { return NULL; } char *Sys_DefaultInstallPath(void) { return Sys_Cwd(); } ================================================ FILE: src/engine/platform/win_snd.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include #include "../client/snd_local.h" #define CINTERFACE #include "win_local.h" HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter); #define iDirectSoundCreate(a,b,c) pDirectSoundCreate(a,b,c) #define SECONDARY_BUFFER_SIZE 0x10000 static qboolean dsound_init; static int sample16; static DWORD gSndBufSize; static DWORD locksize; static LPDIRECTSOUND pDS; static LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf; static HINSTANCE hInstDS; static const char *DSoundError( int error ) { switch ( error ) { case DSERR_BUFFERLOST: return "DSERR_BUFFERLOST"; case DSERR_INVALIDCALL: return "DSERR_INVALIDCALLS"; case DSERR_INVALIDPARAM: return "DSERR_INVALIDPARAM"; case DSERR_PRIOLEVELNEEDED: return "DSERR_PRIOLEVELNEEDED"; } return "unknown"; } /* ================== SNDDMA_Shutdown ================== */ void SNDDMA_Shutdown( void ) { Com_DPrintf( "Shutting down sound system\n" ); if ( pDS ) { Com_DPrintf( "Destroying DS buffers\n" ); if ( pDS ) { Com_DPrintf( "...setting NORMAL coop level\n" ); pDS->lpVtbl->SetCooperativeLevel( pDS, g_wv.hWnd, DSSCL_PRIORITY ); } if ( pDSBuf ) { Com_DPrintf( "...stopping and releasing sound buffer\n" ); pDSBuf->lpVtbl->Stop( pDSBuf ); pDSBuf->lpVtbl->Release( pDSBuf ); } // only release primary buffer if it's not also the mixing buffer we just released if ( pDSPBuf && ( pDSBuf != pDSPBuf ) ) { Com_DPrintf( "...releasing primary buffer\n" ); pDSPBuf->lpVtbl->Release( pDSPBuf ); } pDSBuf = NULL; pDSPBuf = NULL; dma.buffer = NULL; Com_DPrintf( "...releasing DS object\n" ); pDS->lpVtbl->Release( pDS ); } if ( hInstDS ) { Com_DPrintf( "...freeing DSOUND.DLL\n" ); FreeLibrary( hInstDS ); hInstDS = NULL; } pDS = NULL; pDSBuf = NULL; pDSPBuf = NULL; dsound_init = qfalse; memset ((void *)&dma, 0, sizeof (dma)); CoUninitialize( ); } /* ================== SNDDMA_Init Initialize direct sound Returns false if failed ================== */ qboolean SNDDMA_Init(void) { memset ((void *)&dma, 0, sizeof (dma)); dsound_init = (qboolean) 0; CoInitialize(NULL); if ( !SNDDMA_InitDS () ) { return qfalse; } dsound_init = qtrue; Com_DPrintf("Completed successfully\n" ); return qtrue; } int SNDDMA_InitDS () { HRESULT hresult; DSBUFFERDESC dsbuf; DSBCAPS dsbcaps; WAVEFORMATEX format; int use8; Com_Printf( "Initializing DirectSound\n"); const GUID CLSID_DirectSound = {0x47d4d946, 0x62e8, 0x11cf, 0x93, 0xbc, 0x44, 0x45, 0x53, 0x54, 0x0, 0x0}; const GUID CLSID_DirectSound8 = {0x3901cc3f, 0x84b5, 0x4fa4, 0xba, 0x35, 0xaa, 0x81, 0x72, 0xb8, 0xa0, 0x9b}; const GUID IID_IDirectSound8 = {0xC50A7E93, 0xF395, 0x4834, 0x9E, 0xF6, 0x7F, 0xA9, 0x9D, 0xE5, 0x09, 0x66}; const GUID IID_IDirectSound = {0x279AFA83, 0x4981, 0x11CE, 0xA5, 0x21, 0x00, 0x20, 0xAF, 0x0B, 0xE5, 0x60}; use8 = 1; // Create IDirectSound using the primary sound device if( FAILED( hresult = CoCreateInstance(CLSID_DirectSound8, NULL, CLSCTX_INPROC_SERVER, IID_IDirectSound8, (void **)&pDS))) { use8 = 0; if( FAILED( hresult = CoCreateInstance(CLSID_DirectSound, NULL, CLSCTX_INPROC_SERVER, IID_IDirectSound, (void **)&pDS))) { Com_Printf ("failed\n"); SNDDMA_Shutdown (); return qfalse; } } hresult = pDS->lpVtbl->Initialize( pDS, NULL); Com_DPrintf( "ok\n" ); Com_DPrintf("...setting DSSCL_PRIORITY coop level: " ); if ( DS_OK != pDS->lpVtbl->SetCooperativeLevel( pDS, g_wv.hWnd, DSSCL_PRIORITY ) ) { Com_Printf ("failed\n"); SNDDMA_Shutdown (); return qfalse; } Com_DPrintf("ok\n" ); // create the secondary buffer we'll actually work with dma.channels = 2; dma.samplebits = 16; // if (s_khz->integer == 44) // dma.speed = 44100; // else if (s_khz->integer == 22) // dma.speed = 22050; // else // dma.speed = 11025; dma.speed = 22050; memset (&format, 0, sizeof(format)); format.wFormatTag = WAVE_FORMAT_PCM; format.nChannels = dma.channels; format.wBitsPerSample = dma.samplebits; format.nSamplesPerSec = dma.speed; format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8; format.cbSize = 0; format.nAvgBytesPerSec = format.nSamplesPerSec*format.nBlockAlign; memset (&dsbuf, 0, sizeof(dsbuf)); dsbuf.dwSize = sizeof(DSBUFFERDESC); // Micah: take advantage of 2D hardware.if available. dsbuf.dwFlags = DSBCAPS_LOCHARDWARE; if (use8) { dsbuf.dwFlags |= DSBCAPS_GETCURRENTPOSITION2; } dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE; dsbuf.lpwfxFormat = &format; memset(&dsbcaps, 0, sizeof(dsbcaps)); dsbcaps.dwSize = sizeof(dsbcaps); Com_DPrintf( "...creating secondary buffer: " ); if (DS_OK == pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL)) { Com_Printf( "locked hardware. ok\n" ); } else { // Couldn't get hardware, fallback to software. dsbuf.dwFlags = DSBCAPS_LOCSOFTWARE; if (use8) { dsbuf.dwFlags |= DSBCAPS_GETCURRENTPOSITION2; } if (DS_OK != pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL)) { Com_Printf( "failed\n" ); SNDDMA_Shutdown (); return qfalse; } Com_DPrintf( "forced to software. ok\n" ); } // Make sure mixer is active if ( DS_OK != pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING) ) { Com_Printf ("*** Looped sound play failed ***\n"); SNDDMA_Shutdown (); return qfalse; } // get the returned buffer size if ( DS_OK != pDSBuf->lpVtbl->GetCaps (pDSBuf, &dsbcaps) ) { Com_Printf ("*** GetCaps failed ***\n"); SNDDMA_Shutdown (); return qfalse; } gSndBufSize = dsbcaps.dwBufferBytes; dma.channels = format.nChannels; dma.samplebits = format.wBitsPerSample; dma.speed = format.nSamplesPerSec; dma.samples = gSndBufSize/(dma.samplebits/8); dma.submission_chunk = 1; dma.buffer = NULL; // must be locked first sample16 = (dma.samplebits/8) - 1; SNDDMA_BeginPainting (); if (dma.buffer) memset(dma.buffer, 0, dma.samples * dma.samplebits/8); SNDDMA_Submit (); return 1; } /* ============== SNDDMA_GetDMAPos return the current sample position (in mono samples read) inside the recirculating dma buffer, so the mixing code will know how many sample are required to fill it up. =============== */ int SNDDMA_GetDMAPos( void ) { MMTIME mmtime; int s; DWORD dwWrite; if ( !dsound_init ) { return 0; } mmtime.wType = TIME_SAMPLES; pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &mmtime.u.sample, &dwWrite); s = mmtime.u.sample; s >>= sample16; s &= (dma.samples-1); return s; } /* ============== SNDDMA_BeginPainting Makes sure dma.buffer is valid =============== */ void SNDDMA_BeginPainting( void ) { int reps; DWORD dwSize2; DWORD *pbuf, *pbuf2; HRESULT hresult; DWORD dwStatus; if ( !pDSBuf ) { return; } // if the buffer was lost or stopped, restore it and/or restart it if ( pDSBuf->lpVtbl->GetStatus (pDSBuf, &dwStatus) != DS_OK ) { Com_Printf ("Couldn't get sound buffer status\n"); } if (dwStatus & DSBSTATUS_BUFFERLOST) pDSBuf->lpVtbl->Restore (pDSBuf); if (!(dwStatus & DSBSTATUS_PLAYING)) pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING); // lock the dsound buffer reps = 0; dma.buffer = NULL; while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, (LPVOID*) &pbuf, &locksize, (LPVOID*) &pbuf2, &dwSize2, 0)) != DS_OK) { if (hresult != DSERR_BUFFERLOST) { Com_Printf( "SNDDMA_BeginPainting: Lock failed with error '%s'\n", DSoundError( hresult ) ); S_Shutdown (); return; } else { pDSBuf->lpVtbl->Restore( pDSBuf ); } if (++reps > 2) return; } dma.buffer = (unsigned char *)pbuf; } /* ============== SNDDMA_Submit Send sound to device if buffer isn't really the dma buffer Also unlocks the dsound buffer =============== */ void SNDDMA_Submit( void ) { // unlock the dsound buffer if ( pDSBuf ) { pDSBuf->lpVtbl->Unlock(pDSBuf, dma.buffer, locksize, NULL, 0); } } /* ================= SNDDMA_Activate When we change windows we need to do this ================= */ void SNDDMA_Activate( void ) { if ( !pDS ) { return; } if ( DS_OK != pDS->lpVtbl->SetCooperativeLevel( pDS, g_wv.hWnd, DSSCL_PRIORITY ) ) { Com_Printf ("sound SetCooperativeLevel failed\n"); SNDDMA_Shutdown (); } } ================================================ FILE: src/engine/platform/win_syscon.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // win_syscon.h #include "../client/client.h" #include "win_local.h" #include "resource.h" #include #include #include #include #include #include #include #define COPY_ID 1 #define QUIT_ID 2 #define CLEAR_ID 3 #define ERRORBOX_ID 10 #define ERRORTEXT_ID 11 #define EDIT_ID 100 #define INPUT_ID 101 typedef struct { HWND hWnd; HWND hwndBuffer; HWND hwndButtonClear; HWND hwndButtonCopy; HWND hwndButtonQuit; HWND hwndErrorBox; HWND hwndErrorText; HBITMAP hbmLogo; HBITMAP hbmClearBitmap; HBRUSH hbrEditBackground; HBRUSH hbrErrorBackground; HFONT hfBufferFont; HFONT hfButtonFont; HWND hwndInputLine; char errorString[80]; char consoleText[512], returnedText[512]; int visLevel; qboolean quitOnClose; int windowWidth, windowHeight; WNDPROC SysInputLineWndProc; } WinConData; static WinConData s_wcd; static LRESULT WINAPI ConWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { char *cmdString; static qboolean s_timePolarity; switch (uMsg) { case WM_ACTIVATE: if ( LOWORD( wParam ) != WA_INACTIVE ) { SetFocus( s_wcd.hwndInputLine ); } if ( com_viewlog && ( com_dedicated && !com_dedicated->integer ) ) { // if the viewlog is open, check to see if it's being minimized if ( com_viewlog->integer == 1 ) { if ( HIWORD( wParam ) ) // minimized flag { Cvar_Set( "viewlog", "2" ); } } else if ( com_viewlog->integer == 2 ) { if ( !HIWORD( wParam ) ) // minimized flag { Cvar_Set( "viewlog", "1" ); } } } break; case WM_CLOSE: if ( ( com_dedicated && com_dedicated->integer ) ) { cmdString = CopyString( "quit" ); Sys_QueEvent( 0, SE_CONSOLE, 0, 0, (int)strlen( cmdString ) + 1, cmdString ); } else if ( s_wcd.quitOnClose ) { PostQuitMessage( 0 ); } else { Sys_ShowConsole( 0, qfalse ); Cvar_Set( "viewlog", "0" ); } return 0; case WM_CTLCOLORSTATIC: if ( ( HWND ) lParam == s_wcd.hwndBuffer ) { SetBkColor( ( HDC ) wParam, RGB( 0x00, 0x00, 0xB0 ) ); SetTextColor( ( HDC ) wParam, RGB( 0xff, 0xff, 0x00 ) ); return ( LRESULT ) s_wcd.hbrEditBackground; } else if ( ( HWND ) lParam == s_wcd.hwndErrorBox ) { if ( s_timePolarity & 1 ) { SetBkColor( ( HDC ) wParam, RGB( 0x80, 0x80, 0x80 ) ); SetTextColor( ( HDC ) wParam, RGB( 0xff, 0x0, 0x00 ) ); } else { SetBkColor( ( HDC ) wParam, RGB( 0x80, 0x80, 0x80 ) ); SetTextColor( ( HDC ) wParam, RGB( 0x00, 0x0, 0x00 ) ); } return ( LRESULT ) s_wcd.hbrErrorBackground; } break; case WM_COMMAND: if ( wParam == COPY_ID ) { SendMessage( s_wcd.hwndBuffer, EM_SETSEL, 0, -1 ); SendMessage( s_wcd.hwndBuffer, WM_COPY, 0, 0 ); } else if ( wParam == QUIT_ID ) { if ( s_wcd.quitOnClose ) { PostQuitMessage( 0 ); } else { cmdString = CopyString( "quit" ); Sys_QueEvent( 0, SE_CONSOLE, 0, 0, (int)strlen( cmdString ) + 1, cmdString ); } } else if ( wParam == CLEAR_ID ) { SendMessage( s_wcd.hwndBuffer, EM_SETSEL, 0, -1 ); SendMessage( s_wcd.hwndBuffer, EM_REPLACESEL, FALSE, ( LPARAM ) "" ); UpdateWindow( s_wcd.hwndBuffer ); } break; case WM_CREATE: s_wcd.hbrEditBackground = CreateSolidBrush( RGB( 0x00, 0x00, 0xB0 ) ); s_wcd.hbrErrorBackground = CreateSolidBrush( RGB( 0x80, 0x80, 0x80 ) ); SetTimer( hWnd, 1, 1000, NULL ); break; case WM_TIMER: if ( wParam == 1 ) { s_timePolarity = (qboolean) !s_timePolarity; if ( s_wcd.hwndErrorBox ) { InvalidateRect( s_wcd.hwndErrorBox, NULL, FALSE ); } } break; } return DefWindowProc( hWnd, uMsg, wParam, lParam ); } LONG WINAPI InputLineWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { char inputBuffer[1024]; switch ( uMsg ) { case WM_KILLFOCUS: if ( ( HWND ) wParam == s_wcd.hWnd || ( HWND ) wParam == s_wcd.hwndErrorBox ) { SetFocus( hWnd ); return 0; } break; case WM_CHAR: if ( wParam == 13 ) { GetWindowText( s_wcd.hwndInputLine, inputBuffer, sizeof( inputBuffer ) ); strncat( s_wcd.consoleText, inputBuffer, sizeof( s_wcd.consoleText ) - (int)strlen( s_wcd.consoleText ) - 5 ); strcat( s_wcd.consoleText, "\n" ); SetWindowText( s_wcd.hwndInputLine, "" ); Sys_Print( va( "]%s\n", inputBuffer ) ); return 0; } } return CallWindowProc( s_wcd.SysInputLineWndProc, hWnd, uMsg, wParam, lParam ); } /* ** Sys_CreateConsole */ void Sys_CreateConsole( void ) { HDC hDC; WNDCLASS wc; RECT rect; const char *DEDCLASS = "Q3 WinConsole"; int nHeight; int swidth, sheight; int DEDSTYLE = WS_POPUPWINDOW | WS_CAPTION | WS_MINIMIZEBOX; memset( &wc, 0, sizeof( wc ) ); wc.style = 0; wc.lpfnWndProc = (WNDPROC) ConWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = g_wv.hInstance; wc.hIcon = LoadIcon( g_wv.hInstance, MAKEINTRESOURCE(IDI_ICON1)); wc.hCursor = LoadCursor (NULL,IDC_ARROW); wc.hbrBackground = (HBRUSH) (void *)COLOR_WINDOW; wc.lpszMenuName = 0; wc.lpszClassName = DEDCLASS; if ( !RegisterClass (&wc) ) return; rect.left = 0; rect.right = 540; rect.top = 0; rect.bottom = 450; AdjustWindowRect( &rect, DEDSTYLE, FALSE ); hDC = GetDC( GetDesktopWindow() ); swidth = GetDeviceCaps( hDC, HORZRES ); sheight = GetDeviceCaps( hDC, VERTRES ); ReleaseDC( GetDesktopWindow(), hDC ); s_wcd.windowWidth = rect.right - rect.left + 1; s_wcd.windowHeight = rect.bottom - rect.top + 1; s_wcd.hWnd = CreateWindowEx( 0, DEDCLASS, "Quake 3 Console", DEDSTYLE, ( swidth - 600 ) / 2, ( sheight - 450 ) / 2 , rect.right - rect.left + 1, rect.bottom - rect.top + 1, NULL, NULL, g_wv.hInstance, NULL ); if ( s_wcd.hWnd == NULL ) { return; } // // create fonts // hDC = GetDC( s_wcd.hWnd ); nHeight = -MulDiv( 8, GetDeviceCaps( hDC, LOGPIXELSY), 72); s_wcd.hfBufferFont = CreateFont( nHeight, 0, 0, 0, FW_LIGHT, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_MODERN | FIXED_PITCH, "Courier New" ); ReleaseDC( s_wcd.hWnd, hDC ); // // create the input line // s_wcd.hwndInputLine = CreateWindow( "edit", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_AUTOHSCROLL, 6, 400, 528, 20, s_wcd.hWnd, ( HMENU ) INPUT_ID, // child window ID g_wv.hInstance, NULL ); // // create the buttons // s_wcd.hwndButtonCopy = CreateWindow( "button", NULL, BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, 5, 425, 72, 24, s_wcd.hWnd, ( HMENU ) COPY_ID, // child window ID g_wv.hInstance, NULL ); SendMessage( s_wcd.hwndButtonCopy, WM_SETTEXT, 0, ( LPARAM ) "copy" ); s_wcd.hwndButtonClear = CreateWindow( "button", NULL, BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, 82, 425, 72, 24, s_wcd.hWnd, ( HMENU ) CLEAR_ID, // child window ID g_wv.hInstance, NULL ); SendMessage( s_wcd.hwndButtonClear, WM_SETTEXT, 0, ( LPARAM ) "clear" ); s_wcd.hwndButtonQuit = CreateWindow( "button", NULL, BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, 462, 425, 72, 24, s_wcd.hWnd, ( HMENU ) QUIT_ID, // child window ID g_wv.hInstance, NULL ); SendMessage( s_wcd.hwndButtonQuit, WM_SETTEXT, 0, ( LPARAM ) "quit" ); // // create the scrollbuffer // s_wcd.hwndBuffer = CreateWindow( "edit", NULL, WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY, 6, 40, 526, 354, s_wcd.hWnd, ( HMENU ) EDIT_ID, // child window ID g_wv.hInstance, NULL ); SendMessage( s_wcd.hwndBuffer, WM_SETFONT, ( WPARAM ) s_wcd.hfBufferFont, 0 ); s_wcd.SysInputLineWndProc = (WNDPROC)SetWindowLongPtr(s_wcd.hwndInputLine, GWLP_WNDPROC, (LONG_PTR)InputLineWndProc); SendMessage( s_wcd.hwndInputLine, WM_SETFONT, ( WPARAM ) s_wcd.hfBufferFont, 0 ); ShowWindow( s_wcd.hWnd, SW_SHOWDEFAULT); UpdateWindow( s_wcd.hWnd ); SetForegroundWindow( s_wcd.hWnd ); SetFocus( s_wcd.hwndInputLine ); s_wcd.visLevel = 1; } /* ** Sys_DestroyConsole */ void Sys_DestroyConsole( void ) { if ( s_wcd.hWnd ) { ShowWindow( s_wcd.hWnd, SW_HIDE ); CloseWindow( s_wcd.hWnd ); DestroyWindow( s_wcd.hWnd ); s_wcd.hWnd = 0; } } /* ** Sys_ShowConsole */ void Sys_ShowConsole( int visLevel, qboolean quitOnClose ) { s_wcd.quitOnClose = quitOnClose; if ( visLevel == s_wcd.visLevel ) { return; } s_wcd.visLevel = visLevel; if ( !s_wcd.hWnd ) return; switch ( visLevel ) { case 0: ShowWindow( s_wcd.hWnd, SW_HIDE ); break; case 1: ShowWindow( s_wcd.hWnd, SW_SHOWNORMAL ); SendMessage( s_wcd.hwndBuffer, EM_LINESCROLL, 0, 0xffff ); break; case 2: ShowWindow( s_wcd.hWnd, SW_MINIMIZE ); break; default: Sys_Error( "Invalid visLevel %d sent to Sys_ShowConsole\n", visLevel ); break; } } /* ** Sys_ConsoleInput */ char *Sys_ConsoleInput( void ) { if ( s_wcd.consoleText[0] == 0 ) { return NULL; } strcpy( s_wcd.returnedText, s_wcd.consoleText ); s_wcd.consoleText[0] = 0; return s_wcd.returnedText; } /* ** Conbuf_AppendText */ void Conbuf_AppendText( const char *pMsg ) { #define CONSOLE_BUFFER_SIZE 16384 char buffer[CONSOLE_BUFFER_SIZE*2]; char *b = buffer; const char *msg; int bufLen; int i = 0; static unsigned long s_totalChars; // // if the message is REALLY long, use just the last portion of it // if ( (int)strlen( pMsg ) > CONSOLE_BUFFER_SIZE - 1 ) { msg = pMsg + (int)strlen( pMsg ) - CONSOLE_BUFFER_SIZE + 1; } else { msg = pMsg; } // // copy into an intermediate buffer // while ( msg[i] && ( ( b - buffer ) < sizeof( buffer ) - 1 ) ) { if ( msg[i] == '\n' && msg[i+1] == '\r' ) { b[0] = '\r'; b[1] = '\n'; b += 2; i++; } else if ( msg[i] == '\r' ) { b[0] = '\r'; b[1] = '\n'; b += 2; } else if ( msg[i] == '\n' ) { b[0] = '\r'; b[1] = '\n'; b += 2; } else if ( Q_IsColorString( &msg[i] ) ) { i++; } else { *b= msg[i]; b++; } i++; } *b = 0; bufLen = b - buffer; s_totalChars += bufLen; // // replace selection instead of appending if we're overflowing // if ( s_totalChars > 0x7fff ) { SendMessage( s_wcd.hwndBuffer, EM_SETSEL, 0, -1 ); s_totalChars = bufLen; } // // put this text into the windows console // SendMessage( s_wcd.hwndBuffer, EM_LINESCROLL, 0, 0xffff ); SendMessage( s_wcd.hwndBuffer, EM_SCROLLCARET, 0, 0 ); SendMessage( s_wcd.hwndBuffer, EM_REPLACESEL, 0, (LPARAM) buffer ); } /* ** Sys_SetErrorText */ void Sys_SetErrorText( const char *buf ) { Q_strncpyz( s_wcd.errorString, buf, sizeof( s_wcd.errorString ) ); if ( !s_wcd.hwndErrorBox ) { s_wcd.hwndErrorBox = CreateWindow( "static", NULL, WS_CHILD | WS_VISIBLE | SS_SUNKEN, 6, 5, 526, 30, s_wcd.hWnd, ( HMENU ) ERRORBOX_ID, // child window ID g_wv.hInstance, NULL ); SendMessage( s_wcd.hwndErrorBox, WM_SETFONT, ( WPARAM ) s_wcd.hfBufferFont, 0 ); SetWindowText( s_wcd.hwndErrorBox, s_wcd.errorString ); DestroyWindow( s_wcd.hwndInputLine ); s_wcd.hwndInputLine = NULL; } } ================================================ FILE: src/engine/platform/win_wndproc.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "../client/client.h" #include "win_local.h" WinVars_t g_wv; #ifndef WM_MOUSEWHEEL #define WM_MOUSEWHEEL (WM_MOUSELAST+1) // message that will be supported by the OS #endif // Console variables that we need to access from this module cvar_t *vid_xpos; // X coordinate of window position cvar_t *vid_ypos; // Y coordinate of window position extern cvar_t *r_fullscreen; #define VID_NUM_MODES ( sizeof( vid_modes ) / sizeof( vid_modes[0] ) ) LONG WINAPI MainWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); /* ================== VID_AppActivate ================== */ static void VID_AppActivate(BOOL fActive, BOOL minimize) { g_wv.isMinimized = (qboolean) minimize; Com_DPrintf("VID_AppActivate: %i\n", fActive ); Key_ClearStates(); // FIXME!!! // we don't want to act like we're active if we're minimized if (fActive && !g_wv.isMinimized ) { g_wv.activeApp = qtrue; } else { g_wv.activeApp = qfalse; } // minimize/restore mouse-capture on demand if (!g_wv.activeApp ) { IN_Activate (qfalse); } else { IN_Activate (qtrue); } } //========================================================================== static byte s_scantokey[128] = { // 0 1 2 3 4 5 6 7 // 8 9 A B C D E F 0 , 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', K_BACKSPACE, 9, // 0 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', 13 , K_CTRL,'a', 's', // 1 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'' , '`', K_SHIFT,'\\', 'z', 'x', 'c', 'v', // 2 'b', 'n', 'm', ',', '.', '/', K_SHIFT,'*', K_ALT,' ', K_CAPSLOCK , K_F1, K_F2, K_F3, K_F4, K_F5, // 3 K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, 0 , K_HOME, K_UPARROW,K_PGUP,K_KP_MINUS,K_LEFTARROW,K_KP_5,K_RIGHTARROW, K_KP_PLUS,K_END, //4 K_DOWNARROW,K_PGDN,K_INS,K_DEL,0,0, 0, K_F11, K_F12,0 , 0 , 0 , 0 , 0 , 0 , 0, // 5 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0, // 6 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 // 7 }; /* ======= MapKey Map from windows to quake keynums ======= */ static int MapKey (int key) { int result; int modified; qboolean is_extended; // Com_Printf( "0x%x\n", key); modified = ( key >> 16 ) & 255; if ( modified > 127 ) return 0; if ( key & ( 1 << 24 ) ) { is_extended = qtrue; } else { is_extended = qfalse; } result = s_scantokey[modified]; if ( !is_extended ) { switch ( result ) { case K_HOME: return K_KP_HOME; case K_UPARROW: return K_KP_UPARROW; case K_PGUP: return K_KP_PGUP; case K_LEFTARROW: return K_KP_LEFTARROW; case K_RIGHTARROW: return K_KP_RIGHTARROW; case K_END: return K_KP_END; case K_DOWNARROW: return K_KP_DOWNARROW; case K_PGDN: return K_KP_PGDN; case K_INS: return K_KP_INS; case K_DEL: return K_KP_DEL; default: return result; } } else { switch ( result ) { case K_PAUSE: return K_KP_NUMLOCK; case 0x0D: return K_KP_ENTER; case 0x2F: return K_KP_SLASH; case 0xAF: return K_KP_PLUS; } return result; } } /* ==================== MainWndProc main window procedure ==================== */ extern cvar_t *in_mouse; extern cvar_t *in_logitechbug; LONG WINAPI MainWndProc ( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { static qboolean flip = qtrue; int zDelta, i; switch (uMsg) { case WM_MOUSEWHEEL: { // 120 increments, might be 240 and multiples if wheel goes too fast // NOTE Logitech: logitech drivers are screwed and send the message twice? // could add a cvar to interpret the message as successive press/release events zDelta = ( short ) HIWORD( wParam ) / 120; if ( zDelta > 0 ) { for(i=0; iinteger) { Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_MWHEELUP, qtrue, 0, NULL ); Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_MWHEELUP, qfalse, 0, NULL ); } else { Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_MWHEELUP, flip, 0, NULL ); flip = (qboolean) !flip; } } } else { for(i=0; i<-zDelta; i++) { if (!in_logitechbug->integer) { Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_MWHEELDOWN, qtrue, 0, NULL ); Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_MWHEELDOWN, qfalse, 0, NULL ); } else { Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, K_MWHEELDOWN, flip, 0, NULL ); flip = (qboolean) !flip; } } } // when an application processes the WM_MOUSEWHEEL message, it must return zero return 0; } break; case WM_CREATE: g_wv.hWnd = hWnd; vid_xpos = Cvar_Get ("vid_xpos", "3", CVAR_ARCHIVE); vid_ypos = Cvar_Get ("vid_ypos", "22", CVAR_ARCHIVE); r_fullscreen = Cvar_Get ("r_fullscreen", "1", CVAR_ARCHIVE | CVAR_LATCH ); break; case WM_DESTROY: // let sound and input know about this? g_wv.hWnd = NULL; break; case WM_CLOSE: Cbuf_ExecuteText( EXEC_APPEND, "quit" ); break; case WM_ACTIVATE: { int fActive, fMinimized; fActive = LOWORD(wParam); fMinimized = (BOOL) HIWORD(wParam); VID_AppActivate( fActive != WA_INACTIVE, fMinimized); SNDDMA_Activate(); } break; case WM_MOVE: { int xPos, yPos; RECT r; int style; if (!r_fullscreen->integer ) { xPos = (short) LOWORD(lParam); // horizontal position yPos = (short) HIWORD(lParam); // vertical position r.left = 0; r.top = 0; r.right = 1; r.bottom = 1; style = GetWindowLong( hWnd, GWL_STYLE ); AdjustWindowRect( &r, style, FALSE ); Cvar_SetValue( "vid_xpos", xPos + r.left); Cvar_SetValue( "vid_ypos", yPos + r.top); vid_xpos->modified = qfalse; vid_ypos->modified = qfalse; if ( g_wv.activeApp ) { IN_Activate (qtrue); } } } break; // this is complicated because Win32 seems to pack multiple mouse events into // one update sometimes, so we always check all states and look for events case WM_LBUTTONDOWN: case WM_LBUTTONUP: case WM_RBUTTONDOWN: case WM_RBUTTONUP: case WM_MBUTTONDOWN: case WM_MBUTTONUP: case WM_MOUSEMOVE: { int temp; temp = 0; if (wParam & MK_LBUTTON) temp |= 1; if (wParam & MK_RBUTTON) temp |= 2; if (wParam & MK_MBUTTON) temp |= 4; IN_MouseEvent (temp); } break; case WM_SYSCOMMAND: if ( wParam == SC_SCREENSAVE ) return 0; break; case WM_SYSKEYDOWN: if ( wParam == 13 ) { if ( r_fullscreen ) { Cvar_SetValue( "r_fullscreen", !r_fullscreen->integer ); Cbuf_AddText( "vid_restart\n" ); } return 0; } // fall through case WM_KEYDOWN: Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, MapKey( lParam ), qtrue, 0, NULL ); break; case WM_SYSKEYUP: case WM_KEYUP: Sys_QueEvent( g_wv.sysMsgTime, SE_KEY, MapKey( lParam ), qfalse, 0, NULL ); break; case WM_CHAR: Sys_QueEvent( g_wv.sysMsgTime, SE_CHAR, wParam, 0, 0, NULL ); break; } return DefWindowProc( hWnd, uMsg, wParam, lParam ); } ================================================ FILE: src/engine/platform/winquake.rc ================================================ //Microsoft Developer Studio generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "winres.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // English (U.S.) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) #ifdef _WIN32 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) #endif //_WIN32 #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE DISCARDABLE BEGIN "resource.h\0" END 2 TEXTINCLUDE DISCARDABLE BEGIN "#include ""winres.h""\r\n" "\0" END 3 TEXTINCLUDE DISCARDABLE BEGIN "\0" END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Icon // // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. IDI_ICON1 ICON DISCARDABLE "qe3.ico" ///////////////////////////////////////////////////////////////////////////// // // String Table // STRINGTABLE DISCARDABLE BEGIN IDS_STRING1 "Quake3" END #endif // English (U.S.) resources ///////////////////////////////////////////////////////////////////////////// ================================================ FILE: src/engine/qcommon/cm_load.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // cmodel.c -- model loading #include "cm_local.h" #ifdef BSPC #include "../bspc/l_qfiles.h" void SetPlaneSignbits (cplane_t *out) { int bits, j; // for fast box on planeside test bits = 0; for (j=0 ; j<3 ; j++) { if (out->normal[j] < 0) { bits |= 1<signbits = bits; } #endif //BSPC // to allow boxes to be treated as brush models, we allocate // some extra indexes along with those needed by the map #define BOX_BRUSHES 1 #define BOX_SIDES 6 #define BOX_LEAFS 2 #define BOX_PLANES 12 #define LL(x) x=LittleLong(x) clipMap_t cm; int c_pointcontents; int c_traces, c_brush_traces, c_patch_traces; byte *cmod_base; #ifndef BSPC cvar_t *cm_noAreas; cvar_t *cm_noCurves; cvar_t *cm_playerCurveClip; #endif cmodel_t box_model; cplane_t *box_planes; cbrush_t *box_brush; void CM_InitBoxHull (void); void CM_FloodAreaConnections (void); /* =============================================================================== MAP LOADING =============================================================================== */ /* ================= CMod_LoadShaders ================= */ void CMod_LoadShaders( lump_t *l ) { dshader_t *in, *out; int i, count; in = (dshader_t*) (void *)(cmod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Com_Error (ERR_DROP, "CMod_LoadShaders: funny lump size"); } count = l->filelen / sizeof(*in); if (count < 1) { Com_Error (ERR_DROP, "Map with no shaders"); } cm.shaders = (dshader_t*) Hunk_Alloc( count * sizeof( *cm.shaders ), h_high ); cm.numShaders = count; Com_Memcpy( cm.shaders, in, count * sizeof( *cm.shaders ) ); out = cm.shaders; for ( i=0 ; icontentFlags = LittleLong( out->contentFlags ); out->surfaceFlags = LittleLong( out->surfaceFlags ); } } /* ================= CMod_LoadSubmodels ================= */ void CMod_LoadSubmodels( lump_t *l ) { dmodel_t *in; cmodel_t *out; int i, j, count; int *indexes; in = (dmodel_t*) (void *)(cmod_base + l->fileofs); if (l->filelen % sizeof(*in)) Com_Error (ERR_DROP, "CMod_LoadSubmodels: funny lump size"); count = l->filelen / sizeof(*in); if (count < 1) Com_Error (ERR_DROP, "Map with no models"); cm.cmodels = (cmodel_t*) Hunk_Alloc( count * sizeof( *cm.cmodels ), h_high ); cm.numSubModels = count; if ( count > MAX_SUBMODELS ) { Com_Error( ERR_DROP, "MAX_SUBMODELS exceeded" ); } for ( i=0 ; imins[j] = LittleFloat (in->mins[j]) - 1; out->maxs[j] = LittleFloat (in->maxs[j]) + 1; } if ( i == 0 ) { continue; // world model doesn't need other info } // make a "leaf" just to hold the model's brushes and surfaces out->leaf.numLeafBrushes = LittleLong( in->numBrushes ); indexes = (int*) Hunk_Alloc( out->leaf.numLeafBrushes * 4, h_high ); out->leaf.firstLeafBrush = indexes - cm.leafbrushes; for ( j = 0 ; j < out->leaf.numLeafBrushes ; j++ ) { indexes[j] = LittleLong( in->firstBrush ) + j; } out->leaf.numLeafSurfaces = LittleLong( in->numSurfaces ); indexes = (int*) Hunk_Alloc( out->leaf.numLeafSurfaces * 4, h_high ); out->leaf.firstLeafSurface = indexes - cm.leafsurfaces; for ( j = 0 ; j < out->leaf.numLeafSurfaces ; j++ ) { indexes[j] = LittleLong( in->firstSurface ) + j; } } } /* ================= CMod_LoadNodes ================= */ void CMod_LoadNodes( lump_t *l ) { dnode_t *in; int child; cNode_t *out; int i, j, count; in = (dnode_t*) (void *)(cmod_base + l->fileofs); if (l->filelen % sizeof(*in)) Com_Error (ERR_DROP, "MOD_LoadBmodel: funny lump size"); count = l->filelen / sizeof(*in); if (count < 1) Com_Error (ERR_DROP, "Map has no nodes"); cm.nodes = (cNode_t*) Hunk_Alloc( count * sizeof( *cm.nodes ), h_high ); cm.numNodes = count; out = cm.nodes; for (i=0 ; iplane = cm.planes + LittleLong( in->planeNum ); for (j=0 ; j<2 ; j++) { child = LittleLong (in->children[j]); out->children[j] = child; } } } /* ================= CM_BoundBrush ================= */ void CM_BoundBrush( cbrush_t *b ) { b->bounds[0][0] = -b->sides[0].plane->dist; b->bounds[1][0] = b->sides[1].plane->dist; b->bounds[0][1] = -b->sides[2].plane->dist; b->bounds[1][1] = b->sides[3].plane->dist; b->bounds[0][2] = -b->sides[4].plane->dist; b->bounds[1][2] = b->sides[5].plane->dist; } /* ================= CMod_LoadBrushes ================= */ void CMod_LoadBrushes( lump_t *l ) { dbrush_t *in; cbrush_t *out; int i, count; in = (dbrush_t*) (void *)(cmod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Com_Error (ERR_DROP, "MOD_LoadBmodel: funny lump size"); } count = l->filelen / sizeof(*in); cm.brushes = (cbrush_t*) Hunk_Alloc( ( BOX_BRUSHES + count ) * sizeof( *cm.brushes ), h_high ); cm.numBrushes = count; out = cm.brushes; for ( i=0 ; isides = cm.brushsides + LittleLong(in->firstSide); out->numsides = LittleLong(in->numSides); out->shaderNum = LittleLong( in->shaderNum ); if ( out->shaderNum < 0 || out->shaderNum >= cm.numShaders ) { Com_Error( ERR_DROP, "CMod_LoadBrushes: bad shaderNum: %i", out->shaderNum ); } out->contents = cm.shaders[out->shaderNum].contentFlags; CM_BoundBrush( out ); } } /* ================= CMod_LoadLeafs ================= */ void CMod_LoadLeafs (lump_t *l) { int i; cLeaf_t *out; dleaf_t *in; int count; in = (dleaf_t*) (void *)(cmod_base + l->fileofs); if (l->filelen % sizeof(*in)) Com_Error (ERR_DROP, "MOD_LoadBmodel: funny lump size"); count = l->filelen / sizeof(*in); if (count < 1) Com_Error (ERR_DROP, "Map with no leafs"); cm.leafs = (cLeaf_t*) Hunk_Alloc( ( BOX_LEAFS + count ) * sizeof( *cm.leafs ), h_high ); cm.numLeafs = count; out = cm.leafs; for ( i=0 ; icluster = LittleLong (in->cluster); out->area = LittleLong (in->area); out->firstLeafBrush = LittleLong (in->firstLeafBrush); out->numLeafBrushes = LittleLong (in->numLeafBrushes); out->firstLeafSurface = LittleLong (in->firstLeafSurface); out->numLeafSurfaces = LittleLong (in->numLeafSurfaces); if (out->cluster >= cm.numClusters) cm.numClusters = out->cluster + 1; if (out->area >= cm.numAreas) cm.numAreas = out->area + 1; } cm.areas = (cArea_t*) Hunk_Alloc( cm.numAreas * sizeof( *cm.areas ), h_high ); cm.areaPortals = (int*) Hunk_Alloc( cm.numAreas * cm.numAreas * sizeof( *cm.areaPortals ), h_high ); } /* ================= CMod_LoadPlanes ================= */ void CMod_LoadPlanes (lump_t *l) { int i, j; cplane_t *out; dplane_t *in; int count; int bits; in = (dplane_t*) (void *)(cmod_base + l->fileofs); if (l->filelen % sizeof(*in)) Com_Error (ERR_DROP, "MOD_LoadBmodel: funny lump size"); count = l->filelen / sizeof(*in); if (count < 1) Com_Error (ERR_DROP, "Map with no planes"); cm.planes = (cplane_t*) Hunk_Alloc( ( BOX_PLANES + count ) * sizeof( *cm.planes ), h_high ); cm.numPlanes = count; out = cm.planes; 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; } } /* ================= CMod_LoadLeafBrushes ================= */ void CMod_LoadLeafBrushes (lump_t *l) { int i; int *out; int *in; int count; in = (int*) (void *)(cmod_base + l->fileofs); if (l->filelen % sizeof(*in)) Com_Error (ERR_DROP, "MOD_LoadBmodel: funny lump size"); count = l->filelen / sizeof(*in); cm.leafbrushes = (int*) Hunk_Alloc( (count + BOX_BRUSHES) * sizeof( *cm.leafbrushes ), h_high ); cm.numLeafBrushes = count; out = cm.leafbrushes; for ( i=0 ; ifileofs); if (l->filelen % sizeof(*in)) Com_Error (ERR_DROP, "MOD_LoadBmodel: funny lump size"); count = l->filelen / sizeof(*in); cm.leafsurfaces = (int*) Hunk_Alloc( count * sizeof( *cm.leafsurfaces ), h_high ); cm.numLeafSurfaces = count; out = cm.leafsurfaces; for ( i=0 ; ifileofs); if ( l->filelen % sizeof(*in) ) { Com_Error (ERR_DROP, "MOD_LoadBmodel: funny lump size"); } count = l->filelen / sizeof(*in); cm.brushsides = (cbrushside_t*) Hunk_Alloc( ( BOX_SIDES + count ) * sizeof( *cm.brushsides ), h_high ); cm.numBrushSides = count; out = cm.brushsides; for ( i=0 ; iplaneNum ); out->plane = &cm.planes[num]; out->shaderNum = LittleLong( in->shaderNum ); if ( out->shaderNum < 0 || out->shaderNum >= cm.numShaders ) { Com_Error( ERR_DROP, "CMod_LoadBrushSides: bad shaderNum: %i", out->shaderNum ); } out->surfaceFlags = cm.shaders[out->shaderNum].surfaceFlags; } } /* ================= CMod_LoadEntityString ================= */ void CMod_LoadEntityString( lump_t *l ) { cm.entityString = (char*) Hunk_Alloc( l->filelen, h_high ); cm.numEntityChars = l->filelen; Com_Memcpy (cm.entityString, cmod_base + l->fileofs, l->filelen); } /* ================= CMod_LoadVisibility ================= */ #define VIS_HEADER 8 void CMod_LoadVisibility( lump_t *l ) { int len; byte *buf; len = l->filelen; if ( !len ) { cm.clusterBytes = ( cm.numClusters + 31 ) & ~31; cm.visibility = (byte*) Hunk_Alloc( cm.clusterBytes, h_high ); Com_Memset( cm.visibility, 255, cm.clusterBytes ); return; } buf = cmod_base + l->fileofs; cm.vised = qtrue; cm.visibility = (byte*) Hunk_Alloc( len, h_high ); cm.numClusters = LittleLong( ((int *)buf)[0] ); cm.clusterBytes = LittleLong( ((int *)buf)[1] ); Com_Memcpy (cm.visibility, buf + VIS_HEADER, len - VIS_HEADER ); } //================================================================== /* ================= CMod_LoadPatches ================= */ #define MAX_PATCH_VERTS 1024 void CMod_LoadPatches( lump_t *surfs, lump_t *verts ) { drawVert_t *dv, *dv_p; dsurface_t *in; int count; int i, j; int c; cPatch_t *patch; vec3_t points[MAX_PATCH_VERTS]; int width, height; int shaderNum; in = (dsurface_t*) (void *)(cmod_base + surfs->fileofs); if (surfs->filelen % sizeof(*in)) Com_Error (ERR_DROP, "MOD_LoadBmodel: funny lump size"); cm.numSurfaces = count = surfs->filelen / sizeof(*in); cm.surfaces = (cPatch_t**) Hunk_Alloc( cm.numSurfaces * sizeof( cm.surfaces[0] ), h_high ); dv = (drawVert_t*) (void *)(cmod_base + verts->fileofs); if (verts->filelen % sizeof(*dv)) Com_Error (ERR_DROP, "MOD_LoadBmodel: funny lump size"); // scan through all the surfaces, but only load patches, // not planar faces for ( i = 0 ; i < count ; i++, in++ ) { if ( LittleLong( in->surfaceType ) != MST_PATCH ) { continue; // ignore other surfaces } // FIXME: check for non-colliding patches cm.surfaces[ i ] = patch = (cPatch_t*) Hunk_Alloc( sizeof( *patch ), h_high ); // load the full drawverts onto the stack width = LittleLong( in->patchWidth ); height = LittleLong( in->patchHeight ); c = width * height; if ( c > MAX_PATCH_VERTS ) { Com_Error( ERR_DROP, "ParseMesh: MAX_PATCH_VERTS" ); } dv_p = dv + LittleLong( in->firstVert ); for ( j = 0 ; j < c ; j++, dv_p++ ) { points[j][0] = LittleFloat( dv_p->xyz[0] ); points[j][1] = LittleFloat( dv_p->xyz[1] ); points[j][2] = LittleFloat( dv_p->xyz[2] ); } shaderNum = LittleLong( in->shaderNum ); patch->contents = cm.shaders[shaderNum].contentFlags; patch->surfaceFlags = cm.shaders[shaderNum].surfaceFlags; // create the internal facet structure patch->pc = CM_GeneratePatchCollide( width, height, points ); } } //================================================================== unsigned CM_LumpChecksum(lump_t *lump) { return LittleLong (Com_BlockChecksum (cmod_base + lump->fileofs, lump->filelen)); } unsigned CM_Checksum(dheader_t *header) { unsigned checksums[16]; checksums[0] = CM_LumpChecksum(&header->lumps[LUMP_SHADERS]); checksums[1] = CM_LumpChecksum(&header->lumps[LUMP_LEAFS]); checksums[2] = CM_LumpChecksum(&header->lumps[LUMP_LEAFBRUSHES]); checksums[3] = CM_LumpChecksum(&header->lumps[LUMP_LEAFSURFACES]); checksums[4] = CM_LumpChecksum(&header->lumps[LUMP_PLANES]); checksums[5] = CM_LumpChecksum(&header->lumps[LUMP_BRUSHSIDES]); checksums[6] = CM_LumpChecksum(&header->lumps[LUMP_BRUSHES]); checksums[7] = CM_LumpChecksum(&header->lumps[LUMP_MODELS]); checksums[8] = CM_LumpChecksum(&header->lumps[LUMP_NODES]); checksums[9] = CM_LumpChecksum(&header->lumps[LUMP_SURFACES]); checksums[10] = CM_LumpChecksum(&header->lumps[LUMP_DRAWVERTS]); return LittleLong(Com_BlockChecksum(checksums, 11 * 4)); } /* ================== CM_LoadMap Loads in the map and all submodels ================== */ void CM_LoadMap( const char *name, qboolean clientload, int *checksum ) { int *buf; int i; dheader_t header; int length; static unsigned last_checksum; if ( !name || !name[0] ) { Com_Error( ERR_DROP, "CM_LoadMap: NULL name" ); } #ifndef BSPC cm_noAreas = Cvar_Get ("cm_noAreas", "0", CVAR_CHEAT); cm_noCurves = Cvar_Get ("cm_noCurves", "0", CVAR_CHEAT); cm_playerCurveClip = Cvar_Get ("cm_playerCurveClip", "1", CVAR_ARCHIVE|CVAR_CHEAT ); #endif Com_DPrintf( "CM_LoadMap( %s, %i )\n", name, clientload ); if ( !strcmp( cm.name, name ) && clientload ) { *checksum = last_checksum; return; } // free old stuff Com_Memset( &cm, 0, sizeof( cm ) ); CM_ClearLevelPatches(); if ( !name[0] ) { cm.numLeafs = 1; cm.numClusters = 1; cm.numAreas = 1; cm.cmodels = (cmodel_t*) Hunk_Alloc( sizeof( *cm.cmodels ), h_high ); *checksum = 0; return; } // // load the file // #ifndef BSPC length = FS_ReadFile( name, (void **)&buf ); #else length = LoadQuakeFile((quakefile_t *) name, (void **)&buf); #endif if ( !buf ) { Com_Error (ERR_DROP, "Couldn't load %s", name); } last_checksum = LittleLong (Com_BlockChecksum (buf, length)); *checksum = last_checksum; header = *(dheader_t *)buf; for (i=0 ; i= cm.numSubModels ) { Com_Error (ERR_DROP, "CM_InlineModel: bad number"); } return index; } int CM_NumClusters( void ) { return cm.numClusters; } int CM_NumInlineModels( void ) { return cm.numSubModels; } char *CM_EntityString( void ) { return cm.entityString; } int CM_LeafCluster( int leafnum ) { if (leafnum < 0 || leafnum >= cm.numLeafs) { Com_Error (ERR_DROP, "CM_LeafCluster: bad number"); } return cm.leafs[leafnum].cluster; } int CM_LeafArea( int leafnum ) { if ( leafnum < 0 || leafnum >= cm.numLeafs ) { Com_Error (ERR_DROP, "CM_LeafArea: bad number"); } return cm.leafs[leafnum].area; } //======================================================================= /* =================== CM_InitBoxHull Set up the planes and nodes so that the six floats of a bounding box can just be stored out and get a proper clipping hull structure. =================== */ void CM_InitBoxHull (void) { int i; int side; cplane_t *p; cbrushside_t *s; box_planes = &cm.planes[cm.numPlanes]; box_brush = &cm.brushes[cm.numBrushes]; box_brush->numsides = 6; box_brush->sides = cm.brushsides + cm.numBrushSides; box_brush->contents = CONTENTS_BODY; box_model.leaf.numLeafBrushes = 1; // box_model.leaf.firstLeafBrush = cm.numBrushes; box_model.leaf.firstLeafBrush = cm.numLeafBrushes; cm.leafbrushes[cm.numLeafBrushes] = cm.numBrushes; for (i=0 ; i<6 ; i++) { side = i&1; // brush sides s = &cm.brushsides[cm.numBrushSides+i]; s->plane = cm.planes + (cm.numPlanes+i*2+side); s->surfaceFlags = 0; // planes p = &box_planes[i*2]; p->type = i>>1; p->signbits = 0; VectorClear (p->normal); p->normal[i>>1] = 1; p = &box_planes[i*2+1]; p->type = 3 + (i>>1); p->signbits = 0; VectorClear (p->normal); p->normal[i>>1] = -1; SetPlaneSignbits( p ); } } /* =================== CM_TempBoxModel To keep everything totally uniform, bounding boxes are turned into small BSP trees instead of being compared directly. Capsules are handled differently though. =================== */ clipHandle_t CM_TempBoxModel( const vec3_t mins, const vec3_t maxs, int capsule ) { VectorCopy( mins, box_model.mins ); VectorCopy( maxs, box_model.maxs ); if ( capsule ) { return CAPSULE_MODEL_HANDLE; } box_planes[0].dist = maxs[0]; box_planes[1].dist = -maxs[0]; box_planes[2].dist = mins[0]; box_planes[3].dist = -mins[0]; box_planes[4].dist = maxs[1]; box_planes[5].dist = -maxs[1]; box_planes[6].dist = mins[1]; box_planes[7].dist = -mins[1]; box_planes[8].dist = maxs[2]; box_planes[9].dist = -maxs[2]; box_planes[10].dist = mins[2]; box_planes[11].dist = -mins[2]; VectorCopy( mins, box_brush->bounds[0] ); VectorCopy( maxs, box_brush->bounds[1] ); return BOX_MODEL_HANDLE; } /* =================== CM_ModelBounds =================== */ void CM_ModelBounds( clipHandle_t model, vec3_t mins, vec3_t maxs ) { cmodel_t *cmod; cmod = CM_ClipHandleToModel( model ); VectorCopy( cmod->mins, mins ); VectorCopy( cmod->maxs, maxs ); } ================================================ FILE: src/engine/qcommon/cm_local.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "../../game/q_shared.h" #include "qcommon.h" #include "cm_polylib.h" #define MAX_SUBMODELS 256 #define BOX_MODEL_HANDLE 255 #define CAPSULE_MODEL_HANDLE 254 typedef struct { cplane_t *plane; int children[2]; // negative numbers are leafs } cNode_t; typedef struct { int cluster; int area; int firstLeafBrush; int numLeafBrushes; int firstLeafSurface; int numLeafSurfaces; } cLeaf_t; typedef struct cmodel_s { vec3_t mins, maxs; cLeaf_t leaf; // submodels don't reference the main tree } cmodel_t; typedef struct { cplane_t *plane; int surfaceFlags; int shaderNum; } cbrushside_t; typedef struct { int shaderNum; // the shader that determined the contents int contents; vec3_t bounds[2]; int numsides; cbrushside_t *sides; int checkcount; // to avoid repeated testings } cbrush_t; typedef struct { int checkcount; // to avoid repeated testings int surfaceFlags; int contents; struct patchCollide_s *pc; } cPatch_t; typedef struct { int floodnum; int floodvalid; } cArea_t; typedef struct { char name[MAX_QPATH]; int numShaders; dshader_t *shaders; int numBrushSides; cbrushside_t *brushsides; int numPlanes; cplane_t *planes; int numNodes; cNode_t *nodes; int numLeafs; cLeaf_t *leafs; int numLeafBrushes; int *leafbrushes; int numLeafSurfaces; int *leafsurfaces; int numSubModels; cmodel_t *cmodels; int numBrushes; cbrush_t *brushes; int numClusters; int clusterBytes; byte *visibility; qboolean vised; // if false, visibility is just a single cluster of ffs int numEntityChars; char *entityString; int numAreas; cArea_t *areas; int *areaPortals; // [ numAreas*numAreas ] reference counts int numSurfaces; cPatch_t **surfaces; // non-patches will be NULL int floodvalid; int checkcount; // incremented on each trace } clipMap_t; // keep 1/8 unit away to keep the position valid before network snapping // and to avoid various numeric issues #define SURFACE_CLIP_EPSILON (0.125) extern clipMap_t cm; extern int c_pointcontents; extern int c_traces, c_brush_traces, c_patch_traces; extern cvar_t *cm_noAreas; extern cvar_t *cm_noCurves; extern cvar_t *cm_playerCurveClip; // cm_test.c // Used for oriented capsule collision detection typedef struct { qboolean use; float radius; float halfheight; vec3_t offset; } sphere_t; typedef struct { vec3_t start; vec3_t end; vec3_t size[2]; // size of the box being swept through the model vec3_t offsets[8]; // [signbits][x] = either size[0][x] or size[1][x] float maxOffset; // longest corner length from origin vec3_t extents; // greatest of abs(size[0]) and abs(size[1]) vec3_t bounds[2]; // enclosing box of start and end surrounding by size vec3_t modelOrigin;// origin of the model tracing through int contents; // ored contents of the model tracing through qboolean isPoint; // optimized case trace_t trace; // returned from trace call sphere_t sphere; // sphere for oriendted capsule collision } traceWork_t; typedef struct leafList_s { int count; int maxcount; qboolean overflowed; int *list; vec3_t bounds[2]; int lastLeaf; // for overflows where each leaf can't be stored individually void (*storeLeafs)( struct leafList_s *ll, int nodenum ); } leafList_t; int CM_BoxBrushes( const vec3_t mins, const vec3_t maxs, cbrush_t **list, int listsize ); void CM_StoreLeafs( leafList_t *ll, int nodenum ); void CM_StoreBrushes( leafList_t *ll, int nodenum ); void CM_BoxLeafnums_r( leafList_t *ll, int nodenum ); cmodel_t *CM_ClipHandleToModel( clipHandle_t handle ); // cm_patch.c struct patchCollide_s *CM_GeneratePatchCollide( int width, int height, vec3_t *points ); void CM_TraceThroughPatchCollide( traceWork_t *tw, const struct patchCollide_s *pc ); qboolean CM_PositionTestInPatchCollide( traceWork_t *tw, const struct patchCollide_s *pc ); void CM_ClearLevelPatches( void ); ================================================ FILE: src/engine/qcommon/cm_patch.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "cm_local.h" #include "cm_patch.h" /* This file does not reference any globals, and has these entry points: void CM_ClearLevelPatches( void ); struct patchCollide_s *CM_GeneratePatchCollide( int width, int height, const vec3_t *points ); void CM_TraceThroughPatchCollide( traceWork_t *tw, const struct patchCollide_s *pc ); qboolean CM_PositionTestInPatchCollide( traceWork_t *tw, const struct patchCollide_s *pc ); void CM_DrawDebugSurface( void (*drawPoly)(int color, int numPoints, flaot *points) ); WARNING: this may misbehave with meshes that have rows or columns that only degenerate a few triangles. Completely degenerate rows and columns are handled properly. */ /* #define MAX_FACETS 1024 #define MAX_PATCH_PLANES 2048 typedef struct { float plane[4]; int signbits; // signx + (signy<<1) + (signz<<2), used as lookup during collision } patchPlane_t; typedef struct { int surfacePlane; int numBorders; // 3 or four + 6 axial bevels + 4 or 3 * 4 edge bevels int borderPlanes[4+6+16]; int borderInward[4+6+16]; qboolean borderNoAdjust[4+6+16]; } facet_t; typedef struct patchCollide_s { vec3_t bounds[2]; int numPlanes; // surface planes plus edge planes patchPlane_t *planes; int numFacets; facet_t *facets; } patchCollide_t; #define MAX_GRID_SIZE 129 typedef struct { int width; int height; qboolean wrapWidth; qboolean wrapHeight; vec3_t points[MAX_GRID_SIZE][MAX_GRID_SIZE]; // [width][height] } cGrid_t; #define SUBDIVIDE_DISTANCE 16 //4 // never more than this units away from curve #define PLANE_TRI_EPSILON 0.1 #define WRAP_POINT_EPSILON 0.1 */ int c_totalPatchBlocks; int c_totalPatchSurfaces; int c_totalPatchEdges; static const patchCollide_t *debugPatchCollide; static const facet_t *debugFacet; static qboolean debugBlock; static vec3_t debugBlockPoints[4]; /* ================= CM_ClearLevelPatches ================= */ void CM_ClearLevelPatches( void ) { debugPatchCollide = NULL; debugFacet = NULL; } /* ================= CM_SignbitsForNormal ================= */ static int CM_SignbitsForNormal( vec3_t normal ) { int bits, j; bits = 0; for (j=0 ; j<3 ; j++) { if ( normal[j] < 0 ) { bits |= 1<= SUBDIVIDE_DISTANCE); } /* =============== CM_Subdivide a, b, and c are control points. the subdivided sequence will be: a, out1, out2, out3, c =============== */ static void CM_Subdivide( vec3_t a, vec3_t b, vec3_t c, vec3_t out1, vec3_t out2, vec3_t out3 ) { int i; for ( i = 0 ; i < 3 ; i++ ) { out1[i] = 0.5 * (a[i] + b[i]); out3[i] = 0.5 * (b[i] + c[i]); out2[i] = 0.5 * (out1[i] + out3[i]); } } /* ================= CM_TransposeGrid Swaps the rows and columns in place ================= */ static void CM_TransposeGrid( cGrid_t *grid ) { int i, j, l; vec3_t temp; qboolean tempWrap; if ( grid->width > grid->height ) { for ( i = 0 ; i < grid->height ; i++ ) { for ( j = i + 1 ; j < grid->width ; j++ ) { if ( j < grid->height ) { // swap the value VectorCopy( grid->points[i][j], temp ); VectorCopy( grid->points[j][i], grid->points[i][j] ); VectorCopy( temp, grid->points[j][i] ); } else { // just copy VectorCopy( grid->points[j][i], grid->points[i][j] ); } } } } else { for ( i = 0 ; i < grid->width ; i++ ) { for ( j = i + 1 ; j < grid->height ; j++ ) { if ( j < grid->width ) { // swap the value VectorCopy( grid->points[j][i], temp ); VectorCopy( grid->points[i][j], grid->points[j][i] ); VectorCopy( temp, grid->points[i][j] ); } else { // just copy VectorCopy( grid->points[i][j], grid->points[j][i] ); } } } } l = grid->width; grid->width = grid->height; grid->height = l; tempWrap = grid->wrapWidth; grid->wrapWidth = grid->wrapHeight; grid->wrapHeight = tempWrap; } /* =================== CM_SetGridWrapWidth If the left and right columns are exactly equal, set grid->wrapWidth qtrue =================== */ static void CM_SetGridWrapWidth( cGrid_t *grid ) { int i, j; float d; for ( i = 0 ; i < grid->height ; i++ ) { for ( j = 0 ; j < 3 ; j++ ) { d = grid->points[0][i][j] - grid->points[grid->width-1][i][j]; if ( d < -WRAP_POINT_EPSILON || d > WRAP_POINT_EPSILON ) { break; } } if ( j != 3 ) { break; } } if ( i == grid->height ) { grid->wrapWidth = qtrue; } else { grid->wrapWidth = qfalse; } } /* ================= CM_SubdivideGridColumns Adds columns as necessary to the grid until all the aproximating points are within SUBDIVIDE_DISTANCE from the true curve ================= */ static void CM_SubdivideGridColumns( cGrid_t *grid ) { int i, j, k; for ( i = 0 ; i < grid->width - 2 ; ) { // grid->points[i][x] is an interpolating control point // grid->points[i+1][x] is an aproximating control point // grid->points[i+2][x] is an interpolating control point // // first see if we can collapse the aproximating collumn away // for ( j = 0 ; j < grid->height ; j++ ) { if ( CM_NeedsSubdivision( grid->points[i][j], grid->points[i+1][j], grid->points[i+2][j] ) ) { break; } } if ( j == grid->height ) { // all of the points were close enough to the linear midpoints // that we can collapse the entire column away for ( j = 0 ; j < grid->height ; j++ ) { // remove the column for ( k = i + 2 ; k < grid->width ; k++ ) { VectorCopy( grid->points[k][j], grid->points[k-1][j] ); } } grid->width--; // go to the next curve segment i++; continue; } // // we need to subdivide the curve // for ( j = 0 ; j < grid->height ; j++ ) { vec3_t prev, mid, next; // save the control points now VectorCopy( grid->points[i][j], prev ); VectorCopy( grid->points[i+1][j], mid ); VectorCopy( grid->points[i+2][j], next ); // make room for two additional columns in the grid // columns i+1 will be replaced, column i+2 will become i+4 // i+1, i+2, and i+3 will be generated for ( k = grid->width - 1 ; k > i + 1 ; k-- ) { VectorCopy( grid->points[k][j], grid->points[k+2][j] ); } // generate the subdivided points CM_Subdivide( prev, mid, next, grid->points[i+1][j], grid->points[i+2][j], grid->points[i+3][j] ); } grid->width += 2; // the new aproximating point at i+1 may need to be removed // or subdivided farther, so don't advance i } } /* ====================== CM_ComparePoints ====================== */ #define POINT_EPSILON 0.1 static qboolean CM_ComparePoints( float *a, float *b ) { float d; d = a[0] - b[0]; if ( d < -POINT_EPSILON || d > POINT_EPSILON ) { return qfalse; } d = a[1] - b[1]; if ( d < -POINT_EPSILON || d > POINT_EPSILON ) { return qfalse; } d = a[2] - b[2]; if ( d < -POINT_EPSILON || d > POINT_EPSILON ) { return qfalse; } return qtrue; } /* ================= CM_RemoveDegenerateColumns If there are any identical columns, remove them ================= */ static void CM_RemoveDegenerateColumns( cGrid_t *grid ) { int i, j, k; for ( i = 0 ; i < grid->width - 1 ; i++ ) { for ( j = 0 ; j < grid->height ; j++ ) { if ( !CM_ComparePoints( grid->points[i][j], grid->points[i+1][j] ) ) { break; } } if ( j != grid->height ) { continue; // not degenerate } for ( j = 0 ; j < grid->height ; j++ ) { // remove the column for ( k = i + 2 ; k < grid->width ; k++ ) { VectorCopy( grid->points[k][j], grid->points[k-1][j] ); } } grid->width--; // check against the next column i--; } } /* ================================================================================ PATCH COLLIDE GENERATION ================================================================================ */ static int numPlanes; static patchPlane_t planes[MAX_PATCH_PLANES]; static int numFacets; static facet_t facets[MAX_PATCH_PLANES]; //maybe MAX_FACETS ?? #define NORMAL_EPSILON 0.0001 #define DIST_EPSILON 0.02 /* ================== CM_PlaneEqual ================== */ int CM_PlaneEqual(patchPlane_t *p, float plane[4], int *flipped) { float invplane[4]; if ( fabs(p->plane[0] - plane[0]) < NORMAL_EPSILON && fabs(p->plane[1] - plane[1]) < NORMAL_EPSILON && fabs(p->plane[2] - plane[2]) < NORMAL_EPSILON && fabs(p->plane[3] - plane[3]) < DIST_EPSILON ) { *flipped = qfalse; return qtrue; } VectorNegate(plane, invplane); invplane[3] = -plane[3]; if ( fabs(p->plane[0] - invplane[0]) < NORMAL_EPSILON && fabs(p->plane[1] - invplane[1]) < NORMAL_EPSILON && fabs(p->plane[2] - invplane[2]) < NORMAL_EPSILON && fabs(p->plane[3] - invplane[3]) < DIST_EPSILON ) { *flipped = qtrue; return qtrue; } return qfalse; } /* ================== CM_SnapVector ================== */ void CM_SnapVector(vec3_t normal) { int i; for (i=0 ; i<3 ; i++) { if ( fabs(normal[i] - 1) < NORMAL_EPSILON ) { VectorClear (normal); normal[i] = 1; break; } if ( fabs(normal[i] - -1) < NORMAL_EPSILON ) { VectorClear (normal); normal[i] = -1; break; } } } /* ================== CM_FindPlane2 ================== */ int CM_FindPlane2(float plane[4], int *flipped) { int i; // see if the points are close enough to an existing plane for ( i = 0 ; i < numPlanes ; i++ ) { if (CM_PlaneEqual(&planes[i], plane, flipped)) return i; } // add a new plane if ( numPlanes == MAX_PATCH_PLANES ) { Com_Error( ERR_DROP, "MAX_PATCH_PLANES" ); } Vector4Copy( plane, planes[numPlanes].plane ); planes[numPlanes].signbits = CM_SignbitsForNormal( plane ); numPlanes++; *flipped = qfalse; return numPlanes-1; } /* ================== CM_FindPlane ================== */ static int CM_FindPlane( float *p1, float *p2, float *p3 ) { float plane[4]; int i; float d; if ( !CM_PlaneFromPoints( plane, p1, p2, p3 ) ) { return -1; } // see if the points are close enough to an existing plane for ( i = 0 ; i < numPlanes ; i++ ) { if ( DotProduct( plane, planes[i].plane ) < 0 ) { continue; // allow backwards planes? } d = DotProduct( p1, planes[i].plane ) - planes[i].plane[3]; if ( d < -PLANE_TRI_EPSILON || d > PLANE_TRI_EPSILON ) { continue; } d = DotProduct( p2, planes[i].plane ) - planes[i].plane[3]; if ( d < -PLANE_TRI_EPSILON || d > PLANE_TRI_EPSILON ) { continue; } d = DotProduct( p3, planes[i].plane ) - planes[i].plane[3]; if ( d < -PLANE_TRI_EPSILON || d > PLANE_TRI_EPSILON ) { continue; } // found it return i; } // add a new plane if ( numPlanes == MAX_PATCH_PLANES ) { Com_Error( ERR_DROP, "MAX_PATCH_PLANES" ); } Vector4Copy( plane, planes[numPlanes].plane ); planes[numPlanes].signbits = CM_SignbitsForNormal( plane ); numPlanes++; return numPlanes-1; } /* ================== CM_PointOnPlaneSide ================== */ static int CM_PointOnPlaneSide( float *p, int planeNum ) { float *plane; float d; if ( planeNum == -1 ) { return SIDE_ON; } plane = planes[ planeNum ].plane; d = DotProduct( p, plane ) - plane[3]; if ( d > PLANE_TRI_EPSILON ) { return SIDE_FRONT; } if ( d < -PLANE_TRI_EPSILON ) { return SIDE_BACK; } return SIDE_ON; } /* ================== CM_GridPlane ================== */ static int CM_GridPlane( int gridPlanes[MAX_GRID_SIZE][MAX_GRID_SIZE][2], int i, int j, int tri ) { int p; p = gridPlanes[i][j][tri]; if ( p != -1 ) { return p; } p = gridPlanes[i][j][!tri]; if ( p != -1 ) { return p; } // should never happen Com_Printf( "WARNING: CM_GridPlane unresolvable\n" ); return -1; } /* ================== CM_EdgePlaneNum ================== */ static int CM_EdgePlaneNum( cGrid_t *grid, int gridPlanes[MAX_GRID_SIZE][MAX_GRID_SIZE][2], int i, int j, int k ) { float *p1, *p2; vec3_t up; int p; switch ( k ) { case 0: // top border p1 = grid->points[i][j]; p2 = grid->points[i+1][j]; p = CM_GridPlane( gridPlanes, i, j, 0 ); VectorMA( p1, 4, planes[ p ].plane, up ); return CM_FindPlane( p1, p2, up ); case 2: // bottom border p1 = grid->points[i][j+1]; p2 = grid->points[i+1][j+1]; p = CM_GridPlane( gridPlanes, i, j, 1 ); VectorMA( p1, 4, planes[ p ].plane, up ); return CM_FindPlane( p2, p1, up ); case 3: // left border p1 = grid->points[i][j]; p2 = grid->points[i][j+1]; p = CM_GridPlane( gridPlanes, i, j, 1 ); VectorMA( p1, 4, planes[ p ].plane, up ); return CM_FindPlane( p2, p1, up ); case 1: // right border p1 = grid->points[i+1][j]; p2 = grid->points[i+1][j+1]; p = CM_GridPlane( gridPlanes, i, j, 0 ); VectorMA( p1, 4, planes[ p ].plane, up ); return CM_FindPlane( p1, p2, up ); case 4: // diagonal out of triangle 0 p1 = grid->points[i+1][j+1]; p2 = grid->points[i][j]; p = CM_GridPlane( gridPlanes, i, j, 0 ); VectorMA( p1, 4, planes[ p ].plane, up ); return CM_FindPlane( p1, p2, up ); case 5: // diagonal out of triangle 1 p1 = grid->points[i][j]; p2 = grid->points[i+1][j+1]; p = CM_GridPlane( gridPlanes, i, j, 1 ); VectorMA( p1, 4, planes[ p ].plane, up ); return CM_FindPlane( p1, p2, up ); } Com_Error( ERR_DROP, "CM_EdgePlaneNum: bad k" ); return -1; } /* =================== CM_SetBorderInward =================== */ static void CM_SetBorderInward( facet_t *facet, cGrid_t *grid, int gridPlanes[MAX_GRID_SIZE][MAX_GRID_SIZE][2], int i, int j, int which ) { int k, l; float *points[4]; int numPoints; switch ( which ) { case -1: points[0] = grid->points[i][j]; points[1] = grid->points[i+1][j]; points[2] = grid->points[i+1][j+1]; points[3] = grid->points[i][j+1]; numPoints = 4; break; case 0: points[0] = grid->points[i][j]; points[1] = grid->points[i+1][j]; points[2] = grid->points[i+1][j+1]; numPoints = 3; break; case 1: points[0] = grid->points[i+1][j+1]; points[1] = grid->points[i][j+1]; points[2] = grid->points[i][j]; numPoints = 3; break; default: Com_Error( ERR_FATAL, "CM_SetBorderInward: bad parameter" ); numPoints = 0; break; } for ( k = 0 ; k < facet->numBorders ; k++ ) { int front, back; front = 0; back = 0; for ( l = 0 ; l < numPoints ; l++ ) { int side; side = CM_PointOnPlaneSide( points[l], facet->borderPlanes[k] ); if ( side == SIDE_FRONT ) { front++; } if ( side == SIDE_BACK ) { back++; } } if ( front && !back ) { facet->borderInward[k] = qtrue; } else if ( back && !front ) { facet->borderInward[k] = qfalse; } else if ( !front && !back ) { // flat side border facet->borderPlanes[k] = -1; } else { // bisecting side border Com_DPrintf( "WARNING: CM_SetBorderInward: mixed plane sides\n" ); facet->borderInward[k] = qfalse; if ( !debugBlock ) { debugBlock = qtrue; VectorCopy( grid->points[i][j], debugBlockPoints[0] ); VectorCopy( grid->points[i+1][j], debugBlockPoints[1] ); VectorCopy( grid->points[i+1][j+1], debugBlockPoints[2] ); VectorCopy( grid->points[i][j+1], debugBlockPoints[3] ); } } } } /* ================== CM_ValidateFacet If the facet isn't bounded by its borders, we screwed up. ================== */ static qboolean CM_ValidateFacet( facet_t *facet ) { float plane[4]; int j; winding_t *w; vec3_t bounds[2]; if ( facet->surfacePlane == -1 ) { return qfalse; } Vector4Copy( planes[ facet->surfacePlane ].plane, plane ); w = BaseWindingForPlane( plane, plane[3] ); for ( j = 0 ; j < facet->numBorders && w ; j++ ) { if ( facet->borderPlanes[j] == -1 ) { return qfalse; } Vector4Copy( planes[ facet->borderPlanes[j] ].plane, plane ); if ( !facet->borderInward[j] ) { VectorSubtract( vec3_origin, plane, plane ); plane[3] = -plane[3]; } ChopWindingInPlace( &w, plane, plane[3], 0.1f ); } if ( !w ) { return qfalse; // winding was completely chopped away } // see if the facet is unreasonably large WindingBounds( w, bounds[0], bounds[1] ); FreeWinding( w ); for ( j = 0 ; j < 3 ; j++ ) { if ( bounds[1][j] - bounds[0][j] > MAX_MAP_BOUNDS ) { return qfalse; // we must be missing a plane } if ( bounds[0][j] >= MAX_MAP_BOUNDS ) { return qfalse; } if ( bounds[1][j] <= -MAX_MAP_BOUNDS ) { return qfalse; } } return qtrue; // winding is fine } /* ================== CM_AddFacetBevels ================== */ void CM_AddFacetBevels( facet_t *facet ) { int i, j, k, l; int axis, dir, order, flipped; float plane[4], d, newplane[4]; winding_t *w, *w2; vec3_t mins, maxs, vec, vec2; Vector4Copy( planes[ facet->surfacePlane ].plane, plane ); w = BaseWindingForPlane( plane, plane[3] ); for ( j = 0 ; j < facet->numBorders && w ; j++ ) { if (facet->borderPlanes[j] == facet->surfacePlane) continue; Vector4Copy( planes[ facet->borderPlanes[j] ].plane, plane ); if ( !facet->borderInward[j] ) { VectorSubtract( vec3_origin, plane, plane ); plane[3] = -plane[3]; } ChopWindingInPlace( &w, plane, plane[3], 0.1f ); } if ( !w ) { return; } WindingBounds(w, mins, maxs); // add the axial planes order = 0; for ( axis = 0 ; axis < 3 ; axis++ ) { for ( dir = -1 ; dir <= 1 ; dir += 2, order++ ) { VectorClear(plane); plane[axis] = dir; if (dir == 1) { plane[3] = maxs[axis]; } else { plane[3] = -mins[axis]; } //if it's the surface plane if (CM_PlaneEqual(&planes[facet->surfacePlane], plane, &flipped)) { continue; } // see if the plane is allready present for ( i = 0 ; i < facet->numBorders ; i++ ) { if (CM_PlaneEqual(&planes[facet->borderPlanes[i]], plane, &flipped)) break; } if ( i == facet->numBorders ) { if (facet->numBorders > 4 + 6 + 16) Com_Printf("ERROR: too many bevels\n"); facet->borderPlanes[facet->numBorders] = CM_FindPlane2(plane, &flipped); facet->borderNoAdjust[facet->numBorders] = (qboolean) 0; facet->borderInward[facet->numBorders] = flipped; facet->numBorders++; } } } // // add the edge bevels // // test the non-axial plane edges for ( j = 0 ; j < w->numpoints ; j++ ) { k = (j+1)%w->numpoints; VectorSubtract (w->p[j], w->p[k], vec); //if it's a degenerate edge if (VectorNormalize (vec) < 0.5) continue; CM_SnapVector(vec); for ( k = 0; k < 3 ; k++ ) if ( vec[k] == -1 || vec[k] == 1 ) break; // axial if ( k < 3 ) continue; // only test non-axial edges // try the six possible slanted axials from this edge for ( axis = 0 ; axis < 3 ; axis++ ) { for ( dir = -1 ; dir <= 1 ; dir += 2 ) { // construct a plane VectorClear (vec2); vec2[axis] = dir; CrossProduct (vec, vec2, plane); if (VectorNormalize (plane) < 0.5) continue; plane[3] = DotProduct (w->p[j], plane); // if all the points of the facet winding are // behind this plane, it is a proper edge bevel for ( l = 0 ; l < w->numpoints ; l++ ) { d = DotProduct (w->p[l], plane) - plane[3]; if (d > 0.1) break; // point in front } if ( l < w->numpoints ) continue; //if it's the surface plane if (CM_PlaneEqual(&planes[facet->surfacePlane], plane, &flipped)) { continue; } // see if the plane is allready present for ( i = 0 ; i < facet->numBorders ; i++ ) { if (CM_PlaneEqual(&planes[facet->borderPlanes[i]], plane, &flipped)) { break; } } if ( i == facet->numBorders ) { if (facet->numBorders > 4 + 6 + 16) Com_Printf("ERROR: too many bevels\n"); facet->borderPlanes[facet->numBorders] = CM_FindPlane2(plane, &flipped); for ( k = 0 ; k < facet->numBorders ; k++ ) { if (facet->borderPlanes[facet->numBorders] == facet->borderPlanes[k]) Com_Printf("WARNING: bevel plane already used\n"); } facet->borderNoAdjust[facet->numBorders] = (qboolean) 0; facet->borderInward[facet->numBorders] = flipped; // w2 = CopyWinding(w); Vector4Copy(planes[facet->borderPlanes[facet->numBorders]].plane, newplane); if (!facet->borderInward[facet->numBorders]) { VectorNegate(newplane, newplane); newplane[3] = -newplane[3]; } //end if ChopWindingInPlace( &w2, newplane, newplane[3], 0.1f ); if (!w2) { Com_DPrintf("WARNING: CM_AddFacetBevels... invalid bevel\n"); continue; } else { FreeWinding(w2); } // facet->numBorders++; //already got a bevel // break; } } } } FreeWinding( w ); #ifndef BSPC //add opposite plane facet->borderPlanes[facet->numBorders] = facet->surfacePlane; facet->borderNoAdjust[facet->numBorders] = (qboolean) 0; facet->borderInward[facet->numBorders] = qtrue; facet->numBorders++; #endif //BSPC } typedef enum { EN_TOP, EN_RIGHT, EN_BOTTOM, EN_LEFT } edgeName_t; /* ================== CM_PatchCollideFromGrid ================== */ static void CM_PatchCollideFromGrid( cGrid_t *grid, patchCollide_t *pf ) { int i, j; float *p1, *p2, *p3; MAC_STATIC int gridPlanes[MAX_GRID_SIZE][MAX_GRID_SIZE][2]; facet_t *facet; int borders[4]; int noAdjust[4]; numPlanes = 0; numFacets = 0; // find the planes for each triangle of the grid for ( i = 0 ; i < grid->width - 1 ; i++ ) { for ( j = 0 ; j < grid->height - 1 ; j++ ) { p1 = grid->points[i][j]; p2 = grid->points[i+1][j]; p3 = grid->points[i+1][j+1]; gridPlanes[i][j][0] = CM_FindPlane( p1, p2, p3 ); p1 = grid->points[i+1][j+1]; p2 = grid->points[i][j+1]; p3 = grid->points[i][j]; gridPlanes[i][j][1] = CM_FindPlane( p1, p2, p3 ); } } // create the borders for each facet for ( i = 0 ; i < grid->width - 1 ; i++ ) { for ( j = 0 ; j < grid->height - 1 ; j++ ) { borders[EN_TOP] = -1; if ( j > 0 ) { borders[EN_TOP] = gridPlanes[i][j-1][1]; } else if ( grid->wrapHeight ) { borders[EN_TOP] = gridPlanes[i][grid->height-2][1]; } noAdjust[EN_TOP] = ( borders[EN_TOP] == gridPlanes[i][j][0] ); if ( borders[EN_TOP] == -1 || noAdjust[EN_TOP] ) { borders[EN_TOP] = CM_EdgePlaneNum( grid, gridPlanes, i, j, 0 ); } borders[EN_BOTTOM] = -1; if ( j < grid->height - 2 ) { borders[EN_BOTTOM] = gridPlanes[i][j+1][0]; } else if ( grid->wrapHeight ) { borders[EN_BOTTOM] = gridPlanes[i][0][0]; } noAdjust[EN_BOTTOM] = ( borders[EN_BOTTOM] == gridPlanes[i][j][1] ); if ( borders[EN_BOTTOM] == -1 || noAdjust[EN_BOTTOM] ) { borders[EN_BOTTOM] = CM_EdgePlaneNum( grid, gridPlanes, i, j, 2 ); } borders[EN_LEFT] = -1; if ( i > 0 ) { borders[EN_LEFT] = gridPlanes[i-1][j][0]; } else if ( grid->wrapWidth ) { borders[EN_LEFT] = gridPlanes[grid->width-2][j][0]; } noAdjust[EN_LEFT] = ( borders[EN_LEFT] == gridPlanes[i][j][1] ); if ( borders[EN_LEFT] == -1 || noAdjust[EN_LEFT] ) { borders[EN_LEFT] = CM_EdgePlaneNum( grid, gridPlanes, i, j, 3 ); } borders[EN_RIGHT] = -1; if ( i < grid->width - 2 ) { borders[EN_RIGHT] = gridPlanes[i+1][j][1]; } else if ( grid->wrapWidth ) { borders[EN_RIGHT] = gridPlanes[0][j][1]; } noAdjust[EN_RIGHT] = ( borders[EN_RIGHT] == gridPlanes[i][j][0] ); if ( borders[EN_RIGHT] == -1 || noAdjust[EN_RIGHT] ) { borders[EN_RIGHT] = CM_EdgePlaneNum( grid, gridPlanes, i, j, 1 ); } if ( numFacets == MAX_FACETS ) { Com_Error( ERR_DROP, "MAX_FACETS" ); } facet = &facets[numFacets]; Com_Memset( facet, 0, sizeof( *facet ) ); if ( gridPlanes[i][j][0] == gridPlanes[i][j][1] ) { if ( gridPlanes[i][j][0] == -1 ) { continue; // degenrate } facet->surfacePlane = gridPlanes[i][j][0]; facet->numBorders = 4; facet->borderPlanes[0] = borders[EN_TOP]; facet->borderNoAdjust[0] = (qboolean) noAdjust[EN_TOP]; facet->borderPlanes[1] = borders[EN_RIGHT]; facet->borderNoAdjust[1] = (qboolean) noAdjust[EN_RIGHT]; facet->borderPlanes[2] = borders[EN_BOTTOM]; facet->borderNoAdjust[2] = (qboolean)noAdjust[EN_BOTTOM]; facet->borderPlanes[3] = borders[EN_LEFT]; facet->borderNoAdjust[3] = (qboolean) noAdjust[EN_LEFT]; CM_SetBorderInward( facet, grid, gridPlanes, i, j, -1 ); if ( CM_ValidateFacet( facet ) ) { CM_AddFacetBevels( facet ); numFacets++; } } else { // two seperate triangles facet->surfacePlane = gridPlanes[i][j][0]; facet->numBorders = 3; facet->borderPlanes[0] = borders[EN_TOP]; facet->borderNoAdjust[0] = (qboolean)noAdjust[EN_TOP]; facet->borderPlanes[1] = borders[EN_RIGHT]; facet->borderNoAdjust[1] = (qboolean)noAdjust[EN_RIGHT]; facet->borderPlanes[2] = gridPlanes[i][j][1]; if ( facet->borderPlanes[2] == -1 ) { facet->borderPlanes[2] = borders[EN_BOTTOM]; if ( facet->borderPlanes[2] == -1 ) { facet->borderPlanes[2] = CM_EdgePlaneNum( grid, gridPlanes, i, j, 4 ); } } CM_SetBorderInward( facet, grid, gridPlanes, i, j, 0 ); if ( CM_ValidateFacet( facet ) ) { CM_AddFacetBevels( facet ); numFacets++; } if ( numFacets == MAX_FACETS ) { Com_Error( ERR_DROP, "MAX_FACETS" ); } facet = &facets[numFacets]; Com_Memset( facet, 0, sizeof( *facet ) ); facet->surfacePlane = gridPlanes[i][j][1]; facet->numBorders = 3; facet->borderPlanes[0] = borders[EN_BOTTOM]; facet->borderNoAdjust[0] = (qboolean)noAdjust[EN_BOTTOM]; facet->borderPlanes[1] = borders[EN_LEFT]; facet->borderNoAdjust[1] = (qboolean)noAdjust[EN_LEFT]; facet->borderPlanes[2] = gridPlanes[i][j][0]; if ( facet->borderPlanes[2] == -1 ) { facet->borderPlanes[2] = borders[EN_TOP]; if ( facet->borderPlanes[2] == -1 ) { facet->borderPlanes[2] = CM_EdgePlaneNum( grid, gridPlanes, i, j, 5 ); } } CM_SetBorderInward( facet, grid, gridPlanes, i, j, 1 ); if ( CM_ValidateFacet( facet ) ) { CM_AddFacetBevels( facet ); numFacets++; } } } } // copy the results out pf->numPlanes = numPlanes; pf->numFacets = numFacets; pf->facets = (facet_t*) Hunk_Alloc( numFacets * sizeof( *pf->facets ), h_high ); Com_Memcpy( pf->facets, facets, numFacets * sizeof( *pf->facets ) ); pf->planes = (patchPlane_t*) Hunk_Alloc( numPlanes * sizeof( *pf->planes ), h_high ); Com_Memcpy( pf->planes, planes, numPlanes * sizeof( *pf->planes ) ); } /* =================== CM_GeneratePatchCollide Creates an internal structure that will be used to perform collision detection with a patch mesh. Points is packed as concatenated rows. =================== */ struct patchCollide_s *CM_GeneratePatchCollide( int width, int height, vec3_t *points ) { patchCollide_t *pf; MAC_STATIC cGrid_t grid; int i, j; if ( width <= 2 || height <= 2 || !points ) { Com_Error( ERR_DROP, "CM_GeneratePatchFacets: bad parameters: (%i, %i, %p)", width, height, points ); } if ( !(width & 1) || !(height & 1) ) { Com_Error( ERR_DROP, "CM_GeneratePatchFacets: even sizes are invalid for quadratic meshes" ); } if ( width > MAX_GRID_SIZE || height > MAX_GRID_SIZE ) { Com_Error( ERR_DROP, "CM_GeneratePatchFacets: source is > MAX_GRID_SIZE" ); } // build a grid grid.width = width; grid.height = height; grid.wrapWidth = qfalse; grid.wrapHeight = qfalse; for ( i = 0 ; i < width ; i++ ) { for ( j = 0 ; j < height ; j++ ) { VectorCopy( points[j*width + i], grid.points[i][j] ); } } // subdivide the grid CM_SetGridWrapWidth( &grid ); CM_SubdivideGridColumns( &grid ); CM_RemoveDegenerateColumns( &grid ); CM_TransposeGrid( &grid ); CM_SetGridWrapWidth( &grid ); CM_SubdivideGridColumns( &grid ); CM_RemoveDegenerateColumns( &grid ); // we now have a grid of points exactly on the curve // the aproximate surface defined by these points will be // collided against pf = (patchCollide_t*) Hunk_Alloc( sizeof( *pf ), h_high ); ClearBounds( pf->bounds[0], pf->bounds[1] ); for ( i = 0 ; i < grid.width ; i++ ) { for ( j = 0 ; j < grid.height ; j++ ) { AddPointToBounds( grid.points[i][j], pf->bounds[0], pf->bounds[1] ); } } c_totalPatchBlocks += ( grid.width - 1 ) * ( grid.height - 1 ); // generate a bsp tree for the surface CM_PatchCollideFromGrid( &grid, pf ); // expand by one unit for epsilon purposes pf->bounds[0][0] -= 1; pf->bounds[0][1] -= 1; pf->bounds[0][2] -= 1; pf->bounds[1][0] += 1; pf->bounds[1][1] += 1; pf->bounds[1][2] += 1; return pf; } /* ================================================================================ TRACE TESTING ================================================================================ */ /* ==================== CM_TracePointThroughPatchCollide special case for point traces because the patch collide "brushes" have no volume ==================== */ void CM_TracePointThroughPatchCollide( traceWork_t *tw, const struct patchCollide_s *pc ) { qboolean frontFacing[MAX_PATCH_PLANES]; float intersection[MAX_PATCH_PLANES]; float intersect; const patchPlane_t *planes; const facet_t *facet; int i, j, k; float offset; float d1, d2; #ifndef BSPC static cvar_t *cv; #endif //BSPC #ifndef BSPC if ( !cm_playerCurveClip->integer || !tw->isPoint ) { return; } #endif // determine the trace's relationship to all planes planes = pc->planes; for ( i = 0 ; i < pc->numPlanes ; i++, planes++ ) { offset = DotProduct( tw->offsets[ planes->signbits ], planes->plane ); d1 = DotProduct( tw->start, planes->plane ) - planes->plane[3] + offset; d2 = DotProduct( tw->end, planes->plane ) - planes->plane[3] + offset; if ( d1 <= 0 ) { frontFacing[i] = qfalse; } else { frontFacing[i] = qtrue; } if ( d1 == d2 ) { intersection[i] = 99999; } else { intersection[i] = d1 / ( d1 - d2 ); if ( intersection[i] <= 0 ) { intersection[i] = 99999; } } } // see if any of the surface planes are intersected facet = pc->facets; for ( i = 0 ; i < pc->numFacets ; i++, facet++ ) { if ( !frontFacing[facet->surfacePlane] ) { continue; } intersect = intersection[facet->surfacePlane]; if ( intersect < 0 ) { continue; // surface is behind the starting point } if ( intersect > tw->trace.fraction ) { continue; // already hit something closer } for ( j = 0 ; j < facet->numBorders ; j++ ) { k = facet->borderPlanes[j]; if ( frontFacing[k] ^ facet->borderInward[j] ) { if ( intersection[k] > intersect ) { break; } } else { if ( intersection[k] < intersect ) { break; } } } if ( j == facet->numBorders ) { // we hit this facet #ifndef BSPC if (!cv) { cv = Cvar_Get( "r_debugSurfaceUpdate", "1", 0 ); } if (cv->integer) { debugPatchCollide = pc; debugFacet = facet; } #endif //BSPC planes = &pc->planes[facet->surfacePlane]; // calculate intersection with a slight pushoff offset = DotProduct( tw->offsets[ planes->signbits ], planes->plane ); d1 = DotProduct( tw->start, planes->plane ) - planes->plane[3] + offset; d2 = DotProduct( tw->end, planes->plane ) - planes->plane[3] + offset; tw->trace.fraction = ( d1 - SURFACE_CLIP_EPSILON ) / ( d1 - d2 ); if ( tw->trace.fraction < 0 ) { tw->trace.fraction = 0; } VectorCopy( planes->plane, tw->trace.plane.normal ); tw->trace.plane.dist = planes->plane[3]; } } } /* ==================== CM_CheckFacetPlane ==================== */ int CM_CheckFacetPlane(float *plane, vec3_t start, vec3_t end, float *enterFrac, float *leaveFrac, int *hit) { float d1, d2, f; *hit = qfalse; d1 = DotProduct( start, plane ) - plane[3]; d2 = DotProduct( end, plane ) - plane[3]; // if completely in front of face, no intersection with the entire facet if (d1 > 0 && ( d2 >= SURFACE_CLIP_EPSILON || d2 >= d1 ) ) { return qfalse; } // if it doesn't cross the plane, the plane isn't relevent if (d1 <= 0 && d2 <= 0 ) { return qtrue; } // crosses face if (d1 > d2) { // enter f = (d1-SURFACE_CLIP_EPSILON) / (d1-d2); if ( f < 0 ) { f = 0; } //always favor previous plane hits and thus also the surface plane hit if (f > *enterFrac) { *enterFrac = f; *hit = qtrue; } } else { // leave f = (d1+SURFACE_CLIP_EPSILON) / (d1-d2); if ( f > 1 ) { f = 1; } if (f < *leaveFrac) { *leaveFrac = f; } } return qtrue; } /* ==================== CM_TraceThroughPatchCollide ==================== */ void CM_TraceThroughPatchCollide( traceWork_t *tw, const struct patchCollide_s *pc ) { int i, j, hit, hitnum; float offset, enterFrac, leaveFrac, t; patchPlane_t *planes; facet_t *facet; float plane[4], bestplane[4]; vec3_t startp, endp; #ifndef BSPC static cvar_t *cv; #endif //BSPC if (tw->isPoint) { CM_TracePointThroughPatchCollide( tw, pc ); return; } facet = pc->facets; for ( i = 0 ; i < pc->numFacets ; i++, facet++ ) { enterFrac = -1.0; leaveFrac = 1.0; hitnum = -1; // planes = &pc->planes[ facet->surfacePlane ]; VectorCopy(planes->plane, plane); plane[3] = planes->plane[3]; if ( tw->sphere.use ) { // adjust the plane distance apropriately for radius plane[3] += tw->sphere.radius; // find the closest point on the capsule to the plane t = DotProduct( plane, tw->sphere.offset ); if ( t > 0.0f ) { VectorSubtract( tw->start, tw->sphere.offset, startp ); VectorSubtract( tw->end, tw->sphere.offset, endp ); } else { VectorAdd( tw->start, tw->sphere.offset, startp ); VectorAdd( tw->end, tw->sphere.offset, endp ); } } else { offset = DotProduct( tw->offsets[ planes->signbits ], plane); plane[3] -= offset; VectorCopy( tw->start, startp ); VectorCopy( tw->end, endp ); } if (!CM_CheckFacetPlane(plane, startp, endp, &enterFrac, &leaveFrac, &hit)) { continue; } if (hit) { Vector4Copy(plane, bestplane); } for ( j = 0; j < facet->numBorders; j++ ) { planes = &pc->planes[ facet->borderPlanes[j] ]; if (facet->borderInward[j]) { VectorNegate(planes->plane, plane); plane[3] = -planes->plane[3]; } else { VectorCopy(planes->plane, plane); plane[3] = planes->plane[3]; } if ( tw->sphere.use ) { // adjust the plane distance apropriately for radius plane[3] += tw->sphere.radius; // find the closest point on the capsule to the plane t = DotProduct( plane, tw->sphere.offset ); if ( t > 0.0f ) { VectorSubtract( tw->start, tw->sphere.offset, startp ); VectorSubtract( tw->end, tw->sphere.offset, endp ); } else { VectorAdd( tw->start, tw->sphere.offset, startp ); VectorAdd( tw->end, tw->sphere.offset, endp ); } } else { // NOTE: this works even though the plane might be flipped because the bbox is centered offset = DotProduct( tw->offsets[ planes->signbits ], plane); plane[3] += fabs(offset); VectorCopy( tw->start, startp ); VectorCopy( tw->end, endp ); } if (!CM_CheckFacetPlane(plane, startp, endp, &enterFrac, &leaveFrac, &hit)) { break; } if (hit) { hitnum = j; Vector4Copy(plane, bestplane); } } if (j < facet->numBorders) continue; //never clip against the back side if (hitnum == facet->numBorders - 1) continue; if (enterFrac < leaveFrac && enterFrac >= 0) { if (enterFrac < tw->trace.fraction) { if (enterFrac < 0) { enterFrac = 0; } #ifndef BSPC if (!cv) { cv = Cvar_Get( "r_debugSurfaceUpdate", "1", 0 ); } if (cv && cv->integer) { debugPatchCollide = pc; debugFacet = facet; } #endif //BSPC tw->trace.fraction = enterFrac; VectorCopy( bestplane, tw->trace.plane.normal ); tw->trace.plane.dist = bestplane[3]; } } } } /* ======================================================================= POSITION TEST ======================================================================= */ /* ==================== CM_PositionTestInPatchCollide ==================== */ qboolean CM_PositionTestInPatchCollide( traceWork_t *tw, const struct patchCollide_s *pc ) { int i, j; float offset, t; patchPlane_t *planes; facet_t *facet; float plane[4]; vec3_t startp; if (tw->isPoint) { return qfalse; } // facet = pc->facets; for ( i = 0 ; i < pc->numFacets ; i++, facet++ ) { planes = &pc->planes[ facet->surfacePlane ]; VectorCopy(planes->plane, plane); plane[3] = planes->plane[3]; if ( tw->sphere.use ) { // adjust the plane distance apropriately for radius plane[3] += tw->sphere.radius; // find the closest point on the capsule to the plane t = DotProduct( plane, tw->sphere.offset ); if ( t > 0 ) { VectorSubtract( tw->start, tw->sphere.offset, startp ); } else { VectorAdd( tw->start, tw->sphere.offset, startp ); } } else { offset = DotProduct( tw->offsets[ planes->signbits ], plane); plane[3] -= offset; VectorCopy( tw->start, startp ); } if ( DotProduct( plane, startp ) - plane[3] > 0.0f ) { continue; } for ( j = 0; j < facet->numBorders; j++ ) { planes = &pc->planes[ facet->borderPlanes[j] ]; if (facet->borderInward[j]) { VectorNegate(planes->plane, plane); plane[3] = -planes->plane[3]; } else { VectorCopy(planes->plane, plane); plane[3] = planes->plane[3]; } if ( tw->sphere.use ) { // adjust the plane distance apropriately for radius plane[3] += tw->sphere.radius; // find the closest point on the capsule to the plane t = DotProduct( plane, tw->sphere.offset ); if ( t > 0.0f ) { VectorSubtract( tw->start, tw->sphere.offset, startp ); } else { VectorAdd( tw->start, tw->sphere.offset, startp ); } } else { // NOTE: this works even though the plane might be flipped because the bbox is centered offset = DotProduct( tw->offsets[ planes->signbits ], plane); plane[3] += fabs(offset); VectorCopy( tw->start, startp ); } if ( DotProduct( plane, startp ) - plane[3] > 0.0f ) { break; } } if (j < facet->numBorders) { continue; } // inside this patch facet return qtrue; } return qfalse; } /* ======================================================================= DEBUGGING ======================================================================= */ /* ================== CM_DrawDebugSurface Called from the renderer ================== */ #ifndef BSPC void BotDrawDebugPolygons(void (*drawPoly)(int color, int numPoints, float *points), int value); #endif void CM_DrawDebugSurface( void (*drawPoly)(int color, int numPoints, float *points) ) { static cvar_t *cv; #ifndef BSPC static cvar_t *cv2; #endif const patchCollide_t *pc; facet_t *facet; winding_t *w; int i, j, k, n; int curplanenum, planenum, curinward, inward; float plane[4]; vec3_t mins = {-15, -15, -28}, maxs = {15, 15, 28}; //vec3_t mins = {0, 0, 0}, maxs = {0, 0, 0}; vec3_t v1, v2; #ifndef BSPC if ( !cv2 ) { cv2 = Cvar_Get( "r_debugSurface", "0", 0 ); } if (cv2->integer != 1) { BotDrawDebugPolygons(drawPoly, cv2->integer); return; } #endif if ( !debugPatchCollide ) { return; } #ifndef BSPC if ( !cv ) { cv = Cvar_Get( "cm_debugSize", "2", 0 ); } #endif pc = debugPatchCollide; for ( i = 0, facet = pc->facets ; i < pc->numFacets ; i++, facet++ ) { for ( k = 0 ; k < facet->numBorders + 1; k++ ) { // if (k < facet->numBorders) { planenum = facet->borderPlanes[k]; inward = facet->borderInward[k]; } else { planenum = facet->surfacePlane; inward = qfalse; //continue; } Vector4Copy( pc->planes[ planenum ].plane, plane ); //planenum = facet->surfacePlane; if ( inward ) { VectorSubtract( vec3_origin, plane, plane ); plane[3] = -plane[3]; } plane[3] += cv->value; //* for (n = 0; n < 3; n++) { if (plane[n] > 0) v1[n] = maxs[n]; else v1[n] = mins[n]; } //end for VectorNegate(plane, v2); plane[3] += fabs(DotProduct(v1, v2)); //*/ w = BaseWindingForPlane( plane, plane[3] ); for ( j = 0 ; j < facet->numBorders + 1 && w; j++ ) { // if (j < facet->numBorders) { curplanenum = facet->borderPlanes[j]; curinward = facet->borderInward[j]; } else { curplanenum = facet->surfacePlane; curinward = qfalse; //continue; } // if (curplanenum == planenum) continue; Vector4Copy( pc->planes[ curplanenum ].plane, plane ); if ( !curinward ) { VectorSubtract( vec3_origin, plane, plane ); plane[3] = -plane[3]; } // if ( !facet->borderNoAdjust[j] ) { plane[3] -= cv->value; // } for (n = 0; n < 3; n++) { if (plane[n] > 0) v1[n] = maxs[n]; else v1[n] = mins[n]; } //end for VectorNegate(plane, v2); plane[3] -= fabs(DotProduct(v1, v2)); ChopWindingInPlace( &w, plane, plane[3], 0.1f ); } if ( w ) { if ( facet == debugFacet ) { drawPoly( 4, w->numpoints, w->p[0] ); //Com_Printf("blue facet has %d border planes\n", facet->numBorders); } else { drawPoly( 1, w->numpoints, w->p[0] ); } FreeWinding( w ); } else Com_Printf("winding chopped away by border planes\n"); } } // draw the debug block { vec3_t v[3]; VectorCopy( debugBlockPoints[0], v[0] ); VectorCopy( debugBlockPoints[1], v[1] ); VectorCopy( debugBlockPoints[2], v[2] ); drawPoly( 2, 3, v[0] ); VectorCopy( debugBlockPoints[2], v[0] ); VectorCopy( debugBlockPoints[3], v[1] ); VectorCopy( debugBlockPoints[0], v[2] ); drawPoly( 2, 3, v[0] ); } #if 0 vec3_t v[4]; v[0][0] = pc->bounds[1][0]; v[0][1] = pc->bounds[1][1]; v[0][2] = pc->bounds[1][2]; v[1][0] = pc->bounds[1][0]; v[1][1] = pc->bounds[0][1]; v[1][2] = pc->bounds[1][2]; v[2][0] = pc->bounds[0][0]; v[2][1] = pc->bounds[0][1]; v[2][2] = pc->bounds[1][2]; v[3][0] = pc->bounds[0][0]; v[3][1] = pc->bounds[1][1]; v[3][2] = pc->bounds[1][2]; drawPoly( 4, v[0] ); #endif } ================================================ FILE: src/engine/qcommon/cm_patch.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ //#define CULL_BBOX /* This file does not reference any globals, and has these entry points: void CM_ClearLevelPatches( void ); struct patchCollide_s *CM_GeneratePatchCollide( int width, int height, const vec3_t *points ); void CM_TraceThroughPatchCollide( traceWork_t *tw, const struct patchCollide_s *pc ); qboolean CM_PositionTestInPatchCollide( traceWork_t *tw, const struct patchCollide_s *pc ); void CM_DrawDebugSurface( void (*drawPoly)(int color, int numPoints, flaot *points) ); Issues for collision against curved surfaces: Surface edges need to be handled differently than surface planes Plane expansion causes raw surfaces to expand past expanded bounding box Position test of a volume against a surface is tricky. Position test of a point against a surface is not well defined, because the surface has no volume. Tracing leading edge points instead of volumes? Position test by tracing corner to corner? (8*7 traces -- ouch) coplanar edges triangulated patches degenerate patches endcaps degenerate WARNING: this may misbehave with meshes that have rows or columns that only degenerate a few triangles. Completely degenerate rows and columns are handled properly. */ #define MAX_FACETS 1024 #define MAX_PATCH_PLANES 2048 typedef struct { float plane[4]; int signbits; // signx + (signy<<1) + (signz<<2), used as lookup during collision } patchPlane_t; typedef struct { int surfacePlane; int numBorders; // 3 or four + 6 axial bevels + 4 or 3 * 4 edge bevels int borderPlanes[4+6+16]; int borderInward[4+6+16]; qboolean borderNoAdjust[4+6+16]; } facet_t; typedef struct patchCollide_s { vec3_t bounds[2]; int numPlanes; // surface planes plus edge planes patchPlane_t *planes; int numFacets; facet_t *facets; } patchCollide_t; #define MAX_GRID_SIZE 129 typedef struct { int width; int height; qboolean wrapWidth; qboolean wrapHeight; vec3_t points[MAX_GRID_SIZE][MAX_GRID_SIZE]; // [width][height] } cGrid_t; #define SUBDIVIDE_DISTANCE 16 //4 // never more than this units away from curve #define PLANE_TRI_EPSILON 0.1 #define WRAP_POINT_EPSILON 0.1 struct patchCollide_s *CM_GeneratePatchCollide( int width, int height, vec3_t *points ); ================================================ FILE: src/engine/qcommon/cm_polylib.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // this is only used for visualization tools in cm_ debug functions #include "cm_local.h" // counters are only bumped when running single threaded, // because they are an awefull coherence problem int c_active_windings; int c_peak_windings; int c_winding_allocs; int c_winding_points; void pw(winding_t *w) { int i; for (i=0 ; inumpoints ; i++) printf ("(%5.1f, %5.1f, %5.1f)\n",w->p[i][0], w->p[i][1],w->p[i][2]); } /* ============= AllocWinding ============= */ winding_t *AllocWinding (int points) { winding_t *w; int s; c_winding_allocs++; c_winding_points += points; c_active_windings++; if (c_active_windings > c_peak_windings) c_peak_windings = c_active_windings; s = sizeof(vec_t)*3*points + sizeof(int); w = (winding_t*) Z_Malloc (s); Com_Memset (w, 0, s); return w; } void FreeWinding (winding_t *w) { if (*(unsigned *)w == 0xdeaddead) Com_Error (ERR_FATAL, "FreeWinding: freed a freed winding"); *(unsigned *)w = 0xdeaddead; c_active_windings--; Z_Free (w); } /* ============ RemoveColinearPoints ============ */ int c_removed; void RemoveColinearPoints (winding_t *w) { int i, j, k; vec3_t v1, v2; int nump; vec3_t p[MAX_POINTS_ON_WINDING]; nump = 0; for (i=0 ; inumpoints ; i++) { j = (i+1)%w->numpoints; k = (i+w->numpoints-1)%w->numpoints; VectorSubtract (w->p[j], w->p[i], v1); VectorSubtract (w->p[i], w->p[k], v2); VectorNormalize2(v1,v1); VectorNormalize2(v2,v2); if (DotProduct(v1, v2) < 0.999) { VectorCopy (w->p[i], p[nump]); nump++; } } if (nump == w->numpoints) return; c_removed += w->numpoints - nump; w->numpoints = nump; Com_Memcpy (w->p, p, nump*sizeof(p[0])); } /* ============ WindingPlane ============ */ void WindingPlane (winding_t *w, vec3_t normal, vec_t *dist) { vec3_t v1, v2; VectorSubtract (w->p[1], w->p[0], v1); VectorSubtract (w->p[2], w->p[0], v2); CrossProduct (v2, v1, normal); VectorNormalize2(normal, normal); *dist = DotProduct (w->p[0], normal); } /* ============= WindingArea ============= */ vec_t WindingArea (winding_t *w) { int i; vec3_t d1, d2, cross; vec_t total; total = 0; for (i=2 ; inumpoints ; i++) { VectorSubtract (w->p[i-1], w->p[0], d1); VectorSubtract (w->p[i], w->p[0], d2); CrossProduct (d1, d2, cross); total += 0.5 * VectorLength ( cross ); } return total; } /* ============= WindingBounds ============= */ void WindingBounds (winding_t *w, vec3_t mins, vec3_t maxs) { vec_t v; int i,j; mins[0] = mins[1] = mins[2] = MAX_MAP_BOUNDS; maxs[0] = maxs[1] = maxs[2] = -MAX_MAP_BOUNDS; for (i=0 ; inumpoints ; i++) { for (j=0 ; j<3 ; j++) { v = w->p[i][j]; if (v < mins[j]) mins[j] = v; if (v > maxs[j]) maxs[j] = v; } } } /* ============= WindingCenter ============= */ void WindingCenter (winding_t *w, vec3_t center) { int i; float scale; VectorCopy (vec3_origin, center); for (i=0 ; inumpoints ; i++) VectorAdd (w->p[i], center, center); scale = 1.0/w->numpoints; VectorScale (center, scale, center); } /* ================= BaseWindingForPlane ================= */ winding_t *BaseWindingForPlane (vec3_t normal, vec_t dist) { int i, x; vec_t max, v; vec3_t org, vright, vup; winding_t *w; // find the major axis max = -MAX_MAP_BOUNDS; x = -1; for (i=0 ; i<3; i++) { v = fabs(normal[i]); if (v > max) { x = i; max = v; } } if (x==-1) Com_Error (ERR_DROP, "BaseWindingForPlane: no axis found"); VectorCopy (vec3_origin, vup); switch (x) { case 0: case 1: vup[2] = 1; break; case 2: vup[0] = 1; break; } v = DotProduct (vup, normal); VectorMA (vup, -v, normal, vup); VectorNormalize2(vup, vup); VectorScale (normal, dist, org); CrossProduct (vup, normal, vright); VectorScale (vup, MAX_MAP_BOUNDS, vup); VectorScale (vright, MAX_MAP_BOUNDS, vright); // project a really big axis aligned box onto the plane w = AllocWinding (4); VectorSubtract (org, vright, w->p[0]); VectorAdd (w->p[0], vup, w->p[0]); VectorAdd (org, vright, w->p[1]); VectorAdd (w->p[1], vup, w->p[1]); VectorAdd (org, vright, w->p[2]); VectorSubtract (w->p[2], vup, w->p[2]); VectorSubtract (org, vright, w->p[3]); VectorSubtract (w->p[3], vup, w->p[3]); w->numpoints = 4; return w; } /* ================== CopyWinding ================== */ winding_t *CopyWinding (winding_t *w) { int size; winding_t *c; c = AllocWinding (w->numpoints); size = (int)(intptr_t)((winding_t *)0)->p[w->numpoints]; Com_Memcpy (c, w, size); return c; } /* ================== ReverseWinding ================== */ winding_t *ReverseWinding (winding_t *w) { int i; winding_t *c; c = AllocWinding (w->numpoints); for (i=0 ; inumpoints ; i++) { VectorCopy (w->p[w->numpoints-1-i], c->p[i]); } c->numpoints = w->numpoints; return c; } /* ============= ClipWindingEpsilon ============= */ void ClipWindingEpsilon (winding_t *in, vec3_t normal, vec_t dist, vec_t epsilon, winding_t **front, winding_t **back) { vec_t dists[MAX_POINTS_ON_WINDING+4]; int sides[MAX_POINTS_ON_WINDING+4]; int counts[3]; static vec_t dot; // VC 4.2 optimizer bug if not static int i, j; vec_t *p1, *p2; vec3_t mid; winding_t *f, *b; int maxpts; counts[0] = counts[1] = counts[2] = 0; // determine sides for each point for (i=0 ; inumpoints ; i++) { dot = DotProduct (in->p[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]; *front = *back = NULL; if (!counts[0]) { *back = CopyWinding (in); return; } if (!counts[1]) { *front = CopyWinding (in); return; } maxpts = in->numpoints+4; // cant use counts[0]+2 because // of fp grouping errors *front = f = AllocWinding (maxpts); *back = b = AllocWinding (maxpts); for (i=0 ; inumpoints ; i++) { p1 = in->p[i]; if (sides[i] == SIDE_ON) { VectorCopy (p1, f->p[f->numpoints]); f->numpoints++; VectorCopy (p1, b->p[b->numpoints]); b->numpoints++; continue; } if (sides[i] == SIDE_FRONT) { VectorCopy (p1, f->p[f->numpoints]); f->numpoints++; } if (sides[i] == SIDE_BACK) { VectorCopy (p1, b->p[b->numpoints]); b->numpoints++; } if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) continue; // generate a split point p2 = in->p[(i+1)%in->numpoints]; dot = dists[i] / (dists[i]-dists[i+1]); for (j=0 ; j<3 ; j++) { // avoid round off error when possible if (normal[j] == 1) mid[j] = dist; else if (normal[j] == -1) mid[j] = -dist; else mid[j] = p1[j] + dot*(p2[j]-p1[j]); } VectorCopy (mid, f->p[f->numpoints]); f->numpoints++; VectorCopy (mid, b->p[b->numpoints]); b->numpoints++; } if (f->numpoints > maxpts || b->numpoints > maxpts) Com_Error (ERR_DROP, "ClipWinding: points exceeded estimate"); if (f->numpoints > MAX_POINTS_ON_WINDING || b->numpoints > MAX_POINTS_ON_WINDING) Com_Error (ERR_DROP, "ClipWinding: MAX_POINTS_ON_WINDING"); } /* ============= ChopWindingInPlace ============= */ void ChopWindingInPlace (winding_t **inout, vec3_t normal, vec_t dist, vec_t epsilon) { winding_t *in; vec_t dists[MAX_POINTS_ON_WINDING+4]; int sides[MAX_POINTS_ON_WINDING+4]; int counts[3]; static vec_t dot; // VC 4.2 optimizer bug if not static int i, j; vec_t *p1, *p2; vec3_t mid; winding_t *f; int maxpts; in = *inout; counts[0] = counts[1] = counts[2] = 0; // determine sides for each point for (i=0 ; inumpoints ; i++) { dot = DotProduct (in->p[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]; if (!counts[0]) { FreeWinding (in); *inout = NULL; return; } if (!counts[1]) return; // inout stays the same maxpts = in->numpoints+4; // cant use counts[0]+2 because // of fp grouping errors f = AllocWinding (maxpts); for (i=0 ; inumpoints ; i++) { p1 = in->p[i]; if (sides[i] == SIDE_ON) { VectorCopy (p1, f->p[f->numpoints]); f->numpoints++; continue; } if (sides[i] == SIDE_FRONT) { VectorCopy (p1, f->p[f->numpoints]); f->numpoints++; } if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) continue; // generate a split point p2 = in->p[(i+1)%in->numpoints]; dot = dists[i] / (dists[i]-dists[i+1]); for (j=0 ; j<3 ; j++) { // avoid round off error when possible if (normal[j] == 1) mid[j] = dist; else if (normal[j] == -1) mid[j] = -dist; else mid[j] = p1[j] + dot*(p2[j]-p1[j]); } VectorCopy (mid, f->p[f->numpoints]); f->numpoints++; } if (f->numpoints > maxpts) Com_Error (ERR_DROP, "ClipWinding: points exceeded estimate"); if (f->numpoints > MAX_POINTS_ON_WINDING) Com_Error (ERR_DROP, "ClipWinding: MAX_POINTS_ON_WINDING"); FreeWinding (in); *inout = f; } /* ================= ChopWinding Returns the fragment of in that is on the front side of the cliping plane. The original is freed. ================= */ winding_t *ChopWinding (winding_t *in, vec3_t normal, vec_t dist) { winding_t *f, *b; ClipWindingEpsilon (in, normal, dist, ON_EPSILON, &f, &b); FreeWinding (in); if (b) FreeWinding (b); return f; } /* ================= CheckWinding ================= */ void CheckWinding (winding_t *w) { int i, j; vec_t *p1, *p2; vec_t d, edgedist; vec3_t dir, edgenormal, facenormal; vec_t area; vec_t facedist; if (w->numpoints < 3) Com_Error (ERR_DROP, "CheckWinding: %i points",w->numpoints); area = WindingArea(w); if (area < 1) Com_Error (ERR_DROP, "CheckWinding: %f area", area); WindingPlane (w, facenormal, &facedist); for (i=0 ; inumpoints ; i++) { p1 = w->p[i]; for (j=0 ; j<3 ; j++) if (p1[j] > MAX_MAP_BOUNDS || p1[j] < -MAX_MAP_BOUNDS) Com_Error (ERR_DROP, "CheckFace: BUGUS_RANGE: %f",p1[j]); j = i+1 == w->numpoints ? 0 : i+1; // check the point is on the face plane d = DotProduct (p1, facenormal) - facedist; if (d < -ON_EPSILON || d > ON_EPSILON) Com_Error (ERR_DROP, "CheckWinding: point off plane"); // check the edge isnt degenerate p2 = w->p[j]; VectorSubtract (p2, p1, dir); if (VectorLength (dir) < ON_EPSILON) Com_Error (ERR_DROP, "CheckWinding: degenerate edge"); CrossProduct (facenormal, dir, edgenormal); VectorNormalize2 (edgenormal, edgenormal); edgedist = DotProduct (p1, edgenormal); edgedist += ON_EPSILON; // all other points must be on front side for (j=0 ; jnumpoints ; j++) { if (j == i) continue; d = DotProduct (w->p[j], edgenormal); if (d > edgedist) Com_Error (ERR_DROP, "CheckWinding: non-convex"); } } } /* ============ WindingOnPlaneSide ============ */ int WindingOnPlaneSide (winding_t *w, vec3_t normal, vec_t dist) { qboolean front, back; int i; vec_t d; front = qfalse; back = qfalse; for (i=0 ; inumpoints ; i++) { d = DotProduct (w->p[i], normal) - dist; if (d < -ON_EPSILON) { if (front) return SIDE_CROSS; back = qtrue; continue; } if (d > ON_EPSILON) { if (back) return SIDE_CROSS; front = qtrue; continue; } } if (back) return SIDE_BACK; if (front) return SIDE_FRONT; return SIDE_ON; } /* ================= AddWindingToConvexHull Both w and *hull are on the same plane ================= */ #define MAX_HULL_POINTS 128 void AddWindingToConvexHull( winding_t *w, winding_t **hull, vec3_t normal ) { int i, j, k; float *p, *copy; vec3_t dir; float d; int numHullPoints, numNew; vec3_t hullPoints[MAX_HULL_POINTS]; vec3_t newHullPoints[MAX_HULL_POINTS]; vec3_t hullDirs[MAX_HULL_POINTS]; qboolean hullSide[MAX_HULL_POINTS]; qboolean outside; if ( !*hull ) { *hull = CopyWinding( w ); return; } numHullPoints = (*hull)->numpoints; Com_Memcpy( hullPoints, (*hull)->p, numHullPoints * sizeof(vec3_t) ); for ( i = 0 ; i < w->numpoints ; i++ ) { p = w->p[i]; // calculate hull side vectors for ( j = 0 ; j < numHullPoints ; j++ ) { k = ( j + 1 ) % numHullPoints; VectorSubtract( hullPoints[k], hullPoints[j], dir ); VectorNormalize2( dir, dir ); CrossProduct( normal, dir, hullDirs[j] ); } outside = qfalse; for ( j = 0 ; j < numHullPoints ; j++ ) { VectorSubtract( p, hullPoints[j], dir ); d = DotProduct( dir, hullDirs[j] ); if ( d >= ON_EPSILON ) { outside = qtrue; } if ( d >= -ON_EPSILON ) { hullSide[j] = qtrue; } else { hullSide[j] = qfalse; } } // if the point is effectively inside, do nothing if ( !outside ) { continue; } // find the back side to front side transition for ( j = 0 ; j < numHullPoints ; j++ ) { if ( !hullSide[ j % numHullPoints ] && hullSide[ (j + 1) % numHullPoints ] ) { break; } } if ( j == numHullPoints ) { continue; } // insert the point here VectorCopy( p, newHullPoints[0] ); numNew = 1; // copy over all points that aren't double fronts j = (j+1)%numHullPoints; for ( k = 0 ; k < numHullPoints ; k++ ) { if ( hullSide[ (j+k) % numHullPoints ] && hullSide[ (j+k+1) % numHullPoints ] ) { continue; } copy = hullPoints[ (j+k+1) % numHullPoints ]; VectorCopy( copy, newHullPoints[numNew] ); numNew++; } numHullPoints = numNew; Com_Memcpy( hullPoints, newHullPoints, numHullPoints * sizeof(vec3_t) ); } FreeWinding( *hull ); w = AllocWinding( numHullPoints ); w->numpoints = numHullPoints; *hull = w; Com_Memcpy( w->p, hullPoints, numHullPoints * sizeof(vec3_t) ); } ================================================ FILE: src/engine/qcommon/cm_polylib.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // this is only used for visualization tools in cm_ debug functions typedef struct { int numpoints; vec3_t p[4]; // variable sized } winding_t; #define MAX_POINTS_ON_WINDING 64 #define SIDE_FRONT 0 #define SIDE_BACK 1 #define SIDE_ON 2 #define SIDE_CROSS 3 #define CLIP_EPSILON 0.1f #define MAX_MAP_BOUNDS 65535 // you can define on_epsilon in the makefile as tighter #ifndef ON_EPSILON #define ON_EPSILON 0.1f #endif winding_t *AllocWinding (int points); vec_t WindingArea (winding_t *w); void WindingCenter (winding_t *w, vec3_t center); void ClipWindingEpsilon (winding_t *in, vec3_t normal, vec_t dist, vec_t epsilon, winding_t **front, winding_t **back); winding_t *ChopWinding (winding_t *in, vec3_t normal, vec_t dist); winding_t *CopyWinding (winding_t *w); winding_t *ReverseWinding (winding_t *w); winding_t *BaseWindingForPlane (vec3_t normal, vec_t dist); void CheckWinding (winding_t *w); void WindingPlane (winding_t *w, vec3_t normal, vec_t *dist); void RemoveColinearPoints (winding_t *w); int WindingOnPlaneSide (winding_t *w, vec3_t normal, vec_t dist); void FreeWinding (winding_t *w); void WindingBounds (winding_t *w, vec3_t mins, vec3_t maxs); void AddWindingToConvexHull( winding_t *w, winding_t **hull, vec3_t normal ); void ChopWindingInPlace (winding_t **w, vec3_t normal, vec_t dist, vec_t epsilon); // frees the original if clipped void pw(winding_t *w); ================================================ FILE: src/engine/qcommon/cm_public.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "qfiles.h" void CM_LoadMap( const char *name, qboolean clientload, int *checksum); void CM_ClearMap( void ); clipHandle_t CM_InlineModel( int index ); // 0 = world, 1 + are bmodels clipHandle_t CM_TempBoxModel( const vec3_t mins, const vec3_t maxs, int capsule ); void CM_ModelBounds( clipHandle_t model, vec3_t mins, vec3_t maxs ); int CM_NumClusters (void); int CM_NumInlineModels( void ); char *CM_EntityString (void); // returns an ORed contents mask int CM_PointContents( const vec3_t p, clipHandle_t model ); int CM_TransformedPointContents( const vec3_t p, clipHandle_t model, const vec3_t origin, const vec3_t angles ); void CM_BoxTrace ( trace_t *results, const vec3_t start, const vec3_t end, vec3_t mins, vec3_t maxs, clipHandle_t model, int brushmask, int capsule ); void CM_TransformedBoxTrace( trace_t *results, const vec3_t start, const vec3_t end, vec3_t mins, vec3_t maxs, clipHandle_t model, int brushmask, const vec3_t origin, const vec3_t angles, int capsule ); byte *CM_ClusterPVS (int cluster); int CM_PointLeafnum( const vec3_t p ); // only returns non-solid leafs // overflow if return listsize and if *lastLeaf != list[listsize-1] int CM_BoxLeafnums( const vec3_t mins, const vec3_t maxs, int *list, int listsize, int *lastLeaf ); int CM_LeafCluster (int leafnum); int CM_LeafArea (int leafnum); void CM_AdjustAreaPortalState( int area1, int area2, qboolean open ); qboolean CM_AreasConnected( int area1, int area2 ); int CM_WriteAreaBits( byte *buffer, int area ); // cm_tag.c int CM_LerpTag( orientation_t *tag, clipHandle_t model, int startFrame, int endFrame, float frac, const char *tagName ); // cm_marks.c int CM_MarkFragments( int numPoints, const vec3_t *points, const vec3_t projection, int maxPoints, vec3_t pointBuffer, int maxFragments, markFragment_t *fragmentBuffer ); // cm_patch.c void CM_DrawDebugSurface( void (*drawPoly)(int color, int numPoints, float *points) ); ================================================ FILE: src/engine/qcommon/cm_test.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "cm_local.h" /* ================== CM_PointLeafnum_r ================== */ int CM_PointLeafnum_r( const vec3_t p, int num ) { float d; cNode_t *node; cplane_t *plane; while (num >= 0) { node = cm.nodes + num; plane = node->plane; if (plane->type < 3) d = p[plane->type] - plane->dist; else d = DotProduct (plane->normal, p) - plane->dist; if (d < 0) num = node->children[1]; else num = node->children[0]; } c_pointcontents++; // optimize counter return -1 - num; } int CM_PointLeafnum( const vec3_t p ) { if ( !cm.numNodes ) { // map not loaded return 0; } return CM_PointLeafnum_r (p, 0); } /* ====================================================================== LEAF LISTING ====================================================================== */ void CM_StoreLeafs( leafList_t *ll, int nodenum ) { int leafNum; leafNum = -1 - nodenum; // store the lastLeaf even if the list is overflowed if ( cm.leafs[ leafNum ].cluster != -1 ) { ll->lastLeaf = leafNum; } if ( ll->count >= ll->maxcount) { ll->overflowed = qtrue; return; } ll->list[ ll->count++ ] = leafNum; } void CM_StoreBrushes( leafList_t *ll, int nodenum ) { int i, k; int leafnum; int brushnum; cLeaf_t *leaf; cbrush_t *b; leafnum = -1 - nodenum; leaf = &cm.leafs[leafnum]; for ( k = 0 ; k < leaf->numLeafBrushes ; k++ ) { brushnum = cm.leafbrushes[leaf->firstLeafBrush+k]; b = &cm.brushes[brushnum]; if ( b->checkcount == cm.checkcount ) { continue; // already checked this brush in another leaf } b->checkcount = cm.checkcount; for ( i = 0 ; i < 3 ; i++ ) { if ( b->bounds[0][i] >= ll->bounds[1][i] || b->bounds[1][i] <= ll->bounds[0][i] ) { break; } } if ( i != 3 ) { continue; } if ( ll->count >= ll->maxcount) { ll->overflowed = qtrue; return; } ((cbrush_t **)ll->list)[ ll->count++ ] = b; } #if 0 // store patches? for ( k = 0 ; k < leaf->numLeafSurfaces ; k++ ) { patch = cm.surfaces[ cm.leafsurfaces[ leaf->firstleafsurface + k ] ]; if ( !patch ) { continue; } } #endif } /* ============= CM_BoxLeafnums Fills in a list of all the leafs touched ============= */ void CM_BoxLeafnums_r( leafList_t *ll, int nodenum ) { cplane_t *plane; cNode_t *node; int s; while (1) { if (nodenum < 0) { ll->storeLeafs( ll, nodenum ); return; } node = &cm.nodes[nodenum]; plane = node->plane; s = BoxOnPlaneSide( ll->bounds[0], ll->bounds[1], plane ); if (s == 1) { nodenum = node->children[0]; } else if (s == 2) { nodenum = node->children[1]; } else { // go down both CM_BoxLeafnums_r( ll, node->children[0] ); nodenum = node->children[1]; } } } /* ================== CM_BoxLeafnums ================== */ int CM_BoxLeafnums( const vec3_t mins, const vec3_t maxs, int *list, int listsize, int *lastLeaf) { leafList_t ll; cm.checkcount++; VectorCopy( mins, ll.bounds[0] ); VectorCopy( maxs, ll.bounds[1] ); ll.count = 0; ll.maxcount = listsize; ll.list = list; ll.storeLeafs = CM_StoreLeafs; ll.lastLeaf = 0; ll.overflowed = qfalse; CM_BoxLeafnums_r( &ll, 0 ); *lastLeaf = ll.lastLeaf; return ll.count; } /* ================== CM_BoxBrushes ================== */ int CM_BoxBrushes( const vec3_t mins, const vec3_t maxs, cbrush_t **list, int listsize ) { leafList_t ll; cm.checkcount++; VectorCopy( mins, ll.bounds[0] ); VectorCopy( maxs, ll.bounds[1] ); ll.count = 0; ll.maxcount = listsize; ll.list = (int*) (void *)list; ll.storeLeafs = CM_StoreBrushes; ll.lastLeaf = 0; ll.overflowed = qfalse; CM_BoxLeafnums_r( &ll, 0 ); return ll.count; } //==================================================================== /* ================== CM_PointContents ================== */ int CM_PointContents( const vec3_t p, clipHandle_t model ) { int leafnum; int i, k; int brushnum; cLeaf_t *leaf; cbrush_t *b; int contents; float d; cmodel_t *clipm; if (!cm.numNodes) { // map not loaded return 0; } if ( model ) { clipm = CM_ClipHandleToModel( model ); leaf = &clipm->leaf; } else { leafnum = CM_PointLeafnum_r (p, 0); leaf = &cm.leafs[leafnum]; } contents = 0; for (k=0 ; knumLeafBrushes ; k++) { brushnum = cm.leafbrushes[leaf->firstLeafBrush+k]; b = &cm.brushes[brushnum]; // see if the point is in the brush for ( i = 0 ; i < b->numsides ; i++ ) { d = DotProduct( p, b->sides[i].plane->normal ); // FIXME test for Cash // if ( d >= b->sides[i].plane->dist ) { if ( d > b->sides[i].plane->dist ) { break; } } if ( i == b->numsides ) { contents |= b->contents; } } return contents; } /* ================== CM_TransformedPointContents Handles offseting and rotation of the end points for moving and rotating entities ================== */ int CM_TransformedPointContents( const vec3_t p, clipHandle_t model, const vec3_t origin, const vec3_t angles) { vec3_t p_l; vec3_t temp; vec3_t forward, right, up; // subtract origin offset VectorSubtract (p, origin, p_l); // rotate start and end into the models frame of reference if ( model != BOX_MODEL_HANDLE && (angles[0] || angles[1] || angles[2]) ) { AngleVectors (angles, forward, right, up); VectorCopy (p_l, temp); p_l[0] = DotProduct (temp, forward); p_l[1] = -DotProduct (temp, right); p_l[2] = DotProduct (temp, up); } return CM_PointContents( p_l, model ); } /* =============================================================================== PVS =============================================================================== */ byte *CM_ClusterPVS (int cluster) { if (cluster < 0 || cluster >= cm.numClusters || !cm.vised ) { return cm.visibility; } return cm.visibility + cluster * cm.clusterBytes; } /* =============================================================================== AREAPORTALS =============================================================================== */ void CM_FloodArea_r( int areaNum, int floodnum) { int i; cArea_t *area; int *con; area = &cm.areas[ areaNum ]; if ( area->floodvalid == cm.floodvalid ) { if (area->floodnum == floodnum) return; Com_Error (ERR_DROP, "FloodArea_r: reflooded"); } area->floodnum = floodnum; area->floodvalid = cm.floodvalid; con = cm.areaPortals + areaNum * cm.numAreas; for ( i=0 ; i < cm.numAreas ; i++ ) { if ( con[i] > 0 ) { CM_FloodArea_r( i, floodnum ); } } } /* ==================== CM_FloodAreaConnections ==================== */ void CM_FloodAreaConnections( void ) { int i; cArea_t *area; int floodnum; // all current floods are now invalid cm.floodvalid++; floodnum = 0; for (i = 0 ; i < cm.numAreas ; i++) { area = &cm.areas[i]; if (area->floodvalid == cm.floodvalid) { continue; // already flooded into } floodnum++; CM_FloodArea_r (i, floodnum); } } /* ==================== CM_AdjustAreaPortalState ==================== */ void CM_AdjustAreaPortalState( int area1, int area2, qboolean open ) { if ( area1 < 0 || area2 < 0 ) { return; } if ( area1 >= cm.numAreas || area2 >= cm.numAreas ) { Com_Error (ERR_DROP, "CM_ChangeAreaPortalState: bad area number"); } if ( open ) { cm.areaPortals[ area1 * cm.numAreas + area2 ]++; cm.areaPortals[ area2 * cm.numAreas + area1 ]++; } else { cm.areaPortals[ area1 * cm.numAreas + area2 ]--; cm.areaPortals[ area2 * cm.numAreas + area1 ]--; if ( cm.areaPortals[ area2 * cm.numAreas + area1 ] < 0 ) { Com_Error (ERR_DROP, "CM_AdjustAreaPortalState: negative reference count"); } } CM_FloodAreaConnections (); } /* ==================== CM_AreasConnected ==================== */ qboolean CM_AreasConnected( int area1, int area2 ) { #ifndef BSPC if ( cm_noAreas->integer ) { return qtrue; } #endif if ( area1 < 0 || area2 < 0 ) { return qfalse; } if (area1 >= cm.numAreas || area2 >= cm.numAreas) { Com_Error (ERR_DROP, "area >= cm.numAreas"); } if (cm.areas[area1].floodnum == cm.areas[area2].floodnum) { return qtrue; } return qfalse; } /* ================= CM_WriteAreaBits Writes a bit vector of all the areas that are in the same flood as the area parameter Returns the number of bytes needed to hold all the bits. The bits are OR'd in, so you can CM_WriteAreaBits from multiple viewpoints and get the union of all visible areas. This is used to cull non-visible entities from snapshots ================= */ int CM_WriteAreaBits (byte *buffer, int area) { int i; int floodnum; int bytes; bytes = (cm.numAreas+7)>>3; #ifndef BSPC if (cm_noAreas->integer || area == -1) #else if ( area == -1) #endif { // for debugging, send everything Com_Memset (buffer, 255, bytes); } else { floodnum = cm.areas[area].floodnum; for (i=0 ; i>3] |= 1<<(i&7); } } return bytes; } ================================================ FILE: src/engine/qcommon/cm_trace.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "cm_local.h" // always use bbox vs. bbox collision and never capsule vs. bbox or vice versa //#define ALWAYS_BBOX_VS_BBOX // always use capsule vs. capsule collision and never capsule vs. bbox or vice versa //#define ALWAYS_CAPSULE_VS_CAPSULE //#define CAPSULE_DEBUG /* =============================================================================== BASIC MATH =============================================================================== */ /* ================ RotatePoint ================ */ void RotatePoint(vec3_t point, /*const*/ vec3_t matrix[3]) { // bk: FIXME vec3_t tvec; VectorCopy(point, tvec); point[0] = DotProduct(matrix[0], tvec); point[1] = DotProduct(matrix[1], tvec); point[2] = DotProduct(matrix[2], tvec); } /* ================ TransposeMatrix ================ */ void TransposeMatrix(/*const*/ vec3_t matrix[3], vec3_t transpose[3]) { // bk: FIXME int i, j; for (i = 0; i < 3; i++) { for (j = 0; j < 3; j++) { transpose[i][j] = matrix[j][i]; } } } /* ================ CreateRotationMatrix ================ */ void CreateRotationMatrix(const vec3_t angles, vec3_t matrix[3]) { AngleVectors(angles, matrix[0], matrix[1], matrix[2]); VectorInverse(matrix[1]); } /* ================ CM_ProjectPointOntoVector ================ */ void CM_ProjectPointOntoVector( vec3_t point, vec3_t vStart, vec3_t vDir, vec3_t vProj ) { vec3_t pVec; VectorSubtract( point, vStart, pVec ); // project onto the directional vector for this segment VectorMA( vStart, DotProduct( pVec, vDir ), vDir, vProj ); } /* ================ CM_DistanceFromLineSquared ================ */ float CM_DistanceFromLineSquared(vec3_t p, vec3_t lp1, vec3_t lp2, vec3_t dir) { vec3_t proj, t; int j; CM_ProjectPointOntoVector(p, lp1, dir, proj); for (j = 0; j < 3; j++) if ((proj[j] > lp1[j] && proj[j] > lp2[j]) || (proj[j] < lp1[j] && proj[j] < lp2[j])) break; if (j < 3) { if (fabs(proj[j] - lp1[j]) < fabs(proj[j] - lp2[j])) VectorSubtract(p, lp1, t); else VectorSubtract(p, lp2, t); return VectorLengthSquared(t); } VectorSubtract(p, proj, t); return VectorLengthSquared(t); } /* ================ CM_VectorDistanceSquared ================ */ float CM_VectorDistanceSquared(vec3_t p1, vec3_t p2) { vec3_t dir; VectorSubtract(p2, p1, dir); return VectorLengthSquared(dir); } /* ================ SquareRootFloat ================ */ float SquareRootFloat(float number) { long i; float x, y; const float f = 1.5F; x = number * 0.5F; y = number; i = * ( long * ) &y; i = 0x5f3759df - ( i >> 1 ); y = * ( float * ) &i; y = y * ( f - ( x * y * y ) ); y = y * ( f - ( x * y * y ) ); return number * y; } /* =============================================================================== POSITION TESTING =============================================================================== */ /* ================ CM_TestBoxInBrush ================ */ void CM_TestBoxInBrush( traceWork_t *tw, cbrush_t *brush ) { int i; cplane_t *plane; float dist; float d1; cbrushside_t *side; float t; vec3_t startp; if (!brush->numsides) { return; } // special test for axial if ( tw->bounds[0][0] > brush->bounds[1][0] || tw->bounds[0][1] > brush->bounds[1][1] || tw->bounds[0][2] > brush->bounds[1][2] || tw->bounds[1][0] < brush->bounds[0][0] || tw->bounds[1][1] < brush->bounds[0][1] || tw->bounds[1][2] < brush->bounds[0][2] ) { return; } if ( tw->sphere.use ) { // the first six planes are the axial planes, so we only // need to test the remainder for ( i = 6 ; i < brush->numsides ; i++ ) { side = brush->sides + i; plane = side->plane; // adjust the plane distance apropriately for radius dist = plane->dist + tw->sphere.radius; // find the closest point on the capsule to the plane t = DotProduct( plane->normal, tw->sphere.offset ); if ( t > 0 ) { VectorSubtract( tw->start, tw->sphere.offset, startp ); } else { VectorAdd( tw->start, tw->sphere.offset, startp ); } d1 = DotProduct( startp, plane->normal ) - dist; // if completely in front of face, no intersection if ( d1 > 0 ) { return; } } } else { // the first six planes are the axial planes, so we only // need to test the remainder for ( i = 6 ; i < brush->numsides ; i++ ) { side = brush->sides + i; plane = side->plane; // adjust the plane distance apropriately for mins/maxs dist = plane->dist - DotProduct( tw->offsets[ plane->signbits ], plane->normal ); d1 = DotProduct( tw->start, plane->normal ) - dist; // if completely in front of face, no intersection if ( d1 > 0 ) { return; } } } // inside this brush tw->trace.startsolid = tw->trace.allsolid = qtrue; tw->trace.fraction = 0; tw->trace.contents = brush->contents; } /* ================ CM_TestInLeaf ================ */ void CM_TestInLeaf( traceWork_t *tw, cLeaf_t *leaf ) { int k; int brushnum; cbrush_t *b; cPatch_t *patch; // test box position against all brushes in the leaf for (k=0 ; knumLeafBrushes ; k++) { brushnum = cm.leafbrushes[leaf->firstLeafBrush+k]; b = &cm.brushes[brushnum]; if (b->checkcount == cm.checkcount) { continue; // already checked this brush in another leaf } b->checkcount = cm.checkcount; if ( !(b->contents & tw->contents)) { continue; } CM_TestBoxInBrush( tw, b ); if ( tw->trace.allsolid ) { return; } } // test against all patches #ifdef BSPC if (1) { #else if ( !cm_noCurves->integer ) { #endif //BSPC for ( k = 0 ; k < leaf->numLeafSurfaces ; k++ ) { patch = cm.surfaces[ cm.leafsurfaces[ leaf->firstLeafSurface + k ] ]; if ( !patch ) { continue; } if ( patch->checkcount == cm.checkcount ) { continue; // already checked this brush in another leaf } patch->checkcount = cm.checkcount; if ( !(patch->contents & tw->contents)) { continue; } if ( CM_PositionTestInPatchCollide( tw, patch->pc ) ) { tw->trace.startsolid = tw->trace.allsolid = qtrue; tw->trace.fraction = 0; tw->trace.contents = patch->contents; return; } } } } /* ================== CM_TestCapsuleInCapsule capsule inside capsule check ================== */ void CM_TestCapsuleInCapsule( traceWork_t *tw, clipHandle_t model ) { int i; vec3_t mins, maxs; vec3_t top, bottom; vec3_t p1, p2, tmp; vec3_t offset, symetricSize[2]; float radius, halfwidth, halfheight, offs, r; CM_ModelBounds(model, mins, maxs); VectorAdd(tw->start, tw->sphere.offset, top); VectorSubtract(tw->start, tw->sphere.offset, bottom); for ( i = 0 ; i < 3 ; i++ ) { offset[i] = ( mins[i] + maxs[i] ) * 0.5; symetricSize[0][i] = mins[i] - offset[i]; symetricSize[1][i] = maxs[i] - offset[i]; } halfwidth = symetricSize[ 1 ][ 0 ]; halfheight = symetricSize[ 1 ][ 2 ]; radius = ( halfwidth > halfheight ) ? halfheight : halfwidth; offs = halfheight - radius; r = Square(tw->sphere.radius + radius); // check if any of the spheres overlap VectorCopy(offset, p1); p1[2] += offs; VectorSubtract(p1, top, tmp); if ( VectorLengthSquared(tmp) < r ) { tw->trace.startsolid = tw->trace.allsolid = qtrue; tw->trace.fraction = 0; } VectorSubtract(p1, bottom, tmp); if ( VectorLengthSquared(tmp) < r ) { tw->trace.startsolid = tw->trace.allsolid = qtrue; tw->trace.fraction = 0; } VectorCopy(offset, p2); p2[2] -= offs; VectorSubtract(p2, top, tmp); if ( VectorLengthSquared(tmp) < r ) { tw->trace.startsolid = tw->trace.allsolid = qtrue; tw->trace.fraction = 0; } VectorSubtract(p2, bottom, tmp); if ( VectorLengthSquared(tmp) < r ) { tw->trace.startsolid = tw->trace.allsolid = qtrue; tw->trace.fraction = 0; } // if between cylinder up and lower bounds if ( (top[2] >= p1[2] && top[2] <= p2[2]) || (bottom[2] >= p1[2] && bottom[2] <= p2[2]) ) { // 2d coordinates top[2] = p1[2] = 0; // if the cylinders overlap VectorSubtract(top, p1, tmp); if ( VectorLengthSquared(tmp) < r ) { tw->trace.startsolid = tw->trace.allsolid = qtrue; tw->trace.fraction = 0; } } } /* ================== CM_TestBoundingBoxInCapsule bounding box inside capsule check ================== */ void CM_TestBoundingBoxInCapsule( traceWork_t *tw, clipHandle_t model ) { vec3_t mins, maxs, offset, size[2]; clipHandle_t h; cmodel_t *cmod; int i; // mins maxs of the capsule CM_ModelBounds(model, mins, maxs); // offset for capsule center for ( i = 0 ; i < 3 ; i++ ) { offset[i] = ( mins[i] + maxs[i] ) * 0.5; size[0][i] = mins[i] - offset[i]; size[1][i] = maxs[i] - offset[i]; tw->start[i] -= offset[i]; tw->end[i] -= offset[i]; } // replace the bounding box with the capsule tw->sphere.use = qtrue; tw->sphere.radius = ( size[1][0] > size[1][2] ) ? size[1][2]: size[1][0]; tw->sphere.halfheight = size[1][2]; VectorSet( tw->sphere.offset, 0, 0, size[1][2] - tw->sphere.radius ); // replace the capsule with the bounding box h = CM_TempBoxModel(tw->size[0], tw->size[1], qfalse); // calculate collision cmod = CM_ClipHandleToModel( h ); CM_TestInLeaf( tw, &cmod->leaf ); } /* ================== CM_PositionTest ================== */ #define MAX_POSITION_LEAFS 1024 void CM_PositionTest( traceWork_t *tw ) { int leafs[MAX_POSITION_LEAFS]; int i; leafList_t ll; // identify the leafs we are touching VectorAdd( tw->start, tw->size[0], ll.bounds[0] ); VectorAdd( tw->start, tw->size[1], ll.bounds[1] ); for (i=0 ; i<3 ; i++) { ll.bounds[0][i] -= 1; ll.bounds[1][i] += 1; } ll.count = 0; ll.maxcount = MAX_POSITION_LEAFS; ll.list = leafs; ll.storeLeafs = CM_StoreLeafs; ll.lastLeaf = 0; ll.overflowed = qfalse; cm.checkcount++; CM_BoxLeafnums_r( &ll, 0 ); cm.checkcount++; // test the contents of the leafs for (i=0 ; i < ll.count ; i++) { CM_TestInLeaf( tw, &cm.leafs[leafs[i]] ); if ( tw->trace.allsolid ) { break; } } } /* =============================================================================== TRACING =============================================================================== */ /* ================ CM_TraceThroughPatch ================ */ void CM_TraceThroughPatch( traceWork_t *tw, cPatch_t *patch ) { float oldFrac; c_patch_traces++; oldFrac = tw->trace.fraction; CM_TraceThroughPatchCollide( tw, patch->pc ); if ( tw->trace.fraction < oldFrac ) { tw->trace.surfaceFlags = patch->surfaceFlags; tw->trace.contents = patch->contents; } } /* ================ CM_TraceThroughBrush ================ */ void CM_TraceThroughBrush( traceWork_t *tw, cbrush_t *brush ) { int i; cplane_t *plane, *clipplane; float dist; float enterFrac, leaveFrac; float d1, d2; qboolean getout, startout; float f; cbrushside_t *side, *leadside; float t; vec3_t startp; vec3_t endp; enterFrac = -1.0; leaveFrac = 1.0; clipplane = NULL; if ( !brush->numsides ) { return; } c_brush_traces++; getout = qfalse; startout = qfalse; leadside = NULL; if ( tw->sphere.use ) { // // compare the trace against all planes of the brush // find the latest time the trace crosses a plane towards the interior // and the earliest time the trace crosses a plane towards the exterior // for (i = 0; i < brush->numsides; i++) { side = brush->sides + i; plane = side->plane; // adjust the plane distance apropriately for radius dist = plane->dist + tw->sphere.radius; // find the closest point on the capsule to the plane t = DotProduct( plane->normal, tw->sphere.offset ); if ( t > 0 ) { VectorSubtract( tw->start, tw->sphere.offset, startp ); VectorSubtract( tw->end, tw->sphere.offset, endp ); } else { VectorAdd( tw->start, tw->sphere.offset, startp ); VectorAdd( tw->end, tw->sphere.offset, endp ); } d1 = DotProduct( startp, plane->normal ) - dist; d2 = DotProduct( endp, plane->normal ) - dist; if (d2 > 0) { getout = qtrue; // endpoint is not in solid } if (d1 > 0) { startout = qtrue; } // if completely in front of face, no intersection with the entire brush if (d1 > 0 && ( d2 >= SURFACE_CLIP_EPSILON || d2 >= d1 ) ) { return; } // if it doesn't cross the plane, the plane isn't relevent if (d1 <= 0 && d2 <= 0 ) { continue; } // crosses face if (d1 > d2) { // enter f = (d1-SURFACE_CLIP_EPSILON) / (d1-d2); if ( f < 0 ) { f = 0; } if (f > enterFrac) { enterFrac = f; clipplane = plane; leadside = side; } } else { // leave f = (d1+SURFACE_CLIP_EPSILON) / (d1-d2); if ( f > 1 ) { f = 1; } if (f < leaveFrac) { leaveFrac = f; } } } } else { // // compare the trace against all planes of the brush // find the latest time the trace crosses a plane towards the interior // and the earliest time the trace crosses a plane towards the exterior // for (i = 0; i < brush->numsides; i++) { side = brush->sides + i; plane = side->plane; // adjust the plane distance apropriately for mins/maxs dist = plane->dist - DotProduct( tw->offsets[ plane->signbits ], plane->normal ); d1 = DotProduct( tw->start, plane->normal ) - dist; d2 = DotProduct( tw->end, plane->normal ) - dist; if (d2 > 0) { getout = qtrue; // endpoint is not in solid } if (d1 > 0) { startout = qtrue; } // if completely in front of face, no intersection with the entire brush if (d1 > 0 && ( d2 >= SURFACE_CLIP_EPSILON || d2 >= d1 ) ) { return; } // if it doesn't cross the plane, the plane isn't relevent if (d1 <= 0 && d2 <= 0 ) { continue; } // crosses face if (d1 > d2) { // enter f = (d1-SURFACE_CLIP_EPSILON) / (d1-d2); if ( f < 0 ) { f = 0; } if (f > enterFrac) { enterFrac = f; clipplane = plane; leadside = side; } } else { // leave f = (d1+SURFACE_CLIP_EPSILON) / (d1-d2); if ( f > 1 ) { f = 1; } if (f < leaveFrac) { leaveFrac = f; } } } } // // all planes have been checked, and the trace was not // completely outside the brush // if (!startout) { // original point was inside brush tw->trace.startsolid = qtrue; if (!getout) { tw->trace.allsolid = qtrue; tw->trace.fraction = 0; tw->trace.contents = brush->contents; } return; } if (enterFrac < leaveFrac) { if (enterFrac > -1 && enterFrac < tw->trace.fraction) { if (enterFrac < 0) { enterFrac = 0; } tw->trace.fraction = enterFrac; tw->trace.plane = *clipplane; tw->trace.surfaceFlags = leadside->surfaceFlags; tw->trace.contents = brush->contents; } } } /* ================ CM_TraceThroughLeaf ================ */ void CM_TraceThroughLeaf( traceWork_t *tw, cLeaf_t *leaf ) { int k; int brushnum; cbrush_t *b; cPatch_t *patch; // trace line against all brushes in the leaf for ( k = 0 ; k < leaf->numLeafBrushes ; k++ ) { brushnum = cm.leafbrushes[leaf->firstLeafBrush+k]; b = &cm.brushes[brushnum]; if ( b->checkcount == cm.checkcount ) { continue; // already checked this brush in another leaf } b->checkcount = cm.checkcount; if ( !(b->contents & tw->contents) ) { continue; } CM_TraceThroughBrush( tw, b ); if ( !tw->trace.fraction ) { return; } } // trace line against all patches in the leaf #ifdef BSPC if (1) { #else if ( !cm_noCurves->integer ) { #endif for ( k = 0 ; k < leaf->numLeafSurfaces ; k++ ) { patch = cm.surfaces[ cm.leafsurfaces[ leaf->firstLeafSurface + k ] ]; if ( !patch ) { continue; } if ( patch->checkcount == cm.checkcount ) { continue; // already checked this patch in another leaf } patch->checkcount = cm.checkcount; if ( !(patch->contents & tw->contents) ) { continue; } CM_TraceThroughPatch( tw, patch ); if ( !tw->trace.fraction ) { return; } } } } #define RADIUS_EPSILON 1.0f /* ================ CM_TraceThroughSphere get the first intersection of the ray with the sphere ================ */ void CM_TraceThroughSphere( traceWork_t *tw, vec3_t origin, float radius, vec3_t start, vec3_t end ) { float l1, l2, length, scale, fraction; float a, b, c, d, sqrtd; vec3_t v1, dir, intersection; // if inside the sphere VectorSubtract(start, origin, dir); l1 = VectorLengthSquared(dir); if (l1 < Square(radius)) { tw->trace.fraction = 0; tw->trace.startsolid = qtrue; // test for allsolid VectorSubtract(end, origin, dir); l1 = VectorLengthSquared(dir); if (l1 < Square(radius)) { tw->trace.allsolid = qtrue; } return; } // VectorSubtract(end, start, dir); length = VectorNormalize(dir); // l1 = CM_DistanceFromLineSquared(origin, start, end, dir); VectorSubtract(end, origin, v1); l2 = VectorLengthSquared(v1); // if no intersection with the sphere and the end point is at least an epsilon away if (l1 >= Square(radius) && l2 > Square(radius+SURFACE_CLIP_EPSILON)) { return; } // // | origin - (start + t * dir) | = radius // a = dir[0]^2 + dir[1]^2 + dir[2]^2; // b = 2 * (dir[0] * (start[0] - origin[0]) + dir[1] * (start[1] - origin[1]) + dir[2] * (start[2] - origin[2])); // c = (start[0] - origin[0])^2 + (start[1] - origin[1])^2 + (start[2] - origin[2])^2 - radius^2; // VectorSubtract(start, origin, v1); // dir is normalized so a = 1 a = 1.0f;//dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]; b = 2.0f * (dir[0] * v1[0] + dir[1] * v1[1] + dir[2] * v1[2]); c = v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2] - (radius+RADIUS_EPSILON) * (radius+RADIUS_EPSILON); d = b * b - 4.0f * c;// * a; if (d > 0) { sqrtd = SquareRootFloat(d); // = (- b + sqrtd) * 0.5f; // / (2.0f * a); fraction = (- b - sqrtd) * 0.5f; // / (2.0f * a); // if (fraction < 0) { fraction = 0; } else { fraction /= length; } if ( fraction < tw->trace.fraction ) { tw->trace.fraction = fraction; VectorSubtract(end, start, dir); VectorMA(start, fraction, dir, intersection); VectorSubtract(intersection, origin, dir); #ifdef CAPSULE_DEBUG l2 = VectorLength(dir); if (l2 < radius) { int bah = 1; } #endif scale = 1 / (radius+RADIUS_EPSILON); VectorScale(dir, scale, dir); VectorCopy(dir, tw->trace.plane.normal); VectorAdd( tw->modelOrigin, intersection, intersection); tw->trace.plane.dist = DotProduct(tw->trace.plane.normal, intersection); tw->trace.contents = CONTENTS_BODY; } } else if (d == 0) { //t1 = (- b ) / 2; // slide along the sphere } // no intersection at all } /* ================ CM_TraceThroughVerticalCylinder get the first intersection of the ray with the cylinder the cylinder extends halfheight above and below the origin ================ */ void CM_TraceThroughVerticalCylinder( traceWork_t *tw, vec3_t origin, float radius, float halfheight, vec3_t start, vec3_t end) { float length, scale, fraction, l1, l2; float a, b, c, d, sqrtd; vec3_t v1, dir, start2d, end2d, org2d, intersection; // 2d coordinates VectorSet(start2d, start[0], start[1], 0); VectorSet(end2d, end[0], end[1], 0); VectorSet(org2d, origin[0], origin[1], 0); // if between lower and upper cylinder bounds if (start[2] <= origin[2] + halfheight && start[2] >= origin[2] - halfheight) { // if inside the cylinder VectorSubtract(start2d, org2d, dir); l1 = VectorLengthSquared(dir); if (l1 < Square(radius)) { tw->trace.fraction = 0; tw->trace.startsolid = qtrue; VectorSubtract(end2d, org2d, dir); l1 = VectorLengthSquared(dir); if (l1 < Square(radius)) { tw->trace.allsolid = qtrue; } return; } } // VectorSubtract(end2d, start2d, dir); length = VectorNormalize(dir); // l1 = CM_DistanceFromLineSquared(org2d, start2d, end2d, dir); VectorSubtract(end2d, org2d, v1); l2 = VectorLengthSquared(v1); // if no intersection with the cylinder and the end point is at least an epsilon away if (l1 >= Square(radius) && l2 > Square(radius+SURFACE_CLIP_EPSILON)) { return; } // // // (start[0] - origin[0] - t * dir[0]) ^ 2 + (start[1] - origin[1] - t * dir[1]) ^ 2 = radius ^ 2 // (v1[0] + t * dir[0]) ^ 2 + (v1[1] + t * dir[1]) ^ 2 = radius ^ 2; // v1[0] ^ 2 + 2 * v1[0] * t * dir[0] + (t * dir[0]) ^ 2 + // v1[1] ^ 2 + 2 * v1[1] * t * dir[1] + (t * dir[1]) ^ 2 = radius ^ 2 // t ^ 2 * (dir[0] ^ 2 + dir[1] ^ 2) + t * (2 * v1[0] * dir[0] + 2 * v1[1] * dir[1]) + // v1[0] ^ 2 + v1[1] ^ 2 - radius ^ 2 = 0 // VectorSubtract(start, origin, v1); // dir is normalized so we can use a = 1 a = 1.0f;// * (dir[0] * dir[0] + dir[1] * dir[1]); b = 2.0f * (v1[0] * dir[0] + v1[1] * dir[1]); c = v1[0] * v1[0] + v1[1] * v1[1] - (radius+RADIUS_EPSILON) * (radius+RADIUS_EPSILON); d = b * b - 4.0f * c;// * a; if (d > 0) { sqrtd = SquareRootFloat(d); // = (- b + sqrtd) * 0.5f;// / (2.0f * a); fraction = (- b - sqrtd) * 0.5f;// / (2.0f * a); // if (fraction < 0) { fraction = 0; } else { fraction /= length; } if ( fraction < tw->trace.fraction ) { VectorSubtract(end, start, dir); VectorMA(start, fraction, dir, intersection); // if the intersection is between the cylinder lower and upper bound if (intersection[2] <= origin[2] + halfheight && intersection[2] >= origin[2] - halfheight) { // tw->trace.fraction = fraction; VectorSubtract(intersection, origin, dir); dir[2] = 0; #ifdef CAPSULE_DEBUG l2 = VectorLength(dir); if (l2 <= radius) { int bah = 1; } #endif scale = 1 / (radius+RADIUS_EPSILON); VectorScale(dir, scale, dir); VectorCopy(dir, tw->trace.plane.normal); VectorAdd( tw->modelOrigin, intersection, intersection); tw->trace.plane.dist = DotProduct(tw->trace.plane.normal, intersection); tw->trace.contents = CONTENTS_BODY; } } } else if (d == 0) { //t[0] = (- b ) / 2 * a; // slide along the cylinder } // no intersection at all } /* ================ CM_TraceCapsuleThroughCapsule capsule vs. capsule collision (not rotated) ================ */ void CM_TraceCapsuleThroughCapsule( traceWork_t *tw, clipHandle_t model ) { int i; vec3_t mins, maxs; vec3_t top, bottom, starttop, startbottom, endtop, endbottom; vec3_t offset, symetricSize[2]; float radius, halfwidth, halfheight, offs, h; CM_ModelBounds(model, mins, maxs); // test trace bounds vs. capsule bounds if ( tw->bounds[0][0] > maxs[0] + RADIUS_EPSILON || tw->bounds[0][1] > maxs[1] + RADIUS_EPSILON || tw->bounds[0][2] > maxs[2] + RADIUS_EPSILON || tw->bounds[1][0] < mins[0] - RADIUS_EPSILON || tw->bounds[1][1] < mins[1] - RADIUS_EPSILON || tw->bounds[1][2] < mins[2] - RADIUS_EPSILON ) { return; } // top origin and bottom origin of each sphere at start and end of trace VectorAdd(tw->start, tw->sphere.offset, starttop); VectorSubtract(tw->start, tw->sphere.offset, startbottom); VectorAdd(tw->end, tw->sphere.offset, endtop); VectorSubtract(tw->end, tw->sphere.offset, endbottom); // calculate top and bottom of the capsule spheres to collide with for ( i = 0 ; i < 3 ; i++ ) { offset[i] = ( mins[i] + maxs[i] ) * 0.5; symetricSize[0][i] = mins[i] - offset[i]; symetricSize[1][i] = maxs[i] - offset[i]; } halfwidth = symetricSize[ 1 ][ 0 ]; halfheight = symetricSize[ 1 ][ 2 ]; radius = ( halfwidth > halfheight ) ? halfheight : halfwidth; offs = halfheight - radius; VectorCopy(offset, top); top[2] += offs; VectorCopy(offset, bottom); bottom[2] -= offs; // expand radius of spheres radius += tw->sphere.radius; // if there is horizontal movement if ( tw->start[0] != tw->end[0] || tw->start[1] != tw->end[1] ) { // height of the expanded cylinder is the height of both cylinders minus the radius of both spheres h = halfheight + tw->sphere.halfheight - radius; // if the cylinder has a height if ( h > 0 ) { // test for collisions between the cylinders CM_TraceThroughVerticalCylinder(tw, offset, radius, h, tw->start, tw->end); } } // test for collision between the spheres CM_TraceThroughSphere(tw, top, radius, startbottom, endbottom); CM_TraceThroughSphere(tw, bottom, radius, starttop, endtop); } /* ================ CM_TraceBoundingBoxThroughCapsule bounding box vs. capsule collision ================ */ void CM_TraceBoundingBoxThroughCapsule( traceWork_t *tw, clipHandle_t model ) { vec3_t mins, maxs, offset, size[2]; clipHandle_t h; cmodel_t *cmod; int i; // mins maxs of the capsule CM_ModelBounds(model, mins, maxs); // offset for capsule center for ( i = 0 ; i < 3 ; i++ ) { offset[i] = ( mins[i] + maxs[i] ) * 0.5; size[0][i] = mins[i] - offset[i]; size[1][i] = maxs[i] - offset[i]; tw->start[i] -= offset[i]; tw->end[i] -= offset[i]; } // replace the bounding box with the capsule tw->sphere.use = qtrue; tw->sphere.radius = ( size[1][0] > size[1][2] ) ? size[1][2]: size[1][0]; tw->sphere.halfheight = size[1][2]; VectorSet( tw->sphere.offset, 0, 0, size[1][2] - tw->sphere.radius ); // replace the capsule with the bounding box h = CM_TempBoxModel(tw->size[0], tw->size[1], qfalse); // calculate collision cmod = CM_ClipHandleToModel( h ); CM_TraceThroughLeaf( tw, &cmod->leaf ); } //========================================================================================= /* ================== CM_TraceThroughTree Traverse all the contacted leafs from the start to the end position. If the trace is a point, they will be exactly in order, but for larger trace volumes it is possible to hit something in a later leaf with a smaller intercept fraction. ================== */ void CM_TraceThroughTree( traceWork_t *tw, int num, float p1f, float p2f, vec3_t p1, vec3_t p2) { cNode_t *node; cplane_t *plane; float t1, t2, offset; float frac, frac2; float idist; vec3_t mid; int side; float midf; if (tw->trace.fraction <= p1f) { return; // already hit something nearer } // if < 0, we are in a leaf node if (num < 0) { CM_TraceThroughLeaf( tw, &cm.leafs[-1-num] ); return; } // // find the point distances to the seperating plane // and the offset for the size of the box // node = cm.nodes + num; plane = node->plane; // adjust the plane distance apropriately for mins/maxs if ( plane->type < 3 ) { t1 = p1[plane->type] - plane->dist; t2 = p2[plane->type] - plane->dist; offset = tw->extents[plane->type]; } else { t1 = DotProduct (plane->normal, p1) - plane->dist; t2 = DotProduct (plane->normal, p2) - plane->dist; if ( tw->isPoint ) { offset = 0; } else { #if 0 // bk010201 - DEAD // an axial brush right behind a slanted bsp plane // will poke through when expanded, so adjust // by sqrt(3) offset = fabs(tw->extents[0]*plane->normal[0]) + fabs(tw->extents[1]*plane->normal[1]) + fabs(tw->extents[2]*plane->normal[2]); offset *= 2; offset = tw->maxOffset; #endif // this is silly offset = 2048; } } // see which sides we need to consider if ( t1 >= offset + 1 && t2 >= offset + 1 ) { CM_TraceThroughTree( tw, node->children[0], p1f, p2f, p1, p2 ); return; } if ( t1 < -offset - 1 && t2 < -offset - 1 ) { CM_TraceThroughTree( tw, node->children[1], p1f, p2f, p1, p2 ); return; } // put the crosspoint SURFACE_CLIP_EPSILON pixels on the near side if ( t1 < t2 ) { idist = 1.0/(t1-t2); side = 1; frac2 = (t1 + offset + SURFACE_CLIP_EPSILON)*idist; frac = (t1 - offset + SURFACE_CLIP_EPSILON)*idist; } else if (t1 > t2) { idist = 1.0/(t1-t2); side = 0; frac2 = (t1 - offset - SURFACE_CLIP_EPSILON)*idist; frac = (t1 + offset + SURFACE_CLIP_EPSILON)*idist; } else { side = 0; frac = 1; frac2 = 0; } // move up to the node if ( frac < 0 ) { frac = 0; } if ( frac > 1 ) { frac = 1; } midf = p1f + (p2f - p1f)*frac; mid[0] = p1[0] + frac*(p2[0] - p1[0]); mid[1] = p1[1] + frac*(p2[1] - p1[1]); mid[2] = p1[2] + frac*(p2[2] - p1[2]); CM_TraceThroughTree( tw, node->children[side], p1f, midf, p1, mid ); // go past the node if ( frac2 < 0 ) { frac2 = 0; } if ( frac2 > 1 ) { frac2 = 1; } midf = p1f + (p2f - p1f)*frac2; mid[0] = p1[0] + frac2*(p2[0] - p1[0]); mid[1] = p1[1] + frac2*(p2[1] - p1[1]); mid[2] = p1[2] + frac2*(p2[2] - p1[2]); CM_TraceThroughTree( tw, node->children[side^1], midf, p2f, mid, p2 ); } //====================================================================== /* ================== CM_Trace ================== */ void CM_Trace( trace_t *results, const vec3_t start, const vec3_t end, vec3_t mins, vec3_t maxs, clipHandle_t model, const vec3_t origin, int brushmask, int capsule, sphere_t *sphere ) { int i; traceWork_t tw; vec3_t offset; cmodel_t *cmod; cmod = CM_ClipHandleToModel( model ); cm.checkcount++; // for multi-check avoidance c_traces++; // for statistics, may be zeroed // fill in a default trace Com_Memset( &tw, 0, sizeof(tw) ); tw.trace.fraction = 1; // assume it goes the entire distance until shown otherwise VectorCopy(origin, tw.modelOrigin); if (!cm.numNodes) { *results = tw.trace; return; // map not loaded, shouldn't happen } // allow NULL to be passed in for 0,0,0 if ( !mins ) { mins = vec3_origin; } if ( !maxs ) { maxs = vec3_origin; } // set basic parms tw.contents = brushmask; // adjust so that mins and maxs are always symetric, which // avoids some complications with plane expanding of rotated // bmodels for ( i = 0 ; i < 3 ; i++ ) { offset[i] = ( mins[i] + maxs[i] ) * 0.5; tw.size[0][i] = mins[i] - offset[i]; tw.size[1][i] = maxs[i] - offset[i]; tw.start[i] = start[i] + offset[i]; tw.end[i] = end[i] + offset[i]; } // if a sphere is already specified if ( sphere ) { tw.sphere = *sphere; } else { tw.sphere.use = (qboolean) capsule; tw.sphere.radius = ( tw.size[1][0] > tw.size[1][2] ) ? tw.size[1][2]: tw.size[1][0]; tw.sphere.halfheight = tw.size[1][2]; VectorSet( tw.sphere.offset, 0, 0, tw.size[1][2] - tw.sphere.radius ); } tw.maxOffset = tw.size[1][0] + tw.size[1][1] + tw.size[1][2]; // tw.offsets[signbits] = vector to apropriate corner from origin tw.offsets[0][0] = tw.size[0][0]; tw.offsets[0][1] = tw.size[0][1]; tw.offsets[0][2] = tw.size[0][2]; tw.offsets[1][0] = tw.size[1][0]; tw.offsets[1][1] = tw.size[0][1]; tw.offsets[1][2] = tw.size[0][2]; tw.offsets[2][0] = tw.size[0][0]; tw.offsets[2][1] = tw.size[1][1]; tw.offsets[2][2] = tw.size[0][2]; tw.offsets[3][0] = tw.size[1][0]; tw.offsets[3][1] = tw.size[1][1]; tw.offsets[3][2] = tw.size[0][2]; tw.offsets[4][0] = tw.size[0][0]; tw.offsets[4][1] = tw.size[0][1]; tw.offsets[4][2] = tw.size[1][2]; tw.offsets[5][0] = tw.size[1][0]; tw.offsets[5][1] = tw.size[0][1]; tw.offsets[5][2] = tw.size[1][2]; tw.offsets[6][0] = tw.size[0][0]; tw.offsets[6][1] = tw.size[1][1]; tw.offsets[6][2] = tw.size[1][2]; tw.offsets[7][0] = tw.size[1][0]; tw.offsets[7][1] = tw.size[1][1]; tw.offsets[7][2] = tw.size[1][2]; // // calculate bounds // if ( tw.sphere.use ) { for ( i = 0 ; i < 3 ; i++ ) { if ( tw.start[i] < tw.end[i] ) { tw.bounds[0][i] = tw.start[i] - fabs(tw.sphere.offset[i]) - tw.sphere.radius; tw.bounds[1][i] = tw.end[i] + fabs(tw.sphere.offset[i]) + tw.sphere.radius; } else { tw.bounds[0][i] = tw.end[i] - fabs(tw.sphere.offset[i]) - tw.sphere.radius; tw.bounds[1][i] = tw.start[i] + fabs(tw.sphere.offset[i]) + tw.sphere.radius; } } } else { for ( i = 0 ; i < 3 ; i++ ) { if ( tw.start[i] < tw.end[i] ) { tw.bounds[0][i] = tw.start[i] + tw.size[0][i]; tw.bounds[1][i] = tw.end[i] + tw.size[1][i]; } else { tw.bounds[0][i] = tw.end[i] + tw.size[0][i]; tw.bounds[1][i] = tw.start[i] + tw.size[1][i]; } } } // // check for position test special case // if (start[0] == end[0] && start[1] == end[1] && start[2] == end[2]) { if ( model ) { #ifdef ALWAYS_BBOX_VS_BBOX // bk010201 - FIXME - compile time flag? if ( model == BOX_MODEL_HANDLE || model == CAPSULE_MODEL_HANDLE) { tw.sphere.use = qfalse; CM_TestInLeaf( &tw, &cmod->leaf ); } else #elif defined(ALWAYS_CAPSULE_VS_CAPSULE) if ( model == BOX_MODEL_HANDLE || model == CAPSULE_MODEL_HANDLE) { CM_TestCapsuleInCapsule( &tw, model ); } else #endif if ( model == CAPSULE_MODEL_HANDLE ) { if ( tw.sphere.use ) { CM_TestCapsuleInCapsule( &tw, model ); } else { CM_TestBoundingBoxInCapsule( &tw, model ); } } else { CM_TestInLeaf( &tw, &cmod->leaf ); } } else { CM_PositionTest( &tw ); } } else { // // check for point special case // if ( tw.size[0][0] == 0 && tw.size[0][1] == 0 && tw.size[0][2] == 0 ) { tw.isPoint = qtrue; VectorClear( tw.extents ); } else { tw.isPoint = qfalse; tw.extents[0] = tw.size[1][0]; tw.extents[1] = tw.size[1][1]; tw.extents[2] = tw.size[1][2]; } // // general sweeping through world // if ( model ) { #ifdef ALWAYS_BBOX_VS_BBOX if ( model == BOX_MODEL_HANDLE || model == CAPSULE_MODEL_HANDLE) { tw.sphere.use = qfalse; CM_TraceThroughLeaf( &tw, &cmod->leaf ); } else #elif defined(ALWAYS_CAPSULE_VS_CAPSULE) if ( model == BOX_MODEL_HANDLE || model == CAPSULE_MODEL_HANDLE) { CM_TraceCapsuleThroughCapsule( &tw, model ); } else #endif if ( model == CAPSULE_MODEL_HANDLE ) { if ( tw.sphere.use ) { CM_TraceCapsuleThroughCapsule( &tw, model ); } else { CM_TraceBoundingBoxThroughCapsule( &tw, model ); } } else { CM_TraceThroughLeaf( &tw, &cmod->leaf ); } } else { CM_TraceThroughTree( &tw, 0, 0, 1, tw.start, tw.end ); } } // generate endpos from the original, unmodified start/end if ( tw.trace.fraction == 1 ) { VectorCopy (end, tw.trace.endpos); } else { for ( i=0 ; i<3 ; i++ ) { tw.trace.endpos[i] = start[i] + tw.trace.fraction * (end[i] - start[i]); } } // If allsolid is set (was entirely inside something solid), the plane is not valid. // If fraction == 1.0, we never hit anything, and thus the plane is not valid. // Otherwise, the normal on the plane should have unit length assert(tw.trace.allsolid || tw.trace.fraction == 1.0 || VectorLengthSquared(tw.trace.plane.normal) > 0.9999); *results = tw.trace; } /* ================== CM_BoxTrace ================== */ void CM_BoxTrace( trace_t *results, const vec3_t start, const vec3_t end, vec3_t mins, vec3_t maxs, clipHandle_t model, int brushmask, int capsule ) { CM_Trace( results, start, end, mins, maxs, model, vec3_origin, brushmask, capsule, NULL ); } /* ================== CM_TransformedBoxTrace Handles offseting and rotation of the end points for moving and rotating entities ================== */ void CM_TransformedBoxTrace( trace_t *results, const vec3_t start, const vec3_t end, vec3_t mins, vec3_t maxs, clipHandle_t model, int brushmask, const vec3_t origin, const vec3_t angles, int capsule ) { trace_t trace; vec3_t start_l, end_l; qboolean rotated; vec3_t offset; vec3_t symetricSize[2]; vec3_t matrix[3], transpose[3]; int i; float halfwidth; float halfheight; float t; sphere_t sphere; if ( !mins ) { mins = vec3_origin; } if ( !maxs ) { maxs = vec3_origin; } // adjust so that mins and maxs are always symetric, which // avoids some complications with plane expanding of rotated // bmodels for ( i = 0 ; i < 3 ; i++ ) { offset[i] = ( mins[i] + maxs[i] ) * 0.5; symetricSize[0][i] = mins[i] - offset[i]; symetricSize[1][i] = maxs[i] - offset[i]; start_l[i] = start[i] + offset[i]; end_l[i] = end[i] + offset[i]; } // subtract origin offset VectorSubtract( start_l, origin, start_l ); VectorSubtract( end_l, origin, end_l ); // rotate start and end into the models frame of reference if ( model != BOX_MODEL_HANDLE && (angles[0] || angles[1] || angles[2]) ) { rotated = qtrue; } else { rotated = qfalse; } halfwidth = symetricSize[ 1 ][ 0 ]; halfheight = symetricSize[ 1 ][ 2 ]; sphere.use = (qboolean) capsule; sphere.radius = ( halfwidth > halfheight ) ? halfheight : halfwidth; sphere.halfheight = halfheight; t = halfheight - sphere.radius; if (rotated) { // rotation on trace line (start-end) instead of rotating the bmodel // NOTE: This is still incorrect for bounding boxes because the actual bounding // box that is swept through the model is not rotated. We cannot rotate // the bounding box or the bmodel because that would make all the brush // bevels invalid. // However this is correct for capsules since a capsule itself is rotated too. CreateRotationMatrix(angles, matrix); RotatePoint(start_l, matrix); RotatePoint(end_l, matrix); // rotated sphere offset for capsule sphere.offset[0] = matrix[0][ 2 ] * t; sphere.offset[1] = -matrix[1][ 2 ] * t; sphere.offset[2] = matrix[2][ 2 ] * t; } else { VectorSet( sphere.offset, 0, 0, t ); } // sweep the box through the model CM_Trace( &trace, start_l, end_l, symetricSize[0], symetricSize[1], model, origin, brushmask, capsule, &sphere ); // if the bmodel was rotated and there was a collision if ( rotated && trace.fraction != 1.0 ) { // rotation of bmodel collision plane TransposeMatrix(matrix, transpose); RotatePoint(trace.plane.normal, transpose); } // re-calculate the end position of the trace because the trace.endpos // calculated by CM_Trace could be rotated and have an offset trace.endpos[0] = start[0] + trace.fraction * (end[0] - start[0]); trace.endpos[1] = start[1] + trace.fraction * (end[1] - start[1]); trace.endpos[2] = start[2] + trace.fraction * (end[2] - start[2]); *results = trace; } ================================================ FILE: src/engine/qcommon/cmd.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // cmd.c -- Quake script command processing module #include "../../game/q_shared.h" #include "qcommon.h" #define MAX_CMD_BUFFER 16384 #define MAX_CMD_LINE 1024 typedef struct { byte *data; int maxsize; int cursize; } cmd_t; int cmd_wait; cmd_t cmd_text; byte cmd_text_buf[MAX_CMD_BUFFER]; //============================================================================= /* ============ Cmd_Wait_f Causes execution of the remainder of the command buffer to be delayed until next frame. This allows commands like: bind g "cmd use rocket ; +attack ; wait ; -attack ; cmd use blaster" ============ */ void Cmd_Wait_f( void ) { if ( Cmd_Argc() == 2 ) { cmd_wait = atoi( Cmd_Argv( 1 ) ); } else { cmd_wait = 1; } } /* ============================================================================= COMMAND BUFFER ============================================================================= */ /* ============ Cbuf_Init ============ */ void Cbuf_Init (void) { cmd_text.data = cmd_text_buf; cmd_text.maxsize = MAX_CMD_BUFFER; cmd_text.cursize = 0; } /* ============ Cbuf_AddText Adds command text at the end of the buffer, does NOT add a final \n ============ */ void Cbuf_AddText( const char *text ) { int l; l = (int)strlen (text); if (cmd_text.cursize + l >= cmd_text.maxsize) { Com_Printf ("Cbuf_AddText: overflow\n"); return; } Com_Memcpy(&cmd_text.data[cmd_text.cursize], text, l); cmd_text.cursize += l; } /* ============ Cbuf_InsertText Adds command text immediately after the current command Adds a \n to the text ============ */ void Cbuf_InsertText( const char *text ) { int len; int i; len = (int)strlen( text ) + 1; if ( len + cmd_text.cursize > cmd_text.maxsize ) { Com_Printf( "Cbuf_InsertText overflowed\n" ); return; } // move the existing command text for ( i = cmd_text.cursize - 1 ; i >= 0 ; i-- ) { cmd_text.data[ i + len ] = cmd_text.data[ i ]; } // copy the new text in Com_Memcpy( cmd_text.data, text, len - 1 ); // add a \n cmd_text.data[ len - 1 ] = '\n'; cmd_text.cursize += len; } /* ============ Cbuf_ExecuteText ============ */ void Cbuf_ExecuteText (int exec_when, const char *text) { switch (exec_when) { case EXEC_NOW: if (text && (int)strlen(text) > 0) { Cmd_ExecuteString (text); } else { Cbuf_Execute(); } break; case EXEC_INSERT: Cbuf_InsertText (text); break; case EXEC_APPEND: Cbuf_AddText (text); break; default: Com_Error (ERR_FATAL, "Cbuf_ExecuteText: bad exec_when"); } } /* ============ Cbuf_Execute ============ */ void Cbuf_Execute (void) { int i; char *text; char line[MAX_CMD_LINE]; int quotes; while (cmd_text.cursize) { if ( cmd_wait ) { // skip out while text still remains in buffer, leaving it // for next frame cmd_wait--; break; } // find a \n or ; line break text = (char *)cmd_text.data; quotes = 0; for (i=0 ; i< cmd_text.cursize ; i++) { if (text[i] == '"') quotes++; if ( !(quotes&1) && text[i] == ';') break; // don't break if inside a quoted string if (text[i] == '\n' || text[i] == '\r' ) break; } if( i >= (MAX_CMD_LINE - 1)) { i = MAX_CMD_LINE - 1; } Com_Memcpy (line, text, i); line[i] = 0; // delete the text from the command buffer and move remaining commands down // this is necessary because commands (exec) can insert data at the // beginning of the text buffer if (i == cmd_text.cursize) cmd_text.cursize = 0; else { i++; cmd_text.cursize -= i; memmove (text, text+i, cmd_text.cursize); } // execute the command line Cmd_ExecuteString (line); } } /* ============================================================================== SCRIPT COMMANDS ============================================================================== */ /* =============== Cmd_Exec_f =============== */ void Cmd_Exec_f( void ) { char *f; int len; char filename[MAX_QPATH]; if (Cmd_Argc () != 2) { Com_Printf ("exec : execute a script file\n"); return; } Q_strncpyz( filename, Cmd_Argv(1), sizeof( filename ) ); COM_DefaultExtension( filename, sizeof( filename ), ".cfg" ); len = FS_ReadFile( filename, (void **)&f); if (!f) { Com_Printf ("couldn't exec %s\n",Cmd_Argv(1)); return; } Com_Printf ("execing %s\n",Cmd_Argv(1)); Cbuf_InsertText (f); FS_FreeFile (f); } /* =============== Cmd_Vstr_f Inserts the current value of a variable as command text =============== */ void Cmd_Vstr_f( void ) { char *v; if (Cmd_Argc () != 2) { Com_Printf ("vstr : execute a variable command\n"); return; } v = Cvar_VariableString( Cmd_Argv( 1 ) ); Cbuf_InsertText( va("%s\n", v ) ); } /* =============== Cmd_Echo_f Just prints the rest of the line to the console =============== */ void Cmd_Echo_f (void) { int i; for (i=1 ; i= cmd_argc ) { return ""; } return cmd_argv[arg]; } /* ============ Cmd_ArgvBuffer The interpreted versions use this because they can't have pointers returned to them ============ */ void Cmd_ArgvBuffer( int arg, char *buffer, int bufferLength ) { Q_strncpyz( buffer, Cmd_Argv( arg ), bufferLength ); } /* ============ Cmd_Args Returns a single string containing argv(1) to argv(argc()-1) ============ */ char *Cmd_Args( void ) { static char cmd_args[MAX_STRING_CHARS]; int i; cmd_args[0] = 0; for ( i = 1 ; i < cmd_argc ; i++ ) { strcat( cmd_args, cmd_argv[i] ); if ( i != cmd_argc-1 ) { strcat( cmd_args, " " ); } } return cmd_args; } /* ============ Cmd_Args Returns a single string containing argv(arg) to argv(argc()-1) ============ */ char *Cmd_ArgsFrom( int arg ) { static char cmd_args[BIG_INFO_STRING]; int i; cmd_args[0] = 0; if (arg < 0) arg = 0; for ( i = arg ; i < cmd_argc ; i++ ) { strcat( cmd_args, cmd_argv[i] ); if ( i != cmd_argc-1 ) { strcat( cmd_args, " " ); } } return cmd_args; } /* ============ Cmd_ArgsBuffer The interpreted versions use this because they can't have pointers returned to them ============ */ void Cmd_ArgsBuffer( char *buffer, int bufferLength ) { Q_strncpyz( buffer, Cmd_Args(), bufferLength ); } /* ============ Cmd_Cmd Retrieve the unmodified command string For rcon use when you want to transmit without altering quoting https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=543 ============ */ char *Cmd_Cmd() { return cmd_cmd; } /* ============ Cmd_TokenizeString Parses the given string into command line tokens. The text is copied to a seperate buffer and 0 characters are inserted in the apropriate place, The argv array will point into this temporary buffer. ============ */ // NOTE TTimo define that to track tokenization issues //#define TKN_DBG void Cmd_TokenizeString( const char *text_in ) { const char *text; char *textOut; #ifdef TKN_DBG // FIXME TTimo blunt hook to try to find the tokenization of userinfo Com_DPrintf("Cmd_TokenizeString: %s\n", text_in); #endif // clear previous args cmd_argc = 0; if ( !text_in ) { return; } Q_strncpyz( cmd_cmd, text_in, sizeof(cmd_cmd) ); text = text_in; textOut = cmd_tokenized; while ( 1 ) { if ( cmd_argc == MAX_STRING_TOKENS ) { return; // this is usually something malicious } while ( 1 ) { // skip whitespace while ( *text && *text <= ' ' ) { text++; } if ( !*text ) { return; // all tokens parsed } // skip // comments if ( text[0] == '/' && text[1] == '/' ) { return; // all tokens parsed } // skip /* */ comments if ( text[0] == '/' && text[1] =='*' ) { while ( *text && ( text[0] != '*' || text[1] != '/' ) ) { text++; } if ( !*text ) { return; // all tokens parsed } text += 2; } else { break; // we are ready to parse a token } } // handle quoted strings // NOTE TTimo this doesn't handle \" escaping if ( *text == '"' ) { cmd_argv[cmd_argc] = textOut; cmd_argc++; text++; while ( *text && *text != '"' ) { *textOut++ = *text++; } *textOut++ = 0; if ( !*text ) { return; // all tokens parsed } text++; continue; } // regular token cmd_argv[cmd_argc] = textOut; cmd_argc++; // skip until whitespace, quote, or command while ( *text > ' ' ) { if ( text[0] == '"' ) { break; } if ( text[0] == '/' && text[1] == '/' ) { break; } // skip /* */ comments if ( text[0] == '/' && text[1] =='*' ) { break; } *textOut++ = *text++; } *textOut++ = 0; if ( !*text ) { return; // all tokens parsed } } } /* ============ Cmd_AddCommand ============ */ void Cmd_AddCommand( const char *cmd_name, xcommand_t function ) { cmd_function_t *cmd; // fail if the command already exists for ( cmd = cmd_functions ; cmd ; cmd=cmd->next ) { if ( !strcmp( cmd_name, cmd->name ) ) { // allow completion-only commands to be silently doubled if ( function != NULL ) { Com_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name); } return; } } // use a small malloc to avoid zone fragmentation cmd = (cmd_function_t*) S_Malloc(sizeof(cmd_function_t)); cmd->name = CopyString( cmd_name ); cmd->function = function; cmd->next = cmd_functions; cmd_functions = cmd; } /* ============ Cmd_RemoveCommand ============ */ void Cmd_RemoveCommand( const char *cmd_name ) { cmd_function_t *cmd, **back; back = &cmd_functions; while( 1 ) { cmd = *back; if ( !cmd ) { // command wasn't active return; } if ( !strcmp( cmd_name, cmd->name ) ) { *back = cmd->next; if (cmd->name) { Z_Free(cmd->name); } Z_Free (cmd); return; } back = &cmd->next; } } /* ============ Cmd_CommandCompletion ============ */ void Cmd_CommandCompletion( void(*callback)(const char *s) ) { cmd_function_t *cmd; for (cmd=cmd_functions ; cmd ; cmd=cmd->next) { callback( cmd->name ); } } /* ============ Cmd_ExecuteString A complete command line has been parsed, so try to execute it ============ */ void Cmd_ExecuteString( const char *text ) { cmd_function_t *cmd, **prev; // execute the command line Cmd_TokenizeString( text ); if ( !Cmd_Argc() ) { return; // no tokens } // check registered command functions for ( prev = &cmd_functions ; *prev ; prev = &cmd->next ) { cmd = *prev; if ( !Q_stricmp( cmd_argv[0],cmd->name ) ) { // rearrange the links so that the command will be // near the head of the list next time it is used *prev = cmd->next; cmd->next = cmd_functions; cmd_functions = cmd; // perform the action if ( !cmd->function ) { // let the cgame or game handle it break; } else { cmd->function (); } return; } } // check cvars if ( Cvar_Command() ) { return; } // check client game commands if ( com_cl_running && com_cl_running->integer && CL_GameCommand() ) { return; } // check server game commands if ( com_sv_running && com_sv_running->integer && SV_GameCommand() ) { return; } // check ui commands if ( com_cl_running && com_cl_running->integer && UI_GameCommand() ) { return; } // send it as a server command if we are connected // this will usually result in a chat message CL_ForwardCommandToServer ( text ); } /* ============ Cmd_List_f ============ */ void Cmd_List_f (void) { cmd_function_t *cmd; int i; char *match; if ( Cmd_Argc() > 1 ) { match = Cmd_Argv( 1 ); } else { match = NULL; } i = 0; for (cmd=cmd_functions ; cmd ; cmd=cmd->next) { if (match && !Com_Filter(match, cmd->name, qfalse)) continue; Com_Printf ("%s\n", cmd->name); i++; } Com_Printf ("%i commands\n", i); } /* ============ Cmd_Init ============ */ void Cmd_Init (void) { Cmd_AddCommand ("cmdlist",Cmd_List_f); Cmd_AddCommand ("exec",Cmd_Exec_f); Cmd_AddCommand ("vstr",Cmd_Vstr_f); Cmd_AddCommand ("echo",Cmd_Echo_f); Cmd_AddCommand ("wait", Cmd_Wait_f); } ================================================ FILE: src/engine/qcommon/common.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // common.c -- misc functions used in client and server #include "../../game/q_shared.h" #include "qcommon.h" #include #ifdef __linux__ #include #else #if defined(MACOS_X) #include #else #include #endif #endif int demo_protocols[] = { 66, 67, 68, 0 }; #define MAX_NUM_ARGVS 50 #define MIN_DEDICATED_COMHUNKMEGS 1 #define MIN_COMHUNKMEGS 56 #ifdef MACOS_X #define DEF_COMHUNKMEGS "64" #define DEF_COMZONEMEGS "24" #else #define DEF_COMHUNKMEGS "56" #define DEF_COMZONEMEGS "16" #endif int com_argc; char *com_argv[MAX_NUM_ARGVS+1]; jmp_buf abortframe; // an ERR_DROP occured, exit the entire frame FILE *debuglogfile; static fileHandle_t logfile; fileHandle_t com_journalFile; // events are written here fileHandle_t com_journalDataFile; // config files are written here cvar_t *com_viewlog; cvar_t *com_speeds; cvar_t *com_developer; cvar_t *com_dedicated; cvar_t *com_timescale; cvar_t *com_fixedtime; cvar_t *com_dropsim; // 0.0 to 1.0, simulated packet drops cvar_t *com_journal; cvar_t *com_maxfps; cvar_t *com_timedemo; cvar_t *com_sv_running; cvar_t *com_cl_running; cvar_t *com_logfile; // 1 = buffer log, 2 = flush after each print cvar_t *com_showtrace; cvar_t *com_version; cvar_t *com_blood; cvar_t *com_buildScript; // for automated data building scripts cvar_t *com_introPlayed; cvar_t *cl_paused; cvar_t *sv_paused; cvar_t *com_cameraMode; #if defined(_WIN32) && defined(_DEBUG) cvar_t *com_noErrorInterrupt; #endif // com_speeds times int time_game; int time_frontend; // renderer frontend time int time_backend; // renderer backend time int com_frameTime; int com_frameMsec; int com_frameNumber; qboolean com_errorEntered; qboolean com_fullyInitialized; char com_errorMessage[MAXPRINTMSG]; void Com_WriteConfig_f( void ); void CIN_CloseAllVideos(); //============================================================================ static char *rd_buffer; static int rd_buffersize; static void (*rd_flush)( char *buffer ); void Com_BeginRedirect (char *buffer, int buffersize, void (*flush)( char *) ) { if (!buffer || !buffersize || !flush) return; rd_buffer = buffer; rd_buffersize = buffersize; rd_flush = flush; *rd_buffer = 0; } void Com_EndRedirect (void) { if ( rd_flush ) { rd_flush(rd_buffer); } rd_buffer = NULL; rd_buffersize = 0; rd_flush = NULL; } /* ============= Com_Printf Both client and server can use this, and it will output to the apropriate place. A raw string should NEVER be passed as fmt, because of "%f" type crashers. ============= */ void QDECL Com_Printf( const char *fmt, ... ) { va_list argptr; char msg[MAXPRINTMSG]; static qboolean opening_qconsole = qfalse; va_start (argptr,fmt); Q_vsnprintf (msg, sizeof(msg), fmt, argptr); va_end (argptr); if ( rd_buffer ) { if ((strlen (msg) + (int)strlen(rd_buffer)) > (rd_buffersize - 1)) { rd_flush(rd_buffer); *rd_buffer = 0; } Q_strcat(rd_buffer, rd_buffersize, msg); // TTimo nooo .. that would defeat the purpose //rd_flush(rd_buffer); //*rd_buffer = 0; return; } // echo to console if we're not a dedicated server if ( com_dedicated && !com_dedicated->integer ) { CL_ConsolePrint( msg ); } // echo to dedicated console and early console Sys_Print( msg ); // logfile if ( com_logfile && com_logfile->integer ) { // TTimo: only open the qconsole.log if the filesystem is in an initialized state // also, avoid recursing in the qconsole.log opening (i.e. if fs_debug is on) if ( !logfile && FS_Initialized() && !opening_qconsole) { struct tm *newtime; time_t aclock; opening_qconsole = qtrue; time( &aclock ); newtime = localtime( &aclock ); logfile = FS_FOpenFileWrite( "qconsole.log" ); Com_Printf( "logfile opened on %s\n", asctime( newtime ) ); if ( com_logfile->integer > 1 ) { // force it to not buffer so we get valid // data even if we are crashing FS_ForceFlush(logfile); } opening_qconsole = qfalse; } if ( logfile && FS_Initialized()) { FS_Write(msg, (int)strlen(msg), logfile); } } } /* ================ Com_DPrintf A Com_Printf that only shows up if the "developer" cvar is set ================ */ void QDECL Com_DPrintf( const char *fmt, ...) { va_list argptr; char msg[MAXPRINTMSG]; if ( !com_developer || !com_developer->integer ) { return; // don't confuse non-developers with techie stuff... } va_start (argptr,fmt); Q_vsnprintf (msg, sizeof(msg), fmt, argptr); va_end (argptr); Com_Printf ("%s", msg); } /* ============= Com_Error Both client and server can use this, and it will do the apropriate things. ============= */ void QDECL Com_Error( int code, const char *fmt, ... ) { va_list argptr; static int lastErrorTime; static int errorCount; int currentTime; #if defined(_WIN32) && defined(_DEBUG) if ( code != ERR_DISCONNECT && code != ERR_NEED_CD ) { if (!com_noErrorInterrupt->integer) { __debugbreak(); } } #endif // when we are running automated scripts, make sure we // know if anything failed if ( com_buildScript && com_buildScript->integer ) { code = ERR_FATAL; } // make sure we can get at our local stuff FS_PureServerSetLoadedPaks( "", "" ); // if we are getting a solid stream of ERR_DROP, do an ERR_FATAL currentTime = Sys_Milliseconds(); if ( currentTime - lastErrorTime < 100 ) { if ( ++errorCount > 3 ) { code = ERR_FATAL; } } else { errorCount = 0; } lastErrorTime = currentTime; if ( com_errorEntered ) { Sys_Error( "recursive error after: %s", com_errorMessage ); } com_errorEntered = qtrue; va_start (argptr,fmt); vsprintf (com_errorMessage,fmt,argptr); va_end (argptr); if ( code != ERR_DISCONNECT && code != ERR_NEED_CD ) { Cvar_Set("com_errorMessage", com_errorMessage); } if ( code == ERR_SERVERDISCONNECT ) { CL_Disconnect( qtrue ); CL_FlushMemory( ); com_errorEntered = qfalse; longjmp (abortframe, -1); } else if ( code == ERR_DROP || code == ERR_DISCONNECT ) { Com_Printf ("********************\nERROR: %s\n********************\n", com_errorMessage); SV_Shutdown (va("Server crashed: %s\n", com_errorMessage)); CL_Disconnect( qtrue ); CL_FlushMemory( ); com_errorEntered = qfalse; longjmp (abortframe, -1); } else if ( code == ERR_NEED_CD ) { SV_Shutdown( "Server didn't have CD\n" ); if ( com_cl_running && com_cl_running->integer ) { CL_Disconnect( qtrue ); CL_FlushMemory( ); com_errorEntered = qfalse; CL_CDDialog(); } else { Com_Printf("Server didn't have CD\n" ); } longjmp (abortframe, -1); } else { CL_Shutdown (); SV_Shutdown (va("Server fatal crashed: %s\n", com_errorMessage)); } Com_Shutdown (); Sys_Error ("%s", com_errorMessage); } /* ============= Com_Quit_f Both client and server can use this, and it will do the apropriate things. ============= */ void Com_Quit_f( void ) { // don't try to shutdown if we are in a recursive error if ( !com_errorEntered ) { SV_Shutdown ("Server quit\n"); CL_Shutdown (); Com_Shutdown (); FS_Shutdown(qtrue); } Sys_Quit (); } /* ============================================================================ COMMAND LINE FUNCTIONS + characters seperate the commandLine string into multiple console command lines. All of these are valid: quake3 +set test blah +map test quake3 set test blah+map test quake3 set test blah + map test ============================================================================ */ #define MAX_CONSOLE_LINES 32 int com_numConsoleLines; char *com_consoleLines[MAX_CONSOLE_LINES]; /* ================== Com_ParseCommandLine Break it up into multiple console lines ================== */ void Com_ParseCommandLine( char *commandLine ) { int inq = 0; com_consoleLines[0] = commandLine; com_numConsoleLines = 1; while ( *commandLine ) { if (*commandLine == '"') { inq = !inq; } // look for a + seperating character // if commandLine came from a file, we might have real line seperators if ( (*commandLine == '+' && !inq) || *commandLine == '\n' || *commandLine == '\r' ) { if ( com_numConsoleLines == MAX_CONSOLE_LINES ) { return; } com_consoleLines[com_numConsoleLines] = commandLine + 1; com_numConsoleLines++; *commandLine = 0; } commandLine++; } } /* =================== Com_SafeMode Check for "safe" on the command line, which will skip loading of q3config.cfg =================== */ qboolean Com_SafeMode( void ) { int i; for ( i = 0 ; i < com_numConsoleLines ; i++ ) { Cmd_TokenizeString( com_consoleLines[i] ); if ( !Q_stricmp( Cmd_Argv(0), "safe" ) || !Q_stricmp( Cmd_Argv(0), "cvar_restart" ) ) { com_consoleLines[i][0] = 0; return qtrue; } } return qfalse; } /* =============== Com_StartupVariable Searches for command line parameters that are set commands. If match is not NULL, only that cvar will be looked for. That is necessary because cddir and basedir need to be set before the filesystem is started, but all other sets shouls be after execing the config and default. =============== */ void Com_StartupVariable( const char *match ) { int i; char *s; cvar_t *cv; for (i=0 ; i < com_numConsoleLines ; i++) { Cmd_TokenizeString( com_consoleLines[i] ); if ( strcmp( Cmd_Argv(0), "set" ) ) { continue; } s = Cmd_Argv(1); if ( !match || !strcmp( s, match ) ) { Cvar_Set( s, Cmd_Argv(2) ); cv = Cvar_Get( s, "", 0 ); cv->flags |= CVAR_USER_CREATED; // com_consoleLines[i] = 0; } } } /* ================= Com_AddStartupCommands Adds command line parameters as script statements Commands are seperated by + signs Returns qtrue if any late commands were added, which will keep the demoloop from immediately starting ================= */ qboolean Com_AddStartupCommands( void ) { int i; qboolean added; added = qfalse; // quote every token, so args with semicolons can work for (i=0 ; i < com_numConsoleLines ; i++) { if ( !com_consoleLines[i] || !com_consoleLines[i][0] ) { continue; } // set commands won't override menu startup if ( Q_stricmpn( com_consoleLines[i], "set", 3 ) ) { added = qtrue; } Cbuf_AddText( com_consoleLines[i] ); Cbuf_AddText( "\n" ); } return added; } //============================================================================ void Info_Print( const char *s ) { char key[512]; char value[512]; char *o; int l; if (*s == '\\') s++; while (*s) { o = key; while (*s && *s != '\\') *o++ = *s++; l = o - key; if (l < 20) { Com_Memset (o, ' ', 20-l); key[20] = 0; } else *o = 0; Com_Printf ("%s", key); if (!*s) { Com_Printf ("MISSING VALUE\n"); return; } o = value; s++; while (*s && *s != '\\') *o++ = *s++; *o = 0; if (*s) s++; Com_Printf ("%s\n", value); } } /* ============ Com_StringContains ============ */ char *Com_StringContains(char *str1, char *str2, int casesensitive) { int len, i, j; len = (int)strlen(str1) - (int)strlen(str2); for (i = 0; i <= len; i++, str1++) { for (j = 0; str2[j]; j++) { if (casesensitive) { if (str1[j] != str2[j]) { break; } } else { if (toupper(str1[j]) != toupper(str2[j])) { break; } } } if (!str2[j]) { return str1; } } return NULL; } /* ============ Com_Filter ============ */ int Com_Filter(char *filter, char *name, int casesensitive) { char buf[MAX_TOKEN_CHARS]; char *ptr; int i, found; while(*filter) { if (*filter == '*') { filter++; for (i = 0; *filter; i++) { if (*filter == '*' || *filter == '?') break; buf[i] = *filter; filter++; } buf[i] = '\0'; if (strlen(buf)) { ptr = Com_StringContains(name, buf, casesensitive); if (!ptr) return qfalse; name = ptr + (int)strlen(buf); } } else if (*filter == '?') { filter++; name++; } else if (*filter == '[' && *(filter+1) == '[') { filter++; } else if (*filter == '[') { filter++; found = qfalse; while(*filter && !found) { if (*filter == ']' && *(filter+1) != ']') break; if (*(filter+1) == '-' && *(filter+2) && (*(filter+2) != ']' || *(filter+3) == ']')) { if (casesensitive) { if (*name >= *filter && *name <= *(filter+2)) found = qtrue; } else { if (toupper(*name) >= toupper(*filter) && toupper(*name) <= toupper(*(filter+2))) found = qtrue; } filter += 3; } else { if (casesensitive) { if (*filter == *name) found = qtrue; } else { if (toupper(*filter) == toupper(*name)) found = qtrue; } filter++; } } if (!found) return qfalse; while(*filter) { if (*filter == ']' && *(filter+1) != ']') break; filter++; } filter++; name++; } else { if (casesensitive) { if (*filter != *name) return qfalse; } else { if (toupper(*filter) != toupper(*name)) return qfalse; } filter++; name++; } } return qtrue; } /* ============ Com_FilterPath ============ */ int Com_FilterPath(char *filter, char *name, int casesensitive) { int i; char new_filter[MAX_QPATH]; char new_name[MAX_QPATH]; for (i = 0; i < MAX_QPATH-1 && filter[i]; i++) { if ( filter[i] == '\\' || filter[i] == ':' ) { new_filter[i] = '/'; } else { new_filter[i] = filter[i]; } } new_filter[i] = '\0'; for (i = 0; i < MAX_QPATH-1 && name[i]; i++) { if ( name[i] == '\\' || name[i] == ':' ) { new_name[i] = '/'; } else { new_name[i] = name[i]; } } new_name[i] = '\0'; return Com_Filter(new_filter, new_name, casesensitive); } /* ============ Com_HashKey ============ */ int Com_HashKey(char *string, int maxlen) { int register hash, i; hash = 0; for (i = 0; i < maxlen && string[i] != '\0'; i++) { hash += string[i] * (119 + i); } hash = (hash ^ (hash >> 10) ^ (hash >> 20)); return hash; } /* ================ Com_RealTime ================ */ int Com_RealTime(qtime_t *qtime) { time_t t; struct tm *tms; t = time(NULL); if (!qtime) return t; tms = localtime(&t); if (tms) { qtime->tm_sec = tms->tm_sec; qtime->tm_min = tms->tm_min; qtime->tm_hour = tms->tm_hour; qtime->tm_mday = tms->tm_mday; qtime->tm_mon = tms->tm_mon; qtime->tm_year = tms->tm_year; qtime->tm_wday = tms->tm_wday; qtime->tm_yday = tms->tm_yday; qtime->tm_isdst = tms->tm_isdst; } return t; } /* ============================================================================== ZONE MEMORY ALLOCATION There is never any space between memblocks, and there will never be two contiguous free memblocks. The rover can be left pointing at a non-empty block The zone calls are pretty much only used for small strings and structures, all big things are allocated on the hunk. ============================================================================== */ #define ZONEID 0x1d4a11 #define MINFRAGMENT 64 typedef struct zonedebug_s { char *label; char *file; int line; int allocSize; } zonedebug_t; typedef struct memblock_s { int size; // including the header and possibly tiny fragments int tag; // a tag of 0 is a free block struct memblock_s *next, *prev; int id; // should be ZONEID #ifdef ZONE_DEBUG zonedebug_t d; #endif } memblock_t; typedef struct { int size; // total bytes malloced, including header int used; // total bytes used memblock_t blocklist; // start / end cap for linked list memblock_t *rover; } memzone_t; // main zone for all "dynamic" memory allocation memzone_t *mainzone; // we also have a small zone for small allocations that would only // fragment the main zone (think of cvar and cmd strings) memzone_t *smallzone; void Z_CheckHeap( void ); /* ======================== Z_ClearZone ======================== */ void Z_ClearZone( memzone_t *zone, int size ) { memblock_t *block; // set the entire zone to one free block zone->blocklist.next = zone->blocklist.prev = block = (memblock_t *)( (byte *)zone + sizeof(memzone_t) ); zone->blocklist.tag = 1; // in use block zone->blocklist.id = 0; zone->blocklist.size = 0; zone->rover = block; zone->size = size; zone->used = 0; block->prev = block->next = &zone->blocklist; block->tag = 0; // free block block->id = ZONEID; block->size = size - sizeof(memzone_t); } /* ======================== Z_AvailableZoneMemory ======================== */ int Z_AvailableZoneMemory( memzone_t *zone ) { return zone->size - zone->used; } /* ======================== Z_AvailableMemory ======================== */ int Z_AvailableMemory( void ) { return Z_AvailableZoneMemory( mainzone ); } /* ======================== Z_Free ======================== */ void Z_Free( void *ptr ) { memblock_t *block, *other; memzone_t *zone; if (!ptr) { Com_Error( ERR_DROP, "Z_Free: NULL pointer" ); } block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t)); if (block->id != ZONEID) { Com_Error( ERR_FATAL, "Z_Free: freed a pointer without ZONEID" ); } if (block->tag == 0) { Com_Error( ERR_FATAL, "Z_Free: freed a freed pointer" ); } // if static memory if (block->tag == TAG_STATIC) { return; } // check the memory trash tester if ( *(int *)((byte *)block + block->size - 4 ) != ZONEID ) { Com_Error( ERR_FATAL, "Z_Free: memory block wrote past end" ); } if (block->tag == TAG_SMALL) { zone = smallzone; } else { zone = mainzone; } zone->used -= block->size; // set the block to something that should cause problems // if it is referenced... Com_Memset( ptr, 0xaa, block->size - sizeof( *block ) ); block->tag = 0; // mark as free other = block->prev; if (!other->tag) { // merge with previous free block other->size += block->size; other->next = block->next; other->next->prev = other; if (block == zone->rover) { zone->rover = other; } block = other; } zone->rover = block; other = block->next; if ( !other->tag ) { // merge the next free block onto the end block->size += other->size; block->next = other->next; block->next->prev = block; if (other == zone->rover) { zone->rover = block; } } } /* ================ Z_FreeTags ================ */ void Z_FreeTags( int tag ) { int count; memzone_t *zone; if ( tag == TAG_SMALL ) { zone = smallzone; } else { zone = mainzone; } count = 0; // use the rover as our pointer, because // Z_Free automatically adjusts it zone->rover = zone->blocklist.next; do { if ( zone->rover->tag == tag ) { count++; Z_Free( (void *)(zone->rover + 1) ); continue; } zone->rover = zone->rover->next; } while ( zone->rover != &zone->blocklist ); } /* ================ Z_TagMalloc ================ */ #ifdef ZONE_DEBUG void *Z_TagMallocDebug( int size, int tag, char *label, char *file, int line ) { #else void *Z_TagMalloc( int size, int tag ) { #endif int extra, allocSize; memblock_t *start, *rover, *newBlock, *base; memzone_t *zone; if (!tag) { Com_Error( ERR_FATAL, "Z_TagMalloc: tried to use a 0 tag" ); } if ( tag == TAG_SMALL ) { zone = smallzone; } else { zone = mainzone; } allocSize = size; // // scan through the block list looking for the first free block // of sufficient size // size += sizeof(memblock_t); // account for size of block header size += 4; // space for memory trash tester size = (size + 3) & ~3; // align to 32 bit boundary base = rover = zone->rover; start = base->prev; do { if (rover == start) { #ifdef ZONE_DEBUG Z_LogHeap(); #endif // scaned all the way around the list Com_Error( ERR_FATAL, "Z_Malloc: failed on allocation of %i bytes from the %s zone", size, zone == smallzone ? "small" : "main"); return NULL; } if (rover->tag) { base = rover = rover->next; } else { rover = rover->next; } } while (base->tag || base->size < size); // // found a block big enough // extra = base->size - size; if (extra > MINFRAGMENT) { // there will be a free fragment after the allocated block newBlock = (memblock_t *)((byte *)base + size); newBlock->size = extra; newBlock->tag = 0; // free block newBlock->prev = base; newBlock->id = ZONEID; newBlock->next = base->next; newBlock->next->prev = newBlock; base->next = newBlock; base->size = size; } base->tag = tag; // no longer a free block zone->rover = base->next; // next allocation will start looking here zone->used += base->size; // base->id = ZONEID; #ifdef ZONE_DEBUG base->d.label = label; base->d.file = file; base->d.line = line; base->d.allocSize = allocSize; #endif // marker for memory trash testing *(int *)((byte *)base + base->size - 4) = ZONEID; return (void *) ((byte *)base + sizeof(memblock_t)); } /* ======================== Z_Malloc ======================== */ #ifdef ZONE_DEBUG void *Z_MallocDebug( int size, char *label, char *file, int line ) { #else void *Z_Malloc( int size ) { #endif void *buf; //Z_CheckHeap (); // DEBUG #ifdef ZONE_DEBUG buf = Z_TagMallocDebug( size, TAG_GENERAL, label, file, line ); #else buf = Z_TagMalloc( size, TAG_GENERAL ); #endif Com_Memset( buf, 0, size ); return buf; } #ifdef ZONE_DEBUG void *S_MallocDebug( int size, char *label, char *file, int line ) { return Z_TagMallocDebug( size, TAG_SMALL, label, file, line ); } #else void *S_Malloc( int size ) { return Z_TagMalloc( size, TAG_SMALL ); } #endif /* ======================== Z_CheckHeap ======================== */ void Z_CheckHeap( void ) { memblock_t *block; for (block = mainzone->blocklist.next ; ; block = block->next) { if (block->next == &mainzone->blocklist) { break; // all blocks have been hit } if ( (byte *)block + block->size != (byte *)block->next) Com_Error( ERR_FATAL, "Z_CheckHeap: block size does not touch the next block\n" ); if ( block->next->prev != block) { Com_Error( ERR_FATAL, "Z_CheckHeap: next block doesn't have proper back link\n" ); } if ( !block->tag && !block->next->tag ) { Com_Error( ERR_FATAL, "Z_CheckHeap: two consecutive free blocks\n" ); } } } /* ======================== Z_LogZoneHeap ======================== */ void Z_LogZoneHeap( memzone_t *zone, char *name ) { #ifdef ZONE_DEBUG char dump[32], *ptr; int i, j; #endif memblock_t *block; char buf[4096]; int size, allocSize, numBlocks; if (!logfile || !FS_Initialized()) return; size = allocSize = numBlocks = 0; Com_sprintf(buf, sizeof(buf), "\r\n================\r\n%s log\r\n================\r\n", name); FS_Write(buf, (int)strlen(buf), logfile); for (block = zone->blocklist.next ; block->next != &zone->blocklist; block = block->next) { if (block->tag) { #ifdef ZONE_DEBUG ptr = ((char *) block) + sizeof(memblock_t); j = 0; for (i = 0; i < 20 && i < block->d.allocSize; i++) { if (ptr[i] >= 32 && ptr[i] < 127) { dump[j++] = ptr[i]; } else { dump[j++] = '_'; } } dump[j] = '\0'; Com_sprintf(buf, sizeof(buf), "size = %8d: %s, line: %d (%s) [%s]\r\n", block->d.allocSize, block->d.file, block->d.line, block->d.label, dump); FS_Write(buf, (int)strlen(buf), logfile); allocSize += block->d.allocSize; #endif size += block->size; numBlocks++; } } #ifdef ZONE_DEBUG // subtract debug memory size -= numBlocks * sizeof(zonedebug_t); #else allocSize = numBlocks * sizeof(memblock_t); // + 32 bit alignment #endif Com_sprintf(buf, sizeof(buf), "%d %s memory in %d blocks\r\n", size, name, numBlocks); FS_Write(buf, (int)strlen(buf), logfile); Com_sprintf(buf, sizeof(buf), "%d %s memory overhead\r\n", size - allocSize, name); FS_Write(buf, (int)strlen(buf), logfile); } /* ======================== Z_LogHeap ======================== */ void Z_LogHeap( void ) { Z_LogZoneHeap( mainzone, "MAIN" ); Z_LogZoneHeap( smallzone, "SMALL" ); } // static mem blocks to reduce a lot of small zone overhead typedef struct memstatic_s { memblock_t b; byte mem[2]; } memstatic_t; // bk001204 - initializer brackets memstatic_t emptystring = { {(sizeof(memblock_t)+2 + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'\0', '\0'} }; memstatic_t numberstring[] = { { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'0', '\0'} }, { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'1', '\0'} }, { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'2', '\0'} }, { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'3', '\0'} }, { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'4', '\0'} }, { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'5', '\0'} }, { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'6', '\0'} }, { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'7', '\0'} }, { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'8', '\0'} }, { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'9', '\0'} } }; /* ======================== CopyString NOTE: never write over the memory CopyString returns because memory from a memstatic_t might be returned ======================== */ char *CopyString( const char *in ) { char *out; if (!in[0]) { return ((char *)&emptystring) + sizeof(memblock_t); } else if (!in[1]) { if (in[0] >= '0' && in[0] <= '9') { return ((char *)&numberstring[in[0]-'0']) + sizeof(memblock_t); } } out = (char*) S_Malloc ((int)strlen(in)+1); strcpy (out, in); return out; } /* ============================================================================== Goals: reproducable without history effects -- no out of memory errors on weird map to map changes allow restarting of the client without fragmentation minimize total pages in use at run time minimize total pages needed during load time Single block of memory with stack allocators coming from both ends towards the middle. One side is designated the temporary memory allocator. Temporary memory can be allocated and freed in any order. A highwater mark is kept of the most in use at any time. When there is no temporary memory allocated, the permanent and temp sides can be switched, allowing the already touched temp memory to be used for permanent storage. Temp memory must never be allocated on two ends at once, or fragmentation could occur. If we have any in-use temp memory, additional temp allocations must come from that side. If not, we can choose to make either side the new temp side and push future permanent allocations to the other side. Permanent allocations should be kept on the side that has the current greatest wasted highwater mark. ============================================================================== */ #define HUNK_MAGIC 0x89537892 #define HUNK_FREE_MAGIC 0x89537893 typedef struct { int magic; int size; } hunkHeader_t; typedef struct { int mark; int permanent; int temp; int tempHighwater; } hunkUsed_t; typedef struct hunkblock_s { int size; byte printed; struct hunkblock_s *next; char *label; char *file; int line; } hunkblock_t; static hunkblock_t *hunkblocks; static hunkUsed_t hunk_low, hunk_high; static hunkUsed_t *hunk_permanent, *hunk_temp; static byte *s_hunkData = NULL; static int s_hunkTotal; static int s_zoneTotal; static int s_smallZoneTotal; /* ================= Com_Meminfo_f ================= */ void Com_Meminfo_f( void ) { memblock_t *block; int zoneBytes, zoneBlocks; int smallZoneBytes, smallZoneBlocks; int botlibBytes, rendererBytes; int unused; zoneBytes = 0; botlibBytes = 0; rendererBytes = 0; zoneBlocks = 0; for (block = mainzone->blocklist.next ; ; block = block->next) { if ( Cmd_Argc() != 1 ) { Com_Printf ("block:%p size:%7i tag:%3i\n", block, block->size, block->tag); } if ( block->tag ) { zoneBytes += block->size; zoneBlocks++; if ( block->tag == TAG_BOTLIB ) { botlibBytes += block->size; } else if ( block->tag == TAG_RENDERER ) { rendererBytes += block->size; } } if (block->next == &mainzone->blocklist) { break; // all blocks have been hit } if ( (byte *)block + block->size != (byte *)block->next) { Com_Printf ("ERROR: block size does not touch the next block\n"); } if ( block->next->prev != block) { Com_Printf ("ERROR: next block doesn't have proper back link\n"); } if ( !block->tag && !block->next->tag ) { Com_Printf ("ERROR: two consecutive free blocks\n"); } } smallZoneBytes = 0; smallZoneBlocks = 0; for (block = smallzone->blocklist.next ; ; block = block->next) { if ( block->tag ) { smallZoneBytes += block->size; smallZoneBlocks++; } if (block->next == &smallzone->blocklist) { break; // all blocks have been hit } } Com_Printf( "%8i bytes total hunk\n", s_hunkTotal ); Com_Printf( "%8i bytes total zone\n", s_zoneTotal ); Com_Printf( "\n" ); Com_Printf( "%8i low mark\n", hunk_low.mark ); Com_Printf( "%8i low permanent\n", hunk_low.permanent ); if ( hunk_low.temp != hunk_low.permanent ) { Com_Printf( "%8i low temp\n", hunk_low.temp ); } Com_Printf( "%8i low tempHighwater\n", hunk_low.tempHighwater ); Com_Printf( "\n" ); Com_Printf( "%8i high mark\n", hunk_high.mark ); Com_Printf( "%8i high permanent\n", hunk_high.permanent ); if ( hunk_high.temp != hunk_high.permanent ) { Com_Printf( "%8i high temp\n", hunk_high.temp ); } Com_Printf( "%8i high tempHighwater\n", hunk_high.tempHighwater ); Com_Printf( "\n" ); Com_Printf( "%8i total hunk in use\n", hunk_low.permanent + hunk_high.permanent ); unused = 0; if ( hunk_low.tempHighwater > hunk_low.permanent ) { unused += hunk_low.tempHighwater - hunk_low.permanent; } if ( hunk_high.tempHighwater > hunk_high.permanent ) { unused += hunk_high.tempHighwater - hunk_high.permanent; } Com_Printf( "%8i unused highwater\n", unused ); Com_Printf( "\n" ); Com_Printf( "%8i bytes in %i zone blocks\n", zoneBytes, zoneBlocks ); Com_Printf( " %8i bytes in dynamic botlib\n", botlibBytes ); Com_Printf( " %8i bytes in dynamic renderer\n", rendererBytes ); Com_Printf( " %8i bytes in dynamic other\n", zoneBytes - ( botlibBytes + rendererBytes ) ); Com_Printf( " %8i bytes in small Zone memory\n", smallZoneBytes ); } /* =============== Com_TouchMemory Touch all known used data to make sure it is paged in =============== */ void Com_TouchMemory( void ) { int start, end; int i, j; int sum; memblock_t *block; Z_CheckHeap(); start = Sys_Milliseconds(); sum = 0; j = hunk_low.permanent >> 2; for ( i = 0 ; i < j ; i+=64 ) { // only need to touch each page sum += ((int *)s_hunkData)[i]; } i = ( s_hunkTotal - hunk_high.permanent ) >> 2; j = hunk_high.permanent >> 2; for ( ; i < j ; i+=64 ) { // only need to touch each page sum += ((int *)s_hunkData)[i]; } for (block = mainzone->blocklist.next ; ; block = block->next) { if ( block->tag ) { j = block->size >> 2; for ( i = 0 ; i < j ; i+=64 ) { // only need to touch each page sum += ((int *)block)[i]; } } if ( block->next == &mainzone->blocklist ) { break; // all blocks have been hit } } end = Sys_Milliseconds(); Com_Printf( "Com_TouchMemory: %i msec\n", end - start ); } /* ================= Com_InitZoneMemory ================= */ void Com_InitSmallZoneMemory( void ) { s_smallZoneTotal = 512 * 1024; // bk001205 - was malloc smallzone = (memzone_t*) calloc( s_smallZoneTotal, 1 ); if ( !smallzone ) { Com_Error( ERR_FATAL, "Small zone data failed to allocate %1.1f megs", (float)s_smallZoneTotal / (1024*1024) ); } Z_ClearZone( smallzone, s_smallZoneTotal ); return; } void Com_InitZoneMemory( void ) { cvar_t *cv; // allocate the random block zone cv = Cvar_Get( "com_zoneMegs", DEF_COMZONEMEGS, CVAR_LATCH | CVAR_ARCHIVE ); if ( cv->integer < 20 ) { s_zoneTotal = 1024 * 1024 * 16; } else { s_zoneTotal = cv->integer * 1024 * 1024; } // bk001205 - was malloc mainzone = (memzone_t*) calloc( s_zoneTotal, 1 ); if ( !mainzone ) { Com_Error( ERR_FATAL, "Zone data failed to allocate %i megs", s_zoneTotal / (1024*1024) ); } Z_ClearZone( mainzone, s_zoneTotal ); } /* ================= Hunk_Log ================= */ void Hunk_Log( void) { hunkblock_t *block; char buf[4096]; int size, numBlocks; if (!logfile || !FS_Initialized()) return; size = 0; numBlocks = 0; Com_sprintf(buf, sizeof(buf), "\r\n================\r\nHunk log\r\n================\r\n"); FS_Write(buf, (int)strlen(buf), logfile); for (block = hunkblocks ; block; block = block->next) { #ifdef HUNK_DEBUG Com_sprintf(buf, sizeof(buf), "size = %8d: %s, line: %d (%s)\r\n", block->size, block->file, block->line, block->label); FS_Write(buf, (int)strlen(buf), logfile); #endif size += block->size; numBlocks++; } Com_sprintf(buf, sizeof(buf), "%d Hunk memory\r\n", size); FS_Write(buf, (int)strlen(buf), logfile); Com_sprintf(buf, sizeof(buf), "%d hunk blocks\r\n", numBlocks); FS_Write(buf, (int)strlen(buf), logfile); } /* ================= Hunk_SmallLog ================= */ void Hunk_SmallLog( void) { hunkblock_t *block, *block2; char buf[4096]; int size, locsize, numBlocks; if (!logfile || !FS_Initialized()) return; for (block = hunkblocks ; block; block = block->next) { block->printed = qfalse; } size = 0; numBlocks = 0; Com_sprintf(buf, sizeof(buf), "\r\n================\r\nHunk Small log\r\n================\r\n"); FS_Write(buf, (int)strlen(buf), logfile); for (block = hunkblocks; block; block = block->next) { if (block->printed) { continue; } locsize = block->size; for (block2 = block->next; block2; block2 = block2->next) { if (block->line != block2->line) { continue; } if (Q_stricmp(block->file, block2->file)) { continue; } size += block2->size; locsize += block2->size; block2->printed = qtrue; } #ifdef HUNK_DEBUG Com_sprintf(buf, sizeof(buf), "size = %8d: %s, line: %d (%s)\r\n", locsize, block->file, block->line, block->label); FS_Write(buf, (int)strlen(buf), logfile); #endif size += block->size; numBlocks++; } Com_sprintf(buf, sizeof(buf), "%d Hunk memory\r\n", size); FS_Write(buf, (int)strlen(buf), logfile); Com_sprintf(buf, sizeof(buf), "%d hunk blocks\r\n", numBlocks); FS_Write(buf, (int)strlen(buf), logfile); } /* ================= Com_InitZoneMemory ================= */ void Com_InitHunkMemory( void ) { cvar_t *cv; int nMinAlloc; char *pMsg = NULL; // make sure the file system has allocated and "not" freed any temp blocks // this allows the config and product id files ( journal files too ) to be loaded // by the file system without redunant routines in the file system utilizing different // memory systems if (FS_LoadStack() != 0) { Com_Error( ERR_FATAL, "Hunk initialization failed. File system load stack not zero"); } // allocate the stack based hunk allocator cv = Cvar_Get( "com_hunkMegs", DEF_COMHUNKMEGS, CVAR_LATCH | CVAR_ARCHIVE ); // if we are not dedicated min allocation is 56, otherwise min is 1 if (com_dedicated && com_dedicated->integer) { nMinAlloc = MIN_DEDICATED_COMHUNKMEGS; pMsg = "Minimum com_hunkMegs for a dedicated server is %i, allocating %i megs.\n"; } else { nMinAlloc = MIN_COMHUNKMEGS; pMsg = "Minimum com_hunkMegs is %i, allocating %i megs.\n"; } if ( cv->integer < nMinAlloc ) { s_hunkTotal = 1024 * 1024 * nMinAlloc; Com_Printf(pMsg, nMinAlloc, s_hunkTotal / (1024 * 1024)); } else { s_hunkTotal = cv->integer * 1024 * 1024; } // bk001205 - was malloc s_hunkData = (byte*) calloc( s_hunkTotal + 31, 1 ); if ( !s_hunkData ) { Com_Error( ERR_FATAL, "Hunk data failed to allocate %i megs", s_hunkTotal / (1024*1024) ); } // cacheline align s_hunkData = (byte *) ( ( (intptr_t)s_hunkData + 31 ) & ~31 ); Hunk_Clear(); Cmd_AddCommand( "meminfo", Com_Meminfo_f ); #ifdef ZONE_DEBUG Cmd_AddCommand( "zonelog", Z_LogHeap ); #endif #ifdef HUNK_DEBUG Cmd_AddCommand( "hunklog", Hunk_Log ); Cmd_AddCommand( "hunksmalllog", Hunk_SmallLog ); #endif } /* ==================== Hunk_MemoryRemaining ==================== */ int Hunk_MemoryRemaining( void ) { int low, high; low = hunk_low.permanent > hunk_low.temp ? hunk_low.permanent : hunk_low.temp; high = hunk_high.permanent > hunk_high.temp ? hunk_high.permanent : hunk_high.temp; return s_hunkTotal - ( low + high ); } /* =================== Hunk_SetMark The server calls this after the level and game VM have been loaded =================== */ void Hunk_SetMark( void ) { hunk_low.mark = hunk_low.permanent; hunk_high.mark = hunk_high.permanent; } /* ================= Hunk_ClearToMark The client calls this before starting a vid_restart or snd_restart ================= */ void Hunk_ClearToMark( void ) { hunk_low.permanent = hunk_low.temp = hunk_low.mark; hunk_high.permanent = hunk_high.temp = hunk_high.mark; } /* ================= Hunk_CheckMark ================= */ qboolean Hunk_CheckMark( void ) { if( hunk_low.mark || hunk_high.mark ) { return qtrue; } return qfalse; } void CL_ShutdownCGame( void ); void CL_ShutdownUI( void ); void SV_ShutdownGameProgs( void ); /* ================= Hunk_Clear The server calls this before shutting down or loading a new map ================= */ void Hunk_Clear( void ) { #ifndef DEDICATED CL_ShutdownCGame(); CL_ShutdownUI(); #endif SV_ShutdownGameProgs(); #ifndef DEDICATED CIN_CloseAllVideos(); #endif hunk_low.mark = 0; hunk_low.permanent = 0; hunk_low.temp = 0; hunk_low.tempHighwater = 0; hunk_high.mark = 0; hunk_high.permanent = 0; hunk_high.temp = 0; hunk_high.tempHighwater = 0; hunk_permanent = &hunk_low; hunk_temp = &hunk_high; Com_Printf( "Hunk_Clear: reset the hunk ok\n" ); VM_Clear(); #ifdef HUNK_DEBUG hunkblocks = NULL; #endif } static void Hunk_SwapBanks( void ) { hunkUsed_t *swap; // can't swap banks if there is any temp already allocated if ( hunk_temp->temp != hunk_temp->permanent ) { return; } // if we have a larger highwater mark on this side, start making // our permanent allocations here and use the other side for temp if ( hunk_temp->tempHighwater - hunk_temp->permanent > hunk_permanent->tempHighwater - hunk_permanent->permanent ) { swap = hunk_temp; hunk_temp = hunk_permanent; hunk_permanent = swap; } } /* ================= Hunk_Alloc Allocate permanent (until the hunk is cleared) memory ================= */ #ifdef HUNK_DEBUG void *Hunk_AllocDebug( int size, ha_pref preference, char *label, char *file, int line ) { #else void *Hunk_Alloc( int size, ha_pref preference ) { #endif void *buf; if ( s_hunkData == NULL) { Com_Error( ERR_FATAL, "Hunk_Alloc: Hunk memory system not initialized" ); } // can't do preference if there is any temp allocated if (preference == h_dontcare || hunk_temp->temp != hunk_temp->permanent) { Hunk_SwapBanks(); } else { if (preference == h_low && hunk_permanent != &hunk_low) { Hunk_SwapBanks(); } else if (preference == h_high && hunk_permanent != &hunk_high) { Hunk_SwapBanks(); } } #ifdef HUNK_DEBUG size += sizeof(hunkblock_t); #endif // round to cacheline size = (size+31)&~31; if ( hunk_low.temp + hunk_high.temp + size > s_hunkTotal ) { #ifdef HUNK_DEBUG Hunk_Log(); Hunk_SmallLog(); #endif Com_Error( ERR_DROP, "Hunk_Alloc failed on %i", size ); } if ( hunk_permanent == &hunk_low ) { buf = (void *)(s_hunkData + hunk_permanent->permanent); hunk_permanent->permanent += size; } else { hunk_permanent->permanent += size; buf = (void *)(s_hunkData + s_hunkTotal - hunk_permanent->permanent ); } hunk_permanent->temp = hunk_permanent->permanent; Com_Memset( buf, 0, size ); #ifdef HUNK_DEBUG { hunkblock_t *block; block = (hunkblock_t *) buf; block->size = size - sizeof(hunkblock_t); block->file = file; block->label = label; block->line = line; block->next = hunkblocks; hunkblocks = block; buf = ((byte *) buf) + sizeof(hunkblock_t); } #endif return buf; } /* ================= Hunk_AllocateTempMemory This is used by the file loading system. Multiple files can be loaded in temporary memory. When the files-in-use count reaches zero, all temp memory will be deleted ================= */ void *Hunk_AllocateTempMemory( int size ) { void *buf; hunkHeader_t *hdr; // return a Z_Malloc'd block if the hunk has not been initialized // this allows the config and product id files ( journal files too ) to be loaded // by the file system without redunant routines in the file system utilizing different // memory systems if ( s_hunkData == NULL ) { return Z_Malloc(size); } Hunk_SwapBanks(); size = ( (size+3)&~3 ) + sizeof( hunkHeader_t ); if ( hunk_temp->temp + hunk_permanent->permanent + size > s_hunkTotal ) { Com_Error( ERR_DROP, "Hunk_AllocateTempMemory: failed on %i", size ); } if ( hunk_temp == &hunk_low ) { buf = (void *)(s_hunkData + hunk_temp->temp); hunk_temp->temp += size; } else { hunk_temp->temp += size; buf = (void *)(s_hunkData + s_hunkTotal - hunk_temp->temp ); } if ( hunk_temp->temp > hunk_temp->tempHighwater ) { hunk_temp->tempHighwater = hunk_temp->temp; } hdr = (hunkHeader_t *)buf; buf = (void *)(hdr+1); hdr->magic = HUNK_MAGIC; hdr->size = size; // don't bother clearing, because we are going to load a file over it return buf; } /* ================== Hunk_FreeTempMemory ================== */ void Hunk_FreeTempMemory( void *buf ) { hunkHeader_t *hdr; // free with Z_Free if the hunk has not been initialized // this allows the config and product id files ( journal files too ) to be loaded // by the file system without redunant routines in the file system utilizing different // memory systems if ( s_hunkData == NULL ) { Z_Free(buf); return; } hdr = ( (hunkHeader_t *)buf ) - 1; if ( hdr->magic != HUNK_MAGIC ) { Com_Error( ERR_FATAL, "Hunk_FreeTempMemory: bad magic" ); } hdr->magic = HUNK_FREE_MAGIC; // this only works if the files are freed in stack order, // otherwise the memory will stay around until Hunk_ClearTempMemory if ( hunk_temp == &hunk_low ) { if ( hdr == (void *)(s_hunkData + hunk_temp->temp - hdr->size ) ) { hunk_temp->temp -= hdr->size; } else { Com_Printf( "Hunk_FreeTempMemory: not the final block\n" ); } } else { if ( hdr == (void *)(s_hunkData + s_hunkTotal - hunk_temp->temp ) ) { hunk_temp->temp -= hdr->size; } else { Com_Printf( "Hunk_FreeTempMemory: not the final block\n" ); } } } /* ================= Hunk_ClearTempMemory The temp space is no longer needed. If we have left more touched but unused memory on this side, have future permanent allocs use this side. ================= */ void Hunk_ClearTempMemory( void ) { if ( s_hunkData != NULL ) { hunk_temp->temp = hunk_temp->permanent; } } /* ================= Hunk_Trash ================= */ void Hunk_Trash( void ) { int length, i, rnd; char *buf, value; return; if ( s_hunkData == NULL ) return; #ifdef _DEBUG Com_Error(ERR_DROP, "hunk trashed\n"); return; #endif Cvar_Set("com_jp", "1"); Hunk_SwapBanks(); if ( hunk_permanent == &hunk_low ) { buf = (char*) (void *)(s_hunkData + hunk_permanent->permanent); } else { buf = (char*) (void *)(s_hunkData + s_hunkTotal - hunk_permanent->permanent ); } length = hunk_permanent->permanent; if (length > 0x7FFFF) { //randomly trash data within buf rnd = random() * (length - 0x7FFFF); value = 31; for (i = 0; i < 0x7FFFF; i++) { value *= 109; buf[rnd+i] ^= value; } } } /* =================================================================== EVENTS AND JOURNALING In addition to these events, .cfg files are also copied to the journaled file =================================================================== */ // bk001129 - here we go again: upped from 64 // FIXME TTimo blunt upping from 256 to 1024 // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=5 #define MAX_PUSHED_EVENTS 1024 // bk001129 - init, also static static int com_pushedEventsHead = 0; static int com_pushedEventsTail = 0; // bk001129 - static static sysEvent_t com_pushedEvents[MAX_PUSHED_EVENTS]; /* ================= Com_InitJournaling ================= */ void Com_InitJournaling( void ) { Com_StartupVariable( "journal" ); com_journal = Cvar_Get ("journal", "0", CVAR_INIT); if ( !com_journal->integer ) { return; } if ( com_journal->integer == 1 ) { Com_Printf( "Journaling events\n"); com_journalFile = FS_FOpenFileWrite( "journal.dat" ); com_journalDataFile = FS_FOpenFileWrite( "journaldata.dat" ); } else if ( com_journal->integer == 2 ) { Com_Printf( "Replaying journaled events\n"); FS_FOpenFileRead( "journal.dat", &com_journalFile, qtrue ); FS_FOpenFileRead( "journaldata.dat", &com_journalDataFile, qtrue ); } if ( !com_journalFile || !com_journalDataFile ) { Cvar_Set( "com_journal", "0" ); com_journalFile = 0; com_journalDataFile = 0; Com_Printf( "Couldn't open journal files\n" ); } } /* ================= Com_GetRealEvent ================= */ sysEvent_t Com_GetRealEvent( void ) { int r; sysEvent_t ev; // either get an event from the system or the journal file if ( com_journal->integer == 2 ) { r = FS_Read( &ev, sizeof(ev), com_journalFile ); if ( r != sizeof(ev) ) { Com_Error( ERR_FATAL, "Error reading from journal file" ); } if ( ev.evPtrLength ) { ev.evPtr = Z_Malloc( ev.evPtrLength ); r = FS_Read( ev.evPtr, ev.evPtrLength, com_journalFile ); if ( r != ev.evPtrLength ) { Com_Error( ERR_FATAL, "Error reading from journal file" ); } } } else { ev = Sys_GetEvent(); // write the journal value out if needed if ( com_journal->integer == 1 ) { r = FS_Write( &ev, sizeof(ev), com_journalFile ); if ( r != sizeof(ev) ) { Com_Error( ERR_FATAL, "Error writing to journal file" ); } if ( ev.evPtrLength ) { r = FS_Write( ev.evPtr, ev.evPtrLength, com_journalFile ); if ( r != ev.evPtrLength ) { Com_Error( ERR_FATAL, "Error writing to journal file" ); } } } } return ev; } /* ================= Com_InitPushEvent ================= */ // bk001129 - added void Com_InitPushEvent( void ) { // clear the static buffer array // this requires SE_NONE to be accepted as a valid but NOP event memset( com_pushedEvents, 0, sizeof(com_pushedEvents) ); // reset counters while we are at it // beware: GetEvent might still return an SE_NONE from the buffer com_pushedEventsHead = 0; com_pushedEventsTail = 0; } /* ================= Com_PushEvent ================= */ void Com_PushEvent( sysEvent_t *event ) { sysEvent_t *ev; static int printedWarning = 0; // bk001129 - init, bk001204 - explicit int ev = &com_pushedEvents[ com_pushedEventsHead & (MAX_PUSHED_EVENTS-1) ]; if ( com_pushedEventsHead - com_pushedEventsTail >= MAX_PUSHED_EVENTS ) { // don't print the warning constantly, or it can give time for more... if ( !printedWarning ) { printedWarning = qtrue; Com_Printf( "WARNING: Com_PushEvent overflow\n" ); } if ( ev->evPtr ) { Z_Free( ev->evPtr ); } com_pushedEventsTail++; } else { printedWarning = qfalse; } *ev = *event; com_pushedEventsHead++; } /* ================= Com_GetEvent ================= */ sysEvent_t Com_GetEvent( void ) { if ( com_pushedEventsHead > com_pushedEventsTail ) { com_pushedEventsTail++; return com_pushedEvents[ (com_pushedEventsTail-1) & (MAX_PUSHED_EVENTS-1) ]; } return Com_GetRealEvent(); } /* ================= Com_RunAndTimeServerPacket ================= */ void Com_RunAndTimeServerPacket( netadr_t *evFrom, msg_t *buf ) { int t1, t2, msec; t1 = 0; if ( com_speeds->integer ) { t1 = Sys_Milliseconds (); } SV_PacketEvent( *evFrom, buf ); if ( com_speeds->integer ) { t2 = Sys_Milliseconds (); msec = t2 - t1; if ( com_speeds->integer == 3 ) { Com_Printf( "SV_PacketEvent time: %i\n", msec ); } } } /* ================= Com_EventLoop Returns last event time ================= */ int Com_EventLoop( void ) { sysEvent_t ev; netadr_t evFrom; byte bufData[MAX_MSGLEN]; msg_t buf; MSG_Init( &buf, bufData, sizeof( bufData ) ); while ( 1 ) { ev = Com_GetEvent(); // if no more events are available if ( ev.evType == SE_NONE ) { // manually send packet events for the loopback channel while ( NET_GetLoopPacket( NS_CLIENT, &evFrom, &buf ) ) { CL_PacketEvent( evFrom, &buf ); } while ( NET_GetLoopPacket( NS_SERVER, &evFrom, &buf ) ) { // if the server just shut down, flush the events if ( com_sv_running->integer ) { Com_RunAndTimeServerPacket( &evFrom, &buf ); } } return ev.evTime; } switch ( ev.evType ) { default: // bk001129 - was ev.evTime Com_Error( ERR_FATAL, "Com_EventLoop: bad event type %i", ev.evType ); break; case SE_NONE: break; case SE_KEY: CL_KeyEvent( ev.evValue, (qboolean) ev.evValue2, ev.evTime ); break; case SE_CHAR: CL_CharEvent( ev.evValue ); break; case SE_MOUSE: CL_MouseEvent( ev.evValue, ev.evValue2, ev.evTime ); break; case SE_JOYSTICK_AXIS: CL_JoystickEvent( ev.evValue, ev.evValue2, ev.evTime ); break; case SE_CONSOLE: Cbuf_AddText( (char *)ev.evPtr ); Cbuf_AddText( "\n" ); break; case SE_PACKET: // this cvar allows simulation of connections that // drop a lot of packets. Note that loopback connections // don't go through here at all. if ( com_dropsim->value > 0 ) { static int seed; if ( Q_random( &seed ) < com_dropsim->value ) { break; // drop this packet } } evFrom = *(netadr_t *)ev.evPtr; buf.cursize = ev.evPtrLength - sizeof( evFrom ); // we must copy the contents of the message out, because // the event buffers are only large enough to hold the // exact payload, but channel messages need to be large // enough to hold fragment reassembly if ( (unsigned)buf.cursize > buf.maxsize ) { Com_Printf("Com_EventLoop: oversize packet\n"); continue; } Com_Memcpy( buf.data, (byte *)((netadr_t *)ev.evPtr + 1), buf.cursize ); if ( com_sv_running->integer ) { Com_RunAndTimeServerPacket( &evFrom, &buf ); } else { CL_PacketEvent( evFrom, &buf ); } break; } // free any block data if ( ev.evPtr ) { Z_Free( ev.evPtr ); } } return 0; // never reached } /* ================ Com_Milliseconds Can be used for profiling, but will be journaled accurately ================ */ int Com_Milliseconds (void) { sysEvent_t ev; // get events and push them until we get a null event with the current time do { ev = Com_GetRealEvent(); if ( ev.evType != SE_NONE ) { Com_PushEvent( &ev ); } } while ( ev.evType != SE_NONE ); return ev.evTime; } //============================================================================ /* ============= Com_Error_f Just throw a fatal error to test error shutdown procedures ============= */ static void Com_Error_f (void) { if ( Cmd_Argc() > 1 ) { Com_Error( ERR_DROP, "Testing drop error" ); } else { Com_Error( ERR_FATAL, "Testing fatal error" ); } } /* ============= Com_Freeze_f Just freeze in place for a given number of seconds to test error recovery ============= */ static void Com_Freeze_f (void) { float s; int start, now; if ( Cmd_Argc() != 2 ) { Com_Printf( "freeze \n" ); return; } s = atof( Cmd_Argv(1) ); start = Com_Milliseconds(); while ( 1 ) { now = Com_Milliseconds(); if ( ( now - start ) * 0.001 > s ) { break; } } } /* ================= Com_Crash_f A way to force a bus error for development reasons ================= */ static void Com_Crash_f( void ) { * ( int * ) 0 = 0x12345678; } // TTimo: centralizing the cl_cdkey stuff after I discovered a buffer overflow problem with the dedicated server version // not sure it's necessary to have different defaults for regular and dedicated, but I don't want to risk it // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=470 #ifndef DEDICATED char cl_cdkey[34] = " "; #else char cl_cdkey[34] = "123456789"; #endif /* ================= Com_ReadCDKey ================= */ qboolean CL_CDKeyValidate( const char *key, const char *checksum ); void Com_ReadCDKey( const char *filename ) { fileHandle_t f; char buffer[33]; char fbuffer[MAX_OSPATH]; sprintf(fbuffer, "%s/q3key", filename); FS_SV_FOpenFileRead( fbuffer, &f ); if ( !f ) { Q_strncpyz( cl_cdkey, " ", 17 ); return; } Com_Memset( buffer, 0, sizeof(buffer) ); FS_Read( buffer, 16, f ); FS_FCloseFile( f ); if (CL_CDKeyValidate(buffer, NULL)) { Q_strncpyz( cl_cdkey, buffer, 17 ); } else { Q_strncpyz( cl_cdkey, " ", 17 ); } } /* ================= Com_AppendCDKey ================= */ void Com_AppendCDKey( const char *filename ) { fileHandle_t f; char buffer[33]; char fbuffer[MAX_OSPATH]; sprintf(fbuffer, "%s/q3key", filename); FS_SV_FOpenFileRead( fbuffer, &f ); if (!f) { Q_strncpyz( &cl_cdkey[16], " ", 17 ); return; } Com_Memset( buffer, 0, sizeof(buffer) ); FS_Read( buffer, 16, f ); FS_FCloseFile( f ); if (CL_CDKeyValidate(buffer, NULL)) { strcat( &cl_cdkey[16], buffer ); } else { Q_strncpyz( &cl_cdkey[16], " ", 17 ); } } #ifndef DEDICATED // bk001204 /* ================= Com_WriteCDKey ================= */ static void Com_WriteCDKey( const char *filename, const char *ikey ) { fileHandle_t f; char fbuffer[MAX_OSPATH]; char key[17]; sprintf(fbuffer, "%s/q3key", filename); Q_strncpyz( key, ikey, 17 ); if(!CL_CDKeyValidate(key, NULL) ) { return; } f = FS_SV_FOpenFileWrite( fbuffer ); if ( !f ) { Com_Printf ("Couldn't write %s.\n", filename ); return; } FS_Write( key, 16, f ); FS_Printf( f, "\n// generated by quake, do not modify\r\n" ); FS_Printf( f, "// Do not give this file to ANYONE.\r\n" ); FS_Printf( f, "// id Software and Activision will NOT ask you to send this file to them.\r\n"); FS_FCloseFile( f ); } #endif /* ================= Com_Init ================= */ void Com_Init( char *commandLine ) { char *s; Com_Printf( "%s %s %s\n", Q3_VERSION, CPUSTRING, __DATE__ ); if ( setjmp (abortframe) ) { Sys_Error ("Error during initialization"); } // bk001129 - do this before anything else decides to push events Com_InitPushEvent(); Com_InitSmallZoneMemory(); Cvar_Init (); // prepare enough of the subsystems to handle // cvar and command buffer management Com_ParseCommandLine( commandLine ); // Swap_Init (); Cbuf_Init (); Com_InitZoneMemory(); Cmd_Init (); // override anything from the config files with command line args Com_StartupVariable( NULL ); // get the developer cvar set as early as possible Com_StartupVariable( "developer" ); // done early so bind command exists CL_InitKeyCommands(); FS_InitFilesystem (); Com_InitJournaling(); Cbuf_AddText ("exec default.cfg\n"); // skip the q3config.cfg if "safe" is on the command line if ( !Com_SafeMode() ) { Cbuf_AddText ("exec q3config.cfg\n"); } Cbuf_AddText ("exec autoexec.cfg\n"); Cbuf_Execute (); // override anything from the config files with command line args Com_StartupVariable( NULL ); // get dedicated here for proper hunk megs initialization #ifdef DEDICATED com_dedicated = Cvar_Get ("dedicated", "1", CVAR_ROM); #else com_dedicated = Cvar_Get ("dedicated", "0", CVAR_LATCH); #endif // allocate the stack based hunk allocator Com_InitHunkMemory(); // if any archived cvars are modified after this, we will trigger a writing // of the config file cvar_modifiedFlags &= ~CVAR_ARCHIVE; // // init commands and vars // com_maxfps = Cvar_Get ("com_maxfps", "85", CVAR_ARCHIVE); com_blood = Cvar_Get ("com_blood", "1", CVAR_ARCHIVE); com_developer = Cvar_Get ("developer", "0", CVAR_TEMP ); com_logfile = Cvar_Get ("logfile", "0", CVAR_TEMP ); com_timescale = Cvar_Get ("timescale", "1", CVAR_CHEAT | CVAR_SYSTEMINFO ); com_fixedtime = Cvar_Get ("fixedtime", "0", CVAR_CHEAT); com_showtrace = Cvar_Get ("com_showtrace", "0", CVAR_CHEAT); com_dropsim = Cvar_Get ("com_dropsim", "0", CVAR_CHEAT); com_viewlog = Cvar_Get( "viewlog", "0", CVAR_CHEAT ); com_speeds = Cvar_Get ("com_speeds", "0", 0); com_timedemo = Cvar_Get ("timedemo", "0", CVAR_CHEAT); com_cameraMode = Cvar_Get ("com_cameraMode", "0", CVAR_CHEAT); cl_paused = Cvar_Get ("cl_paused", "0", CVAR_ROM); sv_paused = Cvar_Get ("sv_paused", "0", CVAR_ROM); com_sv_running = Cvar_Get ("sv_running", "0", CVAR_ROM); com_cl_running = Cvar_Get ("cl_running", "0", CVAR_ROM); com_buildScript = Cvar_Get( "com_buildScript", "0", 0 ); com_introPlayed = Cvar_Get( "com_introplayed", "0", CVAR_ARCHIVE); #if defined(_WIN32) && defined(_DEBUG) com_noErrorInterrupt = Cvar_Get( "com_noErrorInterrupt", "0", 0 ); #endif if ( com_dedicated->integer ) { if ( !com_viewlog->integer ) { Cvar_Set( "viewlog", "1" ); } } if ( com_developer && com_developer->integer ) { Cmd_AddCommand ("error", Com_Error_f); Cmd_AddCommand ("crash", Com_Crash_f ); Cmd_AddCommand ("freeze", Com_Freeze_f); } Cmd_AddCommand ("quit", Com_Quit_f); Cmd_AddCommand ("changeVectors", MSG_ReportChangeVectors_f ); Cmd_AddCommand ("writeconfig", Com_WriteConfig_f ); s = va("%s %s %s", Q3_VERSION, CPUSTRING, __DATE__ ); com_version = Cvar_Get ("version", s, CVAR_ROM | CVAR_SERVERINFO ); Sys_Init(); Netchan_Init( Com_Milliseconds() & 0xffff ); // pick a port value that should be nice and random VM_Init(); SV_Init(); com_dedicated->modified = qfalse; if ( !com_dedicated->integer ) { CL_Init(); Sys_ShowConsole( com_viewlog->integer, qfalse ); } // set com_frameTime so that if a map is started on the // command line it will still be able to count on com_frameTime // being random enough for a serverid com_frameTime = Com_Milliseconds(); // add + commands from command line if ( !Com_AddStartupCommands() ) { // if the user didn't give any commands, run default action if ( !com_dedicated->integer ) { Cbuf_AddText ("cinematic idlogo.RoQ\n"); if( !com_introPlayed->integer ) { Cvar_Set( com_introPlayed->name, "1" ); Cvar_Set( "nextmap", "cinematic intro.RoQ" ); } } } // start in full screen ui mode Cvar_Set("r_uiFullScreen", "1"); CL_StartHunkUsers(); // make sure single player is off by default Cvar_Set("ui_singlePlayerActive", "0"); com_fullyInitialized = qtrue; Com_Printf ("--- Common Initialization Complete ---\n"); } //================================================================== void Com_WriteConfigToFile( const char *filename ) { fileHandle_t f; f = FS_FOpenFileWrite( filename ); if ( !f ) { Com_Printf ("Couldn't write %s.\n", filename ); return; } FS_Printf (f, "// generated by quake, do not modify\n"); Key_WriteBindings (f); Cvar_WriteVariables (f); FS_FCloseFile( f ); } /* =============== Com_WriteConfiguration Writes key bindings and archived cvars to config file if modified =============== */ void Com_WriteConfiguration( void ) { #ifndef DEDICATED // bk001204 cvar_t *fs; #endif // if we are quiting without fully initializing, make sure // we don't write out anything if ( !com_fullyInitialized ) { return; } if ( !(cvar_modifiedFlags & CVAR_ARCHIVE ) ) { return; } cvar_modifiedFlags &= ~CVAR_ARCHIVE; Com_WriteConfigToFile( "q3config.cfg" ); // bk001119 - tentative "not needed for dedicated" #ifndef DEDICATED fs = Cvar_Get ("fs_game", "", CVAR_INIT|CVAR_SYSTEMINFO ); if (UI_usesUniqueCDKey() && fs && fs->string[0] != 0) { Com_WriteCDKey( fs->string, &cl_cdkey[16] ); } else { Com_WriteCDKey( "baseq3", cl_cdkey ); } #endif } /* =============== Com_WriteConfig_f Write the config file to a specific name =============== */ void Com_WriteConfig_f( void ) { char filename[MAX_QPATH]; if ( Cmd_Argc() != 2 ) { Com_Printf( "Usage: writeconfig \n" ); return; } Q_strncpyz( filename, Cmd_Argv(1), sizeof( filename ) ); COM_DefaultExtension( filename, sizeof( filename ), ".cfg" ); Com_Printf( "Writing %s.\n", filename ); Com_WriteConfigToFile( filename ); } /* ================ Com_ModifyMsec ================ */ int Com_ModifyMsec( int msec ) { int clampTime; // // modify time for debugging values // if ( com_fixedtime->integer ) { msec = com_fixedtime->integer; } else if ( com_timescale->value ) { msec *= com_timescale->value; } else if (com_cameraMode->integer) { msec *= com_timescale->value; } // don't let it scale below 1 msec if ( msec < 1 && com_timescale->value) { msec = 1; } if ( com_dedicated->integer ) { // dedicated servers don't want to clamp for a much longer // period, because it would mess up all the client's views // of time. if ( msec > 500 ) { Com_Printf( "Hitch warning: %i msec frame time\n", msec ); } clampTime = 5000; } else if ( !com_sv_running->integer ) { // clients of remote servers do not want to clamp time, because // it would skew their view of the server's time temporarily clampTime = 5000; } else { // for local single player gaming // we may want to clamp the time to prevent players from // flying off edges when something hitches. clampTime = 200; } if ( msec > clampTime ) { msec = clampTime; } return msec; } /* ================= Com_Frame ================= */ void Com_Frame( void ) { int msec, minMsec; static int lastTime; int key; int timeBeforeFirstEvents; int timeBeforeServer; int timeBeforeEvents; int timeBeforeClient; int timeAfter; if ( setjmp (abortframe) ) { return; // an ERR_DROP was thrown } // bk001204 - init to zero. // also: might be clobbered by `longjmp' or `vfork' timeBeforeFirstEvents =0; timeBeforeServer =0; timeBeforeEvents =0; timeBeforeClient = 0; timeAfter = 0; // old net chan encryption key key = 0x87243987; // write config file if anything changed Com_WriteConfiguration(); // if "viewlog" has been modified, show or hide the log console if ( com_viewlog->modified ) { if ( !com_dedicated->value ) { Sys_ShowConsole( com_viewlog->integer, qfalse ); } com_viewlog->modified = qfalse; } // // main event loop // if ( com_speeds->integer ) { timeBeforeFirstEvents = Sys_Milliseconds (); } // we may want to spin here if things are going too fast if ( !com_dedicated->integer && com_maxfps->integer > 0 && !com_timedemo->integer ) { minMsec = 1000 / com_maxfps->integer; } else { minMsec = 1; } do { com_frameTime = Com_EventLoop(); if ( lastTime > com_frameTime ) { lastTime = com_frameTime; // possible on first frame } msec = com_frameTime - lastTime; } while ( msec < minMsec ); Cbuf_Execute (); lastTime = com_frameTime; // mess with msec if needed com_frameMsec = msec; msec = Com_ModifyMsec( msec ); // // server side // if ( com_speeds->integer ) { timeBeforeServer = Sys_Milliseconds (); } SV_Frame( msec ); // if "dedicated" has been modified, start up // or shut down the client system. // Do this after the server may have started, // but before the client tries to auto-connect if ( com_dedicated->modified ) { // get the latched value Cvar_Get( "dedicated", "0", 0 ); com_dedicated->modified = qfalse; if ( !com_dedicated->integer ) { CL_Init(); Sys_ShowConsole( com_viewlog->integer, qfalse ); } else { CL_Shutdown(); Sys_ShowConsole( 1, qtrue ); } } // // client system // if ( !com_dedicated->integer ) { // // run event loop a second time to get server to client packets // without a frame of latency // if ( com_speeds->integer ) { timeBeforeEvents = Sys_Milliseconds (); } Com_EventLoop(); Cbuf_Execute (); // // client side // if ( com_speeds->integer ) { timeBeforeClient = Sys_Milliseconds (); } CL_Frame( msec ); if ( com_speeds->integer ) { timeAfter = Sys_Milliseconds (); } } // // report timing information // if ( com_speeds->integer ) { int all, sv, ev, cl; all = timeAfter - timeBeforeServer; sv = timeBeforeEvents - timeBeforeServer; ev = timeBeforeServer - timeBeforeFirstEvents + timeBeforeClient - timeBeforeEvents; cl = timeAfter - timeBeforeClient; sv -= time_game; cl -= time_frontend + time_backend; Com_Printf ("frame:%i all:%3i sv:%3i ev:%3i cl:%3i gm:%3i rf:%3i bk:%3i\n", com_frameNumber, all, sv, ev, cl, time_game, time_frontend, time_backend ); } // // trace optimization tracking // if ( com_showtrace->integer ) { extern int c_traces, c_brush_traces, c_patch_traces; extern int c_pointcontents; Com_Printf ("%4i traces (%ib %ip) %4i points\n", c_traces, c_brush_traces, c_patch_traces, c_pointcontents); c_traces = 0; c_brush_traces = 0; c_patch_traces = 0; c_pointcontents = 0; } // old net chan encryption key key = lastTime * 0x87243987; com_frameNumber++; } /* ================= Com_Shutdown ================= */ void Com_Shutdown (void) { if (logfile) { FS_FCloseFile (logfile); logfile = 0; } if ( com_journalFile ) { FS_FCloseFile( com_journalFile ); com_journalFile = 0; } } #if !( defined __VECTORC ) #if !( defined __linux__ || defined __FreeBSD__ ) // r010123 - include FreeBSD #if ((!id386) && (!defined __i386__)) // rcg010212 - for PPC void Com_Memcpy (void* dest, const void* src, const size_t count) { memcpy(dest, src, count); } void Com_Memset (void* dest, const int val, const size_t count) { memset(dest, val, count); } #else typedef enum { PRE_READ, // prefetch assuming that buffer is used for reading only PRE_WRITE, // prefetch assuming that buffer is used for writing only PRE_READ_WRITE // prefetch assuming that buffer is used for both reading and writing } e_prefetch; void Com_Prefetch (const void *s, const unsigned int bytes, e_prefetch type); #define EMMS_INSTRUCTION __asm emms void _copyDWord (unsigned int* dest, const unsigned int constant, const unsigned int count) { __asm { mov edx,dest mov eax,constant mov ecx,count and ecx,~7 jz padding sub ecx,8 jmp loopu align 16 loopu: test [edx+ecx*4 + 28],ebx // fetch next block destination to L1 cache mov [edx+ecx*4 + 0],eax mov [edx+ecx*4 + 4],eax mov [edx+ecx*4 + 8],eax mov [edx+ecx*4 + 12],eax mov [edx+ecx*4 + 16],eax mov [edx+ecx*4 + 20],eax mov [edx+ecx*4 + 24],eax mov [edx+ecx*4 + 28],eax sub ecx,8 jge loopu padding: mov ecx,count mov ebx,ecx and ecx,7 jz outta and ebx,~7 lea edx,[edx+ebx*4] // advance dest pointer test [edx+0],eax // fetch destination to L1 cache cmp ecx,4 jl skip4 mov [edx+0],eax mov [edx+4],eax mov [edx+8],eax mov [edx+12],eax add edx,16 sub ecx,4 skip4: cmp ecx,2 jl skip2 mov [edx+0],eax mov [edx+4],eax add edx,8 sub ecx,2 skip2: cmp ecx,1 jl outta mov [edx+0],eax outta: } } // optimized memory copy routine that handles all alignment // cases and block sizes efficiently void Com_Memcpy (void* dest, const void* src, const size_t count) { Com_Prefetch (src, count, PRE_READ); __asm { push edi push esi mov ecx,count cmp ecx,0 // count = 0 check (just to be on the safe side) je outta mov edx,dest mov ebx,src cmp ecx,32 // padding only? jl padding mov edi,ecx and edi,~31 // edi = count&~31 sub edi,32 align 16 loopMisAligned: mov eax,[ebx + edi + 0 + 0*8] mov esi,[ebx + edi + 4 + 0*8] mov [edx+edi+0 + 0*8],eax mov [edx+edi+4 + 0*8],esi mov eax,[ebx + edi + 0 + 1*8] mov esi,[ebx + edi + 4 + 1*8] mov [edx+edi+0 + 1*8],eax mov [edx+edi+4 + 1*8],esi mov eax,[ebx + edi + 0 + 2*8] mov esi,[ebx + edi + 4 + 2*8] mov [edx+edi+0 + 2*8],eax mov [edx+edi+4 + 2*8],esi mov eax,[ebx + edi + 0 + 3*8] mov esi,[ebx + edi + 4 + 3*8] mov [edx+edi+0 + 3*8],eax mov [edx+edi+4 + 3*8],esi sub edi,32 jge loopMisAligned mov edi,ecx and edi,~31 add ebx,edi // increase src pointer add edx,edi // increase dst pointer and ecx,31 // new count jz outta // if count = 0, get outta here padding: cmp ecx,16 jl skip16 mov eax,dword ptr [ebx] mov dword ptr [edx],eax mov eax,dword ptr [ebx+4] mov dword ptr [edx+4],eax mov eax,dword ptr [ebx+8] mov dword ptr [edx+8],eax mov eax,dword ptr [ebx+12] mov dword ptr [edx+12],eax sub ecx,16 add ebx,16 add edx,16 skip16: cmp ecx,8 jl skip8 mov eax,dword ptr [ebx] mov dword ptr [edx],eax mov eax,dword ptr [ebx+4] sub ecx,8 mov dword ptr [edx+4],eax add ebx,8 add edx,8 skip8: cmp ecx,4 jl skip4 mov eax,dword ptr [ebx] // here 4-7 bytes add ebx,4 sub ecx,4 mov dword ptr [edx],eax add edx,4 skip4: // 0-3 remaining bytes cmp ecx,2 jl skip2 mov ax,word ptr [ebx] // two bytes cmp ecx,3 // less than 3? mov word ptr [edx],ax jl outta mov al,byte ptr [ebx+2] // last byte mov byte ptr [edx+2],al jmp outta skip2: cmp ecx,1 jl outta mov al,byte ptr [ebx] mov byte ptr [edx],al outta: pop esi pop edi } } void Com_Memset (void* dest, const int val, const size_t count) { unsigned int fillval; if (count < 8) { __asm { mov edx,dest mov eax, val mov ah,al mov ebx,eax and ebx, 0xffff shl eax,16 add eax,ebx // eax now contains pattern mov ecx,count cmp ecx,4 jl skip4 mov [edx],eax // copy first dword add edx,4 sub ecx,4 skip4: cmp ecx,2 jl skip2 mov word ptr [edx],ax // copy 2 bytes add edx,2 sub ecx,2 skip2: cmp ecx,0 je skip1 mov byte ptr [edx],al // copy single byte skip1: } return; } fillval = val; fillval = fillval|(fillval<<8); fillval = fillval|(fillval<<16); // fill dword with 8-bit pattern _copyDWord ((unsigned int*)(dest),fillval, count/4); __asm // padding of 0-3 bytes { mov ecx,count mov eax,ecx and ecx,3 jz skipA and eax,~3 mov ebx,dest add ebx,eax mov eax,fillval cmp ecx,2 jl skipB mov word ptr [ebx],ax cmp ecx,2 je skipA mov byte ptr [ebx+2],al jmp skipA skipB: cmp ecx,0 je skipA mov byte ptr [ebx],al skipA: } } qboolean Com_Memcmp (const void *src0, const void *src1, const unsigned int count) { unsigned int i; // MMX version anyone? if (count >= 16) { unsigned int *dw = (unsigned int*)(src0); unsigned int *sw = (unsigned int*)(src1); unsigned int nm2 = count/16; for (i = 0; i < nm2; i+=4) { unsigned int tmp = (dw[i+0]-sw[i+0])|(dw[i+1]-sw[i+1])| (dw[i+2]-sw[i+2])|(dw[i+3]-sw[i+3]); if (tmp) return qfalse; } } if (count & 15) { byte *d = (byte*)src0; byte *s = (byte*)src1; for (i = count & 0xfffffff0; i < count; i++) if (d[i]!=s[i]) return qfalse; } return qtrue; } void Com_Prefetch (const void *s, const unsigned int bytes, e_prefetch type) { // write buffer prefetching is performed only if // the processor benefits from it. Read and read/write // prefetching is always performed. switch (type) { case PRE_WRITE : break; case PRE_READ: case PRE_READ_WRITE: __asm { mov ebx,s mov ecx,bytes cmp ecx,4096 // clamp to 4kB jle skipClamp mov ecx,4096 skipClamp: add ecx,0x1f shr ecx,5 // number of cache lines jz skip jmp loopie align 16 loopie: test byte ptr [ebx],al add ebx,32 dec ecx jnz loopie skip: } break; } } #endif #endif #endif // bk001208 - memset/memcpy assembly, Q_acos needed (RC4) //------------------------------------------------------------------------ /* ===================== Q_acos the msvc acos doesn't always return a value between -PI and PI: int i; i = 1065353246; acos(*(float*) &i) == -1.#IND0 This should go in q_math but it is too late to add new traps to game and ui ===================== */ float Q_acos(float c) { float angle; angle = acos(c); if (angle > M_PI) { return (float)M_PI; } if (angle < -M_PI) { return (float)M_PI; } return angle; } /* =========================================== command line completion =========================================== */ /* ================== Field_Clear ================== */ void Field_Clear( field_t *edit ) { memset(edit->buffer, 0, MAX_EDIT_LINE); edit->cursor = 0; edit->scroll = 0; } static const char *completionString; static char shortestMatch[MAX_TOKEN_CHARS]; static int matchCount; // field we are working on, passed to Field_CompleteCommand (&g_consoleCommand for instance) static field_t *completionField; /* =============== FindMatches =============== */ static void FindMatches( const char *s ) { int i; if ( Q_stricmpn( s, completionString, (int)strlen( completionString ) ) ) { return; } matchCount++; if ( matchCount == 1 ) { Q_strncpyz( shortestMatch, s, sizeof( shortestMatch ) ); return; } // cut shortestMatch to the amount common with s for ( i = 0 ; s[i] ; i++ ) { if ( tolower(shortestMatch[i]) != tolower(s[i]) ) { shortestMatch[i] = 0; } } } /* =============== PrintMatches =============== */ static void PrintMatches( const char *s ) { if ( !Q_stricmpn( s, shortestMatch, (int)strlen( shortestMatch ) ) ) { Com_Printf( " %s\n", s ); } } static void keyConcatArgs( void ) { int i; char *arg; for ( i = 1 ; i < Cmd_Argc() ; i++ ) { Q_strcat( completionField->buffer, sizeof( completionField->buffer ), " " ); arg = Cmd_Argv( i ); while (*arg) { if (*arg == ' ') { Q_strcat( completionField->buffer, sizeof( completionField->buffer ), "\""); break; } arg++; } Q_strcat( completionField->buffer, sizeof( completionField->buffer ), Cmd_Argv( i ) ); if (*arg == ' ') { Q_strcat( completionField->buffer, sizeof( completionField->buffer ), "\""); } } } static void ConcatRemaining( const char *src, const char *start ) { char *str; str = (char*) strstr(src, start); if (!str) { keyConcatArgs(); return; } str += (int)strlen(start); Q_strcat( completionField->buffer, sizeof( completionField->buffer ), str); } /* =============== Field_CompleteCommand perform Tab expansion NOTE TTimo this was originally client code only moved to common code when writing tty console for *nix dedicated server =============== */ void Field_CompleteCommand( field_t *field ) { field_t temp; completionField = field; // only look at the first token for completion purposes Cmd_TokenizeString( completionField->buffer ); completionString = Cmd_Argv(0); if ( completionString[0] == '\\' || completionString[0] == '/' ) { completionString++; } matchCount = 0; shortestMatch[0] = 0; if ( (int)strlen( completionString ) == 0 ) { return; } Cmd_CommandCompletion( FindMatches ); Cvar_CommandCompletion( FindMatches ); if ( matchCount == 0 ) { return; // no matches } Com_Memcpy(&temp, completionField, sizeof(field_t)); if ( matchCount == 1 ) { Com_sprintf( completionField->buffer, sizeof( completionField->buffer ), "\\%s", shortestMatch ); if ( Cmd_Argc() == 1 ) { Q_strcat( completionField->buffer, sizeof( completionField->buffer ), " " ); } else { ConcatRemaining( temp.buffer, completionString ); } completionField->cursor = (int)strlen( completionField->buffer ); return; } // multiple matches, complete to shortest Com_sprintf( completionField->buffer, sizeof( completionField->buffer ), "\\%s", shortestMatch ); completionField->cursor = (int)strlen( completionField->buffer ); ConcatRemaining( temp.buffer, completionString ); Com_Printf( "]%s\n", completionField->buffer ); // run through again, printing matches Cmd_CommandCompletion( PrintMatches ); Cvar_CommandCompletion( PrintMatches ); } ================================================ FILE: src/engine/qcommon/cvar.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // cvar.c -- dynamic variable tracking #include "../../game/q_shared.h" #include "qcommon.h" cvar_t *cvar_vars; cvar_t *cvar_cheats; int cvar_modifiedFlags; #define MAX_CVARS 1024 cvar_t cvar_indexes[MAX_CVARS]; int cvar_numIndexes; #define FILE_HASH_SIZE 256 static cvar_t* hashTable[FILE_HASH_SIZE]; cvar_t *Cvar_Set2( const char *var_name, const char *value, qboolean force); /* ================ 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]); hash+=(long)(letter)*(i+119); i++; } hash &= (FILE_HASH_SIZE-1); return hash; } /* ============ Cvar_ValidateString ============ */ static qboolean Cvar_ValidateString( const char *s ) { if ( !s ) { return qfalse; } if ( strchr( s, '\\' ) ) { return qfalse; } if ( strchr( s, '\"' ) ) { return qfalse; } if ( strchr( s, ';' ) ) { return qfalse; } return qtrue; } /* ============ Cvar_FindVar ============ */ static cvar_t *Cvar_FindVar( const char *var_name ) { cvar_t *var; long hash; hash = generateHashValue(var_name); for (var=hashTable[hash] ; var ; var=var->hashNext) { if (!Q_stricmp(var_name, var->name)) { return var; } } return NULL; } /* ============ Cvar_VariableValue ============ */ float Cvar_VariableValue( const char *var_name ) { cvar_t *var; var = Cvar_FindVar (var_name); if (!var) return 0; return var->value; } /* ============ Cvar_VariableIntegerValue ============ */ int Cvar_VariableIntegerValue( const char *var_name ) { cvar_t *var; var = Cvar_FindVar (var_name); if (!var) return 0; return var->integer; } /* ============ Cvar_VariableString ============ */ char *Cvar_VariableString( const char *var_name ) { cvar_t *var; var = Cvar_FindVar (var_name); if (!var) return ""; return var->string; } /* ============ Cvar_VariableStringBuffer ============ */ void Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize ) { cvar_t *var; var = Cvar_FindVar (var_name); if (!var) { *buffer = 0; } else { Q_strncpyz( buffer, var->string, bufsize ); } } /* ============ Cvar_CommandCompletion ============ */ void Cvar_CommandCompletion( void(*callback)(const char *s) ) { cvar_t *cvar; for ( cvar = cvar_vars ; cvar ; cvar = cvar->next ) { callback( cvar->name ); } } /* ============ Cvar_Get If the variable already exists, the value will not be set unless CVAR_ROM The flags will be or'ed in if the variable exists. ============ */ cvar_t *Cvar_Get( const char *var_name, const char *var_value, int flags ) { cvar_t *var; long hash; if ( !var_name || ! var_value ) { Com_Error( ERR_FATAL, "Cvar_Get: NULL parameter" ); } if ( !Cvar_ValidateString( var_name ) ) { Com_Printf("invalid cvar name string: %s\n", var_name ); var_name = "BADNAME"; } #if 0 // FIXME: values with backslash happen if ( !Cvar_ValidateString( var_value ) ) { Com_Printf("invalid cvar value string: %s\n", var_value ); var_value = "BADVALUE"; } #endif var = Cvar_FindVar (var_name); if ( var ) { // if the C code is now specifying a variable that the user already // set a value for, take the new value as the reset value if ( ( var->flags & CVAR_USER_CREATED ) && !( flags & CVAR_USER_CREATED ) && var_value[0] ) { var->flags &= ~CVAR_USER_CREATED; Z_Free( var->resetString ); var->resetString = CopyString( var_value ); // ZOID--needs to be set so that cvars the game sets as // SERVERINFO get sent to clients cvar_modifiedFlags |= flags; } var->flags |= flags; // only allow one non-empty reset string without a warning if ( !var->resetString[0] ) { // we don't have a reset string yet Z_Free( var->resetString ); var->resetString = CopyString( var_value ); } else if ( var_value[0] && strcmp( var->resetString, var_value ) ) { Com_DPrintf( "Warning: cvar \"%s\" given initial values: \"%s\" and \"%s\"\n", var_name, var->resetString, var_value ); } // if we have a latched string, take that value now if ( var->latchedString ) { char *s; s = var->latchedString; var->latchedString = NULL; // otherwise cvar_set2 would free it Cvar_Set2( var_name, s, qtrue ); Z_Free( s ); } // use a CVAR_SET for rom sets, get won't override #if 0 // CVAR_ROM always overrides if ( flags & CVAR_ROM ) { Cvar_Set2( var_name, var_value, qtrue ); } #endif return var; } // // allocate a new cvar // if ( cvar_numIndexes >= MAX_CVARS ) { Com_Error( ERR_FATAL, "MAX_CVARS" ); } var = &cvar_indexes[cvar_numIndexes]; cvar_numIndexes++; var->name = CopyString (var_name); var->string = CopyString (var_value); var->modified = qtrue; var->modificationCount = 1; var->value = atof (var->string); var->integer = atoi(var->string); var->resetString = CopyString( var_value ); // link the variable in var->next = cvar_vars; cvar_vars = var; var->flags = flags; hash = generateHashValue(var_name); var->hashNext = hashTable[hash]; hashTable[hash] = var; return var; } /* ============ Cvar_Set2 ============ */ cvar_t *Cvar_Set2( const char *var_name, const char *value, qboolean force ) { cvar_t *var; Com_DPrintf( "Cvar_Set2: %s %s\n", var_name, value ); if ( !Cvar_ValidateString( var_name ) ) { Com_Printf("invalid cvar name string: %s\n", var_name ); var_name = "BADNAME"; } #if 0 // FIXME if ( value && !Cvar_ValidateString( value ) ) { Com_Printf("invalid cvar value string: %s\n", value ); var_value = "BADVALUE"; } #endif var = Cvar_FindVar (var_name); if (!var) { if ( !value ) { return NULL; } // create it if ( !force ) { return Cvar_Get( var_name, value, CVAR_USER_CREATED ); } else { return Cvar_Get (var_name, value, 0); } } if (!value ) { value = var->resetString; } if (!strcmp(value,var->string)) { return var; } // note what types of cvars have been modified (userinfo, archive, serverinfo, systeminfo) cvar_modifiedFlags |= var->flags; if (!force) { if (var->flags & CVAR_ROM) { Com_Printf ("%s is read only.\n", var_name); return var; } if (var->flags & CVAR_INIT) { Com_Printf ("%s is write protected.\n", var_name); return var; } if (var->flags & CVAR_LATCH) { if (var->latchedString) { if (strcmp(value, var->latchedString) == 0) return var; Z_Free (var->latchedString); } else { if (strcmp(value, var->string) == 0) return var; } Com_Printf ("%s will be changed upon restarting.\n", var_name); var->latchedString = CopyString(value); var->modified = qtrue; var->modificationCount++; return var; } if ( (var->flags & CVAR_CHEAT) && !cvar_cheats->integer ) { Com_Printf ("%s is cheat protected.\n", var_name); return var; } } else { if (var->latchedString) { Z_Free (var->latchedString); var->latchedString = NULL; } } if (!strcmp(value, var->string)) return var; // not changed var->modified = qtrue; var->modificationCount++; Z_Free (var->string); // free the old value string var->string = CopyString(value); var->value = atof (var->string); var->integer = atoi (var->string); return var; } /* ============ Cvar_Set ============ */ void Cvar_Set( const char *var_name, const char *value) { Cvar_Set2 (var_name, value, qtrue); } /* ============ Cvar_SetLatched ============ */ void Cvar_SetLatched( const char *var_name, const char *value) { Cvar_Set2 (var_name, value, qfalse); } /* ============ Cvar_SetValue ============ */ void Cvar_SetValue( const char *var_name, float value) { char val[32]; if ( value == (int)value ) { Com_sprintf (val, sizeof(val), "%i",(int)value); } else { Com_sprintf (val, sizeof(val), "%f",value); } Cvar_Set (var_name, val); } /* ============ Cvar_Reset ============ */ void Cvar_Reset( const char *var_name ) { Cvar_Set2( var_name, NULL, qfalse ); } /* ============ Cvar_SetCheatState Any testing variables will be reset to the safe values ============ */ void Cvar_SetCheatState( void ) { cvar_t *var; // set all default vars to the safe value for ( var = cvar_vars ; var ; var = var->next ) { if ( var->flags & CVAR_CHEAT ) { // the CVAR_LATCHED|CVAR_CHEAT vars might escape the reset here // because of a different var->latchedString if (var->latchedString) { Z_Free(var->latchedString); var->latchedString = NULL; } if (strcmp(var->resetString,var->string)) { Cvar_Set( var->name, var->resetString ); } } } } /* ============ Cvar_Command Handles variable inspection and changing from the console ============ */ qboolean Cvar_Command( void ) { cvar_t *v; // check variables v = Cvar_FindVar (Cmd_Argv(0)); if (!v) { return qfalse; } // perform a variable print or set if ( Cmd_Argc() == 1 ) { Com_Printf ("\"%s\" is:\"%s" S_COLOR_WHITE "\" default:\"%s" S_COLOR_WHITE "\"\n", v->name, v->string, v->resetString ); if ( v->latchedString ) { Com_Printf( "latched: \"%s\"\n", v->latchedString ); } return qtrue; } // set the value if forcing isn't required Cvar_Set2 (v->name, Cmd_Argv(1), qfalse); return qtrue; } /* ============ Cvar_Toggle_f Toggles a cvar for easy single key binding ============ */ void Cvar_Toggle_f( void ) { int v; if ( Cmd_Argc() != 2 ) { Com_Printf ("usage: toggle \n"); return; } v = Cvar_VariableValue( Cmd_Argv( 1 ) ); v = !v; Cvar_Set2 (Cmd_Argv(1), va("%i", v), qfalse); } /* ============ Cvar_Set_f Allows setting and defining of arbitrary cvars from console, even if they weren't declared in C code. ============ */ void Cvar_Set_f( void ) { int i, c, l, len; char combined[MAX_STRING_TOKENS]; c = Cmd_Argc(); if ( c < 3 ) { Com_Printf ("usage: set \n"); return; } combined[0] = 0; l = 0; for ( i = 2 ; i < c ; i++ ) { len = (int)strlen ( Cmd_Argv( i ) + 1 ); if ( l + len >= MAX_STRING_TOKENS - 2 ) { break; } strcat( combined, Cmd_Argv( i ) ); if ( i != c-1 ) { strcat( combined, " " ); } l += len; } Cvar_Set2 (Cmd_Argv(1), combined, qfalse); } /* ============ Cvar_SetU_f As Cvar_Set, but also flags it as userinfo ============ */ void Cvar_SetU_f( void ) { cvar_t *v; if ( Cmd_Argc() != 3 ) { Com_Printf ("usage: setu \n"); return; } Cvar_Set_f(); v = Cvar_FindVar( Cmd_Argv( 1 ) ); if ( !v ) { return; } v->flags |= CVAR_USERINFO; } /* ============ Cvar_SetS_f As Cvar_Set, but also flags it as userinfo ============ */ void Cvar_SetS_f( void ) { cvar_t *v; if ( Cmd_Argc() != 3 ) { Com_Printf ("usage: sets \n"); return; } Cvar_Set_f(); v = Cvar_FindVar( Cmd_Argv( 1 ) ); if ( !v ) { return; } v->flags |= CVAR_SERVERINFO; } /* ============ Cvar_SetA_f As Cvar_Set, but also flags it as archived ============ */ void Cvar_SetA_f( void ) { cvar_t *v; if ( Cmd_Argc() != 3 ) { Com_Printf ("usage: seta \n"); return; } Cvar_Set_f(); v = Cvar_FindVar( Cmd_Argv( 1 ) ); if ( !v ) { return; } v->flags |= CVAR_ARCHIVE; } /* ============ Cvar_Reset_f ============ */ void Cvar_Reset_f( void ) { if ( Cmd_Argc() != 2 ) { Com_Printf ("usage: reset \n"); return; } Cvar_Reset( Cmd_Argv( 1 ) ); } /* ============ Cvar_WriteVariables Appends lines containing "set variable value" for all variables with the archive flag set to qtrue. ============ */ void Cvar_WriteVariables( fileHandle_t f ) { cvar_t *var; char buffer[1024]; for (var = cvar_vars ; var ; var = var->next) { if( Q_stricmp( var->name, "cl_cdkey" ) == 0 ) { continue; } if( var->flags & CVAR_ARCHIVE ) { // write the latched value, even if it hasn't taken effect yet if ( var->latchedString ) { Com_sprintf (buffer, sizeof(buffer), "seta %s \"%s\"\n", var->name, var->latchedString); } else { Com_sprintf (buffer, sizeof(buffer), "seta %s \"%s\"\n", var->name, var->string); } FS_Printf (f, "%s", buffer); } } } /* ============ Cvar_List_f ============ */ void Cvar_List_f( void ) { cvar_t *var; int i; char *match; if ( Cmd_Argc() > 1 ) { match = Cmd_Argv( 1 ); } else { match = NULL; } i = 0; for (var = cvar_vars ; var ; var = var->next, i++) { if (match && !Com_Filter(match, var->name, qfalse)) continue; if (var->flags & CVAR_SERVERINFO) { Com_Printf("S"); } else { Com_Printf(" "); } if (var->flags & CVAR_USERINFO) { Com_Printf("U"); } else { Com_Printf(" "); } if (var->flags & CVAR_ROM) { Com_Printf("R"); } else { Com_Printf(" "); } if (var->flags & CVAR_INIT) { Com_Printf("I"); } else { Com_Printf(" "); } if (var->flags & CVAR_ARCHIVE) { Com_Printf("A"); } else { Com_Printf(" "); } if (var->flags & CVAR_LATCH) { Com_Printf("L"); } else { Com_Printf(" "); } if (var->flags & CVAR_CHEAT) { Com_Printf("C"); } else { Com_Printf(" "); } Com_Printf (" %s \"%s\"\n", var->name, var->string); } Com_Printf ("\n%i total cvars\n", i); Com_Printf ("%i cvar indexes\n", cvar_numIndexes); } /* ============ Cvar_Restart_f Resets all cvars to their hardcoded values ============ */ void Cvar_Restart_f( void ) { cvar_t *var; cvar_t **prev; prev = &cvar_vars; while ( 1 ) { var = *prev; if ( !var ) { break; } // don't mess with rom values, or some inter-module // communication will get broken (com_cl_running, etc) if ( var->flags & ( CVAR_ROM | CVAR_INIT | CVAR_NORESTART ) ) { prev = &var->next; continue; } // throw out any variables the user created if ( var->flags & CVAR_USER_CREATED ) { *prev = var->next; if ( var->name ) { Z_Free( var->name ); } if ( var->string ) { Z_Free( var->string ); } if ( var->latchedString ) { Z_Free( var->latchedString ); } if ( var->resetString ) { Z_Free( var->resetString ); } // clear the var completely, since we // can't remove the index from the list Com_Memset( var, 0, sizeof( var ) ); continue; } Cvar_Set( var->name, var->resetString ); prev = &var->next; } } /* ===================== Cvar_InfoString ===================== */ char *Cvar_InfoString( int bit ) { static char info[MAX_INFO_STRING]; cvar_t *var; info[0] = 0; for (var = cvar_vars ; var ; var = var->next) { if (var->flags & bit) { Info_SetValueForKey (info, var->name, var->string); } } return info; } /* ===================== Cvar_InfoString_Big handles large info strings ( CS_SYSTEMINFO ) ===================== */ char *Cvar_InfoString_Big( int bit ) { static char info[BIG_INFO_STRING]; cvar_t *var; info[0] = 0; for (var = cvar_vars ; var ; var = var->next) { if (var->flags & bit) { Info_SetValueForKey_Big (info, var->name, var->string); } } return info; } /* ===================== Cvar_InfoStringBuffer ===================== */ void Cvar_InfoStringBuffer( int bit, char* buff, int buffsize ) { Q_strncpyz(buff,Cvar_InfoString(bit),buffsize); } /* ===================== Cvar_Register basically a slightly modified Cvar_Get for the interpreted modules ===================== */ void Cvar_Register( vmCvar_t *vmCvar, const char *varName, const char *defaultValue, int flags ) { cvar_t *cv; cv = Cvar_Get( varName, defaultValue, flags ); if ( !vmCvar ) { return; } vmCvar->handle = cv - cvar_indexes; vmCvar->modificationCount = -1; Cvar_Update( vmCvar ); } /* ===================== Cvar_Register updates an interpreted modules' version of a cvar ===================== */ void Cvar_Update( vmCvar_t *vmCvar ) { cvar_t *cv = NULL; // bk001129 assert(vmCvar); // bk if ( (unsigned)vmCvar->handle >= cvar_numIndexes ) { Com_Error( ERR_DROP, "Cvar_Update: handle out of range" ); } cv = cvar_indexes + vmCvar->handle; if ( cv->modificationCount == vmCvar->modificationCount ) { return; } if ( !cv->string ) { return; // variable might have been cleared by a cvar_restart } vmCvar->modificationCount = cv->modificationCount; // bk001129 - mismatches. if ( (int)strlen(cv->string)+1 > MAX_CVAR_VALUE_STRING ) Com_Error( ERR_DROP, "Cvar_Update: src %s length %d exceeds MAX_CVAR_VALUE_STRING", cv->string, (int)strlen(cv->string), sizeof(vmCvar->string) ); // bk001212 - Q_strncpyz guarantees zero padding and dest[MAX_CVAR_VALUE_STRING-1]==0 // bk001129 - paranoia. Never trust the destination string. // bk001129 - beware, sizeof(char*) is always 4 (for cv->string). // sizeof(vmCvar->string) always MAX_CVAR_VALUE_STRING //Q_strncpyz( vmCvar->string, cv->string, sizeof( vmCvar->string ) ); // id Q_strncpyz( vmCvar->string, cv->string, MAX_CVAR_VALUE_STRING ); vmCvar->value = cv->value; vmCvar->integer = cv->integer; } /* ============ Cvar_Init Reads in all archived cvars ============ */ void Cvar_Init (void) { cvar_cheats = Cvar_Get("sv_cheats", "1", CVAR_ROM | CVAR_SYSTEMINFO ); Cmd_AddCommand ("toggle", Cvar_Toggle_f); Cmd_AddCommand ("set", Cvar_Set_f); Cmd_AddCommand ("sets", Cvar_SetS_f); Cmd_AddCommand ("setu", Cvar_SetU_f); Cmd_AddCommand ("seta", Cvar_SetA_f); Cmd_AddCommand ("reset", Cvar_Reset_f); Cmd_AddCommand ("cvarlist", Cvar_List_f); Cmd_AddCommand ("cvar_restart", Cvar_Restart_f); } ================================================ FILE: src/engine/qcommon/files.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /***************************************************************************** * name: files.c * * desc: handle based filesystem for Quake III Arena * * $Archive: /MissionPack/code/qcommon/files.c $ * *****************************************************************************/ #include "../../game/q_shared.h" #include "qcommon.h" #include "unzip.h" /* ============================================================================= QUAKE3 FILESYSTEM All of Quake's data access is through a hierarchical file system, but the contents of the file system can be transparently merged from several sources. A "qpath" is a reference to game file data. MAX_ZPATH is 256 characters, which must include a terminating zero. "..", "\\", and ":" are explicitly illegal in qpaths to prevent any references outside the quake directory system. The "base path" is the path to the directory holding all the game directories and usually the executable. It defaults to ".", but can be overridden with a "+set fs_basepath c:\quake3" command line to allow code debugging in a different directory. Basepath cannot be modified at all after startup. Any files that are created (demos, screenshots, etc) will be created reletive to the base path, so base path should usually be writable. The "cd path" is the path to an alternate hierarchy that will be searched if a file is not located in the base path. A user can do a partial install that copies some data to a base path created on their hard drive and leave the rest on the cd. Files are never writen to the cd path. It defaults to a value set by the installer, like "e:\quake3", but it can be overridden with "+set ds_cdpath g:\quake3". If a user runs the game directly from a CD, the base path would be on the CD. This should still function correctly, but all file writes will fail (harmlessly). The "home path" is the path used for all write access. On win32 systems we have "base path" == "home path", but on *nix systems the base installation is usually readonly, and "home path" points to ~/.q3a or similar The user can also install custom mods and content in "home path", so it should be searched along with "home path" and "cd path" for game content. The "base game" is the directory under the paths where data comes from by default, and can be either "baseq3" or "demoq3". The "current game" may be the same as the base game, or it may be the name of another directory under the paths that should be searched for files before looking in the base game. This is the basis for addons. Clients automatically set the game directory after receiving a gamestate from a server, so only servers need to worry about +set fs_game. No other directories outside of the base game and current game will ever be referenced by filesystem functions. To save disk space and speed loading, directory trees can be collapsed into zip files. The files use a ".pk3" extension to prevent users from unzipping them accidentally, but otherwise the are simply normal uncompressed zip files. A game directory can have multiple zip files of the form "pak0.pk3", "pak1.pk3", etc. Zip files are searched in decending order from the highest number to the lowest, and will always take precedence over the filesystem. This allows a pk3 distributed as a patch to override all existing data. Because we will have updated executables freely available online, there is no point to trying to restrict demo / oem versions of the game with code changes. Demo / oem versions should be exactly the same executables as release versions, but with different data that automatically restricts where game media can come from to prevent add-ons from working. After the paths are initialized, quake will look for the product.txt file. If not found and verified, the game will run in restricted mode. In restricted mode, only files contained in demoq3/pak0.pk3 will be available for loading, and only if the zip header is verified to not have been modified. A single exception is made for q3config.cfg. Files can still be written out in restricted mode, so screenshots and demos are allowed. Restricted mode can be tested by setting "+set fs_restrict 1" on the command line, even if there is a valid product.txt under the basepath or cdpath. If not running in restricted mode, and a file is not found in any local filesystem, an attempt will be made to download it and save it under the base path. If the "fs_copyfiles" cvar is set to 1, then every time a file is sourced from the cd path, it will be copied over to the base path. This is a development aid to help build test releases and to copy working sets over slow network links. File search order: when FS_FOpenFileRead gets called it will go through the fs_searchpaths structure and stop on the first successful hit. fs_searchpaths is built with successive calls to FS_AddGameDirectory Additionaly, we search in several subdirectories: current game is the current mode base game is a variable to allow mods based on other mods (such as baseq3 + missionpack content combination in a mod for instance) BASEGAME is the hardcoded base game ("baseq3") e.g. the qpath "sound/newstuff/test.wav" would be searched for in the following places: home path + current game's zip files home path + current game's directory base path + current game's zip files base path + current game's directory cd path + current game's zip files cd path + current game's directory home path + base game's zip file home path + base game's directory base path + base game's zip file base path + base game's directory cd path + base game's zip file cd path + base game's directory home path + BASEGAME's zip file home path + BASEGAME's directory base path + BASEGAME's zip file base path + BASEGAME's directory cd path + BASEGAME's zip file cd path + BASEGAME's directory server download, to be written to home path + current game's directory The filesystem can be safely shutdown and reinitialized with different basedir / cddir / game combinations, but all other subsystems that rely on it (sound, video) must also be forced to restart. Because the same files are loaded by both the clip model (CM_) and renderer (TR_) subsystems, a simple single-file caching scheme is used. The CM_ subsystems will load the file with a request to cache. Only one file will be kept cached at a time, so any models that are going to be referenced by both subsystems should alternate between the CM_ load function and the ref load function. TODO: A qpath that starts with a leading slash will always refer to the base game, even if another game is currently active. This allows character models, skins, and sounds to be downloaded to a common directory no matter which game is active. How to prevent downloading zip files? Pass pk3 file names in systeminfo, and download before FS_Restart()? Aborting a download disconnects the client from the server. How to mark files as downloadable? Commercial add-ons won't be downloadable. Non-commercial downloads will want to download the entire zip file. the game would have to be reset to actually read the zip in Auto-update information Path separators Casing separate server gamedir and client gamedir, so if the user starts a local game after having connected to a network game, it won't stick with the network game. allow menu options for game selection? Read / write config to floppy option. Different version coexistance? When building a pak file, make sure a q3config.cfg isn't present in it, or configs will never get loaded from disk! todo: downloading (outside fs?) game directory passing and restarting ============================================================================= */ #define DEMOGAME "demota" // every time a new demo pk3 file is built, this checksum must be updated. // the easiest way to get it is to just run the game and see what it spits out #define DEMO_PAK_CHECKSUM 437558517u // if this is defined, the executable positively won't work with any paks other // than the demo pak, even if productid is present. This is only used for our // last demo release to prevent the mac and linux users from using the demo // executable with the production windows pak before the mac/linux products // hit the shelves a little later // NOW defined in build files //#define PRE_RELEASE_TADEMO #define MAX_ZPATH 256 #define MAX_SEARCH_PATHS 4096 #define MAX_FILEHASH_SIZE 1024 typedef struct fileInPack_s { char *name; // name of the file unsigned long pos; // file info position in zip struct fileInPack_s* next; // next file in the hash } fileInPack_t; typedef struct { char pakFilename[MAX_OSPATH]; // c:\quake3\baseq3\pak0.pk3 char pakBasename[MAX_OSPATH]; // pak0 char pakGamename[MAX_OSPATH]; // baseq3 unzFile handle; // handle to zip file int checksum; // regular checksum int pure_checksum; // checksum for pure int numfiles; // number of files in pk3 int referenced; // referenced file flags int hashSize; // hash table size (power of 2) fileInPack_t* *hashTable; // hash table fileInPack_t* buildBuffer; // buffer with the filenames etc. } pack_t; typedef struct { char path[MAX_OSPATH]; // c:\quake3 char gamedir[MAX_OSPATH]; // baseq3 } directory_t; typedef struct searchpath_s { struct searchpath_s *next; pack_t *pack; // only one of pack / dir will be non NULL directory_t *dir; } searchpath_t; static char fs_gamedir[MAX_OSPATH]; // this will be a single file name with no separators static cvar_t *fs_debug; static cvar_t *fs_homepath; static cvar_t *fs_basepath; static cvar_t *fs_basegame; static cvar_t *fs_cdpath; static cvar_t *fs_copyfiles; static cvar_t *fs_gamedirvar; static cvar_t *fs_restrict; static searchpath_t *fs_searchpaths; static int fs_readCount; // total bytes read static int fs_loadCount; // total files read static int fs_loadStack; // total files in memory static int fs_packFiles; // total number of files in packs static int fs_fakeChkSum; static int fs_checksumFeed; typedef union qfile_gus { FILE* o; unzFile z; } qfile_gut; typedef struct qfile_us { qfile_gut file; qboolean unique; } qfile_ut; typedef struct { qfile_ut handleFiles; qboolean handleSync; int baseOffset; int fileSize; int zipFilePos; qboolean zipFile; qboolean streamed; char name[MAX_ZPATH]; } fileHandleData_t; static fileHandleData_t fsh[MAX_FILE_HANDLES]; // TTimo - https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=540 // wether we did a reorder on the current search path when joining the server static qboolean fs_reordered; // never load anything from pk3 files that are not present at the server when pure static int fs_numServerPaks; static int fs_serverPaks[MAX_SEARCH_PATHS]; // checksums static char *fs_serverPakNames[MAX_SEARCH_PATHS]; // pk3 names // only used for autodownload, to make sure the client has at least // all the pk3 files that are referenced at the server side static int fs_numServerReferencedPaks; static int fs_serverReferencedPaks[MAX_SEARCH_PATHS]; // checksums static char *fs_serverReferencedPakNames[MAX_SEARCH_PATHS]; // pk3 names // last valid game folder used char lastValidBase[MAX_OSPATH]; char lastValidGame[MAX_OSPATH]; // productId: This file is copyright 1999 Id Software, and may not be duplicated except during a licensed installation of the full commercial version of Quake 3:Arena static byte fs_scrambledProductId[152] = { 220, 129, 255, 108, 244, 163, 171, 55, 133, 65, 199, 36, 140, 222, 53, 99, 65, 171, 175, 232, 236, 193, 210, 250, 169, 104, 231, 231, 21, 201, 170, 208, 135, 175, 130, 136, 85, 215, 71, 23, 96, 32, 96, 83, 44, 240, 219, 138, 184, 215, 73, 27, 196, 247, 55, 139, 148, 68, 78, 203, 213, 238, 139, 23, 45, 205, 118, 186, 236, 230, 231, 107, 212, 1, 10, 98, 30, 20, 116, 180, 216, 248, 166, 35, 45, 22, 215, 229, 35, 116, 250, 167, 117, 3, 57, 55, 201, 229, 218, 222, 128, 12, 141, 149, 32, 110, 168, 215, 184, 53, 31, 147, 62, 12, 138, 67, 132, 54, 125, 6, 221, 148, 140, 4, 21, 44, 198, 3, 126, 12, 100, 236, 61, 42, 44, 251, 15, 135, 14, 134, 89, 92, 177, 246, 152, 106, 124, 78, 118, 80, 28, 42 }; #ifdef FS_MISSING FILE* missingFiles = NULL; #endif /* ============== FS_Initialized ============== */ qboolean FS_Initialized() { return (qboolean) (fs_searchpaths != NULL); } /* ================= FS_PakIsPure ================= */ qboolean FS_PakIsPure( pack_t *pack ) { int i; if ( fs_numServerPaks ) { for ( i = 0 ; i < fs_numServerPaks ; i++ ) { // FIXME: also use hashed file names // NOTE TTimo: a pk3 with same checksum but different name would be validated too // I don't see this as allowing for any exploit, it would only happen if the client does manips of it's file names 'not a bug' if ( pack->checksum == fs_serverPaks[i] ) { return qtrue; // on the aproved list } } return qfalse; // not on the pure server pak list } return qtrue; } /* ================= FS_LoadStack return load stack ================= */ int FS_LoadStack() { return fs_loadStack; } /* ================ return a hash value for the filename ================ */ static long FS_HashFileName( const char *fname, int hashSize ) { 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 &= (hashSize-1); return hash; } static fileHandle_t FS_HandleForFile(void) { int i; for ( i = 1 ; i < MAX_FILE_HANDLES ; i++ ) { if ( fsh[i].handleFiles.file.o == NULL ) { return i; } } Com_Error( ERR_DROP, "FS_HandleForFile: none free" ); return 0; } static FILE *FS_FileForHandle( fileHandle_t f ) { if ( f < 0 || f > MAX_FILE_HANDLES ) { Com_Error( ERR_DROP, "FS_FileForHandle: out of reange" ); } if (fsh[f].zipFile == qtrue) { Com_Error( ERR_DROP, "FS_FileForHandle: can't get FILE on zip file" ); } if ( ! fsh[f].handleFiles.file.o ) { Com_Error( ERR_DROP, "FS_FileForHandle: NULL" ); } return fsh[f].handleFiles.file.o; } void FS_ForceFlush( fileHandle_t f ) { FILE *file; file = FS_FileForHandle(f); setvbuf( file, NULL, _IONBF, 0 ); } /* ================ FS_filelength If this is called on a non-unique FILE (from a pak file), it will return the size of the pak file, not the expected size of the file. ================ */ int FS_filelength( fileHandle_t f ) { int pos; int end; FILE* h; h = FS_FileForHandle(f); pos = ftell (h); fseek (h, 0, SEEK_END); end = ftell (h); fseek (h, pos, SEEK_SET); return end; } /* ==================== FS_ReplaceSeparators Fix things up differently for win/unix/mac ==================== */ static void FS_ReplaceSeparators( char *path ) { char *s; for ( s = path ; *s ; s++ ) { if ( *s == '/' || *s == '\\' ) { *s = PATH_SEP; } } } /* =================== FS_BuildOSPath Qpath may have either forward or backwards slashes =================== */ char *FS_BuildOSPath( const char *base, const char *game, const char *qpath ) { char temp[MAX_OSPATH]; static char ospath[2][MAX_OSPATH]; static int toggle; toggle ^= 1; // flip-flop to allow two returns without clash if( !game || !game[0] ) { game = fs_gamedir; } Com_sprintf( temp, sizeof(temp), "/%s/%s", game, qpath ); FS_ReplaceSeparators( temp ); Com_sprintf( ospath[toggle], sizeof( ospath[0] ), "%s%s", base, temp ); return ospath[toggle]; } /* ============ FS_CreatePath Creates any directories needed to store the given filename ============ */ static qboolean FS_CreatePath (char *OSPath) { char *ofs; // make absolutely sure that it can't back up the path // FIXME: is c: allowed??? if ( strstr( OSPath, ".." ) || strstr( OSPath, "::" ) ) { Com_Printf( "WARNING: refusing to create relative path \"%s\"\n", OSPath ); return qtrue; } for (ofs = OSPath+1 ; *ofs ; ofs++) { if (*ofs == PATH_SEP) { // create the directory *ofs = 0; Sys_Mkdir (OSPath); *ofs = PATH_SEP; } } return qfalse; } /* ================= FS_CopyFile Copy a fully specified file from one place to another ================= */ static void FS_CopyFile( char *fromOSPath, char *toOSPath ) { FILE *f; int len; byte *buf; Com_Printf( "copy %s to %s\n", fromOSPath, toOSPath ); if (strstr(fromOSPath, "journal.dat") || strstr(fromOSPath, "journaldata.dat")) { Com_Printf( "Ignoring journal files\n"); return; } f = fopen( fromOSPath, "rb" ); if ( !f ) { return; } fseek (f, 0, SEEK_END); len = ftell (f); fseek (f, 0, SEEK_SET); // we are using direct malloc instead of Z_Malloc here, so it // probably won't work on a mac... Its only for developers anyway... buf = (byte*) malloc( len ); if (fread( buf, 1, len, f ) != len) Com_Error( ERR_FATAL, "Short read in FS_Copyfiles()\n" ); fclose( f ); if( FS_CreatePath( toOSPath ) ) { return; } f = fopen( toOSPath, "wb" ); if ( !f ) { return; } if (fwrite( buf, 1, len, f ) != len) Com_Error( ERR_FATAL, "Short write in FS_Copyfiles()\n" ); fclose( f ); free( buf ); } /* =========== FS_Remove =========== */ static void FS_Remove( const char *osPath ) { remove( osPath ); } /* ================ FS_FileExists Tests if the file exists in the current gamedir, this DOES NOT search the paths. This is to determine if opening a file to write (which always goes into the current gamedir) will cause any overwrites. NOTE TTimo: this goes with FS_FOpenFileWrite for opening the file afterwards ================ */ qboolean FS_FileExists( const char *file ) { FILE *f; char *testpath; testpath = FS_BuildOSPath( fs_homepath->string, fs_gamedir, file ); f = fopen( testpath, "rb" ); if (f) { fclose( f ); return qtrue; } return qfalse; } /* ================ FS_SV_FileExists Tests if the file exists ================ */ qboolean FS_SV_FileExists( const char *file ) { FILE *f; char *testpath; testpath = FS_BuildOSPath( fs_homepath->string, file, ""); testpath[strlen(testpath)-1] = '\0'; f = fopen( testpath, "rb" ); if (f) { fclose( f ); return qtrue; } return qfalse; } /* =========== FS_SV_FOpenFileWrite =========== */ fileHandle_t FS_SV_FOpenFileWrite( const char *filename ) { char *ospath; fileHandle_t f; if ( !fs_searchpaths ) { Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); } ospath = FS_BuildOSPath( fs_homepath->string, filename, "" ); ospath[strlen(ospath)-1] = '\0'; f = FS_HandleForFile(); fsh[f].zipFile = qfalse; if ( fs_debug->integer ) { Com_Printf( "FS_SV_FOpenFileWrite: %s\n", ospath ); } if( FS_CreatePath( ospath ) ) { return 0; } Com_DPrintf( "writing to: %s\n", ospath ); fsh[f].handleFiles.file.o = fopen( ospath, "wb" ); Q_strncpyz( fsh[f].name, filename, sizeof( fsh[f].name ) ); fsh[f].handleSync = qfalse; if (!fsh[f].handleFiles.file.o) { f = 0; } return f; } /* =========== FS_SV_FOpenFileRead search for a file somewhere below the home path, base path or cd path we search in that order, matching FS_SV_FOpenFileRead order =========== */ int FS_SV_FOpenFileRead( const char *filename, fileHandle_t *fp ) { char *ospath; fileHandle_t f = 0; if ( !fs_searchpaths ) { Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); } f = FS_HandleForFile(); fsh[f].zipFile = qfalse; Q_strncpyz( fsh[f].name, filename, sizeof( fsh[f].name ) ); // don't let sound stutter S_ClearSoundBuffer(); // search homepath ospath = FS_BuildOSPath( fs_homepath->string, filename, "" ); // remove trailing slash ospath[strlen(ospath)-1] = '\0'; if ( fs_debug->integer ) { Com_Printf( "FS_SV_FOpenFileRead (fs_homepath): %s\n", ospath ); } fsh[f].handleFiles.file.o = fopen( ospath, "rb" ); fsh[f].handleSync = qfalse; if (!fsh[f].handleFiles.file.o) { // NOTE TTimo on non *nix systems, fs_homepath == fs_basepath, might want to avoid if (Q_stricmp(fs_homepath->string,fs_basepath->string)) { // search basepath ospath = FS_BuildOSPath( fs_basepath->string, filename, "" ); ospath[strlen(ospath)-1] = '\0'; if ( fs_debug->integer ) { Com_Printf( "FS_SV_FOpenFileRead (fs_basepath): %s\n", ospath ); } fsh[f].handleFiles.file.o = fopen( ospath, "rb" ); fsh[f].handleSync = qfalse; if ( !fsh[f].handleFiles.file.o ) { f = 0; } } } if (!fsh[f].handleFiles.file.o) { // search cd path ospath = FS_BuildOSPath( fs_cdpath->string, filename, "" ); ospath[strlen(ospath)-1] = '\0'; if (fs_debug->integer) { Com_Printf( "FS_SV_FOpenFileRead (fs_cdpath) : %s\n", ospath ); } fsh[f].handleFiles.file.o = fopen( ospath, "rb" ); fsh[f].handleSync = qfalse; if( !fsh[f].handleFiles.file.o ) { f = 0; } } *fp = f; if (f) { return FS_filelength(f); } return 0; } /* =========== FS_SV_Rename =========== */ void FS_SV_Rename( const char *from, const char *to ) { char *from_ospath, *to_ospath; if ( !fs_searchpaths ) { Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); } // don't let sound stutter S_ClearSoundBuffer(); from_ospath = FS_BuildOSPath( fs_homepath->string, from, "" ); to_ospath = FS_BuildOSPath( fs_homepath->string, to, "" ); from_ospath[strlen(from_ospath)-1] = '\0'; to_ospath[strlen(to_ospath)-1] = '\0'; if ( fs_debug->integer ) { Com_Printf( "FS_SV_Rename: %s --> %s\n", from_ospath, to_ospath ); } if (rename( from_ospath, to_ospath )) { // Failed, try copying it and deleting the original FS_CopyFile ( from_ospath, to_ospath ); FS_Remove ( from_ospath ); } } /* =========== FS_Rename =========== */ void FS_Rename( const char *from, const char *to ) { char *from_ospath, *to_ospath; if ( !fs_searchpaths ) { Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); } // don't let sound stutter S_ClearSoundBuffer(); from_ospath = FS_BuildOSPath( fs_homepath->string, fs_gamedir, from ); to_ospath = FS_BuildOSPath( fs_homepath->string, fs_gamedir, to ); if ( fs_debug->integer ) { Com_Printf( "FS_Rename: %s --> %s\n", from_ospath, to_ospath ); } if (rename( from_ospath, to_ospath )) { // Failed, try copying it and deleting the original FS_CopyFile ( from_ospath, to_ospath ); FS_Remove ( from_ospath ); } } /* ============== FS_FCloseFile If the FILE pointer is an open pak file, leave it open. For some reason, other dll's can't just cal fclose() on files returned by FS_FOpenFile... ============== */ void FS_FCloseFile( fileHandle_t f ) { if ( !fs_searchpaths ) { Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); } if (fsh[f].streamed) { Sys_EndStreamedFile(f); } if (fsh[f].zipFile == qtrue) { unzCloseCurrentFile( fsh[f].handleFiles.file.z ); if ( fsh[f].handleFiles.unique ) { unzClose( fsh[f].handleFiles.file.z ); } Com_Memset( &fsh[f], 0, sizeof( fsh[f] ) ); return; } // we didn't find it as a pak, so close it as a unique file if (fsh[f].handleFiles.file.o) { fclose (fsh[f].handleFiles.file.o); } Com_Memset( &fsh[f], 0, sizeof( fsh[f] ) ); } /* =========== FS_FOpenFileWrite =========== */ fileHandle_t FS_FOpenFileWrite( const char *filename ) { char *ospath; fileHandle_t f; if ( !fs_searchpaths ) { Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); } f = FS_HandleForFile(); fsh[f].zipFile = qfalse; ospath = FS_BuildOSPath( fs_homepath->string, fs_gamedir, filename ); if ( fs_debug->integer ) { Com_Printf( "FS_FOpenFileWrite: %s\n", ospath ); } if( FS_CreatePath( ospath ) ) { return 0; } // enabling the following line causes a recursive function call loop // when running with +set logfile 1 +set developer 1 //Com_DPrintf( "writing to: %s\n", ospath ); fsh[f].handleFiles.file.o = fopen( ospath, "wb" ); Q_strncpyz( fsh[f].name, filename, sizeof( fsh[f].name ) ); fsh[f].handleSync = qfalse; if (!fsh[f].handleFiles.file.o) { f = 0; } return f; } /* =========== FS_FOpenFileAppend =========== */ fileHandle_t FS_FOpenFileAppend( const char *filename ) { char *ospath; fileHandle_t f; if ( !fs_searchpaths ) { Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); } f = FS_HandleForFile(); fsh[f].zipFile = qfalse; Q_strncpyz( fsh[f].name, filename, sizeof( fsh[f].name ) ); // don't let sound stutter S_ClearSoundBuffer(); ospath = FS_BuildOSPath( fs_homepath->string, fs_gamedir, filename ); if ( fs_debug->integer ) { Com_Printf( "FS_FOpenFileAppend: %s\n", ospath ); } if( FS_CreatePath( ospath ) ) { return 0; } fsh[f].handleFiles.file.o = fopen( ospath, "ab" ); fsh[f].handleSync = qfalse; if (!fsh[f].handleFiles.file.o) { f = 0; } return f; } /* =========== FS_FilenameCompare Ignore case and seprator char distinctions =========== */ qboolean FS_FilenameCompare( const char *s1, const char *s2 ) { int c1, c2; do { c1 = *s1++; c2 = *s2++; if (c1 >= 'a' && c1 <= 'z') { c1 -= ('a' - 'A'); } if (c2 >= 'a' && c2 <= 'z') { c2 -= ('a' - 'A'); } if ( c1 == '\\' || c1 == ':' ) { c1 = '/'; } if ( c2 == '\\' || c2 == ':' ) { c2 = '/'; } if (c1 != c2) { return (qboolean) -1; // strings not equal } } while (c1); return (qboolean) 0; // strings are equal } /* =========== FS_ShiftedStrStr =========== */ char *FS_ShiftedStrStr(const char *string, const char *substring, int shift) { char buf[MAX_STRING_TOKENS]; int i; for (i = 0; substring[i]; i++) { buf[i] = substring[i] + shift; } buf[i] = '\0'; return (char*) strstr(string, buf); } /* =========== FS_FOpenFileRead Finds the file in the search path. Returns filesize and an open FILE pointer. Used for streaming data out of either a separate file or a ZIP file. =========== */ extern qboolean com_fullyInitialized; int FS_FOpenFileRead( const char *filename, fileHandle_t *file, qboolean uniqueFILE ) { searchpath_t *search; char *netpath; pack_t *pak; fileInPack_t *pakFile; directory_t *dir; long hash; unz_s *zfi; FILE *temp; int l; char demoExt[16]; hash = 0; if ( !fs_searchpaths ) { Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); } if ( file == NULL ) { // just wants to see if file is there for ( search = fs_searchpaths ; search ; search = search->next ) { // if ( search->pack ) { hash = FS_HashFileName(filename, search->pack->hashSize); } // is the element a pak file? if ( search->pack && search->pack->hashTable[hash] ) { // look through all the pak file elements pak = search->pack; pakFile = pak->hashTable[hash]; do { // case and separator insensitive comparisons if ( !FS_FilenameCompare( pakFile->name, filename ) ) { // found it! return qtrue; } pakFile = pakFile->next; } while(pakFile != NULL); } else if ( search->dir ) { dir = search->dir; netpath = FS_BuildOSPath( dir->path, dir->gamedir, filename ); temp = fopen (netpath, "rb"); if ( !temp ) { continue; } fclose(temp); return qtrue; } } return qfalse; } if ( !filename ) { Com_Error( ERR_FATAL, "FS_FOpenFileRead: NULL 'filename' parameter passed\n" ); } Com_sprintf (demoExt, sizeof(demoExt), ".dm_%d",PROTOCOL_VERSION ); // qpaths are not supposed to have a leading slash if ( filename[0] == '/' || filename[0] == '\\' ) { filename++; } // make absolutely sure that it can't back up the path. // The searchpaths do guarantee that something will always // be prepended, so we don't need to worry about "c:" or "//limbo" if ( strstr( filename, ".." ) || strstr( filename, "::" ) ) { *file = 0; return -1; } // make sure the q3key file is only readable by the quake3.exe at initialization // any other time the key should only be accessed in memory using the provided functions if( com_fullyInitialized && strstr( filename, "q3key" ) ) { *file = 0; return -1; } // // search through the path, one element at a time // *file = FS_HandleForFile(); fsh[*file].handleFiles.unique = uniqueFILE; for ( search = fs_searchpaths ; search ; search = search->next ) { // if ( search->pack ) { hash = FS_HashFileName(filename, search->pack->hashSize); } // is the element a pak file? if ( search->pack && search->pack->hashTable[hash] ) { // disregard if it doesn't match one of the allowed pure pak files if ( !FS_PakIsPure(search->pack) ) { continue; } // look through all the pak file elements pak = search->pack; pakFile = pak->hashTable[hash]; do { // case and separator insensitive comparisons if ( !FS_FilenameCompare( pakFile->name, filename ) ) { // found it! // mark the pak as having been referenced and mark specifics on cgame and ui // shaders, txt, arena files by themselves do not count as a reference as // these are loaded from all pk3s // from every pk3 file.. l = (int)strlen( filename ); if ( !(pak->referenced & FS_GENERAL_REF)) { if ( Q_stricmp(filename + l - 7, ".shader") != 0 && Q_stricmp(filename + l - 4, ".txt") != 0 && Q_stricmp(filename + l - 4, ".cfg") != 0 && Q_stricmp(filename + l - 7, ".config") != 0 && strstr(filename, "levelshots") == NULL && Q_stricmp(filename + l - 4, ".bot") != 0 && Q_stricmp(filename + l - 6, ".arena") != 0 && Q_stricmp(filename + l - 5, ".menu") != 0) { pak->referenced |= FS_GENERAL_REF; } } // qagame.qvm - 13 // dTZT`X!di` if (!(pak->referenced & FS_QAGAME_REF) && FS_ShiftedStrStr(filename, "dTZT`X!di`", 13)) { pak->referenced |= FS_QAGAME_REF; } // cgame.qvm - 7 // \`Zf^'jof if (!(pak->referenced & FS_CGAME_REF) && FS_ShiftedStrStr(filename , "\\`Zf^'jof", 7)) { pak->referenced |= FS_CGAME_REF; } // ui.qvm - 5 // pd)lqh if (!(pak->referenced & FS_UI_REF) && FS_ShiftedStrStr(filename , "pd)lqh", 5)) { pak->referenced |= FS_UI_REF; } if ( uniqueFILE ) { // open a new file on the pakfile fsh[*file].handleFiles.file.z = unzReOpen (pak->pakFilename, pak->handle); if (fsh[*file].handleFiles.file.z == NULL) { Com_Error (ERR_FATAL, "Couldn't reopen %s", pak->pakFilename); } } else { fsh[*file].handleFiles.file.z = pak->handle; } Q_strncpyz( fsh[*file].name, filename, sizeof( fsh[*file].name ) ); fsh[*file].zipFile = qtrue; zfi = (unz_s *)fsh[*file].handleFiles.file.z; // in case the file was new temp = zfi->file; // set the file position in the zip file (also sets the current file info) unzSetCurrentFileInfoPosition(pak->handle, pakFile->pos); // copy the file info into the unzip structure Com_Memcpy( zfi, pak->handle, sizeof(unz_s) ); // we copy this back into the structure zfi->file = temp; // open the file in the zip unzOpenCurrentFile( fsh[*file].handleFiles.file.z ); fsh[*file].zipFilePos = pakFile->pos; if ( fs_debug->integer ) { Com_Printf( "FS_FOpenFileRead: %s (found in '%s')\n", filename, pak->pakFilename ); } return zfi->cur_file_info.uncompressed_size; } pakFile = pakFile->next; } while(pakFile != NULL); } else if ( search->dir ) { // check a file in the directory tree // if we are running restricted, the only files we // will allow to come from the directory are .cfg files l = (int)strlen( filename ); // FIXME TTimo I'm not sure about the fs_numServerPaks test // if you are using FS_ReadFile to find out if a file exists, // this test can make the search fail although the file is in the directory // I had the problem on https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=8 // turned out I used FS_FileExists instead if ( fs_restrict->integer || fs_numServerPaks ) { if ( Q_stricmp( filename + l - 4, ".cfg" ) // for config files && Q_stricmp( filename + l - 5, ".menu" ) // menu files && Q_stricmp( filename + l - 5, ".game" ) // menu files && Q_stricmp( filename + l - (int)strlen(demoExt), demoExt ) // menu files && Q_stricmp( filename + l - 4, ".dat" ) ) { // for journal files continue; } } dir = search->dir; netpath = FS_BuildOSPath( dir->path, dir->gamedir, filename ); fsh[*file].handleFiles.file.o = fopen (netpath, "rb"); if ( !fsh[*file].handleFiles.file.o ) { continue; } if ( Q_stricmp( filename + l - 4, ".cfg" ) // for config files && Q_stricmp( filename + l - 5, ".menu" ) // menu files && Q_stricmp( filename + l - 5, ".game" ) // menu files && Q_stricmp( filename + l - (int)strlen(demoExt), demoExt ) // menu files && Q_stricmp( filename + l - 4, ".dat" ) ) { // for journal files fs_fakeChkSum = random(); } Q_strncpyz( fsh[*file].name, filename, sizeof( fsh[*file].name ) ); fsh[*file].zipFile = qfalse; if ( fs_debug->integer ) { Com_Printf( "FS_FOpenFileRead: %s (found in '%s/%s')\n", filename, dir->path, dir->gamedir ); } // if we are getting it from the cdpath, optionally copy it // to the basepath if ( fs_copyfiles->integer && !Q_stricmp( dir->path, fs_cdpath->string ) ) { char *copypath; copypath = FS_BuildOSPath( fs_basepath->string, dir->gamedir, filename ); FS_CopyFile( netpath, copypath ); } return FS_filelength (*file); } } Com_DPrintf ("Can't find %s\n", filename); #ifdef FS_MISSING if (missingFiles) { fprintf(missingFiles, "%s\n", filename); } #endif *file = 0; return -1; } /* ================= FS_Read Properly handles partial reads ================= */ int FS_Read2( void *buffer, int len, fileHandle_t f ) { if ( !fs_searchpaths ) { Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); } if ( !f ) { return 0; } if (fsh[f].streamed) { int r; fsh[f].streamed = qfalse; r = Sys_StreamedRead( buffer, len, 1, f); fsh[f].streamed = qtrue; return r; } else { return FS_Read( buffer, len, f); } } int FS_Read( void *buffer, int len, fileHandle_t f ) { int block, remaining; int read; byte *buf; int tries; if ( !fs_searchpaths ) { Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); } if ( !f ) { return 0; } buf = (byte *)buffer; fs_readCount += len; if (fsh[f].zipFile == qfalse) { remaining = len; tries = 0; while (remaining) { block = remaining; read = (int)fread (buf, 1, block, fsh[f].handleFiles.file.o); if (read == 0) { // we might have been trying to read from a CD, which // sometimes returns a 0 read on windows if (!tries) { tries = 1; } else { return len-remaining; //Com_Error (ERR_FATAL, "FS_Read: 0 bytes read"); } } if (read == -1) { Com_Error (ERR_FATAL, "FS_Read: -1 bytes read"); } remaining -= read; buf += read; } return len; } else { return unzReadCurrentFile(fsh[f].handleFiles.file.z, buffer, len); } } /* ================= FS_Write Properly handles partial writes ================= */ int FS_Write( const void *buffer, int len, fileHandle_t h ) { int block, remaining; int written; byte *buf; int tries; FILE *f; if ( !fs_searchpaths ) { Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); } if ( !h ) { return 0; } f = FS_FileForHandle(h); buf = (byte *)buffer; remaining = len; tries = 0; while (remaining) { block = remaining; written = (int)fwrite (buf, 1, block, f); if (written == 0) { if (!tries) { tries = 1; } else { Com_Printf( "FS_Write: 0 bytes written\n" ); return 0; } } if (written == -1) { Com_Printf( "FS_Write: -1 bytes written\n" ); return 0; } remaining -= written; buf += written; } if ( fsh[h].handleSync ) { fflush( f ); } return len; } void QDECL FS_Printf( fileHandle_t h, const char *fmt, ... ) { va_list argptr; char msg[MAXPRINTMSG]; va_start (argptr,fmt); Q_vsnprintf (msg, sizeof(msg), fmt, argptr); va_end (argptr); FS_Write(msg, (int)strlen(msg), h); } /* ================= FS_Seek ================= */ int FS_Seek( fileHandle_t f, long offset, int origin ) { int _origin; char foo[65536]; if ( !fs_searchpaths ) { Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); return -1; } if (fsh[f].streamed) { fsh[f].streamed = qfalse; Sys_StreamSeek( f, offset, origin ); fsh[f].streamed = qtrue; } if (fsh[f].zipFile == qtrue) { if (offset == 0 && origin == FS_SEEK_SET) { // set the file position in the zip file (also sets the current file info) unzSetCurrentFileInfoPosition(fsh[f].handleFiles.file.z, fsh[f].zipFilePos); return unzOpenCurrentFile(fsh[f].handleFiles.file.z); } else if (offset<65536) { // set the file position in the zip file (also sets the current file info) unzSetCurrentFileInfoPosition(fsh[f].handleFiles.file.z, fsh[f].zipFilePos); unzOpenCurrentFile(fsh[f].handleFiles.file.z); return FS_Read(foo, offset, f); } else { Com_Error( ERR_FATAL, "ZIP FILE FSEEK NOT YET IMPLEMENTED\n" ); return -1; } } else { FILE *file; file = FS_FileForHandle(f); switch( origin ) { case FS_SEEK_CUR: _origin = SEEK_CUR; break; case FS_SEEK_END: _origin = SEEK_END; break; case FS_SEEK_SET: _origin = SEEK_SET; break; default: _origin = SEEK_CUR; Com_Error( ERR_FATAL, "Bad origin in FS_Seek\n" ); break; } return fseek( file, offset, _origin ); } } /* ====================================================================================== CONVENIENCE FUNCTIONS FOR ENTIRE FILES ====================================================================================== */ int FS_FileIsInPAK(const char *filename, int *pChecksum ) { searchpath_t *search; pack_t *pak; fileInPack_t *pakFile; long hash = 0; if ( !fs_searchpaths ) { Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); } if ( !filename ) { Com_Error( ERR_FATAL, "FS_FOpenFileRead: NULL 'filename' parameter passed\n" ); } // qpaths are not supposed to have a leading slash if ( filename[0] == '/' || filename[0] == '\\' ) { filename++; } // make absolutely sure that it can't back up the path. // The searchpaths do guarantee that something will always // be prepended, so we don't need to worry about "c:" or "//limbo" if ( strstr( filename, ".." ) || strstr( filename, "::" ) ) { return -1; } // // search through the path, one element at a time // for ( search = fs_searchpaths ; search ; search = search->next ) { // if (search->pack) { hash = FS_HashFileName(filename, search->pack->hashSize); } // is the element a pak file? if ( search->pack && search->pack->hashTable[hash] ) { // disregard if it doesn't match one of the allowed pure pak files if ( !FS_PakIsPure(search->pack) ) { continue; } // look through all the pak file elements pak = search->pack; pakFile = pak->hashTable[hash]; do { // case and separator insensitive comparisons if ( !FS_FilenameCompare( pakFile->name, filename ) ) { if (pChecksum) { *pChecksum = pak->pure_checksum; } return 1; } pakFile = pakFile->next; } while(pakFile != NULL); } } return -1; } /* ============ FS_ReadFile Filename are relative to the quake search path a null buffer will just return the file length without loading ============ */ int FS_ReadFile( const char *qpath, void **buffer ) { fileHandle_t h; byte* buf; qboolean isConfig; int len; if ( !fs_searchpaths ) { Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); } if ( !qpath || !qpath[0] ) { Com_Error( ERR_FATAL, "FS_ReadFile with empty name\n" ); } buf = NULL; // quiet compiler warning // if this is a .cfg file and we are playing back a journal, read // it from the journal file if ( strstr( qpath, ".cfg" ) ) { isConfig = qtrue; if ( com_journal && com_journal->integer == 2 ) { int r; Com_DPrintf( "Loading %s from journal file.\n", qpath ); r = FS_Read( &len, sizeof( len ), com_journalDataFile ); if ( r != sizeof( len ) ) { if (buffer != NULL) *buffer = NULL; return -1; } // if the file didn't exist when the journal was created if (!len) { if (buffer == NULL) { return 1; // hack for old journal files } *buffer = NULL; return -1; } if (buffer == NULL) { return len; } buf = (byte*) Hunk_AllocateTempMemory(len+1); *buffer = buf; r = FS_Read( buf, len, com_journalDataFile ); if ( r != len ) { Com_Error( ERR_FATAL, "Read from journalDataFile failed" ); } fs_loadCount++; fs_loadStack++; // guarantee that it will have a trailing 0 for string operations buf[len] = 0; return len; } } else { isConfig = qfalse; } // look for it in the filesystem or pack files len = FS_FOpenFileRead( qpath, &h, qfalse ); if ( h == 0 ) { if ( buffer ) { *buffer = NULL; } // if we are journalling and it is a config file, write a zero to the journal file if ( isConfig && com_journal && com_journal->integer == 1 ) { Com_DPrintf( "Writing zero for %s to journal file.\n", qpath ); len = 0; FS_Write( &len, sizeof( len ), com_journalDataFile ); FS_Flush( com_journalDataFile ); } return -1; } if ( !buffer ) { if ( isConfig && com_journal && com_journal->integer == 1 ) { Com_DPrintf( "Writing len for %s to journal file.\n", qpath ); FS_Write( &len, sizeof( len ), com_journalDataFile ); FS_Flush( com_journalDataFile ); } FS_FCloseFile( h); return len; } fs_loadCount++; fs_loadStack++; buf = (byte*) Hunk_AllocateTempMemory(len+1); *buffer = buf; FS_Read (buf, len, h); // guarantee that it will have a trailing 0 for string operations buf[len] = 0; FS_FCloseFile( h ); // if we are journalling and it is a config file, write it to the journal file if ( isConfig && com_journal && com_journal->integer == 1 ) { Com_DPrintf( "Writing %s to journal file.\n", qpath ); FS_Write( &len, sizeof( len ), com_journalDataFile ); FS_Write( buf, len, com_journalDataFile ); FS_Flush( com_journalDataFile ); } return len; } /* ============= FS_FreeFile ============= */ void FS_FreeFile( void *buffer ) { if ( !fs_searchpaths ) { Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); } if ( !buffer ) { Com_Error( ERR_FATAL, "FS_FreeFile( NULL )" ); } fs_loadStack--; Hunk_FreeTempMemory( buffer ); // if all of our temp files are free, clear all of our space if ( fs_loadStack == 0 ) { Hunk_ClearTempMemory(); } } /* ============ FS_WriteFile Filename are reletive to the quake search path ============ */ void FS_WriteFile( const char *qpath, const void *buffer, int size ) { fileHandle_t f; if ( !fs_searchpaths ) { Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); } if ( !qpath || !buffer ) { Com_Error( ERR_FATAL, "FS_WriteFile: NULL parameter" ); } f = FS_FOpenFileWrite( qpath ); if ( !f ) { Com_Printf( "Failed to open %s\n", qpath ); return; } FS_Write( buffer, size, f ); FS_FCloseFile( f ); } /* ========================================================================== ZIP FILE LOADING ========================================================================== */ /* ================= FS_LoadZipFile Creates a new pak_t in the search chain for the contents of a zip file. ================= */ static pack_t *FS_LoadZipFile( char *zipfile, const char *basename ) { fileInPack_t *buildBuffer; pack_t *pack; unzFile uf; int err; unz_global_info gi; char filename_inzip[MAX_ZPATH]; unz_file_info file_info; int i, len; long hash; int fs_numHeaderLongs; int *fs_headerLongs; char *namePtr; fs_numHeaderLongs = 0; uf = unzOpen(zipfile); err = unzGetGlobalInfo (uf,&gi); if (err != UNZ_OK) return NULL; fs_packFiles += gi.number_entry; len = 0; unzGoToFirstFile(uf); for (i = 0; i < gi.number_entry; i++) { err = unzGetCurrentFileInfo(uf, &file_info, filename_inzip, sizeof(filename_inzip), NULL, 0, NULL, 0); if (err != UNZ_OK) { break; } len += (int)strlen(filename_inzip) + 1; unzGoToNextFile(uf); } buildBuffer = (fileInPack_t*) Z_Malloc( (gi.number_entry * sizeof( fileInPack_t )) + len ); namePtr = ((char *) buildBuffer) + gi.number_entry * sizeof( fileInPack_t ); fs_headerLongs = (int*) Z_Malloc( gi.number_entry * sizeof(int) ); // get the hash table size from the number of files in the zip // because lots of custom pk3 files have less than 32 or 64 files for (i = 1; i <= MAX_FILEHASH_SIZE; i <<= 1) { if (i > gi.number_entry) { break; } } pack = (pack_t*) Z_Malloc( sizeof( pack_t ) + i * sizeof(fileInPack_t *) ); pack->hashSize = i; pack->hashTable = (fileInPack_t **) (((char *) pack) + sizeof( pack_t )); for(i = 0; i < pack->hashSize; i++) { pack->hashTable[i] = NULL; } Q_strncpyz( pack->pakFilename, zipfile, sizeof( pack->pakFilename ) ); Q_strncpyz( pack->pakBasename, basename, sizeof( pack->pakBasename ) ); // strip .pk3 if needed if ( (int)strlen( pack->pakBasename ) > 4 && !Q_stricmp( pack->pakBasename + (int)strlen( pack->pakBasename ) - 4, ".pk3" ) ) { pack->pakBasename[strlen( pack->pakBasename ) - 4] = 0; } pack->handle = uf; pack->numfiles = gi.number_entry; unzGoToFirstFile(uf); for (i = 0; i < gi.number_entry; i++) { err = unzGetCurrentFileInfo(uf, &file_info, filename_inzip, sizeof(filename_inzip), NULL, 0, NULL, 0); if (err != UNZ_OK) { break; } if (file_info.uncompressed_size > 0) { fs_headerLongs[fs_numHeaderLongs++] = LittleLong(file_info.crc); } Q_strlwr( filename_inzip ); hash = FS_HashFileName(filename_inzip, pack->hashSize); buildBuffer[i].name = namePtr; strcpy( buildBuffer[i].name, filename_inzip ); namePtr += (int)strlen(filename_inzip) + 1; // store the file position in the zip unzGetCurrentFileInfoPosition(uf, &buildBuffer[i].pos); // buildBuffer[i].next = pack->hashTable[hash]; pack->hashTable[hash] = &buildBuffer[i]; unzGoToNextFile(uf); } pack->checksum = Com_BlockChecksum( fs_headerLongs, 4 * fs_numHeaderLongs ); pack->pure_checksum = Com_BlockChecksumKey( fs_headerLongs, 4 * fs_numHeaderLongs, LittleLong(fs_checksumFeed) ); pack->checksum = LittleLong( pack->checksum ); pack->pure_checksum = LittleLong( pack->pure_checksum ); Z_Free(fs_headerLongs); pack->buildBuffer = buildBuffer; return pack; } /* ================================================================================= DIRECTORY SCANNING FUNCTIONS ================================================================================= */ #define MAX_FOUND_FILES 0x1000 static int FS_ReturnPath( const char *zname, char *zpath, int *depth ) { int len, at, newdep; newdep = 0; zpath[0] = 0; len = 0; at = 0; while(zname[at] != 0) { if (zname[at]=='/' || zname[at]=='\\') { len = at; newdep++; } at++; } strcpy(zpath, zname); zpath[len] = 0; *depth = newdep; return len; } /* ================== FS_AddFileToList ================== */ static int FS_AddFileToList( char *name, char *list[MAX_FOUND_FILES], int nfiles ) { int i; if ( nfiles == MAX_FOUND_FILES - 1 ) { return nfiles; } for ( i = 0 ; i < nfiles ; i++ ) { if ( !Q_stricmp( name, list[i] ) ) { return nfiles; // allready in list } } list[nfiles] = CopyString( name ); nfiles++; return nfiles; } /* =============== FS_ListFilteredFiles Returns a uniqued list of files that match the given criteria from all search paths =============== */ char **FS_ListFilteredFiles( const char *path, const char *extension, char *filter, int *numfiles ) { int nfiles; char **listCopy; char *list[MAX_FOUND_FILES]; searchpath_t *search; int i; int pathLength; int extensionLength; int length, pathDepth, temp; pack_t *pak; fileInPack_t *buildBuffer; char zpath[MAX_ZPATH]; if ( !fs_searchpaths ) { Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); } if ( !path ) { *numfiles = 0; return NULL; } if ( !extension ) { extension = ""; } pathLength = (int)strlen( path ); if ( path[pathLength-1] == '\\' || path[pathLength-1] == '/' ) { pathLength--; } extensionLength = (int)strlen( extension ); nfiles = 0; FS_ReturnPath(path, zpath, &pathDepth); // // search through the path, one element at a time, adding to list // for (search = fs_searchpaths ; search ; search = search->next) { // is the element a pak file? if (search->pack) { //ZOID: If we are pure, don't search for files on paks that // aren't on the pure list if ( !FS_PakIsPure(search->pack) ) { continue; } // look through all the pak file elements pak = search->pack; buildBuffer = pak->buildBuffer; for (i = 0; i < pak->numfiles; i++) { char *name; int zpathLen, depth; // check for directory match name = buildBuffer[i].name; // if (filter) { // case insensitive if (!Com_FilterPath( filter, name, qfalse )) continue; // unique the match nfiles = FS_AddFileToList( name, list, nfiles ); } else { zpathLen = FS_ReturnPath(name, zpath, &depth); if ( (depth-pathDepth)>2 || pathLength > zpathLen || Q_stricmpn( name, path, pathLength ) ) { continue; } // check for extension match length = (int)strlen( name ); if ( length < extensionLength ) { continue; } if ( Q_stricmp( name + length - extensionLength, extension ) ) { continue; } // unique the match temp = pathLength; if (pathLength) { temp++; // include the '/' } nfiles = FS_AddFileToList( name + temp, list, nfiles ); } } } else if (search->dir) { // scan for files in the filesystem char *netpath; int numSysFiles; char **sysFiles; char *name; // don't scan directories for files if we are pure or restricted if ( fs_restrict->integer || fs_numServerPaks ) { continue; } else { netpath = FS_BuildOSPath( search->dir->path, search->dir->gamedir, path ); sysFiles = Sys_ListFiles( netpath, extension, filter, &numSysFiles, qfalse ); for ( i = 0 ; i < numSysFiles ; i++ ) { // unique the match name = sysFiles[i]; nfiles = FS_AddFileToList( name, list, nfiles ); } Sys_FreeFileList( sysFiles ); } } } // return a copy of the list *numfiles = nfiles; if ( !nfiles ) { return NULL; } listCopy = (char**) Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ) ); for ( i = 0 ; i < nfiles ; i++ ) { listCopy[i] = list[i]; } listCopy[i] = NULL; return listCopy; } /* ================= FS_ListFiles ================= */ char **FS_ListFiles( const char *path, const char *extension, int *numfiles ) { return FS_ListFilteredFiles( path, extension, NULL, numfiles ); } /* ================= FS_FreeFileList ================= */ void FS_FreeFileList( char **list ) { int i; if ( !fs_searchpaths ) { Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); } if ( !list ) { return; } for ( i = 0 ; list[i] ; i++ ) { Z_Free( list[i] ); } Z_Free( list ); } /* ================ FS_GetFileList ================ */ int FS_GetFileList( const char *path, const char *extension, char *listbuf, int bufsize ) { int nFiles, i, nTotal, nLen; char **pFiles = NULL; *listbuf = 0; nFiles = 0; nTotal = 0; if (Q_stricmp(path, "$modlist") == 0) { return FS_GetModList(listbuf, bufsize); } pFiles = FS_ListFiles(path, extension, &nFiles); for (i =0; i < nFiles; i++) { nLen = (int)strlen(pFiles[i]) + 1; if (nTotal + nLen + 1 < bufsize) { strcpy(listbuf, pFiles[i]); listbuf += nLen; nTotal += nLen; } else { nFiles = i; break; } } FS_FreeFileList(pFiles); return nFiles; } /* ======================= Sys_ConcatenateFileLists mkv: Naive implementation. Concatenates three lists into a new list, and frees the old lists from the heap. bk001129 - from cvs1.17 (mkv) FIXME TTimo those two should move to common.c next to Sys_ListFiles ======================= */ static unsigned int Sys_CountFileList(char **list) { int i = 0; if (list) { while (*list) { list++; i++; } } return i; } static char** Sys_ConcatenateFileLists( char **list0, char **list1, char **list2 ) { int totalLength = 0; char** cat = NULL, **dst, **src; totalLength += Sys_CountFileList(list0); totalLength += Sys_CountFileList(list1); totalLength += Sys_CountFileList(list2); /* Create new list. */ dst = cat = (char**) Z_Malloc( ( totalLength + 1 ) * sizeof( char* ) ); /* Copy over lists. */ if (list0) { for (src = list0; *src; src++, dst++) *dst = *src; } if (list1) { for (src = list1; *src; src++, dst++) *dst = *src; } if (list2) { for (src = list2; *src; src++, dst++) *dst = *src; } // Terminate the list *dst = NULL; // Free our old lists. // NOTE: not freeing their content, it's been merged in dst and still being used if (list0) Z_Free( list0 ); if (list1) Z_Free( list1 ); if (list2) Z_Free( list2 ); return cat; } /* ================ FS_GetModList Returns a list of mod directory names A mod directory is a peer to baseq3 with a pk3 in it The directories are searched in base path, cd path and home path ================ */ int FS_GetModList( char *listbuf, int bufsize ) { int nMods, i, j, nTotal, nLen, nPaks, nPotential, nDescLen; char **pFiles = NULL; char **pPaks = NULL; char *name, *path; char descPath[MAX_OSPATH]; fileHandle_t descHandle; int dummy; char **pFiles0 = NULL; char **pFiles1 = NULL; char **pFiles2 = NULL; qboolean bDrop = qfalse; *listbuf = 0; nMods = nPotential = nTotal = 0; pFiles0 = Sys_ListFiles( fs_homepath->string, NULL, NULL, &dummy, qtrue ); pFiles1 = Sys_ListFiles( fs_basepath->string, NULL, NULL, &dummy, qtrue ); pFiles2 = Sys_ListFiles( fs_cdpath->string, NULL, NULL, &dummy, qtrue ); // we searched for mods in the three paths // it is likely that we have duplicate names now, which we will cleanup below pFiles = Sys_ConcatenateFileLists( pFiles0, pFiles1, pFiles2 ); nPotential = Sys_CountFileList(pFiles); for ( i = 0 ; i < nPotential ; i++ ) { name = pFiles[i]; // NOTE: cleaner would involve more changes // ignore duplicate mod directories if (i!=0) { bDrop = qfalse; for(j=0; jstring, name, "" ); nPaks = 0; pPaks = Sys_ListFiles(path, ".pk3", NULL, &nPaks, qfalse); Sys_FreeFileList( pPaks ); // we only use Sys_ListFiles to check wether .pk3 files are present /* Try on cd path */ if( nPaks <= 0 ) { path = FS_BuildOSPath( fs_cdpath->string, name, "" ); nPaks = 0; pPaks = Sys_ListFiles( path, ".pk3", NULL, &nPaks, qfalse ); Sys_FreeFileList( pPaks ); } /* try on home path */ if ( nPaks <= 0 ) { path = FS_BuildOSPath( fs_homepath->string, name, "" ); nPaks = 0; pPaks = Sys_ListFiles( path, ".pk3", NULL, &nPaks, qfalse ); Sys_FreeFileList( pPaks ); } if (nPaks > 0) { nLen = (int)strlen(name) + 1; // nLen is the length of the mod path // we need to see if there is a description available descPath[0] = '\0'; strcpy(descPath, name); strcat(descPath, "/description.txt"); nDescLen = FS_SV_FOpenFileRead( descPath, &descHandle ); if ( nDescLen > 0 && descHandle) { FILE *file; file = FS_FileForHandle(descHandle); Com_Memset( descPath, 0, sizeof( descPath ) ); nDescLen = (int)fread(descPath, 1, 48, file); if (nDescLen >= 0) { descPath[nDescLen] = '\0'; } FS_FCloseFile(descHandle); } else { strcpy(descPath, name); } nDescLen = (int)strlen(descPath) + 1; if (nTotal + nLen + 1 + nDescLen + 1 < bufsize) { strcpy(listbuf, name); listbuf += nLen; strcpy(listbuf, descPath); listbuf += nDescLen; nTotal += nLen + nDescLen; nMods++; } else { break; } } } } Sys_FreeFileList( pFiles ); return nMods; } //============================================================================ /* ================ FS_Dir_f ================ */ void FS_Dir_f( void ) { char *path; char *extension; char **dirnames; int ndirs; int i; if ( Cmd_Argc() < 2 || Cmd_Argc() > 3 ) { Com_Printf( "usage: dir [extension]\n" ); return; } if ( Cmd_Argc() == 2 ) { path = Cmd_Argv( 1 ); extension = ""; } else { path = Cmd_Argv( 1 ); extension = Cmd_Argv( 2 ); } Com_Printf( "Directory of %s %s\n", path, extension ); Com_Printf( "---------------\n" ); dirnames = FS_ListFiles( path, extension, &ndirs ); for ( i = 0; i < ndirs; i++ ) { Com_Printf( "%s\n", dirnames[i] ); } FS_FreeFileList( dirnames ); } /* =========== FS_ConvertPath =========== */ void FS_ConvertPath( char *s ) { while (*s) { if ( *s == '\\' || *s == ':' ) { *s = '/'; } s++; } } /* =========== FS_PathCmp Ignore case and seprator char distinctions =========== */ int FS_PathCmp( const char *s1, const char *s2 ) { int c1, c2; do { c1 = *s1++; c2 = *s2++; if (c1 >= 'a' && c1 <= 'z') { c1 -= ('a' - 'A'); } if (c2 >= 'a' && c2 <= 'z') { c2 -= ('a' - 'A'); } if ( c1 == '\\' || c1 == ':' ) { c1 = '/'; } if ( c2 == '\\' || c2 == ':' ) { c2 = '/'; } if (c1 < c2) { return -1; // strings not equal } if (c1 > c2) { return 1; } } while (c1); return 0; // strings are equal } /* ================ FS_SortFileList ================ */ void FS_SortFileList(char **filelist, int numfiles) { int i, j, k, numsortedfiles; char **sortedlist; sortedlist = (char**) Z_Malloc( ( numfiles + 1 ) * sizeof( *sortedlist ) ); sortedlist[0] = NULL; numsortedfiles = 0; for (i = 0; i < numfiles; i++) { for (j = 0; j < numsortedfiles; j++) { if (FS_PathCmp(filelist[i], sortedlist[j]) < 0) { break; } } for (k = numsortedfiles; k > j; k--) { sortedlist[k] = sortedlist[k-1]; } sortedlist[j] = filelist[i]; numsortedfiles++; } Com_Memcpy(filelist, sortedlist, numfiles * sizeof( *filelist ) ); Z_Free(sortedlist); } /* ================ FS_NewDir_f ================ */ void FS_NewDir_f( void ) { char *filter; char **dirnames; int ndirs; int i; if ( Cmd_Argc() < 2 ) { Com_Printf( "usage: fdir \n" ); Com_Printf( "example: fdir *q3dm*.bsp\n"); return; } filter = Cmd_Argv( 1 ); Com_Printf( "---------------\n" ); dirnames = FS_ListFilteredFiles( "", "", filter, &ndirs ); FS_SortFileList(dirnames, ndirs); for ( i = 0; i < ndirs; i++ ) { FS_ConvertPath(dirnames[i]); Com_Printf( "%s\n", dirnames[i] ); } Com_Printf( "%d files listed\n", ndirs ); FS_FreeFileList( dirnames ); } /* ============ FS_Path_f ============ */ void FS_Path_f( void ) { searchpath_t *s; int i; Com_Printf ("Current search path:\n"); for (s = fs_searchpaths; s; s = s->next) { if (s->pack) { Com_Printf ("%s (%i files)\n", s->pack->pakFilename, s->pack->numfiles); if ( fs_numServerPaks ) { if ( !FS_PakIsPure(s->pack) ) { Com_Printf( " not on the pure list\n" ); } else { Com_Printf( " on the pure list\n" ); } } } else { Com_Printf ("%s/%s\n", s->dir->path, s->dir->gamedir ); } } Com_Printf( "\n" ); for ( i = 1 ; i < MAX_FILE_HANDLES ; i++ ) { if ( fsh[i].handleFiles.file.o ) { Com_Printf( "handle %i: %s\n", i, fsh[i].name ); } } } /* ============ FS_TouchFile_f The only purpose of this function is to allow game script files to copy arbitrary files furing an "fs_copyfiles 1" run. ============ */ void FS_TouchFile_f( void ) { fileHandle_t f; if ( Cmd_Argc() != 2 ) { Com_Printf( "Usage: touchFile \n" ); return; } FS_FOpenFileRead( Cmd_Argv( 1 ), &f, qfalse ); if ( f ) { FS_FCloseFile( f ); } } //=========================================================================== static int QDECL paksort( const void *a, const void *b ) { char *aa, *bb; aa = *(char **)a; bb = *(char **)b; return FS_PathCmp( aa, bb ); } /* ================ FS_AddGameDirectory Sets fs_gamedir, adds the directory to the head of the path, then loads the zip headers ================ */ #define MAX_PAKFILES 1024 static void FS_AddGameDirectory( const char *path, const char *dir ) { searchpath_t *sp; int i; searchpath_t *search; pack_t *pak; char *pakfile; int numfiles; char **pakfiles; char *sorted[MAX_PAKFILES]; // this fixes the case where fs_basepath is the same as fs_cdpath // which happens on full installs for ( sp = fs_searchpaths ; sp ; sp = sp->next ) { if ( sp->dir && !Q_stricmp(sp->dir->path, path) && !Q_stricmp(sp->dir->gamedir, dir)) { return; // we've already got this one } } Q_strncpyz( fs_gamedir, dir, sizeof( fs_gamedir ) ); // // add the directory to the search path // search = (searchpath_t*) Z_Malloc (sizeof(searchpath_t)); search->dir = (directory_t*) Z_Malloc( sizeof( *search->dir ) ); Q_strncpyz( search->dir->path, path, sizeof( search->dir->path ) ); Q_strncpyz( search->dir->gamedir, dir, sizeof( search->dir->gamedir ) ); search->next = fs_searchpaths; fs_searchpaths = search; // find all pak files in this directory pakfile = FS_BuildOSPath( path, dir, "" ); pakfile[ (int)strlen(pakfile) - 1 ] = 0; // strip the trailing slash pakfiles = Sys_ListFiles( pakfile, ".pk3", NULL, &numfiles, qfalse ); // sort them so that later alphabetic matches override // earlier ones. This makes pak1.pk3 override pak0.pk3 if ( numfiles > MAX_PAKFILES ) { numfiles = MAX_PAKFILES; } for ( i = 0 ; i < numfiles ; i++ ) { sorted[i] = pakfiles[i]; } qsort( sorted, numfiles, sizeof(void*), paksort ); for ( i = 0 ; i < numfiles ; i++ ) { pakfile = FS_BuildOSPath( path, dir, sorted[i] ); if ( ( pak = FS_LoadZipFile( pakfile, sorted[i] ) ) == 0 ) continue; // store the game name for downloading strcpy(pak->pakGamename, dir); search = (searchpath_t*) Z_Malloc (sizeof(searchpath_t)); search->pack = pak; search->next = fs_searchpaths; fs_searchpaths = search; } // done Sys_FreeFileList( pakfiles ); } /* ================ FS_idPak ================ */ qboolean FS_idPak( char *pak, char *base ) { int i; for (i = 0; i < NUM_ID_PAKS; i++) { if ( !FS_FilenameCompare(pak, va("%s/pak%d", base, i)) ) { break; } } if (i < NUM_ID_PAKS) { return qtrue; } return qfalse; } /* ================ FS_ComparePaks ---------------- dlstring == qtrue Returns a list of pak files that we should download from the server. They all get stored in the current gamedir and an FS_Restart will be fired up after we download them all. The string is the format: @remotename@localname [repeat] static int fs_numServerReferencedPaks; static int fs_serverReferencedPaks[MAX_SEARCH_PATHS]; static char *fs_serverReferencedPakNames[MAX_SEARCH_PATHS]; ---------------- dlstring == qfalse we are not interested in a download string format, we want something human-readable (this is used for diagnostics while connecting to a pure server) ================ */ qboolean FS_ComparePaks( char *neededpaks, int len, qboolean dlstring ) { searchpath_t *sp; qboolean havepak, badchecksum; int i; if ( !fs_numServerReferencedPaks ) { return qfalse; // Server didn't send any pack information along } *neededpaks = 0; for ( i = 0 ; i < fs_numServerReferencedPaks ; i++ ) { // Ok, see if we have this pak file badchecksum = qfalse; havepak = qfalse; // never autodownload any of the id paks if ( FS_idPak(fs_serverReferencedPakNames[i], "baseq3") || FS_idPak(fs_serverReferencedPakNames[i], "missionpack") ) { continue; } for ( sp = fs_searchpaths ; sp ; sp = sp->next ) { if ( sp->pack && sp->pack->checksum == fs_serverReferencedPaks[i] ) { havepak = qtrue; // This is it! break; } } if ( !havepak && fs_serverReferencedPakNames[i] && *fs_serverReferencedPakNames[i] ) { // Don't got it if (dlstring) { // Remote name Q_strcat( neededpaks, len, "@"); Q_strcat( neededpaks, len, fs_serverReferencedPakNames[i] ); Q_strcat( neededpaks, len, ".pk3" ); // Local name Q_strcat( neededpaks, len, "@"); // Do we have one with the same name? if ( FS_SV_FileExists( va( "%s.pk3", fs_serverReferencedPakNames[i] ) ) ) { char st[MAX_ZPATH]; // We already have one called this, we need to download it to another name // Make something up with the checksum in it Com_sprintf( st, sizeof( st ), "%s.%08x.pk3", fs_serverReferencedPakNames[i], fs_serverReferencedPaks[i] ); Q_strcat( neededpaks, len, st ); } else { Q_strcat( neededpaks, len, fs_serverReferencedPakNames[i] ); Q_strcat( neededpaks, len, ".pk3" ); } } else { Q_strcat( neededpaks, len, fs_serverReferencedPakNames[i] ); Q_strcat( neededpaks, len, ".pk3" ); // Do we have one with the same name? if ( FS_SV_FileExists( va( "%s.pk3", fs_serverReferencedPakNames[i] ) ) ) { Q_strcat( neededpaks, len, " (local file exists with wrong checksum)"); } Q_strcat( neededpaks, len, "\n"); } } } if ( *neededpaks ) { return qtrue; } return qfalse; // We have them all } /* ================ FS_Shutdown Frees all resources and closes all files ================ */ void FS_Shutdown( qboolean closemfp ) { searchpath_t *p, *next; int i; for(i = 0; i < MAX_FILE_HANDLES; i++) { if (fsh[i].fileSize) { FS_FCloseFile(i); } } // free everything for ( p = fs_searchpaths ; p ; p = next ) { next = p->next; if ( p->pack ) { unzClose(p->pack->handle); Z_Free( p->pack->buildBuffer ); Z_Free( p->pack ); } if ( p->dir ) { Z_Free( p->dir ); } Z_Free( p ); } // any FS_ calls will now be an error until reinitialized fs_searchpaths = NULL; Cmd_RemoveCommand( "path" ); Cmd_RemoveCommand( "dir" ); Cmd_RemoveCommand( "fdir" ); Cmd_RemoveCommand( "touchFile" ); #ifdef FS_MISSING if (closemfp) { fclose(missingFiles); } #endif } void Com_AppendCDKey( const char *filename ); void Com_ReadCDKey( const char *filename ); /* ================ FS_ReorderPurePaks NOTE TTimo: the reordering that happens here is not reflected in the cvars (\cvarlist *pak*) this can lead to misleading situations, see https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=540 ================ */ static void FS_ReorderPurePaks() { searchpath_t *s; int i; searchpath_t **p_insert_index, // for linked list reordering **p_previous; // when doing the scan // only relevant when connected to pure server if ( !fs_numServerPaks ) return; fs_reordered = qfalse; p_insert_index = &fs_searchpaths; // we insert in order at the beginning of the list for ( i = 0 ; i < fs_numServerPaks ; i++ ) { p_previous = p_insert_index; // track the pointer-to-current-item for (s = *p_insert_index; s; s = s->next) { // the part of the list before p_insert_index has been sorted already if (s->pack && fs_serverPaks[i] == s->pack->checksum) { fs_reordered = qtrue; // move this element to the insert list *p_previous = s->next; s->next = *p_insert_index; *p_insert_index = s; // increment insert list p_insert_index = &s->next; break; // iterate to next server pack } p_previous = &s->next; } } } /* ================ FS_Startup ================ */ static void FS_Startup( const char *gameName ) { const char *homePath; cvar_t *fs; Com_Printf( "----- FS_Startup -----\n" ); fs_debug = Cvar_Get( "fs_debug", "0", 0 ); fs_copyfiles = Cvar_Get( "fs_copyfiles", "0", CVAR_INIT ); fs_cdpath = Cvar_Get ("fs_cdpath", Sys_DefaultCDPath(), CVAR_INIT ); fs_basepath = Cvar_Get ("fs_basepath", Sys_DefaultInstallPath(), CVAR_INIT ); fs_basegame = Cvar_Get ("fs_basegame", "", CVAR_INIT ); homePath = Sys_DefaultHomePath(); if (!homePath || !homePath[0]) { homePath = fs_basepath->string; } fs_homepath = Cvar_Get ("fs_homepath", homePath, CVAR_INIT ); fs_gamedirvar = Cvar_Get ("fs_game", "", CVAR_INIT|CVAR_SYSTEMINFO ); fs_restrict = Cvar_Get ("fs_restrict", "", CVAR_INIT ); // add search path elements in reverse priority order if (fs_cdpath->string[0]) { FS_AddGameDirectory( fs_cdpath->string, gameName ); } if (fs_basepath->string[0]) { FS_AddGameDirectory( fs_basepath->string, gameName ); } // fs_homepath is somewhat particular to *nix systems, only add if relevant // NOTE: same filtering below for mods and basegame if (fs_basepath->string[0] && Q_stricmp(fs_homepath->string,fs_basepath->string)) { FS_AddGameDirectory ( fs_homepath->string, gameName ); } // check for additional base game so mods can be based upon other mods if ( fs_basegame->string[0] && !Q_stricmp( gameName, BASEGAME ) && Q_stricmp( fs_basegame->string, gameName ) ) { if (fs_cdpath->string[0]) { FS_AddGameDirectory(fs_cdpath->string, fs_basegame->string); } if (fs_basepath->string[0]) { FS_AddGameDirectory(fs_basepath->string, fs_basegame->string); } if (fs_homepath->string[0] && Q_stricmp(fs_homepath->string,fs_basepath->string)) { FS_AddGameDirectory(fs_homepath->string, fs_basegame->string); } } // check for additional game folder for mods if ( fs_gamedirvar->string[0] && !Q_stricmp( gameName, BASEGAME ) && Q_stricmp( fs_gamedirvar->string, gameName ) ) { if (fs_cdpath->string[0]) { FS_AddGameDirectory(fs_cdpath->string, fs_gamedirvar->string); } if (fs_basepath->string[0]) { FS_AddGameDirectory(fs_basepath->string, fs_gamedirvar->string); } if (fs_homepath->string[0] && Q_stricmp(fs_homepath->string,fs_basepath->string)) { FS_AddGameDirectory(fs_homepath->string, fs_gamedirvar->string); } } Com_ReadCDKey( "baseq3" ); fs = Cvar_Get ("fs_game", "", CVAR_INIT|CVAR_SYSTEMINFO ); if (fs && fs->string[0] != 0) { Com_AppendCDKey( fs->string ); } // add our commands Cmd_AddCommand ("path", FS_Path_f); Cmd_AddCommand ("dir", FS_Dir_f ); Cmd_AddCommand ("fdir", FS_NewDir_f ); Cmd_AddCommand ("touchFile", FS_TouchFile_f ); // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=506 // reorder the pure pk3 files according to server order FS_ReorderPurePaks(); // print the current search paths FS_Path_f(); fs_gamedirvar->modified = qfalse; // We just loaded, it's not modified Com_Printf( "----------------------\n" ); #ifdef FS_MISSING if (missingFiles == NULL) { missingFiles = fopen( "\\missing.txt", "ab" ); } #endif Com_Printf( "%d files in pk3 files\n", fs_packFiles ); } /* =================== FS_SetRestrictions Looks for product keys and restricts media add on ability if the full version is not found =================== */ static void FS_SetRestrictions( void ) { searchpath_t *path; #ifndef PRE_RELEASE_DEMO char *productId; // if fs_restrict is set, don't even look for the id file, // which allows the demo release to be tested even if // the full game is present if ( !fs_restrict->integer ) { // look for the full game id FS_ReadFile( "productid.txt", (void **)&productId ); if ( productId ) { // check against the hardcoded string int seed, i; seed = 5000; for ( i = 0 ; i < sizeof( fs_scrambledProductId ) ; i++ ) { if ( ( fs_scrambledProductId[i] ^ (seed&255) ) != productId[i] ) { break; } seed = (69069 * seed + 1); } FS_FreeFile( productId ); if ( i == sizeof( fs_scrambledProductId ) ) { return; // no restrictions } Com_Error( ERR_FATAL, "Invalid product identification" ); } } #endif Cvar_Set( "fs_restrict", "1" ); Com_Printf( "\nRunning in restricted demo mode.\n\n" ); // restart the filesystem with just the demo directory FS_Shutdown(qfalse); FS_Startup( DEMOGAME ); // make sure that the pak file has the header checksum we expect for ( path = fs_searchpaths ; path ; path = path->next ) { if ( path->pack ) { // a tiny attempt to keep the checksum from being scannable from the exe if ( (path->pack->checksum ^ 0x02261994u) != (DEMO_PAK_CHECKSUM ^ 0x02261994u) ) { Com_Error( ERR_FATAL, "Corrupted pak0.pk3: %u", path->pack->checksum ); } } } } /* ===================== FS_GamePureChecksum Returns the checksum of the pk3 from which the server loaded the qagame.qvm ===================== */ const char *FS_GamePureChecksum( void ) { static char info[MAX_STRING_TOKENS]; searchpath_t *search; info[0] = 0; for ( search = fs_searchpaths ; search ; search = search->next ) { // is the element a pak file? if ( search->pack ) { if (search->pack->referenced & FS_QAGAME_REF) { Com_sprintf(info, sizeof(info), "%d", search->pack->checksum); } } } return info; } /* ===================== FS_LoadedPakChecksums Returns a space separated string containing the checksums of all loaded pk3 files. Servers with sv_pure set will get this string and pass it to clients. ===================== */ const char *FS_LoadedPakChecksums( void ) { static char info[BIG_INFO_STRING]; searchpath_t *search; info[0] = 0; for ( search = fs_searchpaths ; search ; search = search->next ) { // is the element a pak file? if ( !search->pack ) { continue; } Q_strcat( info, sizeof( info ), va("%i ", search->pack->checksum ) ); } return info; } /* ===================== FS_LoadedPakNames Returns a space separated string containing the names of all loaded pk3 files. Servers with sv_pure set will get this string and pass it to clients. ===================== */ const char *FS_LoadedPakNames( void ) { static char info[BIG_INFO_STRING]; searchpath_t *search; info[0] = 0; for ( search = fs_searchpaths ; search ; search = search->next ) { // is the element a pak file? if ( !search->pack ) { continue; } if (*info) { Q_strcat(info, sizeof( info ), " " ); } Q_strcat( info, sizeof( info ), search->pack->pakBasename ); } return info; } /* ===================== FS_LoadedPakPureChecksums Returns a space separated string containing the pure checksums of all loaded pk3 files. Servers with sv_pure use these checksums to compare with the checksums the clients send back to the server. ===================== */ const char *FS_LoadedPakPureChecksums( void ) { static char info[BIG_INFO_STRING]; searchpath_t *search; info[0] = 0; for ( search = fs_searchpaths ; search ; search = search->next ) { // is the element a pak file? if ( !search->pack ) { continue; } Q_strcat( info, sizeof( info ), va("%i ", search->pack->pure_checksum ) ); } return info; } /* ===================== FS_ReferencedPakChecksums Returns a space separated string containing the checksums of all referenced pk3 files. The server will send this to the clients so they can check which files should be auto-downloaded. ===================== */ const char *FS_ReferencedPakChecksums( void ) { static char info[BIG_INFO_STRING]; searchpath_t *search; info[0] = 0; for ( search = fs_searchpaths ; search ; search = search->next ) { // is the element a pak file? if ( search->pack ) { if (search->pack->referenced || Q_stricmpn(search->pack->pakGamename, BASEGAME, (int)strlen(BASEGAME))) { Q_strcat( info, sizeof( info ), va("%i ", search->pack->checksum ) ); } } } return info; } /* ===================== FS_ReferencedPakPureChecksums Returns a space separated string containing the pure checksums of all referenced pk3 files. Servers with sv_pure set will get this string back from clients for pure validation The string has a specific order, "cgame ui @ ref1 ref2 ref3 ..." ===================== */ const char *FS_ReferencedPakPureChecksums( void ) { static char info[BIG_INFO_STRING]; searchpath_t *search; int nFlags, numPaks, checksum; info[0] = 0; checksum = fs_checksumFeed; numPaks = 0; for (nFlags = FS_CGAME_REF; nFlags; nFlags = nFlags >> 1) { if (nFlags & FS_GENERAL_REF) { // add a delimter between must haves and general refs //Q_strcat(info, sizeof(info), "@ "); info[strlen(info)+1] = '\0'; info[strlen(info)+2] = '\0'; info[strlen(info)] = '@'; info[strlen(info)] = ' '; } for ( search = fs_searchpaths ; search ; search = search->next ) { // is the element a pak file and has it been referenced based on flag? if ( search->pack && (search->pack->referenced & nFlags)) { Q_strcat( info, sizeof( info ), va("%i ", search->pack->pure_checksum ) ); if (nFlags & (FS_CGAME_REF | FS_UI_REF)) { break; } checksum ^= search->pack->pure_checksum; numPaks++; } } if (fs_fakeChkSum != 0) { // only added if a non-pure file is referenced Q_strcat( info, sizeof( info ), va("%i ", fs_fakeChkSum ) ); } } // last checksum is the encoded number of referenced pk3s checksum ^= numPaks; Q_strcat( info, sizeof( info ), va("%i ", checksum ) ); return info; } /* ===================== FS_ReferencedPakNames Returns a space separated string containing the names of all referenced pk3 files. The server will send this to the clients so they can check which files should be auto-downloaded. ===================== */ const char *FS_ReferencedPakNames( void ) { static char info[BIG_INFO_STRING]; searchpath_t *search; info[0] = 0; // we want to return ALL pk3's from the fs_game path // and referenced one's from baseq3 for ( search = fs_searchpaths ; search ; search = search->next ) { // is the element a pak file? if ( search->pack ) { if (*info) { Q_strcat(info, sizeof( info ), " " ); } if (search->pack->referenced || Q_stricmpn(search->pack->pakGamename, BASEGAME, (int)strlen(BASEGAME))) { Q_strcat( info, sizeof( info ), search->pack->pakGamename ); Q_strcat( info, sizeof( info ), "/" ); Q_strcat( info, sizeof( info ), search->pack->pakBasename ); } } } return info; } /* ===================== FS_ClearPakReferences ===================== */ void FS_ClearPakReferences( int flags ) { searchpath_t *search; if ( !flags ) { flags = -1; } for ( search = fs_searchpaths; search; search = search->next ) { // is the element a pak file and has it been referenced? if ( search->pack ) { search->pack->referenced &= ~flags; } } } /* ===================== FS_PureServerSetLoadedPaks If the string is empty, all data sources will be allowed. If not empty, only pk3 files that match one of the space separated checksums will be checked for files, with the exception of .cfg and .dat files. ===================== */ void FS_PureServerSetLoadedPaks( const char *pakSums, const char *pakNames ) { int i, c, d; Cmd_TokenizeString( pakSums ); c = Cmd_Argc(); if ( c > MAX_SEARCH_PATHS ) { c = MAX_SEARCH_PATHS; } fs_numServerPaks = c; for ( i = 0 ; i < c ; i++ ) { fs_serverPaks[i] = atoi( Cmd_Argv( i ) ); } if (fs_numServerPaks) { Com_DPrintf( "Connected to a pure server.\n" ); } else { if (fs_reordered) { // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=540 // force a restart to make sure the search order will be correct Com_DPrintf( "FS search reorder is required\n" ); FS_Restart(fs_checksumFeed); return; } } for ( i = 0 ; i < c ; i++ ) { if (fs_serverPakNames[i]) { Z_Free(fs_serverPakNames[i]); } fs_serverPakNames[i] = NULL; } if ( pakNames && *pakNames ) { Cmd_TokenizeString( pakNames ); d = Cmd_Argc(); if ( d > MAX_SEARCH_PATHS ) { d = MAX_SEARCH_PATHS; } for ( i = 0 ; i < d ; i++ ) { fs_serverPakNames[i] = CopyString( Cmd_Argv( i ) ); } } } /* ===================== FS_PureServerSetReferencedPaks The checksums and names of the pk3 files referenced at the server are sent to the client and stored here. The client will use these checksums to see if any pk3 files need to be auto-downloaded. ===================== */ void FS_PureServerSetReferencedPaks( const char *pakSums, const char *pakNames ) { int i, c, d; Cmd_TokenizeString( pakSums ); c = Cmd_Argc(); if ( c > MAX_SEARCH_PATHS ) { c = MAX_SEARCH_PATHS; } fs_numServerReferencedPaks = c; for ( i = 0 ; i < c ; i++ ) { fs_serverReferencedPaks[i] = atoi( Cmd_Argv( i ) ); } for ( i = 0 ; i < c ; i++ ) { if (fs_serverReferencedPakNames[i]) { Z_Free(fs_serverReferencedPakNames[i]); } fs_serverReferencedPakNames[i] = NULL; } if ( pakNames && *pakNames ) { Cmd_TokenizeString( pakNames ); d = Cmd_Argc(); if ( d > MAX_SEARCH_PATHS ) { d = MAX_SEARCH_PATHS; } for ( i = 0 ; i < d ; i++ ) { fs_serverReferencedPakNames[i] = CopyString( Cmd_Argv( i ) ); } } } /* ================ FS_InitFilesystem Called only at inital startup, not when the filesystem is resetting due to a game change ================ */ void FS_InitFilesystem( void ) { // allow command line parms to override our defaults // we have to specially handle this, because normal command // line variable sets don't happen until after the filesystem // has already been initialized Com_StartupVariable( "fs_cdpath" ); Com_StartupVariable( "fs_basepath" ); Com_StartupVariable( "fs_homepath" ); Com_StartupVariable( "fs_game" ); Com_StartupVariable( "fs_copyfiles" ); Com_StartupVariable( "fs_restrict" ); // try to start up normally FS_Startup( BASEGAME ); // see if we are going to allow add-ons FS_SetRestrictions(); // if we can't find default.cfg, assume that the paths are // busted and error out now, rather than getting an unreadable // graphics screen when the font fails to load if ( FS_ReadFile( "default.cfg", NULL ) <= 0 ) { Com_Error( ERR_FATAL, "Couldn't load default.cfg" ); // bk001208 - SafeMode see below, FIXME? } Q_strncpyz(lastValidBase, fs_basepath->string, sizeof(lastValidBase)); Q_strncpyz(lastValidGame, fs_gamedirvar->string, sizeof(lastValidGame)); // bk001208 - SafeMode see below, FIXME? } /* ================ FS_Restart ================ */ void FS_Restart( int checksumFeed ) { // free anything we currently have loaded FS_Shutdown(qfalse); // set the checksum feed fs_checksumFeed = checksumFeed; // clear pak references FS_ClearPakReferences(0); // try to start up normally FS_Startup( BASEGAME ); // see if we are going to allow add-ons FS_SetRestrictions(); // if we can't find default.cfg, assume that the paths are // busted and error out now, rather than getting an unreadable // graphics screen when the font fails to load if ( FS_ReadFile( "default.cfg", NULL ) <= 0 ) { // this might happen when connecting to a pure server not using BASEGAME/pak0.pk3 // (for instance a TA demo server) if (lastValidBase[0]) { FS_PureServerSetLoadedPaks("", ""); Cvar_Set("fs_basepath", lastValidBase); Cvar_Set("fs_gamedirvar", lastValidGame); lastValidBase[0] = '\0'; lastValidGame[0] = '\0'; Cvar_Set( "fs_restrict", "0" ); FS_Restart(checksumFeed); Com_Error( ERR_DROP, "Invalid game folder\n" ); return; } Com_Error( ERR_FATAL, "Couldn't load default.cfg" ); } // bk010116 - new check before safeMode if ( Q_stricmp(fs_gamedirvar->string, lastValidGame) ) { // skip the q3config.cfg if "safe" is on the command line if ( !Com_SafeMode() ) { Cbuf_AddText ("exec q3config.cfg\n"); } } Q_strncpyz(lastValidBase, fs_basepath->string, sizeof(lastValidBase)); Q_strncpyz(lastValidGame, fs_gamedirvar->string, sizeof(lastValidGame)); } /* ================= FS_ConditionalRestart restart if necessary ================= */ qboolean FS_ConditionalRestart( int checksumFeed ) { if( fs_gamedirvar->modified || checksumFeed != fs_checksumFeed ) { FS_Restart( checksumFeed ); return qtrue; } return qfalse; } /* ======================================================================================== Handle based file calls for virtual machines ======================================================================================== */ int FS_FOpenFileByMode( const char *qpath, fileHandle_t *f, fsMode_t mode ) { int r; qboolean sync; sync = qfalse; switch( mode ) { case FS_READ: r = FS_FOpenFileRead( qpath, f, qtrue ); break; case FS_WRITE: *f = FS_FOpenFileWrite( qpath ); r = 0; if (*f == 0) { r = -1; } break; case FS_APPEND_SYNC: sync = qtrue; case FS_APPEND: *f = FS_FOpenFileAppend( qpath ); r = 0; if (*f == 0) { r = -1; } break; default: Com_Error( ERR_FATAL, "FSH_FOpenFile: bad mode" ); return -1; } if (!f) { return r; } if ( *f ) { if (fsh[*f].zipFile == qtrue) { fsh[*f].baseOffset = unztell(fsh[*f].handleFiles.file.z); } else { fsh[*f].baseOffset = ftell(fsh[*f].handleFiles.file.o); } fsh[*f].fileSize = r; fsh[*f].streamed = qfalse; if (mode == FS_READ) { Sys_BeginStreamedFile( *f, 0x4000 ); fsh[*f].streamed = qtrue; } } fsh[*f].handleSync = sync; return r; } int FS_FTell( fileHandle_t f ) { int pos; if (fsh[f].zipFile == qtrue) { pos = unztell(fsh[f].handleFiles.file.z); } else { pos = ftell(fsh[f].handleFiles.file.o); } return pos; } void FS_Flush( fileHandle_t f ) { fflush(fsh[f].handleFiles.file.o); } ================================================ FILE: src/engine/qcommon/huffman.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ /* This is based on the Adaptive Huffman algorithm described in Sayood's Data * Compression book. The ranks are not actually stored, but implicitly defined * by the location of a node within a doubly-linked list */ #include "../../game/q_shared.h" #include "qcommon.h" static int bloc = 0; void Huff_putBit( int bit, byte *fout, int *offset) { bloc = *offset; if ((bloc&7) == 0) { fout[(bloc>>3)] = 0; } fout[(bloc>>3)] |= bit << (bloc&7); bloc++; *offset = bloc; } int Huff_getBit( byte *fin, int *offset) { int t; bloc = *offset; t = (fin[(bloc>>3)] >> (bloc&7)) & 0x1; bloc++; *offset = bloc; return t; } /* Add a bit to the output file (buffered) */ static void add_bit (char bit, byte *fout) { if ((bloc&7) == 0) { fout[(bloc>>3)] = 0; } fout[(bloc>>3)] |= bit << (bloc&7); bloc++; } /* Receive one bit from the input file (buffered) */ static int get_bit (byte *fin) { int t; t = (fin[(bloc>>3)] >> (bloc&7)) & 0x1; bloc++; return t; } static node_t **get_ppnode(huff_t* huff) { node_t **tppnode; if (!huff->freelist) { return &(huff->nodePtrs[huff->blocPtrs++]); } else { tppnode = huff->freelist; huff->freelist = (node_t **)*tppnode; return tppnode; } } static void free_ppnode(huff_t* huff, node_t **ppnode) { *ppnode = (node_t *)huff->freelist; huff->freelist = ppnode; } /* Swap the location of these two nodes in the tree */ static void swap (huff_t* huff, node_t *node1, node_t *node2) { node_t *par1, *par2; par1 = node1->parent; par2 = node2->parent; if (par1) { if (par1->left == node1) { par1->left = node2; } else { par1->right = node2; } } else { huff->tree = node2; } if (par2) { if (par2->left == node2) { par2->left = node1; } else { par2->right = node1; } } else { huff->tree = node1; } node1->parent = par2; node2->parent = par1; } /* Swap these two nodes in the linked list (update ranks) */ static void swaplist(node_t *node1, node_t *node2) { node_t *par1; par1 = node1->next; node1->next = node2->next; node2->next = par1; par1 = node1->prev; node1->prev = node2->prev; node2->prev = par1; if (node1->next == node1) { node1->next = node2; } if (node2->next == node2) { node2->next = node1; } if (node1->next) { node1->next->prev = node1; } if (node2->next) { node2->next->prev = node2; } if (node1->prev) { node1->prev->next = node1; } if (node2->prev) { node2->prev->next = node2; } } /* Do the increments */ static void increment(huff_t* huff, node_t *node) { node_t *lnode; if (!node) { return; } if (node->next != NULL && node->next->weight == node->weight) { lnode = *node->head; if (lnode != node->parent) { swap(huff, lnode, node); } swaplist(lnode, node); } if (node->prev && node->prev->weight == node->weight) { *node->head = node->prev; } else { *node->head = NULL; free_ppnode(huff, node->head); } node->weight++; if (node->next && node->next->weight == node->weight) { node->head = node->next->head; } else { node->head = get_ppnode(huff); *node->head = node; } if (node->parent) { increment(huff, node->parent); if (node->prev == node->parent) { swaplist(node, node->parent); if (*node->head == node) { *node->head = node->parent; } } } } void Huff_addRef(huff_t* huff, byte ch) { node_t *tnode, *tnode2; if (huff->loc[ch] == NULL) { /* if this is the first transmission of this node */ tnode = &(huff->nodeList[huff->blocNode++]); tnode2 = &(huff->nodeList[huff->blocNode++]); tnode2->symbol = INTERNAL_NODE; tnode2->weight = 1; tnode2->next = huff->lhead->next; if (huff->lhead->next) { huff->lhead->next->prev = tnode2; if (huff->lhead->next->weight == 1) { tnode2->head = huff->lhead->next->head; } else { tnode2->head = get_ppnode(huff); *tnode2->head = tnode2; } } else { tnode2->head = get_ppnode(huff); *tnode2->head = tnode2; } huff->lhead->next = tnode2; tnode2->prev = huff->lhead; tnode->symbol = ch; tnode->weight = 1; tnode->next = huff->lhead->next; if (huff->lhead->next) { huff->lhead->next->prev = tnode; if (huff->lhead->next->weight == 1) { tnode->head = huff->lhead->next->head; } else { /* this should never happen */ tnode->head = get_ppnode(huff); *tnode->head = tnode2; } } else { /* this should never happen */ tnode->head = get_ppnode(huff); *tnode->head = tnode; } huff->lhead->next = tnode; tnode->prev = huff->lhead; tnode->left = tnode->right = NULL; if (huff->lhead->parent) { if (huff->lhead->parent->left == huff->lhead) { /* lhead is guaranteed to by the NYT */ huff->lhead->parent->left = tnode2; } else { huff->lhead->parent->right = tnode2; } } else { huff->tree = tnode2; } tnode2->right = tnode; tnode2->left = huff->lhead; tnode2->parent = huff->lhead->parent; huff->lhead->parent = tnode->parent = tnode2; huff->loc[ch] = tnode; increment(huff, tnode2->parent); } else { increment(huff, huff->loc[ch]); } } /* Get a symbol */ int Huff_Receive (node_t *node, int *ch, byte *fin) { while (node && node->symbol == INTERNAL_NODE) { if (get_bit(fin)) { node = node->right; } else { node = node->left; } } if (!node) { return 0; // Com_Error(ERR_DROP, "Illegal tree!\n"); } return (*ch = node->symbol); } /* Get a symbol */ void Huff_offsetReceive (node_t *node, int *ch, byte *fin, int *offset) { bloc = *offset; while (node && node->symbol == INTERNAL_NODE) { if (get_bit(fin)) { node = node->right; } else { node = node->left; } } if (!node) { *ch = 0; return; // Com_Error(ERR_DROP, "Illegal tree!\n"); } *ch = node->symbol; *offset = bloc; } /* Send the prefix code for this node */ static void send(node_t *node, node_t *child, byte *fout) { if (node->parent) { send(node->parent, node, fout); } if (child) { if (node->right == child) { add_bit(1, fout); } else { add_bit(0, fout); } } } /* Send a symbol */ void Huff_transmit (huff_t *huff, int ch, byte *fout) { int i; if (huff->loc[ch] == NULL) { /* node_t hasn't been transmitted, send a NYT, then the symbol */ Huff_transmit(huff, NYT, fout); for (i = 7; i >= 0; i--) { add_bit((char)((ch >> i) & 0x1), fout); } } else { send(huff->loc[ch], NULL, fout); } } void Huff_offsetTransmit (huff_t *huff, int ch, byte *fout, int *offset) { bloc = *offset; send(huff->loc[ch], NULL, fout); *offset = bloc; } void Huff_Decompress(msg_t *mbuf, int offset) { int ch, cch, i, j, size; byte seq[65536]; byte* buffer; huff_t huff; size = mbuf->cursize - offset; buffer = mbuf->data + offset; if ( size <= 0 ) { return; } Com_Memset(&huff, 0, sizeof(huff_t)); // Initialize the tree & list with the NYT node huff.tree = huff.lhead = huff.ltail = huff.loc[NYT] = &(huff.nodeList[huff.blocNode++]); huff.tree->symbol = NYT; huff.tree->weight = 0; huff.lhead->next = huff.lhead->prev = NULL; huff.tree->parent = huff.tree->left = huff.tree->right = NULL; cch = buffer[0]*256 + buffer[1]; // don't overflow with bad messages if ( cch > mbuf->maxsize - offset ) { cch = mbuf->maxsize - offset; } bloc = 16; for ( j = 0; j < cch; j++ ) { ch = 0; // don't overflow reading from the messages // FIXME: would it be better to have a overflow check in get_bit ? if ( (bloc >> 3) > size ) { seq[j] = 0; break; } Huff_Receive(huff.tree, &ch, buffer); /* Get a character */ if ( ch == NYT ) { /* We got a NYT, get the symbol associated with it */ ch = 0; for ( i = 0; i < 8; i++ ) { ch = (ch<<1) + get_bit(buffer); } } seq[j] = ch; /* Write symbol */ Huff_addRef(&huff, (byte)ch); /* Increment node */ } mbuf->cursize = cch + offset; Com_Memcpy(mbuf->data + offset, seq, cch); } extern int oldsize; void Huff_Compress(msg_t *mbuf, int offset) { int i, ch, size; byte seq[65536]; byte* buffer; huff_t huff; size = mbuf->cursize - offset; buffer = mbuf->data+ + offset; if (size<=0) { return; } Com_Memset(&huff, 0, sizeof(huff_t)); // Add the NYT (not yet transmitted) node into the tree/list */ huff.tree = huff.lhead = huff.loc[NYT] = &(huff.nodeList[huff.blocNode++]); huff.tree->symbol = NYT; huff.tree->weight = 0; huff.lhead->next = huff.lhead->prev = NULL; huff.tree->parent = huff.tree->left = huff.tree->right = NULL; huff.loc[NYT] = huff.tree; seq[0] = (size>>8); seq[1] = size&0xff; bloc = 16; for (i=0; icursize = (bloc>>3) + offset; Com_Memcpy(mbuf->data+offset, seq, (bloc>>3)); } void Huff_Init(huffman_t *huff) { Com_Memset(&huff->compressor, 0, sizeof(huff_t)); Com_Memset(&huff->decompressor, 0, sizeof(huff_t)); // Initialize the tree & list with the NYT node huff->decompressor.tree = huff->decompressor.lhead = huff->decompressor.ltail = huff->decompressor.loc[NYT] = &(huff->decompressor.nodeList[huff->decompressor.blocNode++]); huff->decompressor.tree->symbol = NYT; huff->decompressor.tree->weight = 0; huff->decompressor.lhead->next = huff->decompressor.lhead->prev = NULL; huff->decompressor.tree->parent = huff->decompressor.tree->left = huff->decompressor.tree->right = NULL; // Add the NYT (not yet transmitted) node into the tree/list */ huff->compressor.tree = huff->compressor.lhead = huff->compressor.loc[NYT] = &(huff->compressor.nodeList[huff->compressor.blocNode++]); huff->compressor.tree->symbol = NYT; huff->compressor.tree->weight = 0; huff->compressor.lhead->next = huff->compressor.lhead->prev = NULL; huff->compressor.tree->parent = huff->compressor.tree->left = huff->compressor.tree->right = NULL; huff->compressor.loc[NYT] = huff->compressor.tree; } ================================================ FILE: src/engine/qcommon/md4.c ================================================ /* GLOBAL.H - RSAREF types and constants */ #include #if defined(_WIN32) #pragma warning(disable : 4711) // selected for automatic inline expansion #endif /* POINTER defines a generic pointer type */ typedef unsigned char *POINTER; /* UINT2 defines a two byte word */ typedef unsigned short int UINT2; /* UINT4 defines a four byte word */ typedef unsigned long int UINT4; /* MD4.H - header file for MD4C.C */ /* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All rights reserved. License to copy and use this software is granted provided that it is identified as the RSA Data Security, Inc. MD4 Message-Digest Algorithm in all material mentioning or referencing this software or this function. License is also granted to make and use derivative works provided that such works are identified as derived from the RSA Data Security, Inc. MD4 Message-Digest Algorithm in all material mentioning or referencing the derived work. RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided as is without express or implied warranty of any kind. These notices must be retained in any copies of any part of this documentation and/or software. */ /* MD4 context. */ typedef struct { UINT4 state[4]; /* state (ABCD) */ UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ unsigned char buffer[64]; /* input buffer */ } MD4_CTX; void MD4Init (MD4_CTX *); void MD4Update (MD4_CTX *, const unsigned char *, unsigned int); void MD4Final (unsigned char [16], MD4_CTX *); #ifndef __VECTORC void Com_Memset (void* dest, const int val, const size_t count); void Com_Memcpy (void* dest, const void* src, const size_t count); #else #define Com_Memset memset #define Com_Memcpy memcpy #endif /* MD4C.C - RSA Data Security, Inc., MD4 message-digest algorithm */ /* Copyright (C) 1990-2, RSA Data Security, Inc. All rights reserved. License to copy and use this software is granted provided that it is identified as the RSA Data Security, Inc. MD4 Message-Digest Algorithm in all material mentioning or referencing this software or this function. License is also granted to make and use derivative works provided that such works are identified as derived from the RSA Data Security, Inc. MD4 Message-Digest Algorithm in all material mentioning or referencing the derived work. RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided as is without express or implied warranty of any kind. These notices must be retained in any copies of any part of this documentation and/or software. */ /* Constants for MD4Transform routine. */ #define S11 3 #define S12 7 #define S13 11 #define S14 19 #define S21 3 #define S22 5 #define S23 9 #define S24 13 #define S31 3 #define S32 9 #define S33 11 #define S34 15 static void MD4Transform (UINT4 [4], const unsigned char [64]); static void Encode (unsigned char *, UINT4 *, unsigned int); static void Decode (UINT4 *, const unsigned char *, unsigned int); static unsigned char PADDING[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* F, G and H are basic MD4 functions. */ #define F(x, y, z) (((x) & (y)) | ((~x) & (z))) #define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) #define H(x, y, z) ((x) ^ (y) ^ (z)) /* ROTATE_LEFT rotates x left n bits. */ #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) /* FF, GG and HH are transformations for rounds 1, 2 and 3 */ /* Rotation is separate from addition to prevent recomputation */ #define FF(a, b, c, d, x, s) {(a) += F ((b), (c), (d)) + (x); (a) = ROTATE_LEFT ((a), (s));} #define GG(a, b, c, d, x, s) {(a) += G ((b), (c), (d)) + (x) + (UINT4)0x5a827999; (a) = ROTATE_LEFT ((a), (s));} #define HH(a, b, c, d, x, s) {(a) += H ((b), (c), (d)) + (x) + (UINT4)0x6ed9eba1; (a) = ROTATE_LEFT ((a), (s));} /* MD4 initialization. Begins an MD4 operation, writing a new context. */ void MD4Init (MD4_CTX *context) { context->count[0] = context->count[1] = 0; /* Load magic initialization constants.*/ context->state[0] = 0x67452301; context->state[1] = 0xefcdab89; context->state[2] = 0x98badcfe; context->state[3] = 0x10325476; } /* MD4 block update operation. Continues an MD4 message-digest operation, processing another message block, and updating the context. */ void MD4Update (MD4_CTX *context, const unsigned char *input, unsigned int inputLen) { unsigned int i, index, partLen; /* Compute number of bytes mod 64 */ index = (unsigned int)((context->count[0] >> 3) & 0x3F); /* Update number of bits */ if ((context->count[0] += ((UINT4)inputLen << 3))< ((UINT4)inputLen << 3)) context->count[1]++; context->count[1] += ((UINT4)inputLen >> 29); partLen = 64 - index; /* Transform as many times as possible.*/ if (inputLen >= partLen) { Com_Memcpy((POINTER)&context->buffer[index], (POINTER)input, partLen); MD4Transform (context->state, context->buffer); for (i = partLen; i + 63 < inputLen; i += 64) MD4Transform (context->state, &input[i]); index = 0; } else i = 0; /* Buffer remaining input */ Com_Memcpy ((POINTER)&context->buffer[index], (POINTER)&input[i], inputLen-i); } /* MD4 finalization. Ends an MD4 message-digest operation, writing the the message digest and zeroizing the context. */ void MD4Final (unsigned char digest[16], MD4_CTX *context) { unsigned char bits[8]; unsigned int index, padLen; /* Save number of bits */ Encode (bits, context->count, 8); /* Pad out to 56 mod 64.*/ index = (unsigned int)((context->count[0] >> 3) & 0x3f); padLen = (index < 56) ? (56 - index) : (120 - index); MD4Update (context, PADDING, padLen); /* Append length (before padding) */ MD4Update (context, bits, 8); /* Store state in digest */ Encode (digest, context->state, 16); /* Zeroize sensitive information.*/ Com_Memset ((POINTER)context, 0, sizeof (*context)); } /* MD4 basic transformation. Transforms state based on block. */ static void MD4Transform (UINT4 state[4], const unsigned char block[64]) { UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; Decode (x, block, 64); /* Round 1 */ FF (a, b, c, d, x[ 0], S11); /* 1 */ FF (d, a, b, c, x[ 1], S12); /* 2 */ FF (c, d, a, b, x[ 2], S13); /* 3 */ FF (b, c, d, a, x[ 3], S14); /* 4 */ FF (a, b, c, d, x[ 4], S11); /* 5 */ FF (d, a, b, c, x[ 5], S12); /* 6 */ FF (c, d, a, b, x[ 6], S13); /* 7 */ FF (b, c, d, a, x[ 7], S14); /* 8 */ FF (a, b, c, d, x[ 8], S11); /* 9 */ FF (d, a, b, c, x[ 9], S12); /* 10 */ FF (c, d, a, b, x[10], S13); /* 11 */ FF (b, c, d, a, x[11], S14); /* 12 */ FF (a, b, c, d, x[12], S11); /* 13 */ FF (d, a, b, c, x[13], S12); /* 14 */ FF (c, d, a, b, x[14], S13); /* 15 */ FF (b, c, d, a, x[15], S14); /* 16 */ /* Round 2 */ GG (a, b, c, d, x[ 0], S21); /* 17 */ GG (d, a, b, c, x[ 4], S22); /* 18 */ GG (c, d, a, b, x[ 8], S23); /* 19 */ GG (b, c, d, a, x[12], S24); /* 20 */ GG (a, b, c, d, x[ 1], S21); /* 21 */ GG (d, a, b, c, x[ 5], S22); /* 22 */ GG (c, d, a, b, x[ 9], S23); /* 23 */ GG (b, c, d, a, x[13], S24); /* 24 */ GG (a, b, c, d, x[ 2], S21); /* 25 */ GG (d, a, b, c, x[ 6], S22); /* 26 */ GG (c, d, a, b, x[10], S23); /* 27 */ GG (b, c, d, a, x[14], S24); /* 28 */ GG (a, b, c, d, x[ 3], S21); /* 29 */ GG (d, a, b, c, x[ 7], S22); /* 30 */ GG (c, d, a, b, x[11], S23); /* 31 */ GG (b, c, d, a, x[15], S24); /* 32 */ /* Round 3 */ HH (a, b, c, d, x[ 0], S31); /* 33 */ HH (d, a, b, c, x[ 8], S32); /* 34 */ HH (c, d, a, b, x[ 4], S33); /* 35 */ HH (b, c, d, a, x[12], S34); /* 36 */ HH (a, b, c, d, x[ 2], S31); /* 37 */ HH (d, a, b, c, x[10], S32); /* 38 */ HH (c, d, a, b, x[ 6], S33); /* 39 */ HH (b, c, d, a, x[14], S34); /* 40 */ HH (a, b, c, d, x[ 1], S31); /* 41 */ HH (d, a, b, c, x[ 9], S32); /* 42 */ HH (c, d, a, b, x[ 5], S33); /* 43 */ HH (b, c, d, a, x[13], S34); /* 44 */ HH (a, b, c, d, x[ 3], S31); /* 45 */ HH (d, a, b, c, x[11], S32); /* 46 */ HH (c, d, a, b, x[ 7], S33); /* 47 */ HH (b, c, d, a, x[15], S34); /* 48 */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; /* Zeroize sensitive information.*/ Com_Memset ((POINTER)x, 0, sizeof (x)); } /* Encodes input (UINT4) into output (unsigned char). Assumes len is a multiple of 4. */ static void Encode (unsigned char *output, UINT4 *input, unsigned int len) { unsigned int i, j; for (i = 0, j = 0; j < len; i++, j += 4) { output[j] = (unsigned char)(input[i] & 0xff); output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); } } /* Decodes input (unsigned char) into output (UINT4). Assumes len is a multiple of 4. */ static void Decode (UINT4 *output, const unsigned char *input, unsigned int len) { unsigned int i, j; for (i = 0, j = 0; j < len; i++, j += 4) output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); } //=================================================================== unsigned Com_BlockChecksum (const void *buffer, int length) { int digest[4]; unsigned val; MD4_CTX ctx; MD4Init (&ctx); MD4Update (&ctx, (unsigned char *)buffer, length); MD4Final ( (unsigned char *)digest, &ctx); val = digest[0] ^ digest[1] ^ digest[2] ^ digest[3]; return val; } unsigned Com_BlockChecksumKey (void *buffer, int length, int key) { int digest[4]; unsigned val; MD4_CTX ctx; MD4Init (&ctx); MD4Update (&ctx, (unsigned char *)&key, 4); MD4Update (&ctx, (unsigned char *)buffer, length); MD4Final ( (unsigned char *)digest, &ctx); val = digest[0] ^ digest[1] ^ digest[2] ^ digest[3]; return val; } ================================================ FILE: src/engine/qcommon/msg.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "../../game/q_shared.h" #include "qcommon.h" static huffman_t msgHuff; static qboolean msgInit = qfalse; int pcount[256]; /* ============================================================================== MESSAGE IO FUNCTIONS Handles byte ordering and avoids alignment errors ============================================================================== */ int oldsize = 0; void MSG_initHuffman(); void MSG_Init( msg_t *buf, byte *data, int length ) { if (!msgInit) { MSG_initHuffman(); } Com_Memset (buf, 0, sizeof(*buf)); buf->data = data; buf->maxsize = length; } void MSG_InitOOB( msg_t *buf, byte *data, int length ) { if (!msgInit) { MSG_initHuffman(); } Com_Memset (buf, 0, sizeof(*buf)); buf->data = data; buf->maxsize = length; buf->oob = qtrue; } void MSG_Clear( msg_t *buf ) { buf->cursize = 0; buf->overflowed = qfalse; buf->bit = 0; //<- in bits } void MSG_Bitstream( msg_t *buf ) { buf->oob = qfalse; } void MSG_BeginReading( msg_t *msg ) { msg->readcount = 0; msg->bit = 0; msg->oob = qfalse; } void MSG_BeginReadingOOB( msg_t *msg ) { msg->readcount = 0; msg->bit = 0; msg->oob = qtrue; } void MSG_Copy(msg_t *buf, byte *data, int length, msg_t *src) { if (lengthcursize) { Com_Error( ERR_DROP, "MSG_Copy: can't copy into a smaller msg_t buffer"); } Com_Memcpy(buf, src, sizeof(msg_t)); buf->data = data; Com_Memcpy(buf->data, src->data, src->cursize); } /* ============================================================================= bit functions ============================================================================= */ int overflows; // negative bit values include signs void MSG_WriteBits( msg_t *msg, int value, int bits ) { int i; // FILE* fp; oldsize += bits; // this isn't an exact overflow check, but close enough if ( msg->maxsize - msg->cursize < 4 ) { msg->overflowed = qtrue; return; } if ( bits == 0 || bits < -31 || bits > 32 ) { Com_Error( ERR_DROP, "MSG_WriteBits: bad bits %i", bits ); } // check for overflows if ( bits != 32 ) { if ( bits > 0 ) { if ( value > ( ( 1 << bits ) - 1 ) || value < 0 ) { overflows++; } } else { int r; r = 1 << (bits-1); if ( value > r - 1 || value < -r ) { overflows++; } } } if ( bits < 0 ) { bits = -bits; } if (msg->oob) { if (bits==8) { msg->data[msg->cursize] = value; msg->cursize += 1; msg->bit += 8; } else if (bits==16) { unsigned short *sp = (unsigned short *)&msg->data[msg->cursize]; *sp = LittleShort(value); msg->cursize += 2; msg->bit += 16; } else if (bits==32) { unsigned int *ip = (unsigned int *)&msg->data[msg->cursize]; *ip = LittleLong(value); msg->cursize += 4; msg->bit += 8; } else { Com_Error(ERR_DROP, "can't read %d bits\n", bits); } } else { // fp = fopen("c:\\netchan.bin", "a"); value &= (0xffffffff>>(32-bits)); if (bits&7) { int nbits; nbits = bits&7; for(i=0;idata, &msg->bit); value = (value>>1); } bits = bits - nbits; } if (bits) { for(i=0;idata, &msg->bit); value = (value>>8); } } msg->cursize = (msg->bit>>3)+1; // fclose(fp); } } int MSG_ReadBits( msg_t *msg, int bits ) { int value; int get; qboolean sgn; int i, nbits; // FILE* fp; value = 0; if ( bits < 0 ) { bits = -bits; sgn = qtrue; } else { sgn = qfalse; } if (msg->oob) { if (bits==8) { value = msg->data[msg->readcount]; msg->readcount += 1; msg->bit += 8; } else if (bits==16) { unsigned short *sp = (unsigned short *)&msg->data[msg->readcount]; value = LittleShort(*sp); msg->readcount += 2; msg->bit += 16; } else if (bits==32) { unsigned int *ip = (unsigned int *)&msg->data[msg->readcount]; value = LittleLong(*ip); msg->readcount += 4; msg->bit += 32; } else { Com_Error(ERR_DROP, "can't read %d bits\n", bits); } } else { nbits = 0; if (bits&7) { nbits = bits&7; for(i=0;idata, &msg->bit)<data, &msg->bit); // fwrite(&get, 1, 1, fp); value |= (get<<(i+nbits)); } // fclose(fp); } msg->readcount = (msg->bit>>3)+1; } if ( sgn ) { if ( value & ( 1 << ( bits - 1 ) ) ) { value |= -1 ^ ( ( 1 << bits ) - 1 ); } } return value; } //================================================================================ // // writing functions // void MSG_WriteChar( msg_t *sb, int c ) { #ifdef PARANOID if (c < -128 || c > 127) Com_Error (ERR_FATAL, "MSG_WriteChar: range error"); #endif MSG_WriteBits( sb, c, 8 ); } void MSG_WriteByte( msg_t *sb, int c ) { #ifdef PARANOID if (c < 0 || c > 255) Com_Error (ERR_FATAL, "MSG_WriteByte: range error"); #endif MSG_WriteBits( sb, c, 8 ); } void MSG_WriteData( msg_t *buf, const void *data, int length ) { int i; for(i=0;i (short)0x7fff) Com_Error (ERR_FATAL, "MSG_WriteShort: range error"); #endif MSG_WriteBits( sb, c, 16 ); } void MSG_WriteLong( msg_t *sb, int c ) { MSG_WriteBits( sb, c, 32 ); } void MSG_WriteFloat( msg_t *sb, float f ) { union { float f; int l; } dat; dat.f = f; MSG_WriteBits( sb, dat.l, 32 ); } void MSG_WriteString( msg_t *sb, const char *s ) { if ( !s ) { MSG_WriteData (sb, "", 1); } else { int l,i; char string[MAX_STRING_CHARS]; l = (int)strlen( s ); if ( l >= MAX_STRING_CHARS ) { Com_Printf( "MSG_WriteString: MAX_STRING_CHARS" ); MSG_WriteData (sb, "", 1); return; } Q_strncpyz( string, s, sizeof( string ) ); // get rid of 0xff chars, because old clients don't like them for ( i = 0 ; i < l ; i++ ) { if ( ((byte *)string)[i] > 127 ) { string[i] = '.'; } } MSG_WriteData (sb, string, l+1); } } void MSG_WriteBigString( msg_t *sb, const char *s ) { if ( !s ) { MSG_WriteData (sb, "", 1); } else { int l,i; char string[BIG_INFO_STRING]; l = (int)strlen( s ); if ( l >= BIG_INFO_STRING ) { Com_Printf( "MSG_WriteString: BIG_INFO_STRING" ); MSG_WriteData (sb, "", 1); return; } Q_strncpyz( string, s, sizeof( string ) ); // get rid of 0xff chars, because old clients don't like them for ( i = 0 ; i < l ; i++ ) { if ( ((byte *)string)[i] > 127 ) { string[i] = '.'; } } MSG_WriteData (sb, string, l+1); } } void MSG_WriteAngle( msg_t *sb, float f ) { MSG_WriteByte (sb, (int)(f*256/360) & 255); } void MSG_WriteAngle16( msg_t *sb, float f ) { MSG_WriteShort (sb, ANGLE2SHORT(f)); } //============================================================ // // reading functions // // returns -1 if no more characters are available int MSG_ReadChar (msg_t *msg ) { int c; c = (signed char)MSG_ReadBits( msg, 8 ); if ( msg->readcount > msg->cursize ) { c = -1; } return c; } int MSG_ReadByte( msg_t *msg ) { int c; c = (unsigned char)MSG_ReadBits( msg, 8 ); if ( msg->readcount > msg->cursize ) { c = -1; } return c; } int MSG_ReadShort( msg_t *msg ) { int c; c = (short)MSG_ReadBits( msg, 16 ); if ( msg->readcount > msg->cursize ) { c = -1; } return c; } int MSG_ReadLong( msg_t *msg ) { int c; c = MSG_ReadBits( msg, 32 ); if ( msg->readcount > msg->cursize ) { c = -1; } return c; } float MSG_ReadFloat( msg_t *msg ) { union { byte b[4]; float f; int l; } dat; dat.l = MSG_ReadBits( msg, 32 ); if ( msg->readcount > msg->cursize ) { dat.f = -1; } return dat.f; } char *MSG_ReadString( msg_t *msg ) { static char string[MAX_STRING_CHARS]; int l,c; l = 0; do { c = MSG_ReadByte(msg); // use ReadByte so -1 is out of bounds if ( c == -1 || c == 0 ) { break; } // translate all fmt spec to avoid crash bugs if ( c == '%' ) { c = '.'; } // don't allow higher ascii values if ( c > 127 ) { c = '.'; } string[l] = c; l++; } while (l < sizeof(string)-1); string[l] = 0; return string; } char *MSG_ReadBigString( msg_t *msg ) { static char string[BIG_INFO_STRING]; int l,c; l = 0; do { c = MSG_ReadByte(msg); // use ReadByte so -1 is out of bounds if ( c == -1 || c == 0 ) { break; } // translate all fmt spec to avoid crash bugs if ( c == '%' ) { c = '.'; } string[l] = c; l++; } while (l < sizeof(string)-1); string[l] = 0; return string; } char *MSG_ReadStringLine( msg_t *msg ) { static char string[MAX_STRING_CHARS]; int l,c; l = 0; do { c = MSG_ReadByte(msg); // use ReadByte so -1 is out of bounds if (c == -1 || c == 0 || c == '\n') { break; } // translate all fmt spec to avoid crash bugs if ( c == '%' ) { c = '.'; } string[l] = c; l++; } while (l < sizeof(string)-1); string[l] = 0; return string; } float MSG_ReadAngle16( msg_t *msg ) { return SHORT2ANGLE(MSG_ReadShort(msg)); } void MSG_ReadData( msg_t *msg, void *data, int len ) { int i; for (i=0 ; iinteger == 4 ) { Com_Printf("%s ", x ); }; void MSG_WriteDelta( msg_t *msg, int oldV, int newV, int bits ) { if ( oldV == newV ) { MSG_WriteBits( msg, 0, 1 ); return; } MSG_WriteBits( msg, 1, 1 ); MSG_WriteBits( msg, newV, bits ); } int MSG_ReadDelta( msg_t *msg, int oldV, int bits ) { if ( MSG_ReadBits( msg, 1 ) ) { return MSG_ReadBits( msg, bits ); } return oldV; } void MSG_WriteDeltaFloat( msg_t *msg, float oldV, float newV ) { if ( oldV == newV ) { MSG_WriteBits( msg, 0, 1 ); return; } MSG_WriteBits( msg, 1, 1 ); MSG_WriteBits( msg, *(int *)&newV, 32 ); } float MSG_ReadDeltaFloat( msg_t *msg, float oldV ) { if ( MSG_ReadBits( msg, 1 ) ) { float newV; *(int *)&newV = MSG_ReadBits( msg, 32 ); return newV; } return oldV; } /* ============================================================================= delta functions with keys ============================================================================= */ unsigned int kbitmask[32] = { 0x00000001, 0x00000003, 0x00000007, 0x0000000F, 0x0000001F, 0x0000003F, 0x0000007F, 0x000000FF, 0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF, 0x00001FFF, 0x00003FFF, 0x00007FFF, 0x0000FFFF, 0x0001FFFF, 0x0003FFFF, 0x0007FFFF, 0x000FFFFF, 0x001FFFFf, 0x003FFFFF, 0x007FFFFF, 0x00FFFFFF, 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF, 0x0FFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF, }; void MSG_WriteDeltaKey( msg_t *msg, int key, int oldV, int newV, int bits ) { if ( oldV == newV ) { MSG_WriteBits( msg, 0, 1 ); return; } MSG_WriteBits( msg, 1, 1 ); MSG_WriteBits( msg, newV ^ key, bits ); } int MSG_ReadDeltaKey( msg_t *msg, int key, int oldV, int bits ) { if ( MSG_ReadBits( msg, 1 ) ) { return MSG_ReadBits( msg, bits ) ^ (key & (int)kbitmask[bits]); } return oldV; } void MSG_WriteDeltaKeyFloat( msg_t *msg, int key, float oldV, float newV ) { if ( oldV == newV ) { MSG_WriteBits( msg, 0, 1 ); return; } MSG_WriteBits( msg, 1, 1 ); MSG_WriteBits( msg, (*(int *)&newV) ^ key, 32 ); } float MSG_ReadDeltaKeyFloat( msg_t *msg, int key, float oldV ) { if ( MSG_ReadBits( msg, 1 ) ) { float newV; *(int *)&newV = MSG_ReadBits( msg, 32 ) ^ key; return newV; } return oldV; } /* ============================================================================ usercmd_t communication ============================================================================ */ // ms is allways sent, the others are optional #define CM_ANGLE1 (1<<0) #define CM_ANGLE2 (1<<1) #define CM_ANGLE3 (1<<2) #define CM_FORWARD (1<<3) #define CM_SIDE (1<<4) #define CM_UP (1<<5) #define CM_BUTTONS (1<<6) #define CM_WEAPON (1<<7) /* ===================== MSG_WriteDeltaUsercmd ===================== */ void MSG_WriteDeltaUsercmd( msg_t *msg, usercmd_t *from, usercmd_t *to ) { if ( to->serverTime - from->serverTime < 256 ) { MSG_WriteBits( msg, 1, 1 ); MSG_WriteBits( msg, to->serverTime - from->serverTime, 8 ); } else { MSG_WriteBits( msg, 0, 1 ); MSG_WriteBits( msg, to->serverTime, 32 ); } MSG_WriteDelta( msg, from->angles[0], to->angles[0], 16 ); MSG_WriteDelta( msg, from->angles[1], to->angles[1], 16 ); MSG_WriteDelta( msg, from->angles[2], to->angles[2], 16 ); MSG_WriteDelta( msg, from->forwardmove, to->forwardmove, 8 ); MSG_WriteDelta( msg, from->rightmove, to->rightmove, 8 ); MSG_WriteDelta( msg, from->upmove, to->upmove, 8 ); MSG_WriteDelta( msg, from->buttons, to->buttons, 16 ); MSG_WriteDelta( msg, from->weapon, to->weapon, 8 ); } /* ===================== MSG_ReadDeltaUsercmd ===================== */ void MSG_ReadDeltaUsercmd( msg_t *msg, usercmd_t *from, usercmd_t *to ) { if ( MSG_ReadBits( msg, 1 ) ) { to->serverTime = from->serverTime + MSG_ReadBits( msg, 8 ); } else { to->serverTime = MSG_ReadBits( msg, 32 ); } to->angles[0] = MSG_ReadDelta( msg, from->angles[0], 16); to->angles[1] = MSG_ReadDelta( msg, from->angles[1], 16); to->angles[2] = MSG_ReadDelta( msg, from->angles[2], 16); to->forwardmove = MSG_ReadDelta( msg, from->forwardmove, 8); to->rightmove = MSG_ReadDelta( msg, from->rightmove, 8); to->upmove = MSG_ReadDelta( msg, from->upmove, 8); to->buttons = MSG_ReadDelta( msg, from->buttons, 16); to->weapon = MSG_ReadDelta( msg, from->weapon, 8); } /* ===================== MSG_WriteDeltaUsercmd ===================== */ void MSG_WriteDeltaUsercmdKey( msg_t *msg, int key, usercmd_t *from, usercmd_t *to ) { if ( to->serverTime - from->serverTime < 256 ) { MSG_WriteBits( msg, 1, 1 ); MSG_WriteBits( msg, to->serverTime - from->serverTime, 8 ); } else { MSG_WriteBits( msg, 0, 1 ); MSG_WriteBits( msg, to->serverTime, 32 ); } if (from->angles[0] == to->angles[0] && from->angles[1] == to->angles[1] && from->angles[2] == to->angles[2] && from->forwardmove == to->forwardmove && from->rightmove == to->rightmove && from->upmove == to->upmove && from->buttons == to->buttons && from->weapon == to->weapon) { MSG_WriteBits( msg, 0, 1 ); // no change oldsize += 7; return; } key ^= to->serverTime; MSG_WriteBits( msg, 1, 1 ); MSG_WriteDeltaKey( msg, key, from->angles[0], to->angles[0], 16 ); MSG_WriteDeltaKey( msg, key, from->angles[1], to->angles[1], 16 ); MSG_WriteDeltaKey( msg, key, from->angles[2], to->angles[2], 16 ); MSG_WriteDeltaKey( msg, key, from->forwardmove, to->forwardmove, 8 ); MSG_WriteDeltaKey( msg, key, from->rightmove, to->rightmove, 8 ); MSG_WriteDeltaKey( msg, key, from->upmove, to->upmove, 8 ); MSG_WriteDeltaKey( msg, key, from->buttons, to->buttons, 16 ); MSG_WriteDeltaKey( msg, key, from->weapon, to->weapon, 8 ); } /* ===================== MSG_ReadDeltaUsercmd ===================== */ void MSG_ReadDeltaUsercmdKey( msg_t *msg, int key, usercmd_t *from, usercmd_t *to ) { if ( MSG_ReadBits( msg, 1 ) ) { to->serverTime = from->serverTime + MSG_ReadBits( msg, 8 ); } else { to->serverTime = MSG_ReadBits( msg, 32 ); } if ( MSG_ReadBits( msg, 1 ) ) { key ^= to->serverTime; to->angles[0] = MSG_ReadDeltaKey( msg, key, from->angles[0], 16); to->angles[1] = MSG_ReadDeltaKey( msg, key, from->angles[1], 16); to->angles[2] = MSG_ReadDeltaKey( msg, key, from->angles[2], 16); to->forwardmove = MSG_ReadDeltaKey( msg, key, from->forwardmove, 8); to->rightmove = MSG_ReadDeltaKey( msg, key, from->rightmove, 8); to->upmove = MSG_ReadDeltaKey( msg, key, from->upmove, 8); to->buttons = MSG_ReadDeltaKey( msg, key, from->buttons, 16); to->weapon = MSG_ReadDeltaKey( msg, key, from->weapon, 8); } else { to->angles[0] = from->angles[0]; to->angles[1] = from->angles[1]; to->angles[2] = from->angles[2]; to->forwardmove = from->forwardmove; to->rightmove = from->rightmove; to->upmove = from->upmove; to->buttons = from->buttons; to->weapon = from->weapon; } } /* ============================================================================= entityState_t communication ============================================================================= */ /* ================= MSG_ReportChangeVectors_f Prints out a table from the current statistics for copying to code ================= */ void MSG_ReportChangeVectors_f( void ) { int i; for(i=0;i<256;i++) { if (pcount[i]) { Com_Printf("%d used %d\n", i, pcount[i]); } } } typedef struct { char *name; int offset; int bits; // 0 = float } netField_t; // using the stringizing operator to save typing... #define NETF(x) #x,(int)(intptr_t)&((entityState_t*)0)->x netField_t entityStateFields[] = { { NETF(pos.trTime), 32 }, { NETF(pos.trBase[0]), 0 }, { NETF(pos.trBase[1]), 0 }, { NETF(pos.trDelta[0]), 0 }, { NETF(pos.trDelta[1]), 0 }, { NETF(pos.trBase[2]), 0 }, { NETF(apos.trBase[1]), 0 }, { NETF(pos.trDelta[2]), 0 }, { NETF(apos.trBase[0]), 0 }, { NETF(event), 10 }, { NETF(angles2[1]), 0 }, { NETF(eType), 8 }, { NETF(torsoAnim), 8 }, { NETF(eventParm), 8 }, { NETF(legsAnim), 8 }, { NETF(groundEntityNum), GENTITYNUM_BITS }, { NETF(pos.trType), 8 }, { NETF(eFlags), 19 }, { NETF(otherEntityNum), GENTITYNUM_BITS }, { NETF(weapon), 8 }, { NETF(clientNum), 8 }, { NETF(angles[1]), 0 }, { NETF(pos.trDuration), 32 }, { NETF(apos.trType), 8 }, { NETF(origin[0]), 0 }, { NETF(origin[1]), 0 }, { NETF(origin[2]), 0 }, { NETF(solid), 24 }, { NETF(powerups), 16 }, { NETF(modelindex), 8 }, { NETF(otherEntityNum2), GENTITYNUM_BITS }, { NETF(loopSound), 8 }, { NETF(generic1), 8 }, { NETF(origin2[2]), 0 }, { NETF(origin2[0]), 0 }, { NETF(origin2[1]), 0 }, { NETF(modelindex2), 8 }, { NETF(angles[0]), 0 }, { NETF(time), 32 }, { NETF(apos.trTime), 32 }, { NETF(apos.trDuration), 32 }, { NETF(apos.trBase[2]), 0 }, { NETF(apos.trDelta[0]), 0 }, { NETF(apos.trDelta[1]), 0 }, { NETF(apos.trDelta[2]), 0 }, { NETF(time2), 32 }, { NETF(angles[2]), 0 }, { NETF(angles2[0]), 0 }, { NETF(angles2[2]), 0 }, { NETF(constantLight), 32 }, { NETF(frame), 16 } }; // if (int)f == f and (int)f + ( 1<<(FLOAT_INT_BITS-1) ) < ( 1 << FLOAT_INT_BITS ) // the float will be sent with FLOAT_INT_BITS, otherwise all 32 bits will be sent #define FLOAT_INT_BITS 13 #define FLOAT_INT_BIAS (1<<(FLOAT_INT_BITS-1)) /* ================== MSG_WriteDeltaEntity Writes part of a packetentities message, including the entity number. Can delta from either a baseline or a previous packet_entity If to is NULL, a remove entity update will be sent If force is not set, then nothing at all will be generated if the entity is identical, under the assumption that the in-order delta code will catch it. ================== */ void MSG_WriteDeltaEntity( msg_t *msg, struct entityState_s *from, struct entityState_s *to, qboolean force ) { int i, lc; int numFields; netField_t *field; int trunc; float fullFloat; int *fromF, *toF; numFields = sizeof(entityStateFields)/sizeof(entityStateFields[0]); // all fields should be 32 bits to avoid any compiler packing issues // the "number" field is not part of the field list // if this assert fails, someone added a field to the entityState_t // struct without updating the message fields assert( numFields + 1 == sizeof( *from )/4 ); // a NULL to is a delta remove message if ( to == NULL ) { if ( from == NULL ) { return; } MSG_WriteBits( msg, from->number, GENTITYNUM_BITS ); MSG_WriteBits( msg, 1, 1 ); return; } if ( to->number < 0 || to->number >= MAX_GENTITIES ) { Com_Error (ERR_FATAL, "MSG_WriteDeltaEntity: Bad entity number: %i", to->number ); } lc = 0; // build the change vector as bytes so it is endien independent for ( i = 0, field = entityStateFields ; i < numFields ; i++, field++ ) { fromF = (int *)( (byte *)from + field->offset ); toF = (int *)( (byte *)to + field->offset ); if ( *fromF != *toF ) { lc = i+1; } } if ( lc == 0 ) { // nothing at all changed if ( !force ) { return; // nothing at all } // write two bits for no change MSG_WriteBits( msg, to->number, GENTITYNUM_BITS ); MSG_WriteBits( msg, 0, 1 ); // not removed MSG_WriteBits( msg, 0, 1 ); // no delta return; } MSG_WriteBits( msg, to->number, GENTITYNUM_BITS ); MSG_WriteBits( msg, 0, 1 ); // not removed MSG_WriteBits( msg, 1, 1 ); // we have a delta MSG_WriteByte( msg, lc ); // # of changes oldsize += numFields; for ( i = 0, field = entityStateFields ; i < lc ; i++, field++ ) { fromF = (int *)( (byte *)from + field->offset ); toF = (int *)( (byte *)to + field->offset ); if ( *fromF == *toF ) { MSG_WriteBits( msg, 0, 1 ); // no change continue; } MSG_WriteBits( msg, 1, 1 ); // changed if ( field->bits == 0 ) { // float fullFloat = *(float *)toF; trunc = (int)fullFloat; if (fullFloat == 0.0f) { MSG_WriteBits( msg, 0, 1 ); oldsize += FLOAT_INT_BITS; } else { MSG_WriteBits( msg, 1, 1 ); if ( trunc == fullFloat && trunc + FLOAT_INT_BIAS >= 0 && trunc + FLOAT_INT_BIAS < ( 1 << FLOAT_INT_BITS ) ) { // send as small integer MSG_WriteBits( msg, 0, 1 ); MSG_WriteBits( msg, trunc + FLOAT_INT_BIAS, FLOAT_INT_BITS ); } else { // send as full floating point value MSG_WriteBits( msg, 1, 1 ); MSG_WriteBits( msg, *toF, 32 ); } } } else { if (*toF == 0) { MSG_WriteBits( msg, 0, 1 ); } else { MSG_WriteBits( msg, 1, 1 ); // integer MSG_WriteBits( msg, *toF, field->bits ); } } } } /* ================== MSG_ReadDeltaEntity The entity number has already been read from the message, which is how the from state is identified. If the delta removes the entity, entityState_t->number will be set to MAX_GENTITIES-1 Can go from either a baseline or a previous packet_entity ================== */ extern cvar_t *cl_shownet; void MSG_ReadDeltaEntity( msg_t *msg, entityState_t *from, entityState_t *to, int number) { int i, lc; int numFields; netField_t *field; int *fromF, *toF; int print; int trunc; int startBit, endBit; if ( number < 0 || number >= MAX_GENTITIES) { Com_Error( ERR_DROP, "Bad delta entity number: %i", number ); } if ( msg->bit == 0 ) { startBit = msg->readcount * 8 - GENTITYNUM_BITS; } else { startBit = ( msg->readcount - 1 ) * 8 + msg->bit - GENTITYNUM_BITS; } // check for a remove if ( MSG_ReadBits( msg, 1 ) == 1 ) { Com_Memset( to, 0, sizeof( *to ) ); to->number = MAX_GENTITIES - 1; if ( cl_shownet->integer >= 2 || cl_shownet->integer == -1 ) { Com_Printf( "%3i: #%-3i remove\n", msg->readcount, number ); } return; } // check for no delta if ( MSG_ReadBits( msg, 1 ) == 0 ) { *to = *from; to->number = number; return; } numFields = sizeof(entityStateFields)/sizeof(entityStateFields[0]); lc = MSG_ReadByte(msg); // shownet 2/3 will interleave with other printed info, -1 will // just print the delta records` if ( cl_shownet->integer >= 2 || cl_shownet->integer == -1 ) { print = 1; Com_Printf( "%3i: #%-3i ", msg->readcount, to->number ); } else { print = 0; } to->number = number; for ( i = 0, field = entityStateFields ; i < lc ; i++, field++ ) { fromF = (int *)( (byte *)from + field->offset ); toF = (int *)( (byte *)to + field->offset ); if ( ! MSG_ReadBits( msg, 1 ) ) { // no change *toF = *fromF; } else { if ( field->bits == 0 ) { // float if ( MSG_ReadBits( msg, 1 ) == 0 ) { *(float *)toF = 0.0f; } else { if ( MSG_ReadBits( msg, 1 ) == 0 ) { // integral float trunc = MSG_ReadBits( msg, FLOAT_INT_BITS ); // bias to allow equal parts positive and negative trunc -= FLOAT_INT_BIAS; *(float *)toF = trunc; if ( print ) { Com_Printf( "%s:%i ", field->name, trunc ); } } else { // full floating point value *toF = MSG_ReadBits( msg, 32 ); if ( print ) { Com_Printf( "%s:%f ", field->name, *(float *)toF ); } } } } else { if ( MSG_ReadBits( msg, 1 ) == 0 ) { *toF = 0; } else { // integer *toF = MSG_ReadBits( msg, field->bits ); if ( print ) { Com_Printf( "%s:%i ", field->name, *toF ); } } } // pcount[i]++; } } for ( i = lc, field = &entityStateFields[lc] ; i < numFields ; i++, field++ ) { fromF = (int *)( (byte *)from + field->offset ); toF = (int *)( (byte *)to + field->offset ); // no change *toF = *fromF; } if ( print ) { if ( msg->bit == 0 ) { endBit = msg->readcount * 8 - GENTITYNUM_BITS; } else { endBit = ( msg->readcount - 1 ) * 8 + msg->bit - GENTITYNUM_BITS; } Com_Printf( " (%i bits)\n", endBit - startBit ); } } /* ============================================================================ plyer_state_t communication ============================================================================ */ // using the stringizing operator to save typing... #define PSF(x) #x,(int)(intptr_t)&((playerState_t*)0)->x netField_t playerStateFields[] = { { PSF(commandTime), 32 }, { PSF(origin[0]), 0 }, { PSF(origin[1]), 0 }, { PSF(bobCycle), 8 }, { PSF(velocity[0]), 0 }, { PSF(velocity[1]), 0 }, { PSF(viewangles[1]), 0 }, { PSF(viewangles[0]), 0 }, { PSF(weaponTime), -16 }, { PSF(origin[2]), 0 }, { PSF(velocity[2]), 0 }, { PSF(legsTimer), 8 }, { PSF(pm_time), -16 }, { PSF(eventSequence), 16 }, { PSF(torsoAnim), 8 }, { PSF(movementDir), 4 }, { PSF(events[0]), 8 }, { PSF(legsAnim), 8 }, { PSF(events[1]), 8 }, { PSF(pm_flags), 16 }, { PSF(groundEntityNum), GENTITYNUM_BITS }, { PSF(weaponstate), 4 }, { PSF(eFlags), 16 }, { PSF(externalEvent), 10 }, { PSF(gravity), 16 }, { PSF(speed), 16 }, { PSF(delta_angles[1]), 16 }, { PSF(externalEventParm), 8 }, { PSF(viewheight), -8 }, { PSF(damageEvent), 8 }, { PSF(damageYaw), 8 }, { PSF(damagePitch), 8 }, { PSF(damageCount), 8 }, { PSF(generic1), 8 }, { PSF(pm_type), 8 }, { PSF(delta_angles[0]), 16 }, { PSF(delta_angles[2]), 16 }, { PSF(torsoTimer), 12 }, { PSF(eventParms[0]), 8 }, { PSF(eventParms[1]), 8 }, { PSF(clientNum), 8 }, { PSF(weapon), 5 }, { PSF(viewangles[2]), 0 }, { PSF(grapplePoint[0]), 0 }, { PSF(grapplePoint[1]), 0 }, { PSF(grapplePoint[2]), 0 }, { PSF(jumppad_ent), 10 }, { PSF(loopSound), 16 } }; /* ============= MSG_WriteDeltaPlayerstate ============= */ void MSG_WriteDeltaPlayerstate( msg_t *msg, struct playerState_s *from, struct playerState_s *to ) { int i; playerState_t dummy; int statsbits; int persistantbits; int ammobits; int powerupbits; int numFields; int c; netField_t *field; int *fromF, *toF; float fullFloat; int trunc, lc; if (!from) { from = &dummy; Com_Memset (&dummy, 0, sizeof(dummy)); } c = msg->cursize; numFields = sizeof( playerStateFields ) / sizeof( playerStateFields[0] ); lc = 0; for ( i = 0, field = playerStateFields ; i < numFields ; i++, field++ ) { fromF = (int *)( (byte *)from + field->offset ); toF = (int *)( (byte *)to + field->offset ); if ( *fromF != *toF ) { lc = i+1; } } MSG_WriteByte( msg, lc ); // # of changes oldsize += numFields - lc; for ( i = 0, field = playerStateFields ; i < lc ; i++, field++ ) { fromF = (int *)( (byte *)from + field->offset ); toF = (int *)( (byte *)to + field->offset ); if ( *fromF == *toF ) { MSG_WriteBits( msg, 0, 1 ); // no change continue; } MSG_WriteBits( msg, 1, 1 ); // changed // pcount[i]++; if ( field->bits == 0 ) { // float fullFloat = *(float *)toF; trunc = (int)fullFloat; if ( trunc == fullFloat && trunc + FLOAT_INT_BIAS >= 0 && trunc + FLOAT_INT_BIAS < ( 1 << FLOAT_INT_BITS ) ) { // send as small integer MSG_WriteBits( msg, 0, 1 ); MSG_WriteBits( msg, trunc + FLOAT_INT_BIAS, FLOAT_INT_BITS ); } else { // send as full floating point value MSG_WriteBits( msg, 1, 1 ); MSG_WriteBits( msg, *toF, 32 ); } } else { // integer MSG_WriteBits( msg, *toF, field->bits ); } } c = msg->cursize - c; // // send the arrays // statsbits = 0; for (i=0 ; i<16 ; i++) { if (to->stats[i] != from->stats[i]) { statsbits |= 1<persistant[i] != from->persistant[i]) { persistantbits |= 1<ammo[i] != from->ammo[i]) { ammobits |= 1<powerups[i] != from->powerups[i]) { powerupbits |= 1<stats[i]); } else { MSG_WriteBits( msg, 0, 1 ); // no change } if ( persistantbits ) { MSG_WriteBits( msg, 1, 1 ); // changed MSG_WriteShort( msg, persistantbits ); for (i=0 ; i<16 ; i++) if (persistantbits & (1<persistant[i]); } else { MSG_WriteBits( msg, 0, 1 ); // no change } if ( ammobits ) { MSG_WriteBits( msg, 1, 1 ); // changed MSG_WriteShort( msg, ammobits ); for (i=0 ; i<16 ; i++) if (ammobits & (1<ammo[i]); } else { MSG_WriteBits( msg, 0, 1 ); // no change } if ( powerupbits ) { MSG_WriteBits( msg, 1, 1 ); // changed MSG_WriteShort( msg, powerupbits ); for (i=0 ; i<16 ; i++) if (powerupbits & (1<powerups[i] ); } else { MSG_WriteBits( msg, 0, 1 ); // no change } } /* =================== MSG_ReadDeltaPlayerstate =================== */ void MSG_ReadDeltaPlayerstate (msg_t *msg, playerState_t *from, playerState_t *to ) { int i, lc; int bits; netField_t *field; int numFields; int startBit, endBit; int print; int *fromF, *toF; int trunc; playerState_t dummy; if ( !from ) { from = &dummy; Com_Memset( &dummy, 0, sizeof( dummy ) ); } *to = *from; if ( msg->bit == 0 ) { startBit = msg->readcount * 8 - GENTITYNUM_BITS; } else { startBit = ( msg->readcount - 1 ) * 8 + msg->bit - GENTITYNUM_BITS; } // shownet 2/3 will interleave with other printed info, -2 will // just print the delta records if ( cl_shownet->integer >= 2 || cl_shownet->integer == -2 ) { print = 1; Com_Printf( "%3i: playerstate ", msg->readcount ); } else { print = 0; } numFields = sizeof( playerStateFields ) / sizeof( playerStateFields[0] ); lc = MSG_ReadByte(msg); for ( i = 0, field = playerStateFields ; i < lc ; i++, field++ ) { fromF = (int *)( (byte *)from + field->offset ); toF = (int *)( (byte *)to + field->offset ); if ( ! MSG_ReadBits( msg, 1 ) ) { // no change *toF = *fromF; } else { if ( field->bits == 0 ) { // float if ( MSG_ReadBits( msg, 1 ) == 0 ) { // integral float trunc = MSG_ReadBits( msg, FLOAT_INT_BITS ); // bias to allow equal parts positive and negative trunc -= FLOAT_INT_BIAS; *(float *)toF = trunc; if ( print ) { Com_Printf( "%s:%i ", field->name, trunc ); } } else { // full floating point value *toF = MSG_ReadBits( msg, 32 ); if ( print ) { Com_Printf( "%s:%f ", field->name, *(float *)toF ); } } } else { // integer *toF = MSG_ReadBits( msg, field->bits ); if ( print ) { Com_Printf( "%s:%i ", field->name, *toF ); } } } } for ( i=lc,field = &playerStateFields[lc];ioffset ); toF = (int *)( (byte *)to + field->offset ); // no change *toF = *fromF; } // read the arrays if (MSG_ReadBits( msg, 1 ) ) { // parse stats if ( MSG_ReadBits( msg, 1 ) ) { LOG("PS_STATS"); bits = MSG_ReadShort (msg); for (i=0 ; i<16 ; i++) { if (bits & (1<stats[i] = MSG_ReadShort(msg); } } } // parse persistant stats if ( MSG_ReadBits( msg, 1 ) ) { LOG("PS_PERSISTANT"); bits = MSG_ReadShort (msg); for (i=0 ; i<16 ; i++) { if (bits & (1<persistant[i] = MSG_ReadShort(msg); } } } // parse ammo if ( MSG_ReadBits( msg, 1 ) ) { LOG("PS_AMMO"); bits = MSG_ReadShort (msg); for (i=0 ; i<16 ; i++) { if (bits & (1<ammo[i] = MSG_ReadShort(msg); } } } // parse powerups if ( MSG_ReadBits( msg, 1 ) ) { LOG("PS_POWERUPS"); bits = MSG_ReadShort (msg); for (i=0 ; i<16 ; i++) { if (bits & (1<powerups[i] = MSG_ReadLong(msg); } } } } if ( print ) { if ( msg->bit == 0 ) { endBit = msg->readcount * 8 - GENTITYNUM_BITS; } else { endBit = ( msg->readcount - 1 ) * 8 + msg->bit - GENTITYNUM_BITS; } Com_Printf( " (%i bits)\n", endBit - startBit ); } } int msg_hData[256] = { 250315, // 0 41193, // 1 6292, // 2 7106, // 3 3730, // 4 3750, // 5 6110, // 6 23283, // 7 33317, // 8 6950, // 9 7838, // 10 9714, // 11 9257, // 12 17259, // 13 3949, // 14 1778, // 15 8288, // 16 1604, // 17 1590, // 18 1663, // 19 1100, // 20 1213, // 21 1238, // 22 1134, // 23 1749, // 24 1059, // 25 1246, // 26 1149, // 27 1273, // 28 4486, // 29 2805, // 30 3472, // 31 21819, // 32 1159, // 33 1670, // 34 1066, // 35 1043, // 36 1012, // 37 1053, // 38 1070, // 39 1726, // 40 888, // 41 1180, // 42 850, // 43 960, // 44 780, // 45 1752, // 46 3296, // 47 10630, // 48 4514, // 49 5881, // 50 2685, // 51 4650, // 52 3837, // 53 2093, // 54 1867, // 55 2584, // 56 1949, // 57 1972, // 58 940, // 59 1134, // 60 1788, // 61 1670, // 62 1206, // 63 5719, // 64 6128, // 65 7222, // 66 6654, // 67 3710, // 68 3795, // 69 1492, // 70 1524, // 71 2215, // 72 1140, // 73 1355, // 74 971, // 75 2180, // 76 1248, // 77 1328, // 78 1195, // 79 1770, // 80 1078, // 81 1264, // 82 1266, // 83 1168, // 84 965, // 85 1155, // 86 1186, // 87 1347, // 88 1228, // 89 1529, // 90 1600, // 91 2617, // 92 2048, // 93 2546, // 94 3275, // 95 2410, // 96 3585, // 97 2504, // 98 2800, // 99 2675, // 100 6146, // 101 3663, // 102 2840, // 103 14253, // 104 3164, // 105 2221, // 106 1687, // 107 3208, // 108 2739, // 109 3512, // 110 4796, // 111 4091, // 112 3515, // 113 5288, // 114 4016, // 115 7937, // 116 6031, // 117 5360, // 118 3924, // 119 4892, // 120 3743, // 121 4566, // 122 4807, // 123 5852, // 124 6400, // 125 6225, // 126 8291, // 127 23243, // 128 7838, // 129 7073, // 130 8935, // 131 5437, // 132 4483, // 133 3641, // 134 5256, // 135 5312, // 136 5328, // 137 5370, // 138 3492, // 139 2458, // 140 1694, // 141 1821, // 142 2121, // 143 1916, // 144 1149, // 145 1516, // 146 1367, // 147 1236, // 148 1029, // 149 1258, // 150 1104, // 151 1245, // 152 1006, // 153 1149, // 154 1025, // 155 1241, // 156 952, // 157 1287, // 158 997, // 159 1713, // 160 1009, // 161 1187, // 162 879, // 163 1099, // 164 929, // 165 1078, // 166 951, // 167 1656, // 168 930, // 169 1153, // 170 1030, // 171 1262, // 172 1062, // 173 1214, // 174 1060, // 175 1621, // 176 930, // 177 1106, // 178 912, // 179 1034, // 180 892, // 181 1158, // 182 990, // 183 1175, // 184 850, // 185 1121, // 186 903, // 187 1087, // 188 920, // 189 1144, // 190 1056, // 191 3462, // 192 2240, // 193 4397, // 194 12136, // 195 7758, // 196 1345, // 197 1307, // 198 3278, // 199 1950, // 200 886, // 201 1023, // 202 1112, // 203 1077, // 204 1042, // 205 1061, // 206 1071, // 207 1484, // 208 1001, // 209 1096, // 210 915, // 211 1052, // 212 995, // 213 1070, // 214 876, // 215 1111, // 216 851, // 217 1059, // 218 805, // 219 1112, // 220 923, // 221 1103, // 222 817, // 223 1899, // 224 1872, // 225 976, // 226 841, // 227 1127, // 228 956, // 229 1159, // 230 950, // 231 7791, // 232 954, // 233 1289, // 234 933, // 235 1127, // 236 3207, // 237 1020, // 238 927, // 239 1355, // 240 768, // 241 1040, // 242 745, // 243 952, // 244 805, // 245 1073, // 246 740, // 247 1013, // 248 805, // 249 1008, // 250 796, // 251 996, // 252 1057, // 253 11457, // 254 13504, // 255 }; void MSG_initHuffman() { int i,j; msgInit = qtrue; Huff_Init(&msgHuff); for(i=0;i<256;i++) { for (j=0;jsock = sock; chan->remoteAddress = adr; chan->qport = qport; chan->incomingSequence = 0; chan->outgoingSequence = 1; } // TTimo: unused, commenting out to make gcc happy #if 0 /* ============== Netchan_ScramblePacket A probably futile attempt to make proxy hacking somewhat more difficult. ============== */ #define SCRAMBLE_START 6 static void Netchan_ScramblePacket( msg_t *buf ) { unsigned seed; int i, j, c, mask, temp; int seq[MAX_PACKETLEN]; seed = ( LittleLong( *(unsigned *)buf->data ) * 3 ) ^ ( buf->cursize * 123 ); c = buf->cursize; if ( c <= SCRAMBLE_START ) { return; } if ( c > MAX_PACKETLEN ) { Com_Error( ERR_DROP, "MAX_PACKETLEN" ); } // generate a sequence of "random" numbers for (i = 0 ; i < c ; i++) { seed = (119 * seed + 1); seq[i] = seed; } // transpose each character for ( mask = 1 ; mask < c-SCRAMBLE_START ; mask = ( mask << 1 ) + 1 ) { } mask >>= 1; for (i = SCRAMBLE_START ; i < c ; i++) { j = SCRAMBLE_START + ( seq[i] & mask ); temp = buf->data[j]; buf->data[j] = buf->data[i]; buf->data[i] = temp; } // byte xor the data after the header for (i = SCRAMBLE_START ; i < c ; i++) { buf->data[i] ^= seq[i]; } } static void Netchan_UnScramblePacket( msg_t *buf ) { unsigned seed; int i, j, c, mask, temp; int seq[MAX_PACKETLEN]; seed = ( LittleLong( *(unsigned *)buf->data ) * 3 ) ^ ( buf->cursize * 123 ); c = buf->cursize; if ( c <= SCRAMBLE_START ) { return; } if ( c > MAX_PACKETLEN ) { Com_Error( ERR_DROP, "MAX_PACKETLEN" ); } // generate a sequence of "random" numbers for (i = 0 ; i < c ; i++) { seed = (119 * seed + 1); seq[i] = seed; } // byte xor the data after the header for (i = SCRAMBLE_START ; i < c ; i++) { buf->data[i] ^= seq[i]; } // transpose each character in reverse order for ( mask = 1 ; mask < c-SCRAMBLE_START ; mask = ( mask << 1 ) + 1 ) { } mask >>= 1; for (i = c-1 ; i >= SCRAMBLE_START ; i--) { j = SCRAMBLE_START + ( seq[i] & mask ); temp = buf->data[j]; buf->data[j] = buf->data[i]; buf->data[i] = temp; } } #endif /* ================= Netchan_TransmitNextFragment Send one fragment of the current message ================= */ void Netchan_TransmitNextFragment( netchan_t *chan ) { msg_t send; byte send_buf[MAX_PACKETLEN]; int fragmentLength; // write the packet header MSG_InitOOB (&send, send_buf, sizeof(send_buf)); // <-- only do the oob here MSG_WriteLong( &send, chan->outgoingSequence | FRAGMENT_BIT ); // send the qport if we are a client if ( chan->sock == NS_CLIENT ) { MSG_WriteShort( &send, qport->integer ); } // copy the reliable message to the packet first fragmentLength = FRAGMENT_SIZE; if ( chan->unsentFragmentStart + fragmentLength > chan->unsentLength ) { fragmentLength = chan->unsentLength - chan->unsentFragmentStart; } MSG_WriteShort( &send, chan->unsentFragmentStart ); MSG_WriteShort( &send, fragmentLength ); MSG_WriteData( &send, chan->unsentBuffer + chan->unsentFragmentStart, fragmentLength ); // send the datagram NET_SendPacket( chan->sock, send.cursize, send.data, chan->remoteAddress ); if ( showpackets->integer ) { Com_Printf ("%s send %4i : s=%i fragment=%i,%i\n" , netsrcString[ chan->sock ] , send.cursize , chan->outgoingSequence , chan->unsentFragmentStart, fragmentLength); } chan->unsentFragmentStart += fragmentLength; // this exit condition is a little tricky, because a packet // that is exactly the fragment length still needs to send // a second packet of zero length so that the other side // can tell there aren't more to follow if ( chan->unsentFragmentStart == chan->unsentLength && fragmentLength != FRAGMENT_SIZE ) { chan->outgoingSequence++; chan->unsentFragments = qfalse; } } /* =============== Netchan_Transmit Sends a message to a connection, fragmenting if necessary A 0 length will still generate a packet. ================ */ void Netchan_Transmit( netchan_t *chan, int length, const byte *data ) { msg_t send; byte send_buf[MAX_PACKETLEN]; if ( length > MAX_MSGLEN ) { Com_Error( ERR_DROP, "Netchan_Transmit: length = %i", length ); } chan->unsentFragmentStart = 0; // fragment large reliable messages if ( length >= FRAGMENT_SIZE ) { chan->unsentFragments = qtrue; chan->unsentLength = length; Com_Memcpy( chan->unsentBuffer, data, length ); // only send the first fragment now Netchan_TransmitNextFragment( chan ); return; } // write the packet header MSG_InitOOB (&send, send_buf, sizeof(send_buf)); MSG_WriteLong( &send, chan->outgoingSequence ); chan->outgoingSequence++; // send the qport if we are a client if ( chan->sock == NS_CLIENT ) { MSG_WriteShort( &send, qport->integer ); } MSG_WriteData( &send, data, length ); // send the datagram NET_SendPacket( chan->sock, send.cursize, send.data, chan->remoteAddress ); if ( showpackets->integer ) { Com_Printf( "%s send %4i : s=%i ack=%i\n" , netsrcString[ chan->sock ] , send.cursize , chan->outgoingSequence - 1 , chan->incomingSequence ); } } /* ================= Netchan_Process Returns qfalse if the message should not be processed due to being out of order or a fragment. Msg must be large enough to hold MAX_MSGLEN, because if this is the final fragment of a multi-part message, the entire thing will be copied out. ================= */ qboolean Netchan_Process( netchan_t *chan, msg_t *msg ) { int sequence; int qport; int fragmentStart, fragmentLength; qboolean fragmented; // XOR unscramble all data in the packet after the header // Netchan_UnScramblePacket( msg ); // get sequence numbers MSG_BeginReadingOOB( msg ); sequence = MSG_ReadLong( msg ); // check for fragment information if ( sequence & FRAGMENT_BIT ) { sequence &= ~FRAGMENT_BIT; fragmented = qtrue; } else { fragmented = qfalse; } // read the qport if we are a server if ( chan->sock == NS_SERVER ) { qport = MSG_ReadShort( msg ); } // read the fragment information if ( fragmented ) { fragmentStart = MSG_ReadShort( msg ); fragmentLength = MSG_ReadShort( msg ); } else { fragmentStart = 0; // stop warning message fragmentLength = 0; } if ( showpackets->integer ) { if ( fragmented ) { Com_Printf( "%s recv %4i : s=%i fragment=%i,%i\n" , netsrcString[ chan->sock ] , msg->cursize , sequence , fragmentStart, fragmentLength ); } else { Com_Printf( "%s recv %4i : s=%i\n" , netsrcString[ chan->sock ] , msg->cursize , sequence ); } } // // discard out of order or duplicated packets // if ( sequence <= chan->incomingSequence ) { if ( showdrop->integer || showpackets->integer ) { Com_Printf( "%s:Out of order packet %i at %i\n" , NET_AdrToString( chan->remoteAddress ) , sequence , chan->incomingSequence ); } return qfalse; } // // dropped packets don't keep the message from being used // chan->dropped = sequence - (chan->incomingSequence+1); if ( chan->dropped > 0 ) { if ( showdrop->integer || showpackets->integer ) { Com_Printf( "%s:Dropped %i packets at %i\n" , NET_AdrToString( chan->remoteAddress ) , chan->dropped , sequence ); } } // // if this is the final framgent of a reliable message, // bump incoming_reliable_sequence // if ( fragmented ) { // TTimo // make sure we add the fragments in correct order // either a packet was dropped, or we received this one too soon // we don't reconstruct the fragments. we will wait till this fragment gets to us again // (NOTE: we could probably try to rebuild by out of order chunks if needed) if ( sequence != chan->fragmentSequence ) { chan->fragmentSequence = sequence; chan->fragmentLength = 0; } // if we missed a fragment, dump the message if ( fragmentStart != chan->fragmentLength ) { if ( showdrop->integer || showpackets->integer ) { Com_Printf( "%s:Dropped a message fragment\n" , NET_AdrToString( chan->remoteAddress ) , sequence); } // we can still keep the part that we have so far, // so we don't need to clear chan->fragmentLength return qfalse; } // copy the fragment to the fragment buffer if ( fragmentLength < 0 || msg->readcount + fragmentLength > msg->cursize || chan->fragmentLength + fragmentLength > sizeof( chan->fragmentBuffer ) ) { if ( showdrop->integer || showpackets->integer ) { Com_Printf ("%s:illegal fragment length\n" , NET_AdrToString (chan->remoteAddress ) ); } return qfalse; } Com_Memcpy( chan->fragmentBuffer + chan->fragmentLength, msg->data + msg->readcount, fragmentLength ); chan->fragmentLength += fragmentLength; // if this wasn't the last fragment, don't process anything if ( fragmentLength == FRAGMENT_SIZE ) { return qfalse; } if ( chan->fragmentLength > msg->maxsize ) { Com_Printf( "%s:fragmentLength %i > msg->maxsize\n" , NET_AdrToString (chan->remoteAddress ), chan->fragmentLength ); return qfalse; } // copy the full message over the partial fragment // make sure the sequence number is still there *(int *)msg->data = LittleLong( sequence ); Com_Memcpy( msg->data + 4, chan->fragmentBuffer, chan->fragmentLength ); msg->cursize = chan->fragmentLength + 4; chan->fragmentLength = 0; msg->readcount = 4; // past the sequence number msg->bit = 32; // past the sequence number // TTimo // clients were not acking fragmented messages chan->incomingSequence = sequence; return qtrue; } // // the message can now be read from the current message pointer // chan->incomingSequence = sequence; return qtrue; } //============================================================================== /* =================== NET_CompareBaseAdr Compares without the port =================== */ qboolean NET_CompareBaseAdr (netadr_t a, netadr_t b) { if (a.type != b.type) return qfalse; if (a.type == NA_LOOPBACK) return qtrue; if (a.type == NA_IP) { if (a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] && a.ip[2] == b.ip[2] && a.ip[3] == b.ip[3]) return qtrue; return qfalse; } if (a.type == NA_IPX) { if ((memcmp(a.ipx, b.ipx, 10) == 0)) return qtrue; return qfalse; } Com_Printf ("NET_CompareBaseAdr: bad address type\n"); return qfalse; } const char *NET_AdrToString (netadr_t a) { static char s[64]; if (a.type == NA_LOOPBACK) { Com_sprintf (s, sizeof(s), "loopback"); } else if (a.type == NA_BOT) { Com_sprintf (s, sizeof(s), "bot"); } else if (a.type == NA_IP) { Com_sprintf (s, sizeof(s), "%i.%i.%i.%i:%hu", a.ip[0], a.ip[1], a.ip[2], a.ip[3], BigShort(a.port)); } else { Com_sprintf (s, sizeof(s), "%02x%02x%02x%02x.%02x%02x%02x%02x%02x%02x:%hu", a.ipx[0], a.ipx[1], a.ipx[2], a.ipx[3], a.ipx[4], a.ipx[5], a.ipx[6], a.ipx[7], a.ipx[8], a.ipx[9], BigShort(a.port)); } return s; } qboolean NET_CompareAdr (netadr_t a, netadr_t b) { if (a.type != b.type) return qfalse; if (a.type == NA_LOOPBACK) return qtrue; if (a.type == NA_IP) { if (a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] && a.ip[2] == b.ip[2] && a.ip[3] == b.ip[3] && a.port == b.port) return qtrue; return qfalse; } if (a.type == NA_IPX) { if ((memcmp(a.ipx, b.ipx, 10) == 0) && a.port == b.port) return qtrue; return qfalse; } Com_Printf ("NET_CompareAdr: bad address type\n"); return qfalse; } qboolean NET_IsLocalAddress( netadr_t adr ) { return (qboolean) (adr.type == NA_LOOPBACK); } /* ============================================================================= LOOPBACK BUFFERS FOR LOCAL PLAYER ============================================================================= */ // there needs to be enough loopback messages to hold a complete // gamestate of maximum size #define MAX_LOOPBACK 16 typedef struct { byte data[MAX_PACKETLEN]; int datalen; } loopmsg_t; typedef struct { loopmsg_t msgs[MAX_LOOPBACK]; int get, send; } loopback_t; loopback_t loopbacks[2]; qboolean NET_GetLoopPacket (netsrc_t sock, netadr_t *net_from, msg_t *net_message) { int i; loopback_t *loop; loop = &loopbacks[sock]; if (loop->send - loop->get > MAX_LOOPBACK) loop->get = loop->send - MAX_LOOPBACK; if (loop->get >= loop->send) return qfalse; i = loop->get & (MAX_LOOPBACK-1); loop->get++; Com_Memcpy (net_message->data, loop->msgs[i].data, loop->msgs[i].datalen); net_message->cursize = loop->msgs[i].datalen; Com_Memset (net_from, 0, sizeof(*net_from)); net_from->type = NA_LOOPBACK; return qtrue; } void NET_SendLoopPacket (netsrc_t sock, int length, const void *data, netadr_t to) { int i; loopback_t *loop; loop = &loopbacks[sock^1]; i = loop->send & (MAX_LOOPBACK-1); loop->send++; Com_Memcpy (loop->msgs[i].data, data, length); loop->msgs[i].datalen = length; } //============================================================================= void NET_SendPacket( netsrc_t sock, int length, const void *data, netadr_t to ) { // sequenced packets are shown in netchan, so just show oob if ( showpackets->integer && *(int *)data == -1 ) { Com_Printf ("send packet %4i\n", length); } if ( to.type == NA_LOOPBACK ) { NET_SendLoopPacket (sock, length, data, to); return; } if ( to.type == NA_BOT ) { return; } if ( to.type == NA_BAD ) { return; } Sys_SendPacket( length, data, to ); } /* =============== NET_OutOfBandPrint Sends a text message in an out-of-band datagram ================ */ void QDECL NET_OutOfBandPrint( netsrc_t sock, netadr_t adr, const char *format, ... ) { va_list argptr; char string[MAX_MSGLEN]; // set the header string[0] = -1; string[1] = -1; string[2] = -1; string[3] = -1; va_start( argptr, format ); vsprintf( string+4, format, argptr ); va_end( argptr ); // send the datagram NET_SendPacket( sock, (int)strlen( string ), string, adr ); } /* =============== NET_OutOfBandPrint Sends a data message in an out-of-band datagram (only used for "connect") ================ */ void QDECL NET_OutOfBandData( netsrc_t sock, netadr_t adr, byte *format, int len ) { byte string[MAX_MSGLEN*2]; int i; msg_t mbuf; // set the header string[0] = 0xff; string[1] = 0xff; string[2] = 0xff; string[3] = 0xff; for(i=0;itype = NA_LOOPBACK; return qtrue; } // look for a port number Q_strncpyz( base, s, sizeof( base ) ); port = strstr( base, ":" ); if ( port ) { *port = 0; port++; } r = Sys_StringToAdr( base, a ); if ( !r ) { a->type = NA_BAD; return qfalse; } // inet_addr returns this if out of range if ( a->ip[0] == 255 && a->ip[1] == 255 && a->ip[2] == 255 && a->ip[3] == 255 ) { a->type = NA_BAD; return qfalse; } if ( port ) { a->port = BigShort( (short)atoi( port ) ); } else { a->port = BigShort( PORT_SERVER ); } return qtrue; } ================================================ FILE: src/engine/qcommon/qcommon.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // qcommon.h -- definitions common between client and server, but not game.or ref modules #ifndef _QCOMMON_H_ #define _QCOMMON_H_ #include "../qcommon/cm_public.h" //#define PRE_RELEASE_DEMO //============================================================================ // // msg.c // typedef struct { qboolean allowoverflow; // if false, do a Com_Error qboolean overflowed; // set to true if the buffer size failed (with allowoverflow set) qboolean oob; // set to true if the buffer size failed (with allowoverflow set) byte *data; int maxsize; int cursize; int readcount; int bit; // for bitwise reads and writes } msg_t; void MSG_Init (msg_t *buf, byte *data, int length); void MSG_InitOOB( msg_t *buf, byte *data, int length ); void MSG_Clear (msg_t *buf); void MSG_WriteData (msg_t *buf, const void *data, int length); void MSG_Bitstream( msg_t *buf ); // TTimo // copy a msg_t in case we need to store it as is for a bit // (as I needed this to keep an msg_t from a static var for later use) // sets data buffer as MSG_Init does prior to do the copy void MSG_Copy(msg_t *buf, byte *data, int length, msg_t *src); struct usercmd_s; struct entityState_s; struct playerState_s; void MSG_WriteBits( msg_t *msg, int value, int bits ); void MSG_WriteChar (msg_t *sb, int c); void MSG_WriteByte (msg_t *sb, int c); void MSG_WriteShort (msg_t *sb, int c); void MSG_WriteLong (msg_t *sb, int c); void MSG_WriteFloat (msg_t *sb, float f); void MSG_WriteString (msg_t *sb, const char *s); void MSG_WriteBigString (msg_t *sb, const char *s); void MSG_WriteAngle16 (msg_t *sb, float f); void MSG_BeginReading (msg_t *sb); void MSG_BeginReadingOOB(msg_t *sb); int MSG_ReadBits( msg_t *msg, int bits ); int MSG_ReadChar (msg_t *sb); int MSG_ReadByte (msg_t *sb); int MSG_ReadShort (msg_t *sb); int MSG_ReadLong (msg_t *sb); float MSG_ReadFloat (msg_t *sb); char *MSG_ReadString (msg_t *sb); char *MSG_ReadBigString (msg_t *sb); char *MSG_ReadStringLine (msg_t *sb); float MSG_ReadAngle16 (msg_t *sb); void MSG_ReadData (msg_t *sb, void *buffer, int size); void MSG_WriteDeltaUsercmd( msg_t *msg, struct usercmd_s *from, struct usercmd_s *to ); void MSG_ReadDeltaUsercmd( msg_t *msg, struct usercmd_s *from, struct usercmd_s *to ); void MSG_WriteDeltaUsercmdKey( msg_t *msg, int key, usercmd_t *from, usercmd_t *to ); void MSG_ReadDeltaUsercmdKey( msg_t *msg, int key, usercmd_t *from, usercmd_t *to ); void MSG_WriteDeltaEntity( msg_t *msg, struct entityState_s *from, struct entityState_s *to , qboolean force ); void MSG_ReadDeltaEntity( msg_t *msg, entityState_t *from, entityState_t *to, int number ); void MSG_WriteDeltaPlayerstate( msg_t *msg, struct playerState_s *from, struct playerState_s *to ); void MSG_ReadDeltaPlayerstate( msg_t *msg, struct playerState_s *from, struct playerState_s *to ); void MSG_ReportChangeVectors_f( void ); //============================================================================ /* ============================================================== NET ============================================================== */ #define PACKET_BACKUP 32 // number of old messages that must be kept on client and // server for delta comrpession and ping estimation #define PACKET_MASK (PACKET_BACKUP-1) #define MAX_PACKET_USERCMDS 32 // max number of usercmd_t in a packet #define PORT_ANY -1 #define MAX_RELIABLE_COMMANDS 64 // max string commands buffered for restransmit typedef enum { NA_BOT, NA_BAD, // an address lookup failed NA_LOOPBACK, NA_BROADCAST, NA_IP, NA_IPX, NA_BROADCAST_IPX } netadrtype_t; typedef enum { NS_CLIENT, NS_SERVER } netsrc_t; typedef struct { netadrtype_t type; byte ip[4]; byte ipx[10]; unsigned short port; } netadr_t; void NET_Init( void ); void NET_Shutdown( void ); void NET_Restart( void ); void NET_Config( qboolean enableNetworking ); void NET_SendPacket (netsrc_t sock, int length, const void *data, netadr_t to); void QDECL NET_OutOfBandPrint( netsrc_t net_socket, netadr_t adr, const char *format, ...); void QDECL NET_OutOfBandData( netsrc_t sock, netadr_t adr, byte *format, int len ); qboolean NET_CompareAdr (netadr_t a, netadr_t b); qboolean NET_CompareBaseAdr (netadr_t a, netadr_t b); qboolean NET_IsLocalAddress (netadr_t adr); const char *NET_AdrToString (netadr_t a); qboolean NET_StringToAdr ( const char *s, netadr_t *a); qboolean NET_GetLoopPacket (netsrc_t sock, netadr_t *net_from, msg_t *net_message); void NET_Sleep(int msec); #define MAX_MSGLEN 16384 // max length of a message, which may // be fragmented into multiple packets #define MAX_DOWNLOAD_WINDOW 8 // max of eight download frames #define MAX_DOWNLOAD_BLKSIZE 2048 // 2048 byte block chunks /* Netchan handles packet fragmentation and out of order / duplicate suppression */ typedef struct { netsrc_t sock; int dropped; // between last packet and previous netadr_t remoteAddress; int qport; // qport value to write when transmitting // sequencing variables int incomingSequence; int outgoingSequence; // incoming fragment assembly buffer int fragmentSequence; int fragmentLength; byte fragmentBuffer[MAX_MSGLEN]; // outgoing fragment buffer // we need to space out the sending of large fragmented messages qboolean unsentFragments; int unsentFragmentStart; int unsentLength; byte unsentBuffer[MAX_MSGLEN]; } netchan_t; void Netchan_Init( int qport ); void Netchan_Setup( netsrc_t sock, netchan_t *chan, netadr_t adr, int qport ); void Netchan_Transmit( netchan_t *chan, int length, const byte *data ); void Netchan_TransmitNextFragment( netchan_t *chan ); qboolean Netchan_Process( netchan_t *chan, msg_t *msg ); /* ============================================================== PROTOCOL ============================================================== */ #define PROTOCOL_VERSION 68 // 1.31 - 67 // maintain a list of compatible protocols for demo playing // NOTE: that stuff only works with two digits protocols extern int demo_protocols[]; #define UPDATE_SERVER_NAME "update.quake3arena.com" // override on command line, config files etc. #ifndef MASTER_SERVER_NAME #define MASTER_SERVER_NAME "master.quake3arena.com" #endif #ifndef AUTHORIZE_SERVER_NAME #define AUTHORIZE_SERVER_NAME "authorize.quake3arena.com" #endif #define PORT_MASTER 27950 #define PORT_UPDATE 27951 #ifndef PORT_AUTHORIZE #define PORT_AUTHORIZE 27952 #endif #define PORT_SERVER 27960 #define NUM_SERVER_PORTS 4 // broadcast scan this many ports after // PORT_SERVER so a single machine can // run multiple servers // the svc_strings[] array in cl_parse.c should mirror this // // server to client // enum svc_ops_e { svc_bad, svc_nop, svc_gamestate, svc_configstring, // [short] [string] only in gamestate messages svc_baseline, // only in gamestate messages svc_serverCommand, // [string] to be executed by client game module svc_download, // [short] size [size bytes] svc_snapshot, svc_EOF }; // // client to server // enum clc_ops_e { clc_bad, clc_nop, clc_move, // [[usercmd_t] clc_moveNoDelta, // [[usercmd_t] clc_clientCommand, // [string] message clc_EOF }; /* ============================================================== VIRTUAL MACHINE ============================================================== */ typedef struct vm_s vm_t; typedef enum { VMI_NATIVE, VMI_BYTECODE } vmInterpret_t; typedef enum { TRAP_MEMSET = 100, TRAP_MEMCPY, TRAP_STRNCPY, TRAP_SIN, TRAP_COS, TRAP_ATAN2, TRAP_SQRT, TRAP_MATRIXMULTIPLY, TRAP_ANGLEVECTORS, TRAP_PERPENDICULARVECTOR, TRAP_FLOOR, TRAP_CEIL, TRAP_TESTPRINTINT, TRAP_TESTPRINTFLOAT } sharedTraps_t; void VM_Init( void ); vm_t *VM_Create( const char *module, intptr_t (*systemCalls)(intptr_t *), vmInterpret_t interpret ); // module should be bare: "cgame", not "cgame.dll" or "vm/cgame.qvm" void VM_Free( vm_t *vm ); void VM_Clear(void); vm_t *VM_Restart( vm_t *vm ); intptr_t QDECL VM_Call( vm_t *vm, int callNum, ... ); void VM_Debug( int level ); void *VM_ArgPtr( intptr_t intValue ); void *VM_ExplicitArgPtr( vm_t *vm, intptr_t intValue ); /* ============================================================== CMD Command text buffering and command execution ============================================================== */ /* Any number of commands can be added in a frame, from several different sources. Most commands come from either keybindings or console line input, but entire text files can be execed. */ void Cbuf_Init (void); // allocates an initial text buffer that will grow as needed void Cbuf_AddText( const char *text ); // Adds command text at the end of the buffer, does NOT add a final \n void Cbuf_ExecuteText( int exec_when, const char *text ); // this can be used in place of either Cbuf_AddText or Cbuf_InsertText void Cbuf_Execute (void); // Pulls off \n terminated lines of text from the command buffer and sends // them through Cmd_ExecuteString. Stops when the buffer is empty. // Normally called once per frame, but may be explicitly invoked. // Do not call inside a command function, or current args will be destroyed. //=========================================================================== /* Command execution takes a null terminated string, breaks it into tokens, then searches for a command or variable that matches the first token. */ typedef void (*xcommand_t) (void); void Cmd_Init (void); void Cmd_AddCommand( const char *cmd_name, xcommand_t function ); // called by the init functions of other parts of the program to // register commands and functions to call for them. // The cmd_name is referenced later, so it should not be in temp memory // if function is NULL, the command will be forwarded to the server // as a clc_clientCommand instead of executed locally void Cmd_RemoveCommand( const char *cmd_name ); void Cmd_CommandCompletion( void(*callback)(const char *s) ); // callback with each valid string int Cmd_Argc (void); char *Cmd_Argv (int arg); void Cmd_ArgvBuffer( int arg, char *buffer, int bufferLength ); char *Cmd_Args (void); char *Cmd_ArgsFrom( int arg ); void Cmd_ArgsBuffer( char *buffer, int bufferLength ); char *Cmd_Cmd (void); // The functions that execute commands get their parameters with these // functions. Cmd_Argv () will return an empty string, not a NULL // if arg > argc, so string operations are allways safe. void Cmd_TokenizeString( const char *text ); // Takes a null terminated string. Does not need to be /n terminated. // breaks the string up into arg tokens. void Cmd_ExecuteString( const char *text ); // Parses a single line of text into arguments and tries to execute it // as if it was typed at the console /* ============================================================== CVAR ============================================================== */ /* cvar_t variables are used to hold scalar or string variables that can be changed or displayed at the console or prog code as well as accessed directly in C code. The user can access cvars from the console in three ways: r_draworder prints the current value r_draworder 0 sets the current value to 0 set r_draworder 0 as above, but creates the cvar if not present Cvars are restricted from having the same names as commands to keep this interface from being ambiguous. The are also occasionally used to communicated information between different modules of the program. */ cvar_t *Cvar_Get( const char *var_name, const char *value, int flags ); // creates the variable if it doesn't exist, or returns the existing one // if it exists, the value will not be changed, but flags will be ORed in // that allows variables to be unarchived without needing bitflags // if value is "", the value will not override a previously set value. void Cvar_Register( vmCvar_t *vmCvar, const char *varName, const char *defaultValue, int flags ); // basically a slightly modified Cvar_Get for the interpreted modules void Cvar_Update( vmCvar_t *vmCvar ); // updates an interpreted modules' version of a cvar void Cvar_Set( const char *var_name, const char *value ); // will create the variable with no flags if it doesn't exist void Cvar_SetLatched( const char *var_name, const char *value); // don't set the cvar immediately void Cvar_SetValue( const char *var_name, float value ); // expands value to a string and calls Cvar_Set float Cvar_VariableValue( const char *var_name ); int Cvar_VariableIntegerValue( const char *var_name ); // returns 0 if not defined or non numeric char *Cvar_VariableString( const char *var_name ); void Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize ); // returns an empty string if not defined void Cvar_CommandCompletion( void(*callback)(const char *s) ); // callback with each valid string void Cvar_Reset( const char *var_name ); void Cvar_SetCheatState( void ); // reset all testing vars to a safe value qboolean Cvar_Command( void ); // called by Cmd_ExecuteString when Cmd_Argv(0) doesn't match a known // command. Returns true if the command was a variable reference that // was handled. (print or change) void Cvar_WriteVariables( fileHandle_t f ); // writes lines containing "set variable value" for all variables // with the archive flag set to true. void Cvar_Init( void ); char *Cvar_InfoString( int bit ); char *Cvar_InfoString_Big( int bit ); // returns an info string containing all the cvars that have the given bit set // in their flags ( CVAR_USERINFO, CVAR_SERVERINFO, CVAR_SYSTEMINFO, etc ) void Cvar_InfoStringBuffer( int bit, char *buff, int buffsize ); void Cvar_Restart_f( void ); extern int cvar_modifiedFlags; // whenever a cvar is modifed, its flags will be OR'd into this, so // a single check can determine if any CVAR_USERINFO, CVAR_SERVERINFO, // etc, variables have been modified since the last check. The bit // can then be cleared to allow another change detection. /* ============================================================== FILESYSTEM No stdio calls should be used by any part of the game, because we need to deal with all sorts of directory and seperator char issues. ============================================================== */ // referenced flags // these are in loop specific order so don't change the order #define FS_GENERAL_REF 0x01 #define FS_UI_REF 0x02 #define FS_CGAME_REF 0x04 #define FS_QAGAME_REF 0x08 // number of id paks that will never be autodownloaded from baseq3 #define NUM_ID_PAKS 9 #define MAX_FILE_HANDLES 64 #define BASEGAME "baseq3" qboolean FS_Initialized(); void FS_InitFilesystem (void); void FS_Shutdown( qboolean closemfp ); qboolean FS_ConditionalRestart( int checksumFeed ); void FS_Restart( int checksumFeed ); // shutdown and restart the filesystem so changes to fs_gamedir can take effect char **FS_ListFiles( const char *directory, const char *extension, int *numfiles ); // directory should not have either a leading or trailing / // if extension is "/", only subdirectories will be returned // the returned files will not include any directories or / void FS_FreeFileList( char **list ); qboolean FS_FileExists( const char *file ); int FS_LoadStack(); int FS_GetFileList( const char *path, const char *extension, char *listbuf, int bufsize ); int FS_GetModList( char *listbuf, int bufsize ); fileHandle_t FS_FOpenFileWrite( const char *qpath ); // will properly create any needed paths and deal with seperater character issues int FS_filelength( fileHandle_t f ); fileHandle_t FS_SV_FOpenFileWrite( const char *filename ); int FS_SV_FOpenFileRead( const char *filename, fileHandle_t *fp ); void FS_SV_Rename( const char *from, const char *to ); int FS_FOpenFileRead( const char *qpath, fileHandle_t *file, qboolean uniqueFILE ); // if uniqueFILE is true, then a new FILE will be fopened even if the file // is found in an already open pak file. If uniqueFILE is false, you must call // FS_FCloseFile instead of fclose, otherwise the pak FILE would be improperly closed // It is generally safe to always set uniqueFILE to true, because the majority of // file IO goes through FS_ReadFile, which Does The Right Thing already. int FS_FileIsInPAK(const char *filename, int *pChecksum ); // returns 1 if a file is in the PAK file, otherwise -1 int FS_Write( const void *buffer, int len, fileHandle_t f ); int FS_Read2( void *buffer, int len, fileHandle_t f ); int FS_Read( void *buffer, int len, fileHandle_t f ); // properly handles partial reads and reads from other dlls void FS_FCloseFile( fileHandle_t f ); // note: you can't just fclose from another DLL, due to MS libc issues int FS_ReadFile( const char *qpath, void **buffer ); // returns the length of the file // a null buffer will just return the file length without loading // as a quick check for existance. -1 length == not present // A 0 byte will always be appended at the end, so string ops are safe. // the buffer should be considered read-only, because it may be cached // for other uses. void FS_ForceFlush( fileHandle_t f ); // forces flush on files we're writing to. void FS_FreeFile( void *buffer ); // frees the memory returned by FS_ReadFile void FS_WriteFile( const char *qpath, const void *buffer, int size ); // writes a complete file, creating any subdirectories needed int FS_filelength( fileHandle_t f ); // doesn't work for files that are opened from a pack file int FS_FTell( fileHandle_t f ); // where are we? void FS_Flush( fileHandle_t f ); void QDECL FS_Printf( fileHandle_t f, const char *fmt, ... ); // like fprintf int FS_FOpenFileByMode( const char *qpath, fileHandle_t *f, fsMode_t mode ); // opens a file for reading, writing, or appending depending on the value of mode int FS_Seek( fileHandle_t f, long offset, int origin ); // seek on a file (doesn't work for zip files!!!!!!!!) qboolean FS_FilenameCompare( const char *s1, const char *s2 ); const char *FS_GamePureChecksum( void ); // Returns the checksum of the pk3 from which the server loaded the qagame.qvm const char *FS_LoadedPakNames( void ); const char *FS_LoadedPakChecksums( void ); const char *FS_LoadedPakPureChecksums( void ); // Returns a space separated string containing the checksums of all loaded pk3 files. // Servers with sv_pure set will get this string and pass it to clients. const char *FS_ReferencedPakNames( void ); const char *FS_ReferencedPakChecksums( void ); const char *FS_ReferencedPakPureChecksums( void ); // Returns a space separated string containing the checksums of all loaded // AND referenced pk3 files. Servers with sv_pure set will get this string // back from clients for pure validation void FS_ClearPakReferences( int flags ); // clears referenced booleans on loaded pk3s void FS_PureServerSetReferencedPaks( const char *pakSums, const char *pakNames ); void FS_PureServerSetLoadedPaks( const char *pakSums, const char *pakNames ); // If the string is empty, all data sources will be allowed. // If not empty, only pk3 files that match one of the space // separated checksums will be checked for files, with the // sole exception of .cfg files. qboolean FS_idPak( char *pak, char *base ); qboolean FS_ComparePaks( char *neededpaks, int len, qboolean dlstring ); void FS_Rename( const char *from, const char *to ); /* ============================================================== Edit fields and command line history/completion ============================================================== */ #define MAX_EDIT_LINE 256 typedef struct { int cursor; int scroll; int widthInChars; char buffer[MAX_EDIT_LINE]; } field_t; void Field_Clear( field_t *edit ); void Field_CompleteCommand( field_t *edit ); /* ============================================================== MISC ============================================================== */ // TTimo // vsnprintf is ISO/IEC 9899:1999 // abstracting this to make it portable #ifdef WIN32 #define Q_vsnprintf _vsnprintf #else // TODO: do we need Mac define? #define Q_vsnprintf vsnprintf #endif // centralizing the declarations for cl_cdkey // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=470 extern char cl_cdkey[34]; // returnbed by Sys_GetProcessorId #define CPUID_GENERIC 0 // any unrecognized processor // TTimo // centralized and cleaned, that's the max string you can send to a Com_Printf / Com_DPrintf (above gets truncated) #define MAXPRINTMSG 4096 char *CopyString( const char *in ); void Info_Print( const char *s ); void Com_BeginRedirect (char *buffer, int buffersize, void (*flush)(char *)); void Com_EndRedirect( void ); void QDECL Com_Printf( const char *fmt, ... ); void QDECL Com_DPrintf( const char *fmt, ... ); void QDECL Com_Error( int code, const char *fmt, ... ); void Com_Quit_f( void ); int Com_EventLoop( void ); int Com_Milliseconds( void ); // will be journaled properly unsigned Com_BlockChecksum( const void *buffer, int length ); unsigned Com_BlockChecksumKey (void *buffer, int length, int key); int Com_HashKey(char *string, int maxlen); int Com_Filter(char *filter, char *name, int casesensitive); int Com_FilterPath(char *filter, char *name, int casesensitive); int Com_RealTime(qtime_t *qtime); qboolean Com_SafeMode( void ); void Com_StartupVariable( const char *match ); // checks for and removes command line "+set var arg" constructs // if match is NULL, all set commands will be executed, otherwise // only a set with the exact name. Only used during startup. extern cvar_t *com_developer; extern cvar_t *com_dedicated; extern cvar_t *com_speeds; extern cvar_t *com_timescale; extern cvar_t *com_sv_running; extern cvar_t *com_cl_running; extern cvar_t *com_viewlog; // 0 = hidden, 1 = visible, 2 = minimized extern cvar_t *com_version; extern cvar_t *com_blood; extern cvar_t *com_buildScript; // for building release pak files extern cvar_t *com_journal; extern cvar_t *com_cameraMode; // both client and server must agree to pause extern cvar_t *cl_paused; extern cvar_t *sv_paused; // com_speeds times extern int time_game; extern int time_frontend; extern int time_backend; // renderer backend time extern int com_frameTime; extern int com_frameMsec; extern qboolean com_errorEntered; extern fileHandle_t com_journalFile; extern fileHandle_t com_journalDataFile; typedef enum { TAG_FREE, TAG_GENERAL, TAG_BOTLIB, TAG_RENDERER, TAG_SMALL, TAG_STATIC } memtag_t; /* --- low memory ---- server vm server clipmap ---mark--- renderer initialization (shaders, etc) UI vm cgame vm renderer map renderer models ---free--- temp file loading --- high memory --- */ #if defined(_DEBUG) && !defined(BSPC) #define ZONE_DEBUG #endif #ifdef ZONE_DEBUG #define Z_TagMalloc(size, tag) Z_TagMallocDebug(size, tag, #size, __FILE__, __LINE__) #define Z_Malloc(size) Z_MallocDebug(size, #size, __FILE__, __LINE__) #define S_Malloc(size) S_MallocDebug(size, #size, __FILE__, __LINE__) void *Z_TagMallocDebug( int size, int tag, char *label, char *file, int line ); // NOT 0 filled memory void *Z_MallocDebug( int size, char *label, char *file, int line ); // returns 0 filled memory void *S_MallocDebug( int size, char *label, char *file, int line ); // returns 0 filled memory #else void *Z_TagMalloc( int size, int tag ); // NOT 0 filled memory void *Z_Malloc( int size ); // returns 0 filled memory void *S_Malloc( int size ); // NOT 0 filled memory only for small allocations #endif void Z_Free( void *ptr ); void Z_FreeTags( int tag ); int Z_AvailableMemory( void ); void Z_LogHeap( void ); void Hunk_Clear( void ); void Hunk_ClearToMark( void ); void Hunk_SetMark( void ); qboolean Hunk_CheckMark( void ); void Hunk_ClearTempMemory( void ); void *Hunk_AllocateTempMemory( int size ); void Hunk_FreeTempMemory( void *buf ); int Hunk_MemoryRemaining( void ); void Hunk_Log( void); void Hunk_Trash( void ); void Com_TouchMemory( void ); // commandLine should not include the executable name (argv[0]) void Com_Init( char *commandLine ); void Com_Frame( void ); void Com_Shutdown( void ); /* ============================================================== CLIENT / SERVER SYSTEMS ============================================================== */ // // client interface // void CL_InitKeyCommands( void ); // the keyboard binding interface must be setup before execing // config files, but the rest of client startup will happen later void CL_Init( void ); void CL_Disconnect( qboolean showMainMenu ); void CL_Shutdown( void ); void CL_Frame( int msec ); qboolean CL_GameCommand( void ); void CL_KeyEvent (int key, qboolean down, unsigned time); void CL_CharEvent( int key ); // char events are for field typing, not game control void CL_MouseEvent( int dx, int dy, int time ); void CL_JoystickEvent( int axis, int value, int time ); void CL_PacketEvent( netadr_t from, msg_t *msg ); void CL_ConsolePrint( char *text ); void CL_MapLoading( void ); // do a screen update before starting to load a map // when the server is going to load a new map, the entire hunk // will be cleared, so the client must shutdown cgame, ui, and // the renderer void CL_ForwardCommandToServer( const char *string ); // adds the current command line as a clc_clientCommand to the client message. // things like godmode, noclip, etc, are commands directed to the server, // so when they are typed in at the console, they will need to be forwarded. void CL_CDDialog( void ); // bring up the "need a cd to play" dialog void CL_ShutdownAll( void ); // shutdown all the client stuff void CL_FlushMemory( void ); // dump all memory on an error void CL_StartHunkUsers( void ); // start all the client stuff using the hunk void Key_WriteBindings( fileHandle_t f ); // for writing the config files void S_ClearSoundBuffer( void ); // call before filesystem access void SCR_DebugGraph (float value, int color); // FIXME: move logging to common? // // server interface // void SV_Init( void ); void SV_Shutdown( char *finalmsg ); void SV_Frame( int msec ); void SV_PacketEvent( netadr_t from, msg_t *msg ); qboolean SV_GameCommand( void ); // // UI interface // qboolean UI_GameCommand( void ); qboolean UI_usesUniqueCDKey(); /* ============================================================== NON-PORTABLE SYSTEM SERVICES ============================================================== */ typedef enum { AXIS_SIDE, AXIS_FORWARD, AXIS_UP, AXIS_ROLL, AXIS_YAW, AXIS_PITCH, MAX_JOYSTICK_AXIS } joystickAxis_t; typedef enum { // bk001129 - make sure SE_NONE is zero SE_NONE = 0, // evTime is still valid SE_KEY, // evValue is a key code, evValue2 is the down flag SE_CHAR, // evValue is an ascii char SE_MOUSE, // evValue and evValue2 are reletive signed x / y moves SE_JOYSTICK_AXIS, // evValue is an axis number and evValue2 is the current state (-127 to 127) SE_CONSOLE, // evPtr is a char* SE_PACKET // evPtr is a netadr_t followed by data bytes to evPtrLength } sysEventType_t; typedef struct { int evTime; sysEventType_t evType; int evValue, evValue2; int evPtrLength; // bytes of data pointed to by evPtr, for journaling void *evPtr; // this must be manually freed if not NULL } sysEvent_t; sysEvent_t Sys_GetEvent( void ); void Sys_Init (void); // general development dll loading for virtual machine testing // fqpath param added 7/20/02 by T.Ray - Sys_LoadDll is only called in vm.c at this time void * QDECL Sys_LoadDll( const char *name, char *fqpath , intptr_t (QDECL **entryPoint)(int, ...), intptr_t (QDECL *systemcalls)(intptr_t, ...) ); void Sys_UnloadDll( void *dllHandle ); void Sys_UnloadGame( void ); void *Sys_GetGameAPI( void *parms ); void Sys_UnloadCGame( void ); void *Sys_GetCGameAPI( void ); void Sys_UnloadUI( void ); void *Sys_GetUIAPI( void ); //bot libraries void Sys_UnloadBotLib( void ); void *Sys_GetBotLibAPI( void *parms ); char *Sys_GetCurrentUser( void ); void QDECL Sys_Error( const char *error, ...); void Sys_Quit (void); char *Sys_GetClipboardData( void ); // note that this isn't journaled... void Sys_Print( const char *msg ); // Sys_Milliseconds should only be used for profiling purposes, // any game related timing information should come from event timestamps int Sys_Milliseconds (void); void Sys_SnapVector( float *v ); // the system console is shown when a dedicated server is running void Sys_DisplaySystemConsole( qboolean show ); int Sys_GetProcessorId( void ); void Sys_BeginStreamedFile( fileHandle_t f, int readahead ); void Sys_EndStreamedFile( fileHandle_t f ); int Sys_StreamedRead( void *buffer, int size, int count, fileHandle_t f ); void Sys_StreamSeek( fileHandle_t f, int offset, int origin ); void Sys_ShowConsole( int level, qboolean quitOnClose ); void Sys_SetErrorText( const char *text ); void Sys_SendPacket( int length, const void *data, netadr_t to ); qboolean Sys_StringToAdr( const char *s, netadr_t *a ); //Does NOT parse port numbers, only base addresses. qboolean Sys_IsLANAddress (netadr_t adr); void Sys_ShowIP(void); qboolean Sys_CheckCD( void ); void Sys_Mkdir( const char *path ); char *Sys_Cwd( void ); void Sys_SetDefaultCDPath(const char *path); char *Sys_DefaultCDPath(void); void Sys_SetDefaultInstallPath(const char *path); char *Sys_DefaultInstallPath(void); void Sys_SetDefaultHomePath(const char *path); char *Sys_DefaultHomePath(void); char **Sys_ListFiles( const char *directory, const char *extension, char *filter, int *numfiles, qboolean wantsubs ); void Sys_FreeFileList( char **list ); void Sys_BeginProfiling( void ); void Sys_EndProfiling( void ); qboolean Sys_LowPhysicalMemory(); unsigned int Sys_ProcessorCount(); int Sys_MonkeyShouldBeSpanked( void ); /* This is based on the Adaptive Huffman algorithm described in Sayood's Data * Compression book. The ranks are not actually stored, but implicitly defined * by the location of a node within a doubly-linked list */ #define NYT HMAX /* NYT = Not Yet Transmitted */ #define INTERNAL_NODE (HMAX+1) typedef struct nodetype { struct nodetype *left, *right, *parent; /* tree structure */ struct nodetype *next, *prev; /* doubly-linked list */ struct nodetype **head; /* highest ranked node in block */ int weight; int symbol; } node_t; #define HMAX 256 /* Maximum symbol */ typedef struct { int blocNode; int blocPtrs; node_t* tree; node_t* lhead; node_t* ltail; node_t* loc[HMAX+1]; node_t** freelist; node_t nodeList[768]; node_t* nodePtrs[768]; } huff_t; typedef struct { huff_t compressor; huff_t decompressor; } huffman_t; void Huff_Compress(msg_t *buf, int offset); void Huff_Decompress(msg_t *buf, int offset); void Huff_Init(huffman_t *huff); void Huff_addRef(huff_t* huff, byte ch); int Huff_Receive (node_t *node, int *ch, byte *fin); void Huff_transmit (huff_t *huff, int ch, byte *fout); void Huff_offsetReceive (node_t *node, int *ch, byte *fin, int *offset); void Huff_offsetTransmit (huff_t *huff, int ch, byte *fout, int *offset); void Huff_putBit( int bit, byte *fout, int *offset); int Huff_getBit( byte *fout, int *offset); extern huffman_t clientHuffTables; #define SV_ENCODE_START 4 #define SV_DECODE_START 12 #define CL_ENCODE_START 12 #define CL_DECODE_START 4 #endif // _QCOMMON_H_ ================================================ FILE: src/engine/qcommon/qfiles.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #ifndef __QFILES_H__ #define __QFILES_H__ // // qfiles.h: quake file formats // This file must be identical in the quake and utils directories // // surface geometry should not exceed these limits #define SHADER_MAX_VERTEXES 1000 #define SHADER_MAX_INDEXES (6*SHADER_MAX_VERTEXES) // the maximum size of game relative pathnames #define MAX_QPATH 64 /* ======================================================================== QVM files ======================================================================== */ #define VM_MAGIC 0x12721444 typedef struct { int vmMagic; int instructionCount; int codeOffset; int codeLength; int dataOffset; int dataLength; int litLength; // ( dataLength - litLength ) should be byteswapped on load int bssLength; // zero filled memory appended to datalength } vmHeader_t; /* ======================================================================== 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; char filler[58]; unsigned char data; // unbounded } pcx_t; /* ======================================================================== 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; /* ======================================================================== .MD3 triangle model file format ======================================================================== */ #define MD3_IDENT (('3'<<24)+('P'<<16)+('D'<<8)+'I') #define MD3_VERSION 15 // limits #define MD3_MAX_LODS 3 #define MD3_MAX_TRIANGLES 8192 // per surface #define MD3_MAX_VERTS 4096 // per surface #define MD3_MAX_SHADERS 256 // per surface #define MD3_MAX_FRAMES 1024 // per model #define MD3_MAX_SURFACES 32 // per model #define MD3_MAX_TAGS 16 // per frame // vertex scales #define MD3_XYZ_SCALE (1.0/64) typedef struct md3Frame_s { vec3_t bounds[2]; vec3_t localOrigin; float radius; char name[16]; } md3Frame_t; typedef struct md3Tag_s { char name[MAX_QPATH]; // tag name vec3_t origin; vec3_t axis[3]; } md3Tag_t; /* ** md3Surface_t ** ** CHUNK SIZE ** header sizeof( md3Surface_t ) ** shaders sizeof( md3Shader_t ) * numShaders ** triangles[0] sizeof( md3Triangle_t ) * numTriangles ** st sizeof( md3St_t ) * numVerts ** XyzNormals sizeof( md3XyzNormal_t ) * numVerts * numFrames */ typedef struct { int ident; // char name[MAX_QPATH]; // polyset name int flags; int numFrames; // all surfaces in a model should have the same int numShaders; // all surfaces in a model should have the same int numVerts; int numTriangles; int ofsTriangles; int ofsShaders; // offset from start of md3Surface_t int ofsSt; // texture coords are common for all frames int ofsXyzNormals; // numVerts * numFrames int ofsEnd; // next surface follows } md3Surface_t; typedef struct { char name[MAX_QPATH]; int shaderIndex; // for in-game use } md3Shader_t; typedef struct { int indexes[3]; } md3Triangle_t; typedef struct { float st[2]; } md3St_t; typedef struct { short xyz[3]; short normal; } md3XyzNormal_t; typedef struct { int ident; int version; char name[MAX_QPATH]; // model name int flags; int numFrames; int numTags; int numSurfaces; int numSkins; int ofsFrames; // offset for first frame int ofsTags; // numFrames * numTags int ofsSurfaces; // first surface, others follow int ofsEnd; // end of file } md3Header_t; /* ============================================================================== MD4 file format ============================================================================== */ #define MD4_IDENT (('4'<<24)+('P'<<16)+('D'<<8)+'I') #define MD4_VERSION 1 #define MD4_MAX_BONES 128 typedef struct { int boneIndex; // these are indexes into the boneReferences, float boneWeight; // not the global per-frame bone list vec3_t offset; } md4Weight_t; typedef struct { vec3_t normal; vec2_t texCoords; int numWeights; md4Weight_t weights[1]; // variable sized } md4Vertex_t; typedef struct { int indexes[3]; } md4Triangle_t; typedef struct { int ident; char name[MAX_QPATH]; // polyset name char shader[MAX_QPATH]; int shaderIndex; // for in-game use int ofsHeader; // this will be a negative number int numVerts; int ofsVerts; int numTriangles; int ofsTriangles; // Bone references are a set of ints representing all the bones // present in any vertex weights for this surface. This is // needed because a model may have surfaces that need to be // drawn at different sort times, and we don't want to have // to re-interpolate all the bones for each surface. int numBoneReferences; int ofsBoneReferences; int ofsEnd; // next surface follows } md4Surface_t; typedef struct { float matrix[3][4]; } md4Bone_t; typedef struct { vec3_t bounds[2]; // bounds of all surfaces of all LOD's for this frame vec3_t localOrigin; // midpoint of bounds, used for sphere cull float radius; // dist from localOrigin to corner md4Bone_t bones[1]; // [numBones] } md4Frame_t; typedef struct { int numSurfaces; int ofsSurfaces; // first surface, others follow int ofsEnd; // next lod follows } md4LOD_t; typedef struct { int ident; int version; char name[MAX_QPATH]; // model name // frames and bones are shared by all levels of detail int numFrames; int numBones; int ofsBoneNames; // char name[ MAX_QPATH ] int ofsFrames; // md4Frame_t[numFrames] // each level of detail has completely separate sets of surfaces int numLODs; int ofsLODs; int ofsEnd; // end of file } md4Header_t; /* ============================================================================== .BSP file format ============================================================================== */ #define BSP_IDENT (('P'<<24)+('S'<<16)+('B'<<8)+'I') // little-endian "IBSP" #define BSP_VERSION 46 // there shouldn't be any problem with increasing these values at the // expense of more memory allocation in the utilities #define MAX_MAP_MODELS 0x400 #define MAX_MAP_BRUSHES 0x8000 #define MAX_MAP_ENTITIES 0x800 #define MAX_MAP_ENTSTRING 0x40000 #define MAX_MAP_SHADERS 0x400 #define MAX_MAP_AREAS 0x100 // MAX_MAP_AREA_BYTES in q_shared must match! #define MAX_MAP_FOGS 0x100 #define MAX_MAP_PLANES 0x20000 #define MAX_MAP_NODES 0x20000 #define MAX_MAP_BRUSHSIDES 0x20000 #define MAX_MAP_LEAFS 0x20000 #define MAX_MAP_LEAFFACES 0x20000 #define MAX_MAP_LEAFBRUSHES 0x40000 #define MAX_MAP_PORTALS 0x20000 #define MAX_MAP_LIGHTING 0x800000 #define MAX_MAP_LIGHTGRID 0x800000 #define MAX_MAP_VISIBILITY 0x200000 #define MAX_MAP_DRAW_SURFS 0x20000 #define MAX_MAP_DRAW_VERTS 0x80000 #define MAX_MAP_DRAW_INDEXES 0x80000 // key / value pair sizes in the entities lump #define MAX_KEY 32 #define MAX_VALUE 1024 // the editor uses these predefined yaw angles to orient entities up or down #define ANGLE_UP -1 #define ANGLE_DOWN -2 #define LIGHTMAP_WIDTH 128 #define LIGHTMAP_HEIGHT 128 #define MAX_WORLD_COORD ( 128*1024 ) #define MIN_WORLD_COORD ( -128*1024 ) #define WORLD_SIZE ( MAX_WORLD_COORD - MIN_WORLD_COORD ) //============================================================================= typedef struct { int fileofs, filelen; } lump_t; #define LUMP_ENTITIES 0 #define LUMP_SHADERS 1 #define LUMP_PLANES 2 #define LUMP_NODES 3 #define LUMP_LEAFS 4 #define LUMP_LEAFSURFACES 5 #define LUMP_LEAFBRUSHES 6 #define LUMP_MODELS 7 #define LUMP_BRUSHES 8 #define LUMP_BRUSHSIDES 9 #define LUMP_DRAWVERTS 10 #define LUMP_DRAWINDEXES 11 #define LUMP_FOGS 12 #define LUMP_SURFACES 13 #define LUMP_LIGHTMAPS 14 #define LUMP_LIGHTGRID 15 #define LUMP_VISIBILITY 16 #define HEADER_LUMPS 17 typedef struct { int ident; int version; lump_t lumps[HEADER_LUMPS]; } dheader_t; typedef struct { float mins[3], maxs[3]; int firstSurface, numSurfaces; int firstBrush, numBrushes; } dmodel_t; typedef struct { char shader[MAX_QPATH]; int surfaceFlags; int contentFlags; } dshader_t; // planes x^1 is allways the opposite of plane x typedef struct { float normal[3]; float dist; } dplane_t; typedef struct { int planeNum; int children[2]; // negative numbers are -(leafs+1), not nodes int mins[3]; // for frustom culling int maxs[3]; } dnode_t; typedef struct { int cluster; // -1 = opaque cluster (do I still store these?) int area; int mins[3]; // for frustum culling int maxs[3]; int firstLeafSurface; int numLeafSurfaces; int firstLeafBrush; int numLeafBrushes; } dleaf_t; typedef struct { int planeNum; // positive plane side faces out of the leaf int shaderNum; } dbrushside_t; typedef struct { int firstSide; int numSides; int shaderNum; // the shader that determines the contents flags } dbrush_t; typedef struct { char shader[MAX_QPATH]; int brushNum; int visibleSide; // the brush side that ray tests need to clip against (-1 == none) } dfog_t; typedef struct { vec3_t xyz; float st[2]; float lightmap[2]; vec3_t normal; byte color[4]; } drawVert_t; typedef enum { MST_BAD, MST_PLANAR, MST_PATCH, MST_TRIANGLE_SOUP, MST_FLARE } mapSurfaceType_t; typedef struct { int shaderNum; int fogNum; int surfaceType; int firstVert; int numVerts; int firstIndex; int numIndexes; int lightmapNum; int lightmapX, lightmapY; int lightmapWidth, lightmapHeight; vec3_t lightmapOrigin; vec3_t lightmapVecs[3]; // for patches, [0] and [1] are lodbounds int patchWidth; int patchHeight; } dsurface_t; #endif ================================================ FILE: src/engine/qcommon/unzip.c ================================================ /***************************************************************************** * name: unzip.c * * desc: IO on .zip files using portions of zlib * * $Archive: /MissionPack/code/qcommon/unzip.c $ * *****************************************************************************/ #include "../client/client.h" #include "unzip.h" /* unzip.h -- IO for uncompress .zip files using zlib Version 0.15 beta, Mar 19th, 1998, Copyright (C) 1998 Gilles Vollant This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g WinZip, InfoZip tools and compatible. Encryption and multi volume ZipFile (span) are not supported. Old compressions used by old PKZip 1.x are not supported THIS IS AN ALPHA VERSION. AT THIS STAGE OF DEVELOPPEMENT, SOMES API OR STRUCTURE CAN CHANGE IN FUTURE VERSION !! I WAIT FEEDBACK at mail info@winimage.com Visit also http://www.winimage.com/zLibDll/unzip.htm for evolution Condition of use and distribution are the same than zlib : This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* for more info about .ZIP format, see ftp://ftp.cdrom.com/pub/infozip/doc/appnote-970311-iz.zip PkWare has also a specification at : ftp://ftp.pkware.com/probdesc.zip */ /* zlib.h -- interface of the 'zlib' general purpose compression library version 1.1.3, July 9th, 1998 Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Jean-loup Gailly Mark Adler jloup@gzip.org madler@alumni.caltech.edu The data format used by the zlib library is described by RFCs (Request for Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). */ /* zconf.h -- configuration of the zlib compression library * Copyright (C) 1995-1998 Jean-loup Gailly. * For conditions of distribution and use, see copyright notice in zlib.h */ #ifndef _ZCONF_H #define _ZCONF_H /* Maximum value for memLevel in deflateInit2 */ #ifndef MAX_MEM_LEVEL # ifdef MAXSEG_64K # define MAX_MEM_LEVEL 8 # else # define MAX_MEM_LEVEL 9 # endif #endif /* Maximum value for windowBits in deflateInit2 and inflateInit2. * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files * created by gzip. (Files created by minigzip can still be extracted by * gzip.) */ #ifndef MAX_WBITS # define MAX_WBITS 15 /* 32K LZ77 window */ #endif /* The memory requirements for deflate are (in bytes): (1 << (windowBits+2)) + (1 << (memLevel+9)) that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) plus a few kilobytes for small objects. For example, if you want to reduce the default memory requirements from 256K to 128K, compile with make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" Of course this will generally degrade compression (there's no free lunch). The memory requirements for inflate are (in bytes) 1 << windowBits that is, 32K for windowBits=15 (default value) plus a few kilobytes for small objects. */ /* Type declarations */ #ifndef OF /* function prototypes */ #define OF(args) args #endif typedef unsigned char Byte; /* 8 bits */ typedef unsigned int uInt; /* 16 bits or more */ typedef unsigned long uLong; /* 32 bits or more */ typedef Byte *voidp; #ifndef SEEK_SET # define SEEK_SET 0 /* Seek from beginning of file. */ # define SEEK_CUR 1 /* Seek from current position. */ # define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ #endif #endif /* _ZCONF_H */ #define ZLIB_VERSION "1.1.3" /* The 'zlib' compression library provides in-memory compression and decompression functions, including integrity checks of the uncompressed data. This version of the library supports only one compression method (deflation) but other algorithms will be added later and will have the same stream interface. Compression can be done in a single step if the buffers are large enough (for example if an input file is mmap'ed), or can be done by repeated calls of the compression function. In the latter case, the application must provide more input and/or consume the output (providing more output space) before each call. The library also supports reading and writing files in gzip (.gz) format with an interface similar to that of stdio. The library does not install any signal handler. The decoder checks the consistency of the compressed data, so the library should never crash even in case of corrupted input. */ /* The application must update next_in and avail_in when avail_in has dropped to zero. It must update next_out and avail_out when avail_out has dropped to zero. The application must initialize zalloc, zfree and opaque before calling the init function. All other fields are set by the compression library and must not be updated by the application. The opaque value provided by the application will be passed as the first parameter for calls of zalloc and zfree. This can be useful for custom memory management. The compression library attaches no meaning to the opaque value. zalloc must return Z_NULL if there is not enough memory for the object. If zlib is used in a multi-threaded application, zalloc and zfree must be thread safe. On 16-bit systems, the functions zalloc and zfree must be able to allocate exactly 65536 bytes, but will not be required to allocate more than this if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers returned by zalloc for objects of exactly 65536 bytes *must* have their offset normalized to zero. The default allocation function provided by this library ensures this (see zutil.c). To reduce memory requirements and avoid any allocation of 64K objects, at the expense of compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). The fields total_in and total_out can be used for statistics or progress reports. After compression, total_in holds the total size of the uncompressed data and may be saved for use in the decompressor (particularly if the decompressor wants to decompress everything in a single step). */ /* constants */ #define Z_NO_FLUSH 0 #define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */ #define Z_SYNC_FLUSH 2 #define Z_FULL_FLUSH 3 #define Z_FINISH 4 /* Allowed flush values; see deflate() below for details */ #define Z_OK 0 #define Z_STREAM_END 1 #define Z_NEED_DICT 2 #define Z_ERRNO (-1) #define Z_STREAM_ERROR (-2) #define Z_DATA_ERROR (-3) #define Z_MEM_ERROR (-4) #define Z_BUF_ERROR (-5) #define Z_VERSION_ERROR (-6) /* Return codes for the compression/decompression functions. Negative * values are errors, positive values are used for special but normal events. */ #define Z_NO_COMPRESSION 0 #define Z_BEST_SPEED 1 #define Z_BEST_COMPRESSION 9 #define Z_DEFAULT_COMPRESSION (-1) /* compression levels */ #define Z_FILTERED 1 #define Z_HUFFMAN_ONLY 2 #define Z_DEFAULT_STRATEGY 0 /* compression strategy; see deflateInit2() below for details */ #define Z_BINARY 0 #define Z_ASCII 1 #define Z_UNKNOWN 2 /* Possible values of the data_type field */ #define Z_DEFLATED 8 /* The deflate compression method (the only one supported in this version) */ #define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ #define zlib_version zlibVersion() /* for compatibility with versions < 1.0.2 */ /* basic functions */ // static const char * zlibVersion OF((void)); /* The application can compare zlibVersion and ZLIB_VERSION for consistency. If the first character differs, the library code actually used is not compatible with the zlib.h header file used by the application. This check is automatically made by deflateInit and inflateInit. */ /* int deflateInit OF((z_streamp strm, int level)); Initializes the internal stream state for compression. The fields zalloc, zfree and opaque must be initialized before by the caller. If zalloc and zfree are set to Z_NULL, deflateInit updates them to use default allocation functions. The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: 1 gives best speed, 9 gives best compression, 0 gives no compression at all (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION requests a default compromise between speed and compression (currently equivalent to level 6). deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if level is not a valid compression level, Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible with the version assumed by the caller (ZLIB_VERSION). msg is set to null if there is no error message. deflateInit does not perform any compression: this will be done by deflate(). */ // static int deflate OF((z_streamp strm, int flush)); /* deflate compresses as much data as possible, and stops when the input buffer becomes empty or the output buffer becomes full. It may introduce some output latency (reading input without producing any output) except when forced to flush. The detailed semantics are as follows. deflate performs one or both of the following actions: - Compress more input starting at next_in and update next_in and avail_in accordingly. If not all input can be processed (because there is not enough room in the output buffer), next_in and avail_in are updated and processing will resume at this point for the next call of deflate(). - Provide more output starting at next_out and update next_out and avail_out accordingly. This action is forced if the parameter flush is non zero. Forcing flush frequently degrades the compression ratio, so this parameter should be set only when necessary (in interactive applications). Some output may be provided even if flush is not set. Before the call of deflate(), the application should ensure that at least one of the actions is possible, by providing more input and/or consuming more output, and updating avail_in or avail_out accordingly; avail_out should never be zero before the call. The application can consume the compressed output when it wants, for example when the output buffer is full (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK and with zero avail_out, it must be called again after making room in the output buffer because there might be more output pending. If the parameter flush is set to Z_SYNC_FLUSH, all pending output is flushed to the output buffer and the output is aligned on a byte boundary, so that the decompressor can get all input data available so far. (In particular avail_in is zero after the call if enough output space has been provided before the call.) Flushing may degrade compression for some compression algorithms and so it should be used only when necessary. If flush is set to Z_FULL_FLUSH, all output is flushed as with Z_SYNC_FLUSH, and the compression state is reset so that decompression can restart from this point if previous compressed data has been damaged or if random access is desired. Using Z_FULL_FLUSH too often can seriously degrade the compression. If deflate returns with avail_out == 0, this function must be called again with the same value of the flush parameter and more output space (updated avail_out), until the flush is complete (deflate returns with non-zero avail_out). If the parameter flush is set to Z_FINISH, pending input is processed, pending output is flushed and deflate returns with Z_STREAM_END if there was enough output space; if deflate returns with Z_OK, this function must be called again with Z_FINISH and more output space (updated avail_out) but no more input data, until it returns with Z_STREAM_END or an error. After deflate has returned Z_STREAM_END, the only possible operations on the stream are deflateReset or deflateEnd. Z_FINISH can be used immediately after deflateInit if all the compression is to be done in a single step. In this case, avail_out must be at least 0.1% larger than avail_in plus 12 bytes. If deflate does not return Z_STREAM_END, then it must be called again as described above. deflate() sets strm->adler to the adler32 checksum of all input read so (that is, total_in bytes). deflate() may update data_type if it can make a good guess about the input data type (Z_ASCII or Z_BINARY). In doubt, the data is considered binary. This field is only for information purposes and does not affect the compression algorithm in any manner. deflate() returns Z_OK if some progress has been made (more input processed or more output produced), Z_STREAM_END if all input has been consumed and all output has been produced (only when flush is set to Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible (for example avail_in or avail_out was zero). */ // static int deflateEnd OF((z_streamp strm)); /* All dynamically allocated data structures for this stream are freed. This function discards any unprocessed input and does not flush any pending output. deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state was inconsistent, Z_DATA_ERROR if the stream was freed prematurely (some input or output was discarded). In the error case, msg may be set but then points to a static string (which must not be deallocated). */ /* int inflateInit OF((z_streamp strm)); Initializes the internal stream state for decompression. The fields next_in, avail_in, zalloc, zfree and opaque must be initialized before by the caller. If next_in is not Z_NULL and avail_in is large enough (the exact value depends on the compression method), inflateInit determines the compression method from the zlib header and allocates all data structures accordingly; otherwise the allocation will be deferred to the first call of inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to use default allocation functions. inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_VERSION_ERROR if the zlib library version is incompatible with the version assumed by the caller. msg is set to null if there is no error message. inflateInit does not perform any decompression apart from reading the zlib header if present: this will be done by inflate(). (So next_in and avail_in may be modified, but next_out and avail_out are unchanged.) */ static int inflate OF((z_streamp strm, int flush)); /* inflate decompresses as much data as possible, and stops when the input buffer becomes empty or the output buffer becomes full. It may some introduce some output latency (reading input without producing any output) except when forced to flush. The detailed semantics are as follows. inflate performs one or both of the following actions: - Decompress more input starting at next_in and update next_in and avail_in accordingly. If not all input can be processed (because there is not enough room in the output buffer), next_in is updated and processing will resume at this point for the next call of inflate(). - Provide more output starting at next_out and update next_out and avail_out accordingly. inflate() provides as much output as possible, until there is no more input data or no more space in the output buffer (see below about the flush parameter). Before the call of inflate(), the application should ensure that at least one of the actions is possible, by providing more input and/or consuming more output, and updating the next_* and avail_* values accordingly. The application can consume the uncompressed output when it wants, for example when the output buffer is full (avail_out == 0), or after each call of inflate(). If inflate returns Z_OK and with zero avail_out, it must be called again after making room in the output buffer because there might be more output pending. If the parameter flush is set to Z_SYNC_FLUSH, inflate flushes as much output as possible to the output buffer. The flushing behavior of inflate is not specified for values of the flush parameter other than Z_SYNC_FLUSH and Z_FINISH, but the current implementation actually flushes as much output as possible anyway. inflate() should normally be called until it returns Z_STREAM_END or an error. However if all decompression is to be performed in a single step (a single call of inflate), the parameter flush should be set to Z_FINISH. In this case all pending input is processed and all pending output is flushed; avail_out must be large enough to hold all the uncompressed data. (The size of the uncompressed data may have been saved by the compressor for this purpose.) The next operation on this stream must be inflateEnd to deallocate the decompression state. The use of Z_FINISH is never required, but can be used to inform inflate that a faster routine may be used for the single inflate() call. If a preset dictionary is needed at this point (see inflateSetDictionary below), inflate sets strm-adler to the adler32 checksum of the dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise it sets strm->adler to the adler32 checksum of all output produced so (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described below. At the end of the stream, inflate() checks that its computed adler32 checksum is equal to that saved by the compressor and returns Z_STREAM_END only if the checksum is correct. inflate() returns Z_OK if some progress has been made (more input processed or more output produced), Z_STREAM_END if the end of the compressed data has been reached and all uncompressed output has been produced, Z_NEED_DICT if a preset dictionary is needed at this point, Z_DATA_ERROR if the input data was corrupted (input stream not conforming to the zlib format or incorrect adler32 checksum), Z_STREAM_ERROR if the stream structure was inconsistent (for example if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if no progress is possible or if there was not enough room in the output buffer when Z_FINISH is used. In the Z_DATA_ERROR case, the application may then call inflateSync to look for a good compression block. */ static int inflateEnd OF((z_streamp strm)); /* All dynamically allocated data structures for this stream are freed. This function discards any unprocessed input and does not flush any pending output. inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state was inconsistent. In the error case, msg may be set but then points to a static string (which must not be deallocated). */ /* Advanced functions */ /* The following functions are needed only in some special applications. */ /* int deflateInit2 OF((z_streamp strm, int level, int method, int windowBits, int memLevel, int strategy)); This is another version of deflateInit with more compression options. The fields next_in, zalloc, zfree and opaque must be initialized before by the caller. The method parameter is the compression method. It must be Z_DEFLATED in this version of the library. The windowBits parameter is the base two logarithm of the window size (the size of the history buffer). It should be in the range 8..15 for this version of the library. Larger values of this parameter result in better compression at the expense of memory usage. The default value is 15 if deflateInit is used instead. The memLevel parameter specifies how much memory should be allocated for the internal compression state. memLevel=1 uses minimum memory but is slow and reduces compression ratio; memLevel=9 uses maximum memory for optimal speed. The default value is 8. See zconf.h for total memory usage as a function of windowBits and memLevel. The strategy parameter is used to tune the compression algorithm. Use the value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a filter (or predictor), or Z_HUFFMAN_ONLY to force Huffman encoding only (no string match). Filtered data consists mostly of small values with a somewhat random distribution. In this case, the compression algorithm is tuned to compress them better. The effect of Z_FILTERED is to force more Huffman coding and less string matching; it is somewhat intermediate between Z_DEFAULT and Z_HUFFMAN_ONLY. The strategy parameter only affects the compression ratio but not the correctness of the compressed output even if it is not set appropriately. deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid method). msg is set to null if there is no error message. deflateInit2 does not perform any compression: this will be done by deflate(). */ /* static int deflateSetDictionary OF((z_streamp strm, const Byte *dictionary, uInt dictLength)); */ /* Initializes the compression dictionary from the given byte sequence without producing any compressed output. This function must be called immediately after deflateInit, deflateInit2 or deflateReset, before any call of deflate. The compressor and decompressor must use exactly the same dictionary (see inflateSetDictionary). The dictionary should consist of strings (byte sequences) that are likely to be encountered later in the data to be compressed, with the most commonly used strings preferably put towards the end of the dictionary. Using a dictionary is most useful when the data to be compressed is short and can be predicted with good accuracy; the data can then be compressed better than with the default empty dictionary. Depending on the size of the compression data structures selected by deflateInit or deflateInit2, a part of the dictionary may in effect be discarded, for example if the dictionary is larger than the window size in deflate or deflate2. Thus the strings most likely to be useful should be put at the end of the dictionary, not at the front. Upon return of this function, strm->adler is set to the Adler32 value of the dictionary; the decompressor may later use this value to determine which dictionary has been used by the compressor. (The Adler32 value applies to the whole dictionary even if only a subset of the dictionary is actually used by the compressor.) deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a parameter is invalid (such as NULL dictionary) or the stream state is inconsistent (for example if deflate has already been called for this stream or if the compression method is bsort). deflateSetDictionary does not perform any compression: this will be done by deflate(). */ /* static int deflateCopy OF((z_streamp dest, z_streamp source)); */ /* Sets the destination stream as a complete copy of the source stream. This function can be useful when several compression strategies will be tried, for example when there are several ways of pre-processing the input data with a filter. The streams that will be discarded should then be freed by calling deflateEnd. Note that deflateCopy duplicates the internal compression state which can be quite large, so this strategy is slow and can consume lots of memory. deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc being NULL). msg is left unchanged in both source and destination. */ // static int deflateReset OF((z_streamp strm)); /* This function is equivalent to deflateEnd followed by deflateInit, but does not free and reallocate all the internal compression state. The stream will keep the same compression level and any other attributes that may have been set by deflateInit2. deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc or state being NULL). */ /* static int deflateParams OF((z_streamp strm, int level, int strategy)); */ /* Dynamically update the compression level and compression strategy. The interpretation of level and strategy is as in deflateInit2. This can be used to switch between compression and straight copy of the input data, or to switch to a different kind of input data requiring a different strategy. If the compression level is changed, the input available so far is compressed with the old level (and may be flushed); the new level will take effect only at the next call of deflate(). Before the call of deflateParams, the stream state must be set as for a call of deflate(), since the currently available input may have to be compressed and flushed. In particular, strm->avail_out must be non-zero. deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR if strm->avail_out was zero. */ /* int inflateInit2 OF((z_streamp strm, int windowBits)); This is another version of inflateInit with an extra parameter. The fields next_in, avail_in, zalloc, zfree and opaque must be initialized before by the caller. The windowBits parameter is the base two logarithm of the maximum window size (the size of the history buffer). It should be in the range 8..15 for this version of the library. The default value is 15 if inflateInit is used instead. If a compressed stream with a larger window size is given as input, inflate() will return with the error code Z_DATA_ERROR instead of trying to allocate a larger window. inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if a parameter is invalid (such as a negative memLevel). msg is set to null if there is no error message. inflateInit2 does not perform any decompression apart from reading the zlib header if present: this will be done by inflate(). (So next_in and avail_in may be modified, but next_out and avail_out are unchanged.) */ /* static int inflateSetDictionary OF((z_streamp strm, const Byte *dictionary, uInt dictLength)); */ /* Initializes the decompression dictionary from the given uncompressed byte sequence. This function must be called immediately after a call of inflate if this call returned Z_NEED_DICT. The dictionary chosen by the compressor can be determined from the Adler32 value returned by this call of inflate. The compressor and decompressor must use exactly the same dictionary (see deflateSetDictionary). inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a parameter is invalid (such as NULL dictionary) or the stream state is inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the expected one (incorrect Adler32 value). inflateSetDictionary does not perform any decompression: this will be done by subsequent calls of inflate(). */ // static int inflateSync OF((z_streamp strm)); /* Skips invalid compressed data until a full flush point (see above the description of deflate with Z_FULL_FLUSH) can be found, or until all available input is skipped. No output is provided. inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point has been found, or Z_STREAM_ERROR if the stream structure was inconsistent. In the success case, the application may save the current current value of total_in which indicates where valid compressed data was found. In the error case, the application may repeatedly call inflateSync, providing more input each time, until success or end of the input data. */ static int inflateReset OF((z_streamp strm)); /* This function is equivalent to inflateEnd followed by inflateInit, but does not free and reallocate all the internal decompression state. The stream will keep attributes that may have been set by inflateInit2. inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc or state being NULL). */ /* utility functions */ /* The following utility functions are implemented on top of the basic stream-oriented functions. To simplify the interface, some default options are assumed (compression level and memory usage, standard memory allocation functions). The source code of these utility functions can easily be modified if you need special options. */ /* static int compress OF((Byte *dest, uLong *destLen, const Byte *source, uLong sourceLen)); */ /* Compresses the source buffer into the destination buffer. sourceLen is the byte length of the source buffer. Upon entry, destLen is the total size of the destination buffer, which must be at least 0.1% larger than sourceLen plus 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. This function can be used to compress a whole file at once if the input file is mmap'ed. compress returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output buffer. */ /* static int compress2 OF((Byte *dest, uLong *destLen, const Byte *source, uLong sourceLen, int level)); */ /* Compresses the source buffer into the destination buffer. The level parameter has the same meaning as in deflateInit. sourceLen is the byte length of the source buffer. Upon entry, destLen is the total size of the destination buffer, which must be at least 0.1% larger than sourceLen plus 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output buffer, Z_STREAM_ERROR if the level parameter is invalid. */ /* static int uncompress OF((Byte *dest, uLong *destLen, const Byte *source, uLong sourceLen)); */ /* Decompresses the source buffer into the destination buffer. sourceLen is the byte length of the source buffer. Upon entry, destLen is the total size of the destination buffer, which must be large enough to hold the entire uncompressed data. (The size of the uncompressed data must have been saved previously by the compressor and transmitted to the decompressor by some mechanism outside the scope of this compression library.) Upon exit, destLen is the actual size of the compressed buffer. This function can be used to decompress a whole file at once if the input file is mmap'ed. uncompress returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output buffer, or Z_DATA_ERROR if the input data was corrupted. */ typedef voidp gzFile; gzFile gzopen OF((const char *path, const char *mode)); /* Opens a gzip (.gz) file for reading or writing. The mode parameter is as in fopen ("rb" or "wb") but can also include a compression level ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman only compression as in "wb1h". (See the description of deflateInit2 for more information about the strategy parameter.) gzopen can be used to read a file which is not in gzip format; in this case gzread will directly read from the file without decompression. gzopen returns NULL if the file could not be opened or if there was insufficient memory to allocate the (de)compression state; errno can be checked to distinguish the two cases (if errno is zero, the zlib error is Z_MEM_ERROR). */ gzFile gzdopen OF((int fd, const char *mode)); /* gzdopen() associates a gzFile with the file descriptor fd. File descriptors are obtained from calls like open, dup, creat, pipe or fileno (in the file has been previously opened with fopen). The mode parameter is as in gzopen. The next call of gzclose on the returned gzFile will also close the file descriptor fd, just like fclose(fdopen(fd), mode) closes the file descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). gzdopen returns NULL if there was insufficient memory to allocate the (de)compression state. */ int gzsetparams OF((gzFile file, int level, int strategy)); /* Dynamically update the compression level or strategy. See the description of deflateInit2 for the meaning of these parameters. gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not opened for writing. */ int gzread OF((gzFile file, voidp buf, unsigned len)); /* Reads the given number of uncompressed bytes from the compressed file. If the input file was not in gzip format, gzread copies the given number of bytes into the buffer. gzread returns the number of uncompressed bytes actually read (0 for end of file, -1 for error). */ int gzwrite OF((gzFile file, const voidp buf, unsigned len)); /* Writes the given number of uncompressed bytes into the compressed file. gzwrite returns the number of uncompressed bytes actually written (0 in case of error). */ int QDECL gzprintf OF((gzFile file, const char *format, ...)); /* Converts, formats, and writes the args to the compressed file under control of the format string, as in fprintf. gzprintf returns the number of uncompressed bytes actually written (0 in case of error). */ int gzputs OF((gzFile file, const char *s)); /* Writes the given null-terminated string to the compressed file, excluding the terminating null character. gzputs returns the number of characters written, or -1 in case of error. */ char * gzgets OF((gzFile file, char *buf, int len)); /* Reads bytes from the compressed file until len-1 characters are read, or a newline character is read and transferred to buf, or an end-of-file condition is encountered. The string is then terminated with a null character. gzgets returns buf, or Z_NULL in case of error. */ int gzputc OF((gzFile file, int c)); /* Writes c, converted to an unsigned char, into the compressed file. gzputc returns the value that was written, or -1 in case of error. */ int gzgetc OF((gzFile file)); /* Reads one byte from the compressed file. gzgetc returns this byte or -1 in case of end of file or error. */ int gzflush OF((gzFile file, int flush)); /* Flushes all pending output into the compressed file. The parameter flush is as in the deflate() function. The return value is the zlib error number (see function gzerror below). gzflush returns Z_OK if the flush parameter is Z_FINISH and all output could be flushed. gzflush should be called only when strictly necessary because it can degrade compression. */ long gzseek OF((gzFile file, long offset, int whence)); /* Sets the starting position for the next gzread or gzwrite on the given compressed file. The offset represents a number of bytes in the uncompressed data stream. The whence parameter is defined as in lseek(2); the value SEEK_END is not supported. If the file is opened for reading, this function is emulated but can be extremely slow. If the file is opened for writing, only forward seeks are supported; gzseek then compresses a sequence of zeroes up to the new starting position. gzseek returns the resulting offset location as measured in bytes from the beginning of the uncompressed stream, or -1 in case of error, in particular if the file is opened for writing and the new starting position would be before the current position. */ int gzrewind OF((gzFile file)); /* Rewinds the given file. This function is supported only for reading. gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) */ long gztell OF((gzFile file)); /* Returns the starting position for the next gzread or gzwrite on the given compressed file. This position represents a number of bytes in the uncompressed data stream. gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) */ int gzeof OF((gzFile file)); /* Returns 1 when EOF has previously been detected reading the given input stream, otherwise zero. */ int gzclose OF((gzFile file)); /* Flushes all pending output if necessary, closes the compressed file and deallocates all the (de)compression state. The return value is the zlib error number (see function gzerror below). */ // static const char * gzerror OF((gzFile file, int *errnum)); /* Returns the error message for the last error which occurred on the given compressed file. errnum is set to zlib error number. If an error occurred in the file system and not in the compression library, errnum is set to Z_ERRNO and the application may consult errno to get the exact error code. */ /* checksum functions */ /* These functions are not related to compression but are exported anyway because they might be useful in applications using the compression library. */ static uLong adler32 OF((uLong adler, const Byte *buf, uInt len)); /* Update a running Adler-32 checksum with the bytes buf[0..len-1] and return the updated checksum. If buf is NULL, this function returns the required initial value for the checksum. An Adler-32 checksum is almost as reliable as a CRC32 but can be computed much faster. Usage example: uLong adler = adler32(0L, Z_NULL, 0); while (read_buffer(buffer, length) != EOF) { adler = adler32(adler, buffer, length); } if (adler != original_adler) error(); */ /* various hacks, don't look :) */ /* deflateInit and inflateInit are macros to allow checking the zlib version * and the compiler's view of z_stream: */ /* static int deflateInit_ OF((z_streamp strm, int level, const char *version, int stream_size)); static int inflateInit_ OF((z_streamp strm, const char *version, int stream_size)); static int deflateInit2_ OF((z_streamp strm, int level, int method, int windowBits, int memLevel, int strategy, const char *version, int stream_size)); */ static int inflateInit2_ OF((z_streamp strm, int windowBits, const char *version, int stream_size)); #define deflateInit(strm, level) \ deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) #define inflateInit(strm) \ inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) #define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ (strategy), ZLIB_VERSION, sizeof(z_stream)) #define inflateInit2(strm, windowBits) \ inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) // static const char * zError OF((int err)); // static int inflateSyncPoint OF((z_streamp z)); // static const uLong * get_crc_table OF((void)); typedef unsigned char uch; typedef unsigned short ush; typedef unsigned long ulg; // static const char *z_errmsg[10]; /* indexed by 2-zlib_error */ /* (size given to avoid silly warnings with Visual C++) */ #define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] #define ERR_RETURN(strm,err) \ return (strm->msg = (char*)ERR_MSG(err), (err)) /* To be used only when the state is known to be valid */ /* common constants */ #ifndef DEF_WBITS # define DEF_WBITS MAX_WBITS #endif /* default windowBits for decompression. MAX_WBITS is for compression only */ #if MAX_MEM_LEVEL >= 8 # define DEF_MEM_LEVEL 8 #else # define DEF_MEM_LEVEL MAX_MEM_LEVEL #endif /* default memLevel */ #define STORED_BLOCK 0 #define STATIC_TREES 1 #define DYN_TREES 2 /* The three kinds of block type */ #define MIN_MATCH 3 #define MAX_MATCH 258 /* The minimum and maximum match lengths */ #define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ /* target dependencies */ /* Common defaults */ #ifndef OS_CODE # define OS_CODE 0x03 /* assume Unix */ #endif #ifndef F_OPEN # define F_OPEN(name, mode) fopen((name), (mode)) #endif /* functions */ #ifdef HAVE_STRERROR extern char *strerror OF((int)); # define zstrerror(errnum) strerror(errnum) #else # define zstrerror(errnum) "" #endif #define zmemcpy Com_Memcpy #define zmemcmp memcmp #define zmemzero(dest, len) Com_Memset(dest, 0, len) /* Diagnostic functions */ #ifdef _ZIP_DEBUG_ int z_verbose = 0; # define Assert(cond,msg) assert(cond); //{if(!(cond)) Sys_Error(msg);} # define Trace(x) {if (z_verbose>=0) Sys_Error x ;} # define Tracev(x) {if (z_verbose>0) Sys_Error x ;} # define Tracevv(x) {if (z_verbose>1) Sys_Error x ;} # define Tracec(c,x) {if (z_verbose>0 && (c)) Sys_Error x ;} # define Tracecv(c,x) {if (z_verbose>1 && (c)) Sys_Error x ;} #else # define Assert(cond,msg) # define Trace(x) # define Tracev(x) # define Tracevv(x) # define Tracec(c,x) # define Tracecv(c,x) #endif typedef uLong (*check_func) OF((uLong check, const Byte *buf, uInt len)); static voidp zcalloc OF((voidp opaque, unsigned items, unsigned size)); static void zcfree OF((voidp opaque, voidp ptr)); #define ZALLOC(strm, items, size) \ (*((strm)->zalloc))((strm)->opaque, (items), (size)) #define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidp)(addr)) #define TRY_FREE(s, p) {if (p) ZFREE(s, p);} #if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) && \ !defined(CASESENSITIVITYDEFAULT_NO) #define CASESENSITIVITYDEFAULT_NO #endif #ifndef UNZ_BUFSIZE #define UNZ_BUFSIZE (65536) #endif #ifndef UNZ_MAXFILENAMEINZIP #define UNZ_MAXFILENAMEINZIP (256) #endif #ifndef ALLOC # define ALLOC(size) (Z_Malloc(size)) #endif #ifndef TRYFREE # define TRYFREE(p) {if (p) Z_Free(p);} #endif #define SIZECENTRALDIRITEM (0x2e) #define SIZEZIPLOCALHEADER (0x1e) /* =========================================================================== Read a byte from a gz_stream; update next_in and avail_in. Return EOF for end of file. IN assertion: the stream s has been sucessfully opened for reading. */ /* static int unzlocal_getByte(FILE *fin,int *pi) { unsigned char c; int err = fread(&c, 1, 1, fin); if (err==1) { *pi = (int)c; return UNZ_OK; } else { if (ferror(fin)) return UNZ_ERRNO; else return UNZ_EOF; } } */ /* =========================================================================== Reads a long in LSB order from the given gz_stream. Sets */ static int unzlocal_getShort (FILE* fin, uLong *pX) { short v; fread( &v, sizeof(v), 1, fin ); *pX = LittleShort( v); return UNZ_OK; /* uLong x ; int i; int err; err = unzlocal_getByte(fin,&i); x = (uLong)i; if (err==UNZ_OK) err = unzlocal_getByte(fin,&i); x += ((uLong)i)<<8; if (err==UNZ_OK) *pX = x; else *pX = 0; return err; */ } static int unzlocal_getLong (FILE *fin, uLong *pX) { int v; fread( &v, sizeof(v), 1, fin ); *pX = LittleLong( v); return UNZ_OK; /* uLong x ; int i; int err; err = unzlocal_getByte(fin,&i); x = (uLong)i; if (err==UNZ_OK) err = unzlocal_getByte(fin,&i); x += ((uLong)i)<<8; if (err==UNZ_OK) err = unzlocal_getByte(fin,&i); x += ((uLong)i)<<16; if (err==UNZ_OK) err = unzlocal_getByte(fin,&i); x += ((uLong)i)<<24; if (err==UNZ_OK) *pX = x; else *pX = 0; return err; */ } /* My own strcmpi / strcasecmp */ static int strcmpcasenosensitive_internal (const char* fileName1,const char* fileName2) { for (;;) { char c1=*(fileName1++); char c2=*(fileName2++); if ((c1>='a') && (c1<='z')) c1 -= 0x20; if ((c2>='a') && (c2<='z')) c2 -= 0x20; if (c1=='\0') return ((c2=='\0') ? 0 : -1); if (c2=='\0') return 1; if (c1c2) return 1; } } #ifdef CASESENSITIVITYDEFAULT_NO #define CASESENSITIVITYDEFAULTVALUE 2 #else #define CASESENSITIVITYDEFAULTVALUE 1 #endif #ifndef STRCMPCASENOSENTIVEFUNCTION #define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal #endif /* Compare two filename (fileName1,fileName2). If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi or strcasecmp) If iCaseSenisivity = 0, case sensitivity is defaut of your operating system (like 1 on Unix, 2 on Windows) */ extern int unzStringFileNameCompare (const char* fileName1,const char* fileName2,int iCaseSensitivity) { if (iCaseSensitivity==0) iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; if (iCaseSensitivity==1) return strcmp(fileName1,fileName2); return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); } #define BUFREADCOMMENT (0x400) /* Locate the Central directory of a zipfile (at the end, just before the global comment) */ extern uLong unzlocal_SearchCentralDir(FILE *fin) { unsigned char* buf; uLong uSizeFile; uLong uBackRead; uLong uMaxBack=0xffff; /* maximum size of global comment */ uLong uPosFound=0; if (fseek(fin,0,SEEK_END) != 0) return 0; uSizeFile = ftell( fin ); if (uMaxBack>uSizeFile) uMaxBack = uSizeFile; buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); if (buf==NULL) return 0; uBackRead = 4; while (uBackReaduMaxBack) uBackRead = uMaxBack; else uBackRead+=BUFREADCOMMENT; uReadPos = uSizeFile-uBackRead ; uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); if (fseek(fin,uReadPos,SEEK_SET)!=0) break; if (fread(buf,(uInt)uReadSize,1,fin)!=1) break; for (i=(int)uReadSize-3; (i--)>0;) if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) { uPosFound = uReadPos+i; break; } if (uPosFound!=0) break; } TRYFREE(buf); return uPosFound; } extern unzFile unzReOpen (const char* path, unzFile file) { unz_s *s; FILE * fin; fin=fopen(path,"rb"); if (fin==NULL) return NULL; s=(unz_s*)ALLOC(sizeof(unz_s)); Com_Memcpy(s, (unz_s*)file, sizeof(unz_s)); s->file = fin; return (unzFile)s; } /* Open a Zip file. path contain the full pathname (by example, on a Windows NT computer "c:\\test\\zlib109.zip" or on an Unix computer "zlib/zlib109.zip". If the zipfile cannot be opened (file don't exist or in not valid), the return value is NULL. Else, the return value is a unzFile Handle, usable with other function of this unzip package. */ extern unzFile unzOpen (const char* path) { unz_s us; unz_s *s; uLong central_pos,uL; FILE * fin ; uLong number_disk; /* number of the current dist, used for spaning ZIP, unsupported, always 0*/ uLong number_disk_with_CD; /* number the the disk with central dir, used for spaning ZIP, unsupported, always 0*/ uLong number_entry_CD; /* total number of entries in the central dir (same than number_entry on nospan) */ int err=UNZ_OK; fin=fopen(path,"rb"); if (fin==NULL) return NULL; central_pos = unzlocal_SearchCentralDir(fin); if (central_pos==0) err=UNZ_ERRNO; if (fseek(fin,central_pos,SEEK_SET)!=0) err=UNZ_ERRNO; /* the signature, already checked */ if (unzlocal_getLong(fin,&uL)!=UNZ_OK) err=UNZ_ERRNO; /* number of this disk */ if (unzlocal_getShort(fin,&number_disk)!=UNZ_OK) err=UNZ_ERRNO; /* number of the disk with the start of the central directory */ if (unzlocal_getShort(fin,&number_disk_with_CD)!=UNZ_OK) err=UNZ_ERRNO; /* total number of entries in the central dir on this disk */ if (unzlocal_getShort(fin,&us.gi.number_entry)!=UNZ_OK) err=UNZ_ERRNO; /* total number of entries in the central dir */ if (unzlocal_getShort(fin,&number_entry_CD)!=UNZ_OK) err=UNZ_ERRNO; if ((number_entry_CD!=us.gi.number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) err=UNZ_BADZIPFILE; /* size of the central directory */ if (unzlocal_getLong(fin,&us.size_central_dir)!=UNZ_OK) err=UNZ_ERRNO; /* offset of start of central directory with respect to the starting disk number */ if (unzlocal_getLong(fin,&us.offset_central_dir)!=UNZ_OK) err=UNZ_ERRNO; /* zipfile comment length */ if (unzlocal_getShort(fin,&us.gi.size_comment)!=UNZ_OK) err=UNZ_ERRNO; if ((central_pospfile_in_zip_read!=NULL) unzCloseCurrentFile(file); fclose(s->file); TRYFREE(s); return UNZ_OK; } /* Write info about the ZipFile in the *pglobal_info structure. No preparation of the structure is needed return UNZ_OK if there is no problem. */ extern int unzGetGlobalInfo (unzFile file,unz_global_info *pglobal_info) { unz_s* s; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; *pglobal_info=s->gi; return UNZ_OK; } /* Translate date/time from Dos format to tm_unz (readable more easilty) */ static void unzlocal_DosDateToTmuDate (uLong ulDosDate, tm_unz* ptm) { uLong uDate; uDate = (uLong)(ulDosDate>>16); ptm->tm_mday = (uInt)(uDate&0x1f) ; ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; } /* Get Info about the current file in the zipfile, with internal only info */ static int unzlocal_GetCurrentFileInfoInternal (unzFile file, unz_file_info *pfile_info, unz_file_info_internal *pfile_info_internal, char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char *szComment, uLong commentBufferSize) { unz_s* s; unz_file_info file_info; unz_file_info_internal file_info_internal; int err=UNZ_OK; uLong uMagic; long lSeek=0; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; if (fseek(s->file,s->pos_in_central_dir+s->byte_before_the_zipfile,SEEK_SET)!=0) err=UNZ_ERRNO; /* we check the magic */ if (err==UNZ_OK) { if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) err=UNZ_ERRNO; else if (uMagic!=0x02014b50) err=UNZ_BADZIPFILE; } if (unzlocal_getShort(s->file,&file_info.version) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(s->file,&file_info.version_needed) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(s->file,&file_info.flag) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(s->file,&file_info.compression_method) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getLong(s->file,&file_info.dosDate) != UNZ_OK) err=UNZ_ERRNO; unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); if (unzlocal_getLong(s->file,&file_info.crc) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getLong(s->file,&file_info.compressed_size) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getLong(s->file,&file_info.uncompressed_size) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(s->file,&file_info.size_filename) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(s->file,&file_info.size_file_extra) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(s->file,&file_info.size_file_comment) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(s->file,&file_info.disk_num_start) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(s->file,&file_info.internal_fa) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getLong(s->file,&file_info.external_fa) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getLong(s->file,&file_info_internal.offset_curfile) != UNZ_OK) err=UNZ_ERRNO; lSeek+=file_info.size_filename; if ((err==UNZ_OK) && (szFileName!=NULL)) { uLong uSizeRead ; if (file_info.size_filename0) && (fileNameBufferSize>0)) if (fread(szFileName,(uInt)uSizeRead,1,s->file)!=1) err=UNZ_ERRNO; lSeek -= uSizeRead; } if ((err==UNZ_OK) && (extraField!=NULL)) { uLong uSizeRead ; if (file_info.size_file_extrafile,lSeek,SEEK_CUR)==0) lSeek=0; else err=UNZ_ERRNO; } if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) { if (fread(extraField,(uInt)uSizeRead,1,s->file)!=1) err=UNZ_ERRNO; } lSeek += file_info.size_file_extra - uSizeRead; } else lSeek+=file_info.size_file_extra; if ((err==UNZ_OK) && (szComment!=NULL)) { uLong uSizeRead ; if (file_info.size_file_commentfile,lSeek,SEEK_CUR)==0) lSeek=0; else err=UNZ_ERRNO; } if ((file_info.size_file_comment>0) && (commentBufferSize>0)) { if (fread(szComment,(uInt)uSizeRead,1,s->file)!=1) err=UNZ_ERRNO; } lSeek+=file_info.size_file_comment - uSizeRead; } else lSeek+=file_info.size_file_comment; if ((err==UNZ_OK) && (pfile_info!=NULL)) *pfile_info=file_info; if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) *pfile_info_internal=file_info_internal; return err; } /* Write info about the ZipFile in the *pglobal_info structure. No preparation of the structure is needed return UNZ_OK if there is no problem. */ extern int unzGetCurrentFileInfo ( unzFile file, unz_file_info *pfile_info, char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char *szComment, uLong commentBufferSize) { return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL, szFileName,fileNameBufferSize, extraField,extraFieldBufferSize, szComment,commentBufferSize); } /* Set the current file of the zipfile to the first file. return UNZ_OK if there is no problem */ extern int unzGoToFirstFile (unzFile file) { int err=UNZ_OK; unz_s* s; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; s->pos_in_central_dir=s->offset_central_dir; s->num_file=0; err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, &s->cur_file_info_internal, NULL,0,NULL,0,NULL,0); s->current_file_ok = (err == UNZ_OK); return err; } /* Set the current file of the zipfile to the next file. return UNZ_OK if there is no problem return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. */ extern int unzGoToNextFile (unzFile file) { unz_s* s; int err; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; if (!s->current_file_ok) return UNZ_END_OF_LIST_OF_FILE; if (s->num_file+1==s->gi.number_entry) return UNZ_END_OF_LIST_OF_FILE; s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; s->num_file++; err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, &s->cur_file_info_internal, NULL,0,NULL,0,NULL,0); s->current_file_ok = (err == UNZ_OK); return err; } /* Get the position of the info of the current file in the zip. return UNZ_OK if there is no problem */ extern int unzGetCurrentFileInfoPosition (unzFile file, unsigned long *pos ) { unz_s* s; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; *pos = s->pos_in_central_dir; return UNZ_OK; } /* Set the position of the info of the current file in the zip. return UNZ_OK if there is no problem */ extern int unzSetCurrentFileInfoPosition (unzFile file, unsigned long pos ) { unz_s* s; int err; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; s->pos_in_central_dir = pos; err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, &s->cur_file_info_internal, NULL,0,NULL,0,NULL,0); s->current_file_ok = (err == UNZ_OK); return UNZ_OK; } /* Try locate the file szFileName in the zipfile. For the iCaseSensitivity signification, see unzipStringFileNameCompare return value : UNZ_OK if the file is found. It becomes the current file. UNZ_END_OF_LIST_OF_FILE if the file is not found */ extern int unzLocateFile (unzFile file, const char *szFileName, int iCaseSensitivity) { unz_s* s; int err; uLong num_fileSaved; uLong pos_in_central_dirSaved; if (file==NULL) return UNZ_PARAMERROR; if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) return UNZ_PARAMERROR; s=(unz_s*)file; if (!s->current_file_ok) return UNZ_END_OF_LIST_OF_FILE; num_fileSaved = s->num_file; pos_in_central_dirSaved = s->pos_in_central_dir; err = unzGoToFirstFile(file); while (err == UNZ_OK) { char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; unzGetCurrentFileInfo(file,NULL, szCurrentFileName,sizeof(szCurrentFileName)-1, NULL,0,NULL,0); if (unzStringFileNameCompare(szCurrentFileName, szFileName,iCaseSensitivity)==0) return UNZ_OK; err = unzGoToNextFile(file); } s->num_file = num_fileSaved ; s->pos_in_central_dir = pos_in_central_dirSaved ; return err; } /* Read the static header of the current zipfile Check the coherency of the static header and info in the end of central directory about this file store in *piSizeVar the size of extra info in static header (filename and size of extra field data) */ static int unzlocal_CheckCurrentFileCoherencyHeader (unz_s* s, uInt* piSizeVar, uLong *poffset_local_extrafield, uInt *psize_local_extrafield) { uLong uMagic,uData,uFlags; uLong size_filename; uLong size_extra_field; int err=UNZ_OK; *piSizeVar = 0; *poffset_local_extrafield = 0; *psize_local_extrafield = 0; if (fseek(s->file,s->cur_file_info_internal.offset_curfile + s->byte_before_the_zipfile,SEEK_SET)!=0) return UNZ_ERRNO; if (err==UNZ_OK) { if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) err=UNZ_ERRNO; else if (uMagic!=0x04034b50) err=UNZ_BADZIPFILE; } if (unzlocal_getShort(s->file,&uData) != UNZ_OK) err=UNZ_ERRNO; /* else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) err=UNZ_BADZIPFILE; */ if (unzlocal_getShort(s->file,&uFlags) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(s->file,&uData) != UNZ_OK) err=UNZ_ERRNO; else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) err=UNZ_BADZIPFILE; if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && (s->cur_file_info.compression_method!=Z_DEFLATED)) err=UNZ_BADZIPFILE; if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* date/time */ err=UNZ_ERRNO; if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* crc */ err=UNZ_ERRNO; else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && ((uFlags & 8)==0)) err=UNZ_BADZIPFILE; if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* size compr */ err=UNZ_ERRNO; else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && ((uFlags & 8)==0)) err=UNZ_BADZIPFILE; if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* size uncompr */ err=UNZ_ERRNO; else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && ((uFlags & 8)==0)) err=UNZ_BADZIPFILE; if (unzlocal_getShort(s->file,&size_filename) != UNZ_OK) err=UNZ_ERRNO; else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) err=UNZ_BADZIPFILE; *piSizeVar += (uInt)size_filename; if (unzlocal_getShort(s->file,&size_extra_field) != UNZ_OK) err=UNZ_ERRNO; *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + size_filename; *psize_local_extrafield = (uInt)size_extra_field; *piSizeVar += (uInt)size_extra_field; return err; } /* Open for reading data the current file in the zipfile. If there is no error and the file is opened, the return value is UNZ_OK. */ extern int unzOpenCurrentFile (unzFile file) { int err=UNZ_OK; int Store; uInt iSizeVar; unz_s* s; file_in_zip_read_info_s* pfile_in_zip_read_info; uLong offset_local_extrafield; /* offset of the static extra field */ uInt size_local_extrafield; /* size of the static extra field */ if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; if (!s->current_file_ok) return UNZ_PARAMERROR; if (s->pfile_in_zip_read != NULL) unzCloseCurrentFile(file); if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar, &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) return UNZ_BADZIPFILE; pfile_in_zip_read_info = (file_in_zip_read_info_s*) ALLOC(sizeof(file_in_zip_read_info_s)); if (pfile_in_zip_read_info==NULL) return UNZ_INTERNALERROR; pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; pfile_in_zip_read_info->pos_local_extrafield=0; if (pfile_in_zip_read_info->read_buffer==NULL) { TRYFREE(pfile_in_zip_read_info); return UNZ_INTERNALERROR; } pfile_in_zip_read_info->stream_initialised=0; if ((s->cur_file_info.compression_method!=0) && (s->cur_file_info.compression_method!=Z_DEFLATED)) err=UNZ_BADZIPFILE; Store = s->cur_file_info.compression_method==0; pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; pfile_in_zip_read_info->crc32=0; pfile_in_zip_read_info->compression_method = s->cur_file_info.compression_method; pfile_in_zip_read_info->file=s->file; pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; pfile_in_zip_read_info->stream.total_out = 0; if (!Store) { pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; pfile_in_zip_read_info->stream.zfree = (free_func)0; pfile_in_zip_read_info->stream.opaque = (voidp)0; err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); if (err == Z_OK) pfile_in_zip_read_info->stream_initialised=1; /* windowBits is passed < 0 to tell that there is no zlib header. * Note that in this case inflate *requires* an extra "dummy" byte * after the compressed stream in order to complete decompression and * return Z_STREAM_END. * In unzip, i don't wait absolutely Z_STREAM_END because I known the * size of both compressed and uncompressed data */ } pfile_in_zip_read_info->rest_read_compressed = s->cur_file_info.compressed_size ; pfile_in_zip_read_info->rest_read_uncompressed = s->cur_file_info.uncompressed_size ; pfile_in_zip_read_info->pos_in_zipfile = s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + iSizeVar; pfile_in_zip_read_info->stream.avail_in = (uInt)0; s->pfile_in_zip_read = pfile_in_zip_read_info; return UNZ_OK; } /* Read bytes from the current file. buf contain buffer where data must be copied len the size of buf. return the number of byte copied if somes bytes are copied return 0 if the end of file was reached return <0 with error code if there is an error (UNZ_ERRNO for IO error, or zLib error for uncompress error) */ extern int unzReadCurrentFile (unzFile file, void *buf, unsigned len) { int err=UNZ_OK; uInt iRead = 0; unz_s* s; file_in_zip_read_info_s* pfile_in_zip_read_info; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; if ((pfile_in_zip_read_info->read_buffer == NULL)) return UNZ_END_OF_LIST_OF_FILE; if (len==0) return 0; pfile_in_zip_read_info->stream.next_out = (Byte*)buf; pfile_in_zip_read_info->stream.avail_out = (uInt)len; if (len>pfile_in_zip_read_info->rest_read_uncompressed) pfile_in_zip_read_info->stream.avail_out = (uInt)pfile_in_zip_read_info->rest_read_uncompressed; while (pfile_in_zip_read_info->stream.avail_out>0) { if ((pfile_in_zip_read_info->stream.avail_in==0) && (pfile_in_zip_read_info->rest_read_compressed>0)) { uInt uReadThis = UNZ_BUFSIZE; if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; if (uReadThis == 0) return UNZ_EOF; if (s->cur_file_info.compressed_size == pfile_in_zip_read_info->rest_read_compressed) if (fseek(pfile_in_zip_read_info->file, pfile_in_zip_read_info->pos_in_zipfile + pfile_in_zip_read_info->byte_before_the_zipfile,SEEK_SET)!=0) return UNZ_ERRNO; if (fread(pfile_in_zip_read_info->read_buffer,uReadThis,1, pfile_in_zip_read_info->file)!=1) return UNZ_ERRNO; pfile_in_zip_read_info->pos_in_zipfile += uReadThis; pfile_in_zip_read_info->rest_read_compressed-=uReadThis; pfile_in_zip_read_info->stream.next_in = (Byte*)pfile_in_zip_read_info->read_buffer; pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; } if (pfile_in_zip_read_info->compression_method==0) { uInt uDoCopy,i ; if (pfile_in_zip_read_info->stream.avail_out < pfile_in_zip_read_info->stream.avail_in) uDoCopy = pfile_in_zip_read_info->stream.avail_out ; else uDoCopy = pfile_in_zip_read_info->stream.avail_in ; for (i=0;istream.next_out+i) = *(pfile_in_zip_read_info->stream.next_in+i); // pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, // pfile_in_zip_read_info->stream.next_out, // uDoCopy); pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; pfile_in_zip_read_info->stream.avail_in -= uDoCopy; pfile_in_zip_read_info->stream.avail_out -= uDoCopy; pfile_in_zip_read_info->stream.next_out += uDoCopy; pfile_in_zip_read_info->stream.next_in += uDoCopy; pfile_in_zip_read_info->stream.total_out += uDoCopy; iRead += uDoCopy; } else { uLong uTotalOutBefore,uTotalOutAfter; const Byte *bufBefore; uLong uOutThis; int flush=Z_SYNC_FLUSH; uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; bufBefore = pfile_in_zip_read_info->stream.next_out; /* if ((pfile_in_zip_read_info->rest_read_uncompressed == pfile_in_zip_read_info->stream.avail_out) && (pfile_in_zip_read_info->rest_read_compressed == 0)) flush = Z_FINISH; */ err=inflate(&pfile_in_zip_read_info->stream,flush); uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; uOutThis = uTotalOutAfter-uTotalOutBefore; // pfile_in_zip_read_info->crc32 = // crc32(pfile_in_zip_read_info->crc32,bufBefore, // (uInt)(uOutThis)); pfile_in_zip_read_info->rest_read_uncompressed -= uOutThis; iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); if (err==Z_STREAM_END) return (iRead==0) ? UNZ_EOF : iRead; if (err!=Z_OK) break; } } if (err==Z_OK) return iRead; return err; } /* Give the current position in uncompressed data */ extern long unztell (unzFile file) { unz_s* s; file_in_zip_read_info_s* pfile_in_zip_read_info; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; return (long)pfile_in_zip_read_info->stream.total_out; } /* return 1 if the end of file was reached, 0 elsewhere */ extern int unzeof (unzFile file) { unz_s* s; file_in_zip_read_info_s* pfile_in_zip_read_info; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; if (pfile_in_zip_read_info->rest_read_uncompressed == 0) return 1; else return 0; } /* Read extra field from the current file (opened by unzOpenCurrentFile) This is the static-header version of the extra field (sometimes, there is more info in the static-header version than in the central-header) if buf==NULL, it return the size of the static extra field that can be read if buf!=NULL, len is the size of the buffer, the extra header is copied in buf. the return value is the number of bytes copied in buf, or (if <0) the error code */ extern int unzGetLocalExtrafield (unzFile file,void *buf,unsigned len) { unz_s* s; file_in_zip_read_info_s* pfile_in_zip_read_info; uInt read_now; uLong size_to_read; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; size_to_read = (pfile_in_zip_read_info->size_local_extrafield - pfile_in_zip_read_info->pos_local_extrafield); if (buf==NULL) return (int)size_to_read; if (len>size_to_read) read_now = (uInt)size_to_read; else read_now = (uInt)len ; if (read_now==0) return 0; if (fseek(pfile_in_zip_read_info->file, pfile_in_zip_read_info->offset_local_extrafield + pfile_in_zip_read_info->pos_local_extrafield,SEEK_SET)!=0) return UNZ_ERRNO; if (fread(buf,(uInt)size_to_read,1,pfile_in_zip_read_info->file)!=1) return UNZ_ERRNO; return (int)read_now; } /* Close the file in zip opened with unzipOpenCurrentFile Return UNZ_CRCERROR if all the file was read but the CRC is not good */ extern int unzCloseCurrentFile (unzFile file) { int err=UNZ_OK; unz_s* s; file_in_zip_read_info_s* pfile_in_zip_read_info; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; /* if (pfile_in_zip_read_info->rest_read_uncompressed == 0) { if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) err=UNZ_CRCERROR; } */ TRYFREE(pfile_in_zip_read_info->read_buffer); pfile_in_zip_read_info->read_buffer = NULL; if (pfile_in_zip_read_info->stream_initialised) inflateEnd(&pfile_in_zip_read_info->stream); pfile_in_zip_read_info->stream_initialised = 0; TRYFREE(pfile_in_zip_read_info); s->pfile_in_zip_read=NULL; return err; } /* Get the global comment string of the ZipFile, in the szComment buffer. uSizeBuf is the size of the szComment buffer. return the number of byte copied or an error code <0 */ extern int unzGetGlobalComment (unzFile file, char *szComment, uLong uSizeBuf) { unz_s* s; uLong uReadThis ; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; uReadThis = uSizeBuf; if (uReadThis>s->gi.size_comment) uReadThis = s->gi.size_comment; if (fseek(s->file,s->central_pos+22,SEEK_SET)!=0) return UNZ_ERRNO; if (uReadThis>0) { *szComment='\0'; if (fread(szComment,(uInt)uReadThis,1,s->file)!=1) return UNZ_ERRNO; } if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) *(szComment+s->gi.size_comment)='\0'; return (int)uReadThis; } /* infblock.h -- header to use infblock.c * Copyright (C) 1995-1998 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ struct inflate_blocks_state; typedef struct inflate_blocks_state inflate_blocks_statef; static inflate_blocks_statef * inflate_blocks_new OF(( z_streamp z, check_func c, /* check function */ uInt w)); /* window size */ static int inflate_blocks OF(( inflate_blocks_statef *, z_streamp , int)); /* initial return code */ static void inflate_blocks_reset OF(( inflate_blocks_statef *, z_streamp , uLong *)); /* check value on output */ static int inflate_blocks_free OF(( inflate_blocks_statef *, z_streamp)); #if 0 static void inflate_set_dictionary OF(( inflate_blocks_statef *s, const Byte *d, /* dictionary */ uInt n)); /* dictionary length */ static int inflate_blocks_sync_point OF(( inflate_blocks_statef *s)); #endif /* simplify the use of the inflate_huft type with some defines */ #define exop word.what.Exop #define bits word.what.Bits /* Table for deflate from PKZIP's appnote.txt. */ static const uInt border[] = { /* Order of the bit length code lengths */ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; /* inftrees.h -- header to use inftrees.c * Copyright (C) 1995-1998 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ /* Huffman code lookup table entry--this entry is four bytes for machines that have 16-bit pointers (e.g. PC's in the small or medium model). */ typedef struct inflate_huft_s inflate_huft; struct inflate_huft_s { union { struct { Byte Exop; /* number of extra bits or operation */ Byte Bits; /* number of bits in this code or subcode */ } what; uInt pad; /* pad structure to a power of 2 (4 bytes for */ } word; /* 16-bit, 8 bytes for 32-bit int's) */ uInt base; /* literal, length base, distance base, or table offset */ }; /* Maximum size of dynamic tree. The maximum found in a long but non- exhaustive search was 1004 huft structures (850 for length/literals and 154 for distances, the latter actually the result of an exhaustive search). The actual maximum is not known, but the value below is more than safe. */ #define MANY 1440 static int inflate_trees_bits OF(( uInt *, /* 19 code lengths */ uInt *, /* bits tree desired/actual depth */ inflate_huft * *, /* bits tree result */ inflate_huft *, /* space for trees */ z_streamp)); /* for messages */ static int inflate_trees_dynamic OF(( uInt, /* number of literal/length codes */ uInt, /* number of distance codes */ uInt *, /* that many (total) code lengths */ uInt *, /* literal desired/actual bit depth */ uInt *, /* distance desired/actual bit depth */ inflate_huft * *, /* literal/length tree result */ inflate_huft * *, /* distance tree result */ inflate_huft *, /* space for trees */ z_streamp)); /* for messages */ static int inflate_trees_fixed OF(( uInt *, /* literal desired/actual bit depth */ uInt *, /* distance desired/actual bit depth */ inflate_huft * *, /* literal/length tree result */ inflate_huft * *, /* distance tree result */ z_streamp)); /* for memory allocation */ /* infcodes.h -- header to use infcodes.c * Copyright (C) 1995-1998 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ struct inflate_codes_state; typedef struct inflate_codes_state inflate_codes_statef; static inflate_codes_statef *inflate_codes_new OF(( uInt, uInt, inflate_huft *, inflate_huft *, z_streamp )); static int inflate_codes OF(( inflate_blocks_statef *, z_streamp , int)); static void inflate_codes_free OF(( inflate_codes_statef *, z_streamp )); /* infutil.h -- types and macros common to blocks and codes * Copyright (C) 1995-1998 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ #ifndef _INFUTIL_H #define _INFUTIL_H typedef enum { TYPE, /* get type bits (3, including end bit) */ LENS, /* get lengths for stored */ STORED, /* processing stored block */ TABLE, /* get table lengths */ BTREE, /* get bit lengths tree for a dynamic block */ DTREE, /* get length, distance trees for a dynamic block */ CODES, /* processing fixed or dynamic block */ DRY, /* output remaining window bytes */ DONE, /* finished last block, done */ BAD} /* got a data error--stuck here */ inflate_block_mode; /* inflate blocks semi-private state */ struct inflate_blocks_state { /* mode */ inflate_block_mode mode; /* current inflate_block mode */ /* mode dependent information */ union { uInt left; /* if STORED, bytes left to copy */ struct { uInt table; /* table lengths (14 bits) */ uInt index; /* index into blens (or border) */ uInt *blens; /* bit lengths of codes */ uInt bb; /* bit length tree depth */ inflate_huft *tb; /* bit length decoding tree */ } trees; /* if DTREE, decoding info for trees */ struct { inflate_codes_statef *codes; } decode; /* if CODES, current state */ } sub; /* submode */ uInt last; /* true if this block is the last block */ /* mode independent information */ uInt bitk; /* bits in bit buffer */ uLong bitb; /* bit buffer */ inflate_huft *hufts; /* single malloc for tree space */ Byte *window; /* sliding window */ Byte *end; /* one byte after sliding window */ Byte *read; /* window read pointer */ Byte *write; /* window write pointer */ check_func checkfn; /* check function */ uLong check; /* check on output */ }; /* defines for inflate input/output */ /* update pointers and return */ #define UPDBITS {s->bitb=b;s->bitk=k;} #define UPDIN {z->avail_in=n;z->total_in+=p-z->next_in;z->next_in=p;} #define UPDOUT {s->write=q;} #define UPDATE {UPDBITS UPDIN UPDOUT} #define LEAVE {UPDATE return inflate_flush(s,z,r);} /* get bytes and bits */ #define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;} #define NEEDBYTE {if(n)r=Z_OK;else LEAVE} #define NEXTBYTE (n--,*p++) #define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<>=(j);k-=(j);} /* output bytes */ #define WAVAIL (uInt)(qread?s->read-q-1:s->end-q) #define LOADOUT {q=s->write;m=(uInt)WAVAIL;} #define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=(uInt)WAVAIL;}} #define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT} #define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;} #define OUTBYTE(a) {*q++=(Byte)(a);m--;} /* load static pointers */ #define LOAD {LOADIN LOADOUT} /* masks for lower bits (size given to avoid silly warnings with Visual C++) */ //static uInt inflate_mask[17]; static uInt inflate_mask[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff }; /* copy as much as possible from the sliding window to the output area */ static int inflate_flush OF(( inflate_blocks_statef *, z_streamp , int)); #endif /* Notes beyond the 1.93a appnote.txt: 1. Distance pointers never point before the beginning of the output stream. 2. Distance pointers can point back across blocks, up to 32k away. 3. There is an implied maximum of 7 bits for the bit length table and 15 bits for the actual data. 4. If only one code exists, then it is encoded using one bit. (Zero would be more efficient, but perhaps a little confusing.) If two codes exist, they are coded using one bit each (0 and 1). 5. There is no way of sending zero distance codes--a dummy must be sent if there are none. (History: a pre 2.0 version of PKZIP would store blocks with no distance codes, but this was discovered to be too harsh a criterion.) Valid only for 1.93a. 2.04c does allow zero distance codes, which is sent as one code of zero bits in length. 6. There are up to 286 literal/length codes. Code 256 represents the end-of-block. Note however that the static length tree defines 288 codes just to fill out the Huffman codes. Codes 286 and 287 cannot be used though, since there is no length base or extra bits defined for them. Similarily, there are up to 30 distance codes. However, static trees define 32 codes (all 5 bits) to fill out the Huffman codes, but the last two had better not show up in the data. 7. Unzip can check dynamic Huffman blocks for complete code sets. The exception is that a single code would not be complete (see #4). 8. The five bits following the block type is really the number of literal codes sent minus 257. 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits (1+6+6). Therefore, to output three times the length, you output three codes (1+1+1), whereas to output four times the same length, you only need two codes (1+3). Hmm. 10. In the tree reconstruction algorithm, Code = Code + Increment only if BitLength(i) is not zero. (Pretty obvious.) 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) 12. Note: length code 284 can represent 227-258, but length code 285 really is 258. The last length deserves its own, short code since it gets used a lot in very redundant files. The length 258 is special since 258 - 3 (the min match length) is 255. 13. The literal/length and distance code bit lengths are read as a single stream of lengths. It is possible (and advantageous) for a repeat code (16, 17, or 18) to go across the boundary between the two sets of lengths. */ void inflate_blocks_reset(inflate_blocks_statef *s, z_streamp z, uLong *c) { if (c != Z_NULL) *c = s->check; if (s->mode == BTREE || s->mode == DTREE) ZFREE(z, s->sub.trees.blens); if (s->mode == CODES) inflate_codes_free(s->sub.decode.codes, z); s->mode = TYPE; s->bitk = 0; s->bitb = 0; s->read = s->write = s->window; if (s->checkfn != Z_NULL) z->adler = s->check = (*s->checkfn)(0L, (const Byte *)Z_NULL, 0); Tracev(("inflate: blocks reset\n")); } inflate_blocks_statef *inflate_blocks_new(z_streamp z, check_func c, uInt w) { inflate_blocks_statef *s; if ((s = (inflate_blocks_statef *)ZALLOC (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL) return s; if ((s->hufts = (inflate_huft *)ZALLOC(z, sizeof(inflate_huft), MANY)) == Z_NULL) { ZFREE(z, s); return Z_NULL; } if ((s->window = (Byte *)ZALLOC(z, 1, w)) == Z_NULL) { ZFREE(z, s->hufts); ZFREE(z, s); return Z_NULL; } s->end = s->window + w; s->checkfn = c; s->mode = TYPE; Tracev(("inflate: blocks allocated\n")); inflate_blocks_reset(s, z, Z_NULL); return s; } int inflate_blocks(inflate_blocks_statef *s, z_streamp z, int r) { uInt t; /* temporary storage */ uLong b; /* bit buffer */ uInt k; /* bits in bit buffer */ Byte *p; /* input data pointer */ uInt n; /* bytes available there */ Byte *q; /* output window write pointer */ uInt m; /* bytes to end of window or read pointer */ /* copy input/output information to locals (UPDATE macro restores) */ LOAD /* process input based on current state */ while (1) switch (s->mode) { case TYPE: NEEDBITS(3) t = (uInt)b & 7; s->last = t & 1; switch (t >> 1) { case 0: /* stored */ Tracev(("inflate: stored block%s\n", s->last ? " (last)" : "")); DUMPBITS(3) t = k & 7; /* go to byte boundary */ DUMPBITS(t) s->mode = LENS; /* get length of stored block */ break; case 1: /* fixed */ Tracev(("inflate: fixed codes block%s\n", s->last ? " (last)" : "")); { uInt bl, bd; inflate_huft *tl, *td; inflate_trees_fixed(&bl, &bd, &tl, &td, z); s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z); if (s->sub.decode.codes == Z_NULL) { r = Z_MEM_ERROR; LEAVE } } DUMPBITS(3) s->mode = CODES; break; case 2: /* dynamic */ Tracev(("inflate: dynamic codes block%s\n", s->last ? " (last)" : "")); DUMPBITS(3) s->mode = TABLE; break; case 3: /* illegal */ DUMPBITS(3) s->mode = BAD; z->msg = (char*)"invalid block type"; r = Z_DATA_ERROR; LEAVE } break; case LENS: NEEDBITS(32) if ((((~b) >> 16) & 0xffff) != (b & 0xffff)) { s->mode = BAD; z->msg = (char*)"invalid stored block lengths"; r = Z_DATA_ERROR; LEAVE } s->sub.left = (uInt)b & 0xffff; b = k = 0; /* dump bits */ Tracev(("inflate: stored length %u\n", s->sub.left)); s->mode = s->sub.left ? STORED : (s->last ? DRY : TYPE); break; case STORED: if (n == 0) LEAVE NEEDOUT t = s->sub.left; if (t > n) t = n; if (t > m) t = m; zmemcpy(q, p, t); p += t; n -= t; q += t; m -= t; if ((s->sub.left -= t) != 0) break; Tracev(("inflate: stored end, %lu total out\n", z->total_out + (q >= s->read ? q - s->read : (s->end - s->read) + (q - s->window)))); s->mode = s->last ? DRY : TYPE; break; case TABLE: NEEDBITS(14) s->sub.trees.table = t = (uInt)b & 0x3fff; #ifndef PKZIP_BUG_WORKAROUND if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) { s->mode = BAD; z->msg = (char*)"too many length or distance symbols"; r = Z_DATA_ERROR; LEAVE } #endif t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); if ((s->sub.trees.blens = (uInt*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL) { r = Z_MEM_ERROR; LEAVE } DUMPBITS(14) s->sub.trees.index = 0; Tracev(("inflate: table sizes ok\n")); s->mode = BTREE; case BTREE: while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10)) { NEEDBITS(3) s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7; DUMPBITS(3) } while (s->sub.trees.index < 19) s->sub.trees.blens[border[s->sub.trees.index++]] = 0; s->sub.trees.bb = 7; t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb, &s->sub.trees.tb, s->hufts, z); if (t != Z_OK) { ZFREE(z, s->sub.trees.blens); r = t; if (r == Z_DATA_ERROR) s->mode = BAD; LEAVE } s->sub.trees.index = 0; Tracev(("inflate: bits tree ok\n")); s->mode = DTREE; case DTREE: while (t = s->sub.trees.table, s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f)) { inflate_huft *h; uInt i, j, c; t = s->sub.trees.bb; NEEDBITS(t) h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]); t = h->bits; c = h->base; if (c < 16) { DUMPBITS(t) s->sub.trees.blens[s->sub.trees.index++] = c; } else /* c == 16..18 */ { i = c == 18 ? 7 : c - 14; j = c == 18 ? 11 : 3; NEEDBITS(t + i) DUMPBITS(t) j += (uInt)b & inflate_mask[i]; DUMPBITS(i) i = s->sub.trees.index; t = s->sub.trees.table; if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || (c == 16 && i < 1)) { ZFREE(z, s->sub.trees.blens); s->mode = BAD; z->msg = (char*)"invalid bit length repeat"; r = Z_DATA_ERROR; LEAVE } c = c == 16 ? s->sub.trees.blens[i - 1] : 0; do { s->sub.trees.blens[i++] = c; } while (--j); s->sub.trees.index = i; } } s->sub.trees.tb = Z_NULL; { uInt bl, bd; inflate_huft *tl, *td; inflate_codes_statef *c; tl = NULL; td = NULL; bl = 9; /* must be <= 9 for lookahead assumptions */ bd = 6; /* must be <= 9 for lookahead assumptions */ t = s->sub.trees.table; t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), s->sub.trees.blens, &bl, &bd, &tl, &td, s->hufts, z); ZFREE(z, s->sub.trees.blens); if (t != Z_OK) { if (t == (uInt)Z_DATA_ERROR) s->mode = BAD; r = t; LEAVE } Tracev(("inflate: trees ok\n")); if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL) { r = Z_MEM_ERROR; LEAVE } s->sub.decode.codes = c; } s->mode = CODES; case CODES: UPDATE if ((r = inflate_codes(s, z, r)) != Z_STREAM_END) return inflate_flush(s, z, r); r = Z_OK; inflate_codes_free(s->sub.decode.codes, z); LOAD Tracev(("inflate: codes end, %lu total out\n", z->total_out + (q >= s->read ? q - s->read : (s->end - s->read) + (q - s->window)))); if (!s->last) { s->mode = TYPE; break; } s->mode = DRY; case DRY: FLUSH if (s->read != s->write) LEAVE s->mode = DONE; case DONE: r = Z_STREAM_END; LEAVE case BAD: r = Z_DATA_ERROR; LEAVE default: r = Z_STREAM_ERROR; LEAVE } } int inflate_blocks_free(inflate_blocks_statef *s, z_streamp z) { inflate_blocks_reset(s, z, Z_NULL); ZFREE(z, s->window); ZFREE(z, s->hufts); ZFREE(z, s); Tracev(("inflate: blocks freed\n")); return Z_OK; } #if 0 void inflate_set_dictionary(inflate_blocks_statef *s, const Byte *d, uInt n) { zmemcpy(s->window, d, n); s->read = s->write = s->window + n; } /* Returns true if inflate is currently at the end of a block generated * by Z_SYNC_FLUSH or Z_FULL_FLUSH. * IN assertion: s != Z_NULL */ int inflate_blocks_sync_point(inflate_blocks_statef *s) { return s->mode == LENS; } #endif /* And'ing with mask[n] masks the lower n bits */ /* static uInt inflate_mask[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff }; */ /* copy as much as possible from the sliding window to the output area */ int inflate_flush(inflate_blocks_statef *s, z_streamp z, int r) { uInt n; Byte *p; Byte *q; /* static copies of source and destination pointers */ p = z->next_out; q = s->read; /* compute number of bytes to copy as as end of window */ n = (uInt)((q <= s->write ? s->write : s->end) - q); if (n > z->avail_out) n = z->avail_out; if (n && r == Z_BUF_ERROR) r = Z_OK; /* update counters */ z->avail_out -= n; z->total_out += n; /* update check information */ if (s->checkfn != Z_NULL) z->adler = s->check = (*s->checkfn)(s->check, q, n); /* copy as as end of window */ zmemcpy(p, q, n); p += n; q += n; /* see if more to copy at beginning of window */ if (q == s->end) { /* wrap pointers */ q = s->window; if (s->write == s->end) s->write = s->window; /* compute bytes to copy */ n = (uInt)(s->write - q); if (n > z->avail_out) n = z->avail_out; if (n && r == Z_BUF_ERROR) r = Z_OK; /* update counters */ z->avail_out -= n; z->total_out += n; /* update check information */ if (s->checkfn != Z_NULL) z->adler = s->check = (*s->checkfn)(s->check, q, n); /* copy */ zmemcpy(p, q, n); p += n; q += n; } /* update pointers */ z->next_out = p; s->read = q; /* done */ return r; } /* inftrees.c -- generate Huffman trees for efficient decoding * Copyright (C) 1995-1998 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ static const char inflate_copyright[] = " inflate 1.1.3 Copyright 1995-1998 Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot include such an acknowledgment, I would appreciate that you keep this copyright string in the executable of your product. */ /* simplify the use of the inflate_huft type with some defines */ #define exop word.what.Exop #define bits word.what.Bits static int huft_build OF(( uInt *, /* code lengths in bits */ uInt, /* number of codes */ uInt, /* number of "simple" codes */ const uInt *, /* list of base values for non-simple codes */ const uInt *, /* list of extra bits for non-simple codes */ inflate_huft **, /* result: starting table */ uInt *, /* maximum lookup bits (returns actual) */ inflate_huft *, /* space for trees */ uInt *, /* hufts used in space */ uInt * )); /* space for values */ /* Tables for deflate from PKZIP's appnote.txt. */ static const uInt cplens[31] = { /* Copy lengths for literal codes 257..285 */ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; /* see note #13 above about 258 */ static const uInt cplext[31] = { /* Extra bits for literal codes 257..285 */ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112}; /* 112==invalid */ static const uInt cpdist[30] = { /* Copy offsets for distance codes 0..29 */ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577}; static const uInt cpdext[30] = { /* Extra bits for distance codes */ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}; /* Huffman code decoding is performed using a multi-level table lookup. The fastest way to decode is to simply build a lookup table whose size is determined by the longest code. However, the time it takes to build this table can also be a factor if the data being decoded is not very long. The most common codes are necessarily the shortest codes, so those codes dominate the decoding time, and hence the speed. The idea is you can have a shorter table that decodes the shorter, more probable codes, and then point to subsidiary tables for the longer codes. The time it costs to decode the longer codes is then traded against the time it takes to make longer tables. This results of this trade are in the variables lbits and dbits below. lbits is the number of bits the first level table for literal/ length codes can decode in one step, and dbits is the same thing for the distance codes. Subsequent tables are also less than or equal to those sizes. These values may be adjusted either when all of the codes are shorter than that, in which case the longest code length in bits is used, or when the shortest code is *longer* than the requested table size, in which case the length of the shortest code in bits is used. There are two different values for the two tables, since they code a different number of possibilities each. The literal/length table codes 286 possible values, or in a flat code, a little over eight bits. The distance table codes 30 possible values, or a little less than five bits, flat. The optimum values for speed end up being about one bit more than those, so lbits is 8+1 and dbits is 5+1. The optimum values may differ though from machine to machine, and possibly even between compilers. Your mileage may vary. */ /* If BMAX needs to be larger than 16, then h and x[] should be uLong. */ #define BMAX 15 /* maximum bit length of any code */ static int huft_build(uInt *b, uInt n, uInt s, const uInt *d, const uInt *e, inflate_huft ** t, uInt *m, inflate_huft *hp, uInt *hn, uInt *v) //uInt *b; /* code lengths in bits (all assumed <= BMAX) */ //uInt n; /* number of codes (assumed <= 288) */ //uInt s; /* number of simple-valued codes (0..s-1) */ //const uInt *d; /* list of base values for non-simple codes */ //const uInt *e; /* list of extra bits for non-simple codes */ //inflate_huft ** t; /* result: starting table */ //uInt *m; /* maximum lookup bits, returns actual */ //inflate_huft *hp; /* space for trees */ //uInt *hn; /* hufts used in space */ //uInt *v; /* working area: values in order of bit length */ /* Given a list of code lengths and a maximum table size, make a set of tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR if the given code set is incomplete (the tables are still built in this case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of lengths), or Z_MEM_ERROR if not enough memory. */ { uInt a; /* counter for codes of length k */ uInt c[BMAX+1]; /* bit length count table */ uInt f; /* i repeats in table every f entries */ int g; /* maximum code length */ int h; /* table level */ register uInt i; /* counter, current code */ register uInt j; /* counter */ register int k; /* number of bits in current code */ int l; /* bits per table (returned in m) */ uInt mask; /* (1 << w) - 1, to avoid cc -O bug on HP */ register uInt *p; /* pointer into c[], b[], or v[] */ inflate_huft *q; /* points to current table */ struct inflate_huft_s r; /* table entry for structure assignment */ inflate_huft *u[BMAX]; /* table stack */ register int w; /* bits before this table == (l * h) */ uInt x[BMAX+1]; /* bit offsets, then code stack */ uInt *xp; /* pointer into x */ int y; /* number of dummy codes added */ uInt z; /* number of entries in current table */ /* Generate counts for each bit length */ p = c; #define C0 *p++ = 0; #define C2 C0 C0 C0 C0 #define C4 C2 C2 C2 C2 C4 /* clear c[]--assume BMAX+1 is 16 */ p = b; i = n; do { c[*p++]++; /* assume all entries <= BMAX */ } while (--i); if (c[0] == n) /* null input--all zero length codes */ { *t = (inflate_huft *)Z_NULL; *m = 0; return Z_OK; } /* Find minimum and maximum length, bound *m by those */ l = *m; for (j = 1; j <= BMAX; j++) if (c[j]) break; k = j; /* minimum code length */ if ((uInt)l < j) l = j; for (i = BMAX; i; i--) if (c[i]) break; g = i; /* maximum code length */ if ((uInt)l > i) l = i; *m = l; /* Adjust last length count to fill out codes, if needed */ for (y = 1 << j; j < i; j++, y <<= 1) if ((y -= c[j]) < 0) return Z_DATA_ERROR; if ((y -= c[i]) < 0) return Z_DATA_ERROR; c[i] += y; /* Generate starting offsets into the value table for each length */ x[1] = j = 0; p = c + 1; xp = x + 2; while (--i) { /* note that i == g from above */ *xp++ = (j += *p++); } /* Make a table of values in order of bit lengths */ p = b; i = 0; do { if ((j = *p++) != 0) v[x[j]++] = i; } while (++i < n); n = x[g]; /* set n to length of v */ /* Generate the Huffman codes and for each, make the table entries */ x[0] = i = 0; /* first Huffman code is zero */ p = v; /* grab values in bit order */ h = -1; /* no tables yet--level -1 */ w = -l; /* bits decoded == (l * h) */ u[0] = (inflate_huft *)Z_NULL; /* just to keep compilers happy */ q = (inflate_huft *)Z_NULL; /* ditto */ z = 0; /* ditto */ /* go through the bit lengths (k already is bits in shortest code) */ for (; k <= g; k++) { a = c[k]; while (a--) { /* here i is the Huffman code of length k bits for value *p */ /* make tables up to required level */ while (k > w + l) { h++; w += l; /* previous table always l bits */ /* compute minimum size table less than or equal to l bits */ z = g - w; z = z > (uInt)l ? l : z; /* table size upper limit */ if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */ { /* too few codes for k-w bit table */ f -= a + 1; /* deduct codes from patterns left */ xp = c + k; if (j < z) while (++j < z) /* try smaller tables up to z bits */ { if ((f <<= 1) <= *++xp) break; /* enough codes to use up j bits */ f -= *xp; /* else deduct codes from patterns */ } } z = 1 << j; /* table entries for j-bit table */ /* allocate new table */ if (*hn + z > MANY) /* (note: doesn't matter for fixed) */ return Z_MEM_ERROR; /* not enough memory */ u[h] = q = hp + *hn; *hn += z; /* connect to last table, if there is one */ if (h) { x[h] = i; /* save pattern for backing up */ r.bits = (Byte)l; /* bits to dump before this table */ r.exop = (Byte)j; /* bits in this table */ j = i >> (w - l); r.base = (uInt)(q - u[h-1] - j); /* offset to this table */ u[h-1][j] = r; /* connect to last table */ } else *t = q; /* first table is returned result */ } /* set up table entry in r */ r.bits = (Byte)(k - w); if (p >= v + n) r.exop = 128 + 64; /* out of values--invalid code */ else if (*p < s) { r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); /* 256 is end-of-block */ r.base = *p++; /* simple code is just the value */ } else { r.exop = (Byte)(e[*p - s] + 16 + 64);/* non-simple--look up in lists */ r.base = d[*p++ - s]; } /* fill code-like entries with r */ f = 1 << (k - w); for (j = i >> w; j < z; j += f) q[j] = r; /* backwards increment the k-bit code i */ for (j = 1 << (k - 1); i & j; j >>= 1) i ^= j; i ^= j; /* backup over finished tables */ mask = (1 << w) - 1; /* needed on HP, cc -O bug */ while ((i & mask) != x[h]) { h--; /* don't need to update q */ w -= l; mask = (1 << w) - 1; } } } /* Return Z_BUF_ERROR if we were given an incomplete table */ return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; } int inflate_trees_bits(uInt *c, uInt *bb, inflate_huft * *tb, inflate_huft *hp, z_streamp z) //uInt *c; /* 19 code lengths */ //uInt *bb; /* bits tree desired/actual depth */ //inflate_huft * *tb; /* bits tree result */ //inflate_huft *hp; /* space for trees */ //z_streamp z; /* for messages */ { int r; uInt hn = 0; /* hufts used in space */ uInt *v; /* work area for huft_build */ if ((v = (uInt*)ZALLOC(z, 19, sizeof(uInt))) == Z_NULL) return Z_MEM_ERROR; r = huft_build(c, 19, 19, (uInt*)Z_NULL, (uInt*)Z_NULL, tb, bb, hp, &hn, v); if (r == Z_DATA_ERROR) z->msg = (char*)"oversubscribed dynamic bit lengths tree"; else if (r == Z_BUF_ERROR || *bb == 0) { z->msg = (char*)"incomplete dynamic bit lengths tree"; r = Z_DATA_ERROR; } ZFREE(z, v); return r; } int inflate_trees_dynamic(uInt nl, uInt nd, uInt *c, uInt *bl, uInt *bd, inflate_huft * *tl, inflate_huft * *td, inflate_huft *hp, z_streamp z) //uInt nl; /* number of literal/length codes */ //uInt nd; /* number of distance codes */ //uInt *c; /* that many (total) code lengths */ //uInt *bl; /* literal desired/actual bit depth */ //uInt *bd; /* distance desired/actual bit depth */ //inflate_huft * *tl; /* literal/length tree result */ //inflate_huft * *td; /* distance tree result */ //inflate_huft *hp; /* space for trees */ //z_streamp z; /* for messages */ { int r; uInt hn = 0; /* hufts used in space */ uInt *v; /* work area for huft_build */ /* allocate work area */ if ((v = (uInt*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL) return Z_MEM_ERROR; /* build literal/length tree */ r = huft_build(c, nl, 257, cplens, cplext, tl, bl, hp, &hn, v); if (r != Z_OK || *bl == 0) { if (r == Z_DATA_ERROR) z->msg = (char*)"oversubscribed literal/length tree"; else if (r != Z_MEM_ERROR) { z->msg = (char*)"incomplete literal/length tree"; r = Z_DATA_ERROR; } ZFREE(z, v); return r; } /* build distance tree */ r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, hp, &hn, v); if (r != Z_OK || (*bd == 0 && nl > 257)) { if (r == Z_DATA_ERROR) z->msg = (char*)"oversubscribed distance tree"; else if (r == Z_BUF_ERROR) { #ifdef PKZIP_BUG_WORKAROUND r = Z_OK; } #else z->msg = (char*)"incomplete distance tree"; r = Z_DATA_ERROR; } else if (r != Z_MEM_ERROR) { z->msg = (char*)"empty distance tree with lengths"; r = Z_DATA_ERROR; } ZFREE(z, v); return r; #endif } /* done */ ZFREE(z, v); return Z_OK; } /* inffixed.h -- table for decoding fixed codes * Generated automatically by the maketree.c program */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ static uInt fixed_bl = 9; static uInt fixed_bd = 5; static inflate_huft fixed_tl[] = { {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},192}, {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},160}, {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},224}, {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},144}, {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},208}, {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},176}, {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},240}, {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},200}, {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},168}, {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},232}, {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},152}, {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},216}, {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},184}, {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},248}, {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},196}, {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},164}, {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},228}, {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},148}, {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},212}, {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},180}, {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},244}, {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},204}, {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},172}, {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},236}, {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},156}, {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},220}, {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},188}, {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},252}, {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},194}, {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},162}, {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},226}, {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},146}, {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},210}, {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},178}, {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},242}, {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},202}, {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},170}, {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},234}, {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},154}, {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},218}, {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},186}, {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},250}, {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},198}, {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},166}, {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},230}, {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},150}, {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},214}, {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},182}, {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},246}, {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},206}, {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},174}, {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},238}, {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},158}, {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},222}, {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},190}, {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},254}, {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},193}, {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},161}, {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},225}, {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},145}, {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},209}, {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},177}, {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},241}, {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},201}, {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},169}, {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},233}, {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},153}, {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},217}, {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},185}, {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},249}, {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},197}, {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},165}, {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},229}, {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},149}, {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},213}, {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},181}, {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},245}, {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},205}, {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},173}, {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},237}, {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},157}, {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},221}, {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},189}, {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},253}, {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},195}, {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},163}, {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},227}, {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},147}, {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},211}, {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},179}, {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},243}, {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},203}, {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},171}, {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},235}, {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},155}, {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},219}, {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},187}, {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},251}, {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},199}, {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},167}, {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},231}, {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},151}, {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},215}, {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},183}, {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},247}, {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},207}, {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},175}, {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},239}, {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},159}, {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},223}, {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},191}, {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},255} }; static inflate_huft fixed_td[] = { {{{80,5}},1}, {{{87,5}},257}, {{{83,5}},17}, {{{91,5}},4097}, {{{81,5}},5}, {{{89,5}},1025}, {{{85,5}},65}, {{{93,5}},16385}, {{{80,5}},3}, {{{88,5}},513}, {{{84,5}},33}, {{{92,5}},8193}, {{{82,5}},9}, {{{90,5}},2049}, {{{86,5}},129}, {{{192,5}},24577}, {{{80,5}},2}, {{{87,5}},385}, {{{83,5}},25}, {{{91,5}},6145}, {{{81,5}},7}, {{{89,5}},1537}, {{{85,5}},97}, {{{93,5}},24577}, {{{80,5}},4}, {{{88,5}},769}, {{{84,5}},49}, {{{92,5}},12289}, {{{82,5}},13}, {{{90,5}},3073}, {{{86,5}},193}, {{{192,5}},24577} }; int inflate_trees_fixed(uInt *bl, uInt *bd, inflate_huft * *tl, inflate_huft * *td, z_streamp z) //uInt *bl; /* literal desired/actual bit depth */ //uInt *bd; /* distance desired/actual bit depth */ //inflate_huft * *tl; /* literal/length tree result */ //inflate_huft * *td; /* distance tree result */ //z_streamp z; /* for memory allocation */ { *bl = fixed_bl; *bd = fixed_bd; *tl = fixed_tl; *td = fixed_td; return Z_OK; } /* simplify the use of the inflate_huft type with some defines */ #define exop word.what.Exop #define bits word.what.Bits /* macros for bit input with no checking and for returning unused bytes */ #define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3;} /* Called with number of bytes left to write in window at least 258 (the maximum string length) and number of input bytes available at least ten. The ten bytes are six bytes for the longest length/ distance pair plus four bytes for overloading the bit buffer. */ static int inflate_fast(uInt bl, uInt bd, inflate_huft *tl, inflate_huft *td, inflate_blocks_statef *s, z_streamp z) { inflate_huft *t; /* temporary pointer */ uInt e; /* extra bits or operation */ uLong b; /* bit buffer */ uInt k; /* bits in bit buffer */ Byte *p; /* input data pointer */ uInt n; /* bytes available there */ Byte *q; /* output window write pointer */ uInt m; /* bytes to end of window or read pointer */ uInt ml; /* mask for literal/length tree */ uInt md; /* mask for distance tree */ uInt c; /* bytes to copy */ uInt d; /* distance back to copy from */ Byte *r; /* copy source pointer */ /* load input, output, bit values */ LOAD /* initialize masks */ ml = inflate_mask[bl]; md = inflate_mask[bd]; /* do until not enough input or output space for fast loop */ do { /* assume called with m >= 258 && n >= 10 */ /* get literal/length code */ GRABBITS(20) /* max bits for literal/length code */ if ((e = (t = tl + ((uInt)b & ml))->exop) == 0) { DUMPBITS(t->bits) Tracevv((t->base >= 0x20 && t->base < 0x7f ? "inflate: * literal '%c'\n" : "inflate: * literal 0x%02x\n", t->base)); *q++ = (Byte)t->base; m--; continue; } do { DUMPBITS(t->bits) if (e & 16) { /* get extra bits for length */ e &= 15; c = t->base + ((uInt)b & inflate_mask[e]); DUMPBITS(e) Tracevv(("inflate: * length %u\n", c)); /* decode distance base of block to copy */ GRABBITS(15); /* max bits for distance code */ e = (t = td + ((uInt)b & md))->exop; do { DUMPBITS(t->bits) if (e & 16) { /* get extra bits to add to distance base */ e &= 15; GRABBITS(e) /* get extra bits (up to 13) */ d = t->base + ((uInt)b & inflate_mask[e]); DUMPBITS(e) Tracevv(("inflate: * distance %u\n", d)); /* do the copy */ m -= c; if ((uInt)(q - s->window) >= d) /* offset before dest */ { /* just copy */ r = q - d; *q++ = *r++; c--; /* minimum count is three, */ *q++ = *r++; c--; /* so unroll loop a little */ } else /* else offset after destination */ { e = d - (uInt)(q - s->window); /* bytes from offset to end */ r = s->end - e; /* pointer to offset */ if (c > e) /* if source crosses, */ { c -= e; /* copy to end of window */ do { *q++ = *r++; } while (--e); r = s->window; /* copy rest from start of window */ } } do { /* copy all or what's left */ *q++ = *r++; } while (--c); break; } else if ((e & 64) == 0) { t += t->base; e = (t += ((uInt)b & inflate_mask[e]))->exop; } else { z->msg = (char*)"invalid distance code"; UNGRAB UPDATE return Z_DATA_ERROR; } } while (1); break; } if ((e & 64) == 0) { t += t->base; if ((e = (t += ((uInt)b & inflate_mask[e]))->exop) == 0) { DUMPBITS(t->bits) Tracevv((t->base >= 0x20 && t->base < 0x7f ? "inflate: * literal '%c'\n" : "inflate: * literal 0x%02x\n", t->base)); *q++ = (Byte)t->base; m--; break; } } else if (e & 32) { Tracevv(("inflate: * end of block\n")); UNGRAB UPDATE return Z_STREAM_END; } else { z->msg = (char*)"invalid literal/length code"; UNGRAB UPDATE return Z_DATA_ERROR; } } while (1); } while (m >= 258 && n >= 10); /* not enough input or output--restore pointers and return */ UNGRAB UPDATE return Z_OK; } /* infcodes.c -- process literals and length/distance pairs * Copyright (C) 1995-1998 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* simplify the use of the inflate_huft type with some defines */ #define exop word.what.Exop #define bits word.what.Bits typedef enum { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ START, /* x: set up for LEN */ LEN, /* i: get length/literal/eob next */ LENEXT, /* i: getting length extra (have base) */ DIST, /* i: get distance next */ DISTEXT, /* i: getting distance extra */ COPY, /* o: copying bytes in window, waiting for space */ LIT, /* o: got literal, waiting for output space */ WASH, /* o: got eob, possibly still output waiting */ END, /* x: got eob and all data flushed */ BADCODE} /* x: got error */ inflate_codes_mode; /* inflate codes private state */ struct inflate_codes_state { /* mode */ inflate_codes_mode mode; /* current inflate_codes mode */ /* mode dependent information */ uInt len; union { struct { inflate_huft *tree; /* pointer into tree */ uInt need; /* bits needed */ } code; /* if LEN or DIST, where in tree */ uInt lit; /* if LIT, literal */ struct { uInt get; /* bits to get for extra */ uInt dist; /* distance back to copy from */ } copy; /* if EXT or COPY, where and how much */ } sub; /* submode */ /* mode independent information */ Byte lbits; /* ltree bits decoded per branch */ Byte dbits; /* dtree bits decoder per branch */ inflate_huft *ltree; /* literal/length/eob tree */ inflate_huft *dtree; /* distance tree */ }; inflate_codes_statef *inflate_codes_new(uInt bl, uInt bd, inflate_huft *tl, inflate_huft *td, z_streamp z) { inflate_codes_statef *c; if ((c = (inflate_codes_statef *) ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL) { c->mode = START; c->lbits = (Byte)bl; c->dbits = (Byte)bd; c->ltree = tl; c->dtree = td; Tracev(("inflate: codes new\n")); } return c; } int inflate_codes(inflate_blocks_statef *s, z_streamp z, int r) { uInt j; /* temporary storage */ inflate_huft *t; /* temporary pointer */ uInt e; /* extra bits or operation */ uLong b; /* bit buffer */ uInt k; /* bits in bit buffer */ Byte *p; /* input data pointer */ uInt n; /* bytes available there */ Byte *q; /* output window write pointer */ uInt m; /* bytes to end of window or read pointer */ Byte *f; /* pointer to copy strings from */ inflate_codes_statef *c = s->sub.decode.codes; /* codes state */ /* copy input/output information to locals (UPDATE macro restores) */ LOAD /* process input and output based on current state */ while (1) switch (c->mode) { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ case START: /* x: set up for LEN */ #ifndef SLOW if (m >= 258 && n >= 10) { UPDATE r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z); LOAD if (r != Z_OK) { c->mode = r == Z_STREAM_END ? WASH : BADCODE; break; } } #endif /* !SLOW */ c->sub.code.need = c->lbits; c->sub.code.tree = c->ltree; c->mode = LEN; case LEN: /* i: get length/literal/eob next */ j = c->sub.code.need; NEEDBITS(j) t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); DUMPBITS(t->bits) e = (uInt)(t->exop); if (e == 0) /* literal */ { c->sub.lit = t->base; Tracevv((t->base >= 0x20 && t->base < 0x7f ? "inflate: literal '%c'\n" : "inflate: literal 0x%02x\n", t->base)); c->mode = LIT; break; } if (e & 16) /* length */ { c->sub.copy.get = e & 15; c->len = t->base; c->mode = LENEXT; break; } if ((e & 64) == 0) /* next table */ { c->sub.code.need = e; c->sub.code.tree = t + t->base; break; } if (e & 32) /* end of block */ { Tracevv(("inflate: end of block\n")); c->mode = WASH; break; } c->mode = BADCODE; /* invalid code */ z->msg = (char*)"invalid literal/length code"; r = Z_DATA_ERROR; LEAVE case LENEXT: /* i: getting length extra (have base) */ j = c->sub.copy.get; NEEDBITS(j) c->len += (uInt)b & inflate_mask[j]; DUMPBITS(j) c->sub.code.need = c->dbits; c->sub.code.tree = c->dtree; Tracevv(("inflate: length %u\n", c->len)); c->mode = DIST; case DIST: /* i: get distance next */ j = c->sub.code.need; NEEDBITS(j) t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); DUMPBITS(t->bits) e = (uInt)(t->exop); if (e & 16) /* distance */ { c->sub.copy.get = e & 15; c->sub.copy.dist = t->base; c->mode = DISTEXT; break; } if ((e & 64) == 0) /* next table */ { c->sub.code.need = e; c->sub.code.tree = t + t->base; break; } c->mode = BADCODE; /* invalid code */ z->msg = (char*)"invalid distance code"; r = Z_DATA_ERROR; LEAVE case DISTEXT: /* i: getting distance extra */ j = c->sub.copy.get; NEEDBITS(j) c->sub.copy.dist += (uInt)b & inflate_mask[j]; DUMPBITS(j) Tracevv(("inflate: distance %u\n", c->sub.copy.dist)); c->mode = COPY; case COPY: /* o: copying bytes in window, waiting for space */ #ifndef __TURBOC__ /* Turbo C bug for following expression */ f = (uInt)(q - s->window) < c->sub.copy.dist ? s->end - (c->sub.copy.dist - (q - s->window)) : q - c->sub.copy.dist; #else f = q - c->sub.copy.dist; if ((uInt)(q - s->window) < c->sub.copy.dist) f = s->end - (c->sub.copy.dist - (uInt)(q - s->window)); #endif while (c->len) { NEEDOUT OUTBYTE(*f++) if (f == s->end) f = s->window; c->len--; } c->mode = START; break; case LIT: /* o: got literal, waiting for output space */ NEEDOUT OUTBYTE(c->sub.lit) c->mode = START; break; case WASH: /* o: got eob, possibly more output */ if (k > 7) /* return unused byte, if any */ { Assert(k < 16, "inflate_codes grabbed too many bytes") k -= 8; n++; p--; /* can always return one */ } FLUSH if (s->read != s->write) LEAVE c->mode = END; case END: r = Z_STREAM_END; LEAVE case BADCODE: /* x: got error */ r = Z_DATA_ERROR; LEAVE default: r = Z_STREAM_ERROR; LEAVE } #ifdef NEED_DUMMY_RETURN return Z_STREAM_ERROR; /* Some dumb compilers complain without this */ #endif } void inflate_codes_free(inflate_codes_statef *c, z_streamp z) { ZFREE(z, c); Tracev(("inflate: codes free\n")); } /* adler32.c -- compute the Adler-32 checksum of a data stream * Copyright (C) 1995-1998 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #define BASE 65521L /* largest prime smaller than 65536 */ #define NMAX 5552 /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ #undef DO1 #undef DO2 #undef DO4 #undef DO8 #define DO1(buf,i) {s1 += buf[i]; s2 += s1;} #define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); #define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); #define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); #define DO16(buf) DO8(buf,0); DO8(buf,8); /* ========================================================================= */ static uLong adler32(uLong adler, const Byte *buf, uInt len) { unsigned long s1 = adler & 0xffff; unsigned long s2 = (adler >> 16) & 0xffff; int k; if (buf == Z_NULL) return 1L; while (len > 0) { k = len < NMAX ? len : NMAX; len -= k; while (k >= 16) { DO16(buf); buf += 16; k -= 16; } if (k != 0) do { s1 += *buf++; s2 += s1; } while (--k); s1 %= BASE; s2 %= BASE; } return (s2 << 16) | s1; } /* infblock.h -- header to use infblock.c * Copyright (C) 1995-1998 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ static inflate_blocks_statef * inflate_blocks_new OF(( z_streamp z, check_func c, /* check function */ uInt w)); /* window size */ static int inflate_blocks OF(( inflate_blocks_statef *, z_streamp , int)); /* initial return code */ static void inflate_blocks_reset OF(( inflate_blocks_statef *, z_streamp , uLong *)); /* check value on output */ static int inflate_blocks_free OF(( inflate_blocks_statef *, z_streamp)); #if 0 static void inflate_set_dictionary OF(( inflate_blocks_statef *s, const Byte *d, /* dictionary */ uInt n)); /* dictionary length */ static int inflate_blocks_sync_point OF(( inflate_blocks_statef *s)); #endif typedef enum { imMETHOD, /* waiting for method byte */ imFLAG, /* waiting for flag byte */ imDICT4, /* four dictionary check bytes to go */ imDICT3, /* three dictionary check bytes to go */ imDICT2, /* two dictionary check bytes to go */ imDICT1, /* one dictionary check byte to go */ imDICT0, /* waiting for inflateSetDictionary */ imBLOCKS, /* decompressing blocks */ imCHECK4, /* four check bytes to go */ imCHECK3, /* three check bytes to go */ imCHECK2, /* two check bytes to go */ imCHECK1, /* one check byte to go */ imDONE, /* finished check, done */ imBAD} /* got an error--stay here */ inflate_mode; /* inflate private state */ struct internal_state { /* mode */ inflate_mode mode; /* current inflate mode */ /* mode dependent information */ union { uInt method; /* if FLAGS, method byte */ struct { uLong was; /* computed check value */ uLong need; /* stream check value */ } check; /* if CHECK, check values to compare */ uInt marker; /* if BAD, inflateSync's marker bytes count */ } sub; /* submode */ /* mode independent information */ int nowrap; /* flag for no wrapper */ uInt wbits; /* log2(window size) (8..15, defaults to 15) */ inflate_blocks_statef *blocks; /* current inflate_blocks state */ }; int inflateReset(z_streamp z) { if (z == Z_NULL || z->state == Z_NULL) return Z_STREAM_ERROR; z->total_in = z->total_out = 0; z->msg = Z_NULL; z->state->mode = z->state->nowrap ? imBLOCKS : imMETHOD; inflate_blocks_reset(z->state->blocks, z, Z_NULL); Tracev(("inflate: reset\n")); return Z_OK; } int inflateEnd(z_streamp z) { if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL) return Z_STREAM_ERROR; if (z->state->blocks != Z_NULL) inflate_blocks_free(z->state->blocks, z); ZFREE(z, z->state); z->state = Z_NULL; Tracev(("inflate: end\n")); return Z_OK; } int inflateInit2_(z_streamp z, int w, const char *version, int stream_size) { if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || stream_size != sizeof(z_stream)) return Z_VERSION_ERROR; /* initialize state */ if (z == Z_NULL) return Z_STREAM_ERROR; z->msg = Z_NULL; if (z->zalloc == Z_NULL) { z->zalloc = (void *(*)(void *, unsigned, unsigned))zcalloc; z->opaque = (voidp)0; } if (z->zfree == Z_NULL) z->zfree = (void (*)(void *, void *))zcfree; if ((z->state = (struct internal_state *) ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL) return Z_MEM_ERROR; z->state->blocks = Z_NULL; /* handle undocumented nowrap option (no zlib header or check) */ z->state->nowrap = 0; if (w < 0) { w = - w; z->state->nowrap = 1; } /* set window size */ if (w < 8 || w > 15) { inflateEnd(z); return Z_STREAM_ERROR; } z->state->wbits = (uInt)w; /* create inflate_blocks state */ if ((z->state->blocks = inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, (uInt)1 << w)) == Z_NULL) { inflateEnd(z); return Z_MEM_ERROR; } Tracev(("inflate: allocated\n")); /* reset state */ inflateReset(z); return Z_OK; } #if 0 int inflateInit_(z_streamp z, const char *version, int stream_size) { return inflateInit2_(z, DEF_WBITS, version, stream_size); } #endif #define iNEEDBYTE {if(z->avail_in==0)return r;r=f;} #define iNEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++) int inflate(z_streamp z, int f) { int r; uInt b; if (z == Z_NULL || z->state == Z_NULL || z->next_in == Z_NULL) return Z_STREAM_ERROR; f = f == Z_FINISH ? Z_BUF_ERROR : Z_OK; r = Z_BUF_ERROR; while (1) switch (z->state->mode) { case imMETHOD: iNEEDBYTE if (((z->state->sub.method = iNEXTBYTE) & 0xf) != Z_DEFLATED) { z->state->mode = imBAD; z->msg = (char*)"unknown compression method"; z->state->sub.marker = 5; /* can't try inflateSync */ break; } if ((z->state->sub.method >> 4) + 8 > z->state->wbits) { z->state->mode = imBAD; z->msg = (char*)"invalid window size"; z->state->sub.marker = 5; /* can't try inflateSync */ break; } z->state->mode = imFLAG; case imFLAG: iNEEDBYTE b = iNEXTBYTE; if (((z->state->sub.method << 8) + b) % 31) { z->state->mode = imBAD; z->msg = (char*)"incorrect header check"; z->state->sub.marker = 5; /* can't try inflateSync */ break; } Tracev(("inflate: zlib header ok\n")); if (!(b & PRESET_DICT)) { z->state->mode = imBLOCKS; break; } z->state->mode = imDICT4; case imDICT4: iNEEDBYTE z->state->sub.check.need = (uLong)iNEXTBYTE << 24; z->state->mode = imDICT3; case imDICT3: iNEEDBYTE z->state->sub.check.need += (uLong)iNEXTBYTE << 16; z->state->mode = imDICT2; case imDICT2: iNEEDBYTE z->state->sub.check.need += (uLong)iNEXTBYTE << 8; z->state->mode = imDICT1; case imDICT1: iNEEDBYTE z->state->sub.check.need += (uLong)iNEXTBYTE; z->adler = z->state->sub.check.need; z->state->mode = imDICT0; return Z_NEED_DICT; case imDICT0: z->state->mode = imBAD; z->msg = (char*)"need dictionary"; z->state->sub.marker = 0; /* can try inflateSync */ return Z_STREAM_ERROR; case imBLOCKS: r = inflate_blocks(z->state->blocks, z, r); if (r == Z_DATA_ERROR) { z->state->mode = imBAD; z->state->sub.marker = 0; /* can try inflateSync */ break; } if (r == Z_OK) r = f; if (r != Z_STREAM_END) return r; r = f; inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was); if (z->state->nowrap) { z->state->mode = imDONE; break; } z->state->mode = imCHECK4; case imCHECK4: iNEEDBYTE z->state->sub.check.need = (uLong)iNEXTBYTE << 24; z->state->mode = imCHECK3; case imCHECK3: iNEEDBYTE z->state->sub.check.need += (uLong)iNEXTBYTE << 16; z->state->mode = imCHECK2; case imCHECK2: iNEEDBYTE z->state->sub.check.need += (uLong)iNEXTBYTE << 8; z->state->mode = imCHECK1; case imCHECK1: iNEEDBYTE z->state->sub.check.need += (uLong)iNEXTBYTE; if (z->state->sub.check.was != z->state->sub.check.need) { z->state->mode = imBAD; z->msg = (char*)"incorrect data check"; z->state->sub.marker = 5; /* can't try inflateSync */ break; } Tracev(("inflate: zlib check ok\n")); z->state->mode = imDONE; case imDONE: return Z_STREAM_END; case imBAD: return Z_DATA_ERROR; default: return Z_STREAM_ERROR; } #ifdef NEED_DUMMY_RETURN return Z_STREAM_ERROR; /* Some dumb compilers complain without this */ #endif } // defined but not used #if 0 int inflateSetDictionary(z_streamp z, const Byte *dictionary, uInt dictLength) { uInt length = dictLength; if (z == Z_NULL || z->state == Z_NULL || z->state->mode != imDICT0) return Z_STREAM_ERROR; if (adler32(1L, dictionary, dictLength) != z->adler) return Z_DATA_ERROR; z->adler = 1L; if (length >= ((uInt)1<state->wbits)) { length = (1<state->wbits)-1; dictionary += dictLength - length; } inflate_set_dictionary(z->state->blocks, dictionary, length); z->state->mode = imBLOCKS; return Z_OK; } int inflateSync(z_streamp z) { uInt n; /* number of bytes to look at */ Byte *p; /* pointer to bytes */ uInt m; /* number of marker bytes found in a row */ uLong r, w; /* temporaries to save total_in and total_out */ /* set up */ if (z == Z_NULL || z->state == Z_NULL) return Z_STREAM_ERROR; if (z->state->mode != imBAD) { z->state->mode = imBAD; z->state->sub.marker = 0; } if ((n = z->avail_in) == 0) return Z_BUF_ERROR; p = z->next_in; m = z->state->sub.marker; /* search */ while (n && m < 4) { static const Byte mark[4] = {0, 0, 0xff, 0xff}; if (*p == mark[m]) m++; else if (*p) m = 0; else m = 4 - m; p++, n--; } /* restore */ z->total_in += p - z->next_in; z->next_in = p; z->avail_in = n; z->state->sub.marker = m; /* return no joy or set up to restart on a new block */ if (m != 4) return Z_DATA_ERROR; r = z->total_in; w = z->total_out; inflateReset(z); z->total_in = r; z->total_out = w; z->state->mode = imBLOCKS; return Z_OK; } /* Returns true if inflate is currently at the end of a block generated * by Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP * implementation to provide an additional safety check. PPP uses Z_SYNC_FLUSH * but removes the length bytes of the resulting empty stored block. When * decompressing, PPP checks that at the end of input packet, inflate is * waiting for these length bytes. */ int inflateSyncPoint(z_streamp z) { if (z == Z_NULL || z->state == Z_NULL || z->state->blocks == Z_NULL) return Z_STREAM_ERROR; return inflate_blocks_sync_point(z->state->blocks); } #endif voidp zcalloc (voidp opaque, unsigned items, unsigned size) { if (opaque) items += size - size; /* make compiler happy */ return (voidp)Z_Malloc(items*size); } void zcfree (voidp opaque, voidp ptr) { Z_Free(ptr); if (opaque) return; /* make compiler happy */ } ================================================ FILE: src/engine/qcommon/unzip.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) /* like the STRICT of WIN32, we define a pointer that cannot be converted from (void*) without cast */ typedef struct TagunzFile__ { int unused; } unzFile__; typedef unzFile__ *unzFile; #else typedef void* unzFile; #endif /* tm_unz contain date/time info */ typedef struct tm_unz_s { unsigned int tm_sec; /* seconds after the minute - [0,59] */ unsigned int tm_min; /* minutes after the hour - [0,59] */ unsigned int tm_hour; /* hours since midnight - [0,23] */ unsigned int tm_mday; /* day of the month - [1,31] */ unsigned int tm_mon; /* months since January - [0,11] */ unsigned int tm_year; /* years - [1980..2044] */ } tm_unz; /* unz_global_info structure contain global data about the ZIPfile These data comes from the end of central dir */ typedef struct unz_global_info_s { unsigned long number_entry; /* total number of entries in the central dir on this disk */ unsigned long size_comment; /* size of the global comment of the zipfile */ } unz_global_info; /* unz_file_info contain information about a file in the zipfile */ typedef struct unz_file_info_s { unsigned long version; /* version made by 2 unsigned chars */ unsigned long version_needed; /* version needed to extract 2 unsigned chars */ unsigned long flag; /* general purpose bit flag 2 unsigned chars */ unsigned long compression_method; /* compression method 2 unsigned chars */ unsigned long dosDate; /* last mod file date in Dos fmt 4 unsigned chars */ unsigned long crc; /* crc-32 4 unsigned chars */ unsigned long compressed_size; /* compressed size 4 unsigned chars */ unsigned long uncompressed_size; /* uncompressed size 4 unsigned chars */ unsigned long size_filename; /* filename length 2 unsigned chars */ unsigned long size_file_extra; /* extra field length 2 unsigned chars */ unsigned long size_file_comment; /* file comment length 2 unsigned chars */ unsigned long disk_num_start; /* disk number start 2 unsigned chars */ unsigned long internal_fa; /* internal file attributes 2 unsigned chars */ unsigned long external_fa; /* external file attributes 4 unsigned chars */ tm_unz tmu_date; } unz_file_info; /* unz_file_info_interntal contain internal info about a file in zipfile*/ typedef struct unz_file_info_internal_s { unsigned long offset_curfile;/* relative offset of static header 4 unsigned chars */ } unz_file_info_internal; typedef void* (*alloc_func) (void* opaque, unsigned int items, unsigned int size); typedef void (*free_func) (void* opaque, void* address); struct internal_state; typedef struct z_stream_s { unsigned char *next_in; /* next input unsigned char */ unsigned int avail_in; /* number of unsigned chars available at next_in */ unsigned long total_in; /* total nb of input unsigned chars read so */ unsigned char *next_out; /* next output unsigned char should be put there */ unsigned int avail_out; /* remaining free space at next_out */ unsigned long total_out; /* total nb of unsigned chars output so */ char *msg; /* last error message, NULL if no error */ struct internal_state *state; /* not visible by applications */ alloc_func zalloc; /* used to allocate the internal state */ free_func zfree; /* used to free the internal state */ unsigned char* opaque; /* private data object passed to zalloc and zfree */ int data_type; /* best guess about the data type: ascii or binary */ unsigned long adler; /* adler32 value of the uncompressed data */ unsigned long reserved; /* reserved for future use */ } z_stream; typedef z_stream *z_streamp; /* file_in_zip_read_info_s contain internal information about a file in zipfile, when reading and decompress it */ typedef struct { char *read_buffer; /* internal buffer for compressed data */ z_stream stream; /* zLib stream structure for inflate */ unsigned long pos_in_zipfile; /* position in unsigned char on the zipfile, for fseek*/ unsigned long stream_initialised; /* flag set if stream structure is initialised*/ unsigned long offset_local_extrafield;/* offset of the static extra field */ unsigned int size_local_extrafield;/* size of the static extra field */ unsigned long pos_local_extrafield; /* position in the static extra field in read*/ unsigned long crc32; /* crc32 of all data uncompressed */ unsigned long crc32_wait; /* crc32 we must obtain after decompress all */ unsigned long rest_read_compressed; /* number of unsigned char to be decompressed */ unsigned long rest_read_uncompressed;/*number of unsigned char to be obtained after decomp*/ FILE* file; /* io structore of the zipfile */ unsigned long compression_method; /* compression method (0==store) */ unsigned long byte_before_the_zipfile;/* unsigned char before the zipfile, (>0 for sfx)*/ } file_in_zip_read_info_s; /* unz_s contain internal information about the zipfile */ typedef struct { FILE* file; /* io structore of the zipfile */ unz_global_info gi; /* public global information */ unsigned long byte_before_the_zipfile;/* unsigned char before the zipfile, (>0 for sfx)*/ unsigned long num_file; /* number of the current file in the zipfile*/ unsigned long pos_in_central_dir; /* pos of the current file in the central dir*/ unsigned long current_file_ok; /* flag about the usability of the current file*/ unsigned long central_pos; /* position of the beginning of the central dir*/ unsigned long size_central_dir; /* size of the central directory */ unsigned long offset_central_dir; /* offset of start of central directory with respect to the starting disk number */ unz_file_info cur_file_info; /* public info about the current file in zip*/ unz_file_info_internal cur_file_info_internal; /* private info about it*/ file_in_zip_read_info_s* pfile_in_zip_read; /* structure about the current file if we are decompressing it */ unsigned char* tmpFile; int tmpPos,tmpSize; } unz_s; #define UNZ_OK (0) #define UNZ_END_OF_LIST_OF_FILE (-100) #define UNZ_ERRNO (Z_ERRNO) #define UNZ_EOF (0) #define UNZ_PARAMERROR (-102) #define UNZ_BADZIPFILE (-103) #define UNZ_INTERNALERROR (-104) #define UNZ_CRCERROR (-105) #define UNZ_CASESENSITIVE 1 #define UNZ_NOTCASESENSITIVE 2 #define UNZ_OSDEFAULTCASE 0 extern int unzStringFileNameCompare (const char* fileName1, const char* fileName2, int iCaseSensitivity); /* Compare two filename (fileName1,fileName2). If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi or strcasecmp) If iCaseSenisivity = 0, case sensitivity is defaut of your operating system (like 1 on Unix, 2 on Windows) */ extern unzFile unzOpen (const char *path); extern unzFile unzReOpen (const char* path, unzFile file); /* Open a Zip file. path contain the full pathname (by example, on a Windows NT computer "c:\\zlib\\zlib111.zip" or on an Unix computer "zlib/zlib111.zip". If the zipfile cannot be opened (file don't exist or in not valid), the return value is NULL. Else, the return value is a unzFile Handle, usable with other function of this unzip package. */ extern int unzClose (unzFile file); /* Close a ZipFile opened with unzipOpen. If there is files inside the .Zip opened with unzOpenCurrentFile (see later), these files MUST be closed with unzipCloseCurrentFile before call unzipClose. return UNZ_OK if there is no problem. */ extern int unzGetGlobalInfo (unzFile file, unz_global_info *pglobal_info); /* Write info about the ZipFile in the *pglobal_info structure. No preparation of the structure is needed return UNZ_OK if there is no problem. */ extern int unzGetGlobalComment (unzFile file, char *szComment, unsigned long uSizeBuf); /* Get the global comment string of the ZipFile, in the szComment buffer. uSizeBuf is the size of the szComment buffer. return the number of unsigned char copied or an error code <0 */ /***************************************************************************/ /* Unzip package allow you browse the directory of the zipfile */ extern int unzGoToFirstFile (unzFile file); /* Set the current file of the zipfile to the first file. return UNZ_OK if there is no problem */ extern int unzGoToNextFile (unzFile file); /* Set the current file of the zipfile to the next file. return UNZ_OK if there is no problem return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. */ extern int unzGetCurrentFileInfoPosition (unzFile file, unsigned long *pos ); /* Get the position of the info of the current file in the zip. return UNZ_OK if there is no problem */ extern int unzSetCurrentFileInfoPosition (unzFile file, unsigned long pos ); /* Set the position of the info of the current file in the zip. return UNZ_OK if there is no problem */ extern int unzLocateFile (unzFile file, const char *szFileName, int iCaseSensitivity); /* Try locate the file szFileName in the zipfile. For the iCaseSensitivity signification, see unzStringFileNameCompare return value : UNZ_OK if the file is found. It becomes the current file. UNZ_END_OF_LIST_OF_FILE if the file is not found */ extern int unzGetCurrentFileInfo (unzFile file, unz_file_info *pfile_info, char *szFileName, unsigned long fileNameBufferSize, void *extraField, unsigned long extraFieldBufferSize, char *szComment, unsigned long commentBufferSize); /* Get Info about the current file if pfile_info!=NULL, the *pfile_info structure will contain somes info about the current file if szFileName!=NULL, the filemane string will be copied in szFileName (fileNameBufferSize is the size of the buffer) if extraField!=NULL, the extra field information will be copied in extraField (extraFieldBufferSize is the size of the buffer). This is the Central-header version of the extra field if szComment!=NULL, the comment string of the file will be copied in szComment (commentBufferSize is the size of the buffer) */ /***************************************************************************/ /* for reading the content of the current zipfile, you can open it, read data from it, and close it (you can close it before reading all the file) */ extern int unzOpenCurrentFile (unzFile file); /* Open for reading data the current file in the zipfile. If there is no error, the return value is UNZ_OK. */ extern int unzCloseCurrentFile (unzFile file); /* Close the file in zip opened with unzOpenCurrentFile Return UNZ_CRCERROR if all the file was read but the CRC is not good */ extern int unzReadCurrentFile (unzFile file, void* buf, unsigned len); /* Read unsigned chars from the current file (opened by unzOpenCurrentFile) buf contain buffer where data must be copied len the size of buf. return the number of unsigned char copied if somes unsigned chars are copied return 0 if the end of file was reached return <0 with error code if there is an error (UNZ_ERRNO for IO error, or zLib error for uncompress error) */ extern long unztell(unzFile file); /* Give the current position in uncompressed data */ extern int unzeof (unzFile file); /* return 1 if the end of file was reached, 0 elsewhere */ extern int unzGetLocalExtrafield (unzFile file, void* buf, unsigned len); /* Read extra field from the current file (opened by unzOpenCurrentFile) This is the local-header version of the extra field (sometimes, there is more info in the local-header version than in the central-header) if buf==NULL, it return the size of the local extra field if buf!=NULL, len is the size of the buffer, the extra header is copied in buf. the return value is the number of unsigned chars copied in buf, or (if <0) the error code */ ================================================ FILE: src/engine/qcommon/vm.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // vm.c -- virtual machine /* intermix code and data symbol table a dll has one imported function: VM_SystemCall and one exported function: Perform */ #include "vm_local.h" vm_t *currentVM = NULL; // bk001212 vm_t *lastVM = NULL; // bk001212 int vm_debugLevel; #define MAX_VM 3 vm_t vmTable[MAX_VM]; void VM_VmInfo_f( void ); void VM_VmProfile_f( void ); void VM_Debug( int level ) { vm_debugLevel = level; } /* ============== VM_Init ============== */ void VM_Init( void ) { Cvar_Get( "vm_cgame", "1", CVAR_ARCHIVE ); Cvar_Get( "vm_game", "1", CVAR_ARCHIVE ); Cvar_Get( "vm_ui", "1", CVAR_ARCHIVE ); Cmd_AddCommand ("vmprofile", VM_VmProfile_f ); Cmd_AddCommand ("vminfo", VM_VmInfo_f ); Com_Memset( vmTable, 0, sizeof( vmTable ) ); } /* =============== VM_ValueToSymbol Assumes a program counter value =============== */ const char *VM_ValueToSymbol( vm_t *vm, int value ) { vmSymbol_t *sym; static char text[MAX_TOKEN_CHARS]; sym = vm->symbols; if ( !sym ) { return "NO SYMBOLS"; } // find the symbol while ( sym->next && sym->next->symValue <= value ) { sym = sym->next; } if ( value == sym->symValue ) { return sym->symName; } Com_sprintf( text, sizeof( text ), "%s+%i", sym->symName, value - sym->symValue ); return text; } /* =============== VM_ValueToFunctionSymbol For profiling, find the symbol behind this value =============== */ vmSymbol_t *VM_ValueToFunctionSymbol( vm_t *vm, int value ) { vmSymbol_t *sym; static vmSymbol_t nullSym; sym = vm->symbols; if ( !sym ) { return &nullSym; } while ( sym->next && sym->next->symValue <= value ) { sym = sym->next; } return sym; } /* =============== VM_SymbolToValue =============== */ int VM_SymbolToValue( vm_t *vm, const char *symbol ) { vmSymbol_t *sym; for ( sym = vm->symbols ; sym ; sym = sym->next ) { if ( !strcmp( symbol, sym->symName ) ) { return sym->symValue; } } return 0; } /* =============== ParseHex =============== */ int ParseHex( const char *text ) { int value; int c; value = 0; while ( ( c = *text++ ) != 0 ) { if ( c >= '0' && c <= '9' ) { value = value * 16 + c - '0'; continue; } if ( c >= 'a' && c <= 'f' ) { value = value * 16 + 10 + c - 'a'; continue; } if ( c >= 'A' && c <= 'F' ) { value = value * 16 + 10 + c - 'A'; continue; } } return value; } /* =============== VM_LoadSymbols =============== */ void VM_LoadSymbols( vm_t *vm ) { int len; char *mapfile, *text_p, *token; char name[MAX_QPATH]; char symbols[MAX_QPATH]; vmSymbol_t **prev, *sym; int count; int value; int chars; int segment; int numInstructions; // don't load symbols if not developer if ( !com_developer->integer ) { return; } COM_StripExtension( vm->name, name ); Com_sprintf( symbols, sizeof( symbols ), "vm/%s.map", name ); len = FS_ReadFile( symbols, (void **)&mapfile ); if ( !mapfile ) { Com_Printf( "Couldn't load symbol file: %s\n", symbols ); return; } numInstructions = vm->instructionPointersLength >> 2; // parse the symbols text_p = mapfile; prev = &vm->symbols; count = 0; while ( 1 ) { token = COM_Parse( &text_p ); if ( !token[0] ) { break; } segment = ParseHex( token ); if ( segment ) { COM_Parse( &text_p ); COM_Parse( &text_p ); continue; // only load code segment values } token = COM_Parse( &text_p ); if ( !token[0] ) { Com_Printf( "WARNING: incomplete line at end of file\n" ); break; } value = ParseHex( token ); token = COM_Parse( &text_p ); if ( !token[0] ) { Com_Printf( "WARNING: incomplete line at end of file\n" ); break; } chars = (int)strlen( token ); sym = (vmSymbol_t*) Hunk_Alloc( sizeof( *sym ) + chars, h_high ); *prev = sym; prev = &sym->next; sym->next = NULL; // convert value from an instruction number to a code offset if ( value >= 0 && value < numInstructions ) { value = vm->instructionPointers[value]; } sym->symValue = value; Q_strncpyz( sym->symName, token, chars + 1 ); count++; } vm->numSymbols = count; Com_Printf( "%i symbols parsed from %s\n", count, symbols ); FS_FreeFile( mapfile ); } intptr_t QDECL VM_DllSyscall( intptr_t arg, ... ) { intptr_t args[MAX_VMSYSCALL_ARGS]; int i; va_list ap; args[0] = arg; va_start(ap, arg); for (i = 1; i < ARRAY_LEN(args); i++) args[i] = va_arg(ap, intptr_t); va_end(ap); return currentVM->systemCall( args ); } /* ================= VM_Restart Reload the data, but leave everything else in place This allows a server to do a map_restart without changing memory allocation ================= */ vm_t *VM_Restart( vm_t *vm ) { vmHeader_t *header; int length; int dataLength; int i; char filename[MAX_QPATH]; // DLL's can't be restarted in place if ( vm->dllHandle ) { char name[MAX_QPATH]; intptr_t (*systemCall)( intptr_t *parms ); systemCall = vm->systemCall; Q_strncpyz( name, vm->name, sizeof( name ) ); VM_Free( vm ); vm = VM_Create( name, systemCall, VMI_NATIVE ); return vm; } // load the image Com_Printf( "VM_Restart()\n", filename ); Com_sprintf( filename, sizeof(filename), "vm/%s.qvm", vm->name ); Com_Printf( "Loading vm file %s.\n", filename ); length = FS_ReadFile( filename, (void **)&header ); if ( !header ) { Com_Error( ERR_DROP, "VM_Restart failed.\n" ); } // byte swap the header for ( i = 0 ; i < sizeof( *header ) / 4 ; i++ ) { ((int *)header)[i] = LittleLong( ((int *)header)[i] ); } // validate if ( header->vmMagic != VM_MAGIC || header->bssLength < 0 || header->dataLength < 0 || header->litLength < 0 || header->codeLength <= 0 ) { VM_Free( vm ); Com_Error( ERR_FATAL, "%s has bad header", filename ); } // round up to next power of 2 so all data operations can // be mask protected dataLength = header->dataLength + header->litLength + header->bssLength; for ( i = 0 ; dataLength > ( 1 << i ) ; i++ ) { } dataLength = 1 << i; // clear the data Com_Memset( vm->dataBase, 0, dataLength ); // copy the intialized data Com_Memcpy( vm->dataBase, (byte *)header + header->dataOffset, header->dataLength + header->litLength ); // byte swap the longs for ( i = 0 ; i < header->dataLength ; i += 4 ) { *(int *)(vm->dataBase + i) = LittleLong( *(int *)(vm->dataBase + i ) ); } // free the original file FS_FreeFile( header ); return vm; } /* ================ VM_Create If image ends in .qvm it will be interpreted, otherwise it will attempt to load as a system dll ================ */ #define STACK_SIZE 0x20000 vm_t *VM_Create( const char *module, intptr_t (*systemCalls)(intptr_t *), vmInterpret_t interpret ) { vm_t *vm; vmHeader_t *header; int length; int dataLength; int i, remaining; char filename[MAX_QPATH]; if ( !module || !module[0] || !systemCalls ) { Com_Error( ERR_FATAL, "VM_Create: bad parms" ); } remaining = Hunk_MemoryRemaining(); // see if we already have the VM for ( i = 0 ; i < MAX_VM ; i++ ) { if (!Q_stricmp(vmTable[i].name, module)) { vm = &vmTable[i]; return vm; } } // find a free vm for ( i = 0 ; i < MAX_VM ; i++ ) { if ( !vmTable[i].name[0] ) { break; } } if ( i == MAX_VM ) { Com_Error( ERR_FATAL, "VM_Create: no free vm_t" ); } vm = &vmTable[i]; Q_strncpyz( vm->name, module, sizeof( vm->name ) ); vm->systemCall = systemCalls; // never allow dll loading with a demo if ( interpret == VMI_NATIVE ) { if ( Cvar_VariableValue( "fs_restrict" ) ) { interpret = VMI_BYTECODE; } } if ( interpret == VMI_NATIVE ) { // try to load as a system dll Com_Printf( "Loading dll file %s.\n", vm->name ); vm->dllHandle = Sys_LoadDll( module, vm->fqpath , &vm->entryPoint, VM_DllSyscall ); if ( vm->dllHandle ) { return vm; } Com_Printf( "Failed to load dll, looking for qvm.\n" ); } // load the image Com_sprintf( filename, sizeof(filename), "vm/%s.qvm", vm->name ); Com_Printf( "Loading vm file %s.\n", filename ); length = FS_ReadFile( filename, (void **)&header ); if ( !header ) { Com_Printf( "Failed.\n" ); VM_Free( vm ); return NULL; } // byte swap the header for ( i = 0 ; i < sizeof( *header ) / 4 ; i++ ) { ((int *)header)[i] = LittleLong( ((int *)header)[i] ); } // validate if ( header->vmMagic != VM_MAGIC || header->bssLength < 0 || header->dataLength < 0 || header->litLength < 0 || header->codeLength <= 0 ) { VM_Free( vm ); Com_Error( ERR_FATAL, "%s has bad header", filename ); } // round up to next power of 2 so all data operations can // be mask protected dataLength = header->dataLength + header->litLength + header->bssLength; for ( i = 0 ; dataLength > ( 1 << i ) ; i++ ) { } dataLength = 1 << i; // allocate zero filled space for initialized and uninitialized data vm->dataBase = (byte*) Hunk_Alloc( dataLength, h_high ); vm->dataMask = dataLength - 1; // copy the intialized data Com_Memcpy( vm->dataBase, (byte *)header + header->dataOffset, header->dataLength + header->litLength ); // byte swap the longs for ( i = 0 ; i < header->dataLength ; i += 4 ) { *(int *)(vm->dataBase + i) = LittleLong( *(int *)(vm->dataBase + i ) ); } // allocate space for the jump targets, which will be filled in by the compile/prep functions vm->instructionPointersLength = header->instructionCount * 4; vm->instructionPointers = (int*) Hunk_Alloc( vm->instructionPointersLength, h_high ); // copy or compile the instructions vm->codeLength = header->codeLength; VM_PrepareInterpreter( vm, header ); // free the original file FS_FreeFile( header ); // load the map file VM_LoadSymbols( vm ); // the stack is implicitly at the end of the image vm->programStack = vm->dataMask + 1; vm->stackBottom = vm->programStack - STACK_SIZE; Com_Printf("%s loaded in %d bytes on the hunk\n", module, remaining - Hunk_MemoryRemaining()); return vm; } /* ============== VM_Free ============== */ void VM_Free( vm_t *vm ) { if ( vm->dllHandle ) { Sys_UnloadDll( vm->dllHandle ); Com_Memset( vm, 0, sizeof( *vm ) ); } #if 0 // now automatically freed by hunk if ( vm->codeBase ) { Z_Free( vm->codeBase ); } if ( vm->dataBase ) { Z_Free( vm->dataBase ); } if ( vm->instructionPointers ) { Z_Free( vm->instructionPointers ); } #endif Com_Memset( vm, 0, sizeof( *vm ) ); currentVM = NULL; lastVM = NULL; } void VM_Clear(void) { int i; for (i=0;ientryPoint ) { return (void *)(currentVM->dataBase + intValue); } else { return (void *)(currentVM->dataBase + (intValue & currentVM->dataMask)); } } void *VM_ExplicitArgPtr( vm_t *vm, intptr_t intValue ) { if ( !intValue ) { return NULL; } // bk010124 - currentVM is missing on reconnect here as well? if ( currentVM==NULL ) return NULL; // if ( vm->entryPoint ) { return (void *)(vm->dataBase + intValue); } else { return (void *)(vm->dataBase + (intValue & vm->dataMask)); } } /* ============== VM_Call Upon a system call, the stack will look like: sp+32 parm1 sp+28 parm0 sp+24 return value sp+20 return address sp+16 local1 sp+14 local0 sp+12 arg1 sp+8 arg0 sp+4 return stack sp return address An interpreted function will immediately execute an OP_ENTER instruction, which will subtract space for locals from sp ============== */ #define MAX_STACK 256 #define STACK_MASK (MAX_STACK-1) intptr_t QDECL VM_Call( vm_t *vm, int callnum, ... ) { vm_t *oldVM; intptr_t r; int i; int args[16]; va_list ap; if ( !vm ) { Com_Error( ERR_FATAL, "VM_Call with NULL vm" ); } oldVM = currentVM; currentVM = vm; lastVM = vm; if ( vm_debugLevel ) { Com_Printf( "VM_Call( %i )\n", callnum ); } // if we have a dll loaded, call it directly if ( vm->entryPoint ) { //rcg010207 - see dissertation at top of VM_DllSyscall() in this file. va_start(ap, callnum); for (i = 0; i < sizeof (args) / sizeof (args[i]); i++) { args[i] = va_arg(ap, int); } va_end(ap); r = vm->entryPoint( callnum, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], args[14], args[15]); } else { struct { int callnum; int args[10]; } a; a.callnum = callnum; va_start(ap, callnum); for (i = 0; i < sizeof (a.args) / sizeof (a.args[i]); i++) { a.args[i] = va_arg(ap, int); } va_end(ap); r = VM_CallInterpreted( vm, &a.callnum ); } if ( oldVM != NULL ) // bk001220 - assert(currentVM!=NULL) for oldVM==NULL currentVM = oldVM; return r; } //================================================================= static int QDECL VM_ProfileSort( const void *a, const void *b ) { vmSymbol_t *sa, *sb; sa = *(vmSymbol_t **)a; sb = *(vmSymbol_t **)b; if ( sa->profileCount < sb->profileCount ) { return -1; } if ( sa->profileCount > sb->profileCount ) { return 1; } return 0; } /* ============== VM_VmProfile_f ============== */ void VM_VmProfile_f( void ) { vm_t *vm; vmSymbol_t **sorted, *sym; int i; double total; if ( !lastVM ) { return; } vm = lastVM; if ( !vm->numSymbols ) { return; } sorted = (vmSymbol_t**) Z_Malloc( vm->numSymbols * sizeof( *sorted ) ); sorted[0] = vm->symbols; total = sorted[0]->profileCount; for ( i = 1 ; i < vm->numSymbols ; i++ ) { sorted[i] = sorted[i-1]->next; total += sorted[i]->profileCount; } qsort( sorted, vm->numSymbols, sizeof( *sorted ), VM_ProfileSort ); for ( i = 0 ; i < vm->numSymbols ; i++ ) { int perc; sym = sorted[i]; perc = 100 * (float) sym->profileCount / total; Com_Printf( "%2i%% %9i %s\n", perc, sym->profileCount, sym->symName ); sym->profileCount = 0; } Com_Printf(" %9.0f total\n", total ); Z_Free( sorted ); } /* ============== VM_VmInfo_f ============== */ void VM_VmInfo_f( void ) { vm_t *vm; int i; Com_Printf( "Registered virtual machines:\n" ); for ( i = 0 ; i < MAX_VM ; i++ ) { vm = &vmTable[i]; if ( !vm->name[0] ) { break; } Com_Printf( "%s : ", vm->name ); if ( vm->dllHandle ) { Com_Printf( "native\n" ); continue; } Com_Printf( "interpreted\n" ); Com_Printf( " code length : %7i\n", vm->codeLength ); Com_Printf( " table length: %7i\n", vm->instructionPointersLength ); Com_Printf( " data length : %7i\n", vm->dataMask + 1 ); } } /* =============== VM_LogSyscalls Insert calls to this while debugging the vm compiler =============== */ void VM_LogSyscalls( int *args ) { static int callnum; static FILE *f; if ( !f ) { f = fopen("syscalls.log", "w" ); } callnum++; fprintf(f, "%i: %i (%i) = %i %i %i %i\n", callnum, (int)(intptr_t)( args - (int *)currentVM->dataBase ), args[0], args[1], args[2], args[3], args[4] ); } ================================================ FILE: src/engine/qcommon/vm_interpreted.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "vm_local.h" #ifdef DEBUG_VM // bk001204 static char *opnames[256] = { "OP_UNDEF", "OP_IGNORE", "OP_BREAK", "OP_ENTER", "OP_LEAVE", "OP_CALL", "OP_PUSH", "OP_POP", "OP_CONST", "OP_LOCAL", "OP_JUMP", //------------------- "OP_EQ", "OP_NE", "OP_LTI", "OP_LEI", "OP_GTI", "OP_GEI", "OP_LTU", "OP_LEU", "OP_GTU", "OP_GEU", "OP_EQF", "OP_NEF", "OP_LTF", "OP_LEF", "OP_GTF", "OP_GEF", //------------------- "OP_LOAD1", "OP_LOAD2", "OP_LOAD4", "OP_STORE1", "OP_STORE2", "OP_STORE4", "OP_ARG", "OP_BLOCK_COPY", //------------------- "OP_SEX8", "OP_SEX16", "OP_NEGI", "OP_ADD", "OP_SUB", "OP_DIVI", "OP_DIVU", "OP_MODI", "OP_MODU", "OP_MULI", "OP_MULU", "OP_BAND", "OP_BOR", "OP_BXOR", "OP_BCOM", "OP_LSH", "OP_RSHI", "OP_RSHU", "OP_NEGF", "OP_ADDF", "OP_SUBF", "OP_DIVF", "OP_MULF", "OP_CVIF", "OP_CVFI" }; #endif #if idppc #if defined(__GNUC__) static inline unsigned int loadWord(void *addr) { unsigned int word; asm("lwbrx %0,0,%1" : "=r" (word) : "r" (addr)); return word; } #else #define loadWord(addr) __lwbrx(addr,0) #endif #else #define loadWord(addr) *((int *)addr) #endif char *VM_Indent( vm_t *vm ) { static char *string = " "; if ( vm->callLevel > 20 ) { return string; } return string + 2 * ( 20 - vm->callLevel ); } void VM_StackTrace( vm_t *vm, int programCounter, int programStack ) { int count; count = 0; do { Com_Printf( "%s\n", VM_ValueToSymbol( vm, programCounter ) ); programStack = *(int *)&vm->dataBase[programStack+4]; programCounter = *(int *)&vm->dataBase[programStack]; } while ( programCounter != -1 && ++count < 32 ); } /* ==================== VM_PrepareInterpreter ==================== */ void VM_PrepareInterpreter( vm_t *vm, vmHeader_t *header ) { int op; int pc; byte *code; int instruction; int *codeBase; vm->codeBase = (byte*) Hunk_Alloc( vm->codeLength*4, h_high ); // we're now int aligned // memcpy( vm->codeBase, (byte *)header + header->codeOffset, vm->codeLength ); // we don't need to translate the instructions, but we still need // to find each instructions starting point for jumps pc = 0; instruction = 0; code = (byte *)header + header->codeOffset; codeBase = (int *)vm->codeBase; while ( instruction < header->instructionCount ) { vm->instructionPointers[ instruction ] = pc; instruction++; op = code[ pc ]; codeBase[pc] = op; if ( pc > header->codeLength ) { Com_Error( ERR_FATAL, "VM_PrepareInterpreter: pc > header->codeLength" ); } pc++; // these are the only opcodes that aren't a single byte switch ( op ) { case OP_ENTER: case OP_CONST: case OP_LOCAL: case OP_LEAVE: case OP_EQ: case OP_NE: case OP_LTI: case OP_LEI: case OP_GTI: case OP_GEI: case OP_LTU: case OP_LEU: case OP_GTU: case OP_GEU: case OP_EQF: case OP_NEF: case OP_LTF: case OP_LEF: case OP_GTF: case OP_GEF: case OP_BLOCK_COPY: codeBase[pc+0] = loadWord(&code[pc]); pc += 4; break; case OP_ARG: codeBase[pc+0] = code[pc]; pc += 1; break; default: break; } } pc = 0; instruction = 0; code = (byte *)header + header->codeOffset; codeBase = (int *)vm->codeBase; while ( instruction < header->instructionCount ) { op = code[ pc ]; instruction++; pc++; switch ( op ) { case OP_ENTER: case OP_CONST: case OP_LOCAL: case OP_LEAVE: case OP_EQ: case OP_NE: case OP_LTI: case OP_LEI: case OP_GTI: case OP_GEI: case OP_LTU: case OP_LEU: case OP_GTU: case OP_GEU: case OP_EQF: case OP_NEF: case OP_LTF: case OP_LEF: case OP_GTF: case OP_GEF: case OP_BLOCK_COPY: switch(op) { case OP_EQ: case OP_NE: case OP_LTI: case OP_LEI: case OP_GTI: case OP_GEI: case OP_LTU: case OP_LEU: case OP_GTU: case OP_GEU: case OP_EQF: case OP_NEF: case OP_LTF: case OP_LEF: case OP_GTF: case OP_GEF: codeBase[pc] = vm->instructionPointers[codeBase[pc]]; break; default: break; } pc += 4; break; case OP_ARG: pc += 1; break; default: break; } } } /* ============== VM_Call Upon a system call, the stack will look like: sp+32 parm1 sp+28 parm0 sp+24 return stack sp+20 return address sp+16 local1 sp+14 local0 sp+12 arg1 sp+8 arg0 sp+4 return stack sp return address An interpreted function will immediately execute an OP_ENTER instruction, which will subtract space for locals from sp ============== */ #define MAX_STACK 256 #define STACK_MASK (MAX_STACK-1) //#define DEBUG_VM #define DEBUGSTR va("%s%i", VM_Indent(vm), opStack-stack ) int VM_CallInterpreted( vm_t *vm, int *args ) { int stack[MAX_STACK]; int *opStack; int programCounter; int programStack; int stackOnEntry; byte *image; int *codeImage; int v1; int dataMask; #ifdef DEBUG_VM vmSymbol_t *profileSymbol; #endif // interpret the code vm->currentlyInterpreting = qtrue; // we might be called recursively, so this might not be the very top programStack = stackOnEntry = vm->programStack; #ifdef DEBUG_VM profileSymbol = VM_ValueToFunctionSymbol( vm, 0 ); // uncomment this for debugging breakpoints vm->breakFunction = 0; #endif // set up the stack frame image = vm->dataBase; codeImage = (int *)vm->codeBase; dataMask = vm->dataMask; // leave a free spot at start of stack so // that as long as opStack is valid, opStack-1 will // not corrupt anything opStack = stack; programCounter = 0; programStack -= 48; *(int *)&image[ programStack + 44] = args[9]; *(int *)&image[ programStack + 40] = args[8]; *(int *)&image[ programStack + 36] = args[7]; *(int *)&image[ programStack + 32] = args[6]; *(int *)&image[ programStack + 28] = args[5]; *(int *)&image[ programStack + 24] = args[4]; *(int *)&image[ programStack + 20] = args[3]; *(int *)&image[ programStack + 16] = args[2]; *(int *)&image[ programStack + 12] = args[1]; *(int *)&image[ programStack + 8 ] = args[0]; *(int *)&image[ programStack + 4 ] = 0; // return stack *(int *)&image[ programStack ] = -1; // will terminate the loop on return vm->callLevel = 0; VM_Debug(0); // vm_debugLevel=2; // main interpreter loop, will exit when a LEAVE instruction // grabs the -1 program counter #define r2 codeImage[programCounter] while ( 1 ) { int opcode, r0, r1; // unsigned int r2; nextInstruction: r0 = ((int *)opStack)[0]; r1 = ((int *)opStack)[-1]; nextInstruction2: opcode = codeImage[ programCounter++ ]; #ifdef DEBUG_VM if ( (unsigned)programCounter > vm->codeLength ) { Com_Error( ERR_DROP, "VM pc out of range" ); } if ( opStack < stack ) { Com_Error( ERR_DROP, "VM opStack underflow" ); } if ( opStack >= stack+MAX_STACK ) { Com_Error( ERR_DROP, "VM opStack overflow" ); } if ( programStack <= vm->stackBottom ) { Com_Error( ERR_DROP, "VM stack overflow" ); } if ( programStack & 3 ) { Com_Error( ERR_DROP, "VM program stack misaligned" ); } if ( vm_debugLevel > 1 ) { Com_Printf( "%s %s\n", DEBUGSTR, opnames[opcode] ); } profileSymbol->profileCount++; #endif switch ( opcode ) { #ifdef DEBUG_VM default: Com_Error( ERR_DROP, "Bad VM instruction" ); // this should be scanned on load! #endif case OP_BREAK: vm->breakCount++; goto nextInstruction2; case OP_CONST: opStack++; r1 = r0; r0 = *opStack = r2; programCounter += 4; goto nextInstruction2; case OP_LOCAL: opStack++; r1 = r0; r0 = *opStack = r2+programStack; programCounter += 4; goto nextInstruction2; case OP_LOAD4: #ifdef DEBUG_VM if ( *opStack & 3 ) { Com_Error( ERR_DROP, "OP_LOAD4 misaligned" ); } #endif r0 = *opStack = *(int *)&image[ r0&dataMask ]; goto nextInstruction2; case OP_LOAD2: r0 = *opStack = *(unsigned short *)&image[ r0&dataMask ]; goto nextInstruction2; case OP_LOAD1: r0 = *opStack = image[ r0&dataMask ]; goto nextInstruction2; case OP_STORE4: *(int *)&image[ r1&(dataMask & ~3) ] = r0; opStack -= 2; goto nextInstruction; case OP_STORE2: *(short *)&image[ r1&(dataMask & ~1) ] = r0; opStack -= 2; goto nextInstruction; case OP_STORE1: image[ r1&dataMask ] = r0; opStack -= 2; goto nextInstruction; case OP_ARG: // single byte offset from programStack *(int *)&image[ codeImage[programCounter] + programStack ] = r0; opStack--; programCounter += 1; goto nextInstruction; case OP_BLOCK_COPY: { int src = r0; int dest = r1; size_t n = r2; if ((dest & dataMask) != dest || (src & dataMask) != src || ((dest + n) & dataMask) != dest + n || ((src + n) & dataMask) != src + n) { Com_Error(ERR_DROP, "OP_BLOCK_COPY out of range!"); } Com_Memcpy(vm->dataBase + dest, vm->dataBase + src, n); programCounter += 4; opStack -= 2; } goto nextInstruction; case OP_CALL: // save current program counter *(int *)&image[ programStack ] = programCounter; // jump to the location on the stack programCounter = r0; opStack--; if ( programCounter < 0 ) { // system call int r; int temp; #ifdef DEBUG_VM int stomped; if ( vm_debugLevel ) { Com_Printf( "%s---> systemcall(%i)\n", DEBUGSTR, -1 - programCounter ); } #endif // save the stack to allow recursive VM entry temp = vm->callLevel; vm->programStack = programStack - 4; #ifdef DEBUG_VM stomped = *(int *)&image[ programStack + 4 ]; #endif *(int *)&image[ programStack + 4 ] = -1 - programCounter; //VM_LogSyscalls( (int *)&image[ programStack + 4 ] ); { intptr_t argarr[MAX_VMSYSCALL_ARGS]; int *imagePtr = (int *)&image[ programStack + 4 ]; int i; for (i = 0; i < ARRAY_LEN(argarr); i++) { argarr[i] = *imagePtr; imagePtr++; } r = vm->systemCall( argarr ); } #ifdef DEBUG_VM // this is just our stack frame pointer, only needed // for debugging *(int *)&image[ programStack + 4 ] = stomped; #endif // save return value opStack++; *opStack = r; programCounter = *(int *)&image[ programStack ]; vm->callLevel = temp; #ifdef DEBUG_VM if ( vm_debugLevel ) { Com_Printf( "%s<--- %s\n", DEBUGSTR, VM_ValueToSymbol( vm, programCounter ) ); } #endif } else { programCounter = vm->instructionPointers[ programCounter ]; } goto nextInstruction; // push and pop are only needed for discarded or bad function return values case OP_PUSH: opStack++; goto nextInstruction; case OP_POP: opStack--; goto nextInstruction; case OP_ENTER: #ifdef DEBUG_VM profileSymbol = VM_ValueToFunctionSymbol( vm, programCounter ); #endif // get size of stack frame v1 = r2; programCounter += 4; programStack -= v1; #ifdef DEBUG_VM // save old stack frame for debugging traces *(int *)&image[programStack+4] = programStack + v1; if ( vm_debugLevel ) { Com_Printf( "%s---> %s\n", DEBUGSTR, VM_ValueToSymbol( vm, programCounter - 5 ) ); if ( vm->breakFunction && programCounter - 5 == vm->breakFunction ) { // this is to allow setting breakpoints here in the debugger vm->breakCount++; // vm_debugLevel = 2; // VM_StackTrace( vm, programCounter, programStack ); } vm->callLevel++; } #endif goto nextInstruction; case OP_LEAVE: // remove our stack frame v1 = r2; programStack += v1; // grab the saved program counter programCounter = *(int *)&image[ programStack ]; #ifdef DEBUG_VM profileSymbol = VM_ValueToFunctionSymbol( vm, programCounter ); if ( vm_debugLevel ) { vm->callLevel--; Com_Printf( "%s<--- %s\n", DEBUGSTR, VM_ValueToSymbol( vm, programCounter ) ); } #endif // check for leaving the VM if ( programCounter == -1 ) { goto done; } goto nextInstruction; /* =================================================================== BRANCHES =================================================================== */ case OP_JUMP: programCounter = r0; programCounter = vm->instructionPointers[ programCounter ]; opStack--; goto nextInstruction; case OP_EQ: opStack -= 2; if ( r1 == r0 ) { programCounter = r2; //vm->instructionPointers[r2]; goto nextInstruction; } else { programCounter += 4; goto nextInstruction; } case OP_NE: opStack -= 2; if ( r1 != r0 ) { programCounter = r2; //vm->instructionPointers[r2]; goto nextInstruction; } else { programCounter += 4; goto nextInstruction; } case OP_LTI: opStack -= 2; if ( r1 < r0 ) { programCounter = r2; //vm->instructionPointers[r2]; goto nextInstruction; } else { programCounter += 4; goto nextInstruction; } case OP_LEI: opStack -= 2; if ( r1 <= r0 ) { programCounter = r2; //vm->instructionPointers[r2]; goto nextInstruction; } else { programCounter += 4; goto nextInstruction; } case OP_GTI: opStack -= 2; if ( r1 > r0 ) { programCounter = r2; //vm->instructionPointers[r2]; goto nextInstruction; } else { programCounter += 4; goto nextInstruction; } case OP_GEI: opStack -= 2; if ( r1 >= r0 ) { programCounter = r2; //vm->instructionPointers[r2]; goto nextInstruction; } else { programCounter += 4; goto nextInstruction; } case OP_LTU: opStack -= 2; if ( ((unsigned)r1) < ((unsigned)r0) ) { programCounter = r2; //vm->instructionPointers[r2]; goto nextInstruction; } else { programCounter += 4; goto nextInstruction; } case OP_LEU: opStack -= 2; if ( ((unsigned)r1) <= ((unsigned)r0) ) { programCounter = r2; //vm->instructionPointers[r2]; goto nextInstruction; } else { programCounter += 4; goto nextInstruction; } case OP_GTU: opStack -= 2; if ( ((unsigned)r1) > ((unsigned)r0) ) { programCounter = r2; //vm->instructionPointers[r2]; goto nextInstruction; } else { programCounter += 4; goto nextInstruction; } case OP_GEU: opStack -= 2; if ( ((unsigned)r1) >= ((unsigned)r0) ) { programCounter = r2; //vm->instructionPointers[r2]; goto nextInstruction; } else { programCounter += 4; goto nextInstruction; } case OP_EQF: if ( ((float *)opStack)[-1] == *(float *)opStack ) { programCounter = r2; //vm->instructionPointers[r2]; opStack -= 2; goto nextInstruction; } else { programCounter += 4; opStack -= 2; goto nextInstruction; } case OP_NEF: if ( ((float *)opStack)[-1] != *(float *)opStack ) { programCounter = r2; //vm->instructionPointers[r2]; opStack -= 2; goto nextInstruction; } else { programCounter += 4; opStack -= 2; goto nextInstruction; } case OP_LTF: if ( ((float *)opStack)[-1] < *(float *)opStack ) { programCounter = r2; //vm->instructionPointers[r2]; opStack -= 2; goto nextInstruction; } else { programCounter += 4; opStack -= 2; goto nextInstruction; } case OP_LEF: if ( ((float *)opStack)[-1] <= *(float *)opStack ) { programCounter = r2; //vm->instructionPointers[r2]; opStack -= 2; goto nextInstruction; } else { programCounter += 4; opStack -= 2; goto nextInstruction; } case OP_GTF: if ( ((float *)opStack)[-1] > *(float *)opStack ) { programCounter = r2; //vm->instructionPointers[r2]; opStack -= 2; goto nextInstruction; } else { programCounter += 4; opStack -= 2; goto nextInstruction; } case OP_GEF: if ( ((float *)opStack)[-1] >= *(float *)opStack ) { programCounter = r2; //vm->instructionPointers[r2]; opStack -= 2; goto nextInstruction; } else { programCounter += 4; opStack -= 2; goto nextInstruction; } //=================================================================== case OP_NEGI: *opStack = -r0; goto nextInstruction; case OP_ADD: opStack[-1] = r1 + r0; opStack--; goto nextInstruction; case OP_SUB: opStack[-1] = r1 - r0; opStack--; goto nextInstruction; case OP_DIVI: opStack[-1] = r1 / r0; opStack--; goto nextInstruction; case OP_DIVU: opStack[-1] = ((unsigned)r1) / ((unsigned)r0); opStack--; goto nextInstruction; case OP_MODI: opStack[-1] = r1 % r0; opStack--; goto nextInstruction; case OP_MODU: opStack[-1] = ((unsigned)r1) % (unsigned)r0; opStack--; goto nextInstruction; case OP_MULI: opStack[-1] = r1 * r0; opStack--; goto nextInstruction; case OP_MULU: opStack[-1] = ((unsigned)r1) * ((unsigned)r0); opStack--; goto nextInstruction; case OP_BAND: opStack[-1] = ((unsigned)r1) & ((unsigned)r0); opStack--; goto nextInstruction; case OP_BOR: opStack[-1] = ((unsigned)r1) | ((unsigned)r0); opStack--; goto nextInstruction; case OP_BXOR: opStack[-1] = ((unsigned)r1) ^ ((unsigned)r0); opStack--; goto nextInstruction; case OP_BCOM: opStack[-1] = ~ ((unsigned)r0); goto nextInstruction; case OP_LSH: opStack[-1] = r1 << r0; opStack--; goto nextInstruction; case OP_RSHI: opStack[-1] = r1 >> r0; opStack--; goto nextInstruction; case OP_RSHU: opStack[-1] = ((unsigned)r1) >> r0; opStack--; goto nextInstruction; case OP_NEGF: *(float *)opStack = -*(float *)opStack; goto nextInstruction; case OP_ADDF: *(float *)(opStack-1) = *(float *)(opStack-1) + *(float *)opStack; opStack--; goto nextInstruction; case OP_SUBF: *(float *)(opStack-1) = *(float *)(opStack-1) - *(float *)opStack; opStack--; goto nextInstruction; case OP_DIVF: *(float *)(opStack-1) = *(float *)(opStack-1) / *(float *)opStack; opStack--; goto nextInstruction; case OP_MULF: *(float *)(opStack-1) = *(float *)(opStack-1) * *(float *)opStack; opStack--; goto nextInstruction; case OP_CVIF: *(float *)opStack = (float)*opStack; goto nextInstruction; case OP_CVFI: *opStack = (int) *(float *)opStack; goto nextInstruction; case OP_SEX8: *opStack = (signed char)*opStack; goto nextInstruction; case OP_SEX16: *opStack = (short)*opStack; goto nextInstruction; } } done: vm->currentlyInterpreting = qfalse; if ( opStack != &stack[1] ) { Com_Error( ERR_DROP, "Interpreter error: opStack = %i", opStack - stack ); } vm->programStack = stackOnEntry; // return the result return *opStack; } ================================================ FILE: src/engine/qcommon/vm_local.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "../../game/q_shared.h" #include "qcommon.h" // Max number of arguments to pass from a vm to engine's syscall handler function for the vm. // syscall number + 15 arguments #define MAX_VMSYSCALL_ARGS 16 typedef enum { OP_UNDEF, OP_IGNORE, OP_BREAK, OP_ENTER, OP_LEAVE, OP_CALL, OP_PUSH, OP_POP, OP_CONST, OP_LOCAL, OP_JUMP, //------------------- OP_EQ, OP_NE, OP_LTI, OP_LEI, OP_GTI, OP_GEI, OP_LTU, OP_LEU, OP_GTU, OP_GEU, OP_EQF, OP_NEF, OP_LTF, OP_LEF, OP_GTF, OP_GEF, //------------------- OP_LOAD1, OP_LOAD2, OP_LOAD4, OP_STORE1, OP_STORE2, OP_STORE4, // *(stack[top-1]) = stack[top] OP_ARG, OP_BLOCK_COPY, //------------------- OP_SEX8, OP_SEX16, OP_NEGI, OP_ADD, OP_SUB, OP_DIVI, OP_DIVU, OP_MODI, OP_MODU, OP_MULI, OP_MULU, OP_BAND, OP_BOR, OP_BXOR, OP_BCOM, OP_LSH, OP_RSHI, OP_RSHU, OP_NEGF, OP_ADDF, OP_SUBF, OP_DIVF, OP_MULF, OP_CVIF, OP_CVFI } opcode_t; typedef int vmptr_t; typedef struct vmSymbol_s { struct vmSymbol_s *next; int symValue; int profileCount; char symName[1]; // variable sized } vmSymbol_t; #define VM_OFFSET_PROGRAM_STACK 0 #define VM_OFFSET_SYSTEM_CALL 4 struct vm_s { // DO NOT MOVE OR CHANGE THESE WITHOUT CHANGING THE VM_OFFSET_* DEFINES // USED BY THE ASM CODE int programStack; // the vm may be recursively entered intptr_t (*systemCall)( intptr_t *parms ); //------------------------------------ char name[MAX_QPATH]; // for dynamic linked modules void *dllHandle; intptr_t (QDECL *entryPoint)( int callNum, ... ); // for interpreted modules qboolean currentlyInterpreting; byte *codeBase; int codeLength; int *instructionPointers; int instructionPointersLength; byte *dataBase; int dataMask; int stackBottom; // if programStack < stackBottom, error int numSymbols; struct vmSymbol_s *symbols; int callLevel; // for debug indenting int breakFunction; // increment breakCount on function entry to this int breakCount; // fqpath member added 7/20/02 by T.Ray char fqpath[MAX_QPATH+1] ; }; extern vm_t *currentVM; extern int vm_debugLevel; void VM_PrepareInterpreter( vm_t *vm, vmHeader_t *header ); int VM_CallInterpreted( vm_t *vm, int *args ); vmSymbol_t *VM_ValueToFunctionSymbol( vm_t *vm, int value ); int VM_SymbolToValue( vm_t *vm, const char *symbol ); const char *VM_ValueToSymbol( vm_t *vm, int value ); void VM_LogSyscalls( int *args ); ================================================ FILE: src/engine/renderer/dx.cpp ================================================ #include "tr_local.h" #include "../../engine/platform/win_local.h" #include #include #ifdef ENABLE_DX12 #include "D3d12.h" #include "DXGI1_5.h" #pragma comment (lib, "D3d12.lib") #pragma comment (lib, "DXGI.lib") const int VERTEX_CHUNK_SIZE = 512 * 1024; const int XYZ_SIZE = 4 * VERTEX_CHUNK_SIZE; const int COLOR_SIZE = 1 * VERTEX_CHUNK_SIZE; const int ST0_SIZE = 2 * VERTEX_CHUNK_SIZE; const int ST1_SIZE = 2 * VERTEX_CHUNK_SIZE; const int XYZ_OFFSET = 0; const int COLOR_OFFSET = XYZ_OFFSET + XYZ_SIZE; const int ST0_OFFSET = COLOR_OFFSET + COLOR_SIZE; const int ST1_OFFSET = ST0_OFFSET + ST0_SIZE; const int VERTEX_BUFFER_SIZE = XYZ_SIZE + COLOR_SIZE + ST0_SIZE + ST1_SIZE; const int INDEX_BUFFER_SIZE = 2 * 1024 * 1024; #define DX_CHECK(function_call) { \ HRESULT hr = function_call; \ if (FAILED(hr)) \ ri.Error(ERR_FATAL, "Direct3D: error returned by %s", #function_call); \ } static DXGI_FORMAT get_depth_format() { if (r_stencilbits->integer > 0) { glConfig.stencilBits = 8; return DXGI_FORMAT_D24_UNORM_S8_UINT; } else { glConfig.stencilBits = 0; return DXGI_FORMAT_D32_FLOAT; } } static void get_hardware_adapter(IDXGIFactory4* factory, IDXGIAdapter1** adapter) { DXGI_ADAPTER_DESC1 desc; UINT adapter_index = 0; while (factory->EnumAdapters1(adapter_index++, adapter) != DXGI_ERROR_NOT_FOUND) { (*adapter)->GetDesc1(&desc); if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) { continue; } // check for 11_0 feature level support if (SUCCEEDED(D3D12CreateDevice(*adapter, D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), nullptr))) { return; } } *adapter = nullptr; } static void record_and_run_commands(std::function recorder) { ID3D12GraphicsCommandList* command_list; DX_CHECK(dx.device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, dx.helper_command_allocator, nullptr, IID_PPV_ARGS(&command_list))); recorder(command_list); DX_CHECK(command_list->Close()); ID3D12CommandList* command_lists[] = { command_list }; dx.command_queue->ExecuteCommandLists(1, command_lists); dx_wait_device_idle(); command_list->Release(); dx.helper_command_allocator->Reset(); } static D3D12_RESOURCE_BARRIER get_transition_barrier( ID3D12Resource* resource, D3D12_RESOURCE_STATES state_before, D3D12_RESOURCE_STATES state_after) { D3D12_RESOURCE_BARRIER barrier; barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; barrier.Transition.pResource = resource; barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; barrier.Transition.StateBefore = state_before; barrier.Transition.StateAfter = state_after; return barrier; } static D3D12_HEAP_PROPERTIES get_heap_properties(D3D12_HEAP_TYPE heap_type) { D3D12_HEAP_PROPERTIES properties; properties.Type = heap_type; properties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; properties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; properties.CreationNodeMask = 1; properties.VisibleNodeMask = 1; return properties; } static D3D12_RESOURCE_DESC get_buffer_desc(UINT64 size) { D3D12_RESOURCE_DESC desc; desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; desc.Alignment = 0; desc.Width = size; desc.Height = 1; desc.DepthOrArraySize = 1; desc.MipLevels = 1; desc.Format = DXGI_FORMAT_UNKNOWN; desc.SampleDesc.Count = 1; desc.SampleDesc.Quality = 0; desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; desc.Flags = D3D12_RESOURCE_FLAG_NONE; return desc; } ID3D12PipelineState* create_pipeline(const Vk_Pipeline_Def& def); void dx_initialize() { // enable validation in debug configuration #if defined(_DEBUG) ID3D12Debug* debug_controller; if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debug_controller)))) { debug_controller->EnableDebugLayer(); debug_controller->Release(); } #endif IDXGIFactory5* factory; DX_CHECK(CreateDXGIFactory1(IID_PPV_ARGS(&factory))); // Create device. { IDXGIAdapter1* hardware_adapter; get_hardware_adapter(factory, &hardware_adapter); DX_CHECK(D3D12CreateDevice(hardware_adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&dx.device))); hardware_adapter->Release(); } // Create command queue. { D3D12_COMMAND_QUEUE_DESC queue_desc{}; queue_desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; queue_desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; DX_CHECK(dx.device->CreateCommandQueue(&queue_desc, IID_PPV_ARGS(&dx.command_queue))); } // // Create swap chain. // { DX_CHECK(factory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &dx.present_allow_tearing, sizeof(dx.present_allow_tearing))); DXGI_SWAP_CHAIN_DESC1 swap_chain_desc{}; swap_chain_desc.BufferCount = SWAPCHAIN_BUFFER_COUNT; swap_chain_desc.Width = glConfig.vidWidth; swap_chain_desc.Height = glConfig.vidHeight; swap_chain_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; swap_chain_desc.SampleDesc.Count = 1; swap_chain_desc.Flags = dx.present_allow_tearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0; IDXGISwapChain1* swapchain; DX_CHECK(factory->CreateSwapChainForHwnd( dx.command_queue, g_wv.hWnd_dx, &swap_chain_desc, nullptr, nullptr, &swapchain )); DX_CHECK(factory->MakeWindowAssociation(g_wv.hWnd_dx, DXGI_MWA_NO_ALT_ENTER)); swapchain->QueryInterface(__uuidof(IDXGISwapChain3), (void**)&dx.swapchain); swapchain->Release(); for (int i = 0; i < SWAPCHAIN_BUFFER_COUNT; i++) { DX_CHECK(dx.swapchain->GetBuffer(i, IID_PPV_ARGS(&dx.render_targets[i]))); } } factory->Release(); factory = nullptr; // // Create command allocators and command list. // { DX_CHECK(dx.device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&dx.command_allocator))); DX_CHECK(dx.device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&dx.helper_command_allocator))); DX_CHECK(dx.device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, dx.command_allocator, nullptr, IID_PPV_ARGS(&dx.command_list))); DX_CHECK(dx.command_list->Close()); } // // Create synchronization objects. // { DX_CHECK(dx.device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&dx.fence))); dx.fence_event = CreateEvent(nullptr, FALSE, FALSE, nullptr); } // // Create descriptor heaps. // { // RTV heap. { D3D12_DESCRIPTOR_HEAP_DESC heap_desc; heap_desc.NumDescriptors = SWAPCHAIN_BUFFER_COUNT; heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; heap_desc.NodeMask = 0; DX_CHECK(dx.device->CreateDescriptorHeap(&heap_desc, IID_PPV_ARGS(&dx.rtv_heap))); dx.rtv_descriptor_size = dx.device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); } // DSV heap. { D3D12_DESCRIPTOR_HEAP_DESC heap_desc; heap_desc.NumDescriptors = 1; heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV; heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; heap_desc.NodeMask = 0; DX_CHECK(dx.device->CreateDescriptorHeap(&heap_desc, IID_PPV_ARGS(&dx.dsv_heap))); } // SRV heap. { D3D12_DESCRIPTOR_HEAP_DESC heap_desc; heap_desc.NumDescriptors = MAX_DRAWIMAGES; heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; heap_desc.NodeMask = 0; DX_CHECK(dx.device->CreateDescriptorHeap(&heap_desc, IID_PPV_ARGS(&dx.srv_heap))); dx.srv_descriptor_size = dx.device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); } // Sampler heap. { D3D12_DESCRIPTOR_HEAP_DESC heap_desc; heap_desc.NumDescriptors = SAMPLER_COUNT; heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER; heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; heap_desc.NodeMask = 0; DX_CHECK(dx.device->CreateDescriptorHeap(&heap_desc, IID_PPV_ARGS(&dx.sampler_heap))); dx.sampler_descriptor_size = dx.device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); } } // // Create descriptors. // { // RTV descriptors. { D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle = dx.rtv_heap->GetCPUDescriptorHandleForHeapStart(); for (int i = 0; i < SWAPCHAIN_BUFFER_COUNT; i++) { dx.device->CreateRenderTargetView(dx.render_targets[i], nullptr, rtv_handle); rtv_handle.ptr += dx.rtv_descriptor_size; } } // Samplers. { { Vk_Sampler_Def def; def.repeat_texture = true; def.gl_mag_filter = gl_filter_max; def.gl_min_filter = gl_filter_min; dx_create_sampler_descriptor(def, SAMPLER_MIP_REPEAT); } { Vk_Sampler_Def def; def.repeat_texture = false; def.gl_mag_filter = gl_filter_max; def.gl_min_filter = gl_filter_min; dx_create_sampler_descriptor(def, SAMPLER_MIP_CLAMP); } { Vk_Sampler_Def def; def.repeat_texture = true; def.gl_mag_filter = GL_LINEAR; def.gl_min_filter = GL_LINEAR; dx_create_sampler_descriptor(def, SAMPLER_NOMIP_REPEAT); } { Vk_Sampler_Def def; def.repeat_texture = false; def.gl_mag_filter = GL_LINEAR; def.gl_min_filter = GL_LINEAR; dx_create_sampler_descriptor(def, SAMPLER_NOMIP_CLAMP); } } } // // Create depth buffer resources. // { D3D12_RESOURCE_DESC depth_desc{}; depth_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; depth_desc.Alignment = 0; depth_desc.Width = glConfig.vidWidth; depth_desc.Height = glConfig.vidHeight; depth_desc.DepthOrArraySize = 1; depth_desc.MipLevels = 1; depth_desc.Format = get_depth_format(); depth_desc.SampleDesc.Count = 1; depth_desc.SampleDesc.Quality = 0; depth_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; depth_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; D3D12_CLEAR_VALUE optimized_clear_value{}; optimized_clear_value.Format = get_depth_format(); optimized_clear_value.DepthStencil.Depth = 1.0f; optimized_clear_value.DepthStencil.Stencil = 0; DX_CHECK(dx.device->CreateCommittedResource( &get_heap_properties(D3D12_HEAP_TYPE_DEFAULT), D3D12_HEAP_FLAG_NONE, &depth_desc, D3D12_RESOURCE_STATE_DEPTH_WRITE, &optimized_clear_value, IID_PPV_ARGS(&dx.depth_stencil_buffer))); D3D12_DEPTH_STENCIL_VIEW_DESC view_desc{}; view_desc.Format = get_depth_format(); view_desc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D; view_desc.Flags = D3D12_DSV_FLAG_NONE; dx.device->CreateDepthStencilView(dx.depth_stencil_buffer, &view_desc, dx.dsv_heap->GetCPUDescriptorHandleForHeapStart()); } // // Create root signature. // { D3D12_DESCRIPTOR_RANGE ranges[4] = {}; ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; ranges[0].NumDescriptors = 1; ranges[0].BaseShaderRegister = 0; ranges[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER; ranges[1].NumDescriptors = 1; ranges[1].BaseShaderRegister = 0; ranges[2].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; ranges[2].NumDescriptors = 1; ranges[2].BaseShaderRegister = 1; ranges[3].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER; ranges[3].NumDescriptors = 1; ranges[3].BaseShaderRegister = 1; D3D12_ROOT_PARAMETER root_parameters[5] {}; root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS; root_parameters[0].Constants.ShaderRegister = 0; root_parameters[0].Constants.Num32BitValues = 32; root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; for (int i = 1; i < 5; i++) { root_parameters[i].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; root_parameters[i].DescriptorTable.NumDescriptorRanges = 1; root_parameters[i].DescriptorTable.pDescriptorRanges = &ranges[i-1]; root_parameters[i].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; } D3D12_ROOT_SIGNATURE_DESC root_signature_desc; root_signature_desc.NumParameters = _countof(root_parameters); root_signature_desc.pParameters = root_parameters; root_signature_desc.NumStaticSamplers = 0; root_signature_desc.pStaticSamplers = nullptr; root_signature_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT; ID3DBlob* signature; ID3DBlob* error; DX_CHECK(D3D12SerializeRootSignature(&root_signature_desc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error)); DX_CHECK(dx.device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&dx.root_signature))); if (signature != nullptr) signature->Release(); if (error != nullptr) error->Release(); } // // Geometry buffers. // { // store geometry in upload heap since Q3 regenerates it every frame DX_CHECK(dx.device->CreateCommittedResource( &get_heap_properties(D3D12_HEAP_TYPE_UPLOAD), D3D12_HEAP_FLAG_NONE, &get_buffer_desc(VERTEX_BUFFER_SIZE + INDEX_BUFFER_SIZE), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&dx.geometry_buffer))); void* p_data; D3D12_RANGE read_range{}; DX_CHECK(dx.geometry_buffer->Map(0, &read_range, &p_data)); dx.vertex_buffer_ptr = static_cast(p_data); assert((VERTEX_BUFFER_SIZE & 0xffff) == 0); // index buffer offset should be 64K aligned. dx.index_buffer_ptr = static_cast(p_data) + VERTEX_BUFFER_SIZE; } // // Standard pipelines. // { // skybox { Vk_Pipeline_Def def; def.shader_type = Vk_Shader_Type::single_texture; def.state_bits = 0; def.face_culling = CT_FRONT_SIDED; def.polygon_offset = false; def.clipping_plane = false; def.mirror = false; dx.skybox_pipeline = create_pipeline(def); } // Q3 stencil shadows { { Vk_Pipeline_Def def; def.polygon_offset = false; def.state_bits = 0; def.shader_type = Vk_Shader_Type::single_texture; def.clipping_plane = false; def.shadow_phase = Vk_Shadow_Phase::shadow_edges_rendering; cullType_t cull_types[2] = {CT_FRONT_SIDED, CT_BACK_SIDED}; bool mirror_flags[2] = {false, true}; for (int i = 0; i < 2; i++) { def.face_culling = cull_types[i]; for (int j = 0; j < 2; j++) { def.mirror = mirror_flags[j]; dx.shadow_volume_pipelines[i][j] = create_pipeline(def); } } } { Vk_Pipeline_Def def; def.face_culling = CT_FRONT_SIDED; def.polygon_offset = false; def.state_bits = GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO; def.shader_type = Vk_Shader_Type::single_texture; def.clipping_plane = false; def.mirror = false; def.shadow_phase = Vk_Shadow_Phase::fullscreen_quad_rendering; dx.shadow_finish_pipeline = create_pipeline(def); } } // fog and dlights { Vk_Pipeline_Def def; def.shader_type = Vk_Shader_Type::single_texture; def.clipping_plane = false; def.mirror = false; unsigned int fog_state_bits[2] = { GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA | GLS_DEPTHFUNC_EQUAL, GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA }; unsigned int dlight_state_bits[2] = { GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL }; bool polygon_offset[2] = {false, true}; for (int i = 0; i < 2; i++) { unsigned fog_state = fog_state_bits[i]; unsigned dlight_state = dlight_state_bits[i]; for (int j = 0; j < 3; j++) { def.face_culling = j; // cullType_t value for (int k = 0; k < 2; k++) { def.polygon_offset = polygon_offset[k]; def.state_bits = fog_state; dx.fog_pipelines[i][j][k] = create_pipeline(def); def.state_bits = dlight_state; dx.dlight_pipelines[i][j][k] = create_pipeline(def); } } } } // debug pipelines { Vk_Pipeline_Def def; def.state_bits = GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE; dx.tris_debug_pipeline = create_pipeline(def); } { Vk_Pipeline_Def def; def.state_bits = GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE; def.face_culling = CT_BACK_SIDED; dx.tris_mirror_debug_pipeline = create_pipeline(def); } { Vk_Pipeline_Def def; def.state_bits = GLS_DEPTHMASK_TRUE; def.line_primitives = true; dx.normals_debug_pipeline = create_pipeline(def); } { Vk_Pipeline_Def def; def.state_bits = GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE; dx.surface_debug_pipeline_solid = create_pipeline(def); } { Vk_Pipeline_Def def; def.state_bits = GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE; def.line_primitives = true; dx.surface_debug_pipeline_outline = create_pipeline(def); } { Vk_Pipeline_Def def; def.state_bits = GLS_DEPTHTEST_DISABLE | GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; dx.images_debug_pipeline = create_pipeline(def); } } dx.active = true; } void dx_shutdown() { ::CloseHandle(dx.fence_event); for (int i = 0; i < SWAPCHAIN_BUFFER_COUNT; i++) { dx.render_targets[i]->Release(); } for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { dx.shadow_volume_pipelines[i][j]->Release(); } } for (int i = 0; i < 2; i++) { for (int j = 0; j < 3; j++) { for (int k = 0; k < 2; k++) { dx.fog_pipelines[i][j][k]->Release(); dx.dlight_pipelines[i][j][k]->Release(); } } } dx.swapchain->Release(); dx.command_allocator->Release(); dx.helper_command_allocator->Release(); dx.rtv_heap->Release(); dx.srv_heap->Release(); dx.sampler_heap->Release(); dx.root_signature->Release(); dx.command_queue->Release(); dx.command_list->Release(); dx.fence->Release(); dx.depth_stencil_buffer->Release(); dx.dsv_heap->Release(); dx.geometry_buffer->Release(); dx.skybox_pipeline->Release(); dx.shadow_finish_pipeline->Release(); dx.tris_debug_pipeline->Release(); dx.tris_mirror_debug_pipeline->Release(); dx.normals_debug_pipeline->Release(); dx.surface_debug_pipeline_solid->Release(); dx.surface_debug_pipeline_outline->Release(); dx.images_debug_pipeline->Release(); dx.device->Release(); Com_Memset(&dx, 0, sizeof(dx)); } void dx_release_resources() { dx_wait_device_idle(); dx_world.pipeline_create_time = 0.0f; for (int i = 0; i < dx_world.num_pipelines; i++) { dx_world.pipelines[i]->Release(); } for (int i = 0; i < MAX_VK_IMAGES; i++) { if (dx_world.images[i].texture != nullptr) { dx_world.images[i].texture->Release(); } } Com_Memset(&dx_world, 0, sizeof(dx_world)); // Reset geometry buffer's current offsets. dx.xyz_elements = 0; dx.color_st_elements = 0; dx.index_buffer_offset = 0; } void dx_wait_device_idle() { dx.fence_value++; DX_CHECK(dx.command_queue->Signal(dx.fence, dx.fence_value)); DX_CHECK(dx.fence->SetEventOnCompletion(dx.fence_value, dx.fence_event)); WaitForSingleObject(dx.fence_event, INFINITE); } Dx_Image dx_create_image(int width, int height, Dx_Image_Format format, int mip_levels, bool repeat_texture, int image_index) { Dx_Image image; DXGI_FORMAT dx_format; if (format == IMAGE_FORMAT_RGBA8) dx_format = DXGI_FORMAT_R8G8B8A8_UNORM; else if (format == IMAGE_FORMAT_BGRA4) dx_format = DXGI_FORMAT_B4G4R4A4_UNORM; else { assert(format == IMAGE_FORMAT_BGR5A1); dx_format = DXGI_FORMAT_B5G5R5A1_UNORM; } // create texture { D3D12_RESOURCE_DESC desc; desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; desc.Alignment = 0; desc.Width = width; desc.Height = height; desc.DepthOrArraySize = 1; desc.MipLevels = mip_levels; desc.Format = dx_format; desc.SampleDesc.Count = 1; desc.SampleDesc.Quality = 0; desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; desc.Flags = D3D12_RESOURCE_FLAG_NONE; DX_CHECK(dx.device->CreateCommittedResource( &get_heap_properties(D3D12_HEAP_TYPE_DEFAULT), D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, nullptr, IID_PPV_ARGS(&image.texture))); } // create texture descriptor { D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc{}; srv_desc.Format = dx_format; srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; srv_desc.Texture2D.MipLevels = mip_levels; D3D12_CPU_DESCRIPTOR_HANDLE handle; handle.ptr = dx.srv_heap->GetCPUDescriptorHandleForHeapStart().ptr + image_index * dx.srv_descriptor_size; dx.device->CreateShaderResourceView(image.texture, &srv_desc, handle); dx_world.current_image_indices[glState.currenttmu] = image_index; } if (mip_levels > 0) image.sampler_index = repeat_texture ? SAMPLER_MIP_REPEAT : SAMPLER_MIP_CLAMP; else image.sampler_index = repeat_texture ? SAMPLER_NOMIP_REPEAT : SAMPLER_NOMIP_CLAMP; return image; } void dx_upload_image_data(ID3D12Resource* texture, int width, int height, int mip_levels, const uint8_t* pixels, int bytes_per_pixel) { // // Initialize subresource layouts int the upload texture. // auto align =[](size_t value, size_t alignment) { return (value + alignment - 1) & ~(alignment - 1); }; D3D12_PLACED_SUBRESOURCE_FOOTPRINT regions[16]; UINT64 buffer_size = 0; int w = width; int h = height; for (int i = 0; i < mip_levels; i++) { regions[i].Offset = buffer_size; regions[i].Footprint.Format = texture->GetDesc().Format; regions[i].Footprint.Width = w; regions[i].Footprint.Height = h; regions[i].Footprint.Depth = 1; regions[i].Footprint.RowPitch = static_cast(align(w * bytes_per_pixel, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT)); buffer_size += align(regions[i].Footprint.RowPitch * h, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT); w >>= 1; if (w < 1) w = 1; h >>= 1; if (h < 1) h = 1; } // // Create upload upload texture. // ID3D12Resource* upload_texture; DX_CHECK(dx.device->CreateCommittedResource( &get_heap_properties(D3D12_HEAP_TYPE_UPLOAD), D3D12_HEAP_FLAG_NONE, &get_buffer_desc(buffer_size), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&upload_texture))); byte* upload_texture_data; DX_CHECK(upload_texture->Map(0, nullptr, reinterpret_cast(&upload_texture_data))); w = width; h = height; for (int i = 0; i < mip_levels; i++) { byte* upload_subresource_base = upload_texture_data + regions[i].Offset; for (int y = 0; y < h; y++) { Com_Memcpy(upload_subresource_base + regions[i].Footprint.RowPitch * y, pixels, w * bytes_per_pixel); pixels += w * bytes_per_pixel; } w >>= 1; if (w < 1) w = 1; h >>= 1; if (h < 1) h = 1; } upload_texture->Unmap(0, nullptr); // // Copy data from upload texture to destination texture. // record_and_run_commands([texture, upload_texture, ®ions, mip_levels] (ID3D12GraphicsCommandList* command_list) { command_list->ResourceBarrier(1, &get_transition_barrier(texture, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_COPY_DEST)); for (UINT i = 0; i < mip_levels; ++i) { D3D12_TEXTURE_COPY_LOCATION dst; dst.pResource = texture; dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; dst.SubresourceIndex = i; D3D12_TEXTURE_COPY_LOCATION src; src.pResource = upload_texture; src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; src.PlacedFootprint = regions[i]; command_list->CopyTextureRegion(&dst, 0, 0, 0, &src, nullptr); } command_list->ResourceBarrier(1, &get_transition_barrier(texture, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE)); }); upload_texture->Release(); } static ID3D12PipelineState* create_pipeline(const Vk_Pipeline_Def& def) { // single texture VS extern unsigned char single_texture_vs[]; extern long long single_texture_vs_size; extern unsigned char single_texture_clipping_plane_vs[]; extern long long single_texture_clipping_plane_vs_size; // multi texture VS extern unsigned char multi_texture_vs[]; extern long long multi_texture_vs_size; extern unsigned char multi_texture_clipping_plane_vs[]; extern long long multi_texture_clipping_plane_vs_size; // single texture PS extern unsigned char single_texture_ps[]; extern long long single_texture_ps_size; extern unsigned char single_texture_gt0_ps[]; extern long long single_texture_gt0_ps_size; extern unsigned char single_texture_lt80_ps[]; extern long long single_texture_lt80_ps_size; extern unsigned char single_texture_ge80_ps[]; extern long long single_texture_ge80_ps_size; // multi texture mul PS extern unsigned char multi_texture_mul_ps[]; extern long long multi_texture_mul_ps_size; extern unsigned char multi_texture_mul_gt0_ps[]; extern long long multi_texture_mul_gt0_ps_size; extern unsigned char multi_texture_mul_lt80_ps[]; extern long long multi_texture_mul_lt80_ps_size; extern unsigned char multi_texture_mul_ge80_ps[]; extern long long multi_texture_mul_ge80_ps_size; // multi texture add PS extern unsigned char multi_texture_add_ps[]; extern long long multi_texture_add_ps_size; extern unsigned char multi_texture_add_gt0_ps[]; extern long long multi_texture_add_gt0_ps_size; extern unsigned char multi_texture_add_lt80_ps[]; extern long long multi_texture_add_lt80_ps_size; extern unsigned char multi_texture_add_ge80_ps[]; extern long long multi_texture_add_ge80_ps_size; #define BYTECODE(name) D3D12_SHADER_BYTECODE{name, (SIZE_T)name##_size} #define GET_PS_BYTECODE(base_name) \ if ((def.state_bits & GLS_ATEST_BITS) == 0) \ ps_bytecode = BYTECODE(base_name##_ps); \ else if (def.state_bits & GLS_ATEST_GT_0) \ ps_bytecode = BYTECODE(base_name##_gt0_ps); \ else if (def.state_bits & GLS_ATEST_LT_80) \ ps_bytecode = BYTECODE(base_name##_lt80_ps); \ else if (def.state_bits & GLS_ATEST_GE_80) \ ps_bytecode = BYTECODE(base_name##_ge80_ps); \ else \ ri.Error(ERR_DROP, "create_pipeline: invalid alpha test state bits\n"); D3D12_SHADER_BYTECODE vs_bytecode; D3D12_SHADER_BYTECODE ps_bytecode; if (def.shader_type == Vk_Shader_Type::single_texture) { if (def.clipping_plane) { vs_bytecode = BYTECODE(single_texture_clipping_plane_vs); } else { vs_bytecode = BYTECODE(single_texture_vs); } GET_PS_BYTECODE(single_texture) } else if (def.shader_type == Vk_Shader_Type::multi_texture_mul) { if (def.clipping_plane) { vs_bytecode = BYTECODE(multi_texture_clipping_plane_vs); } else { vs_bytecode = BYTECODE(multi_texture_vs); } GET_PS_BYTECODE(multi_texture_mul) } else if (def.shader_type == Vk_Shader_Type::multi_texture_add) { if (def.clipping_plane) { vs_bytecode = BYTECODE(multi_texture_clipping_plane_vs); } else { vs_bytecode = BYTECODE(multi_texture_vs); } GET_PS_BYTECODE(multi_texture_add) } #undef GET_PS_BYTECODE #undef BYTECODE // Vertex elements. D3D12_INPUT_ELEMENT_DESC input_element_desc[] = { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 1, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 2, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, { "TEXCOORD", 1, DXGI_FORMAT_R32G32_FLOAT, 3, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 } }; // // Blend. // D3D12_BLEND_DESC blend_state; blend_state.AlphaToCoverageEnable = FALSE; blend_state.IndependentBlendEnable = FALSE; auto& rt_blend_desc = blend_state.RenderTarget[0]; rt_blend_desc.BlendEnable = (def.state_bits & (GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS)) ? TRUE : FALSE; rt_blend_desc.LogicOpEnable = FALSE; if (rt_blend_desc.BlendEnable) { switch (def.state_bits & GLS_SRCBLEND_BITS) { case GLS_SRCBLEND_ZERO: rt_blend_desc.SrcBlend = D3D12_BLEND_ZERO; rt_blend_desc.SrcBlendAlpha = D3D12_BLEND_ZERO; break; case GLS_SRCBLEND_ONE: rt_blend_desc.SrcBlend = D3D12_BLEND_ONE; rt_blend_desc.SrcBlendAlpha = D3D12_BLEND_ONE; break; case GLS_SRCBLEND_DST_COLOR: rt_blend_desc.SrcBlend = D3D12_BLEND_DEST_COLOR; rt_blend_desc.SrcBlendAlpha = D3D12_BLEND_DEST_ALPHA; break; case GLS_SRCBLEND_ONE_MINUS_DST_COLOR: rt_blend_desc.SrcBlend = D3D12_BLEND_INV_DEST_COLOR; rt_blend_desc.SrcBlendAlpha = D3D12_BLEND_INV_DEST_ALPHA; break; case GLS_SRCBLEND_SRC_ALPHA: rt_blend_desc.SrcBlend = D3D12_BLEND_SRC_ALPHA; rt_blend_desc.SrcBlendAlpha = D3D12_BLEND_SRC_ALPHA; break; case GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA: rt_blend_desc.SrcBlend = D3D12_BLEND_INV_SRC_ALPHA; rt_blend_desc.SrcBlendAlpha = D3D12_BLEND_INV_SRC_ALPHA; break; case GLS_SRCBLEND_DST_ALPHA: rt_blend_desc.SrcBlend = D3D12_BLEND_DEST_ALPHA; rt_blend_desc.SrcBlendAlpha = D3D12_BLEND_DEST_ALPHA; break; case GLS_SRCBLEND_ONE_MINUS_DST_ALPHA: rt_blend_desc.SrcBlend = D3D12_BLEND_INV_DEST_ALPHA; rt_blend_desc.SrcBlendAlpha = D3D12_BLEND_INV_DEST_ALPHA; break; case GLS_SRCBLEND_ALPHA_SATURATE: rt_blend_desc.SrcBlend = D3D12_BLEND_SRC_ALPHA_SAT; rt_blend_desc.SrcBlendAlpha = D3D12_BLEND_SRC_ALPHA_SAT; break; default: ri.Error( ERR_DROP, "create_pipeline: invalid src blend state bits\n" ); break; } switch (def.state_bits & GLS_DSTBLEND_BITS) { case GLS_DSTBLEND_ZERO: rt_blend_desc.DestBlend = D3D12_BLEND_ZERO; rt_blend_desc.DestBlendAlpha = D3D12_BLEND_ZERO; break; case GLS_DSTBLEND_ONE: rt_blend_desc.DestBlend = D3D12_BLEND_ONE; rt_blend_desc.DestBlendAlpha = D3D12_BLEND_ONE; break; case GLS_DSTBLEND_SRC_COLOR: rt_blend_desc.DestBlend = D3D12_BLEND_SRC_COLOR; rt_blend_desc.DestBlendAlpha = D3D12_BLEND_SRC_ALPHA; break; case GLS_DSTBLEND_ONE_MINUS_SRC_COLOR: rt_blend_desc.DestBlend = D3D12_BLEND_INV_SRC_COLOR; rt_blend_desc.DestBlendAlpha = D3D12_BLEND_INV_SRC_ALPHA; break; case GLS_DSTBLEND_SRC_ALPHA: rt_blend_desc.DestBlend = D3D12_BLEND_SRC_ALPHA; rt_blend_desc.DestBlendAlpha = D3D12_BLEND_SRC_ALPHA; break; case GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA: rt_blend_desc.DestBlend = D3D12_BLEND_INV_SRC_ALPHA; rt_blend_desc.DestBlendAlpha = D3D12_BLEND_INV_SRC_ALPHA; break; case GLS_DSTBLEND_DST_ALPHA: rt_blend_desc.DestBlend = D3D12_BLEND_DEST_ALPHA; rt_blend_desc.DestBlendAlpha = D3D12_BLEND_DEST_ALPHA; break; case GLS_DSTBLEND_ONE_MINUS_DST_ALPHA: rt_blend_desc.DestBlend = D3D12_BLEND_INV_DEST_ALPHA; rt_blend_desc.DestBlendAlpha = D3D12_BLEND_INV_DEST_ALPHA; break; default: ri.Error( ERR_DROP, "create_pipeline: invalid dst blend state bits\n" ); break; } } rt_blend_desc.BlendOp = D3D12_BLEND_OP_ADD; rt_blend_desc.BlendOpAlpha = D3D12_BLEND_OP_ADD; rt_blend_desc.LogicOp = D3D12_LOGIC_OP_COPY; rt_blend_desc.RenderTargetWriteMask = (def.shadow_phase == Vk_Shadow_Phase::shadow_edges_rendering) ? 0 : D3D12_COLOR_WRITE_ENABLE_ALL; // // Rasteriazation state. // D3D12_RASTERIZER_DESC rasterization_state = {}; rasterization_state.FillMode = (def.state_bits & GLS_POLYMODE_LINE) ? D3D12_FILL_MODE_WIREFRAME : D3D12_FILL_MODE_SOLID; if (def.face_culling == CT_TWO_SIDED) rasterization_state.CullMode = D3D12_CULL_MODE_NONE; else if (def.face_culling == CT_FRONT_SIDED) rasterization_state.CullMode = (def.mirror ? D3D12_CULL_MODE_FRONT : D3D12_CULL_MODE_BACK); else if (def.face_culling == CT_BACK_SIDED) rasterization_state.CullMode = (def.mirror ? D3D12_CULL_MODE_BACK : D3D12_CULL_MODE_FRONT); else ri.Error(ERR_DROP, "create_pipeline: invalid face culling mode\n"); rasterization_state.FrontCounterClockwise = FALSE; // Q3 defaults to clockwise vertex order rasterization_state.DepthBias = def.polygon_offset ? r_offsetUnits->integer : 0; rasterization_state.DepthBiasClamp = 0.0f; rasterization_state.SlopeScaledDepthBias = def.polygon_offset ? r_offsetFactor->value : 0.0f; rasterization_state.DepthClipEnable = TRUE; rasterization_state.MultisampleEnable = FALSE; rasterization_state.AntialiasedLineEnable = FALSE; rasterization_state.ForcedSampleCount = 0; rasterization_state.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF; // // Depth/stencil state. // D3D12_DEPTH_STENCIL_DESC depth_stencil_state = {}; depth_stencil_state.DepthEnable = (def.state_bits & GLS_DEPTHTEST_DISABLE) ? FALSE : TRUE; depth_stencil_state.DepthWriteMask = (def.state_bits & GLS_DEPTHMASK_TRUE) ? D3D12_DEPTH_WRITE_MASK_ALL : D3D12_DEPTH_WRITE_MASK_ZERO; depth_stencil_state.DepthFunc = (def.state_bits & GLS_DEPTHFUNC_EQUAL) ? D3D12_COMPARISON_FUNC_EQUAL : D3D12_COMPARISON_FUNC_LESS_EQUAL; depth_stencil_state.StencilEnable = (def.shadow_phase != Vk_Shadow_Phase::disabled) ? TRUE : FALSE; depth_stencil_state.StencilReadMask = 255; depth_stencil_state.StencilWriteMask = 255; if (def.shadow_phase == Vk_Shadow_Phase::shadow_edges_rendering) { depth_stencil_state.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP; depth_stencil_state.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP; depth_stencil_state.FrontFace.StencilPassOp = (def.face_culling == CT_FRONT_SIDED) ? D3D12_STENCIL_OP_INCR_SAT : D3D12_STENCIL_OP_DECR_SAT; depth_stencil_state.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS; depth_stencil_state.BackFace = depth_stencil_state.FrontFace; } else if (def.shadow_phase == Vk_Shadow_Phase::fullscreen_quad_rendering) { depth_stencil_state.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP; depth_stencil_state.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP; depth_stencil_state.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP; depth_stencil_state.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_NOT_EQUAL; depth_stencil_state.BackFace = depth_stencil_state.FrontFace; } else { depth_stencil_state.FrontFace = {}; depth_stencil_state.BackFace = {}; } // // Create pipeline state. // D3D12_GRAPHICS_PIPELINE_STATE_DESC pipeline_desc = {}; pipeline_desc.pRootSignature = dx.root_signature; pipeline_desc.VS = vs_bytecode; pipeline_desc.PS = ps_bytecode; pipeline_desc.BlendState = blend_state; pipeline_desc.SampleMask = UINT_MAX; pipeline_desc.RasterizerState = rasterization_state; pipeline_desc.DepthStencilState = depth_stencil_state; pipeline_desc.InputLayout = { input_element_desc, def.shader_type == Vk_Shader_Type::single_texture ? 3u : 4u }; pipeline_desc.PrimitiveTopologyType = def.line_primitives ? D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE : D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; pipeline_desc.NumRenderTargets = 1; pipeline_desc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM; pipeline_desc.DSVFormat = get_depth_format(); pipeline_desc.SampleDesc.Count = 1; pipeline_desc.SampleDesc.Quality = 0; ID3D12PipelineState* pipeline; DX_CHECK(dx.device->CreateGraphicsPipelineState(&pipeline_desc, IID_PPV_ARGS(&pipeline))); return pipeline; } struct Timer { using Clock = std::chrono::high_resolution_clock; using Second = std::chrono::duration>; Clock::time_point start = Clock::now(); double elapsed_seconds() const { const auto duration = Clock::now() - start; double seconds = std::chrono::duration_cast(duration).count(); return seconds; } }; void dx_create_sampler_descriptor(const Vk_Sampler_Def& def, Dx_Sampler_Index sampler_index) { uint32_t min, mag, mip; if (def.gl_mag_filter == GL_NEAREST) { mag = 0; } else if (def.gl_mag_filter == GL_LINEAR) { mag = 1; } else { ri.Error(ERR_FATAL, "create_sampler_descriptor: invalid gl_mag_filter"); } bool max_lod_0_25 = false; // used to emulate OpenGL's GL_LINEAR/GL_NEAREST minification filter if (def.gl_min_filter == GL_NEAREST) { min = 0; mip = 0; max_lod_0_25 = true; } else if (def.gl_min_filter == GL_LINEAR) { min = 1; mip = 0; max_lod_0_25 = true; } else if (def.gl_min_filter == GL_NEAREST_MIPMAP_NEAREST) { min = 0; mip = 0; } else if (def.gl_min_filter == GL_LINEAR_MIPMAP_NEAREST) { min = 1; mip = 0; } else if (def.gl_min_filter == GL_NEAREST_MIPMAP_LINEAR) { min = 0; mip = 1; } else if (def.gl_min_filter == GL_LINEAR_MIPMAP_LINEAR) { min = 1; mip = 1; } else { ri.Error(ERR_FATAL, "vk_find_sampler: invalid gl_min_filter"); } D3D12_TEXTURE_ADDRESS_MODE address_mode = def.repeat_texture ? D3D12_TEXTURE_ADDRESS_MODE_WRAP : D3D12_TEXTURE_ADDRESS_MODE_CLAMP; D3D12_SAMPLER_DESC sampler_desc; sampler_desc.Filter = D3D12_ENCODE_BASIC_FILTER(min, mag, mip, 0); sampler_desc.AddressU = address_mode; sampler_desc.AddressV = address_mode; sampler_desc.AddressW = address_mode; sampler_desc.MipLODBias = 0.0f; sampler_desc.MaxAnisotropy = 1; sampler_desc.ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS; sampler_desc.BorderColor[0] = 0.0f; sampler_desc.BorderColor[1] = 0.0f; sampler_desc.BorderColor[2] = 0.0f; sampler_desc.BorderColor[3] = 0.0f; sampler_desc.MinLOD = 0.0f; sampler_desc.MaxLOD = max_lod_0_25 ? 0.25f : 12.0f; D3D12_CPU_DESCRIPTOR_HANDLE sampler_handle = dx.sampler_heap->GetCPUDescriptorHandleForHeapStart(); sampler_handle.ptr += dx.sampler_descriptor_size * sampler_index; dx.device->CreateSampler(&sampler_desc, sampler_handle); } ID3D12PipelineState* dx_find_pipeline(const Vk_Pipeline_Def& def) { for (int i = 0; i < dx_world.num_pipelines; i++) { const auto& cur_def = dx_world.pipeline_defs[i]; if (cur_def.shader_type == def.shader_type && cur_def.state_bits == def.state_bits && cur_def.face_culling == def.face_culling && cur_def.polygon_offset == def.polygon_offset && cur_def.clipping_plane == def.clipping_plane && cur_def.mirror == def.mirror && cur_def.line_primitives == def.line_primitives && cur_def.shadow_phase == def.shadow_phase) { return dx_world.pipelines[i]; } } if (dx_world.num_pipelines >= MAX_VK_PIPELINES) { ri.Error(ERR_DROP, "dx_find_pipeline: MAX_VK_PIPELINES hit\n"); } Timer t; ID3D12PipelineState* pipeline = create_pipeline(def); dx_world.pipeline_create_time += t.elapsed_seconds(); dx_world.pipeline_defs[dx_world.num_pipelines] = def; dx_world.pipelines[dx_world.num_pipelines] = pipeline; dx_world.num_pipelines++; return pipeline; } static void get_mvp_transform(float* mvp) { if (backEnd.projection2D) { float mvp0 = 2.0f / glConfig.vidWidth; float mvp5 = 2.0f / glConfig.vidHeight; mvp[0] = mvp0; mvp[1] = 0.0f; mvp[2] = 0.0f; mvp[3] = 0.0f; mvp[4] = 0.0f; mvp[5] = -mvp5; mvp[6] = 0.0f; mvp[7] = 0.0f; mvp[8] = 0.0f; mvp[9] = 0.0f; mvp[10] = 1.0f; mvp[11] = 0.0f; mvp[12] = -1.0f; mvp[13] = 1.0f; mvp[14] = 0.0f; mvp[15] = 1.0f; } else { const float* p = backEnd.viewParms.projectionMatrix; // update q3's proj matrix (opengl) to d3d conventions: z - [0, 1] instead of [-1, 1] float zNear = r_znear->value; float zFar = backEnd.viewParms.zFar; float P10 = -zFar / (zFar - zNear); float P14 = -zFar*zNear / (zFar - zNear); float proj[16] = { p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], P10, p[11], p[12], p[13], P14, p[15] }; myGlMultMatrix(dx_world.modelview_transform, proj, mvp); } } static D3D12_RECT get_viewport_rect() { D3D12_RECT r; if (backEnd.projection2D) { r.left = 0.0f; r.top = 0.0f; r.right = glConfig.vidWidth; r.bottom = glConfig.vidHeight; } else { r.left = backEnd.viewParms.viewportX; r.top = glConfig.vidHeight - (backEnd.viewParms.viewportY + backEnd.viewParms.viewportHeight); r.right = r.left + backEnd.viewParms.viewportWidth; r.bottom = r.top + backEnd.viewParms.viewportHeight; } return r; } static D3D12_VIEWPORT get_viewport(Vk_Depth_Range depth_range) { D3D12_RECT r = get_viewport_rect(); D3D12_VIEWPORT viewport; viewport.TopLeftX = (float)r.left; viewport.TopLeftY = (float)r.top; viewport.Width = (float)(r.right - r.left); viewport.Height = (float)(r.bottom - r.top); if (depth_range == Vk_Depth_Range::force_zero) { viewport.MinDepth = 0.0f; viewport.MaxDepth = 0.0f; } else if (depth_range == Vk_Depth_Range::force_one) { viewport.MinDepth = 1.0f; viewport.MaxDepth = 1.0f; } else if (depth_range == Vk_Depth_Range::weapon) { viewport.MinDepth = 0.0f; viewport.MaxDepth = 0.3f; } else { viewport.MinDepth = 0.0f; viewport.MaxDepth = 1.0f; } return viewport; } static D3D12_RECT get_scissor_rect() { D3D12_RECT r = get_viewport_rect(); if (r.left < 0) r.left = 0; if (r.top < 0) r.top = 0; if (r.right > glConfig.vidWidth) r.right = glConfig.vidWidth; if (r.bottom > glConfig.vidHeight) r.bottom = glConfig.vidHeight; return r; } void dx_clear_attachments(bool clear_depth_stencil, bool clear_color, vec4_t color) { if (!dx.active) return; if (!clear_depth_stencil && !clear_color) return; D3D12_RECT clear_rect = get_scissor_rect(); if (clear_depth_stencil) { D3D12_CLEAR_FLAGS flags = D3D12_CLEAR_FLAG_DEPTH; if (r_shadows->integer == 2) flags |= D3D12_CLEAR_FLAG_STENCIL; D3D12_CPU_DESCRIPTOR_HANDLE dsv_handle = dx.dsv_heap->GetCPUDescriptorHandleForHeapStart(); dx.command_list->ClearDepthStencilView(dsv_handle, flags, 1.0f, 0, 1, &clear_rect); } if (clear_color) { D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle = dx.rtv_heap->GetCPUDescriptorHandleForHeapStart(); rtv_handle.ptr += dx.frame_index * dx.rtv_descriptor_size; dx.command_list->ClearRenderTargetView(rtv_handle, color, 1, &clear_rect); } } void dx_bind_geometry() { // xyz stream { if ((dx.xyz_elements + tess.numVertexes) * sizeof(vec4_t) > XYZ_SIZE) ri.Error(ERR_DROP, "dx_bind_geometry: vertex buffer overflow (xyz)\n"); byte* dst = dx.vertex_buffer_ptr + XYZ_OFFSET + dx.xyz_elements * sizeof(vec4_t); Com_Memcpy(dst, tess.xyz, tess.numVertexes * sizeof(vec4_t)); uint32_t xyz_offset = XYZ_OFFSET + dx.xyz_elements * sizeof(vec4_t); D3D12_VERTEX_BUFFER_VIEW xyz_view; xyz_view.BufferLocation = dx.geometry_buffer->GetGPUVirtualAddress() + xyz_offset; xyz_view.SizeInBytes = static_cast(tess.numVertexes * sizeof(vec4_t)); xyz_view.StrideInBytes = static_cast(sizeof(vec4_t)); dx.command_list->IASetVertexBuffers(0, 1, &xyz_view); dx.xyz_elements += tess.numVertexes; } // indexes stream { std::size_t indexes_size = tess.numIndexes * sizeof(uint32_t); if (dx.index_buffer_offset + indexes_size > INDEX_BUFFER_SIZE) ri.Error(ERR_DROP, "dx_bind_geometry: index buffer overflow\n"); byte* dst = dx.index_buffer_ptr + dx.index_buffer_offset; Com_Memcpy(dst, tess.indexes, indexes_size); D3D12_INDEX_BUFFER_VIEW index_view; index_view.BufferLocation = dx.geometry_buffer->GetGPUVirtualAddress() + VERTEX_BUFFER_SIZE + dx.index_buffer_offset; index_view.SizeInBytes = static_cast(indexes_size); index_view.Format = DXGI_FORMAT_R32_UINT; dx.command_list->IASetIndexBuffer(&index_view); dx.index_buffer_offset += static_cast(indexes_size); } // // Specify push constants. // float root_constants[16 + 12 + 4]; // mvp transform + eye transform + clipping plane in eye space get_mvp_transform(root_constants); int root_constant_count = 16; if (backEnd.viewParms.isPortal) { // Eye space transform. // NOTE: backEnd.or.modelMatrix incorporates s_flipMatrix, so it should be taken into account // when computing clipping plane too. float* eye_xform = root_constants + 16; for (int i = 0; i < 12; i++) { eye_xform[i] = backEnd.or.modelMatrix[(i%4)*4 + i/4 ]; } // Clipping plane in eye coordinates. float world_plane[4]; world_plane[0] = backEnd.viewParms.portalPlane.normal[0]; world_plane[1] = backEnd.viewParms.portalPlane.normal[1]; world_plane[2] = backEnd.viewParms.portalPlane.normal[2]; world_plane[3] = backEnd.viewParms.portalPlane.dist; float eye_plane[4]; eye_plane[0] = DotProduct (backEnd.viewParms.or.axis[0], world_plane); eye_plane[1] = DotProduct (backEnd.viewParms.or.axis[1], world_plane); eye_plane[2] = DotProduct (backEnd.viewParms.or.axis[2], world_plane); eye_plane[3] = DotProduct (world_plane, backEnd.viewParms.or.origin) - world_plane[3]; // Apply s_flipMatrix to be in the same coordinate system as eye_xfrom. root_constants[28] = -eye_plane[1]; root_constants[29] = eye_plane[2]; root_constants[30] = -eye_plane[0]; root_constants[31] = eye_plane[3]; root_constant_count += 16; } dx.command_list->SetGraphicsRoot32BitConstants(0, root_constant_count, root_constants, 0); } void dx_shade_geometry(ID3D12PipelineState* pipeline, bool multitexture, Vk_Depth_Range depth_range, bool indexed, bool lines) { // color { if ((dx.color_st_elements + tess.numVertexes) * sizeof(color4ub_t) > COLOR_SIZE) ri.Error(ERR_DROP, "vulkan: vertex buffer overflow (color)\n"); byte* dst = dx.vertex_buffer_ptr + COLOR_OFFSET + dx.color_st_elements * sizeof(color4ub_t); Com_Memcpy(dst, tess.svars.colors, tess.numVertexes * sizeof(color4ub_t)); } // st0 { if ((dx.color_st_elements + tess.numVertexes) * sizeof(vec2_t) > ST0_SIZE) ri.Error(ERR_DROP, "vulkan: vertex buffer overflow (st0)\n"); byte* dst = dx.vertex_buffer_ptr + ST0_OFFSET + dx.color_st_elements * sizeof(vec2_t); Com_Memcpy(dst, tess.svars.texcoords[0], tess.numVertexes * sizeof(vec2_t)); } // st1 if (multitexture) { if ((dx.color_st_elements + tess.numVertexes) * sizeof(vec2_t) > ST1_SIZE) ri.Error(ERR_DROP, "vulkan: vertex buffer overflow (st1)\n"); byte* dst = dx.vertex_buffer_ptr + ST1_OFFSET + dx.color_st_elements * sizeof(vec2_t); Com_Memcpy(dst, tess.svars.texcoords[1], tess.numVertexes * sizeof(vec2_t)); } // // Configure vertex data stream. // D3D12_VERTEX_BUFFER_VIEW color_st_views[3]; color_st_views[0].BufferLocation = dx.geometry_buffer->GetGPUVirtualAddress() + COLOR_OFFSET + dx.color_st_elements * sizeof(color4ub_t); color_st_views[0].SizeInBytes = static_cast(tess.numVertexes * sizeof(color4ub_t)); color_st_views[0].StrideInBytes = static_cast(sizeof(color4ub_t)); color_st_views[1].BufferLocation = dx.geometry_buffer->GetGPUVirtualAddress() + ST0_OFFSET + dx.color_st_elements * sizeof(vec2_t); color_st_views[1].SizeInBytes = static_cast(tess.numVertexes * sizeof(vec2_t)); color_st_views[1].StrideInBytes = static_cast(sizeof(vec2_t)); color_st_views[2].BufferLocation = dx.geometry_buffer->GetGPUVirtualAddress() + ST1_OFFSET + dx.color_st_elements * sizeof(vec2_t); color_st_views[2].SizeInBytes = static_cast(tess.numVertexes * sizeof(vec2_t)); color_st_views[2].StrideInBytes = static_cast(sizeof(vec2_t)); dx.command_list->IASetVertexBuffers(1, multitexture ? 3 : 2, color_st_views); dx.color_st_elements += tess.numVertexes; // // Set descriptor tables. // { D3D12_GPU_DESCRIPTOR_HANDLE srv_handle = dx.srv_heap->GetGPUDescriptorHandleForHeapStart(); srv_handle.ptr += dx.srv_descriptor_size * dx_world.current_image_indices[0]; dx.command_list->SetGraphicsRootDescriptorTable(1, srv_handle); D3D12_GPU_DESCRIPTOR_HANDLE sampler_handle = dx.sampler_heap->GetGPUDescriptorHandleForHeapStart(); const int sampler_index = dx_world.images[dx_world.current_image_indices[0]].sampler_index; sampler_handle.ptr += dx.sampler_descriptor_size * sampler_index; dx.command_list->SetGraphicsRootDescriptorTable(2, sampler_handle); } if (multitexture) { D3D12_GPU_DESCRIPTOR_HANDLE srv_handle = dx.srv_heap->GetGPUDescriptorHandleForHeapStart(); srv_handle.ptr += dx.srv_descriptor_size * dx_world.current_image_indices[1]; dx.command_list->SetGraphicsRootDescriptorTable(3, srv_handle); D3D12_GPU_DESCRIPTOR_HANDLE sampler_handle = dx.sampler_heap->GetGPUDescriptorHandleForHeapStart(); const int sampler_index = dx_world.images[dx_world.current_image_indices[1]].sampler_index; sampler_handle.ptr += dx.sampler_descriptor_size * sampler_index; dx.command_list->SetGraphicsRootDescriptorTable(4, sampler_handle); } // // Configure pipeline. // dx.command_list->SetPipelineState(pipeline); dx.command_list->IASetPrimitiveTopology(lines ? D3D10_PRIMITIVE_TOPOLOGY_LINELIST : D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); D3D12_RECT scissor_rect = get_scissor_rect(); dx.command_list->RSSetScissorRects(1, &scissor_rect); D3D12_VIEWPORT viewport = get_viewport(depth_range); dx.command_list->RSSetViewports(1, &viewport); // // Draw. // if (indexed) dx.command_list->DrawIndexedInstanced(tess.numIndexes, 1, 0, 0, 0); else dx.command_list->DrawInstanced(tess.numVertexes, 1, 0, 0); } void dx_begin_frame() { if (!dx.active) return; if (dx.fence->GetCompletedValue() < dx.fence_value) { DX_CHECK(dx.fence->SetEventOnCompletion(dx.fence_value, dx.fence_event)); WaitForSingleObject(dx.fence_event, INFINITE); } dx.frame_index = dx.swapchain->GetCurrentBackBufferIndex(); DX_CHECK(dx.command_allocator->Reset()); DX_CHECK(dx.command_list->Reset(dx.command_allocator, nullptr)); dx.command_list->SetGraphicsRootSignature(dx.root_signature); ID3D12DescriptorHeap* heaps[] = { dx.srv_heap, dx.sampler_heap }; dx.command_list->SetDescriptorHeaps(_countof(heaps), heaps); dx.command_list->ResourceBarrier(1, &get_transition_barrier(dx.render_targets[dx.frame_index], D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET)); D3D12_CPU_DESCRIPTOR_HANDLE dsv_handle = dx.dsv_heap->GetCPUDescriptorHandleForHeapStart(); D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle = dx.rtv_heap->GetCPUDescriptorHandleForHeapStart(); rtv_handle.ptr += dx.frame_index * dx.rtv_descriptor_size; dx.command_list->OMSetRenderTargets(1, &rtv_handle, FALSE, &dsv_handle); dx.xyz_elements = 0; dx.color_st_elements = 0; dx.index_buffer_offset = 0; } void dx_end_frame() { if (!dx.active) return; dx.command_list->ResourceBarrier(1, &get_transition_barrier(dx.render_targets[dx.frame_index], D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT)); DX_CHECK(dx.command_list->Close()); ID3D12CommandList* command_list = dx.command_list; dx.command_queue->ExecuteCommandLists(1, &command_list); dx.fence_value++; DX_CHECK(dx.command_queue->Signal(dx.fence, dx.fence_value)); DX_CHECK(dx.swapchain->Present(0, dx.present_allow_tearing ? DXGI_PRESENT_ALLOW_TEARING : 0)); } #else // ENABLE_DX12 void dx_initialize() {} void dx_shutdown() {} void dx_release_resources() {} void dx_wait_device_idle() {} Dx_Image dx_create_image(int width, int height, Dx_Image_Format format, int mip_levels, bool repeat_texture, int image_index) { return Dx_Image{}; } void dx_upload_image_data(ID3D12Resource* texture, int width, int height, int mip_levels, const uint8_t* pixels, int bytes_per_pixel) {} void dx_create_sampler_descriptor(const Vk_Sampler_Def& def, Dx_Sampler_Index sampler_index) {} ID3D12PipelineState* dx_find_pipeline(const Vk_Pipeline_Def& def) { return nullptr; } void dx_clear_attachments(bool clear_depth_stencil, bool clear_color, vec4_t color) {} void dx_bind_geometry() {} void dx_shade_geometry(ID3D12PipelineState* pipeline, bool multitexture, Vk_Depth_Range depth_range, bool indexed, bool lines) {} void dx_begin_frame() {} void dx_end_frame() {} #endif // ENABLE_DX12 ================================================ FILE: src/engine/renderer/dx.h ================================================ #pragma once // // DirectX 12 backend implementation is provided mostly for educational purposes and is not included in the prebuild binaries. // It can be enabled by uncommenting the following line. // //#define ENABLE_DX12 struct ID3D12CommandAllocator; struct ID3D12GraphicsCommandList; struct ID3D12CommandQueue; struct ID3D12Device; struct ID3D12DescriptorHeap; struct ID3D12Fence; struct ID3D12PipelineState; struct ID3D12Resource; struct ID3D12RootSignature; struct IDXGISwapChain3; constexpr int SWAPCHAIN_BUFFER_COUNT = 2; enum Dx_Sampler_Index { SAMPLER_MIP_REPEAT, SAMPLER_MIP_CLAMP, SAMPLER_NOMIP_REPEAT, SAMPLER_NOMIP_CLAMP, SAMPLER_COUNT }; enum Dx_Image_Format { IMAGE_FORMAT_RGBA8, IMAGE_FORMAT_BGRA4, IMAGE_FORMAT_BGR5A1 }; struct Dx_Image { ID3D12Resource* texture = nullptr; Dx_Sampler_Index sampler_index = SAMPLER_COUNT; }; // // Initialization. // void dx_initialize(); void dx_shutdown(); void dx_release_resources(); void dx_wait_device_idle(); // // Resources allocation. // Dx_Image dx_create_image(int width, int height, Dx_Image_Format format, int mip_levels, bool repeat_texture, int image_index); void dx_upload_image_data(ID3D12Resource* texture, int width, int height, int mip_levels, const uint8_t* pixels, int bytes_per_pixel); void dx_create_sampler_descriptor(const Vk_Sampler_Def& def, Dx_Sampler_Index sampler_index); ID3D12PipelineState* dx_find_pipeline(const Vk_Pipeline_Def& def); // // Rendering setup. // void dx_clear_attachments(bool clear_depth_stencil, bool clear_color, vec4_t color); void dx_bind_geometry(); void dx_shade_geometry(ID3D12PipelineState* pipeline, bool multitexture, Vk_Depth_Range depth_range, bool indexed, bool lines); void dx_begin_frame(); void dx_end_frame(); struct Dx_Instance { bool active = false; ID3D12Device* device = nullptr; ID3D12CommandQueue* command_queue = nullptr; IDXGISwapChain3* swapchain = nullptr; BOOL present_allow_tearing = false; UINT frame_index = 0; ID3D12CommandAllocator* command_allocator = nullptr; ID3D12CommandAllocator* helper_command_allocator = nullptr; ID3D12GraphicsCommandList* command_list = nullptr; ID3D12Fence* fence = nullptr; UINT64 fence_value = 0; HANDLE fence_event = NULL; ID3D12Resource* render_targets[SWAPCHAIN_BUFFER_COUNT]; ID3D12Resource* depth_stencil_buffer = nullptr; ID3D12RootSignature* root_signature = nullptr; // // Descriptor heaps. // ID3D12DescriptorHeap* rtv_heap = nullptr; UINT rtv_descriptor_size = 0; ID3D12DescriptorHeap* dsv_heap = nullptr; ID3D12DescriptorHeap* srv_heap = nullptr; UINT srv_descriptor_size = 0; ID3D12DescriptorHeap* sampler_heap = nullptr; UINT sampler_descriptor_size = 0; // // Geometry buffers. // byte* vertex_buffer_ptr = nullptr; // pointer to mapped vertex buffer int xyz_elements = 0; int color_st_elements = 0; byte* index_buffer_ptr = nullptr; // pointer to mapped index buffer int index_buffer_offset = 0; ID3D12Resource* geometry_buffer = nullptr; // // Standard pipelines. // ID3D12PipelineState* skybox_pipeline = nullptr; // dim 0: 0 - front side, 1 - back size // dim 1: 0 - normal view, 1 - mirror view ID3D12PipelineState* shadow_volume_pipelines[2][2]; ID3D12PipelineState* shadow_finish_pipeline = nullptr; // dim 0 is based on fogPass_t: 0 - corresponds to FP_EQUAL, 1 - corresponds to FP_LE. // dim 1 is directly a cullType_t enum value. // dim 2 is a polygon offset value (0 - off, 1 - on). ID3D12PipelineState* fog_pipelines[2][3][2]; // dim 0 is based on dlight additive flag: 0 - not additive, 1 - additive // dim 1 is directly a cullType_t enum value. // dim 2 is a polygon offset value (0 - off, 1 - on). ID3D12PipelineState* dlight_pipelines[2][3][2]; // debug visualization pipelines ID3D12PipelineState* tris_debug_pipeline = nullptr; ID3D12PipelineState* tris_mirror_debug_pipeline = nullptr; ID3D12PipelineState* normals_debug_pipeline = nullptr; ID3D12PipelineState* surface_debug_pipeline_solid = nullptr; ID3D12PipelineState* surface_debug_pipeline_outline = nullptr; ID3D12PipelineState* images_debug_pipeline = nullptr; }; struct Dx_World { // // Resources. // int num_pipelines = 0; Vk_Pipeline_Def pipeline_defs[MAX_VK_PIPELINES]; ID3D12PipelineState* pipelines[MAX_VK_PIPELINES]; float pipeline_create_time; Dx_Image images[MAX_VK_IMAGES]; // // State. // int current_image_indices[2]; float modelview_transform[16]; }; ================================================ FILE: src/engine/renderer/jpeg/stb_image.h ================================================ /* stb_image - v2.14 - public domain image loader - http://nothings.org/stb_image.h no warranty implied; use at your own risk Do this: #define STB_IMAGE_IMPLEMENTATION before you include this file in *one* C or C++ file to create the implementation. // i.e. it should look like this: #include ... #include ... #include ... #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free QUICK NOTES: Primarily of interest to game developers and other people who can avoid problematic images and only need the trivial interface JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) PNG 1/2/4/8/16-bit-per-channel TGA (not sure what subset, if a subset) BMP non-1bpp, non-RLE PSD (composited view only, no extra channels, 8/16 bit-per-channel) GIF (*comp always reports as 4-channel) HDR (radiance rgbE format) PIC (Softimage PIC) PNM (PPM and PGM binary only) Animated GIF still needs a proper API, but here's one way to do it: http://gist.github.com/urraka/685d9a6340b26b830d49 - decode from memory or through FILE (define STBI_NO_STDIO to remove code) - decode from arbitrary I/O callbacks - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) Full documentation under "DOCUMENTATION" below. LICENSE See end of file for license information. RECENT REVISION HISTORY: 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 RGB-format JPEG; remove white matting in PSD; allocate large structures on the stack; correct channel count for PNG & BMP 2.10 (2016-01-22) avoid warning introduced in 2.09 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA 2.07 (2015-09-13) partial animated GIF support limited 16-bit PSD support minor bugs, code cleanup, and compiler warnings See end of file for full revision history. ============================ Contributors ========================= Image formats Extensions, features Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) github:urraka (animated gif) Junggon Kim (PNM comments) Daniel Gibson (16-bit TGA) socks-the-fox (16-bit TGA) Jeremy Sawicki (handle all ImageNet JPGs) Optimizations & bugfixes Fabian "ryg" Giesen Arseny Kapoulkine Bug & warning fixes Marc LeBlanc David Woo Guillaume George Martins Mozeiko Christpher Lloyd Martin Golini Jerry Jansson Joseph Thomson Dave Moore Roy Eltham Hayaki Saito Phil Jordan Won Chun Luke Graham Johan Duparc Nathan Reed the Horde3D community Thomas Ruf Ronny Chevalier Nick Verigakis Janez Zemva John Bartholomew Michal Cichon github:svdijk Jonathan Blow Ken Hamada Tero Hanninen Baldur Karlsson Laurent Gomila Cort Stratton Sergio Gonzalez github:romigrou Aruelien Pocheville Thibault Reuille Cass Everitt Matthew Gregan Ryamond Barbiero Paul Du Bois Engin Manap github:snagar Michaelangel007@github Oriol Ferrer Mesia Dale Weiler github:Zelex Philipp Wiesemann Josh Tobin github:rlyeh github:grim210@github Blazej Dariusz Roszkowski github:sammyhw */ #ifndef STBI_INCLUDE_STB_IMAGE_H #define STBI_INCLUDE_STB_IMAGE_H // DOCUMENTATION // // Limitations: // - no 16-bit-per-channel PNG // - no 12-bit-per-channel JPEG // - no JPEGs with arithmetic coding // - no 1-bit BMP // - GIF always returns *comp=4 // // Basic usage (see HDR discussion below for HDR usage): // int x,y,n; // unsigned char *data = stbi_load(filename, &x, &y, &n, 0); // // ... process data if not NULL ... // // ... x = width, y = height, n = # 8-bit components per pixel ... // // ... replace '0' with '1'..'4' to force that many components per pixel // // ... but 'n' will always be the number that it would have been if you said 0 // stbi_image_free(data) // // Standard parameters: // int *x -- outputs image width in pixels // int *y -- outputs image height in pixels // int *channels_in_file -- outputs # of image components in image file // int desired_channels -- if non-zero, # of image components requested in result // // The return value from an image loader is an 'unsigned char *' which points // to the pixel data, or NULL on an allocation failure or if the image is // corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, // with each pixel consisting of N interleaved 8-bit components; the first // pixel pointed to is top-left-most in the image. There is no padding between // image scanlines or between pixels, regardless of format. The number of // components N is 'req_comp' if req_comp is non-zero, or *comp otherwise. // If req_comp is non-zero, *comp has the number of components that _would_ // have been output otherwise. E.g. if you set req_comp to 4, you will always // get RGBA output, but you can check *comp to see if it's trivially opaque // because e.g. there were only 3 channels in the source image. // // An output image with N components has the following components interleaved // in this order in each pixel: // // N=#comp components // 1 grey // 2 grey, alpha // 3 red, green, blue // 4 red, green, blue, alpha // // If image loading fails for any reason, the return value will be NULL, // and *x, *y, *comp will be unchanged. The function stbi_failure_reason() // can be queried for an extremely brief, end-user unfriendly explanation // of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid // compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly // more user-friendly ones. // // Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. // // =========================================================================== // // Philosophy // // stb libraries are designed with the following priorities: // // 1. easy to use // 2. easy to maintain // 3. good performance // // Sometimes I let "good performance" creep up in priority over "easy to maintain", // and for best performance I may provide less-easy-to-use APIs that give higher // performance, in addition to the easy to use ones. Nevertheless, it's important // to keep in mind that from the standpoint of you, a client of this library, // all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. // // Some secondary priorities arise directly from the first two, some of which // make more explicit reasons why performance can't be emphasized. // // - Portable ("ease of use") // - Small source code footprint ("easy to maintain") // - No dependencies ("ease of use") // // =========================================================================== // // I/O callbacks // // I/O callbacks allow you to read from arbitrary sources, like packaged // files or some other source. Data read from callbacks are processed // through a small internal buffer (currently 128 bytes) to try to reduce // overhead. // // The three functions you must define are "read" (reads some bytes of data), // "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). // // =========================================================================== // // SIMD support // // The JPEG decoder will try to automatically use SIMD kernels on x86 when // supported by the compiler. For ARM Neon support, you must explicitly // request it. // // (The old do-it-yourself SIMD API is no longer supported in the current // code.) // // On x86, SSE2 will automatically be used when available based on a run-time // test; if not, the generic C versions are used as a fall-back. On ARM targets, // the typical path is to have separate builds for NEON and non-NEON devices // (at least this is true for iOS and Android). Therefore, the NEON support is // toggled by a build flag: define STBI_NEON to get NEON loops. // // If for some reason you do not want to use any of SIMD code, or if // you have issues compiling it, you can disable it entirely by // defining STBI_NO_SIMD. // // =========================================================================== // // HDR image support (disable by defining STBI_NO_HDR) // // stb_image now supports loading HDR images in general, and currently // the Radiance .HDR file format, although the support is provided // generically. You can still load any file through the existing interface; // if you attempt to load an HDR file, it will be automatically remapped to // LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; // both of these constants can be reconfigured through this interface: // // stbi_hdr_to_ldr_gamma(2.2f); // stbi_hdr_to_ldr_scale(1.0f); // // (note, do not use _inverse_ constants; stbi_image will invert them // appropriately). // // Additionally, there is a new, parallel interface for loading files as // (linear) floats to preserve the full dynamic range: // // float *data = stbi_loadf(filename, &x, &y, &n, 0); // // If you load LDR images through this interface, those images will // be promoted to floating point values, run through the inverse of // constants corresponding to the above: // // stbi_ldr_to_hdr_scale(1.0f); // stbi_ldr_to_hdr_gamma(2.2f); // // Finally, given a filename (or an open file or memory block--see header // file for details) containing image data, you can query for the "most // appropriate" interface to use (that is, whether the image is HDR or // not), using: // // stbi_is_hdr(char *filename); // // =========================================================================== // // iPhone PNG support: // // By default we convert iphone-formatted PNGs back to RGB, even though // they are internally encoded differently. You can disable this conversion // by by calling stbi_convert_iphone_png_to_rgb(0), in which case // you will always just get the native iphone "format" through (which // is BGR stored in RGB). // // Call stbi_set_unpremultiply_on_load(1) as well to force a divide per // pixel to remove any premultiplied alpha *only* if the image file explicitly // says there's premultiplied data (currently only happens in iPhone images, // and only if iPhone convert-to-rgb processing is on). // // =========================================================================== // // ADDITIONAL CONFIGURATION // // - You can suppress implementation of any of the decoders to reduce // your code footprint by #defining one or more of the following // symbols before creating the implementation. // // STBI_NO_JPEG // STBI_NO_PNG // STBI_NO_BMP // STBI_NO_PSD // STBI_NO_TGA // STBI_NO_GIF // STBI_NO_HDR // STBI_NO_PIC // STBI_NO_PNM (.ppm and .pgm) // // - You can request *only* certain decoders and suppress all other ones // (this will be more forward-compatible, as addition of new decoders // doesn't require you to disable them explicitly): // // STBI_ONLY_JPEG // STBI_ONLY_PNG // STBI_ONLY_BMP // STBI_ONLY_PSD // STBI_ONLY_TGA // STBI_ONLY_GIF // STBI_ONLY_HDR // STBI_ONLY_PIC // STBI_ONLY_PNM (.ppm and .pgm) // // - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still // want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB // #ifndef STBI_NO_STDIO #include #endif // STBI_NO_STDIO #define STBI_VERSION 1 enum { STBI_default = 0, // only used for req_comp STBI_grey = 1, STBI_grey_alpha = 2, STBI_rgb = 3, STBI_rgb_alpha = 4 }; typedef unsigned char stbi_uc; typedef unsigned short stbi_us; #ifdef __cplusplus extern "C" { #endif #ifdef STB_IMAGE_STATIC #define STBIDEF static #else #define STBIDEF extern #endif ////////////////////////////////////////////////////////////////////////////// // // PRIMARY API - works on images of any type // // // load image by filename, open file, or memory buffer // typedef struct { int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative int (*eof) (void *user); // returns nonzero if we are at end of file/data } stbi_io_callbacks; //////////////////////////////////// // // 8-bits-per-channel interface // STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); #ifndef STBI_NO_STDIO STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); // for stbi_load_from_file, file pointer is left pointing immediately after image #endif //////////////////////////////////// // // 16-bits-per-channel interface // STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); #ifndef STBI_NO_STDIO STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); #endif // @TODO the other variants //////////////////////////////////// // // float-per-channel interface // #ifndef STBI_NO_LINEAR STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); #ifndef STBI_NO_STDIO STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); #endif #endif #ifndef STBI_NO_HDR STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); STBIDEF void stbi_hdr_to_ldr_scale(float scale); #endif // STBI_NO_HDR #ifndef STBI_NO_LINEAR STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); STBIDEF void stbi_ldr_to_hdr_scale(float scale); #endif // STBI_NO_LINEAR // stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); #ifndef STBI_NO_STDIO STBIDEF int stbi_is_hdr (char const *filename); STBIDEF int stbi_is_hdr_from_file(FILE *f); #endif // STBI_NO_STDIO // get a VERY brief reason for failure // NOT THREADSAFE STBIDEF const char *stbi_failure_reason (void); // free the loaded image -- this is just free() STBIDEF void stbi_image_free (void *retval_from_stbi_load); // get image dimensions & components without fully decoding STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); #ifndef STBI_NO_STDIO STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); #endif // for image formats that explicitly notate that they have premultiplied alpha, // we just return the colors as stored in the file. set this flag to force // unpremultiplication. results are undefined if the unpremultiply overflow. STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); // indicate whether we should process iphone images back to canonical format, // or just pass them through "as-is" STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); // flip the image vertically, so the first pixel in the output array is the bottom left STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); // ZLIB client - used by PNG, available for other purposes STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); #ifdef __cplusplus } #endif // // //// end header file ///////////////////////////////////////////////////// #endif // STBI_INCLUDE_STB_IMAGE_H #ifdef STB_IMAGE_IMPLEMENTATION #if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ || defined(STBI_ONLY_ZLIB) #ifndef STBI_ONLY_JPEG #define STBI_NO_JPEG #endif #ifndef STBI_ONLY_PNG #define STBI_NO_PNG #endif #ifndef STBI_ONLY_BMP #define STBI_NO_BMP #endif #ifndef STBI_ONLY_PSD #define STBI_NO_PSD #endif #ifndef STBI_ONLY_TGA #define STBI_NO_TGA #endif #ifndef STBI_ONLY_GIF #define STBI_NO_GIF #endif #ifndef STBI_ONLY_HDR #define STBI_NO_HDR #endif #ifndef STBI_ONLY_PIC #define STBI_NO_PIC #endif #ifndef STBI_ONLY_PNM #define STBI_NO_PNM #endif #endif #if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) #define STBI_NO_ZLIB #endif #include #include // ptrdiff_t on osx #include #include #include #if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) #include // ldexp #endif #ifndef STBI_NO_STDIO #include #endif #ifndef STBI_ASSERT #include #define STBI_ASSERT(x) assert(x) #endif #ifndef _MSC_VER #ifdef __cplusplus #define stbi_inline inline #else #define stbi_inline #endif #else #define stbi_inline __forceinline #endif #ifdef _MSC_VER typedef unsigned short stbi__uint16; typedef signed short stbi__int16; typedef unsigned int stbi__uint32; typedef signed int stbi__int32; #else #include typedef uint16_t stbi__uint16; typedef int16_t stbi__int16; typedef uint32_t stbi__uint32; typedef int32_t stbi__int32; #endif // should produce compiler error if size is wrong typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; #ifdef _MSC_VER #define STBI_NOTUSED(v) (void)(v) #else #define STBI_NOTUSED(v) (void)sizeof(v) #endif #ifdef _MSC_VER #define STBI_HAS_LROTL #endif #ifdef STBI_HAS_LROTL #define stbi_lrot(x,y) _lrotl(x,y) #else #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) #endif #if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) // ok #elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) // ok #else #error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." #endif #ifndef STBI_MALLOC #define STBI_MALLOC(sz) malloc(sz) #define STBI_REALLOC(p,newsz) realloc(p,newsz) #define STBI_FREE(p) free(p) #endif #ifndef STBI_REALLOC_SIZED #define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) #endif // x86/x64 detection #if defined(__x86_64__) || defined(_M_X64) #define STBI__X64_TARGET #elif defined(__i386) || defined(_M_IX86) #define STBI__X86_TARGET #endif #if defined(__GNUC__) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) // NOTE: not clear do we actually need this for the 64-bit path? // gcc doesn't support sse2 intrinsics unless you compile with -msse2, // (but compiling with -msse2 allows the compiler to use SSE2 everywhere; // this is just broken and gcc are jerks for not fixing it properly // http://www.virtualdub.org/blog/pivot/entry.php?id=363 ) #define STBI_NO_SIMD #endif #if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) // Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET // // 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the // Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. // As a result, enabling SSE2 on 32-bit MinGW is dangerous when not // simultaneously enabling "-mstackrealign". // // See https://github.com/nothings/stb/issues/81 for more information. // // So default to no SSE2 on 32-bit MinGW. If you've read this far and added // -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. #define STBI_NO_SIMD #endif #if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) #define STBI_SSE2 #include #ifdef _MSC_VER #if _MSC_VER >= 1400 // not VC6 #include // __cpuid static int stbi__cpuid3(void) { int info[4]; __cpuid(info,1); return info[3]; } #else static int stbi__cpuid3(void) { int res; __asm { mov eax,1 cpuid mov res,edx } return res; } #endif #define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name static int stbi__sse2_available() { int info3 = stbi__cpuid3(); return ((info3 >> 26) & 1) != 0; } #else // assume GCC-style if not VC++ #define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) static int stbi__sse2_available() { #if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 // GCC 4.8 or later // GCC 4.8+ has a nice way to do this return __builtin_cpu_supports("sse2"); #else // portable way to do this, preferably without using GCC inline ASM? // just bail for now. return 0; #endif } #endif #endif // ARM NEON #if defined(STBI_NO_SIMD) && defined(STBI_NEON) #undef STBI_NEON #endif #ifdef STBI_NEON #include // assume GCC or Clang on ARM targets #define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) #endif #ifndef STBI_SIMD_ALIGN #define STBI_SIMD_ALIGN(type, name) type name #endif /////////////////////////////////////////////// // // stbi__context struct and start_xxx functions // stbi__context structure is our basic context used by all images, so it // contains all the IO context, plus some basic image information typedef struct { stbi__uint32 img_x, img_y; int img_n, img_out_n; stbi_io_callbacks io; void *io_user_data; int read_from_callbacks; int buflen; stbi_uc buffer_start[128]; stbi_uc *img_buffer, *img_buffer_end; stbi_uc *img_buffer_original, *img_buffer_original_end; } stbi__context; static void stbi__refill_buffer(stbi__context *s); // initialize a memory-decode context static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) { s->io.read = NULL; s->read_from_callbacks = 0; s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; } // initialize a callback-based context static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) { s->io = *c; s->io_user_data = user; s->buflen = sizeof(s->buffer_start); s->read_from_callbacks = 1; s->img_buffer_original = s->buffer_start; stbi__refill_buffer(s); s->img_buffer_original_end = s->img_buffer_end; } #ifndef STBI_NO_STDIO static int stbi__stdio_read(void *user, char *data, int size) { return (int) fread(data,1,size,(FILE*) user); } static void stbi__stdio_skip(void *user, int n) { fseek((FILE*) user, n, SEEK_CUR); } static int stbi__stdio_eof(void *user) { return feof((FILE*) user); } static stbi_io_callbacks stbi__stdio_callbacks = { stbi__stdio_read, stbi__stdio_skip, stbi__stdio_eof, }; static void stbi__start_file(stbi__context *s, FILE *f) { stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); } //static void stop_file(stbi__context *s) { } #endif // !STBI_NO_STDIO static void stbi__rewind(stbi__context *s) { // conceptually rewind SHOULD rewind to the beginning of the stream, // but we just rewind to the beginning of the initial buffer, because // we only use it after doing 'test', which only ever looks at at most 92 bytes s->img_buffer = s->img_buffer_original; s->img_buffer_end = s->img_buffer_original_end; } enum { STBI_ORDER_RGB, STBI_ORDER_BGR }; typedef struct { int bits_per_channel; int num_channels; int channel_order; } stbi__result_info; #ifndef STBI_NO_JPEG static int stbi__jpeg_test(stbi__context *s); static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PNG static int stbi__png_test(stbi__context *s); static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_BMP static int stbi__bmp_test(stbi__context *s); static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_TGA static int stbi__tga_test(stbi__context *s); static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PSD static int stbi__psd_test(stbi__context *s); static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_HDR static int stbi__hdr_test(stbi__context *s); static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PIC static int stbi__pic_test(stbi__context *s); static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_GIF static int stbi__gif_test(stbi__context *s); static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PNM static int stbi__pnm_test(stbi__context *s); static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); #endif // this is not threadsafe static const char *stbi__g_failure_reason; STBIDEF const char *stbi_failure_reason(void) { return stbi__g_failure_reason; } static int stbi__err(const char *str) { stbi__g_failure_reason = str; return 0; } static void *stbi__malloc(size_t size) { return STBI_MALLOC(size); } // stb_image uses ints pervasively, including for offset calculations. // therefore the largest decoded image size we can support with the // current code, even on 64-bit targets, is INT_MAX. this is not a // significant limitation for the intended use case. // // we do, however, need to make sure our size calculations don't // overflow. hence a few helper functions for size calculations that // multiply integers together, making sure that they're non-negative // and no overflow occurs. // return 1 if the sum is valid, 0 on overflow. // negative terms are considered invalid. static int stbi__addsizes_valid(int a, int b) { if (b < 0) return 0; // now 0 <= b <= INT_MAX, hence also // 0 <= INT_MAX - b <= INTMAX. // And "a + b <= INT_MAX" (which might overflow) is the // same as a <= INT_MAX - b (no overflow) return a <= INT_MAX - b; } // returns 1 if the product is valid, 0 on overflow. // negative factors are considered invalid. static int stbi__mul2sizes_valid(int a, int b) { if (a < 0 || b < 0) return 0; if (b == 0) return 1; // mul-by-0 is always safe // portable way to check for no overflows in a*b return a <= INT_MAX/b; } // returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow static int stbi__mad2sizes_valid(int a, int b, int add) { return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); } // returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow static int stbi__mad3sizes_valid(int a, int b, int c, int add) { return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && stbi__addsizes_valid(a*b*c, add); } // returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) { return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); } // mallocs with size overflow checking static void *stbi__malloc_mad2(int a, int b, int add) { if (!stbi__mad2sizes_valid(a, b, add)) return NULL; return stbi__malloc(a*b + add); } static void *stbi__malloc_mad3(int a, int b, int c, int add) { if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; return stbi__malloc(a*b*c + add); } static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) { if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; return stbi__malloc(a*b*c*d + add); } // stbi__err - error // stbi__errpf - error returning pointer to float // stbi__errpuc - error returning pointer to unsigned char #ifdef STBI_NO_FAILURE_STRINGS #define stbi__err(x,y) 0 #elif defined(STBI_FAILURE_USERMSG) #define stbi__err(x,y) stbi__err(y) #else #define stbi__err(x,y) stbi__err(x) #endif #define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) #define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) STBIDEF void stbi_image_free(void *retval_from_stbi_load) { STBI_FREE(retval_from_stbi_load); } #ifndef STBI_NO_LINEAR static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); #endif #ifndef STBI_NO_HDR static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); #endif static int stbi__vertically_flip_on_load = 0; STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) { stbi__vertically_flip_on_load = flag_true_if_should_flip; } static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) { memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order ri->num_channels = 0; #ifndef STBI_NO_JPEG if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_PNG if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_BMP if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_GIF if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_PSD if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); #endif #ifndef STBI_NO_PIC if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_PNM if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_HDR if (stbi__hdr_test(s)) { float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); } #endif #ifndef STBI_NO_TGA // test tga last because it's a crappy test! if (stbi__tga_test(s)) return stbi__tga_load(s,x,y,comp,req_comp, ri); #endif return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); } static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) { int i; int img_len = w * h * channels; stbi_uc *reduced; reduced = (stbi_uc *) stbi__malloc(img_len); if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); for (i = 0; i < img_len; ++i) reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling STBI_FREE(orig); return reduced; } static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) { int i; int img_len = w * h * channels; stbi__uint16 *enlarged; enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); for (i = 0; i < img_len; ++i) enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff STBI_FREE(orig); return enlarged; } static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) { stbi__result_info ri; void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); if (result == NULL) return NULL; if (ri.bits_per_channel != 8) { STBI_ASSERT(ri.bits_per_channel == 16); result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); ri.bits_per_channel = 8; } // @TODO: move stbi__convert_format to here if (stbi__vertically_flip_on_load) { int w = *x, h = *y; int channels = req_comp ? req_comp : *comp; int row,col,z; stbi_uc *image = (stbi_uc *) result; // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once for (row = 0; row < (h>>1); row++) { for (col = 0; col < w; col++) { for (z = 0; z < channels; z++) { stbi_uc temp = image[(row * w + col) * channels + z]; image[(row * w + col) * channels + z] = image[((h - row - 1) * w + col) * channels + z]; image[((h - row - 1) * w + col) * channels + z] = temp; } } } } return (unsigned char *) result; } static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) { stbi__result_info ri; void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); if (result == NULL) return NULL; if (ri.bits_per_channel != 16) { STBI_ASSERT(ri.bits_per_channel == 8); result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); ri.bits_per_channel = 16; } // @TODO: move stbi__convert_format16 to here // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision if (stbi__vertically_flip_on_load) { int w = *x, h = *y; int channels = req_comp ? req_comp : *comp; int row,col,z; stbi__uint16 *image = (stbi__uint16 *) result; // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once for (row = 0; row < (h>>1); row++) { for (col = 0; col < w; col++) { for (z = 0; z < channels; z++) { stbi__uint16 temp = image[(row * w + col) * channels + z]; image[(row * w + col) * channels + z] = image[((h - row - 1) * w + col) * channels + z]; image[((h - row - 1) * w + col) * channels + z] = temp; } } } } return (stbi__uint16 *) result; } #ifndef STBI_NO_HDR static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) { if (stbi__vertically_flip_on_load && result != NULL) { int w = *x, h = *y; int depth = req_comp ? req_comp : *comp; int row,col,z; float temp; // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once for (row = 0; row < (h>>1); row++) { for (col = 0; col < w; col++) { for (z = 0; z < depth; z++) { temp = result[(row * w + col) * depth + z]; result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z]; result[((h - row - 1) * w + col) * depth + z] = temp; } } } } } #endif #ifndef STBI_NO_STDIO static FILE *stbi__fopen(char const *filename, char const *mode) { FILE *f; #if defined(_MSC_VER) && _MSC_VER >= 1400 if (0 != fopen_s(&f, filename, mode)) f=0; #else f = fopen(filename, mode); #endif return f; } STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) { FILE *f = stbi__fopen(filename, "rb"); unsigned char *result; if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); result = stbi_load_from_file(f,x,y,comp,req_comp); fclose(f); return result; } STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) { unsigned char *result; stbi__context s; stbi__start_file(&s,f); result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); if (result) { // need to 'unget' all the characters in the IO buffer fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); } return result; } STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) { stbi__uint16 *result; stbi__context s; stbi__start_file(&s,f); result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); if (result) { // need to 'unget' all the characters in the IO buffer fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); } return result; } STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) { FILE *f = stbi__fopen(filename, "rb"); stbi__uint16 *result; if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); result = stbi_load_from_file_16(f,x,y,comp,req_comp); fclose(f); return result; } #endif //!STBI_NO_STDIO STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) { stbi__context s; stbi__start_mem(&s,buffer,len); return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); } STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) { stbi__context s; stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); } #ifndef STBI_NO_LINEAR static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) { unsigned char *data; #ifndef STBI_NO_HDR if (stbi__hdr_test(s)) { stbi__result_info ri; float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); if (hdr_data) stbi__float_postprocess(hdr_data,x,y,comp,req_comp); return hdr_data; } #endif data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); if (data) return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); } STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) { stbi__context s; stbi__start_mem(&s,buffer,len); return stbi__loadf_main(&s,x,y,comp,req_comp); } STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) { stbi__context s; stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); return stbi__loadf_main(&s,x,y,comp,req_comp); } #ifndef STBI_NO_STDIO STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) { float *result; FILE *f = stbi__fopen(filename, "rb"); if (!f) return stbi__errpf("can't fopen", "Unable to open file"); result = stbi_loadf_from_file(f,x,y,comp,req_comp); fclose(f); return result; } STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) { stbi__context s; stbi__start_file(&s,f); return stbi__loadf_main(&s,x,y,comp,req_comp); } #endif // !STBI_NO_STDIO #endif // !STBI_NO_LINEAR // these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is // defined, for API simplicity; if STBI_NO_LINEAR is defined, it always // reports false! STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) { #ifndef STBI_NO_HDR stbi__context s; stbi__start_mem(&s,buffer,len); return stbi__hdr_test(&s); #else STBI_NOTUSED(buffer); STBI_NOTUSED(len); return 0; #endif } #ifndef STBI_NO_STDIO STBIDEF int stbi_is_hdr (char const *filename) { FILE *f = stbi__fopen(filename, "rb"); int result=0; if (f) { result = stbi_is_hdr_from_file(f); fclose(f); } return result; } STBIDEF int stbi_is_hdr_from_file(FILE *f) { #ifndef STBI_NO_HDR stbi__context s; stbi__start_file(&s,f); return stbi__hdr_test(&s); #else STBI_NOTUSED(f); return 0; #endif } #endif // !STBI_NO_STDIO STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) { #ifndef STBI_NO_HDR stbi__context s; stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); return stbi__hdr_test(&s); #else STBI_NOTUSED(clbk); STBI_NOTUSED(user); return 0; #endif } #ifndef STBI_NO_LINEAR static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } #endif static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } ////////////////////////////////////////////////////////////////////////////// // // Common code used by all image loaders // enum { STBI__SCAN_load=0, STBI__SCAN_type, STBI__SCAN_header }; static void stbi__refill_buffer(stbi__context *s) { int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); if (n == 0) { // at end of file, treat same as if from memory, but need to handle case // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file s->read_from_callbacks = 0; s->img_buffer = s->buffer_start; s->img_buffer_end = s->buffer_start+1; *s->img_buffer = 0; } else { s->img_buffer = s->buffer_start; s->img_buffer_end = s->buffer_start + n; } } stbi_inline static stbi_uc stbi__get8(stbi__context *s) { if (s->img_buffer < s->img_buffer_end) return *s->img_buffer++; if (s->read_from_callbacks) { stbi__refill_buffer(s); return *s->img_buffer++; } return 0; } stbi_inline static int stbi__at_eof(stbi__context *s) { if (s->io.read) { if (!(s->io.eof)(s->io_user_data)) return 0; // if feof() is true, check if buffer = end // special case: we've only got the special 0 character at the end if (s->read_from_callbacks == 0) return 1; } return s->img_buffer >= s->img_buffer_end; } static void stbi__skip(stbi__context *s, int n) { if (n < 0) { s->img_buffer = s->img_buffer_end; return; } if (s->io.read) { int blen = (int) (s->img_buffer_end - s->img_buffer); if (blen < n) { s->img_buffer = s->img_buffer_end; (s->io.skip)(s->io_user_data, n - blen); return; } } s->img_buffer += n; } static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) { if (s->io.read) { int blen = (int) (s->img_buffer_end - s->img_buffer); if (blen < n) { int res, count; memcpy(buffer, s->img_buffer, blen); count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); res = (count == (n-blen)); s->img_buffer = s->img_buffer_end; return res; } } if (s->img_buffer+n <= s->img_buffer_end) { memcpy(buffer, s->img_buffer, n); s->img_buffer += n; return 1; } else return 0; } static int stbi__get16be(stbi__context *s) { int z = stbi__get8(s); return (z << 8) + stbi__get8(s); } static stbi__uint32 stbi__get32be(stbi__context *s) { stbi__uint32 z = stbi__get16be(s); return (z << 16) + stbi__get16be(s); } #if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) // nothing #else static int stbi__get16le(stbi__context *s) { int z = stbi__get8(s); return z + (stbi__get8(s) << 8); } #endif #ifndef STBI_NO_BMP static stbi__uint32 stbi__get32le(stbi__context *s) { stbi__uint32 z = stbi__get16le(s); return z + (stbi__get16le(s) << 16); } #endif #define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings ////////////////////////////////////////////////////////////////////////////// // // generic converter from built-in img_n to req_comp // individual types do this automatically as much as possible (e.g. jpeg // does all cases internally since it needs to colorspace convert anyway, // and it never has alpha, so very few cases ). png can automatically // interleave an alpha=255 channel, but falls back to this for other cases // // assume data buffer is malloced, so malloc a new one and free that one // only failure mode is malloc failing static stbi_uc stbi__compute_y(int r, int g, int b) { return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); } static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) { int i,j; unsigned char *good; if (req_comp == img_n) return data; STBI_ASSERT(req_comp >= 1 && req_comp <= 4); good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); if (good == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } for (j=0; j < (int) y; ++j) { unsigned char *src = data + j * x * img_n ; unsigned char *dest = good + j * x * req_comp; #define STBI__COMBO(a,b) ((a)*8+(b)) #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) // convert source image with img_n components to one with req_comp components; // avoid switch per pixel, so use switch per scanline and massive macros switch (STBI__COMBO(img_n, req_comp)) { STBI__CASE(1,2) { dest[0]=src[0], dest[1]=255; } break; STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; } break; STBI__CASE(2,1) { dest[0]=src[0]; } break; STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; } break; STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; } break; STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; } break; STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; default: STBI_ASSERT(0); } #undef STBI__CASE } STBI_FREE(data); return good; } static stbi__uint16 stbi__compute_y_16(int r, int g, int b) { return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); } static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) { int i,j; stbi__uint16 *good; if (req_comp == img_n) return data; STBI_ASSERT(req_comp >= 1 && req_comp <= 4); good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); if (good == NULL) { STBI_FREE(data); return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); } for (j=0; j < (int) y; ++j) { stbi__uint16 *src = data + j * x * img_n ; stbi__uint16 *dest = good + j * x * req_comp; #define STBI__COMBO(a,b) ((a)*8+(b)) #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) // convert source image with img_n components to one with req_comp components; // avoid switch per pixel, so use switch per scanline and massive macros switch (STBI__COMBO(img_n, req_comp)) { STBI__CASE(1,2) { dest[0]=src[0], dest[1]=0xffff; } break; STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=0xffff; } break; STBI__CASE(2,1) { dest[0]=src[0]; } break; STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=0xffff; } break; STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = 0xffff; } break; STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = src[3]; } break; STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; default: STBI_ASSERT(0); } #undef STBI__CASE } STBI_FREE(data); return good; } #ifndef STBI_NO_LINEAR static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) { int i,k,n; float *output; if (!data) return NULL; output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } // compute number of non-alpha components if (comp & 1) n = comp; else n = comp-1; for (i=0; i < x*y; ++i) { for (k=0; k < n; ++k) { output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); } if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; } STBI_FREE(data); return output; } #endif #ifndef STBI_NO_HDR #define stbi__float2int(x) ((int) (x)) static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) { int i,k,n; stbi_uc *output; if (!data) return NULL; output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } // compute number of non-alpha components if (comp & 1) n = comp; else n = comp-1; for (i=0; i < x*y; ++i) { for (k=0; k < n; ++k) { float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; if (z < 0) z = 0; if (z > 255) z = 255; output[i*comp + k] = (stbi_uc) stbi__float2int(z); } if (k < comp) { float z = data[i*comp+k] * 255 + 0.5f; if (z < 0) z = 0; if (z > 255) z = 255; output[i*comp + k] = (stbi_uc) stbi__float2int(z); } } STBI_FREE(data); return output; } #endif ////////////////////////////////////////////////////////////////////////////// // // "baseline" JPEG/JFIF decoder // // simple implementation // - doesn't support delayed output of y-dimension // - simple interface (only one output format: 8-bit interleaved RGB) // - doesn't try to recover corrupt jpegs // - doesn't allow partial loading, loading multiple at once // - still fast on x86 (copying globals into locals doesn't help x86) // - allocates lots of intermediate memory (full size of all components) // - non-interleaved case requires this anyway // - allows good upsampling (see next) // high-quality // - upsampled channels are bilinearly interpolated, even across blocks // - quality integer IDCT derived from IJG's 'slow' // performance // - fast huffman; reasonable integer IDCT // - some SIMD kernels for common paths on targets with SSE2/NEON // - uses a lot of intermediate memory, could cache poorly #ifndef STBI_NO_JPEG // huffman decoding acceleration #define FAST_BITS 9 // larger handles more cases; smaller stomps less cache typedef struct { stbi_uc fast[1 << FAST_BITS]; // weirdly, repacking this into AoS is a 10% speed loss, instead of a win stbi__uint16 code[256]; stbi_uc values[256]; stbi_uc size[257]; unsigned int maxcode[18]; int delta[17]; // old 'firstsymbol' - old 'firstcode' } stbi__huffman; typedef struct { stbi__context *s; stbi__huffman huff_dc[4]; stbi__huffman huff_ac[4]; stbi__uint16 dequant[4][64]; stbi__int16 fast_ac[4][1 << FAST_BITS]; // sizes for components, interleaved MCUs int img_h_max, img_v_max; int img_mcu_x, img_mcu_y; int img_mcu_w, img_mcu_h; // definition of jpeg image component struct { int id; int h,v; int tq; int hd,ha; int dc_pred; int x,y,w2,h2; stbi_uc *data; void *raw_data, *raw_coeff; stbi_uc *linebuf; short *coeff; // progressive only int coeff_w, coeff_h; // number of 8x8 coefficient blocks } img_comp[4]; stbi__uint32 code_buffer; // jpeg entropy-coded buffer int code_bits; // number of valid bits unsigned char marker; // marker seen while filling entropy buffer int nomore; // flag if we saw a marker so must stop int progressive; int spec_start; int spec_end; int succ_high; int succ_low; int eob_run; int rgb; int scan_n, order[4]; int restart_interval, todo; // kernels void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); } stbi__jpeg; static int stbi__build_huffman(stbi__huffman *h, int *count) { int i,j,k=0,code; // build size list for each symbol (from JPEG spec) for (i=0; i < 16; ++i) for (j=0; j < count[i]; ++j) h->size[k++] = (stbi_uc) (i+1); h->size[k] = 0; // compute actual symbols (from jpeg spec) code = 0; k = 0; for(j=1; j <= 16; ++j) { // compute delta to add to code to compute symbol id h->delta[j] = k - code; if (h->size[k] == j) { while (h->size[k] == j) h->code[k++] = (stbi__uint16) (code++); if (code-1 >= (1 << j)) return stbi__err("bad code lengths","Corrupt JPEG"); } // compute largest code + 1 for this size, preshifted as needed later h->maxcode[j] = code << (16-j); code <<= 1; } h->maxcode[j] = 0xffffffff; // build non-spec acceleration table; 255 is flag for not-accelerated memset(h->fast, 255, 1 << FAST_BITS); for (i=0; i < k; ++i) { int s = h->size[i]; if (s <= FAST_BITS) { int c = h->code[i] << (FAST_BITS-s); int m = 1 << (FAST_BITS-s); for (j=0; j < m; ++j) { h->fast[c+j] = (stbi_uc) i; } } } return 1; } // build a table that decodes both magnitude and value of small ACs in // one go. static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) { int i; for (i=0; i < (1 << FAST_BITS); ++i) { stbi_uc fast = h->fast[i]; fast_ac[i] = 0; if (fast < 255) { int rs = h->values[fast]; int run = (rs >> 4) & 15; int magbits = rs & 15; int len = h->size[fast]; if (magbits && len + magbits <= FAST_BITS) { // magnitude code followed by receive_extend code int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); int m = 1 << (magbits - 1); if (k < m) k += (-1 << magbits) + 1; // if the result is small enough, we can fit it in fast_ac table if (k >= -128 && k <= 127) fast_ac[i] = (stbi__int16) ((k << 8) + (run << 4) + (len + magbits)); } } } } static void stbi__grow_buffer_unsafe(stbi__jpeg *j) { do { int b = j->nomore ? 0 : stbi__get8(j->s); if (b == 0xff) { int c = stbi__get8(j->s); while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes if (c != 0) { j->marker = (unsigned char) c; j->nomore = 1; return; } } j->code_buffer |= b << (24 - j->code_bits); j->code_bits += 8; } while (j->code_bits <= 24); } // (1 << n) - 1 static stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; // decode a jpeg huffman value from the bitstream stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) { unsigned int temp; int c,k; if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); // look at the top FAST_BITS and determine what symbol ID it is, // if the code is <= FAST_BITS c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); k = h->fast[c]; if (k < 255) { int s = h->size[k]; if (s > j->code_bits) return -1; j->code_buffer <<= s; j->code_bits -= s; return h->values[k]; } // naive test is to shift the code_buffer down so k bits are // valid, then test against maxcode. To speed this up, we've // preshifted maxcode left so that it has (16-k) 0s at the // end; in other words, regardless of the number of bits, it // wants to be compared against something shifted to have 16; // that way we don't need to shift inside the loop. temp = j->code_buffer >> 16; for (k=FAST_BITS+1 ; ; ++k) if (temp < h->maxcode[k]) break; if (k == 17) { // error! code not found j->code_bits -= 16; return -1; } if (k > j->code_bits) return -1; // convert the huffman code to the symbol id c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); // convert the id to a symbol j->code_bits -= k; j->code_buffer <<= k; return h->values[c]; } // bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB k = stbi_lrot(j->code_buffer, n); STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))); j->code_buffer = k & ~stbi__bmask[n]; k &= stbi__bmask[n]; j->code_bits -= n; return k + (stbi__jbias[n] & ~sgn); } // get some unsigned bits stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) { unsigned int k; if (j->code_bits < n) stbi__grow_buffer_unsafe(j); k = stbi_lrot(j->code_buffer, n); j->code_buffer = k & ~stbi__bmask[n]; k &= stbi__bmask[n]; j->code_bits -= n; return k; } stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) { unsigned int k; if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); k = j->code_buffer; j->code_buffer <<= 1; --j->code_bits; return k & 0x80000000; } // given a value that's at position X in the zigzag stream, // where does it appear in the 8x8 matrix coded as row-major? static stbi_uc stbi__jpeg_dezigzag[64+15] = { 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63, // let corrupt input sample past end 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }; // decode one 64-entry block-- static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) { int diff,dc,k; int t; if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); t = stbi__jpeg_huff_decode(j, hdc); if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); // 0 all the ac values now so we can do it 32-bits at a time memset(data,0,64*sizeof(data[0])); diff = t ? stbi__extend_receive(j, t) : 0; dc = j->img_comp[b].dc_pred + diff; j->img_comp[b].dc_pred = dc; data[0] = (short) (dc * dequant[0]); // decode AC components, see JPEG spec k = 1; do { unsigned int zig; int c,r,s; if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); r = fac[c]; if (r) { // fast-AC path k += (r >> 4) & 15; // run s = r & 15; // combined length j->code_buffer <<= s; j->code_bits -= s; // decode into unzigzag'd location zig = stbi__jpeg_dezigzag[k++]; data[zig] = (short) ((r >> 8) * dequant[zig]); } else { int rs = stbi__jpeg_huff_decode(j, hac); if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); s = rs & 15; r = rs >> 4; if (s == 0) { if (rs != 0xf0) break; // end block k += 16; } else { k += r; // decode into unzigzag'd location zig = stbi__jpeg_dezigzag[k++]; data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); } } } while (k < 64); return 1; } static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) { int diff,dc; int t; if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); if (j->succ_high == 0) { // first scan for DC coefficient, must be first memset(data,0,64*sizeof(data[0])); // 0 all the ac values now t = stbi__jpeg_huff_decode(j, hdc); diff = t ? stbi__extend_receive(j, t) : 0; dc = j->img_comp[b].dc_pred + diff; j->img_comp[b].dc_pred = dc; data[0] = (short) (dc << j->succ_low); } else { // refinement scan for DC coefficient if (stbi__jpeg_get_bit(j)) data[0] += (short) (1 << j->succ_low); } return 1; } // @OPTIMIZE: store non-zigzagged during the decode passes, // and only de-zigzag when dequantizing static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) { int k; if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); if (j->succ_high == 0) { int shift = j->succ_low; if (j->eob_run) { --j->eob_run; return 1; } k = j->spec_start; do { unsigned int zig; int c,r,s; if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); r = fac[c]; if (r) { // fast-AC path k += (r >> 4) & 15; // run s = r & 15; // combined length j->code_buffer <<= s; j->code_bits -= s; zig = stbi__jpeg_dezigzag[k++]; data[zig] = (short) ((r >> 8) << shift); } else { int rs = stbi__jpeg_huff_decode(j, hac); if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); s = rs & 15; r = rs >> 4; if (s == 0) { if (r < 15) { j->eob_run = (1 << r); if (r) j->eob_run += stbi__jpeg_get_bits(j, r); --j->eob_run; break; } k += 16; } else { k += r; zig = stbi__jpeg_dezigzag[k++]; data[zig] = (short) (stbi__extend_receive(j,s) << shift); } } } while (k <= j->spec_end); } else { // refinement scan for these AC coefficients short bit = (short) (1 << j->succ_low); if (j->eob_run) { --j->eob_run; for (k = j->spec_start; k <= j->spec_end; ++k) { short *p = &data[stbi__jpeg_dezigzag[k]]; if (*p != 0) if (stbi__jpeg_get_bit(j)) if ((*p & bit)==0) { if (*p > 0) *p += bit; else *p -= bit; } } } else { k = j->spec_start; do { int r,s; int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); s = rs & 15; r = rs >> 4; if (s == 0) { if (r < 15) { j->eob_run = (1 << r) - 1; if (r) j->eob_run += stbi__jpeg_get_bits(j, r); r = 64; // force end of block } else { // r=15 s=0 should write 16 0s, so we just do // a run of 15 0s and then write s (which is 0), // so we don't have to do anything special here } } else { if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); // sign bit if (stbi__jpeg_get_bit(j)) s = bit; else s = -bit; } // advance by r while (k <= j->spec_end) { short *p = &data[stbi__jpeg_dezigzag[k++]]; if (*p != 0) { if (stbi__jpeg_get_bit(j)) if ((*p & bit)==0) { if (*p > 0) *p += bit; else *p -= bit; } } else { if (r == 0) { *p = (short) s; break; } --r; } } } while (k <= j->spec_end); } } return 1; } // take a -128..127 value and stbi__clamp it and convert to 0..255 stbi_inline static stbi_uc stbi__clamp(int x) { // trick to use a single test to catch both cases if ((unsigned int) x > 255) { if (x < 0) return 0; if (x > 255) return 255; } return (stbi_uc) x; } #define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) #define stbi__fsh(x) ((x) << 12) // derived from jidctint -- DCT_ISLOW #define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ p2 = s2; \ p3 = s6; \ p1 = (p2+p3) * stbi__f2f(0.5411961f); \ t2 = p1 + p3*stbi__f2f(-1.847759065f); \ t3 = p1 + p2*stbi__f2f( 0.765366865f); \ p2 = s0; \ p3 = s4; \ t0 = stbi__fsh(p2+p3); \ t1 = stbi__fsh(p2-p3); \ x0 = t0+t3; \ x3 = t0-t3; \ x1 = t1+t2; \ x2 = t1-t2; \ t0 = s7; \ t1 = s5; \ t2 = s3; \ t3 = s1; \ p3 = t0+t2; \ p4 = t1+t3; \ p1 = t0+t3; \ p2 = t1+t2; \ p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ t0 = t0*stbi__f2f( 0.298631336f); \ t1 = t1*stbi__f2f( 2.053119869f); \ t2 = t2*stbi__f2f( 3.072711026f); \ t3 = t3*stbi__f2f( 1.501321110f); \ p1 = p5 + p1*stbi__f2f(-0.899976223f); \ p2 = p5 + p2*stbi__f2f(-2.562915447f); \ p3 = p3*stbi__f2f(-1.961570560f); \ p4 = p4*stbi__f2f(-0.390180644f); \ t3 += p1+p4; \ t2 += p2+p3; \ t1 += p2+p4; \ t0 += p1+p3; static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) { int i,val[64],*v=val; stbi_uc *o; short *d = data; // columns for (i=0; i < 8; ++i,++d, ++v) { // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 && d[40]==0 && d[48]==0 && d[56]==0) { // no shortcut 0 seconds // (1|2|3|4|5|6|7)==0 0 seconds // all separate -0.047 seconds // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds int dcterm = d[0] << 2; v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; } else { STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) // constants scaled things up by 1<<12; let's bring them back // down, but keep 2 extra bits of precision x0 += 512; x1 += 512; x2 += 512; x3 += 512; v[ 0] = (x0+t3) >> 10; v[56] = (x0-t3) >> 10; v[ 8] = (x1+t2) >> 10; v[48] = (x1-t2) >> 10; v[16] = (x2+t1) >> 10; v[40] = (x2-t1) >> 10; v[24] = (x3+t0) >> 10; v[32] = (x3-t0) >> 10; } } for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { // no fast case since the first 1D IDCT spread components out STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) // constants scaled things up by 1<<12, plus we had 1<<2 from first // loop, plus horizontal and vertical each scale by sqrt(8) so together // we've got an extra 1<<3, so 1<<17 total we need to remove. // so we want to round that, which means adding 0.5 * 1<<17, // aka 65536. Also, we'll end up with -128 to 127 that we want // to encode as 0..255 by adding 128, so we'll add that before the shift x0 += 65536 + (128<<17); x1 += 65536 + (128<<17); x2 += 65536 + (128<<17); x3 += 65536 + (128<<17); // tried computing the shifts into temps, or'ing the temps to see // if any were out of range, but that was slower o[0] = stbi__clamp((x0+t3) >> 17); o[7] = stbi__clamp((x0-t3) >> 17); o[1] = stbi__clamp((x1+t2) >> 17); o[6] = stbi__clamp((x1-t2) >> 17); o[2] = stbi__clamp((x2+t1) >> 17); o[5] = stbi__clamp((x2-t1) >> 17); o[3] = stbi__clamp((x3+t0) >> 17); o[4] = stbi__clamp((x3-t0) >> 17); } } #ifdef STBI_SSE2 // sse2 integer IDCT. not the fastest possible implementation but it // produces bit-identical results to the generic C version so it's // fully "transparent". static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) { // This is constructed to match our regular (generic) integer IDCT exactly. __m128i row0, row1, row2, row3, row4, row5, row6, row7; __m128i tmp; // dot product constant: even elems=x, odd elems=y #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) // out(1) = c1[even]*x + c1[odd]*y #define dct_rot(out0,out1, x,y,c0,c1) \ __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) // out = in << 12 (in 16-bit, out 32-bit) #define dct_widen(out, in) \ __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) // wide add #define dct_wadd(out, a, b) \ __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ __m128i out##_h = _mm_add_epi32(a##_h, b##_h) // wide sub #define dct_wsub(out, a, b) \ __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) // butterfly a/b, add bias, then shift by "s" and pack #define dct_bfly32o(out0, out1, a,b,bias,s) \ { \ __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ dct_wadd(sum, abiased, b); \ dct_wsub(dif, abiased, b); \ out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ } // 8-bit interleave step (for transposes) #define dct_interleave8(a, b) \ tmp = a; \ a = _mm_unpacklo_epi8(a, b); \ b = _mm_unpackhi_epi8(tmp, b) // 16-bit interleave step (for transposes) #define dct_interleave16(a, b) \ tmp = a; \ a = _mm_unpacklo_epi16(a, b); \ b = _mm_unpackhi_epi16(tmp, b) #define dct_pass(bias,shift) \ { \ /* even part */ \ dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ __m128i sum04 = _mm_add_epi16(row0, row4); \ __m128i dif04 = _mm_sub_epi16(row0, row4); \ dct_widen(t0e, sum04); \ dct_widen(t1e, dif04); \ dct_wadd(x0, t0e, t3e); \ dct_wsub(x3, t0e, t3e); \ dct_wadd(x1, t1e, t2e); \ dct_wsub(x2, t1e, t2e); \ /* odd part */ \ dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ __m128i sum17 = _mm_add_epi16(row1, row7); \ __m128i sum35 = _mm_add_epi16(row3, row5); \ dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ dct_wadd(x4, y0o, y4o); \ dct_wadd(x5, y1o, y5o); \ dct_wadd(x6, y2o, y5o); \ dct_wadd(x7, y3o, y4o); \ dct_bfly32o(row0,row7, x0,x7,bias,shift); \ dct_bfly32o(row1,row6, x1,x6,bias,shift); \ dct_bfly32o(row2,row5, x2,x5,bias,shift); \ dct_bfly32o(row3,row4, x3,x4,bias,shift); \ } __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); // rounding biases in column/row passes, see stbi__idct_block for explanation. __m128i bias_0 = _mm_set1_epi32(512); __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); // load row0 = _mm_load_si128((const __m128i *) (data + 0*8)); row1 = _mm_load_si128((const __m128i *) (data + 1*8)); row2 = _mm_load_si128((const __m128i *) (data + 2*8)); row3 = _mm_load_si128((const __m128i *) (data + 3*8)); row4 = _mm_load_si128((const __m128i *) (data + 4*8)); row5 = _mm_load_si128((const __m128i *) (data + 5*8)); row6 = _mm_load_si128((const __m128i *) (data + 6*8)); row7 = _mm_load_si128((const __m128i *) (data + 7*8)); // column pass dct_pass(bias_0, 10); { // 16bit 8x8 transpose pass 1 dct_interleave16(row0, row4); dct_interleave16(row1, row5); dct_interleave16(row2, row6); dct_interleave16(row3, row7); // transpose pass 2 dct_interleave16(row0, row2); dct_interleave16(row1, row3); dct_interleave16(row4, row6); dct_interleave16(row5, row7); // transpose pass 3 dct_interleave16(row0, row1); dct_interleave16(row2, row3); dct_interleave16(row4, row5); dct_interleave16(row6, row7); } // row pass dct_pass(bias_1, 17); { // pack __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 __m128i p1 = _mm_packus_epi16(row2, row3); __m128i p2 = _mm_packus_epi16(row4, row5); __m128i p3 = _mm_packus_epi16(row6, row7); // 8bit 8x8 transpose pass 1 dct_interleave8(p0, p2); // a0e0a1e1... dct_interleave8(p1, p3); // c0g0c1g1... // transpose pass 2 dct_interleave8(p0, p1); // a0c0e0g0... dct_interleave8(p2, p3); // b0d0f0h0... // transpose pass 3 dct_interleave8(p0, p2); // a0b0c0d0... dct_interleave8(p1, p3); // a4b4c4d4... // store _mm_storel_epi64((__m128i *) out, p0); out += out_stride; _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; _mm_storel_epi64((__m128i *) out, p2); out += out_stride; _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; _mm_storel_epi64((__m128i *) out, p1); out += out_stride; _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; _mm_storel_epi64((__m128i *) out, p3); out += out_stride; _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); } #undef dct_const #undef dct_rot #undef dct_widen #undef dct_wadd #undef dct_wsub #undef dct_bfly32o #undef dct_interleave8 #undef dct_interleave16 #undef dct_pass } #endif // STBI_SSE2 #ifdef STBI_NEON // NEON integer IDCT. should produce bit-identical // results to the generic C version. static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) { int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); #define dct_long_mul(out, inq, coeff) \ int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) #define dct_long_mac(out, acc, inq, coeff) \ int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) #define dct_widen(out, inq) \ int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) // wide add #define dct_wadd(out, a, b) \ int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ int32x4_t out##_h = vaddq_s32(a##_h, b##_h) // wide sub #define dct_wsub(out, a, b) \ int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ int32x4_t out##_h = vsubq_s32(a##_h, b##_h) // butterfly a/b, then shift using "shiftop" by "s" and pack #define dct_bfly32o(out0,out1, a,b,shiftop,s) \ { \ dct_wadd(sum, a, b); \ dct_wsub(dif, a, b); \ out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ } #define dct_pass(shiftop, shift) \ { \ /* even part */ \ int16x8_t sum26 = vaddq_s16(row2, row6); \ dct_long_mul(p1e, sum26, rot0_0); \ dct_long_mac(t2e, p1e, row6, rot0_1); \ dct_long_mac(t3e, p1e, row2, rot0_2); \ int16x8_t sum04 = vaddq_s16(row0, row4); \ int16x8_t dif04 = vsubq_s16(row0, row4); \ dct_widen(t0e, sum04); \ dct_widen(t1e, dif04); \ dct_wadd(x0, t0e, t3e); \ dct_wsub(x3, t0e, t3e); \ dct_wadd(x1, t1e, t2e); \ dct_wsub(x2, t1e, t2e); \ /* odd part */ \ int16x8_t sum15 = vaddq_s16(row1, row5); \ int16x8_t sum17 = vaddq_s16(row1, row7); \ int16x8_t sum35 = vaddq_s16(row3, row5); \ int16x8_t sum37 = vaddq_s16(row3, row7); \ int16x8_t sumodd = vaddq_s16(sum17, sum35); \ dct_long_mul(p5o, sumodd, rot1_0); \ dct_long_mac(p1o, p5o, sum17, rot1_1); \ dct_long_mac(p2o, p5o, sum35, rot1_2); \ dct_long_mul(p3o, sum37, rot2_0); \ dct_long_mul(p4o, sum15, rot2_1); \ dct_wadd(sump13o, p1o, p3o); \ dct_wadd(sump24o, p2o, p4o); \ dct_wadd(sump23o, p2o, p3o); \ dct_wadd(sump14o, p1o, p4o); \ dct_long_mac(x4, sump13o, row7, rot3_0); \ dct_long_mac(x5, sump24o, row5, rot3_1); \ dct_long_mac(x6, sump23o, row3, rot3_2); \ dct_long_mac(x7, sump14o, row1, rot3_3); \ dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ } // load row0 = vld1q_s16(data + 0*8); row1 = vld1q_s16(data + 1*8); row2 = vld1q_s16(data + 2*8); row3 = vld1q_s16(data + 3*8); row4 = vld1q_s16(data + 4*8); row5 = vld1q_s16(data + 5*8); row6 = vld1q_s16(data + 6*8); row7 = vld1q_s16(data + 7*8); // add DC bias row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); // column pass dct_pass(vrshrn_n_s32, 10); // 16bit 8x8 transpose { // these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. // whether compilers actually get this is another story, sadly. #define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } #define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } #define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } // pass 1 dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 dct_trn16(row2, row3); dct_trn16(row4, row5); dct_trn16(row6, row7); // pass 2 dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 dct_trn32(row1, row3); dct_trn32(row4, row6); dct_trn32(row5, row7); // pass 3 dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 dct_trn64(row1, row5); dct_trn64(row2, row6); dct_trn64(row3, row7); #undef dct_trn16 #undef dct_trn32 #undef dct_trn64 } // row pass // vrshrn_n_s32 only supports shifts up to 16, we need // 17. so do a non-rounding shift of 16 first then follow // up with a rounding shift by 1. dct_pass(vshrn_n_s32, 16); { // pack and round uint8x8_t p0 = vqrshrun_n_s16(row0, 1); uint8x8_t p1 = vqrshrun_n_s16(row1, 1); uint8x8_t p2 = vqrshrun_n_s16(row2, 1); uint8x8_t p3 = vqrshrun_n_s16(row3, 1); uint8x8_t p4 = vqrshrun_n_s16(row4, 1); uint8x8_t p5 = vqrshrun_n_s16(row5, 1); uint8x8_t p6 = vqrshrun_n_s16(row6, 1); uint8x8_t p7 = vqrshrun_n_s16(row7, 1); // again, these can translate into one instruction, but often don't. #define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } #define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } #define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } // sadly can't use interleaved stores here since we only write // 8 bytes to each scan line! // 8x8 8-bit transpose pass 1 dct_trn8_8(p0, p1); dct_trn8_8(p2, p3); dct_trn8_8(p4, p5); dct_trn8_8(p6, p7); // pass 2 dct_trn8_16(p0, p2); dct_trn8_16(p1, p3); dct_trn8_16(p4, p6); dct_trn8_16(p5, p7); // pass 3 dct_trn8_32(p0, p4); dct_trn8_32(p1, p5); dct_trn8_32(p2, p6); dct_trn8_32(p3, p7); // store vst1_u8(out, p0); out += out_stride; vst1_u8(out, p1); out += out_stride; vst1_u8(out, p2); out += out_stride; vst1_u8(out, p3); out += out_stride; vst1_u8(out, p4); out += out_stride; vst1_u8(out, p5); out += out_stride; vst1_u8(out, p6); out += out_stride; vst1_u8(out, p7); #undef dct_trn8_8 #undef dct_trn8_16 #undef dct_trn8_32 } #undef dct_long_mul #undef dct_long_mac #undef dct_widen #undef dct_wadd #undef dct_wsub #undef dct_bfly32o #undef dct_pass } #endif // STBI_NEON #define STBI__MARKER_none 0xff // if there's a pending marker from the entropy stream, return that // otherwise, fetch from the stream and get a marker. if there's no // marker, return 0xff, which is never a valid marker value static stbi_uc stbi__get_marker(stbi__jpeg *j) { stbi_uc x; if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } x = stbi__get8(j->s); if (x != 0xff) return STBI__MARKER_none; while (x == 0xff) x = stbi__get8(j->s); // consume repeated 0xff fill bytes return x; } // in each scan, we'll have scan_n components, and the order // of the components is specified by order[] #define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) // after a restart interval, stbi__jpeg_reset the entropy decoder and // the dc prediction static void stbi__jpeg_reset(stbi__jpeg *j) { j->code_bits = 0; j->code_buffer = 0; j->nomore = 0; j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = 0; j->marker = STBI__MARKER_none; j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; j->eob_run = 0; // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, // since we don't even allow 1<<30 pixels } static int stbi__parse_entropy_coded_data(stbi__jpeg *z) { stbi__jpeg_reset(z); if (!z->progressive) { if (z->scan_n == 1) { int i,j; STBI_SIMD_ALIGN(short, data[64]); int n = z->order[0]; // non-interleaved data, we just need to process one block at a time, // in trivial scanline order // number of blocks to do just depends on how many actual "pixels" this // component has, independent of interleaved MCU blocking and such int w = (z->img_comp[n].x+7) >> 3; int h = (z->img_comp[n].y+7) >> 3; for (j=0; j < h; ++j) { for (i=0; i < w; ++i) { int ha = z->img_comp[n].ha; if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); // every data block is an MCU, so countdown the restart interval if (--z->todo <= 0) { if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); // if it's NOT a restart, then just bail, so we get corrupt data // rather than no data if (!STBI__RESTART(z->marker)) return 1; stbi__jpeg_reset(z); } } } return 1; } else { // interleaved int i,j,k,x,y; STBI_SIMD_ALIGN(short, data[64]); for (j=0; j < z->img_mcu_y; ++j) { for (i=0; i < z->img_mcu_x; ++i) { // scan an interleaved mcu... process scan_n components in order for (k=0; k < z->scan_n; ++k) { int n = z->order[k]; // scan out an mcu's worth of this component; that's just determined // by the basic H and V specified for the component for (y=0; y < z->img_comp[n].v; ++y) { for (x=0; x < z->img_comp[n].h; ++x) { int x2 = (i*z->img_comp[n].h + x)*8; int y2 = (j*z->img_comp[n].v + y)*8; int ha = z->img_comp[n].ha; if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); } } } // after all interleaved components, that's an interleaved MCU, // so now count down the restart interval if (--z->todo <= 0) { if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); if (!STBI__RESTART(z->marker)) return 1; stbi__jpeg_reset(z); } } } return 1; } } else { if (z->scan_n == 1) { int i,j; int n = z->order[0]; // non-interleaved data, we just need to process one block at a time, // in trivial scanline order // number of blocks to do just depends on how many actual "pixels" this // component has, independent of interleaved MCU blocking and such int w = (z->img_comp[n].x+7) >> 3; int h = (z->img_comp[n].y+7) >> 3; for (j=0; j < h; ++j) { for (i=0; i < w; ++i) { short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); if (z->spec_start == 0) { if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) return 0; } else { int ha = z->img_comp[n].ha; if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) return 0; } // every data block is an MCU, so countdown the restart interval if (--z->todo <= 0) { if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); if (!STBI__RESTART(z->marker)) return 1; stbi__jpeg_reset(z); } } } return 1; } else { // interleaved int i,j,k,x,y; for (j=0; j < z->img_mcu_y; ++j) { for (i=0; i < z->img_mcu_x; ++i) { // scan an interleaved mcu... process scan_n components in order for (k=0; k < z->scan_n; ++k) { int n = z->order[k]; // scan out an mcu's worth of this component; that's just determined // by the basic H and V specified for the component for (y=0; y < z->img_comp[n].v; ++y) { for (x=0; x < z->img_comp[n].h; ++x) { int x2 = (i*z->img_comp[n].h + x); int y2 = (j*z->img_comp[n].v + y); short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) return 0; } } } // after all interleaved components, that's an interleaved MCU, // so now count down the restart interval if (--z->todo <= 0) { if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); if (!STBI__RESTART(z->marker)) return 1; stbi__jpeg_reset(z); } } } return 1; } } } static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) { int i; for (i=0; i < 64; ++i) data[i] *= dequant[i]; } static void stbi__jpeg_finish(stbi__jpeg *z) { if (z->progressive) { // dequantize and idct the data int i,j,n; for (n=0; n < z->s->img_n; ++n) { int w = (z->img_comp[n].x+7) >> 3; int h = (z->img_comp[n].y+7) >> 3; for (j=0; j < h; ++j) { for (i=0; i < w; ++i) { short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); } } } } } static int stbi__process_marker(stbi__jpeg *z, int m) { int L; switch (m) { case STBI__MARKER_none: // no marker found return stbi__err("expected marker","Corrupt JPEG"); case 0xDD: // DRI - specify restart interval if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); z->restart_interval = stbi__get16be(z->s); return 1; case 0xDB: // DQT - define quantization table L = stbi__get16be(z->s)-2; while (L > 0) { int q = stbi__get8(z->s); int p = q >> 4, sixteen = (p != 0); int t = q & 15,i; if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); for (i=0; i < 64; ++i) z->dequant[t][stbi__jpeg_dezigzag[i]] = sixteen ? stbi__get16be(z->s) : stbi__get8(z->s); L -= (sixteen ? 129 : 65); } return L==0; case 0xC4: // DHT - define huffman table L = stbi__get16be(z->s)-2; while (L > 0) { stbi_uc *v; int sizes[16],i,n=0; int q = stbi__get8(z->s); int tc = q >> 4; int th = q & 15; if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); for (i=0; i < 16; ++i) { sizes[i] = stbi__get8(z->s); n += sizes[i]; } L -= 17; if (tc == 0) { if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; v = z->huff_dc[th].values; } else { if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; v = z->huff_ac[th].values; } for (i=0; i < n; ++i) v[i] = stbi__get8(z->s); if (tc != 0) stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); L -= n; } return L==0; } // check for comment block or APP blocks if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { stbi__skip(z->s, stbi__get16be(z->s)-2); return 1; } return stbi__err("unknown marker","Corrupt JPEG"); } // after we see SOS static int stbi__process_scan_header(stbi__jpeg *z) { int i; int Ls = stbi__get16be(z->s); z->scan_n = stbi__get8(z->s); if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); for (i=0; i < z->scan_n; ++i) { int id = stbi__get8(z->s), which; int q = stbi__get8(z->s); for (which = 0; which < z->s->img_n; ++which) if (z->img_comp[which].id == id) break; if (which == z->s->img_n) return 0; // no match z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); z->order[i] = which; } { int aa; z->spec_start = stbi__get8(z->s); z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 aa = stbi__get8(z->s); z->succ_high = (aa >> 4); z->succ_low = (aa & 15); if (z->progressive) { if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) return stbi__err("bad SOS", "Corrupt JPEG"); } else { if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); z->spec_end = 63; } } return 1; } static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) { int i; for (i=0; i < ncomp; ++i) { if (z->img_comp[i].raw_data) { STBI_FREE(z->img_comp[i].raw_data); z->img_comp[i].raw_data = NULL; z->img_comp[i].data = NULL; } if (z->img_comp[i].raw_coeff) { STBI_FREE(z->img_comp[i].raw_coeff); z->img_comp[i].raw_coeff = 0; z->img_comp[i].coeff = 0; } if (z->img_comp[i].linebuf) { STBI_FREE(z->img_comp[i].linebuf); z->img_comp[i].linebuf = NULL; } } return why; } static int stbi__process_frame_header(stbi__jpeg *z, int scan) { stbi__context *s = z->s; int Lf,p,i,q, h_max=1,v_max=1,c; Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires c = stbi__get8(s); if (c != 3 && c != 1) return stbi__err("bad component count","Corrupt JPEG"); // JFIF requires s->img_n = c; for (i=0; i < c; ++i) { z->img_comp[i].data = NULL; z->img_comp[i].linebuf = NULL; } if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); z->rgb = 0; for (i=0; i < s->img_n; ++i) { static unsigned char rgb[3] = { 'R', 'G', 'B' }; z->img_comp[i].id = stbi__get8(s); if (z->img_comp[i].id == rgb[i]) ++z->rgb; q = stbi__get8(s); z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); } if (scan != STBI__SCAN_load) return 1; if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); for (i=0; i < s->img_n; ++i) { if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; } // compute interleaved mcu info z->img_h_max = h_max; z->img_v_max = v_max; z->img_mcu_w = h_max * 8; z->img_mcu_h = v_max * 8; // these sizes can't be more than 17 bits z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; for (i=0; i < s->img_n; ++i) { // number of effective pixels (e.g. for non-interleaved MCU) z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; // to simplify generation, we'll allocate enough memory to decode // the bogus oversized data from using interleaved MCUs and their // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't // discard the extra data until colorspace conversion // // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) // so these muls can't overflow with 32-bit ints (which we require) z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; z->img_comp[i].coeff = 0; z->img_comp[i].raw_coeff = 0; z->img_comp[i].linebuf = NULL; z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); if (z->img_comp[i].raw_data == NULL) return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); // align blocks for idct using mmx/sse z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); if (z->progressive) { // w2, h2 are multiples of 8 (see above) z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); if (z->img_comp[i].raw_coeff == NULL) return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); } } return 1; } // use comparisons since in some cases we handle more than one case (e.g. SOF) #define stbi__DNL(x) ((x) == 0xdc) #define stbi__SOI(x) ((x) == 0xd8) #define stbi__EOI(x) ((x) == 0xd9) #define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) #define stbi__SOS(x) ((x) == 0xda) #define stbi__SOF_progressive(x) ((x) == 0xc2) static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) { int m; z->marker = STBI__MARKER_none; // initialize cached marker to empty m = stbi__get_marker(z); if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); if (scan == STBI__SCAN_type) return 1; m = stbi__get_marker(z); while (!stbi__SOF(m)) { if (!stbi__process_marker(z,m)) return 0; m = stbi__get_marker(z); while (m == STBI__MARKER_none) { // some files have extra padding after their blocks, so ok, we'll scan if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); m = stbi__get_marker(z); } } z->progressive = stbi__SOF_progressive(m); if (!stbi__process_frame_header(z, scan)) return 0; return 1; } // decode image to YCbCr format static int stbi__decode_jpeg_image(stbi__jpeg *j) { int m; for (m = 0; m < 4; m++) { j->img_comp[m].raw_data = NULL; j->img_comp[m].raw_coeff = NULL; } j->restart_interval = 0; if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; m = stbi__get_marker(j); while (!stbi__EOI(m)) { if (stbi__SOS(m)) { if (!stbi__process_scan_header(j)) return 0; if (!stbi__parse_entropy_coded_data(j)) return 0; if (j->marker == STBI__MARKER_none ) { // handle 0s at the end of image data from IP Kamera 9060 while (!stbi__at_eof(j->s)) { int x = stbi__get8(j->s); if (x == 255) { j->marker = stbi__get8(j->s); break; } } // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 } } else if (stbi__DNL(m)) { int Ld = stbi__get16be(j->s); stbi__uint32 NL = stbi__get16be(j->s); if (Ld != 4) stbi__err("bad DNL len", "Corrupt JPEG"); if (NL != j->s->img_y) stbi__err("bad DNL height", "Corrupt JPEG"); } else { if (!stbi__process_marker(j, m)) return 0; } m = stbi__get_marker(j); } if (j->progressive) stbi__jpeg_finish(j); return 1; } // static jfif-centered resampling (across block boundaries) typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, int w, int hs); #define stbi__div4(x) ((stbi_uc) ((x) >> 2)) static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { STBI_NOTUSED(out); STBI_NOTUSED(in_far); STBI_NOTUSED(w); STBI_NOTUSED(hs); return in_near; } static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { // need to generate two samples vertically for every one in input int i; STBI_NOTUSED(hs); for (i=0; i < w; ++i) out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); return out; } static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { // need to generate two samples horizontally for every one in input int i; stbi_uc *input = in_near; if (w == 1) { // if only one sample, can't do any interpolation out[0] = out[1] = input[0]; return out; } out[0] = input[0]; out[1] = stbi__div4(input[0]*3 + input[1] + 2); for (i=1; i < w-1; ++i) { int n = 3*input[i]+2; out[i*2+0] = stbi__div4(n+input[i-1]); out[i*2+1] = stbi__div4(n+input[i+1]); } out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); out[i*2+1] = input[w-1]; STBI_NOTUSED(in_far); STBI_NOTUSED(hs); return out; } #define stbi__div16(x) ((stbi_uc) ((x) >> 4)) static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { // need to generate 2x2 samples for every one in input int i,t0,t1; if (w == 1) { out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); return out; } t1 = 3*in_near[0] + in_far[0]; out[0] = stbi__div4(t1+2); for (i=1; i < w; ++i) { t0 = t1; t1 = 3*in_near[i]+in_far[i]; out[i*2-1] = stbi__div16(3*t0 + t1 + 8); out[i*2 ] = stbi__div16(3*t1 + t0 + 8); } out[w*2-1] = stbi__div4(t1+2); STBI_NOTUSED(hs); return out; } #if defined(STBI_SSE2) || defined(STBI_NEON) static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { // need to generate 2x2 samples for every one in input int i=0,t0,t1; if (w == 1) { out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); return out; } t1 = 3*in_near[0] + in_far[0]; // process groups of 8 pixels for as long as we can. // note we can't handle the last pixel in a row in this loop // because we need to handle the filter boundary conditions. for (; i < ((w-1) & ~7); i += 8) { #if defined(STBI_SSE2) // load and perform the vertical filtering pass // this uses 3*x + y = 4*x + (y - x) __m128i zero = _mm_setzero_si128(); __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); __m128i farw = _mm_unpacklo_epi8(farb, zero); __m128i nearw = _mm_unpacklo_epi8(nearb, zero); __m128i diff = _mm_sub_epi16(farw, nearw); __m128i nears = _mm_slli_epi16(nearw, 2); __m128i curr = _mm_add_epi16(nears, diff); // current row // horizontal filter works the same based on shifted vers of current // row. "prev" is current row shifted right by 1 pixel; we need to // insert the previous pixel value (from t1). // "next" is current row shifted left by 1 pixel, with first pixel // of next block of 8 pixels added in. __m128i prv0 = _mm_slli_si128(curr, 2); __m128i nxt0 = _mm_srli_si128(curr, 2); __m128i prev = _mm_insert_epi16(prv0, t1, 0); __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); // horizontal filter, polyphase implementation since it's convenient: // even pixels = 3*cur + prev = cur*4 + (prev - cur) // odd pixels = 3*cur + next = cur*4 + (next - cur) // note the shared term. __m128i bias = _mm_set1_epi16(8); __m128i curs = _mm_slli_epi16(curr, 2); __m128i prvd = _mm_sub_epi16(prev, curr); __m128i nxtd = _mm_sub_epi16(next, curr); __m128i curb = _mm_add_epi16(curs, bias); __m128i even = _mm_add_epi16(prvd, curb); __m128i odd = _mm_add_epi16(nxtd, curb); // interleave even and odd pixels, then undo scaling. __m128i int0 = _mm_unpacklo_epi16(even, odd); __m128i int1 = _mm_unpackhi_epi16(even, odd); __m128i de0 = _mm_srli_epi16(int0, 4); __m128i de1 = _mm_srli_epi16(int1, 4); // pack and write output __m128i outv = _mm_packus_epi16(de0, de1); _mm_storeu_si128((__m128i *) (out + i*2), outv); #elif defined(STBI_NEON) // load and perform the vertical filtering pass // this uses 3*x + y = 4*x + (y - x) uint8x8_t farb = vld1_u8(in_far + i); uint8x8_t nearb = vld1_u8(in_near + i); int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); int16x8_t curr = vaddq_s16(nears, diff); // current row // horizontal filter works the same based on shifted vers of current // row. "prev" is current row shifted right by 1 pixel; we need to // insert the previous pixel value (from t1). // "next" is current row shifted left by 1 pixel, with first pixel // of next block of 8 pixels added in. int16x8_t prv0 = vextq_s16(curr, curr, 7); int16x8_t nxt0 = vextq_s16(curr, curr, 1); int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); // horizontal filter, polyphase implementation since it's convenient: // even pixels = 3*cur + prev = cur*4 + (prev - cur) // odd pixels = 3*cur + next = cur*4 + (next - cur) // note the shared term. int16x8_t curs = vshlq_n_s16(curr, 2); int16x8_t prvd = vsubq_s16(prev, curr); int16x8_t nxtd = vsubq_s16(next, curr); int16x8_t even = vaddq_s16(curs, prvd); int16x8_t odd = vaddq_s16(curs, nxtd); // undo scaling and round, then store with even/odd phases interleaved uint8x8x2_t o; o.val[0] = vqrshrun_n_s16(even, 4); o.val[1] = vqrshrun_n_s16(odd, 4); vst2_u8(out + i*2, o); #endif // "previous" value for next iter t1 = 3*in_near[i+7] + in_far[i+7]; } t0 = t1; t1 = 3*in_near[i] + in_far[i]; out[i*2] = stbi__div16(3*t1 + t0 + 8); for (++i; i < w; ++i) { t0 = t1; t1 = 3*in_near[i]+in_far[i]; out[i*2-1] = stbi__div16(3*t0 + t1 + 8); out[i*2 ] = stbi__div16(3*t1 + t0 + 8); } out[w*2-1] = stbi__div4(t1+2); STBI_NOTUSED(hs); return out; } #endif static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { // resample with nearest-neighbor int i,j; STBI_NOTUSED(in_far); for (i=0; i < w; ++i) for (j=0; j < hs; ++j) out[i*hs+j] = in_near[i]; return out; } // this is a reduced-precision calculation of YCbCr-to-RGB introduced // to make sure the code produces the same results in both SIMD and scalar #define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) { int i; for (i=0; i < count; ++i) { int y_fixed = (y[i] << 20) + (1<<19); // rounding int r,g,b; int cr = pcr[i] - 128; int cb = pcb[i] - 128; r = y_fixed + cr* stbi__float2fixed(1.40200f); g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); b = y_fixed + cb* stbi__float2fixed(1.77200f); r >>= 20; g >>= 20; b >>= 20; if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } out[0] = (stbi_uc)r; out[1] = (stbi_uc)g; out[2] = (stbi_uc)b; out[3] = 255; out += step; } } #if defined(STBI_SSE2) || defined(STBI_NEON) static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) { int i = 0; #ifdef STBI_SSE2 // step == 3 is pretty ugly on the final interleave, and i'm not convinced // it's useful in practice (you wouldn't use it for textures, for example). // so just accelerate step == 4 case. if (step == 4) { // this is a fairly straightforward implementation and not super-optimized. __m128i signflip = _mm_set1_epi8(-0x80); __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); __m128i xw = _mm_set1_epi16(255); // alpha channel for (; i+7 < count; i += 8) { // load __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 // unpack to short (and left-shift cr, cb by 8) __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); // color transform __m128i yws = _mm_srli_epi16(yw, 4); __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); __m128i rws = _mm_add_epi16(cr0, yws); __m128i gwt = _mm_add_epi16(cb0, yws); __m128i bws = _mm_add_epi16(yws, cb1); __m128i gws = _mm_add_epi16(gwt, cr1); // descale __m128i rw = _mm_srai_epi16(rws, 4); __m128i bw = _mm_srai_epi16(bws, 4); __m128i gw = _mm_srai_epi16(gws, 4); // back to byte, set up for transpose __m128i brb = _mm_packus_epi16(rw, bw); __m128i gxb = _mm_packus_epi16(gw, xw); // transpose to interleave channels __m128i t0 = _mm_unpacklo_epi8(brb, gxb); __m128i t1 = _mm_unpackhi_epi8(brb, gxb); __m128i o0 = _mm_unpacklo_epi16(t0, t1); __m128i o1 = _mm_unpackhi_epi16(t0, t1); // store _mm_storeu_si128((__m128i *) (out + 0), o0); _mm_storeu_si128((__m128i *) (out + 16), o1); out += 32; } } #endif #ifdef STBI_NEON // in this version, step=3 support would be easy to add. but is there demand? if (step == 4) { // this is a fairly straightforward implementation and not super-optimized. uint8x8_t signflip = vdup_n_u8(0x80); int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); for (; i+7 < count; i += 8) { // load uint8x8_t y_bytes = vld1_u8(y + i); uint8x8_t cr_bytes = vld1_u8(pcr + i); uint8x8_t cb_bytes = vld1_u8(pcb + i); int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); // expand to s16 int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); int16x8_t crw = vshll_n_s8(cr_biased, 7); int16x8_t cbw = vshll_n_s8(cb_biased, 7); // color transform int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); int16x8_t rws = vaddq_s16(yws, cr0); int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); int16x8_t bws = vaddq_s16(yws, cb1); // undo scaling, round, convert to byte uint8x8x4_t o; o.val[0] = vqrshrun_n_s16(rws, 4); o.val[1] = vqrshrun_n_s16(gws, 4); o.val[2] = vqrshrun_n_s16(bws, 4); o.val[3] = vdup_n_u8(255); // store, interleaving r/g/b/a vst4_u8(out, o); out += 8*4; } } #endif for (; i < count; ++i) { int y_fixed = (y[i] << 20) + (1<<19); // rounding int r,g,b; int cr = pcr[i] - 128; int cb = pcb[i] - 128; r = y_fixed + cr* stbi__float2fixed(1.40200f); g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); b = y_fixed + cb* stbi__float2fixed(1.77200f); r >>= 20; g >>= 20; b >>= 20; if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } out[0] = (stbi_uc)r; out[1] = (stbi_uc)g; out[2] = (stbi_uc)b; out[3] = 255; out += step; } } #endif // set up the kernels static void stbi__setup_jpeg(stbi__jpeg *j) { j->idct_block_kernel = stbi__idct_block; j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; #ifdef STBI_SSE2 if (stbi__sse2_available()) { j->idct_block_kernel = stbi__idct_simd; j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; } #endif #ifdef STBI_NEON j->idct_block_kernel = stbi__idct_simd; j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; #endif } // clean up the temporary component buffers static void stbi__cleanup_jpeg(stbi__jpeg *j) { stbi__free_jpeg_components(j, j->s->img_n, 0); } typedef struct { resample_row_func resample; stbi_uc *line0,*line1; int hs,vs; // expansion factor in each axis int w_lores; // horizontal pixels pre-expansion int ystep; // how far through vertical expansion we are int ypos; // which pre-expansion row we're on } stbi__resample; static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) { int n, decode_n; z->s->img_n = 0; // make stbi__cleanup_jpeg safe // validate req_comp if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); // load a jpeg image from whichever source, but leave in YCbCr format if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } // determine actual number of components to generate n = req_comp ? req_comp : z->s->img_n; if (z->s->img_n == 3 && n < 3 && z->rgb != 3) decode_n = 1; else decode_n = z->s->img_n; // resample and color-convert { int k; unsigned int i,j; stbi_uc *output; stbi_uc *coutput[4]; stbi__resample res_comp[4]; for (k=0; k < decode_n; ++k) { stbi__resample *r = &res_comp[k]; // allocate line buffer big enough for upsampling off the edges // with upsample factor of 4 z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } r->hs = z->img_h_max / z->img_comp[k].h; r->vs = z->img_v_max / z->img_comp[k].v; r->ystep = r->vs >> 1; r->w_lores = (z->s->img_x + r->hs-1) / r->hs; r->ypos = 0; r->line0 = r->line1 = z->img_comp[k].data; if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; else r->resample = stbi__resample_row_generic; } // can't error after this so, this is safe output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } // now go ahead and resample for (j=0; j < z->s->img_y; ++j) { stbi_uc *out = output + n * z->s->img_x * j; for (k=0; k < decode_n; ++k) { stbi__resample *r = &res_comp[k]; int y_bot = r->ystep >= (r->vs >> 1); coutput[k] = r->resample(z->img_comp[k].linebuf, y_bot ? r->line1 : r->line0, y_bot ? r->line0 : r->line1, r->w_lores, r->hs); if (++r->ystep >= r->vs) { r->ystep = 0; r->line0 = r->line1; if (++r->ypos < z->img_comp[k].y) r->line1 += z->img_comp[k].w2; } } if (n >= 3) { stbi_uc *y = coutput[0]; if (z->s->img_n == 3) { if (z->rgb == 3) { for (i=0; i < z->s->img_x; ++i) { out[0] = y[i]; out[1] = coutput[1][i]; out[2] = coutput[2][i]; out[3] = 255; out += n; } } else { z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); } } else for (i=0; i < z->s->img_x; ++i) { out[0] = out[1] = out[2] = y[i]; out[3] = 255; // not used if n==3 out += n; } } else { if (z->rgb == 3) { if (n == 1) for (i=0; i < z->s->img_x; ++i) *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); else { for (i=0; i < z->s->img_x; ++i, out += 2) { out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); out[1] = 255; } } } else { stbi_uc *y = coutput[0]; if (n == 1) for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; else for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; } } } stbi__cleanup_jpeg(z); *out_x = z->s->img_x; *out_y = z->s->img_y; if (comp) *comp = z->s->img_n; // report original components, not output return output; } } static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { unsigned char* result; stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); j->s = s; stbi__setup_jpeg(j); result = load_jpeg_image(j, x,y,comp,req_comp); STBI_FREE(j); return result; } static int stbi__jpeg_test(stbi__context *s) { int r; stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); j->s = s; stbi__setup_jpeg(j); r = stbi__decode_jpeg_header(j, STBI__SCAN_type); stbi__rewind(s); STBI_FREE(j); return r; } static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) { if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { stbi__rewind( j->s ); return 0; } if (x) *x = j->s->img_x; if (y) *y = j->s->img_y; if (comp) *comp = j->s->img_n; return 1; } static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) { int result; stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); j->s = s; result = stbi__jpeg_info_raw(j, x, y, comp); STBI_FREE(j); return result; } #endif // public domain zlib decode v0.2 Sean Barrett 2006-11-18 // simple implementation // - all input must be provided in an upfront buffer // - all output is written to a single output buffer (can malloc/realloc) // performance // - fast huffman #ifndef STBI_NO_ZLIB // fast-way is faster to check than jpeg huffman, but slow way is slower #define STBI__ZFAST_BITS 9 // accelerate all cases in default tables #define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) // zlib-style huffman encoding // (jpegs packs from left, zlib from right, so can't share code) typedef struct { stbi__uint16 fast[1 << STBI__ZFAST_BITS]; stbi__uint16 firstcode[16]; int maxcode[17]; stbi__uint16 firstsymbol[16]; stbi_uc size[288]; stbi__uint16 value[288]; } stbi__zhuffman; stbi_inline static int stbi__bitreverse16(int n) { n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); return n; } stbi_inline static int stbi__bit_reverse(int v, int bits) { STBI_ASSERT(bits <= 16); // to bit reverse n bits, reverse 16 and shift // e.g. 11 bits, bit reverse and shift away 5 return stbi__bitreverse16(v) >> (16-bits); } static int stbi__zbuild_huffman(stbi__zhuffman *z, stbi_uc *sizelist, int num) { int i,k=0; int code, next_code[16], sizes[17]; // DEFLATE spec for generating codes memset(sizes, 0, sizeof(sizes)); memset(z->fast, 0, sizeof(z->fast)); for (i=0; i < num; ++i) ++sizes[sizelist[i]]; sizes[0] = 0; for (i=1; i < 16; ++i) if (sizes[i] > (1 << i)) return stbi__err("bad sizes", "Corrupt PNG"); code = 0; for (i=1; i < 16; ++i) { next_code[i] = code; z->firstcode[i] = (stbi__uint16) code; z->firstsymbol[i] = (stbi__uint16) k; code = (code + sizes[i]); if (sizes[i]) if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); z->maxcode[i] = code << (16-i); // preshift for inner loop code <<= 1; k += sizes[i]; } z->maxcode[16] = 0x10000; // sentinel for (i=0; i < num; ++i) { int s = sizelist[i]; if (s) { int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); z->size [c] = (stbi_uc ) s; z->value[c] = (stbi__uint16) i; if (s <= STBI__ZFAST_BITS) { int j = stbi__bit_reverse(next_code[s],s); while (j < (1 << STBI__ZFAST_BITS)) { z->fast[j] = fastv; j += (1 << s); } } ++next_code[s]; } } return 1; } // zlib-from-memory implementation for PNG reading // because PNG allows splitting the zlib stream arbitrarily, // and it's annoying structurally to have PNG call ZLIB call PNG, // we require PNG read all the IDATs and combine them into a single // memory buffer typedef struct { stbi_uc *zbuffer, *zbuffer_end; int num_bits; stbi__uint32 code_buffer; char *zout; char *zout_start; char *zout_end; int z_expandable; stbi__zhuffman z_length, z_distance; } stbi__zbuf; stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) { if (z->zbuffer >= z->zbuffer_end) return 0; return *z->zbuffer++; } static void stbi__fill_bits(stbi__zbuf *z) { do { STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; z->num_bits += 8; } while (z->num_bits <= 24); } stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) { unsigned int k; if (z->num_bits < n) stbi__fill_bits(z); k = z->code_buffer & ((1 << n) - 1); z->code_buffer >>= n; z->num_bits -= n; return k; } static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) { int b,s,k; // not resolved by fast table, so compute it the slow way // use jpeg approach, which requires MSbits at top k = stbi__bit_reverse(a->code_buffer, 16); for (s=STBI__ZFAST_BITS+1; ; ++s) if (k < z->maxcode[s]) break; if (s == 16) return -1; // invalid code! // code size is s, so: b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; STBI_ASSERT(z->size[b] == s); a->code_buffer >>= s; a->num_bits -= s; return z->value[b]; } stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) { int b,s; if (a->num_bits < 16) stbi__fill_bits(a); b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; if (b) { s = b >> 9; a->code_buffer >>= s; a->num_bits -= s; return b & 511; } return stbi__zhuffman_decode_slowpath(a, z); } static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes { char *q; int cur, limit, old_limit; z->zout = zout; if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); cur = (int) (z->zout - z->zout_start); limit = old_limit = (int) (z->zout_end - z->zout_start); while (cur + n > limit) limit *= 2; q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); STBI_NOTUSED(old_limit); if (q == NULL) return stbi__err("outofmem", "Out of memory"); z->zout_start = q; z->zout = q + cur; z->zout_end = q + limit; return 1; } static int stbi__zlength_base[31] = { 3,4,5,6,7,8,9,10,11,13, 15,17,19,23,27,31,35,43,51,59, 67,83,99,115,131,163,195,227,258,0,0 }; static int stbi__zlength_extra[31]= { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; static int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; static int stbi__zdist_extra[32] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; static int stbi__parse_huffman_block(stbi__zbuf *a) { char *zout = a->zout; for(;;) { int z = stbi__zhuffman_decode(a, &a->z_length); if (z < 256) { if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes if (zout >= a->zout_end) { if (!stbi__zexpand(a, zout, 1)) return 0; zout = a->zout; } *zout++ = (char) z; } else { stbi_uc *p; int len,dist; if (z == 256) { a->zout = zout; return 1; } z -= 257; len = stbi__zlength_base[z]; if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); z = stbi__zhuffman_decode(a, &a->z_distance); if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); dist = stbi__zdist_base[z]; if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); if (zout + len > a->zout_end) { if (!stbi__zexpand(a, zout, len)) return 0; zout = a->zout; } p = (stbi_uc *) (zout - dist); if (dist == 1) { // run of one byte; common in images. stbi_uc v = *p; if (len) { do *zout++ = v; while (--len); } } else { if (len) { do *zout++ = *p++; while (--len); } } } } } static int stbi__compute_huffman_codes(stbi__zbuf *a) { static stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; stbi__zhuffman z_codelength; stbi_uc lencodes[286+32+137];//padding for maximum single op stbi_uc codelength_sizes[19]; int i,n; int hlit = stbi__zreceive(a,5) + 257; int hdist = stbi__zreceive(a,5) + 1; int hclen = stbi__zreceive(a,4) + 4; int ntot = hlit + hdist; memset(codelength_sizes, 0, sizeof(codelength_sizes)); for (i=0; i < hclen; ++i) { int s = stbi__zreceive(a,3); codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; } if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; n = 0; while (n < ntot) { int c = stbi__zhuffman_decode(a, &z_codelength); if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); if (c < 16) lencodes[n++] = (stbi_uc) c; else { stbi_uc fill = 0; if (c == 16) { c = stbi__zreceive(a,2)+3; if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); fill = lencodes[n-1]; } else if (c == 17) c = stbi__zreceive(a,3)+3; else { STBI_ASSERT(c == 18); c = stbi__zreceive(a,7)+11; } if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); memset(lencodes+n, fill, c); n += c; } } if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; return 1; } static int stbi__parse_uncompressed_block(stbi__zbuf *a) { stbi_uc header[4]; int len,nlen,k; if (a->num_bits & 7) stbi__zreceive(a, a->num_bits & 7); // discard // drain the bit-packed data into header k = 0; while (a->num_bits > 0) { header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check a->code_buffer >>= 8; a->num_bits -= 8; } STBI_ASSERT(a->num_bits == 0); // now fill header the normal way while (k < 4) header[k++] = stbi__zget8(a); len = header[1] * 256 + header[0]; nlen = header[3] * 256 + header[2]; if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); if (a->zout + len > a->zout_end) if (!stbi__zexpand(a, a->zout, len)) return 0; memcpy(a->zout, a->zbuffer, len); a->zbuffer += len; a->zout += len; return 1; } static int stbi__parse_zlib_header(stbi__zbuf *a) { int cmf = stbi__zget8(a); int cm = cmf & 15; /* int cinfo = cmf >> 4; */ int flg = stbi__zget8(a); if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png // window = 1 << (8 + cinfo)... but who cares, we fully buffer output return 1; } // @TODO: should statically initialize these for optimal thread safety static stbi_uc stbi__zdefault_length[288], stbi__zdefault_distance[32]; static void stbi__init_zdefaults(void) { int i; // use <= to match clearly with spec for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; } static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) { int final, type; if (parse_header) if (!stbi__parse_zlib_header(a)) return 0; a->num_bits = 0; a->code_buffer = 0; do { final = stbi__zreceive(a,1); type = stbi__zreceive(a,2); if (type == 0) { if (!stbi__parse_uncompressed_block(a)) return 0; } else if (type == 3) { return 0; } else { if (type == 1) { // use fixed code lengths if (!stbi__zdefault_distance[31]) stbi__init_zdefaults(); if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; } else { if (!stbi__compute_huffman_codes(a)) return 0; } if (!stbi__parse_huffman_block(a)) return 0; } } while (!final); return 1; } static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) { a->zout_start = obuf; a->zout = obuf; a->zout_end = obuf + olen; a->z_expandable = exp; return stbi__parse_zlib(a, parse_header); } STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) { stbi__zbuf a; char *p = (char *) stbi__malloc(initial_size); if (p == NULL) return NULL; a.zbuffer = (stbi_uc *) buffer; a.zbuffer_end = (stbi_uc *) buffer + len; if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { if (outlen) *outlen = (int) (a.zout - a.zout_start); return a.zout_start; } else { STBI_FREE(a.zout_start); return NULL; } } STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) { return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); } STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) { stbi__zbuf a; char *p = (char *) stbi__malloc(initial_size); if (p == NULL) return NULL; a.zbuffer = (stbi_uc *) buffer; a.zbuffer_end = (stbi_uc *) buffer + len; if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { if (outlen) *outlen = (int) (a.zout - a.zout_start); return a.zout_start; } else { STBI_FREE(a.zout_start); return NULL; } } STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) { stbi__zbuf a; a.zbuffer = (stbi_uc *) ibuffer; a.zbuffer_end = (stbi_uc *) ibuffer + ilen; if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) return (int) (a.zout - a.zout_start); else return -1; } STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) { stbi__zbuf a; char *p = (char *) stbi__malloc(16384); if (p == NULL) return NULL; a.zbuffer = (stbi_uc *) buffer; a.zbuffer_end = (stbi_uc *) buffer+len; if (stbi__do_zlib(&a, p, 16384, 1, 0)) { if (outlen) *outlen = (int) (a.zout - a.zout_start); return a.zout_start; } else { STBI_FREE(a.zout_start); return NULL; } } STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) { stbi__zbuf a; a.zbuffer = (stbi_uc *) ibuffer; a.zbuffer_end = (stbi_uc *) ibuffer + ilen; if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) return (int) (a.zout - a.zout_start); else return -1; } #endif // public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 // simple implementation // - only 8-bit samples // - no CRC checking // - allocates lots of intermediate memory // - avoids problem of streaming data between subsystems // - avoids explicit window management // performance // - uses stb_zlib, a PD zlib implementation with fast huffman decoding #ifndef STBI_NO_PNG typedef struct { stbi__uint32 length; stbi__uint32 type; } stbi__pngchunk; static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) { stbi__pngchunk c; c.length = stbi__get32be(s); c.type = stbi__get32be(s); return c; } static int stbi__check_png_header(stbi__context *s) { static stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; int i; for (i=0; i < 8; ++i) if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); return 1; } typedef struct { stbi__context *s; stbi_uc *idata, *expanded, *out; int depth; } stbi__png; enum { STBI__F_none=0, STBI__F_sub=1, STBI__F_up=2, STBI__F_avg=3, STBI__F_paeth=4, // synthetic filters used for first scanline to avoid needing a dummy row of 0s STBI__F_avg_first, STBI__F_paeth_first }; static stbi_uc first_row_filter[5] = { STBI__F_none, STBI__F_sub, STBI__F_none, STBI__F_avg_first, STBI__F_paeth_first }; static int stbi__paeth(int a, int b, int c) { int p = a + b - c; int pa = abs(p-a); int pb = abs(p-b); int pc = abs(p-c); if (pa <= pb && pa <= pc) return a; if (pb <= pc) return b; return c; } static stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; // create the png data from post-deflated data static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) { int bytes = (depth == 16? 2 : 1); stbi__context *s = a->s; stbi__uint32 i,j,stride = x*out_n*bytes; stbi__uint32 img_len, img_width_bytes; int k; int img_n = s->img_n; // copy it into a local for later int output_bytes = out_n*bytes; int filter_bytes = img_n*bytes; int width = x; STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into if (!a->out) return stbi__err("outofmem", "Out of memory"); img_width_bytes = (((img_n * x * depth) + 7) >> 3); img_len = (img_width_bytes + 1) * y; if (s->img_x == x && s->img_y == y) { if (raw_len != img_len) return stbi__err("not enough pixels","Corrupt PNG"); } else { // interlaced: if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); } for (j=0; j < y; ++j) { stbi_uc *cur = a->out + stride*j; stbi_uc *prior = cur - stride; int filter = *raw++; if (filter > 4) return stbi__err("invalid filter","Corrupt PNG"); if (depth < 8) { STBI_ASSERT(img_width_bytes <= x); cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place filter_bytes = 1; width = img_width_bytes; } // if first row, use special filter that doesn't sample previous row if (j == 0) filter = first_row_filter[filter]; // handle first byte explicitly for (k=0; k < filter_bytes; ++k) { switch (filter) { case STBI__F_none : cur[k] = raw[k]; break; case STBI__F_sub : cur[k] = raw[k]; break; case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; case STBI__F_avg_first : cur[k] = raw[k]; break; case STBI__F_paeth_first: cur[k] = raw[k]; break; } } if (depth == 8) { if (img_n != out_n) cur[img_n] = 255; // first pixel raw += img_n; cur += out_n; prior += out_n; } else if (depth == 16) { if (img_n != out_n) { cur[filter_bytes] = 255; // first pixel top byte cur[filter_bytes+1] = 255; // first pixel bottom byte } raw += filter_bytes; cur += output_bytes; prior += output_bytes; } else { raw += 1; cur += 1; prior += 1; } // this is a little gross, so that we don't switch per-pixel or per-component if (depth < 8 || img_n == out_n) { int nk = (width - 1)*filter_bytes; #define STBI__CASE(f) \ case f: \ for (k=0; k < nk; ++k) switch (filter) { // "none" filter turns into a memcpy here; make that explicit. case STBI__F_none: memcpy(cur, raw, nk); break; STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; } #undef STBI__CASE raw += nk; } else { STBI_ASSERT(img_n+1 == out_n); #define STBI__CASE(f) \ case f: \ for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ for (k=0; k < filter_bytes; ++k) switch (filter) { STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; } #undef STBI__CASE // the loop above sets the high byte of the pixels' alpha, but for // 16 bit png files we also need the low byte set. we'll do that here. if (depth == 16) { cur = a->out + stride*j; // start at the beginning of the row again for (i=0; i < x; ++i,cur+=output_bytes) { cur[filter_bytes+1] = 255; } } } } // we make a separate pass to expand bits to pixels; for performance, // this could run two scanlines behind the above code, so it won't // intefere with filtering but will still be in the cache. if (depth < 8) { for (j=0; j < y; ++j) { stbi_uc *cur = a->out + stride*j; stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range // note that the final byte might overshoot and write more data than desired. // we can allocate enough data that this never writes out of memory, but it // could also overwrite the next scanline. can it overwrite non-empty data // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. // so we need to explicitly clamp the final ones if (depth == 4) { for (k=x*img_n; k >= 2; k-=2, ++in) { *cur++ = scale * ((*in >> 4) ); *cur++ = scale * ((*in ) & 0x0f); } if (k > 0) *cur++ = scale * ((*in >> 4) ); } else if (depth == 2) { for (k=x*img_n; k >= 4; k-=4, ++in) { *cur++ = scale * ((*in >> 6) ); *cur++ = scale * ((*in >> 4) & 0x03); *cur++ = scale * ((*in >> 2) & 0x03); *cur++ = scale * ((*in ) & 0x03); } if (k > 0) *cur++ = scale * ((*in >> 6) ); if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); } else if (depth == 1) { for (k=x*img_n; k >= 8; k-=8, ++in) { *cur++ = scale * ((*in >> 7) ); *cur++ = scale * ((*in >> 6) & 0x01); *cur++ = scale * ((*in >> 5) & 0x01); *cur++ = scale * ((*in >> 4) & 0x01); *cur++ = scale * ((*in >> 3) & 0x01); *cur++ = scale * ((*in >> 2) & 0x01); *cur++ = scale * ((*in >> 1) & 0x01); *cur++ = scale * ((*in ) & 0x01); } if (k > 0) *cur++ = scale * ((*in >> 7) ); if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); } if (img_n != out_n) { int q; // insert alpha = 255 cur = a->out + stride*j; if (img_n == 1) { for (q=x-1; q >= 0; --q) { cur[q*2+1] = 255; cur[q*2+0] = cur[q]; } } else { STBI_ASSERT(img_n == 3); for (q=x-1; q >= 0; --q) { cur[q*4+3] = 255; cur[q*4+2] = cur[q*3+2]; cur[q*4+1] = cur[q*3+1]; cur[q*4+0] = cur[q*3+0]; } } } } } else if (depth == 16) { // force the image data from big-endian to platform-native. // this is done in a separate pass due to the decoding relying // on the data being untouched, but could probably be done // per-line during decode if care is taken. stbi_uc *cur = a->out; stbi__uint16 *cur16 = (stbi__uint16*)cur; for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { *cur16 = (cur[0] << 8) | cur[1]; } } return 1; } static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) { int bytes = (depth == 16 ? 2 : 1); int out_bytes = out_n * bytes; stbi_uc *final; int p; if (!interlaced) return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); // de-interlacing final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); for (p=0; p < 7; ++p) { int xorig[] = { 0,4,0,2,0,1,0 }; int yorig[] = { 0,0,4,0,2,0,1 }; int xspc[] = { 8,8,4,4,2,2,1 }; int yspc[] = { 8,8,8,4,4,2,2 }; int i,j,x,y; // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; if (x && y) { stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { STBI_FREE(final); return 0; } for (j=0; j < y; ++j) { for (i=0; i < x; ++i) { int out_y = j*yspc[p]+yorig[p]; int out_x = i*xspc[p]+xorig[p]; memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, a->out + (j*x+i)*out_bytes, out_bytes); } } STBI_FREE(a->out); image_data += img_len; image_data_len -= img_len; } } a->out = final; return 1; } static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) { stbi__context *s = z->s; stbi__uint32 i, pixel_count = s->img_x * s->img_y; stbi_uc *p = z->out; // compute color-based transparency, assuming we've // already got 255 as the alpha value in the output STBI_ASSERT(out_n == 2 || out_n == 4); if (out_n == 2) { for (i=0; i < pixel_count; ++i) { p[1] = (p[0] == tc[0] ? 0 : 255); p += 2; } } else { for (i=0; i < pixel_count; ++i) { if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) p[3] = 0; p += 4; } } return 1; } static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) { stbi__context *s = z->s; stbi__uint32 i, pixel_count = s->img_x * s->img_y; stbi__uint16 *p = (stbi__uint16*) z->out; // compute color-based transparency, assuming we've // already got 65535 as the alpha value in the output STBI_ASSERT(out_n == 2 || out_n == 4); if (out_n == 2) { for (i = 0; i < pixel_count; ++i) { p[1] = (p[0] == tc[0] ? 0 : 65535); p += 2; } } else { for (i = 0; i < pixel_count; ++i) { if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) p[3] = 0; p += 4; } } return 1; } static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) { stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; stbi_uc *p, *temp_out, *orig = a->out; p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); if (p == NULL) return stbi__err("outofmem", "Out of memory"); // between here and free(out) below, exitting would leak temp_out = p; if (pal_img_n == 3) { for (i=0; i < pixel_count; ++i) { int n = orig[i]*4; p[0] = palette[n ]; p[1] = palette[n+1]; p[2] = palette[n+2]; p += 3; } } else { for (i=0; i < pixel_count; ++i) { int n = orig[i]*4; p[0] = palette[n ]; p[1] = palette[n+1]; p[2] = palette[n+2]; p[3] = palette[n+3]; p += 4; } } STBI_FREE(a->out); a->out = temp_out; STBI_NOTUSED(len); return 1; } static int stbi__unpremultiply_on_load = 0; static int stbi__de_iphone_flag = 0; STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) { stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; } STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) { stbi__de_iphone_flag = flag_true_if_should_convert; } static void stbi__de_iphone(stbi__png *z) { stbi__context *s = z->s; stbi__uint32 i, pixel_count = s->img_x * s->img_y; stbi_uc *p = z->out; if (s->img_out_n == 3) { // convert bgr to rgb for (i=0; i < pixel_count; ++i) { stbi_uc t = p[0]; p[0] = p[2]; p[2] = t; p += 3; } } else { STBI_ASSERT(s->img_out_n == 4); if (stbi__unpremultiply_on_load) { // convert bgr to rgb and unpremultiply for (i=0; i < pixel_count; ++i) { stbi_uc a = p[3]; stbi_uc t = p[0]; if (a) { p[0] = p[2] * 255 / a; p[1] = p[1] * 255 / a; p[2] = t * 255 / a; } else { p[0] = p[2]; p[2] = t; } p += 4; } } else { // convert bgr to rgb for (i=0; i < pixel_count; ++i) { stbi_uc t = p[0]; p[0] = p[2]; p[2] = t; p += 4; } } } } #define STBI__PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) { stbi_uc palette[1024], pal_img_n=0; stbi_uc has_trans=0, tc[3]; stbi__uint16 tc16[3]; stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; int first=1,k,interlace=0, color=0, is_iphone=0; stbi__context *s = z->s; z->expanded = NULL; z->idata = NULL; z->out = NULL; if (!stbi__check_png_header(s)) return 0; if (scan == STBI__SCAN_type) return 1; for (;;) { stbi__pngchunk c = stbi__get_chunk_header(s); switch (c.type) { case STBI__PNG_TYPE('C','g','B','I'): is_iphone = 1; stbi__skip(s, c.length); break; case STBI__PNG_TYPE('I','H','D','R'): { int comp,filter; if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); first = 0; if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); if (!pal_img_n) { s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); if (scan == STBI__SCAN_header) return 1; } else { // if paletted, then pal_n is our final components, and // img_n is # components to decompress/filter. s->img_n = 1; if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); // if SCAN_header, have to scan to see if we have a tRNS } break; } case STBI__PNG_TYPE('P','L','T','E'): { if (first) return stbi__err("first not IHDR", "Corrupt PNG"); if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); pal_len = c.length / 3; if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); for (i=0; i < pal_len; ++i) { palette[i*4+0] = stbi__get8(s); palette[i*4+1] = stbi__get8(s); palette[i*4+2] = stbi__get8(s); palette[i*4+3] = 255; } break; } case STBI__PNG_TYPE('t','R','N','S'): { if (first) return stbi__err("first not IHDR", "Corrupt PNG"); if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); if (pal_img_n) { if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); pal_img_n = 4; for (i=0; i < c.length; ++i) palette[i*4+3] = stbi__get8(s); } else { if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); has_trans = 1; if (z->depth == 16) { for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is } else { for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger } } break; } case STBI__PNG_TYPE('I','D','A','T'): { if (first) return stbi__err("first not IHDR", "Corrupt PNG"); if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } if ((int)(ioff + c.length) < (int)ioff) return 0; if (ioff + c.length > idata_limit) { stbi__uint32 idata_limit_old = idata_limit; stbi_uc *p; if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; while (ioff + c.length > idata_limit) idata_limit *= 2; STBI_NOTUSED(idata_limit_old); p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); z->idata = p; } if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); ioff += c.length; break; } case STBI__PNG_TYPE('I','E','N','D'): { stbi__uint32 raw_len, bpl; if (first) return stbi__err("first not IHDR", "Corrupt PNG"); if (scan != STBI__SCAN_load) return 1; if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); // initial guess for decoded data size to avoid unnecessary reallocs bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); if (z->expanded == NULL) return 0; // zlib should set error STBI_FREE(z->idata); z->idata = NULL; if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) s->img_out_n = s->img_n+1; else s->img_out_n = s->img_n; if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; if (has_trans) { if (z->depth == 16) { if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; } else { if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; } } if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) stbi__de_iphone(z); if (pal_img_n) { // pal_img_n == 3 or 4 s->img_n = pal_img_n; // record the actual colors we had s->img_out_n = pal_img_n; if (req_comp >= 3) s->img_out_n = req_comp; if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) return 0; } STBI_FREE(z->expanded); z->expanded = NULL; return 1; } default: // if critical, fail if (first) return stbi__err("first not IHDR", "Corrupt PNG"); if ((c.type & (1 << 29)) == 0) { #ifndef STBI_NO_FAILURE_STRINGS // not threadsafe static char invalid_chunk[] = "XXXX PNG chunk not known"; invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); #endif return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); } stbi__skip(s, c.length); break; } // end of PNG chunk, read and skip CRC stbi__get32be(s); } } static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) { void *result=NULL; if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { if (p->depth < 8) ri->bits_per_channel = 8; else ri->bits_per_channel = p->depth; result = p->out; p->out = NULL; if (req_comp && req_comp != p->s->img_out_n) { if (ri->bits_per_channel == 8) result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); else result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); p->s->img_out_n = req_comp; if (result == NULL) return result; } *x = p->s->img_x; *y = p->s->img_y; if (n) *n = p->s->img_n; } STBI_FREE(p->out); p->out = NULL; STBI_FREE(p->expanded); p->expanded = NULL; STBI_FREE(p->idata); p->idata = NULL; return result; } static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { stbi__png p; p.s = s; return stbi__do_png(&p, x,y,comp,req_comp, ri); } static int stbi__png_test(stbi__context *s) { int r; r = stbi__check_png_header(s); stbi__rewind(s); return r; } static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) { if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { stbi__rewind( p->s ); return 0; } if (x) *x = p->s->img_x; if (y) *y = p->s->img_y; if (comp) *comp = p->s->img_n; return 1; } static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) { stbi__png p; p.s = s; return stbi__png_info_raw(&p, x, y, comp); } #endif // Microsoft/Windows BMP image #ifndef STBI_NO_BMP static int stbi__bmp_test_raw(stbi__context *s) { int r; int sz; if (stbi__get8(s) != 'B') return 0; if (stbi__get8(s) != 'M') return 0; stbi__get32le(s); // discard filesize stbi__get16le(s); // discard reserved stbi__get16le(s); // discard reserved stbi__get32le(s); // discard data offset sz = stbi__get32le(s); r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); return r; } static int stbi__bmp_test(stbi__context *s) { int r = stbi__bmp_test_raw(s); stbi__rewind(s); return r; } // returns 0..31 for the highest set bit static int stbi__high_bit(unsigned int z) { int n=0; if (z == 0) return -1; if (z >= 0x10000) n += 16, z >>= 16; if (z >= 0x00100) n += 8, z >>= 8; if (z >= 0x00010) n += 4, z >>= 4; if (z >= 0x00004) n += 2, z >>= 2; if (z >= 0x00002) n += 1, z >>= 1; return n; } static int stbi__bitcount(unsigned int a) { a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits a = (a + (a >> 8)); // max 16 per 8 bits a = (a + (a >> 16)); // max 32 per 8 bits return a & 0xff; } static int stbi__shiftsigned(int v, int shift, int bits) { int result; int z=0; if (shift < 0) v <<= -shift; else v >>= shift; result = v; z = bits; while (z < 8) { result += v >> z; z += bits; } return result; } typedef struct { int bpp, offset, hsz; unsigned int mr,mg,mb,ma, all_a; } stbi__bmp_data; static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) { int hsz; if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); stbi__get32le(s); // discard filesize stbi__get16le(s); // discard reserved stbi__get16le(s); // discard reserved info->offset = stbi__get32le(s); info->hsz = hsz = stbi__get32le(s); info->mr = info->mg = info->mb = info->ma = 0; if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); if (hsz == 12) { s->img_x = stbi__get16le(s); s->img_y = stbi__get16le(s); } else { s->img_x = stbi__get32le(s); s->img_y = stbi__get32le(s); } if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); info->bpp = stbi__get16le(s); if (info->bpp == 1) return stbi__errpuc("monochrome", "BMP type not supported: 1-bit"); if (hsz != 12) { int compress = stbi__get32le(s); if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); stbi__get32le(s); // discard sizeof stbi__get32le(s); // discard hres stbi__get32le(s); // discard vres stbi__get32le(s); // discard colorsused stbi__get32le(s); // discard max important if (hsz == 40 || hsz == 56) { if (hsz == 56) { stbi__get32le(s); stbi__get32le(s); stbi__get32le(s); stbi__get32le(s); } if (info->bpp == 16 || info->bpp == 32) { if (compress == 0) { if (info->bpp == 32) { info->mr = 0xffu << 16; info->mg = 0xffu << 8; info->mb = 0xffu << 0; info->ma = 0xffu << 24; info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 } else { info->mr = 31u << 10; info->mg = 31u << 5; info->mb = 31u << 0; } } else if (compress == 3) { info->mr = stbi__get32le(s); info->mg = stbi__get32le(s); info->mb = stbi__get32le(s); // not documented, but generated by photoshop and handled by mspaint if (info->mr == info->mg && info->mg == info->mb) { // ?!?!? return stbi__errpuc("bad BMP", "bad BMP"); } } else return stbi__errpuc("bad BMP", "bad BMP"); } } else { int i; if (hsz != 108 && hsz != 124) return stbi__errpuc("bad BMP", "bad BMP"); info->mr = stbi__get32le(s); info->mg = stbi__get32le(s); info->mb = stbi__get32le(s); info->ma = stbi__get32le(s); stbi__get32le(s); // discard color space for (i=0; i < 12; ++i) stbi__get32le(s); // discard color space parameters if (hsz == 124) { stbi__get32le(s); // discard rendering intent stbi__get32le(s); // discard offset of profile data stbi__get32le(s); // discard size of profile data stbi__get32le(s); // discard reserved } } } return (void *) 1; } static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { stbi_uc *out; unsigned int mr=0,mg=0,mb=0,ma=0, all_a; stbi_uc pal[256][4]; int psize=0,i,j,width; int flip_vertically, pad, target; stbi__bmp_data info; STBI_NOTUSED(ri); info.all_a = 255; if (stbi__bmp_parse_header(s, &info) == NULL) return NULL; // error code already set flip_vertically = ((int) s->img_y) > 0; s->img_y = abs((int) s->img_y); mr = info.mr; mg = info.mg; mb = info.mb; ma = info.ma; all_a = info.all_a; if (info.hsz == 12) { if (info.bpp < 24) psize = (info.offset - 14 - 24) / 3; } else { if (info.bpp < 16) psize = (info.offset - 14 - info.hsz) >> 2; } s->img_n = ma ? 4 : 3; if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 target = req_comp; else target = s->img_n; // if they want monochrome, we'll post-convert // sanity-check size if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) return stbi__errpuc("too large", "Corrupt BMP"); out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); if (!out) return stbi__errpuc("outofmem", "Out of memory"); if (info.bpp < 16) { int z=0; if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } for (i=0; i < psize; ++i) { pal[i][2] = stbi__get8(s); pal[i][1] = stbi__get8(s); pal[i][0] = stbi__get8(s); if (info.hsz != 12) stbi__get8(s); pal[i][3] = 255; } stbi__skip(s, info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); if (info.bpp == 4) width = (s->img_x + 1) >> 1; else if (info.bpp == 8) width = s->img_x; else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } pad = (-width)&3; for (j=0; j < (int) s->img_y; ++j) { for (i=0; i < (int) s->img_x; i += 2) { int v=stbi__get8(s),v2=0; if (info.bpp == 4) { v2 = v & 15; v >>= 4; } out[z++] = pal[v][0]; out[z++] = pal[v][1]; out[z++] = pal[v][2]; if (target == 4) out[z++] = 255; if (i+1 == (int) s->img_x) break; v = (info.bpp == 8) ? stbi__get8(s) : v2; out[z++] = pal[v][0]; out[z++] = pal[v][1]; out[z++] = pal[v][2]; if (target == 4) out[z++] = 255; } stbi__skip(s, pad); } } else { int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; int z = 0; int easy=0; stbi__skip(s, info.offset - 14 - info.hsz); if (info.bpp == 24) width = 3 * s->img_x; else if (info.bpp == 16) width = 2*s->img_x; else /* bpp = 32 and pad = 0 */ width=0; pad = (-width) & 3; if (info.bpp == 24) { easy = 1; } else if (info.bpp == 32) { if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) easy = 2; } if (!easy) { if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } // right shift amt to put high bit in position #7 rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); } for (j=0; j < (int) s->img_y; ++j) { if (easy) { for (i=0; i < (int) s->img_x; ++i) { unsigned char a; out[z+2] = stbi__get8(s); out[z+1] = stbi__get8(s); out[z+0] = stbi__get8(s); z += 3; a = (easy == 2 ? stbi__get8(s) : 255); all_a |= a; if (target == 4) out[z++] = a; } } else { int bpp = info.bpp; for (i=0; i < (int) s->img_x; ++i) { stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); int a; out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); all_a |= a; if (target == 4) out[z++] = STBI__BYTECAST(a); } } stbi__skip(s, pad); } } // if alpha channel is all 0s, replace with all 255s if (target == 4 && all_a == 0) for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) out[i] = 255; if (flip_vertically) { stbi_uc t; for (j=0; j < (int) s->img_y>>1; ++j) { stbi_uc *p1 = out + j *s->img_x*target; stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; for (i=0; i < (int) s->img_x*target; ++i) { t = p1[i], p1[i] = p2[i], p2[i] = t; } } } if (req_comp && req_comp != target) { out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); if (out == NULL) return out; // stbi__convert_format frees input on failure } *x = s->img_x; *y = s->img_y; if (comp) *comp = s->img_n; return out; } #endif // Targa Truevision - TGA // by Jonathan Dummer #ifndef STBI_NO_TGA // returns STBI_rgb or whatever, 0 on error static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) { // only RGB or RGBA (incl. 16bit) or grey allowed if(is_rgb16) *is_rgb16 = 0; switch(bits_per_pixel) { case 8: return STBI_grey; case 16: if(is_grey) return STBI_grey_alpha; // else: fall-through case 15: if(is_rgb16) *is_rgb16 = 1; return STBI_rgb; case 24: // fall-through case 32: return bits_per_pixel/8; default: return 0; } } static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) { int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; int sz, tga_colormap_type; stbi__get8(s); // discard Offset tga_colormap_type = stbi__get8(s); // colormap type if( tga_colormap_type > 1 ) { stbi__rewind(s); return 0; // only RGB or indexed allowed } tga_image_type = stbi__get8(s); // image type if ( tga_colormap_type == 1 ) { // colormapped (paletted) image if (tga_image_type != 1 && tga_image_type != 9) { stbi__rewind(s); return 0; } stbi__skip(s,4); // skip index of first colormap entry and number of entries sz = stbi__get8(s); // check bits per palette color entry if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { stbi__rewind(s); return 0; } stbi__skip(s,4); // skip image x and y origin tga_colormap_bpp = sz; } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { stbi__rewind(s); return 0; // only RGB or grey allowed, +/- RLE } stbi__skip(s,9); // skip colormap specification and image x/y origin tga_colormap_bpp = 0; } tga_w = stbi__get16le(s); if( tga_w < 1 ) { stbi__rewind(s); return 0; // test width } tga_h = stbi__get16le(s); if( tga_h < 1 ) { stbi__rewind(s); return 0; // test height } tga_bits_per_pixel = stbi__get8(s); // bits per pixel stbi__get8(s); // ignore alpha bits if (tga_colormap_bpp != 0) { if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { // when using a colormap, tga_bits_per_pixel is the size of the indexes // I don't think anything but 8 or 16bit indexes makes sense stbi__rewind(s); return 0; } tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); } else { tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); } if(!tga_comp) { stbi__rewind(s); return 0; } if (x) *x = tga_w; if (y) *y = tga_h; if (comp) *comp = tga_comp; return 1; // seems to have passed everything } static int stbi__tga_test(stbi__context *s) { int res = 0; int sz, tga_color_type; stbi__get8(s); // discard Offset tga_color_type = stbi__get8(s); // color type if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed sz = stbi__get8(s); // image type if ( tga_color_type == 1 ) { // colormapped (paletted) image if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 stbi__skip(s,4); // skip index of first colormap entry and number of entries sz = stbi__get8(s); // check bits per palette color entry if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; stbi__skip(s,4); // skip image x and y origin } else { // "normal" image w/o colormap if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE stbi__skip(s,9); // skip colormap specification and image x/y origin } if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height sz = stbi__get8(s); // bits per pixel if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; res = 1; // if we got this far, everything's good and we can return 1 instead of 0 errorEnd: stbi__rewind(s); return res; } // read 16bit value and convert to 24bit RGB static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) { stbi__uint16 px = (stbi__uint16)stbi__get16le(s); stbi__uint16 fiveBitMask = 31; // we have 3 channels with 5bits each int r = (px >> 10) & fiveBitMask; int g = (px >> 5) & fiveBitMask; int b = px & fiveBitMask; // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later out[0] = (stbi_uc)((r * 255)/31); out[1] = (stbi_uc)((g * 255)/31); out[2] = (stbi_uc)((b * 255)/31); // some people claim that the most significant bit might be used for alpha // (possibly if an alpha-bit is set in the "image descriptor byte") // but that only made 16bit test images completely translucent.. // so let's treat all 15 and 16bit TGAs as RGB with no alpha. } static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { // read in the TGA header stuff int tga_offset = stbi__get8(s); int tga_indexed = stbi__get8(s); int tga_image_type = stbi__get8(s); int tga_is_RLE = 0; int tga_palette_start = stbi__get16le(s); int tga_palette_len = stbi__get16le(s); int tga_palette_bits = stbi__get8(s); int tga_x_origin = stbi__get16le(s); int tga_y_origin = stbi__get16le(s); int tga_width = stbi__get16le(s); int tga_height = stbi__get16le(s); int tga_bits_per_pixel = stbi__get8(s); int tga_comp, tga_rgb16=0; int tga_inverted = stbi__get8(s); // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) // image data unsigned char *tga_data; unsigned char *tga_palette = NULL; int i, j; unsigned char raw_data[4] = {0}; int RLE_count = 0; int RLE_repeating = 0; int read_next_pixel = 1; STBI_NOTUSED(ri); // do a tiny bit of precessing if ( tga_image_type >= 8 ) { tga_image_type -= 8; tga_is_RLE = 1; } tga_inverted = 1 - ((tga_inverted >> 5) & 1); // If I'm paletted, then I'll use the number of bits from the palette if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); // tga info *x = tga_width; *y = tga_height; if (comp) *comp = tga_comp; if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) return stbi__errpuc("too large", "Corrupt TGA"); tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); // skip to the data's starting position (offset usually = 0) stbi__skip(s, tga_offset ); if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { for (i=0; i < tga_height; ++i) { int row = tga_inverted ? tga_height -i - 1 : i; stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; stbi__getn(s, tga_row, tga_width * tga_comp); } } else { // do I need to load a palette? if ( tga_indexed) { // any data to skip? (offset usually = 0) stbi__skip(s, tga_palette_start ); // load the palette tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); if (!tga_palette) { STBI_FREE(tga_data); return stbi__errpuc("outofmem", "Out of memory"); } if (tga_rgb16) { stbi_uc *pal_entry = tga_palette; STBI_ASSERT(tga_comp == STBI_rgb); for (i=0; i < tga_palette_len; ++i) { stbi__tga_read_rgb16(s, pal_entry); pal_entry += tga_comp; } } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { STBI_FREE(tga_data); STBI_FREE(tga_palette); return stbi__errpuc("bad palette", "Corrupt TGA"); } } // load the data for (i=0; i < tga_width * tga_height; ++i) { // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? if ( tga_is_RLE ) { if ( RLE_count == 0 ) { // yep, get the next byte as a RLE command int RLE_cmd = stbi__get8(s); RLE_count = 1 + (RLE_cmd & 127); RLE_repeating = RLE_cmd >> 7; read_next_pixel = 1; } else if ( !RLE_repeating ) { read_next_pixel = 1; } } else { read_next_pixel = 1; } // OK, if I need to read a pixel, do it now if ( read_next_pixel ) { // load however much data we did have if ( tga_indexed ) { // read in index, then perform the lookup int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); if ( pal_idx >= tga_palette_len ) { // invalid index pal_idx = 0; } pal_idx *= tga_comp; for (j = 0; j < tga_comp; ++j) { raw_data[j] = tga_palette[pal_idx+j]; } } else if(tga_rgb16) { STBI_ASSERT(tga_comp == STBI_rgb); stbi__tga_read_rgb16(s, raw_data); } else { // read in the data raw for (j = 0; j < tga_comp; ++j) { raw_data[j] = stbi__get8(s); } } // clear the reading flag for the next pixel read_next_pixel = 0; } // end of reading a pixel // copy data for (j = 0; j < tga_comp; ++j) tga_data[i*tga_comp+j] = raw_data[j]; // in case we're in RLE mode, keep counting down --RLE_count; } // do I need to invert the image? if ( tga_inverted ) { for (j = 0; j*2 < tga_height; ++j) { int index1 = j * tga_width * tga_comp; int index2 = (tga_height - 1 - j) * tga_width * tga_comp; for (i = tga_width * tga_comp; i > 0; --i) { unsigned char temp = tga_data[index1]; tga_data[index1] = tga_data[index2]; tga_data[index2] = temp; ++index1; ++index2; } } } // clear my palette, if I had one if ( tga_palette != NULL ) { STBI_FREE( tga_palette ); } } // swap RGB - if the source data was RGB16, it already is in the right order if (tga_comp >= 3 && !tga_rgb16) { unsigned char* tga_pixel = tga_data; for (i=0; i < tga_width * tga_height; ++i) { unsigned char temp = tga_pixel[0]; tga_pixel[0] = tga_pixel[2]; tga_pixel[2] = temp; tga_pixel += tga_comp; } } // convert to target component count if (req_comp && req_comp != tga_comp) tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); // the things I do to get rid of an error message, and yet keep // Microsoft's C compilers happy... [8^( tga_palette_start = tga_palette_len = tga_palette_bits = tga_x_origin = tga_y_origin = 0; // OK, done return tga_data; } #endif // ************************************************************************************************* // Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB #ifndef STBI_NO_PSD static int stbi__psd_test(stbi__context *s) { int r = (stbi__get32be(s) == 0x38425053); stbi__rewind(s); return r; } static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) { int count, nleft, len; count = 0; while ((nleft = pixelCount - count) > 0) { len = stbi__get8(s); if (len == 128) { // No-op. } else if (len < 128) { // Copy next len+1 bytes literally. len++; if (len > nleft) return 0; // corrupt data count += len; while (len) { *p = stbi__get8(s); p += 4; len--; } } else if (len > 128) { stbi_uc val; // Next -len+1 bytes in the dest are replicated from next source byte. // (Interpret len as a negative 8-bit int.) len = 257 - len; if (len > nleft) return 0; // corrupt data val = stbi__get8(s); count += len; while (len) { *p = val; p += 4; len--; } } } return 1; } static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) { int pixelCount; int channelCount, compression; int channel, i; int bitdepth; int w,h; stbi_uc *out; STBI_NOTUSED(ri); // Check identifier if (stbi__get32be(s) != 0x38425053) // "8BPS" return stbi__errpuc("not PSD", "Corrupt PSD image"); // Check file type version. if (stbi__get16be(s) != 1) return stbi__errpuc("wrong version", "Unsupported version of PSD image"); // Skip 6 reserved bytes. stbi__skip(s, 6 ); // Read the number of channels (R, G, B, A, etc). channelCount = stbi__get16be(s); if (channelCount < 0 || channelCount > 16) return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); // Read the rows and columns of the image. h = stbi__get32be(s); w = stbi__get32be(s); // Make sure the depth is 8 bits. bitdepth = stbi__get16be(s); if (bitdepth != 8 && bitdepth != 16) return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); // Make sure the color mode is RGB. // Valid options are: // 0: Bitmap // 1: Grayscale // 2: Indexed color // 3: RGB color // 4: CMYK color // 7: Multichannel // 8: Duotone // 9: Lab color if (stbi__get16be(s) != 3) return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) stbi__skip(s,stbi__get32be(s) ); // Skip the image resources. (resolution, pen tool paths, etc) stbi__skip(s, stbi__get32be(s) ); // Skip the reserved data. stbi__skip(s, stbi__get32be(s) ); // Find out if the data is compressed. // Known values: // 0: no compression // 1: RLE compressed compression = stbi__get16be(s); if (compression > 1) return stbi__errpuc("bad compression", "PSD has an unknown compression format"); // Check size if (!stbi__mad3sizes_valid(4, w, h, 0)) return stbi__errpuc("too large", "Corrupt PSD"); // Create the destination image. if (!compression && bitdepth == 16 && bpc == 16) { out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); ri->bits_per_channel = 16; } else out = (stbi_uc *) stbi__malloc(4 * w*h); if (!out) return stbi__errpuc("outofmem", "Out of memory"); pixelCount = w*h; // Initialize the data to zero. //memset( out, 0, pixelCount * 4 ); // Finally, the image data. if (compression) { // RLE as used by .PSD and .TIFF // Loop until you get the number of unpacked bytes you are expecting: // Read the next source byte into n. // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. // Else if n is 128, noop. // Endloop // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, // which we're going to just skip. stbi__skip(s, h * channelCount * 2 ); // Read the RLE data by channel. for (channel = 0; channel < 4; channel++) { stbi_uc *p; p = out+channel; if (channel >= channelCount) { // Fill this channel with default data. for (i = 0; i < pixelCount; i++, p += 4) *p = (channel == 3 ? 255 : 0); } else { // Read the RLE data. if (!stbi__psd_decode_rle(s, p, pixelCount)) { STBI_FREE(out); return stbi__errpuc("corrupt", "bad RLE data"); } } } } else { // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. // Read the data by channel. for (channel = 0; channel < 4; channel++) { if (channel >= channelCount) { // Fill this channel with default data. if (bitdepth == 16 && bpc == 16) { stbi__uint16 *q = ((stbi__uint16 *) out) + channel; stbi__uint16 val = channel == 3 ? 65535 : 0; for (i = 0; i < pixelCount; i++, q += 4) *q = val; } else { stbi_uc *p = out+channel; stbi_uc val = channel == 3 ? 255 : 0; for (i = 0; i < pixelCount; i++, p += 4) *p = val; } } else { if (ri->bits_per_channel == 16) { // output bpc stbi__uint16 *q = ((stbi__uint16 *) out) + channel; for (i = 0; i < pixelCount; i++, q += 4) *q = (stbi__uint16) stbi__get16be(s); } else { stbi_uc *p = out+channel; if (bitdepth == 16) { // input bpc for (i = 0; i < pixelCount; i++, p += 4) *p = (stbi_uc) (stbi__get16be(s) >> 8); } else { for (i = 0; i < pixelCount; i++, p += 4) *p = stbi__get8(s); } } } } } // remove weird white matte from PSD if (channelCount >= 4) { if (ri->bits_per_channel == 16) { for (i=0; i < w*h; ++i) { stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; if (pixel[3] != 0 && pixel[3] != 65535) { float a = pixel[3] / 65535.0f; float ra = 1.0f / a; float inv_a = 65535.0f * (1 - ra); pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); } } } else { for (i=0; i < w*h; ++i) { unsigned char *pixel = out + 4*i; if (pixel[3] != 0 && pixel[3] != 255) { float a = pixel[3] / 255.0f; float ra = 1.0f / a; float inv_a = 255.0f * (1 - ra); pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); } } } } // convert to desired output format if (req_comp && req_comp != 4) { if (ri->bits_per_channel == 16) out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); else out = stbi__convert_format(out, 4, req_comp, w, h); if (out == NULL) return out; // stbi__convert_format frees input on failure } if (comp) *comp = 4; *y = h; *x = w; return out; } #endif // ************************************************************************************************* // Softimage PIC loader // by Tom Seddon // // See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format // See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ #ifndef STBI_NO_PIC static int stbi__pic_is4(stbi__context *s,const char *str) { int i; for (i=0; i<4; ++i) if (stbi__get8(s) != (stbi_uc)str[i]) return 0; return 1; } static int stbi__pic_test_core(stbi__context *s) { int i; if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) return 0; for(i=0;i<84;++i) stbi__get8(s); if (!stbi__pic_is4(s,"PICT")) return 0; return 1; } typedef struct { stbi_uc size,type,channel; } stbi__pic_packet; static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) { int mask=0x80, i; for (i=0; i<4; ++i, mask>>=1) { if (channel & mask) { if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); dest[i]=stbi__get8(s); } } return dest; } static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) { int mask=0x80,i; for (i=0;i<4; ++i, mask>>=1) if (channel&mask) dest[i]=src[i]; } static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) { int act_comp=0,num_packets=0,y,chained; stbi__pic_packet packets[10]; // this will (should...) cater for even some bizarre stuff like having data // for the same channel in multiple packets. do { stbi__pic_packet *packet; if (num_packets==sizeof(packets)/sizeof(packets[0])) return stbi__errpuc("bad format","too many packets"); packet = &packets[num_packets++]; chained = stbi__get8(s); packet->size = stbi__get8(s); packet->type = stbi__get8(s); packet->channel = stbi__get8(s); act_comp |= packet->channel; if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); } while (chained); *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? for(y=0; ytype) { default: return stbi__errpuc("bad format","packet has bad compression type"); case 0: {//uncompressed int x; for(x=0;xchannel,dest)) return 0; break; } case 1://Pure RLE { int left=width, i; while (left>0) { stbi_uc count,value[4]; count=stbi__get8(s); if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); if (count > left) count = (stbi_uc) left; if (!stbi__readval(s,packet->channel,value)) return 0; for(i=0; ichannel,dest,value); left -= count; } } break; case 2: {//Mixed RLE int left=width; while (left>0) { int count = stbi__get8(s), i; if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); if (count >= 128) { // Repeated stbi_uc value[4]; if (count==128) count = stbi__get16be(s); else count -= 127; if (count > left) return stbi__errpuc("bad file","scanline overrun"); if (!stbi__readval(s,packet->channel,value)) return 0; for(i=0;ichannel,dest,value); } else { // Raw ++count; if (count>left) return stbi__errpuc("bad file","scanline overrun"); for(i=0;ichannel,dest)) return 0; } left-=count; } break; } } } } return result; } static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) { stbi_uc *result; int i, x,y; STBI_NOTUSED(ri); for (i=0; i<92; ++i) stbi__get8(s); x = stbi__get16be(s); y = stbi__get16be(s); if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); stbi__get32be(s); //skip `ratio' stbi__get16be(s); //skip `fields' stbi__get16be(s); //skip `pad' // intermediate buffer is RGBA result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); memset(result, 0xff, x*y*4); if (!stbi__pic_load_core(s,x,y,comp, result)) { STBI_FREE(result); result=0; } *px = x; *py = y; if (req_comp == 0) req_comp = *comp; result=stbi__convert_format(result,4,req_comp,x,y); return result; } static int stbi__pic_test(stbi__context *s) { int r = stbi__pic_test_core(s); stbi__rewind(s); return r; } #endif // ************************************************************************************************* // GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb #ifndef STBI_NO_GIF typedef struct { stbi__int16 prefix; stbi_uc first; stbi_uc suffix; } stbi__gif_lzw; typedef struct { int w,h; stbi_uc *out, *old_out; // output buffer (always 4 components) int flags, bgindex, ratio, transparent, eflags, delay; stbi_uc pal[256][4]; stbi_uc lpal[256][4]; stbi__gif_lzw codes[4096]; stbi_uc *color_table; int parse, step; int lflags; int start_x, start_y; int max_x, max_y; int cur_x, cur_y; int line_size; } stbi__gif; static int stbi__gif_test_raw(stbi__context *s) { int sz; if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; sz = stbi__get8(s); if (sz != '9' && sz != '7') return 0; if (stbi__get8(s) != 'a') return 0; return 1; } static int stbi__gif_test(stbi__context *s) { int r = stbi__gif_test_raw(s); stbi__rewind(s); return r; } static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) { int i; for (i=0; i < num_entries; ++i) { pal[i][2] = stbi__get8(s); pal[i][1] = stbi__get8(s); pal[i][0] = stbi__get8(s); pal[i][3] = transp == i ? 0 : 255; } } static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) { stbi_uc version; if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return stbi__err("not GIF", "Corrupt GIF"); version = stbi__get8(s); if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); stbi__g_failure_reason = ""; g->w = stbi__get16le(s); g->h = stbi__get16le(s); g->flags = stbi__get8(s); g->bgindex = stbi__get8(s); g->ratio = stbi__get8(s); g->transparent = -1; if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments if (is_info) return 1; if (g->flags & 0x80) stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); return 1; } static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) { stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); if (!stbi__gif_header(s, g, comp, 1)) { STBI_FREE(g); stbi__rewind( s ); return 0; } if (x) *x = g->w; if (y) *y = g->h; STBI_FREE(g); return 1; } static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) { stbi_uc *p, *c; // recurse to decode the prefixes, since the linked-list is backwards, // and working backwards through an interleaved image would be nasty if (g->codes[code].prefix >= 0) stbi__out_gif_code(g, g->codes[code].prefix); if (g->cur_y >= g->max_y) return; p = &g->out[g->cur_x + g->cur_y]; c = &g->color_table[g->codes[code].suffix * 4]; if (c[3] >= 128) { p[0] = c[2]; p[1] = c[1]; p[2] = c[0]; p[3] = c[3]; } g->cur_x += 4; if (g->cur_x >= g->max_x) { g->cur_x = g->start_x; g->cur_y += g->step; while (g->cur_y >= g->max_y && g->parse > 0) { g->step = (1 << g->parse) * g->line_size; g->cur_y = g->start_y + (g->step >> 1); --g->parse; } } } static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) { stbi_uc lzw_cs; stbi__int32 len, init_code; stbi__uint32 first; stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; stbi__gif_lzw *p; lzw_cs = stbi__get8(s); if (lzw_cs > 12) return NULL; clear = 1 << lzw_cs; first = 1; codesize = lzw_cs + 1; codemask = (1 << codesize) - 1; bits = 0; valid_bits = 0; for (init_code = 0; init_code < clear; init_code++) { g->codes[init_code].prefix = -1; g->codes[init_code].first = (stbi_uc) init_code; g->codes[init_code].suffix = (stbi_uc) init_code; } // support no starting clear code avail = clear+2; oldcode = -1; len = 0; for(;;) { if (valid_bits < codesize) { if (len == 0) { len = stbi__get8(s); // start new block if (len == 0) return g->out; } --len; bits |= (stbi__int32) stbi__get8(s) << valid_bits; valid_bits += 8; } else { stbi__int32 code = bits & codemask; bits >>= codesize; valid_bits -= codesize; // @OPTIMIZE: is there some way we can accelerate the non-clear path? if (code == clear) { // clear code codesize = lzw_cs + 1; codemask = (1 << codesize) - 1; avail = clear + 2; oldcode = -1; first = 0; } else if (code == clear + 1) { // end of stream code stbi__skip(s, len); while ((len = stbi__get8(s)) > 0) stbi__skip(s,len); return g->out; } else if (code <= avail) { if (first) return stbi__errpuc("no clear code", "Corrupt GIF"); if (oldcode >= 0) { p = &g->codes[avail++]; if (avail > 4096) return stbi__errpuc("too many codes", "Corrupt GIF"); p->prefix = (stbi__int16) oldcode; p->first = g->codes[oldcode].first; p->suffix = (code == avail) ? p->first : g->codes[code].first; } else if (code == avail) return stbi__errpuc("illegal code in raster", "Corrupt GIF"); stbi__out_gif_code(g, (stbi__uint16) code); if ((avail & codemask) == 0 && avail <= 0x0FFF) { codesize++; codemask = (1 << codesize) - 1; } oldcode = code; } else { return stbi__errpuc("illegal code in raster", "Corrupt GIF"); } } } } static void stbi__fill_gif_background(stbi__gif *g, int x0, int y0, int x1, int y1) { int x, y; stbi_uc *c = g->pal[g->bgindex]; for (y = y0; y < y1; y += 4 * g->w) { for (x = x0; x < x1; x += 4) { stbi_uc *p = &g->out[y + x]; p[0] = c[2]; p[1] = c[1]; p[2] = c[0]; p[3] = 0; } } } // this function is designed to support animated gifs, although stb_image doesn't support it static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp) { int i; stbi_uc *prev_out = 0; if (g->out == 0 && !stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header if (!stbi__mad3sizes_valid(g->w, g->h, 4, 0)) return stbi__errpuc("too large", "GIF too large"); prev_out = g->out; g->out = (stbi_uc *) stbi__malloc_mad3(4, g->w, g->h, 0); if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); switch ((g->eflags & 0x1C) >> 2) { case 0: // unspecified (also always used on 1st frame) stbi__fill_gif_background(g, 0, 0, 4 * g->w, 4 * g->w * g->h); break; case 1: // do not dispose if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h); g->old_out = prev_out; break; case 2: // dispose to background if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h); stbi__fill_gif_background(g, g->start_x, g->start_y, g->max_x, g->max_y); break; case 3: // dispose to previous if (g->old_out) { for (i = g->start_y; i < g->max_y; i += 4 * g->w) memcpy(&g->out[i + g->start_x], &g->old_out[i + g->start_x], g->max_x - g->start_x); } break; } for (;;) { switch (stbi__get8(s)) { case 0x2C: /* Image Descriptor */ { int prev_trans = -1; stbi__int32 x, y, w, h; stbi_uc *o; x = stbi__get16le(s); y = stbi__get16le(s); w = stbi__get16le(s); h = stbi__get16le(s); if (((x + w) > (g->w)) || ((y + h) > (g->h))) return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); g->line_size = g->w * 4; g->start_x = x * 4; g->start_y = y * g->line_size; g->max_x = g->start_x + w * 4; g->max_y = g->start_y + h * g->line_size; g->cur_x = g->start_x; g->cur_y = g->start_y; g->lflags = stbi__get8(s); if (g->lflags & 0x40) { g->step = 8 * g->line_size; // first interlaced spacing g->parse = 3; } else { g->step = g->line_size; g->parse = 0; } if (g->lflags & 0x80) { stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); g->color_table = (stbi_uc *) g->lpal; } else if (g->flags & 0x80) { if (g->transparent >= 0 && (g->eflags & 0x01)) { prev_trans = g->pal[g->transparent][3]; g->pal[g->transparent][3] = 0; } g->color_table = (stbi_uc *) g->pal; } else return stbi__errpuc("missing color table", "Corrupt GIF"); o = stbi__process_gif_raster(s, g); if (o == NULL) return NULL; if (prev_trans != -1) g->pal[g->transparent][3] = (stbi_uc) prev_trans; return o; } case 0x21: // Comment Extension. { int len; if (stbi__get8(s) == 0xF9) { // Graphic Control Extension. len = stbi__get8(s); if (len == 4) { g->eflags = stbi__get8(s); g->delay = stbi__get16le(s); g->transparent = stbi__get8(s); } else { stbi__skip(s, len); break; } } while ((len = stbi__get8(s)) != 0) stbi__skip(s, len); break; } case 0x3B: // gif stream termination code return (stbi_uc *) s; // using '1' causes warning on some compilers default: return stbi__errpuc("unknown code", "Corrupt GIF"); } } STBI_NOTUSED(req_comp); } static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { stbi_uc *u = 0; stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); memset(g, 0, sizeof(*g)); STBI_NOTUSED(ri); u = stbi__gif_load_next(s, g, comp, req_comp); if (u == (stbi_uc *) s) u = 0; // end of animated gif marker if (u) { *x = g->w; *y = g->h; if (req_comp && req_comp != 4) u = stbi__convert_format(u, 4, req_comp, g->w, g->h); } else if (g->out) STBI_FREE(g->out); STBI_FREE(g); return u; } static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) { return stbi__gif_info_raw(s,x,y,comp); } #endif // ************************************************************************************************* // Radiance RGBE HDR loader // originally by Nicolas Schulz #ifndef STBI_NO_HDR static int stbi__hdr_test_core(stbi__context *s, const char *signature) { int i; for (i=0; signature[i]; ++i) if (stbi__get8(s) != signature[i]) return 0; stbi__rewind(s); return 1; } static int stbi__hdr_test(stbi__context* s) { int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); stbi__rewind(s); if(!r) { r = stbi__hdr_test_core(s, "#?RGBE\n"); stbi__rewind(s); } return r; } #define STBI__HDR_BUFLEN 1024 static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) { int len=0; char c = '\0'; c = (char) stbi__get8(z); while (!stbi__at_eof(z) && c != '\n') { buffer[len++] = c; if (len == STBI__HDR_BUFLEN-1) { // flush to end of line while (!stbi__at_eof(z) && stbi__get8(z) != '\n') ; break; } c = (char) stbi__get8(z); } buffer[len] = 0; return buffer; } static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) { if ( input[3] != 0 ) { float f1; // Exponent f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); if (req_comp <= 2) output[0] = (input[0] + input[1] + input[2]) * f1 / 3; else { output[0] = input[0] * f1; output[1] = input[1] * f1; output[2] = input[2] * f1; } if (req_comp == 2) output[1] = 1; if (req_comp == 4) output[3] = 1; } else { switch (req_comp) { case 4: output[3] = 1; /* fallthrough */ case 3: output[0] = output[1] = output[2] = 0; break; case 2: output[1] = 1; /* fallthrough */ case 1: output[0] = 0; break; } } } static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { char buffer[STBI__HDR_BUFLEN]; char *token; int valid = 0; int width, height; stbi_uc *scanline; float *hdr_data; int len; unsigned char count, value; int i, j, k, c1,c2, z; const char *headerToken; STBI_NOTUSED(ri); // Check identifier headerToken = stbi__hdr_gettoken(s,buffer); if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) return stbi__errpf("not HDR", "Corrupt HDR image"); // Parse header for(;;) { token = stbi__hdr_gettoken(s,buffer); if (token[0] == 0) break; if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; } if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); // Parse width and height // can't use sscanf() if we're not using stdio! token = stbi__hdr_gettoken(s,buffer); if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); token += 3; height = (int) strtol(token, &token, 10); while (*token == ' ') ++token; if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); token += 3; width = (int) strtol(token, NULL, 10); *x = width; *y = height; if (comp) *comp = 3; if (req_comp == 0) req_comp = 3; if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) return stbi__errpf("too large", "HDR image is too large"); // Read data hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); if (!hdr_data) return stbi__errpf("outofmem", "Out of memory"); // Load image data // image data is stored as some number of sca if ( width < 8 || width >= 32768) { // Read flat data for (j=0; j < height; ++j) { for (i=0; i < width; ++i) { stbi_uc rgbe[4]; main_decode_loop: stbi__getn(s, rgbe, 4); stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); } } } else { // Read RLE-encoded data scanline = NULL; for (j = 0; j < height; ++j) { c1 = stbi__get8(s); c2 = stbi__get8(s); len = stbi__get8(s); if (c1 != 2 || c2 != 2 || (len & 0x80)) { // not run-length encoded, so we have to actually use THIS data as a decoded // pixel (note this can't be a valid pixel--one of RGB must be >= 128) stbi_uc rgbe[4]; rgbe[0] = (stbi_uc) c1; rgbe[1] = (stbi_uc) c2; rgbe[2] = (stbi_uc) len; rgbe[3] = (stbi_uc) stbi__get8(s); stbi__hdr_convert(hdr_data, rgbe, req_comp); i = 1; j = 0; STBI_FREE(scanline); goto main_decode_loop; // yes, this makes no sense } len <<= 8; len |= stbi__get8(s); if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } if (scanline == NULL) { scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); if (!scanline) { STBI_FREE(hdr_data); return stbi__errpf("outofmem", "Out of memory"); } } for (k = 0; k < 4; ++k) { int nleft; i = 0; while ((nleft = width - i) > 0) { count = stbi__get8(s); if (count > 128) { // Run value = stbi__get8(s); count -= 128; if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } for (z = 0; z < count; ++z) scanline[i++ * 4 + k] = value; } else { // Dump if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } for (z = 0; z < count; ++z) scanline[i++ * 4 + k] = stbi__get8(s); } } } for (i=0; i < width; ++i) stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); } if (scanline) STBI_FREE(scanline); } return hdr_data; } static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) { char buffer[STBI__HDR_BUFLEN]; char *token; int valid = 0; if (stbi__hdr_test(s) == 0) { stbi__rewind( s ); return 0; } for(;;) { token = stbi__hdr_gettoken(s,buffer); if (token[0] == 0) break; if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; } if (!valid) { stbi__rewind( s ); return 0; } token = stbi__hdr_gettoken(s,buffer); if (strncmp(token, "-Y ", 3)) { stbi__rewind( s ); return 0; } token += 3; *y = (int) strtol(token, &token, 10); while (*token == ' ') ++token; if (strncmp(token, "+X ", 3)) { stbi__rewind( s ); return 0; } token += 3; *x = (int) strtol(token, NULL, 10); *comp = 3; return 1; } #endif // STBI_NO_HDR #ifndef STBI_NO_BMP static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) { void *p; stbi__bmp_data info; info.all_a = 255; p = stbi__bmp_parse_header(s, &info); stbi__rewind( s ); if (p == NULL) return 0; *x = s->img_x; *y = s->img_y; *comp = info.ma ? 4 : 3; return 1; } #endif #ifndef STBI_NO_PSD static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) { int channelCount; if (stbi__get32be(s) != 0x38425053) { stbi__rewind( s ); return 0; } if (stbi__get16be(s) != 1) { stbi__rewind( s ); return 0; } stbi__skip(s, 6); channelCount = stbi__get16be(s); if (channelCount < 0 || channelCount > 16) { stbi__rewind( s ); return 0; } *y = stbi__get32be(s); *x = stbi__get32be(s); if (stbi__get16be(s) != 8) { stbi__rewind( s ); return 0; } if (stbi__get16be(s) != 3) { stbi__rewind( s ); return 0; } *comp = 4; return 1; } #endif #ifndef STBI_NO_PIC static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) { int act_comp=0,num_packets=0,chained; stbi__pic_packet packets[10]; if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { stbi__rewind(s); return 0; } stbi__skip(s, 88); *x = stbi__get16be(s); *y = stbi__get16be(s); if (stbi__at_eof(s)) { stbi__rewind( s); return 0; } if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { stbi__rewind( s ); return 0; } stbi__skip(s, 8); do { stbi__pic_packet *packet; if (num_packets==sizeof(packets)/sizeof(packets[0])) return 0; packet = &packets[num_packets++]; chained = stbi__get8(s); packet->size = stbi__get8(s); packet->type = stbi__get8(s); packet->channel = stbi__get8(s); act_comp |= packet->channel; if (stbi__at_eof(s)) { stbi__rewind( s ); return 0; } if (packet->size != 8) { stbi__rewind( s ); return 0; } } while (chained); *comp = (act_comp & 0x10 ? 4 : 3); return 1; } #endif // ************************************************************************************************* // Portable Gray Map and Portable Pixel Map loader // by Ken Miller // // PGM: http://netpbm.sourceforge.net/doc/pgm.html // PPM: http://netpbm.sourceforge.net/doc/ppm.html // // Known limitations: // Does not support comments in the header section // Does not support ASCII image data (formats P2 and P3) // Does not support 16-bit-per-channel #ifndef STBI_NO_PNM static int stbi__pnm_test(stbi__context *s) { char p, t; p = (char) stbi__get8(s); t = (char) stbi__get8(s); if (p != 'P' || (t != '5' && t != '6')) { stbi__rewind( s ); return 0; } return 1; } static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { stbi_uc *out; STBI_NOTUSED(ri); if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) return 0; *x = s->img_x; *y = s->img_y; *comp = s->img_n; if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0)) return stbi__errpuc("too large", "PNM too large"); out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0); if (!out) return stbi__errpuc("outofmem", "Out of memory"); stbi__getn(s, out, s->img_n * s->img_x * s->img_y); if (req_comp && req_comp != s->img_n) { out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); if (out == NULL) return out; // stbi__convert_format frees input on failure } return out; } static int stbi__pnm_isspace(char c) { return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; } static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) { for (;;) { while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) *c = (char) stbi__get8(s); if (stbi__at_eof(s) || *c != '#') break; while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) *c = (char) stbi__get8(s); } } static int stbi__pnm_isdigit(char c) { return c >= '0' && c <= '9'; } static int stbi__pnm_getinteger(stbi__context *s, char *c) { int value = 0; while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { value = value*10 + (*c - '0'); *c = (char) stbi__get8(s); } return value; } static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) { int maxv; char c, p, t; stbi__rewind( s ); // Get identifier p = (char) stbi__get8(s); t = (char) stbi__get8(s); if (p != 'P' || (t != '5' && t != '6')) { stbi__rewind( s ); return 0; } *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm c = (char) stbi__get8(s); stbi__pnm_skip_whitespace(s, &c); *x = stbi__pnm_getinteger(s, &c); // read width stbi__pnm_skip_whitespace(s, &c); *y = stbi__pnm_getinteger(s, &c); // read height stbi__pnm_skip_whitespace(s, &c); maxv = stbi__pnm_getinteger(s, &c); // read max value if (maxv > 255) return stbi__err("max value > 255", "PPM image not 8-bit"); else return 1; } #endif static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) { #ifndef STBI_NO_JPEG if (stbi__jpeg_info(s, x, y, comp)) return 1; #endif #ifndef STBI_NO_PNG if (stbi__png_info(s, x, y, comp)) return 1; #endif #ifndef STBI_NO_GIF if (stbi__gif_info(s, x, y, comp)) return 1; #endif #ifndef STBI_NO_BMP if (stbi__bmp_info(s, x, y, comp)) return 1; #endif #ifndef STBI_NO_PSD if (stbi__psd_info(s, x, y, comp)) return 1; #endif #ifndef STBI_NO_PIC if (stbi__pic_info(s, x, y, comp)) return 1; #endif #ifndef STBI_NO_PNM if (stbi__pnm_info(s, x, y, comp)) return 1; #endif #ifndef STBI_NO_HDR if (stbi__hdr_info(s, x, y, comp)) return 1; #endif // test tga last because it's a crappy test! #ifndef STBI_NO_TGA if (stbi__tga_info(s, x, y, comp)) return 1; #endif return stbi__err("unknown image type", "Image not of any known type, or corrupt"); } #ifndef STBI_NO_STDIO STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) { FILE *f = stbi__fopen(filename, "rb"); int result; if (!f) return stbi__err("can't fopen", "Unable to open file"); result = stbi_info_from_file(f, x, y, comp); fclose(f); return result; } STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) { int r; stbi__context s; long pos = ftell(f); stbi__start_file(&s, f); r = stbi__info_main(&s,x,y,comp); fseek(f,pos,SEEK_SET); return r; } #endif // !STBI_NO_STDIO STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) { stbi__context s; stbi__start_mem(&s,buffer,len); return stbi__info_main(&s,x,y,comp); } STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) { stbi__context s; stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); return stbi__info_main(&s,x,y,comp); } #endif // STB_IMAGE_IMPLEMENTATION /* revision history: 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes 2.11 (2016-04-02) allocate large structures on the stack remove white matting for transparent PSD fix reported channel count for PNG & BMP re-enable SSE2 in non-gcc 64-bit support RGB-formatted JPEG read 16-bit PNGs (only as 8-bit) 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED 2.09 (2016-01-16) allow comments in PNM files 16-bit-per-pixel TGA (not bit-per-component) info() for TGA could break due to .hdr handling info() for BMP to shares code instead of sloppy parse can use STBI_REALLOC_SIZED if allocator doesn't support realloc code cleanup 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA 2.07 (2015-09-13) fix compiler warnings partial animated GIF support limited 16-bpc PSD support #ifdef unused functions bug with < 92 byte PIC,PNM,HDR,TGA 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit 2.03 (2015-04-12) extra corruption checking (mmozeiko) stbi_set_flip_vertically_on_load (nguillemot) fix NEON support; fix mingw support 2.02 (2015-01-19) fix incorrect assert, fix warning 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) progressive JPEG (stb) PGM/PPM support (Ken Miller) STBI_MALLOC,STBI_REALLOC,STBI_FREE GIF bugfix -- seemingly never worked STBI_NO_*, STBI_ONLY_* 1.48 (2014-12-14) fix incorrectly-named assert() 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) optimize PNG (ryg) fix bug in interlaced PNG with user-specified channel count (stb) 1.46 (2014-08-26) fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG 1.45 (2014-08-16) fix MSVC-ARM internal compiler error by wrapping malloc 1.44 (2014-08-07) various warning fixes from Ronny Chevalier 1.43 (2014-07-15) fix MSVC-only compiler problem in code changed in 1.42 1.42 (2014-07-09) don't define _CRT_SECURE_NO_WARNINGS (affects user code) fixes to stbi__cleanup_jpeg path added STBI_ASSERT to avoid requiring assert.h 1.41 (2014-06-25) fix search&replace from 1.36 that messed up comments/error messages 1.40 (2014-06-22) fix gcc struct-initialization warning 1.39 (2014-06-15) fix to TGA optimization when req_comp != number of components in TGA; fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) add support for BMP version 5 (more ignored fields) 1.38 (2014-06-06) suppress MSVC warnings on integer casts truncating values fix accidental rename of 'skip' field of I/O 1.37 (2014-06-04) remove duplicate typedef 1.36 (2014-06-03) convert to header file single-file library if de-iphone isn't set, load iphone images color-swapped instead of returning NULL 1.35 (2014-05-27) various warnings fix broken STBI_SIMD path fix bug where stbi_load_from_file no longer left file pointer in correct place fix broken non-easy path for 32-bit BMP (possibly never used) TGA optimization by Arseny Kapoulkine 1.34 (unknown) use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case 1.33 (2011-07-14) make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements 1.32 (2011-07-13) support for "info" function for all supported filetypes (SpartanJ) 1.31 (2011-06-20) a few more leak fixes, bug in PNG handling (SpartanJ) 1.30 (2011-06-11) added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) removed deprecated format-specific test/load functions removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) fix inefficiency in decoding 32-bit BMP (David Woo) 1.29 (2010-08-16) various warning fixes from Aurelien Pocheville 1.28 (2010-08-01) fix bug in GIF palette transparency (SpartanJ) 1.27 (2010-08-01) cast-to-stbi_uc to fix warnings 1.26 (2010-07-24) fix bug in file buffering for PNG reported by SpartanJ 1.25 (2010-07-17) refix trans_data warning (Won Chun) 1.24 (2010-07-12) perf improvements reading from files on platforms with lock-heavy fgetc() minor perf improvements for jpeg deprecated type-specific functions so we'll get feedback if they're needed attempt to fix trans_data warning (Won Chun) 1.23 fixed bug in iPhone support 1.22 (2010-07-10) removed image *writing* support stbi_info support from Jetro Lauha GIF support from Jean-Marc Lienher iPhone PNG-extensions from James Brown warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) 1.21 fix use of 'stbi_uc' in header (reported by jon blow) 1.20 added support for Softimage PIC, by Tom Seddon 1.19 bug in interlaced PNG corruption check (found by ryg) 1.18 (2008-08-02) fix a threading bug (local mutable static) 1.17 support interlaced PNG 1.16 major bugfix - stbi__convert_format converted one too many pixels 1.15 initialize some fields for thread safety 1.14 fix threadsafe conversion bug header-file-only version (#define STBI_HEADER_FILE_ONLY before including) 1.13 threadsafe 1.12 const qualifiers in the API 1.11 Support installable IDCT, colorspace conversion routines 1.10 Fixes for 64-bit (don't use "unsigned long") optimized upsampling by Fabian "ryg" Giesen 1.09 Fix format-conversion for PSD code (bad global variables!) 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz 1.07 attempt to fix C++ warning/errors again 1.06 attempt to fix C++ warning/errors again 1.05 fix TGA loading to return correct *comp and use good luminance calc 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR 1.02 support for (subset of) HDR files, float interface for preferred access to them 1.01 fix bug: possible bug in handling right-side up bmps... not sure fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all 1.00 interface to zlib that skips zlib header 0.99 correct handling of alpha in palette 0.98 TGA loader by lonesock; dynamically add loaders (untested) 0.97 jpeg errors on too large a file; also catch another malloc failure 0.96 fix detection of invalid v value - particleman@mollyrocket forum 0.95 during header scan, seek to markers in case of padding 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same 0.93 handle jpegtran output; verbose errors 0.92 read 4,8,16,24,32-bit BMP files of several formats 0.91 output 24-bit Windows 3.0 BMP files 0.90 fix a few more warnings; bump version number to approach 1.0 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd 0.60 fix compiling as c++ 0.59 fix warnings: merge Dave Moore's -Wall fixes 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available 0.56 fix bug: zlib uncompressed mode len vs. nlen 0.55 fix bug: restart_interval not initialized to 0 0.54 allow NULL for 'int *comp' 0.53 fix bug in png 3->4; speedup png decoding 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments 0.51 obey req_comp requests, 1-component jpegs return as 1-component, on 'test' only check type, not whether we support this variant 0.50 (2006-11-19) first released version */ /* ------------------------------------------------------------------------------ This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License Copyright (c) 2017 Sean Barrett Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ ALTERNATIVE B - Public Domain (www.unlicense.org) This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ */ ================================================ FILE: src/engine/renderer/jpeg/tiny_jpeg.h ================================================ /** * tiny_jpeg.h * * Tiny JPEG Encoder * - Sergio Gonzalez * * This is a readable and simple single-header JPEG encoder. * * Features * - Implements Baseline DCT JPEG compression. * - No dynamic allocations. * * This library is coded in the spirit of the stb libraries and mostly follows * the stb guidelines. * * It is written in C99. And depends on the C standard library. * Works with C++11 * * * ==== Thanks ==== * * AssociationSirius (Bug reports) * Bernard van Gastel (Thread-safe defaults, BSD compilation) * * * ==== License ==== * * This software is in the public domain. Where that dedication is not * recognized, you are granted a perpetual, irrevocable license to copy and * modify this file as you see fit. * */ // ============================================================ // Usage // ============================================================ // Include "tiny_jpeg.h" to and use the public interface defined below. // // You *must* do: // // #define TJE_IMPLEMENTATION // #include "tiny_jpeg.h" // // in exactly one of your C files to actually compile the implementation. // Here is an example program that loads a bmp with stb_image and writes it // with Tiny JPEG /* #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" #define TJE_IMPLEMENTATION #include "tiny_jpeg.h" int main() { int width, height, num_components; unsigned char* data = stbi_load("in.bmp", &width, &height, &num_components, 0); if ( !data ) { puts("Could not find file"); return EXIT_FAILURE; } if ( !tje_encode_to_file("out.jpg", width, height, num_components, data) ) { fprintf(stderr, "Could not write JPEG\n"); return EXIT_FAILURE; } return EXIT_SUCCESS; } */ #ifdef __cplusplus extern "C" { #endif #if defined(__GNUC__) || defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmissing-field-initializers" // We use {0}, which will zero-out the struct. #pragma GCC diagnostic ignored "-Wmissing-braces" #pragma GCC diagnostic ignored "-Wpadded" #endif // ============================================================ // Public interface: // ============================================================ #ifndef TJE_HEADER_GUARD #define TJE_HEADER_GUARD // - tje_encode_to_file - // // Usage: // Takes bitmap data and writes a JPEG-encoded image to disk. // // PARAMETERS // dest_path: filename to which we will write. e.g. "out.jpg" // width, height: image size in pixels // num_components: 3 is RGB. 4 is RGBA. Those are the only supported values // src_data: pointer to the pixel data. // // RETURN: // 0 on error. 1 on success. int tje_encode_to_file(const char* dest_path, const int width, const int height, const int num_components, const unsigned char* src_data); // - tje_encode_to_file_at_quality - // // Usage: // Takes bitmap data and writes a JPEG-encoded image to disk. // // PARAMETERS // dest_path: filename to which we will write. e.g. "out.jpg" // quality: 3: Highest. Compression varies wildly (between 1/3 and 1/20). // 2: Very good quality. About 1/2 the size of 3. // 1: Noticeable. About 1/6 the size of 3, or 1/3 the size of 2. // width, height: image size in pixels // num_components: 3 is RGB. 4 is RGBA. Those are the only supported values // src_data: pointer to the pixel data. // // RETURN: // 0 on error. 1 on success. int tje_encode_to_file_at_quality(const char* dest_path, const int quality, const int width, const int height, const int num_components, const unsigned char* src_data); // - tje_encode_with_func - // // Usage // Same as tje_encode_to_file_at_quality, but it takes a callback that knows // how to handle (or ignore) `context`. The callback receives an array `data` // of `size` bytes, which can be written directly to a file. There is no need // to free the data. typedef void tje_write_func(void* context, void* data, int size); int tje_encode_with_func(tje_write_func* func, void* context, const int quality, const int width, const int height, const int num_components, const unsigned char* src_data); #endif // TJE_HEADER_GUARD // Implementation: In exactly one of the source files of your application, // define TJE_IMPLEMENTATION and include tiny_jpeg.h // ============================================================ // Internal // ============================================================ #ifdef TJE_IMPLEMENTATION #define tjei_min(a, b) ((a) < b) ? (a) : (b); #define tjei_max(a, b) ((a) < b) ? (b) : (a); #if defined(_MSC_VER) #define TJEI_FORCE_INLINE __forceinline // #define TJEI_FORCE_INLINE __declspec(noinline) // For profiling #else #define TJEI_FORCE_INLINE static // TODO: equivalent for gcc & clang #endif // Only use zero for debugging and/or inspection. #define TJE_USE_FAST_DCT 1 // C std lib #include #include #include // floorf, ceilf #include // FILE, puts #include // memcpy #define TJEI_BUFFER_SIZE 1024 #ifdef _WIN32 #include #ifndef snprintf #define snprintf sprintf_s #endif // Not quite the same but it works for us. If I am not mistaken, it differs // only in the return value. #endif #ifndef NDEBUG #ifdef _WIN32 #define tje_log(msg) OutputDebugStringA(msg) #elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) #define tje_log(msg) puts(msg) #else #warning "need a tje_log definition for your platform for debugging purposes (not needed if compiling with NDEBUG)" #endif #else // NDEBUG #define tje_log(msg) #endif // NDEBUG typedef struct { void* context; tje_write_func* func; } TJEWriteContext; typedef struct { // Huffman data. uint8_t ehuffsize[4][257]; uint16_t ehuffcode[4][256]; uint8_t const * ht_bits[4]; uint8_t const * ht_vals[4]; // Cuantization tables. uint8_t qt_luma[64]; uint8_t qt_chroma[64]; // fwrite by default. User-defined when using tje_encode_with_func. TJEWriteContext write_context; // Buffered output. Big performance win when using the usual stdlib implementations. size_t output_buffer_count; uint8_t output_buffer[TJEI_BUFFER_SIZE]; } TJEState; // ============================================================ // Table definitions. // // The spec defines tjei_default reasonably good quantization matrices and huffman // specification tables. // // // Instead of hard-coding the final huffman table, we only hard-code the table // spec suggested by the specification, and then derive the full table from // there. This is only for didactic purposes but it might be useful if there // ever is the case that we need to swap huffman tables from various sources. // ============================================================ // K.1 - suggested luminance QT static const uint8_t tjei_default_qt_luma_from_spec[] = { 16,11,10,16, 24, 40, 51, 61, 12,12,14,19, 26, 58, 60, 55, 14,13,16,24, 40, 57, 69, 56, 14,17,22,29, 51, 87, 80, 62, 18,22,37,56, 68,109,103, 77, 24,35,55,64, 81,104,113, 92, 49,64,78,87,103,121,120,101, 72,92,95,98,112,100,103, 99, }; // Unused #if 0 static const uint8_t tjei_default_qt_chroma_from_spec[] = { // K.1 - suggested chrominance QT 17,18,24,47,99,99,99,99, 18,21,26,66,99,99,99,99, 24,26,56,99,99,99,99,99, 47,66,99,99,99,99,99,99, 99,99,99,99,99,99,99,99, 99,99,99,99,99,99,99,99, 99,99,99,99,99,99,99,99, 99,99,99,99,99,99,99,99, }; #endif static const uint8_t tjei_default_qt_chroma_from_paper[] = { // Example QT from JPEG paper 16, 12, 14, 14, 18, 24, 49, 72, 11, 10, 16, 24, 40, 51, 61, 12, 13, 17, 22, 35, 64, 92, 14, 16, 22, 37, 55, 78, 95, 19, 24, 29, 56, 64, 87, 98, 26, 40, 51, 68, 81, 103, 112, 58, 57, 87, 109, 104, 121,100, 60, 69, 80, 103, 113, 120, 103, 55, 56, 62, 77, 92, 101, 99, }; // == Procedure to 'deflate' the huffman tree: JPEG spec, C.2 // Number of 16 bit values for every code length. (K.3.3.1) static const uint8_t tjei_default_ht_luma_dc_len[16] = { 0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0 }; // values static const uint8_t tjei_default_ht_luma_dc[12] = { 0,1,2,3,4,5,6,7,8,9,10,11 }; // Number of 16 bit values for every code length. (K.3.3.1) static const uint8_t tjei_default_ht_chroma_dc_len[16] = { 0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0 }; // values static const uint8_t tjei_default_ht_chroma_dc[12] = { 0,1,2,3,4,5,6,7,8,9,10,11 }; // Same as above, but AC coefficients. static const uint8_t tjei_default_ht_luma_ac_len[16] = { 0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d }; static const uint8_t tjei_default_ht_luma_ac[] = { 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA }; static const uint8_t tjei_default_ht_chroma_ac_len[16] = { 0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77 }; static const uint8_t tjei_default_ht_chroma_ac[] = { 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA }; // ============================================================ // Code // ============================================================ // Zig-zag order: static const uint8_t tjei_zig_zag[64] = { 0, 1, 5, 6, 14, 15, 27, 28, 2, 4, 7, 13, 16, 26, 29, 42, 3, 8, 12, 17, 25, 30, 41, 43, 9, 11, 18, 24, 31, 40, 44, 53, 10, 19, 23, 32, 39, 45, 52, 54, 20, 22, 33, 38, 46, 51, 55, 60, 21, 34, 37, 47, 50, 56, 59, 61, 35, 36, 48, 49, 57, 58, 62, 63, }; // Memory order as big endian. 0xhilo -> 0xlohi which looks as 0xhilo in memory. static uint16_t tjei_be_word(const uint16_t le_word) { uint16_t lo = (le_word & 0x00ff); uint16_t hi = ((le_word & 0xff00) >> 8); return (uint16_t)((lo << 8) | hi); } // ============================================================ // The following structs exist only for code clarity, debugability, and // readability. They are used when writing to disk, but it is useful to have // 1-packed-structs to document how the format works, and to inspect memory // while developing. // ============================================================ static const uint8_t tjeik_jfif_id[] = "JFIF"; static const uint8_t tjeik_com_str[] = "Created by Tiny JPEG Encoder"; // TODO: Get rid of packed structs! #pragma pack(push) #pragma pack(1) typedef struct { uint16_t SOI; // JFIF header. uint16_t APP0; uint16_t jfif_len; uint8_t jfif_id[5]; uint16_t version; uint8_t units; uint16_t x_density; uint16_t y_density; uint8_t x_thumb; uint8_t y_thumb; } TJEJPEGHeader; typedef struct { uint16_t com; uint16_t com_len; char com_str[sizeof(tjeik_com_str) - 1]; } TJEJPEGComment; // Helper struct for TJEFrameHeader (below). typedef struct { uint8_t component_id; uint8_t sampling_factors; // most significant 4 bits: horizontal. 4 LSB: vertical (A.1.1) uint8_t qt; // Quantization table selector. } TJEComponentSpec; typedef struct { uint16_t SOF; uint16_t len; // 8 + 3 * frame.num_components uint8_t precision; // Sample precision (bits per sample). uint16_t height; uint16_t width; uint8_t num_components; // For this implementation, will be equal to 3. TJEComponentSpec component_spec[3]; } TJEFrameHeader; typedef struct { uint8_t component_id; // Just as with TJEComponentSpec uint8_t dc_ac; // (dc|ac) } TJEFrameComponentSpec; typedef struct { uint16_t SOS; uint16_t len; uint8_t num_components; // 3. TJEFrameComponentSpec component_spec[3]; uint8_t first; // 0 uint8_t last; // 63 uint8_t ah_al; // o } TJEScanHeader; #pragma pack(pop) static void tjei_write(TJEState* state, const void* data, size_t num_bytes, size_t num_elements) { size_t to_write = num_bytes * num_elements; // Cap to the buffer available size and copy memory. size_t capped_count = tjei_min(to_write, TJEI_BUFFER_SIZE - 1 - state->output_buffer_count); memcpy(state->output_buffer + state->output_buffer_count, data, capped_count); state->output_buffer_count += capped_count; assert (state->output_buffer_count <= TJEI_BUFFER_SIZE - 1); // Flush the buffer. if ( state->output_buffer_count == TJEI_BUFFER_SIZE - 1 ) { state->write_context.func(state->write_context.context, state->output_buffer, (int)state->output_buffer_count); state->output_buffer_count = 0; } // Recursively calling ourselves with the rest of the buffer. if (capped_count < to_write) { tjei_write(state, (uint8_t*)data+capped_count, to_write - capped_count, 1); } } static void tjei_write_DQT(TJEState* state, const uint8_t* matrix, uint8_t id) { uint16_t DQT = tjei_be_word(0xffdb); tjei_write(state, &DQT, sizeof(uint16_t), 1); uint16_t len = tjei_be_word(0x0043); // 2(len) + 1(id) + 64(matrix) = 67 = 0x43 tjei_write(state, &len, sizeof(uint16_t), 1); assert(id < 4); uint8_t precision_and_id = id; // 0x0000 8 bits | 0x00id tjei_write(state, &precision_and_id, sizeof(uint8_t), 1); // Write matrix tjei_write(state, matrix, 64*sizeof(uint8_t), 1); } typedef enum { TJEI_DC = 0, TJEI_AC = 1 } TJEHuffmanTableClass; static void tjei_write_DHT(TJEState* state, uint8_t const * matrix_len, uint8_t const * matrix_val, TJEHuffmanTableClass ht_class, uint8_t id) { int num_values = 0; for ( int i = 0; i < 16; ++i ) { num_values += matrix_len[i]; } assert(num_values <= 0xffff); uint16_t DHT = tjei_be_word(0xffc4); // 2(len) + 1(Tc|th) + 16 (num lengths) + ?? (num values) uint16_t len = tjei_be_word(2 + 1 + 16 + (uint16_t)num_values); assert(id < 4); uint8_t tc_th = (uint8_t)((((uint8_t)ht_class) << 4) | id); tjei_write(state, &DHT, sizeof(uint16_t), 1); tjei_write(state, &len, sizeof(uint16_t), 1); tjei_write(state, &tc_th, sizeof(uint8_t), 1); tjei_write(state, matrix_len, sizeof(uint8_t), 16); tjei_write(state, matrix_val, sizeof(uint8_t), (size_t)num_values); } // ============================================================ // Huffman deflation code. // ============================================================ // Returns all code sizes from the BITS specification (JPEG C.3) static uint8_t* tjei_huff_get_code_lengths(uint8_t huffsize[/*256*/], uint8_t const * bits) { int k = 0; for ( int i = 0; i < 16; ++i ) { for ( int j = 0; j < bits[i]; ++j ) { huffsize[k++] = (uint8_t)(i + 1); } huffsize[k] = 0; } return huffsize; } // Fills out the prefixes for each code. static uint16_t* tjei_huff_get_codes(uint16_t codes[], uint8_t* huffsize, int64_t count) { uint16_t code = 0; int k = 0; uint8_t sz = huffsize[0]; for(;;) { do { assert(k < count); codes[k++] = code++; } while (huffsize[k] == sz); if (huffsize[k] == 0) { return codes; } do { code = (uint16_t)(code << 1); ++sz; } while( huffsize[k] != sz ); } } static void tjei_huff_get_extended(uint8_t* out_ehuffsize, uint16_t* out_ehuffcode, uint8_t const * huffval, uint8_t* huffsize, uint16_t* huffcode, int64_t count) { int k = 0; do { uint8_t val = huffval[k]; out_ehuffcode[val] = huffcode[k]; out_ehuffsize[val] = huffsize[k]; k++; } while ( k < count ); } // ============================================================ // Returns: // out[1] : number of bits // out[0] : bits TJEI_FORCE_INLINE void tjei_calculate_variable_length_int(int value, uint16_t out[2]) { int abs_val = value; if ( value < 0 ) { abs_val = -abs_val; --value; } out[1] = 1; while( abs_val >>= 1 ) { ++out[1]; } out[0] = (uint16_t)(value & ((1 << out[1]) - 1)); } // Write bits to file. TJEI_FORCE_INLINE void tjei_write_bits(TJEState* state, uint32_t* bitbuffer, uint32_t* location, uint16_t num_bits, uint16_t bits) { // v-- location // [ ] <-- bit buffer // 32 0 // // This call pushes to the bitbuffer and saves the location. Data is pushed // from most significant to less significant. // When we can write a full byte, we write a byte and shift. // Push the stack. uint32_t nloc = *location + num_bits; *bitbuffer |= (uint32_t)(bits << (32 - nloc)); *location = nloc; while ( *location >= 8 ) { // Grab the most significant byte. uint8_t c = (uint8_t)((*bitbuffer) >> 24); // Write it to file. tjei_write(state, &c, 1, 1); if ( c == 0xff ) { // Special case: tell JPEG this is not a marker. char z = 0; tjei_write(state, &z, 1, 1); } // Pop the stack. *bitbuffer <<= 8; *location -= 8; } } // DCT implementation by Thomas G. Lane. // Obtained through NVIDIA // http://developer.download.nvidia.com/SDK/9.5/Samples/vidimaging_samples.html#gpgpu_dct // // QUOTE: // This implementation is based on Arai, Agui, and Nakajima's algorithm for // scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in // Japanese, but the algorithm is described in the Pennebaker & Mitchell // JPEG textbook (see REFERENCES section in file README). The following code // is based directly on figure 4-8 in P&M. // static void tjei_fdct (float * data) { float tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; float tmp10, tmp11, tmp12, tmp13; float z1, z2, z3, z4, z5, z11, z13; float *dataptr; int ctr; /* Pass 1: process rows. */ dataptr = data; for ( ctr = 7; ctr >= 0; ctr-- ) { tmp0 = dataptr[0] + dataptr[7]; tmp7 = dataptr[0] - dataptr[7]; tmp1 = dataptr[1] + dataptr[6]; tmp6 = dataptr[1] - dataptr[6]; tmp2 = dataptr[2] + dataptr[5]; tmp5 = dataptr[2] - dataptr[5]; tmp3 = dataptr[3] + dataptr[4]; tmp4 = dataptr[3] - dataptr[4]; /* Even part */ tmp10 = tmp0 + tmp3; /* phase 2 */ tmp13 = tmp0 - tmp3; tmp11 = tmp1 + tmp2; tmp12 = tmp1 - tmp2; dataptr[0] = tmp10 + tmp11; /* phase 3 */ dataptr[4] = tmp10 - tmp11; z1 = (tmp12 + tmp13) * ((float) 0.707106781); /* c4 */ dataptr[2] = tmp13 + z1; /* phase 5 */ dataptr[6] = tmp13 - z1; /* Odd part */ tmp10 = tmp4 + tmp5; /* phase 2 */ tmp11 = tmp5 + tmp6; tmp12 = tmp6 + tmp7; /* The rotator is modified from fig 4-8 to avoid extra negations. */ z5 = (tmp10 - tmp12) * ((float) 0.382683433); /* c6 */ z2 = ((float) 0.541196100) * tmp10 + z5; /* c2-c6 */ z4 = ((float) 1.306562965) * tmp12 + z5; /* c2+c6 */ z3 = tmp11 * ((float) 0.707106781); /* c4 */ z11 = tmp7 + z3; /* phase 5 */ z13 = tmp7 - z3; dataptr[5] = z13 + z2; /* phase 6 */ dataptr[3] = z13 - z2; dataptr[1] = z11 + z4; dataptr[7] = z11 - z4; dataptr += 8; /* advance pointer to next row */ } /* Pass 2: process columns. */ dataptr = data; for ( ctr = 8-1; ctr >= 0; ctr-- ) { tmp0 = dataptr[8*0] + dataptr[8*7]; tmp7 = dataptr[8*0] - dataptr[8*7]; tmp1 = dataptr[8*1] + dataptr[8*6]; tmp6 = dataptr[8*1] - dataptr[8*6]; tmp2 = dataptr[8*2] + dataptr[8*5]; tmp5 = dataptr[8*2] - dataptr[8*5]; tmp3 = dataptr[8*3] + dataptr[8*4]; tmp4 = dataptr[8*3] - dataptr[8*4]; /* Even part */ tmp10 = tmp0 + tmp3; /* phase 2 */ tmp13 = tmp0 - tmp3; tmp11 = tmp1 + tmp2; tmp12 = tmp1 - tmp2; dataptr[8*0] = tmp10 + tmp11; /* phase 3 */ dataptr[8*4] = tmp10 - tmp11; z1 = (tmp12 + tmp13) * ((float) 0.707106781); /* c4 */ dataptr[8*2] = tmp13 + z1; /* phase 5 */ dataptr[8*6] = tmp13 - z1; /* Odd part */ tmp10 = tmp4 + tmp5; /* phase 2 */ tmp11 = tmp5 + tmp6; tmp12 = tmp6 + tmp7; /* The rotator is modified from fig 4-8 to avoid extra negations. */ z5 = (tmp10 - tmp12) * ((float) 0.382683433); /* c6 */ z2 = ((float) 0.541196100) * tmp10 + z5; /* c2-c6 */ z4 = ((float) 1.306562965) * tmp12 + z5; /* c2+c6 */ z3 = tmp11 * ((float) 0.707106781); /* c4 */ z11 = tmp7 + z3; /* phase 5 */ z13 = tmp7 - z3; dataptr[8*5] = z13 + z2; /* phase 6 */ dataptr[8*3] = z13 - z2; dataptr[8*1] = z11 + z4; dataptr[8*7] = z11 - z4; dataptr++; /* advance pointer to next column */ } } #if !TJE_USE_FAST_DCT static float slow_fdct(int u, int v, float* data) { #define kPI 3.14159265f float res = 0.0f; float cu = (u == 0) ? 0.70710678118654f : 1; float cv = (v == 0) ? 0.70710678118654f : 1; for ( int y = 0; y < 8; ++y ) { for ( int x = 0; x < 8; ++x ) { res += (data[y * 8 + x]) * cosf(((2.0f * x + 1.0f) * u * kPI) / 16.0f) * cosf(((2.0f * y + 1.0f) * v * kPI) / 16.0f); } } res *= 0.25f * cu * cv; return res; #undef kPI } #endif #define ABS(x) ((x) < 0 ? -(x) : (x)) static void tjei_encode_and_write_MCU(TJEState* state, float* mcu, #if TJE_USE_FAST_DCT float* qt, // Pre-processed quantization matrix. #else uint8_t* qt, #endif uint8_t* huff_dc_len, uint16_t* huff_dc_code, // Huffman tables uint8_t* huff_ac_len, uint16_t* huff_ac_code, int* pred, // Previous DC coefficient uint32_t* bitbuffer, // Bitstack. uint32_t* location) { int du[64]; // Data unit in zig-zag order float dct_mcu[64]; memcpy(dct_mcu, mcu, 64 * sizeof(float)); #if TJE_USE_FAST_DCT tjei_fdct(dct_mcu); for ( int i = 0; i < 64; ++i ) { float fval = dct_mcu[i]; fval *= qt[i]; #if 0 fval = (fval > 0) ? floorf(fval + 0.5f) : ceilf(fval - 0.5f); #else fval = floorf(fval + 1024 + 0.5f); fval -= 1024; #endif int val = (int)fval; du[tjei_zig_zag[i]] = val; } #else for ( int v = 0; v < 8; ++v ) { for ( int u = 0; u < 8; ++u ) { dct_mcu[v * 8 + u] = slow_fdct(u, v, mcu); } } for ( int i = 0; i < 64; ++i ) { float fval = dct_mcu[i] / (qt[i]); int val = (int)((fval > 0) ? floorf(fval + 0.5f) : ceilf(fval - 0.5f)); du[tjei_zig_zag[i]] = val; } #endif uint16_t vli[2]; // Encode DC coefficient. int diff = du[0] - *pred; *pred = du[0]; if ( diff != 0 ) { tjei_calculate_variable_length_int(diff, vli); // Write number of bits with Huffman coding tjei_write_bits(state, bitbuffer, location, huff_dc_len[vli[1]], huff_dc_code[vli[1]]); // Write the bits. tjei_write_bits(state, bitbuffer, location, vli[1], vli[0]); } else { tjei_write_bits(state, bitbuffer, location, huff_dc_len[0], huff_dc_code[0]); } // ==== Encode AC coefficients ==== int last_non_zero_i = 0; // Find the last non-zero element. for ( int i = 63; i > 0; --i ) { if (du[i] != 0) { last_non_zero_i = i; break; } } for ( int i = 1; i <= last_non_zero_i; ++i ) { // If zero, increase count. If >=15, encode (FF,00) int zero_count = 0; while ( du[i] == 0 ) { ++zero_count; ++i; if (zero_count == 16) { // encode (ff,00) == 0xf0 tjei_write_bits(state, bitbuffer, location, huff_ac_len[0xf0], huff_ac_code[0xf0]); zero_count = 0; } } tjei_calculate_variable_length_int(du[i], vli); assert(zero_count < 0x10); assert(vli[1] <= 10); uint16_t sym1 = (uint16_t)((uint16_t)zero_count << 4) | vli[1]; assert(huff_ac_len[sym1] != 0); // Write symbol 1 --- (RUNLENGTH, SIZE) tjei_write_bits(state, bitbuffer, location, huff_ac_len[sym1], huff_ac_code[sym1]); // Write symbol 2 --- (AMPLITUDE) tjei_write_bits(state, bitbuffer, location, vli[1], vli[0]); } if (last_non_zero_i != 63) { // write EOB HUFF(00,00) tjei_write_bits(state, bitbuffer, location, huff_ac_len[0], huff_ac_code[0]); } return; } enum { TJEI_LUMA_DC, TJEI_LUMA_AC, TJEI_CHROMA_DC, TJEI_CHROMA_AC, }; #if TJE_USE_FAST_DCT struct TJEProcessedQT { float chroma[64]; float luma[64]; }; #endif // Set up huffman tables in state. static void tjei_huff_expand(TJEState* state) { assert(state); state->ht_bits[TJEI_LUMA_DC] = tjei_default_ht_luma_dc_len; state->ht_bits[TJEI_LUMA_AC] = tjei_default_ht_luma_ac_len; state->ht_bits[TJEI_CHROMA_DC] = tjei_default_ht_chroma_dc_len; state->ht_bits[TJEI_CHROMA_AC] = tjei_default_ht_chroma_ac_len; state->ht_vals[TJEI_LUMA_DC] = tjei_default_ht_luma_dc; state->ht_vals[TJEI_LUMA_AC] = tjei_default_ht_luma_ac; state->ht_vals[TJEI_CHROMA_DC] = tjei_default_ht_chroma_dc; state->ht_vals[TJEI_CHROMA_AC] = tjei_default_ht_chroma_ac; // How many codes in total for each of LUMA_(DC|AC) and CHROMA_(DC|AC) int32_t spec_tables_len[4] = { 0 }; for ( int i = 0; i < 4; ++i ) { for ( int k = 0; k < 16; ++k ) { spec_tables_len[i] += state->ht_bits[i][k]; } } // Fill out the extended tables.. uint8_t huffsize[4][257]; uint16_t huffcode[4][256]; for ( int i = 0; i < 4; ++i ) { assert (256 >= spec_tables_len[i]); tjei_huff_get_code_lengths(huffsize[i], state->ht_bits[i]); tjei_huff_get_codes(huffcode[i], huffsize[i], spec_tables_len[i]); } for ( int i = 0; i < 4; ++i ) { int64_t count = spec_tables_len[i]; tjei_huff_get_extended(state->ehuffsize[i], state->ehuffcode[i], state->ht_vals[i], &huffsize[i][0], &huffcode[i][0], count); } } static int tjei_encode_main(TJEState* state, const unsigned char* src_data, const int width, const int height, const int src_num_components) { if (src_num_components != 3 && src_num_components != 4) { return 0; } if (width > 0xffff || height > 0xffff) { return 0; } #if TJE_USE_FAST_DCT struct TJEProcessedQT pqt; // Again, taken from classic japanese implementation. // /* For float AA&N IDCT method, divisors are equal to quantization * coefficients scaled by scalefactor[row]*scalefactor[col], where * scalefactor[0] = 1 * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 * We apply a further scale factor of 8. * What's actually stored is 1/divisor so that the inner loop can * use a multiplication rather than a division. */ static const float aan_scales[] = { 1.0f, 1.387039845f, 1.306562965f, 1.175875602f, 1.0f, 0.785694958f, 0.541196100f, 0.275899379f }; // build (de)quantization tables for(int y=0; y<8; y++) { for(int x=0; x<8; x++) { int i = y*8 + x; pqt.luma[y*8+x] = 1.0f / (8 * aan_scales[x] * aan_scales[y] * state->qt_luma[tjei_zig_zag[i]]); pqt.chroma[y*8+x] = 1.0f / (8 * aan_scales[x] * aan_scales[y] * state->qt_chroma[tjei_zig_zag[i]]); } } #endif { // Write header TJEJPEGHeader header; // JFIF header. header.SOI = tjei_be_word(0xffd8); // Sequential DCT header.APP0 = tjei_be_word(0xffe0); uint16_t jfif_len = sizeof(TJEJPEGHeader) - 4 /*SOI & APP0 markers*/; header.jfif_len = tjei_be_word(jfif_len); memcpy(header.jfif_id, (void*)tjeik_jfif_id, 5); header.version = tjei_be_word(0x0102); header.units = 0x01; // Dots-per-inch header.x_density = tjei_be_word(0x0060); // 96 DPI header.y_density = tjei_be_word(0x0060); // 96 DPI header.x_thumb = 0; header.y_thumb = 0; tjei_write(state, &header, sizeof(TJEJPEGHeader), 1); } { // Write comment TJEJPEGComment com; uint16_t com_len = 2 + sizeof(tjeik_com_str) - 1; // Comment com.com = tjei_be_word(0xfffe); com.com_len = tjei_be_word(com_len); memcpy(com.com_str, (void*)tjeik_com_str, sizeof(tjeik_com_str)-1); tjei_write(state, &com, sizeof(TJEJPEGComment), 1); } // Write quantization tables. tjei_write_DQT(state, state->qt_luma, 0x00); tjei_write_DQT(state, state->qt_chroma, 0x01); { // Write the frame marker. TJEFrameHeader header; header.SOF = tjei_be_word(0xffc0); header.len = tjei_be_word(8 + 3 * 3); header.precision = 8; assert(width <= 0xffff); assert(height <= 0xffff); header.width = tjei_be_word((uint16_t)width); header.height = tjei_be_word((uint16_t)height); header.num_components = 3; uint8_t tables[3] = { 0, // Luma component gets luma table (see tjei_write_DQT call above.) 1, // Chroma component gets chroma table 1, // Chroma component gets chroma table }; for (int i = 0; i < 3; ++i) { TJEComponentSpec spec; spec.component_id = (uint8_t)(i + 1); // No particular reason. Just 1, 2, 3. spec.sampling_factors = (uint8_t)0x11; spec.qt = tables[i]; header.component_spec[i] = spec; } // Write to file. tjei_write(state, &header, sizeof(TJEFrameHeader), 1); } tjei_write_DHT(state, state->ht_bits[TJEI_LUMA_DC], state->ht_vals[TJEI_LUMA_DC], TJEI_DC, 0); tjei_write_DHT(state, state->ht_bits[TJEI_LUMA_AC], state->ht_vals[TJEI_LUMA_AC], TJEI_AC, 0); tjei_write_DHT(state, state->ht_bits[TJEI_CHROMA_DC], state->ht_vals[TJEI_CHROMA_DC], TJEI_DC, 1); tjei_write_DHT(state, state->ht_bits[TJEI_CHROMA_AC], state->ht_vals[TJEI_CHROMA_AC], TJEI_AC, 1); // Write start of scan { TJEScanHeader header; header.SOS = tjei_be_word(0xffda); header.len = tjei_be_word((uint16_t)(6 + (sizeof(TJEFrameComponentSpec) * 3))); header.num_components = 3; uint8_t tables[3] = { 0x00, 0x11, 0x11, }; for (int i = 0; i < 3; ++i) { TJEFrameComponentSpec cs; // Must be equal to component_id from frame header above. cs.component_id = (uint8_t)(i + 1); cs.dc_ac = (uint8_t)tables[i]; header.component_spec[i] = cs; } header.first = 0; header.last = 63; header.ah_al = 0; tjei_write(state, &header, sizeof(TJEScanHeader), 1); } // Write compressed data. float du_y[64]; float du_b[64]; float du_r[64]; // Set diff to 0. int pred_y = 0; int pred_b = 0; int pred_r = 0; // Bit stack uint32_t bitbuffer = 0; uint32_t location = 0; for ( int y = 0; y < height; y += 8 ) { for ( int x = 0; x < width; x += 8 ) { // Block loop: ==== for ( int off_y = 0; off_y < 8; ++off_y ) { for ( int off_x = 0; off_x < 8; ++off_x ) { int block_index = (off_y * 8 + off_x); int src_index = (((y + off_y) * width) + (x + off_x)) * src_num_components; int col = x + off_x; int row = y + off_y; if(row >= height) { src_index -= (width * (row - height + 1)) * src_num_components; } if(col >= width) { src_index -= (col - width + 1) * src_num_components; } assert(src_index < width * height * src_num_components); uint8_t r = src_data[src_index + 0]; uint8_t g = src_data[src_index + 1]; uint8_t b = src_data[src_index + 2]; float luma = 0.299f * r + 0.587f * g + 0.114f * b - 128; float cb = -0.1687f * r - 0.3313f * g + 0.5f * b; float cr = 0.5f * r - 0.4187f * g - 0.0813f * b; du_y[block_index] = luma; du_b[block_index] = cb; du_r[block_index] = cr; } } tjei_encode_and_write_MCU(state, du_y, #if TJE_USE_FAST_DCT pqt.luma, #else state->qt_luma, #endif state->ehuffsize[TJEI_LUMA_DC], state->ehuffcode[TJEI_LUMA_DC], state->ehuffsize[TJEI_LUMA_AC], state->ehuffcode[TJEI_LUMA_AC], &pred_y, &bitbuffer, &location); tjei_encode_and_write_MCU(state, du_b, #if TJE_USE_FAST_DCT pqt.chroma, #else state->qt_chroma, #endif state->ehuffsize[TJEI_CHROMA_DC], state->ehuffcode[TJEI_CHROMA_DC], state->ehuffsize[TJEI_CHROMA_AC], state->ehuffcode[TJEI_CHROMA_AC], &pred_b, &bitbuffer, &location); tjei_encode_and_write_MCU(state, du_r, #if TJE_USE_FAST_DCT pqt.chroma, #else state->qt_chroma, #endif state->ehuffsize[TJEI_CHROMA_DC], state->ehuffcode[TJEI_CHROMA_DC], state->ehuffsize[TJEI_CHROMA_AC], state->ehuffcode[TJEI_CHROMA_AC], &pred_r, &bitbuffer, &location); } } // Finish the image. { // Flush if (location > 0 && location < 8) { tjei_write_bits(state, &bitbuffer, &location, (uint16_t)(8 - location), 0); } } uint16_t EOI = tjei_be_word(0xffd9); tjei_write(state, &EOI, sizeof(uint16_t), 1); if (state->output_buffer_count) { state->write_context.func(state->write_context.context, state->output_buffer, (int)state->output_buffer_count); state->output_buffer_count = 0; } return 1; } int tje_encode_to_file(const char* dest_path, const int width, const int height, const int num_components, const unsigned char* src_data) { int res = tje_encode_to_file_at_quality(dest_path, 3, width, height, num_components, src_data); return res; } static void tjei_stdlib_func(void* context, void* data, int size) { FILE* fd = (FILE*)context; fwrite(data, size, 1, fd); } // Define public interface. int tje_encode_to_file_at_quality(const char* dest_path, const int quality, const int width, const int height, const int num_components, const unsigned char* src_data) { FILE* fd = fopen(dest_path, "wb"); if (!fd) { tje_log("Could not open file for writing."); return 0; } int result = tje_encode_with_func(tjei_stdlib_func, fd, quality, width, height, num_components, src_data); result |= 0 == fclose(fd); return result; } int tje_encode_with_func(tje_write_func* func, void* context, const int quality, const int width, const int height, const int num_components, const unsigned char* src_data) { if (quality < 1 || quality > 3) { tje_log("[ERROR] -- Valid 'quality' values are 1 (lowest), 2, or 3 (highest)\n"); return 0; } TJEState state = { 0 }; uint8_t qt_factor = 1; switch(quality) { case 3: for ( int i = 0; i < 64; ++i ) { state.qt_luma[i] = 1; state.qt_chroma[i] = 1; } break; case 2: qt_factor = 10; // don't break. fall through. case 1: for ( int i = 0; i < 64; ++i ) { state.qt_luma[i] = tjei_default_qt_luma_from_spec[i] / qt_factor; if (state.qt_luma[i] == 0) { state.qt_luma[i] = 1; } state.qt_chroma[i] = tjei_default_qt_chroma_from_paper[i] / qt_factor; if (state.qt_chroma[i] == 0) { state.qt_chroma[i] = 1; } } break; default: assert(!"invalid code path"); break; } TJEWriteContext wc = { 0 }; wc.context = context; wc.func = func; state.write_context = wc; tjei_huff_expand(&state); int result = tjei_encode_main(&state, src_data, width, height, num_components); return result; } // ============================================================ #endif // TJE_IMPLEMENTATION // ============================================================ // #if defined(__GNUC__) || defined(__clang__) #pragma GCC diagnostic pop #endif #ifdef __cplusplus } // extern C #endif ================================================ FILE: src/engine/renderer/qgl.h ================================================ /* =========================================================================== 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 Foobar; 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__ #if defined( _WIN32 ) #pragma warning (disable: 4201) #pragma warning (disable: 4214) #pragma warning (disable: 4514) #pragma warning (disable: 4032) #pragma warning (disable: 4201) #pragma warning (disable: 4214) #define NOMINMAX #include #include #endif /* ** multitexture extension definitions */ #define GL_MAX_ACTIVE_TEXTURES_ARB 0x84E2 #define GL_TEXTURE0_ARB 0x84C0 #define GL_TEXTURE1_ARB 0x84C1 /* ** extension constants */ // S3TC compression constants #define GL_RGB4_S3TC 0x83A1 // extensions will be function pointers on all platforms extern void ( APIENTRY * qglActiveTextureARB )( GLenum texture ); extern void ( APIENTRY * qglClientActiveTextureARB )( GLenum texture ); extern void ( APIENTRY * qglLockArraysEXT) (GLint, GLint); extern void ( APIENTRY * qglUnlockArraysEXT) (void); //=========================================================================== extern void ( APIENTRY * qglAlphaFunc )(GLenum func, GLclampf ref); extern void ( APIENTRY * qglBegin )(GLenum mode); extern void ( APIENTRY * qglBindTexture )(GLenum target, GLuint texture); extern void ( APIENTRY * qglBlendFunc )(GLenum sfactor, GLenum dfactor); extern void ( APIENTRY * qglClear )(GLbitfield mask); extern void ( APIENTRY * qglClearColor )(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); extern void ( APIENTRY * qglClipPlane )(GLenum plane, const GLdouble *equation); extern void ( APIENTRY * qglColor3f )(GLfloat red, GLfloat green, GLfloat blue); extern void ( APIENTRY * qglColorMask )(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); extern void ( APIENTRY * qglColorPointer )(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); extern void ( APIENTRY * qglCullFace )(GLenum mode); extern void ( APIENTRY * qglDeleteTextures )(GLsizei n, const GLuint *textures); extern void ( APIENTRY * qglDepthFunc )(GLenum func); extern void ( APIENTRY * qglDepthMask )(GLboolean flag); extern void ( APIENTRY * qglDepthRange )(GLclampd zNear, GLclampd zFar); extern void ( APIENTRY * qglDisable )(GLenum cap); extern void ( APIENTRY * qglDisableClientState )(GLenum array); extern void ( APIENTRY * qglDrawBuffer )(GLenum mode); extern void ( APIENTRY * qglDrawElements )(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices); extern void ( APIENTRY * qglEnable )(GLenum cap); extern void ( APIENTRY * qglEnableClientState )(GLenum array); extern void ( APIENTRY * qglEnd )(void); extern void ( APIENTRY * qglFinish )(void); extern GLenum ( APIENTRY * qglGetError )(void); extern void ( APIENTRY * qglGetIntegerv )(GLenum pname, GLint *params); extern const GLubyte * ( APIENTRY * qglGetString )(GLenum name); extern void ( APIENTRY * qglLineWidth )(GLfloat width); extern void ( APIENTRY * qglLoadIdentity )(void); extern void ( APIENTRY * qglLoadMatrixf )(const GLfloat *m); extern void ( APIENTRY * qglMatrixMode )(GLenum mode); extern void ( APIENTRY * qglOrtho )(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); extern void ( APIENTRY * qglPolygonMode )(GLenum face, GLenum mode); extern void ( APIENTRY * qglPolygonOffset )(GLfloat factor, GLfloat units); extern void ( APIENTRY * qglPopMatrix )(void); extern void ( APIENTRY * qglPushMatrix )(void); extern void ( APIENTRY * qglReadPixels )(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels); extern void ( APIENTRY * qglScissor )(GLint x, GLint y, GLsizei width, GLsizei height); extern void ( APIENTRY * qglStencilFunc )(GLenum func, GLint ref, GLuint mask); extern void ( APIENTRY * qglStencilOp )(GLenum fail, GLenum zfail, GLenum zpass); extern void ( APIENTRY * qglTexCoord2f )(GLfloat s, GLfloat t); extern void ( APIENTRY * qglTexCoord2fv )(const GLfloat *v); extern void ( APIENTRY * qglTexCoordPointer )(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); extern void ( APIENTRY * qglTexEnvf )(GLenum target, GLenum pname, GLfloat param); extern void ( APIENTRY * qglTexImage2D )(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels); extern void ( APIENTRY * qglTexParameterf )(GLenum target, GLenum pname, GLfloat param); extern void ( APIENTRY * qglTexParameterfv )(GLenum target, GLenum pname, const GLfloat *params); extern void ( APIENTRY * qglTexSubImage2D )(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); extern void ( APIENTRY * qglVertex2f )(GLfloat x, GLfloat y); extern void ( APIENTRY * qglVertex3f )(GLfloat x, GLfloat y, GLfloat z); extern void ( APIENTRY * qglVertex3fv )(const GLfloat *v); extern void ( APIENTRY * qglVertexPointer )(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); extern void ( APIENTRY * qglViewport )(GLint x, GLint y, GLsizei width, GLsizei height); #if defined( _WIN32 ) extern HGLRC ( WINAPI * qwglCreateContext)(HDC); extern BOOL ( WINAPI * qwglDeleteContext)(HGLRC); extern PROC ( WINAPI * qwglGetProcAddress)(LPCSTR); extern BOOL ( WINAPI * qwglMakeCurrent)(HDC, HGLRC); extern BOOL ( WINAPI * qwglSwapIntervalEXT)( int interval ); #endif #endif ================================================ FILE: src/engine/renderer/shaders/apply_gamma.comp ================================================ #version 450 #extension GL_EXT_samplerless_texture_functions : require layout(local_size_x = 8, local_size_y = 8) in; layout(push_constant) uniform Push_Constants { uvec2 output_size; uint identity_gamma; }; layout(binding=0) uniform texture2D output_image; layout(binding=1) uniform writeonly image2D swapchain_image; layout(binding=2) buffer Gamma_Buffer {float data[256];} gamma_buffer; float apply_gamma(float c) { int index = clamp(int(c * 256.0), 0, 255); float cc = gamma_buffer.data[index]; return cc; } void main() { ivec2 loc = ivec2(gl_GlobalInvocationID.xy); if (loc.x < output_size.x && loc.y < output_size.y) { vec4 color = texelFetch(output_image, loc, 0); if (identity_gamma == 0) { color.x = apply_gamma(color.x); color.y = apply_gamma(color.y); color.z = apply_gamma(color.z); } imageStore(swapchain_image, loc, color); } } ================================================ FILE: src/engine/renderer/shaders/compile.bat ================================================ @echo off set "VSCMD_START_DIR=%CD%" call "C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\Tools\VsDevCmd.bat" set tools_dir=..\..\..\..\tools set bin2hex=%tools_dir%\bin2hex.exe set bin2hex_cpp=%tools_dir%\bin2hex.cpp if not exist %bin2hex% ( cl.exe /EHsc /nologo /Fe%tools_dir%\ /Fo%tools_dir%\ %bin2hex_cpp% ) set PATH=%tools_dir%;%PATH% for %%f in (*.vert) do ( %VULKAN_SDK%\Bin\glslangValidator.exe -V %%f %bin2hex% vert.spv %%~nf_vert_spv > spirv/%%~nf_vert.cpp del vert.spv ) for %%f in (*.frag) do ( %VULKAN_SDK%\Bin\glslangValidator.exe -V %%f %bin2hex% frag.spv %%~nf_frag_spv > spirv/%%~nf_frag.cpp del frag.spv ) for %%f in (*.comp) do ( %VULKAN_SDK%\Bin\glslangValidator.exe -V %%f %bin2hex% comp.spv %%~nf_comp_spv > spirv/%%~nf_comp.cpp del comp.spv ) ================================================ FILE: src/engine/renderer/shaders/compile_hlsl.bat ================================================ @echo off set "VSCMD_START_DIR=%CD%" call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\VsDevCmd.bat" set tools_dir=..\..\..\..\tools set bin2hex=%tools_dir%\bin2hex.exe set bin2hex_cpp=%tools_dir%\bin2hex.cpp if not exist %bin2hex% ( cl.exe /EHsc /nologo /Fe%tools_dir%\ /Fo%tools_dir%\ %bin2hex_cpp% ) set PATH=%tools_dir%;%PATH% @rem single texture VS fxc.exe /nologo /T vs_4_0 /E single_texture_vs /Fo shader.bin shaders.hlsl %bin2hex% shader.bin single_texture_vs > hlsl_compiled/single_texture_vs.cpp del shader.bin fxc.exe /nologo /T vs_4_0 /E single_texture_clipping_plane_vs /Fo shader.bin shaders.hlsl %bin2hex% shader.bin single_texture_clipping_plane_vs > hlsl_compiled/single_texture_clipping_plane_vs.cpp del shader.bin @rem multi texture VS fxc.exe /nologo /T vs_4_0 /E multi_texture_vs /Fo shader.bin shaders.hlsl %bin2hex% shader.bin multi_texture_vs > hlsl_compiled/multi_texture_vs.cpp del shader.bin fxc.exe /nologo /T vs_4_0 /E multi_texture_clipping_plane_vs /Fo shader.bin shaders.hlsl %bin2hex% shader.bin multi_texture_clipping_plane_vs > hlsl_compiled/multi_texture_clipping_plane_vs.cpp del shader.bin @rem signle texture PS fxc.exe /nologo /T ps_4_0 /E single_texture_ps /Fo shader.bin shaders.hlsl %bin2hex% shader.bin single_texture_ps > hlsl_compiled/single_texture_ps.cpp del shader.bin fxc.exe /nologo /T ps_4_0 /E single_texture_ps /Fo shader.bin shaders.hlsl /DALPHA_TEST_GT0 %bin2hex% shader.bin single_texture_gt0_ps > hlsl_compiled/single_texture_gt0_ps.cpp del shader.bin fxc.exe /nologo /T ps_4_0 /E single_texture_ps /Fo shader.bin shaders.hlsl /DALPHA_TEST_LT80 %bin2hex% shader.bin single_texture_lt80_ps > hlsl_compiled/single_texture_lt80_ps.cpp del shader.bin fxc.exe /nologo /T ps_4_0 /E single_texture_ps /Fo shader.bin shaders.hlsl /DALPHA_TEST_GE80 %bin2hex% shader.bin single_texture_ge80_ps > hlsl_compiled/single_texture_ge80_ps.cpp del shader.bin @rem multi texture mul PS fxc.exe /nologo /T ps_4_0 /E multi_texture_mul_ps /Fo shader.bin shaders.hlsl %bin2hex% shader.bin multi_texture_mul_ps > hlsl_compiled/multi_texture_mul_ps.cpp del shader.bin fxc.exe /nologo /T ps_4_0 /E multi_texture_mul_ps /Fo shader.bin shaders.hlsl /DALPHA_TEST_GT0 %bin2hex% shader.bin multi_texture_mul_gt0_ps > hlsl_compiled/multi_texture_mul_gt0_ps.cpp del shader.bin fxc.exe /nologo /T ps_4_0 /E multi_texture_mul_ps /Fo shader.bin shaders.hlsl /DALPHA_TEST_LT80 %bin2hex% shader.bin multi_texture_mul_lt80_ps > hlsl_compiled/multi_texture_mul_lt80_ps.cpp del shader.bin fxc.exe /nologo /T ps_4_0 /E multi_texture_mul_ps /Fo shader.bin shaders.hlsl /DALPHA_TEST_GE80 %bin2hex% shader.bin multi_texture_mul_ge80_ps > hlsl_compiled/multi_texture_mul_ge80_ps.cpp del shader.bin @rem multi texture add PS fxc.exe /nologo /T ps_4_0 /E multi_texture_add_ps /Fo shader.bin shaders.hlsl %bin2hex% shader.bin multi_texture_add_ps > hlsl_compiled/multi_texture_add_ps.cpp del shader.bin fxc.exe /nologo /T ps_4_0 /E multi_texture_add_ps /Fo shader.bin shaders.hlsl /DALPHA_TEST_GT0 %bin2hex% shader.bin multi_texture_add_gt0_ps > hlsl_compiled/multi_texture_add_gt0_ps.cpp del shader.bin fxc.exe /nologo /T ps_4_0 /E multi_texture_add_ps /Fo shader.bin shaders.hlsl /DALPHA_TEST_LT80 %bin2hex% shader.bin multi_texture_add_lt80_ps > hlsl_compiled/multi_texture_add_lt80_ps.cpp del shader.bin fxc.exe /nologo /T ps_4_0 /E multi_texture_add_ps /Fo shader.bin shaders.hlsl /DALPHA_TEST_GE80 %bin2hex% shader.bin multi_texture_add_ge80_ps > hlsl_compiled/multi_texture_add_ge80_ps.cpp del shader.bin ================================================ FILE: src/engine/renderer/shaders/hlsl_compiled/multi_texture_add_ge80_ps.cpp ================================================ unsigned char multi_texture_add_ge80_ps[] = { 0x44, 0x58, 0x42, 0x43, 0xAC, 0x4E, 0x52, 0x94, 0x46, 0xAA, 0xBC, 0x34, 0x76, 0x4F, 0x73, 0x0C, 0xB5, 0x77, 0xB0, 0xB1, 0x01, 0x00, 0x00, 0x00, 0xC4, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x24, 0x01, 0x00, 0x00, 0xB0, 0x01, 0x00, 0x00, 0xE4, 0x01, 0x00, 0x00, 0x48, 0x03, 0x00, 0x00, 0x52, 0x44, 0x45, 0x46, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x04, 0xFF, 0xFF, 0x00, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x9C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xA5, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xAE, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0xB7, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x72, 0x30, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x72, 0x31, 0x00, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x30, 0x00, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x31, 0x00, 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4C, 0x53, 0x4C, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6F, 0x6D, 0x70, 0x69, 0x6C, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2E, 0x31, 0x00, 0x49, 0x53, 0x47, 0x4E, 0x84, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x53, 0x56, 0x5F, 0x50, 0x4F, 0x53, 0x49, 0x54, 0x49, 0x4F, 0x4E, 0x00, 0x43, 0x4F, 0x4C, 0x4F, 0x52, 0x00, 0x54, 0x45, 0x58, 0x43, 0x4F, 0x4F, 0x52, 0x44, 0x00, 0xAB, 0x4F, 0x53, 0x47, 0x4E, 0x2C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x53, 0x56, 0x5F, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x00, 0xAB, 0xAB, 0x53, 0x48, 0x44, 0x52, 0x5C, 0x01, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x03, 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x03, 0x00, 0x60, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x58, 0x18, 0x00, 0x04, 0x00, 0x70, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x58, 0x18, 0x00, 0x04, 0x00, 0x70, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0xF2, 0x10, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0x32, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0xC2, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x03, 0xF2, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x09, 0xF2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x7E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x07, 0x82, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x10, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x09, 0xF2, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0xE6, 0x1A, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x7E, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x60, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x07, 0x82, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x07, 0x82, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x0D, 0x00, 0x04, 0x03, 0x3A, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x09, 0x72, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x12, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x05, 0x82, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x01, 0x53, 0x54, 0x41, 0x54, 0x74, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; long long multi_texture_add_ge80_ps_size = 964; ================================================ FILE: src/engine/renderer/shaders/hlsl_compiled/multi_texture_add_gt0_ps.cpp ================================================ unsigned char multi_texture_add_gt0_ps[] = { 0x44, 0x58, 0x42, 0x43, 0x0D, 0x09, 0x41, 0xC2, 0x6D, 0x93, 0x9E, 0x74, 0xE5, 0xCB, 0x0D, 0x68, 0xCD, 0x2F, 0x32, 0x7B, 0x01, 0x00, 0x00, 0x00, 0xC4, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x24, 0x01, 0x00, 0x00, 0xB0, 0x01, 0x00, 0x00, 0xE4, 0x01, 0x00, 0x00, 0x48, 0x03, 0x00, 0x00, 0x52, 0x44, 0x45, 0x46, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x04, 0xFF, 0xFF, 0x00, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x9C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xA5, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xAE, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0xB7, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x72, 0x30, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x72, 0x31, 0x00, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x30, 0x00, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x31, 0x00, 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4C, 0x53, 0x4C, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6F, 0x6D, 0x70, 0x69, 0x6C, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2E, 0x31, 0x00, 0x49, 0x53, 0x47, 0x4E, 0x84, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x53, 0x56, 0x5F, 0x50, 0x4F, 0x53, 0x49, 0x54, 0x49, 0x4F, 0x4E, 0x00, 0x43, 0x4F, 0x4C, 0x4F, 0x52, 0x00, 0x54, 0x45, 0x58, 0x43, 0x4F, 0x4F, 0x52, 0x44, 0x00, 0xAB, 0x4F, 0x53, 0x47, 0x4E, 0x2C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x53, 0x56, 0x5F, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x00, 0xAB, 0xAB, 0x53, 0x48, 0x44, 0x52, 0x5C, 0x01, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x03, 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x03, 0x00, 0x60, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x58, 0x18, 0x00, 0x04, 0x00, 0x70, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x58, 0x18, 0x00, 0x04, 0x00, 0x70, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0xF2, 0x10, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0x32, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0xC2, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x03, 0xF2, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x09, 0xF2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x7E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x07, 0x82, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x10, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x09, 0xF2, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0xE6, 0x1A, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x7E, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x60, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x07, 0x82, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x07, 0x82, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x04, 0x03, 0x3A, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x09, 0x72, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x12, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x05, 0x82, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x01, 0x53, 0x54, 0x41, 0x54, 0x74, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; long long multi_texture_add_gt0_ps_size = 964; ================================================ FILE: src/engine/renderer/shaders/hlsl_compiled/multi_texture_add_lt80_ps.cpp ================================================ unsigned char multi_texture_add_lt80_ps[] = { 0x44, 0x58, 0x42, 0x43, 0x50, 0x45, 0xD8, 0x74, 0x74, 0x33, 0xD0, 0x77, 0x28, 0x3E, 0xBE, 0x82, 0xD6, 0x6E, 0x38, 0x66, 0x01, 0x00, 0x00, 0x00, 0xC4, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x24, 0x01, 0x00, 0x00, 0xB0, 0x01, 0x00, 0x00, 0xE4, 0x01, 0x00, 0x00, 0x48, 0x03, 0x00, 0x00, 0x52, 0x44, 0x45, 0x46, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x04, 0xFF, 0xFF, 0x00, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x9C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xA5, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xAE, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0xB7, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x72, 0x30, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x72, 0x31, 0x00, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x30, 0x00, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x31, 0x00, 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4C, 0x53, 0x4C, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6F, 0x6D, 0x70, 0x69, 0x6C, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2E, 0x31, 0x00, 0x49, 0x53, 0x47, 0x4E, 0x84, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x53, 0x56, 0x5F, 0x50, 0x4F, 0x53, 0x49, 0x54, 0x49, 0x4F, 0x4E, 0x00, 0x43, 0x4F, 0x4C, 0x4F, 0x52, 0x00, 0x54, 0x45, 0x58, 0x43, 0x4F, 0x4F, 0x52, 0x44, 0x00, 0xAB, 0x4F, 0x53, 0x47, 0x4E, 0x2C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x53, 0x56, 0x5F, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x00, 0xAB, 0xAB, 0x53, 0x48, 0x44, 0x52, 0x5C, 0x01, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x03, 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x03, 0x00, 0x60, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x58, 0x18, 0x00, 0x04, 0x00, 0x70, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x58, 0x18, 0x00, 0x04, 0x00, 0x70, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0xF2, 0x10, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0x32, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0xC2, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x03, 0xF2, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x09, 0xF2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x7E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x07, 0x82, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x10, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x09, 0xF2, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0xE6, 0x1A, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x7E, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x60, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x07, 0x82, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x07, 0x82, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x0D, 0x00, 0x04, 0x03, 0x3A, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x09, 0x72, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x12, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x05, 0x82, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x01, 0x53, 0x54, 0x41, 0x54, 0x74, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; long long multi_texture_add_lt80_ps_size = 964; ================================================ FILE: src/engine/renderer/shaders/hlsl_compiled/multi_texture_add_ps.cpp ================================================ unsigned char multi_texture_add_ps[] = { 0x44, 0x58, 0x42, 0x43, 0x19, 0xA2, 0x34, 0xAB, 0xC2, 0x02, 0x75, 0x6D, 0xC1, 0x92, 0xFF, 0x56, 0xAD, 0xD4, 0xE6, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x88, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x24, 0x01, 0x00, 0x00, 0xB0, 0x01, 0x00, 0x00, 0xE4, 0x01, 0x00, 0x00, 0x0C, 0x03, 0x00, 0x00, 0x52, 0x44, 0x45, 0x46, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x04, 0xFF, 0xFF, 0x00, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x9C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xA5, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xAE, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0xB7, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x72, 0x30, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x72, 0x31, 0x00, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x30, 0x00, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x31, 0x00, 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4C, 0x53, 0x4C, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6F, 0x6D, 0x70, 0x69, 0x6C, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2E, 0x31, 0x00, 0x49, 0x53, 0x47, 0x4E, 0x84, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x53, 0x56, 0x5F, 0x50, 0x4F, 0x53, 0x49, 0x54, 0x49, 0x4F, 0x4E, 0x00, 0x43, 0x4F, 0x4C, 0x4F, 0x52, 0x00, 0x54, 0x45, 0x58, 0x43, 0x4F, 0x4F, 0x52, 0x44, 0x00, 0xAB, 0x4F, 0x53, 0x47, 0x4E, 0x2C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x53, 0x56, 0x5F, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x00, 0xAB, 0xAB, 0x53, 0x48, 0x44, 0x52, 0x20, 0x01, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x03, 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x03, 0x00, 0x60, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x58, 0x18, 0x00, 0x04, 0x00, 0x70, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x58, 0x18, 0x00, 0x04, 0x00, 0x70, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0xF2, 0x10, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0x32, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0xC2, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x03, 0xF2, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x09, 0xF2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x7E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x07, 0x82, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x10, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x09, 0xF2, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0xE6, 0x1A, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x7E, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x60, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x07, 0x82, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x09, 0x72, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x12, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x01, 0x53, 0x54, 0x41, 0x54, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; long long multi_texture_add_ps_size = 904; ================================================ FILE: src/engine/renderer/shaders/hlsl_compiled/multi_texture_clipping_plane_vs.cpp ================================================ unsigned char multi_texture_clipping_plane_vs[] = { 0x44, 0x58, 0x42, 0x43, 0xF1, 0x59, 0xAC, 0x11, 0xC8, 0x58, 0x63, 0x10, 0x19, 0x87, 0x4B, 0xD7, 0xC4, 0x11, 0x7E, 0xBF, 0x01, 0x00, 0x00, 0x00, 0x3C, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x70, 0x01, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x00, 0xAC, 0x02, 0x00, 0x00, 0xC0, 0x04, 0x00, 0x00, 0x52, 0x44, 0x45, 0x46, 0x34, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x04, 0xFE, 0xFF, 0x00, 0x01, 0x00, 0x00, 0x0C, 0x01, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x43, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74, 0x73, 0x00, 0xAB, 0xAB, 0x3C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEC, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x6C, 0x69, 0x70, 0x5F, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5F, 0x78, 0x66, 0x6F, 0x72, 0x6D, 0x00, 0xAB, 0xAB, 0xAB, 0x03, 0x00, 0x03, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x79, 0x65, 0x5F, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5F, 0x78, 0x66, 0x6F, 0x72, 0x6D, 0x00, 0x03, 0x00, 0x03, 0x00, 0x04, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x6C, 0x69, 0x70, 0x70, 0x69, 0x6E, 0x67, 0x5F, 0x70, 0x6C, 0x61, 0x6E, 0x65, 0x00, 0xAB, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4C, 0x53, 0x4C, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6F, 0x6D, 0x70, 0x69, 0x6C, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2E, 0x31, 0x00, 0x49, 0x53, 0x47, 0x4E, 0x80, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x50, 0x4F, 0x53, 0x49, 0x54, 0x49, 0x4F, 0x4E, 0x00, 0x43, 0x4F, 0x4C, 0x4F, 0x52, 0x00, 0x54, 0x45, 0x58, 0x43, 0x4F, 0x4F, 0x52, 0x44, 0x00, 0x4F, 0x53, 0x47, 0x4E, 0xAC, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x8C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x0C, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0C, 0x03, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x0E, 0x00, 0x00, 0x53, 0x56, 0x5F, 0x50, 0x4F, 0x53, 0x49, 0x54, 0x49, 0x4F, 0x4E, 0x00, 0x43, 0x4F, 0x4C, 0x4F, 0x52, 0x00, 0x54, 0x45, 0x58, 0x43, 0x4F, 0x4F, 0x52, 0x44, 0x00, 0x53, 0x56, 0x5F, 0x43, 0x6C, 0x69, 0x70, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6E, 0x63, 0x65, 0x00, 0xAB, 0x53, 0x48, 0x44, 0x52, 0x0C, 0x02, 0x00, 0x00, 0x40, 0x00, 0x01, 0x00, 0x83, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x04, 0x46, 0x8E, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x03, 0xF2, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x03, 0xF2, 0x10, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x03, 0x32, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x03, 0x32, 0x10, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x04, 0xF2, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x03, 0xF2, 0x20, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x03, 0x32, 0x20, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x03, 0xC2, 0x20, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x04, 0x12, 0x20, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x08, 0xF2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x15, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x8E, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x0A, 0xF2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x8E, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x0A, 0xF2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x8E, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xA6, 0x1A, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x0A, 0xF2, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x8E, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF6, 0x1F, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x05, 0xF2, 0x20, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x46, 0x1E, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x05, 0x32, 0x20, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x05, 0xC2, 0x20, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x14, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x08, 0x12, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x1E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x8E, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x08, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x1E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x8E, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x08, 0x42, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x1E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x8E, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x12, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x82, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x12, 0x20, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x80, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x01, 0x53, 0x54, 0x41, 0x54, 0x74, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; long long multi_texture_clipping_plane_vs_size = 1340; ================================================ FILE: src/engine/renderer/shaders/hlsl_compiled/multi_texture_mul_ge80_ps.cpp ================================================ unsigned char multi_texture_mul_ge80_ps[] = { 0x44, 0x58, 0x42, 0x43, 0xB4, 0x1D, 0x86, 0x5A, 0x10, 0x10, 0x46, 0x4B, 0x46, 0xFB, 0xB7, 0x04, 0x73, 0x85, 0xC8, 0xF5, 0x01, 0x00, 0x00, 0x00, 0xA0, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x24, 0x01, 0x00, 0x00, 0xB0, 0x01, 0x00, 0x00, 0xE4, 0x01, 0x00, 0x00, 0x24, 0x03, 0x00, 0x00, 0x52, 0x44, 0x45, 0x46, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x04, 0xFF, 0xFF, 0x00, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x9C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xA5, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xAE, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0xB7, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x72, 0x30, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x72, 0x31, 0x00, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x30, 0x00, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x31, 0x00, 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4C, 0x53, 0x4C, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6F, 0x6D, 0x70, 0x69, 0x6C, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2E, 0x31, 0x00, 0x49, 0x53, 0x47, 0x4E, 0x84, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x53, 0x56, 0x5F, 0x50, 0x4F, 0x53, 0x49, 0x54, 0x49, 0x4F, 0x4E, 0x00, 0x43, 0x4F, 0x4C, 0x4F, 0x52, 0x00, 0x54, 0x45, 0x58, 0x43, 0x4F, 0x4F, 0x52, 0x44, 0x00, 0xAB, 0x4F, 0x53, 0x47, 0x4E, 0x2C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x53, 0x56, 0x5F, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x00, 0xAB, 0xAB, 0x53, 0x48, 0x44, 0x52, 0x38, 0x01, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x03, 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x03, 0x00, 0x60, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x58, 0x18, 0x00, 0x04, 0x00, 0x70, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x58, 0x18, 0x00, 0x04, 0x00, 0x70, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0xF2, 0x10, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0x32, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0xC2, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x03, 0xF2, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x09, 0xF2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x7E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x07, 0xF2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x1E, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x09, 0xF2, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0xE6, 0x1A, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x7E, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x60, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x07, 0xF2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0E, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x07, 0x12, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x0D, 0x00, 0x04, 0x03, 0x0A, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x05, 0xF2, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x01, 0x53, 0x54, 0x41, 0x54, 0x74, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; long long multi_texture_mul_ge80_ps_size = 928; ================================================ FILE: src/engine/renderer/shaders/hlsl_compiled/multi_texture_mul_gt0_ps.cpp ================================================ unsigned char multi_texture_mul_gt0_ps[] = { 0x44, 0x58, 0x42, 0x43, 0x26, 0x5B, 0x38, 0x4E, 0xB1, 0x89, 0x0B, 0x4A, 0x5F, 0x1D, 0x8F, 0x1F, 0x8E, 0xD9, 0x81, 0x22, 0x01, 0x00, 0x00, 0x00, 0xA0, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x24, 0x01, 0x00, 0x00, 0xB0, 0x01, 0x00, 0x00, 0xE4, 0x01, 0x00, 0x00, 0x24, 0x03, 0x00, 0x00, 0x52, 0x44, 0x45, 0x46, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x04, 0xFF, 0xFF, 0x00, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x9C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xA5, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xAE, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0xB7, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x72, 0x30, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x72, 0x31, 0x00, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x30, 0x00, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x31, 0x00, 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4C, 0x53, 0x4C, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6F, 0x6D, 0x70, 0x69, 0x6C, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2E, 0x31, 0x00, 0x49, 0x53, 0x47, 0x4E, 0x84, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x53, 0x56, 0x5F, 0x50, 0x4F, 0x53, 0x49, 0x54, 0x49, 0x4F, 0x4E, 0x00, 0x43, 0x4F, 0x4C, 0x4F, 0x52, 0x00, 0x54, 0x45, 0x58, 0x43, 0x4F, 0x4F, 0x52, 0x44, 0x00, 0xAB, 0x4F, 0x53, 0x47, 0x4E, 0x2C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x53, 0x56, 0x5F, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x00, 0xAB, 0xAB, 0x53, 0x48, 0x44, 0x52, 0x38, 0x01, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x03, 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x03, 0x00, 0x60, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x58, 0x18, 0x00, 0x04, 0x00, 0x70, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x58, 0x18, 0x00, 0x04, 0x00, 0x70, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0xF2, 0x10, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0x32, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0xC2, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x03, 0xF2, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x09, 0xF2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x7E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x07, 0xF2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x1E, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x09, 0xF2, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0xE6, 0x1A, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x7E, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x60, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x07, 0xF2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0E, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x07, 0x12, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x04, 0x03, 0x0A, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x05, 0xF2, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x01, 0x53, 0x54, 0x41, 0x54, 0x74, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; long long multi_texture_mul_gt0_ps_size = 928; ================================================ FILE: src/engine/renderer/shaders/hlsl_compiled/multi_texture_mul_lt80_ps.cpp ================================================ unsigned char multi_texture_mul_lt80_ps[] = { 0x44, 0x58, 0x42, 0x43, 0x64, 0x9F, 0xD8, 0x94, 0x47, 0xF5, 0x30, 0xE4, 0x04, 0x30, 0x47, 0xB3, 0xEF, 0xC3, 0xAE, 0xC1, 0x01, 0x00, 0x00, 0x00, 0xA0, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x24, 0x01, 0x00, 0x00, 0xB0, 0x01, 0x00, 0x00, 0xE4, 0x01, 0x00, 0x00, 0x24, 0x03, 0x00, 0x00, 0x52, 0x44, 0x45, 0x46, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x04, 0xFF, 0xFF, 0x00, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x9C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xA5, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xAE, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0xB7, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x72, 0x30, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x72, 0x31, 0x00, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x30, 0x00, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x31, 0x00, 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4C, 0x53, 0x4C, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6F, 0x6D, 0x70, 0x69, 0x6C, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2E, 0x31, 0x00, 0x49, 0x53, 0x47, 0x4E, 0x84, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x53, 0x56, 0x5F, 0x50, 0x4F, 0x53, 0x49, 0x54, 0x49, 0x4F, 0x4E, 0x00, 0x43, 0x4F, 0x4C, 0x4F, 0x52, 0x00, 0x54, 0x45, 0x58, 0x43, 0x4F, 0x4F, 0x52, 0x44, 0x00, 0xAB, 0x4F, 0x53, 0x47, 0x4E, 0x2C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x53, 0x56, 0x5F, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x00, 0xAB, 0xAB, 0x53, 0x48, 0x44, 0x52, 0x38, 0x01, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x03, 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x03, 0x00, 0x60, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x58, 0x18, 0x00, 0x04, 0x00, 0x70, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x58, 0x18, 0x00, 0x04, 0x00, 0x70, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0xF2, 0x10, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0x32, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0xC2, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x03, 0xF2, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x09, 0xF2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x7E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x07, 0xF2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x1E, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x09, 0xF2, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0xE6, 0x1A, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x7E, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x60, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x07, 0xF2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0E, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x07, 0x12, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x0D, 0x00, 0x04, 0x03, 0x0A, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x05, 0xF2, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x01, 0x53, 0x54, 0x41, 0x54, 0x74, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; long long multi_texture_mul_lt80_ps_size = 928; ================================================ FILE: src/engine/renderer/shaders/hlsl_compiled/multi_texture_mul_ps.cpp ================================================ unsigned char multi_texture_mul_ps[] = { 0x44, 0x58, 0x42, 0x43, 0x50, 0x19, 0x9C, 0x2F, 0x2D, 0xEE, 0xD3, 0xC4, 0xEA, 0x2F, 0x4A, 0x49, 0x6E, 0x96, 0x6E, 0xF8, 0x01, 0x00, 0x00, 0x00, 0x64, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x24, 0x01, 0x00, 0x00, 0xB0, 0x01, 0x00, 0x00, 0xE4, 0x01, 0x00, 0x00, 0xE8, 0x02, 0x00, 0x00, 0x52, 0x44, 0x45, 0x46, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x04, 0xFF, 0xFF, 0x00, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x9C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xA5, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xAE, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0xB7, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x72, 0x30, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x72, 0x31, 0x00, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x30, 0x00, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x31, 0x00, 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4C, 0x53, 0x4C, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6F, 0x6D, 0x70, 0x69, 0x6C, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2E, 0x31, 0x00, 0x49, 0x53, 0x47, 0x4E, 0x84, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x53, 0x56, 0x5F, 0x50, 0x4F, 0x53, 0x49, 0x54, 0x49, 0x4F, 0x4E, 0x00, 0x43, 0x4F, 0x4C, 0x4F, 0x52, 0x00, 0x54, 0x45, 0x58, 0x43, 0x4F, 0x4F, 0x52, 0x44, 0x00, 0xAB, 0x4F, 0x53, 0x47, 0x4E, 0x2C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x53, 0x56, 0x5F, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x00, 0xAB, 0xAB, 0x53, 0x48, 0x44, 0x52, 0xFC, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x03, 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x03, 0x00, 0x60, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x58, 0x18, 0x00, 0x04, 0x00, 0x70, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x58, 0x18, 0x00, 0x04, 0x00, 0x70, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0xF2, 0x10, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0x32, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0xC2, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x03, 0xF2, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x09, 0xF2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x7E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x07, 0xF2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x1E, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x09, 0xF2, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0xE6, 0x1A, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x7E, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x60, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x07, 0xF2, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0E, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x01, 0x53, 0x54, 0x41, 0x54, 0x74, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; long long multi_texture_mul_ps_size = 868; ================================================ FILE: src/engine/renderer/shaders/hlsl_compiled/multi_texture_vs.cpp ================================================ unsigned char multi_texture_vs[] = { 0x44, 0x58, 0x42, 0x43, 0x17, 0xD0, 0x46, 0xDC, 0xB7, 0x33, 0x49, 0x2C, 0x96, 0x9D, 0x70, 0xBB, 0x8A, 0xE5, 0x18, 0x13, 0x01, 0x00, 0x00, 0x00, 0x64, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x70, 0x01, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x00, 0x84, 0x02, 0x00, 0x00, 0xE8, 0x03, 0x00, 0x00, 0x52, 0x44, 0x45, 0x46, 0x34, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x04, 0xFE, 0xFF, 0x00, 0x01, 0x00, 0x00, 0x0C, 0x01, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x43, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74, 0x73, 0x00, 0xAB, 0xAB, 0x3C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEC, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x6C, 0x69, 0x70, 0x5F, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5F, 0x78, 0x66, 0x6F, 0x72, 0x6D, 0x00, 0xAB, 0xAB, 0xAB, 0x03, 0x00, 0x03, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x79, 0x65, 0x5F, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5F, 0x78, 0x66, 0x6F, 0x72, 0x6D, 0x00, 0x03, 0x00, 0x03, 0x00, 0x04, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x6C, 0x69, 0x70, 0x70, 0x69, 0x6E, 0x67, 0x5F, 0x70, 0x6C, 0x61, 0x6E, 0x65, 0x00, 0xAB, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4C, 0x53, 0x4C, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6F, 0x6D, 0x70, 0x69, 0x6C, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2E, 0x31, 0x00, 0x49, 0x53, 0x47, 0x4E, 0x80, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x50, 0x4F, 0x53, 0x49, 0x54, 0x49, 0x4F, 0x4E, 0x00, 0x43, 0x4F, 0x4C, 0x4F, 0x52, 0x00, 0x54, 0x45, 0x58, 0x43, 0x4F, 0x4F, 0x52, 0x44, 0x00, 0x4F, 0x53, 0x47, 0x4E, 0x84, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x0C, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0C, 0x03, 0x00, 0x00, 0x53, 0x56, 0x5F, 0x50, 0x4F, 0x53, 0x49, 0x54, 0x49, 0x4F, 0x4E, 0x00, 0x43, 0x4F, 0x4C, 0x4F, 0x52, 0x00, 0x54, 0x45, 0x58, 0x43, 0x4F, 0x4F, 0x52, 0x44, 0x00, 0xAB, 0x53, 0x48, 0x44, 0x52, 0x5C, 0x01, 0x00, 0x00, 0x40, 0x00, 0x01, 0x00, 0x57, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x04, 0x46, 0x8E, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x03, 0xF2, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x03, 0xF2, 0x10, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x03, 0x32, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x03, 0x32, 0x10, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x04, 0xF2, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x03, 0xF2, 0x20, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x03, 0x32, 0x20, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x03, 0xC2, 0x20, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x08, 0xF2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x15, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x8E, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x0A, 0xF2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x8E, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x0A, 0xF2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x8E, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xA6, 0x1A, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x0A, 0xF2, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x8E, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF6, 0x1F, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x05, 0xF2, 0x20, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x46, 0x1E, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x05, 0x32, 0x20, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x05, 0xC2, 0x20, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x14, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x01, 0x53, 0x54, 0x41, 0x54, 0x74, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; long long multi_texture_vs_size = 1124; ================================================ FILE: src/engine/renderer/shaders/hlsl_compiled/single_texture_clipping_plane_vs.cpp ================================================ unsigned char single_texture_clipping_plane_vs[] = { 0x44, 0x58, 0x42, 0x43, 0x18, 0xE4, 0x5E, 0x68, 0xE6, 0xA1, 0xD6, 0xB0, 0x1E, 0xC3, 0x9A, 0x71, 0x82, 0xE8, 0x0B, 0x54, 0x01, 0x00, 0x00, 0x00, 0xE0, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x70, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x7C, 0x02, 0x00, 0x00, 0x64, 0x04, 0x00, 0x00, 0x52, 0x44, 0x45, 0x46, 0x34, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x04, 0xFE, 0xFF, 0x00, 0x01, 0x00, 0x00, 0x0C, 0x01, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x43, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74, 0x73, 0x00, 0xAB, 0xAB, 0x3C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEC, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x6C, 0x69, 0x70, 0x5F, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5F, 0x78, 0x66, 0x6F, 0x72, 0x6D, 0x00, 0xAB, 0xAB, 0xAB, 0x03, 0x00, 0x03, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x79, 0x65, 0x5F, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5F, 0x78, 0x66, 0x6F, 0x72, 0x6D, 0x00, 0x03, 0x00, 0x03, 0x00, 0x04, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x6C, 0x69, 0x70, 0x70, 0x69, 0x6E, 0x67, 0x5F, 0x70, 0x6C, 0x61, 0x6E, 0x65, 0x00, 0xAB, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4C, 0x53, 0x4C, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6F, 0x6D, 0x70, 0x69, 0x6C, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2E, 0x31, 0x00, 0x49, 0x53, 0x47, 0x4E, 0x68, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x50, 0x4F, 0x53, 0x49, 0x54, 0x49, 0x4F, 0x4E, 0x00, 0x43, 0x4F, 0x4C, 0x4F, 0x52, 0x00, 0x54, 0x45, 0x58, 0x43, 0x4F, 0x4F, 0x52, 0x44, 0x00, 0x4F, 0x53, 0x47, 0x4E, 0x94, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x0C, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x0E, 0x00, 0x00, 0x53, 0x56, 0x5F, 0x50, 0x4F, 0x53, 0x49, 0x54, 0x49, 0x4F, 0x4E, 0x00, 0x43, 0x4F, 0x4C, 0x4F, 0x52, 0x00, 0x54, 0x45, 0x58, 0x43, 0x4F, 0x4F, 0x52, 0x44, 0x00, 0x53, 0x56, 0x5F, 0x43, 0x6C, 0x69, 0x70, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6E, 0x63, 0x65, 0x00, 0xAB, 0x53, 0x48, 0x44, 0x52, 0xE0, 0x01, 0x00, 0x00, 0x40, 0x00, 0x01, 0x00, 0x78, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x04, 0x46, 0x8E, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x03, 0xF2, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x03, 0xF2, 0x10, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x03, 0x32, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x04, 0xF2, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x03, 0xF2, 0x20, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x03, 0x32, 0x20, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x04, 0x12, 0x20, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x08, 0xF2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x15, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x8E, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x0A, 0xF2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x8E, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x0A, 0xF2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x8E, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xA6, 0x1A, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x0A, 0xF2, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x8E, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF6, 0x1F, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x05, 0xF2, 0x20, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x46, 0x1E, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x05, 0x32, 0x20, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x08, 0x12, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x1E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x8E, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x08, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x1E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x8E, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x08, 0x42, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x1E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x8E, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x12, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x82, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x12, 0x20, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x80, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x01, 0x53, 0x54, 0x41, 0x54, 0x74, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; long long single_texture_clipping_plane_vs_size = 1248; ================================================ FILE: src/engine/renderer/shaders/hlsl_compiled/single_texture_ge80_ps.cpp ================================================ unsigned char single_texture_ge80_ps[] = { 0x44, 0x58, 0x42, 0x43, 0xE7, 0xDE, 0xCF, 0x2D, 0xC7, 0xD8, 0xE0, 0x89, 0x9E, 0x1C, 0xA4, 0x45, 0x04, 0xBF, 0x5B, 0x4D, 0x01, 0x00, 0x00, 0x00, 0xD0, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0xD4, 0x00, 0x00, 0x00, 0x48, 0x01, 0x00, 0x00, 0x7C, 0x01, 0x00, 0x00, 0x54, 0x02, 0x00, 0x00, 0x52, 0x44, 0x45, 0x46, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x04, 0xFF, 0xFF, 0x00, 0x01, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x72, 0x30, 0x00, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x30, 0x00, 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4C, 0x53, 0x4C, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6F, 0x6D, 0x70, 0x69, 0x6C, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2E, 0x31, 0x00, 0xAB, 0xAB, 0x49, 0x53, 0x47, 0x4E, 0x6C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x53, 0x56, 0x5F, 0x50, 0x4F, 0x53, 0x49, 0x54, 0x49, 0x4F, 0x4E, 0x00, 0x43, 0x4F, 0x4C, 0x4F, 0x52, 0x00, 0x54, 0x45, 0x58, 0x43, 0x4F, 0x4F, 0x52, 0x44, 0x00, 0xAB, 0x4F, 0x53, 0x47, 0x4E, 0x2C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x53, 0x56, 0x5F, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x00, 0xAB, 0xAB, 0x53, 0x48, 0x44, 0x52, 0xD0, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x03, 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x18, 0x00, 0x04, 0x00, 0x70, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0xF2, 0x10, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0x32, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x03, 0xF2, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x09, 0xF2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x7E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x07, 0xF2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x1E, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x07, 0x12, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x0D, 0x00, 0x04, 0x03, 0x0A, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x05, 0xF2, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x01, 0x53, 0x54, 0x41, 0x54, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; long long single_texture_ge80_ps_size = 720; ================================================ FILE: src/engine/renderer/shaders/hlsl_compiled/single_texture_gt0_ps.cpp ================================================ unsigned char single_texture_gt0_ps[] = { 0x44, 0x58, 0x42, 0x43, 0xB0, 0xD6, 0x4F, 0x95, 0xC6, 0xA4, 0x6B, 0x65, 0x14, 0x39, 0x26, 0xF3, 0x40, 0xEA, 0x31, 0x47, 0x01, 0x00, 0x00, 0x00, 0xD0, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0xD4, 0x00, 0x00, 0x00, 0x48, 0x01, 0x00, 0x00, 0x7C, 0x01, 0x00, 0x00, 0x54, 0x02, 0x00, 0x00, 0x52, 0x44, 0x45, 0x46, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x04, 0xFF, 0xFF, 0x00, 0x01, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x72, 0x30, 0x00, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x30, 0x00, 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4C, 0x53, 0x4C, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6F, 0x6D, 0x70, 0x69, 0x6C, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2E, 0x31, 0x00, 0xAB, 0xAB, 0x49, 0x53, 0x47, 0x4E, 0x6C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x53, 0x56, 0x5F, 0x50, 0x4F, 0x53, 0x49, 0x54, 0x49, 0x4F, 0x4E, 0x00, 0x43, 0x4F, 0x4C, 0x4F, 0x52, 0x00, 0x54, 0x45, 0x58, 0x43, 0x4F, 0x4F, 0x52, 0x44, 0x00, 0xAB, 0x4F, 0x53, 0x47, 0x4E, 0x2C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x53, 0x56, 0x5F, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x00, 0xAB, 0xAB, 0x53, 0x48, 0x44, 0x52, 0xD0, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x03, 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x18, 0x00, 0x04, 0x00, 0x70, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0xF2, 0x10, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0x32, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x03, 0xF2, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x09, 0xF2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x7E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x07, 0xF2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x1E, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x07, 0x12, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x04, 0x03, 0x0A, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x05, 0xF2, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x01, 0x53, 0x54, 0x41, 0x54, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; long long single_texture_gt0_ps_size = 720; ================================================ FILE: src/engine/renderer/shaders/hlsl_compiled/single_texture_lt80_ps.cpp ================================================ unsigned char single_texture_lt80_ps[] = { 0x44, 0x58, 0x42, 0x43, 0xAF, 0x67, 0xD3, 0x17, 0x36, 0xC1, 0x86, 0xDD, 0x1E, 0xFC, 0xEF, 0x7A, 0x35, 0x8B, 0x10, 0xAB, 0x01, 0x00, 0x00, 0x00, 0xD0, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0xD4, 0x00, 0x00, 0x00, 0x48, 0x01, 0x00, 0x00, 0x7C, 0x01, 0x00, 0x00, 0x54, 0x02, 0x00, 0x00, 0x52, 0x44, 0x45, 0x46, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x04, 0xFF, 0xFF, 0x00, 0x01, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x72, 0x30, 0x00, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x30, 0x00, 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4C, 0x53, 0x4C, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6F, 0x6D, 0x70, 0x69, 0x6C, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2E, 0x31, 0x00, 0xAB, 0xAB, 0x49, 0x53, 0x47, 0x4E, 0x6C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x53, 0x56, 0x5F, 0x50, 0x4F, 0x53, 0x49, 0x54, 0x49, 0x4F, 0x4E, 0x00, 0x43, 0x4F, 0x4C, 0x4F, 0x52, 0x00, 0x54, 0x45, 0x58, 0x43, 0x4F, 0x4F, 0x52, 0x44, 0x00, 0xAB, 0x4F, 0x53, 0x47, 0x4E, 0x2C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x53, 0x56, 0x5F, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x00, 0xAB, 0xAB, 0x53, 0x48, 0x44, 0x52, 0xD0, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x03, 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x18, 0x00, 0x04, 0x00, 0x70, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0xF2, 0x10, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0x32, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x03, 0xF2, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x09, 0xF2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x7E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x07, 0xF2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x1E, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x07, 0x12, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x0D, 0x00, 0x04, 0x03, 0x0A, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x05, 0xF2, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x01, 0x53, 0x54, 0x41, 0x54, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; long long single_texture_lt80_ps_size = 720; ================================================ FILE: src/engine/renderer/shaders/hlsl_compiled/single_texture_ps.cpp ================================================ unsigned char single_texture_ps[] = { 0x44, 0x58, 0x42, 0x43, 0x70, 0x57, 0xC0, 0xFF, 0x10, 0xA3, 0x7E, 0x94, 0x79, 0xEA, 0x49, 0xFE, 0x49, 0xED, 0x5A, 0x36, 0x01, 0x00, 0x00, 0x00, 0x94, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0xD4, 0x00, 0x00, 0x00, 0x48, 0x01, 0x00, 0x00, 0x7C, 0x01, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x52, 0x44, 0x45, 0x46, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x04, 0xFF, 0xFF, 0x00, 0x01, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x72, 0x30, 0x00, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x30, 0x00, 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4C, 0x53, 0x4C, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6F, 0x6D, 0x70, 0x69, 0x6C, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2E, 0x31, 0x00, 0xAB, 0xAB, 0x49, 0x53, 0x47, 0x4E, 0x6C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x53, 0x56, 0x5F, 0x50, 0x4F, 0x53, 0x49, 0x54, 0x49, 0x4F, 0x4E, 0x00, 0x43, 0x4F, 0x4C, 0x4F, 0x52, 0x00, 0x54, 0x45, 0x58, 0x43, 0x4F, 0x4F, 0x52, 0x44, 0x00, 0xAB, 0x4F, 0x53, 0x47, 0x4E, 0x2C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x53, 0x56, 0x5F, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x00, 0xAB, 0xAB, 0x53, 0x48, 0x44, 0x52, 0x94, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x03, 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x18, 0x00, 0x04, 0x00, 0x70, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0xF2, 0x10, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0x32, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x03, 0xF2, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x09, 0xF2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x7E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x07, 0xF2, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x1E, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x01, 0x53, 0x54, 0x41, 0x54, 0x74, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; long long single_texture_ps_size = 660; ================================================ FILE: src/engine/renderer/shaders/hlsl_compiled/single_texture_vs.cpp ================================================ unsigned char single_texture_vs[] = { 0x44, 0x58, 0x42, 0x43, 0xBD, 0x59, 0x1D, 0x43, 0x84, 0x50, 0x25, 0xE8, 0x09, 0x04, 0x7B, 0x8B, 0x15, 0x47, 0xA1, 0x0E, 0x01, 0x00, 0x00, 0x00, 0x08, 0x04, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x70, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x54, 0x02, 0x00, 0x00, 0x8C, 0x03, 0x00, 0x00, 0x52, 0x44, 0x45, 0x46, 0x34, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x04, 0xFE, 0xFF, 0x00, 0x01, 0x00, 0x00, 0x0C, 0x01, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x43, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74, 0x73, 0x00, 0xAB, 0xAB, 0x3C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEC, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x6C, 0x69, 0x70, 0x5F, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5F, 0x78, 0x66, 0x6F, 0x72, 0x6D, 0x00, 0xAB, 0xAB, 0xAB, 0x03, 0x00, 0x03, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x79, 0x65, 0x5F, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5F, 0x78, 0x66, 0x6F, 0x72, 0x6D, 0x00, 0x03, 0x00, 0x03, 0x00, 0x04, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x6C, 0x69, 0x70, 0x70, 0x69, 0x6E, 0x67, 0x5F, 0x70, 0x6C, 0x61, 0x6E, 0x65, 0x00, 0xAB, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4C, 0x53, 0x4C, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6F, 0x6D, 0x70, 0x69, 0x6C, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2E, 0x31, 0x00, 0x49, 0x53, 0x47, 0x4E, 0x68, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x50, 0x4F, 0x53, 0x49, 0x54, 0x49, 0x4F, 0x4E, 0x00, 0x43, 0x4F, 0x4C, 0x4F, 0x52, 0x00, 0x54, 0x45, 0x58, 0x43, 0x4F, 0x4F, 0x52, 0x44, 0x00, 0x4F, 0x53, 0x47, 0x4E, 0x6C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x0C, 0x00, 0x00, 0x53, 0x56, 0x5F, 0x50, 0x4F, 0x53, 0x49, 0x54, 0x49, 0x4F, 0x4E, 0x00, 0x43, 0x4F, 0x4C, 0x4F, 0x52, 0x00, 0x54, 0x45, 0x58, 0x43, 0x4F, 0x4F, 0x52, 0x44, 0x00, 0xAB, 0x53, 0x48, 0x44, 0x52, 0x30, 0x01, 0x00, 0x00, 0x40, 0x00, 0x01, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x04, 0x46, 0x8E, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x03, 0xF2, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x03, 0xF2, 0x10, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x03, 0x32, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x04, 0xF2, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x03, 0xF2, 0x20, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x03, 0x32, 0x20, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x08, 0xF2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x15, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x8E, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x0A, 0xF2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x8E, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x0A, 0xF2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x8E, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xA6, 0x1A, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x0A, 0xF2, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x8E, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF6, 0x1F, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x05, 0xF2, 0x20, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x46, 0x1E, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x05, 0x32, 0x20, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x01, 0x53, 0x54, 0x41, 0x54, 0x74, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; long long single_texture_vs_size = 1032; ================================================ FILE: src/engine/renderer/shaders/multi_texture.vert ================================================ #version 450 layout(push_constant) uniform Transform { mat4 mvp; }; layout(location = 0) in vec3 in_position; layout(location = 1) in vec4 in_color; layout(location = 2) in vec2 in_tex_coord0; layout(location = 3) in vec2 in_tex_coord1; layout(location = 0) out vec4 frag_color; layout(location = 1) out vec2 frag_tex_coord0; layout(location = 2) out vec2 frag_tex_coord1; out gl_PerVertex { vec4 gl_Position; }; void main() { gl_Position = mvp * vec4(in_position, 1.0); frag_color = in_color; frag_tex_coord0 = in_tex_coord0; frag_tex_coord1 = in_tex_coord1; } ================================================ FILE: src/engine/renderer/shaders/multi_texture_add.frag ================================================ #version 450 layout(set = 0, binding = 0) uniform sampler2D texture0; layout(set = 1, binding = 0) uniform sampler2D texture1; layout(location = 0) in vec4 frag_color; layout(location = 1) in vec2 frag_tex_coord0; layout(location = 2) in vec2 frag_tex_coord1; layout(location = 0) out vec4 out_color; layout (constant_id = 0) const int alpha_test_func = 0; void main() { vec4 color_a = frag_color * texture(texture0, frag_tex_coord0); vec4 color_b = texture(texture1, frag_tex_coord1); out_color = vec4(color_a.rgb + color_b.rgb, color_a.a * color_b.a); if (alpha_test_func == 1) { if (out_color.a == 0.0f) discard; } else if (alpha_test_func == 2) { if (out_color.a >= 0.5f) discard; } else if (alpha_test_func == 3) { if (out_color.a < 0.5f) discard; } } ================================================ FILE: src/engine/renderer/shaders/multi_texture_clipping_plane.vert ================================================ #version 450 // 128 bytes layout(push_constant) uniform Transform { mat4 clip_space_xform; mat3x4 eye_space_xform; vec4 clipping_plane; // in eye space }; layout(location = 0) in vec3 in_position; layout(location = 1) in vec4 in_color; layout(location = 2) in vec2 in_tex_coord0; layout(location = 3) in vec2 in_tex_coord1; layout(location = 0) out vec4 frag_color; layout(location = 1) out vec2 frag_tex_coord0; layout(location = 2) out vec2 frag_tex_coord1; out gl_PerVertex { vec4 gl_Position; float gl_ClipDistance[1]; }; void main() { vec4 p = vec4(in_position, 1.0); gl_Position = clip_space_xform * p; gl_ClipDistance[0] = dot(clipping_plane, vec4(p * eye_space_xform, 1.0)); frag_color = in_color; frag_tex_coord0 = in_tex_coord0; frag_tex_coord1 = in_tex_coord1; } ================================================ FILE: src/engine/renderer/shaders/multi_texture_mul.frag ================================================ #version 450 layout(set = 0, binding = 0) uniform sampler2D texture0; layout(set = 1, binding = 0) uniform sampler2D texture1; layout(location = 0) in vec4 frag_color; layout(location = 1) in vec2 frag_tex_coord0; layout(location = 2) in vec2 frag_tex_coord1; layout(location = 0) out vec4 out_color; layout (constant_id = 0) const int alpha_test_func = 0; void main() { out_color = frag_color * texture(texture0, frag_tex_coord0) * texture(texture1, frag_tex_coord1); if (alpha_test_func == 1) { if (out_color.a == 0.0f) discard; } else if (alpha_test_func == 2) { if (out_color.a >= 0.5f) discard; } else if (alpha_test_func == 3) { if (out_color.a < 0.5f) discard; } } ================================================ FILE: src/engine/renderer/shaders/shaders.hlsl ================================================ struct Single_Texture_PS_Data { float4 position : SV_POSITION; float4 color : COLOR; float2 uv0 : TEXCOORD; }; struct Multi_Texture_PS_Data { float4 position : SV_POSITION; float4 color : COLOR; float2 uv0 : TEXCOORD0; float2 uv1 : TEXCOORD1; }; cbuffer Constants : register(b0) { float4x4 clip_space_xform; float4x3 eye_space_xform; float4 clipping_plane; // in eye space }; Texture2D texture0 : register(t0); SamplerState sampler0 : register(s0); Texture2D texture1 : register(t1); SamplerState sampler1 : register(s1); Single_Texture_PS_Data single_texture_vs( float4 position : POSITION, float4 color : COLOR, float2 uv0 : TEXCOORD) { Single_Texture_PS_Data ps_data; ps_data.position = mul(clip_space_xform, position); ps_data.color = color; ps_data.uv0 = uv0; return ps_data; } Single_Texture_PS_Data single_texture_clipping_plane_vs( float4 position : POSITION, float4 color : COLOR, float2 uv0 : TEXCOORD, out float clip : SV_ClipDistance) { clip = dot(clipping_plane.xyz, mul(position, eye_space_xform)) + clipping_plane.w; Single_Texture_PS_Data ps_data; ps_data.position = mul(clip_space_xform, position); ps_data.color = color; ps_data.uv0 = uv0; return ps_data; } Multi_Texture_PS_Data multi_texture_vs( float4 position : POSITION, float4 color : COLOR, float2 uv0 : TEXCOORD0, float2 uv1 : TEXCOORD1) { Multi_Texture_PS_Data ps_data; ps_data.position = mul(clip_space_xform, position); ps_data.color = color; ps_data.uv0 = uv0; ps_data.uv1 = uv1; return ps_data; } Multi_Texture_PS_Data multi_texture_clipping_plane_vs( float4 position : POSITION, float4 color : COLOR, float2 uv0 : TEXCOORD0, float2 uv1 : TEXCOORD1, out float clip : SV_ClipDistance) { clip = dot(clipping_plane.xyz, mul(position, eye_space_xform)) + clipping_plane.w; Multi_Texture_PS_Data ps_data; ps_data.position = mul(clip_space_xform, position); ps_data.color = color; ps_data.uv0 = uv0; ps_data.uv1 = uv1; return ps_data; } float4 single_texture_ps(Single_Texture_PS_Data data) : SV_TARGET { float4 out_color = data.color * texture0.Sample(sampler0, data.uv0); #if defined(ALPHA_TEST_GT0) if (out_color.a == 0.0f) discard; #elif defined(ALPHA_TEST_LT80) if (out_color.a >= 0.5f) discard; #elif defined(ALPHA_TEST_GE80) if (out_color.a < 0.5f) discard; #endif return out_color; } float4 multi_texture_mul_ps(Multi_Texture_PS_Data data) : SV_TARGET { float4 out_color = data.color * texture0.Sample(sampler0, data.uv0) * texture1.Sample(sampler1, data.uv1); #if defined(ALPHA_TEST_GT0) if (out_color.a == 0.0f) discard; #elif defined(ALPHA_TEST_LT80) if (out_color.a >= 0.5f) discard; #elif defined(ALPHA_TEST_GE80) if (out_color.a < 0.5f) discard; #endif return out_color; } float4 multi_texture_add_ps(Multi_Texture_PS_Data data) : SV_TARGET { float4 color_a = data.color * texture0.Sample(sampler0, data.uv0); float4 color_b = texture1.Sample(sampler1, data.uv1); float4 out_color = float4( color_a.r + color_b.r, color_a.g + color_b.g, color_a.b + color_b.b, color_a.a * color_b.a); #if defined(ALPHA_TEST_GT0) if (out_color.a == 0.0f) discard; #elif defined(ALPHA_TEST_LT80) if (out_color.a >= 0.5f) discard; #elif defined(ALPHA_TEST_GE80) if (out_color.a < 0.5f) discard; #endif return out_color; } ================================================ FILE: src/engine/renderer/shaders/single_texture.frag ================================================ #version 450 layout(set = 0, binding = 0) uniform sampler2D texture0; layout(location = 0) in vec4 frag_color; layout(location = 1) in vec2 frag_tex_coord; layout(location = 0) out vec4 out_color; layout (constant_id = 0) const int alpha_test_func = 0; void main() { out_color = frag_color * texture(texture0, frag_tex_coord); if (alpha_test_func == 1) { if (out_color.a == 0.0f) discard; } else if (alpha_test_func == 2) { if (out_color.a >= 0.5f) discard; } else if (alpha_test_func == 3) { if (out_color.a < 0.5f) discard; } } ================================================ FILE: src/engine/renderer/shaders/single_texture.vert ================================================ #version 450 layout(push_constant) uniform Transform { mat4 mvp; }; layout(location = 0) in vec3 in_position; layout(location = 1) in vec4 in_color; layout(location = 2) in vec2 in_tex_coord; layout(location = 0) out vec4 frag_color; layout(location = 1) out vec2 frag_tex_coord; out gl_PerVertex { vec4 gl_Position; }; void main() { gl_Position = mvp * vec4(in_position, 1.0); frag_color = in_color; frag_tex_coord = in_tex_coord; } ================================================ FILE: src/engine/renderer/shaders/single_texture_clipping_plane.vert ================================================ #version 450 // 128 bytes layout(push_constant) uniform Transform { mat4 clip_space_xform; mat3x4 eye_space_xform; vec4 clipping_plane; // in eye space }; layout(location = 0) in vec3 in_position; layout(location = 1) in vec4 in_color; layout(location = 2) in vec2 in_tex_coord; layout(location = 0) out vec4 frag_color; layout(location = 1) out vec2 frag_tex_coord; out gl_PerVertex { vec4 gl_Position; float gl_ClipDistance[1]; }; void main() { vec4 p = vec4(in_position, 1.0); gl_Position = clip_space_xform * p; gl_ClipDistance[0] = dot(clipping_plane, vec4(p * eye_space_xform, 1.0)); frag_color = in_color; frag_tex_coord = in_tex_coord; } ================================================ FILE: src/engine/renderer/shaders/spirv/apply_gamma_comp.cpp ================================================ unsigned char apply_gamma_comp_spv[] = { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x6D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x38, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x47, 0x4C, 0x5F, 0x45, 0x58, 0x54, 0x5F, 0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x72, 0x6C, 0x65, 0x73, 0x73, 0x5F, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5F, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x61, 0x70, 0x70, 0x6C, 0x79, 0x5F, 0x67, 0x61, 0x6D, 0x6D, 0x61, 0x28, 0x66, 0x31, 0x3B, 0x00, 0x05, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x63, 0x63, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x47, 0x61, 0x6D, 0x6D, 0x61, 0x5F, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x67, 0x61, 0x6D, 0x6D, 0x61, 0x5F, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x26, 0x00, 0x00, 0x00, 0x6C, 0x6F, 0x63, 0x00, 0x05, 0x00, 0x08, 0x00, 0x29, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x47, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x49, 0x6E, 0x76, 0x6F, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x49, 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x33, 0x00, 0x00, 0x00, 0x50, 0x75, 0x73, 0x68, 0x5F, 0x43, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74, 0x73, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6F, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5F, 0x73, 0x69, 0x7A, 0x65, 0x00, 0x06, 0x00, 0x07, 0x00, 0x33, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x69, 0x64, 0x65, 0x6E, 0x74, 0x69, 0x74, 0x79, 0x5F, 0x67, 0x61, 0x6D, 0x6D, 0x61, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x48, 0x00, 0x00, 0x00, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x6F, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5F, 0x69, 0x6D, 0x61, 0x67, 0x65, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x55, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x67, 0x00, 0x00, 0x00, 0x73, 0x77, 0x61, 0x70, 0x63, 0x68, 0x61, 0x69, 0x6E, 0x5F, 0x69, 0x6D, 0x61, 0x67, 0x65, 0x00, 0x47, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x33, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x67, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x67, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x67, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x21, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x43, 0x2B, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1C, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x24, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x25, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x28, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x04, 0x00, 0x33, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x34, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x34, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x36, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x46, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x47, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x49, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x65, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x66, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x06, 0x00, 0x27, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x25, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x47, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x24, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x26, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x36, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0xB0, 0x00, 0x05, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x39, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x36, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xB0, 0x00, 0x05, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x3B, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x3B, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x43, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x44, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x49, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x24, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0x46, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x48, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x36, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x05, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x52, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x53, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x55, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x39, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x59, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x39, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x39, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x54, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x54, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x65, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x24, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x46, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x63, 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x45, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x45, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x02, 0x00, 0x21, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00 }; long long apply_gamma_comp_spv_size = 2852; ================================================ FILE: src/engine/renderer/shaders/spirv/multi_texture_add_frag.cpp ================================================ unsigned char multi_texture_add_frag_spv[] = { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x09, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x5F, 0x61, 0x00, 0x05, 0x00, 0x05, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x66, 0x72, 0x61, 0x67, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x30, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x14, 0x00, 0x00, 0x00, 0x66, 0x72, 0x61, 0x67, 0x5F, 0x74, 0x65, 0x78, 0x5F, 0x63, 0x6F, 0x6F, 0x72, 0x64, 0x30, 0x00, 0x05, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x5F, 0x62, 0x00, 0x05, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x31, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x66, 0x72, 0x61, 0x67, 0x5F, 0x74, 0x65, 0x78, 0x5F, 0x63, 0x6F, 0x6F, 0x72, 0x64, 0x31, 0x00, 0x05, 0x00, 0x05, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x6F, 0x75, 0x74, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x33, 0x00, 0x00, 0x00, 0x61, 0x6C, 0x70, 0x68, 0x61, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x5F, 0x66, 0x75, 0x6E, 0x63, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x33, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x26, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x26, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x28, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x32, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x32, 0x00, 0x04, 0x00, 0x32, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x32, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, 0x35, 0x00, 0x00, 0x00, 0x34, 0x00, 0x06, 0x00, 0x35, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x39, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x32, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x34, 0x00, 0x06, 0x00, 0x35, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x2B, 0x00, 0x04, 0x00, 0x32, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x34, 0x00, 0x06, 0x00, 0x35, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x57, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x57, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x08, 0x00, 0x20, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x08, 0x00, 0x20, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x20, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x28, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x28, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x36, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x37, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x39, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x05, 0x00, 0x35, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x3E, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x01, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x3F, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x38, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x41, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x43, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x44, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x39, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0xBE, 0x00, 0x05, 0x00, 0x35, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x49, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x4A, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x01, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x4B, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x45, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x4D, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x50, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x39, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x05, 0x00, 0x35, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x54, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x55, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x01, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x56, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x51, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x51, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x45, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x45, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x38, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x38, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00 }; long long multi_texture_add_frag_spv_size = 2068; ================================================ FILE: src/engine/renderer/shaders/spirv/multi_texture_clipping_plane_vert.cpp ================================================ unsigned char multi_texture_clipping_plane_vert_spv[] = { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x20, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x70, 0x6F, 0x73, 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x00, 0x05, 0x00, 0x06, 0x00, 0x16, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50, 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50, 0x6F, 0x73, 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x00, 0x06, 0x00, 0x07, 0x00, 0x16, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x43, 0x6C, 0x69, 0x70, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6E, 0x63, 0x65, 0x00, 0x05, 0x00, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x54, 0x72, 0x61, 0x6E, 0x73, 0x66, 0x6F, 0x72, 0x6D, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x6C, 0x69, 0x70, 0x5F, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5F, 0x78, 0x66, 0x6F, 0x72, 0x6D, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x65, 0x79, 0x65, 0x5F, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5F, 0x78, 0x66, 0x6F, 0x72, 0x6D, 0x00, 0x06, 0x00, 0x07, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x63, 0x6C, 0x69, 0x70, 0x70, 0x69, 0x6E, 0x67, 0x5F, 0x70, 0x6C, 0x61, 0x6E, 0x65, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x38, 0x00, 0x00, 0x00, 0x66, 0x72, 0x61, 0x67, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x66, 0x72, 0x61, 0x67, 0x5F, 0x74, 0x65, 0x78, 0x5F, 0x63, 0x6F, 0x6F, 0x72, 0x64, 0x30, 0x00, 0x05, 0x00, 0x06, 0x00, 0x40, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x74, 0x65, 0x78, 0x5F, 0x63, 0x6F, 0x6F, 0x72, 0x64, 0x30, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x42, 0x00, 0x00, 0x00, 0x66, 0x72, 0x61, 0x67, 0x5F, 0x74, 0x65, 0x78, 0x5F, 0x63, 0x6F, 0x6F, 0x72, 0x64, 0x31, 0x00, 0x05, 0x00, 0x06, 0x00, 0x43, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x74, 0x65, 0x78, 0x5F, 0x63, 0x6F, 0x6F, 0x72, 0x64, 0x31, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x04, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x48, 0x00, 0x04, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x38, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x42, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x43, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x15, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x04, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x18, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x05, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x25, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x36, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x25, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x39, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x39, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x20, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x91, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x25, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x26, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x29, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x90, 0x00, 0x05, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x36, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x37, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x38, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x42, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00 }; long long multi_texture_clipping_plane_vert_spv_size = 2056; ================================================ FILE: src/engine/renderer/shaders/spirv/multi_texture_mul_frag.cpp ================================================ unsigned char multi_texture_mul_frag_spv[] = { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x09, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x6F, 0x75, 0x74, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x66, 0x72, 0x61, 0x67, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x30, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x14, 0x00, 0x00, 0x00, 0x66, 0x72, 0x61, 0x67, 0x5F, 0x74, 0x65, 0x78, 0x5F, 0x63, 0x6F, 0x6F, 0x72, 0x64, 0x30, 0x00, 0x05, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x31, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x66, 0x72, 0x61, 0x67, 0x5F, 0x74, 0x65, 0x78, 0x5F, 0x63, 0x6F, 0x6F, 0x72, 0x64, 0x31, 0x00, 0x05, 0x00, 0x06, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x61, 0x6C, 0x70, 0x68, 0x61, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x5F, 0x66, 0x75, 0x6E, 0x63, 0x00, 0x47, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x32, 0x00, 0x04, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, 0x21, 0x00, 0x00, 0x00, 0x34, 0x00, 0x06, 0x00, 0x21, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x25, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x25, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x34, 0x00, 0x06, 0x00, 0x21, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x2B, 0x00, 0x04, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x34, 0x00, 0x06, 0x00, 0x21, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x57, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x57, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x23, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x27, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x05, 0x00, 0x21, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x2C, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x01, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x2D, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x24, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x2F, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x31, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x32, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x27, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0xBE, 0x00, 0x05, 0x00, 0x21, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x37, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x38, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x01, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x39, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x33, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x3B, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x27, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x05, 0x00, 0x21, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x42, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x43, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x01, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x44, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x3F, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x3F, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x33, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x33, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x24, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x24, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00 }; long long multi_texture_mul_frag_spv_size = 1656; ================================================ FILE: src/engine/renderer/shaders/spirv/multi_texture_vert.cpp ================================================ unsigned char multi_texture_vert_spv[] = { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50, 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50, 0x6F, 0x73, 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x00, 0x05, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x54, 0x72, 0x61, 0x6E, 0x73, 0x66, 0x6F, 0x72, 0x6D, 0x00, 0x00, 0x00, 0x06, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6D, 0x76, 0x70, 0x00, 0x05, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x70, 0x6F, 0x73, 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x00, 0x05, 0x00, 0x05, 0x00, 0x20, 0x00, 0x00, 0x00, 0x66, 0x72, 0x61, 0x67, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x22, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x26, 0x00, 0x00, 0x00, 0x66, 0x72, 0x61, 0x67, 0x5F, 0x74, 0x65, 0x78, 0x5F, 0x63, 0x6F, 0x6F, 0x72, 0x64, 0x30, 0x00, 0x05, 0x00, 0x06, 0x00, 0x28, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x74, 0x65, 0x78, 0x5F, 0x63, 0x6F, 0x6F, 0x72, 0x64, 0x30, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x66, 0x72, 0x61, 0x67, 0x5F, 0x74, 0x65, 0x78, 0x5F, 0x63, 0x6F, 0x6F, 0x72, 0x64, 0x31, 0x00, 0x05, 0x00, 0x06, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x74, 0x65, 0x78, 0x5F, 0x63, 0x6F, 0x6F, 0x72, 0x64, 0x31, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x22, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x26, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x28, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x04, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x20, 0x00, 0x04, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x21, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x24, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x25, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x25, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x25, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x91, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x24, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x26, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x24, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00 }; long long multi_texture_vert_spv_size = 1372; ================================================ FILE: src/engine/renderer/shaders/spirv/single_texture_clipping_plane_vert.cpp ================================================ unsigned char single_texture_clipping_plane_vert_spv[] = { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x20, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x70, 0x6F, 0x73, 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x00, 0x05, 0x00, 0x06, 0x00, 0x16, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50, 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50, 0x6F, 0x73, 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x00, 0x06, 0x00, 0x07, 0x00, 0x16, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x43, 0x6C, 0x69, 0x70, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6E, 0x63, 0x65, 0x00, 0x05, 0x00, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x54, 0x72, 0x61, 0x6E, 0x73, 0x66, 0x6F, 0x72, 0x6D, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x6C, 0x69, 0x70, 0x5F, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5F, 0x78, 0x66, 0x6F, 0x72, 0x6D, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x65, 0x79, 0x65, 0x5F, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5F, 0x78, 0x66, 0x6F, 0x72, 0x6D, 0x00, 0x06, 0x00, 0x07, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x63, 0x6C, 0x69, 0x70, 0x70, 0x69, 0x6E, 0x67, 0x5F, 0x70, 0x6C, 0x61, 0x6E, 0x65, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x38, 0x00, 0x00, 0x00, 0x66, 0x72, 0x61, 0x67, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x66, 0x72, 0x61, 0x67, 0x5F, 0x74, 0x65, 0x78, 0x5F, 0x63, 0x6F, 0x6F, 0x72, 0x64, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x40, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x74, 0x65, 0x78, 0x5F, 0x63, 0x6F, 0x6F, 0x72, 0x64, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x04, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x48, 0x00, 0x04, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x38, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x15, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x04, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x18, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x05, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x25, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x36, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x25, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x39, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x39, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x20, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x91, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x25, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x26, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x29, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x90, 0x00, 0x05, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x36, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x37, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x38, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00 }; long long single_texture_clipping_plane_vert_spv_size = 1908; ================================================ FILE: src/engine/renderer/shaders/spirv/single_texture_frag.cpp ================================================ unsigned char single_texture_frag_spv[] = { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x6F, 0x75, 0x74, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x66, 0x72, 0x61, 0x67, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x30, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x14, 0x00, 0x00, 0x00, 0x66, 0x72, 0x61, 0x67, 0x5F, 0x74, 0x65, 0x78, 0x5F, 0x63, 0x6F, 0x6F, 0x72, 0x64, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x19, 0x00, 0x00, 0x00, 0x61, 0x6C, 0x70, 0x68, 0x61, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x5F, 0x66, 0x75, 0x6E, 0x63, 0x00, 0x47, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x32, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x34, 0x00, 0x06, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x34, 0x00, 0x06, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x2B, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x34, 0x00, 0x06, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x57, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x21, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x05, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x25, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x26, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x01, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x27, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x1E, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x29, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x21, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0xBE, 0x00, 0x05, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x31, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x32, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x01, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x33, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x2D, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x35, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x37, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x38, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x21, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x05, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x3D, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x01, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x3E, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x39, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x39, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x2D, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x2D, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x1E, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x1E, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00 }; long long single_texture_frag_spv_size = 1456; ================================================ FILE: src/engine/renderer/shaders/spirv/single_texture_vert.cpp ================================================ unsigned char single_texture_vert_spv[] = { 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50, 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50, 0x6F, 0x73, 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x00, 0x05, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x54, 0x72, 0x61, 0x6E, 0x73, 0x66, 0x6F, 0x72, 0x6D, 0x00, 0x00, 0x00, 0x06, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6D, 0x76, 0x70, 0x00, 0x05, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x70, 0x6F, 0x73, 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x00, 0x05, 0x00, 0x05, 0x00, 0x20, 0x00, 0x00, 0x00, 0x66, 0x72, 0x61, 0x67, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x22, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x26, 0x00, 0x00, 0x00, 0x66, 0x72, 0x61, 0x67, 0x5F, 0x74, 0x65, 0x78, 0x5F, 0x63, 0x6F, 0x6F, 0x72, 0x64, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x28, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x74, 0x65, 0x78, 0x5F, 0x63, 0x6F, 0x6F, 0x72, 0x64, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x22, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x26, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x28, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x04, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x20, 0x00, 0x04, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x21, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x24, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x25, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x25, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x91, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x24, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x26, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00 }; long long single_texture_vert_spv_size = 1224; ================================================ FILE: src/engine/renderer/tr_animation.c ================================================ /* =========================================================================== 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 Foobar; 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 = tr.currentModel->md4; 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( (surfaceType_t*) (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 = (int)(intptr_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; } ================================================ FILE: src/engine/renderer/tr_backend.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "tr_local.h" // DX12 #include "d3d12.h" backEndData_t *backEndData[SMP_FRAMES]; 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 }; #ifdef _DEBUG static float fast_sky_color[4] = { 0.8f, 0.7f, 0.4f, 1.0f }; #else static float fast_sky_color[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; #endif /* ** GL_Bind */ void GL_Bind( image_t *image ) { image_t* final_image = image; if (!final_image) { ri.Printf( PRINT_WARNING, "GL_Bind: NULL image\n" ); final_image = tr.defaultImage; } if ( r_nobind->integer && tr.dlightImage ) { // performance evaluation option final_image = tr.dlightImage; } int texnum = final_image->texnum; if ( glState.currenttextures[glState.currenttmu] != texnum ) { image->frameUsed = tr.frameCount; glState.currenttextures[glState.currenttmu] = texnum; qglBindTexture (GL_TEXTURE_2D, texnum); // VULKAN if (vk.active) { VkDescriptorSet set = vk_world.images[final_image->index].descriptor_set; vk_world.current_descriptor_sets[glState.currenttmu] = set; } // DX12 if (dx.active) { dx_world.current_image_indices[glState.currenttmu] = final_image->index; } } } /* ** 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_Cull */ void GL_Cull( int cullType ) { if ( glState.faceCulling == cullType ) { return; } glState.faceCulling = cullType; if ( cullType == CT_TWO_SIDED ) { qglDisable( GL_CULL_FACE ); } else { qglEnable( GL_CULL_FACE ); if ( cullType == CT_BACK_SIDED ) { if ( backEnd.viewParms.isMirror ) { qglCullFace( GL_FRONT ); } else { qglCullFace( GL_BACK ); } } else { if ( backEnd.viewParms.isMirror ) { qglCullFace( GL_BACK ); } else { qglCullFace( GL_FRONT ); } } } } /* ** 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_ADD: qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD ); break; default: ri.Error( ERR_DROP, "GL_TexEnv: invalid env '%d' passed\n", 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\n" ); 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\n" ); 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 = ( backEnd.refdef.time & 255 ) / 255.0f; qglClearColor( c, c, c, 1 ); qglClear( GL_COLOR_BUFFER_BIT ); float color[4] = { c, c, c, 1 }; // VULKAN vk_clear_attachments(false, true, color); // DX12 dx_clear_attachments(false, true, color); 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) { // 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 int clearBits = GL_DEPTH_BUFFER_BIT; bool clear_stencil = (r_shadows->integer == 2); if ( clear_stencil ) { clearBits |= GL_STENCIL_BUFFER_BIT; } bool fast_sky = r_fastsky->integer && !( backEnd.refdef.rdflags & RDF_NOWORLDMODEL ); if ( fast_sky ) { clearBits |= GL_COLOR_BUFFER_BIT; // FIXME: only if sky shaders have been used qglClearColor(fast_sky_color[0], fast_sky_color[1], fast_sky_color[2], fast_sky_color[3]); } qglClear( clearBits ); // VULKAN vk_clear_attachments(vk_world.dirty_depth_attachment, fast_sky, fast_sky_color); // DX12 dx_clear_attachments(true, fast_sky, fast_sky_color); if ( ( backEnd.refdef.rdflags & RDF_HYPERSPACE ) ) { RB_Hyperspace(); return; } else { backEnd.isHyperspace = qfalse; } glState.faceCulling = -1; // force face culling to set next time // 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); } } /* ================== 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; 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; oldDlighted = qfalse; oldSort = -1; depthRange = qfalse; backEnd.pc.c_surfaces += numDrawSurfs; for (i = 0, drawSurf = drawSurfs ; i < numDrawSurfs ; i++, drawSurf++) { if ( (int)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 = qfalse; if ( entityNum != ENTITYNUM_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; } } 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 ); // VULKAN Com_Memcpy(vk_world.modelview_transform, backEnd.or.modelMatrix, 64); // DX12 Com_Memcpy(dx_world.modelview_transform, backEnd.or.modelMatrix, 64); // // change depthrange if needed // if ( oldDepthRange != depthRange ) { if ( depthRange ) { qglDepthRange (0, 0.3); } else { qglDepthRange (0, 1); } oldDepthRange = depthRange; } 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 ); // VULKAN Com_Memcpy(vk_world.modelview_transform, backEnd.viewParms.world.modelMatrix, 64); // DX12 Com_Memcpy(dx_world.modelview_transform, backEnd.viewParms.world.modelMatrix, 64); if ( depthRange ) { qglDepthRange (0, 1); } // darken down any stencil shadows RB_ShadowFinish(); } /* ============================================================================ RENDER BACK END THREAD 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_SyncRenderThread(); start = end = 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); } RE_UploadCinematic(w, h, cols, rows, data, client, dirty); if ( r_speeds->integer ) { end = ri.Milliseconds(); ri.Printf( PRINT_ALL, "qglTexSubImage2D %i, %i: %i msec\n", cols, rows, end - start ); } tr.cinematicShader->stages[0]->bundle[0].image[0] = tr.scratchImage[client]; RE_StretchPic(x, y, w, h, 0.5f / cols, 0.5f / rows, 1.0f - 0.5f / cols, 1.0f - 0.5 / rows, tr.cinematicShader->index); } 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 ); qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP ); // VULKAN if (vk.active) { Vk_Image& image = vk_world.images[tr.scratchImage[client]->index]; vkDestroyImage(vk.device, image.handle, nullptr); vkDestroyImageView(vk.device, image.view, nullptr); vkFreeDescriptorSets(vk.device, vk.descriptor_pool, 1, &image.descriptor_set); image = vk_create_image(cols, rows, VK_FORMAT_R8G8B8A8_UNORM, 1, false); vk_upload_image_data(image.handle, cols, rows, false, data, 4); } // DX12 if (dx.active) { int image_index = tr.scratchImage[client]->index; Dx_Image& image = dx_world.images[image_index]; image.texture->Release(); image = dx_create_image(cols, rows, IMAGE_FORMAT_RGBA8, 1, false, image_index); dx_upload_image_data(image.texture, cols, rows, 1, data, 4); } } 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 ); // VULKAN if (vk.active) { const Vk_Image& image = vk_world.images[tr.scratchImage[client]->index]; vk_upload_image_data(image.handle, cols, rows, false, data, 4); } // DX12 if (dx.active) { const Dx_Image& image = dx_world.images[tr.scratchImage[client]->index]; dx_upload_image_data(image.texture, cols, rows, 1, data, 4); } } } } /* ============= 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 ); // VULKAN vk_begin_frame(); // DX12 dx_begin_frame(); // clear screen for debugging if ( r_clear->integer ) { float color[4] = {1, 0, 0.5, 1}; qglClearColor( color[0], color[1], color[2], color[3] ); qglClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); // VULKAN // DX12 if (vk.active || dx.active) { RB_SetGL2D(); // to ensure we have viewport that occupies entire window vk_clear_attachments(false, true, color); dx_clear_attachments(false, true, color); } } 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 (!gl_active) return; if ( !backEnd.projection2D ) { RB_SetGL2D(); } qglClearColor(0, 0, 0, 1); 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 ); } // VULKAN // DX12 void RB_Show_Vk_Dx_Images() { if (!vk.active && !dx.active) { return; } if ( !backEnd.projection2D ) { RB_SetGL2D(); } float black[4] = {0, 0, 0, 1}; vk_clear_attachments(false, true, black); dx_clear_attachments(false, true, black); for (int i = 0 ; i < tr.numImages ; i++) { auto image = tr.images[i]; float w = glConfig.vidWidth / 20; float h = glConfig.vidHeight / 15; float x = i % 20 * w; float y = i / 20 * h; // show in proportional size in mode 2 if ( r_showImages->integer == 2 ) { w *= image->uploadWidth / 512.0f; h *= image->uploadHeight / 512.0f; } GL_Bind( image ); Com_Memset( tess.svars.colors, tr.identityLightByte, tess.numVertexes * 4 ); tess.numIndexes = 6; tess.numVertexes = 4; tess.indexes[0] = 0; tess.indexes[1] = 1; tess.indexes[2] = 2; tess.indexes[3] = 0; tess.indexes[4] = 2; tess.indexes[5] = 3; tess.xyz[0][0] = x; tess.xyz[0][1] = y; tess.svars.texcoords[0][0][0] = 0; tess.svars.texcoords[0][0][1] = 0; tess.xyz[1][0] = x + w; tess.xyz[1][1] = y; tess.svars.texcoords[0][1][0] = 1; tess.svars.texcoords[0][1][1] = 0; tess.xyz[2][0] = x + w; tess.xyz[2][1] = y + h; tess.svars.texcoords[0][2][0] = 1; tess.svars.texcoords[0][2][1] = 1; tess.xyz[3][0] = x; tess.xyz[3][1] = y + h; tess.svars.texcoords[0][3][0] = 0; tess.svars.texcoords[0][3][1] = 1; if (vk.active) { vk_bind_geometry(); vk_shade_geometry(vk.images_debug_pipeline, false, Vk_Depth_Range::normal); } if (dx.active) { dx_bind_geometry(); dx_shade_geometry(dx.images_debug_pipeline, false, Vk_Depth_Range::normal, true, false); } } tess.numIndexes = 0; tess.numVertexes = 0; } /* ============= 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(); RB_Show_Vk_Dx_Images(); } cmd = (const swapBuffersCommand_t *)data; GLimp_LogComment( "***************** RB_SwapBuffers *****************\n\n\n" ); GLimp_EndFrame(); backEnd.projection2D = qfalse; // VULKAN vk_end_frame(); // DX12 dx_end_frame(); return (const void *)(cmd + 1); } /* ==================== RB_ExecuteRenderCommands This function will be called synchronously if running without smp extensions, or asynchronously by another thread. ==================== */ void RB_ExecuteRenderCommands( const void *data ) { int t1, t2; t1 = ri.Milliseconds (); if ( !r_smp->integer || data == backEndData[0]->commands.cmds ) { backEnd.smpFrame = 0; } else { backEnd.smpFrame = 1; } bool begin_frame_called = false; bool end_frame_called = false; while ( 1 ) { 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 ); begin_frame_called = true; break; case RC_SWAP_BUFFERS: data = RB_SwapBuffers( data ); end_frame_called = true; break; case RC_SCREENSHOT: data = RB_TakeScreenshotCmd( data ); break; case RC_END_OF_LIST: default: // stop rendering on this thread t2 = ri.Milliseconds (); backEnd.pc.msec = t2 - t1; // VULKAN // DX12 if (com_errorEntered && (begin_frame_called && !end_frame_called)) { vk_end_frame(); dx_end_frame(); } return; } } } /* ================ RB_RenderThread ================ */ void RB_RenderThread( void ) { const void *data; // wait for either a rendering command or a quit command while ( 1 ) { // sleep until we have work to do data = GLimp_RendererSleep(); if ( !data ) { return; // all done, renderer is shutting down } renderThreadActive = qtrue; RB_ExecuteRenderCommands( data ); renderThreadActive = qfalse; } } ================================================ FILE: src/engine/renderer/tr_bsp.c ================================================ /* =========================================================================== 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 Foobar; 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; MAC_STATIC 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_SyncRenderThread(); // 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 ) { return; } 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]; 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 ); } 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 = (byte*) 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 = (byte*) 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; 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 ) { 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 = (int)(intptr_t) &((srfSurfaceFace_t *)0)->points[numPoints]; ofsIndexes = sfaceSize; sfaceSize += sizeof( int ) * numIndexes; cv = (srfSurfaceFace_t*) 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; MAC_STATIC 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 = (srfTriangles_t*) 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 = (srfFlare_t*) 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 = (surfaceType_t*) (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 = (surfaceType_t*) (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 = (surfaceType_t*) (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 = (surfaceType_t*) (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 = (surfaceType_t*) (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 = (surfaceType_t*)(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 = (surfaceType_t*)(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 = (surfaceType_t*)(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 = (srfGridMesh_t*) ri.Hunk_Alloc( size, h_low ); Com_Memcpy(hunkgrid, grid, size); hunkgrid->widthLodError = (float*) ri.Hunk_Alloc( grid->width * 4, h_low ); Com_Memcpy( hunkgrid->widthLodError, grid->widthLodError, grid->width * 4 ); hunkgrid->heightLodError = (float*) ri.Hunk_Alloc( grid->height * 4, h_low ); Com_Memcpy( grid->heightLodError, grid->heightLodError, grid->height * 4 ); R_FreeSurfaceGridMesh( grid ); s_worldData.surfaces[i].data = (surfaceType_t*) (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 = (dsurface_t*) (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 = (drawVert_t*) (void *)(fileBase + verts->fileofs); if (verts->filelen % sizeof(*dv)) ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); indexes = (int*) (void *)(fileBase + indexLump->fileofs); if ( indexLump->filelen % sizeof(*indexes)) ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); out = (msurface_t*) 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 = (dmodel_t*) (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 = (bmodel_t*) 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 = (dnode_t*) (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 = (mnode_t*) 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 = (dleaf_t*) (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 = (dshader_t*) (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 = (dshader_t*) 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 = (msurface_t**) 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 = (cplane_t*) 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 = (dfog_t*) (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 = (fog_t*) ri.Hunk_Alloc ( s_worldData.numfogs*sizeof(*out), h_low); out = s_worldData.fogs + 1; if ( !count ) { return; } brushes = (dbrush_t*) (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 = (dbrushside_t*) (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 = (byte*) 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 = (char*) 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, (int)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, (int)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; byte *buffer; byte *startMarker; if ( tr.worldMapLoaded ) { ri.Error( ERR_DROP, "ERROR: attempted to redundantly load world map\n" ); } // 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, (void **)&buffer ); if ( !buffer ) { 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 ); startMarker = (byte*) ri.Hunk_Alloc(0, h_low); c_gridVerts = 0; header = (dheader_t *)buffer; 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 ); } ================================================ FILE: src/engine/renderer/tr_cmds.c ================================================ /* =========================================================================== 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 Foobar; 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; volatile qboolean renderThreadActive; /* ===================== 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\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)); } 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 ); } Com_Memset( &tr.pc, 0, sizeof( tr.pc ) ); Com_Memset( &backEnd.pc, 0, sizeof( backEnd.pc ) ); } /* ==================== R_InitCommandBuffers ==================== */ void R_InitCommandBuffers( void ) { glConfig.smpActive = qfalse; if ( r_smp->integer ) { ri.Printf( PRINT_ALL, "Trying SMP acceleration...\n" ); if ( GLimp_SpawnRenderThread( RB_RenderThread ) ) { ri.Printf( PRINT_ALL, "...succeeded.\n" ); glConfig.smpActive = qtrue; } else { ri.Printf( PRINT_ALL, "...failed.\n" ); } } } /* ==================== R_ShutdownCommandBuffers ==================== */ void R_ShutdownCommandBuffers( void ) { // kill the rendering thread if ( glConfig.smpActive ) { GLimp_WakeRenderer( NULL ); glConfig.smpActive = qfalse; } } /* ==================== R_IssueRenderCommands ==================== */ int c_blockedOnRender; int c_blockedOnMain; void R_IssueRenderCommands( qboolean runPerformanceCounters ) { renderCommandList_t *cmdList; cmdList = &backEndData[tr.smpFrame]->commands; assert(cmdList); // bk001205 // 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 ( glConfig.smpActive ) { // if the render thread is not idle, wait for it if ( renderThreadActive ) { c_blockedOnRender++; if ( r_showSmp->integer ) { ri.Printf( PRINT_ALL, "R" ); } } else { c_blockedOnMain++; if ( r_showSmp->integer ) { ri.Printf( PRINT_ALL, "." ); } } // sleep until the renderer has completed GLimp_FrontEndSleep(); } // at this point, the back end thread is idle, so it is ok // to look at it's performance counters if ( runPerformanceCounters ) { R_PerformanceCounters(); } // actually start the commands going if ( !r_skipBackEnd->integer ) { // let it start on the new batch if ( !glConfig.smpActive ) { RB_ExecuteRenderCommands( cmdList->cmds ); } else { GLimp_WakeRenderer( cmdList ); } } } /* ==================== R_SyncRenderThread Issue any pending commands and wait for them to complete. After exiting, the render thread will have completed its work and will remain idle and the main thread is free to issue OpenGL calls until R_IssueRenderCommands is called. ==================== */ void R_SyncRenderThread( void ) { if ( !tr.registered ) { return; } R_IssueRenderCommands( qfalse ); if ( !glConfig.smpActive ) { return; } GLimp_FrontEndSleep(); } /* ============ R_GetCommandBuffer make sure there is enough command space, waiting on the render thread if needed. ============ */ void *R_GetCommandBuffer( int bytes ) { renderCommandList_t *cmdList; cmdList = &backEndData[tr.smpFrame]->commands; // 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 = (drawSurfsCommand_t*) 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 = (setColorCommand_t*) 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]; } /* ============= 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; } cmd = (stretchPicCommand_t*) 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; } /* ==================== 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; if ( !tr.registered ) { return; } tr.frameCount++; // // texturemode stuff // if ( r_textureMode->modified ) { R_SyncRenderThread(); GL_TextureMode( r_textureMode->string ); r_textureMode->modified = qfalse; } // // gamma stuff // if ( r_gamma->modified || r_shaderGamma->modified ) { r_gamma->modified = qfalse; R_SyncRenderThread(); R_SetColorMappings(); r_shaderGamma->modified = qfalse; } // check for errors if ( !r_ignoreGLErrors->integer ) { int err; R_SyncRenderThread(); if ( ( err = qglGetError() ) != GL_NO_ERROR ) { ri.Error( ERR_FATAL, "RE_BeginFrame() - glGetError() failed (0x%x)!\n", err ); } } // // draw buffer stuff // cmd = (drawBufferCommand_t*) R_GetCommandBuffer(sizeof(*cmd)); if ( !cmd ) { return; } cmd->commandId = RC_DRAW_BUFFER; if ( glConfig.stereoEnabled ) { 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 ( stereoFrame != STEREO_CENTER ) { ri.Error( ERR_FATAL, "RE_BeginFrame: Stereo is disabled, but stereoFrame was %i", stereoFrame ); } if ( !Q_stricmp( r_drawBuffer->string, "GL_FRONT" ) ) { cmd->buffer = (int)GL_FRONT; } else { cmd->buffer = (int)GL_BACK; } } } /* ============= 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 = (swapBuffersCommand_t*) R_GetCommandBuffer(sizeof(*cmd)); if ( !cmd ) { return; } cmd->commandId = RC_SWAP_BUFFERS; R_IssueRenderCommands( qtrue ); // use the other buffers next frame, because another CPU // may still be rendering into the current ones R_ToggleSmpFrame(); if ( frontEndMsec ) { *frontEndMsec = tr.frontEndMsec; } tr.frontEndMsec = 0; if ( backEndMsec ) { *backEndMsec = backEnd.pc.msec; } backEnd.pc.msec = 0; } ================================================ FILE: src/engine/renderer/tr_curve.c ================================================ /* =========================================================================== 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 Foobar; 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; 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"); count = 1; } 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*/ (srfGridMesh_t*) ri.Malloc( size ); Com_Memset(grid, 0, size); grid->widthLodError = /*ri.Hunk_Alloc*/ (float*) ri.Malloc( width * 4 ); Com_Memcpy( grid->widthLodError, errorTable[0], width * 4 ); grid->heightLodError = /*ri.Hunk_Alloc*/ (float*) 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 prev, next, mid; float len, maxLen; int dir; int t; MAC_STATIC 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; MAC_STATIC 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; MAC_STATIC 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; } ================================================ FILE: src/engine/renderer/tr_font.c ================================================ /* =========================================================================== 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 Foobar; 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. You will need to flip the tga's in Photoshop as the tga output code writes them upside // down. // 6. 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. // 7. 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 "../ft2/fterrors.h" #include "../ft2/ftsystem.h" #include "../ft2/ftimage.h" #include "../ft2/freetype.h" #include "../ft2/ftoutln.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 = Z_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 = Z_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; buffer = Z_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) { Z_Free(bitmap->buffer); Z_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) { if (*yOut + *maxHeight + 1 >= 255) { *yOut = -1; *xOut = -1; Z_Free(bitmap->buffer); Z_Free(bitmap); return &glyph; } else { *xOut = 0; *yOut += *maxHeight + 1; } } else if (*yOut + *maxHeight + 1 >= 255) { *yOut = -1; *xOut = -1; Z_Free(bitmap->buffer); Z_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; } Z_Free(bitmap->buffer); Z_Free(bitmap); return &glyph; } #endif static int fdOffset; static byte *fdFile; int readInt() { 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() { poor me; #if idppc me.fred[0] = fdFile[fdOffset+3]; me.fred[1] = fdFile[fdOffset+2]; me.fred[2] = fdFile[fdOffset+1]; me.fred[3] = fdFile[fdOffset+0]; #else 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, satLevels; unsigned char *out, *imageBuff; glyphInfo_t *glyph; image_t *image; qhandle_t h; float max; #endif void *faceData; int i, len; char name[1024]; float dpi = 72; // float glyphScale = 72.0f / dpi; // change the scale to be relative to 1 based on 72 dpi ( so dpi of 144 means a scale of .5 ) if (pointSize <= 0) { pointSize = 12; } // 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; // make sure the render thread is stopped R_SyncRenderThread(); if (registeredFontCount >= MAX_FONTS) { ri.Printf(PRINT_ALL, "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 = (byte*) 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(); Com_Memcpy(font->glyphs[i].shaderName, &fdFile[fdOffset], 32); fdOffset += 32; } 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_ALL, "RE_RegisterFont: FreeType code not available\n"); #else if (ftLibrary == NULL) { ri.Printf(PRINT_ALL, "RE_RegisterFont: FreeType not initialized.\n"); return; } len = ri.FS_ReadFile(fontName, &faceData); if (len <= 0) { ri.Printf(PRINT_ALL, "RE_RegisterFont: Unable to read font file\n"); return; } // allocate on the stack first in case we fail if (FT_New_Memory_Face( ftLibrary, faceData, len, 0, &face )) { ri.Printf(PRINT_ALL, "RE_RegisterFont: FreeType2, unable to allocate new face.\n"); return; } if (FT_Set_Char_Size( face, pointSize << 6, pointSize << 6, dpi, dpi)) { ri.Printf(PRINT_ALL, "RE_RegisterFont: FreeType2, 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 = Z_Malloc(1024*1024); if (out == NULL) { ri.Printf(PRINT_ALL, "RE_RegisterFont: Z_Malloc failure during output image creation.\n"); return; } Com_Memset(out, 0, 1024*1024); maxHeight = 0; for (i = GLYPH_START; i < GLYPH_END; i++) { glyph = 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 = Z_Malloc(newSize); left = 0; max = 0; satLevels = 255; 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); 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; Z_Free(imageBuff); i++; } else { Com_Memcpy(&font->glyphs[i], glyph, sizeof(glyphInfo_t)); i++; } } 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)); } Z_Free(out); ri.FS_FreeFile(faceData); #endif } void R_InitFreeType() { #ifdef BUILD_FREETYPE if (FT_Init_FreeType( &ftLibrary )) { ri.Printf(PRINT_ALL, "R_InitFreeType: Unable to initialize FreeType.\n"); } #endif registeredFontCount = 0; } void R_DoneFreeType() { #ifdef BUILD_FREETYPE if (ftLibrary) { FT_Done_FreeType( ftLibrary ); ftLibrary = NULL; } #endif registeredFontCount = 0; } ================================================ FILE: src/engine/renderer/tr_image.c ================================================ /* =========================================================================== 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 Foobar; 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 void* q3_stbi_malloc(size_t size) { return ri.Malloc((int)size); } static void q3_stbi_free(void* p) { ri.Free(p); } static void* q3_stbi_realloc(void* p, size_t old_size, size_t new_size) { if (p == nullptr) return q3_stbi_malloc(new_size); void* p_new; if (old_size < new_size) { p_new = q3_stbi_malloc(new_size); memcpy(p_new, p, old_size); q3_stbi_free(p); } else { p_new = p; } return p_new; } #define STBI_MALLOC q3_stbi_malloc #define STBI_FREE q3_stbi_free #define STBI_REALLOC_SIZED q3_stbi_realloc #define STB_IMAGE_IMPLEMENTATION #include "jpeg/stb_image.h" #define TJE_IMPLEMENTATION #include "jpeg/tiny_jpeg.h" static void LoadBMP( const char *name, byte **pic, int *width, int *height ); static void LoadTGA( const char *name, byte **pic, int *width, int *height ); static void LoadJPG( const char *name, byte **pic, int *width, int *height ); 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; for ( i=0 ; i< 6 ; i++ ) { if ( !Q_stricmp( modes[i].name, string ) ) { break; } } 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++ ) { image_t* 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); } } // VULKAN if (vk.active) { VK_CHECK(vkDeviceWaitIdle(vk.device)); for ( i = 0 ; i < tr.numImages ; i++ ) { image_t* glt = tr.images[i]; if (glt->mipmap) { Vk_Image& image = vk_world.images[i]; vk_update_descriptor_set(image.descriptor_set, image.view, true, glt->wrapClampMode == GL_REPEAT); } } } // DX12 if (dx.active) { dx_wait_device_idle(); Vk_Sampler_Def def; def.gl_mag_filter = gl_filter_max; def.gl_min_filter = gl_filter_min; def.repeat_texture = true; dx_create_sampler_descriptor(def, SAMPLER_MIP_REPEAT); def.repeat_texture = false; dx_create_sampler_descriptor(def, SAMPLER_MIP_CLAMP); } } /* =============== 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- -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 ", i, image->uploadWidth, image->uploadHeight, yesno[image->mipmap] ); 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_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: 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 = (unsigned*) 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}, }; struct Image_Upload_Data { byte* buffer; int buffer_size; int mip_levels; int base_level_width; int base_level_height; }; static Image_Upload_Data generate_image_upload_data(const byte* data, int width, int height, qboolean mipmap, qboolean picmip) { // // convert to exact power of 2 sizes // int scaled_width, scaled_height; 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; Image_Upload_Data upload_data; upload_data.buffer = (byte*) ri.Hunk_AllocateTempMemory(2 * 4 * scaled_width * scaled_height); byte* resampled_buffer = nullptr; if ( scaled_width != width || scaled_height != height ) { resampled_buffer = (byte*) ri.Hunk_AllocateTempMemory( scaled_width * scaled_height * 4 ); ResampleTexture ((unsigned*)data, width, height, (unsigned*)resampled_buffer, scaled_width, scaled_height); data = resampled_buffer; 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 // int max_texture_size = gl_active ? glConfig.maxTextureSize : 2048; while ( scaled_width > max_texture_size || scaled_height > max_texture_size ) { scaled_width >>= 1; scaled_height >>= 1; } upload_data.base_level_width = scaled_width; upload_data.base_level_height = scaled_height; if (scaled_width == width && scaled_height == height && !mipmap) { upload_data.mip_levels = 1; upload_data.buffer_size = scaled_width * scaled_height * 4; Com_Memcpy(upload_data.buffer, data, upload_data.buffer_size); if (resampled_buffer != nullptr) ri.Hunk_FreeTempMemory(resampled_buffer); return upload_data; } // Use the normal mip-mapping to go down from [width, height] to [scaled_width, scaled_height] dimensions. while (width > scaled_width || height > scaled_height) { R_MipMap((byte *)data, width, height); width >>= 1; if (width < 1) width = 1; height >>= 1; if (height < 1) height = 1; } // At this point width == scaled_width and height == scaled_height. unsigned* scaled_buffer = (unsigned int*) ri.Hunk_AllocateTempMemory( sizeof( unsigned ) * scaled_width * scaled_height ); Com_Memcpy(scaled_buffer, data, scaled_width * scaled_height * 4); R_LightScaleTexture(scaled_buffer, scaled_width, scaled_height, (qboolean) !mipmap); int miplevel = 0; int mip_level_size = scaled_width * scaled_height * 4; Com_Memcpy(upload_data.buffer, scaled_buffer, mip_level_size); upload_data.buffer_size = mip_level_size; if (mipmap) { while (scaled_width > 1 || scaled_height > 1) { R_MipMap((byte *)scaled_buffer, scaled_width, scaled_height); scaled_width >>= 1; if (scaled_width < 1) scaled_width = 1; scaled_height >>= 1; if (scaled_height < 1) scaled_height = 1; miplevel++; mip_level_size = scaled_width * scaled_height * 4; if ( r_colorMipLevels->integer ) { R_BlendOverTexture( (byte *)scaled_buffer, scaled_width * scaled_height, mipBlendColors[miplevel] ); } Com_Memcpy(&upload_data.buffer[upload_data.buffer_size], scaled_buffer, mip_level_size); upload_data.buffer_size += mip_level_size; } } upload_data.mip_levels = miplevel + 1; ri.Hunk_FreeTempMemory(scaled_buffer); if (resampled_buffer != nullptr) ri.Hunk_FreeTempMemory(resampled_buffer); return upload_data; } static int upload_gl_image(const Image_Upload_Data& upload_data, int texture_address_mode) { int w = upload_data.base_level_width; int h = upload_data.base_level_height; bool has_alpha = false; for (int i = 0; i < w * h; i++) { if (upload_data.buffer[i*4 + 3] != 255) { has_alpha = true; break; } } int internal_format = GL_RGBA8; if (glConfig.textureCompression && !has_alpha) { internal_format = GL_RGB4_S3TC; } else if (r_texturebits->integer <= 16) { internal_format = has_alpha ? GL_RGBA4 : GL_RGB5; } auto buffer = upload_data.buffer; for (int i = 0; i < upload_data.mip_levels; i++) { qglTexImage2D(GL_TEXTURE_2D, i, internal_format, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer); buffer += w * h * 4; w >>= 1; if (w < 1) w = 1; h >>= 1; if (h < 1) h = 1; } if (upload_data.mip_levels > 1) { qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); } else { 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, texture_address_mode); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, texture_address_mode); GL_CheckErrors(); return internal_format; } // VULKAN static Vk_Image upload_vk_image(const Image_Upload_Data& upload_data, bool repeat_texture) { int w = upload_data.base_level_width; int h = upload_data.base_level_height; bool has_alpha = false; for (int i = 0; i < w * h; i++) { if (upload_data.buffer[i*4 + 3] != 255) { has_alpha = true; break; } } byte* buffer = upload_data.buffer; VkFormat format = VK_FORMAT_R8G8B8A8_UNORM; int bytes_per_pixel = 4; if (r_texturebits->integer <= 16) { buffer = (byte*) ri.Hunk_AllocateTempMemory( upload_data.buffer_size / 2 ); format = has_alpha ? VK_FORMAT_B4G4R4A4_UNORM_PACK16 : VK_FORMAT_A1R5G5B5_UNORM_PACK16; bytes_per_pixel = 2; } if (format == VK_FORMAT_A1R5G5B5_UNORM_PACK16) { auto p = (uint16_t*)buffer; for (int i = 0; i < upload_data.buffer_size; i += 4, p++) { byte r = upload_data.buffer[i+0]; byte g = upload_data.buffer[i+1]; byte b = upload_data.buffer[i+2]; *p = uint32_t((b/255.0) * 31.0 + 0.5) | (uint32_t((g/255.0) * 31.0 + 0.5) << 5) | (uint32_t((r/255.0) * 31.0 + 0.5) << 10) | (1 << 15); } } else if (format == VK_FORMAT_B4G4R4A4_UNORM_PACK16) { auto p = (uint16_t*)buffer; for (int i = 0; i < upload_data.buffer_size; i += 4, p++) { byte r = upload_data.buffer[i+0]; byte g = upload_data.buffer[i+1]; byte b = upload_data.buffer[i+2]; byte a = upload_data.buffer[i+3]; *p = uint32_t((a/255.0) * 15.0 + 0.5) | (uint32_t((r/255.0) * 15.0 + 0.5) << 4) | (uint32_t((g/255.0) * 15.0 + 0.5) << 8) | (uint32_t((b/255.0) * 15.0 + 0.5) << 12); } } Vk_Image image = vk_create_image(w, h, format, upload_data.mip_levels, repeat_texture); vk_upload_image_data(image.handle, w, h, upload_data.mip_levels > 1, buffer, bytes_per_pixel); if (bytes_per_pixel == 2) ri.Hunk_FreeTempMemory(buffer); return image; } // DX12 static Dx_Image upload_dx_image(const Image_Upload_Data& upload_data, bool repeat_texture, int image_index) { int w = upload_data.base_level_width; int h = upload_data.base_level_height; bool has_alpha = false; for (int i = 0; i < w * h; i++) { if (upload_data.buffer[i*4 + 3] != 255) { has_alpha = true; break; } } byte* buffer = upload_data.buffer; Dx_Image_Format format = IMAGE_FORMAT_RGBA8; int bytes_per_pixel = 4; if (r_texturebits->integer <= 16) { buffer = (byte*) ri.Hunk_AllocateTempMemory( upload_data.buffer_size / 2 ); format = has_alpha ? IMAGE_FORMAT_BGRA4 : IMAGE_FORMAT_BGR5A1; bytes_per_pixel = 2; } if (format == IMAGE_FORMAT_BGR5A1) { auto p = (uint16_t*)buffer; for (int i = 0; i < upload_data.buffer_size; i += 4, p++) { byte r = upload_data.buffer[i+0]; byte g = upload_data.buffer[i+1]; byte b = upload_data.buffer[i+2]; *p = (uint32_t((b/255.0) * 31.0 + 0.5) << 0) | (uint32_t((g/255.0) * 31.0 + 0.5) << 5) | (uint32_t((r/255.0) * 31.0 + 0.5) << 10) | (1 << 15); } } else if (format == IMAGE_FORMAT_BGRA4) { auto p = (uint16_t*)buffer; for (int i = 0; i < upload_data.buffer_size; i += 4, p++) { byte r = upload_data.buffer[i+0]; byte g = upload_data.buffer[i+1]; byte b = upload_data.buffer[i+2]; byte a = upload_data.buffer[i+3]; *p =(uint32_t((b/255.0) * 15.0 + 0.5) << 0) | (uint32_t((g/255.0) * 15.0 + 0.5) << 4) | (uint32_t((r/255.0) * 15.0 + 0.5) << 8) | (uint32_t((a/255.0) * 15.0 + 0.5) << 12); } } Dx_Image image = dx_create_image(w, h, format, upload_data.mip_levels, repeat_texture, image_index); dx_upload_image_data(image.texture, w, h, upload_data.mip_levels, buffer, bytes_per_pixel); if (bytes_per_pixel == 2) ri.Hunk_FreeTempMemory(buffer); return image; } /* ================ 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 ) { if (strlen(name) >= MAX_QPATH ) { ri.Error (ERR_DROP, "R_CreateImage: \"%s\" is too long\n", name); } if ( tr.numImages == MAX_DRAWIMAGES ) { ri.Error( ERR_DROP, "R_CreateImage: MAX_DRAWIMAGES hit\n"); } // Create image_t object. auto image = tr.images[tr.numImages] = (image_t*) ri.Hunk_Alloc( sizeof( image_t ), h_low ); image->index = tr.numImages; image->texnum = 1024 + tr.numImages; image->mipmap = mipmap; image->allowPicmip = allowPicmip; strcpy (image->imgName, name); image->width = width; image->height = height; image->wrapClampMode = glWrapClampMode; long hash = generateHashValue(name); image->next = hashTable[hash]; hashTable[hash] = image; tr.numImages++; // Create corresponding GPU resource. bool isLightmap = (strncmp(name, "*lightmap", 9) == 0); GL_SelectTexture(isLightmap ? 1 : 0); GL_Bind(image); Image_Upload_Data upload_data = generate_image_upload_data(pic, width, height, mipmap, allowPicmip); if (gl_active) { image->internalFormat = upload_gl_image(upload_data, glWrapClampMode); } // VULKAN if (vk.active) { vk_world.images[image->index] = upload_vk_image(upload_data, glWrapClampMode == GL_REPEAT); } // DX12 if (dx.active) { dx_world.images[image->index] = upload_dx_image(upload_data, glWrapClampMode == GL_REPEAT, image->index); } if (isLightmap) { GL_SelectTexture( 0 ); } ri.Hunk_FreeTempMemory(upload_data.buffer); return image; } /* ========================================================= BMP LOADING ========================================================= */ typedef struct { char id[2]; unsigned long fileSize; unsigned long reserved0; unsigned long bitmapDataOffset; unsigned long bitmapHeaderSize; unsigned long width; unsigned long height; unsigned short planes; unsigned short bitsPerPixel; unsigned long compression; unsigned long bitmapDataSize; unsigned long hRes; unsigned long vRes; unsigned long colors; unsigned long importantColors; unsigned char palette[256][4]; } BMPHeader_t; static void LoadBMP( const char *name, byte **pic, int *width, int *height ) { int columns, rows, numPixels; byte *pixbuf; int row, column; byte *buf_p; byte *buffer; int length; BMPHeader_t bmpHeader; byte *bmpRGBA; *pic = NULL; // // load the file // length = ri.FS_ReadFile( ( char * ) name, (void **)&buffer); if (!buffer) { return; } buf_p = buffer; bmpHeader.id[0] = *buf_p++; bmpHeader.id[1] = *buf_p++; bmpHeader.fileSize = LittleLong( * ( long * ) buf_p ); buf_p += 4; bmpHeader.reserved0 = LittleLong( * ( long * ) buf_p ); buf_p += 4; bmpHeader.bitmapDataOffset = LittleLong( * ( long * ) buf_p ); buf_p += 4; bmpHeader.bitmapHeaderSize = LittleLong( * ( long * ) buf_p ); buf_p += 4; bmpHeader.width = LittleLong( * ( long * ) buf_p ); buf_p += 4; bmpHeader.height = LittleLong( * ( long * ) 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( * ( long * ) buf_p ); buf_p += 4; bmpHeader.bitmapDataSize = LittleLong( * ( long * ) buf_p ); buf_p += 4; bmpHeader.hRes = LittleLong( * ( long * ) buf_p ); buf_p += 4; bmpHeader.vRes = LittleLong( * ( long * ) buf_p ); buf_p += 4; bmpHeader.colors = LittleLong( * ( long * ) buf_p ); buf_p += 4; bmpHeader.importantColors = LittleLong( * ( long * ) buf_p ); buf_p += 4; Com_Memcpy( bmpHeader.palette, buf_p, sizeof( bmpHeader.palette ) ); if ( bmpHeader.bitsPerPixel == 8 ) buf_p += 1024; if ( bmpHeader.id[0] != 'B' && bmpHeader.id[1] != 'M' ) { ri.Error( ERR_DROP, "LoadBMP: only Windows-style BMP files supported (%s)\n", name ); } if ( (int)bmpHeader.fileSize != length ) { ri.Error( ERR_DROP, "LoadBMP: header size does not match file size (%d vs. %d) (%s)\n", bmpHeader.fileSize, length, name ); } if ( bmpHeader.compression != 0 ) { ri.Error( ERR_DROP, "LoadBMP: only uncompressed BMP files supported (%s)\n", name ); } if ( bmpHeader.bitsPerPixel < 8 ) { ri.Error( ERR_DROP, "LoadBMP: monochrome and 4-bit BMP files not supported (%s)\n", name ); } columns = bmpHeader.width; rows = bmpHeader.height; if ( rows < 0 ) rows = -rows; numPixels = columns * rows; if ( width ) *width = columns; if ( height ) *height = rows; bmpRGBA = (byte*) 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; default: ri.Error( ERR_DROP, "LoadBMP: illegal pixel_size '%d' in file '%s'\n", bmpHeader.bitsPerPixel, name ); break; } } } ri.FS_FreeFile( buffer ); } /* ================================================================= PCX LOADING ================================================================= */ /* ============== LoadPCX ============== */ static void LoadPCX ( const char *filename, byte **pic, byte **palette, int *width, int *height) { byte *raw; pcx_t *pcx; int x, y; int len; int dataByte, runLength; byte *out, *pix; int xmax, ymax; *pic = NULL; *palette = NULL; // // load the file // len = ri.FS_ReadFile( ( char * ) filename, (void **)&raw); if (!raw) { return; } // // parse the PCX file // pcx = (pcx_t *)raw; raw = &pcx->data; xmax = LittleShort(pcx->xmax); ymax = LittleShort(pcx->ymax); if (pcx->manufacturer != 0x0a || pcx->version != 5 || pcx->encoding != 1 || pcx->bits_per_pixel != 8 || xmax >= 1024 || ymax >= 1024) { ri.Printf (PRINT_ALL, "Bad pcx file %s (%i x %i) (%i x %i)\n", filename, xmax+1, ymax+1, pcx->xmax, pcx->ymax); return; } out = (byte*) ri.Malloc ( (ymax+1) * (xmax+1) ); *pic = out; pix = out; if (palette) { *palette = (byte*) ri.Malloc(768); Com_Memcpy (*palette, (byte *)pcx + len - 768, 768); } if (width) *width = xmax+1; if (height) *height = ymax+1; // FIXME: use bytes_per_line here? for (y=0 ; y<=ymax ; y++, pix += xmax+1) { for (x=0 ; x<=xmax ; ) { dataByte = *raw++; if((dataByte & 0xC0) == 0xC0) { runLength = dataByte & 0x3F; dataByte = *raw++; } else runLength = 1; while(runLength-- > 0) pix[x++] = dataByte; } } if ( raw - (byte *)pcx > len) { ri.Printf (PRINT_DEVELOPER, "PCX file %s was malformed", filename); ri.Free (*pic); *pic = NULL; } ri.FS_FreeFile (pcx); } /* ============== LoadPCX32 ============== */ static void LoadPCX32 ( const char *filename, byte **pic, int *width, int *height) { byte *palette; byte *pic8; int i, c, p; byte *pic32; LoadPCX (filename, &pic8, &palette, width, height); if (!pic8) { *pic = NULL; return; } c = (*width) * (*height); pic32 = *pic = (byte*) ri.Malloc(4 * c ); for (i = 0 ; i < c ; i++) { p = pic8[i]; pic32[0] = palette[p*3]; pic32[1] = palette[p*3 + 1]; pic32[2] = palette[p*3 + 2]; pic32[3] = 255; pic32 += 4; } ri.Free (pic8); ri.Free (palette); } /* ========================================================= TARGA LOADING ========================================================= */ /* ============= LoadTGA ============= */ static void LoadTGA ( const char *name, byte **pic, int *width, int *height) { int columns, rows, numPixels; byte *pixbuf; int row, column; byte *buf_p; byte *buffer; TargaHeader targa_header; byte *targa_rgba; *pic = NULL; // // load the file // ri.FS_ReadFile ( ( char * ) name, (void **)&buffer); if (!buffer) { return; } buf_p = buffer; targa_header.id_length = *buf_p++; targa_header.colormap_type = *buf_p++; targa_header.image_type = *buf_p++; targa_header.colormap_index = LittleShort ( *(short *)buf_p ); buf_p += 2; targa_header.colormap_length = LittleShort ( *(short *)buf_p ); buf_p += 2; targa_header.colormap_size = *buf_p++; targa_header.x_origin = LittleShort ( *(short *)buf_p ); buf_p += 2; targa_header.y_origin = LittleShort ( *(short *)buf_p ); buf_p += 2; targa_header.width = LittleShort ( *(short *)buf_p ); buf_p += 2; targa_header.height = LittleShort ( *(short *)buf_p ); buf_p += 2; targa_header.pixel_size = *buf_p++; targa_header.attributes = *buf_p++; 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\n"); } if ( targa_header.colormap_type != 0 ) { ri.Error( ERR_DROP, "LoadTGA: colormaps not supported\n" ); } 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)\n"); } columns = targa_header.width; rows = targa_header.height; numPixels = columns * rows; if (width) *width = columns; if (height) *height = rows; targa_rgba = (byte*) ri.Malloc (numPixels*4); *pic = targa_rgba; if (targa_header.id_length != 0) buf_p += targa_header.id_length; // skip TARGA image comment if ( targa_header.image_type==2 || targa_header.image_type == 3 ) { // 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; column0) row--; else goto breakOut; pixbuf = targa_rgba + row*columns*4; } } } else { // non run-length packet 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 // bk0101024 - fix from Leonardo // 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); } ri.FS_FreeFile (buffer); } static void LoadJPG( const char *filename, byte **pic, int *width, int *height ) { byte* fbuffer; int len = ri.FS_ReadFile ( ( char * ) filename, (void **)&fbuffer); if (!fbuffer) { return; } int components; *pic = stbi_load_from_memory(fbuffer, len, width, height, &components, STBI_rgb_alpha); if (*pic == nullptr) { ri.FS_FreeFile(fbuffer); return; } // clear all the alphas to 255 { int i, j; byte *buf; buf = *pic; j = *width * *height * 4; for (i = 3; i < j; i += 4) { buf[i] = 255; } } ri.FS_FreeFile(fbuffer); } struct Write_Context { byte* buffer; int capacity; int size = 0; Write_Context(int capacity) { this->capacity = capacity; buffer = (byte*)ri.Malloc(capacity); } ~Write_Context() { if (buffer != nullptr) ri.Free(buffer); } }; static void jpeg_write_func(void* context, void* data, int size) { auto ctx = static_cast(context); if (ctx->capacity < ctx->size + size) { int new_capacity = 1.5 * (ctx->size + size); byte* new_buffer = (byte*)ri.Malloc(new_capacity); memcpy(new_buffer, ctx->buffer, ctx->size); ri.Free(ctx->buffer); ctx->buffer = new_buffer; ctx->capacity = new_capacity; } memcpy(ctx->buffer + ctx->size, data, size); ctx->size += size; } void SaveJPG(char * filename, int quality, int image_width, int image_height, unsigned char *image_buffer) { // jpeg encoder requires top-bottom rows ordering auto buffer = (byte*)ri.Hunk_AllocateTempMemory(image_width * image_height * 4); int row_bytes = image_width * 4; for (int i = 0; i < image_height; i++) { memcpy(&buffer[i * row_bytes], &image_buffer[(image_height - 1 - i) * row_bytes], row_bytes); } Write_Context context(1024*1024); if (tje_encode_with_func(jpeg_write_func, &context, 2, image_width, image_height, 4, buffer)) { ri.FS_WriteFile(filename, context.buffer, context.size); } ri.Hunk_FreeTempMemory(buffer); } //=================================================================== /* ================= 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 ) { int len; *pic = NULL; *width = 0; *height = 0; len = (int)strlen(name); if (len<5) { return; } if ( !Q_stricmp( name+len-4, ".tga" ) ) { LoadTGA( name, pic, width, height ); // try tga first if (!*pic) { // char altname[MAX_QPATH]; // try jpg in place of tga strcpy( altname, name ); len = (int)strlen( altname ); altname[len-3] = 'j'; altname[len-2] = 'p'; altname[len-1] = 'g'; LoadJPG( altname, pic, width, height ); } } else if ( !Q_stricmp(name+len-4, ".pcx") ) { LoadPCX32( name, pic, width, height ); } else if ( !Q_stricmp( name+len-4, ".bmp" ) ) { LoadBMP( name, pic, width, height ); } else if ( !Q_stricmp( name+len-4, ".jpg" ) ) { LoadJPG( name, pic, width, height ); } } /* =============== 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 ) { // if we dont get a successful load char altname[MAX_QPATH]; // copy the name int len; // strcpy( altname, name ); // len = (int)strlen( altname ); // altname[len-3] = toupper(altname[len-3]); // and try upper case extension for unix systems altname[len-2] = toupper(altname[len-2]); // altname[len-1] = toupper(altname[len-1]); // ri.Printf( PRINT_ALL, "trying %s...\n", altname ); // R_LoadImage( altname, &pic, &width, &height ); // if (pic == NULL) { // if that fails return NULL; // bail } } 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 ); } /* ================= 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 g; float d; float borderColor[4]; data = (byte*) ri.Hunk_AllocateTempMemory( FOG_S * FOG_T * 4 ); g = 2.0; // 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 (vk.active && r_shaderGamma->integer) { if (r_ignorehwgamma->integer) { return; } if (r_shaderGamma->modified) { GLimp_RestoreGamma(); } float shader_gamma_table[256]; for (int i = 0; i < 256; i++) { shader_gamma_table[i] = float((((unsigned short)s_gammatable[i]) << 8) | s_gammatable[i]) / UINT16_MAX; } vk_update_gamma_buffer(shader_gamma_table); } else if ( glConfig.deviceSupportsGamma ) { GLimp_SetGamma( 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 ) { for ( int i=0; itexnum ); } Com_Memset( tr.images, 0, sizeof( tr.images ) ); tr.numImages = 0; Com_Memset( glState.currenttextures, 0, sizeof( glState.currenttextures ) ); if ( qglBindTexture ) { GL_SelectTexture( 1 ); qglBindTexture( GL_TEXTURE_2D, 0 ); GL_SelectTexture( 0 ); 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) { // Com_Printf ("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; char *text, *text_p; char *token; char surfName[MAX_QPATH]; if ( !name || !name[0] ) { Com_Printf( "Empty name passed to RE_RegisterSkin\n" ); return 0; } if ( (int)strlen( name ) >= MAX_QPATH ) { Com_Printf( "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 = (skin_t*) ri.Hunk_Alloc( sizeof( skin_t ), h_low ); tr.skins[hSkin] = skin; Q_strncpyz( skin->name, name, sizeof( skin->name ) ); skin->numSurfaces = 0; // make sure the render thread is stopped R_SyncRenderThread(); // If not a .skin file, load as a single shader if ( strcmp( name + (int)strlen( name ) - 5, ".skin" ) ) { skin->numSurfaces = 1; skin->surfaces[0] = (skinSurface_t*) 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, (void **)&text ); if ( !text ) { return 0; } text_p = text; 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 ] = (skinSurface_t*) 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 ); // 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] = (skin_t*) ri.Hunk_Alloc( sizeof( skin_t ), h_low ); Q_strncpyz( skin->name, "", sizeof( skin->name ) ); skin->numSurfaces = 1; skin->surfaces[0] = (skinSurface_t*) 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"); } ================================================ FILE: src/engine/renderer/tr_init.c ================================================ /* =========================================================================== 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 Foobar; 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" bool gl_active; glconfig_t glConfig; glstate_t glState; // VULKAN Vk_Instance vk; Vk_World vk_world; // DX12 Dx_Instance dx; Dx_World dx_world; static void GfxInfo_f( void ); cvar_t *r_renderAPI; cvar_t* r_gpu; cvar_t* r_vsync; cvar_t *r_shaderGamma; cvar_t* r_twinMode; cvar_t *r_railWidth; cvar_t *r_railCoreWidth; cvar_t *r_railSegmentLength; cvar_t *r_verbose; cvar_t *r_detailTextures; cvar_t *r_znear; cvar_t *r_smp; cvar_t *r_showSmp; cvar_t *r_skipBackEnd; cvar_t *r_ignorehwgamma; cvar_t *r_inGameVideo; cvar_t *r_fastsky; cvar_t *r_dynamiclight; 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_ext_compressed_textures; cvar_t *r_ext_gamma_control; cvar_t *r_ext_compiled_vertex_array; cvar_t *r_ext_texture_env_add; cvar_t *r_ignoreGLErrors; cvar_t *r_logFile; cvar_t *r_stencilbits; cvar_t *r_depthbits; cvar_t *r_stereo; cvar_t *r_texturebits; cvar_t *r_drawBuffer; cvar_t *r_glDriver; cvar_t *r_lightmap; cvar_t *r_vertexLight; cvar_t *r_uiFullScreen; cvar_t *r_shadows; 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_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_customwidth; cvar_t *r_customheight; cvar_t *r_customaspect; 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_maxpolys; int max_polys; cvar_t *r_maxpolyverts; int max_polyverts; void ( APIENTRY * qglActiveTextureARB )( GLenum texture ); void ( APIENTRY * qglClientActiveTextureARB )( GLenum texture ); void ( APIENTRY * qglLockArraysEXT)( GLint, GLint); void ( APIENTRY * qglUnlockArraysEXT) ( void ); static void AssertCvarRange( cvar_t *cv, float minVal, float maxVal, qboolean shouldBeIntegral ) { if ( shouldBeIntegral ) { if ( ( int ) cv->value != cv->integer ) { ri.Printf( PRINT_WARNING, "WARNING: cvar '%s' must be integral (%f)\n", cv->name, cv->value ); ri.Cvar_Set( cv->name, va( "%d", cv->integer ) ); } } if ( cv->value < minVal ) { ri.Printf( PRINT_WARNING, "WARNING: cvar '%s' out of range (%f < %f)\n", cv->name, cv->value, minVal ); ri.Cvar_Set( cv->name, va( "%f", minVal ) ); } else if ( cv->value > maxVal ) { ri.Printf( PRINT_WARNING, "WARNING: cvar '%s' out of range (%f > %f)\n", cv->name, cv->value, maxVal ); ri.Cvar_Set( cv->name, va( "%f", maxVal ) ); } } RenderApi get_render_api() { if (r_renderAPI->integer == 0) return RENDER_API_GL; else if (r_renderAPI->integer == 1) return RENDER_API_VK; else if (r_renderAPI->integer == 2) #ifdef ENABLE_DX12 return RENDER_API_DX; #else return RENDER_API_VK; // use default (Vulkan) if dx12 is disabled #endif else return RENDER_API_VK; // use default (Vulkan) if invalid r_renderAPI value is specified } /* ** This function is responsible for initializing a valid OpenGL/Vulkan subsystem. */ static void InitRenderAPI( void ) { // // initialize OS specific portions of the renderer // // GLimp_Init directly or indirectly references the following cvars: // - r_fullscreen // - r_glDriver // - r_mode // - r_(depth|stencil)bits // - r_ignorehwgamma // - r_gamma // if ( glConfig.vidWidth == 0 ) { #ifndef ENABLE_DX12 if (r_renderAPI->integer == 2) { ri.Printf(PRINT_WARNING, "DirectX 12 backend is disabled (code was compiled without ENABLE_DX12). Vulkan backend will be used instead.\n"); } #endif // OpenGL if (get_render_api() == RENDER_API_GL || r_twinMode->integer) { GLimp_Init(); GLint temp; qglGetIntegerv( GL_MAX_TEXTURE_SIZE, &temp ); glConfig.maxTextureSize = temp; gl_active = true; } // VULKAN if (get_render_api() == RENDER_API_VK || r_twinMode->integer) { vk_imp_init(); vk_initialize(); } // DX12 #ifdef ENABLE_DX12 if (get_render_api() == RENDER_API_DX || r_twinMode->integer) { dx_imp_init(); dx_initialize(); } #endif } // init command buffers and SMP R_InitCommandBuffers(); // print info GfxInfo_f(); // 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 ); } /* ** R_GetModeInfo */ typedef struct vidmode_s { const char *description; int width, height; float pixelAspect; // pixel width / height } vidmode_t; vidmode_t r_vidModes[] = { { "Mode 0: 320x240", 320, 240, 1 }, { "Mode 1: 400x300", 400, 300, 1 }, { "Mode 2: 512x384", 512, 384, 1 }, { "Mode 3: 640x480", 640, 480, 1 }, { "Mode 4: 800x600", 800, 600, 1 }, { "Mode 5: 960x720", 960, 720, 1 }, { "Mode 6: 1024x768", 1024, 768, 1 }, { "Mode 7: 1152x864", 1152, 864, 1 }, { "Mode 8: 1280x1024", 1280, 1024, 1 }, { "Mode 9: 1600x1200", 1600, 1200, 1 }, { "Mode 10: 2048x1536", 2048, 1536, 1 }, { "Mode 11: 856x480 (wide)",856, 480, 1 } }; static int s_numVidModes = ( sizeof( r_vidModes ) / sizeof( r_vidModes[0] ) ); qboolean R_GetModeInfo( int *width, int *height, float *windowAspect, int mode ) { vidmode_t *vm; if ( mode < -1 ) { return qfalse; } if ( mode >= s_numVidModes ) { return qfalse; } if ( mode == -1 ) { *width = r_customwidth->integer; *height = r_customheight->integer; *windowAspect = r_customaspect->value; return qtrue; } vm = &r_vidModes[mode]; *width = vm->width; *height = vm->height; *windowAspect = (float)vm->width / ( vm->height * vm->pixelAspect ); return qtrue; } /* ** R_ModeList_f */ static void R_ModeList_f( void ) { int i; ri.Printf( PRINT_ALL, "\n" ); for ( i = 0; i < s_numVidModes; i++ ) { ri.Printf( PRINT_ALL, "%s\n", r_vidModes[i].description ); } ri.Printf( PRINT_ALL, "\n" ); } /* ============================================================================== 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_TakeScreenshot ================== */ void RB_TakeScreenshot( int x, int y, int width, int height, char *fileName ) { byte *buffer; int i, c, temp; buffer = (byte*) ri.Hunk_AllocateTempMemory(glConfig.vidWidth*glConfig.vidHeight*3+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 if (get_render_api() == RENDER_API_GL) { qglReadPixels( x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, buffer+18 ); } else if (get_render_api() == RENDER_API_VK) { // VULKAN byte* buffer2 = (byte*) ri.Hunk_AllocateTempMemory(glConfig.vidWidth*glConfig.vidHeight*4); vk_read_pixels(buffer2); byte* buffer_ptr = buffer + 18; byte* buffer2_ptr = buffer2; for (int i = 0; i < glConfig.vidWidth * glConfig.vidHeight; i++) { buffer_ptr[0] = buffer2_ptr[0]; buffer_ptr[1] = buffer2_ptr[1]; buffer_ptr[2] = buffer2_ptr[2]; buffer_ptr += 3; buffer2_ptr += 4; } ri.Hunk_FreeTempMemory(buffer2); } else if (get_render_api() == RENDER_API_DX) { // DX12 ri.Printf(PRINT_WARNING, "RT_TakeScreenshot is not implemented for DX12"); } // swap rgb to bgr c = 18 + width * height * 3; for (i=18 ; i 0 ) && glConfig.deviceSupportsGamma ) { R_GammaCorrect( buffer + 18, glConfig.vidWidth * glConfig.vidHeight * 3 ); } ri.FS_WriteFile( fileName, buffer, c ); ri.Hunk_FreeTempMemory( buffer ); } /* ================== RB_TakeScreenshotJPEG ================== */ void RB_TakeScreenshotJPEG( int x, int y, int width, int height, char *fileName ) { byte* buffer = (byte*) ri.Hunk_AllocateTempMemory(glConfig.vidWidth*glConfig.vidHeight*4);; if (get_render_api() == RENDER_API_GL) { qglReadPixels( x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer ); } else if (get_render_api() == RENDER_API_VK) { // VULKAN vk_read_pixels(buffer); } else if (get_render_api() == RENDER_API_DX) { // DX12 ri.Printf(PRINT_WARNING, "RT_TakeScreenshotJPEG is not implemented for DX12"); } // gamma correct if ( ( tr.overbrightBits > 0 ) && glConfig.deviceSupportsGamma ) { R_GammaCorrect( buffer, glConfig.vidWidth * glConfig.vidHeight * 4 ); } ri.FS_WriteFile( fileName, buffer, 1 ); // create path SaveJPG( fileName, 95, glConfig.vidWidth, glConfig.vidHeight, buffer); 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 = (screenshotCommand_t*) 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; byte *src, *dst; int x, y; int r, g, b; float xScale, yScale; int xx, yy; sprintf( checkname, "levelshots/%s.tga", tr.world->baseName ); source = (byte*) ri.Hunk_AllocateTempMemory( glConfig.vidWidth * glConfig.vidHeight * 3 ); buffer = (byte*) 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 if (get_render_api() == RENDER_API_GL) { qglReadPixels( 0, 0, glConfig.vidWidth, glConfig.vidHeight, GL_RGB, GL_UNSIGNED_BYTE, source ); } else if (get_render_api() == RENDER_API_VK) { // VULKAN byte* buffer2 = (byte*) ri.Hunk_AllocateTempMemory(glConfig.vidWidth*glConfig.vidHeight*4); vk_read_pixels(buffer2); byte* buffer_ptr = source; byte* buffer2_ptr = buffer2; for (int i = 0; i < glConfig.vidWidth * glConfig.vidHeight; i++) { buffer_ptr[0] = buffer2_ptr[0]; buffer_ptr[1] = buffer2_ptr[1]; buffer_ptr[2] = buffer2_ptr[2]; buffer_ptr += 3; buffer2_ptr += 4; } ri.Hunk_FreeTempMemory(buffer2); } else if (get_render_api() == RENDER_API_DX) { // DX12 ri.Printf(PRINT_WARNING, "R_LevelShot is not implemented for DX12"); } // 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 * (int)( (y*3+yy)*yScale ) + (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 ( ( tr.overbrightBits > 0 ) && 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( source ); 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); } } //============================================================================ /* ** GL_SetDefaultState */ void GL_SetDefaultState( void ) { qglCullFace(GL_FRONT); qglColor3f (1,1,1); // initialize downstream texture unit 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 ); 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 ); } /* ================ GfxInfo_f ================ */ void GfxInfo_f( void ) { const char *enablestrings[] = { "disabled", "enabled" }; const char *fsstrings[] = { "windowed", "fullscreen" }; if (gl_active) { ri.Printf( PRINT_ALL, "\nActive 3D API: OpenGL\n" ); ri.Printf( PRINT_ALL, "GL_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_MAX_TEXTURE_SIZE: %d\n", glConfig.maxTextureSize ); ri.Printf( PRINT_ALL, "GL_MAX_ACTIVE_TEXTURES_ARB: %d\n", glConfig.maxActiveTextures ); ri.Printf( PRINT_ALL, "PIXELFORMAT: color(%d-bits) Z(%d-bit) stencil(%d-bits)\n", glConfig.colorBits, glConfig.depthBits, glConfig.stencilBits ); 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 (glConfig.smpActive) { ri.Printf( PRINT_ALL, "Using dual processor acceleration\n" ); } } // VULKAN if (vk.active) { ri.Printf( PRINT_ALL, "\nActive 3D API: Vulkan\n" ); VkPhysicalDeviceProperties props; vkGetPhysicalDeviceProperties(vk.physical_device, &props); uint32_t major = VK_VERSION_MAJOR(props.apiVersion); uint32_t minor = VK_VERSION_MINOR(props.apiVersion); uint32_t patch = VK_VERSION_PATCH(props.apiVersion); const char* device_type; if (props.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) device_type = "INTEGRATED_GPU"; else if (props.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) device_type = "DISCRETE_GPU"; else if (props.deviceType == VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU) device_type = "VIRTUAL_GPU"; else if (props.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU) device_type = "CPU"; else device_type = "Unknown"; const char* vendor_name = "unknown"; if (props.vendorID == 0x1002) { vendor_name = "Advanced Micro Devices, Inc."; } else if (props.vendorID == 0x10DE) { vendor_name = "NVIDIA"; } else if (props.vendorID == 0x8086) { vendor_name = "Intel Corporation"; } ri.Printf(PRINT_ALL, "Vk api version: %d.%d.%d\n", major, minor, patch); ri.Printf(PRINT_ALL, "Vk driver version: %d\n", props.driverVersion); ri.Printf(PRINT_ALL, "Vk vendor id: 0x%X (%s)\n", props.vendorID, vendor_name); ri.Printf(PRINT_ALL, "Vk device id: 0x%X\n", props.deviceID); ri.Printf(PRINT_ALL, "Vk device type: %s\n", device_type); ri.Printf(PRINT_ALL, "Vk device name: %s\n", props.deviceName); } // DX12 if (dx.active) { ri.Printf( PRINT_ALL, "\nActive 3D API: DirectX 12\n" ); } // // Info that doesn't depend on r_renderAPI // ri.Printf( PRINT_ALL, "\nMODE: %d, %d x %d %s\n", r_mode->integer, glConfig.vidWidth, glConfig.vidHeight, fsstrings[r_fullscreen->integer == 1] ); 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 ); if ( r_vertexLight->integer ) { ri.Printf( PRINT_ALL, "HACK: using vertex lightmap approximation\n" ); } } /* =============== R_Register =============== */ void R_Register( void ) { r_renderAPI = ri.Cvar_Get( "r_renderAPI", "1", CVAR_ARCHIVE | CVAR_LATCH ) ; r_gpu = ri.Cvar_Get( "r_gpu", "0", CVAR_ARCHIVE | CVAR_LATCH ); r_vsync = ri.Cvar_Get( "r_vsync", "0", CVAR_ARCHIVE | CVAR_LATCH ); r_shaderGamma = ri.Cvar_Get("r_shaderGamma", "0", CVAR_ARCHIVE); r_twinMode = ri.Cvar_Get( "r_twinMode", "0", CVAR_LATCH ); // // latched and archived variables // r_glDriver = ri.Cvar_Get( "r_glDriver", OPENGL_DRIVER_NAME, CVAR_ARCHIVE | CVAR_LATCH ); r_ext_compressed_textures = ri.Cvar_Get( "r_ext_compressed_textures", "0", CVAR_ARCHIVE | CVAR_LATCH ); r_ext_gamma_control = ri.Cvar_Get( "r_ext_gamma_control", "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", "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 ); AssertCvarRange( 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_stereo = ri.Cvar_Get( "r_stereo", "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_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", "3", CVAR_ARCHIVE | CVAR_LATCH ); r_fullscreen = ri.Cvar_Get( "r_fullscreen", "1", CVAR_ARCHIVE | CVAR_LATCH ); r_customwidth = ri.Cvar_Get( "r_customwidth", "1600", CVAR_ARCHIVE | CVAR_LATCH ); r_customheight = ri.Cvar_Get( "r_customheight", "1024", CVAR_ARCHIVE | CVAR_LATCH ); r_customaspect = ri.Cvar_Get( "r_customaspect", "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_smp = ri.Cvar_Get( "r_smp", "0", 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_znear = ri.Cvar_Get( "r_znear", "4", CVAR_CHEAT ); AssertCvarRange( r_znear, 0.001f, 200, qtrue ); 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_dynamiclight = ri.Cvar_Get( "r_dynamiclight", "1", 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 ); 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 ); // // 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_showSmp = ri.Cvar_Get ("r_showSmp", "0", CVAR_CHEAT); r_skipBackEnd = ri.Cvar_Get ("r_skipBackEnd", "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_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_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( "modelist", R_ModeList_f ); ri.Cmd_AddCommand( "screenshot", R_ScreenShot_f ); ri.Cmd_AddCommand( "screenshotJPEG", R_ScreenShotJPEG_f ); ri.Cmd_AddCommand( "gfxinfo", GfxInfo_f ); } /* =============== 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 ) ); Com_Memset( &vk_world, 0, sizeof( vk_world ) ); if ( (intptr_t)tess.xyz & 15 ) { Com_Printf( "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 = (byte*) ri.Hunk_Alloc( sizeof( *backEndData[0] ) + sizeof(srfPoly_t) * max_polys + sizeof(polyVert_t) * max_polyverts, h_low); backEndData[0] = (backEndData_t *) ptr; backEndData[0]->polys = (srfPoly_t *) ((char *) ptr + sizeof( *backEndData[0] )); backEndData[0]->polyVerts = (polyVert_t *) ((char *) ptr + sizeof( *backEndData[0] ) + sizeof(srfPoly_t) * max_polys); if ( r_smp->integer ) { ptr = (byte*) ri.Hunk_Alloc( sizeof( *backEndData[1] ) + sizeof(srfPoly_t) * max_polys + sizeof(polyVert_t) * max_polyverts, h_low); backEndData[1] = (backEndData_t *) ptr; backEndData[1]->polys = (srfPoly_t *) ((char *) ptr + sizeof( *backEndData[1] )); backEndData[1]->polyVerts = (polyVert_t *) ((char *) ptr + sizeof( *backEndData[1] ) + sizeof(srfPoly_t) * max_polys); } else { backEndData[1] = NULL; } R_ToggleSmpFrame(); InitRenderAPI(); 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); 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( "modelist" ); ri.Cmd_RemoveCommand( "shaderstate" ); if ( tr.registered ) { R_SyncRenderThread(); R_ShutdownCommandBuffers(); R_DeleteTextures(); } R_DoneFreeType(); // shut down platform specific OpenGL stuff if ( gl_active ) { if (destroyWindow) GLimp_Shutdown(); } // VULKAN if (vk.active) { vk_release_resources(); if (destroyWindow) { vk_shutdown(); vk_imp_shutdown(); } } // DX12 if (dx.active) { dx_release_resources(); if (destroyWindow) { dx_shutdown(); dx_imp_shutdown(); } } tr.registered = qfalse; } /* ============= RE_EndRegistration Touch all images to make sure they are resident ============= */ void RE_EndRegistration( void ) { R_SyncRenderThread(); if (!Sys_LowPhysicalMemory()) { RB_ShowImages(); } // VULKAN if (vk.active) { ri.Printf(PRINT_ALL, "Vulkan: pipelines create time %d msec\n", (int)(vk_world.pipeline_create_time * 1000)); } // DX12 if (dx.active) { ri.Printf(PRINT_ALL, "DX12: pipelines create time %d msec\n", (int)(dx_world.pipeline_create_time * 1000)); } } /* @@@@@@@@@@@@@@@@@@@@@ GetRefAPI @@@@@@@@@@@@@@@@@@@@@ */ refexport_t *GetRefAPI ( int apiVersion, refimport_t *rimp ) { 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.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; return &re; } ================================================ FILE: src/engine/renderer/tr_light.c ================================================ /* =========================================================================== 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 Foobar; 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 = (qboolean) (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[ tr.smpFrame ] = mask; } else if ( *surf->data == SF_GRID ) { ((srfGridMesh_t *)surf->data)->dlightBits[ tr.smpFrame ] = mask; } else if ( *surf->data == SF_TRIANGLES ) { ((srfTriangles_t *)surf->data)->dlightBits[ tr.smpFrame ] = 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 ); // bk010103 - 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] = myftol( ent->ambientLight[0] ); ((byte *)&ent->ambientLightInt)[1] = myftol( ent->ambientLight[1] ); ((byte *)&ent->ambientLightInt)[2] = myftol( 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; // bk010103 - this segfaults with -nolight maps 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; } ================================================ FILE: src/engine/renderer/tr_local.h ================================================ /* =========================================================================== 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 Foobar; 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 "../../game/q_shared.h" #include "../qcommon/qfiles.h" #include "../qcommon/qcommon.h" #include "tr_public.h" #include "qgl.h" // VULKAN #include "vk.h" // DX12 #include "dx.h" #define GL_INDEX_TYPE GL_UNSIGNED_INT typedef unsigned int glIndex_t; // fast float to int conversion #if id386 && !( (defined __linux__ || defined __FreeBSD__ ) && (defined __i386__ ) ) // rb010123 long myftol( float f ); #else #define myftol(x) ((int)(x)) #endif // everything that is needed by the backend needs // to be double buffered to allow it to run in // parallel on a dual cpu machine #define SMP_FRAMES 2 // 12 bits // see QSORT_SHADERNUM_SHIFT #define MAX_SHADERS 16384 // can't be increased without changing bit packing for drawsurfs typedef struct dlight_s { vec3_t origin; vec3_t color; // range from 0.0 to 1.0, should be color normalized float radius; vec3_t transformed; // origin in local coordinate system int additive; // texture detail is lost tho when the lightmap is dark } dlight_t; // a trRefEntity_t has all the information passed in by // the client game, as well as some locally derived info typedef struct { refEntity_t e; float axisLength; // compensate for non-normalized axis qboolean needDlights; // true for bmodels that touch a dlight qboolean lightingCalculated; vec3_t lightDir; // normalized direction towards light vec3_t ambientLight; // color normalized to 0-255 int ambientLightInt; // 32 bit rgba packed vec3_t directedLight; } trRefEntity_t; typedef struct { vec3_t origin; // in world coordinates vec3_t axis[3]; // orientation in world vec3_t viewOrigin; // viewParms->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; qboolean mipmap; qboolean allowPicmip; int wrapClampMode; // GL_CLAMP or GL_REPEAT int index; // this image == tr.images[index] 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 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; // VULKAN VkPipeline vk_pipeline; VkPipeline vk_portal_pipeline; VkPipeline vk_mirror_pipeline; // DX12 ID3D12PipelineState* dx_pipeline; ID3D12PipelineState* dx_portal_pipeline; ID3D12PipelineState* dx_mirror_pipeline; } shaderStage_t; struct shaderCommands_s; #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]; float clampTime; // time this shader is clamped to float timeOffset; // current time offset for this shader struct shader_s *remappedShader; // current shader this one is remapped too struct shader_s *next; } shader_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 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 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; } 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, SF_FLARE, SF_ENTITY, // beams, rails, lightning, etc that can be determined by entity 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 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[SMP_FRAMES]; // 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[SMP_FRAMES]; // 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[SMP_FRAMES]; // 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; 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 } 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 md4Header_t *md4; // only if type == MOD_MD4 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_LIGHTMAPS 256 #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: 21 - 31 : sorted shader index 11 - 20 : entity index 2 - 6 : fog index //2 : used to be clipped flag REMOVED - 03.21.00 rad 0 - 1 : dlightmap index TTimo - 1.32 17-31 : sorted shader index 7-16 : entity index 2-6 : fog index 0-1 : dlightmap index */ #define QSORT_SHADERNUM_SHIFT 17 #define QSORT_ENTITYNUM_SHIFT 7 #define QSORT_FOGNUM_SHIFT 2 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; int texEnv[2]; int faceCulling; unsigned long glStateBits; } glstate_t; typedef struct { int c_surfaces, c_shaders, c_vertexes, c_indexes, c_totalIndexes; int c_dlightVertexes; int c_dlightIndexes; 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 { int smpFrame; trRefdef_t refdef; viewParms_t viewParms; orientationr_t or; backEndCounters_t pc; qboolean isHyperspace; trRefEntity_t *currentEntity; qboolean projection2D; // if qtrue, drawstretchpic doesn't need to change modes byte color2D[4]; 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 viewCount; // incremented every view (twice a scene if portaled) // and every R_MarkFragments call int smpFrame; // toggles from 0 to 1 every endFrame 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 *whiteImage; // full of 0xff image_t *identityLightImage; // full of tr.identityLightByte shader_t *defaultShader; shader_t *cinematicShader; shader_t *shadowShader; shader_t *projectionShadowShader; int numLightmaps; image_t *lightmaps[MAX_LIGHTMAPS]; trRefEntity_t *currentEntity; trRefEntity_t worldEntity; // point currentEntity at this when rendering world int currentEntityNum; int shiftedEntityNum; // currentEntityNum << QSORT_ENTITYNUM_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 // // 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 bool gl_active; // set to true if OpenGL is used for rendering 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 // VULKAN extern Vk_Instance vk; // shouldn't be cleared during ref re-init extern Vk_World vk_world; // this data is cleared during ref re-init // DX12 extern Dx_Instance dx; // shouldn't be cleared during ref re-init extern Dx_World dx_world; // this data is cleared during ref re-init enum RenderApi { RENDER_API_GL, RENDER_API_VK, RENDER_API_DX }; RenderApi get_render_api(); // // cvars // extern cvar_t *r_renderAPI; // 3D API to use: 0 - OpenGL, 1 - Vulkan extern cvar_t *r_gpu; // Select GPU in multi-GPU system (zero-based index, only in Vulkan). By default, GPU 0 is selected. extern cvar_t *r_vsync; // Enable vsync in Vulkan (com_maxFPS may be set to 0) extern cvar_t *r_shaderGamma; // Use compute shader to apply gamma (only in Vulkan) instead of legacy HW gamma API. extern cvar_t *r_twinMode; // Debug feature to compare rendering output between OpenGL/Vulkan APIs extern cvar_t *r_railWidth; extern cvar_t *r_railCoreWidth; extern cvar_t *r_railSegmentLength; extern cvar_t *r_verbose; // used for verbose debug spew extern cvar_t *r_znear; // near Z clip plane extern cvar_t *r_stencilbits; // number of desired stencil bits extern cvar_t *r_depthbits; // number of desired depth bits extern cvar_t *r_stereo; // desired pixelformat stereo flag extern cvar_t *r_texturebits; // number of desired texture bits // 0 = use framebuffer depth // 16 = use 16-bit textures // 32 = use 32-bit textures // all else = error 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_dynamiclight; // dynamic lights enabled/disabled 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_gamma; extern cvar_t *r_ignorehwgamma; // overrides hardware gamma capabilities extern cvar_t *r_ext_compressed_textures; // these control use of specific extensions extern cvar_t *r_ext_gamma_control; extern cvar_t *r_ext_texenv_op; extern cvar_t *r_ext_compiled_vertex_array; extern cvar_t *r_ext_texture_env_add; 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_drawBuffer; extern cvar_t *r_glDriver; 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_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_smp; extern cvar_t *r_showSmp; extern cvar_t *r_skipBackEnd; 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; //==================================================================== float R_NoiseGet4f( float x, float y, float z, float t ); void R_NoiseInit( void ); void R_RenderView( viewParms_t *parms ); void R_AddMD3Surfaces( 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_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 ); 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 ); // // 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_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 ); qboolean GLimp_SpawnRenderThread( void (*function)( void ) ); void *GLimp_RendererSleep( void ); void GLimp_FrontEndSleep( void ); void GLimp_WakeRenderer( void *data ); void vk_imp_init(); void vk_imp_shutdown(); void vk_imp_create_surface(); void dx_imp_init(); void dx_imp_shutdown(); // 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 mapping[256] ); void GLimp_RestoreGamma(); /* ==================================================================== 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]; vec4_t xyz[SHADER_MAX_VERTEXES]; vec4_t normal[SHADER_MAX_VERTEXES]; vec2_t texCoords[SHADER_MAX_VERTEXES][2]; color4ub_t vertexColors[SHADER_MAX_VERTEXES]; int vertexDlightBits[SHADER_MAX_VERTEXES]; stageVars_t svars; color4ub_t constantColor255[SHADER_MAX_VERTEXES]; 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; 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_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 ); /* ============================================================ 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_InitSkyTexCoords( float cloudLayerHeight ); /* ============================================================ 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_ToggleSmpFrame( 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 ); /* ============================================================= ANIMATED MODELS ============================================================= */ void R_AddAnimSurfaces( trRefEntity_t *ent ); void RB_SurfaceAnim( md4Surface_t *surfType ); /* ============================================================= ============================================================= */ void R_TransformModelToClip( const vec3_t src, const float *modelMatrix, const float *projectionMatrix, vec4_t eye, vec4_t dst ); 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 ); void myGlMultMatrix( const float *a, const float *b, float *out ); /* ============================================================= RENDERER BACK END FUNCTIONS ============================================================= */ void RB_RenderThread( void ); 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 enum { RC_END_OF_LIST, RC_SET_COLOR, RC_STRETCH_PIC, RC_DRAW_SURFS, RC_DRAW_BUFFER, RC_SWAP_BUFFERS, RC_SCREENSHOT } 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. This entire structure is // duplicated so the front and back end can run in parallel // on an SMP machine typedef struct { drawSurf_t drawSurfs[MAX_DRAWSURFS]; dlight_t dlights[MAX_DLIGHTS]; trRefEntity_t entities[MAX_ENTITIES]; 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[SMP_FRAMES]; // the second one may not be allocated extern volatile renderCommandList_t *renderCommandList; extern volatile qboolean renderThreadActive; void *R_GetCommandBuffer( int bytes ); void RB_ExecuteRenderCommands( const void *data ); void R_InitCommandBuffers( void ); void R_ShutdownCommandBuffers( void ); void R_SyncRenderThread( void ); void R_AddDrawSurfCmd( drawSurf_t *drawSurfs, int numDrawSurfs ); void RE_SetColor( const float *rgba ); 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 SaveJPG(char * filename, int quality, int image_width, int image_height, unsigned char *image_buffer); // font stuff void R_InitFreeType(); void R_DoneFreeType(); void RE_RegisterFont(const char *fontName, int pointSize, fontInfo_t *font); #endif //TR_LOCAL_H ================================================ FILE: src/engine/renderer/tr_main.c ================================================ /* =========================================================================== 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 Foobar; 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" 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 ]; } } /* ========================== myGlMultMatrix ========================== */ // // NOTE; out = b * a, // a, b and c are specified in column-major order // 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 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_SetupProjection =============== */ void R_SetupProjection( void ) { float xmin, xmax, ymin, ymax; float width, height, depth; float zNear, zFar; // dynamically compute far clip plane distance SetFarClip(); // // set up projection matrix // zNear = r_znear->value; zFar = tr.viewParms.zFar; ymax = zNear * tan( tr.refdef.fov_y * M_PI / 360.0f ); ymin = -ymax; xmax = zNear * tan( tr.refdef.fov_x * M_PI / 360.0f ); xmin = -xmax; width = xmax - xmin; height = ymax - ymin; depth = zFar - zNear; tr.viewParms.projectionMatrix[0] = 2 * zNear / width; tr.viewParms.projectionMatrix[4] = 0; tr.viewParms.projectionMatrix[8] = ( xmax + xmin ) / width; // normally 0 tr.viewParms.projectionMatrix[12] = 0; tr.viewParms.projectionMatrix[1] = 0; tr.viewParms.projectionMatrix[5] = 2 * zNear / height; tr.viewParms.projectionMatrix[9] = ( ymax + ymin ) / height; // normally 0 tr.viewParms.projectionMatrix[13] = 0; tr.viewParms.projectionMatrix[2] = 0; tr.viewParms.projectionMatrix[6] = 0; tr.viewParms.projectionMatrix[10] = -( zFar + zNear ) / depth; tr.viewParms.projectionMatrix[14] = -2 * zFar * zNear / depth; tr.viewParms.projectionMatrix[3] = 0; tr.viewParms.projectionMatrix[7] = 0; tr.viewParms.projectionMatrix[11] = -1; tr.viewParms.projectionMatrix[15] = 0; } /* ================= R_SetupFrustum Setup that culling frustum planes for the current view ================= */ void R_SetupFrustum (void) { int i; float xs, xc; float ang; ang = tr.viewParms.fovX / 180 * M_PI * 0.5f; xs = sin( ang ); xc = cos( ang ); VectorScale( tr.viewParms.or.axis[0], xs, tr.viewParms.frustum[0].normal ); VectorMA( tr.viewParms.frustum[0].normal, xc, tr.viewParms.or.axis[1], tr.viewParms.frustum[0].normal ); VectorScale( tr.viewParms.or.axis[0], xs, tr.viewParms.frustum[1].normal ); VectorMA( tr.viewParms.frustum[1].normal, -xc, tr.viewParms.or.axis[1], tr.viewParms.frustum[1].normal ); ang = tr.viewParms.fovY / 180 * M_PI * 0.5f; xs = sin( ang ); xc = cos( ang ); VectorScale( tr.viewParms.or.axis[0], xs, tr.viewParms.frustum[2].normal ); VectorMA( tr.viewParms.frustum[2].normal, xc, tr.viewParms.or.axis[2], tr.viewParms.frustum[2].normal ); VectorScale( tr.viewParms.or.axis[0], xs, tr.viewParms.frustum[3].normal ); VectorMA( tr.viewParms.frustum[3].normal, -xc, tr.viewParms.or.axis[2], tr.viewParms.frustum[3].normal ); for (i=0 ; i<4 ; i++) { tr.viewParms.frustum[i].type = PLANE_NON_AXIAL; tr.viewParms.frustum[i].dist = DotProduct (tr.viewParms.or.origin, tr.viewParms.frustum[i].normal); SetPlaneSignbits( &tr.viewParms.frustum[i] ); } } /* ================= 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 != ENTITYNUM_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 != ENTITYNUM_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; if ( glConfig.smpActive ) { // FIXME! we can't do RB_BeginSurface/RB_EndSurface stuff with smp! return qfalse; } 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 ) { tess.numIndexes = 0; 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 dot; 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 ( ( dot = DotProduct( normal, tess.normal[tess.indexes[i]] ) ) >= 0 ) { numTriangles--; } } tess.numIndexes = 0; 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 ========================================================================================== */ /* ================= qsort replacement ================= */ ID_INLINE void SWAP_DRAW_SURF(void* a, void* b) { char buf[sizeof(drawSurf_t)]; Com_Memcpy(buf, a, sizeof(drawSurf_t)); Com_Memcpy(a, b, sizeof(drawSurf_t)); Com_Memcpy(b, buf, sizeof(drawSurf_t)); } /* this parameter defines the cutoff between using quick sort and insertion sort for arrays; arrays with lengths shorter or equal to the below value use insertion sort */ #define CUTOFF 8 /* testing shows that this is good value */ static void shortsort( drawSurf_t *lo, drawSurf_t *hi ) { drawSurf_t *p, *max; while (hi > lo) { max = lo; for (p = lo + 1; p <= hi; p++ ) { if ( p->sort > max->sort ) { max = p; } } SWAP_DRAW_SURF(max, hi); hi--; } } /* sort the array between lo and hi (inclusive) FIXME: this was lifted and modified from the microsoft lib source... */ void qsortFast ( void *base, unsigned num, unsigned width ) { char *lo, *hi; /* ends of sub-array currently sorting */ char *mid; /* points to middle of subarray */ char *loguy, *higuy; /* traveling pointers for partition step */ unsigned size; /* size of the sub-array */ char *lostk[30], *histk[30]; int stkptr; /* stack for saving sub-array to be processed */ /* Note: the number of stack entries required is no more than 1 + log2(size), so 30 is sufficient for any array */ if (num < 2 || width == 0) return; /* nothing to do */ stkptr = 0; /* initialize stack */ lo = (char*) base; hi = (char *)base + width * (num-1); /* initialize limits */ /* this entry point is for pseudo-recursion calling: setting lo and hi and jumping to here is like recursion, but stkptr is prserved, locals aren't, so we preserve stuff on the stack */ recurse: size = (hi - lo) / width + 1; /* number of el's to sort */ /* below a certain size, it is faster to use a O(n^2) sorting method */ if (size <= CUTOFF) { shortsort((drawSurf_t *)lo, (drawSurf_t *)hi); } else { /* First we pick a partititioning element. The efficiency of the algorithm demands that we find one that is approximately the median of the values, but also that we select one fast. Using the first one produces bad performace if the array is already sorted, so we use the middle one, which would require a very wierdly arranged array for worst case performance. Testing shows that a median-of-three algorithm does not, in general, increase performance. */ mid = lo + (size / 2) * width; /* find middle element */ SWAP_DRAW_SURF(mid, lo); /* swap it to beginning of array */ /* We now wish to partition the array into three pieces, one consisiting of elements <= partition element, one of elements equal to the parition element, and one of element >= to it. This is done below; comments indicate conditions established at every step. */ loguy = lo; higuy = hi + width; /* Note that higuy decreases and loguy increases on every iteration, so loop must terminate. */ for (;;) { /* lo <= loguy < hi, lo < higuy <= hi + 1, A[i] <= A[lo] for lo <= i <= loguy, A[i] >= A[lo] for higuy <= i <= hi */ do { loguy += width; } while (loguy <= hi && ( ((drawSurf_t *)loguy)->sort <= ((drawSurf_t *)lo)->sort ) ); /* lo < loguy <= hi+1, A[i] <= A[lo] for lo <= i < loguy, either loguy > hi or A[loguy] > A[lo] */ do { higuy -= width; } while (higuy > lo && ( ((drawSurf_t *)higuy)->sort >= ((drawSurf_t *)lo)->sort ) ); /* lo-1 <= higuy <= hi, A[i] >= A[lo] for higuy < i <= hi, either higuy <= lo or A[higuy] < A[lo] */ if (higuy < loguy) break; /* if loguy > hi or higuy <= lo, then we would have exited, so A[loguy] > A[lo], A[higuy] < A[lo], loguy < hi, highy > lo */ SWAP_DRAW_SURF(loguy, higuy); /* A[loguy] < A[lo], A[higuy] > A[lo]; so condition at top of loop is re-established */ } /* A[i] >= A[lo] for higuy < i <= hi, A[i] <= A[lo] for lo <= i < loguy, higuy < loguy, lo <= higuy <= hi implying: A[i] >= A[lo] for loguy <= i <= hi, A[i] <= A[lo] for lo <= i <= higuy, A[i] = A[lo] for higuy < i < loguy */ SWAP_DRAW_SURF(lo, higuy); /* put partition element in place */ /* OK, now we have the following: A[i] >= A[higuy] for loguy <= i <= hi, A[i] <= A[higuy] for lo <= i < higuy A[i] = A[lo] for higuy <= i < loguy */ /* We've finished the partition, now we want to sort the subarrays [lo, higuy-1] and [loguy, hi]. We do the smaller one first to minimize stack usage. We only sort arrays of length 2 or more.*/ if ( higuy - 1 - lo >= hi - loguy ) { if (lo + width < higuy) { lostk[stkptr] = lo; histk[stkptr] = higuy - width; ++stkptr; } /* save big recursion for later */ if (loguy < hi) { lo = loguy; goto recurse; /* do small recursion */ } } else { if (loguy < hi) { lostk[stkptr] = loguy; histk[stkptr] = hi; ++stkptr; /* save big recursion for later */ } if (lo + width < higuy) { hi = higuy - width; goto recurse; /* do small recursion */ } } } /* We have sorted the array, except for any pending sorts on the stack. Check if there are any, and do them. */ --stkptr; if (stkptr >= 0) { lo = lostk[stkptr]; hi = histk[stkptr]; goto recurse; /* pop subarray from stack */ } else return; /* all subarrays done */ } //========================================================================================== /* ================= 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_ENTITYNUM_SHIFT ) & 1023; *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 qsortFast (drawSurfs, numDrawSurfs, sizeof(drawSurf_t) ); // 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_ENTITYNUM_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; case MOD_BRUSH: R_AddBrushModelSurfaces( ent ); break; case MOD_BAD: // null model axis if ( (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal) { break; } shader = R_GetShaderByHandle( ent->e.customShader ); 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 R_SetupProjection (); R_AddEntitySurfaces (); } /* ================ R_DebugPolygon ================ */ void R_DebugPolygon( int color, int numPoints, float *points ) { 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 (int 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 (int i = 0 ; i < numPoints ; i++ ) { qglVertex3fv( points + i * 3 ); } qglEnd(); qglDepthRange( 0, 1 ); // VULKAN // DX12 if (!vk.active && !dx.active) return; if (numPoints < 3 || numPoints >= SHADER_MAX_VERTEXES/2) return; // In Vulkan we don't have GL_POLYGON + GLS_POLYMODE_LINE equivalent, so we use lines to draw polygon outlines. // This approach has additional implication that we need to do manual backface culling to reject outlines that // belong to back facing polygons. // The code assumes that polygons are convex. // Backface culling. auto transform_to_eye_space = [](vec3_t v, vec3_t v_eye) { auto m = vk.active ? vk_world.modelview_transform : dx_world.modelview_transform; v_eye[0] = m[0]*v[0] + m[4]*v[1] + m[8 ]*v[2] + m[12]; v_eye[1] = m[1]*v[0] + m[5]*v[1] + m[9 ]*v[2] + m[13]; v_eye[2] = m[2]*v[0] + m[6]*v[1] + m[10]*v[2] + m[14]; }; vec3_t pa; vec3_t pb; transform_to_eye_space(&points[0], pa); transform_to_eye_space(&points[3], pb); vec3_t p; VectorSubtract(pb, pa, p); vec3_t n; for (int i = 2; i < numPoints; i++) { transform_to_eye_space(&points[3*i], pb); vec3_t q; VectorSubtract(pb, pa, q); CrossProduct(q, p, n); if (VectorLength(n) > 1e-5) break; } if (DotProduct(n, pa) >= 0) return; // discard backfacing polygon // Solid shade. for (int i = 0; i < numPoints; i++) { VectorCopy(&points[3*i], tess.xyz[i]); tess.svars.colors[i][0] = (color&1) ? 255 : 0; tess.svars.colors[i][1] = (color&2) ? 255 : 0; tess.svars.colors[i][2] = (color&4) ? 255 : 0; tess.svars.colors[i][3] = 255; } tess.numVertexes = numPoints; tess.numIndexes = 0; for (int i = 1; i < numPoints - 1; i++) { tess.indexes[tess.numIndexes + 0] = 0; tess.indexes[tess.numIndexes + 1] = i; tess.indexes[tess.numIndexes + 2] = i + 1; tess.numIndexes += 3; } if (vk.active) { vk_bind_geometry(); vk_shade_geometry(vk.surface_debug_pipeline_solid, false, Vk_Depth_Range::normal); } if (dx.active) { dx_bind_geometry(); dx_shade_geometry(dx.surface_debug_pipeline_solid, false, Vk_Depth_Range::normal, true, false); } // Outline. Com_Memset(tess.svars.colors, tr.identityLightByte, numPoints * 2 * sizeof(color4ub_t)); for (int i = 0; i < numPoints; i++) { VectorCopy(&points[3*i], tess.xyz[2*i]); VectorCopy(&points[3*((i + 1) % numPoints)], tess.xyz[2*i + 1]); } tess.numVertexes = numPoints * 2; tess.numIndexes = 0; if (vk.active) { vk_bind_geometry(); vk_shade_geometry(vk.surface_debug_pipeline_outline, false, Vk_Depth_Range::force_zero, false); } if (dx.active) { dx_bind_geometry(); dx_shade_geometry(dx.surface_debug_pipeline_outline, false, Vk_Depth_Range::force_zero, false, true); } tess.numVertexes = 0; } /* ==================== R_DebugGraphics Visualization aid for movement clipping debugging ==================== */ void R_DebugGraphics( void ) { if ( !r_debugSurface->integer ) { return; } // the render thread can't make callbacks to the main thread R_SyncRenderThread(); 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.frameCount = tr.frameCount; firstDrawSurf = tr.refdef.numDrawSurfs; tr.viewCount++; // set viewParms.world R_RotateForViewer (); R_SetupFrustum (); R_GenerateDrawSurfs(); R_SortDrawSurfs( tr.refdef.drawSurfs + firstDrawSurf, tr.refdef.numDrawSurfs - firstDrawSurf ); // draw main system development information (surface outlines, etc) R_DebugGraphics(); } ================================================ FILE: src/engine/renderer/tr_marks.c ================================================ /* =========================================================================== 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 Foobar; 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) 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; srfSurfaceFace_t *surf; srfGridMesh_t *cv; drawVert_t *dv; vec3_t normal; vec3_t projectionDir; vec3_t v1, v2; int *indexes; //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) { surf = ( srfSurfaceFace_t * ) surfaces[i]; // check the normal of this face if (DotProduct(surf->plane.normal, projectionDir) > -0.5) { continue; } /* VectorSubtract(clipPoints[0][0], clipPoints[0][1], v1); VectorSubtract(clipPoints[0][2], clipPoints[0][1], v2); CrossProduct(v1, v2, normal); VectorNormalize(normal); if (DotProduct(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 } } continue; } else { // ignore all other world surfaces // might be cool to also project polygons on a triangle soup // however this will probably create huge amounts of extra polys // even more than the projection onto curves continue; } } return returnedFragments; } ================================================ FILE: src/engine/renderer/tr_mesh.c ================================================ /* =========================================================================== 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 Foobar; 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; 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 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 = myftol( 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 = 0; md3Surface_t *surface = 0; md3Shader_t *md3Shader = 0; shader_t *shader = 0; int cull; int lod; int fogNum; qboolean personalModel; // don't add third_person objects if not in a portal personalModel = (qboolean) ((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( (surfaceType_t*) (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((surfaceType_t*) (void *)surface, tr.projectionShadowShader, 0, qfalse); } // don't add third_person objects if not viewing through a portal if ( !personalModel ) { R_AddDrawSurf((surfaceType_t*) (void *)surface, shader, fogNum, qfalse); } surface = (md3Surface_t *)( (byte *)surface + surface->ofsEnd ); } } ================================================ FILE: src/engine/renderer/tr_model.c ================================================ /* =========================================================================== 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 Foobar; 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 ); model_t *loadmodel; /* ** 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 = (model_t*) 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; unsigned *buf; int lod; int ident; qboolean loaded; qhandle_t hModel; int numLoaded; if ( !name || !name[0] ) { ri.Printf( PRINT_ALL, "RE_RegisterModel: NULL name\n" ); return 0; } if ( (int)strlen( name ) >= MAX_QPATH ) { Com_Printf( "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 ) ); // make sure the render thread is stopped R_SyncRenderThread(); mod->numLods = 0; // // load the files // numLoaded = 0; for ( lod = MD3_MAX_LODS - 1 ; lod >= 0 ; lod-- ) { char filename[1024]; strcpy( filename, name ); if ( lod != 0 ) { char namebuf[80]; if ( strrchr( filename, '.' ) ) { *strrchr( filename, '.' ) = 0; } sprintf( namebuf, "_%d.md3", lod ); strcat( filename, namebuf ); } ri.FS_ReadFile( filename, (void **)&buf ); if ( !buf ) { continue; } loadmodel = mod; ident = LittleLong(*(unsigned *)buf); if ( ident == MD4_IDENT ) { loaded = R_LoadMD4( mod, buf, name ); } else { if ( ident != MD3_IDENT ) { ri.Printf (PRINT_WARNING,"RE_RegisterModel: unknown fileid for %s\n", name); goto fail; } loaded = R_LoadMD3( mod, lod, buf, name ); } ri.FS_FreeFile (buf); if ( !loaded ) { if ( lod == 0 ) { goto fail; } else { break; } } else { mod->numLods++; numLoaded++; // if we have a valid model and are biased // so that we won't see any higher detail ones, // stop loading them // if ( lod <= r_lodbias->integer ) { // 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 else { ri.Printf (PRINT_WARNING,"RE_RegisterModel: couldn't load %s\n", name); } #endif fail: // we still keep the model_t around, so if the model name is asked for // again, we won't bother scanning the filesystem mod->type = MOD_BAD; return 0; } /* ================= 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] = (md3Header_t*) 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.Error (ERR_DROP, "R_LoadMD3: %s has more than %i verts on a surface (%i)", mod_name, SHADER_MAX_VERTEXES, surf->numVerts ); } if ( surf->numTriangles*3 > SHADER_MAX_INDEXES ) { ri.Error (ERR_DROP, "R_LoadMD3: %s has more than %i triangles on a surface (%i)", mod_name, SHADER_MAX_INDEXES / 3, surf->numTriangles ); } // 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 = (int)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; } /* ================= 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; md4 = mod->md4 = (md4Header_t*) ri.Hunk_Alloc( size, h_low ); Com_Memcpy( md4, buffer, LittleLong(pinmodel->ofsEnd) ); LL(md4->ident); LL(md4->version); LL(md4->numFrames); LL(md4->numBones); LL(md4->numLODs); LL(md4->ofsFrames); LL(md4->ofsLODs); LL(md4->ofsEnd); 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 = (int)(intptr_t)( &((md4Frame_t *)0)->bones[ md4->numBones ] ); for ( i = 0 ; i < md4->numFrames ; i++, frame++) { 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.Error (ERR_DROP, "R_LoadMD3: %s has more than %i verts on a surface (%i)", mod_name, SHADER_MAX_VERTEXES, surf->numVerts ); } if ( surf->numTriangles*3 > SHADER_MAX_INDEXES ) { ri.Error (ERR_DROP, "R_LoadMD3: %s has more than %i triangles on a surface (%i)", mod_name, SHADER_MAX_INDEXES / 3, surf->numTriangles ); } // 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_SyncRenderThread(); tr.viewCluster = -1; // force markleafs to regenerate RE_ClearScene(); tr.registered = qtrue; } //============================================================================= /* =============== 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; } /* ================ R_LerpTag ================ */ int R_LerpTag( orientation_t *tag, qhandle_t handle, int startFrame, int endFrame, float frac, const char *tagName ) { md3Tag_t *start, *end; int i; float frontLerp, backLerp; model_t *model; model = R_GetModelByHandle( handle ); if ( !model->md3[0] ) { AxisClear( tag->axis ); VectorClear( tag->origin ); return qfalse; } 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; md3Header_t *header; md3Frame_t *frame; model = R_GetModelByHandle( handle ); if ( model->bmodel ) { VectorCopy( model->bmodel->bounds[0], mins ); VectorCopy( model->bmodel->bounds[1], maxs ); return; } if ( !model->md3[0] ) { VectorClear( mins ); VectorClear( maxs ); return; } header = model->md3[0]; frame = (md3Frame_t *)( (byte *)header + header->ofsFrames ); VectorCopy( frame->bounds[0], mins ); VectorCopy( frame->bounds[1], maxs ); } ================================================ FILE: src/engine/renderer/tr_noise.c ================================================ /* =========================================================================== 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 Foobar; 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]; #define LERP( a, b, w ) ( a * ( 1.0f - w ) + b * w ) 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; srand( 1001 ); 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; } ================================================ FILE: src/engine/renderer/tr_public.h ================================================ /* =========================================================================== 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 Foobar; 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 "../../cgame/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 (*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 ); } 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, ...); // abort the game void (QDECL *Error)( int errorLevel, const char *fmt, ...); // 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 (*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); // 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 ); int (*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); } 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. refexport_t*GetRefAPI( int apiVersion, refimport_t *rimp ); #endif // __TR_PUBLIC_H ================================================ FILE: src/engine/renderer/tr_scene.c ================================================ /* =========================================================================== 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 Foobar; 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_ToggleSmpFrame ==================== */ void R_ToggleSmpFrame( void ) { if ( r_smp->integer ) { // use the other buffers next frame, because another CPU // may still be rendering into the current ones tr.smpFrame ^= 1; } else { tr.smpFrame = 0; } backEndData[tr.smpFrame]->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 = ENTITYNUM_WORLD; tr.shiftedEntityNum = tr.currentEntityNum << QSORT_ENTITYNUM_SHIFT; for ( i = 0, poly = tr.refdef.polys; i < tr.refdef.numPolys ; i++, poly++ ) { sh = R_GetShaderByHandle( poly->hShader ); R_AddDrawSurf( (surfaceType_t*) ( 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[tr.smpFrame]->polys[r_numpolys]; poly->surfaceType = SF_POLY; poly->hShader = hShader; poly->numVerts = numVerts; poly->verts = &backEndData[tr.smpFrame]->polyVerts[r_numpolyverts]; Com_Memcpy( poly->verts, &verts[numVerts*j], numVerts * sizeof( *verts ) ); // 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; } // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=402 if ( r_numentities >= ENTITYNUM_WORLD ) { return; } if ( ent->reType < 0 || ent->reType >= RT_MAX_REF_ENTITY_TYPE ) { ri.Error( ERR_DROP, "RE_AddRefEntityToScene: bad reType %i", ent->reType ); } backEndData[tr.smpFrame]->entities[r_numentities].e = *ent; backEndData[tr.smpFrame]->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; } dl = &backEndData[tr.smpFrame]->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[tr.smpFrame]->drawSurfs; tr.refdef.num_entities = r_numentities - r_firstSceneEntity; tr.refdef.entities = &backEndData[tr.smpFrame]->entities[r_firstSceneEntity]; tr.refdef.num_dlights = r_numdlights - r_firstSceneDlight; tr.refdef.dlights = &backEndData[tr.smpFrame]->dlights[r_firstSceneDlight]; tr.refdef.numPolys = r_numpolys - r_firstScenePoly; tr.refdef.polys = &backEndData[tr.smpFrame]->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 ) { tr.refdef.num_dlights = 0; } // 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; 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; } ================================================ FILE: src/engine/renderer/tr_shade.c ================================================ /* =========================================================================== 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 Foobar; 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" /* THIS ENTIRE FILE IS BACK END This file deals with applying shaders to surface data in the tess struct. */ /* ================== R_DrawElements ================== */ static void R_DrawElements( int numIndexes, const glIndex_t *indexes ) { qglDrawElements(GL_TRIANGLES, numIndexes, GL_INDEX_TYPE, indexes); } /* ============================================================= 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 = myftol( 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 ); // VULKAN if (vk.active) { Com_Memset(tess.svars.colors, tr.identityLightByte, tess.numVertexes * 4 ); auto pipeline = backEnd.viewParms.isMirror ? vk.tris_mirror_debug_pipeline : vk.tris_debug_pipeline; vk_shade_geometry(pipeline, false, Vk_Depth_Range::force_zero); } // DX12 if (dx.active) { Com_Memset(tess.svars.colors, tr.identityLightByte, tess.numVertexes * 4 ); auto pipeline = backEnd.viewParms.isMirror ? dx.tris_mirror_debug_pipeline : dx.tris_debug_pipeline; dx_shade_geometry(pipeline, false, Vk_Depth_Range::force_zero, true, false); } } /* ================ 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 ); // VULKAN // DX12 if (vk.active || dx.active) { vec4_t xyz[SHADER_MAX_VERTEXES]; Com_Memcpy(xyz, tess.xyz, tess.numVertexes * sizeof(vec4_t)); Com_Memset(tess.svars.colors, tr.identityLightByte, SHADER_MAX_VERTEXES * sizeof(color4ub_t)); int numVertexes = tess.numVertexes; i = 0; while (i < numVertexes) { int count = numVertexes - i; if (count >= SHADER_MAX_VERTEXES/2 - 1) count = SHADER_MAX_VERTEXES/2 - 1; for (int k = 0; k < count; k++) { VectorCopy(xyz[i + k], tess.xyz[2*k]); VectorMA(xyz[i + k], 2, input->normal[i + k], tess.xyz[2*k + 1]); } tess.numVertexes = 2 * count; tess.numIndexes = 0; if (vk.active) { vk_bind_geometry(); vk_shade_geometry(vk.normals_debug_pipeline, false, Vk_Depth_Range::force_zero, false); } if (dx.active) { dx_bind_geometry(); dx_shade_geometry(dx.normals_debug_pipeline, false, Vk_Depth_Range::force_zero, false, true); } i += count; } } } /* ============== 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.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 =================== */ static void ProjectDlightTexture( void ) { int i, l; vec3_t origin; float *texCoords; byte *colors; byte clipBits[SHADER_MAX_VERTEXES]; unsigned hitIndexes[SHADER_MAX_INDEXES]; int numIndexes; float scale; float radius; vec3_t floatColor; float modulate; 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 = tess.svars.texcoords[0][0]; colors = tess.svars.colors[0]; dl = &backEnd.refdef.dlights[l]; VectorCopy( dl->transformed, origin ); radius = dl->radius; scale = 1.0f / radius; 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 ) { vec3_t dist; int clip; backEnd.pc.c_dlightVertexes++; VectorSubtract( origin, tess.xyz[i], dist ); texCoords[0] = 0.5f + dist[0] * scale; texCoords[1] = 0.5f + dist[1] * scale; clip = 0; 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; } // 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] = myftol(floatColor[0] * modulate); colors[1] = myftol(floatColor[1] * modulate); colors[2] = myftol(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, tess.svars.texcoords[0] ); qglEnableClientState( GL_COLOR_ARRAY ); qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, tess.svars.colors ); 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; // VULKAN if (vk.active) { VkPipeline pipeline = vk.dlight_pipelines[dl->additive > 0 ? 1 : 0][tess.shader->cullType][tess.shader->polygonOffset]; vk_shade_geometry(pipeline, false, Vk_Depth_Range::normal); } // DX12 if (dx.active) { auto pipeline = dx.dlight_pipelines[dl->additive > 0 ? 1 : 0][tess.shader->cullType][tess.shader->polygonOffset]; dx_shade_geometry(pipeline, false, Vk_Depth_Range::normal, true, false); } } } /* =================== 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 ); // VULKAN if (vk.active) { assert(tess.shader->fogPass > 0); VkPipeline pipeline = vk.fog_pipelines[tess.shader->fogPass - 1][tess.shader->cullType][tess.shader->polygonOffset]; vk_shade_geometry(pipeline, false, Vk_Depth_Range::normal); } // DX12 if (dx.active) { assert(tess.shader->fogPass > 0); auto pipeline = dx.fog_pipelines[tess.shader->fogPass - 1][tess.shader->cullType][tess.shader->polygonOffset]; dx_shade_geometry(pipeline, false, Vk_Depth_Range::normal, true, false); } } /* =============== 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; } } } /* =============== 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'\n", pStage->bundle[b].texMods[tm].type, tess.shader->name ); break; } } } } /* ** RB_IterateStagesGeneric */ static void RB_IterateStagesGeneric( shaderCommands_t *input ) { // VULKAN if (vk.active) vk_bind_geometry(); // DX12 if (dx.active) dx_bind_geometry(); for ( int 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 // bool multitexture = (pStage->bundle[1].image[0] != nullptr); if ( multitexture ) { DrawMultitextured( input, stage ); } else { if ( !setArraysOnce ) { qglTexCoordPointer( 2, GL_FLOAT, 0, input->svars.texcoords[0] ); } // // set state // R_BindAnimatedImage( &pStage->bundle[0] ); GL_State( pStage->stateBits ); // // draw // R_DrawElements( input->numIndexes, input->indexes ); } // VULKAN // DX12 if (vk.active || dx.active) { VkPipeline vk_pipeline = pStage->vk_pipeline; ID3D12PipelineState* dx_pipeline = pStage->dx_pipeline; if (backEnd.viewParms.isMirror) { vk_pipeline = pStage->vk_mirror_pipeline; dx_pipeline = pStage->dx_mirror_pipeline; } else if (backEnd.viewParms.isPortal) { vk_pipeline = pStage->vk_portal_pipeline; dx_pipeline = pStage->dx_portal_pipeline; } Vk_Depth_Range depth_range = Vk_Depth_Range::normal; if (input->shader->isSky) { depth_range = Vk_Depth_Range::force_one; if (r_showsky->integer) depth_range = Vk_Depth_Range::force_zero; } else if (backEnd.currentEntity->e.renderfx & RF_DEPTHHACK) { depth_range = Vk_Depth_Range::weapon; } if (r_lightmap->integer && multitexture) GL_Bind(tr.whiteImage); // replace diffuse texture with a white one thus effectively render only lightmap if (vk.active) vk_shade_geometry(vk_pipeline, multitexture, depth_range); if (dx.active) dx_shade_geometry(dx_pipeline, multitexture, depth_range, true, false); } // allow skipping out to show just lightmaps during development if ( r_lightmap->integer && ( pStage->bundle[0].isLightmap || pStage->bundle[1].isLightmap ) ) { break; } } } /* ** RB_StageIteratorGeneric */ void RB_StageIteratorGeneric( void ) { shaderCommands_t *input; input = &tess; 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( input->shader->cullType ); // set polygon offset if necessary if ( input->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 || input->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 ( input->shader->polygonOffset ) { qglDisable( GL_POLYGON_OFFSET_FILL ); } } /* ** RB_EndSurface */ void RB_EndSurface( void ) { shaderCommands_t *input; input = &tess; if (input->numIndexes == 0) { return; } if (tess.shader->isSky && r_fastsky->integer) { 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 // if (tess.shader->isSky) RB_StageIteratorSky(); else RB_StageIteratorGeneric(); // // 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; GLimp_LogComment( "----------\n" ); } ================================================ FILE: src/engine/renderer/tr_shade_calc.c ================================================ /* =========================================================================== 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 Foobar; 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" #define WAVEVALUE( table, base, amplitude, phase, freq ) ((base) + table[ myftol( ( ( (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'\n", 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 = (int)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", tess.shader->name ); } if ( tess.numIndexes != ( tess.numVertexes >> 2 ) * 6 ) { ri.Printf( PRINT_WARNING, "Autosprite shader %s had odd index count", 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 ( (int)tess.indexes[ indexes + k ] == i + edgeVerts[nums[j]][0] && (int)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[3]; 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 = * ( int * ) invModulate; } } /* ** 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 = myftol( 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; 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 ); } #if id386 && !( (defined __linux__ || defined __FreeBSD__ ) && (defined __i386__ ) ) // rb010123 long myftol( float f ) { static int tmp; __asm fld f __asm fistp tmp __asm mov eax, tmp } #endif /* ** 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 */ void RB_CalcDiffuseColor( 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; #if idppc_altivec vector unsigned char vSel = (vector unsigned char)(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; #endif ent = backEnd.currentEntity; ambientLightInt = ent->ambientLightInt; #if idppc_altivec // 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 ); #else VectorCopy( ent->ambientLight, ambientLight ); VectorCopy( ent->directedLight, directedLight ); VectorCopy( ent->lightDir, lightDir ); #endif v = tess.xyz[0]; normal = tess.normal[0]; #if idppc_altivec normalPerm = vec_lvsl(0,normal); #endif numVertexes = tess.numVertexes; for (i = 0 ; i < numVertexes ; i++, v += 4, normal += 4) { #if idppc_altivec 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 #else incoming = DotProduct (normal, lightDir); if ( incoming <= 0 ) { *(int *)&colors[i*4] = ambientLightInt; continue; } j = myftol( ambientLight[0] + incoming * directedLight[0] ); if ( j > 255 ) { j = 255; } colors[i*4+0] = j; j = myftol( ambientLight[1] + incoming * directedLight[1] ); if ( j > 255 ) { j = 255; } colors[i*4+1] = j; j = myftol( ambientLight[2] + incoming * directedLight[2] ); if ( j > 255 ) { j = 255; } colors[i*4+2] = j; colors[i*4+3] = 255; #endif } } ================================================ FILE: src/engine/renderer/tr_shader.c ================================================ /* =========================================================================== 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 Foobar; 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 ================ */ 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 ); 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'\n", 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 { stage->bundle[0].image[0] = R_FindImageFile(token, (qboolean) !shader.noMipMaps, (qboolean)!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, (qboolean)!shader.noMipMaps, (qboolean) !shader.noPicMip, GL_CLAMP); 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, (qboolean)!shader.noMipMaps, (qboolean) !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 == CGEN_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 = static_cast(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 ); 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; unsigned int 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 = sizeof(infoParms) / sizeof(infoParms[0]); int i; token = COM_ParseExt( text, qfalse ); for ( i = 0 ; i < numInfoParms ; i++ ) { if ( !Q_stricmp( token, infoParms[i].name ) ) { shader.surfaceFlags |= (int)infoParms[i].surfaceFlags; shader.contentFlags |= (int)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 ( !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 ======================================================================================== */ 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 }, { -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; // make sure both stages are active if ( !stages[0].active || !stages[1].active ) { 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 == CGEN_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[tr.smpFrame]->commands; if( cmdList ) { const void *curCmd = cmdList->cmds; while ( 1 ) { 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 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 = (shader_t*) 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] = (shaderStage_t*) 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 = (texModInfo_t*) 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; hasLightmapStage = 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; stage++ ) { 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; continue; } // // ditch this stage if it's detail and detail textures are disabled // if ( pStage->isDetail && !r_detailTextures->integer ) { if ( stage < ( MAX_SHADER_STAGES - 1 ) ) { memmove( pStage, pStage + 1, sizeof( *pStage ) * ( MAX_SHADER_STAGES - stage - 1 ) ); Com_Memset( pStage + 1, 0, sizeof( *pStage ) ); } 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; } } // // 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; } } } } // 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 ) ) { VertexLightingCollapse(); stage = 1; hasLightmapStage = qfalse; } // // look for multitexture potential // if ( stage > 1 && CollapseMultitexture() ) { stage--; } if ( shader.lightmapIndex >= 0 && !hasLightmapStage ) { 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.sort = SS_FOG; } // VULKAN: create pipelines for each shader stage // DX12 if (vk.active || dx.active) { Vk_Pipeline_Def def; def.face_culling = shader.cullType; def.polygon_offset = (shader.polygonOffset == qtrue); for (int i = 0; i < stage; i++) { shaderStage_t *pStage = &stages[i]; def.state_bits = pStage->stateBits; if (pStage->bundle[1].image[0] == nullptr) def.shader_type = Vk_Shader_Type::single_texture; else if (shader.multitextureEnv == GL_MODULATE) def.shader_type = Vk_Shader_Type::multi_texture_mul; else if (shader.multitextureEnv == GL_ADD) def.shader_type = Vk_Shader_Type::multi_texture_add; else ri.Error(ERR_FATAL, "Vulkan: could not create pipelines for q3 shader '%s'\n", shader.name); def.clipping_plane = false; def.mirror = false; if (vk.active) pStage->vk_pipeline = vk_find_pipeline(def); if (dx.active) pStage->dx_pipeline = dx_find_pipeline(def); def.clipping_plane = true; def.mirror = false; if (vk.active) pStage->vk_portal_pipeline = vk_find_pipeline(def); if (dx.active) pStage->dx_portal_pipeline = dx_find_pipeline(def); def.clipping_plane = true; def.mirror = true; if (vk.active) pStage->vk_mirror_pipeline = vk_find_pipeline(def); if (dx.active) pStage->dx_mirror_pipeline = dx_find_pipeline(def); } } 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); 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) ) { // bk001205 return tr.defaultShader; } COM_StripExtension( name, 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]; char fileName[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; } COM_StripExtension( name, 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; } } // make sure the render thread is stopped, because we are probably // going to have to upload an image if (r_smp->integer) { R_SyncRenderThread(); } // 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 TGA, BMP, or PCX // Q_strncpyz( fileName, name, sizeof( fileName ) ); COM_DefaultExtension( fileName, sizeof( fileName ), ".tga" ); image = R_FindImageFile( fileName, mipRawImage, mipRawImage, mipRawImage ? GL_REPEAT : GL_CLAMP ); if ( !image ) { ri.Printf( PRINT_DEVELOPER, "Couldn't find image 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); // // 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; } } // make sure the render thread is stopped, because we are probably // going to have to upload an image if (r_smp->integer) { R_SyncRenderThread(); } // 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 ( (int)strlen( name ) >= MAX_QPATH ) { Com_Printf( "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 ( (int)strlen( name ) >= MAX_QPATH ) { Com_Printf( "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 ( (int)strlen( name ) >= MAX_QPATH ) { Com_Printf( "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 ); // bk: FIXME name 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 { ri.Printf( PRINT_ALL, " " ); } if ( shader->explicitlyDefined ) { ri.Printf( PRINT_ALL, "E " ); } else { ri.Printf( PRINT_ALL, " " ); } if ( !shader->isSky ) { ri.Printf( PRINT_ALL, "gen " ); } else { ri.Printf( PRINT_ALL, "sky " ); } 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 numShaders; int i; char *oldp, *token, *hashMem; int shaderTextHashTableSizes[MAX_SHADERTEXT_HASH], hash, size; long sum = 0; // scan for shader files shaderFiles = ri.FS_ListFiles( "scripts", ".shader", &numShaders ); if ( !shaderFiles || !numShaders ) { ri.Printf( PRINT_WARNING, "WARNING: no shader files found\n" ); return; } if ( numShaders > MAX_SHADER_FILES ) { numShaders = MAX_SHADER_FILES; } // load and parse shader files for ( i = 0; i < numShaders; i++ ) { char filename[MAX_QPATH]; Com_sprintf( filename, sizeof( filename ), "scripts/%s", shaderFiles[i] ); // ri.Printf( PRINT_ALL, "...loading '%s'\n", filename ); sum += ri.FS_ReadFile( filename, (void **)&buffers[i] ); if ( !buffers[i] ) { ri.Error( ERR_DROP, "Couldn't load %s", filename ); } } // build single large buffer s_shaderText = (char*) ri.Hunk_Alloc( sum + numShaders*2, h_low ); // free in reverse order, so the temp files are all dumped for ( i = numShaders - 1; i >= 0 ; i-- ) { strcat( s_shaderText, "\n" ); p = &s_shaderText[strlen(s_shaderText)]; strcat( s_shaderText, buffers[i] ); ri.FS_FreeFile( buffers[i] ); buffers[i] = p; COM_Compress(p); } // free up memory ri.FS_FreeFileList( shaderFiles ); Com_Memset(shaderTextHashTableSizes, 0, sizeof(shaderTextHashTableSizes)); size = 0; // for ( i = 0; i < numShaders; i++ ) { // pointer to the first shader file p = buffers[i]; // look for label while ( 1 ) { token = COM_ParseExt( &p, qtrue ); if ( token[0] == 0 ) { break; } hash = generateHashValue(token, MAX_SHADERTEXT_HASH); shaderTextHashTableSizes[hash]++; size++; SkipBracedSection(&p); // if we passed the pointer to the next shader file if ( i < numShaders - 1 ) { if ( p > buffers[i+1] ) { break; } } } } size += MAX_SHADERTEXT_HASH; hashMem = (char*) 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)); // for ( i = 0; i < numShaders; i++ ) { // pointer to the first shader file p = buffers[i]; // look for label 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); // if we passed the pointer to the next shader file if ( i < numShaders - 1 ) { if ( p > buffers[i+1] ) { break; } } } } 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(); // cinematic 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; // will be updated by specific cinematic images stages[0].active = qtrue; stages[0].rgbGen = CGEN_IDENTITY_LIGHTING; stages[0].stateBits = GLS_DEPTHTEST_DISABLE; tr.cinematicShader = FinishShader(); } static void CreateExternalShaders( void ) { tr.projectionShadowShader = R_FindShader( "projectionShadow", 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(); } ================================================ FILE: src/engine/renderer/tr_shadows.c ================================================ /* =========================================================================== 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 Foobar; 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]; static vec4_t extrudedEdges[SHADER_MAX_VERTEXES * 4]; static int numExtrudedEdges; static 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 ]++; } static void R_ExtrudeShadowEdges( void ) { int i; int c, c2; int j, k; int i2; numExtrudedEdges = 0; // 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 for ( i = 0 ; i < tess.numVertexes ; i++ ) { c = numEdgeDefs[ i ]; for ( j = 0 ; j < c ; j++ ) { if ( !edgeDefs[ i ][ j ].facing ) { continue; } bool sil_edge = true; i2 = edgeDefs[ i ][ j ].i2; c2 = numEdgeDefs[ i2 ]; for ( k = 0 ; k < c2 ; k++ ) { if ( edgeDefs[ i2 ][ k ].i2 == i && edgeDefs[ i2 ][ k ].facing) { sil_edge = false; break; } } // if it doesn't share the edge with another front facing // triangle, it is a sil edge if ( sil_edge ) { VectorCopy(tess.xyz[ i ], extrudedEdges[numExtrudedEdges * 4 + 0]); VectorCopy(tess.xyz[ i + tess.numVertexes ], extrudedEdges[numExtrudedEdges * 4 + 1]); VectorCopy(tess.xyz[ i2 ], extrudedEdges[numExtrudedEdges * 4 + 2]); VectorCopy(tess.xyz[ i2 + tess.numVertexes ], extrudedEdges[numExtrudedEdges * 4 + 3]); numExtrudedEdges++; } } } } static void R_GL_RenderShadowEdges() { qglBegin( GL_QUADS); for (int i = 0; i < numExtrudedEdges; i++) { qglVertex3fv(extrudedEdges[i*4 + 0]); qglVertex3fv(extrudedEdges[i*4 + 1]); qglVertex3fv(extrudedEdges[i*4 + 3]); qglVertex3fv(extrudedEdges[i*4 + 2]); } qglEnd(); } // VULKAN // DX12 static void R_Vk_Dx_RenderShadowEdges(VkPipeline vk_pipeline, ID3D12PipelineState* dx_pipeline) { if (!vk.active && !dx.active) return; int i = 0; while (i < numExtrudedEdges) { int count = numExtrudedEdges - i; if (count > (SHADER_MAX_VERTEXES - 1) / 4) count = (SHADER_MAX_VERTEXES - 1) / 4; Com_Memcpy(tess.xyz, extrudedEdges[i*4], 4 * count * sizeof(vec4_t)); tess.numVertexes = count * 4; for (int k = 0; k < count; k++) { tess.indexes[k * 6 + 0] = k * 4 + 0; tess.indexes[k * 6 + 1] = k * 4 + 2; tess.indexes[k * 6 + 2] = k * 4 + 1; tess.indexes[k * 6 + 3] = k * 4 + 2; tess.indexes[k * 6 + 4] = k * 4 + 3; tess.indexes[k * 6 + 5] = k * 4 + 1; } tess.numIndexes = count * 6; for (int k = 0; k < tess.numVertexes; k++) { VectorSet(tess.svars.colors[k], 50, 50, 50); tess.svars.colors[k][3] = 255; } if (vk.active) { vk_bind_geometry(); vk_shade_geometry(vk_pipeline, false, Vk_Depth_Range::normal); } if (dx.active) { dx_bind_geometry(); dx_shade_geometry(dx_pipeline, false, Vk_Depth_Range::normal, true, false); } i += count; } } /* ================= 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; // 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 qglColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE ); qglEnable( GL_STENCIL_TEST ); qglStencilFunc( GL_ALWAYS, 1, 255 ); R_ExtrudeShadowEdges(); // mirrors have the culling order reversed if ( backEnd.viewParms.isMirror ) { qglCullFace( GL_FRONT ); qglStencilOp( GL_KEEP, GL_KEEP, GL_INCR ); R_GL_RenderShadowEdges(); qglCullFace( GL_BACK ); qglStencilOp( GL_KEEP, GL_KEEP, GL_DECR ); R_GL_RenderShadowEdges(); // VULKAN // DX12 R_Vk_Dx_RenderShadowEdges(vk.shadow_volume_pipelines[0][1], dx.shadow_volume_pipelines[0][1]); R_Vk_Dx_RenderShadowEdges(vk.shadow_volume_pipelines[1][1], dx.shadow_volume_pipelines[1][1]); } else { qglCullFace( GL_BACK ); qglStencilOp( GL_KEEP, GL_KEEP, GL_INCR ); R_GL_RenderShadowEdges(); qglCullFace( GL_FRONT ); qglStencilOp( GL_KEEP, GL_KEEP, GL_DECR ); R_GL_RenderShadowEdges(); // VULKAN // DX12 R_Vk_Dx_RenderShadowEdges(vk.shadow_volume_pipelines[0][0], dx.shadow_volume_pipelines[0][0]); R_Vk_Dx_RenderShadowEdges(vk.shadow_volume_pipelines[1][0], dx.shadow_volume_pipelines[1][0]); } qglDisable(GL_STENCIL_TEST); // reenable writing to the color buffer qglColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE ); } /* ================= 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); GL_Cull(CT_TWO_SIDED); GL_Bind( tr.whiteImage ); qglPushMatrix(); qglLoadIdentity (); qglColor3f( 0.6f, 0.6f, 0.6f ); GL_State( GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO ); qglBegin( GL_QUADS ); qglVertex3f( -100, 100, -10 ); qglVertex3f( 100, 100, -10 ); qglVertex3f( 100, -100, -10 ); qglVertex3f( -100, -100, -10 ); qglEnd (); qglColor3f(1,1,1); qglDisable( GL_STENCIL_TEST ); qglPopMatrix(); // VULKAN // DX12 if (vk.active || dx.active) { tess.indexes[0] = 0; tess.indexes[1] = 1; tess.indexes[2] = 2; tess.indexes[3] = 0; tess.indexes[4] = 2; tess.indexes[5] = 3; tess.numIndexes = 6; VectorSet(tess.xyz[0], -100, 100, -10); VectorSet(tess.xyz[1], 100, 100, -10); VectorSet(tess.xyz[2], 100, -100, -10); VectorSet(tess.xyz[3], -100, -100, -10); for (int i = 0; i < 4; i++) { VectorSet(tess.svars.colors[i], 153, 153, 153); tess.svars.colors[i][3] = 255; } tess.numVertexes = 4; if (vk.active) { // set backEnd.or.modelMatrix to identity matrix float tmp[16]; Com_Memcpy(tmp, vk_world.modelview_transform, 64); Com_Memset(vk_world.modelview_transform, 0, 64); vk_world.modelview_transform[0] = 1.0f; vk_world.modelview_transform[5] = 1.0f; vk_world.modelview_transform[10] = 1.0f; vk_world.modelview_transform[15] = 1.0f; vk_bind_geometry(); vk_shade_geometry(vk.shadow_finish_pipeline, false, Vk_Depth_Range::normal); Com_Memcpy(vk_world.modelview_transform, tmp, 64); } if (dx.active) { // set backEnd.or.modelMatrix to identity matrix float tmp[16]; Com_Memcpy(tmp, dx_world.modelview_transform, 64); Com_Memset(dx_world.modelview_transform, 0, 64); dx_world.modelview_transform[0] = 1.0f; dx_world.modelview_transform[5] = 1.0f; dx_world.modelview_transform[10] = 1.0f; dx_world.modelview_transform[15] = 1.0f; dx_bind_geometry(); dx_shade_geometry(dx.shadow_finish_pipeline, false, Vk_Depth_Range::normal, true, false); Com_Memcpy(dx_world.modelview_transform, tmp, 64); } tess.numIndexes = 0; tess.numVertexes = 0; } } /* ================= 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; } } ================================================ FILE: src/engine/renderer/tr_sky.c ================================================ /* =========================================================================== 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 Foobar; 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]; /* =================================================================================== 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 ); // VULKAN: draw skybox side // DX12 if (vk.active || dx.active) { GL_Bind(shader->sky.outerbox[sky_texorder[i]]); tess.numVertexes = 0; tess.numIndexes = 0; 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++ ) { int ndx = tess.numVertexes; tess.indexes[ tess.numIndexes ] = ndx; tess.indexes[ tess.numIndexes + 1 ] = ndx + 1; tess.indexes[ tess.numIndexes + 2 ] = ndx + 2; tess.indexes[ tess.numIndexes + 3 ] = ndx + 2; tess.indexes[ tess.numIndexes + 4 ] = ndx + 1; tess.indexes[ tess.numIndexes + 5 ] = ndx + 3; tess.numIndexes += 6; VectorCopy(s_skyPoints[t][s], tess.xyz[ndx]); tess.svars.texcoords[0][ndx][0] = s_skyTexCoords[t][s][0]; tess.svars.texcoords[0][ndx][1] = s_skyTexCoords[t][s][1]; VectorCopy(s_skyPoints[t + 1][s], tess.xyz[ndx + 1]); tess.svars.texcoords[0][ndx + 1][0] = s_skyTexCoords[t + 1][s][0]; tess.svars.texcoords[0][ndx + 1][1] = s_skyTexCoords[t + 1][s][1]; VectorCopy(s_skyPoints[t][s + 1], tess.xyz[ndx + 2]); tess.svars.texcoords[0][ndx + 2][0] = s_skyTexCoords[t][s + 1][0]; tess.svars.texcoords[0][ndx + 2][1] = s_skyTexCoords[t][s + 1][1]; VectorCopy(s_skyPoints[t + 1][s + 1], tess.xyz[ndx + 3]); tess.svars.texcoords[0][ndx + 3][0] = s_skyTexCoords[t + 1][s + 1][0]; tess.svars.texcoords[0][ndx + 3][1] = s_skyTexCoords[t + 1][s + 1][1]; tess.numVertexes += 4; } } Com_Memset( tess.svars.colors, tr.identityLightByte, tess.numVertexes * 4 ); if (vk.active) { vk_bind_geometry(); vk_shade_geometry(vk.skybox_pipeline, false, r_showsky->integer ? Vk_Depth_Range::force_zero : Vk_Depth_Range::force_one); } if (dx.active) { dx_bind_geometry(); dx_shade_geometry(dx.skybox_pipeline, false, r_showsky->integer ? Vk_Depth_Range::force_zero : Vk_Depth_Range::force_one, true, false); } } } } static void FillCloudySkySide( const int mins[2], const int maxs[2] ) { 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()\n" ); } } } 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() { for ( int i = 0; i < 5; i++ ) { int sky_mins_subd[2], sky_maxs_subd[2]; int s, t; float MIN_T = -HALF_SKY_SUBDIVISIONS; 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] = myftol( sky_mins[0][i] * HALF_SKY_SUBDIVISIONS ); sky_mins_subd[1] = myftol( sky_mins[1][i] * HALF_SKY_SUBDIVISIONS ); sky_maxs_subd[0] = myftol( sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS ); sky_maxs_subd[1] = myftol( 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); } } /* ** R_BuildCloudData */ void R_BuildCloudData( shaderCommands_t *input ) { 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 ( input->shader->sky.cloudHeight && tess.xstages[0] ) { FillCloudBox(); } } /* ** 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 ) ) ); // 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_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 ) { // 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 ) { float modelMatrix_original[16]; Com_Memcpy(modelMatrix_original, vk_world.modelview_transform, sizeof(float[16])); float skybox_translate[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, backEnd.viewParms.or.origin[0], backEnd.viewParms.or.origin[1], backEnd.viewParms.or.origin[2], 1 }; myGlMultMatrix(skybox_translate, modelMatrix_original, vk_world.modelview_transform); myGlMultMatrix(skybox_translate, modelMatrix_original, dx_world.modelview_transform); GL_State( 0 ); qglColor3f( tr.identityLight, tr.identityLight, tr.identityLight ); qglPushMatrix (); qglLoadMatrixf(vk_world.modelview_transform); DrawSkyBox( tess.shader ); qglPopMatrix(); Com_Memcpy(vk_world.modelview_transform, modelMatrix_original, sizeof(float[16])); Com_Memcpy(dx_world.modelview_transform, modelMatrix_original, sizeof(float[16])); } // 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 ); } ================================================ FILE: src/engine/renderer/tr_surface.c ================================================ /* =========================================================================== 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 Foobar; 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" /* 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 ============= */ 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 ============= */ void RB_SurfaceTriangles( srfTriangles_t *srf ) { int i; drawVert_t *dv; float *xyz, *normal, *texCoords; byte *color; int dlightBits; qboolean needsNormal; dlightBits = srf->dlightBits[backEnd.smpFrame]; 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 ============== */ 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 */ 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 */ 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 */ 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 */ static void LerpMeshVertexes (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 ) { #if idppc_altivec 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 // // 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]; } #endif } 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); } } /* ============= RB_SurfaceMesh ============= */ 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 ============== */ 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[backEnd.smpFrame]; 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; v = surf->points[0]; ndx = tess.numVertexes; 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 ============= */ 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[backEnd.smpFrame]; 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; rows = 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 =================== */ 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 ==================== */ 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; } void RB_SurfaceBad( surfaceType_t *surfType ) { ri.Printf( PRINT_ALL, "Bad surface tesselated.\n" ); } 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, (void(*)(void*))RB_SurfaceSkip, // SF_FLARE, (void(*)(void*))RB_SurfaceEntity, // SF_ENTITY }; ================================================ FILE: src/engine/renderer/tr_world.c ================================================ /* =========================================================================== 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 Foobar; 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 != ENTITYNUM_WORLD ) { sphereCull = R_CullLocalPointAndRadius( cv->localOrigin, cv->meshRadius ); } else { sphereCull = R_CullPointAndRadius( cv->localOrigin, cv->meshRadius ); } boxCull = CULL_OUT; // 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[ tr.smpFrame ] = 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[ tr.smpFrame ] = dlightBits; return dlightBits; } static int R_DlightTrisurf( srfTriangles_t *surf, int dlightBits ) { // FIXME: more dlight culling to trisurfs... surf->dlightBits[ tr.smpFrame ] = 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[ tr.smpFrame ] = 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_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 = CM_ClusterPVS( leaf->cluster ); 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 = ENTITYNUM_WORLD; tr.shiftedEntityNum = tr.currentEntityNum << QSORT_ENTITYNUM_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 ); } ================================================ FILE: src/engine/renderer/vk.cpp ================================================ #include "tr_local.h" #include #include #include #include const int VERTEX_CHUNK_SIZE = 512 * 1024; const int XYZ_SIZE = 4 * VERTEX_CHUNK_SIZE; const int COLOR_SIZE = 1 * VERTEX_CHUNK_SIZE; const int ST0_SIZE = 2 * VERTEX_CHUNK_SIZE; const int ST1_SIZE = 2 * VERTEX_CHUNK_SIZE; const int XYZ_OFFSET = 0; const int COLOR_OFFSET = XYZ_OFFSET + XYZ_SIZE; const int ST0_OFFSET = COLOR_OFFSET + COLOR_SIZE; const int ST1_OFFSET = ST0_OFFSET + ST0_SIZE; static const int VERTEX_BUFFER_SIZE = XYZ_SIZE + COLOR_SIZE + ST0_SIZE + ST1_SIZE; static const int INDEX_BUFFER_SIZE = 2 * 1024 * 1024; constexpr VkFormat output_image_format = VK_FORMAT_R8G8B8A8_UNORM; // // Vulkan API functions used by the renderer. // PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; PFN_vkCreateInstance vkCreateInstance; PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties; PFN_vkCreateDevice vkCreateDevice; PFN_vkDestroyInstance vkDestroyInstance; PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties; PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices; PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr; PFN_vkGetPhysicalDeviceFeatures vkGetPhysicalDeviceFeatures; PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties; PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties; PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties; PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties; PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR; PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR; PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR; PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR; PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR; PFN_vkGetPhysicalDeviceSurfaceSupportKHR vkGetPhysicalDeviceSurfaceSupportKHR; PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallbackEXT; PFN_vkDestroyDebugReportCallbackEXT vkDestroyDebugReportCallbackEXT; PFN_vkAllocateCommandBuffers vkAllocateCommandBuffers; PFN_vkAllocateDescriptorSets vkAllocateDescriptorSets; PFN_vkAllocateMemory vkAllocateMemory; PFN_vkBeginCommandBuffer vkBeginCommandBuffer; PFN_vkBindBufferMemory vkBindBufferMemory; PFN_vkBindImageMemory vkBindImageMemory; PFN_vkCmdBeginRenderPass vkCmdBeginRenderPass; PFN_vkCmdBindDescriptorSets vkCmdBindDescriptorSets; PFN_vkCmdBindIndexBuffer vkCmdBindIndexBuffer; PFN_vkCmdBindPipeline vkCmdBindPipeline; PFN_vkCmdBindVertexBuffers vkCmdBindVertexBuffers; PFN_vkCmdBlitImage vkCmdBlitImage; PFN_vkCmdClearAttachments vkCmdClearAttachments; PFN_vkCmdCopyBuffer vkCmdCopyBuffer; PFN_vkCmdCopyBufferToImage vkCmdCopyBufferToImage; PFN_vkCmdCopyImage vkCmdCopyImage; PFN_vkCmdDispatch vkCmdDispatch; PFN_vkCmdDraw vkCmdDraw; PFN_vkCmdDrawIndexed vkCmdDrawIndexed; PFN_vkCmdEndRenderPass vkCmdEndRenderPass; PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier; PFN_vkCmdPushConstants vkCmdPushConstants; PFN_vkCmdSetDepthBias vkCmdSetDepthBias; PFN_vkCmdSetScissor vkCmdSetScissor; PFN_vkCmdSetViewport vkCmdSetViewport; PFN_vkCreateBuffer vkCreateBuffer; PFN_vkCreateCommandPool vkCreateCommandPool; PFN_vkCreateComputePipelines vkCreateComputePipelines; PFN_vkCreateDescriptorPool vkCreateDescriptorPool; PFN_vkCreateDescriptorSetLayout vkCreateDescriptorSetLayout; PFN_vkCreateFence vkCreateFence; PFN_vkCreateFramebuffer vkCreateFramebuffer; PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines; PFN_vkCreateImage vkCreateImage; PFN_vkCreateImageView vkCreateImageView; PFN_vkCreatePipelineLayout vkCreatePipelineLayout; PFN_vkCreateRenderPass vkCreateRenderPass; PFN_vkCreateSampler vkCreateSampler; PFN_vkCreateSemaphore vkCreateSemaphore; PFN_vkCreateShaderModule vkCreateShaderModule; PFN_vkDestroyBuffer vkDestroyBuffer; PFN_vkDestroyCommandPool vkDestroyCommandPool; PFN_vkDestroyDescriptorPool vkDestroyDescriptorPool; PFN_vkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout; PFN_vkDestroyDevice vkDestroyDevice; PFN_vkDestroyFence vkDestroyFence; PFN_vkDestroyFramebuffer vkDestroyFramebuffer; PFN_vkDestroyImage vkDestroyImage; PFN_vkDestroyImageView vkDestroyImageView; PFN_vkDestroyPipeline vkDestroyPipeline; PFN_vkDestroyPipelineLayout vkDestroyPipelineLayout; PFN_vkDestroyRenderPass vkDestroyRenderPass; PFN_vkDestroySampler vkDestroySampler; PFN_vkDestroySemaphore vkDestroySemaphore; PFN_vkDestroyShaderModule vkDestroyShaderModule; PFN_vkDeviceWaitIdle vkDeviceWaitIdle; PFN_vkEndCommandBuffer vkEndCommandBuffer; PFN_vkFreeCommandBuffers vkFreeCommandBuffers; PFN_vkFreeDescriptorSets vkFreeDescriptorSets; PFN_vkFreeMemory vkFreeMemory; PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements; PFN_vkGetDeviceQueue vkGetDeviceQueue; PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements; PFN_vkGetImageSubresourceLayout vkGetImageSubresourceLayout; PFN_vkMapMemory vkMapMemory; PFN_vkQueueSubmit vkQueueSubmit; PFN_vkQueueWaitIdle vkQueueWaitIdle; PFN_vkResetDescriptorPool vkResetDescriptorPool; PFN_vkResetFences vkResetFences; PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets; PFN_vkWaitForFences vkWaitForFences; PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR; PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR; PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR; PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR; PFN_vkQueuePresentKHR vkQueuePresentKHR; //////////////////////////////////////////////////////////////////////////// static uint32_t find_memory_type(VkPhysicalDevice physical_device, uint32_t memory_type_bits, VkMemoryPropertyFlags properties) { VkPhysicalDeviceMemoryProperties memory_properties; vkGetPhysicalDeviceMemoryProperties(physical_device, &memory_properties); for (uint32_t i = 0; i < memory_properties.memoryTypeCount; i++) { if ((memory_type_bits & (1 << i)) != 0 && (memory_properties.memoryTypes[i].propertyFlags & properties) == properties) { return i; } } ri.Error(ERR_FATAL, "Vulkan: failed to find matching memory type with requested properties"); return -1; } static VkFormat get_depth_format(VkPhysicalDevice physical_device) { VkFormat formats[2]; if (r_stencilbits->integer > 0) { formats[0] = VK_FORMAT_D24_UNORM_S8_UINT; formats[1] = VK_FORMAT_D32_SFLOAT_S8_UINT; glConfig.stencilBits = 8; } else { formats[0] = VK_FORMAT_X8_D24_UNORM_PACK32; formats[1] = VK_FORMAT_D32_SFLOAT; glConfig.stencilBits = 0; } for (int i = 0; i < 2; i++) { VkFormatProperties props; vkGetPhysicalDeviceFormatProperties(physical_device, formats[i], &props); if ((props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0) { return formats[i]; } } ri.Error(ERR_FATAL, "get_depth_format: failed to find depth attachment format"); return VK_FORMAT_UNDEFINED; // never get here } static VkSwapchainKHR create_swapchain(VkPhysicalDevice physical_device, VkDevice device, VkSurfaceKHR surface, VkSurfaceFormatKHR surface_format) { VkSurfaceCapabilitiesKHR surface_caps; VK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface, &surface_caps)); VkExtent2D image_extent = surface_caps.currentExtent; if (image_extent.width == 0xffffffff && image_extent.height == 0xffffffff) { image_extent.width = std::min(surface_caps.maxImageExtent.width, std::max(surface_caps.minImageExtent.width, 640u)); image_extent.height = std::min(surface_caps.maxImageExtent.height, std::max(surface_caps.minImageExtent.height, 480u)); } // VK_IMAGE_USAGE_TRANSFER_DST_BIT is required by image clear operations. if ((surface_caps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT) == 0) ri.Error(ERR_FATAL, "create_swapchain: VK_IMAGE_USAGE_TRANSFER_DST_BIT is not supported by the swapchain"); // VK_IMAGE_USAGE_TRANSFER_SRC_BIT is required in order to take screenshots. if ((surface_caps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT) == 0) ri.Error(ERR_FATAL, "create_swapchain: VK_IMAGE_USAGE_TRANSFER_SRC_BIT is not supported by the swapchain"); // determine present mode and swapchain image count uint32_t present_mode_count; VK_CHECK(vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &present_mode_count, nullptr)); std::vector present_modes(present_mode_count); VK_CHECK(vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &present_mode_count, present_modes.data())); bool mailbox_supported = false; bool immediate_supported = false; bool fifo_relaxed_supported = false; for (auto pm : present_modes) { if (pm == VK_PRESENT_MODE_MAILBOX_KHR) mailbox_supported = true; else if (pm == VK_PRESENT_MODE_IMMEDIATE_KHR) immediate_supported = true; else if (pm == VK_PRESENT_MODE_FIFO_RELAXED_KHR) fifo_relaxed_supported = true; } VkPresentModeKHR present_mode; uint32_t image_count = surface_caps.minImageCount; if (!r_vsync->integer && mailbox_supported) { present_mode = VK_PRESENT_MODE_MAILBOX_KHR; // Additional image over reported min count is to ensure AcquireNextImage does not block // (check Q/A section for VK_KHR_swapchain extension in the specification) image_count = surface_caps.minImageCount + 1; } else if (!r_vsync->integer && immediate_supported) { present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR; } else if (fifo_relaxed_supported) { present_mode = VK_PRESENT_MODE_FIFO_RELAXED_KHR; } else { present_mode = VK_PRESENT_MODE_FIFO_KHR; } if (surface_caps.maxImageCount > 0) { image_count = std::min(image_count, surface_caps.maxImageCount); } // create swap chain VkSwapchainCreateInfoKHR desc{VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR}; desc.surface = surface; desc.minImageCount = image_count; desc.imageFormat = surface_format.format; desc.imageColorSpace = surface_format.colorSpace; desc.imageExtent = image_extent; desc.imageArrayLayers = 1; desc.imageUsage = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; desc.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; desc.preTransform = surface_caps.currentTransform; desc.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; desc.presentMode = present_mode; desc.clipped = VK_TRUE; VkSwapchainKHR swapchain; VK_CHECK(vkCreateSwapchainKHR(device, &desc, nullptr, &swapchain)); return swapchain; } static VkRenderPass create_render_pass(VkDevice device, VkFormat color_format, VkFormat depth_format) { VkAttachmentDescription attachments[2]; attachments[0].flags = 0; attachments[0].format = color_format; attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; attachments[0].finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; attachments[1].flags = 0; attachments[1].format = depth_format; attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attachments[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; VkAttachmentReference color_attachment_ref; color_attachment_ref.attachment = 0; color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; VkAttachmentReference depth_attachment_ref; depth_attachment_ref.attachment = 1; depth_attachment_ref.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; VkSubpassDescription subpass{}; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &color_attachment_ref; subpass.pDepthStencilAttachment = &depth_attachment_ref; // Synchronize color attachment output stage (main rendering) and compute state (gamma shader). // This replaces Vulkan's implicit barrier that transitions output image to final layout (SHADER_READ). VkSubpassDependency dependency; dependency.srcSubpass = 0; dependency.dstSubpass = VK_SUBPASS_EXTERNAL; dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dependency.dstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; dependency.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; dependency.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; dependency.dependencyFlags = 0; VkRenderPassCreateInfo desc{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO }; desc.attachmentCount = sizeof(attachments) / sizeof(attachments[0]); desc.pAttachments = attachments; desc.subpassCount = 1; desc.pSubpasses = &subpass; desc.dependencyCount = 1; desc.pDependencies = &dependency; VkRenderPass render_pass; VK_CHECK(vkCreateRenderPass(device, &desc, nullptr, &render_pass)); return render_pass; } static void record_and_run_commands(VkCommandPool command_pool, VkQueue queue, std::function recorder) { VkCommandBufferAllocateInfo alloc_info{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; alloc_info.commandPool = command_pool; alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; alloc_info.commandBufferCount = 1; VkCommandBuffer command_buffer; VK_CHECK(vkAllocateCommandBuffers(vk.device, &alloc_info, &command_buffer)); VkCommandBufferBeginInfo begin_info{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; VK_CHECK(vkBeginCommandBuffer(command_buffer, &begin_info)); recorder(command_buffer); VK_CHECK(vkEndCommandBuffer(command_buffer)); VkSubmitInfo submit_info{ VK_STRUCTURE_TYPE_SUBMIT_INFO }; submit_info.commandBufferCount = 1; submit_info.pCommandBuffers = &command_buffer; VK_CHECK(vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE)); VK_CHECK(vkQueueWaitIdle(queue)); vkFreeCommandBuffers(vk.device, command_pool, 1, &command_buffer); } static void record_image_layout_transition(VkCommandBuffer command_buffer, VkImage image, VkImageAspectFlags image_aspect_flags, VkAccessFlags src_access_flags, VkImageLayout old_layout, VkAccessFlags dst_access_flags, VkImageLayout new_layout) { VkImageMemoryBarrier barrier{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; barrier.srcAccessMask = src_access_flags; barrier.dstAccessMask = dst_access_flags; barrier.oldLayout = old_layout; barrier.newLayout = new_layout; barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.image = image; barrier.subresourceRange.aspectMask = image_aspect_flags; barrier.subresourceRange.baseMipLevel = 0; barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; barrier.subresourceRange.baseArrayLayer = 0; barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); } static void allocate_and_bind_image_memory(VkImage image) { VkMemoryRequirements memory_requirements; vkGetImageMemoryRequirements(vk.device, image, &memory_requirements); if (memory_requirements.size > IMAGE_CHUNK_SIZE) { ri.Error(ERR_FATAL, "Vulkan: could not allocate memory, image is too large."); } Vk_World::Chunk* chunk = nullptr; // Try to find an existing chunk of sufficient capacity. const auto mask = ~(memory_requirements.alignment - 1); for (int i = 0; i < vk_world.num_image_chunks; i++) { // ensure that memory region has proper alignment VkDeviceSize offset = (vk_world.image_chunks[i].used + memory_requirements.alignment - 1) & mask; if (offset + memory_requirements.size <= IMAGE_CHUNK_SIZE) { chunk = &vk_world.image_chunks[i]; chunk->used = offset + memory_requirements.size; break; } } // Allocate a new chunk in case we couldn't find suitable existing chunk. if (chunk == nullptr) { if (vk_world.num_image_chunks >= MAX_IMAGE_CHUNKS) { ri.Error(ERR_FATAL, "Vulkan: image chunk limit has been reached"); } VkMemoryAllocateInfo alloc_info{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; alloc_info.allocationSize = IMAGE_CHUNK_SIZE; alloc_info.memoryTypeIndex = find_memory_type(vk.physical_device, memory_requirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); VkDeviceMemory memory; VK_CHECK(vkAllocateMemory(vk.device, &alloc_info, nullptr, &memory)); chunk = &vk_world.image_chunks[vk_world.num_image_chunks]; vk_world.num_image_chunks++; chunk->memory = memory; chunk->used = memory_requirements.size; } VK_CHECK(vkBindImageMemory(vk.device, image, chunk->memory, chunk->used - memory_requirements.size)); } static void ensure_staging_buffer_allocation(VkDeviceSize size) { if (vk_world.staging_buffer_size >= size) return; if (vk_world.staging_buffer != VK_NULL_HANDLE) vkDestroyBuffer(vk.device, vk_world.staging_buffer, nullptr); if (vk_world.staging_buffer_memory != VK_NULL_HANDLE) vkFreeMemory(vk.device, vk_world.staging_buffer_memory, nullptr); vk_world.staging_buffer_size = size; VkBufferCreateInfo buffer_desc{ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; buffer_desc.size = size; buffer_desc.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; buffer_desc.sharingMode = VK_SHARING_MODE_EXCLUSIVE; VK_CHECK(vkCreateBuffer(vk.device, &buffer_desc, nullptr, &vk_world.staging_buffer)); VkMemoryRequirements memory_requirements; vkGetBufferMemoryRequirements(vk.device, vk_world.staging_buffer, &memory_requirements); uint32_t memory_type = find_memory_type(vk.physical_device, memory_requirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); VkMemoryAllocateInfo alloc_info{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; alloc_info.allocationSize = memory_requirements.size; alloc_info.memoryTypeIndex = memory_type; VK_CHECK(vkAllocateMemory(vk.device, &alloc_info, nullptr, &vk_world.staging_buffer_memory)); VK_CHECK(vkBindBufferMemory(vk.device, vk_world.staging_buffer, vk_world.staging_buffer_memory, 0)); void* data; VK_CHECK(vkMapMemory(vk.device, vk_world.staging_buffer_memory, 0, VK_WHOLE_SIZE, 0, &data)); vk_world.staging_buffer_ptr = (byte*)data; } static VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT object_type, uint64_t object, size_t location, int32_t message_code, const char* layer_prefix, const char* message, void* user_data) { #ifdef _WIN32 OutputDebugString(message); OutputDebugString("\n"); DebugBreak(); #endif return VK_FALSE; } static void create_instance() { const char* instance_extensions[] = { VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_WIN32_SURFACE_EXTENSION_NAME, #ifndef NDEBUG VK_EXT_DEBUG_REPORT_EXTENSION_NAME #endif }; // check extensions availability { uint32_t count = 0; VK_CHECK(vkEnumerateInstanceExtensionProperties(nullptr, &count, nullptr)); std::vector extension_properties(count); VK_CHECK(vkEnumerateInstanceExtensionProperties(nullptr, &count, extension_properties.data())); for (auto name : instance_extensions) { bool supported = false; for (const auto& property : extension_properties) { if (!strcmp(property.extensionName, name)) { supported = true; break; } } if (!supported) ri.Error(ERR_FATAL, "Vulkan: required instance extension is not available: %s", name); } } // create instance { VkInstanceCreateInfo desc{ VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO }; desc.enabledExtensionCount = sizeof(instance_extensions)/sizeof(instance_extensions[0]); desc.ppEnabledExtensionNames = instance_extensions; #ifndef NDEBUG const char* validation_layer_name = "VK_LAYER_KHRONOS_validation"; desc.enabledLayerCount = 1; desc.ppEnabledLayerNames = &validation_layer_name; #endif VK_CHECK(vkCreateInstance(&desc, nullptr, &vk.instance)); } } static void create_device() { // select physical device { uint32_t count = 0; VK_CHECK(vkEnumeratePhysicalDevices(vk.instance, &count, nullptr)); if (count == 0) { ri.Error(ERR_FATAL, "Vulkan: no physical device found"); } std::vector physical_devices(count); VK_CHECK(vkEnumeratePhysicalDevices(vk.instance, &count, physical_devices.data())); int gpu_index = r_gpu->integer; if (gpu_index >= count) { ri.Printf(PRINT_WARNING, "r_gpu %d is too large. Maximum value is %u. Vulkan backend will use GPU 0\n", gpu_index, count - 1); gpu_index = 0; } vk.physical_device = physical_devices[gpu_index]; } vk_imp_create_surface(); // select surface format { uint32_t format_count; VK_CHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(vk.physical_device, vk.surface, &format_count, nullptr)); assert(format_count > 0); std::vector candidates(format_count); VK_CHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(vk.physical_device, vk.surface, &format_count, candidates.data())); if (candidates.size() == 1 && candidates[0].format == VK_FORMAT_UNDEFINED) { // special case that means we can choose any format vk.surface_format.format = VK_FORMAT_R8G8B8A8_UNORM; vk.surface_format.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; } else { vk.surface_format = candidates[0]; } } // select queue family { uint32_t queue_family_count; vkGetPhysicalDeviceQueueFamilyProperties(vk.physical_device, &queue_family_count, nullptr); std::vector queue_families(queue_family_count); vkGetPhysicalDeviceQueueFamilyProperties(vk.physical_device, &queue_family_count, queue_families.data()); // select queue family with presentation and graphics support vk.queue_family_index = -1; for (uint32_t i = 0; i < queue_family_count; i++) { VkBool32 presentation_supported; VK_CHECK(vkGetPhysicalDeviceSurfaceSupportKHR(vk.physical_device, i, vk.surface, &presentation_supported)); if (presentation_supported && (queue_families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) { vk.queue_family_index = i; break; } } if (vk.queue_family_index == -1) ri.Error(ERR_FATAL, "Vulkan: failed to find queue family"); } // create VkDevice { const char* device_extensions[] = { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; uint32_t count = 0; VK_CHECK(vkEnumerateDeviceExtensionProperties(vk.physical_device, nullptr, &count, nullptr)); std::vector extension_properties(count); VK_CHECK(vkEnumerateDeviceExtensionProperties(vk.physical_device, nullptr, &count, extension_properties.data())); for (auto name : device_extensions) { bool supported = false; for (const auto& property : extension_properties) { if (!strcmp(property.extensionName, name)) { supported = true; break; } } if (!supported) ri.Error(ERR_FATAL, "Vulkan: required device extension is not available: %s", name); } const float priority = 1.0; VkDeviceQueueCreateInfo queue_desc{ VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO }; queue_desc.queueFamilyIndex = vk.queue_family_index; queue_desc.queueCount = 1; queue_desc.pQueuePriorities = &priority; VkPhysicalDeviceFeatures features{}; features.shaderClipDistance = VK_TRUE; features.fillModeNonSolid = VK_TRUE; features.shaderStorageImageWriteWithoutFormat = VK_TRUE; VkDeviceCreateInfo device_desc{ VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO }; device_desc.queueCreateInfoCount = 1; device_desc.pQueueCreateInfos = &queue_desc; device_desc.enabledExtensionCount = sizeof(device_extensions)/sizeof(device_extensions[0]); device_desc.ppEnabledExtensionNames = device_extensions; device_desc.pEnabledFeatures = &features; VK_CHECK(vkCreateDevice(vk.physical_device, &device_desc, nullptr, &vk.device)); } } #define INIT_INSTANCE_FUNCTION(func) func = (PFN_ ## func)vkGetInstanceProcAddr(vk.instance, #func); #define INIT_DEVICE_FUNCTION(func) func = (PFN_ ## func)vkGetDeviceProcAddr(vk.device, #func); static void init_vulkan_library() { // // Get functions that do not depend on VkInstance (vk.instance == nullptr at this point). // INIT_INSTANCE_FUNCTION(vkCreateInstance) INIT_INSTANCE_FUNCTION(vkEnumerateInstanceExtensionProperties) // // Get instance level functions. // create_instance(); INIT_INSTANCE_FUNCTION(vkCreateDevice) INIT_INSTANCE_FUNCTION(vkDestroyInstance) INIT_INSTANCE_FUNCTION(vkEnumerateDeviceExtensionProperties) INIT_INSTANCE_FUNCTION(vkEnumeratePhysicalDevices) INIT_INSTANCE_FUNCTION(vkGetDeviceProcAddr) INIT_INSTANCE_FUNCTION(vkGetPhysicalDeviceFeatures) INIT_INSTANCE_FUNCTION(vkGetPhysicalDeviceFormatProperties) INIT_INSTANCE_FUNCTION(vkGetPhysicalDeviceMemoryProperties) INIT_INSTANCE_FUNCTION(vkGetPhysicalDeviceProperties) INIT_INSTANCE_FUNCTION(vkGetPhysicalDeviceQueueFamilyProperties) INIT_INSTANCE_FUNCTION(vkCreateWin32SurfaceKHR) INIT_INSTANCE_FUNCTION(vkDestroySurfaceKHR) INIT_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfaceCapabilitiesKHR) INIT_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfaceFormatsKHR) INIT_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfacePresentModesKHR) INIT_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfaceSupportKHR) INIT_INSTANCE_FUNCTION(vkCreateDebugReportCallbackEXT) INIT_INSTANCE_FUNCTION(vkDestroyDebugReportCallbackEXT) // // Create debug callback. // #ifndef NDEBUG { VkDebugReportCallbackCreateInfoEXT desc{ VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT }; desc.flags = VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT | VK_DEBUG_REPORT_ERROR_BIT_EXT; desc.pfnCallback = &debug_callback; VK_CHECK(vkCreateDebugReportCallbackEXT(vk.instance, &desc, nullptr, &vk.debug_callback)); } #endif // // Get device level functions. // create_device(); INIT_DEVICE_FUNCTION(vkAllocateCommandBuffers) INIT_DEVICE_FUNCTION(vkAllocateDescriptorSets) INIT_DEVICE_FUNCTION(vkAllocateMemory) INIT_DEVICE_FUNCTION(vkBeginCommandBuffer) INIT_DEVICE_FUNCTION(vkBindBufferMemory) INIT_DEVICE_FUNCTION(vkBindImageMemory) INIT_DEVICE_FUNCTION(vkCmdBeginRenderPass) INIT_DEVICE_FUNCTION(vkCmdBindDescriptorSets) INIT_DEVICE_FUNCTION(vkCmdBindIndexBuffer) INIT_DEVICE_FUNCTION(vkCmdBindPipeline) INIT_DEVICE_FUNCTION(vkCmdBindVertexBuffers) INIT_DEVICE_FUNCTION(vkCmdBlitImage) INIT_DEVICE_FUNCTION(vkCmdClearAttachments) INIT_DEVICE_FUNCTION(vkCmdCopyBuffer) INIT_DEVICE_FUNCTION(vkCmdCopyBufferToImage) INIT_DEVICE_FUNCTION(vkCmdCopyImage) INIT_DEVICE_FUNCTION(vkCmdDispatch) INIT_DEVICE_FUNCTION(vkCmdDraw) INIT_DEVICE_FUNCTION(vkCmdDrawIndexed) INIT_DEVICE_FUNCTION(vkCmdEndRenderPass) INIT_DEVICE_FUNCTION(vkCmdPipelineBarrier) INIT_DEVICE_FUNCTION(vkCmdPushConstants) INIT_DEVICE_FUNCTION(vkCmdSetDepthBias) INIT_DEVICE_FUNCTION(vkCmdSetScissor) INIT_DEVICE_FUNCTION(vkCmdSetViewport) INIT_DEVICE_FUNCTION(vkCreateBuffer) INIT_DEVICE_FUNCTION(vkCreateCommandPool) INIT_DEVICE_FUNCTION(vkCreateComputePipelines) INIT_DEVICE_FUNCTION(vkCreateDescriptorPool) INIT_DEVICE_FUNCTION(vkCreateDescriptorSetLayout) INIT_DEVICE_FUNCTION(vkCreateFence) INIT_DEVICE_FUNCTION(vkCreateFramebuffer) INIT_DEVICE_FUNCTION(vkCreateGraphicsPipelines) INIT_DEVICE_FUNCTION(vkCreateImage) INIT_DEVICE_FUNCTION(vkCreateImageView) INIT_DEVICE_FUNCTION(vkCreatePipelineLayout) INIT_DEVICE_FUNCTION(vkCreateRenderPass) INIT_DEVICE_FUNCTION(vkCreateSampler) INIT_DEVICE_FUNCTION(vkCreateSemaphore) INIT_DEVICE_FUNCTION(vkCreateShaderModule) INIT_DEVICE_FUNCTION(vkDestroyBuffer) INIT_DEVICE_FUNCTION(vkDestroyCommandPool) INIT_DEVICE_FUNCTION(vkDestroyDescriptorPool) INIT_DEVICE_FUNCTION(vkDestroyDescriptorSetLayout) INIT_DEVICE_FUNCTION(vkDestroyDevice) INIT_DEVICE_FUNCTION(vkDestroyFence) INIT_DEVICE_FUNCTION(vkDestroyFramebuffer) INIT_DEVICE_FUNCTION(vkDestroyImage) INIT_DEVICE_FUNCTION(vkDestroyImageView) INIT_DEVICE_FUNCTION(vkDestroyPipeline) INIT_DEVICE_FUNCTION(vkDestroyPipelineLayout) INIT_DEVICE_FUNCTION(vkDestroyRenderPass) INIT_DEVICE_FUNCTION(vkDestroySampler) INIT_DEVICE_FUNCTION(vkDestroySemaphore) INIT_DEVICE_FUNCTION(vkDestroyShaderModule) INIT_DEVICE_FUNCTION(vkDeviceWaitIdle) INIT_DEVICE_FUNCTION(vkEndCommandBuffer) INIT_DEVICE_FUNCTION(vkFreeCommandBuffers) INIT_DEVICE_FUNCTION(vkFreeDescriptorSets) INIT_DEVICE_FUNCTION(vkFreeMemory) INIT_DEVICE_FUNCTION(vkGetBufferMemoryRequirements) INIT_DEVICE_FUNCTION(vkGetDeviceQueue) INIT_DEVICE_FUNCTION(vkGetImageMemoryRequirements) INIT_DEVICE_FUNCTION(vkGetImageSubresourceLayout) INIT_DEVICE_FUNCTION(vkMapMemory) INIT_DEVICE_FUNCTION(vkQueueSubmit) INIT_DEVICE_FUNCTION(vkQueueWaitIdle) INIT_DEVICE_FUNCTION(vkResetDescriptorPool) INIT_DEVICE_FUNCTION(vkResetFences) INIT_DEVICE_FUNCTION(vkUpdateDescriptorSets) INIT_DEVICE_FUNCTION(vkWaitForFences) INIT_DEVICE_FUNCTION(vkAcquireNextImageKHR) INIT_DEVICE_FUNCTION(vkCreateSwapchainKHR) INIT_DEVICE_FUNCTION(vkDestroySwapchainKHR) INIT_DEVICE_FUNCTION(vkGetSwapchainImagesKHR) INIT_DEVICE_FUNCTION(vkQueuePresentKHR) } #undef INIT_INSTANCE_FUNCTION #undef INIT_DEVICE_FUNCTION static void deinit_vulkan_library() { vkCreateInstance = nullptr; vkEnumerateInstanceExtensionProperties = nullptr; vkCreateDevice = nullptr; vkDestroyInstance = nullptr; vkEnumerateDeviceExtensionProperties = nullptr; vkEnumeratePhysicalDevices = nullptr; vkGetDeviceProcAddr = nullptr; vkGetPhysicalDeviceFeatures = nullptr; vkGetPhysicalDeviceFormatProperties = nullptr; vkGetPhysicalDeviceMemoryProperties = nullptr; vkGetPhysicalDeviceProperties = nullptr; vkGetPhysicalDeviceQueueFamilyProperties = nullptr; vkCreateWin32SurfaceKHR = nullptr; vkDestroySurfaceKHR = nullptr; vkGetPhysicalDeviceSurfaceCapabilitiesKHR = nullptr; vkGetPhysicalDeviceSurfaceFormatsKHR = nullptr; vkGetPhysicalDeviceSurfacePresentModesKHR = nullptr; vkGetPhysicalDeviceSurfaceSupportKHR = nullptr; #ifndef NDEBUG vkCreateDebugReportCallbackEXT = nullptr; vkDestroyDebugReportCallbackEXT = nullptr; #endif vkAllocateCommandBuffers = nullptr; vkAllocateDescriptorSets = nullptr; vkAllocateMemory = nullptr; vkBeginCommandBuffer = nullptr; vkBindBufferMemory = nullptr; vkBindImageMemory = nullptr; vkCmdBeginRenderPass = nullptr; vkCmdBindDescriptorSets = nullptr; vkCmdBindIndexBuffer = nullptr; vkCmdBindPipeline = nullptr; vkCmdBindVertexBuffers = nullptr; vkCmdBlitImage = nullptr; vkCmdClearAttachments = nullptr; vkCmdCopyBuffer = nullptr; vkCmdCopyBufferToImage = nullptr; vkCmdCopyImage = nullptr; vkCmdDispatch = nullptr; vkCmdDraw = nullptr; vkCmdDrawIndexed = nullptr; vkCmdEndRenderPass = nullptr; vkCmdPipelineBarrier = nullptr; vkCmdPushConstants = nullptr; vkCmdSetDepthBias = nullptr; vkCmdSetScissor = nullptr; vkCmdSetViewport = nullptr; vkCreateBuffer = nullptr; vkCreateCommandPool = nullptr; vkCreateComputePipelines = nullptr; vkCreateDescriptorPool = nullptr; vkCreateDescriptorSetLayout = nullptr; vkCreateFence = nullptr; vkCreateFramebuffer = nullptr; vkCreateGraphicsPipelines = nullptr; vkCreateImage = nullptr; vkCreateImageView = nullptr; vkCreatePipelineLayout = nullptr; vkCreateRenderPass = nullptr; vkCreateSampler = nullptr; vkCreateSemaphore = nullptr; vkCreateShaderModule = nullptr; vkDestroyBuffer = nullptr; vkDestroyCommandPool = nullptr; vkDestroyDescriptorPool = nullptr; vkDestroyDescriptorSetLayout = nullptr; vkDestroyDevice = nullptr; vkDestroyFence = nullptr; vkDestroyFramebuffer = nullptr; vkDestroyImage = nullptr; vkDestroyImageView = nullptr; vkDestroyPipeline = nullptr; vkDestroyPipelineLayout = nullptr; vkDestroyRenderPass = nullptr; vkDestroySampler = nullptr; vkDestroySemaphore = nullptr; vkDestroyShaderModule = nullptr; vkDeviceWaitIdle = nullptr; vkEndCommandBuffer = nullptr; vkFreeCommandBuffers = nullptr; vkFreeDescriptorSets = nullptr; vkFreeMemory = nullptr; vkGetBufferMemoryRequirements = nullptr; vkGetDeviceQueue = nullptr; vkGetImageMemoryRequirements = nullptr; vkGetImageSubresourceLayout = nullptr; vkMapMemory = nullptr; vkQueueSubmit = nullptr; vkQueueWaitIdle = nullptr; vkResetDescriptorPool = nullptr; vkResetFences = nullptr; vkUpdateDescriptorSets = nullptr; vkWaitForFences = nullptr; vkAcquireNextImageKHR = nullptr; vkCreateSwapchainKHR = nullptr; vkDestroySwapchainKHR = nullptr; vkGetSwapchainImagesKHR = nullptr; vkQueuePresentKHR = nullptr; } VkPipeline create_pipeline(const Vk_Pipeline_Def&); void create_gamma_pipeline(); void vk_initialize() { init_vulkan_library(); VkPhysicalDeviceFeatures features; vkGetPhysicalDeviceFeatures(vk.physical_device, &features); if (features.shaderClipDistance == VK_FALSE) ri.Error(ERR_FATAL, "vk_create_instance: shaderClipDistance feature is not supported"); if (features.fillModeNonSolid == VK_FALSE) ri.Error(ERR_FATAL, "vk_create_instance: fillModeNonSolid feature is not supported"); if (features.shaderStorageImageWriteWithoutFormat == VK_FALSE) ri.Error(ERR_FATAL, "vk_create_instance: shaderStorageImageWriteWithoutFormat feature is not supported"); vkGetDeviceQueue(vk.device, vk.queue_family_index, 0, &vk.queue); // // Swapchain. // { vk.swapchain = create_swapchain(vk.physical_device, vk.device, vk.surface, vk.surface_format); VK_CHECK(vkGetSwapchainImagesKHR(vk.device, vk.swapchain, &vk.swapchain_image_count, nullptr)); vk.swapchain_image_count = std::min(vk.swapchain_image_count, (uint32_t)MAX_SWAPCHAIN_IMAGES); VK_CHECK(vkGetSwapchainImagesKHR(vk.device, vk.swapchain, &vk.swapchain_image_count, vk.swapchain_images)); for (uint32_t i = 0; i < vk.swapchain_image_count; i++) { VkImageViewCreateInfo desc{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; desc.image = vk.swapchain_images[i]; desc.viewType = VK_IMAGE_VIEW_TYPE_2D; desc.format = vk.surface_format.format; desc.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; desc.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; desc.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; desc.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; desc.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; desc.subresourceRange.baseMipLevel = 0; desc.subresourceRange.levelCount = 1; desc.subresourceRange.baseArrayLayer = 0; desc.subresourceRange.layerCount = 1; VK_CHECK(vkCreateImageView(vk.device, &desc, nullptr, &vk.swapchain_image_views[i])); } } // // Sync primitives. // { VkSemaphoreCreateInfo desc{ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; for (uint32_t i = 0; i < vk.swapchain_image_count; i++) { VK_CHECK(vkCreateSemaphore(vk.device, &desc, nullptr, &vk.rendering_finished[i])); } VK_CHECK(vkCreateSemaphore(vk.device, &desc, nullptr, &vk.image_acquired)); VkFenceCreateInfo fence_desc{ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO }; fence_desc.flags = VK_FENCE_CREATE_SIGNALED_BIT; VK_CHECK(vkCreateFence(vk.device, &fence_desc, nullptr, &vk.rendering_finished_fence)); } // // Command pool. // { VkCommandPoolCreateInfo desc{ VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO }; desc.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; desc.queueFamilyIndex = vk.queue_family_index; VK_CHECK(vkCreateCommandPool(vk.device, &desc, nullptr, &vk.command_pool)); } // // Command buffer. // { VkCommandBufferAllocateInfo alloc_info{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; alloc_info.commandPool = vk.command_pool; alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; alloc_info.commandBufferCount = 1; VK_CHECK(vkAllocateCommandBuffers(vk.device, &alloc_info, &vk.command_buffer)); } // // Output image. // { VkImageCreateInfo image_ci{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; image_ci.imageType = VK_IMAGE_TYPE_2D; image_ci.format = output_image_format;; image_ci.extent.width = glConfig.vidWidth; image_ci.extent.height = glConfig.vidHeight; image_ci.extent.depth = 1; image_ci.mipLevels = 1; image_ci.arrayLayers = 1; image_ci.samples = VK_SAMPLE_COUNT_1_BIT; image_ci.tiling = VK_IMAGE_TILING_OPTIMAL; image_ci.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; image_ci.sharingMode = VK_SHARING_MODE_EXCLUSIVE; image_ci.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; VK_CHECK(vkCreateImage(vk.device, &image_ci, nullptr, &vk.output_image)); VkMemoryRequirements memory_requirements; vkGetImageMemoryRequirements(vk.device, vk.output_image, &memory_requirements); VkMemoryAllocateInfo alloc_info{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; alloc_info.allocationSize = memory_requirements.size; alloc_info.memoryTypeIndex = find_memory_type(vk.physical_device, memory_requirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); VK_CHECK(vkAllocateMemory(vk.device, &alloc_info, nullptr, &vk.output_image_memory)); VK_CHECK(vkBindImageMemory(vk.device, vk.output_image, vk.output_image_memory, 0)); VkImageViewCreateInfo image_view_ci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; image_view_ci.image = vk.output_image; image_view_ci.viewType = VK_IMAGE_VIEW_TYPE_2D; image_view_ci.format = output_image_format; image_view_ci.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; VK_CHECK(vkCreateImageView(vk.device, &image_view_ci, nullptr, &vk.output_image_view)); } // // Depth attachment image. // { VkFormat depth_format = get_depth_format(vk.physical_device); // create depth image { VkImageCreateInfo desc{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; desc.imageType = VK_IMAGE_TYPE_2D; desc.format = depth_format; desc.extent.width = glConfig.vidWidth; desc.extent.height = glConfig.vidHeight; desc.extent.depth = 1; desc.mipLevels = 1; desc.arrayLayers = 1; desc.samples = VK_SAMPLE_COUNT_1_BIT; desc.tiling = VK_IMAGE_TILING_OPTIMAL; desc.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; desc.sharingMode = VK_SHARING_MODE_EXCLUSIVE; desc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; VK_CHECK(vkCreateImage(vk.device, &desc, nullptr, &vk.depth_image)); } // allocate depth image memory { VkMemoryRequirements memory_requirements; vkGetImageMemoryRequirements(vk.device, vk.depth_image, &memory_requirements); VkMemoryAllocateInfo alloc_info{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; alloc_info.allocationSize = memory_requirements.size; alloc_info.memoryTypeIndex = find_memory_type(vk.physical_device, memory_requirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); VK_CHECK(vkAllocateMemory(vk.device, &alloc_info, nullptr, &vk.depth_image_memory)); VK_CHECK(vkBindImageMemory(vk.device, vk.depth_image, vk.depth_image_memory, 0)); } // create depth image view { VkImageViewCreateInfo desc{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; desc.image = vk.depth_image; desc.viewType = VK_IMAGE_VIEW_TYPE_2D; desc.format = depth_format; desc.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; desc.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; desc.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; desc.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; desc.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; desc.subresourceRange.baseMipLevel = 0; desc.subresourceRange.levelCount = 1; desc.subresourceRange.baseArrayLayer = 0; desc.subresourceRange.layerCount = 1; VK_CHECK(vkCreateImageView(vk.device, &desc, nullptr, &vk.depth_image_view)); } VkImageAspectFlags image_aspect_flags = VK_IMAGE_ASPECT_DEPTH_BIT; if (r_stencilbits->integer) image_aspect_flags |= VK_IMAGE_ASPECT_STENCIL_BIT; record_and_run_commands(vk.command_pool, vk.queue, [&image_aspect_flags](VkCommandBuffer command_buffer) { record_image_layout_transition(command_buffer, vk.depth_image, image_aspect_flags, 0, VK_IMAGE_LAYOUT_UNDEFINED, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); }); } // // Renderpass. // { VkFormat depth_format = get_depth_format(vk.physical_device); vk.render_pass = create_render_pass(vk.device, output_image_format, depth_format); } // // Framebuffer for output image rendering. // { VkImageView attachments[2] = { vk.output_image_view, vk.depth_image_view }; VkFramebufferCreateInfo create_info{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; create_info.renderPass = vk.render_pass; create_info.attachmentCount = 2; create_info.pAttachments = attachments; create_info.width = glConfig.vidWidth; create_info.height = glConfig.vidHeight; create_info.layers = 1; VK_CHECK(vkCreateFramebuffer(vk.device, &create_info, nullptr, &vk.framebuffer)); } // Descriptor pool. { VkDescriptorPoolSize pool_size; pool_size.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; pool_size.descriptorCount = MAX_DRAWIMAGES; VkDescriptorPoolCreateInfo create_info{ VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; create_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; // used by the cinematic images create_info.maxSets = MAX_DRAWIMAGES; create_info.poolSizeCount = 1; create_info.pPoolSizes = &pool_size; VK_CHECK(vkCreateDescriptorPool(vk.device, &create_info, nullptr, &vk.descriptor_pool)); } // Gamma descriptor pool { VkDescriptorPoolSize pool_sizes[3]; pool_sizes[0].type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; pool_sizes[0].descriptorCount = 1; pool_sizes[1].type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; pool_sizes[1].descriptorCount = 1; pool_sizes[2].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; pool_sizes[2].descriptorCount = 1; VkDescriptorPoolCreateInfo create_info{ VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; create_info.maxSets = 1; create_info.poolSizeCount = 3; create_info.pPoolSizes = pool_sizes; VK_CHECK(vkCreateDescriptorPool(vk.device, &create_info, nullptr, &vk.gamma_descriptor_pool)); } // Main set layout { VkDescriptorSetLayoutBinding binding; binding.binding = 0; binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; binding.descriptorCount = 1; binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; binding.pImmutableSamplers = nullptr; VkDescriptorSetLayoutCreateInfo set_layout_ci{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; set_layout_ci.bindingCount = 1; set_layout_ci.pBindings = &binding; VK_CHECK(vkCreateDescriptorSetLayout(vk.device, &set_layout_ci, nullptr, &vk.set_layout)); } // Gamma set layout { VkDescriptorSetLayoutBinding bindings[3]; bindings[0].binding = 0; bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; bindings[0].descriptorCount = 1; bindings[0].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; bindings[0].pImmutableSamplers = nullptr; bindings[1].binding = 1; bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; bindings[1].descriptorCount = 1; bindings[1].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; bindings[1].pImmutableSamplers = nullptr; bindings[2].binding = 2; bindings[2].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; bindings[2].descriptorCount = 1; bindings[2].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; bindings[2].pImmutableSamplers = nullptr; VkDescriptorSetLayoutCreateInfo set_layout_ci{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; set_layout_ci.bindingCount = 3; set_layout_ci.pBindings = bindings; VK_CHECK(vkCreateDescriptorSetLayout(vk.device, &set_layout_ci, nullptr, &vk.gamma_set_layout)); } // Main pipeline layout { VkPushConstantRange push_range; push_range.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; push_range.offset = 0; push_range.size = 128; // 32 floats VkDescriptorSetLayout set_layouts[2] = {vk.set_layout, vk.set_layout}; VkPipelineLayoutCreateInfo pipeline_layout_ci{ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; pipeline_layout_ci.setLayoutCount = 2; pipeline_layout_ci.pSetLayouts = set_layouts; pipeline_layout_ci.pushConstantRangeCount = 1; pipeline_layout_ci.pPushConstantRanges = &push_range; VK_CHECK(vkCreatePipelineLayout(vk.device, &pipeline_layout_ci, nullptr, &vk.pipeline_layout)); } // Gamma pipeline layout { VkPushConstantRange push_constants; push_constants.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; push_constants.offset = 0; push_constants.size = 12; // 3 uint VkPipelineLayoutCreateInfo pipeline_layout_ci{ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; pipeline_layout_ci.setLayoutCount = 1; pipeline_layout_ci.pSetLayouts = &vk.gamma_set_layout; pipeline_layout_ci.pushConstantRangeCount = 1; pipeline_layout_ci.pPushConstantRanges = &push_constants; VK_CHECK(vkCreatePipelineLayout(vk.device, &pipeline_layout_ci, nullptr, &vk.gamma_pipeline_layout)); } // // Geometry buffers. // { VkBufferCreateInfo desc{ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; desc.sharingMode = VK_SHARING_MODE_EXCLUSIVE; desc.size = VERTEX_BUFFER_SIZE; desc.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; VK_CHECK(vkCreateBuffer(vk.device, &desc, nullptr, &vk.vertex_buffer)); desc.size = INDEX_BUFFER_SIZE; desc.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT; VK_CHECK(vkCreateBuffer(vk.device, &desc, nullptr, &vk.index_buffer)); VkMemoryRequirements vb_memory_requirements; vkGetBufferMemoryRequirements(vk.device, vk.vertex_buffer, &vb_memory_requirements); VkMemoryRequirements ib_memory_requirements; vkGetBufferMemoryRequirements(vk.device, vk.index_buffer, &ib_memory_requirements); VkDeviceSize mask = ~(ib_memory_requirements.alignment - 1); VkDeviceSize index_buffer_offset = (vb_memory_requirements.size + ib_memory_requirements.alignment - 1) & mask; uint32_t memory_type_bits = vb_memory_requirements.memoryTypeBits & ib_memory_requirements.memoryTypeBits; uint32_t memory_type = find_memory_type(vk.physical_device, memory_type_bits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); VkMemoryAllocateInfo alloc_info{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; alloc_info.allocationSize = index_buffer_offset + ib_memory_requirements.size; alloc_info.memoryTypeIndex = memory_type; VK_CHECK(vkAllocateMemory(vk.device, &alloc_info, nullptr, &vk.geometry_buffer_memory)); vkBindBufferMemory(vk.device, vk.vertex_buffer, vk.geometry_buffer_memory, 0); vkBindBufferMemory(vk.device, vk.index_buffer, vk.geometry_buffer_memory, index_buffer_offset); void* data; VK_CHECK(vkMapMemory(vk.device, vk.geometry_buffer_memory, 0, VK_WHOLE_SIZE, 0, &data)); vk.vertex_buffer_ptr = (byte*)data; vk.index_buffer_ptr = (byte*)data + index_buffer_offset; } // // Gamma buffer. // { VkBufferCreateInfo desc{ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; desc.size = 256 * sizeof(float); desc.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; VK_CHECK(vkCreateBuffer(vk.device, &desc, nullptr, &vk.gamma_buffer)); VkMemoryRequirements memory_requirements; vkGetBufferMemoryRequirements(vk.device, vk.gamma_buffer, &memory_requirements); uint32_t memory_type = find_memory_type(vk.physical_device, memory_requirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); VkMemoryAllocateInfo alloc_info{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; alloc_info.allocationSize = memory_requirements.size; alloc_info.memoryTypeIndex = memory_type; VK_CHECK(vkAllocateMemory(vk.device, &alloc_info, nullptr, &vk.gamma_buffer_memory)); vkBindBufferMemory(vk.device, vk.gamma_buffer, vk.gamma_buffer_memory, 0); } // // Shader modules. // { auto create_shader_module = [](uint8_t* bytes, int count) { if (count % 4 != 0) { ri.Error(ERR_FATAL, "Vulkan: SPIR-V binary buffer size is not multiple of 4"); } VkShaderModuleCreateInfo desc{ VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO }; desc.codeSize = count; desc.pCode = reinterpret_cast(bytes); VkShaderModule module; VK_CHECK(vkCreateShaderModule(vk.device, &desc, nullptr, &module)); return module; }; extern unsigned char single_texture_vert_spv[]; extern long long single_texture_vert_spv_size; vk.single_texture_vs = create_shader_module(single_texture_vert_spv, single_texture_vert_spv_size); extern unsigned char single_texture_clipping_plane_vert_spv[]; extern long long single_texture_clipping_plane_vert_spv_size; vk.single_texture_clipping_plane_vs = create_shader_module(single_texture_clipping_plane_vert_spv, single_texture_clipping_plane_vert_spv_size); extern unsigned char single_texture_frag_spv[]; extern long long single_texture_frag_spv_size; vk.single_texture_fs = create_shader_module(single_texture_frag_spv, single_texture_frag_spv_size); extern unsigned char multi_texture_vert_spv[]; extern long long multi_texture_vert_spv_size; vk.multi_texture_vs = create_shader_module(multi_texture_vert_spv, multi_texture_vert_spv_size); extern unsigned char multi_texture_clipping_plane_vert_spv[]; extern long long multi_texture_clipping_plane_vert_spv_size; vk.multi_texture_clipping_plane_vs = create_shader_module(multi_texture_clipping_plane_vert_spv, multi_texture_clipping_plane_vert_spv_size); extern unsigned char multi_texture_mul_frag_spv[]; extern long long multi_texture_mul_frag_spv_size; vk.multi_texture_mul_fs = create_shader_module(multi_texture_mul_frag_spv, multi_texture_mul_frag_spv_size); extern unsigned char multi_texture_add_frag_spv[]; extern long long multi_texture_add_frag_spv_size; vk.multi_texture_add_fs = create_shader_module(multi_texture_add_frag_spv, multi_texture_add_frag_spv_size); } // // Standard pipelines. // { create_gamma_pipeline(); // skybox { Vk_Pipeline_Def def; def.shader_type = Vk_Shader_Type::single_texture; def.state_bits = 0; def.face_culling = CT_FRONT_SIDED; def.polygon_offset = false; def.clipping_plane = false; def.mirror = false; vk.skybox_pipeline = create_pipeline(def); } // Q3 stencil shadows { { Vk_Pipeline_Def def; def.polygon_offset = false; def.state_bits = 0; def.shader_type = Vk_Shader_Type::single_texture; def.clipping_plane = false; def.shadow_phase = Vk_Shadow_Phase::shadow_edges_rendering; cullType_t cull_types[2] = {CT_FRONT_SIDED, CT_BACK_SIDED}; bool mirror_flags[2] = {false, true}; for (int i = 0; i < 2; i++) { def.face_culling = cull_types[i]; for (int j = 0; j < 2; j++) { def.mirror = mirror_flags[j]; vk.shadow_volume_pipelines[i][j] = create_pipeline(def); } } } { Vk_Pipeline_Def def; def.face_culling = CT_FRONT_SIDED; def.polygon_offset = false; def.state_bits = GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO; def.shader_type = Vk_Shader_Type::single_texture; def.clipping_plane = false; def.mirror = false; def.shadow_phase = Vk_Shadow_Phase::fullscreen_quad_rendering; vk.shadow_finish_pipeline = create_pipeline(def); } } // fog and dlights { Vk_Pipeline_Def def; def.shader_type = Vk_Shader_Type::single_texture; def.clipping_plane = false; def.mirror = false; unsigned int fog_state_bits[2] = { GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA | GLS_DEPTHFUNC_EQUAL, GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA }; unsigned int dlight_state_bits[2] = { GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL }; bool polygon_offset[2] = {false, true}; for (int i = 0; i < 2; i++) { unsigned fog_state = fog_state_bits[i]; unsigned dlight_state = dlight_state_bits[i]; for (int j = 0; j < 3; j++) { def.face_culling = j; // cullType_t value for (int k = 0; k < 2; k++) { def.polygon_offset = polygon_offset[k]; def.state_bits = fog_state; vk.fog_pipelines[i][j][k] = create_pipeline(def); def.state_bits = dlight_state; vk.dlight_pipelines[i][j][k] = create_pipeline(def); } } } } // debug pipelines { Vk_Pipeline_Def def; def.state_bits = GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE; vk.tris_debug_pipeline = create_pipeline(def); } { Vk_Pipeline_Def def; def.state_bits = GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE; def.face_culling = CT_BACK_SIDED; vk.tris_mirror_debug_pipeline = create_pipeline(def); } { Vk_Pipeline_Def def; def.state_bits = GLS_DEPTHMASK_TRUE; def.line_primitives = true; vk.normals_debug_pipeline = create_pipeline(def); } { Vk_Pipeline_Def def; def.state_bits = GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE; vk.surface_debug_pipeline_solid = create_pipeline(def); } { Vk_Pipeline_Def def; def.state_bits = GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE; def.line_primitives = true; vk.surface_debug_pipeline_outline = create_pipeline(def); } { Vk_Pipeline_Def def; def.state_bits = GLS_DEPTHTEST_DISABLE | GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; vk.images_debug_pipeline = create_pipeline(def); } } vk.active = true; } void vk_shutdown() { vkDestroyImage(vk.device, vk.output_image, nullptr); vkFreeMemory(vk.device, vk.output_image_memory, nullptr); vkDestroyImageView(vk.device, vk.output_image_view, nullptr); vkDestroyImage(vk.device, vk.depth_image, nullptr); vkFreeMemory(vk.device, vk.depth_image_memory, nullptr); vkDestroyImageView(vk.device, vk.depth_image_view, nullptr); vkDestroyFramebuffer(vk.device, vk.framebuffer, nullptr); vkDestroyRenderPass(vk.device, vk.render_pass, nullptr); vkDestroyCommandPool(vk.device, vk.command_pool, nullptr); for (uint32_t i = 0; i < vk.swapchain_image_count; i++) { vkDestroyImageView(vk.device, vk.swapchain_image_views[i], nullptr); vkDestroySemaphore(vk.device, vk.rendering_finished[i], nullptr); } vkDestroyDescriptorPool(vk.device, vk.descriptor_pool, nullptr); vkDestroyDescriptorPool(vk.device, vk.gamma_descriptor_pool, nullptr); vkDestroyDescriptorSetLayout(vk.device, vk.set_layout, nullptr); vkDestroyDescriptorSetLayout(vk.device, vk.gamma_set_layout, nullptr); vkDestroyPipelineLayout(vk.device, vk.pipeline_layout, nullptr); vkDestroyPipelineLayout(vk.device, vk.gamma_pipeline_layout, nullptr); vkDestroyBuffer(vk.device, vk.vertex_buffer, nullptr); vkDestroyBuffer(vk.device, vk.index_buffer, nullptr); vkFreeMemory(vk.device, vk.geometry_buffer_memory, nullptr); vkDestroyBuffer(vk.device, vk.gamma_buffer, nullptr); vkFreeMemory(vk.device, vk.gamma_buffer_memory, nullptr); vkDestroySemaphore(vk.device, vk.image_acquired, nullptr); vkDestroyFence(vk.device, vk.rendering_finished_fence, nullptr); vkDestroyShaderModule(vk.device, vk.single_texture_vs, nullptr); vkDestroyShaderModule(vk.device, vk.single_texture_clipping_plane_vs, nullptr); vkDestroyShaderModule(vk.device, vk.single_texture_fs, nullptr); vkDestroyShaderModule(vk.device, vk.multi_texture_vs, nullptr); vkDestroyShaderModule(vk.device, vk.multi_texture_clipping_plane_vs, nullptr); vkDestroyShaderModule(vk.device, vk.multi_texture_mul_fs, nullptr); vkDestroyShaderModule(vk.device, vk.multi_texture_add_fs, nullptr); vkDestroyPipeline(vk.device, vk.gamma_pipeline, nullptr); vkDestroyPipeline(vk.device, vk.skybox_pipeline, nullptr); for (int i = 0; i < 2; i++) for (int j = 0; j < 2; j++) { vkDestroyPipeline(vk.device, vk.shadow_volume_pipelines[i][j], nullptr); } vkDestroyPipeline(vk.device, vk.shadow_finish_pipeline, nullptr); for (int i = 0; i < 2; i++) for (int j = 0; j < 3; j++) for (int k = 0; k < 2; k++) { vkDestroyPipeline(vk.device, vk.fog_pipelines[i][j][k], nullptr); vkDestroyPipeline(vk.device, vk.dlight_pipelines[i][j][k], nullptr); } vkDestroyPipeline(vk.device, vk.tris_debug_pipeline, nullptr); vkDestroyPipeline(vk.device, vk.tris_mirror_debug_pipeline, nullptr); vkDestroyPipeline(vk.device, vk.normals_debug_pipeline, nullptr); vkDestroyPipeline(vk.device, vk.surface_debug_pipeline_solid, nullptr); vkDestroyPipeline(vk.device, vk.surface_debug_pipeline_outline, nullptr); vkDestroyPipeline(vk.device, vk.images_debug_pipeline, nullptr); vkDestroySwapchainKHR(vk.device, vk.swapchain, nullptr); vkDestroyDevice(vk.device, nullptr); vkDestroySurfaceKHR(vk.instance, vk.surface, nullptr); #ifndef NDEBUG vkDestroyDebugReportCallbackEXT(vk.instance, vk.debug_callback, nullptr); #endif vkDestroyInstance(vk.instance, nullptr); Com_Memset(&vk, 0, sizeof(vk)); deinit_vulkan_library(); } void vk_release_resources() { vkDeviceWaitIdle(vk.device); for (int i = 0; i < vk_world.num_image_chunks; i++) vkFreeMemory(vk.device, vk_world.image_chunks[i].memory, nullptr); if (vk_world.staging_buffer != VK_NULL_HANDLE) vkDestroyBuffer(vk.device, vk_world.staging_buffer, nullptr); if (vk_world.staging_buffer_memory != VK_NULL_HANDLE) vkFreeMemory(vk.device, vk_world.staging_buffer_memory, nullptr); for (int i = 0; i < vk_world.num_samplers; i++) vkDestroySampler(vk.device, vk_world.samplers[i], nullptr); for (int i = 0; i < vk_world.num_pipelines; i++) vkDestroyPipeline(vk.device, vk_world.pipelines[i], nullptr); vk_world.pipeline_create_time = 0.0f; for (int i = 0; i < MAX_VK_IMAGES; i++) { Vk_Image& image = vk_world.images[i]; if (image.handle != VK_NULL_HANDLE) { vkDestroyImage(vk.device, image.handle, nullptr); vkDestroyImageView(vk.device, image.view, nullptr); } } Com_Memset(&vk_world, 0, sizeof(vk_world)); VK_CHECK(vkResetDescriptorPool(vk.device, vk.descriptor_pool, 0)); // Reset geometry buffer's current offsets. vk.xyz_elements = 0; vk.color_st_elements = 0; vk.index_buffer_offset = 0; } Vk_Image vk_create_image(int width, int height, VkFormat format, int mip_levels, bool repeat_texture) { Vk_Image image; // create image { VkImageCreateInfo desc{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; desc.imageType = VK_IMAGE_TYPE_2D; desc.format = format; desc.extent.width = width; desc.extent.height = height; desc.extent.depth = 1; desc.mipLevels = mip_levels; desc.arrayLayers = 1; desc.samples = VK_SAMPLE_COUNT_1_BIT; desc.tiling = VK_IMAGE_TILING_OPTIMAL; desc.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; desc.sharingMode = VK_SHARING_MODE_EXCLUSIVE; desc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; VK_CHECK(vkCreateImage(vk.device, &desc, nullptr, &image.handle)); allocate_and_bind_image_memory(image.handle); } // create image view { VkImageViewCreateInfo desc{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; desc.image = image.handle; desc.viewType = VK_IMAGE_VIEW_TYPE_2D; desc.format = format; desc.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; desc.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; desc.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; desc.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; desc.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; desc.subresourceRange.baseMipLevel = 0; desc.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; desc.subresourceRange.baseArrayLayer = 0; desc.subresourceRange.layerCount = 1; VK_CHECK(vkCreateImageView(vk.device, &desc, nullptr, &image.view)); } // create associated descriptor set { VkDescriptorSetAllocateInfo desc{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; desc.descriptorPool = vk.descriptor_pool; desc.descriptorSetCount = 1; desc.pSetLayouts = &vk.set_layout; VK_CHECK(vkAllocateDescriptorSets(vk.device, &desc, &image.descriptor_set)); vk_update_descriptor_set(image.descriptor_set, image.view, mip_levels > 1, repeat_texture); vk_world.current_descriptor_sets[glState.currenttmu] = image.descriptor_set; } return image; } void vk_upload_image_data(VkImage image, int width, int height, bool mipmap, const uint8_t* pixels, int bytes_per_pixel) { VkBufferImageCopy regions[16]; int num_regions = 0; int buffer_size = 0; while (true) { VkBufferImageCopy region; region.bufferOffset = buffer_size; region.bufferRowLength = 0; region.bufferImageHeight = 0; region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.imageSubresource.mipLevel = num_regions; region.imageSubresource.baseArrayLayer = 0; region.imageSubresource.layerCount = 1; region.imageOffset = VkOffset3D{ 0, 0, 0 }; region.imageExtent = VkExtent3D{ (uint32_t)width, (uint32_t)height, 1 }; regions[num_regions] = region; num_regions++; buffer_size += width * height * bytes_per_pixel; if (!mipmap || (width == 1 && height == 1)) break; width >>= 1; if (width < 1) width = 1; height >>= 1; if (height < 1) height = 1; } ensure_staging_buffer_allocation(buffer_size); Com_Memcpy(vk_world.staging_buffer_ptr, pixels, buffer_size); record_and_run_commands(vk.command_pool, vk.queue, [&image, &num_regions, ®ions](VkCommandBuffer command_buffer) { record_image_layout_transition(command_buffer, image, VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_IMAGE_LAYOUT_UNDEFINED, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); vkCmdCopyBufferToImage(command_buffer, vk_world.staging_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, num_regions, regions); record_image_layout_transition(command_buffer, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); }); } void vk_update_descriptor_set(VkDescriptorSet set, VkImageView image_view, bool mipmap, bool repeat_texture) { Vk_Sampler_Def sampler_def; sampler_def.repeat_texture = repeat_texture; if (mipmap) { sampler_def.gl_mag_filter = gl_filter_max; sampler_def.gl_min_filter = gl_filter_min; } else { sampler_def.gl_mag_filter = GL_LINEAR; sampler_def.gl_min_filter = GL_LINEAR; } VkDescriptorImageInfo image_info; image_info.sampler = vk_find_sampler(sampler_def); image_info.imageView = image_view; image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; VkWriteDescriptorSet descriptor_write{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; descriptor_write.dstSet = set; descriptor_write.dstBinding = 0; descriptor_write.dstArrayElement = 0; descriptor_write.descriptorCount = 1; descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; descriptor_write.pImageInfo = &image_info; vkUpdateDescriptorSets(vk.device, 1, &descriptor_write, 0, nullptr); } static void create_gamma_pipeline() { extern unsigned char apply_gamma_comp_spv[]; extern long long apply_gamma_comp_spv_size; VkShaderModuleCreateInfo shader_ci{ VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO }; shader_ci.codeSize = apply_gamma_comp_spv_size; shader_ci.pCode = reinterpret_cast(apply_gamma_comp_spv); VkShaderModule apply_gamma_shader; VK_CHECK(vkCreateShaderModule(vk.device, &shader_ci, nullptr, &apply_gamma_shader)); VkComputePipelineCreateInfo pipeline_ci{ VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO }; pipeline_ci.stage = VkPipelineShaderStageCreateInfo{ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO }; pipeline_ci.stage.stage = VK_SHADER_STAGE_COMPUTE_BIT; pipeline_ci.stage.module = apply_gamma_shader; pipeline_ci.stage.pName = "main"; pipeline_ci.layout = vk.gamma_pipeline_layout; VK_CHECK(vkCreateComputePipelines(vk.device, VK_NULL_HANDLE, 1, &pipeline_ci, nullptr, &vk.gamma_pipeline)); vkDestroyShaderModule(vk.device, apply_gamma_shader, nullptr); VkDescriptorSetAllocateInfo set_alloc_info{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; set_alloc_info.descriptorPool = vk.gamma_descriptor_pool; set_alloc_info.descriptorSetCount = 1; set_alloc_info.pSetLayouts = &vk.gamma_set_layout; VK_CHECK(vkAllocateDescriptorSets(vk.device, &set_alloc_info, &vk.gamma_descriptor_set)); // Write output image descriptor { VkDescriptorImageInfo image_info{}; image_info.imageView = vk.output_image_view; image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; VkWriteDescriptorSet descriptor_write{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; descriptor_write.dstSet = vk.gamma_descriptor_set; descriptor_write.dstBinding = 0; descriptor_write.dstArrayElement = 0; descriptor_write.descriptorCount = 1; descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; descriptor_write.pImageInfo = &image_info; vkUpdateDescriptorSets(vk.device, 1, &descriptor_write, 0, nullptr); } // Write gamma buffer descriptor { VkDescriptorBufferInfo buffer_info{}; buffer_info.buffer = vk.gamma_buffer; buffer_info.range = VK_WHOLE_SIZE; VkWriteDescriptorSet descriptor_write{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; descriptor_write.dstSet = vk.gamma_descriptor_set; descriptor_write.dstBinding = 2; descriptor_write.dstArrayElement = 0; descriptor_write.descriptorCount = 1; descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; descriptor_write.pBufferInfo = &buffer_info; vkUpdateDescriptorSets(vk.device, 1, &descriptor_write, 0, nullptr); } } static VkPipeline create_pipeline(const Vk_Pipeline_Def& def) { auto get_shader_stage_desc = [](VkShaderStageFlagBits stage, VkShaderModule shader_module, const char* entry) { VkPipelineShaderStageCreateInfo desc{ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO }; desc.stage = stage; desc.module = shader_module; desc.pName = entry; return desc; }; struct Specialization_Data { int32_t alpha_test_func; } specialization_data; if ((def.state_bits & GLS_ATEST_BITS) == 0) specialization_data.alpha_test_func = 0; else if (def.state_bits & GLS_ATEST_GT_0) specialization_data.alpha_test_func = 1; else if (def.state_bits & GLS_ATEST_LT_80) specialization_data.alpha_test_func = 2; else if (def.state_bits & GLS_ATEST_GE_80) specialization_data.alpha_test_func = 3; else ri.Error(ERR_DROP, "create_pipeline: invalid alpha test state bits\n"); VkSpecializationMapEntry specialization_entries[1]; specialization_entries[0].constantID = 0; specialization_entries[0].offset = offsetof(struct Specialization_Data, alpha_test_func); specialization_entries[0].size = sizeof(int32_t); VkSpecializationInfo specialization_info; specialization_info.mapEntryCount = 1; specialization_info.pMapEntries = specialization_entries; specialization_info.dataSize = sizeof(Specialization_Data); specialization_info.pData = &specialization_data; std::vector shader_stages_state; VkShaderModule* vs_module, *fs_module; if (def.shader_type == Vk_Shader_Type::single_texture) { vs_module = def.clipping_plane ? &vk.single_texture_clipping_plane_vs : &vk.single_texture_vs; fs_module = &vk.single_texture_fs; } else if (def.shader_type == Vk_Shader_Type::multi_texture_mul) { vs_module = def.clipping_plane ? &vk.multi_texture_clipping_plane_vs : &vk.multi_texture_vs; fs_module = &vk.multi_texture_mul_fs; } else if (def.shader_type == Vk_Shader_Type::multi_texture_add) { vs_module = def.clipping_plane ? &vk.multi_texture_clipping_plane_vs : &vk.multi_texture_vs; fs_module = &vk.multi_texture_add_fs; } shader_stages_state.push_back(get_shader_stage_desc(VK_SHADER_STAGE_VERTEX_BIT, *vs_module, "main")); shader_stages_state.push_back(get_shader_stage_desc(VK_SHADER_STAGE_FRAGMENT_BIT, *fs_module, "main")); if (def.state_bits & GLS_ATEST_BITS) shader_stages_state.back().pSpecializationInfo = &specialization_info; // // Vertex input // VkVertexInputBindingDescription bindings[4]; // xyz array bindings[0].binding = 0; bindings[0].stride = sizeof(vec4_t); bindings[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX; // color array bindings[1].binding = 1; bindings[1].stride = sizeof(color4ub_t); bindings[1].inputRate = VK_VERTEX_INPUT_RATE_VERTEX; // st0 array bindings[2].binding = 2; bindings[2].stride = sizeof(vec2_t); bindings[2].inputRate = VK_VERTEX_INPUT_RATE_VERTEX; // st1 array bindings[3].binding = 3; bindings[3].stride = sizeof(vec2_t); bindings[3].inputRate = VK_VERTEX_INPUT_RATE_VERTEX; VkVertexInputAttributeDescription attribs[4]; // xyz attribs[0].location = 0; attribs[0].binding = 0; attribs[0].format = VK_FORMAT_R32G32B32A32_SFLOAT; attribs[0].offset = 0; // color attribs[1].location = 1; attribs[1].binding = 1; attribs[1].format = VK_FORMAT_R8G8B8A8_UNORM; attribs[1].offset = 0; // st0 attribs[2].location = 2; attribs[2].binding = 2; attribs[2].format = VK_FORMAT_R32G32_SFLOAT; attribs[2].offset = 0; // st1 attribs[3].location = 3; attribs[3].binding = 3; attribs[3].format = VK_FORMAT_R32G32_SFLOAT; attribs[3].offset = 0; VkPipelineVertexInputStateCreateInfo vertex_input_state{ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; vertex_input_state.vertexBindingDescriptionCount = (def.shader_type == Vk_Shader_Type::single_texture) ? 3 : 4; vertex_input_state.pVertexBindingDescriptions = bindings; vertex_input_state.vertexAttributeDescriptionCount = (def.shader_type == Vk_Shader_Type::single_texture) ? 3 : 4; vertex_input_state.pVertexAttributeDescriptions = attribs; // // Primitive assembly. // VkPipelineInputAssemblyStateCreateInfo input_assembly_state{ VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO }; input_assembly_state.topology = def.line_primitives ? VK_PRIMITIVE_TOPOLOGY_LINE_LIST : VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; input_assembly_state.primitiveRestartEnable = VK_FALSE; // // Viewport. // VkPipelineViewportStateCreateInfo viewport_state{ VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO }; viewport_state.viewportCount = 1; viewport_state.pViewports = nullptr; // dynamic viewport state viewport_state.scissorCount = 1; viewport_state.pScissors = nullptr; // dynamic scissor state // // Rasterization. // VkPipelineRasterizationStateCreateInfo rasterization_state{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; rasterization_state.depthClampEnable = VK_FALSE; rasterization_state.rasterizerDiscardEnable = VK_FALSE; rasterization_state.polygonMode = (def.state_bits & GLS_POLYMODE_LINE) ? VK_POLYGON_MODE_LINE : VK_POLYGON_MODE_FILL; if (def.face_culling == CT_TWO_SIDED) rasterization_state.cullMode = VK_CULL_MODE_NONE; else if (def.face_culling == CT_FRONT_SIDED) rasterization_state.cullMode = (def.mirror ? VK_CULL_MODE_FRONT_BIT : VK_CULL_MODE_BACK_BIT); else if (def.face_culling == CT_BACK_SIDED) rasterization_state.cullMode = (def.mirror ? VK_CULL_MODE_BACK_BIT : VK_CULL_MODE_FRONT_BIT); else ri.Error(ERR_DROP, "create_pipeline: invalid face culling mode\n"); rasterization_state.frontFace = VK_FRONT_FACE_CLOCKWISE; // Q3 defaults to clockwise vertex order rasterization_state.depthBiasEnable = def.polygon_offset ? VK_TRUE : VK_FALSE; rasterization_state.depthBiasConstantFactor = 0.0f; // dynamic depth bias state rasterization_state.depthBiasClamp = 0.0f; // dynamic depth bias state rasterization_state.depthBiasSlopeFactor = 0.0f; // dynamic depth bias state rasterization_state.lineWidth = 1.0f; VkPipelineMultisampleStateCreateInfo multisample_state{ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO }; multisample_state.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; multisample_state.sampleShadingEnable = VK_FALSE; multisample_state.minSampleShading = 1.0f; multisample_state.pSampleMask = nullptr; multisample_state.alphaToCoverageEnable = VK_FALSE; multisample_state.alphaToOneEnable = VK_FALSE; VkPipelineDepthStencilStateCreateInfo depth_stencil_state{ VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO }; depth_stencil_state.depthTestEnable = (def.state_bits & GLS_DEPTHTEST_DISABLE) ? VK_FALSE : VK_TRUE; depth_stencil_state.depthWriteEnable = (def.state_bits & GLS_DEPTHMASK_TRUE) ? VK_TRUE : VK_FALSE; depth_stencil_state.depthCompareOp = (def.state_bits & GLS_DEPTHFUNC_EQUAL) ? VK_COMPARE_OP_EQUAL : VK_COMPARE_OP_LESS_OR_EQUAL; depth_stencil_state.depthBoundsTestEnable = VK_FALSE; depth_stencil_state.stencilTestEnable = (def.shadow_phase != Vk_Shadow_Phase::disabled) ? VK_TRUE : VK_FALSE; if (def.shadow_phase == Vk_Shadow_Phase::shadow_edges_rendering) { depth_stencil_state.front.failOp = VK_STENCIL_OP_KEEP; depth_stencil_state.front.passOp = (def.face_culling == CT_FRONT_SIDED) ? VK_STENCIL_OP_INCREMENT_AND_CLAMP : VK_STENCIL_OP_DECREMENT_AND_CLAMP; depth_stencil_state.front.depthFailOp = VK_STENCIL_OP_KEEP; depth_stencil_state.front.compareOp = VK_COMPARE_OP_ALWAYS; depth_stencil_state.front.compareMask = 255; depth_stencil_state.front.writeMask = 255; depth_stencil_state.front.reference = 0; depth_stencil_state.back = depth_stencil_state.front; } else if (def.shadow_phase == Vk_Shadow_Phase::fullscreen_quad_rendering) { depth_stencil_state.front.failOp = VK_STENCIL_OP_KEEP; depth_stencil_state.front.passOp = VK_STENCIL_OP_KEEP; depth_stencil_state.front.depthFailOp = VK_STENCIL_OP_KEEP; depth_stencil_state.front.compareOp = VK_COMPARE_OP_NOT_EQUAL; depth_stencil_state.front.compareMask = 255; depth_stencil_state.front.writeMask = 255; depth_stencil_state.front.reference = 0; depth_stencil_state.back = depth_stencil_state.front; } else { depth_stencil_state.front = {}; depth_stencil_state.back = {}; } depth_stencil_state.minDepthBounds = 0.0; depth_stencil_state.maxDepthBounds = 0.0; VkPipelineColorBlendAttachmentState attachment_blend_state = {}; attachment_blend_state.blendEnable = (def.state_bits & (GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS)) ? VK_TRUE : VK_FALSE; if (def.shadow_phase == Vk_Shadow_Phase::shadow_edges_rendering) attachment_blend_state.colorWriteMask = 0; else attachment_blend_state.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; if (attachment_blend_state.blendEnable) { switch (def.state_bits & GLS_SRCBLEND_BITS) { case GLS_SRCBLEND_ZERO: attachment_blend_state.srcColorBlendFactor = VK_BLEND_FACTOR_ZERO; break; case GLS_SRCBLEND_ONE: attachment_blend_state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; break; case GLS_SRCBLEND_DST_COLOR: attachment_blend_state.srcColorBlendFactor = VK_BLEND_FACTOR_DST_COLOR; break; case GLS_SRCBLEND_ONE_MINUS_DST_COLOR: attachment_blend_state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR; break; case GLS_SRCBLEND_SRC_ALPHA: attachment_blend_state.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; break; case GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA: attachment_blend_state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; break; case GLS_SRCBLEND_DST_ALPHA: attachment_blend_state.srcColorBlendFactor = VK_BLEND_FACTOR_DST_ALPHA; break; case GLS_SRCBLEND_ONE_MINUS_DST_ALPHA: attachment_blend_state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA; break; case GLS_SRCBLEND_ALPHA_SATURATE: attachment_blend_state.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA_SATURATE; break; default: ri.Error( ERR_DROP, "create_pipeline: invalid src blend state bits\n" ); break; } switch (def.state_bits & GLS_DSTBLEND_BITS) { case GLS_DSTBLEND_ZERO: attachment_blend_state.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; break; case GLS_DSTBLEND_ONE: attachment_blend_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE; break; case GLS_DSTBLEND_SRC_COLOR: attachment_blend_state.dstColorBlendFactor = VK_BLEND_FACTOR_SRC_COLOR; break; case GLS_DSTBLEND_ONE_MINUS_SRC_COLOR: attachment_blend_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR; break; case GLS_DSTBLEND_SRC_ALPHA: attachment_blend_state.dstColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; break; case GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA: attachment_blend_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; break; case GLS_DSTBLEND_DST_ALPHA: attachment_blend_state.dstColorBlendFactor = VK_BLEND_FACTOR_DST_ALPHA; break; case GLS_DSTBLEND_ONE_MINUS_DST_ALPHA: attachment_blend_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA; break; default: ri.Error( ERR_DROP, "create_pipeline: invalid dst blend state bits\n" ); break; } attachment_blend_state.srcAlphaBlendFactor = attachment_blend_state.srcColorBlendFactor; attachment_blend_state.dstAlphaBlendFactor = attachment_blend_state.dstColorBlendFactor; attachment_blend_state.colorBlendOp = VK_BLEND_OP_ADD; attachment_blend_state.alphaBlendOp = VK_BLEND_OP_ADD; } VkPipelineColorBlendStateCreateInfo blend_state{ VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO }; blend_state.logicOpEnable = VK_FALSE; blend_state.logicOp = VK_LOGIC_OP_COPY; blend_state.attachmentCount = 1; blend_state.pAttachments = &attachment_blend_state; blend_state.blendConstants[0] = 0.0f; blend_state.blendConstants[1] = 0.0f; blend_state.blendConstants[2] = 0.0f; blend_state.blendConstants[3] = 0.0f; VkPipelineDynamicStateCreateInfo dynamic_state{ VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO }; dynamic_state.dynamicStateCount = 3; VkDynamicState dynamic_state_array[3] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, VK_DYNAMIC_STATE_DEPTH_BIAS }; dynamic_state.pDynamicStates = dynamic_state_array; VkGraphicsPipelineCreateInfo create_info{ VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO }; create_info.stageCount = static_cast(shader_stages_state.size()); create_info.pStages = shader_stages_state.data(); create_info.pVertexInputState = &vertex_input_state; create_info.pInputAssemblyState = &input_assembly_state; create_info.pViewportState = &viewport_state; create_info.pRasterizationState = &rasterization_state; create_info.pMultisampleState = &multisample_state; create_info.pDepthStencilState = &depth_stencil_state; create_info.pColorBlendState = &blend_state; create_info.pDynamicState = &dynamic_state; create_info.layout = vk.pipeline_layout; create_info.renderPass = vk.render_pass; create_info.subpass = 0; create_info.basePipelineHandle = VK_NULL_HANDLE; create_info.basePipelineIndex = -1; VkPipeline pipeline; VK_CHECK(vkCreateGraphicsPipelines(vk.device, VK_NULL_HANDLE, 1, &create_info, nullptr, &pipeline)); return pipeline; } VkSampler vk_find_sampler(const Vk_Sampler_Def& def) { // Look for sampler among existing samplers. for (int i = 0; i < vk_world.num_samplers; i++) { const auto& cur_def = vk_world.sampler_defs[i]; if (cur_def.repeat_texture == def.repeat_texture && cur_def.gl_mag_filter == def.gl_mag_filter && cur_def.gl_min_filter == def.gl_min_filter) { return vk_world.samplers[i]; } } // Create new sampler. if (vk_world.num_samplers >= MAX_VK_SAMPLERS) { ri.Error(ERR_DROP, "vk_find_sampler: MAX_VK_SAMPLERS hit\n"); } VkSamplerAddressMode address_mode = def.repeat_texture ? VK_SAMPLER_ADDRESS_MODE_REPEAT : VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; VkFilter mag_filter; if (def.gl_mag_filter == GL_NEAREST) { mag_filter = VK_FILTER_NEAREST; } else if (def.gl_mag_filter == GL_LINEAR) { mag_filter = VK_FILTER_LINEAR; } else { ri.Error(ERR_FATAL, "vk_find_sampler: invalid gl_mag_filter"); } VkFilter min_filter; VkSamplerMipmapMode mipmap_mode; bool max_lod_0_25 = false; // used to emulate OpenGL's GL_LINEAR/GL_NEAREST minification filter if (def.gl_min_filter == GL_NEAREST) { min_filter = VK_FILTER_NEAREST; mipmap_mode = VK_SAMPLER_MIPMAP_MODE_NEAREST; max_lod_0_25 = true; } else if (def.gl_min_filter == GL_LINEAR) { min_filter = VK_FILTER_LINEAR; mipmap_mode = VK_SAMPLER_MIPMAP_MODE_NEAREST; max_lod_0_25 = true; } else if (def.gl_min_filter == GL_NEAREST_MIPMAP_NEAREST) { min_filter = VK_FILTER_NEAREST; mipmap_mode = VK_SAMPLER_MIPMAP_MODE_NEAREST; } else if (def.gl_min_filter == GL_LINEAR_MIPMAP_NEAREST) { min_filter = VK_FILTER_LINEAR; mipmap_mode = VK_SAMPLER_MIPMAP_MODE_NEAREST; } else if (def.gl_min_filter == GL_NEAREST_MIPMAP_LINEAR) { min_filter = VK_FILTER_NEAREST; mipmap_mode = VK_SAMPLER_MIPMAP_MODE_LINEAR; } else if (def.gl_min_filter == GL_LINEAR_MIPMAP_LINEAR) { min_filter = VK_FILTER_LINEAR; mipmap_mode = VK_SAMPLER_MIPMAP_MODE_LINEAR; } else { ri.Error(ERR_FATAL, "vk_find_sampler: invalid gl_min_filter"); } VkSamplerCreateInfo desc{ VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; desc.magFilter = mag_filter; desc.minFilter = min_filter; desc.mipmapMode = mipmap_mode; desc.addressModeU = address_mode; desc.addressModeV = address_mode; desc.addressModeW = address_mode; desc.mipLodBias = 0.0f; desc.anisotropyEnable = VK_FALSE; desc.maxAnisotropy = 1; desc.compareEnable = VK_FALSE; desc.compareOp = VK_COMPARE_OP_ALWAYS; desc.minLod = 0.0f; desc.maxLod = max_lod_0_25 ? 0.25f : 12.0f; desc.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; desc.unnormalizedCoordinates = VK_FALSE; VkSampler sampler; VK_CHECK(vkCreateSampler(vk.device, &desc, nullptr, &sampler)); vk_world.sampler_defs[vk_world.num_samplers] = def; vk_world.samplers[vk_world.num_samplers] = sampler; vk_world.num_samplers++; return sampler; } struct Timer { using Clock = std::chrono::high_resolution_clock; using Second = std::chrono::duration>; Clock::time_point start = Clock::now(); double elapsed_seconds() const { const auto duration = Clock::now() - start; double seconds = std::chrono::duration_cast(duration).count(); return seconds; } }; VkPipeline vk_find_pipeline(const Vk_Pipeline_Def& def) { for (int i = 0; i < vk_world.num_pipelines; i++) { const auto& cur_def = vk_world.pipeline_defs[i]; if (cur_def.shader_type == def.shader_type && cur_def.state_bits == def.state_bits && cur_def.face_culling == def.face_culling && cur_def.polygon_offset == def.polygon_offset && cur_def.clipping_plane == def.clipping_plane && cur_def.mirror == def.mirror && cur_def.line_primitives == def.line_primitives && cur_def.shadow_phase == def.shadow_phase) { return vk_world.pipelines[i]; } } if (vk_world.num_pipelines >= MAX_VK_PIPELINES) { ri.Error(ERR_DROP, "vk_find_pipeline: MAX_VK_PIPELINES hit\n"); } Timer t; VkPipeline pipeline = create_pipeline(def); vk_world.pipeline_create_time += t.elapsed_seconds(); vk_world.pipeline_defs[vk_world.num_pipelines] = def; vk_world.pipelines[vk_world.num_pipelines] = pipeline; vk_world.num_pipelines++; return pipeline; } static VkRect2D get_viewport_rect() { VkRect2D r; if (backEnd.projection2D) { r.offset.x = 0.0f; r.offset.y = 0.0f; r.extent.width = glConfig.vidWidth; r.extent.height = glConfig.vidHeight; } else { r.offset.x = backEnd.viewParms.viewportX; r.offset.y = glConfig.vidHeight - (backEnd.viewParms.viewportY + backEnd.viewParms.viewportHeight); r.extent.width = backEnd.viewParms.viewportWidth; r.extent.height = backEnd.viewParms.viewportHeight; } return r; } static VkViewport get_viewport(Vk_Depth_Range depth_range) { VkRect2D r = get_viewport_rect(); VkViewport viewport; viewport.x = (float)r.offset.x; viewport.y = (float)r.offset.y; viewport.width = (float)r.extent.width; viewport.height = (float)r.extent.height; if (depth_range == Vk_Depth_Range::force_zero) { viewport.minDepth = 0.0f; viewport.maxDepth = 0.0f; } else if (depth_range == Vk_Depth_Range::force_one) { viewport.minDepth = 1.0f; viewport.maxDepth = 1.0f; } else if (depth_range == Vk_Depth_Range::weapon) { viewport.minDepth = 0.0f; viewport.maxDepth = 0.3f; } else { viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; } return viewport; } static VkRect2D get_scissor_rect() { VkRect2D r = get_viewport_rect(); if (r.offset.x < 0) r.offset.x = 0; if (r.offset.y < 0) r.offset.y = 0; if (r.offset.x + r.extent.width > glConfig.vidWidth) r.extent.width = glConfig.vidWidth - r.offset.x; if (r.offset.y + r.extent.height > glConfig.vidHeight) r.extent.height = glConfig.vidHeight - r.offset.y; return r; } static void get_mvp_transform(float* mvp) { if (backEnd.projection2D) { float mvp0 = 2.0f / glConfig.vidWidth; float mvp5 = 2.0f / glConfig.vidHeight; mvp[0] = mvp0; mvp[1] = 0.0f; mvp[2] = 0.0f; mvp[3] = 0.0f; mvp[4] = 0.0f; mvp[5] = mvp5; mvp[6] = 0.0f; mvp[7] = 0.0f; mvp[8] = 0.0f; mvp[9] = 0.0f; mvp[10] = 1.0f; mvp[11] = 0.0f; mvp[12] = -1.0f; mvp[13] = -1.0f; mvp[14] = 0.0f; mvp[15] = 1.0f; } else { const float* p = backEnd.viewParms.projectionMatrix; // update q3's proj matrix (opengl) to vulkan conventions: z - [0, 1] instead of [-1, 1] and invert y direction float zNear = r_znear->value; float zFar = backEnd.viewParms.zFar; float P10 = -zFar / (zFar - zNear); float P14 = -zFar*zNear / (zFar - zNear); float P5 = -p[5]; float proj[16] = { p[0], p[1], p[2], p[3], p[4], P5, p[6], p[7], p[8], p[9], P10, p[11], p[12], p[13], P14, p[15] }; myGlMultMatrix(vk_world.modelview_transform, proj, mvp); } } void vk_clear_attachments(bool clear_depth_stencil, bool clear_color, vec4_t color) { if (!vk.active) return; if (!clear_depth_stencil && !clear_color) return; VkClearAttachment attachments[2]; uint32_t attachment_count = 0; if (clear_depth_stencil) { attachments[0].aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; attachments[0].clearValue.depthStencil.depth = 1.0f; if (r_shadows->integer == 2) { attachments[0].aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; attachments[0].clearValue.depthStencil.stencil = 0; } attachment_count = 1; } if (clear_color) { attachments[attachment_count].aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; attachments[attachment_count].colorAttachment = 0; attachments[attachment_count].clearValue.color = { color[0], color[1], color[2], color[3] }; attachment_count++; } VkClearRect clear_rect{}; clear_rect.rect = get_scissor_rect(); clear_rect.baseArrayLayer = 0; clear_rect.layerCount = 1; vkCmdClearAttachments(vk.command_buffer, attachment_count, attachments, 1, &clear_rect); } void vk_bind_geometry() { // xyz stream { if ((vk.xyz_elements + tess.numVertexes) * sizeof(vec4_t) > XYZ_SIZE) ri.Error(ERR_DROP, "vk_bind_geometry: vertex buffer overflow (xyz)\n"); byte* dst = vk.vertex_buffer_ptr + XYZ_OFFSET + vk.xyz_elements * sizeof(vec4_t); Com_Memcpy(dst, tess.xyz, tess.numVertexes * sizeof(vec4_t)); VkDeviceSize xyz_offset = XYZ_OFFSET + vk.xyz_elements * sizeof(vec4_t); vkCmdBindVertexBuffers(vk.command_buffer, 0, 1, &vk.vertex_buffer, &xyz_offset); vk.xyz_elements += tess.numVertexes; } // indexes stream { std::size_t indexes_size = tess.numIndexes * sizeof(uint32_t); if (vk.index_buffer_offset + indexes_size > INDEX_BUFFER_SIZE) ri.Error(ERR_DROP, "vk_bind_geometry: index buffer overflow\n"); byte* dst = vk.index_buffer_ptr + vk.index_buffer_offset; Com_Memcpy(dst, tess.indexes, indexes_size); vkCmdBindIndexBuffer(vk.command_buffer, vk.index_buffer, vk.index_buffer_offset, VK_INDEX_TYPE_UINT32); vk.index_buffer_offset += indexes_size; } // // Specify push constants. // float push_constants[16 + 12 + 4]; // mvp transform + eye transform + clipping plane in eye space get_mvp_transform(push_constants); int push_constants_size = 64; if (backEnd.viewParms.isPortal) { // Eye space transform. // NOTE: backEnd.or.modelMatrix incorporates s_flipMatrix, so it should be taken into account // when computing clipping plane too. float* eye_xform = push_constants + 16; for (int i = 0; i < 12; i++) { eye_xform[i] = backEnd.or.modelMatrix[(i%4)*4 + i/4 ]; } // Clipping plane in eye coordinates. float world_plane[4]; world_plane[0] = backEnd.viewParms.portalPlane.normal[0]; world_plane[1] = backEnd.viewParms.portalPlane.normal[1]; world_plane[2] = backEnd.viewParms.portalPlane.normal[2]; world_plane[3] = backEnd.viewParms.portalPlane.dist; float eye_plane[4]; eye_plane[0] = DotProduct (backEnd.viewParms.or.axis[0], world_plane); eye_plane[1] = DotProduct (backEnd.viewParms.or.axis[1], world_plane); eye_plane[2] = DotProduct (backEnd.viewParms.or.axis[2], world_plane); eye_plane[3] = DotProduct (world_plane, backEnd.viewParms.or.origin) - world_plane[3]; // Apply s_flipMatrix to be in the same coordinate system as eye_xfrom. push_constants[28] = -eye_plane[1]; push_constants[29] = eye_plane[2]; push_constants[30] = -eye_plane[0]; push_constants[31] = eye_plane[3]; push_constants_size += 64; } vkCmdPushConstants(vk.command_buffer, vk.pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, 0, push_constants_size, push_constants); } void vk_shade_geometry(VkPipeline pipeline, bool multitexture, Vk_Depth_Range depth_range, bool indexed) { // color { if ((vk.color_st_elements + tess.numVertexes) * sizeof(color4ub_t) > COLOR_SIZE) ri.Error(ERR_DROP, "vulkan: vertex buffer overflow (color)\n"); byte* dst = vk.vertex_buffer_ptr + COLOR_OFFSET + vk.color_st_elements * sizeof(color4ub_t); Com_Memcpy(dst, tess.svars.colors, tess.numVertexes * sizeof(color4ub_t)); } // st0 { if ((vk.color_st_elements + tess.numVertexes) * sizeof(vec2_t) > ST0_SIZE) ri.Error(ERR_DROP, "vulkan: vertex buffer overflow (st0)\n"); byte* dst = vk.vertex_buffer_ptr + ST0_OFFSET + vk.color_st_elements * sizeof(vec2_t); Com_Memcpy(dst, tess.svars.texcoords[0], tess.numVertexes * sizeof(vec2_t)); } // st1 if (multitexture) { if ((vk.color_st_elements + tess.numVertexes) * sizeof(vec2_t) > ST1_SIZE) ri.Error(ERR_DROP, "vulkan: vertex buffer overflow (st1)\n"); byte* dst = vk.vertex_buffer_ptr + ST1_OFFSET + vk.color_st_elements * sizeof(vec2_t); Com_Memcpy(dst, tess.svars.texcoords[1], tess.numVertexes * sizeof(vec2_t)); } // configure vertex data stream VkBuffer bufs[3] = { vk.vertex_buffer, vk.vertex_buffer, vk.vertex_buffer }; VkDeviceSize offs[3] = { COLOR_OFFSET + vk.color_st_elements * sizeof(color4ub_t), ST0_OFFSET + vk.color_st_elements * sizeof(vec2_t), ST1_OFFSET + vk.color_st_elements * sizeof(vec2_t) }; vkCmdBindVertexBuffers(vk.command_buffer, 1, multitexture ? 3 : 2, bufs, offs); vk.color_st_elements += tess.numVertexes; // bind descriptor sets uint32_t set_count = multitexture ? 2 : 1; vkCmdBindDescriptorSets(vk.command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, vk.pipeline_layout, 0, set_count, vk_world.current_descriptor_sets, 0, nullptr); // bind pipeline vkCmdBindPipeline(vk.command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); // configure pipeline's dynamic state VkRect2D scissor_rect = get_scissor_rect(); vkCmdSetScissor(vk.command_buffer, 0, 1, &scissor_rect); VkViewport viewport = get_viewport(depth_range); vkCmdSetViewport(vk.command_buffer, 0, 1, &viewport); if (tess.shader->polygonOffset) { vkCmdSetDepthBias(vk.command_buffer, r_offsetUnits->value, 0.0f, r_offsetFactor->value); } // issue draw call if (indexed) vkCmdDrawIndexed(vk.command_buffer, tess.numIndexes, 1, 0, 0, 0); else vkCmdDraw(vk.command_buffer, tess.numVertexes, 1, 0, 0); vk_world.dirty_depth_attachment = true; } void vk_begin_frame() { if (!vk.active) return; VK_CHECK(vkWaitForFences(vk.device, 1, &vk.rendering_finished_fence, VK_FALSE, 1e9)); VK_CHECK(vkResetFences(vk.device, 1, &vk.rendering_finished_fence)); VK_CHECK(vkAcquireNextImageKHR(vk.device, vk.swapchain, UINT64_MAX, vk.image_acquired, VK_NULL_HANDLE, &vk.swapchain_image_index)); // Write swapchain image descriptor for gamma shader VkDescriptorImageInfo image_info{}; image_info.imageView = vk.swapchain_image_views[vk.swapchain_image_index]; image_info.imageLayout = VK_IMAGE_LAYOUT_GENERAL; VkWriteDescriptorSet descriptor_write{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; descriptor_write.dstSet = vk.gamma_descriptor_set; descriptor_write.dstBinding = 1; descriptor_write.dstArrayElement = 0; descriptor_write.descriptorCount = 1; descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; descriptor_write.pImageInfo = &image_info; vkUpdateDescriptorSets(vk.device, 1, &descriptor_write, 0, nullptr); VkCommandBufferBeginInfo begin_info{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; VK_CHECK(vkBeginCommandBuffer(vk.command_buffer, &begin_info)); // Begin render pass. VkClearValue clear_values[2]; /// ignore clear_values[0] which corresponds to color attachment clear_values[1].depthStencil.depth = 1.0; clear_values[1].depthStencil.stencil = 0; VkRenderPassBeginInfo render_pass_begin_info{ VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO }; render_pass_begin_info.renderPass = vk.render_pass; render_pass_begin_info.framebuffer = vk.framebuffer; render_pass_begin_info.renderArea.offset = { 0, 0 }; render_pass_begin_info.renderArea.extent = { (uint32_t)glConfig.vidWidth, (uint32_t)glConfig.vidHeight }; render_pass_begin_info.clearValueCount = 2; render_pass_begin_info.pClearValues = clear_values; vkCmdBeginRenderPass(vk.command_buffer, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); vk_world.dirty_depth_attachment = false; vk.xyz_elements = 0; vk.color_st_elements = 0; vk.index_buffer_offset = 0; } void vk_end_frame() { if (!vk.active) return; vkCmdEndRenderPass(vk.command_buffer); record_image_layout_transition(vk.command_buffer, vk.swapchain_images[vk.swapchain_image_index], VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_IMAGE_LAYOUT_UNDEFINED, VK_ACCESS_SHADER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL); const uint32_t group_size_x = 8; // according to shader const uint32_t group_size_y = 8; uint32_t group_count_x = ((uint32_t)glConfig.vidWidth + group_size_x - 1) / group_size_x; uint32_t group_count_y = ((uint32_t)glConfig.vidHeight + group_size_y - 1) / group_size_y; const uint32_t push_constants[3] = { (uint32_t)glConfig.vidWidth, (uint32_t)glConfig.vidHeight, r_shaderGamma->integer && !r_ignorehwgamma->integer ? 0u : 1u // identity_gamma }; vkCmdBindDescriptorSets(vk.command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, vk.gamma_pipeline_layout, 0, 1, &vk.gamma_descriptor_set, 0, nullptr); vkCmdPushConstants(vk.command_buffer, vk.gamma_pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, 12, push_constants); vkCmdBindPipeline(vk.command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, vk.gamma_pipeline); vkCmdDispatch(vk.command_buffer, group_count_x, group_count_y, 1); record_image_layout_transition(vk.command_buffer, vk.swapchain_images[vk.swapchain_image_index], VK_IMAGE_ASPECT_COLOR_BIT, VK_ACCESS_SHADER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL, 0, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); VK_CHECK(vkEndCommandBuffer(vk.command_buffer)); // Compute shader generates final swapchain image by applying gamma shader to the output image. // Ensure that compute stage waits for image acquire semaphore. const VkPipelineStageFlags wait_dst_stage_mask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; VkSubmitInfo submit_info{ VK_STRUCTURE_TYPE_SUBMIT_INFO }; submit_info.waitSemaphoreCount = 1; submit_info.pWaitSemaphores = &vk.image_acquired; submit_info.pWaitDstStageMask = &wait_dst_stage_mask; submit_info.commandBufferCount = 1; submit_info.pCommandBuffers = &vk.command_buffer; submit_info.signalSemaphoreCount = 1; submit_info.pSignalSemaphores = &vk.rendering_finished[vk.swapchain_image_index]; VK_CHECK(vkQueueSubmit(vk.queue, 1, &submit_info, vk.rendering_finished_fence)); VkPresentInfoKHR present_info{ VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; present_info.waitSemaphoreCount = 1; present_info.pWaitSemaphores = &vk.rendering_finished[vk.swapchain_image_index]; present_info.swapchainCount = 1; present_info.pSwapchains = &vk.swapchain; present_info.pImageIndices = &vk.swapchain_image_index; VK_CHECK(vkQueuePresentKHR(vk.queue, &present_info)); } void vk_read_pixels(byte* buffer) { vkDeviceWaitIdle(vk.device); // Create image in host visible memory to serve as a destination for framebuffer pixels. VkImageCreateInfo desc{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; desc.imageType = VK_IMAGE_TYPE_2D; desc.format = VK_FORMAT_R8G8B8A8_UNORM; desc.extent.width = glConfig.vidWidth; desc.extent.height = glConfig.vidHeight; desc.extent.depth = 1; desc.mipLevels = 1; desc.arrayLayers = 1; desc.samples = VK_SAMPLE_COUNT_1_BIT; desc.tiling = VK_IMAGE_TILING_LINEAR; desc.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT; desc.sharingMode = VK_SHARING_MODE_EXCLUSIVE; desc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; VkImage image; VK_CHECK(vkCreateImage(vk.device, &desc, nullptr, &image)); VkMemoryRequirements memory_requirements; vkGetImageMemoryRequirements(vk.device, image, &memory_requirements); VkMemoryAllocateInfo alloc_info{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; alloc_info.allocationSize = memory_requirements.size; alloc_info.memoryTypeIndex = find_memory_type(vk.physical_device, memory_requirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); VkDeviceMemory memory; VK_CHECK(vkAllocateMemory(vk.device, &alloc_info, nullptr, &memory)); VK_CHECK(vkBindImageMemory(vk.device, image, memory, 0)); record_and_run_commands(vk.command_pool, vk.queue, [&image](VkCommandBuffer command_buffer) { record_image_layout_transition(command_buffer, vk.swapchain_images[vk.swapchain_image_index], VK_IMAGE_ASPECT_COLOR_BIT, VK_ACCESS_MEMORY_READ_BIT, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); record_image_layout_transition(command_buffer, image, VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_IMAGE_LAYOUT_UNDEFINED, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL); }); // Check if we can use vkCmdBlitImage for the given source and destination image formats. bool blit_enabled = true; { VkFormatProperties formatProps; vkGetPhysicalDeviceFormatProperties(vk.physical_device, vk.surface_format.format, &formatProps); if ((formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT) == 0) blit_enabled = false; vkGetPhysicalDeviceFormatProperties(vk.physical_device, VK_FORMAT_R8G8B8A8_UNORM, &formatProps); if ((formatProps.linearTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT) == 0) blit_enabled = false; } if (blit_enabled) { record_and_run_commands(vk.command_pool, vk.queue, [&image](VkCommandBuffer command_buffer) { VkImageBlit region; region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.srcSubresource.mipLevel = 0; region.srcSubresource.baseArrayLayer = 0; region.srcSubresource.layerCount = 1; region.srcOffsets[0] = {0, 0, 0}; region.srcOffsets[1] = {glConfig.vidWidth, glConfig.vidHeight, 1}; region.dstSubresource = region.srcSubresource; region.dstOffsets[0] = region.srcOffsets[0]; region.dstOffsets[1] = region.srcOffsets[1]; vkCmdBlitImage(command_buffer, vk.swapchain_images[vk.swapchain_image_index], VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image, VK_IMAGE_LAYOUT_GENERAL, 1, ®ion, VK_FILTER_NEAREST); }); } else { record_and_run_commands(vk.command_pool, vk.queue, [&image](VkCommandBuffer command_buffer) { VkImageCopy region; region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.srcSubresource.mipLevel = 0; region.srcSubresource.baseArrayLayer = 0; region.srcSubresource.layerCount = 1; region.srcOffset = {0, 0, 0}; region.dstSubresource = region.srcSubresource; region.dstOffset = region.srcOffset; region.extent = {(uint32_t)glConfig.vidWidth, (uint32_t)glConfig.vidHeight, 1}; vkCmdCopyImage(command_buffer, vk.swapchain_images[vk.swapchain_image_index], VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image, VK_IMAGE_LAYOUT_GENERAL, 1, ®ion); }); } // Copy data from destination image to memory buffer. VkImageSubresource subresource; subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subresource.mipLevel = 0; subresource.arrayLayer = 0; VkSubresourceLayout layout; vkGetImageSubresourceLayout(vk.device, image, &subresource, &layout); byte* data; VK_CHECK(vkMapMemory(vk.device, memory, 0, VK_WHOLE_SIZE, 0, (void**)&data)); data += layout.offset; byte* buffer_ptr = buffer + glConfig.vidWidth * (glConfig.vidHeight - 1) * 4; for (int i = 0; i < glConfig.vidHeight; i++) { Com_Memcpy(buffer_ptr, data, glConfig.vidWidth * 4); buffer_ptr -= glConfig.vidWidth * 4; data += layout.rowPitch; } if (!blit_enabled) { auto fmt = vk.surface_format.format; bool swizzle_components = (fmt == VK_FORMAT_B8G8R8A8_SRGB || fmt == VK_FORMAT_B8G8R8A8_UNORM || fmt == VK_FORMAT_B8G8R8A8_SNORM); if (swizzle_components) { buffer_ptr = buffer; for (int i = 0; i < glConfig.vidWidth * glConfig.vidHeight; i++) { byte tmp = buffer_ptr[0]; buffer_ptr[0] = buffer_ptr[2]; buffer_ptr[2] = tmp; buffer_ptr += 4; } } } vkDestroyImage(vk.device, image, nullptr); vkFreeMemory(vk.device, memory, nullptr); } void vk_update_gamma_buffer(float gamma_table[256]) { vkDeviceWaitIdle(vk.device); ensure_staging_buffer_allocation(256 * sizeof(float)); Com_Memcpy(vk_world.staging_buffer_ptr, gamma_table, 256 * sizeof(float)); record_and_run_commands(vk.command_pool, vk.queue, [](VkCommandBuffer command_buffer) { VkBufferCopy region{}; region.size = 256 * sizeof(float); vkCmdCopyBuffer(command_buffer, vk_world.staging_buffer, vk.gamma_buffer, 1, ®ion); }); } ================================================ FILE: src/engine/renderer/vk.h ================================================ #pragma once #ifdef _WIN32 #define VK_USE_PLATFORM_WIN32_KHR #define NOMINMAX #endif #define VK_NO_PROTOTYPES #include "vulkan/vulkan.h" const int MAX_SWAPCHAIN_IMAGES = 8; const int MAX_VK_SAMPLERS = 32; const int MAX_VK_PIPELINES = 1024; const int MAX_VK_IMAGES = 2048; // should be the same as MAX_DRAWIMAGES const int IMAGE_CHUNK_SIZE = 32 * 1024 * 1024; const int MAX_IMAGE_CHUNKS = 16; #define VK_CHECK(function_call) { \ VkResult result = function_call; \ if (result < 0) \ ri.Error(ERR_FATAL, "Vulkan: error code %d returned by %s", result, #function_call); \ } enum class Vk_Shader_Type { single_texture, multi_texture_mul, multi_texture_add }; // used with cg_shadows == 2 enum class Vk_Shadow_Phase { disabled, shadow_edges_rendering, fullscreen_quad_rendering }; enum class Vk_Depth_Range { normal, // [0..1] force_zero, // [0..0] force_one, // [1..1] weapon // [0..0.3] }; struct Vk_Sampler_Def { bool repeat_texture = false; // clamp/repeat texture addressing mode int gl_mag_filter = 0; // GL_XXX mag filter int gl_min_filter = 0; // GL_XXX min filter }; struct Vk_Pipeline_Def { Vk_Shader_Type shader_type = Vk_Shader_Type::single_texture; unsigned int state_bits = 0; // GLS_XXX flags int face_culling = 0;// cullType_t bool polygon_offset = false; bool clipping_plane = false; bool mirror = false; bool line_primitives = false; Vk_Shadow_Phase shadow_phase = Vk_Shadow_Phase::disabled; }; struct Vk_Image { VkImage handle = VK_NULL_HANDLE; VkImageView view = VK_NULL_HANDLE; // Descriptor set that contains single descriptor used to access the given image. // It is updated only once during image initialization. VkDescriptorSet descriptor_set = VK_NULL_HANDLE; }; // // Initialization. // // Initializes VK_Instance structure. // After calling this function we get fully functional vulkan subsystem. void vk_initialize(); // Shutdown vulkan subsystem by releasing resources acquired by Vk_Instance. void vk_shutdown(); // Releases vulkan resources allocated during program execution. // This effectively puts vulkan subsystem into initial state (the state we have after vk_initialize call). void vk_release_resources(); // // Resources allocation. // Vk_Image vk_create_image(int width, int height, VkFormat format, int mip_levels, bool repeat_texture); void vk_upload_image_data(VkImage image, int width, int height, bool mipmap, const uint8_t* pixels, int bytes_per_pixel); void vk_update_descriptor_set(VkDescriptorSet set, VkImageView image_view, bool mipmap, bool repeat_texture); VkSampler vk_find_sampler(const Vk_Sampler_Def& def); VkPipeline vk_find_pipeline(const Vk_Pipeline_Def& def); // // Rendering setup. // void vk_clear_attachments(bool clear_depth_stencil, bool clear_color, vec4_t color); void vk_bind_geometry(); void vk_shade_geometry(VkPipeline pipeline, bool multitexture, Vk_Depth_Range depth_range, bool indexed = true); void vk_begin_frame(); void vk_end_frame(); void vk_read_pixels(byte* buffer); // screenshots void vk_update_gamma_buffer(float gamma_table[256]); // Vk_Instance contains engine-specific vulkan resources that persist entire renderer lifetime. // This structure is initialized/deinitialized by vk_initialize/vk_shutdown functions correspondingly. struct Vk_Instance { bool active = false; VkInstance instance = VK_NULL_HANDLE; VkPhysicalDevice physical_device = VK_NULL_HANDLE; VkSurfaceKHR surface = VK_NULL_HANDLE; VkSurfaceFormatKHR surface_format = {}; uint32_t queue_family_index = 0; VkDevice device = VK_NULL_HANDLE; VkQueue queue = VK_NULL_HANDLE; VkSwapchainKHR swapchain = VK_NULL_HANDLE; uint32_t swapchain_image_count = 0; VkImage swapchain_images[MAX_SWAPCHAIN_IMAGES]; VkImageView swapchain_image_views[MAX_SWAPCHAIN_IMAGES]; VkSemaphore rendering_finished[MAX_SWAPCHAIN_IMAGES]; uint32_t swapchain_image_index = -1; VkSemaphore image_acquired = VK_NULL_HANDLE; VkFence rendering_finished_fence = VK_NULL_HANDLE; VkCommandPool command_pool = VK_NULL_HANDLE; VkCommandBuffer command_buffer = VK_NULL_HANDLE; VkImage output_image = VK_NULL_HANDLE; VkDeviceMemory output_image_memory = VK_NULL_HANDLE; VkImageView output_image_view = VK_NULL_HANDLE; VkImage depth_image = VK_NULL_HANDLE; VkDeviceMemory depth_image_memory = VK_NULL_HANDLE; VkImageView depth_image_view = VK_NULL_HANDLE; VkRenderPass render_pass = VK_NULL_HANDLE; VkFramebuffer framebuffer = VK_NULL_HANDLE; VkDescriptorPool descriptor_pool = VK_NULL_HANDLE; VkDescriptorSetLayout set_layout = VK_NULL_HANDLE; VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; VkBuffer vertex_buffer = VK_NULL_HANDLE; byte* vertex_buffer_ptr = nullptr; // pointer to mapped vertex buffer int xyz_elements = 0; int color_st_elements = 0; VkBuffer index_buffer = VK_NULL_HANDLE; byte* index_buffer_ptr = nullptr; // pointer to mapped index buffer VkDeviceSize index_buffer_offset = 0; // host visible memory that holds both vertex and index data VkDeviceMemory geometry_buffer_memory = VK_NULL_HANDLE; VkDescriptorPool gamma_descriptor_pool = VK_NULL_HANDLE; VkDescriptorSetLayout gamma_set_layout = VK_NULL_HANDLE; VkPipelineLayout gamma_pipeline_layout = VK_NULL_HANDLE; VkDescriptorSet gamma_descriptor_set = VK_NULL_HANDLE; VkPipeline gamma_pipeline = VK_NULL_HANDLE; VkBuffer gamma_buffer = VK_NULL_HANDLE; VkDeviceMemory gamma_buffer_memory = VK_NULL_HANDLE; // // Shader modules. // VkShaderModule single_texture_vs = VK_NULL_HANDLE; VkShaderModule single_texture_clipping_plane_vs = VK_NULL_HANDLE; VkShaderModule single_texture_fs = VK_NULL_HANDLE; VkShaderModule multi_texture_vs = VK_NULL_HANDLE; VkShaderModule multi_texture_clipping_plane_vs = VK_NULL_HANDLE; VkShaderModule multi_texture_mul_fs = VK_NULL_HANDLE; VkShaderModule multi_texture_add_fs = VK_NULL_HANDLE; // // Standard pipelines. // VkPipeline skybox_pipeline = VK_NULL_HANDLE; // dim 0: 0 - front side, 1 - back size // dim 1: 0 - normal view, 1 - mirror view VkPipeline shadow_volume_pipelines[2][2]; VkPipeline shadow_finish_pipeline; // dim 0 is based on fogPass_t: 0 - corresponds to FP_EQUAL, 1 - corresponds to FP_LE. // dim 1 is directly a cullType_t enum value. // dim 2 is a polygon offset value (0 - off, 1 - on). VkPipeline fog_pipelines[2][3][2]; // dim 0 is based on dlight additive flag: 0 - not additive, 1 - additive // dim 1 is directly a cullType_t enum value. // dim 2 is a polygon offset value (0 - off, 1 - on). VkPipeline dlight_pipelines[2][3][2]; // debug visualization pipelines VkPipeline tris_debug_pipeline; VkPipeline tris_mirror_debug_pipeline; VkPipeline normals_debug_pipeline; VkPipeline surface_debug_pipeline_solid; VkPipeline surface_debug_pipeline_outline; VkPipeline images_debug_pipeline; #ifndef NDEBUG VkDebugReportCallbackEXT debug_callback; #endif }; // Vk_World contains vulkan resources/state requested by the game code. // It is reinitialized on a map change. struct Vk_World { // // Resources. // int num_samplers = 0; Vk_Sampler_Def sampler_defs[MAX_VK_SAMPLERS]; VkSampler samplers[MAX_VK_SAMPLERS]; int num_pipelines = 0; Vk_Pipeline_Def pipeline_defs[MAX_VK_PIPELINES]; VkPipeline pipelines[MAX_VK_PIPELINES]; float pipeline_create_time; Vk_Image images[MAX_VK_IMAGES]; // // Memory allocations. // struct Chunk { VkDeviceMemory memory = VK_NULL_HANDLE; VkDeviceSize used = 0; }; int num_image_chunks = 0; Chunk image_chunks[MAX_IMAGE_CHUNKS]; // Host visible memory used to copy image data to device local memory. VkBuffer staging_buffer = VK_NULL_HANDLE; VkDeviceMemory staging_buffer_memory = VK_NULL_HANDLE; VkDeviceSize staging_buffer_size = 0; byte* staging_buffer_ptr = nullptr; // pointer to mapped staging buffer // // State. // // Descriptor sets corresponding to bound texture images. VkDescriptorSet current_descriptor_sets[2]; // This flag is used to decide whether framebuffer's depth attachment should be cleared // with vmCmdClearAttachment (dirty_depth_attachment == true), or it have just been // cleared by render pass instance clear op (dirty_depth_attachment == false). bool dirty_depth_attachment; float modelview_transform[16]; }; // Most of the renderer's code uses Vulkan API via function provides in this file but // there are few places outside of vk.cpp where we use Vulkan commands directly. extern PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR; extern PFN_vkFreeDescriptorSets vkFreeDescriptorSets; extern PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; extern PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties; extern PFN_vkDestroyImage vkDestroyImage; extern PFN_vkDestroyImageView vkDestroyImageView; extern PFN_vkDeviceWaitIdle vkDeviceWaitIdle; ================================================ FILE: src/engine/renderer/vulkan/GLSL.std.450.h ================================================ /* ** Copyright (c) 2014-2016 The Khronos Group Inc. ** ** Permission is hereby granted, free of charge, to any person obtaining a copy ** of this software and/or associated documentation files (the "Materials"), ** to deal in the Materials without restriction, including without limitation ** the rights to use, copy, modify, merge, publish, distribute, sublicense, ** and/or sell copies of the Materials, and to permit persons to whom the ** Materials are furnished to do so, subject to the following conditions: ** ** The above copyright notice and this permission notice shall be included in ** all copies or substantial portions of the Materials. ** ** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS ** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND ** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ** ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS ** IN THE MATERIALS. */ #ifndef GLSLstd450_H #define GLSLstd450_H static const int GLSLstd450Version = 100; static const int GLSLstd450Revision = 3; enum GLSLstd450 { GLSLstd450Bad = 0, // Don't use GLSLstd450Round = 1, GLSLstd450RoundEven = 2, GLSLstd450Trunc = 3, GLSLstd450FAbs = 4, GLSLstd450SAbs = 5, GLSLstd450FSign = 6, GLSLstd450SSign = 7, GLSLstd450Floor = 8, GLSLstd450Ceil = 9, GLSLstd450Fract = 10, GLSLstd450Radians = 11, GLSLstd450Degrees = 12, GLSLstd450Sin = 13, GLSLstd450Cos = 14, GLSLstd450Tan = 15, GLSLstd450Asin = 16, GLSLstd450Acos = 17, GLSLstd450Atan = 18, GLSLstd450Sinh = 19, GLSLstd450Cosh = 20, GLSLstd450Tanh = 21, GLSLstd450Asinh = 22, GLSLstd450Acosh = 23, GLSLstd450Atanh = 24, GLSLstd450Atan2 = 25, GLSLstd450Pow = 26, GLSLstd450Exp = 27, GLSLstd450Log = 28, GLSLstd450Exp2 = 29, GLSLstd450Log2 = 30, GLSLstd450Sqrt = 31, GLSLstd450InverseSqrt = 32, GLSLstd450Determinant = 33, GLSLstd450MatrixInverse = 34, GLSLstd450Modf = 35, // second operand needs an OpVariable to write to GLSLstd450ModfStruct = 36, // no OpVariable operand GLSLstd450FMin = 37, GLSLstd450UMin = 38, GLSLstd450SMin = 39, GLSLstd450FMax = 40, GLSLstd450UMax = 41, GLSLstd450SMax = 42, GLSLstd450FClamp = 43, GLSLstd450UClamp = 44, GLSLstd450SClamp = 45, GLSLstd450FMix = 46, GLSLstd450IMix = 47, // Reserved GLSLstd450Step = 48, GLSLstd450SmoothStep = 49, GLSLstd450Fma = 50, GLSLstd450Frexp = 51, // second operand needs an OpVariable to write to GLSLstd450FrexpStruct = 52, // no OpVariable operand GLSLstd450Ldexp = 53, GLSLstd450PackSnorm4x8 = 54, GLSLstd450PackUnorm4x8 = 55, GLSLstd450PackSnorm2x16 = 56, GLSLstd450PackUnorm2x16 = 57, GLSLstd450PackHalf2x16 = 58, GLSLstd450PackDouble2x32 = 59, GLSLstd450UnpackSnorm2x16 = 60, GLSLstd450UnpackUnorm2x16 = 61, GLSLstd450UnpackHalf2x16 = 62, GLSLstd450UnpackSnorm4x8 = 63, GLSLstd450UnpackUnorm4x8 = 64, GLSLstd450UnpackDouble2x32 = 65, GLSLstd450Length = 66, GLSLstd450Distance = 67, GLSLstd450Cross = 68, GLSLstd450Normalize = 69, GLSLstd450FaceForward = 70, GLSLstd450Reflect = 71, GLSLstd450Refract = 72, GLSLstd450FindILsb = 73, GLSLstd450FindSMsb = 74, GLSLstd450FindUMsb = 75, GLSLstd450InterpolateAtCentroid = 76, GLSLstd450InterpolateAtSample = 77, GLSLstd450InterpolateAtOffset = 78, GLSLstd450NMin = 79, GLSLstd450NMax = 80, GLSLstd450NClamp = 81, GLSLstd450Count }; #endif // #ifndef GLSLstd450_H ================================================ FILE: src/engine/renderer/vulkan/spirv.h ================================================ /* ** Copyright (c) 2014-2017 The Khronos Group Inc. ** ** Permission is hereby granted, free of charge, to any person obtaining a copy ** of this software and/or associated documentation files (the "Materials"), ** to deal in the Materials without restriction, including without limitation ** the rights to use, copy, modify, merge, publish, distribute, sublicense, ** and/or sell copies of the Materials, and to permit persons to whom the ** Materials are furnished to do so, subject to the following conditions: ** ** The above copyright notice and this permission notice shall be included in ** all copies or substantial portions of the Materials. ** ** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS ** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND ** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ** ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS ** IN THE MATERIALS. */ /* ** This header is automatically generated by the same tool that creates ** the Binary Section of the SPIR-V specification. */ /* ** Enumeration tokens for SPIR-V, in various styles: ** C, C++, C++11, JSON, Lua, Python ** ** - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL ** - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL ** - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL ** - Lua will use tables, e.g.: spv.SourceLanguage.GLSL ** - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] ** ** Some tokens act like mask values, which can be OR'd together, ** while others are mutually exclusive. The mask-like ones have ** "Mask" in their name, and a parallel enum that has the shift ** amount (1 << x) for each corresponding enumerant. */ #ifndef spirv_H #define spirv_H typedef unsigned int SpvId; #define SPV_VERSION 0x10100 #define SPV_REVISION 6 static const unsigned int SpvMagicNumber = 0x07230203; static const unsigned int SpvVersion = 0x00010100; static const unsigned int SpvRevision = 6; static const unsigned int SpvOpCodeMask = 0xffff; static const unsigned int SpvWordCountShift = 16; typedef enum SpvSourceLanguage_ { SpvSourceLanguageUnknown = 0, SpvSourceLanguageESSL = 1, SpvSourceLanguageGLSL = 2, SpvSourceLanguageOpenCL_C = 3, SpvSourceLanguageOpenCL_CPP = 4, SpvSourceLanguageHLSL = 5, SpvSourceLanguageMax = 0x7fffffff, } SpvSourceLanguage; typedef enum SpvExecutionModel_ { SpvExecutionModelVertex = 0, SpvExecutionModelTessellationControl = 1, SpvExecutionModelTessellationEvaluation = 2, SpvExecutionModelGeometry = 3, SpvExecutionModelFragment = 4, SpvExecutionModelGLCompute = 5, SpvExecutionModelKernel = 6, SpvExecutionModelMax = 0x7fffffff, } SpvExecutionModel; typedef enum SpvAddressingModel_ { SpvAddressingModelLogical = 0, SpvAddressingModelPhysical32 = 1, SpvAddressingModelPhysical64 = 2, SpvAddressingModelMax = 0x7fffffff, } SpvAddressingModel; typedef enum SpvMemoryModel_ { SpvMemoryModelSimple = 0, SpvMemoryModelGLSL450 = 1, SpvMemoryModelOpenCL = 2, SpvMemoryModelMax = 0x7fffffff, } SpvMemoryModel; typedef enum SpvExecutionMode_ { SpvExecutionModeInvocations = 0, SpvExecutionModeSpacingEqual = 1, SpvExecutionModeSpacingFractionalEven = 2, SpvExecutionModeSpacingFractionalOdd = 3, SpvExecutionModeVertexOrderCw = 4, SpvExecutionModeVertexOrderCcw = 5, SpvExecutionModePixelCenterInteger = 6, SpvExecutionModeOriginUpperLeft = 7, SpvExecutionModeOriginLowerLeft = 8, SpvExecutionModeEarlyFragmentTests = 9, SpvExecutionModePointMode = 10, SpvExecutionModeXfb = 11, SpvExecutionModeDepthReplacing = 12, SpvExecutionModeDepthGreater = 14, SpvExecutionModeDepthLess = 15, SpvExecutionModeDepthUnchanged = 16, SpvExecutionModeLocalSize = 17, SpvExecutionModeLocalSizeHint = 18, SpvExecutionModeInputPoints = 19, SpvExecutionModeInputLines = 20, SpvExecutionModeInputLinesAdjacency = 21, SpvExecutionModeTriangles = 22, SpvExecutionModeInputTrianglesAdjacency = 23, SpvExecutionModeQuads = 24, SpvExecutionModeIsolines = 25, SpvExecutionModeOutputVertices = 26, SpvExecutionModeOutputPoints = 27, SpvExecutionModeOutputLineStrip = 28, SpvExecutionModeOutputTriangleStrip = 29, SpvExecutionModeVecTypeHint = 30, SpvExecutionModeContractionOff = 31, SpvExecutionModeInitializer = 33, SpvExecutionModeFinalizer = 34, SpvExecutionModeSubgroupSize = 35, SpvExecutionModeSubgroupsPerWorkgroup = 36, SpvExecutionModeMax = 0x7fffffff, } SpvExecutionMode; typedef enum SpvStorageClass_ { SpvStorageClassUniformConstant = 0, SpvStorageClassInput = 1, SpvStorageClassUniform = 2, SpvStorageClassOutput = 3, SpvStorageClassWorkgroup = 4, SpvStorageClassCrossWorkgroup = 5, SpvStorageClassPrivate = 6, SpvStorageClassFunction = 7, SpvStorageClassGeneric = 8, SpvStorageClassPushConstant = 9, SpvStorageClassAtomicCounter = 10, SpvStorageClassImage = 11, SpvStorageClassStorageBuffer = 12, SpvStorageClassMax = 0x7fffffff, } SpvStorageClass; typedef enum SpvDim_ { SpvDim1D = 0, SpvDim2D = 1, SpvDim3D = 2, SpvDimCube = 3, SpvDimRect = 4, SpvDimBuffer = 5, SpvDimSubpassData = 6, SpvDimMax = 0x7fffffff, } SpvDim; typedef enum SpvSamplerAddressingMode_ { SpvSamplerAddressingModeNone = 0, SpvSamplerAddressingModeClampToEdge = 1, SpvSamplerAddressingModeClamp = 2, SpvSamplerAddressingModeRepeat = 3, SpvSamplerAddressingModeRepeatMirrored = 4, SpvSamplerAddressingModeMax = 0x7fffffff, } SpvSamplerAddressingMode; typedef enum SpvSamplerFilterMode_ { SpvSamplerFilterModeNearest = 0, SpvSamplerFilterModeLinear = 1, SpvSamplerFilterModeMax = 0x7fffffff, } SpvSamplerFilterMode; typedef enum SpvImageFormat_ { SpvImageFormatUnknown = 0, SpvImageFormatRgba32f = 1, SpvImageFormatRgba16f = 2, SpvImageFormatR32f = 3, SpvImageFormatRgba8 = 4, SpvImageFormatRgba8Snorm = 5, SpvImageFormatRg32f = 6, SpvImageFormatRg16f = 7, SpvImageFormatR11fG11fB10f = 8, SpvImageFormatR16f = 9, SpvImageFormatRgba16 = 10, SpvImageFormatRgb10A2 = 11, SpvImageFormatRg16 = 12, SpvImageFormatRg8 = 13, SpvImageFormatR16 = 14, SpvImageFormatR8 = 15, SpvImageFormatRgba16Snorm = 16, SpvImageFormatRg16Snorm = 17, SpvImageFormatRg8Snorm = 18, SpvImageFormatR16Snorm = 19, SpvImageFormatR8Snorm = 20, SpvImageFormatRgba32i = 21, SpvImageFormatRgba16i = 22, SpvImageFormatRgba8i = 23, SpvImageFormatR32i = 24, SpvImageFormatRg32i = 25, SpvImageFormatRg16i = 26, SpvImageFormatRg8i = 27, SpvImageFormatR16i = 28, SpvImageFormatR8i = 29, SpvImageFormatRgba32ui = 30, SpvImageFormatRgba16ui = 31, SpvImageFormatRgba8ui = 32, SpvImageFormatR32ui = 33, SpvImageFormatRgb10a2ui = 34, SpvImageFormatRg32ui = 35, SpvImageFormatRg16ui = 36, SpvImageFormatRg8ui = 37, SpvImageFormatR16ui = 38, SpvImageFormatR8ui = 39, SpvImageFormatMax = 0x7fffffff, } SpvImageFormat; typedef enum SpvImageChannelOrder_ { SpvImageChannelOrderR = 0, SpvImageChannelOrderA = 1, SpvImageChannelOrderRG = 2, SpvImageChannelOrderRA = 3, SpvImageChannelOrderRGB = 4, SpvImageChannelOrderRGBA = 5, SpvImageChannelOrderBGRA = 6, SpvImageChannelOrderARGB = 7, SpvImageChannelOrderIntensity = 8, SpvImageChannelOrderLuminance = 9, SpvImageChannelOrderRx = 10, SpvImageChannelOrderRGx = 11, SpvImageChannelOrderRGBx = 12, SpvImageChannelOrderDepth = 13, SpvImageChannelOrderDepthStencil = 14, SpvImageChannelOrdersRGB = 15, SpvImageChannelOrdersRGBx = 16, SpvImageChannelOrdersRGBA = 17, SpvImageChannelOrdersBGRA = 18, SpvImageChannelOrderABGR = 19, SpvImageChannelOrderMax = 0x7fffffff, } SpvImageChannelOrder; typedef enum SpvImageChannelDataType_ { SpvImageChannelDataTypeSnormInt8 = 0, SpvImageChannelDataTypeSnormInt16 = 1, SpvImageChannelDataTypeUnormInt8 = 2, SpvImageChannelDataTypeUnormInt16 = 3, SpvImageChannelDataTypeUnormShort565 = 4, SpvImageChannelDataTypeUnormShort555 = 5, SpvImageChannelDataTypeUnormInt101010 = 6, SpvImageChannelDataTypeSignedInt8 = 7, SpvImageChannelDataTypeSignedInt16 = 8, SpvImageChannelDataTypeSignedInt32 = 9, SpvImageChannelDataTypeUnsignedInt8 = 10, SpvImageChannelDataTypeUnsignedInt16 = 11, SpvImageChannelDataTypeUnsignedInt32 = 12, SpvImageChannelDataTypeHalfFloat = 13, SpvImageChannelDataTypeFloat = 14, SpvImageChannelDataTypeUnormInt24 = 15, SpvImageChannelDataTypeUnormInt101010_2 = 16, SpvImageChannelDataTypeMax = 0x7fffffff, } SpvImageChannelDataType; typedef enum SpvImageOperandsShift_ { SpvImageOperandsBiasShift = 0, SpvImageOperandsLodShift = 1, SpvImageOperandsGradShift = 2, SpvImageOperandsConstOffsetShift = 3, SpvImageOperandsOffsetShift = 4, SpvImageOperandsConstOffsetsShift = 5, SpvImageOperandsSampleShift = 6, SpvImageOperandsMinLodShift = 7, SpvImageOperandsMax = 0x7fffffff, } SpvImageOperandsShift; typedef enum SpvImageOperandsMask_ { SpvImageOperandsMaskNone = 0, SpvImageOperandsBiasMask = 0x00000001, SpvImageOperandsLodMask = 0x00000002, SpvImageOperandsGradMask = 0x00000004, SpvImageOperandsConstOffsetMask = 0x00000008, SpvImageOperandsOffsetMask = 0x00000010, SpvImageOperandsConstOffsetsMask = 0x00000020, SpvImageOperandsSampleMask = 0x00000040, SpvImageOperandsMinLodMask = 0x00000080, } SpvImageOperandsMask; typedef enum SpvFPFastMathModeShift_ { SpvFPFastMathModeNotNaNShift = 0, SpvFPFastMathModeNotInfShift = 1, SpvFPFastMathModeNSZShift = 2, SpvFPFastMathModeAllowRecipShift = 3, SpvFPFastMathModeFastShift = 4, SpvFPFastMathModeMax = 0x7fffffff, } SpvFPFastMathModeShift; typedef enum SpvFPFastMathModeMask_ { SpvFPFastMathModeMaskNone = 0, SpvFPFastMathModeNotNaNMask = 0x00000001, SpvFPFastMathModeNotInfMask = 0x00000002, SpvFPFastMathModeNSZMask = 0x00000004, SpvFPFastMathModeAllowRecipMask = 0x00000008, SpvFPFastMathModeFastMask = 0x00000010, } SpvFPFastMathModeMask; typedef enum SpvFPRoundingMode_ { SpvFPRoundingModeRTE = 0, SpvFPRoundingModeRTZ = 1, SpvFPRoundingModeRTP = 2, SpvFPRoundingModeRTN = 3, SpvFPRoundingModeMax = 0x7fffffff, } SpvFPRoundingMode; typedef enum SpvLinkageType_ { SpvLinkageTypeExport = 0, SpvLinkageTypeImport = 1, SpvLinkageTypeMax = 0x7fffffff, } SpvLinkageType; typedef enum SpvAccessQualifier_ { SpvAccessQualifierReadOnly = 0, SpvAccessQualifierWriteOnly = 1, SpvAccessQualifierReadWrite = 2, SpvAccessQualifierMax = 0x7fffffff, } SpvAccessQualifier; typedef enum SpvFunctionParameterAttribute_ { SpvFunctionParameterAttributeZext = 0, SpvFunctionParameterAttributeSext = 1, SpvFunctionParameterAttributeByVal = 2, SpvFunctionParameterAttributeSret = 3, SpvFunctionParameterAttributeNoAlias = 4, SpvFunctionParameterAttributeNoCapture = 5, SpvFunctionParameterAttributeNoWrite = 6, SpvFunctionParameterAttributeNoReadWrite = 7, SpvFunctionParameterAttributeMax = 0x7fffffff, } SpvFunctionParameterAttribute; typedef enum SpvDecoration_ { SpvDecorationRelaxedPrecision = 0, SpvDecorationSpecId = 1, SpvDecorationBlock = 2, SpvDecorationBufferBlock = 3, SpvDecorationRowMajor = 4, SpvDecorationColMajor = 5, SpvDecorationArrayStride = 6, SpvDecorationMatrixStride = 7, SpvDecorationGLSLShared = 8, SpvDecorationGLSLPacked = 9, SpvDecorationCPacked = 10, SpvDecorationBuiltIn = 11, SpvDecorationNoPerspective = 13, SpvDecorationFlat = 14, SpvDecorationPatch = 15, SpvDecorationCentroid = 16, SpvDecorationSample = 17, SpvDecorationInvariant = 18, SpvDecorationRestrict = 19, SpvDecorationAliased = 20, SpvDecorationVolatile = 21, SpvDecorationConstant = 22, SpvDecorationCoherent = 23, SpvDecorationNonWritable = 24, SpvDecorationNonReadable = 25, SpvDecorationUniform = 26, SpvDecorationSaturatedConversion = 28, SpvDecorationStream = 29, SpvDecorationLocation = 30, SpvDecorationComponent = 31, SpvDecorationIndex = 32, SpvDecorationBinding = 33, SpvDecorationDescriptorSet = 34, SpvDecorationOffset = 35, SpvDecorationXfbBuffer = 36, SpvDecorationXfbStride = 37, SpvDecorationFuncParamAttr = 38, SpvDecorationFPRoundingMode = 39, SpvDecorationFPFastMathMode = 40, SpvDecorationLinkageAttributes = 41, SpvDecorationNoContraction = 42, SpvDecorationInputAttachmentIndex = 43, SpvDecorationAlignment = 44, SpvDecorationMaxByteOffset = 45, SpvDecorationOverrideCoverageNV = 5248, SpvDecorationPassthroughNV = 5250, SpvDecorationViewportRelativeNV = 5252, SpvDecorationSecondaryViewportRelativeNV = 5256, SpvDecorationMax = 0x7fffffff, } SpvDecoration; typedef enum SpvBuiltIn_ { SpvBuiltInPosition = 0, SpvBuiltInPointSize = 1, SpvBuiltInClipDistance = 3, SpvBuiltInCullDistance = 4, SpvBuiltInVertexId = 5, SpvBuiltInInstanceId = 6, SpvBuiltInPrimitiveId = 7, SpvBuiltInInvocationId = 8, SpvBuiltInLayer = 9, SpvBuiltInViewportIndex = 10, SpvBuiltInTessLevelOuter = 11, SpvBuiltInTessLevelInner = 12, SpvBuiltInTessCoord = 13, SpvBuiltInPatchVertices = 14, SpvBuiltInFragCoord = 15, SpvBuiltInPointCoord = 16, SpvBuiltInFrontFacing = 17, SpvBuiltInSampleId = 18, SpvBuiltInSamplePosition = 19, SpvBuiltInSampleMask = 20, SpvBuiltInFragDepth = 22, SpvBuiltInHelperInvocation = 23, SpvBuiltInNumWorkgroups = 24, SpvBuiltInWorkgroupSize = 25, SpvBuiltInWorkgroupId = 26, SpvBuiltInLocalInvocationId = 27, SpvBuiltInGlobalInvocationId = 28, SpvBuiltInLocalInvocationIndex = 29, SpvBuiltInWorkDim = 30, SpvBuiltInGlobalSize = 31, SpvBuiltInEnqueuedWorkgroupSize = 32, SpvBuiltInGlobalOffset = 33, SpvBuiltInGlobalLinearId = 34, SpvBuiltInSubgroupSize = 36, SpvBuiltInSubgroupMaxSize = 37, SpvBuiltInNumSubgroups = 38, SpvBuiltInNumEnqueuedSubgroups = 39, SpvBuiltInSubgroupId = 40, SpvBuiltInSubgroupLocalInvocationId = 41, SpvBuiltInVertexIndex = 42, SpvBuiltInInstanceIndex = 43, SpvBuiltInSubgroupEqMaskKHR = 4416, SpvBuiltInSubgroupGeMaskKHR = 4417, SpvBuiltInSubgroupGtMaskKHR = 4418, SpvBuiltInSubgroupLeMaskKHR = 4419, SpvBuiltInSubgroupLtMaskKHR = 4420, SpvBuiltInBaseVertex = 4424, SpvBuiltInBaseInstance = 4425, SpvBuiltInDrawIndex = 4426, SpvBuiltInDeviceIndex = 4438, SpvBuiltInViewIndex = 4440, SpvBuiltInViewportMaskNV = 5253, SpvBuiltInSecondaryPositionNV = 5257, SpvBuiltInSecondaryViewportMaskNV = 5258, SpvBuiltInPositionPerViewNV = 5261, SpvBuiltInViewportMaskPerViewNV = 5262, SpvBuiltInMax = 0x7fffffff, } SpvBuiltIn; typedef enum SpvSelectionControlShift_ { SpvSelectionControlFlattenShift = 0, SpvSelectionControlDontFlattenShift = 1, SpvSelectionControlMax = 0x7fffffff, } SpvSelectionControlShift; typedef enum SpvSelectionControlMask_ { SpvSelectionControlMaskNone = 0, SpvSelectionControlFlattenMask = 0x00000001, SpvSelectionControlDontFlattenMask = 0x00000002, } SpvSelectionControlMask; typedef enum SpvLoopControlShift_ { SpvLoopControlUnrollShift = 0, SpvLoopControlDontUnrollShift = 1, SpvLoopControlDependencyInfiniteShift = 2, SpvLoopControlDependencyLengthShift = 3, SpvLoopControlMax = 0x7fffffff, } SpvLoopControlShift; typedef enum SpvLoopControlMask_ { SpvLoopControlMaskNone = 0, SpvLoopControlUnrollMask = 0x00000001, SpvLoopControlDontUnrollMask = 0x00000002, SpvLoopControlDependencyInfiniteMask = 0x00000004, SpvLoopControlDependencyLengthMask = 0x00000008, } SpvLoopControlMask; typedef enum SpvFunctionControlShift_ { SpvFunctionControlInlineShift = 0, SpvFunctionControlDontInlineShift = 1, SpvFunctionControlPureShift = 2, SpvFunctionControlConstShift = 3, SpvFunctionControlMax = 0x7fffffff, } SpvFunctionControlShift; typedef enum SpvFunctionControlMask_ { SpvFunctionControlMaskNone = 0, SpvFunctionControlInlineMask = 0x00000001, SpvFunctionControlDontInlineMask = 0x00000002, SpvFunctionControlPureMask = 0x00000004, SpvFunctionControlConstMask = 0x00000008, } SpvFunctionControlMask; typedef enum SpvMemorySemanticsShift_ { SpvMemorySemanticsAcquireShift = 1, SpvMemorySemanticsReleaseShift = 2, SpvMemorySemanticsAcquireReleaseShift = 3, SpvMemorySemanticsSequentiallyConsistentShift = 4, SpvMemorySemanticsUniformMemoryShift = 6, SpvMemorySemanticsSubgroupMemoryShift = 7, SpvMemorySemanticsWorkgroupMemoryShift = 8, SpvMemorySemanticsCrossWorkgroupMemoryShift = 9, SpvMemorySemanticsAtomicCounterMemoryShift = 10, SpvMemorySemanticsImageMemoryShift = 11, SpvMemorySemanticsMax = 0x7fffffff, } SpvMemorySemanticsShift; typedef enum SpvMemorySemanticsMask_ { SpvMemorySemanticsMaskNone = 0, SpvMemorySemanticsAcquireMask = 0x00000002, SpvMemorySemanticsReleaseMask = 0x00000004, SpvMemorySemanticsAcquireReleaseMask = 0x00000008, SpvMemorySemanticsSequentiallyConsistentMask = 0x00000010, SpvMemorySemanticsUniformMemoryMask = 0x00000040, SpvMemorySemanticsSubgroupMemoryMask = 0x00000080, SpvMemorySemanticsWorkgroupMemoryMask = 0x00000100, SpvMemorySemanticsCrossWorkgroupMemoryMask = 0x00000200, SpvMemorySemanticsAtomicCounterMemoryMask = 0x00000400, SpvMemorySemanticsImageMemoryMask = 0x00000800, } SpvMemorySemanticsMask; typedef enum SpvMemoryAccessShift_ { SpvMemoryAccessVolatileShift = 0, SpvMemoryAccessAlignedShift = 1, SpvMemoryAccessNontemporalShift = 2, SpvMemoryAccessMax = 0x7fffffff, } SpvMemoryAccessShift; typedef enum SpvMemoryAccessMask_ { SpvMemoryAccessMaskNone = 0, SpvMemoryAccessVolatileMask = 0x00000001, SpvMemoryAccessAlignedMask = 0x00000002, SpvMemoryAccessNontemporalMask = 0x00000004, } SpvMemoryAccessMask; typedef enum SpvScope_ { SpvScopeCrossDevice = 0, SpvScopeDevice = 1, SpvScopeWorkgroup = 2, SpvScopeSubgroup = 3, SpvScopeInvocation = 4, SpvScopeMax = 0x7fffffff, } SpvScope; typedef enum SpvGroupOperation_ { SpvGroupOperationReduce = 0, SpvGroupOperationInclusiveScan = 1, SpvGroupOperationExclusiveScan = 2, SpvGroupOperationMax = 0x7fffffff, } SpvGroupOperation; typedef enum SpvKernelEnqueueFlags_ { SpvKernelEnqueueFlagsNoWait = 0, SpvKernelEnqueueFlagsWaitKernel = 1, SpvKernelEnqueueFlagsWaitWorkGroup = 2, SpvKernelEnqueueFlagsMax = 0x7fffffff, } SpvKernelEnqueueFlags; typedef enum SpvKernelProfilingInfoShift_ { SpvKernelProfilingInfoCmdExecTimeShift = 0, SpvKernelProfilingInfoMax = 0x7fffffff, } SpvKernelProfilingInfoShift; typedef enum SpvKernelProfilingInfoMask_ { SpvKernelProfilingInfoMaskNone = 0, SpvKernelProfilingInfoCmdExecTimeMask = 0x00000001, } SpvKernelProfilingInfoMask; typedef enum SpvCapability_ { SpvCapabilityMatrix = 0, SpvCapabilityShader = 1, SpvCapabilityGeometry = 2, SpvCapabilityTessellation = 3, SpvCapabilityAddresses = 4, SpvCapabilityLinkage = 5, SpvCapabilityKernel = 6, SpvCapabilityVector16 = 7, SpvCapabilityFloat16Buffer = 8, SpvCapabilityFloat16 = 9, SpvCapabilityFloat64 = 10, SpvCapabilityInt64 = 11, SpvCapabilityInt64Atomics = 12, SpvCapabilityImageBasic = 13, SpvCapabilityImageReadWrite = 14, SpvCapabilityImageMipmap = 15, SpvCapabilityPipes = 17, SpvCapabilityGroups = 18, SpvCapabilityDeviceEnqueue = 19, SpvCapabilityLiteralSampler = 20, SpvCapabilityAtomicStorage = 21, SpvCapabilityInt16 = 22, SpvCapabilityTessellationPointSize = 23, SpvCapabilityGeometryPointSize = 24, SpvCapabilityImageGatherExtended = 25, SpvCapabilityStorageImageMultisample = 27, SpvCapabilityUniformBufferArrayDynamicIndexing = 28, SpvCapabilitySampledImageArrayDynamicIndexing = 29, SpvCapabilityStorageBufferArrayDynamicIndexing = 30, SpvCapabilityStorageImageArrayDynamicIndexing = 31, SpvCapabilityClipDistance = 32, SpvCapabilityCullDistance = 33, SpvCapabilityImageCubeArray = 34, SpvCapabilitySampleRateShading = 35, SpvCapabilityImageRect = 36, SpvCapabilitySampledRect = 37, SpvCapabilityGenericPointer = 38, SpvCapabilityInt8 = 39, SpvCapabilityInputAttachment = 40, SpvCapabilitySparseResidency = 41, SpvCapabilityMinLod = 42, SpvCapabilitySampled1D = 43, SpvCapabilityImage1D = 44, SpvCapabilitySampledCubeArray = 45, SpvCapabilitySampledBuffer = 46, SpvCapabilityImageBuffer = 47, SpvCapabilityImageMSArray = 48, SpvCapabilityStorageImageExtendedFormats = 49, SpvCapabilityImageQuery = 50, SpvCapabilityDerivativeControl = 51, SpvCapabilityInterpolationFunction = 52, SpvCapabilityTransformFeedback = 53, SpvCapabilityGeometryStreams = 54, SpvCapabilityStorageImageReadWithoutFormat = 55, SpvCapabilityStorageImageWriteWithoutFormat = 56, SpvCapabilityMultiViewport = 57, SpvCapabilitySubgroupDispatch = 58, SpvCapabilityNamedBarrier = 59, SpvCapabilityPipeStorage = 60, SpvCapabilitySubgroupBallotKHR = 4423, SpvCapabilityDrawParameters = 4427, SpvCapabilitySubgroupVoteKHR = 4431, SpvCapabilityStorageBuffer16BitAccess = 4433, SpvCapabilityStorageUniformBufferBlock16 = 4433, SpvCapabilityStorageUniform16 = 4434, SpvCapabilityUniformAndStorageBuffer16BitAccess = 4434, SpvCapabilityStoragePushConstant16 = 4435, SpvCapabilityStorageInputOutput16 = 4436, SpvCapabilityDeviceGroup = 4437, SpvCapabilityMultiView = 4439, SpvCapabilityVariablePointersStorageBuffer = 4441, SpvCapabilityVariablePointers = 4442, SpvCapabilitySampleMaskOverrideCoverageNV = 5249, SpvCapabilityGeometryShaderPassthroughNV = 5251, SpvCapabilityShaderViewportIndexLayerNV = 5254, SpvCapabilityShaderViewportMaskNV = 5255, SpvCapabilityShaderStereoViewNV = 5259, SpvCapabilityPerViewAttributesNV = 5260, SpvCapabilityMax = 0x7fffffff, } SpvCapability; typedef enum SpvOp_ { SpvOpNop = 0, SpvOpUndef = 1, SpvOpSourceContinued = 2, SpvOpSource = 3, SpvOpSourceExtension = 4, SpvOpName = 5, SpvOpMemberName = 6, SpvOpString = 7, SpvOpLine = 8, SpvOpExtension = 10, SpvOpExtInstImport = 11, SpvOpExtInst = 12, SpvOpMemoryModel = 14, SpvOpEntryPoint = 15, SpvOpExecutionMode = 16, SpvOpCapability = 17, SpvOpTypeVoid = 19, SpvOpTypeBool = 20, SpvOpTypeInt = 21, SpvOpTypeFloat = 22, SpvOpTypeVector = 23, SpvOpTypeMatrix = 24, SpvOpTypeImage = 25, SpvOpTypeSampler = 26, SpvOpTypeSampledImage = 27, SpvOpTypeArray = 28, SpvOpTypeRuntimeArray = 29, SpvOpTypeStruct = 30, SpvOpTypeOpaque = 31, SpvOpTypePointer = 32, SpvOpTypeFunction = 33, SpvOpTypeEvent = 34, SpvOpTypeDeviceEvent = 35, SpvOpTypeReserveId = 36, SpvOpTypeQueue = 37, SpvOpTypePipe = 38, SpvOpTypeForwardPointer = 39, SpvOpConstantTrue = 41, SpvOpConstantFalse = 42, SpvOpConstant = 43, SpvOpConstantComposite = 44, SpvOpConstantSampler = 45, SpvOpConstantNull = 46, SpvOpSpecConstantTrue = 48, SpvOpSpecConstantFalse = 49, SpvOpSpecConstant = 50, SpvOpSpecConstantComposite = 51, SpvOpSpecConstantOp = 52, SpvOpFunction = 54, SpvOpFunctionParameter = 55, SpvOpFunctionEnd = 56, SpvOpFunctionCall = 57, SpvOpVariable = 59, SpvOpImageTexelPointer = 60, SpvOpLoad = 61, SpvOpStore = 62, SpvOpCopyMemory = 63, SpvOpCopyMemorySized = 64, SpvOpAccessChain = 65, SpvOpInBoundsAccessChain = 66, SpvOpPtrAccessChain = 67, SpvOpArrayLength = 68, SpvOpGenericPtrMemSemantics = 69, SpvOpInBoundsPtrAccessChain = 70, SpvOpDecorate = 71, SpvOpMemberDecorate = 72, SpvOpDecorationGroup = 73, SpvOpGroupDecorate = 74, SpvOpGroupMemberDecorate = 75, SpvOpVectorExtractDynamic = 77, SpvOpVectorInsertDynamic = 78, SpvOpVectorShuffle = 79, SpvOpCompositeConstruct = 80, SpvOpCompositeExtract = 81, SpvOpCompositeInsert = 82, SpvOpCopyObject = 83, SpvOpTranspose = 84, SpvOpSampledImage = 86, SpvOpImageSampleImplicitLod = 87, SpvOpImageSampleExplicitLod = 88, SpvOpImageSampleDrefImplicitLod = 89, SpvOpImageSampleDrefExplicitLod = 90, SpvOpImageSampleProjImplicitLod = 91, SpvOpImageSampleProjExplicitLod = 92, SpvOpImageSampleProjDrefImplicitLod = 93, SpvOpImageSampleProjDrefExplicitLod = 94, SpvOpImageFetch = 95, SpvOpImageGather = 96, SpvOpImageDrefGather = 97, SpvOpImageRead = 98, SpvOpImageWrite = 99, SpvOpImage = 100, SpvOpImageQueryFormat = 101, SpvOpImageQueryOrder = 102, SpvOpImageQuerySizeLod = 103, SpvOpImageQuerySize = 104, SpvOpImageQueryLod = 105, SpvOpImageQueryLevels = 106, SpvOpImageQuerySamples = 107, SpvOpConvertFToU = 109, SpvOpConvertFToS = 110, SpvOpConvertSToF = 111, SpvOpConvertUToF = 112, SpvOpUConvert = 113, SpvOpSConvert = 114, SpvOpFConvert = 115, SpvOpQuantizeToF16 = 116, SpvOpConvertPtrToU = 117, SpvOpSatConvertSToU = 118, SpvOpSatConvertUToS = 119, SpvOpConvertUToPtr = 120, SpvOpPtrCastToGeneric = 121, SpvOpGenericCastToPtr = 122, SpvOpGenericCastToPtrExplicit = 123, SpvOpBitcast = 124, SpvOpSNegate = 126, SpvOpFNegate = 127, SpvOpIAdd = 128, SpvOpFAdd = 129, SpvOpISub = 130, SpvOpFSub = 131, SpvOpIMul = 132, SpvOpFMul = 133, SpvOpUDiv = 134, SpvOpSDiv = 135, SpvOpFDiv = 136, SpvOpUMod = 137, SpvOpSRem = 138, SpvOpSMod = 139, SpvOpFRem = 140, SpvOpFMod = 141, SpvOpVectorTimesScalar = 142, SpvOpMatrixTimesScalar = 143, SpvOpVectorTimesMatrix = 144, SpvOpMatrixTimesVector = 145, SpvOpMatrixTimesMatrix = 146, SpvOpOuterProduct = 147, SpvOpDot = 148, SpvOpIAddCarry = 149, SpvOpISubBorrow = 150, SpvOpUMulExtended = 151, SpvOpSMulExtended = 152, SpvOpAny = 154, SpvOpAll = 155, SpvOpIsNan = 156, SpvOpIsInf = 157, SpvOpIsFinite = 158, SpvOpIsNormal = 159, SpvOpSignBitSet = 160, SpvOpLessOrGreater = 161, SpvOpOrdered = 162, SpvOpUnordered = 163, SpvOpLogicalEqual = 164, SpvOpLogicalNotEqual = 165, SpvOpLogicalOr = 166, SpvOpLogicalAnd = 167, SpvOpLogicalNot = 168, SpvOpSelect = 169, SpvOpIEqual = 170, SpvOpINotEqual = 171, SpvOpUGreaterThan = 172, SpvOpSGreaterThan = 173, SpvOpUGreaterThanEqual = 174, SpvOpSGreaterThanEqual = 175, SpvOpULessThan = 176, SpvOpSLessThan = 177, SpvOpULessThanEqual = 178, SpvOpSLessThanEqual = 179, SpvOpFOrdEqual = 180, SpvOpFUnordEqual = 181, SpvOpFOrdNotEqual = 182, SpvOpFUnordNotEqual = 183, SpvOpFOrdLessThan = 184, SpvOpFUnordLessThan = 185, SpvOpFOrdGreaterThan = 186, SpvOpFUnordGreaterThan = 187, SpvOpFOrdLessThanEqual = 188, SpvOpFUnordLessThanEqual = 189, SpvOpFOrdGreaterThanEqual = 190, SpvOpFUnordGreaterThanEqual = 191, SpvOpShiftRightLogical = 194, SpvOpShiftRightArithmetic = 195, SpvOpShiftLeftLogical = 196, SpvOpBitwiseOr = 197, SpvOpBitwiseXor = 198, SpvOpBitwiseAnd = 199, SpvOpNot = 200, SpvOpBitFieldInsert = 201, SpvOpBitFieldSExtract = 202, SpvOpBitFieldUExtract = 203, SpvOpBitReverse = 204, SpvOpBitCount = 205, SpvOpDPdx = 207, SpvOpDPdy = 208, SpvOpFwidth = 209, SpvOpDPdxFine = 210, SpvOpDPdyFine = 211, SpvOpFwidthFine = 212, SpvOpDPdxCoarse = 213, SpvOpDPdyCoarse = 214, SpvOpFwidthCoarse = 215, SpvOpEmitVertex = 218, SpvOpEndPrimitive = 219, SpvOpEmitStreamVertex = 220, SpvOpEndStreamPrimitive = 221, SpvOpControlBarrier = 224, SpvOpMemoryBarrier = 225, SpvOpAtomicLoad = 227, SpvOpAtomicStore = 228, SpvOpAtomicExchange = 229, SpvOpAtomicCompareExchange = 230, SpvOpAtomicCompareExchangeWeak = 231, SpvOpAtomicIIncrement = 232, SpvOpAtomicIDecrement = 233, SpvOpAtomicIAdd = 234, SpvOpAtomicISub = 235, SpvOpAtomicSMin = 236, SpvOpAtomicUMin = 237, SpvOpAtomicSMax = 238, SpvOpAtomicUMax = 239, SpvOpAtomicAnd = 240, SpvOpAtomicOr = 241, SpvOpAtomicXor = 242, SpvOpPhi = 245, SpvOpLoopMerge = 246, SpvOpSelectionMerge = 247, SpvOpLabel = 248, SpvOpBranch = 249, SpvOpBranchConditional = 250, SpvOpSwitch = 251, SpvOpKill = 252, SpvOpReturn = 253, SpvOpReturnValue = 254, SpvOpUnreachable = 255, SpvOpLifetimeStart = 256, SpvOpLifetimeStop = 257, SpvOpGroupAsyncCopy = 259, SpvOpGroupWaitEvents = 260, SpvOpGroupAll = 261, SpvOpGroupAny = 262, SpvOpGroupBroadcast = 263, SpvOpGroupIAdd = 264, SpvOpGroupFAdd = 265, SpvOpGroupFMin = 266, SpvOpGroupUMin = 267, SpvOpGroupSMin = 268, SpvOpGroupFMax = 269, SpvOpGroupUMax = 270, SpvOpGroupSMax = 271, SpvOpReadPipe = 274, SpvOpWritePipe = 275, SpvOpReservedReadPipe = 276, SpvOpReservedWritePipe = 277, SpvOpReserveReadPipePackets = 278, SpvOpReserveWritePipePackets = 279, SpvOpCommitReadPipe = 280, SpvOpCommitWritePipe = 281, SpvOpIsValidReserveId = 282, SpvOpGetNumPipePackets = 283, SpvOpGetMaxPipePackets = 284, SpvOpGroupReserveReadPipePackets = 285, SpvOpGroupReserveWritePipePackets = 286, SpvOpGroupCommitReadPipe = 287, SpvOpGroupCommitWritePipe = 288, SpvOpEnqueueMarker = 291, SpvOpEnqueueKernel = 292, SpvOpGetKernelNDrangeSubGroupCount = 293, SpvOpGetKernelNDrangeMaxSubGroupSize = 294, SpvOpGetKernelWorkGroupSize = 295, SpvOpGetKernelPreferredWorkGroupSizeMultiple = 296, SpvOpRetainEvent = 297, SpvOpReleaseEvent = 298, SpvOpCreateUserEvent = 299, SpvOpIsValidEvent = 300, SpvOpSetUserEventStatus = 301, SpvOpCaptureEventProfilingInfo = 302, SpvOpGetDefaultQueue = 303, SpvOpBuildNDRange = 304, SpvOpImageSparseSampleImplicitLod = 305, SpvOpImageSparseSampleExplicitLod = 306, SpvOpImageSparseSampleDrefImplicitLod = 307, SpvOpImageSparseSampleDrefExplicitLod = 308, SpvOpImageSparseSampleProjImplicitLod = 309, SpvOpImageSparseSampleProjExplicitLod = 310, SpvOpImageSparseSampleProjDrefImplicitLod = 311, SpvOpImageSparseSampleProjDrefExplicitLod = 312, SpvOpImageSparseFetch = 313, SpvOpImageSparseGather = 314, SpvOpImageSparseDrefGather = 315, SpvOpImageSparseTexelsResident = 316, SpvOpNoLine = 317, SpvOpAtomicFlagTestAndSet = 318, SpvOpAtomicFlagClear = 319, SpvOpImageSparseRead = 320, SpvOpSizeOf = 321, SpvOpTypePipeStorage = 322, SpvOpConstantPipeStorage = 323, SpvOpCreatePipeFromPipeStorage = 324, SpvOpGetKernelLocalSizeForSubgroupCount = 325, SpvOpGetKernelMaxNumSubgroups = 326, SpvOpTypeNamedBarrier = 327, SpvOpNamedBarrierInitialize = 328, SpvOpMemoryNamedBarrier = 329, SpvOpModuleProcessed = 330, SpvOpSubgroupBallotKHR = 4421, SpvOpSubgroupFirstInvocationKHR = 4422, SpvOpSubgroupAllKHR = 4428, SpvOpSubgroupAnyKHR = 4429, SpvOpSubgroupAllEqualKHR = 4430, SpvOpSubgroupReadInvocationKHR = 4432, SpvOpMax = 0x7fffffff, } SpvOp; #endif // #ifndef spirv_H ================================================ FILE: src/engine/renderer/vulkan/spirv.hpp ================================================ // Copyright (c) 2014-2017 The Khronos Group Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and/or associated documentation files (the "Materials"), // to deal in the Materials without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Materials, and to permit persons to whom the // Materials are furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Materials. // // MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS // STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND // HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ // // THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS // IN THE MATERIALS. // This header is automatically generated by the same tool that creates // the Binary Section of the SPIR-V specification. // Enumeration tokens for SPIR-V, in various styles: // C, C++, C++11, JSON, Lua, Python // // - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL // - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL // - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL // - Lua will use tables, e.g.: spv.SourceLanguage.GLSL // - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] // // Some tokens act like mask values, which can be OR'd together, // while others are mutually exclusive. The mask-like ones have // "Mask" in their name, and a parallel enum that has the shift // amount (1 << x) for each corresponding enumerant. #ifndef spirv_HPP #define spirv_HPP namespace spv { typedef unsigned int Id; #define SPV_VERSION 0x10100 #define SPV_REVISION 6 static const unsigned int MagicNumber = 0x07230203; static const unsigned int Version = 0x00010100; static const unsigned int Revision = 6; static const unsigned int OpCodeMask = 0xffff; static const unsigned int WordCountShift = 16; enum SourceLanguage { SourceLanguageUnknown = 0, SourceLanguageESSL = 1, SourceLanguageGLSL = 2, SourceLanguageOpenCL_C = 3, SourceLanguageOpenCL_CPP = 4, SourceLanguageHLSL = 5, SourceLanguageMax = 0x7fffffff, }; enum ExecutionModel { ExecutionModelVertex = 0, ExecutionModelTessellationControl = 1, ExecutionModelTessellationEvaluation = 2, ExecutionModelGeometry = 3, ExecutionModelFragment = 4, ExecutionModelGLCompute = 5, ExecutionModelKernel = 6, ExecutionModelMax = 0x7fffffff, }; enum AddressingModel { AddressingModelLogical = 0, AddressingModelPhysical32 = 1, AddressingModelPhysical64 = 2, AddressingModelMax = 0x7fffffff, }; enum MemoryModel { MemoryModelSimple = 0, MemoryModelGLSL450 = 1, MemoryModelOpenCL = 2, MemoryModelMax = 0x7fffffff, }; enum ExecutionMode { ExecutionModeInvocations = 0, ExecutionModeSpacingEqual = 1, ExecutionModeSpacingFractionalEven = 2, ExecutionModeSpacingFractionalOdd = 3, ExecutionModeVertexOrderCw = 4, ExecutionModeVertexOrderCcw = 5, ExecutionModePixelCenterInteger = 6, ExecutionModeOriginUpperLeft = 7, ExecutionModeOriginLowerLeft = 8, ExecutionModeEarlyFragmentTests = 9, ExecutionModePointMode = 10, ExecutionModeXfb = 11, ExecutionModeDepthReplacing = 12, ExecutionModeDepthGreater = 14, ExecutionModeDepthLess = 15, ExecutionModeDepthUnchanged = 16, ExecutionModeLocalSize = 17, ExecutionModeLocalSizeHint = 18, ExecutionModeInputPoints = 19, ExecutionModeInputLines = 20, ExecutionModeInputLinesAdjacency = 21, ExecutionModeTriangles = 22, ExecutionModeInputTrianglesAdjacency = 23, ExecutionModeQuads = 24, ExecutionModeIsolines = 25, ExecutionModeOutputVertices = 26, ExecutionModeOutputPoints = 27, ExecutionModeOutputLineStrip = 28, ExecutionModeOutputTriangleStrip = 29, ExecutionModeVecTypeHint = 30, ExecutionModeContractionOff = 31, ExecutionModeInitializer = 33, ExecutionModeFinalizer = 34, ExecutionModeSubgroupSize = 35, ExecutionModeSubgroupsPerWorkgroup = 36, ExecutionModeMax = 0x7fffffff, }; enum StorageClass { StorageClassUniformConstant = 0, StorageClassInput = 1, StorageClassUniform = 2, StorageClassOutput = 3, StorageClassWorkgroup = 4, StorageClassCrossWorkgroup = 5, StorageClassPrivate = 6, StorageClassFunction = 7, StorageClassGeneric = 8, StorageClassPushConstant = 9, StorageClassAtomicCounter = 10, StorageClassImage = 11, StorageClassStorageBuffer = 12, StorageClassMax = 0x7fffffff, }; enum Dim { Dim1D = 0, Dim2D = 1, Dim3D = 2, DimCube = 3, DimRect = 4, DimBuffer = 5, DimSubpassData = 6, DimMax = 0x7fffffff, }; enum SamplerAddressingMode { SamplerAddressingModeNone = 0, SamplerAddressingModeClampToEdge = 1, SamplerAddressingModeClamp = 2, SamplerAddressingModeRepeat = 3, SamplerAddressingModeRepeatMirrored = 4, SamplerAddressingModeMax = 0x7fffffff, }; enum SamplerFilterMode { SamplerFilterModeNearest = 0, SamplerFilterModeLinear = 1, SamplerFilterModeMax = 0x7fffffff, }; enum ImageFormat { ImageFormatUnknown = 0, ImageFormatRgba32f = 1, ImageFormatRgba16f = 2, ImageFormatR32f = 3, ImageFormatRgba8 = 4, ImageFormatRgba8Snorm = 5, ImageFormatRg32f = 6, ImageFormatRg16f = 7, ImageFormatR11fG11fB10f = 8, ImageFormatR16f = 9, ImageFormatRgba16 = 10, ImageFormatRgb10A2 = 11, ImageFormatRg16 = 12, ImageFormatRg8 = 13, ImageFormatR16 = 14, ImageFormatR8 = 15, ImageFormatRgba16Snorm = 16, ImageFormatRg16Snorm = 17, ImageFormatRg8Snorm = 18, ImageFormatR16Snorm = 19, ImageFormatR8Snorm = 20, ImageFormatRgba32i = 21, ImageFormatRgba16i = 22, ImageFormatRgba8i = 23, ImageFormatR32i = 24, ImageFormatRg32i = 25, ImageFormatRg16i = 26, ImageFormatRg8i = 27, ImageFormatR16i = 28, ImageFormatR8i = 29, ImageFormatRgba32ui = 30, ImageFormatRgba16ui = 31, ImageFormatRgba8ui = 32, ImageFormatR32ui = 33, ImageFormatRgb10a2ui = 34, ImageFormatRg32ui = 35, ImageFormatRg16ui = 36, ImageFormatRg8ui = 37, ImageFormatR16ui = 38, ImageFormatR8ui = 39, ImageFormatMax = 0x7fffffff, }; enum ImageChannelOrder { ImageChannelOrderR = 0, ImageChannelOrderA = 1, ImageChannelOrderRG = 2, ImageChannelOrderRA = 3, ImageChannelOrderRGB = 4, ImageChannelOrderRGBA = 5, ImageChannelOrderBGRA = 6, ImageChannelOrderARGB = 7, ImageChannelOrderIntensity = 8, ImageChannelOrderLuminance = 9, ImageChannelOrderRx = 10, ImageChannelOrderRGx = 11, ImageChannelOrderRGBx = 12, ImageChannelOrderDepth = 13, ImageChannelOrderDepthStencil = 14, ImageChannelOrdersRGB = 15, ImageChannelOrdersRGBx = 16, ImageChannelOrdersRGBA = 17, ImageChannelOrdersBGRA = 18, ImageChannelOrderABGR = 19, ImageChannelOrderMax = 0x7fffffff, }; enum ImageChannelDataType { ImageChannelDataTypeSnormInt8 = 0, ImageChannelDataTypeSnormInt16 = 1, ImageChannelDataTypeUnormInt8 = 2, ImageChannelDataTypeUnormInt16 = 3, ImageChannelDataTypeUnormShort565 = 4, ImageChannelDataTypeUnormShort555 = 5, ImageChannelDataTypeUnormInt101010 = 6, ImageChannelDataTypeSignedInt8 = 7, ImageChannelDataTypeSignedInt16 = 8, ImageChannelDataTypeSignedInt32 = 9, ImageChannelDataTypeUnsignedInt8 = 10, ImageChannelDataTypeUnsignedInt16 = 11, ImageChannelDataTypeUnsignedInt32 = 12, ImageChannelDataTypeHalfFloat = 13, ImageChannelDataTypeFloat = 14, ImageChannelDataTypeUnormInt24 = 15, ImageChannelDataTypeUnormInt101010_2 = 16, ImageChannelDataTypeMax = 0x7fffffff, }; enum ImageOperandsShift { ImageOperandsBiasShift = 0, ImageOperandsLodShift = 1, ImageOperandsGradShift = 2, ImageOperandsConstOffsetShift = 3, ImageOperandsOffsetShift = 4, ImageOperandsConstOffsetsShift = 5, ImageOperandsSampleShift = 6, ImageOperandsMinLodShift = 7, ImageOperandsMax = 0x7fffffff, }; enum ImageOperandsMask { ImageOperandsMaskNone = 0, ImageOperandsBiasMask = 0x00000001, ImageOperandsLodMask = 0x00000002, ImageOperandsGradMask = 0x00000004, ImageOperandsConstOffsetMask = 0x00000008, ImageOperandsOffsetMask = 0x00000010, ImageOperandsConstOffsetsMask = 0x00000020, ImageOperandsSampleMask = 0x00000040, ImageOperandsMinLodMask = 0x00000080, }; enum FPFastMathModeShift { FPFastMathModeNotNaNShift = 0, FPFastMathModeNotInfShift = 1, FPFastMathModeNSZShift = 2, FPFastMathModeAllowRecipShift = 3, FPFastMathModeFastShift = 4, FPFastMathModeMax = 0x7fffffff, }; enum FPFastMathModeMask { FPFastMathModeMaskNone = 0, FPFastMathModeNotNaNMask = 0x00000001, FPFastMathModeNotInfMask = 0x00000002, FPFastMathModeNSZMask = 0x00000004, FPFastMathModeAllowRecipMask = 0x00000008, FPFastMathModeFastMask = 0x00000010, }; enum FPRoundingMode { FPRoundingModeRTE = 0, FPRoundingModeRTZ = 1, FPRoundingModeRTP = 2, FPRoundingModeRTN = 3, FPRoundingModeMax = 0x7fffffff, }; enum LinkageType { LinkageTypeExport = 0, LinkageTypeImport = 1, LinkageTypeMax = 0x7fffffff, }; enum AccessQualifier { AccessQualifierReadOnly = 0, AccessQualifierWriteOnly = 1, AccessQualifierReadWrite = 2, AccessQualifierMax = 0x7fffffff, }; enum FunctionParameterAttribute { FunctionParameterAttributeZext = 0, FunctionParameterAttributeSext = 1, FunctionParameterAttributeByVal = 2, FunctionParameterAttributeSret = 3, FunctionParameterAttributeNoAlias = 4, FunctionParameterAttributeNoCapture = 5, FunctionParameterAttributeNoWrite = 6, FunctionParameterAttributeNoReadWrite = 7, FunctionParameterAttributeMax = 0x7fffffff, }; enum Decoration { DecorationRelaxedPrecision = 0, DecorationSpecId = 1, DecorationBlock = 2, DecorationBufferBlock = 3, DecorationRowMajor = 4, DecorationColMajor = 5, DecorationArrayStride = 6, DecorationMatrixStride = 7, DecorationGLSLShared = 8, DecorationGLSLPacked = 9, DecorationCPacked = 10, DecorationBuiltIn = 11, DecorationNoPerspective = 13, DecorationFlat = 14, DecorationPatch = 15, DecorationCentroid = 16, DecorationSample = 17, DecorationInvariant = 18, DecorationRestrict = 19, DecorationAliased = 20, DecorationVolatile = 21, DecorationConstant = 22, DecorationCoherent = 23, DecorationNonWritable = 24, DecorationNonReadable = 25, DecorationUniform = 26, DecorationSaturatedConversion = 28, DecorationStream = 29, DecorationLocation = 30, DecorationComponent = 31, DecorationIndex = 32, DecorationBinding = 33, DecorationDescriptorSet = 34, DecorationOffset = 35, DecorationXfbBuffer = 36, DecorationXfbStride = 37, DecorationFuncParamAttr = 38, DecorationFPRoundingMode = 39, DecorationFPFastMathMode = 40, DecorationLinkageAttributes = 41, DecorationNoContraction = 42, DecorationInputAttachmentIndex = 43, DecorationAlignment = 44, DecorationMaxByteOffset = 45, DecorationOverrideCoverageNV = 5248, DecorationPassthroughNV = 5250, DecorationViewportRelativeNV = 5252, DecorationSecondaryViewportRelativeNV = 5256, DecorationMax = 0x7fffffff, }; enum BuiltIn { BuiltInPosition = 0, BuiltInPointSize = 1, BuiltInClipDistance = 3, BuiltInCullDistance = 4, BuiltInVertexId = 5, BuiltInInstanceId = 6, BuiltInPrimitiveId = 7, BuiltInInvocationId = 8, BuiltInLayer = 9, BuiltInViewportIndex = 10, BuiltInTessLevelOuter = 11, BuiltInTessLevelInner = 12, BuiltInTessCoord = 13, BuiltInPatchVertices = 14, BuiltInFragCoord = 15, BuiltInPointCoord = 16, BuiltInFrontFacing = 17, BuiltInSampleId = 18, BuiltInSamplePosition = 19, BuiltInSampleMask = 20, BuiltInFragDepth = 22, BuiltInHelperInvocation = 23, BuiltInNumWorkgroups = 24, BuiltInWorkgroupSize = 25, BuiltInWorkgroupId = 26, BuiltInLocalInvocationId = 27, BuiltInGlobalInvocationId = 28, BuiltInLocalInvocationIndex = 29, BuiltInWorkDim = 30, BuiltInGlobalSize = 31, BuiltInEnqueuedWorkgroupSize = 32, BuiltInGlobalOffset = 33, BuiltInGlobalLinearId = 34, BuiltInSubgroupSize = 36, BuiltInSubgroupMaxSize = 37, BuiltInNumSubgroups = 38, BuiltInNumEnqueuedSubgroups = 39, BuiltInSubgroupId = 40, BuiltInSubgroupLocalInvocationId = 41, BuiltInVertexIndex = 42, BuiltInInstanceIndex = 43, BuiltInSubgroupEqMaskKHR = 4416, BuiltInSubgroupGeMaskKHR = 4417, BuiltInSubgroupGtMaskKHR = 4418, BuiltInSubgroupLeMaskKHR = 4419, BuiltInSubgroupLtMaskKHR = 4420, BuiltInBaseVertex = 4424, BuiltInBaseInstance = 4425, BuiltInDrawIndex = 4426, BuiltInDeviceIndex = 4438, BuiltInViewIndex = 4440, BuiltInViewportMaskNV = 5253, BuiltInSecondaryPositionNV = 5257, BuiltInSecondaryViewportMaskNV = 5258, BuiltInPositionPerViewNV = 5261, BuiltInViewportMaskPerViewNV = 5262, BuiltInMax = 0x7fffffff, }; enum SelectionControlShift { SelectionControlFlattenShift = 0, SelectionControlDontFlattenShift = 1, SelectionControlMax = 0x7fffffff, }; enum SelectionControlMask { SelectionControlMaskNone = 0, SelectionControlFlattenMask = 0x00000001, SelectionControlDontFlattenMask = 0x00000002, }; enum LoopControlShift { LoopControlUnrollShift = 0, LoopControlDontUnrollShift = 1, LoopControlDependencyInfiniteShift = 2, LoopControlDependencyLengthShift = 3, LoopControlMax = 0x7fffffff, }; enum LoopControlMask { LoopControlMaskNone = 0, LoopControlUnrollMask = 0x00000001, LoopControlDontUnrollMask = 0x00000002, LoopControlDependencyInfiniteMask = 0x00000004, LoopControlDependencyLengthMask = 0x00000008, }; enum FunctionControlShift { FunctionControlInlineShift = 0, FunctionControlDontInlineShift = 1, FunctionControlPureShift = 2, FunctionControlConstShift = 3, FunctionControlMax = 0x7fffffff, }; enum FunctionControlMask { FunctionControlMaskNone = 0, FunctionControlInlineMask = 0x00000001, FunctionControlDontInlineMask = 0x00000002, FunctionControlPureMask = 0x00000004, FunctionControlConstMask = 0x00000008, }; enum MemorySemanticsShift { MemorySemanticsAcquireShift = 1, MemorySemanticsReleaseShift = 2, MemorySemanticsAcquireReleaseShift = 3, MemorySemanticsSequentiallyConsistentShift = 4, MemorySemanticsUniformMemoryShift = 6, MemorySemanticsSubgroupMemoryShift = 7, MemorySemanticsWorkgroupMemoryShift = 8, MemorySemanticsCrossWorkgroupMemoryShift = 9, MemorySemanticsAtomicCounterMemoryShift = 10, MemorySemanticsImageMemoryShift = 11, MemorySemanticsMax = 0x7fffffff, }; enum MemorySemanticsMask { MemorySemanticsMaskNone = 0, MemorySemanticsAcquireMask = 0x00000002, MemorySemanticsReleaseMask = 0x00000004, MemorySemanticsAcquireReleaseMask = 0x00000008, MemorySemanticsSequentiallyConsistentMask = 0x00000010, MemorySemanticsUniformMemoryMask = 0x00000040, MemorySemanticsSubgroupMemoryMask = 0x00000080, MemorySemanticsWorkgroupMemoryMask = 0x00000100, MemorySemanticsCrossWorkgroupMemoryMask = 0x00000200, MemorySemanticsAtomicCounterMemoryMask = 0x00000400, MemorySemanticsImageMemoryMask = 0x00000800, }; enum MemoryAccessShift { MemoryAccessVolatileShift = 0, MemoryAccessAlignedShift = 1, MemoryAccessNontemporalShift = 2, MemoryAccessMax = 0x7fffffff, }; enum MemoryAccessMask { MemoryAccessMaskNone = 0, MemoryAccessVolatileMask = 0x00000001, MemoryAccessAlignedMask = 0x00000002, MemoryAccessNontemporalMask = 0x00000004, }; enum Scope { ScopeCrossDevice = 0, ScopeDevice = 1, ScopeWorkgroup = 2, ScopeSubgroup = 3, ScopeInvocation = 4, ScopeMax = 0x7fffffff, }; enum GroupOperation { GroupOperationReduce = 0, GroupOperationInclusiveScan = 1, GroupOperationExclusiveScan = 2, GroupOperationMax = 0x7fffffff, }; enum KernelEnqueueFlags { KernelEnqueueFlagsNoWait = 0, KernelEnqueueFlagsWaitKernel = 1, KernelEnqueueFlagsWaitWorkGroup = 2, KernelEnqueueFlagsMax = 0x7fffffff, }; enum KernelProfilingInfoShift { KernelProfilingInfoCmdExecTimeShift = 0, KernelProfilingInfoMax = 0x7fffffff, }; enum KernelProfilingInfoMask { KernelProfilingInfoMaskNone = 0, KernelProfilingInfoCmdExecTimeMask = 0x00000001, }; enum Capability { CapabilityMatrix = 0, CapabilityShader = 1, CapabilityGeometry = 2, CapabilityTessellation = 3, CapabilityAddresses = 4, CapabilityLinkage = 5, CapabilityKernel = 6, CapabilityVector16 = 7, CapabilityFloat16Buffer = 8, CapabilityFloat16 = 9, CapabilityFloat64 = 10, CapabilityInt64 = 11, CapabilityInt64Atomics = 12, CapabilityImageBasic = 13, CapabilityImageReadWrite = 14, CapabilityImageMipmap = 15, CapabilityPipes = 17, CapabilityGroups = 18, CapabilityDeviceEnqueue = 19, CapabilityLiteralSampler = 20, CapabilityAtomicStorage = 21, CapabilityInt16 = 22, CapabilityTessellationPointSize = 23, CapabilityGeometryPointSize = 24, CapabilityImageGatherExtended = 25, CapabilityStorageImageMultisample = 27, CapabilityUniformBufferArrayDynamicIndexing = 28, CapabilitySampledImageArrayDynamicIndexing = 29, CapabilityStorageBufferArrayDynamicIndexing = 30, CapabilityStorageImageArrayDynamicIndexing = 31, CapabilityClipDistance = 32, CapabilityCullDistance = 33, CapabilityImageCubeArray = 34, CapabilitySampleRateShading = 35, CapabilityImageRect = 36, CapabilitySampledRect = 37, CapabilityGenericPointer = 38, CapabilityInt8 = 39, CapabilityInputAttachment = 40, CapabilitySparseResidency = 41, CapabilityMinLod = 42, CapabilitySampled1D = 43, CapabilityImage1D = 44, CapabilitySampledCubeArray = 45, CapabilitySampledBuffer = 46, CapabilityImageBuffer = 47, CapabilityImageMSArray = 48, CapabilityStorageImageExtendedFormats = 49, CapabilityImageQuery = 50, CapabilityDerivativeControl = 51, CapabilityInterpolationFunction = 52, CapabilityTransformFeedback = 53, CapabilityGeometryStreams = 54, CapabilityStorageImageReadWithoutFormat = 55, CapabilityStorageImageWriteWithoutFormat = 56, CapabilityMultiViewport = 57, CapabilitySubgroupDispatch = 58, CapabilityNamedBarrier = 59, CapabilityPipeStorage = 60, CapabilitySubgroupBallotKHR = 4423, CapabilityDrawParameters = 4427, CapabilitySubgroupVoteKHR = 4431, CapabilityStorageBuffer16BitAccess = 4433, CapabilityStorageUniformBufferBlock16 = 4433, CapabilityStorageUniform16 = 4434, CapabilityUniformAndStorageBuffer16BitAccess = 4434, CapabilityStoragePushConstant16 = 4435, CapabilityStorageInputOutput16 = 4436, CapabilityDeviceGroup = 4437, CapabilityMultiView = 4439, CapabilityVariablePointersStorageBuffer = 4441, CapabilityVariablePointers = 4442, CapabilitySampleMaskOverrideCoverageNV = 5249, CapabilityGeometryShaderPassthroughNV = 5251, CapabilityShaderViewportIndexLayerNV = 5254, CapabilityShaderViewportMaskNV = 5255, CapabilityShaderStereoViewNV = 5259, CapabilityPerViewAttributesNV = 5260, CapabilityMax = 0x7fffffff, }; enum Op { OpNop = 0, OpUndef = 1, OpSourceContinued = 2, OpSource = 3, OpSourceExtension = 4, OpName = 5, OpMemberName = 6, OpString = 7, OpLine = 8, OpExtension = 10, OpExtInstImport = 11, OpExtInst = 12, OpMemoryModel = 14, OpEntryPoint = 15, OpExecutionMode = 16, OpCapability = 17, OpTypeVoid = 19, OpTypeBool = 20, OpTypeInt = 21, OpTypeFloat = 22, OpTypeVector = 23, OpTypeMatrix = 24, OpTypeImage = 25, OpTypeSampler = 26, OpTypeSampledImage = 27, OpTypeArray = 28, OpTypeRuntimeArray = 29, OpTypeStruct = 30, OpTypeOpaque = 31, OpTypePointer = 32, OpTypeFunction = 33, OpTypeEvent = 34, OpTypeDeviceEvent = 35, OpTypeReserveId = 36, OpTypeQueue = 37, OpTypePipe = 38, OpTypeForwardPointer = 39, OpConstantTrue = 41, OpConstantFalse = 42, OpConstant = 43, OpConstantComposite = 44, OpConstantSampler = 45, OpConstantNull = 46, OpSpecConstantTrue = 48, OpSpecConstantFalse = 49, OpSpecConstant = 50, OpSpecConstantComposite = 51, OpSpecConstantOp = 52, OpFunction = 54, OpFunctionParameter = 55, OpFunctionEnd = 56, OpFunctionCall = 57, OpVariable = 59, OpImageTexelPointer = 60, OpLoad = 61, OpStore = 62, OpCopyMemory = 63, OpCopyMemorySized = 64, OpAccessChain = 65, OpInBoundsAccessChain = 66, OpPtrAccessChain = 67, OpArrayLength = 68, OpGenericPtrMemSemantics = 69, OpInBoundsPtrAccessChain = 70, OpDecorate = 71, OpMemberDecorate = 72, OpDecorationGroup = 73, OpGroupDecorate = 74, OpGroupMemberDecorate = 75, OpVectorExtractDynamic = 77, OpVectorInsertDynamic = 78, OpVectorShuffle = 79, OpCompositeConstruct = 80, OpCompositeExtract = 81, OpCompositeInsert = 82, OpCopyObject = 83, OpTranspose = 84, OpSampledImage = 86, OpImageSampleImplicitLod = 87, OpImageSampleExplicitLod = 88, OpImageSampleDrefImplicitLod = 89, OpImageSampleDrefExplicitLod = 90, OpImageSampleProjImplicitLod = 91, OpImageSampleProjExplicitLod = 92, OpImageSampleProjDrefImplicitLod = 93, OpImageSampleProjDrefExplicitLod = 94, OpImageFetch = 95, OpImageGather = 96, OpImageDrefGather = 97, OpImageRead = 98, OpImageWrite = 99, OpImage = 100, OpImageQueryFormat = 101, OpImageQueryOrder = 102, OpImageQuerySizeLod = 103, OpImageQuerySize = 104, OpImageQueryLod = 105, OpImageQueryLevels = 106, OpImageQuerySamples = 107, OpConvertFToU = 109, OpConvertFToS = 110, OpConvertSToF = 111, OpConvertUToF = 112, OpUConvert = 113, OpSConvert = 114, OpFConvert = 115, OpQuantizeToF16 = 116, OpConvertPtrToU = 117, OpSatConvertSToU = 118, OpSatConvertUToS = 119, OpConvertUToPtr = 120, OpPtrCastToGeneric = 121, OpGenericCastToPtr = 122, OpGenericCastToPtrExplicit = 123, OpBitcast = 124, OpSNegate = 126, OpFNegate = 127, OpIAdd = 128, OpFAdd = 129, OpISub = 130, OpFSub = 131, OpIMul = 132, OpFMul = 133, OpUDiv = 134, OpSDiv = 135, OpFDiv = 136, OpUMod = 137, OpSRem = 138, OpSMod = 139, OpFRem = 140, OpFMod = 141, OpVectorTimesScalar = 142, OpMatrixTimesScalar = 143, OpVectorTimesMatrix = 144, OpMatrixTimesVector = 145, OpMatrixTimesMatrix = 146, OpOuterProduct = 147, OpDot = 148, OpIAddCarry = 149, OpISubBorrow = 150, OpUMulExtended = 151, OpSMulExtended = 152, OpAny = 154, OpAll = 155, OpIsNan = 156, OpIsInf = 157, OpIsFinite = 158, OpIsNormal = 159, OpSignBitSet = 160, OpLessOrGreater = 161, OpOrdered = 162, OpUnordered = 163, OpLogicalEqual = 164, OpLogicalNotEqual = 165, OpLogicalOr = 166, OpLogicalAnd = 167, OpLogicalNot = 168, OpSelect = 169, OpIEqual = 170, OpINotEqual = 171, OpUGreaterThan = 172, OpSGreaterThan = 173, OpUGreaterThanEqual = 174, OpSGreaterThanEqual = 175, OpULessThan = 176, OpSLessThan = 177, OpULessThanEqual = 178, OpSLessThanEqual = 179, OpFOrdEqual = 180, OpFUnordEqual = 181, OpFOrdNotEqual = 182, OpFUnordNotEqual = 183, OpFOrdLessThan = 184, OpFUnordLessThan = 185, OpFOrdGreaterThan = 186, OpFUnordGreaterThan = 187, OpFOrdLessThanEqual = 188, OpFUnordLessThanEqual = 189, OpFOrdGreaterThanEqual = 190, OpFUnordGreaterThanEqual = 191, OpShiftRightLogical = 194, OpShiftRightArithmetic = 195, OpShiftLeftLogical = 196, OpBitwiseOr = 197, OpBitwiseXor = 198, OpBitwiseAnd = 199, OpNot = 200, OpBitFieldInsert = 201, OpBitFieldSExtract = 202, OpBitFieldUExtract = 203, OpBitReverse = 204, OpBitCount = 205, OpDPdx = 207, OpDPdy = 208, OpFwidth = 209, OpDPdxFine = 210, OpDPdyFine = 211, OpFwidthFine = 212, OpDPdxCoarse = 213, OpDPdyCoarse = 214, OpFwidthCoarse = 215, OpEmitVertex = 218, OpEndPrimitive = 219, OpEmitStreamVertex = 220, OpEndStreamPrimitive = 221, OpControlBarrier = 224, OpMemoryBarrier = 225, OpAtomicLoad = 227, OpAtomicStore = 228, OpAtomicExchange = 229, OpAtomicCompareExchange = 230, OpAtomicCompareExchangeWeak = 231, OpAtomicIIncrement = 232, OpAtomicIDecrement = 233, OpAtomicIAdd = 234, OpAtomicISub = 235, OpAtomicSMin = 236, OpAtomicUMin = 237, OpAtomicSMax = 238, OpAtomicUMax = 239, OpAtomicAnd = 240, OpAtomicOr = 241, OpAtomicXor = 242, OpPhi = 245, OpLoopMerge = 246, OpSelectionMerge = 247, OpLabel = 248, OpBranch = 249, OpBranchConditional = 250, OpSwitch = 251, OpKill = 252, OpReturn = 253, OpReturnValue = 254, OpUnreachable = 255, OpLifetimeStart = 256, OpLifetimeStop = 257, OpGroupAsyncCopy = 259, OpGroupWaitEvents = 260, OpGroupAll = 261, OpGroupAny = 262, OpGroupBroadcast = 263, OpGroupIAdd = 264, OpGroupFAdd = 265, OpGroupFMin = 266, OpGroupUMin = 267, OpGroupSMin = 268, OpGroupFMax = 269, OpGroupUMax = 270, OpGroupSMax = 271, OpReadPipe = 274, OpWritePipe = 275, OpReservedReadPipe = 276, OpReservedWritePipe = 277, OpReserveReadPipePackets = 278, OpReserveWritePipePackets = 279, OpCommitReadPipe = 280, OpCommitWritePipe = 281, OpIsValidReserveId = 282, OpGetNumPipePackets = 283, OpGetMaxPipePackets = 284, OpGroupReserveReadPipePackets = 285, OpGroupReserveWritePipePackets = 286, OpGroupCommitReadPipe = 287, OpGroupCommitWritePipe = 288, OpEnqueueMarker = 291, OpEnqueueKernel = 292, OpGetKernelNDrangeSubGroupCount = 293, OpGetKernelNDrangeMaxSubGroupSize = 294, OpGetKernelWorkGroupSize = 295, OpGetKernelPreferredWorkGroupSizeMultiple = 296, OpRetainEvent = 297, OpReleaseEvent = 298, OpCreateUserEvent = 299, OpIsValidEvent = 300, OpSetUserEventStatus = 301, OpCaptureEventProfilingInfo = 302, OpGetDefaultQueue = 303, OpBuildNDRange = 304, OpImageSparseSampleImplicitLod = 305, OpImageSparseSampleExplicitLod = 306, OpImageSparseSampleDrefImplicitLod = 307, OpImageSparseSampleDrefExplicitLod = 308, OpImageSparseSampleProjImplicitLod = 309, OpImageSparseSampleProjExplicitLod = 310, OpImageSparseSampleProjDrefImplicitLod = 311, OpImageSparseSampleProjDrefExplicitLod = 312, OpImageSparseFetch = 313, OpImageSparseGather = 314, OpImageSparseDrefGather = 315, OpImageSparseTexelsResident = 316, OpNoLine = 317, OpAtomicFlagTestAndSet = 318, OpAtomicFlagClear = 319, OpImageSparseRead = 320, OpSizeOf = 321, OpTypePipeStorage = 322, OpConstantPipeStorage = 323, OpCreatePipeFromPipeStorage = 324, OpGetKernelLocalSizeForSubgroupCount = 325, OpGetKernelMaxNumSubgroups = 326, OpTypeNamedBarrier = 327, OpNamedBarrierInitialize = 328, OpMemoryNamedBarrier = 329, OpModuleProcessed = 330, OpSubgroupBallotKHR = 4421, OpSubgroupFirstInvocationKHR = 4422, OpSubgroupAllKHR = 4428, OpSubgroupAnyKHR = 4429, OpSubgroupAllEqualKHR = 4430, OpSubgroupReadInvocationKHR = 4432, OpMax = 0x7fffffff, }; // Overload operator| for mask bit combining inline ImageOperandsMask operator|(ImageOperandsMask a, ImageOperandsMask b) { return ImageOperandsMask(unsigned(a) | unsigned(b)); } inline FPFastMathModeMask operator|(FPFastMathModeMask a, FPFastMathModeMask b) { return FPFastMathModeMask(unsigned(a) | unsigned(b)); } inline SelectionControlMask operator|(SelectionControlMask a, SelectionControlMask b) { return SelectionControlMask(unsigned(a) | unsigned(b)); } inline LoopControlMask operator|(LoopControlMask a, LoopControlMask b) { return LoopControlMask(unsigned(a) | unsigned(b)); } inline FunctionControlMask operator|(FunctionControlMask a, FunctionControlMask b) { return FunctionControlMask(unsigned(a) | unsigned(b)); } inline MemorySemanticsMask operator|(MemorySemanticsMask a, MemorySemanticsMask b) { return MemorySemanticsMask(unsigned(a) | unsigned(b)); } inline MemoryAccessMask operator|(MemoryAccessMask a, MemoryAccessMask b) { return MemoryAccessMask(unsigned(a) | unsigned(b)); } inline KernelProfilingInfoMask operator|(KernelProfilingInfoMask a, KernelProfilingInfoMask b) { return KernelProfilingInfoMask(unsigned(a) | unsigned(b)); } } // end namespace spv #endif // #ifndef spirv_HPP ================================================ FILE: src/engine/renderer/vulkan/spirv.hpp11 ================================================ // Copyright (c) 2014-2017 The Khronos Group Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and/or associated documentation files (the "Materials"), // to deal in the Materials without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Materials, and to permit persons to whom the // Materials are furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Materials. // // MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS // STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND // HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ // // THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS // IN THE MATERIALS. // This header is automatically generated by the same tool that creates // the Binary Section of the SPIR-V specification. // Enumeration tokens for SPIR-V, in various styles: // C, C++, C++11, JSON, Lua, Python // // - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL // - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL // - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL // - Lua will use tables, e.g.: spv.SourceLanguage.GLSL // - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] // // Some tokens act like mask values, which can be OR'd together, // while others are mutually exclusive. The mask-like ones have // "Mask" in their name, and a parallel enum that has the shift // amount (1 << x) for each corresponding enumerant. #ifndef spirv_HPP #define spirv_HPP namespace spv { typedef unsigned int Id; #define SPV_VERSION 0x10100 #define SPV_REVISION 6 static const unsigned int MagicNumber = 0x07230203; static const unsigned int Version = 0x00010100; static const unsigned int Revision = 6; static const unsigned int OpCodeMask = 0xffff; static const unsigned int WordCountShift = 16; enum class SourceLanguage : unsigned { Unknown = 0, ESSL = 1, GLSL = 2, OpenCL_C = 3, OpenCL_CPP = 4, HLSL = 5, Max = 0x7fffffff, }; enum class ExecutionModel : unsigned { Vertex = 0, TessellationControl = 1, TessellationEvaluation = 2, Geometry = 3, Fragment = 4, GLCompute = 5, Kernel = 6, Max = 0x7fffffff, }; enum class AddressingModel : unsigned { Logical = 0, Physical32 = 1, Physical64 = 2, Max = 0x7fffffff, }; enum class MemoryModel : unsigned { Simple = 0, GLSL450 = 1, OpenCL = 2, Max = 0x7fffffff, }; enum class ExecutionMode : unsigned { Invocations = 0, SpacingEqual = 1, SpacingFractionalEven = 2, SpacingFractionalOdd = 3, VertexOrderCw = 4, VertexOrderCcw = 5, PixelCenterInteger = 6, OriginUpperLeft = 7, OriginLowerLeft = 8, EarlyFragmentTests = 9, PointMode = 10, Xfb = 11, DepthReplacing = 12, DepthGreater = 14, DepthLess = 15, DepthUnchanged = 16, LocalSize = 17, LocalSizeHint = 18, InputPoints = 19, InputLines = 20, InputLinesAdjacency = 21, Triangles = 22, InputTrianglesAdjacency = 23, Quads = 24, Isolines = 25, OutputVertices = 26, OutputPoints = 27, OutputLineStrip = 28, OutputTriangleStrip = 29, VecTypeHint = 30, ContractionOff = 31, Initializer = 33, Finalizer = 34, SubgroupSize = 35, SubgroupsPerWorkgroup = 36, Max = 0x7fffffff, }; enum class StorageClass : unsigned { UniformConstant = 0, Input = 1, Uniform = 2, Output = 3, Workgroup = 4, CrossWorkgroup = 5, Private = 6, Function = 7, Generic = 8, PushConstant = 9, AtomicCounter = 10, Image = 11, StorageBuffer = 12, Max = 0x7fffffff, }; enum class Dim : unsigned { Dim1D = 0, Dim2D = 1, Dim3D = 2, Cube = 3, Rect = 4, Buffer = 5, SubpassData = 6, Max = 0x7fffffff, }; enum class SamplerAddressingMode : unsigned { None = 0, ClampToEdge = 1, Clamp = 2, Repeat = 3, RepeatMirrored = 4, Max = 0x7fffffff, }; enum class SamplerFilterMode : unsigned { Nearest = 0, Linear = 1, Max = 0x7fffffff, }; enum class ImageFormat : unsigned { Unknown = 0, Rgba32f = 1, Rgba16f = 2, R32f = 3, Rgba8 = 4, Rgba8Snorm = 5, Rg32f = 6, Rg16f = 7, R11fG11fB10f = 8, R16f = 9, Rgba16 = 10, Rgb10A2 = 11, Rg16 = 12, Rg8 = 13, R16 = 14, R8 = 15, Rgba16Snorm = 16, Rg16Snorm = 17, Rg8Snorm = 18, R16Snorm = 19, R8Snorm = 20, Rgba32i = 21, Rgba16i = 22, Rgba8i = 23, R32i = 24, Rg32i = 25, Rg16i = 26, Rg8i = 27, R16i = 28, R8i = 29, Rgba32ui = 30, Rgba16ui = 31, Rgba8ui = 32, R32ui = 33, Rgb10a2ui = 34, Rg32ui = 35, Rg16ui = 36, Rg8ui = 37, R16ui = 38, R8ui = 39, Max = 0x7fffffff, }; enum class ImageChannelOrder : unsigned { R = 0, A = 1, RG = 2, RA = 3, RGB = 4, RGBA = 5, BGRA = 6, ARGB = 7, Intensity = 8, Luminance = 9, Rx = 10, RGx = 11, RGBx = 12, Depth = 13, DepthStencil = 14, sRGB = 15, sRGBx = 16, sRGBA = 17, sBGRA = 18, ABGR = 19, Max = 0x7fffffff, }; enum class ImageChannelDataType : unsigned { SnormInt8 = 0, SnormInt16 = 1, UnormInt8 = 2, UnormInt16 = 3, UnormShort565 = 4, UnormShort555 = 5, UnormInt101010 = 6, SignedInt8 = 7, SignedInt16 = 8, SignedInt32 = 9, UnsignedInt8 = 10, UnsignedInt16 = 11, UnsignedInt32 = 12, HalfFloat = 13, Float = 14, UnormInt24 = 15, UnormInt101010_2 = 16, Max = 0x7fffffff, }; enum class ImageOperandsShift : unsigned { Bias = 0, Lod = 1, Grad = 2, ConstOffset = 3, Offset = 4, ConstOffsets = 5, Sample = 6, MinLod = 7, Max = 0x7fffffff, }; enum class ImageOperandsMask : unsigned { MaskNone = 0, Bias = 0x00000001, Lod = 0x00000002, Grad = 0x00000004, ConstOffset = 0x00000008, Offset = 0x00000010, ConstOffsets = 0x00000020, Sample = 0x00000040, MinLod = 0x00000080, }; enum class FPFastMathModeShift : unsigned { NotNaN = 0, NotInf = 1, NSZ = 2, AllowRecip = 3, Fast = 4, Max = 0x7fffffff, }; enum class FPFastMathModeMask : unsigned { MaskNone = 0, NotNaN = 0x00000001, NotInf = 0x00000002, NSZ = 0x00000004, AllowRecip = 0x00000008, Fast = 0x00000010, }; enum class FPRoundingMode : unsigned { RTE = 0, RTZ = 1, RTP = 2, RTN = 3, Max = 0x7fffffff, }; enum class LinkageType : unsigned { Export = 0, Import = 1, Max = 0x7fffffff, }; enum class AccessQualifier : unsigned { ReadOnly = 0, WriteOnly = 1, ReadWrite = 2, Max = 0x7fffffff, }; enum class FunctionParameterAttribute : unsigned { Zext = 0, Sext = 1, ByVal = 2, Sret = 3, NoAlias = 4, NoCapture = 5, NoWrite = 6, NoReadWrite = 7, Max = 0x7fffffff, }; enum class Decoration : unsigned { RelaxedPrecision = 0, SpecId = 1, Block = 2, BufferBlock = 3, RowMajor = 4, ColMajor = 5, ArrayStride = 6, MatrixStride = 7, GLSLShared = 8, GLSLPacked = 9, CPacked = 10, BuiltIn = 11, NoPerspective = 13, Flat = 14, Patch = 15, Centroid = 16, Sample = 17, Invariant = 18, Restrict = 19, Aliased = 20, Volatile = 21, Constant = 22, Coherent = 23, NonWritable = 24, NonReadable = 25, Uniform = 26, SaturatedConversion = 28, Stream = 29, Location = 30, Component = 31, Index = 32, Binding = 33, DescriptorSet = 34, Offset = 35, XfbBuffer = 36, XfbStride = 37, FuncParamAttr = 38, FPRoundingMode = 39, FPFastMathMode = 40, LinkageAttributes = 41, NoContraction = 42, InputAttachmentIndex = 43, Alignment = 44, MaxByteOffset = 45, OverrideCoverageNV = 5248, PassthroughNV = 5250, ViewportRelativeNV = 5252, SecondaryViewportRelativeNV = 5256, Max = 0x7fffffff, }; enum class BuiltIn : unsigned { Position = 0, PointSize = 1, ClipDistance = 3, CullDistance = 4, VertexId = 5, InstanceId = 6, PrimitiveId = 7, InvocationId = 8, Layer = 9, ViewportIndex = 10, TessLevelOuter = 11, TessLevelInner = 12, TessCoord = 13, PatchVertices = 14, FragCoord = 15, PointCoord = 16, FrontFacing = 17, SampleId = 18, SamplePosition = 19, SampleMask = 20, FragDepth = 22, HelperInvocation = 23, NumWorkgroups = 24, WorkgroupSize = 25, WorkgroupId = 26, LocalInvocationId = 27, GlobalInvocationId = 28, LocalInvocationIndex = 29, WorkDim = 30, GlobalSize = 31, EnqueuedWorkgroupSize = 32, GlobalOffset = 33, GlobalLinearId = 34, SubgroupSize = 36, SubgroupMaxSize = 37, NumSubgroups = 38, NumEnqueuedSubgroups = 39, SubgroupId = 40, SubgroupLocalInvocationId = 41, VertexIndex = 42, InstanceIndex = 43, SubgroupEqMaskKHR = 4416, SubgroupGeMaskKHR = 4417, SubgroupGtMaskKHR = 4418, SubgroupLeMaskKHR = 4419, SubgroupLtMaskKHR = 4420, BaseVertex = 4424, BaseInstance = 4425, DrawIndex = 4426, DeviceIndex = 4438, ViewIndex = 4440, ViewportMaskNV = 5253, SecondaryPositionNV = 5257, SecondaryViewportMaskNV = 5258, PositionPerViewNV = 5261, ViewportMaskPerViewNV = 5262, Max = 0x7fffffff, }; enum class SelectionControlShift : unsigned { Flatten = 0, DontFlatten = 1, Max = 0x7fffffff, }; enum class SelectionControlMask : unsigned { MaskNone = 0, Flatten = 0x00000001, DontFlatten = 0x00000002, }; enum class LoopControlShift : unsigned { Unroll = 0, DontUnroll = 1, DependencyInfinite = 2, DependencyLength = 3, Max = 0x7fffffff, }; enum class LoopControlMask : unsigned { MaskNone = 0, Unroll = 0x00000001, DontUnroll = 0x00000002, DependencyInfinite = 0x00000004, DependencyLength = 0x00000008, }; enum class FunctionControlShift : unsigned { Inline = 0, DontInline = 1, Pure = 2, Const = 3, Max = 0x7fffffff, }; enum class FunctionControlMask : unsigned { MaskNone = 0, Inline = 0x00000001, DontInline = 0x00000002, Pure = 0x00000004, Const = 0x00000008, }; enum class MemorySemanticsShift : unsigned { Acquire = 1, Release = 2, AcquireRelease = 3, SequentiallyConsistent = 4, UniformMemory = 6, SubgroupMemory = 7, WorkgroupMemory = 8, CrossWorkgroupMemory = 9, AtomicCounterMemory = 10, ImageMemory = 11, Max = 0x7fffffff, }; enum class MemorySemanticsMask : unsigned { MaskNone = 0, Acquire = 0x00000002, Release = 0x00000004, AcquireRelease = 0x00000008, SequentiallyConsistent = 0x00000010, UniformMemory = 0x00000040, SubgroupMemory = 0x00000080, WorkgroupMemory = 0x00000100, CrossWorkgroupMemory = 0x00000200, AtomicCounterMemory = 0x00000400, ImageMemory = 0x00000800, }; enum class MemoryAccessShift : unsigned { Volatile = 0, Aligned = 1, Nontemporal = 2, Max = 0x7fffffff, }; enum class MemoryAccessMask : unsigned { MaskNone = 0, Volatile = 0x00000001, Aligned = 0x00000002, Nontemporal = 0x00000004, }; enum class Scope : unsigned { CrossDevice = 0, Device = 1, Workgroup = 2, Subgroup = 3, Invocation = 4, Max = 0x7fffffff, }; enum class GroupOperation : unsigned { Reduce = 0, InclusiveScan = 1, ExclusiveScan = 2, Max = 0x7fffffff, }; enum class KernelEnqueueFlags : unsigned { NoWait = 0, WaitKernel = 1, WaitWorkGroup = 2, Max = 0x7fffffff, }; enum class KernelProfilingInfoShift : unsigned { CmdExecTime = 0, Max = 0x7fffffff, }; enum class KernelProfilingInfoMask : unsigned { MaskNone = 0, CmdExecTime = 0x00000001, }; enum class Capability : unsigned { Matrix = 0, Shader = 1, Geometry = 2, Tessellation = 3, Addresses = 4, Linkage = 5, Kernel = 6, Vector16 = 7, Float16Buffer = 8, Float16 = 9, Float64 = 10, Int64 = 11, Int64Atomics = 12, ImageBasic = 13, ImageReadWrite = 14, ImageMipmap = 15, Pipes = 17, Groups = 18, DeviceEnqueue = 19, LiteralSampler = 20, AtomicStorage = 21, Int16 = 22, TessellationPointSize = 23, GeometryPointSize = 24, ImageGatherExtended = 25, StorageImageMultisample = 27, UniformBufferArrayDynamicIndexing = 28, SampledImageArrayDynamicIndexing = 29, StorageBufferArrayDynamicIndexing = 30, StorageImageArrayDynamicIndexing = 31, ClipDistance = 32, CullDistance = 33, ImageCubeArray = 34, SampleRateShading = 35, ImageRect = 36, SampledRect = 37, GenericPointer = 38, Int8 = 39, InputAttachment = 40, SparseResidency = 41, MinLod = 42, Sampled1D = 43, Image1D = 44, SampledCubeArray = 45, SampledBuffer = 46, ImageBuffer = 47, ImageMSArray = 48, StorageImageExtendedFormats = 49, ImageQuery = 50, DerivativeControl = 51, InterpolationFunction = 52, TransformFeedback = 53, GeometryStreams = 54, StorageImageReadWithoutFormat = 55, StorageImageWriteWithoutFormat = 56, MultiViewport = 57, SubgroupDispatch = 58, NamedBarrier = 59, PipeStorage = 60, SubgroupBallotKHR = 4423, DrawParameters = 4427, SubgroupVoteKHR = 4431, StorageBuffer16BitAccess = 4433, StorageUniformBufferBlock16 = 4433, StorageUniform16 = 4434, UniformAndStorageBuffer16BitAccess = 4434, StoragePushConstant16 = 4435, StorageInputOutput16 = 4436, DeviceGroup = 4437, MultiView = 4439, VariablePointersStorageBuffer = 4441, VariablePointers = 4442, SampleMaskOverrideCoverageNV = 5249, GeometryShaderPassthroughNV = 5251, ShaderViewportIndexLayerNV = 5254, ShaderViewportMaskNV = 5255, ShaderStereoViewNV = 5259, PerViewAttributesNV = 5260, Max = 0x7fffffff, }; enum class Op : unsigned { OpNop = 0, OpUndef = 1, OpSourceContinued = 2, OpSource = 3, OpSourceExtension = 4, OpName = 5, OpMemberName = 6, OpString = 7, OpLine = 8, OpExtension = 10, OpExtInstImport = 11, OpExtInst = 12, OpMemoryModel = 14, OpEntryPoint = 15, OpExecutionMode = 16, OpCapability = 17, OpTypeVoid = 19, OpTypeBool = 20, OpTypeInt = 21, OpTypeFloat = 22, OpTypeVector = 23, OpTypeMatrix = 24, OpTypeImage = 25, OpTypeSampler = 26, OpTypeSampledImage = 27, OpTypeArray = 28, OpTypeRuntimeArray = 29, OpTypeStruct = 30, OpTypeOpaque = 31, OpTypePointer = 32, OpTypeFunction = 33, OpTypeEvent = 34, OpTypeDeviceEvent = 35, OpTypeReserveId = 36, OpTypeQueue = 37, OpTypePipe = 38, OpTypeForwardPointer = 39, OpConstantTrue = 41, OpConstantFalse = 42, OpConstant = 43, OpConstantComposite = 44, OpConstantSampler = 45, OpConstantNull = 46, OpSpecConstantTrue = 48, OpSpecConstantFalse = 49, OpSpecConstant = 50, OpSpecConstantComposite = 51, OpSpecConstantOp = 52, OpFunction = 54, OpFunctionParameter = 55, OpFunctionEnd = 56, OpFunctionCall = 57, OpVariable = 59, OpImageTexelPointer = 60, OpLoad = 61, OpStore = 62, OpCopyMemory = 63, OpCopyMemorySized = 64, OpAccessChain = 65, OpInBoundsAccessChain = 66, OpPtrAccessChain = 67, OpArrayLength = 68, OpGenericPtrMemSemantics = 69, OpInBoundsPtrAccessChain = 70, OpDecorate = 71, OpMemberDecorate = 72, OpDecorationGroup = 73, OpGroupDecorate = 74, OpGroupMemberDecorate = 75, OpVectorExtractDynamic = 77, OpVectorInsertDynamic = 78, OpVectorShuffle = 79, OpCompositeConstruct = 80, OpCompositeExtract = 81, OpCompositeInsert = 82, OpCopyObject = 83, OpTranspose = 84, OpSampledImage = 86, OpImageSampleImplicitLod = 87, OpImageSampleExplicitLod = 88, OpImageSampleDrefImplicitLod = 89, OpImageSampleDrefExplicitLod = 90, OpImageSampleProjImplicitLod = 91, OpImageSampleProjExplicitLod = 92, OpImageSampleProjDrefImplicitLod = 93, OpImageSampleProjDrefExplicitLod = 94, OpImageFetch = 95, OpImageGather = 96, OpImageDrefGather = 97, OpImageRead = 98, OpImageWrite = 99, OpImage = 100, OpImageQueryFormat = 101, OpImageQueryOrder = 102, OpImageQuerySizeLod = 103, OpImageQuerySize = 104, OpImageQueryLod = 105, OpImageQueryLevels = 106, OpImageQuerySamples = 107, OpConvertFToU = 109, OpConvertFToS = 110, OpConvertSToF = 111, OpConvertUToF = 112, OpUConvert = 113, OpSConvert = 114, OpFConvert = 115, OpQuantizeToF16 = 116, OpConvertPtrToU = 117, OpSatConvertSToU = 118, OpSatConvertUToS = 119, OpConvertUToPtr = 120, OpPtrCastToGeneric = 121, OpGenericCastToPtr = 122, OpGenericCastToPtrExplicit = 123, OpBitcast = 124, OpSNegate = 126, OpFNegate = 127, OpIAdd = 128, OpFAdd = 129, OpISub = 130, OpFSub = 131, OpIMul = 132, OpFMul = 133, OpUDiv = 134, OpSDiv = 135, OpFDiv = 136, OpUMod = 137, OpSRem = 138, OpSMod = 139, OpFRem = 140, OpFMod = 141, OpVectorTimesScalar = 142, OpMatrixTimesScalar = 143, OpVectorTimesMatrix = 144, OpMatrixTimesVector = 145, OpMatrixTimesMatrix = 146, OpOuterProduct = 147, OpDot = 148, OpIAddCarry = 149, OpISubBorrow = 150, OpUMulExtended = 151, OpSMulExtended = 152, OpAny = 154, OpAll = 155, OpIsNan = 156, OpIsInf = 157, OpIsFinite = 158, OpIsNormal = 159, OpSignBitSet = 160, OpLessOrGreater = 161, OpOrdered = 162, OpUnordered = 163, OpLogicalEqual = 164, OpLogicalNotEqual = 165, OpLogicalOr = 166, OpLogicalAnd = 167, OpLogicalNot = 168, OpSelect = 169, OpIEqual = 170, OpINotEqual = 171, OpUGreaterThan = 172, OpSGreaterThan = 173, OpUGreaterThanEqual = 174, OpSGreaterThanEqual = 175, OpULessThan = 176, OpSLessThan = 177, OpULessThanEqual = 178, OpSLessThanEqual = 179, OpFOrdEqual = 180, OpFUnordEqual = 181, OpFOrdNotEqual = 182, OpFUnordNotEqual = 183, OpFOrdLessThan = 184, OpFUnordLessThan = 185, OpFOrdGreaterThan = 186, OpFUnordGreaterThan = 187, OpFOrdLessThanEqual = 188, OpFUnordLessThanEqual = 189, OpFOrdGreaterThanEqual = 190, OpFUnordGreaterThanEqual = 191, OpShiftRightLogical = 194, OpShiftRightArithmetic = 195, OpShiftLeftLogical = 196, OpBitwiseOr = 197, OpBitwiseXor = 198, OpBitwiseAnd = 199, OpNot = 200, OpBitFieldInsert = 201, OpBitFieldSExtract = 202, OpBitFieldUExtract = 203, OpBitReverse = 204, OpBitCount = 205, OpDPdx = 207, OpDPdy = 208, OpFwidth = 209, OpDPdxFine = 210, OpDPdyFine = 211, OpFwidthFine = 212, OpDPdxCoarse = 213, OpDPdyCoarse = 214, OpFwidthCoarse = 215, OpEmitVertex = 218, OpEndPrimitive = 219, OpEmitStreamVertex = 220, OpEndStreamPrimitive = 221, OpControlBarrier = 224, OpMemoryBarrier = 225, OpAtomicLoad = 227, OpAtomicStore = 228, OpAtomicExchange = 229, OpAtomicCompareExchange = 230, OpAtomicCompareExchangeWeak = 231, OpAtomicIIncrement = 232, OpAtomicIDecrement = 233, OpAtomicIAdd = 234, OpAtomicISub = 235, OpAtomicSMin = 236, OpAtomicUMin = 237, OpAtomicSMax = 238, OpAtomicUMax = 239, OpAtomicAnd = 240, OpAtomicOr = 241, OpAtomicXor = 242, OpPhi = 245, OpLoopMerge = 246, OpSelectionMerge = 247, OpLabel = 248, OpBranch = 249, OpBranchConditional = 250, OpSwitch = 251, OpKill = 252, OpReturn = 253, OpReturnValue = 254, OpUnreachable = 255, OpLifetimeStart = 256, OpLifetimeStop = 257, OpGroupAsyncCopy = 259, OpGroupWaitEvents = 260, OpGroupAll = 261, OpGroupAny = 262, OpGroupBroadcast = 263, OpGroupIAdd = 264, OpGroupFAdd = 265, OpGroupFMin = 266, OpGroupUMin = 267, OpGroupSMin = 268, OpGroupFMax = 269, OpGroupUMax = 270, OpGroupSMax = 271, OpReadPipe = 274, OpWritePipe = 275, OpReservedReadPipe = 276, OpReservedWritePipe = 277, OpReserveReadPipePackets = 278, OpReserveWritePipePackets = 279, OpCommitReadPipe = 280, OpCommitWritePipe = 281, OpIsValidReserveId = 282, OpGetNumPipePackets = 283, OpGetMaxPipePackets = 284, OpGroupReserveReadPipePackets = 285, OpGroupReserveWritePipePackets = 286, OpGroupCommitReadPipe = 287, OpGroupCommitWritePipe = 288, OpEnqueueMarker = 291, OpEnqueueKernel = 292, OpGetKernelNDrangeSubGroupCount = 293, OpGetKernelNDrangeMaxSubGroupSize = 294, OpGetKernelWorkGroupSize = 295, OpGetKernelPreferredWorkGroupSizeMultiple = 296, OpRetainEvent = 297, OpReleaseEvent = 298, OpCreateUserEvent = 299, OpIsValidEvent = 300, OpSetUserEventStatus = 301, OpCaptureEventProfilingInfo = 302, OpGetDefaultQueue = 303, OpBuildNDRange = 304, OpImageSparseSampleImplicitLod = 305, OpImageSparseSampleExplicitLod = 306, OpImageSparseSampleDrefImplicitLod = 307, OpImageSparseSampleDrefExplicitLod = 308, OpImageSparseSampleProjImplicitLod = 309, OpImageSparseSampleProjExplicitLod = 310, OpImageSparseSampleProjDrefImplicitLod = 311, OpImageSparseSampleProjDrefExplicitLod = 312, OpImageSparseFetch = 313, OpImageSparseGather = 314, OpImageSparseDrefGather = 315, OpImageSparseTexelsResident = 316, OpNoLine = 317, OpAtomicFlagTestAndSet = 318, OpAtomicFlagClear = 319, OpImageSparseRead = 320, OpSizeOf = 321, OpTypePipeStorage = 322, OpConstantPipeStorage = 323, OpCreatePipeFromPipeStorage = 324, OpGetKernelLocalSizeForSubgroupCount = 325, OpGetKernelMaxNumSubgroups = 326, OpTypeNamedBarrier = 327, OpNamedBarrierInitialize = 328, OpMemoryNamedBarrier = 329, OpModuleProcessed = 330, OpSubgroupBallotKHR = 4421, OpSubgroupFirstInvocationKHR = 4422, OpSubgroupAllKHR = 4428, OpSubgroupAnyKHR = 4429, OpSubgroupAllEqualKHR = 4430, OpSubgroupReadInvocationKHR = 4432, Max = 0x7fffffff, }; // Overload operator| for mask bit combining inline ImageOperandsMask operator|(ImageOperandsMask a, ImageOperandsMask b) { return ImageOperandsMask(unsigned(a) | unsigned(b)); } inline FPFastMathModeMask operator|(FPFastMathModeMask a, FPFastMathModeMask b) { return FPFastMathModeMask(unsigned(a) | unsigned(b)); } inline SelectionControlMask operator|(SelectionControlMask a, SelectionControlMask b) { return SelectionControlMask(unsigned(a) | unsigned(b)); } inline LoopControlMask operator|(LoopControlMask a, LoopControlMask b) { return LoopControlMask(unsigned(a) | unsigned(b)); } inline FunctionControlMask operator|(FunctionControlMask a, FunctionControlMask b) { return FunctionControlMask(unsigned(a) | unsigned(b)); } inline MemorySemanticsMask operator|(MemorySemanticsMask a, MemorySemanticsMask b) { return MemorySemanticsMask(unsigned(a) | unsigned(b)); } inline MemoryAccessMask operator|(MemoryAccessMask a, MemoryAccessMask b) { return MemoryAccessMask(unsigned(a) | unsigned(b)); } inline KernelProfilingInfoMask operator|(KernelProfilingInfoMask a, KernelProfilingInfoMask b) { return KernelProfilingInfoMask(unsigned(a) | unsigned(b)); } } // end namespace spv #endif // #ifndef spirv_HPP ================================================ FILE: src/engine/renderer/vulkan/spirv.json ================================================ { "spv": { "meta": { "Comment": [ [ "Copyright (c) 2014-2017 The Khronos Group Inc.", "", "Permission is hereby granted, free of charge, to any person obtaining a copy", "of this software and/or associated documentation files (the \"Materials\"),", "to deal in the Materials without restriction, including without limitation", "the rights to use, copy, modify, merge, publish, distribute, sublicense,", "and/or sell copies of the Materials, and to permit persons to whom the", "Materials are furnished to do so, subject to the following conditions:", "", "The above copyright notice and this permission notice shall be included in", "all copies or substantial portions of the Materials.", "", "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", "", "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", "IN THE MATERIALS." ], [ "This header is automatically generated by the same tool that creates", "the Binary Section of the SPIR-V specification." ], [ "Enumeration tokens for SPIR-V, in various styles:", " C, C++, C++11, JSON, Lua, Python", "", "- C will have tokens with a \"Spv\" prefix, e.g.: SpvSourceLanguageGLSL", "- C++ will have tokens in the \"spv\" name space, e.g.: spv::SourceLanguageGLSL", "- C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL", "- Lua will use tables, e.g.: spv.SourceLanguage.GLSL", "- Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL']", "", "Some tokens act like mask values, which can be OR'd together,", "while others are mutually exclusive. The mask-like ones have", "\"Mask\" in their name, and a parallel enum that has the shift", "amount (1 << x) for each corresponding enumerant." ] ], "MagicNumber": 119734787, "Version": 65792, "Revision": 6, "OpCodeMask": 65535, "WordCountShift": 16 }, "enum": [ { "Name": "SourceLanguage", "Type": "Value", "Values": { "Unknown": 0, "ESSL": 1, "GLSL": 2, "OpenCL_C": 3, "OpenCL_CPP": 4, "HLSL": 5 } }, { "Name": "ExecutionModel", "Type": "Value", "Values": { "Vertex": 0, "TessellationControl": 1, "TessellationEvaluation": 2, "Geometry": 3, "Fragment": 4, "GLCompute": 5, "Kernel": 6 } }, { "Name": "AddressingModel", "Type": "Value", "Values": { "Logical": 0, "Physical32": 1, "Physical64": 2 } }, { "Name": "MemoryModel", "Type": "Value", "Values": { "Simple": 0, "GLSL450": 1, "OpenCL": 2 } }, { "Name": "ExecutionMode", "Type": "Value", "Values": { "Invocations": 0, "SpacingEqual": 1, "SpacingFractionalEven": 2, "SpacingFractionalOdd": 3, "VertexOrderCw": 4, "VertexOrderCcw": 5, "PixelCenterInteger": 6, "OriginUpperLeft": 7, "OriginLowerLeft": 8, "EarlyFragmentTests": 9, "PointMode": 10, "Xfb": 11, "DepthReplacing": 12, "DepthGreater": 14, "DepthLess": 15, "DepthUnchanged": 16, "LocalSize": 17, "LocalSizeHint": 18, "InputPoints": 19, "InputLines": 20, "InputLinesAdjacency": 21, "Triangles": 22, "InputTrianglesAdjacency": 23, "Quads": 24, "Isolines": 25, "OutputVertices": 26, "OutputPoints": 27, "OutputLineStrip": 28, "OutputTriangleStrip": 29, "VecTypeHint": 30, "ContractionOff": 31, "Initializer": 33, "Finalizer": 34, "SubgroupSize": 35, "SubgroupsPerWorkgroup": 36 } }, { "Name": "StorageClass", "Type": "Value", "Values": { "UniformConstant": 0, "Input": 1, "Uniform": 2, "Output": 3, "Workgroup": 4, "CrossWorkgroup": 5, "Private": 6, "Function": 7, "Generic": 8, "PushConstant": 9, "AtomicCounter": 10, "Image": 11, "StorageBuffer": 12 } }, { "Name": "Dim", "Type": "Value", "Values": { "Dim1D": 0, "Dim2D": 1, "Dim3D": 2, "Cube": 3, "Rect": 4, "Buffer": 5, "SubpassData": 6 } }, { "Name": "SamplerAddressingMode", "Type": "Value", "Values": { "None": 0, "ClampToEdge": 1, "Clamp": 2, "Repeat": 3, "RepeatMirrored": 4 } }, { "Name": "SamplerFilterMode", "Type": "Value", "Values": { "Nearest": 0, "Linear": 1 } }, { "Name": "ImageFormat", "Type": "Value", "Values": { "Unknown": 0, "Rgba32f": 1, "Rgba16f": 2, "R32f": 3, "Rgba8": 4, "Rgba8Snorm": 5, "Rg32f": 6, "Rg16f": 7, "R11fG11fB10f": 8, "R16f": 9, "Rgba16": 10, "Rgb10A2": 11, "Rg16": 12, "Rg8": 13, "R16": 14, "R8": 15, "Rgba16Snorm": 16, "Rg16Snorm": 17, "Rg8Snorm": 18, "R16Snorm": 19, "R8Snorm": 20, "Rgba32i": 21, "Rgba16i": 22, "Rgba8i": 23, "R32i": 24, "Rg32i": 25, "Rg16i": 26, "Rg8i": 27, "R16i": 28, "R8i": 29, "Rgba32ui": 30, "Rgba16ui": 31, "Rgba8ui": 32, "R32ui": 33, "Rgb10a2ui": 34, "Rg32ui": 35, "Rg16ui": 36, "Rg8ui": 37, "R16ui": 38, "R8ui": 39 } }, { "Name": "ImageChannelOrder", "Type": "Value", "Values": { "R": 0, "A": 1, "RG": 2, "RA": 3, "RGB": 4, "RGBA": 5, "BGRA": 6, "ARGB": 7, "Intensity": 8, "Luminance": 9, "Rx": 10, "RGx": 11, "RGBx": 12, "Depth": 13, "DepthStencil": 14, "sRGB": 15, "sRGBx": 16, "sRGBA": 17, "sBGRA": 18, "ABGR": 19 } }, { "Name": "ImageChannelDataType", "Type": "Value", "Values": { "SnormInt8": 0, "SnormInt16": 1, "UnormInt8": 2, "UnormInt16": 3, "UnormShort565": 4, "UnormShort555": 5, "UnormInt101010": 6, "SignedInt8": 7, "SignedInt16": 8, "SignedInt32": 9, "UnsignedInt8": 10, "UnsignedInt16": 11, "UnsignedInt32": 12, "HalfFloat": 13, "Float": 14, "UnormInt24": 15, "UnormInt101010_2": 16 } }, { "Name": "ImageOperands", "Type": "Bit", "Values": { "Bias": 0, "Lod": 1, "Grad": 2, "ConstOffset": 3, "Offset": 4, "ConstOffsets": 5, "Sample": 6, "MinLod": 7 } }, { "Name": "FPFastMathMode", "Type": "Bit", "Values": { "NotNaN": 0, "NotInf": 1, "NSZ": 2, "AllowRecip": 3, "Fast": 4 } }, { "Name": "FPRoundingMode", "Type": "Value", "Values": { "RTE": 0, "RTZ": 1, "RTP": 2, "RTN": 3 } }, { "Name": "LinkageType", "Type": "Value", "Values": { "Export": 0, "Import": 1 } }, { "Name": "AccessQualifier", "Type": "Value", "Values": { "ReadOnly": 0, "WriteOnly": 1, "ReadWrite": 2 } }, { "Name": "FunctionParameterAttribute", "Type": "Value", "Values": { "Zext": 0, "Sext": 1, "ByVal": 2, "Sret": 3, "NoAlias": 4, "NoCapture": 5, "NoWrite": 6, "NoReadWrite": 7 } }, { "Name": "Decoration", "Type": "Value", "Values": { "RelaxedPrecision": 0, "SpecId": 1, "Block": 2, "BufferBlock": 3, "RowMajor": 4, "ColMajor": 5, "ArrayStride": 6, "MatrixStride": 7, "GLSLShared": 8, "GLSLPacked": 9, "CPacked": 10, "BuiltIn": 11, "NoPerspective": 13, "Flat": 14, "Patch": 15, "Centroid": 16, "Sample": 17, "Invariant": 18, "Restrict": 19, "Aliased": 20, "Volatile": 21, "Constant": 22, "Coherent": 23, "NonWritable": 24, "NonReadable": 25, "Uniform": 26, "SaturatedConversion": 28, "Stream": 29, "Location": 30, "Component": 31, "Index": 32, "Binding": 33, "DescriptorSet": 34, "Offset": 35, "XfbBuffer": 36, "XfbStride": 37, "FuncParamAttr": 38, "FPRoundingMode": 39, "FPFastMathMode": 40, "LinkageAttributes": 41, "NoContraction": 42, "InputAttachmentIndex": 43, "Alignment": 44, "MaxByteOffset": 45, "OverrideCoverageNV": 5248, "PassthroughNV": 5250, "ViewportRelativeNV": 5252, "SecondaryViewportRelativeNV": 5256 } }, { "Name": "BuiltIn", "Type": "Value", "Values": { "Position": 0, "PointSize": 1, "ClipDistance": 3, "CullDistance": 4, "VertexId": 5, "InstanceId": 6, "PrimitiveId": 7, "InvocationId": 8, "Layer": 9, "ViewportIndex": 10, "TessLevelOuter": 11, "TessLevelInner": 12, "TessCoord": 13, "PatchVertices": 14, "FragCoord": 15, "PointCoord": 16, "FrontFacing": 17, "SampleId": 18, "SamplePosition": 19, "SampleMask": 20, "FragDepth": 22, "HelperInvocation": 23, "NumWorkgroups": 24, "WorkgroupSize": 25, "WorkgroupId": 26, "LocalInvocationId": 27, "GlobalInvocationId": 28, "LocalInvocationIndex": 29, "WorkDim": 30, "GlobalSize": 31, "EnqueuedWorkgroupSize": 32, "GlobalOffset": 33, "GlobalLinearId": 34, "SubgroupSize": 36, "SubgroupMaxSize": 37, "NumSubgroups": 38, "NumEnqueuedSubgroups": 39, "SubgroupId": 40, "SubgroupLocalInvocationId": 41, "VertexIndex": 42, "InstanceIndex": 43, "SubgroupEqMaskKHR": 4416, "SubgroupGeMaskKHR": 4417, "SubgroupGtMaskKHR": 4418, "SubgroupLeMaskKHR": 4419, "SubgroupLtMaskKHR": 4420, "BaseVertex": 4424, "BaseInstance": 4425, "DrawIndex": 4426, "DeviceIndex": 4438, "ViewIndex": 4440, "ViewportMaskNV": 5253, "SecondaryPositionNV": 5257, "SecondaryViewportMaskNV": 5258, "PositionPerViewNV": 5261, "ViewportMaskPerViewNV": 5262 } }, { "Name": "SelectionControl", "Type": "Bit", "Values": { "Flatten": 0, "DontFlatten": 1 } }, { "Name": "LoopControl", "Type": "Bit", "Values": { "Unroll": 0, "DontUnroll": 1, "DependencyInfinite": 2, "DependencyLength": 3 } }, { "Name": "FunctionControl", "Type": "Bit", "Values": { "Inline": 0, "DontInline": 1, "Pure": 2, "Const": 3 } }, { "Name": "MemorySemantics", "Type": "Bit", "Values": { "Acquire": 1, "Release": 2, "AcquireRelease": 3, "SequentiallyConsistent": 4, "UniformMemory": 6, "SubgroupMemory": 7, "WorkgroupMemory": 8, "CrossWorkgroupMemory": 9, "AtomicCounterMemory": 10, "ImageMemory": 11 } }, { "Name": "MemoryAccess", "Type": "Bit", "Values": { "Volatile": 0, "Aligned": 1, "Nontemporal": 2 } }, { "Name": "Scope", "Type": "Value", "Values": { "CrossDevice": 0, "Device": 1, "Workgroup": 2, "Subgroup": 3, "Invocation": 4 } }, { "Name": "GroupOperation", "Type": "Value", "Values": { "Reduce": 0, "InclusiveScan": 1, "ExclusiveScan": 2 } }, { "Name": "KernelEnqueueFlags", "Type": "Value", "Values": { "NoWait": 0, "WaitKernel": 1, "WaitWorkGroup": 2 } }, { "Name": "KernelProfilingInfo", "Type": "Bit", "Values": { "CmdExecTime": 0 } }, { "Name": "Capability", "Type": "Value", "Values": { "Matrix": 0, "Shader": 1, "Geometry": 2, "Tessellation": 3, "Addresses": 4, "Linkage": 5, "Kernel": 6, "Vector16": 7, "Float16Buffer": 8, "Float16": 9, "Float64": 10, "Int64": 11, "Int64Atomics": 12, "ImageBasic": 13, "ImageReadWrite": 14, "ImageMipmap": 15, "Pipes": 17, "Groups": 18, "DeviceEnqueue": 19, "LiteralSampler": 20, "AtomicStorage": 21, "Int16": 22, "TessellationPointSize": 23, "GeometryPointSize": 24, "ImageGatherExtended": 25, "StorageImageMultisample": 27, "UniformBufferArrayDynamicIndexing": 28, "SampledImageArrayDynamicIndexing": 29, "StorageBufferArrayDynamicIndexing": 30, "StorageImageArrayDynamicIndexing": 31, "ClipDistance": 32, "CullDistance": 33, "ImageCubeArray": 34, "SampleRateShading": 35, "ImageRect": 36, "SampledRect": 37, "GenericPointer": 38, "Int8": 39, "InputAttachment": 40, "SparseResidency": 41, "MinLod": 42, "Sampled1D": 43, "Image1D": 44, "SampledCubeArray": 45, "SampledBuffer": 46, "ImageBuffer": 47, "ImageMSArray": 48, "StorageImageExtendedFormats": 49, "ImageQuery": 50, "DerivativeControl": 51, "InterpolationFunction": 52, "TransformFeedback": 53, "GeometryStreams": 54, "StorageImageReadWithoutFormat": 55, "StorageImageWriteWithoutFormat": 56, "MultiViewport": 57, "SubgroupDispatch": 58, "NamedBarrier": 59, "PipeStorage": 60, "SubgroupBallotKHR": 4423, "DrawParameters": 4427, "SubgroupVoteKHR": 4431, "StorageBuffer16BitAccess": 4433, "StorageUniformBufferBlock16": 4433, "StorageUniform16": 4434, "UniformAndStorageBuffer16BitAccess": 4434, "StoragePushConstant16": 4435, "StorageInputOutput16": 4436, "DeviceGroup": 4437, "MultiView": 4439, "VariablePointersStorageBuffer": 4441, "VariablePointers": 4442, "SampleMaskOverrideCoverageNV": 5249, "GeometryShaderPassthroughNV": 5251, "ShaderViewportIndexLayerNV": 5254, "ShaderViewportMaskNV": 5255, "ShaderStereoViewNV": 5259, "PerViewAttributesNV": 5260 } }, { "Name": "Op", "Type": "Value", "Values": { "OpNop": 0, "OpUndef": 1, "OpSourceContinued": 2, "OpSource": 3, "OpSourceExtension": 4, "OpName": 5, "OpMemberName": 6, "OpString": 7, "OpLine": 8, "OpExtension": 10, "OpExtInstImport": 11, "OpExtInst": 12, "OpMemoryModel": 14, "OpEntryPoint": 15, "OpExecutionMode": 16, "OpCapability": 17, "OpTypeVoid": 19, "OpTypeBool": 20, "OpTypeInt": 21, "OpTypeFloat": 22, "OpTypeVector": 23, "OpTypeMatrix": 24, "OpTypeImage": 25, "OpTypeSampler": 26, "OpTypeSampledImage": 27, "OpTypeArray": 28, "OpTypeRuntimeArray": 29, "OpTypeStruct": 30, "OpTypeOpaque": 31, "OpTypePointer": 32, "OpTypeFunction": 33, "OpTypeEvent": 34, "OpTypeDeviceEvent": 35, "OpTypeReserveId": 36, "OpTypeQueue": 37, "OpTypePipe": 38, "OpTypeForwardPointer": 39, "OpConstantTrue": 41, "OpConstantFalse": 42, "OpConstant": 43, "OpConstantComposite": 44, "OpConstantSampler": 45, "OpConstantNull": 46, "OpSpecConstantTrue": 48, "OpSpecConstantFalse": 49, "OpSpecConstant": 50, "OpSpecConstantComposite": 51, "OpSpecConstantOp": 52, "OpFunction": 54, "OpFunctionParameter": 55, "OpFunctionEnd": 56, "OpFunctionCall": 57, "OpVariable": 59, "OpImageTexelPointer": 60, "OpLoad": 61, "OpStore": 62, "OpCopyMemory": 63, "OpCopyMemorySized": 64, "OpAccessChain": 65, "OpInBoundsAccessChain": 66, "OpPtrAccessChain": 67, "OpArrayLength": 68, "OpGenericPtrMemSemantics": 69, "OpInBoundsPtrAccessChain": 70, "OpDecorate": 71, "OpMemberDecorate": 72, "OpDecorationGroup": 73, "OpGroupDecorate": 74, "OpGroupMemberDecorate": 75, "OpVectorExtractDynamic": 77, "OpVectorInsertDynamic": 78, "OpVectorShuffle": 79, "OpCompositeConstruct": 80, "OpCompositeExtract": 81, "OpCompositeInsert": 82, "OpCopyObject": 83, "OpTranspose": 84, "OpSampledImage": 86, "OpImageSampleImplicitLod": 87, "OpImageSampleExplicitLod": 88, "OpImageSampleDrefImplicitLod": 89, "OpImageSampleDrefExplicitLod": 90, "OpImageSampleProjImplicitLod": 91, "OpImageSampleProjExplicitLod": 92, "OpImageSampleProjDrefImplicitLod": 93, "OpImageSampleProjDrefExplicitLod": 94, "OpImageFetch": 95, "OpImageGather": 96, "OpImageDrefGather": 97, "OpImageRead": 98, "OpImageWrite": 99, "OpImage": 100, "OpImageQueryFormat": 101, "OpImageQueryOrder": 102, "OpImageQuerySizeLod": 103, "OpImageQuerySize": 104, "OpImageQueryLod": 105, "OpImageQueryLevels": 106, "OpImageQuerySamples": 107, "OpConvertFToU": 109, "OpConvertFToS": 110, "OpConvertSToF": 111, "OpConvertUToF": 112, "OpUConvert": 113, "OpSConvert": 114, "OpFConvert": 115, "OpQuantizeToF16": 116, "OpConvertPtrToU": 117, "OpSatConvertSToU": 118, "OpSatConvertUToS": 119, "OpConvertUToPtr": 120, "OpPtrCastToGeneric": 121, "OpGenericCastToPtr": 122, "OpGenericCastToPtrExplicit": 123, "OpBitcast": 124, "OpSNegate": 126, "OpFNegate": 127, "OpIAdd": 128, "OpFAdd": 129, "OpISub": 130, "OpFSub": 131, "OpIMul": 132, "OpFMul": 133, "OpUDiv": 134, "OpSDiv": 135, "OpFDiv": 136, "OpUMod": 137, "OpSRem": 138, "OpSMod": 139, "OpFRem": 140, "OpFMod": 141, "OpVectorTimesScalar": 142, "OpMatrixTimesScalar": 143, "OpVectorTimesMatrix": 144, "OpMatrixTimesVector": 145, "OpMatrixTimesMatrix": 146, "OpOuterProduct": 147, "OpDot": 148, "OpIAddCarry": 149, "OpISubBorrow": 150, "OpUMulExtended": 151, "OpSMulExtended": 152, "OpAny": 154, "OpAll": 155, "OpIsNan": 156, "OpIsInf": 157, "OpIsFinite": 158, "OpIsNormal": 159, "OpSignBitSet": 160, "OpLessOrGreater": 161, "OpOrdered": 162, "OpUnordered": 163, "OpLogicalEqual": 164, "OpLogicalNotEqual": 165, "OpLogicalOr": 166, "OpLogicalAnd": 167, "OpLogicalNot": 168, "OpSelect": 169, "OpIEqual": 170, "OpINotEqual": 171, "OpUGreaterThan": 172, "OpSGreaterThan": 173, "OpUGreaterThanEqual": 174, "OpSGreaterThanEqual": 175, "OpULessThan": 176, "OpSLessThan": 177, "OpULessThanEqual": 178, "OpSLessThanEqual": 179, "OpFOrdEqual": 180, "OpFUnordEqual": 181, "OpFOrdNotEqual": 182, "OpFUnordNotEqual": 183, "OpFOrdLessThan": 184, "OpFUnordLessThan": 185, "OpFOrdGreaterThan": 186, "OpFUnordGreaterThan": 187, "OpFOrdLessThanEqual": 188, "OpFUnordLessThanEqual": 189, "OpFOrdGreaterThanEqual": 190, "OpFUnordGreaterThanEqual": 191, "OpShiftRightLogical": 194, "OpShiftRightArithmetic": 195, "OpShiftLeftLogical": 196, "OpBitwiseOr": 197, "OpBitwiseXor": 198, "OpBitwiseAnd": 199, "OpNot": 200, "OpBitFieldInsert": 201, "OpBitFieldSExtract": 202, "OpBitFieldUExtract": 203, "OpBitReverse": 204, "OpBitCount": 205, "OpDPdx": 207, "OpDPdy": 208, "OpFwidth": 209, "OpDPdxFine": 210, "OpDPdyFine": 211, "OpFwidthFine": 212, "OpDPdxCoarse": 213, "OpDPdyCoarse": 214, "OpFwidthCoarse": 215, "OpEmitVertex": 218, "OpEndPrimitive": 219, "OpEmitStreamVertex": 220, "OpEndStreamPrimitive": 221, "OpControlBarrier": 224, "OpMemoryBarrier": 225, "OpAtomicLoad": 227, "OpAtomicStore": 228, "OpAtomicExchange": 229, "OpAtomicCompareExchange": 230, "OpAtomicCompareExchangeWeak": 231, "OpAtomicIIncrement": 232, "OpAtomicIDecrement": 233, "OpAtomicIAdd": 234, "OpAtomicISub": 235, "OpAtomicSMin": 236, "OpAtomicUMin": 237, "OpAtomicSMax": 238, "OpAtomicUMax": 239, "OpAtomicAnd": 240, "OpAtomicOr": 241, "OpAtomicXor": 242, "OpPhi": 245, "OpLoopMerge": 246, "OpSelectionMerge": 247, "OpLabel": 248, "OpBranch": 249, "OpBranchConditional": 250, "OpSwitch": 251, "OpKill": 252, "OpReturn": 253, "OpReturnValue": 254, "OpUnreachable": 255, "OpLifetimeStart": 256, "OpLifetimeStop": 257, "OpGroupAsyncCopy": 259, "OpGroupWaitEvents": 260, "OpGroupAll": 261, "OpGroupAny": 262, "OpGroupBroadcast": 263, "OpGroupIAdd": 264, "OpGroupFAdd": 265, "OpGroupFMin": 266, "OpGroupUMin": 267, "OpGroupSMin": 268, "OpGroupFMax": 269, "OpGroupUMax": 270, "OpGroupSMax": 271, "OpReadPipe": 274, "OpWritePipe": 275, "OpReservedReadPipe": 276, "OpReservedWritePipe": 277, "OpReserveReadPipePackets": 278, "OpReserveWritePipePackets": 279, "OpCommitReadPipe": 280, "OpCommitWritePipe": 281, "OpIsValidReserveId": 282, "OpGetNumPipePackets": 283, "OpGetMaxPipePackets": 284, "OpGroupReserveReadPipePackets": 285, "OpGroupReserveWritePipePackets": 286, "OpGroupCommitReadPipe": 287, "OpGroupCommitWritePipe": 288, "OpEnqueueMarker": 291, "OpEnqueueKernel": 292, "OpGetKernelNDrangeSubGroupCount": 293, "OpGetKernelNDrangeMaxSubGroupSize": 294, "OpGetKernelWorkGroupSize": 295, "OpGetKernelPreferredWorkGroupSizeMultiple": 296, "OpRetainEvent": 297, "OpReleaseEvent": 298, "OpCreateUserEvent": 299, "OpIsValidEvent": 300, "OpSetUserEventStatus": 301, "OpCaptureEventProfilingInfo": 302, "OpGetDefaultQueue": 303, "OpBuildNDRange": 304, "OpImageSparseSampleImplicitLod": 305, "OpImageSparseSampleExplicitLod": 306, "OpImageSparseSampleDrefImplicitLod": 307, "OpImageSparseSampleDrefExplicitLod": 308, "OpImageSparseSampleProjImplicitLod": 309, "OpImageSparseSampleProjExplicitLod": 310, "OpImageSparseSampleProjDrefImplicitLod": 311, "OpImageSparseSampleProjDrefExplicitLod": 312, "OpImageSparseFetch": 313, "OpImageSparseGather": 314, "OpImageSparseDrefGather": 315, "OpImageSparseTexelsResident": 316, "OpNoLine": 317, "OpAtomicFlagTestAndSet": 318, "OpAtomicFlagClear": 319, "OpImageSparseRead": 320, "OpSizeOf": 321, "OpTypePipeStorage": 322, "OpConstantPipeStorage": 323, "OpCreatePipeFromPipeStorage": 324, "OpGetKernelLocalSizeForSubgroupCount": 325, "OpGetKernelMaxNumSubgroups": 326, "OpTypeNamedBarrier": 327, "OpNamedBarrierInitialize": 328, "OpMemoryNamedBarrier": 329, "OpModuleProcessed": 330, "OpSubgroupBallotKHR": 4421, "OpSubgroupFirstInvocationKHR": 4422, "OpSubgroupAllKHR": 4428, "OpSubgroupAnyKHR": 4429, "OpSubgroupAllEqualKHR": 4430, "OpSubgroupReadInvocationKHR": 4432 } } ] } } ================================================ FILE: src/engine/renderer/vulkan/spirv.lua ================================================ -- Copyright (c) 2014-2017 The Khronos Group Inc. -- -- Permission is hereby granted, free of charge, to any person obtaining a copy -- of this software and/or associated documentation files (the "Materials"), -- to deal in the Materials without restriction, including without limitation -- the rights to use, copy, modify, merge, publish, distribute, sublicense, -- and/or sell copies of the Materials, and to permit persons to whom the -- Materials are furnished to do so, subject to the following conditions: -- -- The above copyright notice and this permission notice shall be included in -- all copies or substantial portions of the Materials. -- -- MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS -- STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND -- HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ -- -- THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -- FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS -- IN THE MATERIALS. -- This header is automatically generated by the same tool that creates -- the Binary Section of the SPIR-V specification. -- Enumeration tokens for SPIR-V, in various styles: -- C, C++, C++11, JSON, Lua, Python -- -- - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL -- - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL -- - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL -- - Lua will use tables, e.g.: spv.SourceLanguage.GLSL -- - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] -- -- Some tokens act like mask values, which can be OR'd together, -- while others are mutually exclusive. The mask-like ones have -- "Mask" in their name, and a parallel enum that has the shift -- amount (1 << x) for each corresponding enumerant. spv = { MagicNumber = 0x07230203, Version = 0x00010100, Revision = 6, OpCodeMask = 0xffff, WordCountShift = 16, SourceLanguage = { Unknown = 0, ESSL = 1, GLSL = 2, OpenCL_C = 3, OpenCL_CPP = 4, HLSL = 5, }, ExecutionModel = { Vertex = 0, TessellationControl = 1, TessellationEvaluation = 2, Geometry = 3, Fragment = 4, GLCompute = 5, Kernel = 6, }, AddressingModel = { Logical = 0, Physical32 = 1, Physical64 = 2, }, MemoryModel = { Simple = 0, GLSL450 = 1, OpenCL = 2, }, ExecutionMode = { Invocations = 0, SpacingEqual = 1, SpacingFractionalEven = 2, SpacingFractionalOdd = 3, VertexOrderCw = 4, VertexOrderCcw = 5, PixelCenterInteger = 6, OriginUpperLeft = 7, OriginLowerLeft = 8, EarlyFragmentTests = 9, PointMode = 10, Xfb = 11, DepthReplacing = 12, DepthGreater = 14, DepthLess = 15, DepthUnchanged = 16, LocalSize = 17, LocalSizeHint = 18, InputPoints = 19, InputLines = 20, InputLinesAdjacency = 21, Triangles = 22, InputTrianglesAdjacency = 23, Quads = 24, Isolines = 25, OutputVertices = 26, OutputPoints = 27, OutputLineStrip = 28, OutputTriangleStrip = 29, VecTypeHint = 30, ContractionOff = 31, Initializer = 33, Finalizer = 34, SubgroupSize = 35, SubgroupsPerWorkgroup = 36, }, StorageClass = { UniformConstant = 0, Input = 1, Uniform = 2, Output = 3, Workgroup = 4, CrossWorkgroup = 5, Private = 6, Function = 7, Generic = 8, PushConstant = 9, AtomicCounter = 10, Image = 11, StorageBuffer = 12, }, Dim = { Dim1D = 0, Dim2D = 1, Dim3D = 2, Cube = 3, Rect = 4, Buffer = 5, SubpassData = 6, }, SamplerAddressingMode = { None = 0, ClampToEdge = 1, Clamp = 2, Repeat = 3, RepeatMirrored = 4, }, SamplerFilterMode = { Nearest = 0, Linear = 1, }, ImageFormat = { Unknown = 0, Rgba32f = 1, Rgba16f = 2, R32f = 3, Rgba8 = 4, Rgba8Snorm = 5, Rg32f = 6, Rg16f = 7, R11fG11fB10f = 8, R16f = 9, Rgba16 = 10, Rgb10A2 = 11, Rg16 = 12, Rg8 = 13, R16 = 14, R8 = 15, Rgba16Snorm = 16, Rg16Snorm = 17, Rg8Snorm = 18, R16Snorm = 19, R8Snorm = 20, Rgba32i = 21, Rgba16i = 22, Rgba8i = 23, R32i = 24, Rg32i = 25, Rg16i = 26, Rg8i = 27, R16i = 28, R8i = 29, Rgba32ui = 30, Rgba16ui = 31, Rgba8ui = 32, R32ui = 33, Rgb10a2ui = 34, Rg32ui = 35, Rg16ui = 36, Rg8ui = 37, R16ui = 38, R8ui = 39, }, ImageChannelOrder = { R = 0, A = 1, RG = 2, RA = 3, RGB = 4, RGBA = 5, BGRA = 6, ARGB = 7, Intensity = 8, Luminance = 9, Rx = 10, RGx = 11, RGBx = 12, Depth = 13, DepthStencil = 14, sRGB = 15, sRGBx = 16, sRGBA = 17, sBGRA = 18, ABGR = 19, }, ImageChannelDataType = { SnormInt8 = 0, SnormInt16 = 1, UnormInt8 = 2, UnormInt16 = 3, UnormShort565 = 4, UnormShort555 = 5, UnormInt101010 = 6, SignedInt8 = 7, SignedInt16 = 8, SignedInt32 = 9, UnsignedInt8 = 10, UnsignedInt16 = 11, UnsignedInt32 = 12, HalfFloat = 13, Float = 14, UnormInt24 = 15, UnormInt101010_2 = 16, }, ImageOperandsShift = { Bias = 0, Lod = 1, Grad = 2, ConstOffset = 3, Offset = 4, ConstOffsets = 5, Sample = 6, MinLod = 7, }, ImageOperandsMask = { MaskNone = 0, Bias = 0x00000001, Lod = 0x00000002, Grad = 0x00000004, ConstOffset = 0x00000008, Offset = 0x00000010, ConstOffsets = 0x00000020, Sample = 0x00000040, MinLod = 0x00000080, }, FPFastMathModeShift = { NotNaN = 0, NotInf = 1, NSZ = 2, AllowRecip = 3, Fast = 4, }, FPFastMathModeMask = { MaskNone = 0, NotNaN = 0x00000001, NotInf = 0x00000002, NSZ = 0x00000004, AllowRecip = 0x00000008, Fast = 0x00000010, }, FPRoundingMode = { RTE = 0, RTZ = 1, RTP = 2, RTN = 3, }, LinkageType = { Export = 0, Import = 1, }, AccessQualifier = { ReadOnly = 0, WriteOnly = 1, ReadWrite = 2, }, FunctionParameterAttribute = { Zext = 0, Sext = 1, ByVal = 2, Sret = 3, NoAlias = 4, NoCapture = 5, NoWrite = 6, NoReadWrite = 7, }, Decoration = { RelaxedPrecision = 0, SpecId = 1, Block = 2, BufferBlock = 3, RowMajor = 4, ColMajor = 5, ArrayStride = 6, MatrixStride = 7, GLSLShared = 8, GLSLPacked = 9, CPacked = 10, BuiltIn = 11, NoPerspective = 13, Flat = 14, Patch = 15, Centroid = 16, Sample = 17, Invariant = 18, Restrict = 19, Aliased = 20, Volatile = 21, Constant = 22, Coherent = 23, NonWritable = 24, NonReadable = 25, Uniform = 26, SaturatedConversion = 28, Stream = 29, Location = 30, Component = 31, Index = 32, Binding = 33, DescriptorSet = 34, Offset = 35, XfbBuffer = 36, XfbStride = 37, FuncParamAttr = 38, FPRoundingMode = 39, FPFastMathMode = 40, LinkageAttributes = 41, NoContraction = 42, InputAttachmentIndex = 43, Alignment = 44, MaxByteOffset = 45, OverrideCoverageNV = 5248, PassthroughNV = 5250, ViewportRelativeNV = 5252, SecondaryViewportRelativeNV = 5256, }, BuiltIn = { Position = 0, PointSize = 1, ClipDistance = 3, CullDistance = 4, VertexId = 5, InstanceId = 6, PrimitiveId = 7, InvocationId = 8, Layer = 9, ViewportIndex = 10, TessLevelOuter = 11, TessLevelInner = 12, TessCoord = 13, PatchVertices = 14, FragCoord = 15, PointCoord = 16, FrontFacing = 17, SampleId = 18, SamplePosition = 19, SampleMask = 20, FragDepth = 22, HelperInvocation = 23, NumWorkgroups = 24, WorkgroupSize = 25, WorkgroupId = 26, LocalInvocationId = 27, GlobalInvocationId = 28, LocalInvocationIndex = 29, WorkDim = 30, GlobalSize = 31, EnqueuedWorkgroupSize = 32, GlobalOffset = 33, GlobalLinearId = 34, SubgroupSize = 36, SubgroupMaxSize = 37, NumSubgroups = 38, NumEnqueuedSubgroups = 39, SubgroupId = 40, SubgroupLocalInvocationId = 41, VertexIndex = 42, InstanceIndex = 43, SubgroupEqMaskKHR = 4416, SubgroupGeMaskKHR = 4417, SubgroupGtMaskKHR = 4418, SubgroupLeMaskKHR = 4419, SubgroupLtMaskKHR = 4420, BaseVertex = 4424, BaseInstance = 4425, DrawIndex = 4426, DeviceIndex = 4438, ViewIndex = 4440, ViewportMaskNV = 5253, SecondaryPositionNV = 5257, SecondaryViewportMaskNV = 5258, PositionPerViewNV = 5261, ViewportMaskPerViewNV = 5262, }, SelectionControlShift = { Flatten = 0, DontFlatten = 1, }, SelectionControlMask = { MaskNone = 0, Flatten = 0x00000001, DontFlatten = 0x00000002, }, LoopControlShift = { Unroll = 0, DontUnroll = 1, DependencyInfinite = 2, DependencyLength = 3, }, LoopControlMask = { MaskNone = 0, Unroll = 0x00000001, DontUnroll = 0x00000002, DependencyInfinite = 0x00000004, DependencyLength = 0x00000008, }, FunctionControlShift = { Inline = 0, DontInline = 1, Pure = 2, Const = 3, }, FunctionControlMask = { MaskNone = 0, Inline = 0x00000001, DontInline = 0x00000002, Pure = 0x00000004, Const = 0x00000008, }, MemorySemanticsShift = { Acquire = 1, Release = 2, AcquireRelease = 3, SequentiallyConsistent = 4, UniformMemory = 6, SubgroupMemory = 7, WorkgroupMemory = 8, CrossWorkgroupMemory = 9, AtomicCounterMemory = 10, ImageMemory = 11, }, MemorySemanticsMask = { MaskNone = 0, Acquire = 0x00000002, Release = 0x00000004, AcquireRelease = 0x00000008, SequentiallyConsistent = 0x00000010, UniformMemory = 0x00000040, SubgroupMemory = 0x00000080, WorkgroupMemory = 0x00000100, CrossWorkgroupMemory = 0x00000200, AtomicCounterMemory = 0x00000400, ImageMemory = 0x00000800, }, MemoryAccessShift = { Volatile = 0, Aligned = 1, Nontemporal = 2, }, MemoryAccessMask = { MaskNone = 0, Volatile = 0x00000001, Aligned = 0x00000002, Nontemporal = 0x00000004, }, Scope = { CrossDevice = 0, Device = 1, Workgroup = 2, Subgroup = 3, Invocation = 4, }, GroupOperation = { Reduce = 0, InclusiveScan = 1, ExclusiveScan = 2, }, KernelEnqueueFlags = { NoWait = 0, WaitKernel = 1, WaitWorkGroup = 2, }, KernelProfilingInfoShift = { CmdExecTime = 0, }, KernelProfilingInfoMask = { MaskNone = 0, CmdExecTime = 0x00000001, }, Capability = { Matrix = 0, Shader = 1, Geometry = 2, Tessellation = 3, Addresses = 4, Linkage = 5, Kernel = 6, Vector16 = 7, Float16Buffer = 8, Float16 = 9, Float64 = 10, Int64 = 11, Int64Atomics = 12, ImageBasic = 13, ImageReadWrite = 14, ImageMipmap = 15, Pipes = 17, Groups = 18, DeviceEnqueue = 19, LiteralSampler = 20, AtomicStorage = 21, Int16 = 22, TessellationPointSize = 23, GeometryPointSize = 24, ImageGatherExtended = 25, StorageImageMultisample = 27, UniformBufferArrayDynamicIndexing = 28, SampledImageArrayDynamicIndexing = 29, StorageBufferArrayDynamicIndexing = 30, StorageImageArrayDynamicIndexing = 31, ClipDistance = 32, CullDistance = 33, ImageCubeArray = 34, SampleRateShading = 35, ImageRect = 36, SampledRect = 37, GenericPointer = 38, Int8 = 39, InputAttachment = 40, SparseResidency = 41, MinLod = 42, Sampled1D = 43, Image1D = 44, SampledCubeArray = 45, SampledBuffer = 46, ImageBuffer = 47, ImageMSArray = 48, StorageImageExtendedFormats = 49, ImageQuery = 50, DerivativeControl = 51, InterpolationFunction = 52, TransformFeedback = 53, GeometryStreams = 54, StorageImageReadWithoutFormat = 55, StorageImageWriteWithoutFormat = 56, MultiViewport = 57, SubgroupDispatch = 58, NamedBarrier = 59, PipeStorage = 60, SubgroupBallotKHR = 4423, DrawParameters = 4427, SubgroupVoteKHR = 4431, StorageBuffer16BitAccess = 4433, StorageUniformBufferBlock16 = 4433, StorageUniform16 = 4434, UniformAndStorageBuffer16BitAccess = 4434, StoragePushConstant16 = 4435, StorageInputOutput16 = 4436, DeviceGroup = 4437, MultiView = 4439, VariablePointersStorageBuffer = 4441, VariablePointers = 4442, SampleMaskOverrideCoverageNV = 5249, GeometryShaderPassthroughNV = 5251, ShaderViewportIndexLayerNV = 5254, ShaderViewportMaskNV = 5255, ShaderStereoViewNV = 5259, PerViewAttributesNV = 5260, }, Op = { OpNop = 0, OpUndef = 1, OpSourceContinued = 2, OpSource = 3, OpSourceExtension = 4, OpName = 5, OpMemberName = 6, OpString = 7, OpLine = 8, OpExtension = 10, OpExtInstImport = 11, OpExtInst = 12, OpMemoryModel = 14, OpEntryPoint = 15, OpExecutionMode = 16, OpCapability = 17, OpTypeVoid = 19, OpTypeBool = 20, OpTypeInt = 21, OpTypeFloat = 22, OpTypeVector = 23, OpTypeMatrix = 24, OpTypeImage = 25, OpTypeSampler = 26, OpTypeSampledImage = 27, OpTypeArray = 28, OpTypeRuntimeArray = 29, OpTypeStruct = 30, OpTypeOpaque = 31, OpTypePointer = 32, OpTypeFunction = 33, OpTypeEvent = 34, OpTypeDeviceEvent = 35, OpTypeReserveId = 36, OpTypeQueue = 37, OpTypePipe = 38, OpTypeForwardPointer = 39, OpConstantTrue = 41, OpConstantFalse = 42, OpConstant = 43, OpConstantComposite = 44, OpConstantSampler = 45, OpConstantNull = 46, OpSpecConstantTrue = 48, OpSpecConstantFalse = 49, OpSpecConstant = 50, OpSpecConstantComposite = 51, OpSpecConstantOp = 52, OpFunction = 54, OpFunctionParameter = 55, OpFunctionEnd = 56, OpFunctionCall = 57, OpVariable = 59, OpImageTexelPointer = 60, OpLoad = 61, OpStore = 62, OpCopyMemory = 63, OpCopyMemorySized = 64, OpAccessChain = 65, OpInBoundsAccessChain = 66, OpPtrAccessChain = 67, OpArrayLength = 68, OpGenericPtrMemSemantics = 69, OpInBoundsPtrAccessChain = 70, OpDecorate = 71, OpMemberDecorate = 72, OpDecorationGroup = 73, OpGroupDecorate = 74, OpGroupMemberDecorate = 75, OpVectorExtractDynamic = 77, OpVectorInsertDynamic = 78, OpVectorShuffle = 79, OpCompositeConstruct = 80, OpCompositeExtract = 81, OpCompositeInsert = 82, OpCopyObject = 83, OpTranspose = 84, OpSampledImage = 86, OpImageSampleImplicitLod = 87, OpImageSampleExplicitLod = 88, OpImageSampleDrefImplicitLod = 89, OpImageSampleDrefExplicitLod = 90, OpImageSampleProjImplicitLod = 91, OpImageSampleProjExplicitLod = 92, OpImageSampleProjDrefImplicitLod = 93, OpImageSampleProjDrefExplicitLod = 94, OpImageFetch = 95, OpImageGather = 96, OpImageDrefGather = 97, OpImageRead = 98, OpImageWrite = 99, OpImage = 100, OpImageQueryFormat = 101, OpImageQueryOrder = 102, OpImageQuerySizeLod = 103, OpImageQuerySize = 104, OpImageQueryLod = 105, OpImageQueryLevels = 106, OpImageQuerySamples = 107, OpConvertFToU = 109, OpConvertFToS = 110, OpConvertSToF = 111, OpConvertUToF = 112, OpUConvert = 113, OpSConvert = 114, OpFConvert = 115, OpQuantizeToF16 = 116, OpConvertPtrToU = 117, OpSatConvertSToU = 118, OpSatConvertUToS = 119, OpConvertUToPtr = 120, OpPtrCastToGeneric = 121, OpGenericCastToPtr = 122, OpGenericCastToPtrExplicit = 123, OpBitcast = 124, OpSNegate = 126, OpFNegate = 127, OpIAdd = 128, OpFAdd = 129, OpISub = 130, OpFSub = 131, OpIMul = 132, OpFMul = 133, OpUDiv = 134, OpSDiv = 135, OpFDiv = 136, OpUMod = 137, OpSRem = 138, OpSMod = 139, OpFRem = 140, OpFMod = 141, OpVectorTimesScalar = 142, OpMatrixTimesScalar = 143, OpVectorTimesMatrix = 144, OpMatrixTimesVector = 145, OpMatrixTimesMatrix = 146, OpOuterProduct = 147, OpDot = 148, OpIAddCarry = 149, OpISubBorrow = 150, OpUMulExtended = 151, OpSMulExtended = 152, OpAny = 154, OpAll = 155, OpIsNan = 156, OpIsInf = 157, OpIsFinite = 158, OpIsNormal = 159, OpSignBitSet = 160, OpLessOrGreater = 161, OpOrdered = 162, OpUnordered = 163, OpLogicalEqual = 164, OpLogicalNotEqual = 165, OpLogicalOr = 166, OpLogicalAnd = 167, OpLogicalNot = 168, OpSelect = 169, OpIEqual = 170, OpINotEqual = 171, OpUGreaterThan = 172, OpSGreaterThan = 173, OpUGreaterThanEqual = 174, OpSGreaterThanEqual = 175, OpULessThan = 176, OpSLessThan = 177, OpULessThanEqual = 178, OpSLessThanEqual = 179, OpFOrdEqual = 180, OpFUnordEqual = 181, OpFOrdNotEqual = 182, OpFUnordNotEqual = 183, OpFOrdLessThan = 184, OpFUnordLessThan = 185, OpFOrdGreaterThan = 186, OpFUnordGreaterThan = 187, OpFOrdLessThanEqual = 188, OpFUnordLessThanEqual = 189, OpFOrdGreaterThanEqual = 190, OpFUnordGreaterThanEqual = 191, OpShiftRightLogical = 194, OpShiftRightArithmetic = 195, OpShiftLeftLogical = 196, OpBitwiseOr = 197, OpBitwiseXor = 198, OpBitwiseAnd = 199, OpNot = 200, OpBitFieldInsert = 201, OpBitFieldSExtract = 202, OpBitFieldUExtract = 203, OpBitReverse = 204, OpBitCount = 205, OpDPdx = 207, OpDPdy = 208, OpFwidth = 209, OpDPdxFine = 210, OpDPdyFine = 211, OpFwidthFine = 212, OpDPdxCoarse = 213, OpDPdyCoarse = 214, OpFwidthCoarse = 215, OpEmitVertex = 218, OpEndPrimitive = 219, OpEmitStreamVertex = 220, OpEndStreamPrimitive = 221, OpControlBarrier = 224, OpMemoryBarrier = 225, OpAtomicLoad = 227, OpAtomicStore = 228, OpAtomicExchange = 229, OpAtomicCompareExchange = 230, OpAtomicCompareExchangeWeak = 231, OpAtomicIIncrement = 232, OpAtomicIDecrement = 233, OpAtomicIAdd = 234, OpAtomicISub = 235, OpAtomicSMin = 236, OpAtomicUMin = 237, OpAtomicSMax = 238, OpAtomicUMax = 239, OpAtomicAnd = 240, OpAtomicOr = 241, OpAtomicXor = 242, OpPhi = 245, OpLoopMerge = 246, OpSelectionMerge = 247, OpLabel = 248, OpBranch = 249, OpBranchConditional = 250, OpSwitch = 251, OpKill = 252, OpReturn = 253, OpReturnValue = 254, OpUnreachable = 255, OpLifetimeStart = 256, OpLifetimeStop = 257, OpGroupAsyncCopy = 259, OpGroupWaitEvents = 260, OpGroupAll = 261, OpGroupAny = 262, OpGroupBroadcast = 263, OpGroupIAdd = 264, OpGroupFAdd = 265, OpGroupFMin = 266, OpGroupUMin = 267, OpGroupSMin = 268, OpGroupFMax = 269, OpGroupUMax = 270, OpGroupSMax = 271, OpReadPipe = 274, OpWritePipe = 275, OpReservedReadPipe = 276, OpReservedWritePipe = 277, OpReserveReadPipePackets = 278, OpReserveWritePipePackets = 279, OpCommitReadPipe = 280, OpCommitWritePipe = 281, OpIsValidReserveId = 282, OpGetNumPipePackets = 283, OpGetMaxPipePackets = 284, OpGroupReserveReadPipePackets = 285, OpGroupReserveWritePipePackets = 286, OpGroupCommitReadPipe = 287, OpGroupCommitWritePipe = 288, OpEnqueueMarker = 291, OpEnqueueKernel = 292, OpGetKernelNDrangeSubGroupCount = 293, OpGetKernelNDrangeMaxSubGroupSize = 294, OpGetKernelWorkGroupSize = 295, OpGetKernelPreferredWorkGroupSizeMultiple = 296, OpRetainEvent = 297, OpReleaseEvent = 298, OpCreateUserEvent = 299, OpIsValidEvent = 300, OpSetUserEventStatus = 301, OpCaptureEventProfilingInfo = 302, OpGetDefaultQueue = 303, OpBuildNDRange = 304, OpImageSparseSampleImplicitLod = 305, OpImageSparseSampleExplicitLod = 306, OpImageSparseSampleDrefImplicitLod = 307, OpImageSparseSampleDrefExplicitLod = 308, OpImageSparseSampleProjImplicitLod = 309, OpImageSparseSampleProjExplicitLod = 310, OpImageSparseSampleProjDrefImplicitLod = 311, OpImageSparseSampleProjDrefExplicitLod = 312, OpImageSparseFetch = 313, OpImageSparseGather = 314, OpImageSparseDrefGather = 315, OpImageSparseTexelsResident = 316, OpNoLine = 317, OpAtomicFlagTestAndSet = 318, OpAtomicFlagClear = 319, OpImageSparseRead = 320, OpSizeOf = 321, OpTypePipeStorage = 322, OpConstantPipeStorage = 323, OpCreatePipeFromPipeStorage = 324, OpGetKernelLocalSizeForSubgroupCount = 325, OpGetKernelMaxNumSubgroups = 326, OpTypeNamedBarrier = 327, OpNamedBarrierInitialize = 328, OpMemoryNamedBarrier = 329, OpModuleProcessed = 330, OpSubgroupBallotKHR = 4421, OpSubgroupFirstInvocationKHR = 4422, OpSubgroupAllKHR = 4428, OpSubgroupAnyKHR = 4429, OpSubgroupAllEqualKHR = 4430, OpSubgroupReadInvocationKHR = 4432, }, } ================================================ FILE: src/engine/renderer/vulkan/spirv.py ================================================ # Copyright (c) 2014-2017 The Khronos Group Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and/or associated documentation files (the "Materials"), # to deal in the Materials without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Materials, and to permit persons to whom the # Materials are furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Materials. # # MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS # STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND # HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ # # THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS # IN THE MATERIALS. # This header is automatically generated by the same tool that creates # the Binary Section of the SPIR-V specification. # Enumeration tokens for SPIR-V, in various styles: # C, C++, C++11, JSON, Lua, Python # # - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL # - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL # - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL # - Lua will use tables, e.g.: spv.SourceLanguage.GLSL # - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] # # Some tokens act like mask values, which can be OR'd together, # while others are mutually exclusive. The mask-like ones have # "Mask" in their name, and a parallel enum that has the shift # amount (1 << x) for each corresponding enumerant. spv = { 'MagicNumber' : 0x07230203, 'Version' : 0x00010100, 'Revision' : 6, 'OpCodeMask' : 0xffff, 'WordCountShift' : 16, 'SourceLanguage' : { 'Unknown' : 0, 'ESSL' : 1, 'GLSL' : 2, 'OpenCL_C' : 3, 'OpenCL_CPP' : 4, 'HLSL' : 5, }, 'ExecutionModel' : { 'Vertex' : 0, 'TessellationControl' : 1, 'TessellationEvaluation' : 2, 'Geometry' : 3, 'Fragment' : 4, 'GLCompute' : 5, 'Kernel' : 6, }, 'AddressingModel' : { 'Logical' : 0, 'Physical32' : 1, 'Physical64' : 2, }, 'MemoryModel' : { 'Simple' : 0, 'GLSL450' : 1, 'OpenCL' : 2, }, 'ExecutionMode' : { 'Invocations' : 0, 'SpacingEqual' : 1, 'SpacingFractionalEven' : 2, 'SpacingFractionalOdd' : 3, 'VertexOrderCw' : 4, 'VertexOrderCcw' : 5, 'PixelCenterInteger' : 6, 'OriginUpperLeft' : 7, 'OriginLowerLeft' : 8, 'EarlyFragmentTests' : 9, 'PointMode' : 10, 'Xfb' : 11, 'DepthReplacing' : 12, 'DepthGreater' : 14, 'DepthLess' : 15, 'DepthUnchanged' : 16, 'LocalSize' : 17, 'LocalSizeHint' : 18, 'InputPoints' : 19, 'InputLines' : 20, 'InputLinesAdjacency' : 21, 'Triangles' : 22, 'InputTrianglesAdjacency' : 23, 'Quads' : 24, 'Isolines' : 25, 'OutputVertices' : 26, 'OutputPoints' : 27, 'OutputLineStrip' : 28, 'OutputTriangleStrip' : 29, 'VecTypeHint' : 30, 'ContractionOff' : 31, 'Initializer' : 33, 'Finalizer' : 34, 'SubgroupSize' : 35, 'SubgroupsPerWorkgroup' : 36, }, 'StorageClass' : { 'UniformConstant' : 0, 'Input' : 1, 'Uniform' : 2, 'Output' : 3, 'Workgroup' : 4, 'CrossWorkgroup' : 5, 'Private' : 6, 'Function' : 7, 'Generic' : 8, 'PushConstant' : 9, 'AtomicCounter' : 10, 'Image' : 11, 'StorageBuffer' : 12, }, 'Dim' : { 'Dim1D' : 0, 'Dim2D' : 1, 'Dim3D' : 2, 'Cube' : 3, 'Rect' : 4, 'Buffer' : 5, 'SubpassData' : 6, }, 'SamplerAddressingMode' : { 'None' : 0, 'ClampToEdge' : 1, 'Clamp' : 2, 'Repeat' : 3, 'RepeatMirrored' : 4, }, 'SamplerFilterMode' : { 'Nearest' : 0, 'Linear' : 1, }, 'ImageFormat' : { 'Unknown' : 0, 'Rgba32f' : 1, 'Rgba16f' : 2, 'R32f' : 3, 'Rgba8' : 4, 'Rgba8Snorm' : 5, 'Rg32f' : 6, 'Rg16f' : 7, 'R11fG11fB10f' : 8, 'R16f' : 9, 'Rgba16' : 10, 'Rgb10A2' : 11, 'Rg16' : 12, 'Rg8' : 13, 'R16' : 14, 'R8' : 15, 'Rgba16Snorm' : 16, 'Rg16Snorm' : 17, 'Rg8Snorm' : 18, 'R16Snorm' : 19, 'R8Snorm' : 20, 'Rgba32i' : 21, 'Rgba16i' : 22, 'Rgba8i' : 23, 'R32i' : 24, 'Rg32i' : 25, 'Rg16i' : 26, 'Rg8i' : 27, 'R16i' : 28, 'R8i' : 29, 'Rgba32ui' : 30, 'Rgba16ui' : 31, 'Rgba8ui' : 32, 'R32ui' : 33, 'Rgb10a2ui' : 34, 'Rg32ui' : 35, 'Rg16ui' : 36, 'Rg8ui' : 37, 'R16ui' : 38, 'R8ui' : 39, }, 'ImageChannelOrder' : { 'R' : 0, 'A' : 1, 'RG' : 2, 'RA' : 3, 'RGB' : 4, 'RGBA' : 5, 'BGRA' : 6, 'ARGB' : 7, 'Intensity' : 8, 'Luminance' : 9, 'Rx' : 10, 'RGx' : 11, 'RGBx' : 12, 'Depth' : 13, 'DepthStencil' : 14, 'sRGB' : 15, 'sRGBx' : 16, 'sRGBA' : 17, 'sBGRA' : 18, 'ABGR' : 19, }, 'ImageChannelDataType' : { 'SnormInt8' : 0, 'SnormInt16' : 1, 'UnormInt8' : 2, 'UnormInt16' : 3, 'UnormShort565' : 4, 'UnormShort555' : 5, 'UnormInt101010' : 6, 'SignedInt8' : 7, 'SignedInt16' : 8, 'SignedInt32' : 9, 'UnsignedInt8' : 10, 'UnsignedInt16' : 11, 'UnsignedInt32' : 12, 'HalfFloat' : 13, 'Float' : 14, 'UnormInt24' : 15, 'UnormInt101010_2' : 16, }, 'ImageOperandsShift' : { 'Bias' : 0, 'Lod' : 1, 'Grad' : 2, 'ConstOffset' : 3, 'Offset' : 4, 'ConstOffsets' : 5, 'Sample' : 6, 'MinLod' : 7, }, 'ImageOperandsMask' : { 'MaskNone' : 0, 'Bias' : 0x00000001, 'Lod' : 0x00000002, 'Grad' : 0x00000004, 'ConstOffset' : 0x00000008, 'Offset' : 0x00000010, 'ConstOffsets' : 0x00000020, 'Sample' : 0x00000040, 'MinLod' : 0x00000080, }, 'FPFastMathModeShift' : { 'NotNaN' : 0, 'NotInf' : 1, 'NSZ' : 2, 'AllowRecip' : 3, 'Fast' : 4, }, 'FPFastMathModeMask' : { 'MaskNone' : 0, 'NotNaN' : 0x00000001, 'NotInf' : 0x00000002, 'NSZ' : 0x00000004, 'AllowRecip' : 0x00000008, 'Fast' : 0x00000010, }, 'FPRoundingMode' : { 'RTE' : 0, 'RTZ' : 1, 'RTP' : 2, 'RTN' : 3, }, 'LinkageType' : { 'Export' : 0, 'Import' : 1, }, 'AccessQualifier' : { 'ReadOnly' : 0, 'WriteOnly' : 1, 'ReadWrite' : 2, }, 'FunctionParameterAttribute' : { 'Zext' : 0, 'Sext' : 1, 'ByVal' : 2, 'Sret' : 3, 'NoAlias' : 4, 'NoCapture' : 5, 'NoWrite' : 6, 'NoReadWrite' : 7, }, 'Decoration' : { 'RelaxedPrecision' : 0, 'SpecId' : 1, 'Block' : 2, 'BufferBlock' : 3, 'RowMajor' : 4, 'ColMajor' : 5, 'ArrayStride' : 6, 'MatrixStride' : 7, 'GLSLShared' : 8, 'GLSLPacked' : 9, 'CPacked' : 10, 'BuiltIn' : 11, 'NoPerspective' : 13, 'Flat' : 14, 'Patch' : 15, 'Centroid' : 16, 'Sample' : 17, 'Invariant' : 18, 'Restrict' : 19, 'Aliased' : 20, 'Volatile' : 21, 'Constant' : 22, 'Coherent' : 23, 'NonWritable' : 24, 'NonReadable' : 25, 'Uniform' : 26, 'SaturatedConversion' : 28, 'Stream' : 29, 'Location' : 30, 'Component' : 31, 'Index' : 32, 'Binding' : 33, 'DescriptorSet' : 34, 'Offset' : 35, 'XfbBuffer' : 36, 'XfbStride' : 37, 'FuncParamAttr' : 38, 'FPRoundingMode' : 39, 'FPFastMathMode' : 40, 'LinkageAttributes' : 41, 'NoContraction' : 42, 'InputAttachmentIndex' : 43, 'Alignment' : 44, 'MaxByteOffset' : 45, 'OverrideCoverageNV' : 5248, 'PassthroughNV' : 5250, 'ViewportRelativeNV' : 5252, 'SecondaryViewportRelativeNV' : 5256, }, 'BuiltIn' : { 'Position' : 0, 'PointSize' : 1, 'ClipDistance' : 3, 'CullDistance' : 4, 'VertexId' : 5, 'InstanceId' : 6, 'PrimitiveId' : 7, 'InvocationId' : 8, 'Layer' : 9, 'ViewportIndex' : 10, 'TessLevelOuter' : 11, 'TessLevelInner' : 12, 'TessCoord' : 13, 'PatchVertices' : 14, 'FragCoord' : 15, 'PointCoord' : 16, 'FrontFacing' : 17, 'SampleId' : 18, 'SamplePosition' : 19, 'SampleMask' : 20, 'FragDepth' : 22, 'HelperInvocation' : 23, 'NumWorkgroups' : 24, 'WorkgroupSize' : 25, 'WorkgroupId' : 26, 'LocalInvocationId' : 27, 'GlobalInvocationId' : 28, 'LocalInvocationIndex' : 29, 'WorkDim' : 30, 'GlobalSize' : 31, 'EnqueuedWorkgroupSize' : 32, 'GlobalOffset' : 33, 'GlobalLinearId' : 34, 'SubgroupSize' : 36, 'SubgroupMaxSize' : 37, 'NumSubgroups' : 38, 'NumEnqueuedSubgroups' : 39, 'SubgroupId' : 40, 'SubgroupLocalInvocationId' : 41, 'VertexIndex' : 42, 'InstanceIndex' : 43, 'SubgroupEqMaskKHR' : 4416, 'SubgroupGeMaskKHR' : 4417, 'SubgroupGtMaskKHR' : 4418, 'SubgroupLeMaskKHR' : 4419, 'SubgroupLtMaskKHR' : 4420, 'BaseVertex' : 4424, 'BaseInstance' : 4425, 'DrawIndex' : 4426, 'DeviceIndex' : 4438, 'ViewIndex' : 4440, 'ViewportMaskNV' : 5253, 'SecondaryPositionNV' : 5257, 'SecondaryViewportMaskNV' : 5258, 'PositionPerViewNV' : 5261, 'ViewportMaskPerViewNV' : 5262, }, 'SelectionControlShift' : { 'Flatten' : 0, 'DontFlatten' : 1, }, 'SelectionControlMask' : { 'MaskNone' : 0, 'Flatten' : 0x00000001, 'DontFlatten' : 0x00000002, }, 'LoopControlShift' : { 'Unroll' : 0, 'DontUnroll' : 1, 'DependencyInfinite' : 2, 'DependencyLength' : 3, }, 'LoopControlMask' : { 'MaskNone' : 0, 'Unroll' : 0x00000001, 'DontUnroll' : 0x00000002, 'DependencyInfinite' : 0x00000004, 'DependencyLength' : 0x00000008, }, 'FunctionControlShift' : { 'Inline' : 0, 'DontInline' : 1, 'Pure' : 2, 'Const' : 3, }, 'FunctionControlMask' : { 'MaskNone' : 0, 'Inline' : 0x00000001, 'DontInline' : 0x00000002, 'Pure' : 0x00000004, 'Const' : 0x00000008, }, 'MemorySemanticsShift' : { 'Acquire' : 1, 'Release' : 2, 'AcquireRelease' : 3, 'SequentiallyConsistent' : 4, 'UniformMemory' : 6, 'SubgroupMemory' : 7, 'WorkgroupMemory' : 8, 'CrossWorkgroupMemory' : 9, 'AtomicCounterMemory' : 10, 'ImageMemory' : 11, }, 'MemorySemanticsMask' : { 'MaskNone' : 0, 'Acquire' : 0x00000002, 'Release' : 0x00000004, 'AcquireRelease' : 0x00000008, 'SequentiallyConsistent' : 0x00000010, 'UniformMemory' : 0x00000040, 'SubgroupMemory' : 0x00000080, 'WorkgroupMemory' : 0x00000100, 'CrossWorkgroupMemory' : 0x00000200, 'AtomicCounterMemory' : 0x00000400, 'ImageMemory' : 0x00000800, }, 'MemoryAccessShift' : { 'Volatile' : 0, 'Aligned' : 1, 'Nontemporal' : 2, }, 'MemoryAccessMask' : { 'MaskNone' : 0, 'Volatile' : 0x00000001, 'Aligned' : 0x00000002, 'Nontemporal' : 0x00000004, }, 'Scope' : { 'CrossDevice' : 0, 'Device' : 1, 'Workgroup' : 2, 'Subgroup' : 3, 'Invocation' : 4, }, 'GroupOperation' : { 'Reduce' : 0, 'InclusiveScan' : 1, 'ExclusiveScan' : 2, }, 'KernelEnqueueFlags' : { 'NoWait' : 0, 'WaitKernel' : 1, 'WaitWorkGroup' : 2, }, 'KernelProfilingInfoShift' : { 'CmdExecTime' : 0, }, 'KernelProfilingInfoMask' : { 'MaskNone' : 0, 'CmdExecTime' : 0x00000001, }, 'Capability' : { 'Matrix' : 0, 'Shader' : 1, 'Geometry' : 2, 'Tessellation' : 3, 'Addresses' : 4, 'Linkage' : 5, 'Kernel' : 6, 'Vector16' : 7, 'Float16Buffer' : 8, 'Float16' : 9, 'Float64' : 10, 'Int64' : 11, 'Int64Atomics' : 12, 'ImageBasic' : 13, 'ImageReadWrite' : 14, 'ImageMipmap' : 15, 'Pipes' : 17, 'Groups' : 18, 'DeviceEnqueue' : 19, 'LiteralSampler' : 20, 'AtomicStorage' : 21, 'Int16' : 22, 'TessellationPointSize' : 23, 'GeometryPointSize' : 24, 'ImageGatherExtended' : 25, 'StorageImageMultisample' : 27, 'UniformBufferArrayDynamicIndexing' : 28, 'SampledImageArrayDynamicIndexing' : 29, 'StorageBufferArrayDynamicIndexing' : 30, 'StorageImageArrayDynamicIndexing' : 31, 'ClipDistance' : 32, 'CullDistance' : 33, 'ImageCubeArray' : 34, 'SampleRateShading' : 35, 'ImageRect' : 36, 'SampledRect' : 37, 'GenericPointer' : 38, 'Int8' : 39, 'InputAttachment' : 40, 'SparseResidency' : 41, 'MinLod' : 42, 'Sampled1D' : 43, 'Image1D' : 44, 'SampledCubeArray' : 45, 'SampledBuffer' : 46, 'ImageBuffer' : 47, 'ImageMSArray' : 48, 'StorageImageExtendedFormats' : 49, 'ImageQuery' : 50, 'DerivativeControl' : 51, 'InterpolationFunction' : 52, 'TransformFeedback' : 53, 'GeometryStreams' : 54, 'StorageImageReadWithoutFormat' : 55, 'StorageImageWriteWithoutFormat' : 56, 'MultiViewport' : 57, 'SubgroupDispatch' : 58, 'NamedBarrier' : 59, 'PipeStorage' : 60, 'SubgroupBallotKHR' : 4423, 'DrawParameters' : 4427, 'SubgroupVoteKHR' : 4431, 'StorageBuffer16BitAccess' : 4433, 'StorageUniformBufferBlock16' : 4433, 'StorageUniform16' : 4434, 'UniformAndStorageBuffer16BitAccess' : 4434, 'StoragePushConstant16' : 4435, 'StorageInputOutput16' : 4436, 'DeviceGroup' : 4437, 'MultiView' : 4439, 'VariablePointersStorageBuffer' : 4441, 'VariablePointers' : 4442, 'SampleMaskOverrideCoverageNV' : 5249, 'GeometryShaderPassthroughNV' : 5251, 'ShaderViewportIndexLayerNV' : 5254, 'ShaderViewportMaskNV' : 5255, 'ShaderStereoViewNV' : 5259, 'PerViewAttributesNV' : 5260, }, 'Op' : { 'OpNop' : 0, 'OpUndef' : 1, 'OpSourceContinued' : 2, 'OpSource' : 3, 'OpSourceExtension' : 4, 'OpName' : 5, 'OpMemberName' : 6, 'OpString' : 7, 'OpLine' : 8, 'OpExtension' : 10, 'OpExtInstImport' : 11, 'OpExtInst' : 12, 'OpMemoryModel' : 14, 'OpEntryPoint' : 15, 'OpExecutionMode' : 16, 'OpCapability' : 17, 'OpTypeVoid' : 19, 'OpTypeBool' : 20, 'OpTypeInt' : 21, 'OpTypeFloat' : 22, 'OpTypeVector' : 23, 'OpTypeMatrix' : 24, 'OpTypeImage' : 25, 'OpTypeSampler' : 26, 'OpTypeSampledImage' : 27, 'OpTypeArray' : 28, 'OpTypeRuntimeArray' : 29, 'OpTypeStruct' : 30, 'OpTypeOpaque' : 31, 'OpTypePointer' : 32, 'OpTypeFunction' : 33, 'OpTypeEvent' : 34, 'OpTypeDeviceEvent' : 35, 'OpTypeReserveId' : 36, 'OpTypeQueue' : 37, 'OpTypePipe' : 38, 'OpTypeForwardPointer' : 39, 'OpConstantTrue' : 41, 'OpConstantFalse' : 42, 'OpConstant' : 43, 'OpConstantComposite' : 44, 'OpConstantSampler' : 45, 'OpConstantNull' : 46, 'OpSpecConstantTrue' : 48, 'OpSpecConstantFalse' : 49, 'OpSpecConstant' : 50, 'OpSpecConstantComposite' : 51, 'OpSpecConstantOp' : 52, 'OpFunction' : 54, 'OpFunctionParameter' : 55, 'OpFunctionEnd' : 56, 'OpFunctionCall' : 57, 'OpVariable' : 59, 'OpImageTexelPointer' : 60, 'OpLoad' : 61, 'OpStore' : 62, 'OpCopyMemory' : 63, 'OpCopyMemorySized' : 64, 'OpAccessChain' : 65, 'OpInBoundsAccessChain' : 66, 'OpPtrAccessChain' : 67, 'OpArrayLength' : 68, 'OpGenericPtrMemSemantics' : 69, 'OpInBoundsPtrAccessChain' : 70, 'OpDecorate' : 71, 'OpMemberDecorate' : 72, 'OpDecorationGroup' : 73, 'OpGroupDecorate' : 74, 'OpGroupMemberDecorate' : 75, 'OpVectorExtractDynamic' : 77, 'OpVectorInsertDynamic' : 78, 'OpVectorShuffle' : 79, 'OpCompositeConstruct' : 80, 'OpCompositeExtract' : 81, 'OpCompositeInsert' : 82, 'OpCopyObject' : 83, 'OpTranspose' : 84, 'OpSampledImage' : 86, 'OpImageSampleImplicitLod' : 87, 'OpImageSampleExplicitLod' : 88, 'OpImageSampleDrefImplicitLod' : 89, 'OpImageSampleDrefExplicitLod' : 90, 'OpImageSampleProjImplicitLod' : 91, 'OpImageSampleProjExplicitLod' : 92, 'OpImageSampleProjDrefImplicitLod' : 93, 'OpImageSampleProjDrefExplicitLod' : 94, 'OpImageFetch' : 95, 'OpImageGather' : 96, 'OpImageDrefGather' : 97, 'OpImageRead' : 98, 'OpImageWrite' : 99, 'OpImage' : 100, 'OpImageQueryFormat' : 101, 'OpImageQueryOrder' : 102, 'OpImageQuerySizeLod' : 103, 'OpImageQuerySize' : 104, 'OpImageQueryLod' : 105, 'OpImageQueryLevels' : 106, 'OpImageQuerySamples' : 107, 'OpConvertFToU' : 109, 'OpConvertFToS' : 110, 'OpConvertSToF' : 111, 'OpConvertUToF' : 112, 'OpUConvert' : 113, 'OpSConvert' : 114, 'OpFConvert' : 115, 'OpQuantizeToF16' : 116, 'OpConvertPtrToU' : 117, 'OpSatConvertSToU' : 118, 'OpSatConvertUToS' : 119, 'OpConvertUToPtr' : 120, 'OpPtrCastToGeneric' : 121, 'OpGenericCastToPtr' : 122, 'OpGenericCastToPtrExplicit' : 123, 'OpBitcast' : 124, 'OpSNegate' : 126, 'OpFNegate' : 127, 'OpIAdd' : 128, 'OpFAdd' : 129, 'OpISub' : 130, 'OpFSub' : 131, 'OpIMul' : 132, 'OpFMul' : 133, 'OpUDiv' : 134, 'OpSDiv' : 135, 'OpFDiv' : 136, 'OpUMod' : 137, 'OpSRem' : 138, 'OpSMod' : 139, 'OpFRem' : 140, 'OpFMod' : 141, 'OpVectorTimesScalar' : 142, 'OpMatrixTimesScalar' : 143, 'OpVectorTimesMatrix' : 144, 'OpMatrixTimesVector' : 145, 'OpMatrixTimesMatrix' : 146, 'OpOuterProduct' : 147, 'OpDot' : 148, 'OpIAddCarry' : 149, 'OpISubBorrow' : 150, 'OpUMulExtended' : 151, 'OpSMulExtended' : 152, 'OpAny' : 154, 'OpAll' : 155, 'OpIsNan' : 156, 'OpIsInf' : 157, 'OpIsFinite' : 158, 'OpIsNormal' : 159, 'OpSignBitSet' : 160, 'OpLessOrGreater' : 161, 'OpOrdered' : 162, 'OpUnordered' : 163, 'OpLogicalEqual' : 164, 'OpLogicalNotEqual' : 165, 'OpLogicalOr' : 166, 'OpLogicalAnd' : 167, 'OpLogicalNot' : 168, 'OpSelect' : 169, 'OpIEqual' : 170, 'OpINotEqual' : 171, 'OpUGreaterThan' : 172, 'OpSGreaterThan' : 173, 'OpUGreaterThanEqual' : 174, 'OpSGreaterThanEqual' : 175, 'OpULessThan' : 176, 'OpSLessThan' : 177, 'OpULessThanEqual' : 178, 'OpSLessThanEqual' : 179, 'OpFOrdEqual' : 180, 'OpFUnordEqual' : 181, 'OpFOrdNotEqual' : 182, 'OpFUnordNotEqual' : 183, 'OpFOrdLessThan' : 184, 'OpFUnordLessThan' : 185, 'OpFOrdGreaterThan' : 186, 'OpFUnordGreaterThan' : 187, 'OpFOrdLessThanEqual' : 188, 'OpFUnordLessThanEqual' : 189, 'OpFOrdGreaterThanEqual' : 190, 'OpFUnordGreaterThanEqual' : 191, 'OpShiftRightLogical' : 194, 'OpShiftRightArithmetic' : 195, 'OpShiftLeftLogical' : 196, 'OpBitwiseOr' : 197, 'OpBitwiseXor' : 198, 'OpBitwiseAnd' : 199, 'OpNot' : 200, 'OpBitFieldInsert' : 201, 'OpBitFieldSExtract' : 202, 'OpBitFieldUExtract' : 203, 'OpBitReverse' : 204, 'OpBitCount' : 205, 'OpDPdx' : 207, 'OpDPdy' : 208, 'OpFwidth' : 209, 'OpDPdxFine' : 210, 'OpDPdyFine' : 211, 'OpFwidthFine' : 212, 'OpDPdxCoarse' : 213, 'OpDPdyCoarse' : 214, 'OpFwidthCoarse' : 215, 'OpEmitVertex' : 218, 'OpEndPrimitive' : 219, 'OpEmitStreamVertex' : 220, 'OpEndStreamPrimitive' : 221, 'OpControlBarrier' : 224, 'OpMemoryBarrier' : 225, 'OpAtomicLoad' : 227, 'OpAtomicStore' : 228, 'OpAtomicExchange' : 229, 'OpAtomicCompareExchange' : 230, 'OpAtomicCompareExchangeWeak' : 231, 'OpAtomicIIncrement' : 232, 'OpAtomicIDecrement' : 233, 'OpAtomicIAdd' : 234, 'OpAtomicISub' : 235, 'OpAtomicSMin' : 236, 'OpAtomicUMin' : 237, 'OpAtomicSMax' : 238, 'OpAtomicUMax' : 239, 'OpAtomicAnd' : 240, 'OpAtomicOr' : 241, 'OpAtomicXor' : 242, 'OpPhi' : 245, 'OpLoopMerge' : 246, 'OpSelectionMerge' : 247, 'OpLabel' : 248, 'OpBranch' : 249, 'OpBranchConditional' : 250, 'OpSwitch' : 251, 'OpKill' : 252, 'OpReturn' : 253, 'OpReturnValue' : 254, 'OpUnreachable' : 255, 'OpLifetimeStart' : 256, 'OpLifetimeStop' : 257, 'OpGroupAsyncCopy' : 259, 'OpGroupWaitEvents' : 260, 'OpGroupAll' : 261, 'OpGroupAny' : 262, 'OpGroupBroadcast' : 263, 'OpGroupIAdd' : 264, 'OpGroupFAdd' : 265, 'OpGroupFMin' : 266, 'OpGroupUMin' : 267, 'OpGroupSMin' : 268, 'OpGroupFMax' : 269, 'OpGroupUMax' : 270, 'OpGroupSMax' : 271, 'OpReadPipe' : 274, 'OpWritePipe' : 275, 'OpReservedReadPipe' : 276, 'OpReservedWritePipe' : 277, 'OpReserveReadPipePackets' : 278, 'OpReserveWritePipePackets' : 279, 'OpCommitReadPipe' : 280, 'OpCommitWritePipe' : 281, 'OpIsValidReserveId' : 282, 'OpGetNumPipePackets' : 283, 'OpGetMaxPipePackets' : 284, 'OpGroupReserveReadPipePackets' : 285, 'OpGroupReserveWritePipePackets' : 286, 'OpGroupCommitReadPipe' : 287, 'OpGroupCommitWritePipe' : 288, 'OpEnqueueMarker' : 291, 'OpEnqueueKernel' : 292, 'OpGetKernelNDrangeSubGroupCount' : 293, 'OpGetKernelNDrangeMaxSubGroupSize' : 294, 'OpGetKernelWorkGroupSize' : 295, 'OpGetKernelPreferredWorkGroupSizeMultiple' : 296, 'OpRetainEvent' : 297, 'OpReleaseEvent' : 298, 'OpCreateUserEvent' : 299, 'OpIsValidEvent' : 300, 'OpSetUserEventStatus' : 301, 'OpCaptureEventProfilingInfo' : 302, 'OpGetDefaultQueue' : 303, 'OpBuildNDRange' : 304, 'OpImageSparseSampleImplicitLod' : 305, 'OpImageSparseSampleExplicitLod' : 306, 'OpImageSparseSampleDrefImplicitLod' : 307, 'OpImageSparseSampleDrefExplicitLod' : 308, 'OpImageSparseSampleProjImplicitLod' : 309, 'OpImageSparseSampleProjExplicitLod' : 310, 'OpImageSparseSampleProjDrefImplicitLod' : 311, 'OpImageSparseSampleProjDrefExplicitLod' : 312, 'OpImageSparseFetch' : 313, 'OpImageSparseGather' : 314, 'OpImageSparseDrefGather' : 315, 'OpImageSparseTexelsResident' : 316, 'OpNoLine' : 317, 'OpAtomicFlagTestAndSet' : 318, 'OpAtomicFlagClear' : 319, 'OpImageSparseRead' : 320, 'OpSizeOf' : 321, 'OpTypePipeStorage' : 322, 'OpConstantPipeStorage' : 323, 'OpCreatePipeFromPipeStorage' : 324, 'OpGetKernelLocalSizeForSubgroupCount' : 325, 'OpGetKernelMaxNumSubgroups' : 326, 'OpTypeNamedBarrier' : 327, 'OpNamedBarrierInitialize' : 328, 'OpMemoryNamedBarrier' : 329, 'OpModuleProcessed' : 330, 'OpSubgroupBallotKHR' : 4421, 'OpSubgroupFirstInvocationKHR' : 4422, 'OpSubgroupAllKHR' : 4428, 'OpSubgroupAnyKHR' : 4429, 'OpSubgroupAllEqualKHR' : 4430, 'OpSubgroupReadInvocationKHR' : 4432, }, } ================================================ FILE: src/engine/renderer/vulkan/vk_icd.h ================================================ // // File: vk_icd.h // /* * Copyright (c) 2015-2016 The Khronos Group Inc. * Copyright (c) 2015-2016 Valve Corporation * Copyright (c) 2015-2016 LunarG, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ #ifndef VKICD_H #define VKICD_H #include "vulkan.h" #include // Loader-ICD version negotiation API. Versions add the following features: // Version 0 - Initial. Doesn't support vk_icdGetInstanceProcAddr // or vk_icdNegotiateLoaderICDInterfaceVersion. // Version 1 - Add support for vk_icdGetInstanceProcAddr. // Version 2 - Add Loader/ICD Interface version negotiation // via vk_icdNegotiateLoaderICDInterfaceVersion. // Version 3 - Add ICD creation/destruction of KHR_surface objects. // Version 4 - Add unknown physical device extension qyering via // vk_icdGetPhysicalDeviceProcAddr. #define CURRENT_LOADER_ICD_INTERFACE_VERSION 4 #define MIN_SUPPORTED_LOADER_ICD_INTERFACE_VERSION 0 #define MIN_PHYS_DEV_EXTENSION_ICD_INTERFACE_VERSION 4 typedef VkResult (VKAPI_PTR *PFN_vkNegotiateLoaderICDInterfaceVersion)(uint32_t *pVersion); // This is defined in vk_layer.h which will be found by the loader, but if an ICD is building against this // flie directly, it won't be found. #ifndef PFN_GetPhysicalDeviceProcAddr typedef PFN_vkVoidFunction (VKAPI_PTR *PFN_GetPhysicalDeviceProcAddr)(VkInstance instance, const char* pName); #endif /* * The ICD must reserve space for a pointer for the loader's dispatch * table, at the start of . * The ICD must initialize this variable using the SET_LOADER_MAGIC_VALUE macro. */ #define ICD_LOADER_MAGIC 0x01CDC0DE typedef union { uintptr_t loaderMagic; void *loaderData; } VK_LOADER_DATA; static inline void set_loader_magic_value(void *pNewObject) { VK_LOADER_DATA *loader_info = (VK_LOADER_DATA *)pNewObject; loader_info->loaderMagic = ICD_LOADER_MAGIC; } static inline bool valid_loader_magic_value(void *pNewObject) { const VK_LOADER_DATA *loader_info = (VK_LOADER_DATA *)pNewObject; return (loader_info->loaderMagic & 0xffffffff) == ICD_LOADER_MAGIC; } /* * Windows and Linux ICDs will treat VkSurfaceKHR as a pointer to a struct that * contains the platform-specific connection and surface information. */ typedef enum { VK_ICD_WSI_PLATFORM_MIR, VK_ICD_WSI_PLATFORM_WAYLAND, VK_ICD_WSI_PLATFORM_WIN32, VK_ICD_WSI_PLATFORM_XCB, VK_ICD_WSI_PLATFORM_XLIB, VK_ICD_WSI_PLATFORM_DISPLAY } VkIcdWsiPlatform; typedef struct { VkIcdWsiPlatform platform; } VkIcdSurfaceBase; #ifdef VK_USE_PLATFORM_MIR_KHR typedef struct { VkIcdSurfaceBase base; MirConnection *connection; MirSurface *mirSurface; } VkIcdSurfaceMir; #endif // VK_USE_PLATFORM_MIR_KHR #ifdef VK_USE_PLATFORM_WAYLAND_KHR typedef struct { VkIcdSurfaceBase base; struct wl_display *display; struct wl_surface *surface; } VkIcdSurfaceWayland; #endif // VK_USE_PLATFORM_WAYLAND_KHR #ifdef VK_USE_PLATFORM_WIN32_KHR typedef struct { VkIcdSurfaceBase base; HINSTANCE hinstance; HWND hwnd; } VkIcdSurfaceWin32; #endif // VK_USE_PLATFORM_WIN32_KHR #ifdef VK_USE_PLATFORM_XCB_KHR typedef struct { VkIcdSurfaceBase base; xcb_connection_t *connection; xcb_window_t window; } VkIcdSurfaceXcb; #endif // VK_USE_PLATFORM_XCB_KHR #ifdef VK_USE_PLATFORM_XLIB_KHR typedef struct { VkIcdSurfaceBase base; Display *dpy; Window window; } VkIcdSurfaceXlib; #endif // VK_USE_PLATFORM_XLIB_KHR #ifdef VK_USE_PLATFORM_ANDROID_KHR typedef struct { ANativeWindow* window; } VkIcdSurfaceAndroid; #endif //VK_USE_PLATFORM_ANDROID_KHR typedef struct { VkIcdSurfaceBase base; VkDisplayModeKHR displayMode; uint32_t planeIndex; uint32_t planeStackIndex; VkSurfaceTransformFlagBitsKHR transform; float globalAlpha; VkDisplayPlaneAlphaFlagBitsKHR alphaMode; VkExtent2D imageExtent; } VkIcdSurfaceDisplay; #endif // VKICD_H ================================================ FILE: src/engine/renderer/vulkan/vk_layer.h ================================================ // // File: vk_layer.h // /* * Copyright (c) 2015-2017 The Khronos Group Inc. * Copyright (c) 2015-2017 Valve Corporation * Copyright (c) 2015-2017 LunarG, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ /* Need to define dispatch table * Core struct can then have ptr to dispatch table at the top * Along with object ptrs for current and next OBJ */ #pragma once #include "vulkan.h" #if defined(__GNUC__) && __GNUC__ >= 4 #define VK_LAYER_EXPORT __attribute__((visibility("default"))) #elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590) #define VK_LAYER_EXPORT __attribute__((visibility("default"))) #else #define VK_LAYER_EXPORT #endif // Definition for VkLayerDispatchTable and VkLayerInstanceDispatchTable now appear in externally generated header #include "vk_layer_dispatch_table.h" #define MAX_NUM_UNKNOWN_EXTS 250 // Loader-Layer version negotiation API. Versions add the following features: // Versions 0/1 - Initial. Doesn't support vk_layerGetPhysicalDeviceProcAddr // or vk_icdNegotiateLoaderLayerInterfaceVersion. // Version 2 - Add support for vk_layerGetPhysicalDeviceProcAddr and // vk_icdNegotiateLoaderLayerInterfaceVersion. #define CURRENT_LOADER_LAYER_INTERFACE_VERSION 2 #define MIN_SUPPORTED_LOADER_LAYER_INTERFACE_VERSION 1 // Version negotiation values typedef enum VkNegotiateLayerStructType { LAYER_NEGOTIATE_UNINTIALIZED = 0, LAYER_NEGOTIATE_INTERFACE_STRUCT = 1, } VkNegotiateLayerStructType; // Version negotiation structures typedef struct VkNegotiateLayerInterface { VkNegotiateLayerStructType sType; void *pNext; uint32_t loaderLayerInterfaceVersion; PFN_vkGetInstanceProcAddr pfnGetInstanceProcAddr; PFN_vkGetDeviceProcAddr pfnGetDeviceProcAddr; PFN_GetPhysicalDeviceProcAddr pfnGetPhysicalDeviceProcAddr; } VkNegotiateLayerInterface; // Version negotiation functions typedef VkResult (VKAPI_PTR *PFN_vkNegotiateLoaderLayerInterfaceVersion)(VkNegotiateLayerInterface *pVersionStruct); // Function prototype for unknown physical device extension command typedef VkResult(VKAPI_PTR *PFN_PhysDevExt)(VkPhysicalDevice phys_device, ...); // ------------------------------------------------------------------------------------------------ // CreateInstance and CreateDevice support structures /* Sub type of structure for instance and device loader ext of CreateInfo. * When sType == VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO * or sType == VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO * then VkLayerFunction indicates struct type pointed to by pNext */ typedef enum VkLayerFunction_ { VK_LAYER_LINK_INFO = 0, VK_LOADER_DATA_CALLBACK = 1 } VkLayerFunction; typedef struct VkLayerInstanceLink_ { struct VkLayerInstanceLink_ *pNext; PFN_vkGetInstanceProcAddr pfnNextGetInstanceProcAddr; PFN_GetPhysicalDeviceProcAddr pfnNextGetPhysicalDeviceProcAddr; } VkLayerInstanceLink; /* * When creating the device chain the loader needs to pass * down information about it's device structure needed at * the end of the chain. Passing the data via the * VkLayerDeviceInfo avoids issues with finding the * exact instance being used. */ typedef struct VkLayerDeviceInfo_ { void *device_info; PFN_vkGetInstanceProcAddr pfnNextGetInstanceProcAddr; } VkLayerDeviceInfo; typedef VkResult (VKAPI_PTR *PFN_vkSetInstanceLoaderData)(VkInstance instance, void *object); typedef VkResult (VKAPI_PTR *PFN_vkSetDeviceLoaderData)(VkDevice device, void *object); typedef struct { VkStructureType sType; // VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO const void *pNext; VkLayerFunction function; union { VkLayerInstanceLink *pLayerInfo; PFN_vkSetInstanceLoaderData pfnSetInstanceLoaderData; } u; } VkLayerInstanceCreateInfo; typedef struct VkLayerDeviceLink_ { struct VkLayerDeviceLink_ *pNext; PFN_vkGetInstanceProcAddr pfnNextGetInstanceProcAddr; PFN_vkGetDeviceProcAddr pfnNextGetDeviceProcAddr; } VkLayerDeviceLink; typedef struct { VkStructureType sType; // VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO const void *pNext; VkLayerFunction function; union { VkLayerDeviceLink *pLayerInfo; PFN_vkSetDeviceLoaderData pfnSetDeviceLoaderData; } u; } VkLayerDeviceCreateInfo; #ifdef __cplusplus extern "C" { #endif VKAPI_ATTR VkResult VKAPI_CALL vkNegotiateLoaderLayerInterfaceVersion(VkNegotiateLayerInterface *pVersionStruct); #ifdef __cplusplus } #endif ================================================ FILE: src/engine/renderer/vulkan/vk_layer_dispatch_table.h ================================================ // *** THIS FILE IS GENERATED - DO NOT EDIT *** // See loader_extension_generator.py for modifications /* * Copyright (c) 2015-2017 The Khronos Group Inc. * Copyright (c) 2015-2017 Valve Corporation * Copyright (c) 2015-2017 LunarG, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Author: Mark Lobodzinski * Author: Mark Young */ #pragma once typedef PFN_vkVoidFunction (VKAPI_PTR *PFN_GetPhysicalDeviceProcAddr)(VkInstance instance, const char* pName); // Instance function pointer dispatch table typedef struct VkLayerInstanceDispatchTable_ { // Manually add in GetPhysicalDeviceProcAddr entry PFN_GetPhysicalDeviceProcAddr GetPhysicalDeviceProcAddr; // ---- Core 1_0 commands PFN_vkCreateInstance CreateInstance; PFN_vkDestroyInstance DestroyInstance; PFN_vkEnumeratePhysicalDevices EnumeratePhysicalDevices; PFN_vkGetPhysicalDeviceFeatures GetPhysicalDeviceFeatures; PFN_vkGetPhysicalDeviceFormatProperties GetPhysicalDeviceFormatProperties; PFN_vkGetPhysicalDeviceImageFormatProperties GetPhysicalDeviceImageFormatProperties; PFN_vkGetPhysicalDeviceProperties GetPhysicalDeviceProperties; PFN_vkGetPhysicalDeviceQueueFamilyProperties GetPhysicalDeviceQueueFamilyProperties; PFN_vkGetPhysicalDeviceMemoryProperties GetPhysicalDeviceMemoryProperties; PFN_vkGetInstanceProcAddr GetInstanceProcAddr; PFN_vkCreateDevice CreateDevice; PFN_vkEnumerateInstanceExtensionProperties EnumerateInstanceExtensionProperties; PFN_vkEnumerateDeviceExtensionProperties EnumerateDeviceExtensionProperties; PFN_vkEnumerateInstanceLayerProperties EnumerateInstanceLayerProperties; PFN_vkEnumerateDeviceLayerProperties EnumerateDeviceLayerProperties; PFN_vkGetPhysicalDeviceSparseImageFormatProperties GetPhysicalDeviceSparseImageFormatProperties; // ---- VK_KHR_surface extension commands PFN_vkDestroySurfaceKHR DestroySurfaceKHR; PFN_vkGetPhysicalDeviceSurfaceSupportKHR GetPhysicalDeviceSurfaceSupportKHR; PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR GetPhysicalDeviceSurfaceCapabilitiesKHR; PFN_vkGetPhysicalDeviceSurfaceFormatsKHR GetPhysicalDeviceSurfaceFormatsKHR; PFN_vkGetPhysicalDeviceSurfacePresentModesKHR GetPhysicalDeviceSurfacePresentModesKHR; // ---- VK_KHR_display extension commands PFN_vkGetPhysicalDeviceDisplayPropertiesKHR GetPhysicalDeviceDisplayPropertiesKHR; PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR GetPhysicalDeviceDisplayPlanePropertiesKHR; PFN_vkGetDisplayPlaneSupportedDisplaysKHR GetDisplayPlaneSupportedDisplaysKHR; PFN_vkGetDisplayModePropertiesKHR GetDisplayModePropertiesKHR; PFN_vkCreateDisplayModeKHR CreateDisplayModeKHR; PFN_vkGetDisplayPlaneCapabilitiesKHR GetDisplayPlaneCapabilitiesKHR; PFN_vkCreateDisplayPlaneSurfaceKHR CreateDisplayPlaneSurfaceKHR; // ---- VK_KHR_xlib_surface extension commands #ifdef VK_USE_PLATFORM_XLIB_KHR PFN_vkCreateXlibSurfaceKHR CreateXlibSurfaceKHR; #endif // VK_USE_PLATFORM_XLIB_KHR #ifdef VK_USE_PLATFORM_XLIB_KHR PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR GetPhysicalDeviceXlibPresentationSupportKHR; #endif // VK_USE_PLATFORM_XLIB_KHR // ---- VK_KHR_xcb_surface extension commands #ifdef VK_USE_PLATFORM_XCB_KHR PFN_vkCreateXcbSurfaceKHR CreateXcbSurfaceKHR; #endif // VK_USE_PLATFORM_XCB_KHR #ifdef VK_USE_PLATFORM_XCB_KHR PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR GetPhysicalDeviceXcbPresentationSupportKHR; #endif // VK_USE_PLATFORM_XCB_KHR // ---- VK_KHR_wayland_surface extension commands #ifdef VK_USE_PLATFORM_WAYLAND_KHR PFN_vkCreateWaylandSurfaceKHR CreateWaylandSurfaceKHR; #endif // VK_USE_PLATFORM_WAYLAND_KHR #ifdef VK_USE_PLATFORM_WAYLAND_KHR PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR GetPhysicalDeviceWaylandPresentationSupportKHR; #endif // VK_USE_PLATFORM_WAYLAND_KHR // ---- VK_KHR_mir_surface extension commands #ifdef VK_USE_PLATFORM_MIR_KHR PFN_vkCreateMirSurfaceKHR CreateMirSurfaceKHR; #endif // VK_USE_PLATFORM_MIR_KHR #ifdef VK_USE_PLATFORM_MIR_KHR PFN_vkGetPhysicalDeviceMirPresentationSupportKHR GetPhysicalDeviceMirPresentationSupportKHR; #endif // VK_USE_PLATFORM_MIR_KHR // ---- VK_KHR_android_surface extension commands #ifdef VK_USE_PLATFORM_ANDROID_KHR PFN_vkCreateAndroidSurfaceKHR CreateAndroidSurfaceKHR; #endif // VK_USE_PLATFORM_ANDROID_KHR // ---- VK_KHR_win32_surface extension commands #ifdef VK_USE_PLATFORM_WIN32_KHR PFN_vkCreateWin32SurfaceKHR CreateWin32SurfaceKHR; #endif // VK_USE_PLATFORM_WIN32_KHR #ifdef VK_USE_PLATFORM_WIN32_KHR PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR GetPhysicalDeviceWin32PresentationSupportKHR; #endif // VK_USE_PLATFORM_WIN32_KHR // ---- VK_KHR_get_physical_device_properties2 extension commands PFN_vkGetPhysicalDeviceFeatures2KHR GetPhysicalDeviceFeatures2KHR; PFN_vkGetPhysicalDeviceProperties2KHR GetPhysicalDeviceProperties2KHR; PFN_vkGetPhysicalDeviceFormatProperties2KHR GetPhysicalDeviceFormatProperties2KHR; PFN_vkGetPhysicalDeviceImageFormatProperties2KHR GetPhysicalDeviceImageFormatProperties2KHR; PFN_vkGetPhysicalDeviceQueueFamilyProperties2KHR GetPhysicalDeviceQueueFamilyProperties2KHR; PFN_vkGetPhysicalDeviceMemoryProperties2KHR GetPhysicalDeviceMemoryProperties2KHR; PFN_vkGetPhysicalDeviceSparseImageFormatProperties2KHR GetPhysicalDeviceSparseImageFormatProperties2KHR; // ---- VK_KHR_get_surface_capabilities2 extension commands PFN_vkGetPhysicalDeviceSurfaceCapabilities2KHR GetPhysicalDeviceSurfaceCapabilities2KHR; PFN_vkGetPhysicalDeviceSurfaceFormats2KHR GetPhysicalDeviceSurfaceFormats2KHR; // ---- VK_EXT_debug_report extension commands PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallbackEXT; PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallbackEXT; PFN_vkDebugReportMessageEXT DebugReportMessageEXT; // ---- VK_NV_external_memory_capabilities extension commands PFN_vkGetPhysicalDeviceExternalImageFormatPropertiesNV GetPhysicalDeviceExternalImageFormatPropertiesNV; // ---- VK_KHX_device_group extension commands PFN_vkGetPhysicalDevicePresentRectanglesKHX GetPhysicalDevicePresentRectanglesKHX; // ---- VK_NN_vi_surface extension commands #ifdef VK_USE_PLATFORM_VI_NN PFN_vkCreateViSurfaceNN CreateViSurfaceNN; #endif // VK_USE_PLATFORM_VI_NN // ---- VK_KHX_device_group_creation extension commands PFN_vkEnumeratePhysicalDeviceGroupsKHX EnumeratePhysicalDeviceGroupsKHX; // ---- VK_KHX_external_memory_capabilities extension commands PFN_vkGetPhysicalDeviceExternalBufferPropertiesKHX GetPhysicalDeviceExternalBufferPropertiesKHX; // ---- VK_KHX_external_semaphore_capabilities extension commands PFN_vkGetPhysicalDeviceExternalSemaphorePropertiesKHX GetPhysicalDeviceExternalSemaphorePropertiesKHX; // ---- VK_NVX_device_generated_commands extension commands PFN_vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX GetPhysicalDeviceGeneratedCommandsPropertiesNVX; // ---- VK_EXT_direct_mode_display extension commands PFN_vkReleaseDisplayEXT ReleaseDisplayEXT; // ---- VK_EXT_acquire_xlib_display extension commands #ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT PFN_vkAcquireXlibDisplayEXT AcquireXlibDisplayEXT; #endif // VK_USE_PLATFORM_XLIB_XRANDR_EXT #ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT PFN_vkGetRandROutputDisplayEXT GetRandROutputDisplayEXT; #endif // VK_USE_PLATFORM_XLIB_XRANDR_EXT // ---- VK_EXT_display_surface_counter extension commands PFN_vkGetPhysicalDeviceSurfaceCapabilities2EXT GetPhysicalDeviceSurfaceCapabilities2EXT; // ---- VK_MVK_ios_surface extension commands #ifdef VK_USE_PLATFORM_IOS_MVK PFN_vkCreateIOSSurfaceMVK CreateIOSSurfaceMVK; #endif // VK_USE_PLATFORM_IOS_MVK // ---- VK_MVK_macos_surface extension commands #ifdef VK_USE_PLATFORM_MACOS_MVK PFN_vkCreateMacOSSurfaceMVK CreateMacOSSurfaceMVK; #endif // VK_USE_PLATFORM_MACOS_MVK } VkLayerInstanceDispatchTable; // Device function pointer dispatch table typedef struct VkLayerDispatchTable_ { // ---- Core 1_0 commands PFN_vkGetDeviceProcAddr GetDeviceProcAddr; PFN_vkDestroyDevice DestroyDevice; PFN_vkGetDeviceQueue GetDeviceQueue; PFN_vkQueueSubmit QueueSubmit; PFN_vkQueueWaitIdle QueueWaitIdle; PFN_vkDeviceWaitIdle DeviceWaitIdle; PFN_vkAllocateMemory AllocateMemory; PFN_vkFreeMemory FreeMemory; PFN_vkMapMemory MapMemory; PFN_vkUnmapMemory UnmapMemory; PFN_vkFlushMappedMemoryRanges FlushMappedMemoryRanges; PFN_vkInvalidateMappedMemoryRanges InvalidateMappedMemoryRanges; PFN_vkGetDeviceMemoryCommitment GetDeviceMemoryCommitment; PFN_vkBindBufferMemory BindBufferMemory; PFN_vkBindImageMemory BindImageMemory; PFN_vkGetBufferMemoryRequirements GetBufferMemoryRequirements; PFN_vkGetImageMemoryRequirements GetImageMemoryRequirements; PFN_vkGetImageSparseMemoryRequirements GetImageSparseMemoryRequirements; PFN_vkQueueBindSparse QueueBindSparse; PFN_vkCreateFence CreateFence; PFN_vkDestroyFence DestroyFence; PFN_vkResetFences ResetFences; PFN_vkGetFenceStatus GetFenceStatus; PFN_vkWaitForFences WaitForFences; PFN_vkCreateSemaphore CreateSemaphore; PFN_vkDestroySemaphore DestroySemaphore; PFN_vkCreateEvent CreateEvent; PFN_vkDestroyEvent DestroyEvent; PFN_vkGetEventStatus GetEventStatus; PFN_vkSetEvent SetEvent; PFN_vkResetEvent ResetEvent; PFN_vkCreateQueryPool CreateQueryPool; PFN_vkDestroyQueryPool DestroyQueryPool; PFN_vkGetQueryPoolResults GetQueryPoolResults; PFN_vkCreateBuffer CreateBuffer; PFN_vkDestroyBuffer DestroyBuffer; PFN_vkCreateBufferView CreateBufferView; PFN_vkDestroyBufferView DestroyBufferView; PFN_vkCreateImage CreateImage; PFN_vkDestroyImage DestroyImage; PFN_vkGetImageSubresourceLayout GetImageSubresourceLayout; PFN_vkCreateImageView CreateImageView; PFN_vkDestroyImageView DestroyImageView; PFN_vkCreateShaderModule CreateShaderModule; PFN_vkDestroyShaderModule DestroyShaderModule; PFN_vkCreatePipelineCache CreatePipelineCache; PFN_vkDestroyPipelineCache DestroyPipelineCache; PFN_vkGetPipelineCacheData GetPipelineCacheData; PFN_vkMergePipelineCaches MergePipelineCaches; PFN_vkCreateGraphicsPipelines CreateGraphicsPipelines; PFN_vkCreateComputePipelines CreateComputePipelines; PFN_vkDestroyPipeline DestroyPipeline; PFN_vkCreatePipelineLayout CreatePipelineLayout; PFN_vkDestroyPipelineLayout DestroyPipelineLayout; PFN_vkCreateSampler CreateSampler; PFN_vkDestroySampler DestroySampler; PFN_vkCreateDescriptorSetLayout CreateDescriptorSetLayout; PFN_vkDestroyDescriptorSetLayout DestroyDescriptorSetLayout; PFN_vkCreateDescriptorPool CreateDescriptorPool; PFN_vkDestroyDescriptorPool DestroyDescriptorPool; PFN_vkResetDescriptorPool ResetDescriptorPool; PFN_vkAllocateDescriptorSets AllocateDescriptorSets; PFN_vkFreeDescriptorSets FreeDescriptorSets; PFN_vkUpdateDescriptorSets UpdateDescriptorSets; PFN_vkCreateFramebuffer CreateFramebuffer; PFN_vkDestroyFramebuffer DestroyFramebuffer; PFN_vkCreateRenderPass CreateRenderPass; PFN_vkDestroyRenderPass DestroyRenderPass; PFN_vkGetRenderAreaGranularity GetRenderAreaGranularity; PFN_vkCreateCommandPool CreateCommandPool; PFN_vkDestroyCommandPool DestroyCommandPool; PFN_vkResetCommandPool ResetCommandPool; PFN_vkAllocateCommandBuffers AllocateCommandBuffers; PFN_vkFreeCommandBuffers FreeCommandBuffers; PFN_vkBeginCommandBuffer BeginCommandBuffer; PFN_vkEndCommandBuffer EndCommandBuffer; PFN_vkResetCommandBuffer ResetCommandBuffer; PFN_vkCmdBindPipeline CmdBindPipeline; PFN_vkCmdSetViewport CmdSetViewport; PFN_vkCmdSetScissor CmdSetScissor; PFN_vkCmdSetLineWidth CmdSetLineWidth; PFN_vkCmdSetDepthBias CmdSetDepthBias; PFN_vkCmdSetBlendConstants CmdSetBlendConstants; PFN_vkCmdSetDepthBounds CmdSetDepthBounds; PFN_vkCmdSetStencilCompareMask CmdSetStencilCompareMask; PFN_vkCmdSetStencilWriteMask CmdSetStencilWriteMask; PFN_vkCmdSetStencilReference CmdSetStencilReference; PFN_vkCmdBindDescriptorSets CmdBindDescriptorSets; PFN_vkCmdBindIndexBuffer CmdBindIndexBuffer; PFN_vkCmdBindVertexBuffers CmdBindVertexBuffers; PFN_vkCmdDraw CmdDraw; PFN_vkCmdDrawIndexed CmdDrawIndexed; PFN_vkCmdDrawIndirect CmdDrawIndirect; PFN_vkCmdDrawIndexedIndirect CmdDrawIndexedIndirect; PFN_vkCmdDispatch CmdDispatch; PFN_vkCmdDispatchIndirect CmdDispatchIndirect; PFN_vkCmdCopyBuffer CmdCopyBuffer; PFN_vkCmdCopyImage CmdCopyImage; PFN_vkCmdBlitImage CmdBlitImage; PFN_vkCmdCopyBufferToImage CmdCopyBufferToImage; PFN_vkCmdCopyImageToBuffer CmdCopyImageToBuffer; PFN_vkCmdUpdateBuffer CmdUpdateBuffer; PFN_vkCmdFillBuffer CmdFillBuffer; PFN_vkCmdClearColorImage CmdClearColorImage; PFN_vkCmdClearDepthStencilImage CmdClearDepthStencilImage; PFN_vkCmdClearAttachments CmdClearAttachments; PFN_vkCmdResolveImage CmdResolveImage; PFN_vkCmdSetEvent CmdSetEvent; PFN_vkCmdResetEvent CmdResetEvent; PFN_vkCmdWaitEvents CmdWaitEvents; PFN_vkCmdPipelineBarrier CmdPipelineBarrier; PFN_vkCmdBeginQuery CmdBeginQuery; PFN_vkCmdEndQuery CmdEndQuery; PFN_vkCmdResetQueryPool CmdResetQueryPool; PFN_vkCmdWriteTimestamp CmdWriteTimestamp; PFN_vkCmdCopyQueryPoolResults CmdCopyQueryPoolResults; PFN_vkCmdPushConstants CmdPushConstants; PFN_vkCmdBeginRenderPass CmdBeginRenderPass; PFN_vkCmdNextSubpass CmdNextSubpass; PFN_vkCmdEndRenderPass CmdEndRenderPass; PFN_vkCmdExecuteCommands CmdExecuteCommands; // ---- VK_KHR_swapchain extension commands PFN_vkCreateSwapchainKHR CreateSwapchainKHR; PFN_vkDestroySwapchainKHR DestroySwapchainKHR; PFN_vkGetSwapchainImagesKHR GetSwapchainImagesKHR; PFN_vkAcquireNextImageKHR AcquireNextImageKHR; PFN_vkQueuePresentKHR QueuePresentKHR; // ---- VK_KHR_display_swapchain extension commands PFN_vkCreateSharedSwapchainsKHR CreateSharedSwapchainsKHR; // ---- VK_KHR_maintenance1 extension commands PFN_vkTrimCommandPoolKHR TrimCommandPoolKHR; // ---- VK_KHR_push_descriptor extension commands PFN_vkCmdPushDescriptorSetKHR CmdPushDescriptorSetKHR; // ---- VK_KHR_descriptor_update_template extension commands PFN_vkCreateDescriptorUpdateTemplateKHR CreateDescriptorUpdateTemplateKHR; PFN_vkDestroyDescriptorUpdateTemplateKHR DestroyDescriptorUpdateTemplateKHR; PFN_vkUpdateDescriptorSetWithTemplateKHR UpdateDescriptorSetWithTemplateKHR; PFN_vkCmdPushDescriptorSetWithTemplateKHR CmdPushDescriptorSetWithTemplateKHR; // ---- VK_KHR_shared_presentable_image extension commands PFN_vkGetSwapchainStatusKHR GetSwapchainStatusKHR; // ---- VK_EXT_debug_marker extension commands PFN_vkDebugMarkerSetObjectTagEXT DebugMarkerSetObjectTagEXT; PFN_vkDebugMarkerSetObjectNameEXT DebugMarkerSetObjectNameEXT; PFN_vkCmdDebugMarkerBeginEXT CmdDebugMarkerBeginEXT; PFN_vkCmdDebugMarkerEndEXT CmdDebugMarkerEndEXT; PFN_vkCmdDebugMarkerInsertEXT CmdDebugMarkerInsertEXT; // ---- VK_AMD_draw_indirect_count extension commands PFN_vkCmdDrawIndirectCountAMD CmdDrawIndirectCountAMD; PFN_vkCmdDrawIndexedIndirectCountAMD CmdDrawIndexedIndirectCountAMD; // ---- VK_NV_external_memory_win32 extension commands #ifdef VK_USE_PLATFORM_WIN32_KHR PFN_vkGetMemoryWin32HandleNV GetMemoryWin32HandleNV; #endif // VK_USE_PLATFORM_WIN32_KHR // ---- VK_KHX_device_group extension commands PFN_vkGetDeviceGroupPeerMemoryFeaturesKHX GetDeviceGroupPeerMemoryFeaturesKHX; PFN_vkBindBufferMemory2KHX BindBufferMemory2KHX; PFN_vkBindImageMemory2KHX BindImageMemory2KHX; PFN_vkCmdSetDeviceMaskKHX CmdSetDeviceMaskKHX; PFN_vkGetDeviceGroupPresentCapabilitiesKHX GetDeviceGroupPresentCapabilitiesKHX; PFN_vkGetDeviceGroupSurfacePresentModesKHX GetDeviceGroupSurfacePresentModesKHX; PFN_vkAcquireNextImage2KHX AcquireNextImage2KHX; PFN_vkCmdDispatchBaseKHX CmdDispatchBaseKHX; // ---- VK_KHX_external_memory_win32 extension commands #ifdef VK_USE_PLATFORM_WIN32_KHX PFN_vkGetMemoryWin32HandleKHX GetMemoryWin32HandleKHX; #endif // VK_USE_PLATFORM_WIN32_KHX #ifdef VK_USE_PLATFORM_WIN32_KHX PFN_vkGetMemoryWin32HandlePropertiesKHX GetMemoryWin32HandlePropertiesKHX; #endif // VK_USE_PLATFORM_WIN32_KHX // ---- VK_KHX_external_memory_fd extension commands PFN_vkGetMemoryFdKHX GetMemoryFdKHX; PFN_vkGetMemoryFdPropertiesKHX GetMemoryFdPropertiesKHX; // ---- VK_KHX_external_semaphore_win32 extension commands #ifdef VK_USE_PLATFORM_WIN32_KHX PFN_vkImportSemaphoreWin32HandleKHX ImportSemaphoreWin32HandleKHX; #endif // VK_USE_PLATFORM_WIN32_KHX #ifdef VK_USE_PLATFORM_WIN32_KHX PFN_vkGetSemaphoreWin32HandleKHX GetSemaphoreWin32HandleKHX; #endif // VK_USE_PLATFORM_WIN32_KHX // ---- VK_KHX_external_semaphore_fd extension commands PFN_vkImportSemaphoreFdKHX ImportSemaphoreFdKHX; PFN_vkGetSemaphoreFdKHX GetSemaphoreFdKHX; // ---- VK_NVX_device_generated_commands extension commands PFN_vkCmdProcessCommandsNVX CmdProcessCommandsNVX; PFN_vkCmdReserveSpaceForCommandsNVX CmdReserveSpaceForCommandsNVX; PFN_vkCreateIndirectCommandsLayoutNVX CreateIndirectCommandsLayoutNVX; PFN_vkDestroyIndirectCommandsLayoutNVX DestroyIndirectCommandsLayoutNVX; PFN_vkCreateObjectTableNVX CreateObjectTableNVX; PFN_vkDestroyObjectTableNVX DestroyObjectTableNVX; PFN_vkRegisterObjectsNVX RegisterObjectsNVX; PFN_vkUnregisterObjectsNVX UnregisterObjectsNVX; // ---- VK_NV_clip_space_w_scaling extension commands PFN_vkCmdSetViewportWScalingNV CmdSetViewportWScalingNV; // ---- VK_EXT_display_control extension commands PFN_vkDisplayPowerControlEXT DisplayPowerControlEXT; PFN_vkRegisterDeviceEventEXT RegisterDeviceEventEXT; PFN_vkRegisterDisplayEventEXT RegisterDisplayEventEXT; PFN_vkGetSwapchainCounterEXT GetSwapchainCounterEXT; // ---- VK_GOOGLE_display_timing extension commands PFN_vkGetRefreshCycleDurationGOOGLE GetRefreshCycleDurationGOOGLE; PFN_vkGetPastPresentationTimingGOOGLE GetPastPresentationTimingGOOGLE; // ---- VK_EXT_discard_rectangles extension commands PFN_vkCmdSetDiscardRectangleEXT CmdSetDiscardRectangleEXT; // ---- VK_EXT_hdr_metadata extension commands PFN_vkSetHdrMetadataEXT SetHdrMetadataEXT; } VkLayerDispatchTable; ================================================ FILE: src/engine/renderer/vulkan/vk_platform.h ================================================ // // File: vk_platform.h // /* ** Copyright (c) 2014-2017 The Khronos Group Inc. ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. */ #ifndef VK_PLATFORM_H_ #define VK_PLATFORM_H_ #ifdef __cplusplus extern "C" { #endif // __cplusplus /* *************************************************************************************************** * Platform-specific directives and type declarations *************************************************************************************************** */ /* Platform-specific calling convention macros. * * Platforms should define these so that Vulkan clients call Vulkan commands * with the same calling conventions that the Vulkan implementation expects. * * VKAPI_ATTR - Placed before the return type in function declarations. * Useful for C++11 and GCC/Clang-style function attribute syntax. * VKAPI_CALL - Placed after the return type in function declarations. * Useful for MSVC-style calling convention syntax. * VKAPI_PTR - Placed between the '(' and '*' in function pointer types. * * Function declaration: VKAPI_ATTR void VKAPI_CALL vkCommand(void); * Function pointer type: typedef void (VKAPI_PTR *PFN_vkCommand)(void); */ #if defined(_WIN32) // On Windows, Vulkan commands use the stdcall convention #define VKAPI_ATTR #define VKAPI_CALL __stdcall #define VKAPI_PTR VKAPI_CALL #elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH < 7 #error "Vulkan isn't supported for the 'armeabi' NDK ABI" #elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7 && defined(__ARM_32BIT_STATE) // On Android 32-bit ARM targets, Vulkan functions use the "hardfloat" // calling convention, i.e. float parameters are passed in registers. This // is true even if the rest of the application passes floats on the stack, // as it does by default when compiling for the armeabi-v7a NDK ABI. #define VKAPI_ATTR __attribute__((pcs("aapcs-vfp"))) #define VKAPI_CALL #define VKAPI_PTR VKAPI_ATTR #else // On other platforms, use the default calling convention #define VKAPI_ATTR #define VKAPI_CALL #define VKAPI_PTR #endif #include #if !defined(VK_NO_STDINT_H) #if defined(_MSC_VER) && (_MSC_VER < 1600) typedef signed __int8 int8_t; typedef unsigned __int8 uint8_t; typedef signed __int16 int16_t; typedef unsigned __int16 uint16_t; typedef signed __int32 int32_t; typedef unsigned __int32 uint32_t; typedef signed __int64 int64_t; typedef unsigned __int64 uint64_t; #else #include #endif #endif // !defined(VK_NO_STDINT_H) #ifdef __cplusplus } // extern "C" #endif // __cplusplus // Platform-specific headers required by platform window system extensions. // These are enabled prior to #including "vulkan.h". The same enable then // controls inclusion of the extension interfaces in vulkan.h. #ifdef VK_USE_PLATFORM_ANDROID_KHR #include #endif #ifdef VK_USE_PLATFORM_MIR_KHR #include #endif #ifdef VK_USE_PLATFORM_WAYLAND_KHR #include #endif #ifdef VK_USE_PLATFORM_WIN32_KHR #include #endif #ifdef VK_USE_PLATFORM_XLIB_KHR #include #endif #ifdef VK_USE_PLATFORM_XCB_KHR #include #endif #endif ================================================ FILE: src/engine/renderer/vulkan/vk_sdk_platform.h ================================================ // // File: vk_sdk_platform.h // /* * Copyright (c) 2015-2016 The Khronos Group Inc. * Copyright (c) 2015-2016 Valve Corporation * Copyright (c) 2015-2016 LunarG, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef VK_SDK_PLATFORM_H #define VK_SDK_PLATFORM_H #if defined(_WIN32) #define NOMINMAX #ifndef __cplusplus #undef inline #define inline __inline #endif // __cplusplus #if (defined(_MSC_VER) && _MSC_VER < 1900 /*vs2015*/) // C99: // Microsoft didn't implement C99 in Visual Studio; but started adding it with // VS2013. However, VS2013 still didn't have snprintf(). The following is a // work-around (Note: The _CRT_SECURE_NO_WARNINGS macro must be set in the // "CMakeLists.txt" file). // NOTE: This is fixed in Visual Studio 2015. #define snprintf _snprintf #endif #define strdup _strdup #endif // _WIN32 #endif // VK_SDK_PLATFORM_H ================================================ FILE: src/engine/renderer/vulkan/vulkan.h ================================================ #ifndef VULKAN_H_ #define VULKAN_H_ 1 #ifdef __cplusplus extern "C" { #endif /* ** Copyright (c) 2015-2017 The Khronos Group Inc. ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. */ /* ** This header is generated from the Khronos Vulkan XML API Registry. ** */ #define VK_VERSION_1_0 1 #include "vk_platform.h" #define VK_MAKE_VERSION(major, minor, patch) \ (((major) << 22) | ((minor) << 12) | (patch)) // DEPRECATED: This define has been removed. Specific version defines (e.g. VK_API_VERSION_1_0), or the VK_MAKE_VERSION macro, should be used instead. //#define VK_API_VERSION VK_MAKE_VERSION(1, 0, 0) // Vulkan 1.0 version number #define VK_API_VERSION_1_0 VK_MAKE_VERSION(1, 0, 0) #define VK_VERSION_MAJOR(version) ((uint32_t)(version) >> 22) #define VK_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3ff) #define VK_VERSION_PATCH(version) ((uint32_t)(version) & 0xfff) // Version of this file #define VK_HEADER_VERSION 49 #define VK_NULL_HANDLE 0 #define VK_DEFINE_HANDLE(object) typedef struct object##_T* object; #if !defined(VK_DEFINE_NON_DISPATCHABLE_HANDLE) #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef struct object##_T *object; #else #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object; #endif #endif typedef uint32_t VkFlags; typedef uint32_t VkBool32; typedef uint64_t VkDeviceSize; typedef uint32_t VkSampleMask; VK_DEFINE_HANDLE(VkInstance) VK_DEFINE_HANDLE(VkPhysicalDevice) VK_DEFINE_HANDLE(VkDevice) VK_DEFINE_HANDLE(VkQueue) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSemaphore) VK_DEFINE_HANDLE(VkCommandBuffer) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkFence) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDeviceMemory) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkBuffer) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkImage) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkEvent) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkQueryPool) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkBufferView) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkImageView) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkShaderModule) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkPipelineCache) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkPipelineLayout) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkRenderPass) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkPipeline) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDescriptorSetLayout) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSampler) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDescriptorPool) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDescriptorSet) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkFramebuffer) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkCommandPool) #define VK_LOD_CLAMP_NONE 1000.0f #define VK_REMAINING_MIP_LEVELS (~0U) #define VK_REMAINING_ARRAY_LAYERS (~0U) #define VK_WHOLE_SIZE (~0ULL) #define VK_ATTACHMENT_UNUSED (~0U) #define VK_TRUE 1 #define VK_FALSE 0 #define VK_QUEUE_FAMILY_IGNORED (~0U) #define VK_SUBPASS_EXTERNAL (~0U) #define VK_MAX_PHYSICAL_DEVICE_NAME_SIZE 256 #define VK_UUID_SIZE 16 #define VK_MAX_MEMORY_TYPES 32 #define VK_MAX_MEMORY_HEAPS 16 #define VK_MAX_EXTENSION_NAME_SIZE 256 #define VK_MAX_DESCRIPTION_SIZE 256 typedef enum VkPipelineCacheHeaderVersion { VK_PIPELINE_CACHE_HEADER_VERSION_ONE = 1, VK_PIPELINE_CACHE_HEADER_VERSION_BEGIN_RANGE = VK_PIPELINE_CACHE_HEADER_VERSION_ONE, VK_PIPELINE_CACHE_HEADER_VERSION_END_RANGE = VK_PIPELINE_CACHE_HEADER_VERSION_ONE, VK_PIPELINE_CACHE_HEADER_VERSION_RANGE_SIZE = (VK_PIPELINE_CACHE_HEADER_VERSION_ONE - VK_PIPELINE_CACHE_HEADER_VERSION_ONE + 1), VK_PIPELINE_CACHE_HEADER_VERSION_MAX_ENUM = 0x7FFFFFFF } VkPipelineCacheHeaderVersion; typedef enum VkResult { VK_SUCCESS = 0, VK_NOT_READY = 1, VK_TIMEOUT = 2, VK_EVENT_SET = 3, VK_EVENT_RESET = 4, VK_INCOMPLETE = 5, VK_ERROR_OUT_OF_HOST_MEMORY = -1, VK_ERROR_OUT_OF_DEVICE_MEMORY = -2, VK_ERROR_INITIALIZATION_FAILED = -3, VK_ERROR_DEVICE_LOST = -4, VK_ERROR_MEMORY_MAP_FAILED = -5, VK_ERROR_LAYER_NOT_PRESENT = -6, VK_ERROR_EXTENSION_NOT_PRESENT = -7, VK_ERROR_FEATURE_NOT_PRESENT = -8, VK_ERROR_INCOMPATIBLE_DRIVER = -9, VK_ERROR_TOO_MANY_OBJECTS = -10, VK_ERROR_FORMAT_NOT_SUPPORTED = -11, VK_ERROR_FRAGMENTED_POOL = -12, VK_ERROR_SURFACE_LOST_KHR = -1000000000, VK_ERROR_NATIVE_WINDOW_IN_USE_KHR = -1000000001, VK_SUBOPTIMAL_KHR = 1000001003, VK_ERROR_OUT_OF_DATE_KHR = -1000001004, VK_ERROR_INCOMPATIBLE_DISPLAY_KHR = -1000003001, VK_ERROR_VALIDATION_FAILED_EXT = -1000011001, VK_ERROR_INVALID_SHADER_NV = -1000012000, VK_ERROR_OUT_OF_POOL_MEMORY_KHR = -1000069000, VK_ERROR_INVALID_EXTERNAL_HANDLE_KHX = -1000072003, VK_RESULT_BEGIN_RANGE = VK_ERROR_FRAGMENTED_POOL, VK_RESULT_END_RANGE = VK_INCOMPLETE, VK_RESULT_RANGE_SIZE = (VK_INCOMPLETE - VK_ERROR_FRAGMENTED_POOL + 1), VK_RESULT_MAX_ENUM = 0x7FFFFFFF } VkResult; typedef enum VkStructureType { VK_STRUCTURE_TYPE_APPLICATION_INFO = 0, VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO = 1, VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO = 2, VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO = 3, VK_STRUCTURE_TYPE_SUBMIT_INFO = 4, VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO = 5, VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE = 6, VK_STRUCTURE_TYPE_BIND_SPARSE_INFO = 7, VK_STRUCTURE_TYPE_FENCE_CREATE_INFO = 8, VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO = 9, VK_STRUCTURE_TYPE_EVENT_CREATE_INFO = 10, VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO = 11, VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO = 12, VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO = 13, VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO = 14, VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO = 15, VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO = 16, VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO = 17, VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO = 18, VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO = 19, VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO = 20, VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO = 21, VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO = 22, VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO = 23, VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO = 24, VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO = 25, VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO = 26, VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO = 27, VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO = 28, VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO = 29, VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO = 30, VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO = 31, VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO = 32, VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO = 33, VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO = 34, VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET = 35, VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET = 36, VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO = 37, VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO = 38, VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO = 39, VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO = 40, VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO = 41, VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO = 42, VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO = 43, VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER = 44, VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER = 45, VK_STRUCTURE_TYPE_MEMORY_BARRIER = 46, VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO = 47, VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO = 48, VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR = 1000001000, VK_STRUCTURE_TYPE_PRESENT_INFO_KHR = 1000001001, VK_STRUCTURE_TYPE_DISPLAY_MODE_CREATE_INFO_KHR = 1000002000, VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR = 1000002001, VK_STRUCTURE_TYPE_DISPLAY_PRESENT_INFO_KHR = 1000003000, VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR = 1000004000, VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR = 1000005000, VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR = 1000006000, VK_STRUCTURE_TYPE_MIR_SURFACE_CREATE_INFO_KHR = 1000007000, VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR = 1000008000, VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000, VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT = 1000011000, VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_RASTERIZATION_ORDER_AMD = 1000018000, VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT = 1000022000, VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_TAG_INFO_EXT = 1000022001, VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT = 1000022002, VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_IMAGE_CREATE_INFO_NV = 1000026000, VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_BUFFER_CREATE_INFO_NV = 1000026001, VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_MEMORY_ALLOCATE_INFO_NV = 1000026002, VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO_KHX = 1000053000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES_KHX = 1000053001, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES_KHX = 1000053002, VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_NV = 1000056000, VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_NV = 1000056001, VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_NV = 1000057000, VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_NV = 1000057001, VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_NV = 1000058000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR = 1000059000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR = 1000059001, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR = 1000059002, VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR = 1000059003, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR = 1000059004, VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2_KHR = 1000059005, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR = 1000059006, VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2_KHR = 1000059007, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2_KHR = 1000059008, VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHX = 1000060000, VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHX = 1000060001, VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHX = 1000060002, VK_STRUCTURE_TYPE_DEVICE_GROUP_RENDER_PASS_BEGIN_INFO_KHX = 1000060003, VK_STRUCTURE_TYPE_DEVICE_GROUP_COMMAND_BUFFER_BEGIN_INFO_KHX = 1000060004, VK_STRUCTURE_TYPE_DEVICE_GROUP_SUBMIT_INFO_KHX = 1000060005, VK_STRUCTURE_TYPE_DEVICE_GROUP_BIND_SPARSE_INFO_KHX = 1000060006, VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_CAPABILITIES_KHX = 1000060007, VK_STRUCTURE_TYPE_IMAGE_SWAPCHAIN_CREATE_INFO_KHX = 1000060008, VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_SWAPCHAIN_INFO_KHX = 1000060009, VK_STRUCTURE_TYPE_ACQUIRE_NEXT_IMAGE_INFO_KHX = 1000060010, VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_INFO_KHX = 1000060011, VK_STRUCTURE_TYPE_DEVICE_GROUP_SWAPCHAIN_CREATE_INFO_KHX = 1000060012, VK_STRUCTURE_TYPE_VALIDATION_FLAGS_EXT = 1000061000, VK_STRUCTURE_TYPE_VI_SURFACE_CREATE_INFO_NN = 1000062000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES_KHX = 1000070000, VK_STRUCTURE_TYPE_DEVICE_GROUP_DEVICE_CREATE_INFO_KHX = 1000070001, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO_KHX = 1000071000, VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES_KHX = 1000071001, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO_KHX = 1000071002, VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES_KHX = 1000071003, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES_KHX = 1000071004, VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHX = 1000072000, VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHX = 1000072001, VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHX = 1000072002, VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHX = 1000073000, VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_KHX = 1000073001, VK_STRUCTURE_TYPE_MEMORY_WIN32_HANDLE_PROPERTIES_KHX = 1000073002, VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHX = 1000074000, VK_STRUCTURE_TYPE_MEMORY_FD_PROPERTIES_KHX = 1000074001, VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_KHX = 1000075000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO_KHX = 1000076000, VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES_KHX = 1000076001, VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO_KHX = 1000077000, VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHX = 1000078000, VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHX = 1000078001, VK_STRUCTURE_TYPE_D3D12_FENCE_SUBMIT_INFO_KHX = 1000078002, VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHX = 1000079000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR = 1000080000, VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR = 1000084000, VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR = 1000085000, VK_STRUCTURE_TYPE_OBJECT_TABLE_CREATE_INFO_NVX = 1000086000, VK_STRUCTURE_TYPE_INDIRECT_COMMANDS_LAYOUT_CREATE_INFO_NVX = 1000086001, VK_STRUCTURE_TYPE_CMD_PROCESS_COMMANDS_INFO_NVX = 1000086002, VK_STRUCTURE_TYPE_CMD_RESERVE_SPACE_FOR_COMMANDS_INFO_NVX = 1000086003, VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_LIMITS_NVX = 1000086004, VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_FEATURES_NVX = 1000086005, VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_W_SCALING_STATE_CREATE_INFO_NV = 1000087000, VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES2_EXT = 1000090000, VK_STRUCTURE_TYPE_DISPLAY_POWER_INFO_EXT = 1000091000, VK_STRUCTURE_TYPE_DEVICE_EVENT_INFO_EXT = 1000091001, VK_STRUCTURE_TYPE_DISPLAY_EVENT_INFO_EXT = 1000091002, VK_STRUCTURE_TYPE_SWAPCHAIN_COUNTER_CREATE_INFO_EXT = 1000091003, VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE = 1000092000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PER_VIEW_ATTRIBUTES_PROPERTIES_NVX = 1000097000, VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_SWIZZLE_STATE_CREATE_INFO_NV = 1000098000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DISCARD_RECTANGLE_PROPERTIES_EXT = 1000099000, VK_STRUCTURE_TYPE_PIPELINE_DISCARD_RECTANGLE_STATE_CREATE_INFO_EXT = 1000099001, VK_STRUCTURE_TYPE_HDR_METADATA_EXT = 1000105000, VK_STRUCTURE_TYPE_SHARED_PRESENT_SURFACE_CAPABILITIES_KHR = 1000111000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR = 1000119000, VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR = 1000119001, VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR = 1000119002, VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK = 1000122000, VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK = 1000123000, VK_STRUCTURE_TYPE_BEGIN_RANGE = VK_STRUCTURE_TYPE_APPLICATION_INFO, VK_STRUCTURE_TYPE_END_RANGE = VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO, VK_STRUCTURE_TYPE_RANGE_SIZE = (VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO - VK_STRUCTURE_TYPE_APPLICATION_INFO + 1), VK_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF } VkStructureType; typedef enum VkSystemAllocationScope { VK_SYSTEM_ALLOCATION_SCOPE_COMMAND = 0, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT = 1, VK_SYSTEM_ALLOCATION_SCOPE_CACHE = 2, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE = 3, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE = 4, VK_SYSTEM_ALLOCATION_SCOPE_BEGIN_RANGE = VK_SYSTEM_ALLOCATION_SCOPE_COMMAND, VK_SYSTEM_ALLOCATION_SCOPE_END_RANGE = VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE, VK_SYSTEM_ALLOCATION_SCOPE_RANGE_SIZE = (VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE - VK_SYSTEM_ALLOCATION_SCOPE_COMMAND + 1), VK_SYSTEM_ALLOCATION_SCOPE_MAX_ENUM = 0x7FFFFFFF } VkSystemAllocationScope; typedef enum VkInternalAllocationType { VK_INTERNAL_ALLOCATION_TYPE_EXECUTABLE = 0, VK_INTERNAL_ALLOCATION_TYPE_BEGIN_RANGE = VK_INTERNAL_ALLOCATION_TYPE_EXECUTABLE, VK_INTERNAL_ALLOCATION_TYPE_END_RANGE = VK_INTERNAL_ALLOCATION_TYPE_EXECUTABLE, VK_INTERNAL_ALLOCATION_TYPE_RANGE_SIZE = (VK_INTERNAL_ALLOCATION_TYPE_EXECUTABLE - VK_INTERNAL_ALLOCATION_TYPE_EXECUTABLE + 1), VK_INTERNAL_ALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF } VkInternalAllocationType; typedef enum VkFormat { VK_FORMAT_UNDEFINED = 0, VK_FORMAT_R4G4_UNORM_PACK8 = 1, VK_FORMAT_R4G4B4A4_UNORM_PACK16 = 2, VK_FORMAT_B4G4R4A4_UNORM_PACK16 = 3, VK_FORMAT_R5G6B5_UNORM_PACK16 = 4, VK_FORMAT_B5G6R5_UNORM_PACK16 = 5, VK_FORMAT_R5G5B5A1_UNORM_PACK16 = 6, VK_FORMAT_B5G5R5A1_UNORM_PACK16 = 7, VK_FORMAT_A1R5G5B5_UNORM_PACK16 = 8, VK_FORMAT_R8_UNORM = 9, VK_FORMAT_R8_SNORM = 10, VK_FORMAT_R8_USCALED = 11, VK_FORMAT_R8_SSCALED = 12, VK_FORMAT_R8_UINT = 13, VK_FORMAT_R8_SINT = 14, VK_FORMAT_R8_SRGB = 15, VK_FORMAT_R8G8_UNORM = 16, VK_FORMAT_R8G8_SNORM = 17, VK_FORMAT_R8G8_USCALED = 18, VK_FORMAT_R8G8_SSCALED = 19, VK_FORMAT_R8G8_UINT = 20, VK_FORMAT_R8G8_SINT = 21, VK_FORMAT_R8G8_SRGB = 22, VK_FORMAT_R8G8B8_UNORM = 23, VK_FORMAT_R8G8B8_SNORM = 24, VK_FORMAT_R8G8B8_USCALED = 25, VK_FORMAT_R8G8B8_SSCALED = 26, VK_FORMAT_R8G8B8_UINT = 27, VK_FORMAT_R8G8B8_SINT = 28, VK_FORMAT_R8G8B8_SRGB = 29, VK_FORMAT_B8G8R8_UNORM = 30, VK_FORMAT_B8G8R8_SNORM = 31, VK_FORMAT_B8G8R8_USCALED = 32, VK_FORMAT_B8G8R8_SSCALED = 33, VK_FORMAT_B8G8R8_UINT = 34, VK_FORMAT_B8G8R8_SINT = 35, VK_FORMAT_B8G8R8_SRGB = 36, VK_FORMAT_R8G8B8A8_UNORM = 37, VK_FORMAT_R8G8B8A8_SNORM = 38, VK_FORMAT_R8G8B8A8_USCALED = 39, VK_FORMAT_R8G8B8A8_SSCALED = 40, VK_FORMAT_R8G8B8A8_UINT = 41, VK_FORMAT_R8G8B8A8_SINT = 42, VK_FORMAT_R8G8B8A8_SRGB = 43, VK_FORMAT_B8G8R8A8_UNORM = 44, VK_FORMAT_B8G8R8A8_SNORM = 45, VK_FORMAT_B8G8R8A8_USCALED = 46, VK_FORMAT_B8G8R8A8_SSCALED = 47, VK_FORMAT_B8G8R8A8_UINT = 48, VK_FORMAT_B8G8R8A8_SINT = 49, VK_FORMAT_B8G8R8A8_SRGB = 50, VK_FORMAT_A8B8G8R8_UNORM_PACK32 = 51, VK_FORMAT_A8B8G8R8_SNORM_PACK32 = 52, VK_FORMAT_A8B8G8R8_USCALED_PACK32 = 53, VK_FORMAT_A8B8G8R8_SSCALED_PACK32 = 54, VK_FORMAT_A8B8G8R8_UINT_PACK32 = 55, VK_FORMAT_A8B8G8R8_SINT_PACK32 = 56, VK_FORMAT_A8B8G8R8_SRGB_PACK32 = 57, VK_FORMAT_A2R10G10B10_UNORM_PACK32 = 58, VK_FORMAT_A2R10G10B10_SNORM_PACK32 = 59, VK_FORMAT_A2R10G10B10_USCALED_PACK32 = 60, VK_FORMAT_A2R10G10B10_SSCALED_PACK32 = 61, VK_FORMAT_A2R10G10B10_UINT_PACK32 = 62, VK_FORMAT_A2R10G10B10_SINT_PACK32 = 63, VK_FORMAT_A2B10G10R10_UNORM_PACK32 = 64, VK_FORMAT_A2B10G10R10_SNORM_PACK32 = 65, VK_FORMAT_A2B10G10R10_USCALED_PACK32 = 66, VK_FORMAT_A2B10G10R10_SSCALED_PACK32 = 67, VK_FORMAT_A2B10G10R10_UINT_PACK32 = 68, VK_FORMAT_A2B10G10R10_SINT_PACK32 = 69, VK_FORMAT_R16_UNORM = 70, VK_FORMAT_R16_SNORM = 71, VK_FORMAT_R16_USCALED = 72, VK_FORMAT_R16_SSCALED = 73, VK_FORMAT_R16_UINT = 74, VK_FORMAT_R16_SINT = 75, VK_FORMAT_R16_SFLOAT = 76, VK_FORMAT_R16G16_UNORM = 77, VK_FORMAT_R16G16_SNORM = 78, VK_FORMAT_R16G16_USCALED = 79, VK_FORMAT_R16G16_SSCALED = 80, VK_FORMAT_R16G16_UINT = 81, VK_FORMAT_R16G16_SINT = 82, VK_FORMAT_R16G16_SFLOAT = 83, VK_FORMAT_R16G16B16_UNORM = 84, VK_FORMAT_R16G16B16_SNORM = 85, VK_FORMAT_R16G16B16_USCALED = 86, VK_FORMAT_R16G16B16_SSCALED = 87, VK_FORMAT_R16G16B16_UINT = 88, VK_FORMAT_R16G16B16_SINT = 89, VK_FORMAT_R16G16B16_SFLOAT = 90, VK_FORMAT_R16G16B16A16_UNORM = 91, VK_FORMAT_R16G16B16A16_SNORM = 92, VK_FORMAT_R16G16B16A16_USCALED = 93, VK_FORMAT_R16G16B16A16_SSCALED = 94, VK_FORMAT_R16G16B16A16_UINT = 95, VK_FORMAT_R16G16B16A16_SINT = 96, VK_FORMAT_R16G16B16A16_SFLOAT = 97, VK_FORMAT_R32_UINT = 98, VK_FORMAT_R32_SINT = 99, VK_FORMAT_R32_SFLOAT = 100, VK_FORMAT_R32G32_UINT = 101, VK_FORMAT_R32G32_SINT = 102, VK_FORMAT_R32G32_SFLOAT = 103, VK_FORMAT_R32G32B32_UINT = 104, VK_FORMAT_R32G32B32_SINT = 105, VK_FORMAT_R32G32B32_SFLOAT = 106, VK_FORMAT_R32G32B32A32_UINT = 107, VK_FORMAT_R32G32B32A32_SINT = 108, VK_FORMAT_R32G32B32A32_SFLOAT = 109, VK_FORMAT_R64_UINT = 110, VK_FORMAT_R64_SINT = 111, VK_FORMAT_R64_SFLOAT = 112, VK_FORMAT_R64G64_UINT = 113, VK_FORMAT_R64G64_SINT = 114, VK_FORMAT_R64G64_SFLOAT = 115, VK_FORMAT_R64G64B64_UINT = 116, VK_FORMAT_R64G64B64_SINT = 117, VK_FORMAT_R64G64B64_SFLOAT = 118, VK_FORMAT_R64G64B64A64_UINT = 119, VK_FORMAT_R64G64B64A64_SINT = 120, VK_FORMAT_R64G64B64A64_SFLOAT = 121, VK_FORMAT_B10G11R11_UFLOAT_PACK32 = 122, VK_FORMAT_E5B9G9R9_UFLOAT_PACK32 = 123, VK_FORMAT_D16_UNORM = 124, VK_FORMAT_X8_D24_UNORM_PACK32 = 125, VK_FORMAT_D32_SFLOAT = 126, VK_FORMAT_S8_UINT = 127, VK_FORMAT_D16_UNORM_S8_UINT = 128, VK_FORMAT_D24_UNORM_S8_UINT = 129, VK_FORMAT_D32_SFLOAT_S8_UINT = 130, VK_FORMAT_BC1_RGB_UNORM_BLOCK = 131, VK_FORMAT_BC1_RGB_SRGB_BLOCK = 132, VK_FORMAT_BC1_RGBA_UNORM_BLOCK = 133, VK_FORMAT_BC1_RGBA_SRGB_BLOCK = 134, VK_FORMAT_BC2_UNORM_BLOCK = 135, VK_FORMAT_BC2_SRGB_BLOCK = 136, VK_FORMAT_BC3_UNORM_BLOCK = 137, VK_FORMAT_BC3_SRGB_BLOCK = 138, VK_FORMAT_BC4_UNORM_BLOCK = 139, VK_FORMAT_BC4_SNORM_BLOCK = 140, VK_FORMAT_BC5_UNORM_BLOCK = 141, VK_FORMAT_BC5_SNORM_BLOCK = 142, VK_FORMAT_BC6H_UFLOAT_BLOCK = 143, VK_FORMAT_BC6H_SFLOAT_BLOCK = 144, VK_FORMAT_BC7_UNORM_BLOCK = 145, VK_FORMAT_BC7_SRGB_BLOCK = 146, VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK = 147, VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK = 148, VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK = 149, VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK = 150, VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK = 151, VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK = 152, VK_FORMAT_EAC_R11_UNORM_BLOCK = 153, VK_FORMAT_EAC_R11_SNORM_BLOCK = 154, VK_FORMAT_EAC_R11G11_UNORM_BLOCK = 155, VK_FORMAT_EAC_R11G11_SNORM_BLOCK = 156, VK_FORMAT_ASTC_4x4_UNORM_BLOCK = 157, VK_FORMAT_ASTC_4x4_SRGB_BLOCK = 158, VK_FORMAT_ASTC_5x4_UNORM_BLOCK = 159, VK_FORMAT_ASTC_5x4_SRGB_BLOCK = 160, VK_FORMAT_ASTC_5x5_UNORM_BLOCK = 161, VK_FORMAT_ASTC_5x5_SRGB_BLOCK = 162, VK_FORMAT_ASTC_6x5_UNORM_BLOCK = 163, VK_FORMAT_ASTC_6x5_SRGB_BLOCK = 164, VK_FORMAT_ASTC_6x6_UNORM_BLOCK = 165, VK_FORMAT_ASTC_6x6_SRGB_BLOCK = 166, VK_FORMAT_ASTC_8x5_UNORM_BLOCK = 167, VK_FORMAT_ASTC_8x5_SRGB_BLOCK = 168, VK_FORMAT_ASTC_8x6_UNORM_BLOCK = 169, VK_FORMAT_ASTC_8x6_SRGB_BLOCK = 170, VK_FORMAT_ASTC_8x8_UNORM_BLOCK = 171, VK_FORMAT_ASTC_8x8_SRGB_BLOCK = 172, VK_FORMAT_ASTC_10x5_UNORM_BLOCK = 173, VK_FORMAT_ASTC_10x5_SRGB_BLOCK = 174, VK_FORMAT_ASTC_10x6_UNORM_BLOCK = 175, VK_FORMAT_ASTC_10x6_SRGB_BLOCK = 176, VK_FORMAT_ASTC_10x8_UNORM_BLOCK = 177, VK_FORMAT_ASTC_10x8_SRGB_BLOCK = 178, VK_FORMAT_ASTC_10x10_UNORM_BLOCK = 179, VK_FORMAT_ASTC_10x10_SRGB_BLOCK = 180, VK_FORMAT_ASTC_12x10_UNORM_BLOCK = 181, VK_FORMAT_ASTC_12x10_SRGB_BLOCK = 182, VK_FORMAT_ASTC_12x12_UNORM_BLOCK = 183, VK_FORMAT_ASTC_12x12_SRGB_BLOCK = 184, VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG = 1000054000, VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG = 1000054001, VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG = 1000054002, VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG = 1000054003, VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG = 1000054004, VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG = 1000054005, VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG = 1000054006, VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG = 1000054007, VK_FORMAT_BEGIN_RANGE = VK_FORMAT_UNDEFINED, VK_FORMAT_END_RANGE = VK_FORMAT_ASTC_12x12_SRGB_BLOCK, VK_FORMAT_RANGE_SIZE = (VK_FORMAT_ASTC_12x12_SRGB_BLOCK - VK_FORMAT_UNDEFINED + 1), VK_FORMAT_MAX_ENUM = 0x7FFFFFFF } VkFormat; typedef enum VkImageType { VK_IMAGE_TYPE_1D = 0, VK_IMAGE_TYPE_2D = 1, VK_IMAGE_TYPE_3D = 2, VK_IMAGE_TYPE_BEGIN_RANGE = VK_IMAGE_TYPE_1D, VK_IMAGE_TYPE_END_RANGE = VK_IMAGE_TYPE_3D, VK_IMAGE_TYPE_RANGE_SIZE = (VK_IMAGE_TYPE_3D - VK_IMAGE_TYPE_1D + 1), VK_IMAGE_TYPE_MAX_ENUM = 0x7FFFFFFF } VkImageType; typedef enum VkImageTiling { VK_IMAGE_TILING_OPTIMAL = 0, VK_IMAGE_TILING_LINEAR = 1, VK_IMAGE_TILING_BEGIN_RANGE = VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_TILING_END_RANGE = VK_IMAGE_TILING_LINEAR, VK_IMAGE_TILING_RANGE_SIZE = (VK_IMAGE_TILING_LINEAR - VK_IMAGE_TILING_OPTIMAL + 1), VK_IMAGE_TILING_MAX_ENUM = 0x7FFFFFFF } VkImageTiling; typedef enum VkPhysicalDeviceType { VK_PHYSICAL_DEVICE_TYPE_OTHER = 0, VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU = 1, VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU = 2, VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU = 3, VK_PHYSICAL_DEVICE_TYPE_CPU = 4, VK_PHYSICAL_DEVICE_TYPE_BEGIN_RANGE = VK_PHYSICAL_DEVICE_TYPE_OTHER, VK_PHYSICAL_DEVICE_TYPE_END_RANGE = VK_PHYSICAL_DEVICE_TYPE_CPU, VK_PHYSICAL_DEVICE_TYPE_RANGE_SIZE = (VK_PHYSICAL_DEVICE_TYPE_CPU - VK_PHYSICAL_DEVICE_TYPE_OTHER + 1), VK_PHYSICAL_DEVICE_TYPE_MAX_ENUM = 0x7FFFFFFF } VkPhysicalDeviceType; typedef enum VkQueryType { VK_QUERY_TYPE_OCCLUSION = 0, VK_QUERY_TYPE_PIPELINE_STATISTICS = 1, VK_QUERY_TYPE_TIMESTAMP = 2, VK_QUERY_TYPE_BEGIN_RANGE = VK_QUERY_TYPE_OCCLUSION, VK_QUERY_TYPE_END_RANGE = VK_QUERY_TYPE_TIMESTAMP, VK_QUERY_TYPE_RANGE_SIZE = (VK_QUERY_TYPE_TIMESTAMP - VK_QUERY_TYPE_OCCLUSION + 1), VK_QUERY_TYPE_MAX_ENUM = 0x7FFFFFFF } VkQueryType; typedef enum VkSharingMode { VK_SHARING_MODE_EXCLUSIVE = 0, VK_SHARING_MODE_CONCURRENT = 1, VK_SHARING_MODE_BEGIN_RANGE = VK_SHARING_MODE_EXCLUSIVE, VK_SHARING_MODE_END_RANGE = VK_SHARING_MODE_CONCURRENT, VK_SHARING_MODE_RANGE_SIZE = (VK_SHARING_MODE_CONCURRENT - VK_SHARING_MODE_EXCLUSIVE + 1), VK_SHARING_MODE_MAX_ENUM = 0x7FFFFFFF } VkSharingMode; typedef enum VkImageLayout { VK_IMAGE_LAYOUT_UNDEFINED = 0, VK_IMAGE_LAYOUT_GENERAL = 1, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL = 2, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL = 3, VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL = 4, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL = 5, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL = 6, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL = 7, VK_IMAGE_LAYOUT_PREINITIALIZED = 8, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR = 1000001002, VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR = 1000111000, VK_IMAGE_LAYOUT_BEGIN_RANGE = VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_END_RANGE = VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_RANGE_SIZE = (VK_IMAGE_LAYOUT_PREINITIALIZED - VK_IMAGE_LAYOUT_UNDEFINED + 1), VK_IMAGE_LAYOUT_MAX_ENUM = 0x7FFFFFFF } VkImageLayout; typedef enum VkImageViewType { VK_IMAGE_VIEW_TYPE_1D = 0, VK_IMAGE_VIEW_TYPE_2D = 1, VK_IMAGE_VIEW_TYPE_3D = 2, VK_IMAGE_VIEW_TYPE_CUBE = 3, VK_IMAGE_VIEW_TYPE_1D_ARRAY = 4, VK_IMAGE_VIEW_TYPE_2D_ARRAY = 5, VK_IMAGE_VIEW_TYPE_CUBE_ARRAY = 6, VK_IMAGE_VIEW_TYPE_BEGIN_RANGE = VK_IMAGE_VIEW_TYPE_1D, VK_IMAGE_VIEW_TYPE_END_RANGE = VK_IMAGE_VIEW_TYPE_CUBE_ARRAY, VK_IMAGE_VIEW_TYPE_RANGE_SIZE = (VK_IMAGE_VIEW_TYPE_CUBE_ARRAY - VK_IMAGE_VIEW_TYPE_1D + 1), VK_IMAGE_VIEW_TYPE_MAX_ENUM = 0x7FFFFFFF } VkImageViewType; typedef enum VkComponentSwizzle { VK_COMPONENT_SWIZZLE_IDENTITY = 0, VK_COMPONENT_SWIZZLE_ZERO = 1, VK_COMPONENT_SWIZZLE_ONE = 2, VK_COMPONENT_SWIZZLE_R = 3, VK_COMPONENT_SWIZZLE_G = 4, VK_COMPONENT_SWIZZLE_B = 5, VK_COMPONENT_SWIZZLE_A = 6, VK_COMPONENT_SWIZZLE_BEGIN_RANGE = VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_END_RANGE = VK_COMPONENT_SWIZZLE_A, VK_COMPONENT_SWIZZLE_RANGE_SIZE = (VK_COMPONENT_SWIZZLE_A - VK_COMPONENT_SWIZZLE_IDENTITY + 1), VK_COMPONENT_SWIZZLE_MAX_ENUM = 0x7FFFFFFF } VkComponentSwizzle; typedef enum VkVertexInputRate { VK_VERTEX_INPUT_RATE_VERTEX = 0, VK_VERTEX_INPUT_RATE_INSTANCE = 1, VK_VERTEX_INPUT_RATE_BEGIN_RANGE = VK_VERTEX_INPUT_RATE_VERTEX, VK_VERTEX_INPUT_RATE_END_RANGE = VK_VERTEX_INPUT_RATE_INSTANCE, VK_VERTEX_INPUT_RATE_RANGE_SIZE = (VK_VERTEX_INPUT_RATE_INSTANCE - VK_VERTEX_INPUT_RATE_VERTEX + 1), VK_VERTEX_INPUT_RATE_MAX_ENUM = 0x7FFFFFFF } VkVertexInputRate; typedef enum VkPrimitiveTopology { VK_PRIMITIVE_TOPOLOGY_POINT_LIST = 0, VK_PRIMITIVE_TOPOLOGY_LINE_LIST = 1, VK_PRIMITIVE_TOPOLOGY_LINE_STRIP = 2, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST = 3, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP = 4, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN = 5, VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY = 6, VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY = 7, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY = 8, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY = 9, VK_PRIMITIVE_TOPOLOGY_PATCH_LIST = 10, VK_PRIMITIVE_TOPOLOGY_BEGIN_RANGE = VK_PRIMITIVE_TOPOLOGY_POINT_LIST, VK_PRIMITIVE_TOPOLOGY_END_RANGE = VK_PRIMITIVE_TOPOLOGY_PATCH_LIST, VK_PRIMITIVE_TOPOLOGY_RANGE_SIZE = (VK_PRIMITIVE_TOPOLOGY_PATCH_LIST - VK_PRIMITIVE_TOPOLOGY_POINT_LIST + 1), VK_PRIMITIVE_TOPOLOGY_MAX_ENUM = 0x7FFFFFFF } VkPrimitiveTopology; typedef enum VkPolygonMode { VK_POLYGON_MODE_FILL = 0, VK_POLYGON_MODE_LINE = 1, VK_POLYGON_MODE_POINT = 2, VK_POLYGON_MODE_BEGIN_RANGE = VK_POLYGON_MODE_FILL, VK_POLYGON_MODE_END_RANGE = VK_POLYGON_MODE_POINT, VK_POLYGON_MODE_RANGE_SIZE = (VK_POLYGON_MODE_POINT - VK_POLYGON_MODE_FILL + 1), VK_POLYGON_MODE_MAX_ENUM = 0x7FFFFFFF } VkPolygonMode; typedef enum VkFrontFace { VK_FRONT_FACE_COUNTER_CLOCKWISE = 0, VK_FRONT_FACE_CLOCKWISE = 1, VK_FRONT_FACE_BEGIN_RANGE = VK_FRONT_FACE_COUNTER_CLOCKWISE, VK_FRONT_FACE_END_RANGE = VK_FRONT_FACE_CLOCKWISE, VK_FRONT_FACE_RANGE_SIZE = (VK_FRONT_FACE_CLOCKWISE - VK_FRONT_FACE_COUNTER_CLOCKWISE + 1), VK_FRONT_FACE_MAX_ENUM = 0x7FFFFFFF } VkFrontFace; typedef enum VkCompareOp { VK_COMPARE_OP_NEVER = 0, VK_COMPARE_OP_LESS = 1, VK_COMPARE_OP_EQUAL = 2, VK_COMPARE_OP_LESS_OR_EQUAL = 3, VK_COMPARE_OP_GREATER = 4, VK_COMPARE_OP_NOT_EQUAL = 5, VK_COMPARE_OP_GREATER_OR_EQUAL = 6, VK_COMPARE_OP_ALWAYS = 7, VK_COMPARE_OP_BEGIN_RANGE = VK_COMPARE_OP_NEVER, VK_COMPARE_OP_END_RANGE = VK_COMPARE_OP_ALWAYS, VK_COMPARE_OP_RANGE_SIZE = (VK_COMPARE_OP_ALWAYS - VK_COMPARE_OP_NEVER + 1), VK_COMPARE_OP_MAX_ENUM = 0x7FFFFFFF } VkCompareOp; typedef enum VkStencilOp { VK_STENCIL_OP_KEEP = 0, VK_STENCIL_OP_ZERO = 1, VK_STENCIL_OP_REPLACE = 2, VK_STENCIL_OP_INCREMENT_AND_CLAMP = 3, VK_STENCIL_OP_DECREMENT_AND_CLAMP = 4, VK_STENCIL_OP_INVERT = 5, VK_STENCIL_OP_INCREMENT_AND_WRAP = 6, VK_STENCIL_OP_DECREMENT_AND_WRAP = 7, VK_STENCIL_OP_BEGIN_RANGE = VK_STENCIL_OP_KEEP, VK_STENCIL_OP_END_RANGE = VK_STENCIL_OP_DECREMENT_AND_WRAP, VK_STENCIL_OP_RANGE_SIZE = (VK_STENCIL_OP_DECREMENT_AND_WRAP - VK_STENCIL_OP_KEEP + 1), VK_STENCIL_OP_MAX_ENUM = 0x7FFFFFFF } VkStencilOp; typedef enum VkLogicOp { VK_LOGIC_OP_CLEAR = 0, VK_LOGIC_OP_AND = 1, VK_LOGIC_OP_AND_REVERSE = 2, VK_LOGIC_OP_COPY = 3, VK_LOGIC_OP_AND_INVERTED = 4, VK_LOGIC_OP_NO_OP = 5, VK_LOGIC_OP_XOR = 6, VK_LOGIC_OP_OR = 7, VK_LOGIC_OP_NOR = 8, VK_LOGIC_OP_EQUIVALENT = 9, VK_LOGIC_OP_INVERT = 10, VK_LOGIC_OP_OR_REVERSE = 11, VK_LOGIC_OP_COPY_INVERTED = 12, VK_LOGIC_OP_OR_INVERTED = 13, VK_LOGIC_OP_NAND = 14, VK_LOGIC_OP_SET = 15, VK_LOGIC_OP_BEGIN_RANGE = VK_LOGIC_OP_CLEAR, VK_LOGIC_OP_END_RANGE = VK_LOGIC_OP_SET, VK_LOGIC_OP_RANGE_SIZE = (VK_LOGIC_OP_SET - VK_LOGIC_OP_CLEAR + 1), VK_LOGIC_OP_MAX_ENUM = 0x7FFFFFFF } VkLogicOp; typedef enum VkBlendFactor { VK_BLEND_FACTOR_ZERO = 0, VK_BLEND_FACTOR_ONE = 1, VK_BLEND_FACTOR_SRC_COLOR = 2, VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR = 3, VK_BLEND_FACTOR_DST_COLOR = 4, VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR = 5, VK_BLEND_FACTOR_SRC_ALPHA = 6, VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA = 7, VK_BLEND_FACTOR_DST_ALPHA = 8, VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA = 9, VK_BLEND_FACTOR_CONSTANT_COLOR = 10, VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR = 11, VK_BLEND_FACTOR_CONSTANT_ALPHA = 12, VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA = 13, VK_BLEND_FACTOR_SRC_ALPHA_SATURATE = 14, VK_BLEND_FACTOR_SRC1_COLOR = 15, VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR = 16, VK_BLEND_FACTOR_SRC1_ALPHA = 17, VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA = 18, VK_BLEND_FACTOR_BEGIN_RANGE = VK_BLEND_FACTOR_ZERO, VK_BLEND_FACTOR_END_RANGE = VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA, VK_BLEND_FACTOR_RANGE_SIZE = (VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA - VK_BLEND_FACTOR_ZERO + 1), VK_BLEND_FACTOR_MAX_ENUM = 0x7FFFFFFF } VkBlendFactor; typedef enum VkBlendOp { VK_BLEND_OP_ADD = 0, VK_BLEND_OP_SUBTRACT = 1, VK_BLEND_OP_REVERSE_SUBTRACT = 2, VK_BLEND_OP_MIN = 3, VK_BLEND_OP_MAX = 4, VK_BLEND_OP_BEGIN_RANGE = VK_BLEND_OP_ADD, VK_BLEND_OP_END_RANGE = VK_BLEND_OP_MAX, VK_BLEND_OP_RANGE_SIZE = (VK_BLEND_OP_MAX - VK_BLEND_OP_ADD + 1), VK_BLEND_OP_MAX_ENUM = 0x7FFFFFFF } VkBlendOp; typedef enum VkDynamicState { VK_DYNAMIC_STATE_VIEWPORT = 0, VK_DYNAMIC_STATE_SCISSOR = 1, VK_DYNAMIC_STATE_LINE_WIDTH = 2, VK_DYNAMIC_STATE_DEPTH_BIAS = 3, VK_DYNAMIC_STATE_BLEND_CONSTANTS = 4, VK_DYNAMIC_STATE_DEPTH_BOUNDS = 5, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK = 6, VK_DYNAMIC_STATE_STENCIL_WRITE_MASK = 7, VK_DYNAMIC_STATE_STENCIL_REFERENCE = 8, VK_DYNAMIC_STATE_VIEWPORT_W_SCALING_NV = 1000087000, VK_DYNAMIC_STATE_DISCARD_RECTANGLE_EXT = 1000099000, VK_DYNAMIC_STATE_BEGIN_RANGE = VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_END_RANGE = VK_DYNAMIC_STATE_STENCIL_REFERENCE, VK_DYNAMIC_STATE_RANGE_SIZE = (VK_DYNAMIC_STATE_STENCIL_REFERENCE - VK_DYNAMIC_STATE_VIEWPORT + 1), VK_DYNAMIC_STATE_MAX_ENUM = 0x7FFFFFFF } VkDynamicState; typedef enum VkFilter { VK_FILTER_NEAREST = 0, VK_FILTER_LINEAR = 1, VK_FILTER_CUBIC_IMG = 1000015000, VK_FILTER_BEGIN_RANGE = VK_FILTER_NEAREST, VK_FILTER_END_RANGE = VK_FILTER_LINEAR, VK_FILTER_RANGE_SIZE = (VK_FILTER_LINEAR - VK_FILTER_NEAREST + 1), VK_FILTER_MAX_ENUM = 0x7FFFFFFF } VkFilter; typedef enum VkSamplerMipmapMode { VK_SAMPLER_MIPMAP_MODE_NEAREST = 0, VK_SAMPLER_MIPMAP_MODE_LINEAR = 1, VK_SAMPLER_MIPMAP_MODE_BEGIN_RANGE = VK_SAMPLER_MIPMAP_MODE_NEAREST, VK_SAMPLER_MIPMAP_MODE_END_RANGE = VK_SAMPLER_MIPMAP_MODE_LINEAR, VK_SAMPLER_MIPMAP_MODE_RANGE_SIZE = (VK_SAMPLER_MIPMAP_MODE_LINEAR - VK_SAMPLER_MIPMAP_MODE_NEAREST + 1), VK_SAMPLER_MIPMAP_MODE_MAX_ENUM = 0x7FFFFFFF } VkSamplerMipmapMode; typedef enum VkSamplerAddressMode { VK_SAMPLER_ADDRESS_MODE_REPEAT = 0, VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT = 1, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE = 2, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER = 3, VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE = 4, VK_SAMPLER_ADDRESS_MODE_BEGIN_RANGE = VK_SAMPLER_ADDRESS_MODE_REPEAT, VK_SAMPLER_ADDRESS_MODE_END_RANGE = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, VK_SAMPLER_ADDRESS_MODE_RANGE_SIZE = (VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER - VK_SAMPLER_ADDRESS_MODE_REPEAT + 1), VK_SAMPLER_ADDRESS_MODE_MAX_ENUM = 0x7FFFFFFF } VkSamplerAddressMode; typedef enum VkBorderColor { VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK = 0, VK_BORDER_COLOR_INT_TRANSPARENT_BLACK = 1, VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK = 2, VK_BORDER_COLOR_INT_OPAQUE_BLACK = 3, VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE = 4, VK_BORDER_COLOR_INT_OPAQUE_WHITE = 5, VK_BORDER_COLOR_BEGIN_RANGE = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, VK_BORDER_COLOR_END_RANGE = VK_BORDER_COLOR_INT_OPAQUE_WHITE, VK_BORDER_COLOR_RANGE_SIZE = (VK_BORDER_COLOR_INT_OPAQUE_WHITE - VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK + 1), VK_BORDER_COLOR_MAX_ENUM = 0x7FFFFFFF } VkBorderColor; typedef enum VkDescriptorType { VK_DESCRIPTOR_TYPE_SAMPLER = 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER = 1, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE = 2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE = 3, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER = 4, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER = 5, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER = 6, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER = 7, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC = 8, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC = 9, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT = 10, VK_DESCRIPTOR_TYPE_BEGIN_RANGE = VK_DESCRIPTOR_TYPE_SAMPLER, VK_DESCRIPTOR_TYPE_END_RANGE = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, VK_DESCRIPTOR_TYPE_RANGE_SIZE = (VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT - VK_DESCRIPTOR_TYPE_SAMPLER + 1), VK_DESCRIPTOR_TYPE_MAX_ENUM = 0x7FFFFFFF } VkDescriptorType; typedef enum VkAttachmentLoadOp { VK_ATTACHMENT_LOAD_OP_LOAD = 0, VK_ATTACHMENT_LOAD_OP_CLEAR = 1, VK_ATTACHMENT_LOAD_OP_DONT_CARE = 2, VK_ATTACHMENT_LOAD_OP_BEGIN_RANGE = VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_LOAD_OP_END_RANGE = VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_LOAD_OP_RANGE_SIZE = (VK_ATTACHMENT_LOAD_OP_DONT_CARE - VK_ATTACHMENT_LOAD_OP_LOAD + 1), VK_ATTACHMENT_LOAD_OP_MAX_ENUM = 0x7FFFFFFF } VkAttachmentLoadOp; typedef enum VkAttachmentStoreOp { VK_ATTACHMENT_STORE_OP_STORE = 0, VK_ATTACHMENT_STORE_OP_DONT_CARE = 1, VK_ATTACHMENT_STORE_OP_BEGIN_RANGE = VK_ATTACHMENT_STORE_OP_STORE, VK_ATTACHMENT_STORE_OP_END_RANGE = VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_RANGE_SIZE = (VK_ATTACHMENT_STORE_OP_DONT_CARE - VK_ATTACHMENT_STORE_OP_STORE + 1), VK_ATTACHMENT_STORE_OP_MAX_ENUM = 0x7FFFFFFF } VkAttachmentStoreOp; typedef enum VkPipelineBindPoint { VK_PIPELINE_BIND_POINT_GRAPHICS = 0, VK_PIPELINE_BIND_POINT_COMPUTE = 1, VK_PIPELINE_BIND_POINT_BEGIN_RANGE = VK_PIPELINE_BIND_POINT_GRAPHICS, VK_PIPELINE_BIND_POINT_END_RANGE = VK_PIPELINE_BIND_POINT_COMPUTE, VK_PIPELINE_BIND_POINT_RANGE_SIZE = (VK_PIPELINE_BIND_POINT_COMPUTE - VK_PIPELINE_BIND_POINT_GRAPHICS + 1), VK_PIPELINE_BIND_POINT_MAX_ENUM = 0x7FFFFFFF } VkPipelineBindPoint; typedef enum VkCommandBufferLevel { VK_COMMAND_BUFFER_LEVEL_PRIMARY = 0, VK_COMMAND_BUFFER_LEVEL_SECONDARY = 1, VK_COMMAND_BUFFER_LEVEL_BEGIN_RANGE = VK_COMMAND_BUFFER_LEVEL_PRIMARY, VK_COMMAND_BUFFER_LEVEL_END_RANGE = VK_COMMAND_BUFFER_LEVEL_SECONDARY, VK_COMMAND_BUFFER_LEVEL_RANGE_SIZE = (VK_COMMAND_BUFFER_LEVEL_SECONDARY - VK_COMMAND_BUFFER_LEVEL_PRIMARY + 1), VK_COMMAND_BUFFER_LEVEL_MAX_ENUM = 0x7FFFFFFF } VkCommandBufferLevel; typedef enum VkIndexType { VK_INDEX_TYPE_UINT16 = 0, VK_INDEX_TYPE_UINT32 = 1, VK_INDEX_TYPE_BEGIN_RANGE = VK_INDEX_TYPE_UINT16, VK_INDEX_TYPE_END_RANGE = VK_INDEX_TYPE_UINT32, VK_INDEX_TYPE_RANGE_SIZE = (VK_INDEX_TYPE_UINT32 - VK_INDEX_TYPE_UINT16 + 1), VK_INDEX_TYPE_MAX_ENUM = 0x7FFFFFFF } VkIndexType; typedef enum VkSubpassContents { VK_SUBPASS_CONTENTS_INLINE = 0, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS = 1, VK_SUBPASS_CONTENTS_BEGIN_RANGE = VK_SUBPASS_CONTENTS_INLINE, VK_SUBPASS_CONTENTS_END_RANGE = VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS, VK_SUBPASS_CONTENTS_RANGE_SIZE = (VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS - VK_SUBPASS_CONTENTS_INLINE + 1), VK_SUBPASS_CONTENTS_MAX_ENUM = 0x7FFFFFFF } VkSubpassContents; typedef enum VkObjectType { VK_OBJECT_TYPE_UNKNOWN = 0, VK_OBJECT_TYPE_INSTANCE = 1, VK_OBJECT_TYPE_PHYSICAL_DEVICE = 2, VK_OBJECT_TYPE_DEVICE = 3, VK_OBJECT_TYPE_QUEUE = 4, VK_OBJECT_TYPE_SEMAPHORE = 5, VK_OBJECT_TYPE_COMMAND_BUFFER = 6, VK_OBJECT_TYPE_FENCE = 7, VK_OBJECT_TYPE_DEVICE_MEMORY = 8, VK_OBJECT_TYPE_BUFFER = 9, VK_OBJECT_TYPE_IMAGE = 10, VK_OBJECT_TYPE_EVENT = 11, VK_OBJECT_TYPE_QUERY_POOL = 12, VK_OBJECT_TYPE_BUFFER_VIEW = 13, VK_OBJECT_TYPE_IMAGE_VIEW = 14, VK_OBJECT_TYPE_SHADER_MODULE = 15, VK_OBJECT_TYPE_PIPELINE_CACHE = 16, VK_OBJECT_TYPE_PIPELINE_LAYOUT = 17, VK_OBJECT_TYPE_RENDER_PASS = 18, VK_OBJECT_TYPE_PIPELINE = 19, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT = 20, VK_OBJECT_TYPE_SAMPLER = 21, VK_OBJECT_TYPE_DESCRIPTOR_POOL = 22, VK_OBJECT_TYPE_DESCRIPTOR_SET = 23, VK_OBJECT_TYPE_FRAMEBUFFER = 24, VK_OBJECT_TYPE_COMMAND_POOL = 25, VK_OBJECT_TYPE_SURFACE_KHR = 1000000000, VK_OBJECT_TYPE_SWAPCHAIN_KHR = 1000001000, VK_OBJECT_TYPE_DISPLAY_KHR = 1000002000, VK_OBJECT_TYPE_DISPLAY_MODE_KHR = 1000002001, VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT = 1000011000, VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_KHR = 1000085000, VK_OBJECT_TYPE_OBJECT_TABLE_NVX = 1000086000, VK_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX = 1000086001, VK_OBJECT_TYPE_BEGIN_RANGE = VK_OBJECT_TYPE_UNKNOWN, VK_OBJECT_TYPE_END_RANGE = VK_OBJECT_TYPE_COMMAND_POOL, VK_OBJECT_TYPE_RANGE_SIZE = (VK_OBJECT_TYPE_COMMAND_POOL - VK_OBJECT_TYPE_UNKNOWN + 1), VK_OBJECT_TYPE_MAX_ENUM = 0x7FFFFFFF } VkObjectType; typedef VkFlags VkInstanceCreateFlags; typedef enum VkFormatFeatureFlagBits { VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT = 0x00000001, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT = 0x00000002, VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT = 0x00000004, VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT = 0x00000008, VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT = 0x00000010, VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT = 0x00000020, VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT = 0x00000040, VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT = 0x00000080, VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT = 0x00000100, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT = 0x00000200, VK_FORMAT_FEATURE_BLIT_SRC_BIT = 0x00000400, VK_FORMAT_FEATURE_BLIT_DST_BIT = 0x00000800, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT = 0x00001000, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG = 0x00002000, VK_FORMAT_FEATURE_TRANSFER_SRC_BIT_KHR = 0x00004000, VK_FORMAT_FEATURE_TRANSFER_DST_BIT_KHR = 0x00008000, VK_FORMAT_FEATURE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkFormatFeatureFlagBits; typedef VkFlags VkFormatFeatureFlags; typedef enum VkImageUsageFlagBits { VK_IMAGE_USAGE_TRANSFER_SRC_BIT = 0x00000001, VK_IMAGE_USAGE_TRANSFER_DST_BIT = 0x00000002, VK_IMAGE_USAGE_SAMPLED_BIT = 0x00000004, VK_IMAGE_USAGE_STORAGE_BIT = 0x00000008, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT = 0x00000010, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT = 0x00000020, VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT = 0x00000040, VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT = 0x00000080, VK_IMAGE_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkImageUsageFlagBits; typedef VkFlags VkImageUsageFlags; typedef enum VkImageCreateFlagBits { VK_IMAGE_CREATE_SPARSE_BINDING_BIT = 0x00000001, VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT = 0x00000002, VK_IMAGE_CREATE_SPARSE_ALIASED_BIT = 0x00000004, VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT = 0x00000008, VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT = 0x00000010, VK_IMAGE_CREATE_BIND_SFR_BIT_KHX = 0x00000040, VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT_KHR = 0x00000020, VK_IMAGE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkImageCreateFlagBits; typedef VkFlags VkImageCreateFlags; typedef enum VkSampleCountFlagBits { VK_SAMPLE_COUNT_1_BIT = 0x00000001, VK_SAMPLE_COUNT_2_BIT = 0x00000002, VK_SAMPLE_COUNT_4_BIT = 0x00000004, VK_SAMPLE_COUNT_8_BIT = 0x00000008, VK_SAMPLE_COUNT_16_BIT = 0x00000010, VK_SAMPLE_COUNT_32_BIT = 0x00000020, VK_SAMPLE_COUNT_64_BIT = 0x00000040, VK_SAMPLE_COUNT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkSampleCountFlagBits; typedef VkFlags VkSampleCountFlags; typedef enum VkQueueFlagBits { VK_QUEUE_GRAPHICS_BIT = 0x00000001, VK_QUEUE_COMPUTE_BIT = 0x00000002, VK_QUEUE_TRANSFER_BIT = 0x00000004, VK_QUEUE_SPARSE_BINDING_BIT = 0x00000008, VK_QUEUE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkQueueFlagBits; typedef VkFlags VkQueueFlags; typedef enum VkMemoryPropertyFlagBits { VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT = 0x00000001, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT = 0x00000002, VK_MEMORY_PROPERTY_HOST_COHERENT_BIT = 0x00000004, VK_MEMORY_PROPERTY_HOST_CACHED_BIT = 0x00000008, VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT = 0x00000010, VK_MEMORY_PROPERTY_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkMemoryPropertyFlagBits; typedef VkFlags VkMemoryPropertyFlags; typedef enum VkMemoryHeapFlagBits { VK_MEMORY_HEAP_DEVICE_LOCAL_BIT = 0x00000001, VK_MEMORY_HEAP_MULTI_INSTANCE_BIT_KHX = 0x00000002, VK_MEMORY_HEAP_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkMemoryHeapFlagBits; typedef VkFlags VkMemoryHeapFlags; typedef VkFlags VkDeviceCreateFlags; typedef VkFlags VkDeviceQueueCreateFlags; typedef enum VkPipelineStageFlagBits { VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT = 0x00000001, VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT = 0x00000002, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT = 0x00000004, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT = 0x00000008, VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT = 0x00000010, VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT = 0x00000020, VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT = 0x00000040, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT = 0x00000080, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT = 0x00000100, VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT = 0x00000200, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT = 0x00000400, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT = 0x00000800, VK_PIPELINE_STAGE_TRANSFER_BIT = 0x00001000, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT = 0x00002000, VK_PIPELINE_STAGE_HOST_BIT = 0x00004000, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT = 0x00008000, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT = 0x00010000, VK_PIPELINE_STAGE_COMMAND_PROCESS_BIT_NVX = 0x00020000, VK_PIPELINE_STAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkPipelineStageFlagBits; typedef VkFlags VkPipelineStageFlags; typedef VkFlags VkMemoryMapFlags; typedef enum VkImageAspectFlagBits { VK_IMAGE_ASPECT_COLOR_BIT = 0x00000001, VK_IMAGE_ASPECT_DEPTH_BIT = 0x00000002, VK_IMAGE_ASPECT_STENCIL_BIT = 0x00000004, VK_IMAGE_ASPECT_METADATA_BIT = 0x00000008, VK_IMAGE_ASPECT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkImageAspectFlagBits; typedef VkFlags VkImageAspectFlags; typedef enum VkSparseImageFormatFlagBits { VK_SPARSE_IMAGE_FORMAT_SINGLE_MIPTAIL_BIT = 0x00000001, VK_SPARSE_IMAGE_FORMAT_ALIGNED_MIP_SIZE_BIT = 0x00000002, VK_SPARSE_IMAGE_FORMAT_NONSTANDARD_BLOCK_SIZE_BIT = 0x00000004, VK_SPARSE_IMAGE_FORMAT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkSparseImageFormatFlagBits; typedef VkFlags VkSparseImageFormatFlags; typedef enum VkSparseMemoryBindFlagBits { VK_SPARSE_MEMORY_BIND_METADATA_BIT = 0x00000001, VK_SPARSE_MEMORY_BIND_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkSparseMemoryBindFlagBits; typedef VkFlags VkSparseMemoryBindFlags; typedef enum VkFenceCreateFlagBits { VK_FENCE_CREATE_SIGNALED_BIT = 0x00000001, VK_FENCE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkFenceCreateFlagBits; typedef VkFlags VkFenceCreateFlags; typedef VkFlags VkSemaphoreCreateFlags; typedef VkFlags VkEventCreateFlags; typedef VkFlags VkQueryPoolCreateFlags; typedef enum VkQueryPipelineStatisticFlagBits { VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_VERTICES_BIT = 0x00000001, VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_PRIMITIVES_BIT = 0x00000002, VK_QUERY_PIPELINE_STATISTIC_VERTEX_SHADER_INVOCATIONS_BIT = 0x00000004, VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_INVOCATIONS_BIT = 0x00000008, VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_PRIMITIVES_BIT = 0x00000010, VK_QUERY_PIPELINE_STATISTIC_CLIPPING_INVOCATIONS_BIT = 0x00000020, VK_QUERY_PIPELINE_STATISTIC_CLIPPING_PRIMITIVES_BIT = 0x00000040, VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT = 0x00000080, VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_CONTROL_SHADER_PATCHES_BIT = 0x00000100, VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_EVALUATION_SHADER_INVOCATIONS_BIT = 0x00000200, VK_QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT = 0x00000400, VK_QUERY_PIPELINE_STATISTIC_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkQueryPipelineStatisticFlagBits; typedef VkFlags VkQueryPipelineStatisticFlags; typedef enum VkQueryResultFlagBits { VK_QUERY_RESULT_64_BIT = 0x00000001, VK_QUERY_RESULT_WAIT_BIT = 0x00000002, VK_QUERY_RESULT_WITH_AVAILABILITY_BIT = 0x00000004, VK_QUERY_RESULT_PARTIAL_BIT = 0x00000008, VK_QUERY_RESULT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkQueryResultFlagBits; typedef VkFlags VkQueryResultFlags; typedef enum VkBufferCreateFlagBits { VK_BUFFER_CREATE_SPARSE_BINDING_BIT = 0x00000001, VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT = 0x00000002, VK_BUFFER_CREATE_SPARSE_ALIASED_BIT = 0x00000004, VK_BUFFER_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkBufferCreateFlagBits; typedef VkFlags VkBufferCreateFlags; typedef enum VkBufferUsageFlagBits { VK_BUFFER_USAGE_TRANSFER_SRC_BIT = 0x00000001, VK_BUFFER_USAGE_TRANSFER_DST_BIT = 0x00000002, VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT = 0x00000004, VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT = 0x00000008, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT = 0x00000010, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT = 0x00000020, VK_BUFFER_USAGE_INDEX_BUFFER_BIT = 0x00000040, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT = 0x00000080, VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT = 0x00000100, VK_BUFFER_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkBufferUsageFlagBits; typedef VkFlags VkBufferUsageFlags; typedef VkFlags VkBufferViewCreateFlags; typedef VkFlags VkImageViewCreateFlags; typedef VkFlags VkShaderModuleCreateFlags; typedef VkFlags VkPipelineCacheCreateFlags; typedef enum VkPipelineCreateFlagBits { VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT = 0x00000001, VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT = 0x00000002, VK_PIPELINE_CREATE_DERIVATIVE_BIT = 0x00000004, VK_PIPELINE_CREATE_VIEW_INDEX_FROM_DEVICE_INDEX_BIT_KHX = 0x00000008, VK_PIPELINE_CREATE_DISPATCH_BASE_KHX = 0x00000010, VK_PIPELINE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkPipelineCreateFlagBits; typedef VkFlags VkPipelineCreateFlags; typedef VkFlags VkPipelineShaderStageCreateFlags; typedef enum VkShaderStageFlagBits { VK_SHADER_STAGE_VERTEX_BIT = 0x00000001, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT = 0x00000002, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT = 0x00000004, VK_SHADER_STAGE_GEOMETRY_BIT = 0x00000008, VK_SHADER_STAGE_FRAGMENT_BIT = 0x00000010, VK_SHADER_STAGE_COMPUTE_BIT = 0x00000020, VK_SHADER_STAGE_ALL_GRAPHICS = 0x0000001F, VK_SHADER_STAGE_ALL = 0x7FFFFFFF, VK_SHADER_STAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkShaderStageFlagBits; typedef VkFlags VkPipelineVertexInputStateCreateFlags; typedef VkFlags VkPipelineInputAssemblyStateCreateFlags; typedef VkFlags VkPipelineTessellationStateCreateFlags; typedef VkFlags VkPipelineViewportStateCreateFlags; typedef VkFlags VkPipelineRasterizationStateCreateFlags; typedef enum VkCullModeFlagBits { VK_CULL_MODE_NONE = 0, VK_CULL_MODE_FRONT_BIT = 0x00000001, VK_CULL_MODE_BACK_BIT = 0x00000002, VK_CULL_MODE_FRONT_AND_BACK = 0x00000003, VK_CULL_MODE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkCullModeFlagBits; typedef VkFlags VkCullModeFlags; typedef VkFlags VkPipelineMultisampleStateCreateFlags; typedef VkFlags VkPipelineDepthStencilStateCreateFlags; typedef VkFlags VkPipelineColorBlendStateCreateFlags; typedef enum VkColorComponentFlagBits { VK_COLOR_COMPONENT_R_BIT = 0x00000001, VK_COLOR_COMPONENT_G_BIT = 0x00000002, VK_COLOR_COMPONENT_B_BIT = 0x00000004, VK_COLOR_COMPONENT_A_BIT = 0x00000008, VK_COLOR_COMPONENT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkColorComponentFlagBits; typedef VkFlags VkColorComponentFlags; typedef VkFlags VkPipelineDynamicStateCreateFlags; typedef VkFlags VkPipelineLayoutCreateFlags; typedef VkFlags VkShaderStageFlags; typedef VkFlags VkSamplerCreateFlags; typedef enum VkDescriptorSetLayoutCreateFlagBits { VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR = 0x00000001, VK_DESCRIPTOR_SET_LAYOUT_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkDescriptorSetLayoutCreateFlagBits; typedef VkFlags VkDescriptorSetLayoutCreateFlags; typedef enum VkDescriptorPoolCreateFlagBits { VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT = 0x00000001, VK_DESCRIPTOR_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkDescriptorPoolCreateFlagBits; typedef VkFlags VkDescriptorPoolCreateFlags; typedef VkFlags VkDescriptorPoolResetFlags; typedef VkFlags VkFramebufferCreateFlags; typedef VkFlags VkRenderPassCreateFlags; typedef enum VkAttachmentDescriptionFlagBits { VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT = 0x00000001, VK_ATTACHMENT_DESCRIPTION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkAttachmentDescriptionFlagBits; typedef VkFlags VkAttachmentDescriptionFlags; typedef enum VkSubpassDescriptionFlagBits { VK_SUBPASS_DESCRIPTION_PER_VIEW_ATTRIBUTES_BIT_NVX = 0x00000001, VK_SUBPASS_DESCRIPTION_PER_VIEW_POSITION_X_ONLY_BIT_NVX = 0x00000002, VK_SUBPASS_DESCRIPTION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkSubpassDescriptionFlagBits; typedef VkFlags VkSubpassDescriptionFlags; typedef enum VkAccessFlagBits { VK_ACCESS_INDIRECT_COMMAND_READ_BIT = 0x00000001, VK_ACCESS_INDEX_READ_BIT = 0x00000002, VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT = 0x00000004, VK_ACCESS_UNIFORM_READ_BIT = 0x00000008, VK_ACCESS_INPUT_ATTACHMENT_READ_BIT = 0x00000010, VK_ACCESS_SHADER_READ_BIT = 0x00000020, VK_ACCESS_SHADER_WRITE_BIT = 0x00000040, VK_ACCESS_COLOR_ATTACHMENT_READ_BIT = 0x00000080, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT = 0x00000100, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT = 0x00000200, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT = 0x00000400, VK_ACCESS_TRANSFER_READ_BIT = 0x00000800, VK_ACCESS_TRANSFER_WRITE_BIT = 0x00001000, VK_ACCESS_HOST_READ_BIT = 0x00002000, VK_ACCESS_HOST_WRITE_BIT = 0x00004000, VK_ACCESS_MEMORY_READ_BIT = 0x00008000, VK_ACCESS_MEMORY_WRITE_BIT = 0x00010000, VK_ACCESS_COMMAND_PROCESS_READ_BIT_NVX = 0x00020000, VK_ACCESS_COMMAND_PROCESS_WRITE_BIT_NVX = 0x00040000, VK_ACCESS_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkAccessFlagBits; typedef VkFlags VkAccessFlags; typedef enum VkDependencyFlagBits { VK_DEPENDENCY_BY_REGION_BIT = 0x00000001, VK_DEPENDENCY_VIEW_LOCAL_BIT_KHX = 0x00000002, VK_DEPENDENCY_DEVICE_GROUP_BIT_KHX = 0x00000004, VK_DEPENDENCY_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkDependencyFlagBits; typedef VkFlags VkDependencyFlags; typedef enum VkCommandPoolCreateFlagBits { VK_COMMAND_POOL_CREATE_TRANSIENT_BIT = 0x00000001, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT = 0x00000002, VK_COMMAND_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkCommandPoolCreateFlagBits; typedef VkFlags VkCommandPoolCreateFlags; typedef enum VkCommandPoolResetFlagBits { VK_COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT = 0x00000001, VK_COMMAND_POOL_RESET_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkCommandPoolResetFlagBits; typedef VkFlags VkCommandPoolResetFlags; typedef enum VkCommandBufferUsageFlagBits { VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT = 0x00000001, VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT = 0x00000002, VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT = 0x00000004, VK_COMMAND_BUFFER_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkCommandBufferUsageFlagBits; typedef VkFlags VkCommandBufferUsageFlags; typedef enum VkQueryControlFlagBits { VK_QUERY_CONTROL_PRECISE_BIT = 0x00000001, VK_QUERY_CONTROL_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkQueryControlFlagBits; typedef VkFlags VkQueryControlFlags; typedef enum VkCommandBufferResetFlagBits { VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT = 0x00000001, VK_COMMAND_BUFFER_RESET_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkCommandBufferResetFlagBits; typedef VkFlags VkCommandBufferResetFlags; typedef enum VkStencilFaceFlagBits { VK_STENCIL_FACE_FRONT_BIT = 0x00000001, VK_STENCIL_FACE_BACK_BIT = 0x00000002, VK_STENCIL_FRONT_AND_BACK = 0x00000003, VK_STENCIL_FACE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkStencilFaceFlagBits; typedef VkFlags VkStencilFaceFlags; typedef void* (VKAPI_PTR *PFN_vkAllocationFunction)( void* pUserData, size_t size, size_t alignment, VkSystemAllocationScope allocationScope); typedef void* (VKAPI_PTR *PFN_vkReallocationFunction)( void* pUserData, void* pOriginal, size_t size, size_t alignment, VkSystemAllocationScope allocationScope); typedef void (VKAPI_PTR *PFN_vkFreeFunction)( void* pUserData, void* pMemory); typedef void (VKAPI_PTR *PFN_vkInternalAllocationNotification)( void* pUserData, size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope); typedef void (VKAPI_PTR *PFN_vkInternalFreeNotification)( void* pUserData, size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope); typedef void (VKAPI_PTR *PFN_vkVoidFunction)(void); typedef struct VkApplicationInfo { VkStructureType sType; const void* pNext; const char* pApplicationName; uint32_t applicationVersion; const char* pEngineName; uint32_t engineVersion; uint32_t apiVersion; } VkApplicationInfo; typedef struct VkInstanceCreateInfo { VkStructureType sType; const void* pNext; VkInstanceCreateFlags flags; const VkApplicationInfo* pApplicationInfo; uint32_t enabledLayerCount; const char* const* ppEnabledLayerNames; uint32_t enabledExtensionCount; const char* const* ppEnabledExtensionNames; } VkInstanceCreateInfo; typedef struct VkAllocationCallbacks { void* pUserData; PFN_vkAllocationFunction pfnAllocation; PFN_vkReallocationFunction pfnReallocation; PFN_vkFreeFunction pfnFree; PFN_vkInternalAllocationNotification pfnInternalAllocation; PFN_vkInternalFreeNotification pfnInternalFree; } VkAllocationCallbacks; typedef struct VkPhysicalDeviceFeatures { VkBool32 robustBufferAccess; VkBool32 fullDrawIndexUint32; VkBool32 imageCubeArray; VkBool32 independentBlend; VkBool32 geometryShader; VkBool32 tessellationShader; VkBool32 sampleRateShading; VkBool32 dualSrcBlend; VkBool32 logicOp; VkBool32 multiDrawIndirect; VkBool32 drawIndirectFirstInstance; VkBool32 depthClamp; VkBool32 depthBiasClamp; VkBool32 fillModeNonSolid; VkBool32 depthBounds; VkBool32 wideLines; VkBool32 largePoints; VkBool32 alphaToOne; VkBool32 multiViewport; VkBool32 samplerAnisotropy; VkBool32 textureCompressionETC2; VkBool32 textureCompressionASTC_LDR; VkBool32 textureCompressionBC; VkBool32 occlusionQueryPrecise; VkBool32 pipelineStatisticsQuery; VkBool32 vertexPipelineStoresAndAtomics; VkBool32 fragmentStoresAndAtomics; VkBool32 shaderTessellationAndGeometryPointSize; VkBool32 shaderImageGatherExtended; VkBool32 shaderStorageImageExtendedFormats; VkBool32 shaderStorageImageMultisample; VkBool32 shaderStorageImageReadWithoutFormat; VkBool32 shaderStorageImageWriteWithoutFormat; VkBool32 shaderUniformBufferArrayDynamicIndexing; VkBool32 shaderSampledImageArrayDynamicIndexing; VkBool32 shaderStorageBufferArrayDynamicIndexing; VkBool32 shaderStorageImageArrayDynamicIndexing; VkBool32 shaderClipDistance; VkBool32 shaderCullDistance; VkBool32 shaderFloat64; VkBool32 shaderInt64; VkBool32 shaderInt16; VkBool32 shaderResourceResidency; VkBool32 shaderResourceMinLod; VkBool32 sparseBinding; VkBool32 sparseResidencyBuffer; VkBool32 sparseResidencyImage2D; VkBool32 sparseResidencyImage3D; VkBool32 sparseResidency2Samples; VkBool32 sparseResidency4Samples; VkBool32 sparseResidency8Samples; VkBool32 sparseResidency16Samples; VkBool32 sparseResidencyAliased; VkBool32 variableMultisampleRate; VkBool32 inheritedQueries; } VkPhysicalDeviceFeatures; typedef struct VkFormatProperties { VkFormatFeatureFlags linearTilingFeatures; VkFormatFeatureFlags optimalTilingFeatures; VkFormatFeatureFlags bufferFeatures; } VkFormatProperties; typedef struct VkExtent3D { uint32_t width; uint32_t height; uint32_t depth; } VkExtent3D; typedef struct VkImageFormatProperties { VkExtent3D maxExtent; uint32_t maxMipLevels; uint32_t maxArrayLayers; VkSampleCountFlags sampleCounts; VkDeviceSize maxResourceSize; } VkImageFormatProperties; typedef struct VkPhysicalDeviceLimits { uint32_t maxImageDimension1D; uint32_t maxImageDimension2D; uint32_t maxImageDimension3D; uint32_t maxImageDimensionCube; uint32_t maxImageArrayLayers; uint32_t maxTexelBufferElements; uint32_t maxUniformBufferRange; uint32_t maxStorageBufferRange; uint32_t maxPushConstantsSize; uint32_t maxMemoryAllocationCount; uint32_t maxSamplerAllocationCount; VkDeviceSize bufferImageGranularity; VkDeviceSize sparseAddressSpaceSize; uint32_t maxBoundDescriptorSets; uint32_t maxPerStageDescriptorSamplers; uint32_t maxPerStageDescriptorUniformBuffers; uint32_t maxPerStageDescriptorStorageBuffers; uint32_t maxPerStageDescriptorSampledImages; uint32_t maxPerStageDescriptorStorageImages; uint32_t maxPerStageDescriptorInputAttachments; uint32_t maxPerStageResources; uint32_t maxDescriptorSetSamplers; uint32_t maxDescriptorSetUniformBuffers; uint32_t maxDescriptorSetUniformBuffersDynamic; uint32_t maxDescriptorSetStorageBuffers; uint32_t maxDescriptorSetStorageBuffersDynamic; uint32_t maxDescriptorSetSampledImages; uint32_t maxDescriptorSetStorageImages; uint32_t maxDescriptorSetInputAttachments; uint32_t maxVertexInputAttributes; uint32_t maxVertexInputBindings; uint32_t maxVertexInputAttributeOffset; uint32_t maxVertexInputBindingStride; uint32_t maxVertexOutputComponents; uint32_t maxTessellationGenerationLevel; uint32_t maxTessellationPatchSize; uint32_t maxTessellationControlPerVertexInputComponents; uint32_t maxTessellationControlPerVertexOutputComponents; uint32_t maxTessellationControlPerPatchOutputComponents; uint32_t maxTessellationControlTotalOutputComponents; uint32_t maxTessellationEvaluationInputComponents; uint32_t maxTessellationEvaluationOutputComponents; uint32_t maxGeometryShaderInvocations; uint32_t maxGeometryInputComponents; uint32_t maxGeometryOutputComponents; uint32_t maxGeometryOutputVertices; uint32_t maxGeometryTotalOutputComponents; uint32_t maxFragmentInputComponents; uint32_t maxFragmentOutputAttachments; uint32_t maxFragmentDualSrcAttachments; uint32_t maxFragmentCombinedOutputResources; uint32_t maxComputeSharedMemorySize; uint32_t maxComputeWorkGroupCount[3]; uint32_t maxComputeWorkGroupInvocations; uint32_t maxComputeWorkGroupSize[3]; uint32_t subPixelPrecisionBits; uint32_t subTexelPrecisionBits; uint32_t mipmapPrecisionBits; uint32_t maxDrawIndexedIndexValue; uint32_t maxDrawIndirectCount; float maxSamplerLodBias; float maxSamplerAnisotropy; uint32_t maxViewports; uint32_t maxViewportDimensions[2]; float viewportBoundsRange[2]; uint32_t viewportSubPixelBits; size_t minMemoryMapAlignment; VkDeviceSize minTexelBufferOffsetAlignment; VkDeviceSize minUniformBufferOffsetAlignment; VkDeviceSize minStorageBufferOffsetAlignment; int32_t minTexelOffset; uint32_t maxTexelOffset; int32_t minTexelGatherOffset; uint32_t maxTexelGatherOffset; float minInterpolationOffset; float maxInterpolationOffset; uint32_t subPixelInterpolationOffsetBits; uint32_t maxFramebufferWidth; uint32_t maxFramebufferHeight; uint32_t maxFramebufferLayers; VkSampleCountFlags framebufferColorSampleCounts; VkSampleCountFlags framebufferDepthSampleCounts; VkSampleCountFlags framebufferStencilSampleCounts; VkSampleCountFlags framebufferNoAttachmentsSampleCounts; uint32_t maxColorAttachments; VkSampleCountFlags sampledImageColorSampleCounts; VkSampleCountFlags sampledImageIntegerSampleCounts; VkSampleCountFlags sampledImageDepthSampleCounts; VkSampleCountFlags sampledImageStencilSampleCounts; VkSampleCountFlags storageImageSampleCounts; uint32_t maxSampleMaskWords; VkBool32 timestampComputeAndGraphics; float timestampPeriod; uint32_t maxClipDistances; uint32_t maxCullDistances; uint32_t maxCombinedClipAndCullDistances; uint32_t discreteQueuePriorities; float pointSizeRange[2]; float lineWidthRange[2]; float pointSizeGranularity; float lineWidthGranularity; VkBool32 strictLines; VkBool32 standardSampleLocations; VkDeviceSize optimalBufferCopyOffsetAlignment; VkDeviceSize optimalBufferCopyRowPitchAlignment; VkDeviceSize nonCoherentAtomSize; } VkPhysicalDeviceLimits; typedef struct VkPhysicalDeviceSparseProperties { VkBool32 residencyStandard2DBlockShape; VkBool32 residencyStandard2DMultisampleBlockShape; VkBool32 residencyStandard3DBlockShape; VkBool32 residencyAlignedMipSize; VkBool32 residencyNonResidentStrict; } VkPhysicalDeviceSparseProperties; typedef struct VkPhysicalDeviceProperties { uint32_t apiVersion; uint32_t driverVersion; uint32_t vendorID; uint32_t deviceID; VkPhysicalDeviceType deviceType; char deviceName[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE]; uint8_t pipelineCacheUUID[VK_UUID_SIZE]; VkPhysicalDeviceLimits limits; VkPhysicalDeviceSparseProperties sparseProperties; } VkPhysicalDeviceProperties; typedef struct VkQueueFamilyProperties { VkQueueFlags queueFlags; uint32_t queueCount; uint32_t timestampValidBits; VkExtent3D minImageTransferGranularity; } VkQueueFamilyProperties; typedef struct VkMemoryType { VkMemoryPropertyFlags propertyFlags; uint32_t heapIndex; } VkMemoryType; typedef struct VkMemoryHeap { VkDeviceSize size; VkMemoryHeapFlags flags; } VkMemoryHeap; typedef struct VkPhysicalDeviceMemoryProperties { uint32_t memoryTypeCount; VkMemoryType memoryTypes[VK_MAX_MEMORY_TYPES]; uint32_t memoryHeapCount; VkMemoryHeap memoryHeaps[VK_MAX_MEMORY_HEAPS]; } VkPhysicalDeviceMemoryProperties; typedef struct VkDeviceQueueCreateInfo { VkStructureType sType; const void* pNext; VkDeviceQueueCreateFlags flags; uint32_t queueFamilyIndex; uint32_t queueCount; const float* pQueuePriorities; } VkDeviceQueueCreateInfo; typedef struct VkDeviceCreateInfo { VkStructureType sType; const void* pNext; VkDeviceCreateFlags flags; uint32_t queueCreateInfoCount; const VkDeviceQueueCreateInfo* pQueueCreateInfos; uint32_t enabledLayerCount; const char* const* ppEnabledLayerNames; uint32_t enabledExtensionCount; const char* const* ppEnabledExtensionNames; const VkPhysicalDeviceFeatures* pEnabledFeatures; } VkDeviceCreateInfo; typedef struct VkExtensionProperties { char extensionName[VK_MAX_EXTENSION_NAME_SIZE]; uint32_t specVersion; } VkExtensionProperties; typedef struct VkLayerProperties { char layerName[VK_MAX_EXTENSION_NAME_SIZE]; uint32_t specVersion; uint32_t implementationVersion; char description[VK_MAX_DESCRIPTION_SIZE]; } VkLayerProperties; typedef struct VkSubmitInfo { VkStructureType sType; const void* pNext; uint32_t waitSemaphoreCount; const VkSemaphore* pWaitSemaphores; const VkPipelineStageFlags* pWaitDstStageMask; uint32_t commandBufferCount; const VkCommandBuffer* pCommandBuffers; uint32_t signalSemaphoreCount; const VkSemaphore* pSignalSemaphores; } VkSubmitInfo; typedef struct VkMemoryAllocateInfo { VkStructureType sType; const void* pNext; VkDeviceSize allocationSize; uint32_t memoryTypeIndex; } VkMemoryAllocateInfo; typedef struct VkMappedMemoryRange { VkStructureType sType; const void* pNext; VkDeviceMemory memory; VkDeviceSize offset; VkDeviceSize size; } VkMappedMemoryRange; typedef struct VkMemoryRequirements { VkDeviceSize size; VkDeviceSize alignment; uint32_t memoryTypeBits; } VkMemoryRequirements; typedef struct VkSparseImageFormatProperties { VkImageAspectFlags aspectMask; VkExtent3D imageGranularity; VkSparseImageFormatFlags flags; } VkSparseImageFormatProperties; typedef struct VkSparseImageMemoryRequirements { VkSparseImageFormatProperties formatProperties; uint32_t imageMipTailFirstLod; VkDeviceSize imageMipTailSize; VkDeviceSize imageMipTailOffset; VkDeviceSize imageMipTailStride; } VkSparseImageMemoryRequirements; typedef struct VkSparseMemoryBind { VkDeviceSize resourceOffset; VkDeviceSize size; VkDeviceMemory memory; VkDeviceSize memoryOffset; VkSparseMemoryBindFlags flags; } VkSparseMemoryBind; typedef struct VkSparseBufferMemoryBindInfo { VkBuffer buffer; uint32_t bindCount; const VkSparseMemoryBind* pBinds; } VkSparseBufferMemoryBindInfo; typedef struct VkSparseImageOpaqueMemoryBindInfo { VkImage image; uint32_t bindCount; const VkSparseMemoryBind* pBinds; } VkSparseImageOpaqueMemoryBindInfo; typedef struct VkImageSubresource { VkImageAspectFlags aspectMask; uint32_t mipLevel; uint32_t arrayLayer; } VkImageSubresource; typedef struct VkOffset3D { int32_t x; int32_t y; int32_t z; } VkOffset3D; typedef struct VkSparseImageMemoryBind { VkImageSubresource subresource; VkOffset3D offset; VkExtent3D extent; VkDeviceMemory memory; VkDeviceSize memoryOffset; VkSparseMemoryBindFlags flags; } VkSparseImageMemoryBind; typedef struct VkSparseImageMemoryBindInfo { VkImage image; uint32_t bindCount; const VkSparseImageMemoryBind* pBinds; } VkSparseImageMemoryBindInfo; typedef struct VkBindSparseInfo { VkStructureType sType; const void* pNext; uint32_t waitSemaphoreCount; const VkSemaphore* pWaitSemaphores; uint32_t bufferBindCount; const VkSparseBufferMemoryBindInfo* pBufferBinds; uint32_t imageOpaqueBindCount; const VkSparseImageOpaqueMemoryBindInfo* pImageOpaqueBinds; uint32_t imageBindCount; const VkSparseImageMemoryBindInfo* pImageBinds; uint32_t signalSemaphoreCount; const VkSemaphore* pSignalSemaphores; } VkBindSparseInfo; typedef struct VkFenceCreateInfo { VkStructureType sType; const void* pNext; VkFenceCreateFlags flags; } VkFenceCreateInfo; typedef struct VkSemaphoreCreateInfo { VkStructureType sType; const void* pNext; VkSemaphoreCreateFlags flags; } VkSemaphoreCreateInfo; typedef struct VkEventCreateInfo { VkStructureType sType; const void* pNext; VkEventCreateFlags flags; } VkEventCreateInfo; typedef struct VkQueryPoolCreateInfo { VkStructureType sType; const void* pNext; VkQueryPoolCreateFlags flags; VkQueryType queryType; uint32_t queryCount; VkQueryPipelineStatisticFlags pipelineStatistics; } VkQueryPoolCreateInfo; typedef struct VkBufferCreateInfo { VkStructureType sType; const void* pNext; VkBufferCreateFlags flags; VkDeviceSize size; VkBufferUsageFlags usage; VkSharingMode sharingMode; uint32_t queueFamilyIndexCount; const uint32_t* pQueueFamilyIndices; } VkBufferCreateInfo; typedef struct VkBufferViewCreateInfo { VkStructureType sType; const void* pNext; VkBufferViewCreateFlags flags; VkBuffer buffer; VkFormat format; VkDeviceSize offset; VkDeviceSize range; } VkBufferViewCreateInfo; typedef struct VkImageCreateInfo { VkStructureType sType; const void* pNext; VkImageCreateFlags flags; VkImageType imageType; VkFormat format; VkExtent3D extent; uint32_t mipLevels; uint32_t arrayLayers; VkSampleCountFlagBits samples; VkImageTiling tiling; VkImageUsageFlags usage; VkSharingMode sharingMode; uint32_t queueFamilyIndexCount; const uint32_t* pQueueFamilyIndices; VkImageLayout initialLayout; } VkImageCreateInfo; typedef struct VkSubresourceLayout { VkDeviceSize offset; VkDeviceSize size; VkDeviceSize rowPitch; VkDeviceSize arrayPitch; VkDeviceSize depthPitch; } VkSubresourceLayout; typedef struct VkComponentMapping { VkComponentSwizzle r; VkComponentSwizzle g; VkComponentSwizzle b; VkComponentSwizzle a; } VkComponentMapping; typedef struct VkImageSubresourceRange { VkImageAspectFlags aspectMask; uint32_t baseMipLevel; uint32_t levelCount; uint32_t baseArrayLayer; uint32_t layerCount; } VkImageSubresourceRange; typedef struct VkImageViewCreateInfo { VkStructureType sType; const void* pNext; VkImageViewCreateFlags flags; VkImage image; VkImageViewType viewType; VkFormat format; VkComponentMapping components; VkImageSubresourceRange subresourceRange; } VkImageViewCreateInfo; typedef struct VkShaderModuleCreateInfo { VkStructureType sType; const void* pNext; VkShaderModuleCreateFlags flags; size_t codeSize; const uint32_t* pCode; } VkShaderModuleCreateInfo; typedef struct VkPipelineCacheCreateInfo { VkStructureType sType; const void* pNext; VkPipelineCacheCreateFlags flags; size_t initialDataSize; const void* pInitialData; } VkPipelineCacheCreateInfo; typedef struct VkSpecializationMapEntry { uint32_t constantID; uint32_t offset; size_t size; } VkSpecializationMapEntry; typedef struct VkSpecializationInfo { uint32_t mapEntryCount; const VkSpecializationMapEntry* pMapEntries; size_t dataSize; const void* pData; } VkSpecializationInfo; typedef struct VkPipelineShaderStageCreateInfo { VkStructureType sType; const void* pNext; VkPipelineShaderStageCreateFlags flags; VkShaderStageFlagBits stage; VkShaderModule module; const char* pName; const VkSpecializationInfo* pSpecializationInfo; } VkPipelineShaderStageCreateInfo; typedef struct VkVertexInputBindingDescription { uint32_t binding; uint32_t stride; VkVertexInputRate inputRate; } VkVertexInputBindingDescription; typedef struct VkVertexInputAttributeDescription { uint32_t location; uint32_t binding; VkFormat format; uint32_t offset; } VkVertexInputAttributeDescription; typedef struct VkPipelineVertexInputStateCreateInfo { VkStructureType sType; const void* pNext; VkPipelineVertexInputStateCreateFlags flags; uint32_t vertexBindingDescriptionCount; const VkVertexInputBindingDescription* pVertexBindingDescriptions; uint32_t vertexAttributeDescriptionCount; const VkVertexInputAttributeDescription* pVertexAttributeDescriptions; } VkPipelineVertexInputStateCreateInfo; typedef struct VkPipelineInputAssemblyStateCreateInfo { VkStructureType sType; const void* pNext; VkPipelineInputAssemblyStateCreateFlags flags; VkPrimitiveTopology topology; VkBool32 primitiveRestartEnable; } VkPipelineInputAssemblyStateCreateInfo; typedef struct VkPipelineTessellationStateCreateInfo { VkStructureType sType; const void* pNext; VkPipelineTessellationStateCreateFlags flags; uint32_t patchControlPoints; } VkPipelineTessellationStateCreateInfo; typedef struct VkViewport { float x; float y; float width; float height; float minDepth; float maxDepth; } VkViewport; typedef struct VkOffset2D { int32_t x; int32_t y; } VkOffset2D; typedef struct VkExtent2D { uint32_t width; uint32_t height; } VkExtent2D; typedef struct VkRect2D { VkOffset2D offset; VkExtent2D extent; } VkRect2D; typedef struct VkPipelineViewportStateCreateInfo { VkStructureType sType; const void* pNext; VkPipelineViewportStateCreateFlags flags; uint32_t viewportCount; const VkViewport* pViewports; uint32_t scissorCount; const VkRect2D* pScissors; } VkPipelineViewportStateCreateInfo; typedef struct VkPipelineRasterizationStateCreateInfo { VkStructureType sType; const void* pNext; VkPipelineRasterizationStateCreateFlags flags; VkBool32 depthClampEnable; VkBool32 rasterizerDiscardEnable; VkPolygonMode polygonMode; VkCullModeFlags cullMode; VkFrontFace frontFace; VkBool32 depthBiasEnable; float depthBiasConstantFactor; float depthBiasClamp; float depthBiasSlopeFactor; float lineWidth; } VkPipelineRasterizationStateCreateInfo; typedef struct VkPipelineMultisampleStateCreateInfo { VkStructureType sType; const void* pNext; VkPipelineMultisampleStateCreateFlags flags; VkSampleCountFlagBits rasterizationSamples; VkBool32 sampleShadingEnable; float minSampleShading; const VkSampleMask* pSampleMask; VkBool32 alphaToCoverageEnable; VkBool32 alphaToOneEnable; } VkPipelineMultisampleStateCreateInfo; typedef struct VkStencilOpState { VkStencilOp failOp; VkStencilOp passOp; VkStencilOp depthFailOp; VkCompareOp compareOp; uint32_t compareMask; uint32_t writeMask; uint32_t reference; } VkStencilOpState; typedef struct VkPipelineDepthStencilStateCreateInfo { VkStructureType sType; const void* pNext; VkPipelineDepthStencilStateCreateFlags flags; VkBool32 depthTestEnable; VkBool32 depthWriteEnable; VkCompareOp depthCompareOp; VkBool32 depthBoundsTestEnable; VkBool32 stencilTestEnable; VkStencilOpState front; VkStencilOpState back; float minDepthBounds; float maxDepthBounds; } VkPipelineDepthStencilStateCreateInfo; typedef struct VkPipelineColorBlendAttachmentState { VkBool32 blendEnable; VkBlendFactor srcColorBlendFactor; VkBlendFactor dstColorBlendFactor; VkBlendOp colorBlendOp; VkBlendFactor srcAlphaBlendFactor; VkBlendFactor dstAlphaBlendFactor; VkBlendOp alphaBlendOp; VkColorComponentFlags colorWriteMask; } VkPipelineColorBlendAttachmentState; typedef struct VkPipelineColorBlendStateCreateInfo { VkStructureType sType; const void* pNext; VkPipelineColorBlendStateCreateFlags flags; VkBool32 logicOpEnable; VkLogicOp logicOp; uint32_t attachmentCount; const VkPipelineColorBlendAttachmentState* pAttachments; float blendConstants[4]; } VkPipelineColorBlendStateCreateInfo; typedef struct VkPipelineDynamicStateCreateInfo { VkStructureType sType; const void* pNext; VkPipelineDynamicStateCreateFlags flags; uint32_t dynamicStateCount; const VkDynamicState* pDynamicStates; } VkPipelineDynamicStateCreateInfo; typedef struct VkGraphicsPipelineCreateInfo { VkStructureType sType; const void* pNext; VkPipelineCreateFlags flags; uint32_t stageCount; const VkPipelineShaderStageCreateInfo* pStages; const VkPipelineVertexInputStateCreateInfo* pVertexInputState; const VkPipelineInputAssemblyStateCreateInfo* pInputAssemblyState; const VkPipelineTessellationStateCreateInfo* pTessellationState; const VkPipelineViewportStateCreateInfo* pViewportState; const VkPipelineRasterizationStateCreateInfo* pRasterizationState; const VkPipelineMultisampleStateCreateInfo* pMultisampleState; const VkPipelineDepthStencilStateCreateInfo* pDepthStencilState; const VkPipelineColorBlendStateCreateInfo* pColorBlendState; const VkPipelineDynamicStateCreateInfo* pDynamicState; VkPipelineLayout layout; VkRenderPass renderPass; uint32_t subpass; VkPipeline basePipelineHandle; int32_t basePipelineIndex; } VkGraphicsPipelineCreateInfo; typedef struct VkComputePipelineCreateInfo { VkStructureType sType; const void* pNext; VkPipelineCreateFlags flags; VkPipelineShaderStageCreateInfo stage; VkPipelineLayout layout; VkPipeline basePipelineHandle; int32_t basePipelineIndex; } VkComputePipelineCreateInfo; typedef struct VkPushConstantRange { VkShaderStageFlags stageFlags; uint32_t offset; uint32_t size; } VkPushConstantRange; typedef struct VkPipelineLayoutCreateInfo { VkStructureType sType; const void* pNext; VkPipelineLayoutCreateFlags flags; uint32_t setLayoutCount; const VkDescriptorSetLayout* pSetLayouts; uint32_t pushConstantRangeCount; const VkPushConstantRange* pPushConstantRanges; } VkPipelineLayoutCreateInfo; typedef struct VkSamplerCreateInfo { VkStructureType sType; const void* pNext; VkSamplerCreateFlags flags; VkFilter magFilter; VkFilter minFilter; VkSamplerMipmapMode mipmapMode; VkSamplerAddressMode addressModeU; VkSamplerAddressMode addressModeV; VkSamplerAddressMode addressModeW; float mipLodBias; VkBool32 anisotropyEnable; float maxAnisotropy; VkBool32 compareEnable; VkCompareOp compareOp; float minLod; float maxLod; VkBorderColor borderColor; VkBool32 unnormalizedCoordinates; } VkSamplerCreateInfo; typedef struct VkDescriptorSetLayoutBinding { uint32_t binding; VkDescriptorType descriptorType; uint32_t descriptorCount; VkShaderStageFlags stageFlags; const VkSampler* pImmutableSamplers; } VkDescriptorSetLayoutBinding; typedef struct VkDescriptorSetLayoutCreateInfo { VkStructureType sType; const void* pNext; VkDescriptorSetLayoutCreateFlags flags; uint32_t bindingCount; const VkDescriptorSetLayoutBinding* pBindings; } VkDescriptorSetLayoutCreateInfo; typedef struct VkDescriptorPoolSize { VkDescriptorType type; uint32_t descriptorCount; } VkDescriptorPoolSize; typedef struct VkDescriptorPoolCreateInfo { VkStructureType sType; const void* pNext; VkDescriptorPoolCreateFlags flags; uint32_t maxSets; uint32_t poolSizeCount; const VkDescriptorPoolSize* pPoolSizes; } VkDescriptorPoolCreateInfo; typedef struct VkDescriptorSetAllocateInfo { VkStructureType sType; const void* pNext; VkDescriptorPool descriptorPool; uint32_t descriptorSetCount; const VkDescriptorSetLayout* pSetLayouts; } VkDescriptorSetAllocateInfo; typedef struct VkDescriptorImageInfo { VkSampler sampler; VkImageView imageView; VkImageLayout imageLayout; } VkDescriptorImageInfo; typedef struct VkDescriptorBufferInfo { VkBuffer buffer; VkDeviceSize offset; VkDeviceSize range; } VkDescriptorBufferInfo; typedef struct VkWriteDescriptorSet { VkStructureType sType; const void* pNext; VkDescriptorSet dstSet; uint32_t dstBinding; uint32_t dstArrayElement; uint32_t descriptorCount; VkDescriptorType descriptorType; const VkDescriptorImageInfo* pImageInfo; const VkDescriptorBufferInfo* pBufferInfo; const VkBufferView* pTexelBufferView; } VkWriteDescriptorSet; typedef struct VkCopyDescriptorSet { VkStructureType sType; const void* pNext; VkDescriptorSet srcSet; uint32_t srcBinding; uint32_t srcArrayElement; VkDescriptorSet dstSet; uint32_t dstBinding; uint32_t dstArrayElement; uint32_t descriptorCount; } VkCopyDescriptorSet; typedef struct VkFramebufferCreateInfo { VkStructureType sType; const void* pNext; VkFramebufferCreateFlags flags; VkRenderPass renderPass; uint32_t attachmentCount; const VkImageView* pAttachments; uint32_t width; uint32_t height; uint32_t layers; } VkFramebufferCreateInfo; typedef struct VkAttachmentDescription { VkAttachmentDescriptionFlags flags; VkFormat format; VkSampleCountFlagBits samples; VkAttachmentLoadOp loadOp; VkAttachmentStoreOp storeOp; VkAttachmentLoadOp stencilLoadOp; VkAttachmentStoreOp stencilStoreOp; VkImageLayout initialLayout; VkImageLayout finalLayout; } VkAttachmentDescription; typedef struct VkAttachmentReference { uint32_t attachment; VkImageLayout layout; } VkAttachmentReference; typedef struct VkSubpassDescription { VkSubpassDescriptionFlags flags; VkPipelineBindPoint pipelineBindPoint; uint32_t inputAttachmentCount; const VkAttachmentReference* pInputAttachments; uint32_t colorAttachmentCount; const VkAttachmentReference* pColorAttachments; const VkAttachmentReference* pResolveAttachments; const VkAttachmentReference* pDepthStencilAttachment; uint32_t preserveAttachmentCount; const uint32_t* pPreserveAttachments; } VkSubpassDescription; typedef struct VkSubpassDependency { uint32_t srcSubpass; uint32_t dstSubpass; VkPipelineStageFlags srcStageMask; VkPipelineStageFlags dstStageMask; VkAccessFlags srcAccessMask; VkAccessFlags dstAccessMask; VkDependencyFlags dependencyFlags; } VkSubpassDependency; typedef struct VkRenderPassCreateInfo { VkStructureType sType; const void* pNext; VkRenderPassCreateFlags flags; uint32_t attachmentCount; const VkAttachmentDescription* pAttachments; uint32_t subpassCount; const VkSubpassDescription* pSubpasses; uint32_t dependencyCount; const VkSubpassDependency* pDependencies; } VkRenderPassCreateInfo; typedef struct VkCommandPoolCreateInfo { VkStructureType sType; const void* pNext; VkCommandPoolCreateFlags flags; uint32_t queueFamilyIndex; } VkCommandPoolCreateInfo; typedef struct VkCommandBufferAllocateInfo { VkStructureType sType; const void* pNext; VkCommandPool commandPool; VkCommandBufferLevel level; uint32_t commandBufferCount; } VkCommandBufferAllocateInfo; typedef struct VkCommandBufferInheritanceInfo { VkStructureType sType; const void* pNext; VkRenderPass renderPass; uint32_t subpass; VkFramebuffer framebuffer; VkBool32 occlusionQueryEnable; VkQueryControlFlags queryFlags; VkQueryPipelineStatisticFlags pipelineStatistics; } VkCommandBufferInheritanceInfo; typedef struct VkCommandBufferBeginInfo { VkStructureType sType; const void* pNext; VkCommandBufferUsageFlags flags; const VkCommandBufferInheritanceInfo* pInheritanceInfo; } VkCommandBufferBeginInfo; typedef struct VkBufferCopy { VkDeviceSize srcOffset; VkDeviceSize dstOffset; VkDeviceSize size; } VkBufferCopy; typedef struct VkImageSubresourceLayers { VkImageAspectFlags aspectMask; uint32_t mipLevel; uint32_t baseArrayLayer; uint32_t layerCount; } VkImageSubresourceLayers; typedef struct VkImageCopy { VkImageSubresourceLayers srcSubresource; VkOffset3D srcOffset; VkImageSubresourceLayers dstSubresource; VkOffset3D dstOffset; VkExtent3D extent; } VkImageCopy; typedef struct VkImageBlit { VkImageSubresourceLayers srcSubresource; VkOffset3D srcOffsets[2]; VkImageSubresourceLayers dstSubresource; VkOffset3D dstOffsets[2]; } VkImageBlit; typedef struct VkBufferImageCopy { VkDeviceSize bufferOffset; uint32_t bufferRowLength; uint32_t bufferImageHeight; VkImageSubresourceLayers imageSubresource; VkOffset3D imageOffset; VkExtent3D imageExtent; } VkBufferImageCopy; typedef union VkClearColorValue { float float32[4]; int32_t int32[4]; uint32_t uint32[4]; } VkClearColorValue; typedef struct VkClearDepthStencilValue { float depth; uint32_t stencil; } VkClearDepthStencilValue; typedef union VkClearValue { VkClearColorValue color; VkClearDepthStencilValue depthStencil; } VkClearValue; typedef struct VkClearAttachment { VkImageAspectFlags aspectMask; uint32_t colorAttachment; VkClearValue clearValue; } VkClearAttachment; typedef struct VkClearRect { VkRect2D rect; uint32_t baseArrayLayer; uint32_t layerCount; } VkClearRect; typedef struct VkImageResolve { VkImageSubresourceLayers srcSubresource; VkOffset3D srcOffset; VkImageSubresourceLayers dstSubresource; VkOffset3D dstOffset; VkExtent3D extent; } VkImageResolve; typedef struct VkMemoryBarrier { VkStructureType sType; const void* pNext; VkAccessFlags srcAccessMask; VkAccessFlags dstAccessMask; } VkMemoryBarrier; typedef struct VkBufferMemoryBarrier { VkStructureType sType; const void* pNext; VkAccessFlags srcAccessMask; VkAccessFlags dstAccessMask; uint32_t srcQueueFamilyIndex; uint32_t dstQueueFamilyIndex; VkBuffer buffer; VkDeviceSize offset; VkDeviceSize size; } VkBufferMemoryBarrier; typedef struct VkImageMemoryBarrier { VkStructureType sType; const void* pNext; VkAccessFlags srcAccessMask; VkAccessFlags dstAccessMask; VkImageLayout oldLayout; VkImageLayout newLayout; uint32_t srcQueueFamilyIndex; uint32_t dstQueueFamilyIndex; VkImage image; VkImageSubresourceRange subresourceRange; } VkImageMemoryBarrier; typedef struct VkRenderPassBeginInfo { VkStructureType sType; const void* pNext; VkRenderPass renderPass; VkFramebuffer framebuffer; VkRect2D renderArea; uint32_t clearValueCount; const VkClearValue* pClearValues; } VkRenderPassBeginInfo; typedef struct VkDispatchIndirectCommand { uint32_t x; uint32_t y; uint32_t z; } VkDispatchIndirectCommand; typedef struct VkDrawIndexedIndirectCommand { uint32_t indexCount; uint32_t instanceCount; uint32_t firstIndex; int32_t vertexOffset; uint32_t firstInstance; } VkDrawIndexedIndirectCommand; typedef struct VkDrawIndirectCommand { uint32_t vertexCount; uint32_t instanceCount; uint32_t firstVertex; uint32_t firstInstance; } VkDrawIndirectCommand; typedef VkResult (VKAPI_PTR *PFN_vkCreateInstance)(const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance); typedef void (VKAPI_PTR *PFN_vkDestroyInstance)(VkInstance instance, const VkAllocationCallbacks* pAllocator); typedef VkResult (VKAPI_PTR *PFN_vkEnumeratePhysicalDevices)(VkInstance instance, uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices); typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceFeatures)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures* pFeatures); typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceFormatProperties)(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties* pFormatProperties); typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceImageFormatProperties)(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling, VkImageUsageFlags usage, VkImageCreateFlags flags, VkImageFormatProperties* pImageFormatProperties); typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceProperties)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties* pProperties); typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceQueueFamilyProperties)(VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties* pQueueFamilyProperties); typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceMemoryProperties)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties* pMemoryProperties); typedef PFN_vkVoidFunction (VKAPI_PTR *PFN_vkGetInstanceProcAddr)(VkInstance instance, const char* pName); typedef PFN_vkVoidFunction (VKAPI_PTR *PFN_vkGetDeviceProcAddr)(VkDevice device, const char* pName); typedef VkResult (VKAPI_PTR *PFN_vkCreateDevice)(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDevice* pDevice); typedef void (VKAPI_PTR *PFN_vkDestroyDevice)(VkDevice device, const VkAllocationCallbacks* pAllocator); typedef VkResult (VKAPI_PTR *PFN_vkEnumerateInstanceExtensionProperties)(const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties); typedef VkResult (VKAPI_PTR *PFN_vkEnumerateDeviceExtensionProperties)(VkPhysicalDevice physicalDevice, const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties); typedef VkResult (VKAPI_PTR *PFN_vkEnumerateInstanceLayerProperties)(uint32_t* pPropertyCount, VkLayerProperties* pProperties); typedef VkResult (VKAPI_PTR *PFN_vkEnumerateDeviceLayerProperties)(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkLayerProperties* pProperties); typedef void (VKAPI_PTR *PFN_vkGetDeviceQueue)(VkDevice device, uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue* pQueue); typedef VkResult (VKAPI_PTR *PFN_vkQueueSubmit)(VkQueue queue, uint32_t submitCount, const VkSubmitInfo* pSubmits, VkFence fence); typedef VkResult (VKAPI_PTR *PFN_vkQueueWaitIdle)(VkQueue queue); typedef VkResult (VKAPI_PTR *PFN_vkDeviceWaitIdle)(VkDevice device); typedef VkResult (VKAPI_PTR *PFN_vkAllocateMemory)(VkDevice device, const VkMemoryAllocateInfo* pAllocateInfo, const VkAllocationCallbacks* pAllocator, VkDeviceMemory* pMemory); typedef void (VKAPI_PTR *PFN_vkFreeMemory)(VkDevice device, VkDeviceMemory memory, const VkAllocationCallbacks* pAllocator); typedef VkResult (VKAPI_PTR *PFN_vkMapMemory)(VkDevice device, VkDeviceMemory memory, VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, void** ppData); typedef void (VKAPI_PTR *PFN_vkUnmapMemory)(VkDevice device, VkDeviceMemory memory); typedef VkResult (VKAPI_PTR *PFN_vkFlushMappedMemoryRanges)(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges); typedef VkResult (VKAPI_PTR *PFN_vkInvalidateMappedMemoryRanges)(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges); typedef void (VKAPI_PTR *PFN_vkGetDeviceMemoryCommitment)(VkDevice device, VkDeviceMemory memory, VkDeviceSize* pCommittedMemoryInBytes); typedef VkResult (VKAPI_PTR *PFN_vkBindBufferMemory)(VkDevice device, VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize memoryOffset); typedef VkResult (VKAPI_PTR *PFN_vkBindImageMemory)(VkDevice device, VkImage image, VkDeviceMemory memory, VkDeviceSize memoryOffset); typedef void (VKAPI_PTR *PFN_vkGetBufferMemoryRequirements)(VkDevice device, VkBuffer buffer, VkMemoryRequirements* pMemoryRequirements); typedef void (VKAPI_PTR *PFN_vkGetImageMemoryRequirements)(VkDevice device, VkImage image, VkMemoryRequirements* pMemoryRequirements); typedef void (VKAPI_PTR *PFN_vkGetImageSparseMemoryRequirements)(VkDevice device, VkImage image, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements* pSparseMemoryRequirements); typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceSparseImageFormatProperties)(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkSampleCountFlagBits samples, VkImageUsageFlags usage, VkImageTiling tiling, uint32_t* pPropertyCount, VkSparseImageFormatProperties* pProperties); typedef VkResult (VKAPI_PTR *PFN_vkQueueBindSparse)(VkQueue queue, uint32_t bindInfoCount, const VkBindSparseInfo* pBindInfo, VkFence fence); typedef VkResult (VKAPI_PTR *PFN_vkCreateFence)(VkDevice device, const VkFenceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkFence* pFence); typedef void (VKAPI_PTR *PFN_vkDestroyFence)(VkDevice device, VkFence fence, const VkAllocationCallbacks* pAllocator); typedef VkResult (VKAPI_PTR *PFN_vkResetFences)(VkDevice device, uint32_t fenceCount, const VkFence* pFences); typedef VkResult (VKAPI_PTR *PFN_vkGetFenceStatus)(VkDevice device, VkFence fence); typedef VkResult (VKAPI_PTR *PFN_vkWaitForFences)(VkDevice device, uint32_t fenceCount, const VkFence* pFences, VkBool32 waitAll, uint64_t timeout); typedef VkResult (VKAPI_PTR *PFN_vkCreateSemaphore)(VkDevice device, const VkSemaphoreCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSemaphore* pSemaphore); typedef void (VKAPI_PTR *PFN_vkDestroySemaphore)(VkDevice device, VkSemaphore semaphore, const VkAllocationCallbacks* pAllocator); typedef VkResult (VKAPI_PTR *PFN_vkCreateEvent)(VkDevice device, const VkEventCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkEvent* pEvent); typedef void (VKAPI_PTR *PFN_vkDestroyEvent)(VkDevice device, VkEvent event, const VkAllocationCallbacks* pAllocator); typedef VkResult (VKAPI_PTR *PFN_vkGetEventStatus)(VkDevice device, VkEvent event); typedef VkResult (VKAPI_PTR *PFN_vkSetEvent)(VkDevice device, VkEvent event); typedef VkResult (VKAPI_PTR *PFN_vkResetEvent)(VkDevice device, VkEvent event); typedef VkResult (VKAPI_PTR *PFN_vkCreateQueryPool)(VkDevice device, const VkQueryPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkQueryPool* pQueryPool); typedef void (VKAPI_PTR *PFN_vkDestroyQueryPool)(VkDevice device, VkQueryPool queryPool, const VkAllocationCallbacks* pAllocator); typedef VkResult (VKAPI_PTR *PFN_vkGetQueryPoolResults)(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, size_t dataSize, void* pData, VkDeviceSize stride, VkQueryResultFlags flags); typedef VkResult (VKAPI_PTR *PFN_vkCreateBuffer)(VkDevice device, const VkBufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer); typedef void (VKAPI_PTR *PFN_vkDestroyBuffer)(VkDevice device, VkBuffer buffer, const VkAllocationCallbacks* pAllocator); typedef VkResult (VKAPI_PTR *PFN_vkCreateBufferView)(VkDevice device, const VkBufferViewCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBufferView* pView); typedef void (VKAPI_PTR *PFN_vkDestroyBufferView)(VkDevice device, VkBufferView bufferView, const VkAllocationCallbacks* pAllocator); typedef VkResult (VKAPI_PTR *PFN_vkCreateImage)(VkDevice device, const VkImageCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkImage* pImage); typedef void (VKAPI_PTR *PFN_vkDestroyImage)(VkDevice device, VkImage image, const VkAllocationCallbacks* pAllocator); typedef void (VKAPI_PTR *PFN_vkGetImageSubresourceLayout)(VkDevice device, VkImage image, const VkImageSubresource* pSubresource, VkSubresourceLayout* pLayout); typedef VkResult (VKAPI_PTR *PFN_vkCreateImageView)(VkDevice device, const VkImageViewCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkImageView* pView); typedef void (VKAPI_PTR *PFN_vkDestroyImageView)(VkDevice device, VkImageView imageView, const VkAllocationCallbacks* pAllocator); typedef VkResult (VKAPI_PTR *PFN_vkCreateShaderModule)(VkDevice device, const VkShaderModuleCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkShaderModule* pShaderModule); typedef void (VKAPI_PTR *PFN_vkDestroyShaderModule)(VkDevice device, VkShaderModule shaderModule, const VkAllocationCallbacks* pAllocator); typedef VkResult (VKAPI_PTR *PFN_vkCreatePipelineCache)(VkDevice device, const VkPipelineCacheCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkPipelineCache* pPipelineCache); typedef void (VKAPI_PTR *PFN_vkDestroyPipelineCache)(VkDevice device, VkPipelineCache pipelineCache, const VkAllocationCallbacks* pAllocator); typedef VkResult (VKAPI_PTR *PFN_vkGetPipelineCacheData)(VkDevice device, VkPipelineCache pipelineCache, size_t* pDataSize, void* pData); typedef VkResult (VKAPI_PTR *PFN_vkMergePipelineCaches)(VkDevice device, VkPipelineCache dstCache, uint32_t srcCacheCount, const VkPipelineCache* pSrcCaches); typedef VkResult (VKAPI_PTR *PFN_vkCreateGraphicsPipelines)(VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount, const VkGraphicsPipelineCreateInfo* pCreateInfos, const VkAllocationCallbacks* pAllocator, VkPipeline* pPipelines); typedef VkResult (VKAPI_PTR *PFN_vkCreateComputePipelines)(VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount, const VkComputePipelineCreateInfo* pCreateInfos, const VkAllocationCallbacks* pAllocator, VkPipeline* pPipelines); typedef void (VKAPI_PTR *PFN_vkDestroyPipeline)(VkDevice device, VkPipeline pipeline, const VkAllocationCallbacks* pAllocator); typedef VkResult (VKAPI_PTR *PFN_vkCreatePipelineLayout)(VkDevice device, const VkPipelineLayoutCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkPipelineLayout* pPipelineLayout); typedef void (VKAPI_PTR *PFN_vkDestroyPipelineLayout)(VkDevice device, VkPipelineLayout pipelineLayout, const VkAllocationCallbacks* pAllocator); typedef VkResult (VKAPI_PTR *PFN_vkCreateSampler)(VkDevice device, const VkSamplerCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSampler* pSampler); typedef void (VKAPI_PTR *PFN_vkDestroySampler)(VkDevice device, VkSampler sampler, const VkAllocationCallbacks* pAllocator); typedef VkResult (VKAPI_PTR *PFN_vkCreateDescriptorSetLayout)(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorSetLayout* pSetLayout); typedef void (VKAPI_PTR *PFN_vkDestroyDescriptorSetLayout)(VkDevice device, VkDescriptorSetLayout descriptorSetLayout, const VkAllocationCallbacks* pAllocator); typedef VkResult (VKAPI_PTR *PFN_vkCreateDescriptorPool)(VkDevice device, const VkDescriptorPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorPool* pDescriptorPool); typedef void (VKAPI_PTR *PFN_vkDestroyDescriptorPool)(VkDevice device, VkDescriptorPool descriptorPool, const VkAllocationCallbacks* pAllocator); typedef VkResult (VKAPI_PTR *PFN_vkResetDescriptorPool)(VkDevice device, VkDescriptorPool descriptorPool, VkDescriptorPoolResetFlags flags); typedef VkResult (VKAPI_PTR *PFN_vkAllocateDescriptorSets)(VkDevice device, const VkDescriptorSetAllocateInfo* pAllocateInfo, VkDescriptorSet* pDescriptorSets); typedef VkResult (VKAPI_PTR *PFN_vkFreeDescriptorSets)(VkDevice device, VkDescriptorPool descriptorPool, uint32_t descriptorSetCount, const VkDescriptorSet* pDescriptorSets); typedef void (VKAPI_PTR *PFN_vkUpdateDescriptorSets)(VkDevice device, uint32_t descriptorWriteCount, const VkWriteDescriptorSet* pDescriptorWrites, uint32_t descriptorCopyCount, const VkCopyDescriptorSet* pDescriptorCopies); typedef VkResult (VKAPI_PTR *PFN_vkCreateFramebuffer)(VkDevice device, const VkFramebufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkFramebuffer* pFramebuffer); typedef void (VKAPI_PTR *PFN_vkDestroyFramebuffer)(VkDevice device, VkFramebuffer framebuffer, const VkAllocationCallbacks* pAllocator); typedef VkResult (VKAPI_PTR *PFN_vkCreateRenderPass)(VkDevice device, const VkRenderPassCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass); typedef void (VKAPI_PTR *PFN_vkDestroyRenderPass)(VkDevice device, VkRenderPass renderPass, const VkAllocationCallbacks* pAllocator); typedef void (VKAPI_PTR *PFN_vkGetRenderAreaGranularity)(VkDevice device, VkRenderPass renderPass, VkExtent2D* pGranularity); typedef VkResult (VKAPI_PTR *PFN_vkCreateCommandPool)(VkDevice device, const VkCommandPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkCommandPool* pCommandPool); typedef void (VKAPI_PTR *PFN_vkDestroyCommandPool)(VkDevice device, VkCommandPool commandPool, const VkAllocationCallbacks* pAllocator); typedef VkResult (VKAPI_PTR *PFN_vkResetCommandPool)(VkDevice device, VkCommandPool commandPool, VkCommandPoolResetFlags flags); typedef VkResult (VKAPI_PTR *PFN_vkAllocateCommandBuffers)(VkDevice device, const VkCommandBufferAllocateInfo* pAllocateInfo, VkCommandBuffer* pCommandBuffers); typedef void (VKAPI_PTR *PFN_vkFreeCommandBuffers)(VkDevice device, VkCommandPool commandPool, uint32_t commandBufferCount, const VkCommandBuffer* pCommandBuffers); typedef VkResult (VKAPI_PTR *PFN_vkBeginCommandBuffer)(VkCommandBuffer commandBuffer, const VkCommandBufferBeginInfo* pBeginInfo); typedef VkResult (VKAPI_PTR *PFN_vkEndCommandBuffer)(VkCommandBuffer commandBuffer); typedef VkResult (VKAPI_PTR *PFN_vkResetCommandBuffer)(VkCommandBuffer commandBuffer, VkCommandBufferResetFlags flags); typedef void (VKAPI_PTR *PFN_vkCmdBindPipeline)(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipeline pipeline); typedef void (VKAPI_PTR *PFN_vkCmdSetViewport)(VkCommandBuffer commandBuffer, uint32_t firstViewport, uint32_t viewportCount, const VkViewport* pViewports); typedef void (VKAPI_PTR *PFN_vkCmdSetScissor)(VkCommandBuffer commandBuffer, uint32_t firstScissor, uint32_t scissorCount, const VkRect2D* pScissors); typedef void (VKAPI_PTR *PFN_vkCmdSetLineWidth)(VkCommandBuffer commandBuffer, float lineWidth); typedef void (VKAPI_PTR *PFN_vkCmdSetDepthBias)(VkCommandBuffer commandBuffer, float depthBiasConstantFactor, float depthBiasClamp, float depthBiasSlopeFactor); typedef void (VKAPI_PTR *PFN_vkCmdSetBlendConstants)(VkCommandBuffer commandBuffer, const float blendConstants[4]); typedef void (VKAPI_PTR *PFN_vkCmdSetDepthBounds)(VkCommandBuffer commandBuffer, float minDepthBounds, float maxDepthBounds); typedef void (VKAPI_PTR *PFN_vkCmdSetStencilCompareMask)(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, uint32_t compareMask); typedef void (VKAPI_PTR *PFN_vkCmdSetStencilWriteMask)(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, uint32_t writeMask); typedef void (VKAPI_PTR *PFN_vkCmdSetStencilReference)(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, uint32_t reference); typedef void (VKAPI_PTR *PFN_vkCmdBindDescriptorSets)(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t firstSet, uint32_t descriptorSetCount, const VkDescriptorSet* pDescriptorSets, uint32_t dynamicOffsetCount, const uint32_t* pDynamicOffsets); typedef void (VKAPI_PTR *PFN_vkCmdBindIndexBuffer)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkIndexType indexType); typedef void (VKAPI_PTR *PFN_vkCmdBindVertexBuffers)(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer* pBuffers, const VkDeviceSize* pOffsets); typedef void (VKAPI_PTR *PFN_vkCmdDraw)(VkCommandBuffer commandBuffer, uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance); typedef void (VKAPI_PTR *PFN_vkCmdDrawIndexed)(VkCommandBuffer commandBuffer, uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance); typedef void (VKAPI_PTR *PFN_vkCmdDrawIndirect)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride); typedef void (VKAPI_PTR *PFN_vkCmdDrawIndexedIndirect)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride); typedef void (VKAPI_PTR *PFN_vkCmdDispatch)(VkCommandBuffer commandBuffer, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ); typedef void (VKAPI_PTR *PFN_vkCmdDispatchIndirect)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset); typedef void (VKAPI_PTR *PFN_vkCmdCopyBuffer)(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferCopy* pRegions); typedef void (VKAPI_PTR *PFN_vkCmdCopyImage)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageCopy* pRegions); typedef void (VKAPI_PTR *PFN_vkCmdBlitImage)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageBlit* pRegions, VkFilter filter); typedef void (VKAPI_PTR *PFN_vkCmdCopyBufferToImage)(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkBufferImageCopy* pRegions); typedef void (VKAPI_PTR *PFN_vkCmdCopyImageToBuffer)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferImageCopy* pRegions); typedef void (VKAPI_PTR *PFN_vkCmdUpdateBuffer)(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const void* pData); typedef void (VKAPI_PTR *PFN_vkCmdFillBuffer)(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize size, uint32_t data); typedef void (VKAPI_PTR *PFN_vkCmdClearColorImage)(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearColorValue* pColor, uint32_t rangeCount, const VkImageSubresourceRange* pRanges); typedef void (VKAPI_PTR *PFN_vkCmdClearDepthStencilImage)(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearDepthStencilValue* pDepthStencil, uint32_t rangeCount, const VkImageSubresourceRange* pRanges); typedef void (VKAPI_PTR *PFN_vkCmdClearAttachments)(VkCommandBuffer commandBuffer, uint32_t attachmentCount, const VkClearAttachment* pAttachments, uint32_t rectCount, const VkClearRect* pRects); typedef void (VKAPI_PTR *PFN_vkCmdResolveImage)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageResolve* pRegions); typedef void (VKAPI_PTR *PFN_vkCmdSetEvent)(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags stageMask); typedef void (VKAPI_PTR *PFN_vkCmdResetEvent)(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags stageMask); typedef void (VKAPI_PTR *PFN_vkCmdWaitEvents)(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent* pEvents, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, uint32_t memoryBarrierCount, const VkMemoryBarrier* pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier* pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier* pImageMemoryBarriers); typedef void (VKAPI_PTR *PFN_vkCmdPipelineBarrier)(VkCommandBuffer commandBuffer, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkDependencyFlags dependencyFlags, uint32_t memoryBarrierCount, const VkMemoryBarrier* pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier* pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier* pImageMemoryBarriers); typedef void (VKAPI_PTR *PFN_vkCmdBeginQuery)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query, VkQueryControlFlags flags); typedef void (VKAPI_PTR *PFN_vkCmdEndQuery)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query); typedef void (VKAPI_PTR *PFN_vkCmdResetQueryPool)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount); typedef void (VKAPI_PTR *PFN_vkCmdWriteTimestamp)(VkCommandBuffer commandBuffer, VkPipelineStageFlagBits pipelineStage, VkQueryPool queryPool, uint32_t query); typedef void (VKAPI_PTR *PFN_vkCmdCopyQueryPoolResults)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize stride, VkQueryResultFlags flags); typedef void (VKAPI_PTR *PFN_vkCmdPushConstants)(VkCommandBuffer commandBuffer, VkPipelineLayout layout, VkShaderStageFlags stageFlags, uint32_t offset, uint32_t size, const void* pValues); typedef void (VKAPI_PTR *PFN_vkCmdBeginRenderPass)(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo* pRenderPassBegin, VkSubpassContents contents); typedef void (VKAPI_PTR *PFN_vkCmdNextSubpass)(VkCommandBuffer commandBuffer, VkSubpassContents contents); typedef void (VKAPI_PTR *PFN_vkCmdEndRenderPass)(VkCommandBuffer commandBuffer); typedef void (VKAPI_PTR *PFN_vkCmdExecuteCommands)(VkCommandBuffer commandBuffer, uint32_t commandBufferCount, const VkCommandBuffer* pCommandBuffers); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR VkResult VKAPI_CALL vkCreateInstance( const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance); VKAPI_ATTR void VKAPI_CALL vkDestroyInstance( VkInstance instance, const VkAllocationCallbacks* pAllocator); VKAPI_ATTR VkResult VKAPI_CALL vkEnumeratePhysicalDevices( VkInstance instance, uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices); VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceFeatures( VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures* pFeatures); VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceFormatProperties( VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties* pFormatProperties); VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceImageFormatProperties( VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling, VkImageUsageFlags usage, VkImageCreateFlags flags, VkImageFormatProperties* pImageFormatProperties); VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceProperties( VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties* pProperties); VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceQueueFamilyProperties( VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties* pQueueFamilyProperties); VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceMemoryProperties( VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties* pMemoryProperties); VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetInstanceProcAddr( VkInstance instance, const char* pName); VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetDeviceProcAddr( VkDevice device, const char* pName); VKAPI_ATTR VkResult VKAPI_CALL vkCreateDevice( VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDevice* pDevice); VKAPI_ATTR void VKAPI_CALL vkDestroyDevice( VkDevice device, const VkAllocationCallbacks* pAllocator); VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceExtensionProperties( const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties); VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateDeviceExtensionProperties( VkPhysicalDevice physicalDevice, const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties); VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceLayerProperties( uint32_t* pPropertyCount, VkLayerProperties* pProperties); VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateDeviceLayerProperties( VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkLayerProperties* pProperties); VKAPI_ATTR void VKAPI_CALL vkGetDeviceQueue( VkDevice device, uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue* pQueue); VKAPI_ATTR VkResult VKAPI_CALL vkQueueSubmit( VkQueue queue, uint32_t submitCount, const VkSubmitInfo* pSubmits, VkFence fence); VKAPI_ATTR VkResult VKAPI_CALL vkQueueWaitIdle( VkQueue queue); VKAPI_ATTR VkResult VKAPI_CALL vkDeviceWaitIdle( VkDevice device); VKAPI_ATTR VkResult VKAPI_CALL vkAllocateMemory( VkDevice device, const VkMemoryAllocateInfo* pAllocateInfo, const VkAllocationCallbacks* pAllocator, VkDeviceMemory* pMemory); VKAPI_ATTR void VKAPI_CALL vkFreeMemory( VkDevice device, VkDeviceMemory memory, const VkAllocationCallbacks* pAllocator); VKAPI_ATTR VkResult VKAPI_CALL vkMapMemory( VkDevice device, VkDeviceMemory memory, VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, void** ppData); VKAPI_ATTR void VKAPI_CALL vkUnmapMemory( VkDevice device, VkDeviceMemory memory); VKAPI_ATTR VkResult VKAPI_CALL vkFlushMappedMemoryRanges( VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges); VKAPI_ATTR VkResult VKAPI_CALL vkInvalidateMappedMemoryRanges( VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges); VKAPI_ATTR void VKAPI_CALL vkGetDeviceMemoryCommitment( VkDevice device, VkDeviceMemory memory, VkDeviceSize* pCommittedMemoryInBytes); VKAPI_ATTR VkResult VKAPI_CALL vkBindBufferMemory( VkDevice device, VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize memoryOffset); VKAPI_ATTR VkResult VKAPI_CALL vkBindImageMemory( VkDevice device, VkImage image, VkDeviceMemory memory, VkDeviceSize memoryOffset); VKAPI_ATTR void VKAPI_CALL vkGetBufferMemoryRequirements( VkDevice device, VkBuffer buffer, VkMemoryRequirements* pMemoryRequirements); VKAPI_ATTR void VKAPI_CALL vkGetImageMemoryRequirements( VkDevice device, VkImage image, VkMemoryRequirements* pMemoryRequirements); VKAPI_ATTR void VKAPI_CALL vkGetImageSparseMemoryRequirements( VkDevice device, VkImage image, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements* pSparseMemoryRequirements); VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceSparseImageFormatProperties( VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkSampleCountFlagBits samples, VkImageUsageFlags usage, VkImageTiling tiling, uint32_t* pPropertyCount, VkSparseImageFormatProperties* pProperties); VKAPI_ATTR VkResult VKAPI_CALL vkQueueBindSparse( VkQueue queue, uint32_t bindInfoCount, const VkBindSparseInfo* pBindInfo, VkFence fence); VKAPI_ATTR VkResult VKAPI_CALL vkCreateFence( VkDevice device, const VkFenceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkFence* pFence); VKAPI_ATTR void VKAPI_CALL vkDestroyFence( VkDevice device, VkFence fence, const VkAllocationCallbacks* pAllocator); VKAPI_ATTR VkResult VKAPI_CALL vkResetFences( VkDevice device, uint32_t fenceCount, const VkFence* pFences); VKAPI_ATTR VkResult VKAPI_CALL vkGetFenceStatus( VkDevice device, VkFence fence); VKAPI_ATTR VkResult VKAPI_CALL vkWaitForFences( VkDevice device, uint32_t fenceCount, const VkFence* pFences, VkBool32 waitAll, uint64_t timeout); VKAPI_ATTR VkResult VKAPI_CALL vkCreateSemaphore( VkDevice device, const VkSemaphoreCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSemaphore* pSemaphore); VKAPI_ATTR void VKAPI_CALL vkDestroySemaphore( VkDevice device, VkSemaphore semaphore, const VkAllocationCallbacks* pAllocator); VKAPI_ATTR VkResult VKAPI_CALL vkCreateEvent( VkDevice device, const VkEventCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkEvent* pEvent); VKAPI_ATTR void VKAPI_CALL vkDestroyEvent( VkDevice device, VkEvent event, const VkAllocationCallbacks* pAllocator); VKAPI_ATTR VkResult VKAPI_CALL vkGetEventStatus( VkDevice device, VkEvent event); VKAPI_ATTR VkResult VKAPI_CALL vkSetEvent( VkDevice device, VkEvent event); VKAPI_ATTR VkResult VKAPI_CALL vkResetEvent( VkDevice device, VkEvent event); VKAPI_ATTR VkResult VKAPI_CALL vkCreateQueryPool( VkDevice device, const VkQueryPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkQueryPool* pQueryPool); VKAPI_ATTR void VKAPI_CALL vkDestroyQueryPool( VkDevice device, VkQueryPool queryPool, const VkAllocationCallbacks* pAllocator); VKAPI_ATTR VkResult VKAPI_CALL vkGetQueryPoolResults( VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, size_t dataSize, void* pData, VkDeviceSize stride, VkQueryResultFlags flags); VKAPI_ATTR VkResult VKAPI_CALL vkCreateBuffer( VkDevice device, const VkBufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer); VKAPI_ATTR void VKAPI_CALL vkDestroyBuffer( VkDevice device, VkBuffer buffer, const VkAllocationCallbacks* pAllocator); VKAPI_ATTR VkResult VKAPI_CALL vkCreateBufferView( VkDevice device, const VkBufferViewCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBufferView* pView); VKAPI_ATTR void VKAPI_CALL vkDestroyBufferView( VkDevice device, VkBufferView bufferView, const VkAllocationCallbacks* pAllocator); VKAPI_ATTR VkResult VKAPI_CALL vkCreateImage( VkDevice device, const VkImageCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkImage* pImage); VKAPI_ATTR void VKAPI_CALL vkDestroyImage( VkDevice device, VkImage image, const VkAllocationCallbacks* pAllocator); VKAPI_ATTR void VKAPI_CALL vkGetImageSubresourceLayout( VkDevice device, VkImage image, const VkImageSubresource* pSubresource, VkSubresourceLayout* pLayout); VKAPI_ATTR VkResult VKAPI_CALL vkCreateImageView( VkDevice device, const VkImageViewCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkImageView* pView); VKAPI_ATTR void VKAPI_CALL vkDestroyImageView( VkDevice device, VkImageView imageView, const VkAllocationCallbacks* pAllocator); VKAPI_ATTR VkResult VKAPI_CALL vkCreateShaderModule( VkDevice device, const VkShaderModuleCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkShaderModule* pShaderModule); VKAPI_ATTR void VKAPI_CALL vkDestroyShaderModule( VkDevice device, VkShaderModule shaderModule, const VkAllocationCallbacks* pAllocator); VKAPI_ATTR VkResult VKAPI_CALL vkCreatePipelineCache( VkDevice device, const VkPipelineCacheCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkPipelineCache* pPipelineCache); VKAPI_ATTR void VKAPI_CALL vkDestroyPipelineCache( VkDevice device, VkPipelineCache pipelineCache, const VkAllocationCallbacks* pAllocator); VKAPI_ATTR VkResult VKAPI_CALL vkGetPipelineCacheData( VkDevice device, VkPipelineCache pipelineCache, size_t* pDataSize, void* pData); VKAPI_ATTR VkResult VKAPI_CALL vkMergePipelineCaches( VkDevice device, VkPipelineCache dstCache, uint32_t srcCacheCount, const VkPipelineCache* pSrcCaches); VKAPI_ATTR VkResult VKAPI_CALL vkCreateGraphicsPipelines( VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount, const VkGraphicsPipelineCreateInfo* pCreateInfos, const VkAllocationCallbacks* pAllocator, VkPipeline* pPipelines); VKAPI_ATTR VkResult VKAPI_CALL vkCreateComputePipelines( VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount, const VkComputePipelineCreateInfo* pCreateInfos, const VkAllocationCallbacks* pAllocator, VkPipeline* pPipelines); VKAPI_ATTR void VKAPI_CALL vkDestroyPipeline( VkDevice device, VkPipeline pipeline, const VkAllocationCallbacks* pAllocator); VKAPI_ATTR VkResult VKAPI_CALL vkCreatePipelineLayout( VkDevice device, const VkPipelineLayoutCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkPipelineLayout* pPipelineLayout); VKAPI_ATTR void VKAPI_CALL vkDestroyPipelineLayout( VkDevice device, VkPipelineLayout pipelineLayout, const VkAllocationCallbacks* pAllocator); VKAPI_ATTR VkResult VKAPI_CALL vkCreateSampler( VkDevice device, const VkSamplerCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSampler* pSampler); VKAPI_ATTR void VKAPI_CALL vkDestroySampler( VkDevice device, VkSampler sampler, const VkAllocationCallbacks* pAllocator); VKAPI_ATTR VkResult VKAPI_CALL vkCreateDescriptorSetLayout( VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorSetLayout* pSetLayout); VKAPI_ATTR void VKAPI_CALL vkDestroyDescriptorSetLayout( VkDevice device, VkDescriptorSetLayout descriptorSetLayout, const VkAllocationCallbacks* pAllocator); VKAPI_ATTR VkResult VKAPI_CALL vkCreateDescriptorPool( VkDevice device, const VkDescriptorPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorPool* pDescriptorPool); VKAPI_ATTR void VKAPI_CALL vkDestroyDescriptorPool( VkDevice device, VkDescriptorPool descriptorPool, const VkAllocationCallbacks* pAllocator); VKAPI_ATTR VkResult VKAPI_CALL vkResetDescriptorPool( VkDevice device, VkDescriptorPool descriptorPool, VkDescriptorPoolResetFlags flags); VKAPI_ATTR VkResult VKAPI_CALL vkAllocateDescriptorSets( VkDevice device, const VkDescriptorSetAllocateInfo* pAllocateInfo, VkDescriptorSet* pDescriptorSets); VKAPI_ATTR VkResult VKAPI_CALL vkFreeDescriptorSets( VkDevice device, VkDescriptorPool descriptorPool, uint32_t descriptorSetCount, const VkDescriptorSet* pDescriptorSets); VKAPI_ATTR void VKAPI_CALL vkUpdateDescriptorSets( VkDevice device, uint32_t descriptorWriteCount, const VkWriteDescriptorSet* pDescriptorWrites, uint32_t descriptorCopyCount, const VkCopyDescriptorSet* pDescriptorCopies); VKAPI_ATTR VkResult VKAPI_CALL vkCreateFramebuffer( VkDevice device, const VkFramebufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkFramebuffer* pFramebuffer); VKAPI_ATTR void VKAPI_CALL vkDestroyFramebuffer( VkDevice device, VkFramebuffer framebuffer, const VkAllocationCallbacks* pAllocator); VKAPI_ATTR VkResult VKAPI_CALL vkCreateRenderPass( VkDevice device, const VkRenderPassCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass); VKAPI_ATTR void VKAPI_CALL vkDestroyRenderPass( VkDevice device, VkRenderPass renderPass, const VkAllocationCallbacks* pAllocator); VKAPI_ATTR void VKAPI_CALL vkGetRenderAreaGranularity( VkDevice device, VkRenderPass renderPass, VkExtent2D* pGranularity); VKAPI_ATTR VkResult VKAPI_CALL vkCreateCommandPool( VkDevice device, const VkCommandPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkCommandPool* pCommandPool); VKAPI_ATTR void VKAPI_CALL vkDestroyCommandPool( VkDevice device, VkCommandPool commandPool, const VkAllocationCallbacks* pAllocator); VKAPI_ATTR VkResult VKAPI_CALL vkResetCommandPool( VkDevice device, VkCommandPool commandPool, VkCommandPoolResetFlags flags); VKAPI_ATTR VkResult VKAPI_CALL vkAllocateCommandBuffers( VkDevice device, const VkCommandBufferAllocateInfo* pAllocateInfo, VkCommandBuffer* pCommandBuffers); VKAPI_ATTR void VKAPI_CALL vkFreeCommandBuffers( VkDevice device, VkCommandPool commandPool, uint32_t commandBufferCount, const VkCommandBuffer* pCommandBuffers); VKAPI_ATTR VkResult VKAPI_CALL vkBeginCommandBuffer( VkCommandBuffer commandBuffer, const VkCommandBufferBeginInfo* pBeginInfo); VKAPI_ATTR VkResult VKAPI_CALL vkEndCommandBuffer( VkCommandBuffer commandBuffer); VKAPI_ATTR VkResult VKAPI_CALL vkResetCommandBuffer( VkCommandBuffer commandBuffer, VkCommandBufferResetFlags flags); VKAPI_ATTR void VKAPI_CALL vkCmdBindPipeline( VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipeline pipeline); VKAPI_ATTR void VKAPI_CALL vkCmdSetViewport( VkCommandBuffer commandBuffer, uint32_t firstViewport, uint32_t viewportCount, const VkViewport* pViewports); VKAPI_ATTR void VKAPI_CALL vkCmdSetScissor( VkCommandBuffer commandBuffer, uint32_t firstScissor, uint32_t scissorCount, const VkRect2D* pScissors); VKAPI_ATTR void VKAPI_CALL vkCmdSetLineWidth( VkCommandBuffer commandBuffer, float lineWidth); VKAPI_ATTR void VKAPI_CALL vkCmdSetDepthBias( VkCommandBuffer commandBuffer, float depthBiasConstantFactor, float depthBiasClamp, float depthBiasSlopeFactor); VKAPI_ATTR void VKAPI_CALL vkCmdSetBlendConstants( VkCommandBuffer commandBuffer, const float blendConstants[4]); VKAPI_ATTR void VKAPI_CALL vkCmdSetDepthBounds( VkCommandBuffer commandBuffer, float minDepthBounds, float maxDepthBounds); VKAPI_ATTR void VKAPI_CALL vkCmdSetStencilCompareMask( VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, uint32_t compareMask); VKAPI_ATTR void VKAPI_CALL vkCmdSetStencilWriteMask( VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, uint32_t writeMask); VKAPI_ATTR void VKAPI_CALL vkCmdSetStencilReference( VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, uint32_t reference); VKAPI_ATTR void VKAPI_CALL vkCmdBindDescriptorSets( VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t firstSet, uint32_t descriptorSetCount, const VkDescriptorSet* pDescriptorSets, uint32_t dynamicOffsetCount, const uint32_t* pDynamicOffsets); VKAPI_ATTR void VKAPI_CALL vkCmdBindIndexBuffer( VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkIndexType indexType); VKAPI_ATTR void VKAPI_CALL vkCmdBindVertexBuffers( VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer* pBuffers, const VkDeviceSize* pOffsets); VKAPI_ATTR void VKAPI_CALL vkCmdDraw( VkCommandBuffer commandBuffer, uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance); VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndexed( VkCommandBuffer commandBuffer, uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance); VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndirect( VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride); VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndexedIndirect( VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride); VKAPI_ATTR void VKAPI_CALL vkCmdDispatch( VkCommandBuffer commandBuffer, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ); VKAPI_ATTR void VKAPI_CALL vkCmdDispatchIndirect( VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset); VKAPI_ATTR void VKAPI_CALL vkCmdCopyBuffer( VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferCopy* pRegions); VKAPI_ATTR void VKAPI_CALL vkCmdCopyImage( VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageCopy* pRegions); VKAPI_ATTR void VKAPI_CALL vkCmdBlitImage( VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageBlit* pRegions, VkFilter filter); VKAPI_ATTR void VKAPI_CALL vkCmdCopyBufferToImage( VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkBufferImageCopy* pRegions); VKAPI_ATTR void VKAPI_CALL vkCmdCopyImageToBuffer( VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferImageCopy* pRegions); VKAPI_ATTR void VKAPI_CALL vkCmdUpdateBuffer( VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const void* pData); VKAPI_ATTR void VKAPI_CALL vkCmdFillBuffer( VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize size, uint32_t data); VKAPI_ATTR void VKAPI_CALL vkCmdClearColorImage( VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearColorValue* pColor, uint32_t rangeCount, const VkImageSubresourceRange* pRanges); VKAPI_ATTR void VKAPI_CALL vkCmdClearDepthStencilImage( VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearDepthStencilValue* pDepthStencil, uint32_t rangeCount, const VkImageSubresourceRange* pRanges); VKAPI_ATTR void VKAPI_CALL vkCmdClearAttachments( VkCommandBuffer commandBuffer, uint32_t attachmentCount, const VkClearAttachment* pAttachments, uint32_t rectCount, const VkClearRect* pRects); VKAPI_ATTR void VKAPI_CALL vkCmdResolveImage( VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageResolve* pRegions); VKAPI_ATTR void VKAPI_CALL vkCmdSetEvent( VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags stageMask); VKAPI_ATTR void VKAPI_CALL vkCmdResetEvent( VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags stageMask); VKAPI_ATTR void VKAPI_CALL vkCmdWaitEvents( VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent* pEvents, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, uint32_t memoryBarrierCount, const VkMemoryBarrier* pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier* pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier* pImageMemoryBarriers); VKAPI_ATTR void VKAPI_CALL vkCmdPipelineBarrier( VkCommandBuffer commandBuffer, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkDependencyFlags dependencyFlags, uint32_t memoryBarrierCount, const VkMemoryBarrier* pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier* pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier* pImageMemoryBarriers); VKAPI_ATTR void VKAPI_CALL vkCmdBeginQuery( VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query, VkQueryControlFlags flags); VKAPI_ATTR void VKAPI_CALL vkCmdEndQuery( VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query); VKAPI_ATTR void VKAPI_CALL vkCmdResetQueryPool( VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount); VKAPI_ATTR void VKAPI_CALL vkCmdWriteTimestamp( VkCommandBuffer commandBuffer, VkPipelineStageFlagBits pipelineStage, VkQueryPool queryPool, uint32_t query); VKAPI_ATTR void VKAPI_CALL vkCmdCopyQueryPoolResults( VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize stride, VkQueryResultFlags flags); VKAPI_ATTR void VKAPI_CALL vkCmdPushConstants( VkCommandBuffer commandBuffer, VkPipelineLayout layout, VkShaderStageFlags stageFlags, uint32_t offset, uint32_t size, const void* pValues); VKAPI_ATTR void VKAPI_CALL vkCmdBeginRenderPass( VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo* pRenderPassBegin, VkSubpassContents contents); VKAPI_ATTR void VKAPI_CALL vkCmdNextSubpass( VkCommandBuffer commandBuffer, VkSubpassContents contents); VKAPI_ATTR void VKAPI_CALL vkCmdEndRenderPass( VkCommandBuffer commandBuffer); VKAPI_ATTR void VKAPI_CALL vkCmdExecuteCommands( VkCommandBuffer commandBuffer, uint32_t commandBufferCount, const VkCommandBuffer* pCommandBuffers); #endif #define VK_KHR_surface 1 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSurfaceKHR) #define VK_KHR_SURFACE_SPEC_VERSION 25 #define VK_KHR_SURFACE_EXTENSION_NAME "VK_KHR_surface" #define VK_COLORSPACE_SRGB_NONLINEAR_KHR VK_COLOR_SPACE_SRGB_NONLINEAR_KHR typedef enum VkColorSpaceKHR { VK_COLOR_SPACE_SRGB_NONLINEAR_KHR = 0, VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT = 1000104001, VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT = 1000104002, VK_COLOR_SPACE_DCI_P3_LINEAR_EXT = 1000104003, VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT = 1000104004, VK_COLOR_SPACE_BT709_LINEAR_EXT = 1000104005, VK_COLOR_SPACE_BT709_NONLINEAR_EXT = 1000104006, VK_COLOR_SPACE_BT2020_LINEAR_EXT = 1000104007, VK_COLOR_SPACE_HDR10_ST2084_EXT = 1000104008, VK_COLOR_SPACE_DOLBYVISION_EXT = 1000104009, VK_COLOR_SPACE_HDR10_HLG_EXT = 1000104010, VK_COLOR_SPACE_ADOBERGB_LINEAR_EXT = 1000104011, VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT = 1000104012, VK_COLOR_SPACE_PASS_THROUGH_EXT = 1000104013, VK_COLOR_SPACE_BEGIN_RANGE_KHR = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, VK_COLOR_SPACE_END_RANGE_KHR = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, VK_COLOR_SPACE_RANGE_SIZE_KHR = (VK_COLOR_SPACE_SRGB_NONLINEAR_KHR - VK_COLOR_SPACE_SRGB_NONLINEAR_KHR + 1), VK_COLOR_SPACE_MAX_ENUM_KHR = 0x7FFFFFFF } VkColorSpaceKHR; typedef enum VkPresentModeKHR { VK_PRESENT_MODE_IMMEDIATE_KHR = 0, VK_PRESENT_MODE_MAILBOX_KHR = 1, VK_PRESENT_MODE_FIFO_KHR = 2, VK_PRESENT_MODE_FIFO_RELAXED_KHR = 3, VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR = 1000111000, VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR = 1000111001, VK_PRESENT_MODE_BEGIN_RANGE_KHR = VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_END_RANGE_KHR = VK_PRESENT_MODE_FIFO_RELAXED_KHR, VK_PRESENT_MODE_RANGE_SIZE_KHR = (VK_PRESENT_MODE_FIFO_RELAXED_KHR - VK_PRESENT_MODE_IMMEDIATE_KHR + 1), VK_PRESENT_MODE_MAX_ENUM_KHR = 0x7FFFFFFF } VkPresentModeKHR; typedef enum VkSurfaceTransformFlagBitsKHR { VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR = 0x00000001, VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR = 0x00000002, VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR = 0x00000004, VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR = 0x00000008, VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR = 0x00000010, VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR = 0x00000020, VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR = 0x00000040, VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR = 0x00000080, VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR = 0x00000100, VK_SURFACE_TRANSFORM_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF } VkSurfaceTransformFlagBitsKHR; typedef VkFlags VkSurfaceTransformFlagsKHR; typedef enum VkCompositeAlphaFlagBitsKHR { VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR = 0x00000001, VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR = 0x00000002, VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR = 0x00000004, VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR = 0x00000008, VK_COMPOSITE_ALPHA_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF } VkCompositeAlphaFlagBitsKHR; typedef VkFlags VkCompositeAlphaFlagsKHR; typedef struct VkSurfaceCapabilitiesKHR { uint32_t minImageCount; uint32_t maxImageCount; VkExtent2D currentExtent; VkExtent2D minImageExtent; VkExtent2D maxImageExtent; uint32_t maxImageArrayLayers; VkSurfaceTransformFlagsKHR supportedTransforms; VkSurfaceTransformFlagBitsKHR currentTransform; VkCompositeAlphaFlagsKHR supportedCompositeAlpha; VkImageUsageFlags supportedUsageFlags; } VkSurfaceCapabilitiesKHR; typedef struct VkSurfaceFormatKHR { VkFormat format; VkColorSpaceKHR colorSpace; } VkSurfaceFormatKHR; typedef void (VKAPI_PTR *PFN_vkDestroySurfaceKHR)(VkInstance instance, VkSurfaceKHR surface, const VkAllocationCallbacks* pAllocator); typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfaceSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, VkSurfaceKHR surface, VkBool32* pSupported); typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR* pSurfaceCapabilities); typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfaceFormatsKHR)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pSurfaceFormatCount, VkSurfaceFormatKHR* pSurfaceFormats); typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfacePresentModesKHR)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pPresentModeCount, VkPresentModeKHR* pPresentModes); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR void VKAPI_CALL vkDestroySurfaceKHR( VkInstance instance, VkSurfaceKHR surface, const VkAllocationCallbacks* pAllocator); VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfaceSupportKHR( VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, VkSurfaceKHR surface, VkBool32* pSupported); VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfaceCapabilitiesKHR( VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR* pSurfaceCapabilities); VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfaceFormatsKHR( VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pSurfaceFormatCount, VkSurfaceFormatKHR* pSurfaceFormats); VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfacePresentModesKHR( VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pPresentModeCount, VkPresentModeKHR* pPresentModes); #endif #define VK_KHR_swapchain 1 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSwapchainKHR) #define VK_KHR_SWAPCHAIN_SPEC_VERSION 68 #define VK_KHR_SWAPCHAIN_EXTENSION_NAME "VK_KHR_swapchain" typedef enum VkSwapchainCreateFlagBitsKHR { VK_SWAPCHAIN_CREATE_BIND_SFR_BIT_KHX = 0x00000001, VK_SWAPCHAIN_CREATE_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF } VkSwapchainCreateFlagBitsKHR; typedef VkFlags VkSwapchainCreateFlagsKHR; typedef struct VkSwapchainCreateInfoKHR { VkStructureType sType; const void* pNext; VkSwapchainCreateFlagsKHR flags; VkSurfaceKHR surface; uint32_t minImageCount; VkFormat imageFormat; VkColorSpaceKHR imageColorSpace; VkExtent2D imageExtent; uint32_t imageArrayLayers; VkImageUsageFlags imageUsage; VkSharingMode imageSharingMode; uint32_t queueFamilyIndexCount; const uint32_t* pQueueFamilyIndices; VkSurfaceTransformFlagBitsKHR preTransform; VkCompositeAlphaFlagBitsKHR compositeAlpha; VkPresentModeKHR presentMode; VkBool32 clipped; VkSwapchainKHR oldSwapchain; } VkSwapchainCreateInfoKHR; typedef struct VkPresentInfoKHR { VkStructureType sType; const void* pNext; uint32_t waitSemaphoreCount; const VkSemaphore* pWaitSemaphores; uint32_t swapchainCount; const VkSwapchainKHR* pSwapchains; const uint32_t* pImageIndices; VkResult* pResults; } VkPresentInfoKHR; typedef VkResult (VKAPI_PTR *PFN_vkCreateSwapchainKHR)(VkDevice device, const VkSwapchainCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSwapchainKHR* pSwapchain); typedef void (VKAPI_PTR *PFN_vkDestroySwapchainKHR)(VkDevice device, VkSwapchainKHR swapchain, const VkAllocationCallbacks* pAllocator); typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainImagesKHR)(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pSwapchainImageCount, VkImage* pSwapchainImages); typedef VkResult (VKAPI_PTR *PFN_vkAcquireNextImageKHR)(VkDevice device, VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t* pImageIndex); typedef VkResult (VKAPI_PTR *PFN_vkQueuePresentKHR)(VkQueue queue, const VkPresentInfoKHR* pPresentInfo); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR VkResult VKAPI_CALL vkCreateSwapchainKHR( VkDevice device, const VkSwapchainCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSwapchainKHR* pSwapchain); VKAPI_ATTR void VKAPI_CALL vkDestroySwapchainKHR( VkDevice device, VkSwapchainKHR swapchain, const VkAllocationCallbacks* pAllocator); VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainImagesKHR( VkDevice device, VkSwapchainKHR swapchain, uint32_t* pSwapchainImageCount, VkImage* pSwapchainImages); VKAPI_ATTR VkResult VKAPI_CALL vkAcquireNextImageKHR( VkDevice device, VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t* pImageIndex); VKAPI_ATTR VkResult VKAPI_CALL vkQueuePresentKHR( VkQueue queue, const VkPresentInfoKHR* pPresentInfo); #endif #define VK_KHR_display 1 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDisplayKHR) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDisplayModeKHR) #define VK_KHR_DISPLAY_SPEC_VERSION 21 #define VK_KHR_DISPLAY_EXTENSION_NAME "VK_KHR_display" typedef enum VkDisplayPlaneAlphaFlagBitsKHR { VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR = 0x00000001, VK_DISPLAY_PLANE_ALPHA_GLOBAL_BIT_KHR = 0x00000002, VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_BIT_KHR = 0x00000004, VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_PREMULTIPLIED_BIT_KHR = 0x00000008, VK_DISPLAY_PLANE_ALPHA_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF } VkDisplayPlaneAlphaFlagBitsKHR; typedef VkFlags VkDisplayPlaneAlphaFlagsKHR; typedef VkFlags VkDisplayModeCreateFlagsKHR; typedef VkFlags VkDisplaySurfaceCreateFlagsKHR; typedef struct VkDisplayPropertiesKHR { VkDisplayKHR display; const char* displayName; VkExtent2D physicalDimensions; VkExtent2D physicalResolution; VkSurfaceTransformFlagsKHR supportedTransforms; VkBool32 planeReorderPossible; VkBool32 persistentContent; } VkDisplayPropertiesKHR; typedef struct VkDisplayModeParametersKHR { VkExtent2D visibleRegion; uint32_t refreshRate; } VkDisplayModeParametersKHR; typedef struct VkDisplayModePropertiesKHR { VkDisplayModeKHR displayMode; VkDisplayModeParametersKHR parameters; } VkDisplayModePropertiesKHR; typedef struct VkDisplayModeCreateInfoKHR { VkStructureType sType; const void* pNext; VkDisplayModeCreateFlagsKHR flags; VkDisplayModeParametersKHR parameters; } VkDisplayModeCreateInfoKHR; typedef struct VkDisplayPlaneCapabilitiesKHR { VkDisplayPlaneAlphaFlagsKHR supportedAlpha; VkOffset2D minSrcPosition; VkOffset2D maxSrcPosition; VkExtent2D minSrcExtent; VkExtent2D maxSrcExtent; VkOffset2D minDstPosition; VkOffset2D maxDstPosition; VkExtent2D minDstExtent; VkExtent2D maxDstExtent; } VkDisplayPlaneCapabilitiesKHR; typedef struct VkDisplayPlanePropertiesKHR { VkDisplayKHR currentDisplay; uint32_t currentStackIndex; } VkDisplayPlanePropertiesKHR; typedef struct VkDisplaySurfaceCreateInfoKHR { VkStructureType sType; const void* pNext; VkDisplaySurfaceCreateFlagsKHR flags; VkDisplayModeKHR displayMode; uint32_t planeIndex; uint32_t planeStackIndex; VkSurfaceTransformFlagBitsKHR transform; float globalAlpha; VkDisplayPlaneAlphaFlagBitsKHR alphaMode; VkExtent2D imageExtent; } VkDisplaySurfaceCreateInfoKHR; typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceDisplayPropertiesKHR)(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkDisplayPropertiesKHR* pProperties); typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR)(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkDisplayPlanePropertiesKHR* pProperties); typedef VkResult (VKAPI_PTR *PFN_vkGetDisplayPlaneSupportedDisplaysKHR)(VkPhysicalDevice physicalDevice, uint32_t planeIndex, uint32_t* pDisplayCount, VkDisplayKHR* pDisplays); typedef VkResult (VKAPI_PTR *PFN_vkGetDisplayModePropertiesKHR)(VkPhysicalDevice physicalDevice, VkDisplayKHR display, uint32_t* pPropertyCount, VkDisplayModePropertiesKHR* pProperties); typedef VkResult (VKAPI_PTR *PFN_vkCreateDisplayModeKHR)(VkPhysicalDevice physicalDevice, VkDisplayKHR display, const VkDisplayModeCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDisplayModeKHR* pMode); typedef VkResult (VKAPI_PTR *PFN_vkGetDisplayPlaneCapabilitiesKHR)(VkPhysicalDevice physicalDevice, VkDisplayModeKHR mode, uint32_t planeIndex, VkDisplayPlaneCapabilitiesKHR* pCapabilities); typedef VkResult (VKAPI_PTR *PFN_vkCreateDisplayPlaneSurfaceKHR)(VkInstance instance, const VkDisplaySurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceDisplayPropertiesKHR( VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkDisplayPropertiesKHR* pProperties); VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceDisplayPlanePropertiesKHR( VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkDisplayPlanePropertiesKHR* pProperties); VKAPI_ATTR VkResult VKAPI_CALL vkGetDisplayPlaneSupportedDisplaysKHR( VkPhysicalDevice physicalDevice, uint32_t planeIndex, uint32_t* pDisplayCount, VkDisplayKHR* pDisplays); VKAPI_ATTR VkResult VKAPI_CALL vkGetDisplayModePropertiesKHR( VkPhysicalDevice physicalDevice, VkDisplayKHR display, uint32_t* pPropertyCount, VkDisplayModePropertiesKHR* pProperties); VKAPI_ATTR VkResult VKAPI_CALL vkCreateDisplayModeKHR( VkPhysicalDevice physicalDevice, VkDisplayKHR display, const VkDisplayModeCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDisplayModeKHR* pMode); VKAPI_ATTR VkResult VKAPI_CALL vkGetDisplayPlaneCapabilitiesKHR( VkPhysicalDevice physicalDevice, VkDisplayModeKHR mode, uint32_t planeIndex, VkDisplayPlaneCapabilitiesKHR* pCapabilities); VKAPI_ATTR VkResult VKAPI_CALL vkCreateDisplayPlaneSurfaceKHR( VkInstance instance, const VkDisplaySurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); #endif #define VK_KHR_display_swapchain 1 #define VK_KHR_DISPLAY_SWAPCHAIN_SPEC_VERSION 9 #define VK_KHR_DISPLAY_SWAPCHAIN_EXTENSION_NAME "VK_KHR_display_swapchain" typedef struct VkDisplayPresentInfoKHR { VkStructureType sType; const void* pNext; VkRect2D srcRect; VkRect2D dstRect; VkBool32 persistent; } VkDisplayPresentInfoKHR; typedef VkResult (VKAPI_PTR *PFN_vkCreateSharedSwapchainsKHR)(VkDevice device, uint32_t swapchainCount, const VkSwapchainCreateInfoKHR* pCreateInfos, const VkAllocationCallbacks* pAllocator, VkSwapchainKHR* pSwapchains); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR VkResult VKAPI_CALL vkCreateSharedSwapchainsKHR( VkDevice device, uint32_t swapchainCount, const VkSwapchainCreateInfoKHR* pCreateInfos, const VkAllocationCallbacks* pAllocator, VkSwapchainKHR* pSwapchains); #endif #ifdef VK_USE_PLATFORM_XLIB_KHR #define VK_KHR_xlib_surface 1 #include #define VK_KHR_XLIB_SURFACE_SPEC_VERSION 6 #define VK_KHR_XLIB_SURFACE_EXTENSION_NAME "VK_KHR_xlib_surface" typedef VkFlags VkXlibSurfaceCreateFlagsKHR; typedef struct VkXlibSurfaceCreateInfoKHR { VkStructureType sType; const void* pNext; VkXlibSurfaceCreateFlagsKHR flags; Display* dpy; Window window; } VkXlibSurfaceCreateInfoKHR; typedef VkResult (VKAPI_PTR *PFN_vkCreateXlibSurfaceKHR)(VkInstance instance, const VkXlibSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, Display* dpy, VisualID visualID); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR VkResult VKAPI_CALL vkCreateXlibSurfaceKHR( VkInstance instance, const VkXlibSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); VKAPI_ATTR VkBool32 VKAPI_CALL vkGetPhysicalDeviceXlibPresentationSupportKHR( VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, Display* dpy, VisualID visualID); #endif #endif /* VK_USE_PLATFORM_XLIB_KHR */ #ifdef VK_USE_PLATFORM_XCB_KHR #define VK_KHR_xcb_surface 1 #include #define VK_KHR_XCB_SURFACE_SPEC_VERSION 6 #define VK_KHR_XCB_SURFACE_EXTENSION_NAME "VK_KHR_xcb_surface" typedef VkFlags VkXcbSurfaceCreateFlagsKHR; typedef struct VkXcbSurfaceCreateInfoKHR { VkStructureType sType; const void* pNext; VkXcbSurfaceCreateFlagsKHR flags; xcb_connection_t* connection; xcb_window_t window; } VkXcbSurfaceCreateInfoKHR; typedef VkResult (VKAPI_PTR *PFN_vkCreateXcbSurfaceKHR)(VkInstance instance, const VkXcbSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, xcb_connection_t* connection, xcb_visualid_t visual_id); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR VkResult VKAPI_CALL vkCreateXcbSurfaceKHR( VkInstance instance, const VkXcbSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); VKAPI_ATTR VkBool32 VKAPI_CALL vkGetPhysicalDeviceXcbPresentationSupportKHR( VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, xcb_connection_t* connection, xcb_visualid_t visual_id); #endif #endif /* VK_USE_PLATFORM_XCB_KHR */ #ifdef VK_USE_PLATFORM_WAYLAND_KHR #define VK_KHR_wayland_surface 1 #include #define VK_KHR_WAYLAND_SURFACE_SPEC_VERSION 6 #define VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME "VK_KHR_wayland_surface" typedef VkFlags VkWaylandSurfaceCreateFlagsKHR; typedef struct VkWaylandSurfaceCreateInfoKHR { VkStructureType sType; const void* pNext; VkWaylandSurfaceCreateFlagsKHR flags; struct wl_display* display; struct wl_surface* surface; } VkWaylandSurfaceCreateInfoKHR; typedef VkResult (VKAPI_PTR *PFN_vkCreateWaylandSurfaceKHR)(VkInstance instance, const VkWaylandSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, struct wl_display* display); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR VkResult VKAPI_CALL vkCreateWaylandSurfaceKHR( VkInstance instance, const VkWaylandSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); VKAPI_ATTR VkBool32 VKAPI_CALL vkGetPhysicalDeviceWaylandPresentationSupportKHR( VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, struct wl_display* display); #endif #endif /* VK_USE_PLATFORM_WAYLAND_KHR */ #ifdef VK_USE_PLATFORM_MIR_KHR #define VK_KHR_mir_surface 1 #include #define VK_KHR_MIR_SURFACE_SPEC_VERSION 4 #define VK_KHR_MIR_SURFACE_EXTENSION_NAME "VK_KHR_mir_surface" typedef VkFlags VkMirSurfaceCreateFlagsKHR; typedef struct VkMirSurfaceCreateInfoKHR { VkStructureType sType; const void* pNext; VkMirSurfaceCreateFlagsKHR flags; MirConnection* connection; MirSurface* mirSurface; } VkMirSurfaceCreateInfoKHR; typedef VkResult (VKAPI_PTR *PFN_vkCreateMirSurfaceKHR)(VkInstance instance, const VkMirSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceMirPresentationSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, MirConnection* connection); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR VkResult VKAPI_CALL vkCreateMirSurfaceKHR( VkInstance instance, const VkMirSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); VKAPI_ATTR VkBool32 VKAPI_CALL vkGetPhysicalDeviceMirPresentationSupportKHR( VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, MirConnection* connection); #endif #endif /* VK_USE_PLATFORM_MIR_KHR */ #ifdef VK_USE_PLATFORM_ANDROID_KHR #define VK_KHR_android_surface 1 #include #define VK_KHR_ANDROID_SURFACE_SPEC_VERSION 6 #define VK_KHR_ANDROID_SURFACE_EXTENSION_NAME "VK_KHR_android_surface" typedef VkFlags VkAndroidSurfaceCreateFlagsKHR; typedef struct VkAndroidSurfaceCreateInfoKHR { VkStructureType sType; const void* pNext; VkAndroidSurfaceCreateFlagsKHR flags; ANativeWindow* window; } VkAndroidSurfaceCreateInfoKHR; typedef VkResult (VKAPI_PTR *PFN_vkCreateAndroidSurfaceKHR)(VkInstance instance, const VkAndroidSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR VkResult VKAPI_CALL vkCreateAndroidSurfaceKHR( VkInstance instance, const VkAndroidSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); #endif #endif /* VK_USE_PLATFORM_ANDROID_KHR */ #ifdef VK_USE_PLATFORM_WIN32_KHR #define VK_KHR_win32_surface 1 #include #define VK_KHR_WIN32_SURFACE_SPEC_VERSION 5 #define VK_KHR_WIN32_SURFACE_EXTENSION_NAME "VK_KHR_win32_surface" typedef VkFlags VkWin32SurfaceCreateFlagsKHR; typedef struct VkWin32SurfaceCreateInfoKHR { VkStructureType sType; const void* pNext; VkWin32SurfaceCreateFlagsKHR flags; HINSTANCE hinstance; HWND hwnd; } VkWin32SurfaceCreateInfoKHR; typedef VkResult (VKAPI_PTR *PFN_vkCreateWin32SurfaceKHR)(VkInstance instance, const VkWin32SurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR VkResult VKAPI_CALL vkCreateWin32SurfaceKHR( VkInstance instance, const VkWin32SurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); VKAPI_ATTR VkBool32 VKAPI_CALL vkGetPhysicalDeviceWin32PresentationSupportKHR( VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex); #endif #endif /* VK_USE_PLATFORM_WIN32_KHR */ #define VK_KHR_sampler_mirror_clamp_to_edge 1 #define VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_SPEC_VERSION 1 #define VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME "VK_KHR_sampler_mirror_clamp_to_edge" #define VK_KHR_get_physical_device_properties2 1 #define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_SPEC_VERSION 1 #define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME "VK_KHR_get_physical_device_properties2" typedef struct VkPhysicalDeviceFeatures2KHR { VkStructureType sType; void* pNext; VkPhysicalDeviceFeatures features; } VkPhysicalDeviceFeatures2KHR; typedef struct VkPhysicalDeviceProperties2KHR { VkStructureType sType; void* pNext; VkPhysicalDeviceProperties properties; } VkPhysicalDeviceProperties2KHR; typedef struct VkFormatProperties2KHR { VkStructureType sType; void* pNext; VkFormatProperties formatProperties; } VkFormatProperties2KHR; typedef struct VkImageFormatProperties2KHR { VkStructureType sType; void* pNext; VkImageFormatProperties imageFormatProperties; } VkImageFormatProperties2KHR; typedef struct VkPhysicalDeviceImageFormatInfo2KHR { VkStructureType sType; const void* pNext; VkFormat format; VkImageType type; VkImageTiling tiling; VkImageUsageFlags usage; VkImageCreateFlags flags; } VkPhysicalDeviceImageFormatInfo2KHR; typedef struct VkQueueFamilyProperties2KHR { VkStructureType sType; void* pNext; VkQueueFamilyProperties queueFamilyProperties; } VkQueueFamilyProperties2KHR; typedef struct VkPhysicalDeviceMemoryProperties2KHR { VkStructureType sType; void* pNext; VkPhysicalDeviceMemoryProperties memoryProperties; } VkPhysicalDeviceMemoryProperties2KHR; typedef struct VkSparseImageFormatProperties2KHR { VkStructureType sType; void* pNext; VkSparseImageFormatProperties properties; } VkSparseImageFormatProperties2KHR; typedef struct VkPhysicalDeviceSparseImageFormatInfo2KHR { VkStructureType sType; const void* pNext; VkFormat format; VkImageType type; VkSampleCountFlagBits samples; VkImageUsageFlags usage; VkImageTiling tiling; } VkPhysicalDeviceSparseImageFormatInfo2KHR; typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceFeatures2KHR)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2KHR* pFeatures); typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceProperties2KHR)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2KHR* pProperties); typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceFormatProperties2KHR)(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties2KHR* pFormatProperties); typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceImageFormatProperties2KHR)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2KHR* pImageFormatInfo, VkImageFormatProperties2KHR* pImageFormatProperties); typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceQueueFamilyProperties2KHR)(VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties2KHR* pQueueFamilyProperties); typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceMemoryProperties2KHR)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2KHR* pMemoryProperties); typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceSparseImageFormatProperties2KHR)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2KHR* pFormatInfo, uint32_t* pPropertyCount, VkSparseImageFormatProperties2KHR* pProperties); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceFeatures2KHR( VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2KHR* pFeatures); VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceProperties2KHR( VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2KHR* pProperties); VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceFormatProperties2KHR( VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties2KHR* pFormatProperties); VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceImageFormatProperties2KHR( VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2KHR* pImageFormatInfo, VkImageFormatProperties2KHR* pImageFormatProperties); VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceQueueFamilyProperties2KHR( VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties2KHR* pQueueFamilyProperties); VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceMemoryProperties2KHR( VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2KHR* pMemoryProperties); VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceSparseImageFormatProperties2KHR( VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2KHR* pFormatInfo, uint32_t* pPropertyCount, VkSparseImageFormatProperties2KHR* pProperties); #endif #define VK_KHR_shader_draw_parameters 1 #define VK_KHR_SHADER_DRAW_PARAMETERS_SPEC_VERSION 1 #define VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME "VK_KHR_shader_draw_parameters" #define VK_KHR_maintenance1 1 #define VK_KHR_MAINTENANCE1_SPEC_VERSION 1 #define VK_KHR_MAINTENANCE1_EXTENSION_NAME "VK_KHR_maintenance1" typedef VkFlags VkCommandPoolTrimFlagsKHR; typedef void (VKAPI_PTR *PFN_vkTrimCommandPoolKHR)(VkDevice device, VkCommandPool commandPool, VkCommandPoolTrimFlagsKHR flags); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR void VKAPI_CALL vkTrimCommandPoolKHR( VkDevice device, VkCommandPool commandPool, VkCommandPoolTrimFlagsKHR flags); #endif #define VK_KHR_push_descriptor 1 #define VK_KHR_PUSH_DESCRIPTOR_SPEC_VERSION 1 #define VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME "VK_KHR_push_descriptor" typedef struct VkPhysicalDevicePushDescriptorPropertiesKHR { VkStructureType sType; void* pNext; uint32_t maxPushDescriptors; } VkPhysicalDevicePushDescriptorPropertiesKHR; typedef void (VKAPI_PTR *PFN_vkCmdPushDescriptorSetKHR)(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t set, uint32_t descriptorWriteCount, const VkWriteDescriptorSet* pDescriptorWrites); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR void VKAPI_CALL vkCmdPushDescriptorSetKHR( VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t set, uint32_t descriptorWriteCount, const VkWriteDescriptorSet* pDescriptorWrites); #endif #define VK_KHR_incremental_present 1 #define VK_KHR_INCREMENTAL_PRESENT_SPEC_VERSION 1 #define VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME "VK_KHR_incremental_present" typedef struct VkRectLayerKHR { VkOffset2D offset; VkExtent2D extent; uint32_t layer; } VkRectLayerKHR; typedef struct VkPresentRegionKHR { uint32_t rectangleCount; const VkRectLayerKHR* pRectangles; } VkPresentRegionKHR; typedef struct VkPresentRegionsKHR { VkStructureType sType; const void* pNext; uint32_t swapchainCount; const VkPresentRegionKHR* pRegions; } VkPresentRegionsKHR; #define VK_KHR_descriptor_update_template 1 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDescriptorUpdateTemplateKHR) #define VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_SPEC_VERSION 1 #define VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME "VK_KHR_descriptor_update_template" typedef enum VkDescriptorUpdateTemplateTypeKHR { VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR = 0, VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR = 1, VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_BEGIN_RANGE_KHR = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR, VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_END_RANGE_KHR = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR, VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_RANGE_SIZE_KHR = (VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR - VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR + 1), VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_MAX_ENUM_KHR = 0x7FFFFFFF } VkDescriptorUpdateTemplateTypeKHR; typedef VkFlags VkDescriptorUpdateTemplateCreateFlagsKHR; typedef struct VkDescriptorUpdateTemplateEntryKHR { uint32_t dstBinding; uint32_t dstArrayElement; uint32_t descriptorCount; VkDescriptorType descriptorType; size_t offset; size_t stride; } VkDescriptorUpdateTemplateEntryKHR; typedef struct VkDescriptorUpdateTemplateCreateInfoKHR { VkStructureType sType; void* pNext; VkDescriptorUpdateTemplateCreateFlagsKHR flags; uint32_t descriptorUpdateEntryCount; const VkDescriptorUpdateTemplateEntryKHR* pDescriptorUpdateEntries; VkDescriptorUpdateTemplateTypeKHR templateType; VkDescriptorSetLayout descriptorSetLayout; VkPipelineBindPoint pipelineBindPoint; VkPipelineLayout pipelineLayout; uint32_t set; } VkDescriptorUpdateTemplateCreateInfoKHR; typedef VkResult (VKAPI_PTR *PFN_vkCreateDescriptorUpdateTemplateKHR)(VkDevice device, const VkDescriptorUpdateTemplateCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorUpdateTemplateKHR* pDescriptorUpdateTemplate); typedef void (VKAPI_PTR *PFN_vkDestroyDescriptorUpdateTemplateKHR)(VkDevice device, VkDescriptorUpdateTemplateKHR descriptorUpdateTemplate, const VkAllocationCallbacks* pAllocator); typedef void (VKAPI_PTR *PFN_vkUpdateDescriptorSetWithTemplateKHR)(VkDevice device, VkDescriptorSet descriptorSet, VkDescriptorUpdateTemplateKHR descriptorUpdateTemplate, const void* pData); typedef void (VKAPI_PTR *PFN_vkCmdPushDescriptorSetWithTemplateKHR)(VkCommandBuffer commandBuffer, VkDescriptorUpdateTemplateKHR descriptorUpdateTemplate, VkPipelineLayout layout, uint32_t set, const void* pData); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR VkResult VKAPI_CALL vkCreateDescriptorUpdateTemplateKHR( VkDevice device, const VkDescriptorUpdateTemplateCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorUpdateTemplateKHR* pDescriptorUpdateTemplate); VKAPI_ATTR void VKAPI_CALL vkDestroyDescriptorUpdateTemplateKHR( VkDevice device, VkDescriptorUpdateTemplateKHR descriptorUpdateTemplate, const VkAllocationCallbacks* pAllocator); VKAPI_ATTR void VKAPI_CALL vkUpdateDescriptorSetWithTemplateKHR( VkDevice device, VkDescriptorSet descriptorSet, VkDescriptorUpdateTemplateKHR descriptorUpdateTemplate, const void* pData); VKAPI_ATTR void VKAPI_CALL vkCmdPushDescriptorSetWithTemplateKHR( VkCommandBuffer commandBuffer, VkDescriptorUpdateTemplateKHR descriptorUpdateTemplate, VkPipelineLayout layout, uint32_t set, const void* pData); #endif #define VK_KHR_shared_presentable_image 1 #define VK_KHR_SHARED_PRESENTABLE_IMAGE_SPEC_VERSION 1 #define VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME "VK_KHR_shared_presentable_image" typedef struct VkSharedPresentSurfaceCapabilitiesKHR { VkStructureType sType; void* pNext; VkImageUsageFlags sharedPresentSupportedUsageFlags; } VkSharedPresentSurfaceCapabilitiesKHR; typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainStatusKHR)(VkDevice device, VkSwapchainKHR swapchain); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainStatusKHR( VkDevice device, VkSwapchainKHR swapchain); #endif #define VK_KHR_get_surface_capabilities2 1 #define VK_KHR_GET_SURFACE_CAPABILITIES_2_SPEC_VERSION 1 #define VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME "VK_KHR_get_surface_capabilities2" typedef struct VkPhysicalDeviceSurfaceInfo2KHR { VkStructureType sType; const void* pNext; VkSurfaceKHR surface; } VkPhysicalDeviceSurfaceInfo2KHR; typedef struct VkSurfaceCapabilities2KHR { VkStructureType sType; void* pNext; VkSurfaceCapabilitiesKHR surfaceCapabilities; } VkSurfaceCapabilities2KHR; typedef struct VkSurfaceFormat2KHR { VkStructureType sType; void* pNext; VkSurfaceFormatKHR surfaceFormat; } VkSurfaceFormat2KHR; typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfaceCapabilities2KHR)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, VkSurfaceCapabilities2KHR* pSurfaceCapabilities); typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfaceFormats2KHR)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, uint32_t* pSurfaceFormatCount, VkSurfaceFormat2KHR* pSurfaceFormats); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfaceCapabilities2KHR( VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, VkSurfaceCapabilities2KHR* pSurfaceCapabilities); VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfaceFormats2KHR( VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, uint32_t* pSurfaceFormatCount, VkSurfaceFormat2KHR* pSurfaceFormats); #endif #define VK_EXT_debug_report 1 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDebugReportCallbackEXT) #define VK_EXT_DEBUG_REPORT_SPEC_VERSION 6 #define VK_EXT_DEBUG_REPORT_EXTENSION_NAME "VK_EXT_debug_report" #define VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT typedef enum VkDebugReportObjectTypeEXT { VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT = 0, VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT = 1, VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT = 2, VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT = 3, VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT = 4, VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT = 5, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT = 6, VK_DEBUG_REPORT_OBJECT_TYPE_FENCE_EXT = 7, VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT = 8, VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT = 9, VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT = 10, VK_DEBUG_REPORT_OBJECT_TYPE_EVENT_EXT = 11, VK_DEBUG_REPORT_OBJECT_TYPE_QUERY_POOL_EXT = 12, VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_VIEW_EXT = 13, VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT = 14, VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT = 15, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_CACHE_EXT = 16, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_LAYOUT_EXT = 17, VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT = 18, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT = 19, VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT_EXT = 20, VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_EXT = 21, VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_POOL_EXT = 22, VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT = 23, VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT = 24, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT = 25, VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT = 26, VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT = 27, VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT = 28, VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_KHR_EXT = 29, VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_MODE_KHR_EXT = 30, VK_DEBUG_REPORT_OBJECT_TYPE_OBJECT_TABLE_NVX_EXT = 31, VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT = 32, VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_KHR_EXT = 1000085000, VK_DEBUG_REPORT_OBJECT_TYPE_BEGIN_RANGE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_END_RANGE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_RANGE_SIZE_EXT = (VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT - VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT + 1), VK_DEBUG_REPORT_OBJECT_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF } VkDebugReportObjectTypeEXT; typedef enum VkDebugReportErrorEXT { VK_DEBUG_REPORT_ERROR_NONE_EXT = 0, VK_DEBUG_REPORT_ERROR_CALLBACK_REF_EXT = 1, VK_DEBUG_REPORT_ERROR_BEGIN_RANGE_EXT = VK_DEBUG_REPORT_ERROR_NONE_EXT, VK_DEBUG_REPORT_ERROR_END_RANGE_EXT = VK_DEBUG_REPORT_ERROR_CALLBACK_REF_EXT, VK_DEBUG_REPORT_ERROR_RANGE_SIZE_EXT = (VK_DEBUG_REPORT_ERROR_CALLBACK_REF_EXT - VK_DEBUG_REPORT_ERROR_NONE_EXT + 1), VK_DEBUG_REPORT_ERROR_MAX_ENUM_EXT = 0x7FFFFFFF } VkDebugReportErrorEXT; typedef enum VkDebugReportFlagBitsEXT { VK_DEBUG_REPORT_INFORMATION_BIT_EXT = 0x00000001, VK_DEBUG_REPORT_WARNING_BIT_EXT = 0x00000002, VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT = 0x00000004, VK_DEBUG_REPORT_ERROR_BIT_EXT = 0x00000008, VK_DEBUG_REPORT_DEBUG_BIT_EXT = 0x00000010, VK_DEBUG_REPORT_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF } VkDebugReportFlagBitsEXT; typedef VkFlags VkDebugReportFlagsEXT; typedef VkBool32 (VKAPI_PTR *PFN_vkDebugReportCallbackEXT)( VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage, void* pUserData); typedef struct VkDebugReportCallbackCreateInfoEXT { VkStructureType sType; const void* pNext; VkDebugReportFlagsEXT flags; PFN_vkDebugReportCallbackEXT pfnCallback; void* pUserData; } VkDebugReportCallbackCreateInfoEXT; typedef VkResult (VKAPI_PTR *PFN_vkCreateDebugReportCallbackEXT)(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback); typedef void (VKAPI_PTR *PFN_vkDestroyDebugReportCallbackEXT)(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator); typedef void (VKAPI_PTR *PFN_vkDebugReportMessageEXT)(VkInstance instance, VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR VkResult VKAPI_CALL vkCreateDebugReportCallbackEXT( VkInstance instance, const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback); VKAPI_ATTR void VKAPI_CALL vkDestroyDebugReportCallbackEXT( VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator); VKAPI_ATTR void VKAPI_CALL vkDebugReportMessageEXT( VkInstance instance, VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage); #endif #define VK_NV_glsl_shader 1 #define VK_NV_GLSL_SHADER_SPEC_VERSION 1 #define VK_NV_GLSL_SHADER_EXTENSION_NAME "VK_NV_glsl_shader" #define VK_IMG_filter_cubic 1 #define VK_IMG_FILTER_CUBIC_SPEC_VERSION 1 #define VK_IMG_FILTER_CUBIC_EXTENSION_NAME "VK_IMG_filter_cubic" #define VK_AMD_rasterization_order 1 #define VK_AMD_RASTERIZATION_ORDER_SPEC_VERSION 1 #define VK_AMD_RASTERIZATION_ORDER_EXTENSION_NAME "VK_AMD_rasterization_order" typedef enum VkRasterizationOrderAMD { VK_RASTERIZATION_ORDER_STRICT_AMD = 0, VK_RASTERIZATION_ORDER_RELAXED_AMD = 1, VK_RASTERIZATION_ORDER_BEGIN_RANGE_AMD = VK_RASTERIZATION_ORDER_STRICT_AMD, VK_RASTERIZATION_ORDER_END_RANGE_AMD = VK_RASTERIZATION_ORDER_RELAXED_AMD, VK_RASTERIZATION_ORDER_RANGE_SIZE_AMD = (VK_RASTERIZATION_ORDER_RELAXED_AMD - VK_RASTERIZATION_ORDER_STRICT_AMD + 1), VK_RASTERIZATION_ORDER_MAX_ENUM_AMD = 0x7FFFFFFF } VkRasterizationOrderAMD; typedef struct VkPipelineRasterizationStateRasterizationOrderAMD { VkStructureType sType; const void* pNext; VkRasterizationOrderAMD rasterizationOrder; } VkPipelineRasterizationStateRasterizationOrderAMD; #define VK_AMD_shader_trinary_minmax 1 #define VK_AMD_SHADER_TRINARY_MINMAX_SPEC_VERSION 1 #define VK_AMD_SHADER_TRINARY_MINMAX_EXTENSION_NAME "VK_AMD_shader_trinary_minmax" #define VK_AMD_shader_explicit_vertex_parameter 1 #define VK_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER_SPEC_VERSION 1 #define VK_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER_EXTENSION_NAME "VK_AMD_shader_explicit_vertex_parameter" #define VK_EXT_debug_marker 1 #define VK_EXT_DEBUG_MARKER_SPEC_VERSION 4 #define VK_EXT_DEBUG_MARKER_EXTENSION_NAME "VK_EXT_debug_marker" typedef struct VkDebugMarkerObjectNameInfoEXT { VkStructureType sType; const void* pNext; VkDebugReportObjectTypeEXT objectType; uint64_t object; const char* pObjectName; } VkDebugMarkerObjectNameInfoEXT; typedef struct VkDebugMarkerObjectTagInfoEXT { VkStructureType sType; const void* pNext; VkDebugReportObjectTypeEXT objectType; uint64_t object; uint64_t tagName; size_t tagSize; const void* pTag; } VkDebugMarkerObjectTagInfoEXT; typedef struct VkDebugMarkerMarkerInfoEXT { VkStructureType sType; const void* pNext; const char* pMarkerName; float color[4]; } VkDebugMarkerMarkerInfoEXT; typedef VkResult (VKAPI_PTR *PFN_vkDebugMarkerSetObjectTagEXT)(VkDevice device, VkDebugMarkerObjectTagInfoEXT* pTagInfo); typedef VkResult (VKAPI_PTR *PFN_vkDebugMarkerSetObjectNameEXT)(VkDevice device, VkDebugMarkerObjectNameInfoEXT* pNameInfo); typedef void (VKAPI_PTR *PFN_vkCmdDebugMarkerBeginEXT)(VkCommandBuffer commandBuffer, VkDebugMarkerMarkerInfoEXT* pMarkerInfo); typedef void (VKAPI_PTR *PFN_vkCmdDebugMarkerEndEXT)(VkCommandBuffer commandBuffer); typedef void (VKAPI_PTR *PFN_vkCmdDebugMarkerInsertEXT)(VkCommandBuffer commandBuffer, VkDebugMarkerMarkerInfoEXT* pMarkerInfo); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR VkResult VKAPI_CALL vkDebugMarkerSetObjectTagEXT( VkDevice device, VkDebugMarkerObjectTagInfoEXT* pTagInfo); VKAPI_ATTR VkResult VKAPI_CALL vkDebugMarkerSetObjectNameEXT( VkDevice device, VkDebugMarkerObjectNameInfoEXT* pNameInfo); VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerBeginEXT( VkCommandBuffer commandBuffer, VkDebugMarkerMarkerInfoEXT* pMarkerInfo); VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerEndEXT( VkCommandBuffer commandBuffer); VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerInsertEXT( VkCommandBuffer commandBuffer, VkDebugMarkerMarkerInfoEXT* pMarkerInfo); #endif #define VK_AMD_gcn_shader 1 #define VK_AMD_GCN_SHADER_SPEC_VERSION 1 #define VK_AMD_GCN_SHADER_EXTENSION_NAME "VK_AMD_gcn_shader" #define VK_NV_dedicated_allocation 1 #define VK_NV_DEDICATED_ALLOCATION_SPEC_VERSION 1 #define VK_NV_DEDICATED_ALLOCATION_EXTENSION_NAME "VK_NV_dedicated_allocation" typedef struct VkDedicatedAllocationImageCreateInfoNV { VkStructureType sType; const void* pNext; VkBool32 dedicatedAllocation; } VkDedicatedAllocationImageCreateInfoNV; typedef struct VkDedicatedAllocationBufferCreateInfoNV { VkStructureType sType; const void* pNext; VkBool32 dedicatedAllocation; } VkDedicatedAllocationBufferCreateInfoNV; typedef struct VkDedicatedAllocationMemoryAllocateInfoNV { VkStructureType sType; const void* pNext; VkImage image; VkBuffer buffer; } VkDedicatedAllocationMemoryAllocateInfoNV; #define VK_AMD_draw_indirect_count 1 #define VK_AMD_DRAW_INDIRECT_COUNT_SPEC_VERSION 1 #define VK_AMD_DRAW_INDIRECT_COUNT_EXTENSION_NAME "VK_AMD_draw_indirect_count" typedef void (VKAPI_PTR *PFN_vkCmdDrawIndirectCountAMD)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride); typedef void (VKAPI_PTR *PFN_vkCmdDrawIndexedIndirectCountAMD)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndirectCountAMD( VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride); VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndexedIndirectCountAMD( VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride); #endif #define VK_AMD_negative_viewport_height 1 #define VK_AMD_NEGATIVE_VIEWPORT_HEIGHT_SPEC_VERSION 1 #define VK_AMD_NEGATIVE_VIEWPORT_HEIGHT_EXTENSION_NAME "VK_AMD_negative_viewport_height" #define VK_AMD_gpu_shader_half_float 1 #define VK_AMD_GPU_SHADER_HALF_FLOAT_SPEC_VERSION 1 #define VK_AMD_GPU_SHADER_HALF_FLOAT_EXTENSION_NAME "VK_AMD_gpu_shader_half_float" #define VK_AMD_shader_ballot 1 #define VK_AMD_SHADER_BALLOT_SPEC_VERSION 1 #define VK_AMD_SHADER_BALLOT_EXTENSION_NAME "VK_AMD_shader_ballot" #define VK_KHX_multiview 1 #define VK_KHX_MULTIVIEW_SPEC_VERSION 1 #define VK_KHX_MULTIVIEW_EXTENSION_NAME "VK_KHX_multiview" typedef struct VkRenderPassMultiviewCreateInfoKHX { VkStructureType sType; const void* pNext; uint32_t subpassCount; const uint32_t* pViewMasks; uint32_t dependencyCount; const int32_t* pViewOffsets; uint32_t correlationMaskCount; const uint32_t* pCorrelationMasks; } VkRenderPassMultiviewCreateInfoKHX; typedef struct VkPhysicalDeviceMultiviewFeaturesKHX { VkStructureType sType; void* pNext; VkBool32 multiview; VkBool32 multiviewGeometryShader; VkBool32 multiviewTessellationShader; } VkPhysicalDeviceMultiviewFeaturesKHX; typedef struct VkPhysicalDeviceMultiviewPropertiesKHX { VkStructureType sType; void* pNext; uint32_t maxMultiviewViewCount; uint32_t maxMultiviewInstanceIndex; } VkPhysicalDeviceMultiviewPropertiesKHX; #define VK_IMG_format_pvrtc 1 #define VK_IMG_FORMAT_PVRTC_SPEC_VERSION 1 #define VK_IMG_FORMAT_PVRTC_EXTENSION_NAME "VK_IMG_format_pvrtc" #define VK_NV_external_memory_capabilities 1 #define VK_NV_EXTERNAL_MEMORY_CAPABILITIES_SPEC_VERSION 1 #define VK_NV_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME "VK_NV_external_memory_capabilities" typedef enum VkExternalMemoryHandleTypeFlagBitsNV { VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_NV = 0x00000001, VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_NV = 0x00000002, VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_IMAGE_BIT_NV = 0x00000004, VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_IMAGE_KMT_BIT_NV = 0x00000008, VK_EXTERNAL_MEMORY_HANDLE_TYPE_FLAG_BITS_MAX_ENUM_NV = 0x7FFFFFFF } VkExternalMemoryHandleTypeFlagBitsNV; typedef VkFlags VkExternalMemoryHandleTypeFlagsNV; typedef enum VkExternalMemoryFeatureFlagBitsNV { VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_NV = 0x00000001, VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_NV = 0x00000002, VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_NV = 0x00000004, VK_EXTERNAL_MEMORY_FEATURE_FLAG_BITS_MAX_ENUM_NV = 0x7FFFFFFF } VkExternalMemoryFeatureFlagBitsNV; typedef VkFlags VkExternalMemoryFeatureFlagsNV; typedef struct VkExternalImageFormatPropertiesNV { VkImageFormatProperties imageFormatProperties; VkExternalMemoryFeatureFlagsNV externalMemoryFeatures; VkExternalMemoryHandleTypeFlagsNV exportFromImportedHandleTypes; VkExternalMemoryHandleTypeFlagsNV compatibleHandleTypes; } VkExternalImageFormatPropertiesNV; typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceExternalImageFormatPropertiesNV)(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling, VkImageUsageFlags usage, VkImageCreateFlags flags, VkExternalMemoryHandleTypeFlagsNV externalHandleType, VkExternalImageFormatPropertiesNV* pExternalImageFormatProperties); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceExternalImageFormatPropertiesNV( VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling, VkImageUsageFlags usage, VkImageCreateFlags flags, VkExternalMemoryHandleTypeFlagsNV externalHandleType, VkExternalImageFormatPropertiesNV* pExternalImageFormatProperties); #endif #define VK_NV_external_memory 1 #define VK_NV_EXTERNAL_MEMORY_SPEC_VERSION 1 #define VK_NV_EXTERNAL_MEMORY_EXTENSION_NAME "VK_NV_external_memory" typedef struct VkExternalMemoryImageCreateInfoNV { VkStructureType sType; const void* pNext; VkExternalMemoryHandleTypeFlagsNV handleTypes; } VkExternalMemoryImageCreateInfoNV; typedef struct VkExportMemoryAllocateInfoNV { VkStructureType sType; const void* pNext; VkExternalMemoryHandleTypeFlagsNV handleTypes; } VkExportMemoryAllocateInfoNV; #ifdef VK_USE_PLATFORM_WIN32_KHR #define VK_NV_external_memory_win32 1 #define VK_NV_EXTERNAL_MEMORY_WIN32_SPEC_VERSION 1 #define VK_NV_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME "VK_NV_external_memory_win32" typedef struct VkImportMemoryWin32HandleInfoNV { VkStructureType sType; const void* pNext; VkExternalMemoryHandleTypeFlagsNV handleType; HANDLE handle; } VkImportMemoryWin32HandleInfoNV; typedef struct VkExportMemoryWin32HandleInfoNV { VkStructureType sType; const void* pNext; const SECURITY_ATTRIBUTES* pAttributes; DWORD dwAccess; } VkExportMemoryWin32HandleInfoNV; typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryWin32HandleNV)(VkDevice device, VkDeviceMemory memory, VkExternalMemoryHandleTypeFlagsNV handleType, HANDLE* pHandle); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryWin32HandleNV( VkDevice device, VkDeviceMemory memory, VkExternalMemoryHandleTypeFlagsNV handleType, HANDLE* pHandle); #endif #endif /* VK_USE_PLATFORM_WIN32_KHR */ #ifdef VK_USE_PLATFORM_WIN32_KHR #define VK_NV_win32_keyed_mutex 1 #define VK_NV_WIN32_KEYED_MUTEX_SPEC_VERSION 1 #define VK_NV_WIN32_KEYED_MUTEX_EXTENSION_NAME "VK_NV_win32_keyed_mutex" typedef struct VkWin32KeyedMutexAcquireReleaseInfoNV { VkStructureType sType; const void* pNext; uint32_t acquireCount; const VkDeviceMemory* pAcquireSyncs; const uint64_t* pAcquireKeys; const uint32_t* pAcquireTimeoutMilliseconds; uint32_t releaseCount; const VkDeviceMemory* pReleaseSyncs; const uint64_t* pReleaseKeys; } VkWin32KeyedMutexAcquireReleaseInfoNV; #endif /* VK_USE_PLATFORM_WIN32_KHR */ #define VK_KHX_device_group 1 #define VK_MAX_DEVICE_GROUP_SIZE_KHX 32 #define VK_KHX_DEVICE_GROUP_SPEC_VERSION 1 #define VK_KHX_DEVICE_GROUP_EXTENSION_NAME "VK_KHX_device_group" typedef enum VkPeerMemoryFeatureFlagBitsKHX { VK_PEER_MEMORY_FEATURE_COPY_SRC_BIT_KHX = 0x00000001, VK_PEER_MEMORY_FEATURE_COPY_DST_BIT_KHX = 0x00000002, VK_PEER_MEMORY_FEATURE_GENERIC_SRC_BIT_KHX = 0x00000004, VK_PEER_MEMORY_FEATURE_GENERIC_DST_BIT_KHX = 0x00000008, VK_PEER_MEMORY_FEATURE_FLAG_BITS_MAX_ENUM_KHX = 0x7FFFFFFF } VkPeerMemoryFeatureFlagBitsKHX; typedef VkFlags VkPeerMemoryFeatureFlagsKHX; typedef enum VkMemoryAllocateFlagBitsKHX { VK_MEMORY_ALLOCATE_DEVICE_MASK_BIT_KHX = 0x00000001, VK_MEMORY_ALLOCATE_FLAG_BITS_MAX_ENUM_KHX = 0x7FFFFFFF } VkMemoryAllocateFlagBitsKHX; typedef VkFlags VkMemoryAllocateFlagsKHX; typedef enum VkDeviceGroupPresentModeFlagBitsKHX { VK_DEVICE_GROUP_PRESENT_MODE_LOCAL_BIT_KHX = 0x00000001, VK_DEVICE_GROUP_PRESENT_MODE_REMOTE_BIT_KHX = 0x00000002, VK_DEVICE_GROUP_PRESENT_MODE_SUM_BIT_KHX = 0x00000004, VK_DEVICE_GROUP_PRESENT_MODE_LOCAL_MULTI_DEVICE_BIT_KHX = 0x00000008, VK_DEVICE_GROUP_PRESENT_MODE_FLAG_BITS_MAX_ENUM_KHX = 0x7FFFFFFF } VkDeviceGroupPresentModeFlagBitsKHX; typedef VkFlags VkDeviceGroupPresentModeFlagsKHX; typedef struct VkMemoryAllocateFlagsInfoKHX { VkStructureType sType; const void* pNext; VkMemoryAllocateFlagsKHX flags; uint32_t deviceMask; } VkMemoryAllocateFlagsInfoKHX; typedef struct VkBindBufferMemoryInfoKHX { VkStructureType sType; const void* pNext; VkBuffer buffer; VkDeviceMemory memory; VkDeviceSize memoryOffset; uint32_t deviceIndexCount; const uint32_t* pDeviceIndices; } VkBindBufferMemoryInfoKHX; typedef struct VkBindImageMemoryInfoKHX { VkStructureType sType; const void* pNext; VkImage image; VkDeviceMemory memory; VkDeviceSize memoryOffset; uint32_t deviceIndexCount; const uint32_t* pDeviceIndices; uint32_t SFRRectCount; const VkRect2D* pSFRRects; } VkBindImageMemoryInfoKHX; typedef struct VkDeviceGroupRenderPassBeginInfoKHX { VkStructureType sType; const void* pNext; uint32_t deviceMask; uint32_t deviceRenderAreaCount; const VkRect2D* pDeviceRenderAreas; } VkDeviceGroupRenderPassBeginInfoKHX; typedef struct VkDeviceGroupCommandBufferBeginInfoKHX { VkStructureType sType; const void* pNext; uint32_t deviceMask; } VkDeviceGroupCommandBufferBeginInfoKHX; typedef struct VkDeviceGroupSubmitInfoKHX { VkStructureType sType; const void* pNext; uint32_t waitSemaphoreCount; const uint32_t* pWaitSemaphoreDeviceIndices; uint32_t commandBufferCount; const uint32_t* pCommandBufferDeviceMasks; uint32_t signalSemaphoreCount; const uint32_t* pSignalSemaphoreDeviceIndices; } VkDeviceGroupSubmitInfoKHX; typedef struct VkDeviceGroupBindSparseInfoKHX { VkStructureType sType; const void* pNext; uint32_t resourceDeviceIndex; uint32_t memoryDeviceIndex; } VkDeviceGroupBindSparseInfoKHX; typedef struct VkDeviceGroupPresentCapabilitiesKHX { VkStructureType sType; const void* pNext; uint32_t presentMask[VK_MAX_DEVICE_GROUP_SIZE_KHX]; VkDeviceGroupPresentModeFlagsKHX modes; } VkDeviceGroupPresentCapabilitiesKHX; typedef struct VkImageSwapchainCreateInfoKHX { VkStructureType sType; const void* pNext; VkSwapchainKHR swapchain; } VkImageSwapchainCreateInfoKHX; typedef struct VkBindImageMemorySwapchainInfoKHX { VkStructureType sType; const void* pNext; VkSwapchainKHR swapchain; uint32_t imageIndex; } VkBindImageMemorySwapchainInfoKHX; typedef struct VkAcquireNextImageInfoKHX { VkStructureType sType; const void* pNext; VkSwapchainKHR swapchain; uint64_t timeout; VkSemaphore semaphore; VkFence fence; uint32_t deviceMask; } VkAcquireNextImageInfoKHX; typedef struct VkDeviceGroupPresentInfoKHX { VkStructureType sType; const void* pNext; uint32_t swapchainCount; const uint32_t* pDeviceMasks; VkDeviceGroupPresentModeFlagBitsKHX mode; } VkDeviceGroupPresentInfoKHX; typedef struct VkDeviceGroupSwapchainCreateInfoKHX { VkStructureType sType; const void* pNext; VkDeviceGroupPresentModeFlagsKHX modes; } VkDeviceGroupSwapchainCreateInfoKHX; typedef void (VKAPI_PTR *PFN_vkGetDeviceGroupPeerMemoryFeaturesKHX)(VkDevice device, uint32_t heapIndex, uint32_t localDeviceIndex, uint32_t remoteDeviceIndex, VkPeerMemoryFeatureFlagsKHX* pPeerMemoryFeatures); typedef VkResult (VKAPI_PTR *PFN_vkBindBufferMemory2KHX)(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfoKHX* pBindInfos); typedef VkResult (VKAPI_PTR *PFN_vkBindImageMemory2KHX)(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfoKHX* pBindInfos); typedef void (VKAPI_PTR *PFN_vkCmdSetDeviceMaskKHX)(VkCommandBuffer commandBuffer, uint32_t deviceMask); typedef VkResult (VKAPI_PTR *PFN_vkGetDeviceGroupPresentCapabilitiesKHX)(VkDevice device, VkDeviceGroupPresentCapabilitiesKHX* pDeviceGroupPresentCapabilities); typedef VkResult (VKAPI_PTR *PFN_vkGetDeviceGroupSurfacePresentModesKHX)(VkDevice device, VkSurfaceKHR surface, VkDeviceGroupPresentModeFlagsKHX* pModes); typedef VkResult (VKAPI_PTR *PFN_vkAcquireNextImage2KHX)(VkDevice device, const VkAcquireNextImageInfoKHX* pAcquireInfo, uint32_t* pImageIndex); typedef void (VKAPI_PTR *PFN_vkCmdDispatchBaseKHX)(VkCommandBuffer commandBuffer, uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ); typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDevicePresentRectanglesKHX)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pRectCount, VkRect2D* pRects); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR void VKAPI_CALL vkGetDeviceGroupPeerMemoryFeaturesKHX( VkDevice device, uint32_t heapIndex, uint32_t localDeviceIndex, uint32_t remoteDeviceIndex, VkPeerMemoryFeatureFlagsKHX* pPeerMemoryFeatures); VKAPI_ATTR VkResult VKAPI_CALL vkBindBufferMemory2KHX( VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfoKHX* pBindInfos); VKAPI_ATTR VkResult VKAPI_CALL vkBindImageMemory2KHX( VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfoKHX* pBindInfos); VKAPI_ATTR void VKAPI_CALL vkCmdSetDeviceMaskKHX( VkCommandBuffer commandBuffer, uint32_t deviceMask); VKAPI_ATTR VkResult VKAPI_CALL vkGetDeviceGroupPresentCapabilitiesKHX( VkDevice device, VkDeviceGroupPresentCapabilitiesKHX* pDeviceGroupPresentCapabilities); VKAPI_ATTR VkResult VKAPI_CALL vkGetDeviceGroupSurfacePresentModesKHX( VkDevice device, VkSurfaceKHR surface, VkDeviceGroupPresentModeFlagsKHX* pModes); VKAPI_ATTR VkResult VKAPI_CALL vkAcquireNextImage2KHX( VkDevice device, const VkAcquireNextImageInfoKHX* pAcquireInfo, uint32_t* pImageIndex); VKAPI_ATTR void VKAPI_CALL vkCmdDispatchBaseKHX( VkCommandBuffer commandBuffer, uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ); VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDevicePresentRectanglesKHX( VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pRectCount, VkRect2D* pRects); #endif #define VK_EXT_validation_flags 1 #define VK_EXT_VALIDATION_FLAGS_SPEC_VERSION 1 #define VK_EXT_VALIDATION_FLAGS_EXTENSION_NAME "VK_EXT_validation_flags" typedef enum VkValidationCheckEXT { VK_VALIDATION_CHECK_ALL_EXT = 0, VK_VALIDATION_CHECK_BEGIN_RANGE_EXT = VK_VALIDATION_CHECK_ALL_EXT, VK_VALIDATION_CHECK_END_RANGE_EXT = VK_VALIDATION_CHECK_ALL_EXT, VK_VALIDATION_CHECK_RANGE_SIZE_EXT = (VK_VALIDATION_CHECK_ALL_EXT - VK_VALIDATION_CHECK_ALL_EXT + 1), VK_VALIDATION_CHECK_MAX_ENUM_EXT = 0x7FFFFFFF } VkValidationCheckEXT; typedef struct VkValidationFlagsEXT { VkStructureType sType; const void* pNext; uint32_t disabledValidationCheckCount; VkValidationCheckEXT* pDisabledValidationChecks; } VkValidationFlagsEXT; #ifdef VK_USE_PLATFORM_VI_NN #define VK_NN_vi_surface 1 #define VK_NN_VI_SURFACE_SPEC_VERSION 1 #define VK_NN_VI_SURFACE_EXTENSION_NAME "VK_NN_vi_surface" typedef VkFlags VkViSurfaceCreateFlagsNN; typedef struct VkViSurfaceCreateInfoNN { VkStructureType sType; const void* pNext; VkViSurfaceCreateFlagsNN flags; void* window; } VkViSurfaceCreateInfoNN; typedef VkResult (VKAPI_PTR *PFN_vkCreateViSurfaceNN)(VkInstance instance, const VkViSurfaceCreateInfoNN* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR VkResult VKAPI_CALL vkCreateViSurfaceNN( VkInstance instance, const VkViSurfaceCreateInfoNN* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); #endif #endif /* VK_USE_PLATFORM_VI_NN */ #define VK_EXT_shader_subgroup_ballot 1 #define VK_EXT_SHADER_SUBGROUP_BALLOT_SPEC_VERSION 1 #define VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME "VK_EXT_shader_subgroup_ballot" #define VK_EXT_shader_subgroup_vote 1 #define VK_EXT_SHADER_SUBGROUP_VOTE_SPEC_VERSION 1 #define VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME "VK_EXT_shader_subgroup_vote" #define VK_KHX_device_group_creation 1 #define VK_KHX_DEVICE_GROUP_CREATION_SPEC_VERSION 1 #define VK_KHX_DEVICE_GROUP_CREATION_EXTENSION_NAME "VK_KHX_device_group_creation" typedef struct VkPhysicalDeviceGroupPropertiesKHX { VkStructureType sType; void* pNext; uint32_t physicalDeviceCount; VkPhysicalDevice physicalDevices[VK_MAX_DEVICE_GROUP_SIZE_KHX]; VkBool32 subsetAllocation; } VkPhysicalDeviceGroupPropertiesKHX; typedef struct VkDeviceGroupDeviceCreateInfoKHX { VkStructureType sType; const void* pNext; uint32_t physicalDeviceCount; const VkPhysicalDevice* pPhysicalDevices; } VkDeviceGroupDeviceCreateInfoKHX; typedef VkResult (VKAPI_PTR *PFN_vkEnumeratePhysicalDeviceGroupsKHX)(VkInstance instance, uint32_t* pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupPropertiesKHX* pPhysicalDeviceGroupProperties); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR VkResult VKAPI_CALL vkEnumeratePhysicalDeviceGroupsKHX( VkInstance instance, uint32_t* pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupPropertiesKHX* pPhysicalDeviceGroupProperties); #endif #define VK_KHX_external_memory_capabilities 1 #define VK_LUID_SIZE_KHX 8 #define VK_KHX_EXTERNAL_MEMORY_CAPABILITIES_SPEC_VERSION 1 #define VK_KHX_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME "VK_KHX_external_memory_capabilities" typedef enum VkExternalMemoryHandleTypeFlagBitsKHX { VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHX = 0x00000001, VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHX = 0x00000002, VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHX = 0x00000004, VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT_KHX = 0x00000008, VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_KMT_BIT_KHX = 0x00000010, VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_HEAP_BIT_KHX = 0x00000020, VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT_KHX = 0x00000040, VK_EXTERNAL_MEMORY_HANDLE_TYPE_FLAG_BITS_MAX_ENUM_KHX = 0x7FFFFFFF } VkExternalMemoryHandleTypeFlagBitsKHX; typedef VkFlags VkExternalMemoryHandleTypeFlagsKHX; typedef enum VkExternalMemoryFeatureFlagBitsKHX { VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_KHX = 0x00000001, VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_KHX = 0x00000002, VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHX = 0x00000004, VK_EXTERNAL_MEMORY_FEATURE_FLAG_BITS_MAX_ENUM_KHX = 0x7FFFFFFF } VkExternalMemoryFeatureFlagBitsKHX; typedef VkFlags VkExternalMemoryFeatureFlagsKHX; typedef struct VkExternalMemoryPropertiesKHX { VkExternalMemoryFeatureFlagsKHX externalMemoryFeatures; VkExternalMemoryHandleTypeFlagsKHX exportFromImportedHandleTypes; VkExternalMemoryHandleTypeFlagsKHX compatibleHandleTypes; } VkExternalMemoryPropertiesKHX; typedef struct VkPhysicalDeviceExternalImageFormatInfoKHX { VkStructureType sType; const void* pNext; VkExternalMemoryHandleTypeFlagBitsKHX handleType; } VkPhysicalDeviceExternalImageFormatInfoKHX; typedef struct VkExternalImageFormatPropertiesKHX { VkStructureType sType; void* pNext; VkExternalMemoryPropertiesKHX externalMemoryProperties; } VkExternalImageFormatPropertiesKHX; typedef struct VkPhysicalDeviceExternalBufferInfoKHX { VkStructureType sType; const void* pNext; VkBufferCreateFlags flags; VkBufferUsageFlags usage; VkExternalMemoryHandleTypeFlagBitsKHX handleType; } VkPhysicalDeviceExternalBufferInfoKHX; typedef struct VkExternalBufferPropertiesKHX { VkStructureType sType; void* pNext; VkExternalMemoryPropertiesKHX externalMemoryProperties; } VkExternalBufferPropertiesKHX; typedef struct VkPhysicalDeviceIDPropertiesKHX { VkStructureType sType; void* pNext; uint8_t deviceUUID[VK_UUID_SIZE]; uint8_t driverUUID[VK_UUID_SIZE]; uint8_t deviceLUID[VK_LUID_SIZE_KHX]; VkBool32 deviceLUIDValid; } VkPhysicalDeviceIDPropertiesKHX; typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceExternalBufferPropertiesKHX)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalBufferInfoKHX* pExternalBufferInfo, VkExternalBufferPropertiesKHX* pExternalBufferProperties); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceExternalBufferPropertiesKHX( VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalBufferInfoKHX* pExternalBufferInfo, VkExternalBufferPropertiesKHX* pExternalBufferProperties); #endif #define VK_KHX_external_memory 1 #define VK_KHX_EXTERNAL_MEMORY_SPEC_VERSION 1 #define VK_KHX_EXTERNAL_MEMORY_EXTENSION_NAME "VK_KHX_external_memory" #define VK_QUEUE_FAMILY_EXTERNAL_KHX (~0U-1) typedef struct VkExternalMemoryImageCreateInfoKHX { VkStructureType sType; const void* pNext; VkExternalMemoryHandleTypeFlagsKHX handleTypes; } VkExternalMemoryImageCreateInfoKHX; typedef struct VkExternalMemoryBufferCreateInfoKHX { VkStructureType sType; const void* pNext; VkExternalMemoryHandleTypeFlagsKHX handleTypes; } VkExternalMemoryBufferCreateInfoKHX; typedef struct VkExportMemoryAllocateInfoKHX { VkStructureType sType; const void* pNext; VkExternalMemoryHandleTypeFlagsKHX handleTypes; } VkExportMemoryAllocateInfoKHX; #ifdef VK_USE_PLATFORM_WIN32_KHX #define VK_KHX_external_memory_win32 1 #define VK_KHX_EXTERNAL_MEMORY_WIN32_SPEC_VERSION 1 #define VK_KHX_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME "VK_KHX_external_memory_win32" typedef struct VkImportMemoryWin32HandleInfoKHX { VkStructureType sType; const void* pNext; VkExternalMemoryHandleTypeFlagBitsKHX handleType; HANDLE handle; } VkImportMemoryWin32HandleInfoKHX; typedef struct VkExportMemoryWin32HandleInfoKHX { VkStructureType sType; const void* pNext; const SECURITY_ATTRIBUTES* pAttributes; DWORD dwAccess; LPCWSTR name; } VkExportMemoryWin32HandleInfoKHX; typedef struct VkMemoryWin32HandlePropertiesKHX { VkStructureType sType; void* pNext; uint32_t memoryTypeBits; } VkMemoryWin32HandlePropertiesKHX; typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryWin32HandleKHX)(VkDevice device, VkDeviceMemory memory, VkExternalMemoryHandleTypeFlagBitsKHX handleType, HANDLE* pHandle); typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryWin32HandlePropertiesKHX)(VkDevice device, VkExternalMemoryHandleTypeFlagBitsKHX handleType, HANDLE handle, VkMemoryWin32HandlePropertiesKHX* pMemoryWin32HandleProperties); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryWin32HandleKHX( VkDevice device, VkDeviceMemory memory, VkExternalMemoryHandleTypeFlagBitsKHX handleType, HANDLE* pHandle); VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryWin32HandlePropertiesKHX( VkDevice device, VkExternalMemoryHandleTypeFlagBitsKHX handleType, HANDLE handle, VkMemoryWin32HandlePropertiesKHX* pMemoryWin32HandleProperties); #endif #endif /* VK_USE_PLATFORM_WIN32_KHX */ #define VK_KHX_external_memory_fd 1 #define VK_KHX_EXTERNAL_MEMORY_FD_SPEC_VERSION 1 #define VK_KHX_EXTERNAL_MEMORY_FD_EXTENSION_NAME "VK_KHX_external_memory_fd" typedef struct VkImportMemoryFdInfoKHX { VkStructureType sType; const void* pNext; VkExternalMemoryHandleTypeFlagBitsKHX handleType; int fd; } VkImportMemoryFdInfoKHX; typedef struct VkMemoryFdPropertiesKHX { VkStructureType sType; void* pNext; uint32_t memoryTypeBits; } VkMemoryFdPropertiesKHX; typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryFdKHX)(VkDevice device, VkDeviceMemory memory, VkExternalMemoryHandleTypeFlagBitsKHX handleType, int* pFd); typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryFdPropertiesKHX)(VkDevice device, VkExternalMemoryHandleTypeFlagBitsKHX handleType, int fd, VkMemoryFdPropertiesKHX* pMemoryFdProperties); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryFdKHX( VkDevice device, VkDeviceMemory memory, VkExternalMemoryHandleTypeFlagBitsKHX handleType, int* pFd); VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryFdPropertiesKHX( VkDevice device, VkExternalMemoryHandleTypeFlagBitsKHX handleType, int fd, VkMemoryFdPropertiesKHX* pMemoryFdProperties); #endif #ifdef VK_USE_PLATFORM_WIN32_KHR #define VK_KHX_win32_keyed_mutex 1 #define VK_KHX_WIN32_KEYED_MUTEX_SPEC_VERSION 1 #define VK_KHX_WIN32_KEYED_MUTEX_EXTENSION_NAME "VK_KHX_win32_keyed_mutex" typedef struct VkWin32KeyedMutexAcquireReleaseInfoKHX { VkStructureType sType; const void* pNext; uint32_t acquireCount; const VkDeviceMemory* pAcquireSyncs; const uint64_t* pAcquireKeys; const uint32_t* pAcquireTimeouts; uint32_t releaseCount; const VkDeviceMemory* pReleaseSyncs; const uint64_t* pReleaseKeys; } VkWin32KeyedMutexAcquireReleaseInfoKHX; #endif /* VK_USE_PLATFORM_WIN32_KHR */ #define VK_KHX_external_semaphore_capabilities 1 #define VK_KHX_EXTERNAL_SEMAPHORE_CAPABILITIES_SPEC_VERSION 1 #define VK_KHX_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME "VK_KHX_external_semaphore_capabilities" typedef enum VkExternalSemaphoreHandleTypeFlagBitsKHX { VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHX = 0x00000001, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHX = 0x00000002, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHX = 0x00000004, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT_KHX = 0x00000008, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FENCE_FD_BIT_KHX = 0x00000010, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FLAG_BITS_MAX_ENUM_KHX = 0x7FFFFFFF } VkExternalSemaphoreHandleTypeFlagBitsKHX; typedef VkFlags VkExternalSemaphoreHandleTypeFlagsKHX; typedef enum VkExternalSemaphoreFeatureFlagBitsKHX { VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT_KHX = 0x00000001, VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT_KHX = 0x00000002, VK_EXTERNAL_SEMAPHORE_FEATURE_FLAG_BITS_MAX_ENUM_KHX = 0x7FFFFFFF } VkExternalSemaphoreFeatureFlagBitsKHX; typedef VkFlags VkExternalSemaphoreFeatureFlagsKHX; typedef struct VkPhysicalDeviceExternalSemaphoreInfoKHX { VkStructureType sType; const void* pNext; VkExternalSemaphoreHandleTypeFlagBitsKHX handleType; } VkPhysicalDeviceExternalSemaphoreInfoKHX; typedef struct VkExternalSemaphorePropertiesKHX { VkStructureType sType; void* pNext; VkExternalSemaphoreHandleTypeFlagsKHX exportFromImportedHandleTypes; VkExternalSemaphoreHandleTypeFlagsKHX compatibleHandleTypes; VkExternalSemaphoreFeatureFlagsKHX externalSemaphoreFeatures; } VkExternalSemaphorePropertiesKHX; typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceExternalSemaphorePropertiesKHX)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalSemaphoreInfoKHX* pExternalSemaphoreInfo, VkExternalSemaphorePropertiesKHX* pExternalSemaphoreProperties); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceExternalSemaphorePropertiesKHX( VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalSemaphoreInfoKHX* pExternalSemaphoreInfo, VkExternalSemaphorePropertiesKHX* pExternalSemaphoreProperties); #endif #define VK_KHX_external_semaphore 1 #define VK_KHX_EXTERNAL_SEMAPHORE_SPEC_VERSION 1 #define VK_KHX_EXTERNAL_SEMAPHORE_EXTENSION_NAME "VK_KHX_external_semaphore" typedef struct VkExportSemaphoreCreateInfoKHX { VkStructureType sType; const void* pNext; VkExternalSemaphoreHandleTypeFlagsKHX handleTypes; } VkExportSemaphoreCreateInfoKHX; #ifdef VK_USE_PLATFORM_WIN32_KHX #define VK_KHX_external_semaphore_win32 1 #define VK_KHX_EXTERNAL_SEMAPHORE_WIN32_SPEC_VERSION 1 #define VK_KHX_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME "VK_KHX_external_semaphore_win32" typedef struct VkImportSemaphoreWin32HandleInfoKHX { VkStructureType sType; const void* pNext; VkSemaphore semaphore; VkExternalSemaphoreHandleTypeFlagsKHX handleType; HANDLE handle; } VkImportSemaphoreWin32HandleInfoKHX; typedef struct VkExportSemaphoreWin32HandleInfoKHX { VkStructureType sType; const void* pNext; const SECURITY_ATTRIBUTES* pAttributes; DWORD dwAccess; LPCWSTR name; } VkExportSemaphoreWin32HandleInfoKHX; typedef struct VkD3D12FenceSubmitInfoKHX { VkStructureType sType; const void* pNext; uint32_t waitSemaphoreValuesCount; const uint64_t* pWaitSemaphoreValues; uint32_t signalSemaphoreValuesCount; const uint64_t* pSignalSemaphoreValues; } VkD3D12FenceSubmitInfoKHX; typedef VkResult (VKAPI_PTR *PFN_vkImportSemaphoreWin32HandleKHX)(VkDevice device, const VkImportSemaphoreWin32HandleInfoKHX* pImportSemaphoreWin32HandleInfo); typedef VkResult (VKAPI_PTR *PFN_vkGetSemaphoreWin32HandleKHX)(VkDevice device, VkSemaphore semaphore, VkExternalSemaphoreHandleTypeFlagBitsKHX handleType, HANDLE* pHandle); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR VkResult VKAPI_CALL vkImportSemaphoreWin32HandleKHX( VkDevice device, const VkImportSemaphoreWin32HandleInfoKHX* pImportSemaphoreWin32HandleInfo); VKAPI_ATTR VkResult VKAPI_CALL vkGetSemaphoreWin32HandleKHX( VkDevice device, VkSemaphore semaphore, VkExternalSemaphoreHandleTypeFlagBitsKHX handleType, HANDLE* pHandle); #endif #endif /* VK_USE_PLATFORM_WIN32_KHX */ #define VK_KHX_external_semaphore_fd 1 #define VK_KHX_EXTERNAL_SEMAPHORE_FD_SPEC_VERSION 1 #define VK_KHX_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME "VK_KHX_external_semaphore_fd" typedef struct VkImportSemaphoreFdInfoKHX { VkStructureType sType; const void* pNext; VkSemaphore semaphore; VkExternalSemaphoreHandleTypeFlagBitsKHX handleType; int fd; } VkImportSemaphoreFdInfoKHX; typedef VkResult (VKAPI_PTR *PFN_vkImportSemaphoreFdKHX)(VkDevice device, const VkImportSemaphoreFdInfoKHX* pImportSemaphoreFdInfo); typedef VkResult (VKAPI_PTR *PFN_vkGetSemaphoreFdKHX)(VkDevice device, VkSemaphore semaphore, VkExternalSemaphoreHandleTypeFlagBitsKHX handleType, int* pFd); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR VkResult VKAPI_CALL vkImportSemaphoreFdKHX( VkDevice device, const VkImportSemaphoreFdInfoKHX* pImportSemaphoreFdInfo); VKAPI_ATTR VkResult VKAPI_CALL vkGetSemaphoreFdKHX( VkDevice device, VkSemaphore semaphore, VkExternalSemaphoreHandleTypeFlagBitsKHX handleType, int* pFd); #endif #define VK_NVX_device_generated_commands 1 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkObjectTableNVX) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkIndirectCommandsLayoutNVX) #define VK_NVX_DEVICE_GENERATED_COMMANDS_SPEC_VERSION 1 #define VK_NVX_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME "VK_NVX_device_generated_commands" typedef enum VkIndirectCommandsTokenTypeNVX { VK_INDIRECT_COMMANDS_TOKEN_PIPELINE_NVX = 0, VK_INDIRECT_COMMANDS_TOKEN_DESCRIPTOR_SET_NVX = 1, VK_INDIRECT_COMMANDS_TOKEN_INDEX_BUFFER_NVX = 2, VK_INDIRECT_COMMANDS_TOKEN_VERTEX_BUFFER_NVX = 3, VK_INDIRECT_COMMANDS_TOKEN_PUSH_CONSTANT_NVX = 4, VK_INDIRECT_COMMANDS_TOKEN_DRAW_INDEXED_NVX = 5, VK_INDIRECT_COMMANDS_TOKEN_DRAW_NVX = 6, VK_INDIRECT_COMMANDS_TOKEN_DISPATCH_NVX = 7, VK_INDIRECT_COMMANDS_TOKEN_TYPE_BEGIN_RANGE_NVX = VK_INDIRECT_COMMANDS_TOKEN_PIPELINE_NVX, VK_INDIRECT_COMMANDS_TOKEN_TYPE_END_RANGE_NVX = VK_INDIRECT_COMMANDS_TOKEN_DISPATCH_NVX, VK_INDIRECT_COMMANDS_TOKEN_TYPE_RANGE_SIZE_NVX = (VK_INDIRECT_COMMANDS_TOKEN_DISPATCH_NVX - VK_INDIRECT_COMMANDS_TOKEN_PIPELINE_NVX + 1), VK_INDIRECT_COMMANDS_TOKEN_TYPE_MAX_ENUM_NVX = 0x7FFFFFFF } VkIndirectCommandsTokenTypeNVX; typedef enum VkObjectEntryTypeNVX { VK_OBJECT_ENTRY_DESCRIPTOR_SET_NVX = 0, VK_OBJECT_ENTRY_PIPELINE_NVX = 1, VK_OBJECT_ENTRY_INDEX_BUFFER_NVX = 2, VK_OBJECT_ENTRY_VERTEX_BUFFER_NVX = 3, VK_OBJECT_ENTRY_PUSH_CONSTANT_NVX = 4, VK_OBJECT_ENTRY_TYPE_BEGIN_RANGE_NVX = VK_OBJECT_ENTRY_DESCRIPTOR_SET_NVX, VK_OBJECT_ENTRY_TYPE_END_RANGE_NVX = VK_OBJECT_ENTRY_PUSH_CONSTANT_NVX, VK_OBJECT_ENTRY_TYPE_RANGE_SIZE_NVX = (VK_OBJECT_ENTRY_PUSH_CONSTANT_NVX - VK_OBJECT_ENTRY_DESCRIPTOR_SET_NVX + 1), VK_OBJECT_ENTRY_TYPE_MAX_ENUM_NVX = 0x7FFFFFFF } VkObjectEntryTypeNVX; typedef enum VkIndirectCommandsLayoutUsageFlagBitsNVX { VK_INDIRECT_COMMANDS_LAYOUT_USAGE_UNORDERED_SEQUENCES_BIT_NVX = 0x00000001, VK_INDIRECT_COMMANDS_LAYOUT_USAGE_SPARSE_SEQUENCES_BIT_NVX = 0x00000002, VK_INDIRECT_COMMANDS_LAYOUT_USAGE_EMPTY_EXECUTIONS_BIT_NVX = 0x00000004, VK_INDIRECT_COMMANDS_LAYOUT_USAGE_INDEXED_SEQUENCES_BIT_NVX = 0x00000008, VK_INDIRECT_COMMANDS_LAYOUT_USAGE_FLAG_BITS_MAX_ENUM_NVX = 0x7FFFFFFF } VkIndirectCommandsLayoutUsageFlagBitsNVX; typedef VkFlags VkIndirectCommandsLayoutUsageFlagsNVX; typedef enum VkObjectEntryUsageFlagBitsNVX { VK_OBJECT_ENTRY_USAGE_GRAPHICS_BIT_NVX = 0x00000001, VK_OBJECT_ENTRY_USAGE_COMPUTE_BIT_NVX = 0x00000002, VK_OBJECT_ENTRY_USAGE_FLAG_BITS_MAX_ENUM_NVX = 0x7FFFFFFF } VkObjectEntryUsageFlagBitsNVX; typedef VkFlags VkObjectEntryUsageFlagsNVX; typedef struct VkDeviceGeneratedCommandsFeaturesNVX { VkStructureType sType; const void* pNext; VkBool32 computeBindingPointSupport; } VkDeviceGeneratedCommandsFeaturesNVX; typedef struct VkDeviceGeneratedCommandsLimitsNVX { VkStructureType sType; const void* pNext; uint32_t maxIndirectCommandsLayoutTokenCount; uint32_t maxObjectEntryCounts; uint32_t minSequenceCountBufferOffsetAlignment; uint32_t minSequenceIndexBufferOffsetAlignment; uint32_t minCommandsTokenBufferOffsetAlignment; } VkDeviceGeneratedCommandsLimitsNVX; typedef struct VkIndirectCommandsTokenNVX { VkIndirectCommandsTokenTypeNVX tokenType; VkBuffer buffer; VkDeviceSize offset; } VkIndirectCommandsTokenNVX; typedef struct VkIndirectCommandsLayoutTokenNVX { VkIndirectCommandsTokenTypeNVX tokenType; uint32_t bindingUnit; uint32_t dynamicCount; uint32_t divisor; } VkIndirectCommandsLayoutTokenNVX; typedef struct VkIndirectCommandsLayoutCreateInfoNVX { VkStructureType sType; const void* pNext; VkPipelineBindPoint pipelineBindPoint; VkIndirectCommandsLayoutUsageFlagsNVX flags; uint32_t tokenCount; const VkIndirectCommandsLayoutTokenNVX* pTokens; } VkIndirectCommandsLayoutCreateInfoNVX; typedef struct VkCmdProcessCommandsInfoNVX { VkStructureType sType; const void* pNext; VkObjectTableNVX objectTable; VkIndirectCommandsLayoutNVX indirectCommandsLayout; uint32_t indirectCommandsTokenCount; const VkIndirectCommandsTokenNVX* pIndirectCommandsTokens; uint32_t maxSequencesCount; VkCommandBuffer targetCommandBuffer; VkBuffer sequencesCountBuffer; VkDeviceSize sequencesCountOffset; VkBuffer sequencesIndexBuffer; VkDeviceSize sequencesIndexOffset; } VkCmdProcessCommandsInfoNVX; typedef struct VkCmdReserveSpaceForCommandsInfoNVX { VkStructureType sType; const void* pNext; VkObjectTableNVX objectTable; VkIndirectCommandsLayoutNVX indirectCommandsLayout; uint32_t maxSequencesCount; } VkCmdReserveSpaceForCommandsInfoNVX; typedef struct VkObjectTableCreateInfoNVX { VkStructureType sType; const void* pNext; uint32_t objectCount; const VkObjectEntryTypeNVX* pObjectEntryTypes; const uint32_t* pObjectEntryCounts; const VkObjectEntryUsageFlagsNVX* pObjectEntryUsageFlags; uint32_t maxUniformBuffersPerDescriptor; uint32_t maxStorageBuffersPerDescriptor; uint32_t maxStorageImagesPerDescriptor; uint32_t maxSampledImagesPerDescriptor; uint32_t maxPipelineLayouts; } VkObjectTableCreateInfoNVX; typedef struct VkObjectTableEntryNVX { VkObjectEntryTypeNVX type; VkObjectEntryUsageFlagsNVX flags; } VkObjectTableEntryNVX; typedef struct VkObjectTablePipelineEntryNVX { VkObjectEntryTypeNVX type; VkObjectEntryUsageFlagsNVX flags; VkPipeline pipeline; } VkObjectTablePipelineEntryNVX; typedef struct VkObjectTableDescriptorSetEntryNVX { VkObjectEntryTypeNVX type; VkObjectEntryUsageFlagsNVX flags; VkPipelineLayout pipelineLayout; VkDescriptorSet descriptorSet; } VkObjectTableDescriptorSetEntryNVX; typedef struct VkObjectTableVertexBufferEntryNVX { VkObjectEntryTypeNVX type; VkObjectEntryUsageFlagsNVX flags; VkBuffer buffer; } VkObjectTableVertexBufferEntryNVX; typedef struct VkObjectTableIndexBufferEntryNVX { VkObjectEntryTypeNVX type; VkObjectEntryUsageFlagsNVX flags; VkBuffer buffer; VkIndexType indexType; } VkObjectTableIndexBufferEntryNVX; typedef struct VkObjectTablePushConstantEntryNVX { VkObjectEntryTypeNVX type; VkObjectEntryUsageFlagsNVX flags; VkPipelineLayout pipelineLayout; VkShaderStageFlags stageFlags; } VkObjectTablePushConstantEntryNVX; typedef void (VKAPI_PTR *PFN_vkCmdProcessCommandsNVX)(VkCommandBuffer commandBuffer, const VkCmdProcessCommandsInfoNVX* pProcessCommandsInfo); typedef void (VKAPI_PTR *PFN_vkCmdReserveSpaceForCommandsNVX)(VkCommandBuffer commandBuffer, const VkCmdReserveSpaceForCommandsInfoNVX* pReserveSpaceInfo); typedef VkResult (VKAPI_PTR *PFN_vkCreateIndirectCommandsLayoutNVX)(VkDevice device, const VkIndirectCommandsLayoutCreateInfoNVX* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkIndirectCommandsLayoutNVX* pIndirectCommandsLayout); typedef void (VKAPI_PTR *PFN_vkDestroyIndirectCommandsLayoutNVX)(VkDevice device, VkIndirectCommandsLayoutNVX indirectCommandsLayout, const VkAllocationCallbacks* pAllocator); typedef VkResult (VKAPI_PTR *PFN_vkCreateObjectTableNVX)(VkDevice device, const VkObjectTableCreateInfoNVX* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkObjectTableNVX* pObjectTable); typedef void (VKAPI_PTR *PFN_vkDestroyObjectTableNVX)(VkDevice device, VkObjectTableNVX objectTable, const VkAllocationCallbacks* pAllocator); typedef VkResult (VKAPI_PTR *PFN_vkRegisterObjectsNVX)(VkDevice device, VkObjectTableNVX objectTable, uint32_t objectCount, const VkObjectTableEntryNVX* const* ppObjectTableEntries, const uint32_t* pObjectIndices); typedef VkResult (VKAPI_PTR *PFN_vkUnregisterObjectsNVX)(VkDevice device, VkObjectTableNVX objectTable, uint32_t objectCount, const VkObjectEntryTypeNVX* pObjectEntryTypes, const uint32_t* pObjectIndices); typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX)(VkPhysicalDevice physicalDevice, VkDeviceGeneratedCommandsFeaturesNVX* pFeatures, VkDeviceGeneratedCommandsLimitsNVX* pLimits); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR void VKAPI_CALL vkCmdProcessCommandsNVX( VkCommandBuffer commandBuffer, const VkCmdProcessCommandsInfoNVX* pProcessCommandsInfo); VKAPI_ATTR void VKAPI_CALL vkCmdReserveSpaceForCommandsNVX( VkCommandBuffer commandBuffer, const VkCmdReserveSpaceForCommandsInfoNVX* pReserveSpaceInfo); VKAPI_ATTR VkResult VKAPI_CALL vkCreateIndirectCommandsLayoutNVX( VkDevice device, const VkIndirectCommandsLayoutCreateInfoNVX* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkIndirectCommandsLayoutNVX* pIndirectCommandsLayout); VKAPI_ATTR void VKAPI_CALL vkDestroyIndirectCommandsLayoutNVX( VkDevice device, VkIndirectCommandsLayoutNVX indirectCommandsLayout, const VkAllocationCallbacks* pAllocator); VKAPI_ATTR VkResult VKAPI_CALL vkCreateObjectTableNVX( VkDevice device, const VkObjectTableCreateInfoNVX* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkObjectTableNVX* pObjectTable); VKAPI_ATTR void VKAPI_CALL vkDestroyObjectTableNVX( VkDevice device, VkObjectTableNVX objectTable, const VkAllocationCallbacks* pAllocator); VKAPI_ATTR VkResult VKAPI_CALL vkRegisterObjectsNVX( VkDevice device, VkObjectTableNVX objectTable, uint32_t objectCount, const VkObjectTableEntryNVX* const* ppObjectTableEntries, const uint32_t* pObjectIndices); VKAPI_ATTR VkResult VKAPI_CALL vkUnregisterObjectsNVX( VkDevice device, VkObjectTableNVX objectTable, uint32_t objectCount, const VkObjectEntryTypeNVX* pObjectEntryTypes, const uint32_t* pObjectIndices); VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX( VkPhysicalDevice physicalDevice, VkDeviceGeneratedCommandsFeaturesNVX* pFeatures, VkDeviceGeneratedCommandsLimitsNVX* pLimits); #endif #define VK_NV_clip_space_w_scaling 1 #define VK_NV_CLIP_SPACE_W_SCALING_SPEC_VERSION 1 #define VK_NV_CLIP_SPACE_W_SCALING_EXTENSION_NAME "VK_NV_clip_space_w_scaling" typedef struct VkViewportWScalingNV { float xcoeff; float ycoeff; } VkViewportWScalingNV; typedef struct VkPipelineViewportWScalingStateCreateInfoNV { VkStructureType sType; const void* pNext; VkBool32 viewportWScalingEnable; uint32_t viewportCount; const VkViewportWScalingNV* pViewportWScalings; } VkPipelineViewportWScalingStateCreateInfoNV; typedef void (VKAPI_PTR *PFN_vkCmdSetViewportWScalingNV)(VkCommandBuffer commandBuffer, uint32_t firstViewport, uint32_t viewportCount, const VkViewportWScalingNV* pViewportWScalings); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR void VKAPI_CALL vkCmdSetViewportWScalingNV( VkCommandBuffer commandBuffer, uint32_t firstViewport, uint32_t viewportCount, const VkViewportWScalingNV* pViewportWScalings); #endif #define VK_EXT_direct_mode_display 1 #define VK_EXT_DIRECT_MODE_DISPLAY_SPEC_VERSION 1 #define VK_EXT_DIRECT_MODE_DISPLAY_EXTENSION_NAME "VK_EXT_direct_mode_display" typedef VkResult (VKAPI_PTR *PFN_vkReleaseDisplayEXT)(VkPhysicalDevice physicalDevice, VkDisplayKHR display); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR VkResult VKAPI_CALL vkReleaseDisplayEXT( VkPhysicalDevice physicalDevice, VkDisplayKHR display); #endif #ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT #define VK_EXT_acquire_xlib_display 1 #include #define VK_EXT_ACQUIRE_XLIB_DISPLAY_SPEC_VERSION 1 #define VK_EXT_ACQUIRE_XLIB_DISPLAY_EXTENSION_NAME "VK_EXT_acquire_xlib_display" typedef VkResult (VKAPI_PTR *PFN_vkAcquireXlibDisplayEXT)(VkPhysicalDevice physicalDevice, Display* dpy, VkDisplayKHR display); typedef VkResult (VKAPI_PTR *PFN_vkGetRandROutputDisplayEXT)(VkPhysicalDevice physicalDevice, Display* dpy, RROutput rrOutput, VkDisplayKHR* pDisplay); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR VkResult VKAPI_CALL vkAcquireXlibDisplayEXT( VkPhysicalDevice physicalDevice, Display* dpy, VkDisplayKHR display); VKAPI_ATTR VkResult VKAPI_CALL vkGetRandROutputDisplayEXT( VkPhysicalDevice physicalDevice, Display* dpy, RROutput rrOutput, VkDisplayKHR* pDisplay); #endif #endif /* VK_USE_PLATFORM_XLIB_XRANDR_EXT */ #define VK_EXT_display_surface_counter 1 #define VK_EXT_DISPLAY_SURFACE_COUNTER_SPEC_VERSION 1 #define VK_EXT_DISPLAY_SURFACE_COUNTER_EXTENSION_NAME "VK_EXT_display_surface_counter" typedef enum VkSurfaceCounterFlagBitsEXT { VK_SURFACE_COUNTER_VBLANK_EXT = 0x00000001, VK_SURFACE_COUNTER_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF } VkSurfaceCounterFlagBitsEXT; typedef VkFlags VkSurfaceCounterFlagsEXT; typedef struct VkSurfaceCapabilities2EXT { VkStructureType sType; void* pNext; uint32_t minImageCount; uint32_t maxImageCount; VkExtent2D currentExtent; VkExtent2D minImageExtent; VkExtent2D maxImageExtent; uint32_t maxImageArrayLayers; VkSurfaceTransformFlagsKHR supportedTransforms; VkSurfaceTransformFlagBitsKHR currentTransform; VkCompositeAlphaFlagsKHR supportedCompositeAlpha; VkImageUsageFlags supportedUsageFlags; VkSurfaceCounterFlagsEXT supportedSurfaceCounters; } VkSurfaceCapabilities2EXT; typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfaceCapabilities2EXT)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceCapabilities2EXT* pSurfaceCapabilities); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfaceCapabilities2EXT( VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceCapabilities2EXT* pSurfaceCapabilities); #endif #define VK_EXT_display_control 1 #define VK_EXT_DISPLAY_CONTROL_SPEC_VERSION 1 #define VK_EXT_DISPLAY_CONTROL_EXTENSION_NAME "VK_EXT_display_control" typedef enum VkDisplayPowerStateEXT { VK_DISPLAY_POWER_STATE_OFF_EXT = 0, VK_DISPLAY_POWER_STATE_SUSPEND_EXT = 1, VK_DISPLAY_POWER_STATE_ON_EXT = 2, VK_DISPLAY_POWER_STATE_BEGIN_RANGE_EXT = VK_DISPLAY_POWER_STATE_OFF_EXT, VK_DISPLAY_POWER_STATE_END_RANGE_EXT = VK_DISPLAY_POWER_STATE_ON_EXT, VK_DISPLAY_POWER_STATE_RANGE_SIZE_EXT = (VK_DISPLAY_POWER_STATE_ON_EXT - VK_DISPLAY_POWER_STATE_OFF_EXT + 1), VK_DISPLAY_POWER_STATE_MAX_ENUM_EXT = 0x7FFFFFFF } VkDisplayPowerStateEXT; typedef enum VkDeviceEventTypeEXT { VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT = 0, VK_DEVICE_EVENT_TYPE_BEGIN_RANGE_EXT = VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT, VK_DEVICE_EVENT_TYPE_END_RANGE_EXT = VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT, VK_DEVICE_EVENT_TYPE_RANGE_SIZE_EXT = (VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT - VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT + 1), VK_DEVICE_EVENT_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF } VkDeviceEventTypeEXT; typedef enum VkDisplayEventTypeEXT { VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT = 0, VK_DISPLAY_EVENT_TYPE_BEGIN_RANGE_EXT = VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT, VK_DISPLAY_EVENT_TYPE_END_RANGE_EXT = VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT, VK_DISPLAY_EVENT_TYPE_RANGE_SIZE_EXT = (VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT - VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT + 1), VK_DISPLAY_EVENT_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF } VkDisplayEventTypeEXT; typedef struct VkDisplayPowerInfoEXT { VkStructureType sType; const void* pNext; VkDisplayPowerStateEXT powerState; } VkDisplayPowerInfoEXT; typedef struct VkDeviceEventInfoEXT { VkStructureType sType; const void* pNext; VkDeviceEventTypeEXT deviceEvent; } VkDeviceEventInfoEXT; typedef struct VkDisplayEventInfoEXT { VkStructureType sType; const void* pNext; VkDisplayEventTypeEXT displayEvent; } VkDisplayEventInfoEXT; typedef struct VkSwapchainCounterCreateInfoEXT { VkStructureType sType; const void* pNext; VkSurfaceCounterFlagsEXT surfaceCounters; } VkSwapchainCounterCreateInfoEXT; typedef VkResult (VKAPI_PTR *PFN_vkDisplayPowerControlEXT)(VkDevice device, VkDisplayKHR display, const VkDisplayPowerInfoEXT* pDisplayPowerInfo); typedef VkResult (VKAPI_PTR *PFN_vkRegisterDeviceEventEXT)(VkDevice device, const VkDeviceEventInfoEXT* pDeviceEventInfo, const VkAllocationCallbacks* pAllocator, VkFence* pFence); typedef VkResult (VKAPI_PTR *PFN_vkRegisterDisplayEventEXT)(VkDevice device, VkDisplayKHR display, const VkDisplayEventInfoEXT* pDisplayEventInfo, const VkAllocationCallbacks* pAllocator, VkFence* pFence); typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainCounterEXT)(VkDevice device, VkSwapchainKHR swapchain, VkSurfaceCounterFlagBitsEXT counter, uint64_t* pCounterValue); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR VkResult VKAPI_CALL vkDisplayPowerControlEXT( VkDevice device, VkDisplayKHR display, const VkDisplayPowerInfoEXT* pDisplayPowerInfo); VKAPI_ATTR VkResult VKAPI_CALL vkRegisterDeviceEventEXT( VkDevice device, const VkDeviceEventInfoEXT* pDeviceEventInfo, const VkAllocationCallbacks* pAllocator, VkFence* pFence); VKAPI_ATTR VkResult VKAPI_CALL vkRegisterDisplayEventEXT( VkDevice device, VkDisplayKHR display, const VkDisplayEventInfoEXT* pDisplayEventInfo, const VkAllocationCallbacks* pAllocator, VkFence* pFence); VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainCounterEXT( VkDevice device, VkSwapchainKHR swapchain, VkSurfaceCounterFlagBitsEXT counter, uint64_t* pCounterValue); #endif #define VK_GOOGLE_display_timing 1 #define VK_GOOGLE_DISPLAY_TIMING_SPEC_VERSION 1 #define VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME "VK_GOOGLE_display_timing" typedef struct VkRefreshCycleDurationGOOGLE { uint64_t refreshDuration; } VkRefreshCycleDurationGOOGLE; typedef struct VkPastPresentationTimingGOOGLE { uint32_t presentID; uint64_t desiredPresentTime; uint64_t actualPresentTime; uint64_t earliestPresentTime; uint64_t presentMargin; } VkPastPresentationTimingGOOGLE; typedef struct VkPresentTimeGOOGLE { uint32_t presentID; uint64_t desiredPresentTime; } VkPresentTimeGOOGLE; typedef struct VkPresentTimesInfoGOOGLE { VkStructureType sType; const void* pNext; uint32_t swapchainCount; const VkPresentTimeGOOGLE* pTimes; } VkPresentTimesInfoGOOGLE; typedef VkResult (VKAPI_PTR *PFN_vkGetRefreshCycleDurationGOOGLE)(VkDevice device, VkSwapchainKHR swapchain, VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties); typedef VkResult (VKAPI_PTR *PFN_vkGetPastPresentationTimingGOOGLE)(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pPresentationTimingCount, VkPastPresentationTimingGOOGLE* pPresentationTimings); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR VkResult VKAPI_CALL vkGetRefreshCycleDurationGOOGLE( VkDevice device, VkSwapchainKHR swapchain, VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties); VKAPI_ATTR VkResult VKAPI_CALL vkGetPastPresentationTimingGOOGLE( VkDevice device, VkSwapchainKHR swapchain, uint32_t* pPresentationTimingCount, VkPastPresentationTimingGOOGLE* pPresentationTimings); #endif #define VK_NV_sample_mask_override_coverage 1 #define VK_NV_SAMPLE_MASK_OVERRIDE_COVERAGE_SPEC_VERSION 1 #define VK_NV_SAMPLE_MASK_OVERRIDE_COVERAGE_EXTENSION_NAME "VK_NV_sample_mask_override_coverage" #define VK_NV_geometry_shader_passthrough 1 #define VK_NV_GEOMETRY_SHADER_PASSTHROUGH_SPEC_VERSION 1 #define VK_NV_GEOMETRY_SHADER_PASSTHROUGH_EXTENSION_NAME "VK_NV_geometry_shader_passthrough" #define VK_NV_viewport_array2 1 #define VK_NV_VIEWPORT_ARRAY2_SPEC_VERSION 1 #define VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME "VK_NV_viewport_array2" #define VK_NVX_multiview_per_view_attributes 1 #define VK_NVX_MULTIVIEW_PER_VIEW_ATTRIBUTES_SPEC_VERSION 1 #define VK_NVX_MULTIVIEW_PER_VIEW_ATTRIBUTES_EXTENSION_NAME "VK_NVX_multiview_per_view_attributes" typedef struct VkPhysicalDeviceMultiviewPerViewAttributesPropertiesNVX { VkStructureType sType; void* pNext; VkBool32 perViewPositionAllComponents; } VkPhysicalDeviceMultiviewPerViewAttributesPropertiesNVX; #define VK_NV_viewport_swizzle 1 #define VK_NV_VIEWPORT_SWIZZLE_SPEC_VERSION 1 #define VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME "VK_NV_viewport_swizzle" typedef enum VkViewportCoordinateSwizzleNV { VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_X_NV = 0, VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_X_NV = 1, VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_Y_NV = 2, VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_Y_NV = 3, VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_Z_NV = 4, VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_Z_NV = 5, VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_W_NV = 6, VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_W_NV = 7, VK_VIEWPORT_COORDINATE_SWIZZLE_BEGIN_RANGE_NV = VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_X_NV, VK_VIEWPORT_COORDINATE_SWIZZLE_END_RANGE_NV = VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_W_NV, VK_VIEWPORT_COORDINATE_SWIZZLE_RANGE_SIZE_NV = (VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_W_NV - VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_X_NV + 1), VK_VIEWPORT_COORDINATE_SWIZZLE_MAX_ENUM_NV = 0x7FFFFFFF } VkViewportCoordinateSwizzleNV; typedef VkFlags VkPipelineViewportSwizzleStateCreateFlagsNV; typedef struct VkViewportSwizzleNV { VkViewportCoordinateSwizzleNV x; VkViewportCoordinateSwizzleNV y; VkViewportCoordinateSwizzleNV z; VkViewportCoordinateSwizzleNV w; } VkViewportSwizzleNV; typedef struct VkPipelineViewportSwizzleStateCreateInfoNV { VkStructureType sType; const void* pNext; VkPipelineViewportSwizzleStateCreateFlagsNV flags; uint32_t viewportCount; const VkViewportSwizzleNV* pViewportSwizzles; } VkPipelineViewportSwizzleStateCreateInfoNV; #define VK_EXT_discard_rectangles 1 #define VK_EXT_DISCARD_RECTANGLES_SPEC_VERSION 1 #define VK_EXT_DISCARD_RECTANGLES_EXTENSION_NAME "VK_EXT_discard_rectangles" typedef enum VkDiscardRectangleModeEXT { VK_DISCARD_RECTANGLE_MODE_INCLUSIVE_EXT = 0, VK_DISCARD_RECTANGLE_MODE_EXCLUSIVE_EXT = 1, VK_DISCARD_RECTANGLE_MODE_BEGIN_RANGE_EXT = VK_DISCARD_RECTANGLE_MODE_INCLUSIVE_EXT, VK_DISCARD_RECTANGLE_MODE_END_RANGE_EXT = VK_DISCARD_RECTANGLE_MODE_EXCLUSIVE_EXT, VK_DISCARD_RECTANGLE_MODE_RANGE_SIZE_EXT = (VK_DISCARD_RECTANGLE_MODE_EXCLUSIVE_EXT - VK_DISCARD_RECTANGLE_MODE_INCLUSIVE_EXT + 1), VK_DISCARD_RECTANGLE_MODE_MAX_ENUM_EXT = 0x7FFFFFFF } VkDiscardRectangleModeEXT; typedef VkFlags VkPipelineDiscardRectangleStateCreateFlagsEXT; typedef struct VkPhysicalDeviceDiscardRectanglePropertiesEXT { VkStructureType sType; void* pNext; uint32_t maxDiscardRectangles; } VkPhysicalDeviceDiscardRectanglePropertiesEXT; typedef struct VkPipelineDiscardRectangleStateCreateInfoEXT { VkStructureType sType; const void* pNext; VkPipelineDiscardRectangleStateCreateFlagsEXT flags; VkDiscardRectangleModeEXT discardRectangleMode; uint32_t discardRectangleCount; const VkRect2D* pDiscardRectangles; } VkPipelineDiscardRectangleStateCreateInfoEXT; typedef void (VKAPI_PTR *PFN_vkCmdSetDiscardRectangleEXT)(VkCommandBuffer commandBuffer, uint32_t firstDiscardRectangle, uint32_t discardRectangleCount, const VkRect2D* pDiscardRectangles); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR void VKAPI_CALL vkCmdSetDiscardRectangleEXT( VkCommandBuffer commandBuffer, uint32_t firstDiscardRectangle, uint32_t discardRectangleCount, const VkRect2D* pDiscardRectangles); #endif #define VK_EXT_swapchain_colorspace 1 #define VK_EXT_SWAPCHAIN_COLOR_SPACE_SPEC_VERSION 2 #define VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME "VK_EXT_swapchain_colorspace" #define VK_EXT_hdr_metadata 1 #define VK_EXT_HDR_METADATA_SPEC_VERSION 1 #define VK_EXT_HDR_METADATA_EXTENSION_NAME "VK_EXT_hdr_metadata" typedef struct VkXYColorEXT { float x; float y; } VkXYColorEXT; typedef struct VkHdrMetadataEXT { VkStructureType sType; const void* pNext; VkXYColorEXT displayPrimaryRed; VkXYColorEXT displayPrimaryGreen; VkXYColorEXT displayPrimaryBlue; VkXYColorEXT whitePoint; float maxLuminance; float minLuminance; float maxContentLightLevel; float maxFrameAverageLightLevel; } VkHdrMetadataEXT; typedef void (VKAPI_PTR *PFN_vkSetHdrMetadataEXT)(VkDevice device, uint32_t swapchainCount, const VkSwapchainKHR* pSwapchains, const VkHdrMetadataEXT* pMetadata); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR void VKAPI_CALL vkSetHdrMetadataEXT( VkDevice device, uint32_t swapchainCount, const VkSwapchainKHR* pSwapchains, const VkHdrMetadataEXT* pMetadata); #endif #ifdef VK_USE_PLATFORM_IOS_MVK #define VK_MVK_ios_surface 1 #define VK_MVK_IOS_SURFACE_SPEC_VERSION 2 #define VK_MVK_IOS_SURFACE_EXTENSION_NAME "VK_MVK_ios_surface" typedef VkFlags VkIOSSurfaceCreateFlagsMVK; typedef struct VkIOSSurfaceCreateInfoMVK { VkStructureType sType; const void* pNext; VkIOSSurfaceCreateFlagsMVK flags; const void* pView; } VkIOSSurfaceCreateInfoMVK; typedef VkResult (VKAPI_PTR *PFN_vkCreateIOSSurfaceMVK)(VkInstance instance, const VkIOSSurfaceCreateInfoMVK* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR VkResult VKAPI_CALL vkCreateIOSSurfaceMVK( VkInstance instance, const VkIOSSurfaceCreateInfoMVK* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); #endif #endif /* VK_USE_PLATFORM_IOS_MVK */ #ifdef VK_USE_PLATFORM_MACOS_MVK #define VK_MVK_macos_surface 1 #define VK_MVK_MACOS_SURFACE_SPEC_VERSION 2 #define VK_MVK_MACOS_SURFACE_EXTENSION_NAME "VK_MVK_macos_surface" typedef VkFlags VkMacOSSurfaceCreateFlagsMVK; typedef struct VkMacOSSurfaceCreateInfoMVK { VkStructureType sType; const void* pNext; VkMacOSSurfaceCreateFlagsMVK flags; const void* pView; } VkMacOSSurfaceCreateInfoMVK; typedef VkResult (VKAPI_PTR *PFN_vkCreateMacOSSurfaceMVK)(VkInstance instance, const VkMacOSSurfaceCreateInfoMVK* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR VkResult VKAPI_CALL vkCreateMacOSSurfaceMVK( VkInstance instance, const VkMacOSSurfaceCreateInfoMVK* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); #endif #endif /* VK_USE_PLATFORM_MACOS_MVK */ #ifdef __cplusplus } #endif #endif ================================================ FILE: src/engine/renderer/vulkan/vulkan.hpp ================================================ // Copyright (c) 2015-2017 The Khronos Group Inc. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and/or associated documentation files (the // "Materials"), to deal in the Materials without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Materials, and to // permit persons to whom the Materials are furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Materials. // // THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE // MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. // This header is generated from the Khronos Vulkan XML API Registry. #ifndef VULKAN_HPP #define VULKAN_HPP #include #include #include #include #include #include #include #include #include #include #include #include #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE # include # include #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ static_assert( VK_HEADER_VERSION == 49 , "Wrong VK_HEADER_VERSION!" ); // 32-bit vulkan is not typesafe for handles, so don't allow copy constructors on this platform by default. // To enable this feature on 32-bit platforms please define VULKAN_HPP_TYPESAFE_CONVERSION #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) # if !defined( VULKAN_HPP_TYPESAFE_CONVERSION ) # define VULKAN_HPP_TYPESAFE_CONVERSION # endif #endif #if !defined(VULKAN_HPP_HAS_UNRESTRICTED_UNIONS) # if defined(__clang__) # if __has_feature(cxx_unrestricted_unions) # define VULKAN_HPP_HAS_UNRESTRICTED_UNIONS # endif # elif defined(__GNUC__) # define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) # if 40600 <= GCC_VERSION # define VULKAN_HPP_HAS_UNRESTRICTED_UNIONS # endif # elif defined(_MSC_VER) # if 1900 <= _MSC_VER # define VULKAN_HPP_HAS_UNRESTRICTED_UNIONS # endif # endif #endif #if !defined(VULKAN_HPP_INLINE) # if defined(__clang___) # if __has_attribute(always_inline) # define VULKAN_HPP_INLINE __attribute__((always_inline)) __inline__ # else # define VULKAN_HPP_INLINE inline # endif # elif defined(__GNUC__) # define VULKAN_HPP_INLINE __attribute__((always_inline)) __inline__ # elif defined(_MSC_VER) # define VULKAN_HPP_INLINE __forceinline # else # define VULKAN_HPP_INLINE inline # endif #endif #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) # define VULKAN_HPP_TYPESAFE_EXPLICIT #else # define VULKAN_HPP_TYPESAFE_EXPLICIT explicit #endif namespace vk { template struct FlagTraits { enum { allFlags = 0 }; }; template class Flags { public: Flags() : m_mask(0) { } Flags(BitType bit) : m_mask(static_cast(bit)) { } Flags(Flags const& rhs) : m_mask(rhs.m_mask) { } Flags & operator=(Flags const& rhs) { m_mask = rhs.m_mask; return *this; } Flags & operator|=(Flags const& rhs) { m_mask |= rhs.m_mask; return *this; } Flags & operator&=(Flags const& rhs) { m_mask &= rhs.m_mask; return *this; } Flags & operator^=(Flags const& rhs) { m_mask ^= rhs.m_mask; return *this; } Flags operator|(Flags const& rhs) const { Flags result(*this); result |= rhs; return result; } Flags operator&(Flags const& rhs) const { Flags result(*this); result &= rhs; return result; } Flags operator^(Flags const& rhs) const { Flags result(*this); result ^= rhs; return result; } bool operator!() const { return !m_mask; } Flags operator~() const { Flags result(*this); result.m_mask ^= FlagTraits::allFlags; return result; } bool operator==(Flags const& rhs) const { return m_mask == rhs.m_mask; } bool operator!=(Flags const& rhs) const { return m_mask != rhs.m_mask; } explicit operator bool() const { return !!m_mask; } explicit operator MaskType() const { return m_mask; } private: MaskType m_mask; }; template Flags operator|(BitType bit, Flags const& flags) { return flags | bit; } template Flags operator&(BitType bit, Flags const& flags) { return flags & bit; } template Flags operator^(BitType bit, Flags const& flags) { return flags ^ bit; } template class Optional { public: Optional(RefType & reference) { m_ptr = &reference; } Optional(RefType * ptr) { m_ptr = ptr; } Optional(std::nullptr_t) { m_ptr = nullptr; } operator RefType*() const { return m_ptr; } RefType const* operator->() const { return m_ptr; } explicit operator bool() const { return !!m_ptr; } private: RefType *m_ptr; }; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template class ArrayProxy { public: ArrayProxy(std::nullptr_t) : m_count(0) , m_ptr(nullptr) {} ArrayProxy(T & ptr) : m_count(1) , m_ptr(&ptr) {} ArrayProxy(uint32_t count, T * ptr) : m_count(count) , m_ptr(ptr) {} template ArrayProxy(std::array::type, N> & data) : m_count(N) , m_ptr(data.data()) {} template ArrayProxy(std::array::type, N> const& data) : m_count(N) , m_ptr(data.data()) {} template ::type>> ArrayProxy(std::vector::type, Allocator> & data) : m_count(static_cast(data.size())) , m_ptr(data.data()) {} template ::type>> ArrayProxy(std::vector::type, Allocator> const& data) : m_count(static_cast(data.size())) , m_ptr(data.data()) {} ArrayProxy(std::initializer_list const& data) : m_count(static_cast(data.end() - data.begin())) , m_ptr(data.begin()) {} const T * begin() const { return m_ptr; } const T * end() const { return m_ptr + m_count; } const T & front() const { assert(m_count && m_ptr); return *m_ptr; } const T & back() const { assert(m_count && m_ptr); return *(m_ptr + m_count - 1); } bool empty() const { return (m_count == 0); } uint32_t size() const { return m_count; } T * data() const { return m_ptr; } private: uint32_t m_count; T * m_ptr; }; #endif #if defined(VULKAN_HPP_NO_EXCEPTIONS) && !defined(VULKAN_HPP_NO_SMART_HANDLE) # define VULKAN_HPP_NO_SMART_HANDLE #endif #ifndef VULKAN_HPP_NO_SMART_HANDLE template class UniqueHandle { public: explicit UniqueHandle( Type const& value = Type(), Deleter const& deleter = Deleter() ) : m_value( value ) , m_deleter( deleter ) {} UniqueHandle( UniqueHandle const& ) = delete; UniqueHandle( UniqueHandle && other ) : m_value( other.release() ) , m_deleter( std::move( other.m_deleter ) ) {} ~UniqueHandle() { destroy(); } UniqueHandle & operator=( UniqueHandle const& ) = delete; UniqueHandle & operator=( UniqueHandle && other ) { reset( other.release() ); m_deleter = std::move( other.m_deleter ); return *this; } explicit operator bool() const { return m_value.operator bool(); } Type const* operator->() const { return &m_value; } Type const& operator*() const { return m_value; } Type get() const { return m_value; } Deleter & getDeleter() { return m_deleter; } Deleter const& getDeleter() const { return m_deleter; } void reset( Type const& value = Type() ) { if ( m_value != value ) { destroy(); m_value = value; } } Type release() { Type value = m_value; m_value = nullptr; return value; } void swap( UniqueHandle & rhs ) { std::swap(m_value, rhs.m_value); std::swap(m_deleter, rhs.m_deleter); } private: void destroy() { if ( m_value ) { m_deleter( m_value ); } } private: Type m_value; Deleter m_deleter; }; template VULKAN_HPP_INLINE void swap( UniqueHandle & lhs, UniqueHandle & rhs ) { lhs.swap( rhs ); } #endif enum class Result { eSuccess = VK_SUCCESS, eNotReady = VK_NOT_READY, eTimeout = VK_TIMEOUT, eEventSet = VK_EVENT_SET, eEventReset = VK_EVENT_RESET, eIncomplete = VK_INCOMPLETE, eErrorOutOfHostMemory = VK_ERROR_OUT_OF_HOST_MEMORY, eErrorOutOfDeviceMemory = VK_ERROR_OUT_OF_DEVICE_MEMORY, eErrorInitializationFailed = VK_ERROR_INITIALIZATION_FAILED, eErrorDeviceLost = VK_ERROR_DEVICE_LOST, eErrorMemoryMapFailed = VK_ERROR_MEMORY_MAP_FAILED, eErrorLayerNotPresent = VK_ERROR_LAYER_NOT_PRESENT, eErrorExtensionNotPresent = VK_ERROR_EXTENSION_NOT_PRESENT, eErrorFeatureNotPresent = VK_ERROR_FEATURE_NOT_PRESENT, eErrorIncompatibleDriver = VK_ERROR_INCOMPATIBLE_DRIVER, eErrorTooManyObjects = VK_ERROR_TOO_MANY_OBJECTS, eErrorFormatNotSupported = VK_ERROR_FORMAT_NOT_SUPPORTED, eErrorFragmentedPool = VK_ERROR_FRAGMENTED_POOL, eErrorSurfaceLostKHR = VK_ERROR_SURFACE_LOST_KHR, eErrorNativeWindowInUseKHR = VK_ERROR_NATIVE_WINDOW_IN_USE_KHR, eSuboptimalKHR = VK_SUBOPTIMAL_KHR, eErrorOutOfDateKHR = VK_ERROR_OUT_OF_DATE_KHR, eErrorIncompatibleDisplayKHR = VK_ERROR_INCOMPATIBLE_DISPLAY_KHR, eErrorValidationFailedEXT = VK_ERROR_VALIDATION_FAILED_EXT, eErrorInvalidShaderNV = VK_ERROR_INVALID_SHADER_NV, eErrorOutOfPoolMemoryKHR = VK_ERROR_OUT_OF_POOL_MEMORY_KHR, eErrorInvalidExternalHandleKHX = VK_ERROR_INVALID_EXTERNAL_HANDLE_KHX }; VULKAN_HPP_INLINE std::string to_string(Result value) { switch (value) { case Result::eSuccess: return "Success"; case Result::eNotReady: return "NotReady"; case Result::eTimeout: return "Timeout"; case Result::eEventSet: return "EventSet"; case Result::eEventReset: return "EventReset"; case Result::eIncomplete: return "Incomplete"; case Result::eErrorOutOfHostMemory: return "ErrorOutOfHostMemory"; case Result::eErrorOutOfDeviceMemory: return "ErrorOutOfDeviceMemory"; case Result::eErrorInitializationFailed: return "ErrorInitializationFailed"; case Result::eErrorDeviceLost: return "ErrorDeviceLost"; case Result::eErrorMemoryMapFailed: return "ErrorMemoryMapFailed"; case Result::eErrorLayerNotPresent: return "ErrorLayerNotPresent"; case Result::eErrorExtensionNotPresent: return "ErrorExtensionNotPresent"; case Result::eErrorFeatureNotPresent: return "ErrorFeatureNotPresent"; case Result::eErrorIncompatibleDriver: return "ErrorIncompatibleDriver"; case Result::eErrorTooManyObjects: return "ErrorTooManyObjects"; case Result::eErrorFormatNotSupported: return "ErrorFormatNotSupported"; case Result::eErrorFragmentedPool: return "ErrorFragmentedPool"; case Result::eErrorSurfaceLostKHR: return "ErrorSurfaceLostKHR"; case Result::eErrorNativeWindowInUseKHR: return "ErrorNativeWindowInUseKHR"; case Result::eSuboptimalKHR: return "SuboptimalKHR"; case Result::eErrorOutOfDateKHR: return "ErrorOutOfDateKHR"; case Result::eErrorIncompatibleDisplayKHR: return "ErrorIncompatibleDisplayKHR"; case Result::eErrorValidationFailedEXT: return "ErrorValidationFailedEXT"; case Result::eErrorInvalidShaderNV: return "ErrorInvalidShaderNV"; case Result::eErrorOutOfPoolMemoryKHR: return "ErrorOutOfPoolMemoryKHR"; case Result::eErrorInvalidExternalHandleKHX: return "ErrorInvalidExternalHandleKHX"; default: return "invalid"; } } #if defined(_MSC_VER) && (_MSC_VER == 1800) # define noexcept _NOEXCEPT #endif class ErrorCategoryImpl : public std::error_category { public: virtual const char* name() const noexcept override { return "vk::Result"; } virtual std::string message(int ev) const override { return to_string(static_cast(ev)); } }; #if defined(_MSC_VER) && (_MSC_VER == 1800) # undef noexcept #endif VULKAN_HPP_INLINE const std::error_category& errorCategory() { static ErrorCategoryImpl instance; return instance; } VULKAN_HPP_INLINE std::error_code make_error_code(Result e) { return std::error_code(static_cast(e), errorCategory()); } VULKAN_HPP_INLINE std::error_condition make_error_condition(Result e) { return std::error_condition(static_cast(e), errorCategory()); } } // namespace vk namespace std { template <> struct is_error_code_enum : public true_type {}; } namespace vk { template struct ResultValue { ResultValue( Result r, T & v ) : result( r ) , value( v ) {} Result result; T value; operator std::tuple() { return std::tuple(result, value); } }; template struct ResultValueType { #ifdef VULKAN_HPP_NO_EXCEPTIONS typedef ResultValue type; #else typedef T type; #endif }; template <> struct ResultValueType { #ifdef VULKAN_HPP_NO_EXCEPTIONS typedef Result type; #else typedef void type; #endif }; VULKAN_HPP_INLINE ResultValueType::type createResultValue( Result result, char const * message ) { #ifdef VULKAN_HPP_NO_EXCEPTIONS assert( result == Result::eSuccess ); return result; #else if ( result != Result::eSuccess ) { throw std::system_error( result, message ); } #endif } template VULKAN_HPP_INLINE typename ResultValueType::type createResultValue( Result result, T & data, char const * message ) { #ifdef VULKAN_HPP_NO_EXCEPTIONS assert( result == Result::eSuccess ); return ResultValue( result, data ); #else if ( result != Result::eSuccess ) { throw std::system_error( result, message ); } return data; #endif } VULKAN_HPP_INLINE Result createResultValue( Result result, char const * message, std::initializer_list successCodes ) { #ifdef VULKAN_HPP_NO_EXCEPTIONS assert( std::find( successCodes.begin(), successCodes.end(), result ) != successCodes.end() ); #else if ( std::find( successCodes.begin(), successCodes.end(), result ) == successCodes.end() ) { throw std::system_error( result, message ); } #endif return result; } template VULKAN_HPP_INLINE ResultValue createResultValue( Result result, T & data, char const * message, std::initializer_list successCodes ) { #ifdef VULKAN_HPP_NO_EXCEPTIONS assert( std::find( successCodes.begin(), successCodes.end(), result ) != successCodes.end() ); #else if ( std::find( successCodes.begin(), successCodes.end(), result ) == successCodes.end() ) { throw std::system_error( result, message ); } #endif return ResultValue( result, data ); } using SampleMask = uint32_t; using Bool32 = uint32_t; using DeviceSize = uint64_t; enum class FramebufferCreateFlagBits { }; using FramebufferCreateFlags = Flags; VULKAN_HPP_INLINE FramebufferCreateFlags operator|( FramebufferCreateFlagBits bit0, FramebufferCreateFlagBits bit1 ) { return FramebufferCreateFlags( bit0 ) | bit1; } enum class QueryPoolCreateFlagBits { }; using QueryPoolCreateFlags = Flags; VULKAN_HPP_INLINE QueryPoolCreateFlags operator|( QueryPoolCreateFlagBits bit0, QueryPoolCreateFlagBits bit1 ) { return QueryPoolCreateFlags( bit0 ) | bit1; } enum class RenderPassCreateFlagBits { }; using RenderPassCreateFlags = Flags; VULKAN_HPP_INLINE RenderPassCreateFlags operator|( RenderPassCreateFlagBits bit0, RenderPassCreateFlagBits bit1 ) { return RenderPassCreateFlags( bit0 ) | bit1; } enum class SamplerCreateFlagBits { }; using SamplerCreateFlags = Flags; VULKAN_HPP_INLINE SamplerCreateFlags operator|( SamplerCreateFlagBits bit0, SamplerCreateFlagBits bit1 ) { return SamplerCreateFlags( bit0 ) | bit1; } enum class PipelineLayoutCreateFlagBits { }; using PipelineLayoutCreateFlags = Flags; VULKAN_HPP_INLINE PipelineLayoutCreateFlags operator|( PipelineLayoutCreateFlagBits bit0, PipelineLayoutCreateFlagBits bit1 ) { return PipelineLayoutCreateFlags( bit0 ) | bit1; } enum class PipelineCacheCreateFlagBits { }; using PipelineCacheCreateFlags = Flags; VULKAN_HPP_INLINE PipelineCacheCreateFlags operator|( PipelineCacheCreateFlagBits bit0, PipelineCacheCreateFlagBits bit1 ) { return PipelineCacheCreateFlags( bit0 ) | bit1; } enum class PipelineDepthStencilStateCreateFlagBits { }; using PipelineDepthStencilStateCreateFlags = Flags; VULKAN_HPP_INLINE PipelineDepthStencilStateCreateFlags operator|( PipelineDepthStencilStateCreateFlagBits bit0, PipelineDepthStencilStateCreateFlagBits bit1 ) { return PipelineDepthStencilStateCreateFlags( bit0 ) | bit1; } enum class PipelineDynamicStateCreateFlagBits { }; using PipelineDynamicStateCreateFlags = Flags; VULKAN_HPP_INLINE PipelineDynamicStateCreateFlags operator|( PipelineDynamicStateCreateFlagBits bit0, PipelineDynamicStateCreateFlagBits bit1 ) { return PipelineDynamicStateCreateFlags( bit0 ) | bit1; } enum class PipelineColorBlendStateCreateFlagBits { }; using PipelineColorBlendStateCreateFlags = Flags; VULKAN_HPP_INLINE PipelineColorBlendStateCreateFlags operator|( PipelineColorBlendStateCreateFlagBits bit0, PipelineColorBlendStateCreateFlagBits bit1 ) { return PipelineColorBlendStateCreateFlags( bit0 ) | bit1; } enum class PipelineMultisampleStateCreateFlagBits { }; using PipelineMultisampleStateCreateFlags = Flags; VULKAN_HPP_INLINE PipelineMultisampleStateCreateFlags operator|( PipelineMultisampleStateCreateFlagBits bit0, PipelineMultisampleStateCreateFlagBits bit1 ) { return PipelineMultisampleStateCreateFlags( bit0 ) | bit1; } enum class PipelineRasterizationStateCreateFlagBits { }; using PipelineRasterizationStateCreateFlags = Flags; VULKAN_HPP_INLINE PipelineRasterizationStateCreateFlags operator|( PipelineRasterizationStateCreateFlagBits bit0, PipelineRasterizationStateCreateFlagBits bit1 ) { return PipelineRasterizationStateCreateFlags( bit0 ) | bit1; } enum class PipelineViewportStateCreateFlagBits { }; using PipelineViewportStateCreateFlags = Flags; VULKAN_HPP_INLINE PipelineViewportStateCreateFlags operator|( PipelineViewportStateCreateFlagBits bit0, PipelineViewportStateCreateFlagBits bit1 ) { return PipelineViewportStateCreateFlags( bit0 ) | bit1; } enum class PipelineTessellationStateCreateFlagBits { }; using PipelineTessellationStateCreateFlags = Flags; VULKAN_HPP_INLINE PipelineTessellationStateCreateFlags operator|( PipelineTessellationStateCreateFlagBits bit0, PipelineTessellationStateCreateFlagBits bit1 ) { return PipelineTessellationStateCreateFlags( bit0 ) | bit1; } enum class PipelineInputAssemblyStateCreateFlagBits { }; using PipelineInputAssemblyStateCreateFlags = Flags; VULKAN_HPP_INLINE PipelineInputAssemblyStateCreateFlags operator|( PipelineInputAssemblyStateCreateFlagBits bit0, PipelineInputAssemblyStateCreateFlagBits bit1 ) { return PipelineInputAssemblyStateCreateFlags( bit0 ) | bit1; } enum class PipelineVertexInputStateCreateFlagBits { }; using PipelineVertexInputStateCreateFlags = Flags; VULKAN_HPP_INLINE PipelineVertexInputStateCreateFlags operator|( PipelineVertexInputStateCreateFlagBits bit0, PipelineVertexInputStateCreateFlagBits bit1 ) { return PipelineVertexInputStateCreateFlags( bit0 ) | bit1; } enum class PipelineShaderStageCreateFlagBits { }; using PipelineShaderStageCreateFlags = Flags; VULKAN_HPP_INLINE PipelineShaderStageCreateFlags operator|( PipelineShaderStageCreateFlagBits bit0, PipelineShaderStageCreateFlagBits bit1 ) { return PipelineShaderStageCreateFlags( bit0 ) | bit1; } enum class BufferViewCreateFlagBits { }; using BufferViewCreateFlags = Flags; VULKAN_HPP_INLINE BufferViewCreateFlags operator|( BufferViewCreateFlagBits bit0, BufferViewCreateFlagBits bit1 ) { return BufferViewCreateFlags( bit0 ) | bit1; } enum class InstanceCreateFlagBits { }; using InstanceCreateFlags = Flags; VULKAN_HPP_INLINE InstanceCreateFlags operator|( InstanceCreateFlagBits bit0, InstanceCreateFlagBits bit1 ) { return InstanceCreateFlags( bit0 ) | bit1; } enum class DeviceCreateFlagBits { }; using DeviceCreateFlags = Flags; VULKAN_HPP_INLINE DeviceCreateFlags operator|( DeviceCreateFlagBits bit0, DeviceCreateFlagBits bit1 ) { return DeviceCreateFlags( bit0 ) | bit1; } enum class DeviceQueueCreateFlagBits { }; using DeviceQueueCreateFlags = Flags; VULKAN_HPP_INLINE DeviceQueueCreateFlags operator|( DeviceQueueCreateFlagBits bit0, DeviceQueueCreateFlagBits bit1 ) { return DeviceQueueCreateFlags( bit0 ) | bit1; } enum class ImageViewCreateFlagBits { }; using ImageViewCreateFlags = Flags; VULKAN_HPP_INLINE ImageViewCreateFlags operator|( ImageViewCreateFlagBits bit0, ImageViewCreateFlagBits bit1 ) { return ImageViewCreateFlags( bit0 ) | bit1; } enum class SemaphoreCreateFlagBits { }; using SemaphoreCreateFlags = Flags; VULKAN_HPP_INLINE SemaphoreCreateFlags operator|( SemaphoreCreateFlagBits bit0, SemaphoreCreateFlagBits bit1 ) { return SemaphoreCreateFlags( bit0 ) | bit1; } enum class ShaderModuleCreateFlagBits { }; using ShaderModuleCreateFlags = Flags; VULKAN_HPP_INLINE ShaderModuleCreateFlags operator|( ShaderModuleCreateFlagBits bit0, ShaderModuleCreateFlagBits bit1 ) { return ShaderModuleCreateFlags( bit0 ) | bit1; } enum class EventCreateFlagBits { }; using EventCreateFlags = Flags; VULKAN_HPP_INLINE EventCreateFlags operator|( EventCreateFlagBits bit0, EventCreateFlagBits bit1 ) { return EventCreateFlags( bit0 ) | bit1; } enum class MemoryMapFlagBits { }; using MemoryMapFlags = Flags; VULKAN_HPP_INLINE MemoryMapFlags operator|( MemoryMapFlagBits bit0, MemoryMapFlagBits bit1 ) { return MemoryMapFlags( bit0 ) | bit1; } enum class DescriptorPoolResetFlagBits { }; using DescriptorPoolResetFlags = Flags; VULKAN_HPP_INLINE DescriptorPoolResetFlags operator|( DescriptorPoolResetFlagBits bit0, DescriptorPoolResetFlagBits bit1 ) { return DescriptorPoolResetFlags( bit0 ) | bit1; } enum class DescriptorUpdateTemplateCreateFlagBitsKHR { }; using DescriptorUpdateTemplateCreateFlagsKHR = Flags; VULKAN_HPP_INLINE DescriptorUpdateTemplateCreateFlagsKHR operator|( DescriptorUpdateTemplateCreateFlagBitsKHR bit0, DescriptorUpdateTemplateCreateFlagBitsKHR bit1 ) { return DescriptorUpdateTemplateCreateFlagsKHR( bit0 ) | bit1; } enum class DisplayModeCreateFlagBitsKHR { }; using DisplayModeCreateFlagsKHR = Flags; VULKAN_HPP_INLINE DisplayModeCreateFlagsKHR operator|( DisplayModeCreateFlagBitsKHR bit0, DisplayModeCreateFlagBitsKHR bit1 ) { return DisplayModeCreateFlagsKHR( bit0 ) | bit1; } enum class DisplaySurfaceCreateFlagBitsKHR { }; using DisplaySurfaceCreateFlagsKHR = Flags; VULKAN_HPP_INLINE DisplaySurfaceCreateFlagsKHR operator|( DisplaySurfaceCreateFlagBitsKHR bit0, DisplaySurfaceCreateFlagBitsKHR bit1 ) { return DisplaySurfaceCreateFlagsKHR( bit0 ) | bit1; } #ifdef VK_USE_PLATFORM_ANDROID_KHR enum class AndroidSurfaceCreateFlagBitsKHR { }; #endif /*VK_USE_PLATFORM_ANDROID_KHR*/ #ifdef VK_USE_PLATFORM_ANDROID_KHR using AndroidSurfaceCreateFlagsKHR = Flags; VULKAN_HPP_INLINE AndroidSurfaceCreateFlagsKHR operator|( AndroidSurfaceCreateFlagBitsKHR bit0, AndroidSurfaceCreateFlagBitsKHR bit1 ) { return AndroidSurfaceCreateFlagsKHR( bit0 ) | bit1; } #endif /*VK_USE_PLATFORM_ANDROID_KHR*/ #ifdef VK_USE_PLATFORM_MIR_KHR enum class MirSurfaceCreateFlagBitsKHR { }; #endif /*VK_USE_PLATFORM_MIR_KHR*/ #ifdef VK_USE_PLATFORM_MIR_KHR using MirSurfaceCreateFlagsKHR = Flags; VULKAN_HPP_INLINE MirSurfaceCreateFlagsKHR operator|( MirSurfaceCreateFlagBitsKHR bit0, MirSurfaceCreateFlagBitsKHR bit1 ) { return MirSurfaceCreateFlagsKHR( bit0 ) | bit1; } #endif /*VK_USE_PLATFORM_MIR_KHR*/ #ifdef VK_USE_PLATFORM_VI_NN enum class ViSurfaceCreateFlagBitsNN { }; #endif /*VK_USE_PLATFORM_VI_NN*/ #ifdef VK_USE_PLATFORM_VI_NN using ViSurfaceCreateFlagsNN = Flags; VULKAN_HPP_INLINE ViSurfaceCreateFlagsNN operator|( ViSurfaceCreateFlagBitsNN bit0, ViSurfaceCreateFlagBitsNN bit1 ) { return ViSurfaceCreateFlagsNN( bit0 ) | bit1; } #endif /*VK_USE_PLATFORM_VI_NN*/ #ifdef VK_USE_PLATFORM_WAYLAND_KHR enum class WaylandSurfaceCreateFlagBitsKHR { }; #endif /*VK_USE_PLATFORM_WAYLAND_KHR*/ #ifdef VK_USE_PLATFORM_WAYLAND_KHR using WaylandSurfaceCreateFlagsKHR = Flags; VULKAN_HPP_INLINE WaylandSurfaceCreateFlagsKHR operator|( WaylandSurfaceCreateFlagBitsKHR bit0, WaylandSurfaceCreateFlagBitsKHR bit1 ) { return WaylandSurfaceCreateFlagsKHR( bit0 ) | bit1; } #endif /*VK_USE_PLATFORM_WAYLAND_KHR*/ #ifdef VK_USE_PLATFORM_WIN32_KHR enum class Win32SurfaceCreateFlagBitsKHR { }; #endif /*VK_USE_PLATFORM_WIN32_KHR*/ #ifdef VK_USE_PLATFORM_WIN32_KHR using Win32SurfaceCreateFlagsKHR = Flags; VULKAN_HPP_INLINE Win32SurfaceCreateFlagsKHR operator|( Win32SurfaceCreateFlagBitsKHR bit0, Win32SurfaceCreateFlagBitsKHR bit1 ) { return Win32SurfaceCreateFlagsKHR( bit0 ) | bit1; } #endif /*VK_USE_PLATFORM_WIN32_KHR*/ #ifdef VK_USE_PLATFORM_XLIB_KHR enum class XlibSurfaceCreateFlagBitsKHR { }; #endif /*VK_USE_PLATFORM_XLIB_KHR*/ #ifdef VK_USE_PLATFORM_XLIB_KHR using XlibSurfaceCreateFlagsKHR = Flags; VULKAN_HPP_INLINE XlibSurfaceCreateFlagsKHR operator|( XlibSurfaceCreateFlagBitsKHR bit0, XlibSurfaceCreateFlagBitsKHR bit1 ) { return XlibSurfaceCreateFlagsKHR( bit0 ) | bit1; } #endif /*VK_USE_PLATFORM_XLIB_KHR*/ #ifdef VK_USE_PLATFORM_XCB_KHR enum class XcbSurfaceCreateFlagBitsKHR { }; #endif /*VK_USE_PLATFORM_XCB_KHR*/ #ifdef VK_USE_PLATFORM_XCB_KHR using XcbSurfaceCreateFlagsKHR = Flags; VULKAN_HPP_INLINE XcbSurfaceCreateFlagsKHR operator|( XcbSurfaceCreateFlagBitsKHR bit0, XcbSurfaceCreateFlagBitsKHR bit1 ) { return XcbSurfaceCreateFlagsKHR( bit0 ) | bit1; } #endif /*VK_USE_PLATFORM_XCB_KHR*/ #ifdef VK_USE_PLATFORM_IOS_MVK enum class IOSSurfaceCreateFlagBitsMVK { }; #endif /*VK_USE_PLATFORM_IOS_MVK*/ #ifdef VK_USE_PLATFORM_IOS_MVK using IOSSurfaceCreateFlagsMVK = Flags; VULKAN_HPP_INLINE IOSSurfaceCreateFlagsMVK operator|( IOSSurfaceCreateFlagBitsMVK bit0, IOSSurfaceCreateFlagBitsMVK bit1 ) { return IOSSurfaceCreateFlagsMVK( bit0 ) | bit1; } #endif /*VK_USE_PLATFORM_IOS_MVK*/ #ifdef VK_USE_PLATFORM_MACOS_MVK enum class MacOSSurfaceCreateFlagBitsMVK { }; #endif /*VK_USE_PLATFORM_MACOS_MVK*/ #ifdef VK_USE_PLATFORM_MACOS_MVK using MacOSSurfaceCreateFlagsMVK = Flags; VULKAN_HPP_INLINE MacOSSurfaceCreateFlagsMVK operator|( MacOSSurfaceCreateFlagBitsMVK bit0, MacOSSurfaceCreateFlagBitsMVK bit1 ) { return MacOSSurfaceCreateFlagsMVK( bit0 ) | bit1; } #endif /*VK_USE_PLATFORM_MACOS_MVK*/ enum class CommandPoolTrimFlagBitsKHR { }; using CommandPoolTrimFlagsKHR = Flags; VULKAN_HPP_INLINE CommandPoolTrimFlagsKHR operator|( CommandPoolTrimFlagBitsKHR bit0, CommandPoolTrimFlagBitsKHR bit1 ) { return CommandPoolTrimFlagsKHR( bit0 ) | bit1; } enum class PipelineViewportSwizzleStateCreateFlagBitsNV { }; using PipelineViewportSwizzleStateCreateFlagsNV = Flags; VULKAN_HPP_INLINE PipelineViewportSwizzleStateCreateFlagsNV operator|( PipelineViewportSwizzleStateCreateFlagBitsNV bit0, PipelineViewportSwizzleStateCreateFlagBitsNV bit1 ) { return PipelineViewportSwizzleStateCreateFlagsNV( bit0 ) | bit1; } enum class PipelineDiscardRectangleStateCreateFlagBitsEXT { }; using PipelineDiscardRectangleStateCreateFlagsEXT = Flags; VULKAN_HPP_INLINE PipelineDiscardRectangleStateCreateFlagsEXT operator|( PipelineDiscardRectangleStateCreateFlagBitsEXT bit0, PipelineDiscardRectangleStateCreateFlagBitsEXT bit1 ) { return PipelineDiscardRectangleStateCreateFlagsEXT( bit0 ) | bit1; } class DeviceMemory { public: DeviceMemory() : m_deviceMemory(VK_NULL_HANDLE) {} DeviceMemory( std::nullptr_t ) : m_deviceMemory(VK_NULL_HANDLE) {} VULKAN_HPP_TYPESAFE_EXPLICIT DeviceMemory(VkDeviceMemory deviceMemory) : m_deviceMemory(deviceMemory) {} #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) DeviceMemory& operator=(VkDeviceMemory deviceMemory) { m_deviceMemory = deviceMemory; return *this; } #endif DeviceMemory& operator=( std::nullptr_t ) { m_deviceMemory = VK_NULL_HANDLE; return *this; } bool operator==(DeviceMemory const &rhs) const { return m_deviceMemory == rhs.m_deviceMemory; } bool operator!=(DeviceMemory const &rhs) const { return m_deviceMemory != rhs.m_deviceMemory; } bool operator<(DeviceMemory const &rhs) const { return m_deviceMemory < rhs.m_deviceMemory; } VULKAN_HPP_TYPESAFE_EXPLICIT operator VkDeviceMemory() const { return m_deviceMemory; } explicit operator bool() const { return m_deviceMemory != VK_NULL_HANDLE; } bool operator!() const { return m_deviceMemory == VK_NULL_HANDLE; } private: VkDeviceMemory m_deviceMemory; }; static_assert( sizeof( DeviceMemory ) == sizeof( VkDeviceMemory ), "handle and wrapper have different size!" ); class CommandPool { public: CommandPool() : m_commandPool(VK_NULL_HANDLE) {} CommandPool( std::nullptr_t ) : m_commandPool(VK_NULL_HANDLE) {} VULKAN_HPP_TYPESAFE_EXPLICIT CommandPool(VkCommandPool commandPool) : m_commandPool(commandPool) {} #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) CommandPool& operator=(VkCommandPool commandPool) { m_commandPool = commandPool; return *this; } #endif CommandPool& operator=( std::nullptr_t ) { m_commandPool = VK_NULL_HANDLE; return *this; } bool operator==(CommandPool const &rhs) const { return m_commandPool == rhs.m_commandPool; } bool operator!=(CommandPool const &rhs) const { return m_commandPool != rhs.m_commandPool; } bool operator<(CommandPool const &rhs) const { return m_commandPool < rhs.m_commandPool; } VULKAN_HPP_TYPESAFE_EXPLICIT operator VkCommandPool() const { return m_commandPool; } explicit operator bool() const { return m_commandPool != VK_NULL_HANDLE; } bool operator!() const { return m_commandPool == VK_NULL_HANDLE; } private: VkCommandPool m_commandPool; }; static_assert( sizeof( CommandPool ) == sizeof( VkCommandPool ), "handle and wrapper have different size!" ); class Buffer { public: Buffer() : m_buffer(VK_NULL_HANDLE) {} Buffer( std::nullptr_t ) : m_buffer(VK_NULL_HANDLE) {} VULKAN_HPP_TYPESAFE_EXPLICIT Buffer(VkBuffer buffer) : m_buffer(buffer) {} #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) Buffer& operator=(VkBuffer buffer) { m_buffer = buffer; return *this; } #endif Buffer& operator=( std::nullptr_t ) { m_buffer = VK_NULL_HANDLE; return *this; } bool operator==(Buffer const &rhs) const { return m_buffer == rhs.m_buffer; } bool operator!=(Buffer const &rhs) const { return m_buffer != rhs.m_buffer; } bool operator<(Buffer const &rhs) const { return m_buffer < rhs.m_buffer; } VULKAN_HPP_TYPESAFE_EXPLICIT operator VkBuffer() const { return m_buffer; } explicit operator bool() const { return m_buffer != VK_NULL_HANDLE; } bool operator!() const { return m_buffer == VK_NULL_HANDLE; } private: VkBuffer m_buffer; }; static_assert( sizeof( Buffer ) == sizeof( VkBuffer ), "handle and wrapper have different size!" ); class BufferView { public: BufferView() : m_bufferView(VK_NULL_HANDLE) {} BufferView( std::nullptr_t ) : m_bufferView(VK_NULL_HANDLE) {} VULKAN_HPP_TYPESAFE_EXPLICIT BufferView(VkBufferView bufferView) : m_bufferView(bufferView) {} #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) BufferView& operator=(VkBufferView bufferView) { m_bufferView = bufferView; return *this; } #endif BufferView& operator=( std::nullptr_t ) { m_bufferView = VK_NULL_HANDLE; return *this; } bool operator==(BufferView const &rhs) const { return m_bufferView == rhs.m_bufferView; } bool operator!=(BufferView const &rhs) const { return m_bufferView != rhs.m_bufferView; } bool operator<(BufferView const &rhs) const { return m_bufferView < rhs.m_bufferView; } VULKAN_HPP_TYPESAFE_EXPLICIT operator VkBufferView() const { return m_bufferView; } explicit operator bool() const { return m_bufferView != VK_NULL_HANDLE; } bool operator!() const { return m_bufferView == VK_NULL_HANDLE; } private: VkBufferView m_bufferView; }; static_assert( sizeof( BufferView ) == sizeof( VkBufferView ), "handle and wrapper have different size!" ); class Image { public: Image() : m_image(VK_NULL_HANDLE) {} Image( std::nullptr_t ) : m_image(VK_NULL_HANDLE) {} VULKAN_HPP_TYPESAFE_EXPLICIT Image(VkImage image) : m_image(image) {} #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) Image& operator=(VkImage image) { m_image = image; return *this; } #endif Image& operator=( std::nullptr_t ) { m_image = VK_NULL_HANDLE; return *this; } bool operator==(Image const &rhs) const { return m_image == rhs.m_image; } bool operator!=(Image const &rhs) const { return m_image != rhs.m_image; } bool operator<(Image const &rhs) const { return m_image < rhs.m_image; } VULKAN_HPP_TYPESAFE_EXPLICIT operator VkImage() const { return m_image; } explicit operator bool() const { return m_image != VK_NULL_HANDLE; } bool operator!() const { return m_image == VK_NULL_HANDLE; } private: VkImage m_image; }; static_assert( sizeof( Image ) == sizeof( VkImage ), "handle and wrapper have different size!" ); class ImageView { public: ImageView() : m_imageView(VK_NULL_HANDLE) {} ImageView( std::nullptr_t ) : m_imageView(VK_NULL_HANDLE) {} VULKAN_HPP_TYPESAFE_EXPLICIT ImageView(VkImageView imageView) : m_imageView(imageView) {} #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) ImageView& operator=(VkImageView imageView) { m_imageView = imageView; return *this; } #endif ImageView& operator=( std::nullptr_t ) { m_imageView = VK_NULL_HANDLE; return *this; } bool operator==(ImageView const &rhs) const { return m_imageView == rhs.m_imageView; } bool operator!=(ImageView const &rhs) const { return m_imageView != rhs.m_imageView; } bool operator<(ImageView const &rhs) const { return m_imageView < rhs.m_imageView; } VULKAN_HPP_TYPESAFE_EXPLICIT operator VkImageView() const { return m_imageView; } explicit operator bool() const { return m_imageView != VK_NULL_HANDLE; } bool operator!() const { return m_imageView == VK_NULL_HANDLE; } private: VkImageView m_imageView; }; static_assert( sizeof( ImageView ) == sizeof( VkImageView ), "handle and wrapper have different size!" ); class ShaderModule { public: ShaderModule() : m_shaderModule(VK_NULL_HANDLE) {} ShaderModule( std::nullptr_t ) : m_shaderModule(VK_NULL_HANDLE) {} VULKAN_HPP_TYPESAFE_EXPLICIT ShaderModule(VkShaderModule shaderModule) : m_shaderModule(shaderModule) {} #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) ShaderModule& operator=(VkShaderModule shaderModule) { m_shaderModule = shaderModule; return *this; } #endif ShaderModule& operator=( std::nullptr_t ) { m_shaderModule = VK_NULL_HANDLE; return *this; } bool operator==(ShaderModule const &rhs) const { return m_shaderModule == rhs.m_shaderModule; } bool operator!=(ShaderModule const &rhs) const { return m_shaderModule != rhs.m_shaderModule; } bool operator<(ShaderModule const &rhs) const { return m_shaderModule < rhs.m_shaderModule; } VULKAN_HPP_TYPESAFE_EXPLICIT operator VkShaderModule() const { return m_shaderModule; } explicit operator bool() const { return m_shaderModule != VK_NULL_HANDLE; } bool operator!() const { return m_shaderModule == VK_NULL_HANDLE; } private: VkShaderModule m_shaderModule; }; static_assert( sizeof( ShaderModule ) == sizeof( VkShaderModule ), "handle and wrapper have different size!" ); class Pipeline { public: Pipeline() : m_pipeline(VK_NULL_HANDLE) {} Pipeline( std::nullptr_t ) : m_pipeline(VK_NULL_HANDLE) {} VULKAN_HPP_TYPESAFE_EXPLICIT Pipeline(VkPipeline pipeline) : m_pipeline(pipeline) {} #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) Pipeline& operator=(VkPipeline pipeline) { m_pipeline = pipeline; return *this; } #endif Pipeline& operator=( std::nullptr_t ) { m_pipeline = VK_NULL_HANDLE; return *this; } bool operator==(Pipeline const &rhs) const { return m_pipeline == rhs.m_pipeline; } bool operator!=(Pipeline const &rhs) const { return m_pipeline != rhs.m_pipeline; } bool operator<(Pipeline const &rhs) const { return m_pipeline < rhs.m_pipeline; } VULKAN_HPP_TYPESAFE_EXPLICIT operator VkPipeline() const { return m_pipeline; } explicit operator bool() const { return m_pipeline != VK_NULL_HANDLE; } bool operator!() const { return m_pipeline == VK_NULL_HANDLE; } private: VkPipeline m_pipeline; }; static_assert( sizeof( Pipeline ) == sizeof( VkPipeline ), "handle and wrapper have different size!" ); class PipelineLayout { public: PipelineLayout() : m_pipelineLayout(VK_NULL_HANDLE) {} PipelineLayout( std::nullptr_t ) : m_pipelineLayout(VK_NULL_HANDLE) {} VULKAN_HPP_TYPESAFE_EXPLICIT PipelineLayout(VkPipelineLayout pipelineLayout) : m_pipelineLayout(pipelineLayout) {} #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) PipelineLayout& operator=(VkPipelineLayout pipelineLayout) { m_pipelineLayout = pipelineLayout; return *this; } #endif PipelineLayout& operator=( std::nullptr_t ) { m_pipelineLayout = VK_NULL_HANDLE; return *this; } bool operator==(PipelineLayout const &rhs) const { return m_pipelineLayout == rhs.m_pipelineLayout; } bool operator!=(PipelineLayout const &rhs) const { return m_pipelineLayout != rhs.m_pipelineLayout; } bool operator<(PipelineLayout const &rhs) const { return m_pipelineLayout < rhs.m_pipelineLayout; } VULKAN_HPP_TYPESAFE_EXPLICIT operator VkPipelineLayout() const { return m_pipelineLayout; } explicit operator bool() const { return m_pipelineLayout != VK_NULL_HANDLE; } bool operator!() const { return m_pipelineLayout == VK_NULL_HANDLE; } private: VkPipelineLayout m_pipelineLayout; }; static_assert( sizeof( PipelineLayout ) == sizeof( VkPipelineLayout ), "handle and wrapper have different size!" ); class Sampler { public: Sampler() : m_sampler(VK_NULL_HANDLE) {} Sampler( std::nullptr_t ) : m_sampler(VK_NULL_HANDLE) {} VULKAN_HPP_TYPESAFE_EXPLICIT Sampler(VkSampler sampler) : m_sampler(sampler) {} #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) Sampler& operator=(VkSampler sampler) { m_sampler = sampler; return *this; } #endif Sampler& operator=( std::nullptr_t ) { m_sampler = VK_NULL_HANDLE; return *this; } bool operator==(Sampler const &rhs) const { return m_sampler == rhs.m_sampler; } bool operator!=(Sampler const &rhs) const { return m_sampler != rhs.m_sampler; } bool operator<(Sampler const &rhs) const { return m_sampler < rhs.m_sampler; } VULKAN_HPP_TYPESAFE_EXPLICIT operator VkSampler() const { return m_sampler; } explicit operator bool() const { return m_sampler != VK_NULL_HANDLE; } bool operator!() const { return m_sampler == VK_NULL_HANDLE; } private: VkSampler m_sampler; }; static_assert( sizeof( Sampler ) == sizeof( VkSampler ), "handle and wrapper have different size!" ); class DescriptorSet { public: DescriptorSet() : m_descriptorSet(VK_NULL_HANDLE) {} DescriptorSet( std::nullptr_t ) : m_descriptorSet(VK_NULL_HANDLE) {} VULKAN_HPP_TYPESAFE_EXPLICIT DescriptorSet(VkDescriptorSet descriptorSet) : m_descriptorSet(descriptorSet) {} #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) DescriptorSet& operator=(VkDescriptorSet descriptorSet) { m_descriptorSet = descriptorSet; return *this; } #endif DescriptorSet& operator=( std::nullptr_t ) { m_descriptorSet = VK_NULL_HANDLE; return *this; } bool operator==(DescriptorSet const &rhs) const { return m_descriptorSet == rhs.m_descriptorSet; } bool operator!=(DescriptorSet const &rhs) const { return m_descriptorSet != rhs.m_descriptorSet; } bool operator<(DescriptorSet const &rhs) const { return m_descriptorSet < rhs.m_descriptorSet; } VULKAN_HPP_TYPESAFE_EXPLICIT operator VkDescriptorSet() const { return m_descriptorSet; } explicit operator bool() const { return m_descriptorSet != VK_NULL_HANDLE; } bool operator!() const { return m_descriptorSet == VK_NULL_HANDLE; } private: VkDescriptorSet m_descriptorSet; }; static_assert( sizeof( DescriptorSet ) == sizeof( VkDescriptorSet ), "handle and wrapper have different size!" ); class DescriptorSetLayout { public: DescriptorSetLayout() : m_descriptorSetLayout(VK_NULL_HANDLE) {} DescriptorSetLayout( std::nullptr_t ) : m_descriptorSetLayout(VK_NULL_HANDLE) {} VULKAN_HPP_TYPESAFE_EXPLICIT DescriptorSetLayout(VkDescriptorSetLayout descriptorSetLayout) : m_descriptorSetLayout(descriptorSetLayout) {} #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) DescriptorSetLayout& operator=(VkDescriptorSetLayout descriptorSetLayout) { m_descriptorSetLayout = descriptorSetLayout; return *this; } #endif DescriptorSetLayout& operator=( std::nullptr_t ) { m_descriptorSetLayout = VK_NULL_HANDLE; return *this; } bool operator==(DescriptorSetLayout const &rhs) const { return m_descriptorSetLayout == rhs.m_descriptorSetLayout; } bool operator!=(DescriptorSetLayout const &rhs) const { return m_descriptorSetLayout != rhs.m_descriptorSetLayout; } bool operator<(DescriptorSetLayout const &rhs) const { return m_descriptorSetLayout < rhs.m_descriptorSetLayout; } VULKAN_HPP_TYPESAFE_EXPLICIT operator VkDescriptorSetLayout() const { return m_descriptorSetLayout; } explicit operator bool() const { return m_descriptorSetLayout != VK_NULL_HANDLE; } bool operator!() const { return m_descriptorSetLayout == VK_NULL_HANDLE; } private: VkDescriptorSetLayout m_descriptorSetLayout; }; static_assert( sizeof( DescriptorSetLayout ) == sizeof( VkDescriptorSetLayout ), "handle and wrapper have different size!" ); class DescriptorPool { public: DescriptorPool() : m_descriptorPool(VK_NULL_HANDLE) {} DescriptorPool( std::nullptr_t ) : m_descriptorPool(VK_NULL_HANDLE) {} VULKAN_HPP_TYPESAFE_EXPLICIT DescriptorPool(VkDescriptorPool descriptorPool) : m_descriptorPool(descriptorPool) {} #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) DescriptorPool& operator=(VkDescriptorPool descriptorPool) { m_descriptorPool = descriptorPool; return *this; } #endif DescriptorPool& operator=( std::nullptr_t ) { m_descriptorPool = VK_NULL_HANDLE; return *this; } bool operator==(DescriptorPool const &rhs) const { return m_descriptorPool == rhs.m_descriptorPool; } bool operator!=(DescriptorPool const &rhs) const { return m_descriptorPool != rhs.m_descriptorPool; } bool operator<(DescriptorPool const &rhs) const { return m_descriptorPool < rhs.m_descriptorPool; } VULKAN_HPP_TYPESAFE_EXPLICIT operator VkDescriptorPool() const { return m_descriptorPool; } explicit operator bool() const { return m_descriptorPool != VK_NULL_HANDLE; } bool operator!() const { return m_descriptorPool == VK_NULL_HANDLE; } private: VkDescriptorPool m_descriptorPool; }; static_assert( sizeof( DescriptorPool ) == sizeof( VkDescriptorPool ), "handle and wrapper have different size!" ); class Fence { public: Fence() : m_fence(VK_NULL_HANDLE) {} Fence( std::nullptr_t ) : m_fence(VK_NULL_HANDLE) {} VULKAN_HPP_TYPESAFE_EXPLICIT Fence(VkFence fence) : m_fence(fence) {} #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) Fence& operator=(VkFence fence) { m_fence = fence; return *this; } #endif Fence& operator=( std::nullptr_t ) { m_fence = VK_NULL_HANDLE; return *this; } bool operator==(Fence const &rhs) const { return m_fence == rhs.m_fence; } bool operator!=(Fence const &rhs) const { return m_fence != rhs.m_fence; } bool operator<(Fence const &rhs) const { return m_fence < rhs.m_fence; } VULKAN_HPP_TYPESAFE_EXPLICIT operator VkFence() const { return m_fence; } explicit operator bool() const { return m_fence != VK_NULL_HANDLE; } bool operator!() const { return m_fence == VK_NULL_HANDLE; } private: VkFence m_fence; }; static_assert( sizeof( Fence ) == sizeof( VkFence ), "handle and wrapper have different size!" ); class Semaphore { public: Semaphore() : m_semaphore(VK_NULL_HANDLE) {} Semaphore( std::nullptr_t ) : m_semaphore(VK_NULL_HANDLE) {} VULKAN_HPP_TYPESAFE_EXPLICIT Semaphore(VkSemaphore semaphore) : m_semaphore(semaphore) {} #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) Semaphore& operator=(VkSemaphore semaphore) { m_semaphore = semaphore; return *this; } #endif Semaphore& operator=( std::nullptr_t ) { m_semaphore = VK_NULL_HANDLE; return *this; } bool operator==(Semaphore const &rhs) const { return m_semaphore == rhs.m_semaphore; } bool operator!=(Semaphore const &rhs) const { return m_semaphore != rhs.m_semaphore; } bool operator<(Semaphore const &rhs) const { return m_semaphore < rhs.m_semaphore; } VULKAN_HPP_TYPESAFE_EXPLICIT operator VkSemaphore() const { return m_semaphore; } explicit operator bool() const { return m_semaphore != VK_NULL_HANDLE; } bool operator!() const { return m_semaphore == VK_NULL_HANDLE; } private: VkSemaphore m_semaphore; }; static_assert( sizeof( Semaphore ) == sizeof( VkSemaphore ), "handle and wrapper have different size!" ); class Event { public: Event() : m_event(VK_NULL_HANDLE) {} Event( std::nullptr_t ) : m_event(VK_NULL_HANDLE) {} VULKAN_HPP_TYPESAFE_EXPLICIT Event(VkEvent event) : m_event(event) {} #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) Event& operator=(VkEvent event) { m_event = event; return *this; } #endif Event& operator=( std::nullptr_t ) { m_event = VK_NULL_HANDLE; return *this; } bool operator==(Event const &rhs) const { return m_event == rhs.m_event; } bool operator!=(Event const &rhs) const { return m_event != rhs.m_event; } bool operator<(Event const &rhs) const { return m_event < rhs.m_event; } VULKAN_HPP_TYPESAFE_EXPLICIT operator VkEvent() const { return m_event; } explicit operator bool() const { return m_event != VK_NULL_HANDLE; } bool operator!() const { return m_event == VK_NULL_HANDLE; } private: VkEvent m_event; }; static_assert( sizeof( Event ) == sizeof( VkEvent ), "handle and wrapper have different size!" ); class QueryPool { public: QueryPool() : m_queryPool(VK_NULL_HANDLE) {} QueryPool( std::nullptr_t ) : m_queryPool(VK_NULL_HANDLE) {} VULKAN_HPP_TYPESAFE_EXPLICIT QueryPool(VkQueryPool queryPool) : m_queryPool(queryPool) {} #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) QueryPool& operator=(VkQueryPool queryPool) { m_queryPool = queryPool; return *this; } #endif QueryPool& operator=( std::nullptr_t ) { m_queryPool = VK_NULL_HANDLE; return *this; } bool operator==(QueryPool const &rhs) const { return m_queryPool == rhs.m_queryPool; } bool operator!=(QueryPool const &rhs) const { return m_queryPool != rhs.m_queryPool; } bool operator<(QueryPool const &rhs) const { return m_queryPool < rhs.m_queryPool; } VULKAN_HPP_TYPESAFE_EXPLICIT operator VkQueryPool() const { return m_queryPool; } explicit operator bool() const { return m_queryPool != VK_NULL_HANDLE; } bool operator!() const { return m_queryPool == VK_NULL_HANDLE; } private: VkQueryPool m_queryPool; }; static_assert( sizeof( QueryPool ) == sizeof( VkQueryPool ), "handle and wrapper have different size!" ); class Framebuffer { public: Framebuffer() : m_framebuffer(VK_NULL_HANDLE) {} Framebuffer( std::nullptr_t ) : m_framebuffer(VK_NULL_HANDLE) {} VULKAN_HPP_TYPESAFE_EXPLICIT Framebuffer(VkFramebuffer framebuffer) : m_framebuffer(framebuffer) {} #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) Framebuffer& operator=(VkFramebuffer framebuffer) { m_framebuffer = framebuffer; return *this; } #endif Framebuffer& operator=( std::nullptr_t ) { m_framebuffer = VK_NULL_HANDLE; return *this; } bool operator==(Framebuffer const &rhs) const { return m_framebuffer == rhs.m_framebuffer; } bool operator!=(Framebuffer const &rhs) const { return m_framebuffer != rhs.m_framebuffer; } bool operator<(Framebuffer const &rhs) const { return m_framebuffer < rhs.m_framebuffer; } VULKAN_HPP_TYPESAFE_EXPLICIT operator VkFramebuffer() const { return m_framebuffer; } explicit operator bool() const { return m_framebuffer != VK_NULL_HANDLE; } bool operator!() const { return m_framebuffer == VK_NULL_HANDLE; } private: VkFramebuffer m_framebuffer; }; static_assert( sizeof( Framebuffer ) == sizeof( VkFramebuffer ), "handle and wrapper have different size!" ); class RenderPass { public: RenderPass() : m_renderPass(VK_NULL_HANDLE) {} RenderPass( std::nullptr_t ) : m_renderPass(VK_NULL_HANDLE) {} VULKAN_HPP_TYPESAFE_EXPLICIT RenderPass(VkRenderPass renderPass) : m_renderPass(renderPass) {} #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) RenderPass& operator=(VkRenderPass renderPass) { m_renderPass = renderPass; return *this; } #endif RenderPass& operator=( std::nullptr_t ) { m_renderPass = VK_NULL_HANDLE; return *this; } bool operator==(RenderPass const &rhs) const { return m_renderPass == rhs.m_renderPass; } bool operator!=(RenderPass const &rhs) const { return m_renderPass != rhs.m_renderPass; } bool operator<(RenderPass const &rhs) const { return m_renderPass < rhs.m_renderPass; } VULKAN_HPP_TYPESAFE_EXPLICIT operator VkRenderPass() const { return m_renderPass; } explicit operator bool() const { return m_renderPass != VK_NULL_HANDLE; } bool operator!() const { return m_renderPass == VK_NULL_HANDLE; } private: VkRenderPass m_renderPass; }; static_assert( sizeof( RenderPass ) == sizeof( VkRenderPass ), "handle and wrapper have different size!" ); class PipelineCache { public: PipelineCache() : m_pipelineCache(VK_NULL_HANDLE) {} PipelineCache( std::nullptr_t ) : m_pipelineCache(VK_NULL_HANDLE) {} VULKAN_HPP_TYPESAFE_EXPLICIT PipelineCache(VkPipelineCache pipelineCache) : m_pipelineCache(pipelineCache) {} #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) PipelineCache& operator=(VkPipelineCache pipelineCache) { m_pipelineCache = pipelineCache; return *this; } #endif PipelineCache& operator=( std::nullptr_t ) { m_pipelineCache = VK_NULL_HANDLE; return *this; } bool operator==(PipelineCache const &rhs) const { return m_pipelineCache == rhs.m_pipelineCache; } bool operator!=(PipelineCache const &rhs) const { return m_pipelineCache != rhs.m_pipelineCache; } bool operator<(PipelineCache const &rhs) const { return m_pipelineCache < rhs.m_pipelineCache; } VULKAN_HPP_TYPESAFE_EXPLICIT operator VkPipelineCache() const { return m_pipelineCache; } explicit operator bool() const { return m_pipelineCache != VK_NULL_HANDLE; } bool operator!() const { return m_pipelineCache == VK_NULL_HANDLE; } private: VkPipelineCache m_pipelineCache; }; static_assert( sizeof( PipelineCache ) == sizeof( VkPipelineCache ), "handle and wrapper have different size!" ); class ObjectTableNVX { public: ObjectTableNVX() : m_objectTableNVX(VK_NULL_HANDLE) {} ObjectTableNVX( std::nullptr_t ) : m_objectTableNVX(VK_NULL_HANDLE) {} VULKAN_HPP_TYPESAFE_EXPLICIT ObjectTableNVX(VkObjectTableNVX objectTableNVX) : m_objectTableNVX(objectTableNVX) {} #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) ObjectTableNVX& operator=(VkObjectTableNVX objectTableNVX) { m_objectTableNVX = objectTableNVX; return *this; } #endif ObjectTableNVX& operator=( std::nullptr_t ) { m_objectTableNVX = VK_NULL_HANDLE; return *this; } bool operator==(ObjectTableNVX const &rhs) const { return m_objectTableNVX == rhs.m_objectTableNVX; } bool operator!=(ObjectTableNVX const &rhs) const { return m_objectTableNVX != rhs.m_objectTableNVX; } bool operator<(ObjectTableNVX const &rhs) const { return m_objectTableNVX < rhs.m_objectTableNVX; } VULKAN_HPP_TYPESAFE_EXPLICIT operator VkObjectTableNVX() const { return m_objectTableNVX; } explicit operator bool() const { return m_objectTableNVX != VK_NULL_HANDLE; } bool operator!() const { return m_objectTableNVX == VK_NULL_HANDLE; } private: VkObjectTableNVX m_objectTableNVX; }; static_assert( sizeof( ObjectTableNVX ) == sizeof( VkObjectTableNVX ), "handle and wrapper have different size!" ); class IndirectCommandsLayoutNVX { public: IndirectCommandsLayoutNVX() : m_indirectCommandsLayoutNVX(VK_NULL_HANDLE) {} IndirectCommandsLayoutNVX( std::nullptr_t ) : m_indirectCommandsLayoutNVX(VK_NULL_HANDLE) {} VULKAN_HPP_TYPESAFE_EXPLICIT IndirectCommandsLayoutNVX(VkIndirectCommandsLayoutNVX indirectCommandsLayoutNVX) : m_indirectCommandsLayoutNVX(indirectCommandsLayoutNVX) {} #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) IndirectCommandsLayoutNVX& operator=(VkIndirectCommandsLayoutNVX indirectCommandsLayoutNVX) { m_indirectCommandsLayoutNVX = indirectCommandsLayoutNVX; return *this; } #endif IndirectCommandsLayoutNVX& operator=( std::nullptr_t ) { m_indirectCommandsLayoutNVX = VK_NULL_HANDLE; return *this; } bool operator==(IndirectCommandsLayoutNVX const &rhs) const { return m_indirectCommandsLayoutNVX == rhs.m_indirectCommandsLayoutNVX; } bool operator!=(IndirectCommandsLayoutNVX const &rhs) const { return m_indirectCommandsLayoutNVX != rhs.m_indirectCommandsLayoutNVX; } bool operator<(IndirectCommandsLayoutNVX const &rhs) const { return m_indirectCommandsLayoutNVX < rhs.m_indirectCommandsLayoutNVX; } VULKAN_HPP_TYPESAFE_EXPLICIT operator VkIndirectCommandsLayoutNVX() const { return m_indirectCommandsLayoutNVX; } explicit operator bool() const { return m_indirectCommandsLayoutNVX != VK_NULL_HANDLE; } bool operator!() const { return m_indirectCommandsLayoutNVX == VK_NULL_HANDLE; } private: VkIndirectCommandsLayoutNVX m_indirectCommandsLayoutNVX; }; static_assert( sizeof( IndirectCommandsLayoutNVX ) == sizeof( VkIndirectCommandsLayoutNVX ), "handle and wrapper have different size!" ); class DescriptorUpdateTemplateKHR { public: DescriptorUpdateTemplateKHR() : m_descriptorUpdateTemplateKHR(VK_NULL_HANDLE) {} DescriptorUpdateTemplateKHR( std::nullptr_t ) : m_descriptorUpdateTemplateKHR(VK_NULL_HANDLE) {} VULKAN_HPP_TYPESAFE_EXPLICIT DescriptorUpdateTemplateKHR(VkDescriptorUpdateTemplateKHR descriptorUpdateTemplateKHR) : m_descriptorUpdateTemplateKHR(descriptorUpdateTemplateKHR) {} #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) DescriptorUpdateTemplateKHR& operator=(VkDescriptorUpdateTemplateKHR descriptorUpdateTemplateKHR) { m_descriptorUpdateTemplateKHR = descriptorUpdateTemplateKHR; return *this; } #endif DescriptorUpdateTemplateKHR& operator=( std::nullptr_t ) { m_descriptorUpdateTemplateKHR = VK_NULL_HANDLE; return *this; } bool operator==(DescriptorUpdateTemplateKHR const &rhs) const { return m_descriptorUpdateTemplateKHR == rhs.m_descriptorUpdateTemplateKHR; } bool operator!=(DescriptorUpdateTemplateKHR const &rhs) const { return m_descriptorUpdateTemplateKHR != rhs.m_descriptorUpdateTemplateKHR; } bool operator<(DescriptorUpdateTemplateKHR const &rhs) const { return m_descriptorUpdateTemplateKHR < rhs.m_descriptorUpdateTemplateKHR; } VULKAN_HPP_TYPESAFE_EXPLICIT operator VkDescriptorUpdateTemplateKHR() const { return m_descriptorUpdateTemplateKHR; } explicit operator bool() const { return m_descriptorUpdateTemplateKHR != VK_NULL_HANDLE; } bool operator!() const { return m_descriptorUpdateTemplateKHR == VK_NULL_HANDLE; } private: VkDescriptorUpdateTemplateKHR m_descriptorUpdateTemplateKHR; }; static_assert( sizeof( DescriptorUpdateTemplateKHR ) == sizeof( VkDescriptorUpdateTemplateKHR ), "handle and wrapper have different size!" ); class DisplayKHR { public: DisplayKHR() : m_displayKHR(VK_NULL_HANDLE) {} DisplayKHR( std::nullptr_t ) : m_displayKHR(VK_NULL_HANDLE) {} VULKAN_HPP_TYPESAFE_EXPLICIT DisplayKHR(VkDisplayKHR displayKHR) : m_displayKHR(displayKHR) {} #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) DisplayKHR& operator=(VkDisplayKHR displayKHR) { m_displayKHR = displayKHR; return *this; } #endif DisplayKHR& operator=( std::nullptr_t ) { m_displayKHR = VK_NULL_HANDLE; return *this; } bool operator==(DisplayKHR const &rhs) const { return m_displayKHR == rhs.m_displayKHR; } bool operator!=(DisplayKHR const &rhs) const { return m_displayKHR != rhs.m_displayKHR; } bool operator<(DisplayKHR const &rhs) const { return m_displayKHR < rhs.m_displayKHR; } VULKAN_HPP_TYPESAFE_EXPLICIT operator VkDisplayKHR() const { return m_displayKHR; } explicit operator bool() const { return m_displayKHR != VK_NULL_HANDLE; } bool operator!() const { return m_displayKHR == VK_NULL_HANDLE; } private: VkDisplayKHR m_displayKHR; }; static_assert( sizeof( DisplayKHR ) == sizeof( VkDisplayKHR ), "handle and wrapper have different size!" ); class DisplayModeKHR { public: DisplayModeKHR() : m_displayModeKHR(VK_NULL_HANDLE) {} DisplayModeKHR( std::nullptr_t ) : m_displayModeKHR(VK_NULL_HANDLE) {} VULKAN_HPP_TYPESAFE_EXPLICIT DisplayModeKHR(VkDisplayModeKHR displayModeKHR) : m_displayModeKHR(displayModeKHR) {} #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) DisplayModeKHR& operator=(VkDisplayModeKHR displayModeKHR) { m_displayModeKHR = displayModeKHR; return *this; } #endif DisplayModeKHR& operator=( std::nullptr_t ) { m_displayModeKHR = VK_NULL_HANDLE; return *this; } bool operator==(DisplayModeKHR const &rhs) const { return m_displayModeKHR == rhs.m_displayModeKHR; } bool operator!=(DisplayModeKHR const &rhs) const { return m_displayModeKHR != rhs.m_displayModeKHR; } bool operator<(DisplayModeKHR const &rhs) const { return m_displayModeKHR < rhs.m_displayModeKHR; } VULKAN_HPP_TYPESAFE_EXPLICIT operator VkDisplayModeKHR() const { return m_displayModeKHR; } explicit operator bool() const { return m_displayModeKHR != VK_NULL_HANDLE; } bool operator!() const { return m_displayModeKHR == VK_NULL_HANDLE; } private: VkDisplayModeKHR m_displayModeKHR; }; static_assert( sizeof( DisplayModeKHR ) == sizeof( VkDisplayModeKHR ), "handle and wrapper have different size!" ); class SurfaceKHR { public: SurfaceKHR() : m_surfaceKHR(VK_NULL_HANDLE) {} SurfaceKHR( std::nullptr_t ) : m_surfaceKHR(VK_NULL_HANDLE) {} VULKAN_HPP_TYPESAFE_EXPLICIT SurfaceKHR(VkSurfaceKHR surfaceKHR) : m_surfaceKHR(surfaceKHR) {} #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) SurfaceKHR& operator=(VkSurfaceKHR surfaceKHR) { m_surfaceKHR = surfaceKHR; return *this; } #endif SurfaceKHR& operator=( std::nullptr_t ) { m_surfaceKHR = VK_NULL_HANDLE; return *this; } bool operator==(SurfaceKHR const &rhs) const { return m_surfaceKHR == rhs.m_surfaceKHR; } bool operator!=(SurfaceKHR const &rhs) const { return m_surfaceKHR != rhs.m_surfaceKHR; } bool operator<(SurfaceKHR const &rhs) const { return m_surfaceKHR < rhs.m_surfaceKHR; } VULKAN_HPP_TYPESAFE_EXPLICIT operator VkSurfaceKHR() const { return m_surfaceKHR; } explicit operator bool() const { return m_surfaceKHR != VK_NULL_HANDLE; } bool operator!() const { return m_surfaceKHR == VK_NULL_HANDLE; } private: VkSurfaceKHR m_surfaceKHR; }; static_assert( sizeof( SurfaceKHR ) == sizeof( VkSurfaceKHR ), "handle and wrapper have different size!" ); class SwapchainKHR { public: SwapchainKHR() : m_swapchainKHR(VK_NULL_HANDLE) {} SwapchainKHR( std::nullptr_t ) : m_swapchainKHR(VK_NULL_HANDLE) {} VULKAN_HPP_TYPESAFE_EXPLICIT SwapchainKHR(VkSwapchainKHR swapchainKHR) : m_swapchainKHR(swapchainKHR) {} #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) SwapchainKHR& operator=(VkSwapchainKHR swapchainKHR) { m_swapchainKHR = swapchainKHR; return *this; } #endif SwapchainKHR& operator=( std::nullptr_t ) { m_swapchainKHR = VK_NULL_HANDLE; return *this; } bool operator==(SwapchainKHR const &rhs) const { return m_swapchainKHR == rhs.m_swapchainKHR; } bool operator!=(SwapchainKHR const &rhs) const { return m_swapchainKHR != rhs.m_swapchainKHR; } bool operator<(SwapchainKHR const &rhs) const { return m_swapchainKHR < rhs.m_swapchainKHR; } VULKAN_HPP_TYPESAFE_EXPLICIT operator VkSwapchainKHR() const { return m_swapchainKHR; } explicit operator bool() const { return m_swapchainKHR != VK_NULL_HANDLE; } bool operator!() const { return m_swapchainKHR == VK_NULL_HANDLE; } private: VkSwapchainKHR m_swapchainKHR; }; static_assert( sizeof( SwapchainKHR ) == sizeof( VkSwapchainKHR ), "handle and wrapper have different size!" ); class DebugReportCallbackEXT { public: DebugReportCallbackEXT() : m_debugReportCallbackEXT(VK_NULL_HANDLE) {} DebugReportCallbackEXT( std::nullptr_t ) : m_debugReportCallbackEXT(VK_NULL_HANDLE) {} VULKAN_HPP_TYPESAFE_EXPLICIT DebugReportCallbackEXT(VkDebugReportCallbackEXT debugReportCallbackEXT) : m_debugReportCallbackEXT(debugReportCallbackEXT) {} #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) DebugReportCallbackEXT& operator=(VkDebugReportCallbackEXT debugReportCallbackEXT) { m_debugReportCallbackEXT = debugReportCallbackEXT; return *this; } #endif DebugReportCallbackEXT& operator=( std::nullptr_t ) { m_debugReportCallbackEXT = VK_NULL_HANDLE; return *this; } bool operator==(DebugReportCallbackEXT const &rhs) const { return m_debugReportCallbackEXT == rhs.m_debugReportCallbackEXT; } bool operator!=(DebugReportCallbackEXT const &rhs) const { return m_debugReportCallbackEXT != rhs.m_debugReportCallbackEXT; } bool operator<(DebugReportCallbackEXT const &rhs) const { return m_debugReportCallbackEXT < rhs.m_debugReportCallbackEXT; } VULKAN_HPP_TYPESAFE_EXPLICIT operator VkDebugReportCallbackEXT() const { return m_debugReportCallbackEXT; } explicit operator bool() const { return m_debugReportCallbackEXT != VK_NULL_HANDLE; } bool operator!() const { return m_debugReportCallbackEXT == VK_NULL_HANDLE; } private: VkDebugReportCallbackEXT m_debugReportCallbackEXT; }; static_assert( sizeof( DebugReportCallbackEXT ) == sizeof( VkDebugReportCallbackEXT ), "handle and wrapper have different size!" ); struct Offset2D { Offset2D( int32_t x_ = 0, int32_t y_ = 0 ) : x( x_ ) , y( y_ ) { } Offset2D( VkOffset2D const & rhs ) { memcpy( this, &rhs, sizeof(Offset2D) ); } Offset2D& operator=( VkOffset2D const & rhs ) { memcpy( this, &rhs, sizeof(Offset2D) ); return *this; } Offset2D& setX( int32_t x_ ) { x = x_; return *this; } Offset2D& setY( int32_t y_ ) { y = y_; return *this; } operator const VkOffset2D&() const { return *reinterpret_cast(this); } bool operator==( Offset2D const& rhs ) const { return ( x == rhs.x ) && ( y == rhs.y ); } bool operator!=( Offset2D const& rhs ) const { return !operator==( rhs ); } int32_t x; int32_t y; }; static_assert( sizeof( Offset2D ) == sizeof( VkOffset2D ), "struct and wrapper have different size!" ); struct Offset3D { Offset3D( int32_t x_ = 0, int32_t y_ = 0, int32_t z_ = 0 ) : x( x_ ) , y( y_ ) , z( z_ ) { } Offset3D( VkOffset3D const & rhs ) { memcpy( this, &rhs, sizeof(Offset3D) ); } Offset3D& operator=( VkOffset3D const & rhs ) { memcpy( this, &rhs, sizeof(Offset3D) ); return *this; } Offset3D& setX( int32_t x_ ) { x = x_; return *this; } Offset3D& setY( int32_t y_ ) { y = y_; return *this; } Offset3D& setZ( int32_t z_ ) { z = z_; return *this; } operator const VkOffset3D&() const { return *reinterpret_cast(this); } bool operator==( Offset3D const& rhs ) const { return ( x == rhs.x ) && ( y == rhs.y ) && ( z == rhs.z ); } bool operator!=( Offset3D const& rhs ) const { return !operator==( rhs ); } int32_t x; int32_t y; int32_t z; }; static_assert( sizeof( Offset3D ) == sizeof( VkOffset3D ), "struct and wrapper have different size!" ); struct Extent2D { Extent2D( uint32_t width_ = 0, uint32_t height_ = 0 ) : width( width_ ) , height( height_ ) { } Extent2D( VkExtent2D const & rhs ) { memcpy( this, &rhs, sizeof(Extent2D) ); } Extent2D& operator=( VkExtent2D const & rhs ) { memcpy( this, &rhs, sizeof(Extent2D) ); return *this; } Extent2D& setWidth( uint32_t width_ ) { width = width_; return *this; } Extent2D& setHeight( uint32_t height_ ) { height = height_; return *this; } operator const VkExtent2D&() const { return *reinterpret_cast(this); } bool operator==( Extent2D const& rhs ) const { return ( width == rhs.width ) && ( height == rhs.height ); } bool operator!=( Extent2D const& rhs ) const { return !operator==( rhs ); } uint32_t width; uint32_t height; }; static_assert( sizeof( Extent2D ) == sizeof( VkExtent2D ), "struct and wrapper have different size!" ); struct Extent3D { Extent3D( uint32_t width_ = 0, uint32_t height_ = 0, uint32_t depth_ = 0 ) : width( width_ ) , height( height_ ) , depth( depth_ ) { } Extent3D( VkExtent3D const & rhs ) { memcpy( this, &rhs, sizeof(Extent3D) ); } Extent3D& operator=( VkExtent3D const & rhs ) { memcpy( this, &rhs, sizeof(Extent3D) ); return *this; } Extent3D& setWidth( uint32_t width_ ) { width = width_; return *this; } Extent3D& setHeight( uint32_t height_ ) { height = height_; return *this; } Extent3D& setDepth( uint32_t depth_ ) { depth = depth_; return *this; } operator const VkExtent3D&() const { return *reinterpret_cast(this); } bool operator==( Extent3D const& rhs ) const { return ( width == rhs.width ) && ( height == rhs.height ) && ( depth == rhs.depth ); } bool operator!=( Extent3D const& rhs ) const { return !operator==( rhs ); } uint32_t width; uint32_t height; uint32_t depth; }; static_assert( sizeof( Extent3D ) == sizeof( VkExtent3D ), "struct and wrapper have different size!" ); struct Viewport { Viewport( float x_ = 0, float y_ = 0, float width_ = 0, float height_ = 0, float minDepth_ = 0, float maxDepth_ = 0 ) : x( x_ ) , y( y_ ) , width( width_ ) , height( height_ ) , minDepth( minDepth_ ) , maxDepth( maxDepth_ ) { } Viewport( VkViewport const & rhs ) { memcpy( this, &rhs, sizeof(Viewport) ); } Viewport& operator=( VkViewport const & rhs ) { memcpy( this, &rhs, sizeof(Viewport) ); return *this; } Viewport& setX( float x_ ) { x = x_; return *this; } Viewport& setY( float y_ ) { y = y_; return *this; } Viewport& setWidth( float width_ ) { width = width_; return *this; } Viewport& setHeight( float height_ ) { height = height_; return *this; } Viewport& setMinDepth( float minDepth_ ) { minDepth = minDepth_; return *this; } Viewport& setMaxDepth( float maxDepth_ ) { maxDepth = maxDepth_; return *this; } operator const VkViewport&() const { return *reinterpret_cast(this); } bool operator==( Viewport const& rhs ) const { return ( x == rhs.x ) && ( y == rhs.y ) && ( width == rhs.width ) && ( height == rhs.height ) && ( minDepth == rhs.minDepth ) && ( maxDepth == rhs.maxDepth ); } bool operator!=( Viewport const& rhs ) const { return !operator==( rhs ); } float x; float y; float width; float height; float minDepth; float maxDepth; }; static_assert( sizeof( Viewport ) == sizeof( VkViewport ), "struct and wrapper have different size!" ); struct Rect2D { Rect2D( Offset2D offset_ = Offset2D(), Extent2D extent_ = Extent2D() ) : offset( offset_ ) , extent( extent_ ) { } Rect2D( VkRect2D const & rhs ) { memcpy( this, &rhs, sizeof(Rect2D) ); } Rect2D& operator=( VkRect2D const & rhs ) { memcpy( this, &rhs, sizeof(Rect2D) ); return *this; } Rect2D& setOffset( Offset2D offset_ ) { offset = offset_; return *this; } Rect2D& setExtent( Extent2D extent_ ) { extent = extent_; return *this; } operator const VkRect2D&() const { return *reinterpret_cast(this); } bool operator==( Rect2D const& rhs ) const { return ( offset == rhs.offset ) && ( extent == rhs.extent ); } bool operator!=( Rect2D const& rhs ) const { return !operator==( rhs ); } Offset2D offset; Extent2D extent; }; static_assert( sizeof( Rect2D ) == sizeof( VkRect2D ), "struct and wrapper have different size!" ); struct ClearRect { ClearRect( Rect2D rect_ = Rect2D(), uint32_t baseArrayLayer_ = 0, uint32_t layerCount_ = 0 ) : rect( rect_ ) , baseArrayLayer( baseArrayLayer_ ) , layerCount( layerCount_ ) { } ClearRect( VkClearRect const & rhs ) { memcpy( this, &rhs, sizeof(ClearRect) ); } ClearRect& operator=( VkClearRect const & rhs ) { memcpy( this, &rhs, sizeof(ClearRect) ); return *this; } ClearRect& setRect( Rect2D rect_ ) { rect = rect_; return *this; } ClearRect& setBaseArrayLayer( uint32_t baseArrayLayer_ ) { baseArrayLayer = baseArrayLayer_; return *this; } ClearRect& setLayerCount( uint32_t layerCount_ ) { layerCount = layerCount_; return *this; } operator const VkClearRect&() const { return *reinterpret_cast(this); } bool operator==( ClearRect const& rhs ) const { return ( rect == rhs.rect ) && ( baseArrayLayer == rhs.baseArrayLayer ) && ( layerCount == rhs.layerCount ); } bool operator!=( ClearRect const& rhs ) const { return !operator==( rhs ); } Rect2D rect; uint32_t baseArrayLayer; uint32_t layerCount; }; static_assert( sizeof( ClearRect ) == sizeof( VkClearRect ), "struct and wrapper have different size!" ); struct ExtensionProperties { operator const VkExtensionProperties&() const { return *reinterpret_cast(this); } bool operator==( ExtensionProperties const& rhs ) const { return ( memcmp( extensionName, rhs.extensionName, VK_MAX_EXTENSION_NAME_SIZE * sizeof( char ) ) == 0 ) && ( specVersion == rhs.specVersion ); } bool operator!=( ExtensionProperties const& rhs ) const { return !operator==( rhs ); } char extensionName[VK_MAX_EXTENSION_NAME_SIZE]; uint32_t specVersion; }; static_assert( sizeof( ExtensionProperties ) == sizeof( VkExtensionProperties ), "struct and wrapper have different size!" ); struct LayerProperties { operator const VkLayerProperties&() const { return *reinterpret_cast(this); } bool operator==( LayerProperties const& rhs ) const { return ( memcmp( layerName, rhs.layerName, VK_MAX_EXTENSION_NAME_SIZE * sizeof( char ) ) == 0 ) && ( specVersion == rhs.specVersion ) && ( implementationVersion == rhs.implementationVersion ) && ( memcmp( description, rhs.description, VK_MAX_DESCRIPTION_SIZE * sizeof( char ) ) == 0 ); } bool operator!=( LayerProperties const& rhs ) const { return !operator==( rhs ); } char layerName[VK_MAX_EXTENSION_NAME_SIZE]; uint32_t specVersion; uint32_t implementationVersion; char description[VK_MAX_DESCRIPTION_SIZE]; }; static_assert( sizeof( LayerProperties ) == sizeof( VkLayerProperties ), "struct and wrapper have different size!" ); struct AllocationCallbacks { AllocationCallbacks( void* pUserData_ = nullptr, PFN_vkAllocationFunction pfnAllocation_ = nullptr, PFN_vkReallocationFunction pfnReallocation_ = nullptr, PFN_vkFreeFunction pfnFree_ = nullptr, PFN_vkInternalAllocationNotification pfnInternalAllocation_ = nullptr, PFN_vkInternalFreeNotification pfnInternalFree_ = nullptr ) : pUserData( pUserData_ ) , pfnAllocation( pfnAllocation_ ) , pfnReallocation( pfnReallocation_ ) , pfnFree( pfnFree_ ) , pfnInternalAllocation( pfnInternalAllocation_ ) , pfnInternalFree( pfnInternalFree_ ) { } AllocationCallbacks( VkAllocationCallbacks const & rhs ) { memcpy( this, &rhs, sizeof(AllocationCallbacks) ); } AllocationCallbacks& operator=( VkAllocationCallbacks const & rhs ) { memcpy( this, &rhs, sizeof(AllocationCallbacks) ); return *this; } AllocationCallbacks& setPUserData( void* pUserData_ ) { pUserData = pUserData_; return *this; } AllocationCallbacks& setPfnAllocation( PFN_vkAllocationFunction pfnAllocation_ ) { pfnAllocation = pfnAllocation_; return *this; } AllocationCallbacks& setPfnReallocation( PFN_vkReallocationFunction pfnReallocation_ ) { pfnReallocation = pfnReallocation_; return *this; } AllocationCallbacks& setPfnFree( PFN_vkFreeFunction pfnFree_ ) { pfnFree = pfnFree_; return *this; } AllocationCallbacks& setPfnInternalAllocation( PFN_vkInternalAllocationNotification pfnInternalAllocation_ ) { pfnInternalAllocation = pfnInternalAllocation_; return *this; } AllocationCallbacks& setPfnInternalFree( PFN_vkInternalFreeNotification pfnInternalFree_ ) { pfnInternalFree = pfnInternalFree_; return *this; } operator const VkAllocationCallbacks&() const { return *reinterpret_cast(this); } bool operator==( AllocationCallbacks const& rhs ) const { return ( pUserData == rhs.pUserData ) && ( pfnAllocation == rhs.pfnAllocation ) && ( pfnReallocation == rhs.pfnReallocation ) && ( pfnFree == rhs.pfnFree ) && ( pfnInternalAllocation == rhs.pfnInternalAllocation ) && ( pfnInternalFree == rhs.pfnInternalFree ); } bool operator!=( AllocationCallbacks const& rhs ) const { return !operator==( rhs ); } void* pUserData; PFN_vkAllocationFunction pfnAllocation; PFN_vkReallocationFunction pfnReallocation; PFN_vkFreeFunction pfnFree; PFN_vkInternalAllocationNotification pfnInternalAllocation; PFN_vkInternalFreeNotification pfnInternalFree; }; static_assert( sizeof( AllocationCallbacks ) == sizeof( VkAllocationCallbacks ), "struct and wrapper have different size!" ); struct MemoryRequirements { operator const VkMemoryRequirements&() const { return *reinterpret_cast(this); } bool operator==( MemoryRequirements const& rhs ) const { return ( size == rhs.size ) && ( alignment == rhs.alignment ) && ( memoryTypeBits == rhs.memoryTypeBits ); } bool operator!=( MemoryRequirements const& rhs ) const { return !operator==( rhs ); } DeviceSize size; DeviceSize alignment; uint32_t memoryTypeBits; }; static_assert( sizeof( MemoryRequirements ) == sizeof( VkMemoryRequirements ), "struct and wrapper have different size!" ); struct DescriptorBufferInfo { DescriptorBufferInfo( Buffer buffer_ = Buffer(), DeviceSize offset_ = 0, DeviceSize range_ = 0 ) : buffer( buffer_ ) , offset( offset_ ) , range( range_ ) { } DescriptorBufferInfo( VkDescriptorBufferInfo const & rhs ) { memcpy( this, &rhs, sizeof(DescriptorBufferInfo) ); } DescriptorBufferInfo& operator=( VkDescriptorBufferInfo const & rhs ) { memcpy( this, &rhs, sizeof(DescriptorBufferInfo) ); return *this; } DescriptorBufferInfo& setBuffer( Buffer buffer_ ) { buffer = buffer_; return *this; } DescriptorBufferInfo& setOffset( DeviceSize offset_ ) { offset = offset_; return *this; } DescriptorBufferInfo& setRange( DeviceSize range_ ) { range = range_; return *this; } operator const VkDescriptorBufferInfo&() const { return *reinterpret_cast(this); } bool operator==( DescriptorBufferInfo const& rhs ) const { return ( buffer == rhs.buffer ) && ( offset == rhs.offset ) && ( range == rhs.range ); } bool operator!=( DescriptorBufferInfo const& rhs ) const { return !operator==( rhs ); } Buffer buffer; DeviceSize offset; DeviceSize range; }; static_assert( sizeof( DescriptorBufferInfo ) == sizeof( VkDescriptorBufferInfo ), "struct and wrapper have different size!" ); struct SubresourceLayout { operator const VkSubresourceLayout&() const { return *reinterpret_cast(this); } bool operator==( SubresourceLayout const& rhs ) const { return ( offset == rhs.offset ) && ( size == rhs.size ) && ( rowPitch == rhs.rowPitch ) && ( arrayPitch == rhs.arrayPitch ) && ( depthPitch == rhs.depthPitch ); } bool operator!=( SubresourceLayout const& rhs ) const { return !operator==( rhs ); } DeviceSize offset; DeviceSize size; DeviceSize rowPitch; DeviceSize arrayPitch; DeviceSize depthPitch; }; static_assert( sizeof( SubresourceLayout ) == sizeof( VkSubresourceLayout ), "struct and wrapper have different size!" ); struct BufferCopy { BufferCopy( DeviceSize srcOffset_ = 0, DeviceSize dstOffset_ = 0, DeviceSize size_ = 0 ) : srcOffset( srcOffset_ ) , dstOffset( dstOffset_ ) , size( size_ ) { } BufferCopy( VkBufferCopy const & rhs ) { memcpy( this, &rhs, sizeof(BufferCopy) ); } BufferCopy& operator=( VkBufferCopy const & rhs ) { memcpy( this, &rhs, sizeof(BufferCopy) ); return *this; } BufferCopy& setSrcOffset( DeviceSize srcOffset_ ) { srcOffset = srcOffset_; return *this; } BufferCopy& setDstOffset( DeviceSize dstOffset_ ) { dstOffset = dstOffset_; return *this; } BufferCopy& setSize( DeviceSize size_ ) { size = size_; return *this; } operator const VkBufferCopy&() const { return *reinterpret_cast(this); } bool operator==( BufferCopy const& rhs ) const { return ( srcOffset == rhs.srcOffset ) && ( dstOffset == rhs.dstOffset ) && ( size == rhs.size ); } bool operator!=( BufferCopy const& rhs ) const { return !operator==( rhs ); } DeviceSize srcOffset; DeviceSize dstOffset; DeviceSize size; }; static_assert( sizeof( BufferCopy ) == sizeof( VkBufferCopy ), "struct and wrapper have different size!" ); struct SpecializationMapEntry { SpecializationMapEntry( uint32_t constantID_ = 0, uint32_t offset_ = 0, size_t size_ = 0 ) : constantID( constantID_ ) , offset( offset_ ) , size( size_ ) { } SpecializationMapEntry( VkSpecializationMapEntry const & rhs ) { memcpy( this, &rhs, sizeof(SpecializationMapEntry) ); } SpecializationMapEntry& operator=( VkSpecializationMapEntry const & rhs ) { memcpy( this, &rhs, sizeof(SpecializationMapEntry) ); return *this; } SpecializationMapEntry& setConstantID( uint32_t constantID_ ) { constantID = constantID_; return *this; } SpecializationMapEntry& setOffset( uint32_t offset_ ) { offset = offset_; return *this; } SpecializationMapEntry& setSize( size_t size_ ) { size = size_; return *this; } operator const VkSpecializationMapEntry&() const { return *reinterpret_cast(this); } bool operator==( SpecializationMapEntry const& rhs ) const { return ( constantID == rhs.constantID ) && ( offset == rhs.offset ) && ( size == rhs.size ); } bool operator!=( SpecializationMapEntry const& rhs ) const { return !operator==( rhs ); } uint32_t constantID; uint32_t offset; size_t size; }; static_assert( sizeof( SpecializationMapEntry ) == sizeof( VkSpecializationMapEntry ), "struct and wrapper have different size!" ); struct SpecializationInfo { SpecializationInfo( uint32_t mapEntryCount_ = 0, const SpecializationMapEntry* pMapEntries_ = nullptr, size_t dataSize_ = 0, const void* pData_ = nullptr ) : mapEntryCount( mapEntryCount_ ) , pMapEntries( pMapEntries_ ) , dataSize( dataSize_ ) , pData( pData_ ) { } SpecializationInfo( VkSpecializationInfo const & rhs ) { memcpy( this, &rhs, sizeof(SpecializationInfo) ); } SpecializationInfo& operator=( VkSpecializationInfo const & rhs ) { memcpy( this, &rhs, sizeof(SpecializationInfo) ); return *this; } SpecializationInfo& setMapEntryCount( uint32_t mapEntryCount_ ) { mapEntryCount = mapEntryCount_; return *this; } SpecializationInfo& setPMapEntries( const SpecializationMapEntry* pMapEntries_ ) { pMapEntries = pMapEntries_; return *this; } SpecializationInfo& setDataSize( size_t dataSize_ ) { dataSize = dataSize_; return *this; } SpecializationInfo& setPData( const void* pData_ ) { pData = pData_; return *this; } operator const VkSpecializationInfo&() const { return *reinterpret_cast(this); } bool operator==( SpecializationInfo const& rhs ) const { return ( mapEntryCount == rhs.mapEntryCount ) && ( pMapEntries == rhs.pMapEntries ) && ( dataSize == rhs.dataSize ) && ( pData == rhs.pData ); } bool operator!=( SpecializationInfo const& rhs ) const { return !operator==( rhs ); } uint32_t mapEntryCount; const SpecializationMapEntry* pMapEntries; size_t dataSize; const void* pData; }; static_assert( sizeof( SpecializationInfo ) == sizeof( VkSpecializationInfo ), "struct and wrapper have different size!" ); union ClearColorValue { ClearColorValue( const std::array& float32_ = { {0} } ) { memcpy( &float32, float32_.data(), 4 * sizeof( float ) ); } ClearColorValue( const std::array& int32_ ) { memcpy( &int32, int32_.data(), 4 * sizeof( int32_t ) ); } ClearColorValue( const std::array& uint32_ ) { memcpy( &uint32, uint32_.data(), 4 * sizeof( uint32_t ) ); } ClearColorValue& setFloat32( std::array float32_ ) { memcpy( &float32, float32_.data(), 4 * sizeof( float ) ); return *this; } ClearColorValue& setInt32( std::array int32_ ) { memcpy( &int32, int32_.data(), 4 * sizeof( int32_t ) ); return *this; } ClearColorValue& setUint32( std::array uint32_ ) { memcpy( &uint32, uint32_.data(), 4 * sizeof( uint32_t ) ); return *this; } operator VkClearColorValue const& () const { return *reinterpret_cast(this); } float float32[4]; int32_t int32[4]; uint32_t uint32[4]; }; struct ClearDepthStencilValue { ClearDepthStencilValue( float depth_ = 0, uint32_t stencil_ = 0 ) : depth( depth_ ) , stencil( stencil_ ) { } ClearDepthStencilValue( VkClearDepthStencilValue const & rhs ) { memcpy( this, &rhs, sizeof(ClearDepthStencilValue) ); } ClearDepthStencilValue& operator=( VkClearDepthStencilValue const & rhs ) { memcpy( this, &rhs, sizeof(ClearDepthStencilValue) ); return *this; } ClearDepthStencilValue& setDepth( float depth_ ) { depth = depth_; return *this; } ClearDepthStencilValue& setStencil( uint32_t stencil_ ) { stencil = stencil_; return *this; } operator const VkClearDepthStencilValue&() const { return *reinterpret_cast(this); } bool operator==( ClearDepthStencilValue const& rhs ) const { return ( depth == rhs.depth ) && ( stencil == rhs.stencil ); } bool operator!=( ClearDepthStencilValue const& rhs ) const { return !operator==( rhs ); } float depth; uint32_t stencil; }; static_assert( sizeof( ClearDepthStencilValue ) == sizeof( VkClearDepthStencilValue ), "struct and wrapper have different size!" ); union ClearValue { ClearValue( ClearColorValue color_ = ClearColorValue() ) { color = color_; } ClearValue( ClearDepthStencilValue depthStencil_ ) { depthStencil = depthStencil_; } ClearValue& setColor( ClearColorValue color_ ) { color = color_; return *this; } ClearValue& setDepthStencil( ClearDepthStencilValue depthStencil_ ) { depthStencil = depthStencil_; return *this; } operator VkClearValue const& () const { return *reinterpret_cast(this); } #ifdef VULKAN_HPP_HAS_UNRESTRICTED_UNIONS ClearColorValue color; ClearDepthStencilValue depthStencil; #else VkClearColorValue color; VkClearDepthStencilValue depthStencil; #endif // VULKAN_HPP_HAS_UNRESTRICTED_UNIONS }; struct PhysicalDeviceFeatures { PhysicalDeviceFeatures( Bool32 robustBufferAccess_ = 0, Bool32 fullDrawIndexUint32_ = 0, Bool32 imageCubeArray_ = 0, Bool32 independentBlend_ = 0, Bool32 geometryShader_ = 0, Bool32 tessellationShader_ = 0, Bool32 sampleRateShading_ = 0, Bool32 dualSrcBlend_ = 0, Bool32 logicOp_ = 0, Bool32 multiDrawIndirect_ = 0, Bool32 drawIndirectFirstInstance_ = 0, Bool32 depthClamp_ = 0, Bool32 depthBiasClamp_ = 0, Bool32 fillModeNonSolid_ = 0, Bool32 depthBounds_ = 0, Bool32 wideLines_ = 0, Bool32 largePoints_ = 0, Bool32 alphaToOne_ = 0, Bool32 multiViewport_ = 0, Bool32 samplerAnisotropy_ = 0, Bool32 textureCompressionETC2_ = 0, Bool32 textureCompressionASTC_LDR_ = 0, Bool32 textureCompressionBC_ = 0, Bool32 occlusionQueryPrecise_ = 0, Bool32 pipelineStatisticsQuery_ = 0, Bool32 vertexPipelineStoresAndAtomics_ = 0, Bool32 fragmentStoresAndAtomics_ = 0, Bool32 shaderTessellationAndGeometryPointSize_ = 0, Bool32 shaderImageGatherExtended_ = 0, Bool32 shaderStorageImageExtendedFormats_ = 0, Bool32 shaderStorageImageMultisample_ = 0, Bool32 shaderStorageImageReadWithoutFormat_ = 0, Bool32 shaderStorageImageWriteWithoutFormat_ = 0, Bool32 shaderUniformBufferArrayDynamicIndexing_ = 0, Bool32 shaderSampledImageArrayDynamicIndexing_ = 0, Bool32 shaderStorageBufferArrayDynamicIndexing_ = 0, Bool32 shaderStorageImageArrayDynamicIndexing_ = 0, Bool32 shaderClipDistance_ = 0, Bool32 shaderCullDistance_ = 0, Bool32 shaderFloat64_ = 0, Bool32 shaderInt64_ = 0, Bool32 shaderInt16_ = 0, Bool32 shaderResourceResidency_ = 0, Bool32 shaderResourceMinLod_ = 0, Bool32 sparseBinding_ = 0, Bool32 sparseResidencyBuffer_ = 0, Bool32 sparseResidencyImage2D_ = 0, Bool32 sparseResidencyImage3D_ = 0, Bool32 sparseResidency2Samples_ = 0, Bool32 sparseResidency4Samples_ = 0, Bool32 sparseResidency8Samples_ = 0, Bool32 sparseResidency16Samples_ = 0, Bool32 sparseResidencyAliased_ = 0, Bool32 variableMultisampleRate_ = 0, Bool32 inheritedQueries_ = 0 ) : robustBufferAccess( robustBufferAccess_ ) , fullDrawIndexUint32( fullDrawIndexUint32_ ) , imageCubeArray( imageCubeArray_ ) , independentBlend( independentBlend_ ) , geometryShader( geometryShader_ ) , tessellationShader( tessellationShader_ ) , sampleRateShading( sampleRateShading_ ) , dualSrcBlend( dualSrcBlend_ ) , logicOp( logicOp_ ) , multiDrawIndirect( multiDrawIndirect_ ) , drawIndirectFirstInstance( drawIndirectFirstInstance_ ) , depthClamp( depthClamp_ ) , depthBiasClamp( depthBiasClamp_ ) , fillModeNonSolid( fillModeNonSolid_ ) , depthBounds( depthBounds_ ) , wideLines( wideLines_ ) , largePoints( largePoints_ ) , alphaToOne( alphaToOne_ ) , multiViewport( multiViewport_ ) , samplerAnisotropy( samplerAnisotropy_ ) , textureCompressionETC2( textureCompressionETC2_ ) , textureCompressionASTC_LDR( textureCompressionASTC_LDR_ ) , textureCompressionBC( textureCompressionBC_ ) , occlusionQueryPrecise( occlusionQueryPrecise_ ) , pipelineStatisticsQuery( pipelineStatisticsQuery_ ) , vertexPipelineStoresAndAtomics( vertexPipelineStoresAndAtomics_ ) , fragmentStoresAndAtomics( fragmentStoresAndAtomics_ ) , shaderTessellationAndGeometryPointSize( shaderTessellationAndGeometryPointSize_ ) , shaderImageGatherExtended( shaderImageGatherExtended_ ) , shaderStorageImageExtendedFormats( shaderStorageImageExtendedFormats_ ) , shaderStorageImageMultisample( shaderStorageImageMultisample_ ) , shaderStorageImageReadWithoutFormat( shaderStorageImageReadWithoutFormat_ ) , shaderStorageImageWriteWithoutFormat( shaderStorageImageWriteWithoutFormat_ ) , shaderUniformBufferArrayDynamicIndexing( shaderUniformBufferArrayDynamicIndexing_ ) , shaderSampledImageArrayDynamicIndexing( shaderSampledImageArrayDynamicIndexing_ ) , shaderStorageBufferArrayDynamicIndexing( shaderStorageBufferArrayDynamicIndexing_ ) , shaderStorageImageArrayDynamicIndexing( shaderStorageImageArrayDynamicIndexing_ ) , shaderClipDistance( shaderClipDistance_ ) , shaderCullDistance( shaderCullDistance_ ) , shaderFloat64( shaderFloat64_ ) , shaderInt64( shaderInt64_ ) , shaderInt16( shaderInt16_ ) , shaderResourceResidency( shaderResourceResidency_ ) , shaderResourceMinLod( shaderResourceMinLod_ ) , sparseBinding( sparseBinding_ ) , sparseResidencyBuffer( sparseResidencyBuffer_ ) , sparseResidencyImage2D( sparseResidencyImage2D_ ) , sparseResidencyImage3D( sparseResidencyImage3D_ ) , sparseResidency2Samples( sparseResidency2Samples_ ) , sparseResidency4Samples( sparseResidency4Samples_ ) , sparseResidency8Samples( sparseResidency8Samples_ ) , sparseResidency16Samples( sparseResidency16Samples_ ) , sparseResidencyAliased( sparseResidencyAliased_ ) , variableMultisampleRate( variableMultisampleRate_ ) , inheritedQueries( inheritedQueries_ ) { } PhysicalDeviceFeatures( VkPhysicalDeviceFeatures const & rhs ) { memcpy( this, &rhs, sizeof(PhysicalDeviceFeatures) ); } PhysicalDeviceFeatures& operator=( VkPhysicalDeviceFeatures const & rhs ) { memcpy( this, &rhs, sizeof(PhysicalDeviceFeatures) ); return *this; } PhysicalDeviceFeatures& setRobustBufferAccess( Bool32 robustBufferAccess_ ) { robustBufferAccess = robustBufferAccess_; return *this; } PhysicalDeviceFeatures& setFullDrawIndexUint32( Bool32 fullDrawIndexUint32_ ) { fullDrawIndexUint32 = fullDrawIndexUint32_; return *this; } PhysicalDeviceFeatures& setImageCubeArray( Bool32 imageCubeArray_ ) { imageCubeArray = imageCubeArray_; return *this; } PhysicalDeviceFeatures& setIndependentBlend( Bool32 independentBlend_ ) { independentBlend = independentBlend_; return *this; } PhysicalDeviceFeatures& setGeometryShader( Bool32 geometryShader_ ) { geometryShader = geometryShader_; return *this; } PhysicalDeviceFeatures& setTessellationShader( Bool32 tessellationShader_ ) { tessellationShader = tessellationShader_; return *this; } PhysicalDeviceFeatures& setSampleRateShading( Bool32 sampleRateShading_ ) { sampleRateShading = sampleRateShading_; return *this; } PhysicalDeviceFeatures& setDualSrcBlend( Bool32 dualSrcBlend_ ) { dualSrcBlend = dualSrcBlend_; return *this; } PhysicalDeviceFeatures& setLogicOp( Bool32 logicOp_ ) { logicOp = logicOp_; return *this; } PhysicalDeviceFeatures& setMultiDrawIndirect( Bool32 multiDrawIndirect_ ) { multiDrawIndirect = multiDrawIndirect_; return *this; } PhysicalDeviceFeatures& setDrawIndirectFirstInstance( Bool32 drawIndirectFirstInstance_ ) { drawIndirectFirstInstance = drawIndirectFirstInstance_; return *this; } PhysicalDeviceFeatures& setDepthClamp( Bool32 depthClamp_ ) { depthClamp = depthClamp_; return *this; } PhysicalDeviceFeatures& setDepthBiasClamp( Bool32 depthBiasClamp_ ) { depthBiasClamp = depthBiasClamp_; return *this; } PhysicalDeviceFeatures& setFillModeNonSolid( Bool32 fillModeNonSolid_ ) { fillModeNonSolid = fillModeNonSolid_; return *this; } PhysicalDeviceFeatures& setDepthBounds( Bool32 depthBounds_ ) { depthBounds = depthBounds_; return *this; } PhysicalDeviceFeatures& setWideLines( Bool32 wideLines_ ) { wideLines = wideLines_; return *this; } PhysicalDeviceFeatures& setLargePoints( Bool32 largePoints_ ) { largePoints = largePoints_; return *this; } PhysicalDeviceFeatures& setAlphaToOne( Bool32 alphaToOne_ ) { alphaToOne = alphaToOne_; return *this; } PhysicalDeviceFeatures& setMultiViewport( Bool32 multiViewport_ ) { multiViewport = multiViewport_; return *this; } PhysicalDeviceFeatures& setSamplerAnisotropy( Bool32 samplerAnisotropy_ ) { samplerAnisotropy = samplerAnisotropy_; return *this; } PhysicalDeviceFeatures& setTextureCompressionETC2( Bool32 textureCompressionETC2_ ) { textureCompressionETC2 = textureCompressionETC2_; return *this; } PhysicalDeviceFeatures& setTextureCompressionASTC_LDR( Bool32 textureCompressionASTC_LDR_ ) { textureCompressionASTC_LDR = textureCompressionASTC_LDR_; return *this; } PhysicalDeviceFeatures& setTextureCompressionBC( Bool32 textureCompressionBC_ ) { textureCompressionBC = textureCompressionBC_; return *this; } PhysicalDeviceFeatures& setOcclusionQueryPrecise( Bool32 occlusionQueryPrecise_ ) { occlusionQueryPrecise = occlusionQueryPrecise_; return *this; } PhysicalDeviceFeatures& setPipelineStatisticsQuery( Bool32 pipelineStatisticsQuery_ ) { pipelineStatisticsQuery = pipelineStatisticsQuery_; return *this; } PhysicalDeviceFeatures& setVertexPipelineStoresAndAtomics( Bool32 vertexPipelineStoresAndAtomics_ ) { vertexPipelineStoresAndAtomics = vertexPipelineStoresAndAtomics_; return *this; } PhysicalDeviceFeatures& setFragmentStoresAndAtomics( Bool32 fragmentStoresAndAtomics_ ) { fragmentStoresAndAtomics = fragmentStoresAndAtomics_; return *this; } PhysicalDeviceFeatures& setShaderTessellationAndGeometryPointSize( Bool32 shaderTessellationAndGeometryPointSize_ ) { shaderTessellationAndGeometryPointSize = shaderTessellationAndGeometryPointSize_; return *this; } PhysicalDeviceFeatures& setShaderImageGatherExtended( Bool32 shaderImageGatherExtended_ ) { shaderImageGatherExtended = shaderImageGatherExtended_; return *this; } PhysicalDeviceFeatures& setShaderStorageImageExtendedFormats( Bool32 shaderStorageImageExtendedFormats_ ) { shaderStorageImageExtendedFormats = shaderStorageImageExtendedFormats_; return *this; } PhysicalDeviceFeatures& setShaderStorageImageMultisample( Bool32 shaderStorageImageMultisample_ ) { shaderStorageImageMultisample = shaderStorageImageMultisample_; return *this; } PhysicalDeviceFeatures& setShaderStorageImageReadWithoutFormat( Bool32 shaderStorageImageReadWithoutFormat_ ) { shaderStorageImageReadWithoutFormat = shaderStorageImageReadWithoutFormat_; return *this; } PhysicalDeviceFeatures& setShaderStorageImageWriteWithoutFormat( Bool32 shaderStorageImageWriteWithoutFormat_ ) { shaderStorageImageWriteWithoutFormat = shaderStorageImageWriteWithoutFormat_; return *this; } PhysicalDeviceFeatures& setShaderUniformBufferArrayDynamicIndexing( Bool32 shaderUniformBufferArrayDynamicIndexing_ ) { shaderUniformBufferArrayDynamicIndexing = shaderUniformBufferArrayDynamicIndexing_; return *this; } PhysicalDeviceFeatures& setShaderSampledImageArrayDynamicIndexing( Bool32 shaderSampledImageArrayDynamicIndexing_ ) { shaderSampledImageArrayDynamicIndexing = shaderSampledImageArrayDynamicIndexing_; return *this; } PhysicalDeviceFeatures& setShaderStorageBufferArrayDynamicIndexing( Bool32 shaderStorageBufferArrayDynamicIndexing_ ) { shaderStorageBufferArrayDynamicIndexing = shaderStorageBufferArrayDynamicIndexing_; return *this; } PhysicalDeviceFeatures& setShaderStorageImageArrayDynamicIndexing( Bool32 shaderStorageImageArrayDynamicIndexing_ ) { shaderStorageImageArrayDynamicIndexing = shaderStorageImageArrayDynamicIndexing_; return *this; } PhysicalDeviceFeatures& setShaderClipDistance( Bool32 shaderClipDistance_ ) { shaderClipDistance = shaderClipDistance_; return *this; } PhysicalDeviceFeatures& setShaderCullDistance( Bool32 shaderCullDistance_ ) { shaderCullDistance = shaderCullDistance_; return *this; } PhysicalDeviceFeatures& setShaderFloat64( Bool32 shaderFloat64_ ) { shaderFloat64 = shaderFloat64_; return *this; } PhysicalDeviceFeatures& setShaderInt64( Bool32 shaderInt64_ ) { shaderInt64 = shaderInt64_; return *this; } PhysicalDeviceFeatures& setShaderInt16( Bool32 shaderInt16_ ) { shaderInt16 = shaderInt16_; return *this; } PhysicalDeviceFeatures& setShaderResourceResidency( Bool32 shaderResourceResidency_ ) { shaderResourceResidency = shaderResourceResidency_; return *this; } PhysicalDeviceFeatures& setShaderResourceMinLod( Bool32 shaderResourceMinLod_ ) { shaderResourceMinLod = shaderResourceMinLod_; return *this; } PhysicalDeviceFeatures& setSparseBinding( Bool32 sparseBinding_ ) { sparseBinding = sparseBinding_; return *this; } PhysicalDeviceFeatures& setSparseResidencyBuffer( Bool32 sparseResidencyBuffer_ ) { sparseResidencyBuffer = sparseResidencyBuffer_; return *this; } PhysicalDeviceFeatures& setSparseResidencyImage2D( Bool32 sparseResidencyImage2D_ ) { sparseResidencyImage2D = sparseResidencyImage2D_; return *this; } PhysicalDeviceFeatures& setSparseResidencyImage3D( Bool32 sparseResidencyImage3D_ ) { sparseResidencyImage3D = sparseResidencyImage3D_; return *this; } PhysicalDeviceFeatures& setSparseResidency2Samples( Bool32 sparseResidency2Samples_ ) { sparseResidency2Samples = sparseResidency2Samples_; return *this; } PhysicalDeviceFeatures& setSparseResidency4Samples( Bool32 sparseResidency4Samples_ ) { sparseResidency4Samples = sparseResidency4Samples_; return *this; } PhysicalDeviceFeatures& setSparseResidency8Samples( Bool32 sparseResidency8Samples_ ) { sparseResidency8Samples = sparseResidency8Samples_; return *this; } PhysicalDeviceFeatures& setSparseResidency16Samples( Bool32 sparseResidency16Samples_ ) { sparseResidency16Samples = sparseResidency16Samples_; return *this; } PhysicalDeviceFeatures& setSparseResidencyAliased( Bool32 sparseResidencyAliased_ ) { sparseResidencyAliased = sparseResidencyAliased_; return *this; } PhysicalDeviceFeatures& setVariableMultisampleRate( Bool32 variableMultisampleRate_ ) { variableMultisampleRate = variableMultisampleRate_; return *this; } PhysicalDeviceFeatures& setInheritedQueries( Bool32 inheritedQueries_ ) { inheritedQueries = inheritedQueries_; return *this; } operator const VkPhysicalDeviceFeatures&() const { return *reinterpret_cast(this); } bool operator==( PhysicalDeviceFeatures const& rhs ) const { return ( robustBufferAccess == rhs.robustBufferAccess ) && ( fullDrawIndexUint32 == rhs.fullDrawIndexUint32 ) && ( imageCubeArray == rhs.imageCubeArray ) && ( independentBlend == rhs.independentBlend ) && ( geometryShader == rhs.geometryShader ) && ( tessellationShader == rhs.tessellationShader ) && ( sampleRateShading == rhs.sampleRateShading ) && ( dualSrcBlend == rhs.dualSrcBlend ) && ( logicOp == rhs.logicOp ) && ( multiDrawIndirect == rhs.multiDrawIndirect ) && ( drawIndirectFirstInstance == rhs.drawIndirectFirstInstance ) && ( depthClamp == rhs.depthClamp ) && ( depthBiasClamp == rhs.depthBiasClamp ) && ( fillModeNonSolid == rhs.fillModeNonSolid ) && ( depthBounds == rhs.depthBounds ) && ( wideLines == rhs.wideLines ) && ( largePoints == rhs.largePoints ) && ( alphaToOne == rhs.alphaToOne ) && ( multiViewport == rhs.multiViewport ) && ( samplerAnisotropy == rhs.samplerAnisotropy ) && ( textureCompressionETC2 == rhs.textureCompressionETC2 ) && ( textureCompressionASTC_LDR == rhs.textureCompressionASTC_LDR ) && ( textureCompressionBC == rhs.textureCompressionBC ) && ( occlusionQueryPrecise == rhs.occlusionQueryPrecise ) && ( pipelineStatisticsQuery == rhs.pipelineStatisticsQuery ) && ( vertexPipelineStoresAndAtomics == rhs.vertexPipelineStoresAndAtomics ) && ( fragmentStoresAndAtomics == rhs.fragmentStoresAndAtomics ) && ( shaderTessellationAndGeometryPointSize == rhs.shaderTessellationAndGeometryPointSize ) && ( shaderImageGatherExtended == rhs.shaderImageGatherExtended ) && ( shaderStorageImageExtendedFormats == rhs.shaderStorageImageExtendedFormats ) && ( shaderStorageImageMultisample == rhs.shaderStorageImageMultisample ) && ( shaderStorageImageReadWithoutFormat == rhs.shaderStorageImageReadWithoutFormat ) && ( shaderStorageImageWriteWithoutFormat == rhs.shaderStorageImageWriteWithoutFormat ) && ( shaderUniformBufferArrayDynamicIndexing == rhs.shaderUniformBufferArrayDynamicIndexing ) && ( shaderSampledImageArrayDynamicIndexing == rhs.shaderSampledImageArrayDynamicIndexing ) && ( shaderStorageBufferArrayDynamicIndexing == rhs.shaderStorageBufferArrayDynamicIndexing ) && ( shaderStorageImageArrayDynamicIndexing == rhs.shaderStorageImageArrayDynamicIndexing ) && ( shaderClipDistance == rhs.shaderClipDistance ) && ( shaderCullDistance == rhs.shaderCullDistance ) && ( shaderFloat64 == rhs.shaderFloat64 ) && ( shaderInt64 == rhs.shaderInt64 ) && ( shaderInt16 == rhs.shaderInt16 ) && ( shaderResourceResidency == rhs.shaderResourceResidency ) && ( shaderResourceMinLod == rhs.shaderResourceMinLod ) && ( sparseBinding == rhs.sparseBinding ) && ( sparseResidencyBuffer == rhs.sparseResidencyBuffer ) && ( sparseResidencyImage2D == rhs.sparseResidencyImage2D ) && ( sparseResidencyImage3D == rhs.sparseResidencyImage3D ) && ( sparseResidency2Samples == rhs.sparseResidency2Samples ) && ( sparseResidency4Samples == rhs.sparseResidency4Samples ) && ( sparseResidency8Samples == rhs.sparseResidency8Samples ) && ( sparseResidency16Samples == rhs.sparseResidency16Samples ) && ( sparseResidencyAliased == rhs.sparseResidencyAliased ) && ( variableMultisampleRate == rhs.variableMultisampleRate ) && ( inheritedQueries == rhs.inheritedQueries ); } bool operator!=( PhysicalDeviceFeatures const& rhs ) const { return !operator==( rhs ); } Bool32 robustBufferAccess; Bool32 fullDrawIndexUint32; Bool32 imageCubeArray; Bool32 independentBlend; Bool32 geometryShader; Bool32 tessellationShader; Bool32 sampleRateShading; Bool32 dualSrcBlend; Bool32 logicOp; Bool32 multiDrawIndirect; Bool32 drawIndirectFirstInstance; Bool32 depthClamp; Bool32 depthBiasClamp; Bool32 fillModeNonSolid; Bool32 depthBounds; Bool32 wideLines; Bool32 largePoints; Bool32 alphaToOne; Bool32 multiViewport; Bool32 samplerAnisotropy; Bool32 textureCompressionETC2; Bool32 textureCompressionASTC_LDR; Bool32 textureCompressionBC; Bool32 occlusionQueryPrecise; Bool32 pipelineStatisticsQuery; Bool32 vertexPipelineStoresAndAtomics; Bool32 fragmentStoresAndAtomics; Bool32 shaderTessellationAndGeometryPointSize; Bool32 shaderImageGatherExtended; Bool32 shaderStorageImageExtendedFormats; Bool32 shaderStorageImageMultisample; Bool32 shaderStorageImageReadWithoutFormat; Bool32 shaderStorageImageWriteWithoutFormat; Bool32 shaderUniformBufferArrayDynamicIndexing; Bool32 shaderSampledImageArrayDynamicIndexing; Bool32 shaderStorageBufferArrayDynamicIndexing; Bool32 shaderStorageImageArrayDynamicIndexing; Bool32 shaderClipDistance; Bool32 shaderCullDistance; Bool32 shaderFloat64; Bool32 shaderInt64; Bool32 shaderInt16; Bool32 shaderResourceResidency; Bool32 shaderResourceMinLod; Bool32 sparseBinding; Bool32 sparseResidencyBuffer; Bool32 sparseResidencyImage2D; Bool32 sparseResidencyImage3D; Bool32 sparseResidency2Samples; Bool32 sparseResidency4Samples; Bool32 sparseResidency8Samples; Bool32 sparseResidency16Samples; Bool32 sparseResidencyAliased; Bool32 variableMultisampleRate; Bool32 inheritedQueries; }; static_assert( sizeof( PhysicalDeviceFeatures ) == sizeof( VkPhysicalDeviceFeatures ), "struct and wrapper have different size!" ); struct PhysicalDeviceSparseProperties { operator const VkPhysicalDeviceSparseProperties&() const { return *reinterpret_cast(this); } bool operator==( PhysicalDeviceSparseProperties const& rhs ) const { return ( residencyStandard2DBlockShape == rhs.residencyStandard2DBlockShape ) && ( residencyStandard2DMultisampleBlockShape == rhs.residencyStandard2DMultisampleBlockShape ) && ( residencyStandard3DBlockShape == rhs.residencyStandard3DBlockShape ) && ( residencyAlignedMipSize == rhs.residencyAlignedMipSize ) && ( residencyNonResidentStrict == rhs.residencyNonResidentStrict ); } bool operator!=( PhysicalDeviceSparseProperties const& rhs ) const { return !operator==( rhs ); } Bool32 residencyStandard2DBlockShape; Bool32 residencyStandard2DMultisampleBlockShape; Bool32 residencyStandard3DBlockShape; Bool32 residencyAlignedMipSize; Bool32 residencyNonResidentStrict; }; static_assert( sizeof( PhysicalDeviceSparseProperties ) == sizeof( VkPhysicalDeviceSparseProperties ), "struct and wrapper have different size!" ); struct DrawIndirectCommand { DrawIndirectCommand( uint32_t vertexCount_ = 0, uint32_t instanceCount_ = 0, uint32_t firstVertex_ = 0, uint32_t firstInstance_ = 0 ) : vertexCount( vertexCount_ ) , instanceCount( instanceCount_ ) , firstVertex( firstVertex_ ) , firstInstance( firstInstance_ ) { } DrawIndirectCommand( VkDrawIndirectCommand const & rhs ) { memcpy( this, &rhs, sizeof(DrawIndirectCommand) ); } DrawIndirectCommand& operator=( VkDrawIndirectCommand const & rhs ) { memcpy( this, &rhs, sizeof(DrawIndirectCommand) ); return *this; } DrawIndirectCommand& setVertexCount( uint32_t vertexCount_ ) { vertexCount = vertexCount_; return *this; } DrawIndirectCommand& setInstanceCount( uint32_t instanceCount_ ) { instanceCount = instanceCount_; return *this; } DrawIndirectCommand& setFirstVertex( uint32_t firstVertex_ ) { firstVertex = firstVertex_; return *this; } DrawIndirectCommand& setFirstInstance( uint32_t firstInstance_ ) { firstInstance = firstInstance_; return *this; } operator const VkDrawIndirectCommand&() const { return *reinterpret_cast(this); } bool operator==( DrawIndirectCommand const& rhs ) const { return ( vertexCount == rhs.vertexCount ) && ( instanceCount == rhs.instanceCount ) && ( firstVertex == rhs.firstVertex ) && ( firstInstance == rhs.firstInstance ); } bool operator!=( DrawIndirectCommand const& rhs ) const { return !operator==( rhs ); } uint32_t vertexCount; uint32_t instanceCount; uint32_t firstVertex; uint32_t firstInstance; }; static_assert( sizeof( DrawIndirectCommand ) == sizeof( VkDrawIndirectCommand ), "struct and wrapper have different size!" ); struct DrawIndexedIndirectCommand { DrawIndexedIndirectCommand( uint32_t indexCount_ = 0, uint32_t instanceCount_ = 0, uint32_t firstIndex_ = 0, int32_t vertexOffset_ = 0, uint32_t firstInstance_ = 0 ) : indexCount( indexCount_ ) , instanceCount( instanceCount_ ) , firstIndex( firstIndex_ ) , vertexOffset( vertexOffset_ ) , firstInstance( firstInstance_ ) { } DrawIndexedIndirectCommand( VkDrawIndexedIndirectCommand const & rhs ) { memcpy( this, &rhs, sizeof(DrawIndexedIndirectCommand) ); } DrawIndexedIndirectCommand& operator=( VkDrawIndexedIndirectCommand const & rhs ) { memcpy( this, &rhs, sizeof(DrawIndexedIndirectCommand) ); return *this; } DrawIndexedIndirectCommand& setIndexCount( uint32_t indexCount_ ) { indexCount = indexCount_; return *this; } DrawIndexedIndirectCommand& setInstanceCount( uint32_t instanceCount_ ) { instanceCount = instanceCount_; return *this; } DrawIndexedIndirectCommand& setFirstIndex( uint32_t firstIndex_ ) { firstIndex = firstIndex_; return *this; } DrawIndexedIndirectCommand& setVertexOffset( int32_t vertexOffset_ ) { vertexOffset = vertexOffset_; return *this; } DrawIndexedIndirectCommand& setFirstInstance( uint32_t firstInstance_ ) { firstInstance = firstInstance_; return *this; } operator const VkDrawIndexedIndirectCommand&() const { return *reinterpret_cast(this); } bool operator==( DrawIndexedIndirectCommand const& rhs ) const { return ( indexCount == rhs.indexCount ) && ( instanceCount == rhs.instanceCount ) && ( firstIndex == rhs.firstIndex ) && ( vertexOffset == rhs.vertexOffset ) && ( firstInstance == rhs.firstInstance ); } bool operator!=( DrawIndexedIndirectCommand const& rhs ) const { return !operator==( rhs ); } uint32_t indexCount; uint32_t instanceCount; uint32_t firstIndex; int32_t vertexOffset; uint32_t firstInstance; }; static_assert( sizeof( DrawIndexedIndirectCommand ) == sizeof( VkDrawIndexedIndirectCommand ), "struct and wrapper have different size!" ); struct DispatchIndirectCommand { DispatchIndirectCommand( uint32_t x_ = 0, uint32_t y_ = 0, uint32_t z_ = 0 ) : x( x_ ) , y( y_ ) , z( z_ ) { } DispatchIndirectCommand( VkDispatchIndirectCommand const & rhs ) { memcpy( this, &rhs, sizeof(DispatchIndirectCommand) ); } DispatchIndirectCommand& operator=( VkDispatchIndirectCommand const & rhs ) { memcpy( this, &rhs, sizeof(DispatchIndirectCommand) ); return *this; } DispatchIndirectCommand& setX( uint32_t x_ ) { x = x_; return *this; } DispatchIndirectCommand& setY( uint32_t y_ ) { y = y_; return *this; } DispatchIndirectCommand& setZ( uint32_t z_ ) { z = z_; return *this; } operator const VkDispatchIndirectCommand&() const { return *reinterpret_cast(this); } bool operator==( DispatchIndirectCommand const& rhs ) const { return ( x == rhs.x ) && ( y == rhs.y ) && ( z == rhs.z ); } bool operator!=( DispatchIndirectCommand const& rhs ) const { return !operator==( rhs ); } uint32_t x; uint32_t y; uint32_t z; }; static_assert( sizeof( DispatchIndirectCommand ) == sizeof( VkDispatchIndirectCommand ), "struct and wrapper have different size!" ); struct DisplayPlanePropertiesKHR { operator const VkDisplayPlanePropertiesKHR&() const { return *reinterpret_cast(this); } bool operator==( DisplayPlanePropertiesKHR const& rhs ) const { return ( currentDisplay == rhs.currentDisplay ) && ( currentStackIndex == rhs.currentStackIndex ); } bool operator!=( DisplayPlanePropertiesKHR const& rhs ) const { return !operator==( rhs ); } DisplayKHR currentDisplay; uint32_t currentStackIndex; }; static_assert( sizeof( DisplayPlanePropertiesKHR ) == sizeof( VkDisplayPlanePropertiesKHR ), "struct and wrapper have different size!" ); struct DisplayModeParametersKHR { DisplayModeParametersKHR( Extent2D visibleRegion_ = Extent2D(), uint32_t refreshRate_ = 0 ) : visibleRegion( visibleRegion_ ) , refreshRate( refreshRate_ ) { } DisplayModeParametersKHR( VkDisplayModeParametersKHR const & rhs ) { memcpy( this, &rhs, sizeof(DisplayModeParametersKHR) ); } DisplayModeParametersKHR& operator=( VkDisplayModeParametersKHR const & rhs ) { memcpy( this, &rhs, sizeof(DisplayModeParametersKHR) ); return *this; } DisplayModeParametersKHR& setVisibleRegion( Extent2D visibleRegion_ ) { visibleRegion = visibleRegion_; return *this; } DisplayModeParametersKHR& setRefreshRate( uint32_t refreshRate_ ) { refreshRate = refreshRate_; return *this; } operator const VkDisplayModeParametersKHR&() const { return *reinterpret_cast(this); } bool operator==( DisplayModeParametersKHR const& rhs ) const { return ( visibleRegion == rhs.visibleRegion ) && ( refreshRate == rhs.refreshRate ); } bool operator!=( DisplayModeParametersKHR const& rhs ) const { return !operator==( rhs ); } Extent2D visibleRegion; uint32_t refreshRate; }; static_assert( sizeof( DisplayModeParametersKHR ) == sizeof( VkDisplayModeParametersKHR ), "struct and wrapper have different size!" ); struct DisplayModePropertiesKHR { operator const VkDisplayModePropertiesKHR&() const { return *reinterpret_cast(this); } bool operator==( DisplayModePropertiesKHR const& rhs ) const { return ( displayMode == rhs.displayMode ) && ( parameters == rhs.parameters ); } bool operator!=( DisplayModePropertiesKHR const& rhs ) const { return !operator==( rhs ); } DisplayModeKHR displayMode; DisplayModeParametersKHR parameters; }; static_assert( sizeof( DisplayModePropertiesKHR ) == sizeof( VkDisplayModePropertiesKHR ), "struct and wrapper have different size!" ); struct RectLayerKHR { RectLayerKHR( Offset2D offset_ = Offset2D(), Extent2D extent_ = Extent2D(), uint32_t layer_ = 0 ) : offset( offset_ ) , extent( extent_ ) , layer( layer_ ) { } RectLayerKHR( VkRectLayerKHR const & rhs ) { memcpy( this, &rhs, sizeof(RectLayerKHR) ); } RectLayerKHR& operator=( VkRectLayerKHR const & rhs ) { memcpy( this, &rhs, sizeof(RectLayerKHR) ); return *this; } RectLayerKHR& setOffset( Offset2D offset_ ) { offset = offset_; return *this; } RectLayerKHR& setExtent( Extent2D extent_ ) { extent = extent_; return *this; } RectLayerKHR& setLayer( uint32_t layer_ ) { layer = layer_; return *this; } operator const VkRectLayerKHR&() const { return *reinterpret_cast(this); } bool operator==( RectLayerKHR const& rhs ) const { return ( offset == rhs.offset ) && ( extent == rhs.extent ) && ( layer == rhs.layer ); } bool operator!=( RectLayerKHR const& rhs ) const { return !operator==( rhs ); } Offset2D offset; Extent2D extent; uint32_t layer; }; static_assert( sizeof( RectLayerKHR ) == sizeof( VkRectLayerKHR ), "struct and wrapper have different size!" ); struct PresentRegionKHR { PresentRegionKHR( uint32_t rectangleCount_ = 0, const RectLayerKHR* pRectangles_ = nullptr ) : rectangleCount( rectangleCount_ ) , pRectangles( pRectangles_ ) { } PresentRegionKHR( VkPresentRegionKHR const & rhs ) { memcpy( this, &rhs, sizeof(PresentRegionKHR) ); } PresentRegionKHR& operator=( VkPresentRegionKHR const & rhs ) { memcpy( this, &rhs, sizeof(PresentRegionKHR) ); return *this; } PresentRegionKHR& setRectangleCount( uint32_t rectangleCount_ ) { rectangleCount = rectangleCount_; return *this; } PresentRegionKHR& setPRectangles( const RectLayerKHR* pRectangles_ ) { pRectangles = pRectangles_; return *this; } operator const VkPresentRegionKHR&() const { return *reinterpret_cast(this); } bool operator==( PresentRegionKHR const& rhs ) const { return ( rectangleCount == rhs.rectangleCount ) && ( pRectangles == rhs.pRectangles ); } bool operator!=( PresentRegionKHR const& rhs ) const { return !operator==( rhs ); } uint32_t rectangleCount; const RectLayerKHR* pRectangles; }; static_assert( sizeof( PresentRegionKHR ) == sizeof( VkPresentRegionKHR ), "struct and wrapper have different size!" ); struct XYColorEXT { XYColorEXT( float x_ = 0, float y_ = 0 ) : x( x_ ) , y( y_ ) { } XYColorEXT( VkXYColorEXT const & rhs ) { memcpy( this, &rhs, sizeof(XYColorEXT) ); } XYColorEXT& operator=( VkXYColorEXT const & rhs ) { memcpy( this, &rhs, sizeof(XYColorEXT) ); return *this; } XYColorEXT& setX( float x_ ) { x = x_; return *this; } XYColorEXT& setY( float y_ ) { y = y_; return *this; } operator const VkXYColorEXT&() const { return *reinterpret_cast(this); } bool operator==( XYColorEXT const& rhs ) const { return ( x == rhs.x ) && ( y == rhs.y ); } bool operator!=( XYColorEXT const& rhs ) const { return !operator==( rhs ); } float x; float y; }; static_assert( sizeof( XYColorEXT ) == sizeof( VkXYColorEXT ), "struct and wrapper have different size!" ); struct RefreshCycleDurationGOOGLE { RefreshCycleDurationGOOGLE( uint64_t refreshDuration_ = 0 ) : refreshDuration( refreshDuration_ ) { } RefreshCycleDurationGOOGLE( VkRefreshCycleDurationGOOGLE const & rhs ) { memcpy( this, &rhs, sizeof(RefreshCycleDurationGOOGLE) ); } RefreshCycleDurationGOOGLE& operator=( VkRefreshCycleDurationGOOGLE const & rhs ) { memcpy( this, &rhs, sizeof(RefreshCycleDurationGOOGLE) ); return *this; } RefreshCycleDurationGOOGLE& setRefreshDuration( uint64_t refreshDuration_ ) { refreshDuration = refreshDuration_; return *this; } operator const VkRefreshCycleDurationGOOGLE&() const { return *reinterpret_cast(this); } bool operator==( RefreshCycleDurationGOOGLE const& rhs ) const { return ( refreshDuration == rhs.refreshDuration ); } bool operator!=( RefreshCycleDurationGOOGLE const& rhs ) const { return !operator==( rhs ); } uint64_t refreshDuration; }; static_assert( sizeof( RefreshCycleDurationGOOGLE ) == sizeof( VkRefreshCycleDurationGOOGLE ), "struct and wrapper have different size!" ); struct PastPresentationTimingGOOGLE { PastPresentationTimingGOOGLE( uint32_t presentID_ = 0, uint64_t desiredPresentTime_ = 0, uint64_t actualPresentTime_ = 0, uint64_t earliestPresentTime_ = 0, uint64_t presentMargin_ = 0 ) : presentID( presentID_ ) , desiredPresentTime( desiredPresentTime_ ) , actualPresentTime( actualPresentTime_ ) , earliestPresentTime( earliestPresentTime_ ) , presentMargin( presentMargin_ ) { } PastPresentationTimingGOOGLE( VkPastPresentationTimingGOOGLE const & rhs ) { memcpy( this, &rhs, sizeof(PastPresentationTimingGOOGLE) ); } PastPresentationTimingGOOGLE& operator=( VkPastPresentationTimingGOOGLE const & rhs ) { memcpy( this, &rhs, sizeof(PastPresentationTimingGOOGLE) ); return *this; } PastPresentationTimingGOOGLE& setPresentID( uint32_t presentID_ ) { presentID = presentID_; return *this; } PastPresentationTimingGOOGLE& setDesiredPresentTime( uint64_t desiredPresentTime_ ) { desiredPresentTime = desiredPresentTime_; return *this; } PastPresentationTimingGOOGLE& setActualPresentTime( uint64_t actualPresentTime_ ) { actualPresentTime = actualPresentTime_; return *this; } PastPresentationTimingGOOGLE& setEarliestPresentTime( uint64_t earliestPresentTime_ ) { earliestPresentTime = earliestPresentTime_; return *this; } PastPresentationTimingGOOGLE& setPresentMargin( uint64_t presentMargin_ ) { presentMargin = presentMargin_; return *this; } operator const VkPastPresentationTimingGOOGLE&() const { return *reinterpret_cast(this); } bool operator==( PastPresentationTimingGOOGLE const& rhs ) const { return ( presentID == rhs.presentID ) && ( desiredPresentTime == rhs.desiredPresentTime ) && ( actualPresentTime == rhs.actualPresentTime ) && ( earliestPresentTime == rhs.earliestPresentTime ) && ( presentMargin == rhs.presentMargin ); } bool operator!=( PastPresentationTimingGOOGLE const& rhs ) const { return !operator==( rhs ); } uint32_t presentID; uint64_t desiredPresentTime; uint64_t actualPresentTime; uint64_t earliestPresentTime; uint64_t presentMargin; }; static_assert( sizeof( PastPresentationTimingGOOGLE ) == sizeof( VkPastPresentationTimingGOOGLE ), "struct and wrapper have different size!" ); struct PresentTimeGOOGLE { PresentTimeGOOGLE( uint32_t presentID_ = 0, uint64_t desiredPresentTime_ = 0 ) : presentID( presentID_ ) , desiredPresentTime( desiredPresentTime_ ) { } PresentTimeGOOGLE( VkPresentTimeGOOGLE const & rhs ) { memcpy( this, &rhs, sizeof(PresentTimeGOOGLE) ); } PresentTimeGOOGLE& operator=( VkPresentTimeGOOGLE const & rhs ) { memcpy( this, &rhs, sizeof(PresentTimeGOOGLE) ); return *this; } PresentTimeGOOGLE& setPresentID( uint32_t presentID_ ) { presentID = presentID_; return *this; } PresentTimeGOOGLE& setDesiredPresentTime( uint64_t desiredPresentTime_ ) { desiredPresentTime = desiredPresentTime_; return *this; } operator const VkPresentTimeGOOGLE&() const { return *reinterpret_cast(this); } bool operator==( PresentTimeGOOGLE const& rhs ) const { return ( presentID == rhs.presentID ) && ( desiredPresentTime == rhs.desiredPresentTime ); } bool operator!=( PresentTimeGOOGLE const& rhs ) const { return !operator==( rhs ); } uint32_t presentID; uint64_t desiredPresentTime; }; static_assert( sizeof( PresentTimeGOOGLE ) == sizeof( VkPresentTimeGOOGLE ), "struct and wrapper have different size!" ); struct ViewportWScalingNV { ViewportWScalingNV( float xcoeff_ = 0, float ycoeff_ = 0 ) : xcoeff( xcoeff_ ) , ycoeff( ycoeff_ ) { } ViewportWScalingNV( VkViewportWScalingNV const & rhs ) { memcpy( this, &rhs, sizeof(ViewportWScalingNV) ); } ViewportWScalingNV& operator=( VkViewportWScalingNV const & rhs ) { memcpy( this, &rhs, sizeof(ViewportWScalingNV) ); return *this; } ViewportWScalingNV& setXcoeff( float xcoeff_ ) { xcoeff = xcoeff_; return *this; } ViewportWScalingNV& setYcoeff( float ycoeff_ ) { ycoeff = ycoeff_; return *this; } operator const VkViewportWScalingNV&() const { return *reinterpret_cast(this); } bool operator==( ViewportWScalingNV const& rhs ) const { return ( xcoeff == rhs.xcoeff ) && ( ycoeff == rhs.ycoeff ); } bool operator!=( ViewportWScalingNV const& rhs ) const { return !operator==( rhs ); } float xcoeff; float ycoeff; }; static_assert( sizeof( ViewportWScalingNV ) == sizeof( VkViewportWScalingNV ), "struct and wrapper have different size!" ); enum class ImageLayout { eUndefined = VK_IMAGE_LAYOUT_UNDEFINED, eGeneral = VK_IMAGE_LAYOUT_GENERAL, eColorAttachmentOptimal = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, eDepthStencilAttachmentOptimal = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, eDepthStencilReadOnlyOptimal = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL, eShaderReadOnlyOptimal = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, eTransferSrcOptimal = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, eTransferDstOptimal = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, ePreinitialized = VK_IMAGE_LAYOUT_PREINITIALIZED, ePresentSrcKHR = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, eSharedPresentKHR = VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR }; struct DescriptorImageInfo { DescriptorImageInfo( Sampler sampler_ = Sampler(), ImageView imageView_ = ImageView(), ImageLayout imageLayout_ = ImageLayout::eUndefined ) : sampler( sampler_ ) , imageView( imageView_ ) , imageLayout( imageLayout_ ) { } DescriptorImageInfo( VkDescriptorImageInfo const & rhs ) { memcpy( this, &rhs, sizeof(DescriptorImageInfo) ); } DescriptorImageInfo& operator=( VkDescriptorImageInfo const & rhs ) { memcpy( this, &rhs, sizeof(DescriptorImageInfo) ); return *this; } DescriptorImageInfo& setSampler( Sampler sampler_ ) { sampler = sampler_; return *this; } DescriptorImageInfo& setImageView( ImageView imageView_ ) { imageView = imageView_; return *this; } DescriptorImageInfo& setImageLayout( ImageLayout imageLayout_ ) { imageLayout = imageLayout_; return *this; } operator const VkDescriptorImageInfo&() const { return *reinterpret_cast(this); } bool operator==( DescriptorImageInfo const& rhs ) const { return ( sampler == rhs.sampler ) && ( imageView == rhs.imageView ) && ( imageLayout == rhs.imageLayout ); } bool operator!=( DescriptorImageInfo const& rhs ) const { return !operator==( rhs ); } Sampler sampler; ImageView imageView; ImageLayout imageLayout; }; static_assert( sizeof( DescriptorImageInfo ) == sizeof( VkDescriptorImageInfo ), "struct and wrapper have different size!" ); struct AttachmentReference { AttachmentReference( uint32_t attachment_ = 0, ImageLayout layout_ = ImageLayout::eUndefined ) : attachment( attachment_ ) , layout( layout_ ) { } AttachmentReference( VkAttachmentReference const & rhs ) { memcpy( this, &rhs, sizeof(AttachmentReference) ); } AttachmentReference& operator=( VkAttachmentReference const & rhs ) { memcpy( this, &rhs, sizeof(AttachmentReference) ); return *this; } AttachmentReference& setAttachment( uint32_t attachment_ ) { attachment = attachment_; return *this; } AttachmentReference& setLayout( ImageLayout layout_ ) { layout = layout_; return *this; } operator const VkAttachmentReference&() const { return *reinterpret_cast(this); } bool operator==( AttachmentReference const& rhs ) const { return ( attachment == rhs.attachment ) && ( layout == rhs.layout ); } bool operator!=( AttachmentReference const& rhs ) const { return !operator==( rhs ); } uint32_t attachment; ImageLayout layout; }; static_assert( sizeof( AttachmentReference ) == sizeof( VkAttachmentReference ), "struct and wrapper have different size!" ); enum class AttachmentLoadOp { eLoad = VK_ATTACHMENT_LOAD_OP_LOAD, eClear = VK_ATTACHMENT_LOAD_OP_CLEAR, eDontCare = VK_ATTACHMENT_LOAD_OP_DONT_CARE }; enum class AttachmentStoreOp { eStore = VK_ATTACHMENT_STORE_OP_STORE, eDontCare = VK_ATTACHMENT_STORE_OP_DONT_CARE }; enum class ImageType { e1D = VK_IMAGE_TYPE_1D, e2D = VK_IMAGE_TYPE_2D, e3D = VK_IMAGE_TYPE_3D }; enum class ImageTiling { eOptimal = VK_IMAGE_TILING_OPTIMAL, eLinear = VK_IMAGE_TILING_LINEAR }; enum class ImageViewType { e1D = VK_IMAGE_VIEW_TYPE_1D, e2D = VK_IMAGE_VIEW_TYPE_2D, e3D = VK_IMAGE_VIEW_TYPE_3D, eCube = VK_IMAGE_VIEW_TYPE_CUBE, e1DArray = VK_IMAGE_VIEW_TYPE_1D_ARRAY, e2DArray = VK_IMAGE_VIEW_TYPE_2D_ARRAY, eCubeArray = VK_IMAGE_VIEW_TYPE_CUBE_ARRAY }; enum class CommandBufferLevel { ePrimary = VK_COMMAND_BUFFER_LEVEL_PRIMARY, eSecondary = VK_COMMAND_BUFFER_LEVEL_SECONDARY }; enum class ComponentSwizzle { eIdentity = VK_COMPONENT_SWIZZLE_IDENTITY, eZero = VK_COMPONENT_SWIZZLE_ZERO, eOne = VK_COMPONENT_SWIZZLE_ONE, eR = VK_COMPONENT_SWIZZLE_R, eG = VK_COMPONENT_SWIZZLE_G, eB = VK_COMPONENT_SWIZZLE_B, eA = VK_COMPONENT_SWIZZLE_A }; struct ComponentMapping { ComponentMapping( ComponentSwizzle r_ = ComponentSwizzle::eIdentity, ComponentSwizzle g_ = ComponentSwizzle::eIdentity, ComponentSwizzle b_ = ComponentSwizzle::eIdentity, ComponentSwizzle a_ = ComponentSwizzle::eIdentity ) : r( r_ ) , g( g_ ) , b( b_ ) , a( a_ ) { } ComponentMapping( VkComponentMapping const & rhs ) { memcpy( this, &rhs, sizeof(ComponentMapping) ); } ComponentMapping& operator=( VkComponentMapping const & rhs ) { memcpy( this, &rhs, sizeof(ComponentMapping) ); return *this; } ComponentMapping& setR( ComponentSwizzle r_ ) { r = r_; return *this; } ComponentMapping& setG( ComponentSwizzle g_ ) { g = g_; return *this; } ComponentMapping& setB( ComponentSwizzle b_ ) { b = b_; return *this; } ComponentMapping& setA( ComponentSwizzle a_ ) { a = a_; return *this; } operator const VkComponentMapping&() const { return *reinterpret_cast(this); } bool operator==( ComponentMapping const& rhs ) const { return ( r == rhs.r ) && ( g == rhs.g ) && ( b == rhs.b ) && ( a == rhs.a ); } bool operator!=( ComponentMapping const& rhs ) const { return !operator==( rhs ); } ComponentSwizzle r; ComponentSwizzle g; ComponentSwizzle b; ComponentSwizzle a; }; static_assert( sizeof( ComponentMapping ) == sizeof( VkComponentMapping ), "struct and wrapper have different size!" ); enum class DescriptorType { eSampler = VK_DESCRIPTOR_TYPE_SAMPLER, eCombinedImageSampler = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, eSampledImage = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, eStorageImage = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, eUniformTexelBuffer = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, eStorageTexelBuffer = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, eUniformBuffer = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, eStorageBuffer = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, eUniformBufferDynamic = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, eStorageBufferDynamic = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, eInputAttachment = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT }; struct DescriptorPoolSize { DescriptorPoolSize( DescriptorType type_ = DescriptorType::eSampler, uint32_t descriptorCount_ = 0 ) : type( type_ ) , descriptorCount( descriptorCount_ ) { } DescriptorPoolSize( VkDescriptorPoolSize const & rhs ) { memcpy( this, &rhs, sizeof(DescriptorPoolSize) ); } DescriptorPoolSize& operator=( VkDescriptorPoolSize const & rhs ) { memcpy( this, &rhs, sizeof(DescriptorPoolSize) ); return *this; } DescriptorPoolSize& setType( DescriptorType type_ ) { type = type_; return *this; } DescriptorPoolSize& setDescriptorCount( uint32_t descriptorCount_ ) { descriptorCount = descriptorCount_; return *this; } operator const VkDescriptorPoolSize&() const { return *reinterpret_cast(this); } bool operator==( DescriptorPoolSize const& rhs ) const { return ( type == rhs.type ) && ( descriptorCount == rhs.descriptorCount ); } bool operator!=( DescriptorPoolSize const& rhs ) const { return !operator==( rhs ); } DescriptorType type; uint32_t descriptorCount; }; static_assert( sizeof( DescriptorPoolSize ) == sizeof( VkDescriptorPoolSize ), "struct and wrapper have different size!" ); struct DescriptorUpdateTemplateEntryKHR { DescriptorUpdateTemplateEntryKHR( uint32_t dstBinding_ = 0, uint32_t dstArrayElement_ = 0, uint32_t descriptorCount_ = 0, DescriptorType descriptorType_ = DescriptorType::eSampler, size_t offset_ = 0, size_t stride_ = 0 ) : dstBinding( dstBinding_ ) , dstArrayElement( dstArrayElement_ ) , descriptorCount( descriptorCount_ ) , descriptorType( descriptorType_ ) , offset( offset_ ) , stride( stride_ ) { } DescriptorUpdateTemplateEntryKHR( VkDescriptorUpdateTemplateEntryKHR const & rhs ) { memcpy( this, &rhs, sizeof(DescriptorUpdateTemplateEntryKHR) ); } DescriptorUpdateTemplateEntryKHR& operator=( VkDescriptorUpdateTemplateEntryKHR const & rhs ) { memcpy( this, &rhs, sizeof(DescriptorUpdateTemplateEntryKHR) ); return *this; } DescriptorUpdateTemplateEntryKHR& setDstBinding( uint32_t dstBinding_ ) { dstBinding = dstBinding_; return *this; } DescriptorUpdateTemplateEntryKHR& setDstArrayElement( uint32_t dstArrayElement_ ) { dstArrayElement = dstArrayElement_; return *this; } DescriptorUpdateTemplateEntryKHR& setDescriptorCount( uint32_t descriptorCount_ ) { descriptorCount = descriptorCount_; return *this; } DescriptorUpdateTemplateEntryKHR& setDescriptorType( DescriptorType descriptorType_ ) { descriptorType = descriptorType_; return *this; } DescriptorUpdateTemplateEntryKHR& setOffset( size_t offset_ ) { offset = offset_; return *this; } DescriptorUpdateTemplateEntryKHR& setStride( size_t stride_ ) { stride = stride_; return *this; } operator const VkDescriptorUpdateTemplateEntryKHR&() const { return *reinterpret_cast(this); } bool operator==( DescriptorUpdateTemplateEntryKHR const& rhs ) const { return ( dstBinding == rhs.dstBinding ) && ( dstArrayElement == rhs.dstArrayElement ) && ( descriptorCount == rhs.descriptorCount ) && ( descriptorType == rhs.descriptorType ) && ( offset == rhs.offset ) && ( stride == rhs.stride ); } bool operator!=( DescriptorUpdateTemplateEntryKHR const& rhs ) const { return !operator==( rhs ); } uint32_t dstBinding; uint32_t dstArrayElement; uint32_t descriptorCount; DescriptorType descriptorType; size_t offset; size_t stride; }; static_assert( sizeof( DescriptorUpdateTemplateEntryKHR ) == sizeof( VkDescriptorUpdateTemplateEntryKHR ), "struct and wrapper have different size!" ); enum class QueryType { eOcclusion = VK_QUERY_TYPE_OCCLUSION, ePipelineStatistics = VK_QUERY_TYPE_PIPELINE_STATISTICS, eTimestamp = VK_QUERY_TYPE_TIMESTAMP }; enum class BorderColor { eFloatTransparentBlack = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, eIntTransparentBlack = VK_BORDER_COLOR_INT_TRANSPARENT_BLACK, eFloatOpaqueBlack = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK, eIntOpaqueBlack = VK_BORDER_COLOR_INT_OPAQUE_BLACK, eFloatOpaqueWhite = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE, eIntOpaqueWhite = VK_BORDER_COLOR_INT_OPAQUE_WHITE }; enum class PipelineBindPoint { eGraphics = VK_PIPELINE_BIND_POINT_GRAPHICS, eCompute = VK_PIPELINE_BIND_POINT_COMPUTE }; enum class PipelineCacheHeaderVersion { eOne = VK_PIPELINE_CACHE_HEADER_VERSION_ONE }; enum class PrimitiveTopology { ePointList = VK_PRIMITIVE_TOPOLOGY_POINT_LIST, eLineList = VK_PRIMITIVE_TOPOLOGY_LINE_LIST, eLineStrip = VK_PRIMITIVE_TOPOLOGY_LINE_STRIP, eTriangleList = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, eTriangleStrip = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, eTriangleFan = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN, eLineListWithAdjacency = VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY, eLineStripWithAdjacency = VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY, eTriangleListWithAdjacency = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY, eTriangleStripWithAdjacency = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY, ePatchList = VK_PRIMITIVE_TOPOLOGY_PATCH_LIST }; enum class SharingMode { eExclusive = VK_SHARING_MODE_EXCLUSIVE, eConcurrent = VK_SHARING_MODE_CONCURRENT }; enum class IndexType { eUint16 = VK_INDEX_TYPE_UINT16, eUint32 = VK_INDEX_TYPE_UINT32 }; enum class Filter { eNearest = VK_FILTER_NEAREST, eLinear = VK_FILTER_LINEAR, eCubicIMG = VK_FILTER_CUBIC_IMG }; enum class SamplerMipmapMode { eNearest = VK_SAMPLER_MIPMAP_MODE_NEAREST, eLinear = VK_SAMPLER_MIPMAP_MODE_LINEAR }; enum class SamplerAddressMode { eRepeat = VK_SAMPLER_ADDRESS_MODE_REPEAT, eMirroredRepeat = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT, eClampToEdge = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, eClampToBorder = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, eMirrorClampToEdge = VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE }; enum class CompareOp { eNever = VK_COMPARE_OP_NEVER, eLess = VK_COMPARE_OP_LESS, eEqual = VK_COMPARE_OP_EQUAL, eLessOrEqual = VK_COMPARE_OP_LESS_OR_EQUAL, eGreater = VK_COMPARE_OP_GREATER, eNotEqual = VK_COMPARE_OP_NOT_EQUAL, eGreaterOrEqual = VK_COMPARE_OP_GREATER_OR_EQUAL, eAlways = VK_COMPARE_OP_ALWAYS }; enum class PolygonMode { eFill = VK_POLYGON_MODE_FILL, eLine = VK_POLYGON_MODE_LINE, ePoint = VK_POLYGON_MODE_POINT }; enum class CullModeFlagBits { eNone = VK_CULL_MODE_NONE, eFront = VK_CULL_MODE_FRONT_BIT, eBack = VK_CULL_MODE_BACK_BIT, eFrontAndBack = VK_CULL_MODE_FRONT_AND_BACK }; using CullModeFlags = Flags; VULKAN_HPP_INLINE CullModeFlags operator|( CullModeFlagBits bit0, CullModeFlagBits bit1 ) { return CullModeFlags( bit0 ) | bit1; } VULKAN_HPP_INLINE CullModeFlags operator~( CullModeFlagBits bits ) { return ~( CullModeFlags( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(CullModeFlagBits::eNone) | VkFlags(CullModeFlagBits::eFront) | VkFlags(CullModeFlagBits::eBack) | VkFlags(CullModeFlagBits::eFrontAndBack) }; }; enum class FrontFace { eCounterClockwise = VK_FRONT_FACE_COUNTER_CLOCKWISE, eClockwise = VK_FRONT_FACE_CLOCKWISE }; enum class BlendFactor { eZero = VK_BLEND_FACTOR_ZERO, eOne = VK_BLEND_FACTOR_ONE, eSrcColor = VK_BLEND_FACTOR_SRC_COLOR, eOneMinusSrcColor = VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR, eDstColor = VK_BLEND_FACTOR_DST_COLOR, eOneMinusDstColor = VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR, eSrcAlpha = VK_BLEND_FACTOR_SRC_ALPHA, eOneMinusSrcAlpha = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, eDstAlpha = VK_BLEND_FACTOR_DST_ALPHA, eOneMinusDstAlpha = VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA, eConstantColor = VK_BLEND_FACTOR_CONSTANT_COLOR, eOneMinusConstantColor = VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR, eConstantAlpha = VK_BLEND_FACTOR_CONSTANT_ALPHA, eOneMinusConstantAlpha = VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA, eSrcAlphaSaturate = VK_BLEND_FACTOR_SRC_ALPHA_SATURATE, eSrc1Color = VK_BLEND_FACTOR_SRC1_COLOR, eOneMinusSrc1Color = VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR, eSrc1Alpha = VK_BLEND_FACTOR_SRC1_ALPHA, eOneMinusSrc1Alpha = VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA }; enum class BlendOp { eAdd = VK_BLEND_OP_ADD, eSubtract = VK_BLEND_OP_SUBTRACT, eReverseSubtract = VK_BLEND_OP_REVERSE_SUBTRACT, eMin = VK_BLEND_OP_MIN, eMax = VK_BLEND_OP_MAX }; enum class StencilOp { eKeep = VK_STENCIL_OP_KEEP, eZero = VK_STENCIL_OP_ZERO, eReplace = VK_STENCIL_OP_REPLACE, eIncrementAndClamp = VK_STENCIL_OP_INCREMENT_AND_CLAMP, eDecrementAndClamp = VK_STENCIL_OP_DECREMENT_AND_CLAMP, eInvert = VK_STENCIL_OP_INVERT, eIncrementAndWrap = VK_STENCIL_OP_INCREMENT_AND_WRAP, eDecrementAndWrap = VK_STENCIL_OP_DECREMENT_AND_WRAP }; struct StencilOpState { StencilOpState( StencilOp failOp_ = StencilOp::eKeep, StencilOp passOp_ = StencilOp::eKeep, StencilOp depthFailOp_ = StencilOp::eKeep, CompareOp compareOp_ = CompareOp::eNever, uint32_t compareMask_ = 0, uint32_t writeMask_ = 0, uint32_t reference_ = 0 ) : failOp( failOp_ ) , passOp( passOp_ ) , depthFailOp( depthFailOp_ ) , compareOp( compareOp_ ) , compareMask( compareMask_ ) , writeMask( writeMask_ ) , reference( reference_ ) { } StencilOpState( VkStencilOpState const & rhs ) { memcpy( this, &rhs, sizeof(StencilOpState) ); } StencilOpState& operator=( VkStencilOpState const & rhs ) { memcpy( this, &rhs, sizeof(StencilOpState) ); return *this; } StencilOpState& setFailOp( StencilOp failOp_ ) { failOp = failOp_; return *this; } StencilOpState& setPassOp( StencilOp passOp_ ) { passOp = passOp_; return *this; } StencilOpState& setDepthFailOp( StencilOp depthFailOp_ ) { depthFailOp = depthFailOp_; return *this; } StencilOpState& setCompareOp( CompareOp compareOp_ ) { compareOp = compareOp_; return *this; } StencilOpState& setCompareMask( uint32_t compareMask_ ) { compareMask = compareMask_; return *this; } StencilOpState& setWriteMask( uint32_t writeMask_ ) { writeMask = writeMask_; return *this; } StencilOpState& setReference( uint32_t reference_ ) { reference = reference_; return *this; } operator const VkStencilOpState&() const { return *reinterpret_cast(this); } bool operator==( StencilOpState const& rhs ) const { return ( failOp == rhs.failOp ) && ( passOp == rhs.passOp ) && ( depthFailOp == rhs.depthFailOp ) && ( compareOp == rhs.compareOp ) && ( compareMask == rhs.compareMask ) && ( writeMask == rhs.writeMask ) && ( reference == rhs.reference ); } bool operator!=( StencilOpState const& rhs ) const { return !operator==( rhs ); } StencilOp failOp; StencilOp passOp; StencilOp depthFailOp; CompareOp compareOp; uint32_t compareMask; uint32_t writeMask; uint32_t reference; }; static_assert( sizeof( StencilOpState ) == sizeof( VkStencilOpState ), "struct and wrapper have different size!" ); enum class LogicOp { eClear = VK_LOGIC_OP_CLEAR, eAnd = VK_LOGIC_OP_AND, eAndReverse = VK_LOGIC_OP_AND_REVERSE, eCopy = VK_LOGIC_OP_COPY, eAndInverted = VK_LOGIC_OP_AND_INVERTED, eNoOp = VK_LOGIC_OP_NO_OP, eXor = VK_LOGIC_OP_XOR, eOr = VK_LOGIC_OP_OR, eNor = VK_LOGIC_OP_NOR, eEquivalent = VK_LOGIC_OP_EQUIVALENT, eInvert = VK_LOGIC_OP_INVERT, eOrReverse = VK_LOGIC_OP_OR_REVERSE, eCopyInverted = VK_LOGIC_OP_COPY_INVERTED, eOrInverted = VK_LOGIC_OP_OR_INVERTED, eNand = VK_LOGIC_OP_NAND, eSet = VK_LOGIC_OP_SET }; enum class InternalAllocationType { eExecutable = VK_INTERNAL_ALLOCATION_TYPE_EXECUTABLE }; enum class SystemAllocationScope { eCommand = VK_SYSTEM_ALLOCATION_SCOPE_COMMAND, eObject = VK_SYSTEM_ALLOCATION_SCOPE_OBJECT, eCache = VK_SYSTEM_ALLOCATION_SCOPE_CACHE, eDevice = VK_SYSTEM_ALLOCATION_SCOPE_DEVICE, eInstance = VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE }; enum class PhysicalDeviceType { eOther = VK_PHYSICAL_DEVICE_TYPE_OTHER, eIntegratedGpu = VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU, eDiscreteGpu = VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU, eVirtualGpu = VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU, eCpu = VK_PHYSICAL_DEVICE_TYPE_CPU }; enum class VertexInputRate { eVertex = VK_VERTEX_INPUT_RATE_VERTEX, eInstance = VK_VERTEX_INPUT_RATE_INSTANCE }; struct VertexInputBindingDescription { VertexInputBindingDescription( uint32_t binding_ = 0, uint32_t stride_ = 0, VertexInputRate inputRate_ = VertexInputRate::eVertex ) : binding( binding_ ) , stride( stride_ ) , inputRate( inputRate_ ) { } VertexInputBindingDescription( VkVertexInputBindingDescription const & rhs ) { memcpy( this, &rhs, sizeof(VertexInputBindingDescription) ); } VertexInputBindingDescription& operator=( VkVertexInputBindingDescription const & rhs ) { memcpy( this, &rhs, sizeof(VertexInputBindingDescription) ); return *this; } VertexInputBindingDescription& setBinding( uint32_t binding_ ) { binding = binding_; return *this; } VertexInputBindingDescription& setStride( uint32_t stride_ ) { stride = stride_; return *this; } VertexInputBindingDescription& setInputRate( VertexInputRate inputRate_ ) { inputRate = inputRate_; return *this; } operator const VkVertexInputBindingDescription&() const { return *reinterpret_cast(this); } bool operator==( VertexInputBindingDescription const& rhs ) const { return ( binding == rhs.binding ) && ( stride == rhs.stride ) && ( inputRate == rhs.inputRate ); } bool operator!=( VertexInputBindingDescription const& rhs ) const { return !operator==( rhs ); } uint32_t binding; uint32_t stride; VertexInputRate inputRate; }; static_assert( sizeof( VertexInputBindingDescription ) == sizeof( VkVertexInputBindingDescription ), "struct and wrapper have different size!" ); enum class Format { eUndefined = VK_FORMAT_UNDEFINED, eR4G4UnormPack8 = VK_FORMAT_R4G4_UNORM_PACK8, eR4G4B4A4UnormPack16 = VK_FORMAT_R4G4B4A4_UNORM_PACK16, eB4G4R4A4UnormPack16 = VK_FORMAT_B4G4R4A4_UNORM_PACK16, eR5G6B5UnormPack16 = VK_FORMAT_R5G6B5_UNORM_PACK16, eB5G6R5UnormPack16 = VK_FORMAT_B5G6R5_UNORM_PACK16, eR5G5B5A1UnormPack16 = VK_FORMAT_R5G5B5A1_UNORM_PACK16, eB5G5R5A1UnormPack16 = VK_FORMAT_B5G5R5A1_UNORM_PACK16, eA1R5G5B5UnormPack16 = VK_FORMAT_A1R5G5B5_UNORM_PACK16, eR8Unorm = VK_FORMAT_R8_UNORM, eR8Snorm = VK_FORMAT_R8_SNORM, eR8Uscaled = VK_FORMAT_R8_USCALED, eR8Sscaled = VK_FORMAT_R8_SSCALED, eR8Uint = VK_FORMAT_R8_UINT, eR8Sint = VK_FORMAT_R8_SINT, eR8Srgb = VK_FORMAT_R8_SRGB, eR8G8Unorm = VK_FORMAT_R8G8_UNORM, eR8G8Snorm = VK_FORMAT_R8G8_SNORM, eR8G8Uscaled = VK_FORMAT_R8G8_USCALED, eR8G8Sscaled = VK_FORMAT_R8G8_SSCALED, eR8G8Uint = VK_FORMAT_R8G8_UINT, eR8G8Sint = VK_FORMAT_R8G8_SINT, eR8G8Srgb = VK_FORMAT_R8G8_SRGB, eR8G8B8Unorm = VK_FORMAT_R8G8B8_UNORM, eR8G8B8Snorm = VK_FORMAT_R8G8B8_SNORM, eR8G8B8Uscaled = VK_FORMAT_R8G8B8_USCALED, eR8G8B8Sscaled = VK_FORMAT_R8G8B8_SSCALED, eR8G8B8Uint = VK_FORMAT_R8G8B8_UINT, eR8G8B8Sint = VK_FORMAT_R8G8B8_SINT, eR8G8B8Srgb = VK_FORMAT_R8G8B8_SRGB, eB8G8R8Unorm = VK_FORMAT_B8G8R8_UNORM, eB8G8R8Snorm = VK_FORMAT_B8G8R8_SNORM, eB8G8R8Uscaled = VK_FORMAT_B8G8R8_USCALED, eB8G8R8Sscaled = VK_FORMAT_B8G8R8_SSCALED, eB8G8R8Uint = VK_FORMAT_B8G8R8_UINT, eB8G8R8Sint = VK_FORMAT_B8G8R8_SINT, eB8G8R8Srgb = VK_FORMAT_B8G8R8_SRGB, eR8G8B8A8Unorm = VK_FORMAT_R8G8B8A8_UNORM, eR8G8B8A8Snorm = VK_FORMAT_R8G8B8A8_SNORM, eR8G8B8A8Uscaled = VK_FORMAT_R8G8B8A8_USCALED, eR8G8B8A8Sscaled = VK_FORMAT_R8G8B8A8_SSCALED, eR8G8B8A8Uint = VK_FORMAT_R8G8B8A8_UINT, eR8G8B8A8Sint = VK_FORMAT_R8G8B8A8_SINT, eR8G8B8A8Srgb = VK_FORMAT_R8G8B8A8_SRGB, eB8G8R8A8Unorm = VK_FORMAT_B8G8R8A8_UNORM, eB8G8R8A8Snorm = VK_FORMAT_B8G8R8A8_SNORM, eB8G8R8A8Uscaled = VK_FORMAT_B8G8R8A8_USCALED, eB8G8R8A8Sscaled = VK_FORMAT_B8G8R8A8_SSCALED, eB8G8R8A8Uint = VK_FORMAT_B8G8R8A8_UINT, eB8G8R8A8Sint = VK_FORMAT_B8G8R8A8_SINT, eB8G8R8A8Srgb = VK_FORMAT_B8G8R8A8_SRGB, eA8B8G8R8UnormPack32 = VK_FORMAT_A8B8G8R8_UNORM_PACK32, eA8B8G8R8SnormPack32 = VK_FORMAT_A8B8G8R8_SNORM_PACK32, eA8B8G8R8UscaledPack32 = VK_FORMAT_A8B8G8R8_USCALED_PACK32, eA8B8G8R8SscaledPack32 = VK_FORMAT_A8B8G8R8_SSCALED_PACK32, eA8B8G8R8UintPack32 = VK_FORMAT_A8B8G8R8_UINT_PACK32, eA8B8G8R8SintPack32 = VK_FORMAT_A8B8G8R8_SINT_PACK32, eA8B8G8R8SrgbPack32 = VK_FORMAT_A8B8G8R8_SRGB_PACK32, eA2R10G10B10UnormPack32 = VK_FORMAT_A2R10G10B10_UNORM_PACK32, eA2R10G10B10SnormPack32 = VK_FORMAT_A2R10G10B10_SNORM_PACK32, eA2R10G10B10UscaledPack32 = VK_FORMAT_A2R10G10B10_USCALED_PACK32, eA2R10G10B10SscaledPack32 = VK_FORMAT_A2R10G10B10_SSCALED_PACK32, eA2R10G10B10UintPack32 = VK_FORMAT_A2R10G10B10_UINT_PACK32, eA2R10G10B10SintPack32 = VK_FORMAT_A2R10G10B10_SINT_PACK32, eA2B10G10R10UnormPack32 = VK_FORMAT_A2B10G10R10_UNORM_PACK32, eA2B10G10R10SnormPack32 = VK_FORMAT_A2B10G10R10_SNORM_PACK32, eA2B10G10R10UscaledPack32 = VK_FORMAT_A2B10G10R10_USCALED_PACK32, eA2B10G10R10SscaledPack32 = VK_FORMAT_A2B10G10R10_SSCALED_PACK32, eA2B10G10R10UintPack32 = VK_FORMAT_A2B10G10R10_UINT_PACK32, eA2B10G10R10SintPack32 = VK_FORMAT_A2B10G10R10_SINT_PACK32, eR16Unorm = VK_FORMAT_R16_UNORM, eR16Snorm = VK_FORMAT_R16_SNORM, eR16Uscaled = VK_FORMAT_R16_USCALED, eR16Sscaled = VK_FORMAT_R16_SSCALED, eR16Uint = VK_FORMAT_R16_UINT, eR16Sint = VK_FORMAT_R16_SINT, eR16Sfloat = VK_FORMAT_R16_SFLOAT, eR16G16Unorm = VK_FORMAT_R16G16_UNORM, eR16G16Snorm = VK_FORMAT_R16G16_SNORM, eR16G16Uscaled = VK_FORMAT_R16G16_USCALED, eR16G16Sscaled = VK_FORMAT_R16G16_SSCALED, eR16G16Uint = VK_FORMAT_R16G16_UINT, eR16G16Sint = VK_FORMAT_R16G16_SINT, eR16G16Sfloat = VK_FORMAT_R16G16_SFLOAT, eR16G16B16Unorm = VK_FORMAT_R16G16B16_UNORM, eR16G16B16Snorm = VK_FORMAT_R16G16B16_SNORM, eR16G16B16Uscaled = VK_FORMAT_R16G16B16_USCALED, eR16G16B16Sscaled = VK_FORMAT_R16G16B16_SSCALED, eR16G16B16Uint = VK_FORMAT_R16G16B16_UINT, eR16G16B16Sint = VK_FORMAT_R16G16B16_SINT, eR16G16B16Sfloat = VK_FORMAT_R16G16B16_SFLOAT, eR16G16B16A16Unorm = VK_FORMAT_R16G16B16A16_UNORM, eR16G16B16A16Snorm = VK_FORMAT_R16G16B16A16_SNORM, eR16G16B16A16Uscaled = VK_FORMAT_R16G16B16A16_USCALED, eR16G16B16A16Sscaled = VK_FORMAT_R16G16B16A16_SSCALED, eR16G16B16A16Uint = VK_FORMAT_R16G16B16A16_UINT, eR16G16B16A16Sint = VK_FORMAT_R16G16B16A16_SINT, eR16G16B16A16Sfloat = VK_FORMAT_R16G16B16A16_SFLOAT, eR32Uint = VK_FORMAT_R32_UINT, eR32Sint = VK_FORMAT_R32_SINT, eR32Sfloat = VK_FORMAT_R32_SFLOAT, eR32G32Uint = VK_FORMAT_R32G32_UINT, eR32G32Sint = VK_FORMAT_R32G32_SINT, eR32G32Sfloat = VK_FORMAT_R32G32_SFLOAT, eR32G32B32Uint = VK_FORMAT_R32G32B32_UINT, eR32G32B32Sint = VK_FORMAT_R32G32B32_SINT, eR32G32B32Sfloat = VK_FORMAT_R32G32B32_SFLOAT, eR32G32B32A32Uint = VK_FORMAT_R32G32B32A32_UINT, eR32G32B32A32Sint = VK_FORMAT_R32G32B32A32_SINT, eR32G32B32A32Sfloat = VK_FORMAT_R32G32B32A32_SFLOAT, eR64Uint = VK_FORMAT_R64_UINT, eR64Sint = VK_FORMAT_R64_SINT, eR64Sfloat = VK_FORMAT_R64_SFLOAT, eR64G64Uint = VK_FORMAT_R64G64_UINT, eR64G64Sint = VK_FORMAT_R64G64_SINT, eR64G64Sfloat = VK_FORMAT_R64G64_SFLOAT, eR64G64B64Uint = VK_FORMAT_R64G64B64_UINT, eR64G64B64Sint = VK_FORMAT_R64G64B64_SINT, eR64G64B64Sfloat = VK_FORMAT_R64G64B64_SFLOAT, eR64G64B64A64Uint = VK_FORMAT_R64G64B64A64_UINT, eR64G64B64A64Sint = VK_FORMAT_R64G64B64A64_SINT, eR64G64B64A64Sfloat = VK_FORMAT_R64G64B64A64_SFLOAT, eB10G11R11UfloatPack32 = VK_FORMAT_B10G11R11_UFLOAT_PACK32, eE5B9G9R9UfloatPack32 = VK_FORMAT_E5B9G9R9_UFLOAT_PACK32, eD16Unorm = VK_FORMAT_D16_UNORM, eX8D24UnormPack32 = VK_FORMAT_X8_D24_UNORM_PACK32, eD32Sfloat = VK_FORMAT_D32_SFLOAT, eS8Uint = VK_FORMAT_S8_UINT, eD16UnormS8Uint = VK_FORMAT_D16_UNORM_S8_UINT, eD24UnormS8Uint = VK_FORMAT_D24_UNORM_S8_UINT, eD32SfloatS8Uint = VK_FORMAT_D32_SFLOAT_S8_UINT, eBc1RgbUnormBlock = VK_FORMAT_BC1_RGB_UNORM_BLOCK, eBc1RgbSrgbBlock = VK_FORMAT_BC1_RGB_SRGB_BLOCK, eBc1RgbaUnormBlock = VK_FORMAT_BC1_RGBA_UNORM_BLOCK, eBc1RgbaSrgbBlock = VK_FORMAT_BC1_RGBA_SRGB_BLOCK, eBc2UnormBlock = VK_FORMAT_BC2_UNORM_BLOCK, eBc2SrgbBlock = VK_FORMAT_BC2_SRGB_BLOCK, eBc3UnormBlock = VK_FORMAT_BC3_UNORM_BLOCK, eBc3SrgbBlock = VK_FORMAT_BC3_SRGB_BLOCK, eBc4UnormBlock = VK_FORMAT_BC4_UNORM_BLOCK, eBc4SnormBlock = VK_FORMAT_BC4_SNORM_BLOCK, eBc5UnormBlock = VK_FORMAT_BC5_UNORM_BLOCK, eBc5SnormBlock = VK_FORMAT_BC5_SNORM_BLOCK, eBc6HUfloatBlock = VK_FORMAT_BC6H_UFLOAT_BLOCK, eBc6HSfloatBlock = VK_FORMAT_BC6H_SFLOAT_BLOCK, eBc7UnormBlock = VK_FORMAT_BC7_UNORM_BLOCK, eBc7SrgbBlock = VK_FORMAT_BC7_SRGB_BLOCK, eEtc2R8G8B8UnormBlock = VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK, eEtc2R8G8B8SrgbBlock = VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK, eEtc2R8G8B8A1UnormBlock = VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK, eEtc2R8G8B8A1SrgbBlock = VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK, eEtc2R8G8B8A8UnormBlock = VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK, eEtc2R8G8B8A8SrgbBlock = VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK, eEacR11UnormBlock = VK_FORMAT_EAC_R11_UNORM_BLOCK, eEacR11SnormBlock = VK_FORMAT_EAC_R11_SNORM_BLOCK, eEacR11G11UnormBlock = VK_FORMAT_EAC_R11G11_UNORM_BLOCK, eEacR11G11SnormBlock = VK_FORMAT_EAC_R11G11_SNORM_BLOCK, eAstc4x4UnormBlock = VK_FORMAT_ASTC_4x4_UNORM_BLOCK, eAstc4x4SrgbBlock = VK_FORMAT_ASTC_4x4_SRGB_BLOCK, eAstc5x4UnormBlock = VK_FORMAT_ASTC_5x4_UNORM_BLOCK, eAstc5x4SrgbBlock = VK_FORMAT_ASTC_5x4_SRGB_BLOCK, eAstc5x5UnormBlock = VK_FORMAT_ASTC_5x5_UNORM_BLOCK, eAstc5x5SrgbBlock = VK_FORMAT_ASTC_5x5_SRGB_BLOCK, eAstc6x5UnormBlock = VK_FORMAT_ASTC_6x5_UNORM_BLOCK, eAstc6x5SrgbBlock = VK_FORMAT_ASTC_6x5_SRGB_BLOCK, eAstc6x6UnormBlock = VK_FORMAT_ASTC_6x6_UNORM_BLOCK, eAstc6x6SrgbBlock = VK_FORMAT_ASTC_6x6_SRGB_BLOCK, eAstc8x5UnormBlock = VK_FORMAT_ASTC_8x5_UNORM_BLOCK, eAstc8x5SrgbBlock = VK_FORMAT_ASTC_8x5_SRGB_BLOCK, eAstc8x6UnormBlock = VK_FORMAT_ASTC_8x6_UNORM_BLOCK, eAstc8x6SrgbBlock = VK_FORMAT_ASTC_8x6_SRGB_BLOCK, eAstc8x8UnormBlock = VK_FORMAT_ASTC_8x8_UNORM_BLOCK, eAstc8x8SrgbBlock = VK_FORMAT_ASTC_8x8_SRGB_BLOCK, eAstc10x5UnormBlock = VK_FORMAT_ASTC_10x5_UNORM_BLOCK, eAstc10x5SrgbBlock = VK_FORMAT_ASTC_10x5_SRGB_BLOCK, eAstc10x6UnormBlock = VK_FORMAT_ASTC_10x6_UNORM_BLOCK, eAstc10x6SrgbBlock = VK_FORMAT_ASTC_10x6_SRGB_BLOCK, eAstc10x8UnormBlock = VK_FORMAT_ASTC_10x8_UNORM_BLOCK, eAstc10x8SrgbBlock = VK_FORMAT_ASTC_10x8_SRGB_BLOCK, eAstc10x10UnormBlock = VK_FORMAT_ASTC_10x10_UNORM_BLOCK, eAstc10x10SrgbBlock = VK_FORMAT_ASTC_10x10_SRGB_BLOCK, eAstc12x10UnormBlock = VK_FORMAT_ASTC_12x10_UNORM_BLOCK, eAstc12x10SrgbBlock = VK_FORMAT_ASTC_12x10_SRGB_BLOCK, eAstc12x12UnormBlock = VK_FORMAT_ASTC_12x12_UNORM_BLOCK, eAstc12x12SrgbBlock = VK_FORMAT_ASTC_12x12_SRGB_BLOCK, ePvrtc12BppUnormBlockIMG = VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG, ePvrtc14BppUnormBlockIMG = VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG, ePvrtc22BppUnormBlockIMG = VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG, ePvrtc24BppUnormBlockIMG = VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG, ePvrtc12BppSrgbBlockIMG = VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG, ePvrtc14BppSrgbBlockIMG = VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG, ePvrtc22BppSrgbBlockIMG = VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG, ePvrtc24BppSrgbBlockIMG = VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG }; struct VertexInputAttributeDescription { VertexInputAttributeDescription( uint32_t location_ = 0, uint32_t binding_ = 0, Format format_ = Format::eUndefined, uint32_t offset_ = 0 ) : location( location_ ) , binding( binding_ ) , format( format_ ) , offset( offset_ ) { } VertexInputAttributeDescription( VkVertexInputAttributeDescription const & rhs ) { memcpy( this, &rhs, sizeof(VertexInputAttributeDescription) ); } VertexInputAttributeDescription& operator=( VkVertexInputAttributeDescription const & rhs ) { memcpy( this, &rhs, sizeof(VertexInputAttributeDescription) ); return *this; } VertexInputAttributeDescription& setLocation( uint32_t location_ ) { location = location_; return *this; } VertexInputAttributeDescription& setBinding( uint32_t binding_ ) { binding = binding_; return *this; } VertexInputAttributeDescription& setFormat( Format format_ ) { format = format_; return *this; } VertexInputAttributeDescription& setOffset( uint32_t offset_ ) { offset = offset_; return *this; } operator const VkVertexInputAttributeDescription&() const { return *reinterpret_cast(this); } bool operator==( VertexInputAttributeDescription const& rhs ) const { return ( location == rhs.location ) && ( binding == rhs.binding ) && ( format == rhs.format ) && ( offset == rhs.offset ); } bool operator!=( VertexInputAttributeDescription const& rhs ) const { return !operator==( rhs ); } uint32_t location; uint32_t binding; Format format; uint32_t offset; }; static_assert( sizeof( VertexInputAttributeDescription ) == sizeof( VkVertexInputAttributeDescription ), "struct and wrapper have different size!" ); enum class StructureType { eApplicationInfo = VK_STRUCTURE_TYPE_APPLICATION_INFO, eInstanceCreateInfo = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, eDeviceQueueCreateInfo = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, eDeviceCreateInfo = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, eSubmitInfo = VK_STRUCTURE_TYPE_SUBMIT_INFO, eMemoryAllocateInfo = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, eMappedMemoryRange = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, eBindSparseInfo = VK_STRUCTURE_TYPE_BIND_SPARSE_INFO, eFenceCreateInfo = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, eSemaphoreCreateInfo = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, eEventCreateInfo = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO, eQueryPoolCreateInfo = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO, eBufferCreateInfo = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, eBufferViewCreateInfo = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO, eImageCreateInfo = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, eImageViewCreateInfo = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, eShaderModuleCreateInfo = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, ePipelineCacheCreateInfo = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO, ePipelineShaderStageCreateInfo = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, ePipelineVertexInputStateCreateInfo = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, ePipelineInputAssemblyStateCreateInfo = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, ePipelineTessellationStateCreateInfo = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO, ePipelineViewportStateCreateInfo = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, ePipelineRasterizationStateCreateInfo = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, ePipelineMultisampleStateCreateInfo = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, ePipelineDepthStencilStateCreateInfo = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, ePipelineColorBlendStateCreateInfo = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, ePipelineDynamicStateCreateInfo = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, eGraphicsPipelineCreateInfo = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, eComputePipelineCreateInfo = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, ePipelineLayoutCreateInfo = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, eSamplerCreateInfo = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, eDescriptorSetLayoutCreateInfo = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, eDescriptorPoolCreateInfo = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, eDescriptorSetAllocateInfo = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, eWriteDescriptorSet = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, eCopyDescriptorSet = VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET, eFramebufferCreateInfo = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, eRenderPassCreateInfo = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, eCommandPoolCreateInfo = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, eCommandBufferAllocateInfo = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, eCommandBufferInheritanceInfo = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO, eCommandBufferBeginInfo = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, eRenderPassBeginInfo = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, eBufferMemoryBarrier = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, eImageMemoryBarrier = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, eMemoryBarrier = VK_STRUCTURE_TYPE_MEMORY_BARRIER, eLoaderInstanceCreateInfo = VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO, eLoaderDeviceCreateInfo = VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO, eSwapchainCreateInfoKHR = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, ePresentInfoKHR = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, eDisplayModeCreateInfoKHR = VK_STRUCTURE_TYPE_DISPLAY_MODE_CREATE_INFO_KHR, eDisplaySurfaceCreateInfoKHR = VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR, eDisplayPresentInfoKHR = VK_STRUCTURE_TYPE_DISPLAY_PRESENT_INFO_KHR, eXlibSurfaceCreateInfoKHR = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, eXcbSurfaceCreateInfoKHR = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR, eWaylandSurfaceCreateInfoKHR = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR, eMirSurfaceCreateInfoKHR = VK_STRUCTURE_TYPE_MIR_SURFACE_CREATE_INFO_KHR, eAndroidSurfaceCreateInfoKHR = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR, eWin32SurfaceCreateInfoKHR = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, eDebugReportCallbackCreateInfoEXT = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT, ePipelineRasterizationStateRasterizationOrderAMD = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_RASTERIZATION_ORDER_AMD, eDebugMarkerObjectNameInfoEXT = VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT, eDebugMarkerObjectTagInfoEXT = VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_TAG_INFO_EXT, eDebugMarkerMarkerInfoEXT = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT, eDedicatedAllocationImageCreateInfoNV = VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_IMAGE_CREATE_INFO_NV, eDedicatedAllocationBufferCreateInfoNV = VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_BUFFER_CREATE_INFO_NV, eDedicatedAllocationMemoryAllocateInfoNV = VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_MEMORY_ALLOCATE_INFO_NV, eRenderPassMultiviewCreateInfoKHX = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO_KHX, ePhysicalDeviceMultiviewFeaturesKHX = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES_KHX, ePhysicalDeviceMultiviewPropertiesKHX = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES_KHX, eExternalMemoryImageCreateInfoNV = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_NV, eExportMemoryAllocateInfoNV = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_NV, eImportMemoryWin32HandleInfoNV = VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_NV, eExportMemoryWin32HandleInfoNV = VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_NV, eWin32KeyedMutexAcquireReleaseInfoNV = VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_NV, ePhysicalDeviceFeatures2KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR, ePhysicalDeviceProperties2KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR, eFormatProperties2KHR = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR, eImageFormatProperties2KHR = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR, ePhysicalDeviceImageFormatInfo2KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR, eQueueFamilyProperties2KHR = VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2_KHR, ePhysicalDeviceMemoryProperties2KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR, eSparseImageFormatProperties2KHR = VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2_KHR, ePhysicalDeviceSparseImageFormatInfo2KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2_KHR, eMemoryAllocateFlagsInfoKHX = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHX, eBindBufferMemoryInfoKHX = VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHX, eBindImageMemoryInfoKHX = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHX, eDeviceGroupRenderPassBeginInfoKHX = VK_STRUCTURE_TYPE_DEVICE_GROUP_RENDER_PASS_BEGIN_INFO_KHX, eDeviceGroupCommandBufferBeginInfoKHX = VK_STRUCTURE_TYPE_DEVICE_GROUP_COMMAND_BUFFER_BEGIN_INFO_KHX, eDeviceGroupSubmitInfoKHX = VK_STRUCTURE_TYPE_DEVICE_GROUP_SUBMIT_INFO_KHX, eDeviceGroupBindSparseInfoKHX = VK_STRUCTURE_TYPE_DEVICE_GROUP_BIND_SPARSE_INFO_KHX, eDeviceGroupPresentCapabilitiesKHX = VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_CAPABILITIES_KHX, eImageSwapchainCreateInfoKHX = VK_STRUCTURE_TYPE_IMAGE_SWAPCHAIN_CREATE_INFO_KHX, eBindImageMemorySwapchainInfoKHX = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_SWAPCHAIN_INFO_KHX, eAcquireNextImageInfoKHX = VK_STRUCTURE_TYPE_ACQUIRE_NEXT_IMAGE_INFO_KHX, eDeviceGroupPresentInfoKHX = VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_INFO_KHX, eDeviceGroupSwapchainCreateInfoKHX = VK_STRUCTURE_TYPE_DEVICE_GROUP_SWAPCHAIN_CREATE_INFO_KHX, eValidationFlagsEXT = VK_STRUCTURE_TYPE_VALIDATION_FLAGS_EXT, eViSurfaceCreateInfoNN = VK_STRUCTURE_TYPE_VI_SURFACE_CREATE_INFO_NN, ePhysicalDeviceGroupPropertiesKHX = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES_KHX, eDeviceGroupDeviceCreateInfoKHX = VK_STRUCTURE_TYPE_DEVICE_GROUP_DEVICE_CREATE_INFO_KHX, ePhysicalDeviceExternalImageFormatInfoKHX = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO_KHX, eExternalImageFormatPropertiesKHX = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES_KHX, ePhysicalDeviceExternalBufferInfoKHX = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO_KHX, eExternalBufferPropertiesKHX = VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES_KHX, ePhysicalDeviceIdPropertiesKHX = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES_KHX, eExternalMemoryBufferCreateInfoKHX = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHX, eExternalMemoryImageCreateInfoKHX = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHX, eExportMemoryAllocateInfoKHX = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHX, eImportMemoryWin32HandleInfoKHX = VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHX, eExportMemoryWin32HandleInfoKHX = VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_KHX, eMemoryWin32HandlePropertiesKHX = VK_STRUCTURE_TYPE_MEMORY_WIN32_HANDLE_PROPERTIES_KHX, eImportMemoryFdInfoKHX = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHX, eMemoryFdPropertiesKHX = VK_STRUCTURE_TYPE_MEMORY_FD_PROPERTIES_KHX, eWin32KeyedMutexAcquireReleaseInfoKHX = VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_KHX, ePhysicalDeviceExternalSemaphoreInfoKHX = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO_KHX, eExternalSemaphorePropertiesKHX = VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES_KHX, eExportSemaphoreCreateInfoKHX = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO_KHX, eImportSemaphoreWin32HandleInfoKHX = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHX, eExportSemaphoreWin32HandleInfoKHX = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHX, eD3D12FenceSubmitInfoKHX = VK_STRUCTURE_TYPE_D3D12_FENCE_SUBMIT_INFO_KHX, eImportSemaphoreFdInfoKHX = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHX, ePhysicalDevicePushDescriptorPropertiesKHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR, ePresentRegionsKHR = VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR, eDescriptorUpdateTemplateCreateInfoKHR = VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR, eObjectTableCreateInfoNVX = VK_STRUCTURE_TYPE_OBJECT_TABLE_CREATE_INFO_NVX, eIndirectCommandsLayoutCreateInfoNVX = VK_STRUCTURE_TYPE_INDIRECT_COMMANDS_LAYOUT_CREATE_INFO_NVX, eCmdProcessCommandsInfoNVX = VK_STRUCTURE_TYPE_CMD_PROCESS_COMMANDS_INFO_NVX, eCmdReserveSpaceForCommandsInfoNVX = VK_STRUCTURE_TYPE_CMD_RESERVE_SPACE_FOR_COMMANDS_INFO_NVX, eDeviceGeneratedCommandsLimitsNVX = VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_LIMITS_NVX, eDeviceGeneratedCommandsFeaturesNVX = VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_FEATURES_NVX, ePipelineViewportWScalingStateCreateInfoNV = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_W_SCALING_STATE_CREATE_INFO_NV, eSurfaceCapabilities2EXT = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES2_EXT, eDisplayPowerInfoEXT = VK_STRUCTURE_TYPE_DISPLAY_POWER_INFO_EXT, eDeviceEventInfoEXT = VK_STRUCTURE_TYPE_DEVICE_EVENT_INFO_EXT, eDisplayEventInfoEXT = VK_STRUCTURE_TYPE_DISPLAY_EVENT_INFO_EXT, eSwapchainCounterCreateInfoEXT = VK_STRUCTURE_TYPE_SWAPCHAIN_COUNTER_CREATE_INFO_EXT, ePresentTimesInfoGOOGLE = VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE, ePhysicalDeviceMultiviewPerViewAttributesPropertiesNVX = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PER_VIEW_ATTRIBUTES_PROPERTIES_NVX, ePipelineViewportSwizzleStateCreateInfoNV = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_SWIZZLE_STATE_CREATE_INFO_NV, ePhysicalDeviceDiscardRectanglePropertiesEXT = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DISCARD_RECTANGLE_PROPERTIES_EXT, ePipelineDiscardRectangleStateCreateInfoEXT = VK_STRUCTURE_TYPE_PIPELINE_DISCARD_RECTANGLE_STATE_CREATE_INFO_EXT, eHdrMetadataEXT = VK_STRUCTURE_TYPE_HDR_METADATA_EXT, eSharedPresentSurfaceCapabilitiesKHR = VK_STRUCTURE_TYPE_SHARED_PRESENT_SURFACE_CAPABILITIES_KHR, ePhysicalDeviceSurfaceInfo2KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR, eSurfaceCapabilities2KHR = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR, eSurfaceFormat2KHR = VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR, eIosSurfaceCreateInfoMVK = VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK, eMacosSurfaceCreateInfoMVK = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK }; struct ApplicationInfo { ApplicationInfo( const char* pApplicationName_ = nullptr, uint32_t applicationVersion_ = 0, const char* pEngineName_ = nullptr, uint32_t engineVersion_ = 0, uint32_t apiVersion_ = 0 ) : sType( StructureType::eApplicationInfo ) , pNext( nullptr ) , pApplicationName( pApplicationName_ ) , applicationVersion( applicationVersion_ ) , pEngineName( pEngineName_ ) , engineVersion( engineVersion_ ) , apiVersion( apiVersion_ ) { } ApplicationInfo( VkApplicationInfo const & rhs ) { memcpy( this, &rhs, sizeof(ApplicationInfo) ); } ApplicationInfo& operator=( VkApplicationInfo const & rhs ) { memcpy( this, &rhs, sizeof(ApplicationInfo) ); return *this; } ApplicationInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } ApplicationInfo& setPApplicationName( const char* pApplicationName_ ) { pApplicationName = pApplicationName_; return *this; } ApplicationInfo& setApplicationVersion( uint32_t applicationVersion_ ) { applicationVersion = applicationVersion_; return *this; } ApplicationInfo& setPEngineName( const char* pEngineName_ ) { pEngineName = pEngineName_; return *this; } ApplicationInfo& setEngineVersion( uint32_t engineVersion_ ) { engineVersion = engineVersion_; return *this; } ApplicationInfo& setApiVersion( uint32_t apiVersion_ ) { apiVersion = apiVersion_; return *this; } operator const VkApplicationInfo&() const { return *reinterpret_cast(this); } bool operator==( ApplicationInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( pApplicationName == rhs.pApplicationName ) && ( applicationVersion == rhs.applicationVersion ) && ( pEngineName == rhs.pEngineName ) && ( engineVersion == rhs.engineVersion ) && ( apiVersion == rhs.apiVersion ); } bool operator!=( ApplicationInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; const char* pApplicationName; uint32_t applicationVersion; const char* pEngineName; uint32_t engineVersion; uint32_t apiVersion; }; static_assert( sizeof( ApplicationInfo ) == sizeof( VkApplicationInfo ), "struct and wrapper have different size!" ); struct DeviceQueueCreateInfo { DeviceQueueCreateInfo( DeviceQueueCreateFlags flags_ = DeviceQueueCreateFlags(), uint32_t queueFamilyIndex_ = 0, uint32_t queueCount_ = 0, const float* pQueuePriorities_ = nullptr ) : sType( StructureType::eDeviceQueueCreateInfo ) , pNext( nullptr ) , flags( flags_ ) , queueFamilyIndex( queueFamilyIndex_ ) , queueCount( queueCount_ ) , pQueuePriorities( pQueuePriorities_ ) { } DeviceQueueCreateInfo( VkDeviceQueueCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(DeviceQueueCreateInfo) ); } DeviceQueueCreateInfo& operator=( VkDeviceQueueCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(DeviceQueueCreateInfo) ); return *this; } DeviceQueueCreateInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } DeviceQueueCreateInfo& setFlags( DeviceQueueCreateFlags flags_ ) { flags = flags_; return *this; } DeviceQueueCreateInfo& setQueueFamilyIndex( uint32_t queueFamilyIndex_ ) { queueFamilyIndex = queueFamilyIndex_; return *this; } DeviceQueueCreateInfo& setQueueCount( uint32_t queueCount_ ) { queueCount = queueCount_; return *this; } DeviceQueueCreateInfo& setPQueuePriorities( const float* pQueuePriorities_ ) { pQueuePriorities = pQueuePriorities_; return *this; } operator const VkDeviceQueueCreateInfo&() const { return *reinterpret_cast(this); } bool operator==( DeviceQueueCreateInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( queueFamilyIndex == rhs.queueFamilyIndex ) && ( queueCount == rhs.queueCount ) && ( pQueuePriorities == rhs.pQueuePriorities ); } bool operator!=( DeviceQueueCreateInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; DeviceQueueCreateFlags flags; uint32_t queueFamilyIndex; uint32_t queueCount; const float* pQueuePriorities; }; static_assert( sizeof( DeviceQueueCreateInfo ) == sizeof( VkDeviceQueueCreateInfo ), "struct and wrapper have different size!" ); struct DeviceCreateInfo { DeviceCreateInfo( DeviceCreateFlags flags_ = DeviceCreateFlags(), uint32_t queueCreateInfoCount_ = 0, const DeviceQueueCreateInfo* pQueueCreateInfos_ = nullptr, uint32_t enabledLayerCount_ = 0, const char* const* ppEnabledLayerNames_ = nullptr, uint32_t enabledExtensionCount_ = 0, const char* const* ppEnabledExtensionNames_ = nullptr, const PhysicalDeviceFeatures* pEnabledFeatures_ = nullptr ) : sType( StructureType::eDeviceCreateInfo ) , pNext( nullptr ) , flags( flags_ ) , queueCreateInfoCount( queueCreateInfoCount_ ) , pQueueCreateInfos( pQueueCreateInfos_ ) , enabledLayerCount( enabledLayerCount_ ) , ppEnabledLayerNames( ppEnabledLayerNames_ ) , enabledExtensionCount( enabledExtensionCount_ ) , ppEnabledExtensionNames( ppEnabledExtensionNames_ ) , pEnabledFeatures( pEnabledFeatures_ ) { } DeviceCreateInfo( VkDeviceCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(DeviceCreateInfo) ); } DeviceCreateInfo& operator=( VkDeviceCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(DeviceCreateInfo) ); return *this; } DeviceCreateInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } DeviceCreateInfo& setFlags( DeviceCreateFlags flags_ ) { flags = flags_; return *this; } DeviceCreateInfo& setQueueCreateInfoCount( uint32_t queueCreateInfoCount_ ) { queueCreateInfoCount = queueCreateInfoCount_; return *this; } DeviceCreateInfo& setPQueueCreateInfos( const DeviceQueueCreateInfo* pQueueCreateInfos_ ) { pQueueCreateInfos = pQueueCreateInfos_; return *this; } DeviceCreateInfo& setEnabledLayerCount( uint32_t enabledLayerCount_ ) { enabledLayerCount = enabledLayerCount_; return *this; } DeviceCreateInfo& setPpEnabledLayerNames( const char* const* ppEnabledLayerNames_ ) { ppEnabledLayerNames = ppEnabledLayerNames_; return *this; } DeviceCreateInfo& setEnabledExtensionCount( uint32_t enabledExtensionCount_ ) { enabledExtensionCount = enabledExtensionCount_; return *this; } DeviceCreateInfo& setPpEnabledExtensionNames( const char* const* ppEnabledExtensionNames_ ) { ppEnabledExtensionNames = ppEnabledExtensionNames_; return *this; } DeviceCreateInfo& setPEnabledFeatures( const PhysicalDeviceFeatures* pEnabledFeatures_ ) { pEnabledFeatures = pEnabledFeatures_; return *this; } operator const VkDeviceCreateInfo&() const { return *reinterpret_cast(this); } bool operator==( DeviceCreateInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( queueCreateInfoCount == rhs.queueCreateInfoCount ) && ( pQueueCreateInfos == rhs.pQueueCreateInfos ) && ( enabledLayerCount == rhs.enabledLayerCount ) && ( ppEnabledLayerNames == rhs.ppEnabledLayerNames ) && ( enabledExtensionCount == rhs.enabledExtensionCount ) && ( ppEnabledExtensionNames == rhs.ppEnabledExtensionNames ) && ( pEnabledFeatures == rhs.pEnabledFeatures ); } bool operator!=( DeviceCreateInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; DeviceCreateFlags flags; uint32_t queueCreateInfoCount; const DeviceQueueCreateInfo* pQueueCreateInfos; uint32_t enabledLayerCount; const char* const* ppEnabledLayerNames; uint32_t enabledExtensionCount; const char* const* ppEnabledExtensionNames; const PhysicalDeviceFeatures* pEnabledFeatures; }; static_assert( sizeof( DeviceCreateInfo ) == sizeof( VkDeviceCreateInfo ), "struct and wrapper have different size!" ); struct InstanceCreateInfo { InstanceCreateInfo( InstanceCreateFlags flags_ = InstanceCreateFlags(), const ApplicationInfo* pApplicationInfo_ = nullptr, uint32_t enabledLayerCount_ = 0, const char* const* ppEnabledLayerNames_ = nullptr, uint32_t enabledExtensionCount_ = 0, const char* const* ppEnabledExtensionNames_ = nullptr ) : sType( StructureType::eInstanceCreateInfo ) , pNext( nullptr ) , flags( flags_ ) , pApplicationInfo( pApplicationInfo_ ) , enabledLayerCount( enabledLayerCount_ ) , ppEnabledLayerNames( ppEnabledLayerNames_ ) , enabledExtensionCount( enabledExtensionCount_ ) , ppEnabledExtensionNames( ppEnabledExtensionNames_ ) { } InstanceCreateInfo( VkInstanceCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(InstanceCreateInfo) ); } InstanceCreateInfo& operator=( VkInstanceCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(InstanceCreateInfo) ); return *this; } InstanceCreateInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } InstanceCreateInfo& setFlags( InstanceCreateFlags flags_ ) { flags = flags_; return *this; } InstanceCreateInfo& setPApplicationInfo( const ApplicationInfo* pApplicationInfo_ ) { pApplicationInfo = pApplicationInfo_; return *this; } InstanceCreateInfo& setEnabledLayerCount( uint32_t enabledLayerCount_ ) { enabledLayerCount = enabledLayerCount_; return *this; } InstanceCreateInfo& setPpEnabledLayerNames( const char* const* ppEnabledLayerNames_ ) { ppEnabledLayerNames = ppEnabledLayerNames_; return *this; } InstanceCreateInfo& setEnabledExtensionCount( uint32_t enabledExtensionCount_ ) { enabledExtensionCount = enabledExtensionCount_; return *this; } InstanceCreateInfo& setPpEnabledExtensionNames( const char* const* ppEnabledExtensionNames_ ) { ppEnabledExtensionNames = ppEnabledExtensionNames_; return *this; } operator const VkInstanceCreateInfo&() const { return *reinterpret_cast(this); } bool operator==( InstanceCreateInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( pApplicationInfo == rhs.pApplicationInfo ) && ( enabledLayerCount == rhs.enabledLayerCount ) && ( ppEnabledLayerNames == rhs.ppEnabledLayerNames ) && ( enabledExtensionCount == rhs.enabledExtensionCount ) && ( ppEnabledExtensionNames == rhs.ppEnabledExtensionNames ); } bool operator!=( InstanceCreateInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; InstanceCreateFlags flags; const ApplicationInfo* pApplicationInfo; uint32_t enabledLayerCount; const char* const* ppEnabledLayerNames; uint32_t enabledExtensionCount; const char* const* ppEnabledExtensionNames; }; static_assert( sizeof( InstanceCreateInfo ) == sizeof( VkInstanceCreateInfo ), "struct and wrapper have different size!" ); struct MemoryAllocateInfo { MemoryAllocateInfo( DeviceSize allocationSize_ = 0, uint32_t memoryTypeIndex_ = 0 ) : sType( StructureType::eMemoryAllocateInfo ) , pNext( nullptr ) , allocationSize( allocationSize_ ) , memoryTypeIndex( memoryTypeIndex_ ) { } MemoryAllocateInfo( VkMemoryAllocateInfo const & rhs ) { memcpy( this, &rhs, sizeof(MemoryAllocateInfo) ); } MemoryAllocateInfo& operator=( VkMemoryAllocateInfo const & rhs ) { memcpy( this, &rhs, sizeof(MemoryAllocateInfo) ); return *this; } MemoryAllocateInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } MemoryAllocateInfo& setAllocationSize( DeviceSize allocationSize_ ) { allocationSize = allocationSize_; return *this; } MemoryAllocateInfo& setMemoryTypeIndex( uint32_t memoryTypeIndex_ ) { memoryTypeIndex = memoryTypeIndex_; return *this; } operator const VkMemoryAllocateInfo&() const { return *reinterpret_cast(this); } bool operator==( MemoryAllocateInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( allocationSize == rhs.allocationSize ) && ( memoryTypeIndex == rhs.memoryTypeIndex ); } bool operator!=( MemoryAllocateInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; DeviceSize allocationSize; uint32_t memoryTypeIndex; }; static_assert( sizeof( MemoryAllocateInfo ) == sizeof( VkMemoryAllocateInfo ), "struct and wrapper have different size!" ); struct MappedMemoryRange { MappedMemoryRange( DeviceMemory memory_ = DeviceMemory(), DeviceSize offset_ = 0, DeviceSize size_ = 0 ) : sType( StructureType::eMappedMemoryRange ) , pNext( nullptr ) , memory( memory_ ) , offset( offset_ ) , size( size_ ) { } MappedMemoryRange( VkMappedMemoryRange const & rhs ) { memcpy( this, &rhs, sizeof(MappedMemoryRange) ); } MappedMemoryRange& operator=( VkMappedMemoryRange const & rhs ) { memcpy( this, &rhs, sizeof(MappedMemoryRange) ); return *this; } MappedMemoryRange& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } MappedMemoryRange& setMemory( DeviceMemory memory_ ) { memory = memory_; return *this; } MappedMemoryRange& setOffset( DeviceSize offset_ ) { offset = offset_; return *this; } MappedMemoryRange& setSize( DeviceSize size_ ) { size = size_; return *this; } operator const VkMappedMemoryRange&() const { return *reinterpret_cast(this); } bool operator==( MappedMemoryRange const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( memory == rhs.memory ) && ( offset == rhs.offset ) && ( size == rhs.size ); } bool operator!=( MappedMemoryRange const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; DeviceMemory memory; DeviceSize offset; DeviceSize size; }; static_assert( sizeof( MappedMemoryRange ) == sizeof( VkMappedMemoryRange ), "struct and wrapper have different size!" ); struct WriteDescriptorSet { WriteDescriptorSet( DescriptorSet dstSet_ = DescriptorSet(), uint32_t dstBinding_ = 0, uint32_t dstArrayElement_ = 0, uint32_t descriptorCount_ = 0, DescriptorType descriptorType_ = DescriptorType::eSampler, const DescriptorImageInfo* pImageInfo_ = nullptr, const DescriptorBufferInfo* pBufferInfo_ = nullptr, const BufferView* pTexelBufferView_ = nullptr ) : sType( StructureType::eWriteDescriptorSet ) , pNext( nullptr ) , dstSet( dstSet_ ) , dstBinding( dstBinding_ ) , dstArrayElement( dstArrayElement_ ) , descriptorCount( descriptorCount_ ) , descriptorType( descriptorType_ ) , pImageInfo( pImageInfo_ ) , pBufferInfo( pBufferInfo_ ) , pTexelBufferView( pTexelBufferView_ ) { } WriteDescriptorSet( VkWriteDescriptorSet const & rhs ) { memcpy( this, &rhs, sizeof(WriteDescriptorSet) ); } WriteDescriptorSet& operator=( VkWriteDescriptorSet const & rhs ) { memcpy( this, &rhs, sizeof(WriteDescriptorSet) ); return *this; } WriteDescriptorSet& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } WriteDescriptorSet& setDstSet( DescriptorSet dstSet_ ) { dstSet = dstSet_; return *this; } WriteDescriptorSet& setDstBinding( uint32_t dstBinding_ ) { dstBinding = dstBinding_; return *this; } WriteDescriptorSet& setDstArrayElement( uint32_t dstArrayElement_ ) { dstArrayElement = dstArrayElement_; return *this; } WriteDescriptorSet& setDescriptorCount( uint32_t descriptorCount_ ) { descriptorCount = descriptorCount_; return *this; } WriteDescriptorSet& setDescriptorType( DescriptorType descriptorType_ ) { descriptorType = descriptorType_; return *this; } WriteDescriptorSet& setPImageInfo( const DescriptorImageInfo* pImageInfo_ ) { pImageInfo = pImageInfo_; return *this; } WriteDescriptorSet& setPBufferInfo( const DescriptorBufferInfo* pBufferInfo_ ) { pBufferInfo = pBufferInfo_; return *this; } WriteDescriptorSet& setPTexelBufferView( const BufferView* pTexelBufferView_ ) { pTexelBufferView = pTexelBufferView_; return *this; } operator const VkWriteDescriptorSet&() const { return *reinterpret_cast(this); } bool operator==( WriteDescriptorSet const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( dstSet == rhs.dstSet ) && ( dstBinding == rhs.dstBinding ) && ( dstArrayElement == rhs.dstArrayElement ) && ( descriptorCount == rhs.descriptorCount ) && ( descriptorType == rhs.descriptorType ) && ( pImageInfo == rhs.pImageInfo ) && ( pBufferInfo == rhs.pBufferInfo ) && ( pTexelBufferView == rhs.pTexelBufferView ); } bool operator!=( WriteDescriptorSet const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; DescriptorSet dstSet; uint32_t dstBinding; uint32_t dstArrayElement; uint32_t descriptorCount; DescriptorType descriptorType; const DescriptorImageInfo* pImageInfo; const DescriptorBufferInfo* pBufferInfo; const BufferView* pTexelBufferView; }; static_assert( sizeof( WriteDescriptorSet ) == sizeof( VkWriteDescriptorSet ), "struct and wrapper have different size!" ); struct CopyDescriptorSet { CopyDescriptorSet( DescriptorSet srcSet_ = DescriptorSet(), uint32_t srcBinding_ = 0, uint32_t srcArrayElement_ = 0, DescriptorSet dstSet_ = DescriptorSet(), uint32_t dstBinding_ = 0, uint32_t dstArrayElement_ = 0, uint32_t descriptorCount_ = 0 ) : sType( StructureType::eCopyDescriptorSet ) , pNext( nullptr ) , srcSet( srcSet_ ) , srcBinding( srcBinding_ ) , srcArrayElement( srcArrayElement_ ) , dstSet( dstSet_ ) , dstBinding( dstBinding_ ) , dstArrayElement( dstArrayElement_ ) , descriptorCount( descriptorCount_ ) { } CopyDescriptorSet( VkCopyDescriptorSet const & rhs ) { memcpy( this, &rhs, sizeof(CopyDescriptorSet) ); } CopyDescriptorSet& operator=( VkCopyDescriptorSet const & rhs ) { memcpy( this, &rhs, sizeof(CopyDescriptorSet) ); return *this; } CopyDescriptorSet& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } CopyDescriptorSet& setSrcSet( DescriptorSet srcSet_ ) { srcSet = srcSet_; return *this; } CopyDescriptorSet& setSrcBinding( uint32_t srcBinding_ ) { srcBinding = srcBinding_; return *this; } CopyDescriptorSet& setSrcArrayElement( uint32_t srcArrayElement_ ) { srcArrayElement = srcArrayElement_; return *this; } CopyDescriptorSet& setDstSet( DescriptorSet dstSet_ ) { dstSet = dstSet_; return *this; } CopyDescriptorSet& setDstBinding( uint32_t dstBinding_ ) { dstBinding = dstBinding_; return *this; } CopyDescriptorSet& setDstArrayElement( uint32_t dstArrayElement_ ) { dstArrayElement = dstArrayElement_; return *this; } CopyDescriptorSet& setDescriptorCount( uint32_t descriptorCount_ ) { descriptorCount = descriptorCount_; return *this; } operator const VkCopyDescriptorSet&() const { return *reinterpret_cast(this); } bool operator==( CopyDescriptorSet const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( srcSet == rhs.srcSet ) && ( srcBinding == rhs.srcBinding ) && ( srcArrayElement == rhs.srcArrayElement ) && ( dstSet == rhs.dstSet ) && ( dstBinding == rhs.dstBinding ) && ( dstArrayElement == rhs.dstArrayElement ) && ( descriptorCount == rhs.descriptorCount ); } bool operator!=( CopyDescriptorSet const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; DescriptorSet srcSet; uint32_t srcBinding; uint32_t srcArrayElement; DescriptorSet dstSet; uint32_t dstBinding; uint32_t dstArrayElement; uint32_t descriptorCount; }; static_assert( sizeof( CopyDescriptorSet ) == sizeof( VkCopyDescriptorSet ), "struct and wrapper have different size!" ); struct BufferViewCreateInfo { BufferViewCreateInfo( BufferViewCreateFlags flags_ = BufferViewCreateFlags(), Buffer buffer_ = Buffer(), Format format_ = Format::eUndefined, DeviceSize offset_ = 0, DeviceSize range_ = 0 ) : sType( StructureType::eBufferViewCreateInfo ) , pNext( nullptr ) , flags( flags_ ) , buffer( buffer_ ) , format( format_ ) , offset( offset_ ) , range( range_ ) { } BufferViewCreateInfo( VkBufferViewCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(BufferViewCreateInfo) ); } BufferViewCreateInfo& operator=( VkBufferViewCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(BufferViewCreateInfo) ); return *this; } BufferViewCreateInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } BufferViewCreateInfo& setFlags( BufferViewCreateFlags flags_ ) { flags = flags_; return *this; } BufferViewCreateInfo& setBuffer( Buffer buffer_ ) { buffer = buffer_; return *this; } BufferViewCreateInfo& setFormat( Format format_ ) { format = format_; return *this; } BufferViewCreateInfo& setOffset( DeviceSize offset_ ) { offset = offset_; return *this; } BufferViewCreateInfo& setRange( DeviceSize range_ ) { range = range_; return *this; } operator const VkBufferViewCreateInfo&() const { return *reinterpret_cast(this); } bool operator==( BufferViewCreateInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( buffer == rhs.buffer ) && ( format == rhs.format ) && ( offset == rhs.offset ) && ( range == rhs.range ); } bool operator!=( BufferViewCreateInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; BufferViewCreateFlags flags; Buffer buffer; Format format; DeviceSize offset; DeviceSize range; }; static_assert( sizeof( BufferViewCreateInfo ) == sizeof( VkBufferViewCreateInfo ), "struct and wrapper have different size!" ); struct ShaderModuleCreateInfo { ShaderModuleCreateInfo( ShaderModuleCreateFlags flags_ = ShaderModuleCreateFlags(), size_t codeSize_ = 0, const uint32_t* pCode_ = nullptr ) : sType( StructureType::eShaderModuleCreateInfo ) , pNext( nullptr ) , flags( flags_ ) , codeSize( codeSize_ ) , pCode( pCode_ ) { } ShaderModuleCreateInfo( VkShaderModuleCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(ShaderModuleCreateInfo) ); } ShaderModuleCreateInfo& operator=( VkShaderModuleCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(ShaderModuleCreateInfo) ); return *this; } ShaderModuleCreateInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } ShaderModuleCreateInfo& setFlags( ShaderModuleCreateFlags flags_ ) { flags = flags_; return *this; } ShaderModuleCreateInfo& setCodeSize( size_t codeSize_ ) { codeSize = codeSize_; return *this; } ShaderModuleCreateInfo& setPCode( const uint32_t* pCode_ ) { pCode = pCode_; return *this; } operator const VkShaderModuleCreateInfo&() const { return *reinterpret_cast(this); } bool operator==( ShaderModuleCreateInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( codeSize == rhs.codeSize ) && ( pCode == rhs.pCode ); } bool operator!=( ShaderModuleCreateInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; ShaderModuleCreateFlags flags; size_t codeSize; const uint32_t* pCode; }; static_assert( sizeof( ShaderModuleCreateInfo ) == sizeof( VkShaderModuleCreateInfo ), "struct and wrapper have different size!" ); struct DescriptorSetAllocateInfo { DescriptorSetAllocateInfo( DescriptorPool descriptorPool_ = DescriptorPool(), uint32_t descriptorSetCount_ = 0, const DescriptorSetLayout* pSetLayouts_ = nullptr ) : sType( StructureType::eDescriptorSetAllocateInfo ) , pNext( nullptr ) , descriptorPool( descriptorPool_ ) , descriptorSetCount( descriptorSetCount_ ) , pSetLayouts( pSetLayouts_ ) { } DescriptorSetAllocateInfo( VkDescriptorSetAllocateInfo const & rhs ) { memcpy( this, &rhs, sizeof(DescriptorSetAllocateInfo) ); } DescriptorSetAllocateInfo& operator=( VkDescriptorSetAllocateInfo const & rhs ) { memcpy( this, &rhs, sizeof(DescriptorSetAllocateInfo) ); return *this; } DescriptorSetAllocateInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } DescriptorSetAllocateInfo& setDescriptorPool( DescriptorPool descriptorPool_ ) { descriptorPool = descriptorPool_; return *this; } DescriptorSetAllocateInfo& setDescriptorSetCount( uint32_t descriptorSetCount_ ) { descriptorSetCount = descriptorSetCount_; return *this; } DescriptorSetAllocateInfo& setPSetLayouts( const DescriptorSetLayout* pSetLayouts_ ) { pSetLayouts = pSetLayouts_; return *this; } operator const VkDescriptorSetAllocateInfo&() const { return *reinterpret_cast(this); } bool operator==( DescriptorSetAllocateInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( descriptorPool == rhs.descriptorPool ) && ( descriptorSetCount == rhs.descriptorSetCount ) && ( pSetLayouts == rhs.pSetLayouts ); } bool operator!=( DescriptorSetAllocateInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; DescriptorPool descriptorPool; uint32_t descriptorSetCount; const DescriptorSetLayout* pSetLayouts; }; static_assert( sizeof( DescriptorSetAllocateInfo ) == sizeof( VkDescriptorSetAllocateInfo ), "struct and wrapper have different size!" ); struct PipelineVertexInputStateCreateInfo { PipelineVertexInputStateCreateInfo( PipelineVertexInputStateCreateFlags flags_ = PipelineVertexInputStateCreateFlags(), uint32_t vertexBindingDescriptionCount_ = 0, const VertexInputBindingDescription* pVertexBindingDescriptions_ = nullptr, uint32_t vertexAttributeDescriptionCount_ = 0, const VertexInputAttributeDescription* pVertexAttributeDescriptions_ = nullptr ) : sType( StructureType::ePipelineVertexInputStateCreateInfo ) , pNext( nullptr ) , flags( flags_ ) , vertexBindingDescriptionCount( vertexBindingDescriptionCount_ ) , pVertexBindingDescriptions( pVertexBindingDescriptions_ ) , vertexAttributeDescriptionCount( vertexAttributeDescriptionCount_ ) , pVertexAttributeDescriptions( pVertexAttributeDescriptions_ ) { } PipelineVertexInputStateCreateInfo( VkPipelineVertexInputStateCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(PipelineVertexInputStateCreateInfo) ); } PipelineVertexInputStateCreateInfo& operator=( VkPipelineVertexInputStateCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(PipelineVertexInputStateCreateInfo) ); return *this; } PipelineVertexInputStateCreateInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } PipelineVertexInputStateCreateInfo& setFlags( PipelineVertexInputStateCreateFlags flags_ ) { flags = flags_; return *this; } PipelineVertexInputStateCreateInfo& setVertexBindingDescriptionCount( uint32_t vertexBindingDescriptionCount_ ) { vertexBindingDescriptionCount = vertexBindingDescriptionCount_; return *this; } PipelineVertexInputStateCreateInfo& setPVertexBindingDescriptions( const VertexInputBindingDescription* pVertexBindingDescriptions_ ) { pVertexBindingDescriptions = pVertexBindingDescriptions_; return *this; } PipelineVertexInputStateCreateInfo& setVertexAttributeDescriptionCount( uint32_t vertexAttributeDescriptionCount_ ) { vertexAttributeDescriptionCount = vertexAttributeDescriptionCount_; return *this; } PipelineVertexInputStateCreateInfo& setPVertexAttributeDescriptions( const VertexInputAttributeDescription* pVertexAttributeDescriptions_ ) { pVertexAttributeDescriptions = pVertexAttributeDescriptions_; return *this; } operator const VkPipelineVertexInputStateCreateInfo&() const { return *reinterpret_cast(this); } bool operator==( PipelineVertexInputStateCreateInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( vertexBindingDescriptionCount == rhs.vertexBindingDescriptionCount ) && ( pVertexBindingDescriptions == rhs.pVertexBindingDescriptions ) && ( vertexAttributeDescriptionCount == rhs.vertexAttributeDescriptionCount ) && ( pVertexAttributeDescriptions == rhs.pVertexAttributeDescriptions ); } bool operator!=( PipelineVertexInputStateCreateInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; PipelineVertexInputStateCreateFlags flags; uint32_t vertexBindingDescriptionCount; const VertexInputBindingDescription* pVertexBindingDescriptions; uint32_t vertexAttributeDescriptionCount; const VertexInputAttributeDescription* pVertexAttributeDescriptions; }; static_assert( sizeof( PipelineVertexInputStateCreateInfo ) == sizeof( VkPipelineVertexInputStateCreateInfo ), "struct and wrapper have different size!" ); struct PipelineInputAssemblyStateCreateInfo { PipelineInputAssemblyStateCreateInfo( PipelineInputAssemblyStateCreateFlags flags_ = PipelineInputAssemblyStateCreateFlags(), PrimitiveTopology topology_ = PrimitiveTopology::ePointList, Bool32 primitiveRestartEnable_ = 0 ) : sType( StructureType::ePipelineInputAssemblyStateCreateInfo ) , pNext( nullptr ) , flags( flags_ ) , topology( topology_ ) , primitiveRestartEnable( primitiveRestartEnable_ ) { } PipelineInputAssemblyStateCreateInfo( VkPipelineInputAssemblyStateCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(PipelineInputAssemblyStateCreateInfo) ); } PipelineInputAssemblyStateCreateInfo& operator=( VkPipelineInputAssemblyStateCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(PipelineInputAssemblyStateCreateInfo) ); return *this; } PipelineInputAssemblyStateCreateInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } PipelineInputAssemblyStateCreateInfo& setFlags( PipelineInputAssemblyStateCreateFlags flags_ ) { flags = flags_; return *this; } PipelineInputAssemblyStateCreateInfo& setTopology( PrimitiveTopology topology_ ) { topology = topology_; return *this; } PipelineInputAssemblyStateCreateInfo& setPrimitiveRestartEnable( Bool32 primitiveRestartEnable_ ) { primitiveRestartEnable = primitiveRestartEnable_; return *this; } operator const VkPipelineInputAssemblyStateCreateInfo&() const { return *reinterpret_cast(this); } bool operator==( PipelineInputAssemblyStateCreateInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( topology == rhs.topology ) && ( primitiveRestartEnable == rhs.primitiveRestartEnable ); } bool operator!=( PipelineInputAssemblyStateCreateInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; PipelineInputAssemblyStateCreateFlags flags; PrimitiveTopology topology; Bool32 primitiveRestartEnable; }; static_assert( sizeof( PipelineInputAssemblyStateCreateInfo ) == sizeof( VkPipelineInputAssemblyStateCreateInfo ), "struct and wrapper have different size!" ); struct PipelineTessellationStateCreateInfo { PipelineTessellationStateCreateInfo( PipelineTessellationStateCreateFlags flags_ = PipelineTessellationStateCreateFlags(), uint32_t patchControlPoints_ = 0 ) : sType( StructureType::ePipelineTessellationStateCreateInfo ) , pNext( nullptr ) , flags( flags_ ) , patchControlPoints( patchControlPoints_ ) { } PipelineTessellationStateCreateInfo( VkPipelineTessellationStateCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(PipelineTessellationStateCreateInfo) ); } PipelineTessellationStateCreateInfo& operator=( VkPipelineTessellationStateCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(PipelineTessellationStateCreateInfo) ); return *this; } PipelineTessellationStateCreateInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } PipelineTessellationStateCreateInfo& setFlags( PipelineTessellationStateCreateFlags flags_ ) { flags = flags_; return *this; } PipelineTessellationStateCreateInfo& setPatchControlPoints( uint32_t patchControlPoints_ ) { patchControlPoints = patchControlPoints_; return *this; } operator const VkPipelineTessellationStateCreateInfo&() const { return *reinterpret_cast(this); } bool operator==( PipelineTessellationStateCreateInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( patchControlPoints == rhs.patchControlPoints ); } bool operator!=( PipelineTessellationStateCreateInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; PipelineTessellationStateCreateFlags flags; uint32_t patchControlPoints; }; static_assert( sizeof( PipelineTessellationStateCreateInfo ) == sizeof( VkPipelineTessellationStateCreateInfo ), "struct and wrapper have different size!" ); struct PipelineViewportStateCreateInfo { PipelineViewportStateCreateInfo( PipelineViewportStateCreateFlags flags_ = PipelineViewportStateCreateFlags(), uint32_t viewportCount_ = 0, const Viewport* pViewports_ = nullptr, uint32_t scissorCount_ = 0, const Rect2D* pScissors_ = nullptr ) : sType( StructureType::ePipelineViewportStateCreateInfo ) , pNext( nullptr ) , flags( flags_ ) , viewportCount( viewportCount_ ) , pViewports( pViewports_ ) , scissorCount( scissorCount_ ) , pScissors( pScissors_ ) { } PipelineViewportStateCreateInfo( VkPipelineViewportStateCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(PipelineViewportStateCreateInfo) ); } PipelineViewportStateCreateInfo& operator=( VkPipelineViewportStateCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(PipelineViewportStateCreateInfo) ); return *this; } PipelineViewportStateCreateInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } PipelineViewportStateCreateInfo& setFlags( PipelineViewportStateCreateFlags flags_ ) { flags = flags_; return *this; } PipelineViewportStateCreateInfo& setViewportCount( uint32_t viewportCount_ ) { viewportCount = viewportCount_; return *this; } PipelineViewportStateCreateInfo& setPViewports( const Viewport* pViewports_ ) { pViewports = pViewports_; return *this; } PipelineViewportStateCreateInfo& setScissorCount( uint32_t scissorCount_ ) { scissorCount = scissorCount_; return *this; } PipelineViewportStateCreateInfo& setPScissors( const Rect2D* pScissors_ ) { pScissors = pScissors_; return *this; } operator const VkPipelineViewportStateCreateInfo&() const { return *reinterpret_cast(this); } bool operator==( PipelineViewportStateCreateInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( viewportCount == rhs.viewportCount ) && ( pViewports == rhs.pViewports ) && ( scissorCount == rhs.scissorCount ) && ( pScissors == rhs.pScissors ); } bool operator!=( PipelineViewportStateCreateInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; PipelineViewportStateCreateFlags flags; uint32_t viewportCount; const Viewport* pViewports; uint32_t scissorCount; const Rect2D* pScissors; }; static_assert( sizeof( PipelineViewportStateCreateInfo ) == sizeof( VkPipelineViewportStateCreateInfo ), "struct and wrapper have different size!" ); struct PipelineRasterizationStateCreateInfo { PipelineRasterizationStateCreateInfo( PipelineRasterizationStateCreateFlags flags_ = PipelineRasterizationStateCreateFlags(), Bool32 depthClampEnable_ = 0, Bool32 rasterizerDiscardEnable_ = 0, PolygonMode polygonMode_ = PolygonMode::eFill, CullModeFlags cullMode_ = CullModeFlags(), FrontFace frontFace_ = FrontFace::eCounterClockwise, Bool32 depthBiasEnable_ = 0, float depthBiasConstantFactor_ = 0, float depthBiasClamp_ = 0, float depthBiasSlopeFactor_ = 0, float lineWidth_ = 0 ) : sType( StructureType::ePipelineRasterizationStateCreateInfo ) , pNext( nullptr ) , flags( flags_ ) , depthClampEnable( depthClampEnable_ ) , rasterizerDiscardEnable( rasterizerDiscardEnable_ ) , polygonMode( polygonMode_ ) , cullMode( cullMode_ ) , frontFace( frontFace_ ) , depthBiasEnable( depthBiasEnable_ ) , depthBiasConstantFactor( depthBiasConstantFactor_ ) , depthBiasClamp( depthBiasClamp_ ) , depthBiasSlopeFactor( depthBiasSlopeFactor_ ) , lineWidth( lineWidth_ ) { } PipelineRasterizationStateCreateInfo( VkPipelineRasterizationStateCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(PipelineRasterizationStateCreateInfo) ); } PipelineRasterizationStateCreateInfo& operator=( VkPipelineRasterizationStateCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(PipelineRasterizationStateCreateInfo) ); return *this; } PipelineRasterizationStateCreateInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } PipelineRasterizationStateCreateInfo& setFlags( PipelineRasterizationStateCreateFlags flags_ ) { flags = flags_; return *this; } PipelineRasterizationStateCreateInfo& setDepthClampEnable( Bool32 depthClampEnable_ ) { depthClampEnable = depthClampEnable_; return *this; } PipelineRasterizationStateCreateInfo& setRasterizerDiscardEnable( Bool32 rasterizerDiscardEnable_ ) { rasterizerDiscardEnable = rasterizerDiscardEnable_; return *this; } PipelineRasterizationStateCreateInfo& setPolygonMode( PolygonMode polygonMode_ ) { polygonMode = polygonMode_; return *this; } PipelineRasterizationStateCreateInfo& setCullMode( CullModeFlags cullMode_ ) { cullMode = cullMode_; return *this; } PipelineRasterizationStateCreateInfo& setFrontFace( FrontFace frontFace_ ) { frontFace = frontFace_; return *this; } PipelineRasterizationStateCreateInfo& setDepthBiasEnable( Bool32 depthBiasEnable_ ) { depthBiasEnable = depthBiasEnable_; return *this; } PipelineRasterizationStateCreateInfo& setDepthBiasConstantFactor( float depthBiasConstantFactor_ ) { depthBiasConstantFactor = depthBiasConstantFactor_; return *this; } PipelineRasterizationStateCreateInfo& setDepthBiasClamp( float depthBiasClamp_ ) { depthBiasClamp = depthBiasClamp_; return *this; } PipelineRasterizationStateCreateInfo& setDepthBiasSlopeFactor( float depthBiasSlopeFactor_ ) { depthBiasSlopeFactor = depthBiasSlopeFactor_; return *this; } PipelineRasterizationStateCreateInfo& setLineWidth( float lineWidth_ ) { lineWidth = lineWidth_; return *this; } operator const VkPipelineRasterizationStateCreateInfo&() const { return *reinterpret_cast(this); } bool operator==( PipelineRasterizationStateCreateInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( depthClampEnable == rhs.depthClampEnable ) && ( rasterizerDiscardEnable == rhs.rasterizerDiscardEnable ) && ( polygonMode == rhs.polygonMode ) && ( cullMode == rhs.cullMode ) && ( frontFace == rhs.frontFace ) && ( depthBiasEnable == rhs.depthBiasEnable ) && ( depthBiasConstantFactor == rhs.depthBiasConstantFactor ) && ( depthBiasClamp == rhs.depthBiasClamp ) && ( depthBiasSlopeFactor == rhs.depthBiasSlopeFactor ) && ( lineWidth == rhs.lineWidth ); } bool operator!=( PipelineRasterizationStateCreateInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; PipelineRasterizationStateCreateFlags flags; Bool32 depthClampEnable; Bool32 rasterizerDiscardEnable; PolygonMode polygonMode; CullModeFlags cullMode; FrontFace frontFace; Bool32 depthBiasEnable; float depthBiasConstantFactor; float depthBiasClamp; float depthBiasSlopeFactor; float lineWidth; }; static_assert( sizeof( PipelineRasterizationStateCreateInfo ) == sizeof( VkPipelineRasterizationStateCreateInfo ), "struct and wrapper have different size!" ); struct PipelineDepthStencilStateCreateInfo { PipelineDepthStencilStateCreateInfo( PipelineDepthStencilStateCreateFlags flags_ = PipelineDepthStencilStateCreateFlags(), Bool32 depthTestEnable_ = 0, Bool32 depthWriteEnable_ = 0, CompareOp depthCompareOp_ = CompareOp::eNever, Bool32 depthBoundsTestEnable_ = 0, Bool32 stencilTestEnable_ = 0, StencilOpState front_ = StencilOpState(), StencilOpState back_ = StencilOpState(), float minDepthBounds_ = 0, float maxDepthBounds_ = 0 ) : sType( StructureType::ePipelineDepthStencilStateCreateInfo ) , pNext( nullptr ) , flags( flags_ ) , depthTestEnable( depthTestEnable_ ) , depthWriteEnable( depthWriteEnable_ ) , depthCompareOp( depthCompareOp_ ) , depthBoundsTestEnable( depthBoundsTestEnable_ ) , stencilTestEnable( stencilTestEnable_ ) , front( front_ ) , back( back_ ) , minDepthBounds( minDepthBounds_ ) , maxDepthBounds( maxDepthBounds_ ) { } PipelineDepthStencilStateCreateInfo( VkPipelineDepthStencilStateCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(PipelineDepthStencilStateCreateInfo) ); } PipelineDepthStencilStateCreateInfo& operator=( VkPipelineDepthStencilStateCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(PipelineDepthStencilStateCreateInfo) ); return *this; } PipelineDepthStencilStateCreateInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } PipelineDepthStencilStateCreateInfo& setFlags( PipelineDepthStencilStateCreateFlags flags_ ) { flags = flags_; return *this; } PipelineDepthStencilStateCreateInfo& setDepthTestEnable( Bool32 depthTestEnable_ ) { depthTestEnable = depthTestEnable_; return *this; } PipelineDepthStencilStateCreateInfo& setDepthWriteEnable( Bool32 depthWriteEnable_ ) { depthWriteEnable = depthWriteEnable_; return *this; } PipelineDepthStencilStateCreateInfo& setDepthCompareOp( CompareOp depthCompareOp_ ) { depthCompareOp = depthCompareOp_; return *this; } PipelineDepthStencilStateCreateInfo& setDepthBoundsTestEnable( Bool32 depthBoundsTestEnable_ ) { depthBoundsTestEnable = depthBoundsTestEnable_; return *this; } PipelineDepthStencilStateCreateInfo& setStencilTestEnable( Bool32 stencilTestEnable_ ) { stencilTestEnable = stencilTestEnable_; return *this; } PipelineDepthStencilStateCreateInfo& setFront( StencilOpState front_ ) { front = front_; return *this; } PipelineDepthStencilStateCreateInfo& setBack( StencilOpState back_ ) { back = back_; return *this; } PipelineDepthStencilStateCreateInfo& setMinDepthBounds( float minDepthBounds_ ) { minDepthBounds = minDepthBounds_; return *this; } PipelineDepthStencilStateCreateInfo& setMaxDepthBounds( float maxDepthBounds_ ) { maxDepthBounds = maxDepthBounds_; return *this; } operator const VkPipelineDepthStencilStateCreateInfo&() const { return *reinterpret_cast(this); } bool operator==( PipelineDepthStencilStateCreateInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( depthTestEnable == rhs.depthTestEnable ) && ( depthWriteEnable == rhs.depthWriteEnable ) && ( depthCompareOp == rhs.depthCompareOp ) && ( depthBoundsTestEnable == rhs.depthBoundsTestEnable ) && ( stencilTestEnable == rhs.stencilTestEnable ) && ( front == rhs.front ) && ( back == rhs.back ) && ( minDepthBounds == rhs.minDepthBounds ) && ( maxDepthBounds == rhs.maxDepthBounds ); } bool operator!=( PipelineDepthStencilStateCreateInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; PipelineDepthStencilStateCreateFlags flags; Bool32 depthTestEnable; Bool32 depthWriteEnable; CompareOp depthCompareOp; Bool32 depthBoundsTestEnable; Bool32 stencilTestEnable; StencilOpState front; StencilOpState back; float minDepthBounds; float maxDepthBounds; }; static_assert( sizeof( PipelineDepthStencilStateCreateInfo ) == sizeof( VkPipelineDepthStencilStateCreateInfo ), "struct and wrapper have different size!" ); struct PipelineCacheCreateInfo { PipelineCacheCreateInfo( PipelineCacheCreateFlags flags_ = PipelineCacheCreateFlags(), size_t initialDataSize_ = 0, const void* pInitialData_ = nullptr ) : sType( StructureType::ePipelineCacheCreateInfo ) , pNext( nullptr ) , flags( flags_ ) , initialDataSize( initialDataSize_ ) , pInitialData( pInitialData_ ) { } PipelineCacheCreateInfo( VkPipelineCacheCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(PipelineCacheCreateInfo) ); } PipelineCacheCreateInfo& operator=( VkPipelineCacheCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(PipelineCacheCreateInfo) ); return *this; } PipelineCacheCreateInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } PipelineCacheCreateInfo& setFlags( PipelineCacheCreateFlags flags_ ) { flags = flags_; return *this; } PipelineCacheCreateInfo& setInitialDataSize( size_t initialDataSize_ ) { initialDataSize = initialDataSize_; return *this; } PipelineCacheCreateInfo& setPInitialData( const void* pInitialData_ ) { pInitialData = pInitialData_; return *this; } operator const VkPipelineCacheCreateInfo&() const { return *reinterpret_cast(this); } bool operator==( PipelineCacheCreateInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( initialDataSize == rhs.initialDataSize ) && ( pInitialData == rhs.pInitialData ); } bool operator!=( PipelineCacheCreateInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; PipelineCacheCreateFlags flags; size_t initialDataSize; const void* pInitialData; }; static_assert( sizeof( PipelineCacheCreateInfo ) == sizeof( VkPipelineCacheCreateInfo ), "struct and wrapper have different size!" ); struct SamplerCreateInfo { SamplerCreateInfo( SamplerCreateFlags flags_ = SamplerCreateFlags(), Filter magFilter_ = Filter::eNearest, Filter minFilter_ = Filter::eNearest, SamplerMipmapMode mipmapMode_ = SamplerMipmapMode::eNearest, SamplerAddressMode addressModeU_ = SamplerAddressMode::eRepeat, SamplerAddressMode addressModeV_ = SamplerAddressMode::eRepeat, SamplerAddressMode addressModeW_ = SamplerAddressMode::eRepeat, float mipLodBias_ = 0, Bool32 anisotropyEnable_ = 0, float maxAnisotropy_ = 0, Bool32 compareEnable_ = 0, CompareOp compareOp_ = CompareOp::eNever, float minLod_ = 0, float maxLod_ = 0, BorderColor borderColor_ = BorderColor::eFloatTransparentBlack, Bool32 unnormalizedCoordinates_ = 0 ) : sType( StructureType::eSamplerCreateInfo ) , pNext( nullptr ) , flags( flags_ ) , magFilter( magFilter_ ) , minFilter( minFilter_ ) , mipmapMode( mipmapMode_ ) , addressModeU( addressModeU_ ) , addressModeV( addressModeV_ ) , addressModeW( addressModeW_ ) , mipLodBias( mipLodBias_ ) , anisotropyEnable( anisotropyEnable_ ) , maxAnisotropy( maxAnisotropy_ ) , compareEnable( compareEnable_ ) , compareOp( compareOp_ ) , minLod( minLod_ ) , maxLod( maxLod_ ) , borderColor( borderColor_ ) , unnormalizedCoordinates( unnormalizedCoordinates_ ) { } SamplerCreateInfo( VkSamplerCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(SamplerCreateInfo) ); } SamplerCreateInfo& operator=( VkSamplerCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(SamplerCreateInfo) ); return *this; } SamplerCreateInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } SamplerCreateInfo& setFlags( SamplerCreateFlags flags_ ) { flags = flags_; return *this; } SamplerCreateInfo& setMagFilter( Filter magFilter_ ) { magFilter = magFilter_; return *this; } SamplerCreateInfo& setMinFilter( Filter minFilter_ ) { minFilter = minFilter_; return *this; } SamplerCreateInfo& setMipmapMode( SamplerMipmapMode mipmapMode_ ) { mipmapMode = mipmapMode_; return *this; } SamplerCreateInfo& setAddressModeU( SamplerAddressMode addressModeU_ ) { addressModeU = addressModeU_; return *this; } SamplerCreateInfo& setAddressModeV( SamplerAddressMode addressModeV_ ) { addressModeV = addressModeV_; return *this; } SamplerCreateInfo& setAddressModeW( SamplerAddressMode addressModeW_ ) { addressModeW = addressModeW_; return *this; } SamplerCreateInfo& setMipLodBias( float mipLodBias_ ) { mipLodBias = mipLodBias_; return *this; } SamplerCreateInfo& setAnisotropyEnable( Bool32 anisotropyEnable_ ) { anisotropyEnable = anisotropyEnable_; return *this; } SamplerCreateInfo& setMaxAnisotropy( float maxAnisotropy_ ) { maxAnisotropy = maxAnisotropy_; return *this; } SamplerCreateInfo& setCompareEnable( Bool32 compareEnable_ ) { compareEnable = compareEnable_; return *this; } SamplerCreateInfo& setCompareOp( CompareOp compareOp_ ) { compareOp = compareOp_; return *this; } SamplerCreateInfo& setMinLod( float minLod_ ) { minLod = minLod_; return *this; } SamplerCreateInfo& setMaxLod( float maxLod_ ) { maxLod = maxLod_; return *this; } SamplerCreateInfo& setBorderColor( BorderColor borderColor_ ) { borderColor = borderColor_; return *this; } SamplerCreateInfo& setUnnormalizedCoordinates( Bool32 unnormalizedCoordinates_ ) { unnormalizedCoordinates = unnormalizedCoordinates_; return *this; } operator const VkSamplerCreateInfo&() const { return *reinterpret_cast(this); } bool operator==( SamplerCreateInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( magFilter == rhs.magFilter ) && ( minFilter == rhs.minFilter ) && ( mipmapMode == rhs.mipmapMode ) && ( addressModeU == rhs.addressModeU ) && ( addressModeV == rhs.addressModeV ) && ( addressModeW == rhs.addressModeW ) && ( mipLodBias == rhs.mipLodBias ) && ( anisotropyEnable == rhs.anisotropyEnable ) && ( maxAnisotropy == rhs.maxAnisotropy ) && ( compareEnable == rhs.compareEnable ) && ( compareOp == rhs.compareOp ) && ( minLod == rhs.minLod ) && ( maxLod == rhs.maxLod ) && ( borderColor == rhs.borderColor ) && ( unnormalizedCoordinates == rhs.unnormalizedCoordinates ); } bool operator!=( SamplerCreateInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; SamplerCreateFlags flags; Filter magFilter; Filter minFilter; SamplerMipmapMode mipmapMode; SamplerAddressMode addressModeU; SamplerAddressMode addressModeV; SamplerAddressMode addressModeW; float mipLodBias; Bool32 anisotropyEnable; float maxAnisotropy; Bool32 compareEnable; CompareOp compareOp; float minLod; float maxLod; BorderColor borderColor; Bool32 unnormalizedCoordinates; }; static_assert( sizeof( SamplerCreateInfo ) == sizeof( VkSamplerCreateInfo ), "struct and wrapper have different size!" ); struct CommandBufferAllocateInfo { CommandBufferAllocateInfo( CommandPool commandPool_ = CommandPool(), CommandBufferLevel level_ = CommandBufferLevel::ePrimary, uint32_t commandBufferCount_ = 0 ) : sType( StructureType::eCommandBufferAllocateInfo ) , pNext( nullptr ) , commandPool( commandPool_ ) , level( level_ ) , commandBufferCount( commandBufferCount_ ) { } CommandBufferAllocateInfo( VkCommandBufferAllocateInfo const & rhs ) { memcpy( this, &rhs, sizeof(CommandBufferAllocateInfo) ); } CommandBufferAllocateInfo& operator=( VkCommandBufferAllocateInfo const & rhs ) { memcpy( this, &rhs, sizeof(CommandBufferAllocateInfo) ); return *this; } CommandBufferAllocateInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } CommandBufferAllocateInfo& setCommandPool( CommandPool commandPool_ ) { commandPool = commandPool_; return *this; } CommandBufferAllocateInfo& setLevel( CommandBufferLevel level_ ) { level = level_; return *this; } CommandBufferAllocateInfo& setCommandBufferCount( uint32_t commandBufferCount_ ) { commandBufferCount = commandBufferCount_; return *this; } operator const VkCommandBufferAllocateInfo&() const { return *reinterpret_cast(this); } bool operator==( CommandBufferAllocateInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( commandPool == rhs.commandPool ) && ( level == rhs.level ) && ( commandBufferCount == rhs.commandBufferCount ); } bool operator!=( CommandBufferAllocateInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; CommandPool commandPool; CommandBufferLevel level; uint32_t commandBufferCount; }; static_assert( sizeof( CommandBufferAllocateInfo ) == sizeof( VkCommandBufferAllocateInfo ), "struct and wrapper have different size!" ); struct RenderPassBeginInfo { RenderPassBeginInfo( RenderPass renderPass_ = RenderPass(), Framebuffer framebuffer_ = Framebuffer(), Rect2D renderArea_ = Rect2D(), uint32_t clearValueCount_ = 0, const ClearValue* pClearValues_ = nullptr ) : sType( StructureType::eRenderPassBeginInfo ) , pNext( nullptr ) , renderPass( renderPass_ ) , framebuffer( framebuffer_ ) , renderArea( renderArea_ ) , clearValueCount( clearValueCount_ ) , pClearValues( pClearValues_ ) { } RenderPassBeginInfo( VkRenderPassBeginInfo const & rhs ) { memcpy( this, &rhs, sizeof(RenderPassBeginInfo) ); } RenderPassBeginInfo& operator=( VkRenderPassBeginInfo const & rhs ) { memcpy( this, &rhs, sizeof(RenderPassBeginInfo) ); return *this; } RenderPassBeginInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } RenderPassBeginInfo& setRenderPass( RenderPass renderPass_ ) { renderPass = renderPass_; return *this; } RenderPassBeginInfo& setFramebuffer( Framebuffer framebuffer_ ) { framebuffer = framebuffer_; return *this; } RenderPassBeginInfo& setRenderArea( Rect2D renderArea_ ) { renderArea = renderArea_; return *this; } RenderPassBeginInfo& setClearValueCount( uint32_t clearValueCount_ ) { clearValueCount = clearValueCount_; return *this; } RenderPassBeginInfo& setPClearValues( const ClearValue* pClearValues_ ) { pClearValues = pClearValues_; return *this; } operator const VkRenderPassBeginInfo&() const { return *reinterpret_cast(this); } bool operator==( RenderPassBeginInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( renderPass == rhs.renderPass ) && ( framebuffer == rhs.framebuffer ) && ( renderArea == rhs.renderArea ) && ( clearValueCount == rhs.clearValueCount ) && ( pClearValues == rhs.pClearValues ); } bool operator!=( RenderPassBeginInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; RenderPass renderPass; Framebuffer framebuffer; Rect2D renderArea; uint32_t clearValueCount; const ClearValue* pClearValues; }; static_assert( sizeof( RenderPassBeginInfo ) == sizeof( VkRenderPassBeginInfo ), "struct and wrapper have different size!" ); struct EventCreateInfo { EventCreateInfo( EventCreateFlags flags_ = EventCreateFlags() ) : sType( StructureType::eEventCreateInfo ) , pNext( nullptr ) , flags( flags_ ) { } EventCreateInfo( VkEventCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(EventCreateInfo) ); } EventCreateInfo& operator=( VkEventCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(EventCreateInfo) ); return *this; } EventCreateInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } EventCreateInfo& setFlags( EventCreateFlags flags_ ) { flags = flags_; return *this; } operator const VkEventCreateInfo&() const { return *reinterpret_cast(this); } bool operator==( EventCreateInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ); } bool operator!=( EventCreateInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; EventCreateFlags flags; }; static_assert( sizeof( EventCreateInfo ) == sizeof( VkEventCreateInfo ), "struct and wrapper have different size!" ); struct SemaphoreCreateInfo { SemaphoreCreateInfo( SemaphoreCreateFlags flags_ = SemaphoreCreateFlags() ) : sType( StructureType::eSemaphoreCreateInfo ) , pNext( nullptr ) , flags( flags_ ) { } SemaphoreCreateInfo( VkSemaphoreCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(SemaphoreCreateInfo) ); } SemaphoreCreateInfo& operator=( VkSemaphoreCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(SemaphoreCreateInfo) ); return *this; } SemaphoreCreateInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } SemaphoreCreateInfo& setFlags( SemaphoreCreateFlags flags_ ) { flags = flags_; return *this; } operator const VkSemaphoreCreateInfo&() const { return *reinterpret_cast(this); } bool operator==( SemaphoreCreateInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ); } bool operator!=( SemaphoreCreateInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; SemaphoreCreateFlags flags; }; static_assert( sizeof( SemaphoreCreateInfo ) == sizeof( VkSemaphoreCreateInfo ), "struct and wrapper have different size!" ); struct FramebufferCreateInfo { FramebufferCreateInfo( FramebufferCreateFlags flags_ = FramebufferCreateFlags(), RenderPass renderPass_ = RenderPass(), uint32_t attachmentCount_ = 0, const ImageView* pAttachments_ = nullptr, uint32_t width_ = 0, uint32_t height_ = 0, uint32_t layers_ = 0 ) : sType( StructureType::eFramebufferCreateInfo ) , pNext( nullptr ) , flags( flags_ ) , renderPass( renderPass_ ) , attachmentCount( attachmentCount_ ) , pAttachments( pAttachments_ ) , width( width_ ) , height( height_ ) , layers( layers_ ) { } FramebufferCreateInfo( VkFramebufferCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(FramebufferCreateInfo) ); } FramebufferCreateInfo& operator=( VkFramebufferCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(FramebufferCreateInfo) ); return *this; } FramebufferCreateInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } FramebufferCreateInfo& setFlags( FramebufferCreateFlags flags_ ) { flags = flags_; return *this; } FramebufferCreateInfo& setRenderPass( RenderPass renderPass_ ) { renderPass = renderPass_; return *this; } FramebufferCreateInfo& setAttachmentCount( uint32_t attachmentCount_ ) { attachmentCount = attachmentCount_; return *this; } FramebufferCreateInfo& setPAttachments( const ImageView* pAttachments_ ) { pAttachments = pAttachments_; return *this; } FramebufferCreateInfo& setWidth( uint32_t width_ ) { width = width_; return *this; } FramebufferCreateInfo& setHeight( uint32_t height_ ) { height = height_; return *this; } FramebufferCreateInfo& setLayers( uint32_t layers_ ) { layers = layers_; return *this; } operator const VkFramebufferCreateInfo&() const { return *reinterpret_cast(this); } bool operator==( FramebufferCreateInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( renderPass == rhs.renderPass ) && ( attachmentCount == rhs.attachmentCount ) && ( pAttachments == rhs.pAttachments ) && ( width == rhs.width ) && ( height == rhs.height ) && ( layers == rhs.layers ); } bool operator!=( FramebufferCreateInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; FramebufferCreateFlags flags; RenderPass renderPass; uint32_t attachmentCount; const ImageView* pAttachments; uint32_t width; uint32_t height; uint32_t layers; }; static_assert( sizeof( FramebufferCreateInfo ) == sizeof( VkFramebufferCreateInfo ), "struct and wrapper have different size!" ); struct DisplayModeCreateInfoKHR { DisplayModeCreateInfoKHR( DisplayModeCreateFlagsKHR flags_ = DisplayModeCreateFlagsKHR(), DisplayModeParametersKHR parameters_ = DisplayModeParametersKHR() ) : sType( StructureType::eDisplayModeCreateInfoKHR ) , pNext( nullptr ) , flags( flags_ ) , parameters( parameters_ ) { } DisplayModeCreateInfoKHR( VkDisplayModeCreateInfoKHR const & rhs ) { memcpy( this, &rhs, sizeof(DisplayModeCreateInfoKHR) ); } DisplayModeCreateInfoKHR& operator=( VkDisplayModeCreateInfoKHR const & rhs ) { memcpy( this, &rhs, sizeof(DisplayModeCreateInfoKHR) ); return *this; } DisplayModeCreateInfoKHR& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } DisplayModeCreateInfoKHR& setFlags( DisplayModeCreateFlagsKHR flags_ ) { flags = flags_; return *this; } DisplayModeCreateInfoKHR& setParameters( DisplayModeParametersKHR parameters_ ) { parameters = parameters_; return *this; } operator const VkDisplayModeCreateInfoKHR&() const { return *reinterpret_cast(this); } bool operator==( DisplayModeCreateInfoKHR const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( parameters == rhs.parameters ); } bool operator!=( DisplayModeCreateInfoKHR const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; DisplayModeCreateFlagsKHR flags; DisplayModeParametersKHR parameters; }; static_assert( sizeof( DisplayModeCreateInfoKHR ) == sizeof( VkDisplayModeCreateInfoKHR ), "struct and wrapper have different size!" ); struct DisplayPresentInfoKHR { DisplayPresentInfoKHR( Rect2D srcRect_ = Rect2D(), Rect2D dstRect_ = Rect2D(), Bool32 persistent_ = 0 ) : sType( StructureType::eDisplayPresentInfoKHR ) , pNext( nullptr ) , srcRect( srcRect_ ) , dstRect( dstRect_ ) , persistent( persistent_ ) { } DisplayPresentInfoKHR( VkDisplayPresentInfoKHR const & rhs ) { memcpy( this, &rhs, sizeof(DisplayPresentInfoKHR) ); } DisplayPresentInfoKHR& operator=( VkDisplayPresentInfoKHR const & rhs ) { memcpy( this, &rhs, sizeof(DisplayPresentInfoKHR) ); return *this; } DisplayPresentInfoKHR& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } DisplayPresentInfoKHR& setSrcRect( Rect2D srcRect_ ) { srcRect = srcRect_; return *this; } DisplayPresentInfoKHR& setDstRect( Rect2D dstRect_ ) { dstRect = dstRect_; return *this; } DisplayPresentInfoKHR& setPersistent( Bool32 persistent_ ) { persistent = persistent_; return *this; } operator const VkDisplayPresentInfoKHR&() const { return *reinterpret_cast(this); } bool operator==( DisplayPresentInfoKHR const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( srcRect == rhs.srcRect ) && ( dstRect == rhs.dstRect ) && ( persistent == rhs.persistent ); } bool operator!=( DisplayPresentInfoKHR const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; Rect2D srcRect; Rect2D dstRect; Bool32 persistent; }; static_assert( sizeof( DisplayPresentInfoKHR ) == sizeof( VkDisplayPresentInfoKHR ), "struct and wrapper have different size!" ); #ifdef VK_USE_PLATFORM_ANDROID_KHR struct AndroidSurfaceCreateInfoKHR { AndroidSurfaceCreateInfoKHR( AndroidSurfaceCreateFlagsKHR flags_ = AndroidSurfaceCreateFlagsKHR(), ANativeWindow* window_ = nullptr ) : sType( StructureType::eAndroidSurfaceCreateInfoKHR ) , pNext( nullptr ) , flags( flags_ ) , window( window_ ) { } AndroidSurfaceCreateInfoKHR( VkAndroidSurfaceCreateInfoKHR const & rhs ) { memcpy( this, &rhs, sizeof(AndroidSurfaceCreateInfoKHR) ); } AndroidSurfaceCreateInfoKHR& operator=( VkAndroidSurfaceCreateInfoKHR const & rhs ) { memcpy( this, &rhs, sizeof(AndroidSurfaceCreateInfoKHR) ); return *this; } AndroidSurfaceCreateInfoKHR& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } AndroidSurfaceCreateInfoKHR& setFlags( AndroidSurfaceCreateFlagsKHR flags_ ) { flags = flags_; return *this; } AndroidSurfaceCreateInfoKHR& setWindow( ANativeWindow* window_ ) { window = window_; return *this; } operator const VkAndroidSurfaceCreateInfoKHR&() const { return *reinterpret_cast(this); } bool operator==( AndroidSurfaceCreateInfoKHR const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( window == rhs.window ); } bool operator!=( AndroidSurfaceCreateInfoKHR const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; AndroidSurfaceCreateFlagsKHR flags; ANativeWindow* window; }; static_assert( sizeof( AndroidSurfaceCreateInfoKHR ) == sizeof( VkAndroidSurfaceCreateInfoKHR ), "struct and wrapper have different size!" ); #endif /*VK_USE_PLATFORM_ANDROID_KHR*/ #ifdef VK_USE_PLATFORM_MIR_KHR struct MirSurfaceCreateInfoKHR { MirSurfaceCreateInfoKHR( MirSurfaceCreateFlagsKHR flags_ = MirSurfaceCreateFlagsKHR(), MirConnection* connection_ = nullptr, MirSurface* mirSurface_ = nullptr ) : sType( StructureType::eMirSurfaceCreateInfoKHR ) , pNext( nullptr ) , flags( flags_ ) , connection( connection_ ) , mirSurface( mirSurface_ ) { } MirSurfaceCreateInfoKHR( VkMirSurfaceCreateInfoKHR const & rhs ) { memcpy( this, &rhs, sizeof(MirSurfaceCreateInfoKHR) ); } MirSurfaceCreateInfoKHR& operator=( VkMirSurfaceCreateInfoKHR const & rhs ) { memcpy( this, &rhs, sizeof(MirSurfaceCreateInfoKHR) ); return *this; } MirSurfaceCreateInfoKHR& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } MirSurfaceCreateInfoKHR& setFlags( MirSurfaceCreateFlagsKHR flags_ ) { flags = flags_; return *this; } MirSurfaceCreateInfoKHR& setConnection( MirConnection* connection_ ) { connection = connection_; return *this; } MirSurfaceCreateInfoKHR& setMirSurface( MirSurface* mirSurface_ ) { mirSurface = mirSurface_; return *this; } operator const VkMirSurfaceCreateInfoKHR&() const { return *reinterpret_cast(this); } bool operator==( MirSurfaceCreateInfoKHR const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( connection == rhs.connection ) && ( mirSurface == rhs.mirSurface ); } bool operator!=( MirSurfaceCreateInfoKHR const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; MirSurfaceCreateFlagsKHR flags; MirConnection* connection; MirSurface* mirSurface; }; static_assert( sizeof( MirSurfaceCreateInfoKHR ) == sizeof( VkMirSurfaceCreateInfoKHR ), "struct and wrapper have different size!" ); #endif /*VK_USE_PLATFORM_MIR_KHR*/ #ifdef VK_USE_PLATFORM_VI_NN struct ViSurfaceCreateInfoNN { ViSurfaceCreateInfoNN( ViSurfaceCreateFlagsNN flags_ = ViSurfaceCreateFlagsNN(), void* window_ = nullptr ) : sType( StructureType::eViSurfaceCreateInfoNN ) , pNext( nullptr ) , flags( flags_ ) , window( window_ ) { } ViSurfaceCreateInfoNN( VkViSurfaceCreateInfoNN const & rhs ) { memcpy( this, &rhs, sizeof(ViSurfaceCreateInfoNN) ); } ViSurfaceCreateInfoNN& operator=( VkViSurfaceCreateInfoNN const & rhs ) { memcpy( this, &rhs, sizeof(ViSurfaceCreateInfoNN) ); return *this; } ViSurfaceCreateInfoNN& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } ViSurfaceCreateInfoNN& setFlags( ViSurfaceCreateFlagsNN flags_ ) { flags = flags_; return *this; } ViSurfaceCreateInfoNN& setWindow( void* window_ ) { window = window_; return *this; } operator const VkViSurfaceCreateInfoNN&() const { return *reinterpret_cast(this); } bool operator==( ViSurfaceCreateInfoNN const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( window == rhs.window ); } bool operator!=( ViSurfaceCreateInfoNN const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; ViSurfaceCreateFlagsNN flags; void* window; }; static_assert( sizeof( ViSurfaceCreateInfoNN ) == sizeof( VkViSurfaceCreateInfoNN ), "struct and wrapper have different size!" ); #endif /*VK_USE_PLATFORM_VI_NN*/ #ifdef VK_USE_PLATFORM_WAYLAND_KHR struct WaylandSurfaceCreateInfoKHR { WaylandSurfaceCreateInfoKHR( WaylandSurfaceCreateFlagsKHR flags_ = WaylandSurfaceCreateFlagsKHR(), struct wl_display* display_ = nullptr, struct wl_surface* surface_ = nullptr ) : sType( StructureType::eWaylandSurfaceCreateInfoKHR ) , pNext( nullptr ) , flags( flags_ ) , display( display_ ) , surface( surface_ ) { } WaylandSurfaceCreateInfoKHR( VkWaylandSurfaceCreateInfoKHR const & rhs ) { memcpy( this, &rhs, sizeof(WaylandSurfaceCreateInfoKHR) ); } WaylandSurfaceCreateInfoKHR& operator=( VkWaylandSurfaceCreateInfoKHR const & rhs ) { memcpy( this, &rhs, sizeof(WaylandSurfaceCreateInfoKHR) ); return *this; } WaylandSurfaceCreateInfoKHR& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } WaylandSurfaceCreateInfoKHR& setFlags( WaylandSurfaceCreateFlagsKHR flags_ ) { flags = flags_; return *this; } WaylandSurfaceCreateInfoKHR& setDisplay( struct wl_display* display_ ) { display = display_; return *this; } WaylandSurfaceCreateInfoKHR& setSurface( struct wl_surface* surface_ ) { surface = surface_; return *this; } operator const VkWaylandSurfaceCreateInfoKHR&() const { return *reinterpret_cast(this); } bool operator==( WaylandSurfaceCreateInfoKHR const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( display == rhs.display ) && ( surface == rhs.surface ); } bool operator!=( WaylandSurfaceCreateInfoKHR const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; WaylandSurfaceCreateFlagsKHR flags; struct wl_display* display; struct wl_surface* surface; }; static_assert( sizeof( WaylandSurfaceCreateInfoKHR ) == sizeof( VkWaylandSurfaceCreateInfoKHR ), "struct and wrapper have different size!" ); #endif /*VK_USE_PLATFORM_WAYLAND_KHR*/ #ifdef VK_USE_PLATFORM_WIN32_KHR struct Win32SurfaceCreateInfoKHR { Win32SurfaceCreateInfoKHR( Win32SurfaceCreateFlagsKHR flags_ = Win32SurfaceCreateFlagsKHR(), HINSTANCE hinstance_ = 0, HWND hwnd_ = 0 ) : sType( StructureType::eWin32SurfaceCreateInfoKHR ) , pNext( nullptr ) , flags( flags_ ) , hinstance( hinstance_ ) , hwnd( hwnd_ ) { } Win32SurfaceCreateInfoKHR( VkWin32SurfaceCreateInfoKHR const & rhs ) { memcpy( this, &rhs, sizeof(Win32SurfaceCreateInfoKHR) ); } Win32SurfaceCreateInfoKHR& operator=( VkWin32SurfaceCreateInfoKHR const & rhs ) { memcpy( this, &rhs, sizeof(Win32SurfaceCreateInfoKHR) ); return *this; } Win32SurfaceCreateInfoKHR& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } Win32SurfaceCreateInfoKHR& setFlags( Win32SurfaceCreateFlagsKHR flags_ ) { flags = flags_; return *this; } Win32SurfaceCreateInfoKHR& setHinstance( HINSTANCE hinstance_ ) { hinstance = hinstance_; return *this; } Win32SurfaceCreateInfoKHR& setHwnd( HWND hwnd_ ) { hwnd = hwnd_; return *this; } operator const VkWin32SurfaceCreateInfoKHR&() const { return *reinterpret_cast(this); } bool operator==( Win32SurfaceCreateInfoKHR const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( hinstance == rhs.hinstance ) && ( hwnd == rhs.hwnd ); } bool operator!=( Win32SurfaceCreateInfoKHR const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; Win32SurfaceCreateFlagsKHR flags; HINSTANCE hinstance; HWND hwnd; }; static_assert( sizeof( Win32SurfaceCreateInfoKHR ) == sizeof( VkWin32SurfaceCreateInfoKHR ), "struct and wrapper have different size!" ); #endif /*VK_USE_PLATFORM_WIN32_KHR*/ #ifdef VK_USE_PLATFORM_XLIB_KHR struct XlibSurfaceCreateInfoKHR { XlibSurfaceCreateInfoKHR( XlibSurfaceCreateFlagsKHR flags_ = XlibSurfaceCreateFlagsKHR(), Display* dpy_ = nullptr, Window window_ = 0 ) : sType( StructureType::eXlibSurfaceCreateInfoKHR ) , pNext( nullptr ) , flags( flags_ ) , dpy( dpy_ ) , window( window_ ) { } XlibSurfaceCreateInfoKHR( VkXlibSurfaceCreateInfoKHR const & rhs ) { memcpy( this, &rhs, sizeof(XlibSurfaceCreateInfoKHR) ); } XlibSurfaceCreateInfoKHR& operator=( VkXlibSurfaceCreateInfoKHR const & rhs ) { memcpy( this, &rhs, sizeof(XlibSurfaceCreateInfoKHR) ); return *this; } XlibSurfaceCreateInfoKHR& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } XlibSurfaceCreateInfoKHR& setFlags( XlibSurfaceCreateFlagsKHR flags_ ) { flags = flags_; return *this; } XlibSurfaceCreateInfoKHR& setDpy( Display* dpy_ ) { dpy = dpy_; return *this; } XlibSurfaceCreateInfoKHR& setWindow( Window window_ ) { window = window_; return *this; } operator const VkXlibSurfaceCreateInfoKHR&() const { return *reinterpret_cast(this); } bool operator==( XlibSurfaceCreateInfoKHR const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( dpy == rhs.dpy ) && ( window == rhs.window ); } bool operator!=( XlibSurfaceCreateInfoKHR const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; XlibSurfaceCreateFlagsKHR flags; Display* dpy; Window window; }; static_assert( sizeof( XlibSurfaceCreateInfoKHR ) == sizeof( VkXlibSurfaceCreateInfoKHR ), "struct and wrapper have different size!" ); #endif /*VK_USE_PLATFORM_XLIB_KHR*/ #ifdef VK_USE_PLATFORM_XCB_KHR struct XcbSurfaceCreateInfoKHR { XcbSurfaceCreateInfoKHR( XcbSurfaceCreateFlagsKHR flags_ = XcbSurfaceCreateFlagsKHR(), xcb_connection_t* connection_ = nullptr, xcb_window_t window_ = 0 ) : sType( StructureType::eXcbSurfaceCreateInfoKHR ) , pNext( nullptr ) , flags( flags_ ) , connection( connection_ ) , window( window_ ) { } XcbSurfaceCreateInfoKHR( VkXcbSurfaceCreateInfoKHR const & rhs ) { memcpy( this, &rhs, sizeof(XcbSurfaceCreateInfoKHR) ); } XcbSurfaceCreateInfoKHR& operator=( VkXcbSurfaceCreateInfoKHR const & rhs ) { memcpy( this, &rhs, sizeof(XcbSurfaceCreateInfoKHR) ); return *this; } XcbSurfaceCreateInfoKHR& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } XcbSurfaceCreateInfoKHR& setFlags( XcbSurfaceCreateFlagsKHR flags_ ) { flags = flags_; return *this; } XcbSurfaceCreateInfoKHR& setConnection( xcb_connection_t* connection_ ) { connection = connection_; return *this; } XcbSurfaceCreateInfoKHR& setWindow( xcb_window_t window_ ) { window = window_; return *this; } operator const VkXcbSurfaceCreateInfoKHR&() const { return *reinterpret_cast(this); } bool operator==( XcbSurfaceCreateInfoKHR const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( connection == rhs.connection ) && ( window == rhs.window ); } bool operator!=( XcbSurfaceCreateInfoKHR const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; XcbSurfaceCreateFlagsKHR flags; xcb_connection_t* connection; xcb_window_t window; }; static_assert( sizeof( XcbSurfaceCreateInfoKHR ) == sizeof( VkXcbSurfaceCreateInfoKHR ), "struct and wrapper have different size!" ); #endif /*VK_USE_PLATFORM_XCB_KHR*/ struct DebugMarkerMarkerInfoEXT { DebugMarkerMarkerInfoEXT( const char* pMarkerName_ = nullptr, std::array const& color_ = { { 0, 0, 0, 0 } } ) : sType( StructureType::eDebugMarkerMarkerInfoEXT ) , pNext( nullptr ) , pMarkerName( pMarkerName_ ) { memcpy( &color, color_.data(), 4 * sizeof( float ) ); } DebugMarkerMarkerInfoEXT( VkDebugMarkerMarkerInfoEXT const & rhs ) { memcpy( this, &rhs, sizeof(DebugMarkerMarkerInfoEXT) ); } DebugMarkerMarkerInfoEXT& operator=( VkDebugMarkerMarkerInfoEXT const & rhs ) { memcpy( this, &rhs, sizeof(DebugMarkerMarkerInfoEXT) ); return *this; } DebugMarkerMarkerInfoEXT& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } DebugMarkerMarkerInfoEXT& setPMarkerName( const char* pMarkerName_ ) { pMarkerName = pMarkerName_; return *this; } DebugMarkerMarkerInfoEXT& setColor( std::array color_ ) { memcpy( &color, color_.data(), 4 * sizeof( float ) ); return *this; } operator const VkDebugMarkerMarkerInfoEXT&() const { return *reinterpret_cast(this); } bool operator==( DebugMarkerMarkerInfoEXT const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( pMarkerName == rhs.pMarkerName ) && ( memcmp( color, rhs.color, 4 * sizeof( float ) ) == 0 ); } bool operator!=( DebugMarkerMarkerInfoEXT const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; const char* pMarkerName; float color[4]; }; static_assert( sizeof( DebugMarkerMarkerInfoEXT ) == sizeof( VkDebugMarkerMarkerInfoEXT ), "struct and wrapper have different size!" ); struct DedicatedAllocationImageCreateInfoNV { DedicatedAllocationImageCreateInfoNV( Bool32 dedicatedAllocation_ = 0 ) : sType( StructureType::eDedicatedAllocationImageCreateInfoNV ) , pNext( nullptr ) , dedicatedAllocation( dedicatedAllocation_ ) { } DedicatedAllocationImageCreateInfoNV( VkDedicatedAllocationImageCreateInfoNV const & rhs ) { memcpy( this, &rhs, sizeof(DedicatedAllocationImageCreateInfoNV) ); } DedicatedAllocationImageCreateInfoNV& operator=( VkDedicatedAllocationImageCreateInfoNV const & rhs ) { memcpy( this, &rhs, sizeof(DedicatedAllocationImageCreateInfoNV) ); return *this; } DedicatedAllocationImageCreateInfoNV& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } DedicatedAllocationImageCreateInfoNV& setDedicatedAllocation( Bool32 dedicatedAllocation_ ) { dedicatedAllocation = dedicatedAllocation_; return *this; } operator const VkDedicatedAllocationImageCreateInfoNV&() const { return *reinterpret_cast(this); } bool operator==( DedicatedAllocationImageCreateInfoNV const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( dedicatedAllocation == rhs.dedicatedAllocation ); } bool operator!=( DedicatedAllocationImageCreateInfoNV const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; Bool32 dedicatedAllocation; }; static_assert( sizeof( DedicatedAllocationImageCreateInfoNV ) == sizeof( VkDedicatedAllocationImageCreateInfoNV ), "struct and wrapper have different size!" ); struct DedicatedAllocationBufferCreateInfoNV { DedicatedAllocationBufferCreateInfoNV( Bool32 dedicatedAllocation_ = 0 ) : sType( StructureType::eDedicatedAllocationBufferCreateInfoNV ) , pNext( nullptr ) , dedicatedAllocation( dedicatedAllocation_ ) { } DedicatedAllocationBufferCreateInfoNV( VkDedicatedAllocationBufferCreateInfoNV const & rhs ) { memcpy( this, &rhs, sizeof(DedicatedAllocationBufferCreateInfoNV) ); } DedicatedAllocationBufferCreateInfoNV& operator=( VkDedicatedAllocationBufferCreateInfoNV const & rhs ) { memcpy( this, &rhs, sizeof(DedicatedAllocationBufferCreateInfoNV) ); return *this; } DedicatedAllocationBufferCreateInfoNV& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } DedicatedAllocationBufferCreateInfoNV& setDedicatedAllocation( Bool32 dedicatedAllocation_ ) { dedicatedAllocation = dedicatedAllocation_; return *this; } operator const VkDedicatedAllocationBufferCreateInfoNV&() const { return *reinterpret_cast(this); } bool operator==( DedicatedAllocationBufferCreateInfoNV const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( dedicatedAllocation == rhs.dedicatedAllocation ); } bool operator!=( DedicatedAllocationBufferCreateInfoNV const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; Bool32 dedicatedAllocation; }; static_assert( sizeof( DedicatedAllocationBufferCreateInfoNV ) == sizeof( VkDedicatedAllocationBufferCreateInfoNV ), "struct and wrapper have different size!" ); struct DedicatedAllocationMemoryAllocateInfoNV { DedicatedAllocationMemoryAllocateInfoNV( Image image_ = Image(), Buffer buffer_ = Buffer() ) : sType( StructureType::eDedicatedAllocationMemoryAllocateInfoNV ) , pNext( nullptr ) , image( image_ ) , buffer( buffer_ ) { } DedicatedAllocationMemoryAllocateInfoNV( VkDedicatedAllocationMemoryAllocateInfoNV const & rhs ) { memcpy( this, &rhs, sizeof(DedicatedAllocationMemoryAllocateInfoNV) ); } DedicatedAllocationMemoryAllocateInfoNV& operator=( VkDedicatedAllocationMemoryAllocateInfoNV const & rhs ) { memcpy( this, &rhs, sizeof(DedicatedAllocationMemoryAllocateInfoNV) ); return *this; } DedicatedAllocationMemoryAllocateInfoNV& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } DedicatedAllocationMemoryAllocateInfoNV& setImage( Image image_ ) { image = image_; return *this; } DedicatedAllocationMemoryAllocateInfoNV& setBuffer( Buffer buffer_ ) { buffer = buffer_; return *this; } operator const VkDedicatedAllocationMemoryAllocateInfoNV&() const { return *reinterpret_cast(this); } bool operator==( DedicatedAllocationMemoryAllocateInfoNV const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( image == rhs.image ) && ( buffer == rhs.buffer ); } bool operator!=( DedicatedAllocationMemoryAllocateInfoNV const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; Image image; Buffer buffer; }; static_assert( sizeof( DedicatedAllocationMemoryAllocateInfoNV ) == sizeof( VkDedicatedAllocationMemoryAllocateInfoNV ), "struct and wrapper have different size!" ); #ifdef VK_USE_PLATFORM_WIN32_KHR struct ExportMemoryWin32HandleInfoNV { ExportMemoryWin32HandleInfoNV( const SECURITY_ATTRIBUTES* pAttributes_ = nullptr, DWORD dwAccess_ = 0 ) : sType( StructureType::eExportMemoryWin32HandleInfoNV ) , pNext( nullptr ) , pAttributes( pAttributes_ ) , dwAccess( dwAccess_ ) { } ExportMemoryWin32HandleInfoNV( VkExportMemoryWin32HandleInfoNV const & rhs ) { memcpy( this, &rhs, sizeof(ExportMemoryWin32HandleInfoNV) ); } ExportMemoryWin32HandleInfoNV& operator=( VkExportMemoryWin32HandleInfoNV const & rhs ) { memcpy( this, &rhs, sizeof(ExportMemoryWin32HandleInfoNV) ); return *this; } ExportMemoryWin32HandleInfoNV& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } ExportMemoryWin32HandleInfoNV& setPAttributes( const SECURITY_ATTRIBUTES* pAttributes_ ) { pAttributes = pAttributes_; return *this; } ExportMemoryWin32HandleInfoNV& setDwAccess( DWORD dwAccess_ ) { dwAccess = dwAccess_; return *this; } operator const VkExportMemoryWin32HandleInfoNV&() const { return *reinterpret_cast(this); } bool operator==( ExportMemoryWin32HandleInfoNV const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( pAttributes == rhs.pAttributes ) && ( dwAccess == rhs.dwAccess ); } bool operator!=( ExportMemoryWin32HandleInfoNV const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; const SECURITY_ATTRIBUTES* pAttributes; DWORD dwAccess; }; static_assert( sizeof( ExportMemoryWin32HandleInfoNV ) == sizeof( VkExportMemoryWin32HandleInfoNV ), "struct and wrapper have different size!" ); #endif /*VK_USE_PLATFORM_WIN32_KHR*/ #ifdef VK_USE_PLATFORM_WIN32_KHR struct Win32KeyedMutexAcquireReleaseInfoNV { Win32KeyedMutexAcquireReleaseInfoNV( uint32_t acquireCount_ = 0, const DeviceMemory* pAcquireSyncs_ = nullptr, const uint64_t* pAcquireKeys_ = nullptr, const uint32_t* pAcquireTimeoutMilliseconds_ = nullptr, uint32_t releaseCount_ = 0, const DeviceMemory* pReleaseSyncs_ = nullptr, const uint64_t* pReleaseKeys_ = nullptr ) : sType( StructureType::eWin32KeyedMutexAcquireReleaseInfoNV ) , pNext( nullptr ) , acquireCount( acquireCount_ ) , pAcquireSyncs( pAcquireSyncs_ ) , pAcquireKeys( pAcquireKeys_ ) , pAcquireTimeoutMilliseconds( pAcquireTimeoutMilliseconds_ ) , releaseCount( releaseCount_ ) , pReleaseSyncs( pReleaseSyncs_ ) , pReleaseKeys( pReleaseKeys_ ) { } Win32KeyedMutexAcquireReleaseInfoNV( VkWin32KeyedMutexAcquireReleaseInfoNV const & rhs ) { memcpy( this, &rhs, sizeof(Win32KeyedMutexAcquireReleaseInfoNV) ); } Win32KeyedMutexAcquireReleaseInfoNV& operator=( VkWin32KeyedMutexAcquireReleaseInfoNV const & rhs ) { memcpy( this, &rhs, sizeof(Win32KeyedMutexAcquireReleaseInfoNV) ); return *this; } Win32KeyedMutexAcquireReleaseInfoNV& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } Win32KeyedMutexAcquireReleaseInfoNV& setAcquireCount( uint32_t acquireCount_ ) { acquireCount = acquireCount_; return *this; } Win32KeyedMutexAcquireReleaseInfoNV& setPAcquireSyncs( const DeviceMemory* pAcquireSyncs_ ) { pAcquireSyncs = pAcquireSyncs_; return *this; } Win32KeyedMutexAcquireReleaseInfoNV& setPAcquireKeys( const uint64_t* pAcquireKeys_ ) { pAcquireKeys = pAcquireKeys_; return *this; } Win32KeyedMutexAcquireReleaseInfoNV& setPAcquireTimeoutMilliseconds( const uint32_t* pAcquireTimeoutMilliseconds_ ) { pAcquireTimeoutMilliseconds = pAcquireTimeoutMilliseconds_; return *this; } Win32KeyedMutexAcquireReleaseInfoNV& setReleaseCount( uint32_t releaseCount_ ) { releaseCount = releaseCount_; return *this; } Win32KeyedMutexAcquireReleaseInfoNV& setPReleaseSyncs( const DeviceMemory* pReleaseSyncs_ ) { pReleaseSyncs = pReleaseSyncs_; return *this; } Win32KeyedMutexAcquireReleaseInfoNV& setPReleaseKeys( const uint64_t* pReleaseKeys_ ) { pReleaseKeys = pReleaseKeys_; return *this; } operator const VkWin32KeyedMutexAcquireReleaseInfoNV&() const { return *reinterpret_cast(this); } bool operator==( Win32KeyedMutexAcquireReleaseInfoNV const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( acquireCount == rhs.acquireCount ) && ( pAcquireSyncs == rhs.pAcquireSyncs ) && ( pAcquireKeys == rhs.pAcquireKeys ) && ( pAcquireTimeoutMilliseconds == rhs.pAcquireTimeoutMilliseconds ) && ( releaseCount == rhs.releaseCount ) && ( pReleaseSyncs == rhs.pReleaseSyncs ) && ( pReleaseKeys == rhs.pReleaseKeys ); } bool operator!=( Win32KeyedMutexAcquireReleaseInfoNV const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; uint32_t acquireCount; const DeviceMemory* pAcquireSyncs; const uint64_t* pAcquireKeys; const uint32_t* pAcquireTimeoutMilliseconds; uint32_t releaseCount; const DeviceMemory* pReleaseSyncs; const uint64_t* pReleaseKeys; }; static_assert( sizeof( Win32KeyedMutexAcquireReleaseInfoNV ) == sizeof( VkWin32KeyedMutexAcquireReleaseInfoNV ), "struct and wrapper have different size!" ); #endif /*VK_USE_PLATFORM_WIN32_KHR*/ struct DeviceGeneratedCommandsFeaturesNVX { DeviceGeneratedCommandsFeaturesNVX( Bool32 computeBindingPointSupport_ = 0 ) : sType( StructureType::eDeviceGeneratedCommandsFeaturesNVX ) , pNext( nullptr ) , computeBindingPointSupport( computeBindingPointSupport_ ) { } DeviceGeneratedCommandsFeaturesNVX( VkDeviceGeneratedCommandsFeaturesNVX const & rhs ) { memcpy( this, &rhs, sizeof(DeviceGeneratedCommandsFeaturesNVX) ); } DeviceGeneratedCommandsFeaturesNVX& operator=( VkDeviceGeneratedCommandsFeaturesNVX const & rhs ) { memcpy( this, &rhs, sizeof(DeviceGeneratedCommandsFeaturesNVX) ); return *this; } DeviceGeneratedCommandsFeaturesNVX& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } DeviceGeneratedCommandsFeaturesNVX& setComputeBindingPointSupport( Bool32 computeBindingPointSupport_ ) { computeBindingPointSupport = computeBindingPointSupport_; return *this; } operator const VkDeviceGeneratedCommandsFeaturesNVX&() const { return *reinterpret_cast(this); } bool operator==( DeviceGeneratedCommandsFeaturesNVX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( computeBindingPointSupport == rhs.computeBindingPointSupport ); } bool operator!=( DeviceGeneratedCommandsFeaturesNVX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; Bool32 computeBindingPointSupport; }; static_assert( sizeof( DeviceGeneratedCommandsFeaturesNVX ) == sizeof( VkDeviceGeneratedCommandsFeaturesNVX ), "struct and wrapper have different size!" ); struct DeviceGeneratedCommandsLimitsNVX { DeviceGeneratedCommandsLimitsNVX( uint32_t maxIndirectCommandsLayoutTokenCount_ = 0, uint32_t maxObjectEntryCounts_ = 0, uint32_t minSequenceCountBufferOffsetAlignment_ = 0, uint32_t minSequenceIndexBufferOffsetAlignment_ = 0, uint32_t minCommandsTokenBufferOffsetAlignment_ = 0 ) : sType( StructureType::eDeviceGeneratedCommandsLimitsNVX ) , pNext( nullptr ) , maxIndirectCommandsLayoutTokenCount( maxIndirectCommandsLayoutTokenCount_ ) , maxObjectEntryCounts( maxObjectEntryCounts_ ) , minSequenceCountBufferOffsetAlignment( minSequenceCountBufferOffsetAlignment_ ) , minSequenceIndexBufferOffsetAlignment( minSequenceIndexBufferOffsetAlignment_ ) , minCommandsTokenBufferOffsetAlignment( minCommandsTokenBufferOffsetAlignment_ ) { } DeviceGeneratedCommandsLimitsNVX( VkDeviceGeneratedCommandsLimitsNVX const & rhs ) { memcpy( this, &rhs, sizeof(DeviceGeneratedCommandsLimitsNVX) ); } DeviceGeneratedCommandsLimitsNVX& operator=( VkDeviceGeneratedCommandsLimitsNVX const & rhs ) { memcpy( this, &rhs, sizeof(DeviceGeneratedCommandsLimitsNVX) ); return *this; } DeviceGeneratedCommandsLimitsNVX& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } DeviceGeneratedCommandsLimitsNVX& setMaxIndirectCommandsLayoutTokenCount( uint32_t maxIndirectCommandsLayoutTokenCount_ ) { maxIndirectCommandsLayoutTokenCount = maxIndirectCommandsLayoutTokenCount_; return *this; } DeviceGeneratedCommandsLimitsNVX& setMaxObjectEntryCounts( uint32_t maxObjectEntryCounts_ ) { maxObjectEntryCounts = maxObjectEntryCounts_; return *this; } DeviceGeneratedCommandsLimitsNVX& setMinSequenceCountBufferOffsetAlignment( uint32_t minSequenceCountBufferOffsetAlignment_ ) { minSequenceCountBufferOffsetAlignment = minSequenceCountBufferOffsetAlignment_; return *this; } DeviceGeneratedCommandsLimitsNVX& setMinSequenceIndexBufferOffsetAlignment( uint32_t minSequenceIndexBufferOffsetAlignment_ ) { minSequenceIndexBufferOffsetAlignment = minSequenceIndexBufferOffsetAlignment_; return *this; } DeviceGeneratedCommandsLimitsNVX& setMinCommandsTokenBufferOffsetAlignment( uint32_t minCommandsTokenBufferOffsetAlignment_ ) { minCommandsTokenBufferOffsetAlignment = minCommandsTokenBufferOffsetAlignment_; return *this; } operator const VkDeviceGeneratedCommandsLimitsNVX&() const { return *reinterpret_cast(this); } bool operator==( DeviceGeneratedCommandsLimitsNVX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( maxIndirectCommandsLayoutTokenCount == rhs.maxIndirectCommandsLayoutTokenCount ) && ( maxObjectEntryCounts == rhs.maxObjectEntryCounts ) && ( minSequenceCountBufferOffsetAlignment == rhs.minSequenceCountBufferOffsetAlignment ) && ( minSequenceIndexBufferOffsetAlignment == rhs.minSequenceIndexBufferOffsetAlignment ) && ( minCommandsTokenBufferOffsetAlignment == rhs.minCommandsTokenBufferOffsetAlignment ); } bool operator!=( DeviceGeneratedCommandsLimitsNVX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; uint32_t maxIndirectCommandsLayoutTokenCount; uint32_t maxObjectEntryCounts; uint32_t minSequenceCountBufferOffsetAlignment; uint32_t minSequenceIndexBufferOffsetAlignment; uint32_t minCommandsTokenBufferOffsetAlignment; }; static_assert( sizeof( DeviceGeneratedCommandsLimitsNVX ) == sizeof( VkDeviceGeneratedCommandsLimitsNVX ), "struct and wrapper have different size!" ); struct CmdReserveSpaceForCommandsInfoNVX { CmdReserveSpaceForCommandsInfoNVX( ObjectTableNVX objectTable_ = ObjectTableNVX(), IndirectCommandsLayoutNVX indirectCommandsLayout_ = IndirectCommandsLayoutNVX(), uint32_t maxSequencesCount_ = 0 ) : sType( StructureType::eCmdReserveSpaceForCommandsInfoNVX ) , pNext( nullptr ) , objectTable( objectTable_ ) , indirectCommandsLayout( indirectCommandsLayout_ ) , maxSequencesCount( maxSequencesCount_ ) { } CmdReserveSpaceForCommandsInfoNVX( VkCmdReserveSpaceForCommandsInfoNVX const & rhs ) { memcpy( this, &rhs, sizeof(CmdReserveSpaceForCommandsInfoNVX) ); } CmdReserveSpaceForCommandsInfoNVX& operator=( VkCmdReserveSpaceForCommandsInfoNVX const & rhs ) { memcpy( this, &rhs, sizeof(CmdReserveSpaceForCommandsInfoNVX) ); return *this; } CmdReserveSpaceForCommandsInfoNVX& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } CmdReserveSpaceForCommandsInfoNVX& setObjectTable( ObjectTableNVX objectTable_ ) { objectTable = objectTable_; return *this; } CmdReserveSpaceForCommandsInfoNVX& setIndirectCommandsLayout( IndirectCommandsLayoutNVX indirectCommandsLayout_ ) { indirectCommandsLayout = indirectCommandsLayout_; return *this; } CmdReserveSpaceForCommandsInfoNVX& setMaxSequencesCount( uint32_t maxSequencesCount_ ) { maxSequencesCount = maxSequencesCount_; return *this; } operator const VkCmdReserveSpaceForCommandsInfoNVX&() const { return *reinterpret_cast(this); } bool operator==( CmdReserveSpaceForCommandsInfoNVX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( objectTable == rhs.objectTable ) && ( indirectCommandsLayout == rhs.indirectCommandsLayout ) && ( maxSequencesCount == rhs.maxSequencesCount ); } bool operator!=( CmdReserveSpaceForCommandsInfoNVX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; ObjectTableNVX objectTable; IndirectCommandsLayoutNVX indirectCommandsLayout; uint32_t maxSequencesCount; }; static_assert( sizeof( CmdReserveSpaceForCommandsInfoNVX ) == sizeof( VkCmdReserveSpaceForCommandsInfoNVX ), "struct and wrapper have different size!" ); struct PhysicalDeviceFeatures2KHR { PhysicalDeviceFeatures2KHR( PhysicalDeviceFeatures features_ = PhysicalDeviceFeatures() ) : sType( StructureType::ePhysicalDeviceFeatures2KHR ) , pNext( nullptr ) , features( features_ ) { } PhysicalDeviceFeatures2KHR( VkPhysicalDeviceFeatures2KHR const & rhs ) { memcpy( this, &rhs, sizeof(PhysicalDeviceFeatures2KHR) ); } PhysicalDeviceFeatures2KHR& operator=( VkPhysicalDeviceFeatures2KHR const & rhs ) { memcpy( this, &rhs, sizeof(PhysicalDeviceFeatures2KHR) ); return *this; } PhysicalDeviceFeatures2KHR& setPNext( void* pNext_ ) { pNext = pNext_; return *this; } PhysicalDeviceFeatures2KHR& setFeatures( PhysicalDeviceFeatures features_ ) { features = features_; return *this; } operator const VkPhysicalDeviceFeatures2KHR&() const { return *reinterpret_cast(this); } bool operator==( PhysicalDeviceFeatures2KHR const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( features == rhs.features ); } bool operator!=( PhysicalDeviceFeatures2KHR const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: void* pNext; PhysicalDeviceFeatures features; }; static_assert( sizeof( PhysicalDeviceFeatures2KHR ) == sizeof( VkPhysicalDeviceFeatures2KHR ), "struct and wrapper have different size!" ); struct PhysicalDevicePushDescriptorPropertiesKHR { PhysicalDevicePushDescriptorPropertiesKHR( uint32_t maxPushDescriptors_ = 0 ) : sType( StructureType::ePhysicalDevicePushDescriptorPropertiesKHR ) , pNext( nullptr ) , maxPushDescriptors( maxPushDescriptors_ ) { } PhysicalDevicePushDescriptorPropertiesKHR( VkPhysicalDevicePushDescriptorPropertiesKHR const & rhs ) { memcpy( this, &rhs, sizeof(PhysicalDevicePushDescriptorPropertiesKHR) ); } PhysicalDevicePushDescriptorPropertiesKHR& operator=( VkPhysicalDevicePushDescriptorPropertiesKHR const & rhs ) { memcpy( this, &rhs, sizeof(PhysicalDevicePushDescriptorPropertiesKHR) ); return *this; } PhysicalDevicePushDescriptorPropertiesKHR& setPNext( void* pNext_ ) { pNext = pNext_; return *this; } PhysicalDevicePushDescriptorPropertiesKHR& setMaxPushDescriptors( uint32_t maxPushDescriptors_ ) { maxPushDescriptors = maxPushDescriptors_; return *this; } operator const VkPhysicalDevicePushDescriptorPropertiesKHR&() const { return *reinterpret_cast(this); } bool operator==( PhysicalDevicePushDescriptorPropertiesKHR const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( maxPushDescriptors == rhs.maxPushDescriptors ); } bool operator!=( PhysicalDevicePushDescriptorPropertiesKHR const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: void* pNext; uint32_t maxPushDescriptors; }; static_assert( sizeof( PhysicalDevicePushDescriptorPropertiesKHR ) == sizeof( VkPhysicalDevicePushDescriptorPropertiesKHR ), "struct and wrapper have different size!" ); struct PresentRegionsKHR { PresentRegionsKHR( uint32_t swapchainCount_ = 0, const PresentRegionKHR* pRegions_ = nullptr ) : sType( StructureType::ePresentRegionsKHR ) , pNext( nullptr ) , swapchainCount( swapchainCount_ ) , pRegions( pRegions_ ) { } PresentRegionsKHR( VkPresentRegionsKHR const & rhs ) { memcpy( this, &rhs, sizeof(PresentRegionsKHR) ); } PresentRegionsKHR& operator=( VkPresentRegionsKHR const & rhs ) { memcpy( this, &rhs, sizeof(PresentRegionsKHR) ); return *this; } PresentRegionsKHR& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } PresentRegionsKHR& setSwapchainCount( uint32_t swapchainCount_ ) { swapchainCount = swapchainCount_; return *this; } PresentRegionsKHR& setPRegions( const PresentRegionKHR* pRegions_ ) { pRegions = pRegions_; return *this; } operator const VkPresentRegionsKHR&() const { return *reinterpret_cast(this); } bool operator==( PresentRegionsKHR const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( swapchainCount == rhs.swapchainCount ) && ( pRegions == rhs.pRegions ); } bool operator!=( PresentRegionsKHR const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; uint32_t swapchainCount; const PresentRegionKHR* pRegions; }; static_assert( sizeof( PresentRegionsKHR ) == sizeof( VkPresentRegionsKHR ), "struct and wrapper have different size!" ); struct PhysicalDeviceIDPropertiesKHX { operator const VkPhysicalDeviceIDPropertiesKHX&() const { return *reinterpret_cast(this); } bool operator==( PhysicalDeviceIDPropertiesKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( memcmp( deviceUUID, rhs.deviceUUID, VK_UUID_SIZE * sizeof( uint8_t ) ) == 0 ) && ( memcmp( driverUUID, rhs.driverUUID, VK_UUID_SIZE * sizeof( uint8_t ) ) == 0 ) && ( memcmp( deviceLUID, rhs.deviceLUID, VK_LUID_SIZE_KHX * sizeof( uint8_t ) ) == 0 ) && ( deviceLUIDValid == rhs.deviceLUIDValid ); } bool operator!=( PhysicalDeviceIDPropertiesKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: void* pNext; uint8_t deviceUUID[VK_UUID_SIZE]; uint8_t driverUUID[VK_UUID_SIZE]; uint8_t deviceLUID[VK_LUID_SIZE_KHX]; Bool32 deviceLUIDValid; }; static_assert( sizeof( PhysicalDeviceIDPropertiesKHX ) == sizeof( VkPhysicalDeviceIDPropertiesKHX ), "struct and wrapper have different size!" ); #ifdef VK_USE_PLATFORM_WIN32_KHX struct ExportMemoryWin32HandleInfoKHX { ExportMemoryWin32HandleInfoKHX( const SECURITY_ATTRIBUTES* pAttributes_ = nullptr, DWORD dwAccess_ = 0, LPCWSTR name_ = 0 ) : sType( StructureType::eExportMemoryWin32HandleInfoKHX ) , pNext( nullptr ) , pAttributes( pAttributes_ ) , dwAccess( dwAccess_ ) , name( name_ ) { } ExportMemoryWin32HandleInfoKHX( VkExportMemoryWin32HandleInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(ExportMemoryWin32HandleInfoKHX) ); } ExportMemoryWin32HandleInfoKHX& operator=( VkExportMemoryWin32HandleInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(ExportMemoryWin32HandleInfoKHX) ); return *this; } ExportMemoryWin32HandleInfoKHX& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } ExportMemoryWin32HandleInfoKHX& setPAttributes( const SECURITY_ATTRIBUTES* pAttributes_ ) { pAttributes = pAttributes_; return *this; } ExportMemoryWin32HandleInfoKHX& setDwAccess( DWORD dwAccess_ ) { dwAccess = dwAccess_; return *this; } ExportMemoryWin32HandleInfoKHX& setName( LPCWSTR name_ ) { name = name_; return *this; } operator const VkExportMemoryWin32HandleInfoKHX&() const { return *reinterpret_cast(this); } bool operator==( ExportMemoryWin32HandleInfoKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( pAttributes == rhs.pAttributes ) && ( dwAccess == rhs.dwAccess ) && ( name == rhs.name ); } bool operator!=( ExportMemoryWin32HandleInfoKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; const SECURITY_ATTRIBUTES* pAttributes; DWORD dwAccess; LPCWSTR name; }; static_assert( sizeof( ExportMemoryWin32HandleInfoKHX ) == sizeof( VkExportMemoryWin32HandleInfoKHX ), "struct and wrapper have different size!" ); #endif /*VK_USE_PLATFORM_WIN32_KHX*/ #ifdef VK_USE_PLATFORM_WIN32_KHX struct MemoryWin32HandlePropertiesKHX { operator const VkMemoryWin32HandlePropertiesKHX&() const { return *reinterpret_cast(this); } bool operator==( MemoryWin32HandlePropertiesKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( memoryTypeBits == rhs.memoryTypeBits ); } bool operator!=( MemoryWin32HandlePropertiesKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: void* pNext; uint32_t memoryTypeBits; }; static_assert( sizeof( MemoryWin32HandlePropertiesKHX ) == sizeof( VkMemoryWin32HandlePropertiesKHX ), "struct and wrapper have different size!" ); #endif /*VK_USE_PLATFORM_WIN32_KHX*/ struct MemoryFdPropertiesKHX { operator const VkMemoryFdPropertiesKHX&() const { return *reinterpret_cast(this); } bool operator==( MemoryFdPropertiesKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( memoryTypeBits == rhs.memoryTypeBits ); } bool operator!=( MemoryFdPropertiesKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: void* pNext; uint32_t memoryTypeBits; }; static_assert( sizeof( MemoryFdPropertiesKHX ) == sizeof( VkMemoryFdPropertiesKHX ), "struct and wrapper have different size!" ); #ifdef VK_USE_PLATFORM_WIN32_KHR struct Win32KeyedMutexAcquireReleaseInfoKHX { Win32KeyedMutexAcquireReleaseInfoKHX( uint32_t acquireCount_ = 0, const DeviceMemory* pAcquireSyncs_ = nullptr, const uint64_t* pAcquireKeys_ = nullptr, const uint32_t* pAcquireTimeouts_ = nullptr, uint32_t releaseCount_ = 0, const DeviceMemory* pReleaseSyncs_ = nullptr, const uint64_t* pReleaseKeys_ = nullptr ) : sType( StructureType::eWin32KeyedMutexAcquireReleaseInfoKHX ) , pNext( nullptr ) , acquireCount( acquireCount_ ) , pAcquireSyncs( pAcquireSyncs_ ) , pAcquireKeys( pAcquireKeys_ ) , pAcquireTimeouts( pAcquireTimeouts_ ) , releaseCount( releaseCount_ ) , pReleaseSyncs( pReleaseSyncs_ ) , pReleaseKeys( pReleaseKeys_ ) { } Win32KeyedMutexAcquireReleaseInfoKHX( VkWin32KeyedMutexAcquireReleaseInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(Win32KeyedMutexAcquireReleaseInfoKHX) ); } Win32KeyedMutexAcquireReleaseInfoKHX& operator=( VkWin32KeyedMutexAcquireReleaseInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(Win32KeyedMutexAcquireReleaseInfoKHX) ); return *this; } Win32KeyedMutexAcquireReleaseInfoKHX& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } Win32KeyedMutexAcquireReleaseInfoKHX& setAcquireCount( uint32_t acquireCount_ ) { acquireCount = acquireCount_; return *this; } Win32KeyedMutexAcquireReleaseInfoKHX& setPAcquireSyncs( const DeviceMemory* pAcquireSyncs_ ) { pAcquireSyncs = pAcquireSyncs_; return *this; } Win32KeyedMutexAcquireReleaseInfoKHX& setPAcquireKeys( const uint64_t* pAcquireKeys_ ) { pAcquireKeys = pAcquireKeys_; return *this; } Win32KeyedMutexAcquireReleaseInfoKHX& setPAcquireTimeouts( const uint32_t* pAcquireTimeouts_ ) { pAcquireTimeouts = pAcquireTimeouts_; return *this; } Win32KeyedMutexAcquireReleaseInfoKHX& setReleaseCount( uint32_t releaseCount_ ) { releaseCount = releaseCount_; return *this; } Win32KeyedMutexAcquireReleaseInfoKHX& setPReleaseSyncs( const DeviceMemory* pReleaseSyncs_ ) { pReleaseSyncs = pReleaseSyncs_; return *this; } Win32KeyedMutexAcquireReleaseInfoKHX& setPReleaseKeys( const uint64_t* pReleaseKeys_ ) { pReleaseKeys = pReleaseKeys_; return *this; } operator const VkWin32KeyedMutexAcquireReleaseInfoKHX&() const { return *reinterpret_cast(this); } bool operator==( Win32KeyedMutexAcquireReleaseInfoKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( acquireCount == rhs.acquireCount ) && ( pAcquireSyncs == rhs.pAcquireSyncs ) && ( pAcquireKeys == rhs.pAcquireKeys ) && ( pAcquireTimeouts == rhs.pAcquireTimeouts ) && ( releaseCount == rhs.releaseCount ) && ( pReleaseSyncs == rhs.pReleaseSyncs ) && ( pReleaseKeys == rhs.pReleaseKeys ); } bool operator!=( Win32KeyedMutexAcquireReleaseInfoKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; uint32_t acquireCount; const DeviceMemory* pAcquireSyncs; const uint64_t* pAcquireKeys; const uint32_t* pAcquireTimeouts; uint32_t releaseCount; const DeviceMemory* pReleaseSyncs; const uint64_t* pReleaseKeys; }; static_assert( sizeof( Win32KeyedMutexAcquireReleaseInfoKHX ) == sizeof( VkWin32KeyedMutexAcquireReleaseInfoKHX ), "struct and wrapper have different size!" ); #endif /*VK_USE_PLATFORM_WIN32_KHR*/ #ifdef VK_USE_PLATFORM_WIN32_KHX struct ExportSemaphoreWin32HandleInfoKHX { ExportSemaphoreWin32HandleInfoKHX( const SECURITY_ATTRIBUTES* pAttributes_ = nullptr, DWORD dwAccess_ = 0, LPCWSTR name_ = 0 ) : sType( StructureType::eExportSemaphoreWin32HandleInfoKHX ) , pNext( nullptr ) , pAttributes( pAttributes_ ) , dwAccess( dwAccess_ ) , name( name_ ) { } ExportSemaphoreWin32HandleInfoKHX( VkExportSemaphoreWin32HandleInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(ExportSemaphoreWin32HandleInfoKHX) ); } ExportSemaphoreWin32HandleInfoKHX& operator=( VkExportSemaphoreWin32HandleInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(ExportSemaphoreWin32HandleInfoKHX) ); return *this; } ExportSemaphoreWin32HandleInfoKHX& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } ExportSemaphoreWin32HandleInfoKHX& setPAttributes( const SECURITY_ATTRIBUTES* pAttributes_ ) { pAttributes = pAttributes_; return *this; } ExportSemaphoreWin32HandleInfoKHX& setDwAccess( DWORD dwAccess_ ) { dwAccess = dwAccess_; return *this; } ExportSemaphoreWin32HandleInfoKHX& setName( LPCWSTR name_ ) { name = name_; return *this; } operator const VkExportSemaphoreWin32HandleInfoKHX&() const { return *reinterpret_cast(this); } bool operator==( ExportSemaphoreWin32HandleInfoKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( pAttributes == rhs.pAttributes ) && ( dwAccess == rhs.dwAccess ) && ( name == rhs.name ); } bool operator!=( ExportSemaphoreWin32HandleInfoKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; const SECURITY_ATTRIBUTES* pAttributes; DWORD dwAccess; LPCWSTR name; }; static_assert( sizeof( ExportSemaphoreWin32HandleInfoKHX ) == sizeof( VkExportSemaphoreWin32HandleInfoKHX ), "struct and wrapper have different size!" ); #endif /*VK_USE_PLATFORM_WIN32_KHX*/ #ifdef VK_USE_PLATFORM_WIN32_KHX struct D3D12FenceSubmitInfoKHX { D3D12FenceSubmitInfoKHX( uint32_t waitSemaphoreValuesCount_ = 0, const uint64_t* pWaitSemaphoreValues_ = nullptr, uint32_t signalSemaphoreValuesCount_ = 0, const uint64_t* pSignalSemaphoreValues_ = nullptr ) : sType( StructureType::eD3D12FenceSubmitInfoKHX ) , pNext( nullptr ) , waitSemaphoreValuesCount( waitSemaphoreValuesCount_ ) , pWaitSemaphoreValues( pWaitSemaphoreValues_ ) , signalSemaphoreValuesCount( signalSemaphoreValuesCount_ ) , pSignalSemaphoreValues( pSignalSemaphoreValues_ ) { } D3D12FenceSubmitInfoKHX( VkD3D12FenceSubmitInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(D3D12FenceSubmitInfoKHX) ); } D3D12FenceSubmitInfoKHX& operator=( VkD3D12FenceSubmitInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(D3D12FenceSubmitInfoKHX) ); return *this; } D3D12FenceSubmitInfoKHX& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } D3D12FenceSubmitInfoKHX& setWaitSemaphoreValuesCount( uint32_t waitSemaphoreValuesCount_ ) { waitSemaphoreValuesCount = waitSemaphoreValuesCount_; return *this; } D3D12FenceSubmitInfoKHX& setPWaitSemaphoreValues( const uint64_t* pWaitSemaphoreValues_ ) { pWaitSemaphoreValues = pWaitSemaphoreValues_; return *this; } D3D12FenceSubmitInfoKHX& setSignalSemaphoreValuesCount( uint32_t signalSemaphoreValuesCount_ ) { signalSemaphoreValuesCount = signalSemaphoreValuesCount_; return *this; } D3D12FenceSubmitInfoKHX& setPSignalSemaphoreValues( const uint64_t* pSignalSemaphoreValues_ ) { pSignalSemaphoreValues = pSignalSemaphoreValues_; return *this; } operator const VkD3D12FenceSubmitInfoKHX&() const { return *reinterpret_cast(this); } bool operator==( D3D12FenceSubmitInfoKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( waitSemaphoreValuesCount == rhs.waitSemaphoreValuesCount ) && ( pWaitSemaphoreValues == rhs.pWaitSemaphoreValues ) && ( signalSemaphoreValuesCount == rhs.signalSemaphoreValuesCount ) && ( pSignalSemaphoreValues == rhs.pSignalSemaphoreValues ); } bool operator!=( D3D12FenceSubmitInfoKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; uint32_t waitSemaphoreValuesCount; const uint64_t* pWaitSemaphoreValues; uint32_t signalSemaphoreValuesCount; const uint64_t* pSignalSemaphoreValues; }; static_assert( sizeof( D3D12FenceSubmitInfoKHX ) == sizeof( VkD3D12FenceSubmitInfoKHX ), "struct and wrapper have different size!" ); #endif /*VK_USE_PLATFORM_WIN32_KHX*/ struct PhysicalDeviceMultiviewFeaturesKHX { PhysicalDeviceMultiviewFeaturesKHX( Bool32 multiview_ = 0, Bool32 multiviewGeometryShader_ = 0, Bool32 multiviewTessellationShader_ = 0 ) : sType( StructureType::ePhysicalDeviceMultiviewFeaturesKHX ) , pNext( nullptr ) , multiview( multiview_ ) , multiviewGeometryShader( multiviewGeometryShader_ ) , multiviewTessellationShader( multiviewTessellationShader_ ) { } PhysicalDeviceMultiviewFeaturesKHX( VkPhysicalDeviceMultiviewFeaturesKHX const & rhs ) { memcpy( this, &rhs, sizeof(PhysicalDeviceMultiviewFeaturesKHX) ); } PhysicalDeviceMultiviewFeaturesKHX& operator=( VkPhysicalDeviceMultiviewFeaturesKHX const & rhs ) { memcpy( this, &rhs, sizeof(PhysicalDeviceMultiviewFeaturesKHX) ); return *this; } PhysicalDeviceMultiviewFeaturesKHX& setPNext( void* pNext_ ) { pNext = pNext_; return *this; } PhysicalDeviceMultiviewFeaturesKHX& setMultiview( Bool32 multiview_ ) { multiview = multiview_; return *this; } PhysicalDeviceMultiviewFeaturesKHX& setMultiviewGeometryShader( Bool32 multiviewGeometryShader_ ) { multiviewGeometryShader = multiviewGeometryShader_; return *this; } PhysicalDeviceMultiviewFeaturesKHX& setMultiviewTessellationShader( Bool32 multiviewTessellationShader_ ) { multiviewTessellationShader = multiviewTessellationShader_; return *this; } operator const VkPhysicalDeviceMultiviewFeaturesKHX&() const { return *reinterpret_cast(this); } bool operator==( PhysicalDeviceMultiviewFeaturesKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( multiview == rhs.multiview ) && ( multiviewGeometryShader == rhs.multiviewGeometryShader ) && ( multiviewTessellationShader == rhs.multiviewTessellationShader ); } bool operator!=( PhysicalDeviceMultiviewFeaturesKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: void* pNext; Bool32 multiview; Bool32 multiviewGeometryShader; Bool32 multiviewTessellationShader; }; static_assert( sizeof( PhysicalDeviceMultiviewFeaturesKHX ) == sizeof( VkPhysicalDeviceMultiviewFeaturesKHX ), "struct and wrapper have different size!" ); struct PhysicalDeviceMultiviewPropertiesKHX { operator const VkPhysicalDeviceMultiviewPropertiesKHX&() const { return *reinterpret_cast(this); } bool operator==( PhysicalDeviceMultiviewPropertiesKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( maxMultiviewViewCount == rhs.maxMultiviewViewCount ) && ( maxMultiviewInstanceIndex == rhs.maxMultiviewInstanceIndex ); } bool operator!=( PhysicalDeviceMultiviewPropertiesKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: void* pNext; uint32_t maxMultiviewViewCount; uint32_t maxMultiviewInstanceIndex; }; static_assert( sizeof( PhysicalDeviceMultiviewPropertiesKHX ) == sizeof( VkPhysicalDeviceMultiviewPropertiesKHX ), "struct and wrapper have different size!" ); struct RenderPassMultiviewCreateInfoKHX { RenderPassMultiviewCreateInfoKHX( uint32_t subpassCount_ = 0, const uint32_t* pViewMasks_ = nullptr, uint32_t dependencyCount_ = 0, const int32_t* pViewOffsets_ = nullptr, uint32_t correlationMaskCount_ = 0, const uint32_t* pCorrelationMasks_ = nullptr ) : sType( StructureType::eRenderPassMultiviewCreateInfoKHX ) , pNext( nullptr ) , subpassCount( subpassCount_ ) , pViewMasks( pViewMasks_ ) , dependencyCount( dependencyCount_ ) , pViewOffsets( pViewOffsets_ ) , correlationMaskCount( correlationMaskCount_ ) , pCorrelationMasks( pCorrelationMasks_ ) { } RenderPassMultiviewCreateInfoKHX( VkRenderPassMultiviewCreateInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(RenderPassMultiviewCreateInfoKHX) ); } RenderPassMultiviewCreateInfoKHX& operator=( VkRenderPassMultiviewCreateInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(RenderPassMultiviewCreateInfoKHX) ); return *this; } RenderPassMultiviewCreateInfoKHX& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } RenderPassMultiviewCreateInfoKHX& setSubpassCount( uint32_t subpassCount_ ) { subpassCount = subpassCount_; return *this; } RenderPassMultiviewCreateInfoKHX& setPViewMasks( const uint32_t* pViewMasks_ ) { pViewMasks = pViewMasks_; return *this; } RenderPassMultiviewCreateInfoKHX& setDependencyCount( uint32_t dependencyCount_ ) { dependencyCount = dependencyCount_; return *this; } RenderPassMultiviewCreateInfoKHX& setPViewOffsets( const int32_t* pViewOffsets_ ) { pViewOffsets = pViewOffsets_; return *this; } RenderPassMultiviewCreateInfoKHX& setCorrelationMaskCount( uint32_t correlationMaskCount_ ) { correlationMaskCount = correlationMaskCount_; return *this; } RenderPassMultiviewCreateInfoKHX& setPCorrelationMasks( const uint32_t* pCorrelationMasks_ ) { pCorrelationMasks = pCorrelationMasks_; return *this; } operator const VkRenderPassMultiviewCreateInfoKHX&() const { return *reinterpret_cast(this); } bool operator==( RenderPassMultiviewCreateInfoKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( subpassCount == rhs.subpassCount ) && ( pViewMasks == rhs.pViewMasks ) && ( dependencyCount == rhs.dependencyCount ) && ( pViewOffsets == rhs.pViewOffsets ) && ( correlationMaskCount == rhs.correlationMaskCount ) && ( pCorrelationMasks == rhs.pCorrelationMasks ); } bool operator!=( RenderPassMultiviewCreateInfoKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; uint32_t subpassCount; const uint32_t* pViewMasks; uint32_t dependencyCount; const int32_t* pViewOffsets; uint32_t correlationMaskCount; const uint32_t* pCorrelationMasks; }; static_assert( sizeof( RenderPassMultiviewCreateInfoKHX ) == sizeof( VkRenderPassMultiviewCreateInfoKHX ), "struct and wrapper have different size!" ); struct BindBufferMemoryInfoKHX { BindBufferMemoryInfoKHX( Buffer buffer_ = Buffer(), DeviceMemory memory_ = DeviceMemory(), DeviceSize memoryOffset_ = 0, uint32_t deviceIndexCount_ = 0, const uint32_t* pDeviceIndices_ = nullptr ) : sType( StructureType::eBindBufferMemoryInfoKHX ) , pNext( nullptr ) , buffer( buffer_ ) , memory( memory_ ) , memoryOffset( memoryOffset_ ) , deviceIndexCount( deviceIndexCount_ ) , pDeviceIndices( pDeviceIndices_ ) { } BindBufferMemoryInfoKHX( VkBindBufferMemoryInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(BindBufferMemoryInfoKHX) ); } BindBufferMemoryInfoKHX& operator=( VkBindBufferMemoryInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(BindBufferMemoryInfoKHX) ); return *this; } BindBufferMemoryInfoKHX& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } BindBufferMemoryInfoKHX& setBuffer( Buffer buffer_ ) { buffer = buffer_; return *this; } BindBufferMemoryInfoKHX& setMemory( DeviceMemory memory_ ) { memory = memory_; return *this; } BindBufferMemoryInfoKHX& setMemoryOffset( DeviceSize memoryOffset_ ) { memoryOffset = memoryOffset_; return *this; } BindBufferMemoryInfoKHX& setDeviceIndexCount( uint32_t deviceIndexCount_ ) { deviceIndexCount = deviceIndexCount_; return *this; } BindBufferMemoryInfoKHX& setPDeviceIndices( const uint32_t* pDeviceIndices_ ) { pDeviceIndices = pDeviceIndices_; return *this; } operator const VkBindBufferMemoryInfoKHX&() const { return *reinterpret_cast(this); } bool operator==( BindBufferMemoryInfoKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( buffer == rhs.buffer ) && ( memory == rhs.memory ) && ( memoryOffset == rhs.memoryOffset ) && ( deviceIndexCount == rhs.deviceIndexCount ) && ( pDeviceIndices == rhs.pDeviceIndices ); } bool operator!=( BindBufferMemoryInfoKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; Buffer buffer; DeviceMemory memory; DeviceSize memoryOffset; uint32_t deviceIndexCount; const uint32_t* pDeviceIndices; }; static_assert( sizeof( BindBufferMemoryInfoKHX ) == sizeof( VkBindBufferMemoryInfoKHX ), "struct and wrapper have different size!" ); struct BindImageMemoryInfoKHX { BindImageMemoryInfoKHX( Image image_ = Image(), DeviceMemory memory_ = DeviceMemory(), DeviceSize memoryOffset_ = 0, uint32_t deviceIndexCount_ = 0, const uint32_t* pDeviceIndices_ = nullptr, uint32_t SFRRectCount_ = 0, const Rect2D* pSFRRects_ = nullptr ) : sType( StructureType::eBindImageMemoryInfoKHX ) , pNext( nullptr ) , image( image_ ) , memory( memory_ ) , memoryOffset( memoryOffset_ ) , deviceIndexCount( deviceIndexCount_ ) , pDeviceIndices( pDeviceIndices_ ) , SFRRectCount( SFRRectCount_ ) , pSFRRects( pSFRRects_ ) { } BindImageMemoryInfoKHX( VkBindImageMemoryInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(BindImageMemoryInfoKHX) ); } BindImageMemoryInfoKHX& operator=( VkBindImageMemoryInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(BindImageMemoryInfoKHX) ); return *this; } BindImageMemoryInfoKHX& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } BindImageMemoryInfoKHX& setImage( Image image_ ) { image = image_; return *this; } BindImageMemoryInfoKHX& setMemory( DeviceMemory memory_ ) { memory = memory_; return *this; } BindImageMemoryInfoKHX& setMemoryOffset( DeviceSize memoryOffset_ ) { memoryOffset = memoryOffset_; return *this; } BindImageMemoryInfoKHX& setDeviceIndexCount( uint32_t deviceIndexCount_ ) { deviceIndexCount = deviceIndexCount_; return *this; } BindImageMemoryInfoKHX& setPDeviceIndices( const uint32_t* pDeviceIndices_ ) { pDeviceIndices = pDeviceIndices_; return *this; } BindImageMemoryInfoKHX& setSFRRectCount( uint32_t SFRRectCount_ ) { SFRRectCount = SFRRectCount_; return *this; } BindImageMemoryInfoKHX& setPSFRRects( const Rect2D* pSFRRects_ ) { pSFRRects = pSFRRects_; return *this; } operator const VkBindImageMemoryInfoKHX&() const { return *reinterpret_cast(this); } bool operator==( BindImageMemoryInfoKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( image == rhs.image ) && ( memory == rhs.memory ) && ( memoryOffset == rhs.memoryOffset ) && ( deviceIndexCount == rhs.deviceIndexCount ) && ( pDeviceIndices == rhs.pDeviceIndices ) && ( SFRRectCount == rhs.SFRRectCount ) && ( pSFRRects == rhs.pSFRRects ); } bool operator!=( BindImageMemoryInfoKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; Image image; DeviceMemory memory; DeviceSize memoryOffset; uint32_t deviceIndexCount; const uint32_t* pDeviceIndices; uint32_t SFRRectCount; const Rect2D* pSFRRects; }; static_assert( sizeof( BindImageMemoryInfoKHX ) == sizeof( VkBindImageMemoryInfoKHX ), "struct and wrapper have different size!" ); struct DeviceGroupRenderPassBeginInfoKHX { DeviceGroupRenderPassBeginInfoKHX( uint32_t deviceMask_ = 0, uint32_t deviceRenderAreaCount_ = 0, const Rect2D* pDeviceRenderAreas_ = nullptr ) : sType( StructureType::eDeviceGroupRenderPassBeginInfoKHX ) , pNext( nullptr ) , deviceMask( deviceMask_ ) , deviceRenderAreaCount( deviceRenderAreaCount_ ) , pDeviceRenderAreas( pDeviceRenderAreas_ ) { } DeviceGroupRenderPassBeginInfoKHX( VkDeviceGroupRenderPassBeginInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(DeviceGroupRenderPassBeginInfoKHX) ); } DeviceGroupRenderPassBeginInfoKHX& operator=( VkDeviceGroupRenderPassBeginInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(DeviceGroupRenderPassBeginInfoKHX) ); return *this; } DeviceGroupRenderPassBeginInfoKHX& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } DeviceGroupRenderPassBeginInfoKHX& setDeviceMask( uint32_t deviceMask_ ) { deviceMask = deviceMask_; return *this; } DeviceGroupRenderPassBeginInfoKHX& setDeviceRenderAreaCount( uint32_t deviceRenderAreaCount_ ) { deviceRenderAreaCount = deviceRenderAreaCount_; return *this; } DeviceGroupRenderPassBeginInfoKHX& setPDeviceRenderAreas( const Rect2D* pDeviceRenderAreas_ ) { pDeviceRenderAreas = pDeviceRenderAreas_; return *this; } operator const VkDeviceGroupRenderPassBeginInfoKHX&() const { return *reinterpret_cast(this); } bool operator==( DeviceGroupRenderPassBeginInfoKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( deviceMask == rhs.deviceMask ) && ( deviceRenderAreaCount == rhs.deviceRenderAreaCount ) && ( pDeviceRenderAreas == rhs.pDeviceRenderAreas ); } bool operator!=( DeviceGroupRenderPassBeginInfoKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; uint32_t deviceMask; uint32_t deviceRenderAreaCount; const Rect2D* pDeviceRenderAreas; }; static_assert( sizeof( DeviceGroupRenderPassBeginInfoKHX ) == sizeof( VkDeviceGroupRenderPassBeginInfoKHX ), "struct and wrapper have different size!" ); struct DeviceGroupCommandBufferBeginInfoKHX { DeviceGroupCommandBufferBeginInfoKHX( uint32_t deviceMask_ = 0 ) : sType( StructureType::eDeviceGroupCommandBufferBeginInfoKHX ) , pNext( nullptr ) , deviceMask( deviceMask_ ) { } DeviceGroupCommandBufferBeginInfoKHX( VkDeviceGroupCommandBufferBeginInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(DeviceGroupCommandBufferBeginInfoKHX) ); } DeviceGroupCommandBufferBeginInfoKHX& operator=( VkDeviceGroupCommandBufferBeginInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(DeviceGroupCommandBufferBeginInfoKHX) ); return *this; } DeviceGroupCommandBufferBeginInfoKHX& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } DeviceGroupCommandBufferBeginInfoKHX& setDeviceMask( uint32_t deviceMask_ ) { deviceMask = deviceMask_; return *this; } operator const VkDeviceGroupCommandBufferBeginInfoKHX&() const { return *reinterpret_cast(this); } bool operator==( DeviceGroupCommandBufferBeginInfoKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( deviceMask == rhs.deviceMask ); } bool operator!=( DeviceGroupCommandBufferBeginInfoKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; uint32_t deviceMask; }; static_assert( sizeof( DeviceGroupCommandBufferBeginInfoKHX ) == sizeof( VkDeviceGroupCommandBufferBeginInfoKHX ), "struct and wrapper have different size!" ); struct DeviceGroupSubmitInfoKHX { DeviceGroupSubmitInfoKHX( uint32_t waitSemaphoreCount_ = 0, const uint32_t* pWaitSemaphoreDeviceIndices_ = nullptr, uint32_t commandBufferCount_ = 0, const uint32_t* pCommandBufferDeviceMasks_ = nullptr, uint32_t signalSemaphoreCount_ = 0, const uint32_t* pSignalSemaphoreDeviceIndices_ = nullptr ) : sType( StructureType::eDeviceGroupSubmitInfoKHX ) , pNext( nullptr ) , waitSemaphoreCount( waitSemaphoreCount_ ) , pWaitSemaphoreDeviceIndices( pWaitSemaphoreDeviceIndices_ ) , commandBufferCount( commandBufferCount_ ) , pCommandBufferDeviceMasks( pCommandBufferDeviceMasks_ ) , signalSemaphoreCount( signalSemaphoreCount_ ) , pSignalSemaphoreDeviceIndices( pSignalSemaphoreDeviceIndices_ ) { } DeviceGroupSubmitInfoKHX( VkDeviceGroupSubmitInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(DeviceGroupSubmitInfoKHX) ); } DeviceGroupSubmitInfoKHX& operator=( VkDeviceGroupSubmitInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(DeviceGroupSubmitInfoKHX) ); return *this; } DeviceGroupSubmitInfoKHX& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } DeviceGroupSubmitInfoKHX& setWaitSemaphoreCount( uint32_t waitSemaphoreCount_ ) { waitSemaphoreCount = waitSemaphoreCount_; return *this; } DeviceGroupSubmitInfoKHX& setPWaitSemaphoreDeviceIndices( const uint32_t* pWaitSemaphoreDeviceIndices_ ) { pWaitSemaphoreDeviceIndices = pWaitSemaphoreDeviceIndices_; return *this; } DeviceGroupSubmitInfoKHX& setCommandBufferCount( uint32_t commandBufferCount_ ) { commandBufferCount = commandBufferCount_; return *this; } DeviceGroupSubmitInfoKHX& setPCommandBufferDeviceMasks( const uint32_t* pCommandBufferDeviceMasks_ ) { pCommandBufferDeviceMasks = pCommandBufferDeviceMasks_; return *this; } DeviceGroupSubmitInfoKHX& setSignalSemaphoreCount( uint32_t signalSemaphoreCount_ ) { signalSemaphoreCount = signalSemaphoreCount_; return *this; } DeviceGroupSubmitInfoKHX& setPSignalSemaphoreDeviceIndices( const uint32_t* pSignalSemaphoreDeviceIndices_ ) { pSignalSemaphoreDeviceIndices = pSignalSemaphoreDeviceIndices_; return *this; } operator const VkDeviceGroupSubmitInfoKHX&() const { return *reinterpret_cast(this); } bool operator==( DeviceGroupSubmitInfoKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( waitSemaphoreCount == rhs.waitSemaphoreCount ) && ( pWaitSemaphoreDeviceIndices == rhs.pWaitSemaphoreDeviceIndices ) && ( commandBufferCount == rhs.commandBufferCount ) && ( pCommandBufferDeviceMasks == rhs.pCommandBufferDeviceMasks ) && ( signalSemaphoreCount == rhs.signalSemaphoreCount ) && ( pSignalSemaphoreDeviceIndices == rhs.pSignalSemaphoreDeviceIndices ); } bool operator!=( DeviceGroupSubmitInfoKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; uint32_t waitSemaphoreCount; const uint32_t* pWaitSemaphoreDeviceIndices; uint32_t commandBufferCount; const uint32_t* pCommandBufferDeviceMasks; uint32_t signalSemaphoreCount; const uint32_t* pSignalSemaphoreDeviceIndices; }; static_assert( sizeof( DeviceGroupSubmitInfoKHX ) == sizeof( VkDeviceGroupSubmitInfoKHX ), "struct and wrapper have different size!" ); struct DeviceGroupBindSparseInfoKHX { DeviceGroupBindSparseInfoKHX( uint32_t resourceDeviceIndex_ = 0, uint32_t memoryDeviceIndex_ = 0 ) : sType( StructureType::eDeviceGroupBindSparseInfoKHX ) , pNext( nullptr ) , resourceDeviceIndex( resourceDeviceIndex_ ) , memoryDeviceIndex( memoryDeviceIndex_ ) { } DeviceGroupBindSparseInfoKHX( VkDeviceGroupBindSparseInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(DeviceGroupBindSparseInfoKHX) ); } DeviceGroupBindSparseInfoKHX& operator=( VkDeviceGroupBindSparseInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(DeviceGroupBindSparseInfoKHX) ); return *this; } DeviceGroupBindSparseInfoKHX& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } DeviceGroupBindSparseInfoKHX& setResourceDeviceIndex( uint32_t resourceDeviceIndex_ ) { resourceDeviceIndex = resourceDeviceIndex_; return *this; } DeviceGroupBindSparseInfoKHX& setMemoryDeviceIndex( uint32_t memoryDeviceIndex_ ) { memoryDeviceIndex = memoryDeviceIndex_; return *this; } operator const VkDeviceGroupBindSparseInfoKHX&() const { return *reinterpret_cast(this); } bool operator==( DeviceGroupBindSparseInfoKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( resourceDeviceIndex == rhs.resourceDeviceIndex ) && ( memoryDeviceIndex == rhs.memoryDeviceIndex ); } bool operator!=( DeviceGroupBindSparseInfoKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; uint32_t resourceDeviceIndex; uint32_t memoryDeviceIndex; }; static_assert( sizeof( DeviceGroupBindSparseInfoKHX ) == sizeof( VkDeviceGroupBindSparseInfoKHX ), "struct and wrapper have different size!" ); struct ImageSwapchainCreateInfoKHX { ImageSwapchainCreateInfoKHX( SwapchainKHR swapchain_ = SwapchainKHR() ) : sType( StructureType::eImageSwapchainCreateInfoKHX ) , pNext( nullptr ) , swapchain( swapchain_ ) { } ImageSwapchainCreateInfoKHX( VkImageSwapchainCreateInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(ImageSwapchainCreateInfoKHX) ); } ImageSwapchainCreateInfoKHX& operator=( VkImageSwapchainCreateInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(ImageSwapchainCreateInfoKHX) ); return *this; } ImageSwapchainCreateInfoKHX& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } ImageSwapchainCreateInfoKHX& setSwapchain( SwapchainKHR swapchain_ ) { swapchain = swapchain_; return *this; } operator const VkImageSwapchainCreateInfoKHX&() const { return *reinterpret_cast(this); } bool operator==( ImageSwapchainCreateInfoKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( swapchain == rhs.swapchain ); } bool operator!=( ImageSwapchainCreateInfoKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; SwapchainKHR swapchain; }; static_assert( sizeof( ImageSwapchainCreateInfoKHX ) == sizeof( VkImageSwapchainCreateInfoKHX ), "struct and wrapper have different size!" ); struct BindImageMemorySwapchainInfoKHX { BindImageMemorySwapchainInfoKHX( SwapchainKHR swapchain_ = SwapchainKHR(), uint32_t imageIndex_ = 0 ) : sType( StructureType::eBindImageMemorySwapchainInfoKHX ) , pNext( nullptr ) , swapchain( swapchain_ ) , imageIndex( imageIndex_ ) { } BindImageMemorySwapchainInfoKHX( VkBindImageMemorySwapchainInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(BindImageMemorySwapchainInfoKHX) ); } BindImageMemorySwapchainInfoKHX& operator=( VkBindImageMemorySwapchainInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(BindImageMemorySwapchainInfoKHX) ); return *this; } BindImageMemorySwapchainInfoKHX& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } BindImageMemorySwapchainInfoKHX& setSwapchain( SwapchainKHR swapchain_ ) { swapchain = swapchain_; return *this; } BindImageMemorySwapchainInfoKHX& setImageIndex( uint32_t imageIndex_ ) { imageIndex = imageIndex_; return *this; } operator const VkBindImageMemorySwapchainInfoKHX&() const { return *reinterpret_cast(this); } bool operator==( BindImageMemorySwapchainInfoKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( swapchain == rhs.swapchain ) && ( imageIndex == rhs.imageIndex ); } bool operator!=( BindImageMemorySwapchainInfoKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; SwapchainKHR swapchain; uint32_t imageIndex; }; static_assert( sizeof( BindImageMemorySwapchainInfoKHX ) == sizeof( VkBindImageMemorySwapchainInfoKHX ), "struct and wrapper have different size!" ); struct AcquireNextImageInfoKHX { AcquireNextImageInfoKHX( SwapchainKHR swapchain_ = SwapchainKHR(), uint64_t timeout_ = 0, Semaphore semaphore_ = Semaphore(), Fence fence_ = Fence(), uint32_t deviceMask_ = 0 ) : sType( StructureType::eAcquireNextImageInfoKHX ) , pNext( nullptr ) , swapchain( swapchain_ ) , timeout( timeout_ ) , semaphore( semaphore_ ) , fence( fence_ ) , deviceMask( deviceMask_ ) { } AcquireNextImageInfoKHX( VkAcquireNextImageInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(AcquireNextImageInfoKHX) ); } AcquireNextImageInfoKHX& operator=( VkAcquireNextImageInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(AcquireNextImageInfoKHX) ); return *this; } AcquireNextImageInfoKHX& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } AcquireNextImageInfoKHX& setSwapchain( SwapchainKHR swapchain_ ) { swapchain = swapchain_; return *this; } AcquireNextImageInfoKHX& setTimeout( uint64_t timeout_ ) { timeout = timeout_; return *this; } AcquireNextImageInfoKHX& setSemaphore( Semaphore semaphore_ ) { semaphore = semaphore_; return *this; } AcquireNextImageInfoKHX& setFence( Fence fence_ ) { fence = fence_; return *this; } AcquireNextImageInfoKHX& setDeviceMask( uint32_t deviceMask_ ) { deviceMask = deviceMask_; return *this; } operator const VkAcquireNextImageInfoKHX&() const { return *reinterpret_cast(this); } bool operator==( AcquireNextImageInfoKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( swapchain == rhs.swapchain ) && ( timeout == rhs.timeout ) && ( semaphore == rhs.semaphore ) && ( fence == rhs.fence ) && ( deviceMask == rhs.deviceMask ); } bool operator!=( AcquireNextImageInfoKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; SwapchainKHR swapchain; uint64_t timeout; Semaphore semaphore; Fence fence; uint32_t deviceMask; }; static_assert( sizeof( AcquireNextImageInfoKHX ) == sizeof( VkAcquireNextImageInfoKHX ), "struct and wrapper have different size!" ); struct HdrMetadataEXT { HdrMetadataEXT( XYColorEXT displayPrimaryRed_ = XYColorEXT(), XYColorEXT displayPrimaryGreen_ = XYColorEXT(), XYColorEXT displayPrimaryBlue_ = XYColorEXT(), XYColorEXT whitePoint_ = XYColorEXT(), float maxLuminance_ = 0, float minLuminance_ = 0, float maxContentLightLevel_ = 0, float maxFrameAverageLightLevel_ = 0 ) : sType( StructureType::eHdrMetadataEXT ) , pNext( nullptr ) , displayPrimaryRed( displayPrimaryRed_ ) , displayPrimaryGreen( displayPrimaryGreen_ ) , displayPrimaryBlue( displayPrimaryBlue_ ) , whitePoint( whitePoint_ ) , maxLuminance( maxLuminance_ ) , minLuminance( minLuminance_ ) , maxContentLightLevel( maxContentLightLevel_ ) , maxFrameAverageLightLevel( maxFrameAverageLightLevel_ ) { } HdrMetadataEXT( VkHdrMetadataEXT const & rhs ) { memcpy( this, &rhs, sizeof(HdrMetadataEXT) ); } HdrMetadataEXT& operator=( VkHdrMetadataEXT const & rhs ) { memcpy( this, &rhs, sizeof(HdrMetadataEXT) ); return *this; } HdrMetadataEXT& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } HdrMetadataEXT& setDisplayPrimaryRed( XYColorEXT displayPrimaryRed_ ) { displayPrimaryRed = displayPrimaryRed_; return *this; } HdrMetadataEXT& setDisplayPrimaryGreen( XYColorEXT displayPrimaryGreen_ ) { displayPrimaryGreen = displayPrimaryGreen_; return *this; } HdrMetadataEXT& setDisplayPrimaryBlue( XYColorEXT displayPrimaryBlue_ ) { displayPrimaryBlue = displayPrimaryBlue_; return *this; } HdrMetadataEXT& setWhitePoint( XYColorEXT whitePoint_ ) { whitePoint = whitePoint_; return *this; } HdrMetadataEXT& setMaxLuminance( float maxLuminance_ ) { maxLuminance = maxLuminance_; return *this; } HdrMetadataEXT& setMinLuminance( float minLuminance_ ) { minLuminance = minLuminance_; return *this; } HdrMetadataEXT& setMaxContentLightLevel( float maxContentLightLevel_ ) { maxContentLightLevel = maxContentLightLevel_; return *this; } HdrMetadataEXT& setMaxFrameAverageLightLevel( float maxFrameAverageLightLevel_ ) { maxFrameAverageLightLevel = maxFrameAverageLightLevel_; return *this; } operator const VkHdrMetadataEXT&() const { return *reinterpret_cast(this); } bool operator==( HdrMetadataEXT const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( displayPrimaryRed == rhs.displayPrimaryRed ) && ( displayPrimaryGreen == rhs.displayPrimaryGreen ) && ( displayPrimaryBlue == rhs.displayPrimaryBlue ) && ( whitePoint == rhs.whitePoint ) && ( maxLuminance == rhs.maxLuminance ) && ( minLuminance == rhs.minLuminance ) && ( maxContentLightLevel == rhs.maxContentLightLevel ) && ( maxFrameAverageLightLevel == rhs.maxFrameAverageLightLevel ); } bool operator!=( HdrMetadataEXT const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; XYColorEXT displayPrimaryRed; XYColorEXT displayPrimaryGreen; XYColorEXT displayPrimaryBlue; XYColorEXT whitePoint; float maxLuminance; float minLuminance; float maxContentLightLevel; float maxFrameAverageLightLevel; }; static_assert( sizeof( HdrMetadataEXT ) == sizeof( VkHdrMetadataEXT ), "struct and wrapper have different size!" ); struct PresentTimesInfoGOOGLE { PresentTimesInfoGOOGLE( uint32_t swapchainCount_ = 0, const PresentTimeGOOGLE* pTimes_ = nullptr ) : sType( StructureType::ePresentTimesInfoGOOGLE ) , pNext( nullptr ) , swapchainCount( swapchainCount_ ) , pTimes( pTimes_ ) { } PresentTimesInfoGOOGLE( VkPresentTimesInfoGOOGLE const & rhs ) { memcpy( this, &rhs, sizeof(PresentTimesInfoGOOGLE) ); } PresentTimesInfoGOOGLE& operator=( VkPresentTimesInfoGOOGLE const & rhs ) { memcpy( this, &rhs, sizeof(PresentTimesInfoGOOGLE) ); return *this; } PresentTimesInfoGOOGLE& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } PresentTimesInfoGOOGLE& setSwapchainCount( uint32_t swapchainCount_ ) { swapchainCount = swapchainCount_; return *this; } PresentTimesInfoGOOGLE& setPTimes( const PresentTimeGOOGLE* pTimes_ ) { pTimes = pTimes_; return *this; } operator const VkPresentTimesInfoGOOGLE&() const { return *reinterpret_cast(this); } bool operator==( PresentTimesInfoGOOGLE const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( swapchainCount == rhs.swapchainCount ) && ( pTimes == rhs.pTimes ); } bool operator!=( PresentTimesInfoGOOGLE const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; uint32_t swapchainCount; const PresentTimeGOOGLE* pTimes; }; static_assert( sizeof( PresentTimesInfoGOOGLE ) == sizeof( VkPresentTimesInfoGOOGLE ), "struct and wrapper have different size!" ); #ifdef VK_USE_PLATFORM_IOS_MVK struct IOSSurfaceCreateInfoMVK { IOSSurfaceCreateInfoMVK( IOSSurfaceCreateFlagsMVK flags_ = IOSSurfaceCreateFlagsMVK(), const void* pView_ = nullptr ) : sType( StructureType::eIOSSurfaceCreateInfoMVK ) , pNext( nullptr ) , flags( flags_ ) , pView( pView_ ) { } IOSSurfaceCreateInfoMVK( VkIOSSurfaceCreateInfoMVK const & rhs ) { memcpy( this, &rhs, sizeof(IOSSurfaceCreateInfoMVK) ); } IOSSurfaceCreateInfoMVK& operator=( VkIOSSurfaceCreateInfoMVK const & rhs ) { memcpy( this, &rhs, sizeof(IOSSurfaceCreateInfoMVK) ); return *this; } IOSSurfaceCreateInfoMVK& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } IOSSurfaceCreateInfoMVK& setFlags( IOSSurfaceCreateFlagsMVK flags_ ) { flags = flags_; return *this; } IOSSurfaceCreateInfoMVK& setPView( const void* pView_ ) { pView = pView_; return *this; } operator const VkIOSSurfaceCreateInfoMVK&() const { return *reinterpret_cast(this); } bool operator==( IOSSurfaceCreateInfoMVK const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( pView == rhs.pView ); } bool operator!=( IOSSurfaceCreateInfoMVK const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; IOSSurfaceCreateFlagsMVK flags; const void* pView; }; static_assert( sizeof( IOSSurfaceCreateInfoMVK ) == sizeof( VkIOSSurfaceCreateInfoMVK ), "struct and wrapper have different size!" ); #endif /*VK_USE_PLATFORM_IOS_MVK*/ #ifdef VK_USE_PLATFORM_MACOS_MVK struct MacOSSurfaceCreateInfoMVK { MacOSSurfaceCreateInfoMVK( MacOSSurfaceCreateFlagsMVK flags_ = MacOSSurfaceCreateFlagsMVK(), const void* pView_ = nullptr ) : sType( StructureType::eMacOSSurfaceCreateInfoMVK ) , pNext( nullptr ) , flags( flags_ ) , pView( pView_ ) { } MacOSSurfaceCreateInfoMVK( VkMacOSSurfaceCreateInfoMVK const & rhs ) { memcpy( this, &rhs, sizeof(MacOSSurfaceCreateInfoMVK) ); } MacOSSurfaceCreateInfoMVK& operator=( VkMacOSSurfaceCreateInfoMVK const & rhs ) { memcpy( this, &rhs, sizeof(MacOSSurfaceCreateInfoMVK) ); return *this; } MacOSSurfaceCreateInfoMVK& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } MacOSSurfaceCreateInfoMVK& setFlags( MacOSSurfaceCreateFlagsMVK flags_ ) { flags = flags_; return *this; } MacOSSurfaceCreateInfoMVK& setPView( const void* pView_ ) { pView = pView_; return *this; } operator const VkMacOSSurfaceCreateInfoMVK&() const { return *reinterpret_cast(this); } bool operator==( MacOSSurfaceCreateInfoMVK const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( pView == rhs.pView ); } bool operator!=( MacOSSurfaceCreateInfoMVK const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; MacOSSurfaceCreateFlagsMVK flags; const void* pView; }; static_assert( sizeof( MacOSSurfaceCreateInfoMVK ) == sizeof( VkMacOSSurfaceCreateInfoMVK ), "struct and wrapper have different size!" ); #endif /*VK_USE_PLATFORM_MACOS_MVK*/ struct PipelineViewportWScalingStateCreateInfoNV { PipelineViewportWScalingStateCreateInfoNV( Bool32 viewportWScalingEnable_ = 0, uint32_t viewportCount_ = 0, const ViewportWScalingNV* pViewportWScalings_ = nullptr ) : sType( StructureType::ePipelineViewportWScalingStateCreateInfoNV ) , pNext( nullptr ) , viewportWScalingEnable( viewportWScalingEnable_ ) , viewportCount( viewportCount_ ) , pViewportWScalings( pViewportWScalings_ ) { } PipelineViewportWScalingStateCreateInfoNV( VkPipelineViewportWScalingStateCreateInfoNV const & rhs ) { memcpy( this, &rhs, sizeof(PipelineViewportWScalingStateCreateInfoNV) ); } PipelineViewportWScalingStateCreateInfoNV& operator=( VkPipelineViewportWScalingStateCreateInfoNV const & rhs ) { memcpy( this, &rhs, sizeof(PipelineViewportWScalingStateCreateInfoNV) ); return *this; } PipelineViewportWScalingStateCreateInfoNV& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } PipelineViewportWScalingStateCreateInfoNV& setViewportWScalingEnable( Bool32 viewportWScalingEnable_ ) { viewportWScalingEnable = viewportWScalingEnable_; return *this; } PipelineViewportWScalingStateCreateInfoNV& setViewportCount( uint32_t viewportCount_ ) { viewportCount = viewportCount_; return *this; } PipelineViewportWScalingStateCreateInfoNV& setPViewportWScalings( const ViewportWScalingNV* pViewportWScalings_ ) { pViewportWScalings = pViewportWScalings_; return *this; } operator const VkPipelineViewportWScalingStateCreateInfoNV&() const { return *reinterpret_cast(this); } bool operator==( PipelineViewportWScalingStateCreateInfoNV const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( viewportWScalingEnable == rhs.viewportWScalingEnable ) && ( viewportCount == rhs.viewportCount ) && ( pViewportWScalings == rhs.pViewportWScalings ); } bool operator!=( PipelineViewportWScalingStateCreateInfoNV const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; Bool32 viewportWScalingEnable; uint32_t viewportCount; const ViewportWScalingNV* pViewportWScalings; }; static_assert( sizeof( PipelineViewportWScalingStateCreateInfoNV ) == sizeof( VkPipelineViewportWScalingStateCreateInfoNV ), "struct and wrapper have different size!" ); struct PhysicalDeviceDiscardRectanglePropertiesEXT { PhysicalDeviceDiscardRectanglePropertiesEXT( uint32_t maxDiscardRectangles_ = 0 ) : sType( StructureType::ePhysicalDeviceDiscardRectanglePropertiesEXT ) , pNext( nullptr ) , maxDiscardRectangles( maxDiscardRectangles_ ) { } PhysicalDeviceDiscardRectanglePropertiesEXT( VkPhysicalDeviceDiscardRectanglePropertiesEXT const & rhs ) { memcpy( this, &rhs, sizeof(PhysicalDeviceDiscardRectanglePropertiesEXT) ); } PhysicalDeviceDiscardRectanglePropertiesEXT& operator=( VkPhysicalDeviceDiscardRectanglePropertiesEXT const & rhs ) { memcpy( this, &rhs, sizeof(PhysicalDeviceDiscardRectanglePropertiesEXT) ); return *this; } PhysicalDeviceDiscardRectanglePropertiesEXT& setPNext( void* pNext_ ) { pNext = pNext_; return *this; } PhysicalDeviceDiscardRectanglePropertiesEXT& setMaxDiscardRectangles( uint32_t maxDiscardRectangles_ ) { maxDiscardRectangles = maxDiscardRectangles_; return *this; } operator const VkPhysicalDeviceDiscardRectanglePropertiesEXT&() const { return *reinterpret_cast(this); } bool operator==( PhysicalDeviceDiscardRectanglePropertiesEXT const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( maxDiscardRectangles == rhs.maxDiscardRectangles ); } bool operator!=( PhysicalDeviceDiscardRectanglePropertiesEXT const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: void* pNext; uint32_t maxDiscardRectangles; }; static_assert( sizeof( PhysicalDeviceDiscardRectanglePropertiesEXT ) == sizeof( VkPhysicalDeviceDiscardRectanglePropertiesEXT ), "struct and wrapper have different size!" ); struct PhysicalDeviceMultiviewPerViewAttributesPropertiesNVX { operator const VkPhysicalDeviceMultiviewPerViewAttributesPropertiesNVX&() const { return *reinterpret_cast(this); } bool operator==( PhysicalDeviceMultiviewPerViewAttributesPropertiesNVX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( perViewPositionAllComponents == rhs.perViewPositionAllComponents ); } bool operator!=( PhysicalDeviceMultiviewPerViewAttributesPropertiesNVX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: void* pNext; Bool32 perViewPositionAllComponents; }; static_assert( sizeof( PhysicalDeviceMultiviewPerViewAttributesPropertiesNVX ) == sizeof( VkPhysicalDeviceMultiviewPerViewAttributesPropertiesNVX ), "struct and wrapper have different size!" ); struct PhysicalDeviceSurfaceInfo2KHR { PhysicalDeviceSurfaceInfo2KHR( SurfaceKHR surface_ = SurfaceKHR() ) : sType( StructureType::ePhysicalDeviceSurfaceInfo2KHR ) , pNext( nullptr ) , surface( surface_ ) { } PhysicalDeviceSurfaceInfo2KHR( VkPhysicalDeviceSurfaceInfo2KHR const & rhs ) { memcpy( this, &rhs, sizeof(PhysicalDeviceSurfaceInfo2KHR) ); } PhysicalDeviceSurfaceInfo2KHR& operator=( VkPhysicalDeviceSurfaceInfo2KHR const & rhs ) { memcpy( this, &rhs, sizeof(PhysicalDeviceSurfaceInfo2KHR) ); return *this; } PhysicalDeviceSurfaceInfo2KHR& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } PhysicalDeviceSurfaceInfo2KHR& setSurface( SurfaceKHR surface_ ) { surface = surface_; return *this; } operator const VkPhysicalDeviceSurfaceInfo2KHR&() const { return *reinterpret_cast(this); } bool operator==( PhysicalDeviceSurfaceInfo2KHR const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( surface == rhs.surface ); } bool operator!=( PhysicalDeviceSurfaceInfo2KHR const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; SurfaceKHR surface; }; static_assert( sizeof( PhysicalDeviceSurfaceInfo2KHR ) == sizeof( VkPhysicalDeviceSurfaceInfo2KHR ), "struct and wrapper have different size!" ); enum class SubpassContents { eInline = VK_SUBPASS_CONTENTS_INLINE, eSecondaryCommandBuffers = VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS }; struct PresentInfoKHR { PresentInfoKHR( uint32_t waitSemaphoreCount_ = 0, const Semaphore* pWaitSemaphores_ = nullptr, uint32_t swapchainCount_ = 0, const SwapchainKHR* pSwapchains_ = nullptr, const uint32_t* pImageIndices_ = nullptr, Result* pResults_ = nullptr ) : sType( StructureType::ePresentInfoKHR ) , pNext( nullptr ) , waitSemaphoreCount( waitSemaphoreCount_ ) , pWaitSemaphores( pWaitSemaphores_ ) , swapchainCount( swapchainCount_ ) , pSwapchains( pSwapchains_ ) , pImageIndices( pImageIndices_ ) , pResults( pResults_ ) { } PresentInfoKHR( VkPresentInfoKHR const & rhs ) { memcpy( this, &rhs, sizeof(PresentInfoKHR) ); } PresentInfoKHR& operator=( VkPresentInfoKHR const & rhs ) { memcpy( this, &rhs, sizeof(PresentInfoKHR) ); return *this; } PresentInfoKHR& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } PresentInfoKHR& setWaitSemaphoreCount( uint32_t waitSemaphoreCount_ ) { waitSemaphoreCount = waitSemaphoreCount_; return *this; } PresentInfoKHR& setPWaitSemaphores( const Semaphore* pWaitSemaphores_ ) { pWaitSemaphores = pWaitSemaphores_; return *this; } PresentInfoKHR& setSwapchainCount( uint32_t swapchainCount_ ) { swapchainCount = swapchainCount_; return *this; } PresentInfoKHR& setPSwapchains( const SwapchainKHR* pSwapchains_ ) { pSwapchains = pSwapchains_; return *this; } PresentInfoKHR& setPImageIndices( const uint32_t* pImageIndices_ ) { pImageIndices = pImageIndices_; return *this; } PresentInfoKHR& setPResults( Result* pResults_ ) { pResults = pResults_; return *this; } operator const VkPresentInfoKHR&() const { return *reinterpret_cast(this); } bool operator==( PresentInfoKHR const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( waitSemaphoreCount == rhs.waitSemaphoreCount ) && ( pWaitSemaphores == rhs.pWaitSemaphores ) && ( swapchainCount == rhs.swapchainCount ) && ( pSwapchains == rhs.pSwapchains ) && ( pImageIndices == rhs.pImageIndices ) && ( pResults == rhs.pResults ); } bool operator!=( PresentInfoKHR const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; uint32_t waitSemaphoreCount; const Semaphore* pWaitSemaphores; uint32_t swapchainCount; const SwapchainKHR* pSwapchains; const uint32_t* pImageIndices; Result* pResults; }; static_assert( sizeof( PresentInfoKHR ) == sizeof( VkPresentInfoKHR ), "struct and wrapper have different size!" ); enum class DynamicState { eViewport = VK_DYNAMIC_STATE_VIEWPORT, eScissor = VK_DYNAMIC_STATE_SCISSOR, eLineWidth = VK_DYNAMIC_STATE_LINE_WIDTH, eDepthBias = VK_DYNAMIC_STATE_DEPTH_BIAS, eBlendConstants = VK_DYNAMIC_STATE_BLEND_CONSTANTS, eDepthBounds = VK_DYNAMIC_STATE_DEPTH_BOUNDS, eStencilCompareMask = VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, eStencilWriteMask = VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, eStencilReference = VK_DYNAMIC_STATE_STENCIL_REFERENCE, eViewportWScalingNV = VK_DYNAMIC_STATE_VIEWPORT_W_SCALING_NV, eDiscardRectangleEXT = VK_DYNAMIC_STATE_DISCARD_RECTANGLE_EXT }; struct PipelineDynamicStateCreateInfo { PipelineDynamicStateCreateInfo( PipelineDynamicStateCreateFlags flags_ = PipelineDynamicStateCreateFlags(), uint32_t dynamicStateCount_ = 0, const DynamicState* pDynamicStates_ = nullptr ) : sType( StructureType::ePipelineDynamicStateCreateInfo ) , pNext( nullptr ) , flags( flags_ ) , dynamicStateCount( dynamicStateCount_ ) , pDynamicStates( pDynamicStates_ ) { } PipelineDynamicStateCreateInfo( VkPipelineDynamicStateCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(PipelineDynamicStateCreateInfo) ); } PipelineDynamicStateCreateInfo& operator=( VkPipelineDynamicStateCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(PipelineDynamicStateCreateInfo) ); return *this; } PipelineDynamicStateCreateInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } PipelineDynamicStateCreateInfo& setFlags( PipelineDynamicStateCreateFlags flags_ ) { flags = flags_; return *this; } PipelineDynamicStateCreateInfo& setDynamicStateCount( uint32_t dynamicStateCount_ ) { dynamicStateCount = dynamicStateCount_; return *this; } PipelineDynamicStateCreateInfo& setPDynamicStates( const DynamicState* pDynamicStates_ ) { pDynamicStates = pDynamicStates_; return *this; } operator const VkPipelineDynamicStateCreateInfo&() const { return *reinterpret_cast(this); } bool operator==( PipelineDynamicStateCreateInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( dynamicStateCount == rhs.dynamicStateCount ) && ( pDynamicStates == rhs.pDynamicStates ); } bool operator!=( PipelineDynamicStateCreateInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; PipelineDynamicStateCreateFlags flags; uint32_t dynamicStateCount; const DynamicState* pDynamicStates; }; static_assert( sizeof( PipelineDynamicStateCreateInfo ) == sizeof( VkPipelineDynamicStateCreateInfo ), "struct and wrapper have different size!" ); enum class DescriptorUpdateTemplateTypeKHR { eDescriptorSet = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR, ePushDescriptors = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR }; struct DescriptorUpdateTemplateCreateInfoKHR { DescriptorUpdateTemplateCreateInfoKHR( DescriptorUpdateTemplateCreateFlagsKHR flags_ = DescriptorUpdateTemplateCreateFlagsKHR(), uint32_t descriptorUpdateEntryCount_ = 0, const DescriptorUpdateTemplateEntryKHR* pDescriptorUpdateEntries_ = nullptr, DescriptorUpdateTemplateTypeKHR templateType_ = DescriptorUpdateTemplateTypeKHR::eDescriptorSet, DescriptorSetLayout descriptorSetLayout_ = DescriptorSetLayout(), PipelineBindPoint pipelineBindPoint_ = PipelineBindPoint::eGraphics, PipelineLayout pipelineLayout_ = PipelineLayout(), uint32_t set_ = 0 ) : sType( StructureType::eDescriptorUpdateTemplateCreateInfoKHR ) , pNext( nullptr ) , flags( flags_ ) , descriptorUpdateEntryCount( descriptorUpdateEntryCount_ ) , pDescriptorUpdateEntries( pDescriptorUpdateEntries_ ) , templateType( templateType_ ) , descriptorSetLayout( descriptorSetLayout_ ) , pipelineBindPoint( pipelineBindPoint_ ) , pipelineLayout( pipelineLayout_ ) , set( set_ ) { } DescriptorUpdateTemplateCreateInfoKHR( VkDescriptorUpdateTemplateCreateInfoKHR const & rhs ) { memcpy( this, &rhs, sizeof(DescriptorUpdateTemplateCreateInfoKHR) ); } DescriptorUpdateTemplateCreateInfoKHR& operator=( VkDescriptorUpdateTemplateCreateInfoKHR const & rhs ) { memcpy( this, &rhs, sizeof(DescriptorUpdateTemplateCreateInfoKHR) ); return *this; } DescriptorUpdateTemplateCreateInfoKHR& setPNext( void* pNext_ ) { pNext = pNext_; return *this; } DescriptorUpdateTemplateCreateInfoKHR& setFlags( DescriptorUpdateTemplateCreateFlagsKHR flags_ ) { flags = flags_; return *this; } DescriptorUpdateTemplateCreateInfoKHR& setDescriptorUpdateEntryCount( uint32_t descriptorUpdateEntryCount_ ) { descriptorUpdateEntryCount = descriptorUpdateEntryCount_; return *this; } DescriptorUpdateTemplateCreateInfoKHR& setPDescriptorUpdateEntries( const DescriptorUpdateTemplateEntryKHR* pDescriptorUpdateEntries_ ) { pDescriptorUpdateEntries = pDescriptorUpdateEntries_; return *this; } DescriptorUpdateTemplateCreateInfoKHR& setTemplateType( DescriptorUpdateTemplateTypeKHR templateType_ ) { templateType = templateType_; return *this; } DescriptorUpdateTemplateCreateInfoKHR& setDescriptorSetLayout( DescriptorSetLayout descriptorSetLayout_ ) { descriptorSetLayout = descriptorSetLayout_; return *this; } DescriptorUpdateTemplateCreateInfoKHR& setPipelineBindPoint( PipelineBindPoint pipelineBindPoint_ ) { pipelineBindPoint = pipelineBindPoint_; return *this; } DescriptorUpdateTemplateCreateInfoKHR& setPipelineLayout( PipelineLayout pipelineLayout_ ) { pipelineLayout = pipelineLayout_; return *this; } DescriptorUpdateTemplateCreateInfoKHR& setSet( uint32_t set_ ) { set = set_; return *this; } operator const VkDescriptorUpdateTemplateCreateInfoKHR&() const { return *reinterpret_cast(this); } bool operator==( DescriptorUpdateTemplateCreateInfoKHR const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( descriptorUpdateEntryCount == rhs.descriptorUpdateEntryCount ) && ( pDescriptorUpdateEntries == rhs.pDescriptorUpdateEntries ) && ( templateType == rhs.templateType ) && ( descriptorSetLayout == rhs.descriptorSetLayout ) && ( pipelineBindPoint == rhs.pipelineBindPoint ) && ( pipelineLayout == rhs.pipelineLayout ) && ( set == rhs.set ); } bool operator!=( DescriptorUpdateTemplateCreateInfoKHR const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: void* pNext; DescriptorUpdateTemplateCreateFlagsKHR flags; uint32_t descriptorUpdateEntryCount; const DescriptorUpdateTemplateEntryKHR* pDescriptorUpdateEntries; DescriptorUpdateTemplateTypeKHR templateType; DescriptorSetLayout descriptorSetLayout; PipelineBindPoint pipelineBindPoint; PipelineLayout pipelineLayout; uint32_t set; }; static_assert( sizeof( DescriptorUpdateTemplateCreateInfoKHR ) == sizeof( VkDescriptorUpdateTemplateCreateInfoKHR ), "struct and wrapper have different size!" ); enum class ObjectType { eUnknown = VK_OBJECT_TYPE_UNKNOWN, eInstance = VK_OBJECT_TYPE_INSTANCE, ePhysicalDevice = VK_OBJECT_TYPE_PHYSICAL_DEVICE, eDevice = VK_OBJECT_TYPE_DEVICE, eQueue = VK_OBJECT_TYPE_QUEUE, eSemaphore = VK_OBJECT_TYPE_SEMAPHORE, eCommandBuffer = VK_OBJECT_TYPE_COMMAND_BUFFER, eFence = VK_OBJECT_TYPE_FENCE, eDeviceMemory = VK_OBJECT_TYPE_DEVICE_MEMORY, eBuffer = VK_OBJECT_TYPE_BUFFER, eImage = VK_OBJECT_TYPE_IMAGE, eEvent = VK_OBJECT_TYPE_EVENT, eQueryPool = VK_OBJECT_TYPE_QUERY_POOL, eBufferView = VK_OBJECT_TYPE_BUFFER_VIEW, eImageView = VK_OBJECT_TYPE_IMAGE_VIEW, eShaderModule = VK_OBJECT_TYPE_SHADER_MODULE, ePipelineCache = VK_OBJECT_TYPE_PIPELINE_CACHE, ePipelineLayout = VK_OBJECT_TYPE_PIPELINE_LAYOUT, eRenderPass = VK_OBJECT_TYPE_RENDER_PASS, ePipeline = VK_OBJECT_TYPE_PIPELINE, eDescriptorSetLayout = VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, eSampler = VK_OBJECT_TYPE_SAMPLER, eDescriptorPool = VK_OBJECT_TYPE_DESCRIPTOR_POOL, eDescriptorSet = VK_OBJECT_TYPE_DESCRIPTOR_SET, eFramebuffer = VK_OBJECT_TYPE_FRAMEBUFFER, eCommandPool = VK_OBJECT_TYPE_COMMAND_POOL, eSurfaceKHR = VK_OBJECT_TYPE_SURFACE_KHR, eSwapchainKHR = VK_OBJECT_TYPE_SWAPCHAIN_KHR, eDisplayKHR = VK_OBJECT_TYPE_DISPLAY_KHR, eDisplayModeKHR = VK_OBJECT_TYPE_DISPLAY_MODE_KHR, eDebugReportCallbackEXT = VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT, eDescriptorUpdateTemplateKHR = VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_KHR, eObjectTableNVX = VK_OBJECT_TYPE_OBJECT_TABLE_NVX, eIndirectCommandsLayoutNVX = VK_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX }; enum class QueueFlagBits { eGraphics = VK_QUEUE_GRAPHICS_BIT, eCompute = VK_QUEUE_COMPUTE_BIT, eTransfer = VK_QUEUE_TRANSFER_BIT, eSparseBinding = VK_QUEUE_SPARSE_BINDING_BIT }; using QueueFlags = Flags; VULKAN_HPP_INLINE QueueFlags operator|( QueueFlagBits bit0, QueueFlagBits bit1 ) { return QueueFlags( bit0 ) | bit1; } VULKAN_HPP_INLINE QueueFlags operator~( QueueFlagBits bits ) { return ~( QueueFlags( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(QueueFlagBits::eGraphics) | VkFlags(QueueFlagBits::eCompute) | VkFlags(QueueFlagBits::eTransfer) | VkFlags(QueueFlagBits::eSparseBinding) }; }; struct QueueFamilyProperties { operator const VkQueueFamilyProperties&() const { return *reinterpret_cast(this); } bool operator==( QueueFamilyProperties const& rhs ) const { return ( queueFlags == rhs.queueFlags ) && ( queueCount == rhs.queueCount ) && ( timestampValidBits == rhs.timestampValidBits ) && ( minImageTransferGranularity == rhs.minImageTransferGranularity ); } bool operator!=( QueueFamilyProperties const& rhs ) const { return !operator==( rhs ); } QueueFlags queueFlags; uint32_t queueCount; uint32_t timestampValidBits; Extent3D minImageTransferGranularity; }; static_assert( sizeof( QueueFamilyProperties ) == sizeof( VkQueueFamilyProperties ), "struct and wrapper have different size!" ); struct QueueFamilyProperties2KHR { operator const VkQueueFamilyProperties2KHR&() const { return *reinterpret_cast(this); } bool operator==( QueueFamilyProperties2KHR const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( queueFamilyProperties == rhs.queueFamilyProperties ); } bool operator!=( QueueFamilyProperties2KHR const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: void* pNext; QueueFamilyProperties queueFamilyProperties; }; static_assert( sizeof( QueueFamilyProperties2KHR ) == sizeof( VkQueueFamilyProperties2KHR ), "struct and wrapper have different size!" ); enum class MemoryPropertyFlagBits { eDeviceLocal = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, eHostVisible = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, eHostCoherent = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, eHostCached = VK_MEMORY_PROPERTY_HOST_CACHED_BIT, eLazilyAllocated = VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT }; using MemoryPropertyFlags = Flags; VULKAN_HPP_INLINE MemoryPropertyFlags operator|( MemoryPropertyFlagBits bit0, MemoryPropertyFlagBits bit1 ) { return MemoryPropertyFlags( bit0 ) | bit1; } VULKAN_HPP_INLINE MemoryPropertyFlags operator~( MemoryPropertyFlagBits bits ) { return ~( MemoryPropertyFlags( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(MemoryPropertyFlagBits::eDeviceLocal) | VkFlags(MemoryPropertyFlagBits::eHostVisible) | VkFlags(MemoryPropertyFlagBits::eHostCoherent) | VkFlags(MemoryPropertyFlagBits::eHostCached) | VkFlags(MemoryPropertyFlagBits::eLazilyAllocated) }; }; struct MemoryType { operator const VkMemoryType&() const { return *reinterpret_cast(this); } bool operator==( MemoryType const& rhs ) const { return ( propertyFlags == rhs.propertyFlags ) && ( heapIndex == rhs.heapIndex ); } bool operator!=( MemoryType const& rhs ) const { return !operator==( rhs ); } MemoryPropertyFlags propertyFlags; uint32_t heapIndex; }; static_assert( sizeof( MemoryType ) == sizeof( VkMemoryType ), "struct and wrapper have different size!" ); enum class MemoryHeapFlagBits { eDeviceLocal = VK_MEMORY_HEAP_DEVICE_LOCAL_BIT, eMultiInstanceKHX = VK_MEMORY_HEAP_MULTI_INSTANCE_BIT_KHX }; using MemoryHeapFlags = Flags; VULKAN_HPP_INLINE MemoryHeapFlags operator|( MemoryHeapFlagBits bit0, MemoryHeapFlagBits bit1 ) { return MemoryHeapFlags( bit0 ) | bit1; } VULKAN_HPP_INLINE MemoryHeapFlags operator~( MemoryHeapFlagBits bits ) { return ~( MemoryHeapFlags( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(MemoryHeapFlagBits::eDeviceLocal) | VkFlags(MemoryHeapFlagBits::eMultiInstanceKHX) }; }; struct MemoryHeap { operator const VkMemoryHeap&() const { return *reinterpret_cast(this); } bool operator==( MemoryHeap const& rhs ) const { return ( size == rhs.size ) && ( flags == rhs.flags ); } bool operator!=( MemoryHeap const& rhs ) const { return !operator==( rhs ); } DeviceSize size; MemoryHeapFlags flags; }; static_assert( sizeof( MemoryHeap ) == sizeof( VkMemoryHeap ), "struct and wrapper have different size!" ); struct PhysicalDeviceMemoryProperties { operator const VkPhysicalDeviceMemoryProperties&() const { return *reinterpret_cast(this); } bool operator==( PhysicalDeviceMemoryProperties const& rhs ) const { return ( memoryTypeCount == rhs.memoryTypeCount ) && ( memcmp( memoryTypes, rhs.memoryTypes, VK_MAX_MEMORY_TYPES * sizeof( MemoryType ) ) == 0 ) && ( memoryHeapCount == rhs.memoryHeapCount ) && ( memcmp( memoryHeaps, rhs.memoryHeaps, VK_MAX_MEMORY_HEAPS * sizeof( MemoryHeap ) ) == 0 ); } bool operator!=( PhysicalDeviceMemoryProperties const& rhs ) const { return !operator==( rhs ); } uint32_t memoryTypeCount; MemoryType memoryTypes[VK_MAX_MEMORY_TYPES]; uint32_t memoryHeapCount; MemoryHeap memoryHeaps[VK_MAX_MEMORY_HEAPS]; }; static_assert( sizeof( PhysicalDeviceMemoryProperties ) == sizeof( VkPhysicalDeviceMemoryProperties ), "struct and wrapper have different size!" ); struct PhysicalDeviceMemoryProperties2KHR { operator const VkPhysicalDeviceMemoryProperties2KHR&() const { return *reinterpret_cast(this); } bool operator==( PhysicalDeviceMemoryProperties2KHR const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( memoryProperties == rhs.memoryProperties ); } bool operator!=( PhysicalDeviceMemoryProperties2KHR const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: void* pNext; PhysicalDeviceMemoryProperties memoryProperties; }; static_assert( sizeof( PhysicalDeviceMemoryProperties2KHR ) == sizeof( VkPhysicalDeviceMemoryProperties2KHR ), "struct and wrapper have different size!" ); enum class AccessFlagBits { eIndirectCommandRead = VK_ACCESS_INDIRECT_COMMAND_READ_BIT, eIndexRead = VK_ACCESS_INDEX_READ_BIT, eVertexAttributeRead = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT, eUniformRead = VK_ACCESS_UNIFORM_READ_BIT, eInputAttachmentRead = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT, eShaderRead = VK_ACCESS_SHADER_READ_BIT, eShaderWrite = VK_ACCESS_SHADER_WRITE_BIT, eColorAttachmentRead = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT, eColorAttachmentWrite = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, eDepthStencilAttachmentRead = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT, eDepthStencilAttachmentWrite = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, eTransferRead = VK_ACCESS_TRANSFER_READ_BIT, eTransferWrite = VK_ACCESS_TRANSFER_WRITE_BIT, eHostRead = VK_ACCESS_HOST_READ_BIT, eHostWrite = VK_ACCESS_HOST_WRITE_BIT, eMemoryRead = VK_ACCESS_MEMORY_READ_BIT, eMemoryWrite = VK_ACCESS_MEMORY_WRITE_BIT, eCommandProcessReadNVX = VK_ACCESS_COMMAND_PROCESS_READ_BIT_NVX, eCommandProcessWriteNVX = VK_ACCESS_COMMAND_PROCESS_WRITE_BIT_NVX }; using AccessFlags = Flags; VULKAN_HPP_INLINE AccessFlags operator|( AccessFlagBits bit0, AccessFlagBits bit1 ) { return AccessFlags( bit0 ) | bit1; } VULKAN_HPP_INLINE AccessFlags operator~( AccessFlagBits bits ) { return ~( AccessFlags( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(AccessFlagBits::eIndirectCommandRead) | VkFlags(AccessFlagBits::eIndexRead) | VkFlags(AccessFlagBits::eVertexAttributeRead) | VkFlags(AccessFlagBits::eUniformRead) | VkFlags(AccessFlagBits::eInputAttachmentRead) | VkFlags(AccessFlagBits::eShaderRead) | VkFlags(AccessFlagBits::eShaderWrite) | VkFlags(AccessFlagBits::eColorAttachmentRead) | VkFlags(AccessFlagBits::eColorAttachmentWrite) | VkFlags(AccessFlagBits::eDepthStencilAttachmentRead) | VkFlags(AccessFlagBits::eDepthStencilAttachmentWrite) | VkFlags(AccessFlagBits::eTransferRead) | VkFlags(AccessFlagBits::eTransferWrite) | VkFlags(AccessFlagBits::eHostRead) | VkFlags(AccessFlagBits::eHostWrite) | VkFlags(AccessFlagBits::eMemoryRead) | VkFlags(AccessFlagBits::eMemoryWrite) | VkFlags(AccessFlagBits::eCommandProcessReadNVX) | VkFlags(AccessFlagBits::eCommandProcessWriteNVX) }; }; struct MemoryBarrier { MemoryBarrier( AccessFlags srcAccessMask_ = AccessFlags(), AccessFlags dstAccessMask_ = AccessFlags() ) : sType( StructureType::eMemoryBarrier ) , pNext( nullptr ) , srcAccessMask( srcAccessMask_ ) , dstAccessMask( dstAccessMask_ ) { } MemoryBarrier( VkMemoryBarrier const & rhs ) { memcpy( this, &rhs, sizeof(MemoryBarrier) ); } MemoryBarrier& operator=( VkMemoryBarrier const & rhs ) { memcpy( this, &rhs, sizeof(MemoryBarrier) ); return *this; } MemoryBarrier& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } MemoryBarrier& setSrcAccessMask( AccessFlags srcAccessMask_ ) { srcAccessMask = srcAccessMask_; return *this; } MemoryBarrier& setDstAccessMask( AccessFlags dstAccessMask_ ) { dstAccessMask = dstAccessMask_; return *this; } operator const VkMemoryBarrier&() const { return *reinterpret_cast(this); } bool operator==( MemoryBarrier const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( srcAccessMask == rhs.srcAccessMask ) && ( dstAccessMask == rhs.dstAccessMask ); } bool operator!=( MemoryBarrier const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; AccessFlags srcAccessMask; AccessFlags dstAccessMask; }; static_assert( sizeof( MemoryBarrier ) == sizeof( VkMemoryBarrier ), "struct and wrapper have different size!" ); struct BufferMemoryBarrier { BufferMemoryBarrier( AccessFlags srcAccessMask_ = AccessFlags(), AccessFlags dstAccessMask_ = AccessFlags(), uint32_t srcQueueFamilyIndex_ = 0, uint32_t dstQueueFamilyIndex_ = 0, Buffer buffer_ = Buffer(), DeviceSize offset_ = 0, DeviceSize size_ = 0 ) : sType( StructureType::eBufferMemoryBarrier ) , pNext( nullptr ) , srcAccessMask( srcAccessMask_ ) , dstAccessMask( dstAccessMask_ ) , srcQueueFamilyIndex( srcQueueFamilyIndex_ ) , dstQueueFamilyIndex( dstQueueFamilyIndex_ ) , buffer( buffer_ ) , offset( offset_ ) , size( size_ ) { } BufferMemoryBarrier( VkBufferMemoryBarrier const & rhs ) { memcpy( this, &rhs, sizeof(BufferMemoryBarrier) ); } BufferMemoryBarrier& operator=( VkBufferMemoryBarrier const & rhs ) { memcpy( this, &rhs, sizeof(BufferMemoryBarrier) ); return *this; } BufferMemoryBarrier& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } BufferMemoryBarrier& setSrcAccessMask( AccessFlags srcAccessMask_ ) { srcAccessMask = srcAccessMask_; return *this; } BufferMemoryBarrier& setDstAccessMask( AccessFlags dstAccessMask_ ) { dstAccessMask = dstAccessMask_; return *this; } BufferMemoryBarrier& setSrcQueueFamilyIndex( uint32_t srcQueueFamilyIndex_ ) { srcQueueFamilyIndex = srcQueueFamilyIndex_; return *this; } BufferMemoryBarrier& setDstQueueFamilyIndex( uint32_t dstQueueFamilyIndex_ ) { dstQueueFamilyIndex = dstQueueFamilyIndex_; return *this; } BufferMemoryBarrier& setBuffer( Buffer buffer_ ) { buffer = buffer_; return *this; } BufferMemoryBarrier& setOffset( DeviceSize offset_ ) { offset = offset_; return *this; } BufferMemoryBarrier& setSize( DeviceSize size_ ) { size = size_; return *this; } operator const VkBufferMemoryBarrier&() const { return *reinterpret_cast(this); } bool operator==( BufferMemoryBarrier const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( srcAccessMask == rhs.srcAccessMask ) && ( dstAccessMask == rhs.dstAccessMask ) && ( srcQueueFamilyIndex == rhs.srcQueueFamilyIndex ) && ( dstQueueFamilyIndex == rhs.dstQueueFamilyIndex ) && ( buffer == rhs.buffer ) && ( offset == rhs.offset ) && ( size == rhs.size ); } bool operator!=( BufferMemoryBarrier const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; AccessFlags srcAccessMask; AccessFlags dstAccessMask; uint32_t srcQueueFamilyIndex; uint32_t dstQueueFamilyIndex; Buffer buffer; DeviceSize offset; DeviceSize size; }; static_assert( sizeof( BufferMemoryBarrier ) == sizeof( VkBufferMemoryBarrier ), "struct and wrapper have different size!" ); enum class BufferUsageFlagBits { eTransferSrc = VK_BUFFER_USAGE_TRANSFER_SRC_BIT, eTransferDst = VK_BUFFER_USAGE_TRANSFER_DST_BIT, eUniformTexelBuffer = VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT, eStorageTexelBuffer = VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT, eUniformBuffer = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, eStorageBuffer = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, eIndexBuffer = VK_BUFFER_USAGE_INDEX_BUFFER_BIT, eVertexBuffer = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, eIndirectBuffer = VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT }; using BufferUsageFlags = Flags; VULKAN_HPP_INLINE BufferUsageFlags operator|( BufferUsageFlagBits bit0, BufferUsageFlagBits bit1 ) { return BufferUsageFlags( bit0 ) | bit1; } VULKAN_HPP_INLINE BufferUsageFlags operator~( BufferUsageFlagBits bits ) { return ~( BufferUsageFlags( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(BufferUsageFlagBits::eTransferSrc) | VkFlags(BufferUsageFlagBits::eTransferDst) | VkFlags(BufferUsageFlagBits::eUniformTexelBuffer) | VkFlags(BufferUsageFlagBits::eStorageTexelBuffer) | VkFlags(BufferUsageFlagBits::eUniformBuffer) | VkFlags(BufferUsageFlagBits::eStorageBuffer) | VkFlags(BufferUsageFlagBits::eIndexBuffer) | VkFlags(BufferUsageFlagBits::eVertexBuffer) | VkFlags(BufferUsageFlagBits::eIndirectBuffer) }; }; enum class BufferCreateFlagBits { eSparseBinding = VK_BUFFER_CREATE_SPARSE_BINDING_BIT, eSparseResidency = VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT, eSparseAliased = VK_BUFFER_CREATE_SPARSE_ALIASED_BIT }; using BufferCreateFlags = Flags; VULKAN_HPP_INLINE BufferCreateFlags operator|( BufferCreateFlagBits bit0, BufferCreateFlagBits bit1 ) { return BufferCreateFlags( bit0 ) | bit1; } VULKAN_HPP_INLINE BufferCreateFlags operator~( BufferCreateFlagBits bits ) { return ~( BufferCreateFlags( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(BufferCreateFlagBits::eSparseBinding) | VkFlags(BufferCreateFlagBits::eSparseResidency) | VkFlags(BufferCreateFlagBits::eSparseAliased) }; }; struct BufferCreateInfo { BufferCreateInfo( BufferCreateFlags flags_ = BufferCreateFlags(), DeviceSize size_ = 0, BufferUsageFlags usage_ = BufferUsageFlags(), SharingMode sharingMode_ = SharingMode::eExclusive, uint32_t queueFamilyIndexCount_ = 0, const uint32_t* pQueueFamilyIndices_ = nullptr ) : sType( StructureType::eBufferCreateInfo ) , pNext( nullptr ) , flags( flags_ ) , size( size_ ) , usage( usage_ ) , sharingMode( sharingMode_ ) , queueFamilyIndexCount( queueFamilyIndexCount_ ) , pQueueFamilyIndices( pQueueFamilyIndices_ ) { } BufferCreateInfo( VkBufferCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(BufferCreateInfo) ); } BufferCreateInfo& operator=( VkBufferCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(BufferCreateInfo) ); return *this; } BufferCreateInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } BufferCreateInfo& setFlags( BufferCreateFlags flags_ ) { flags = flags_; return *this; } BufferCreateInfo& setSize( DeviceSize size_ ) { size = size_; return *this; } BufferCreateInfo& setUsage( BufferUsageFlags usage_ ) { usage = usage_; return *this; } BufferCreateInfo& setSharingMode( SharingMode sharingMode_ ) { sharingMode = sharingMode_; return *this; } BufferCreateInfo& setQueueFamilyIndexCount( uint32_t queueFamilyIndexCount_ ) { queueFamilyIndexCount = queueFamilyIndexCount_; return *this; } BufferCreateInfo& setPQueueFamilyIndices( const uint32_t* pQueueFamilyIndices_ ) { pQueueFamilyIndices = pQueueFamilyIndices_; return *this; } operator const VkBufferCreateInfo&() const { return *reinterpret_cast(this); } bool operator==( BufferCreateInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( size == rhs.size ) && ( usage == rhs.usage ) && ( sharingMode == rhs.sharingMode ) && ( queueFamilyIndexCount == rhs.queueFamilyIndexCount ) && ( pQueueFamilyIndices == rhs.pQueueFamilyIndices ); } bool operator!=( BufferCreateInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; BufferCreateFlags flags; DeviceSize size; BufferUsageFlags usage; SharingMode sharingMode; uint32_t queueFamilyIndexCount; const uint32_t* pQueueFamilyIndices; }; static_assert( sizeof( BufferCreateInfo ) == sizeof( VkBufferCreateInfo ), "struct and wrapper have different size!" ); enum class ShaderStageFlagBits { eVertex = VK_SHADER_STAGE_VERTEX_BIT, eTessellationControl = VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, eTessellationEvaluation = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, eGeometry = VK_SHADER_STAGE_GEOMETRY_BIT, eFragment = VK_SHADER_STAGE_FRAGMENT_BIT, eCompute = VK_SHADER_STAGE_COMPUTE_BIT, eAllGraphics = VK_SHADER_STAGE_ALL_GRAPHICS, eAll = VK_SHADER_STAGE_ALL }; using ShaderStageFlags = Flags; VULKAN_HPP_INLINE ShaderStageFlags operator|( ShaderStageFlagBits bit0, ShaderStageFlagBits bit1 ) { return ShaderStageFlags( bit0 ) | bit1; } VULKAN_HPP_INLINE ShaderStageFlags operator~( ShaderStageFlagBits bits ) { return ~( ShaderStageFlags( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(ShaderStageFlagBits::eVertex) | VkFlags(ShaderStageFlagBits::eTessellationControl) | VkFlags(ShaderStageFlagBits::eTessellationEvaluation) | VkFlags(ShaderStageFlagBits::eGeometry) | VkFlags(ShaderStageFlagBits::eFragment) | VkFlags(ShaderStageFlagBits::eCompute) | VkFlags(ShaderStageFlagBits::eAllGraphics) | VkFlags(ShaderStageFlagBits::eAll) }; }; struct DescriptorSetLayoutBinding { DescriptorSetLayoutBinding( uint32_t binding_ = 0, DescriptorType descriptorType_ = DescriptorType::eSampler, uint32_t descriptorCount_ = 0, ShaderStageFlags stageFlags_ = ShaderStageFlags(), const Sampler* pImmutableSamplers_ = nullptr ) : binding( binding_ ) , descriptorType( descriptorType_ ) , descriptorCount( descriptorCount_ ) , stageFlags( stageFlags_ ) , pImmutableSamplers( pImmutableSamplers_ ) { } DescriptorSetLayoutBinding( VkDescriptorSetLayoutBinding const & rhs ) { memcpy( this, &rhs, sizeof(DescriptorSetLayoutBinding) ); } DescriptorSetLayoutBinding& operator=( VkDescriptorSetLayoutBinding const & rhs ) { memcpy( this, &rhs, sizeof(DescriptorSetLayoutBinding) ); return *this; } DescriptorSetLayoutBinding& setBinding( uint32_t binding_ ) { binding = binding_; return *this; } DescriptorSetLayoutBinding& setDescriptorType( DescriptorType descriptorType_ ) { descriptorType = descriptorType_; return *this; } DescriptorSetLayoutBinding& setDescriptorCount( uint32_t descriptorCount_ ) { descriptorCount = descriptorCount_; return *this; } DescriptorSetLayoutBinding& setStageFlags( ShaderStageFlags stageFlags_ ) { stageFlags = stageFlags_; return *this; } DescriptorSetLayoutBinding& setPImmutableSamplers( const Sampler* pImmutableSamplers_ ) { pImmutableSamplers = pImmutableSamplers_; return *this; } operator const VkDescriptorSetLayoutBinding&() const { return *reinterpret_cast(this); } bool operator==( DescriptorSetLayoutBinding const& rhs ) const { return ( binding == rhs.binding ) && ( descriptorType == rhs.descriptorType ) && ( descriptorCount == rhs.descriptorCount ) && ( stageFlags == rhs.stageFlags ) && ( pImmutableSamplers == rhs.pImmutableSamplers ); } bool operator!=( DescriptorSetLayoutBinding const& rhs ) const { return !operator==( rhs ); } uint32_t binding; DescriptorType descriptorType; uint32_t descriptorCount; ShaderStageFlags stageFlags; const Sampler* pImmutableSamplers; }; static_assert( sizeof( DescriptorSetLayoutBinding ) == sizeof( VkDescriptorSetLayoutBinding ), "struct and wrapper have different size!" ); struct PipelineShaderStageCreateInfo { PipelineShaderStageCreateInfo( PipelineShaderStageCreateFlags flags_ = PipelineShaderStageCreateFlags(), ShaderStageFlagBits stage_ = ShaderStageFlagBits::eVertex, ShaderModule module_ = ShaderModule(), const char* pName_ = nullptr, const SpecializationInfo* pSpecializationInfo_ = nullptr ) : sType( StructureType::ePipelineShaderStageCreateInfo ) , pNext( nullptr ) , flags( flags_ ) , stage( stage_ ) , module( module_ ) , pName( pName_ ) , pSpecializationInfo( pSpecializationInfo_ ) { } PipelineShaderStageCreateInfo( VkPipelineShaderStageCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(PipelineShaderStageCreateInfo) ); } PipelineShaderStageCreateInfo& operator=( VkPipelineShaderStageCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(PipelineShaderStageCreateInfo) ); return *this; } PipelineShaderStageCreateInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } PipelineShaderStageCreateInfo& setFlags( PipelineShaderStageCreateFlags flags_ ) { flags = flags_; return *this; } PipelineShaderStageCreateInfo& setStage( ShaderStageFlagBits stage_ ) { stage = stage_; return *this; } PipelineShaderStageCreateInfo& setModule( ShaderModule module_ ) { module = module_; return *this; } PipelineShaderStageCreateInfo& setPName( const char* pName_ ) { pName = pName_; return *this; } PipelineShaderStageCreateInfo& setPSpecializationInfo( const SpecializationInfo* pSpecializationInfo_ ) { pSpecializationInfo = pSpecializationInfo_; return *this; } operator const VkPipelineShaderStageCreateInfo&() const { return *reinterpret_cast(this); } bool operator==( PipelineShaderStageCreateInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( stage == rhs.stage ) && ( module == rhs.module ) && ( pName == rhs.pName ) && ( pSpecializationInfo == rhs.pSpecializationInfo ); } bool operator!=( PipelineShaderStageCreateInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; PipelineShaderStageCreateFlags flags; ShaderStageFlagBits stage; ShaderModule module; const char* pName; const SpecializationInfo* pSpecializationInfo; }; static_assert( sizeof( PipelineShaderStageCreateInfo ) == sizeof( VkPipelineShaderStageCreateInfo ), "struct and wrapper have different size!" ); struct PushConstantRange { PushConstantRange( ShaderStageFlags stageFlags_ = ShaderStageFlags(), uint32_t offset_ = 0, uint32_t size_ = 0 ) : stageFlags( stageFlags_ ) , offset( offset_ ) , size( size_ ) { } PushConstantRange( VkPushConstantRange const & rhs ) { memcpy( this, &rhs, sizeof(PushConstantRange) ); } PushConstantRange& operator=( VkPushConstantRange const & rhs ) { memcpy( this, &rhs, sizeof(PushConstantRange) ); return *this; } PushConstantRange& setStageFlags( ShaderStageFlags stageFlags_ ) { stageFlags = stageFlags_; return *this; } PushConstantRange& setOffset( uint32_t offset_ ) { offset = offset_; return *this; } PushConstantRange& setSize( uint32_t size_ ) { size = size_; return *this; } operator const VkPushConstantRange&() const { return *reinterpret_cast(this); } bool operator==( PushConstantRange const& rhs ) const { return ( stageFlags == rhs.stageFlags ) && ( offset == rhs.offset ) && ( size == rhs.size ); } bool operator!=( PushConstantRange const& rhs ) const { return !operator==( rhs ); } ShaderStageFlags stageFlags; uint32_t offset; uint32_t size; }; static_assert( sizeof( PushConstantRange ) == sizeof( VkPushConstantRange ), "struct and wrapper have different size!" ); struct PipelineLayoutCreateInfo { PipelineLayoutCreateInfo( PipelineLayoutCreateFlags flags_ = PipelineLayoutCreateFlags(), uint32_t setLayoutCount_ = 0, const DescriptorSetLayout* pSetLayouts_ = nullptr, uint32_t pushConstantRangeCount_ = 0, const PushConstantRange* pPushConstantRanges_ = nullptr ) : sType( StructureType::ePipelineLayoutCreateInfo ) , pNext( nullptr ) , flags( flags_ ) , setLayoutCount( setLayoutCount_ ) , pSetLayouts( pSetLayouts_ ) , pushConstantRangeCount( pushConstantRangeCount_ ) , pPushConstantRanges( pPushConstantRanges_ ) { } PipelineLayoutCreateInfo( VkPipelineLayoutCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(PipelineLayoutCreateInfo) ); } PipelineLayoutCreateInfo& operator=( VkPipelineLayoutCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(PipelineLayoutCreateInfo) ); return *this; } PipelineLayoutCreateInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } PipelineLayoutCreateInfo& setFlags( PipelineLayoutCreateFlags flags_ ) { flags = flags_; return *this; } PipelineLayoutCreateInfo& setSetLayoutCount( uint32_t setLayoutCount_ ) { setLayoutCount = setLayoutCount_; return *this; } PipelineLayoutCreateInfo& setPSetLayouts( const DescriptorSetLayout* pSetLayouts_ ) { pSetLayouts = pSetLayouts_; return *this; } PipelineLayoutCreateInfo& setPushConstantRangeCount( uint32_t pushConstantRangeCount_ ) { pushConstantRangeCount = pushConstantRangeCount_; return *this; } PipelineLayoutCreateInfo& setPPushConstantRanges( const PushConstantRange* pPushConstantRanges_ ) { pPushConstantRanges = pPushConstantRanges_; return *this; } operator const VkPipelineLayoutCreateInfo&() const { return *reinterpret_cast(this); } bool operator==( PipelineLayoutCreateInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( setLayoutCount == rhs.setLayoutCount ) && ( pSetLayouts == rhs.pSetLayouts ) && ( pushConstantRangeCount == rhs.pushConstantRangeCount ) && ( pPushConstantRanges == rhs.pPushConstantRanges ); } bool operator!=( PipelineLayoutCreateInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; PipelineLayoutCreateFlags flags; uint32_t setLayoutCount; const DescriptorSetLayout* pSetLayouts; uint32_t pushConstantRangeCount; const PushConstantRange* pPushConstantRanges; }; static_assert( sizeof( PipelineLayoutCreateInfo ) == sizeof( VkPipelineLayoutCreateInfo ), "struct and wrapper have different size!" ); enum class ImageUsageFlagBits { eTransferSrc = VK_IMAGE_USAGE_TRANSFER_SRC_BIT, eTransferDst = VK_IMAGE_USAGE_TRANSFER_DST_BIT, eSampled = VK_IMAGE_USAGE_SAMPLED_BIT, eStorage = VK_IMAGE_USAGE_STORAGE_BIT, eColorAttachment = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, eDepthStencilAttachment = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, eTransientAttachment = VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT, eInputAttachment = VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT }; using ImageUsageFlags = Flags; VULKAN_HPP_INLINE ImageUsageFlags operator|( ImageUsageFlagBits bit0, ImageUsageFlagBits bit1 ) { return ImageUsageFlags( bit0 ) | bit1; } VULKAN_HPP_INLINE ImageUsageFlags operator~( ImageUsageFlagBits bits ) { return ~( ImageUsageFlags( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(ImageUsageFlagBits::eTransferSrc) | VkFlags(ImageUsageFlagBits::eTransferDst) | VkFlags(ImageUsageFlagBits::eSampled) | VkFlags(ImageUsageFlagBits::eStorage) | VkFlags(ImageUsageFlagBits::eColorAttachment) | VkFlags(ImageUsageFlagBits::eDepthStencilAttachment) | VkFlags(ImageUsageFlagBits::eTransientAttachment) | VkFlags(ImageUsageFlagBits::eInputAttachment) }; }; struct SharedPresentSurfaceCapabilitiesKHR { operator const VkSharedPresentSurfaceCapabilitiesKHR&() const { return *reinterpret_cast(this); } bool operator==( SharedPresentSurfaceCapabilitiesKHR const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( sharedPresentSupportedUsageFlags == rhs.sharedPresentSupportedUsageFlags ); } bool operator!=( SharedPresentSurfaceCapabilitiesKHR const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: void* pNext; ImageUsageFlags sharedPresentSupportedUsageFlags; }; static_assert( sizeof( SharedPresentSurfaceCapabilitiesKHR ) == sizeof( VkSharedPresentSurfaceCapabilitiesKHR ), "struct and wrapper have different size!" ); enum class ImageCreateFlagBits { eSparseBinding = VK_IMAGE_CREATE_SPARSE_BINDING_BIT, eSparseResidency = VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT, eSparseAliased = VK_IMAGE_CREATE_SPARSE_ALIASED_BIT, eMutableFormat = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT, eCubeCompatible = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT, eBindSfrKHX = VK_IMAGE_CREATE_BIND_SFR_BIT_KHX, e2DArrayCompatibleKHR = VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT_KHR }; using ImageCreateFlags = Flags; VULKAN_HPP_INLINE ImageCreateFlags operator|( ImageCreateFlagBits bit0, ImageCreateFlagBits bit1 ) { return ImageCreateFlags( bit0 ) | bit1; } VULKAN_HPP_INLINE ImageCreateFlags operator~( ImageCreateFlagBits bits ) { return ~( ImageCreateFlags( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(ImageCreateFlagBits::eSparseBinding) | VkFlags(ImageCreateFlagBits::eSparseResidency) | VkFlags(ImageCreateFlagBits::eSparseAliased) | VkFlags(ImageCreateFlagBits::eMutableFormat) | VkFlags(ImageCreateFlagBits::eCubeCompatible) | VkFlags(ImageCreateFlagBits::eBindSfrKHX) | VkFlags(ImageCreateFlagBits::e2DArrayCompatibleKHR) }; }; struct PhysicalDeviceImageFormatInfo2KHR { PhysicalDeviceImageFormatInfo2KHR( Format format_ = Format::eUndefined, ImageType type_ = ImageType::e1D, ImageTiling tiling_ = ImageTiling::eOptimal, ImageUsageFlags usage_ = ImageUsageFlags(), ImageCreateFlags flags_ = ImageCreateFlags() ) : sType( StructureType::ePhysicalDeviceImageFormatInfo2KHR ) , pNext( nullptr ) , format( format_ ) , type( type_ ) , tiling( tiling_ ) , usage( usage_ ) , flags( flags_ ) { } PhysicalDeviceImageFormatInfo2KHR( VkPhysicalDeviceImageFormatInfo2KHR const & rhs ) { memcpy( this, &rhs, sizeof(PhysicalDeviceImageFormatInfo2KHR) ); } PhysicalDeviceImageFormatInfo2KHR& operator=( VkPhysicalDeviceImageFormatInfo2KHR const & rhs ) { memcpy( this, &rhs, sizeof(PhysicalDeviceImageFormatInfo2KHR) ); return *this; } PhysicalDeviceImageFormatInfo2KHR& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } PhysicalDeviceImageFormatInfo2KHR& setFormat( Format format_ ) { format = format_; return *this; } PhysicalDeviceImageFormatInfo2KHR& setType( ImageType type_ ) { type = type_; return *this; } PhysicalDeviceImageFormatInfo2KHR& setTiling( ImageTiling tiling_ ) { tiling = tiling_; return *this; } PhysicalDeviceImageFormatInfo2KHR& setUsage( ImageUsageFlags usage_ ) { usage = usage_; return *this; } PhysicalDeviceImageFormatInfo2KHR& setFlags( ImageCreateFlags flags_ ) { flags = flags_; return *this; } operator const VkPhysicalDeviceImageFormatInfo2KHR&() const { return *reinterpret_cast(this); } bool operator==( PhysicalDeviceImageFormatInfo2KHR const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( format == rhs.format ) && ( type == rhs.type ) && ( tiling == rhs.tiling ) && ( usage == rhs.usage ) && ( flags == rhs.flags ); } bool operator!=( PhysicalDeviceImageFormatInfo2KHR const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; Format format; ImageType type; ImageTiling tiling; ImageUsageFlags usage; ImageCreateFlags flags; }; static_assert( sizeof( PhysicalDeviceImageFormatInfo2KHR ) == sizeof( VkPhysicalDeviceImageFormatInfo2KHR ), "struct and wrapper have different size!" ); enum class PipelineCreateFlagBits { eDisableOptimization = VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT, eAllowDerivatives = VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT, eDerivative = VK_PIPELINE_CREATE_DERIVATIVE_BIT, eViewIndexFromDeviceIndexKHX = VK_PIPELINE_CREATE_VIEW_INDEX_FROM_DEVICE_INDEX_BIT_KHX, eDispatchBaseKHX = VK_PIPELINE_CREATE_DISPATCH_BASE_KHX }; using PipelineCreateFlags = Flags; VULKAN_HPP_INLINE PipelineCreateFlags operator|( PipelineCreateFlagBits bit0, PipelineCreateFlagBits bit1 ) { return PipelineCreateFlags( bit0 ) | bit1; } VULKAN_HPP_INLINE PipelineCreateFlags operator~( PipelineCreateFlagBits bits ) { return ~( PipelineCreateFlags( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(PipelineCreateFlagBits::eDisableOptimization) | VkFlags(PipelineCreateFlagBits::eAllowDerivatives) | VkFlags(PipelineCreateFlagBits::eDerivative) | VkFlags(PipelineCreateFlagBits::eViewIndexFromDeviceIndexKHX) | VkFlags(PipelineCreateFlagBits::eDispatchBaseKHX) }; }; struct ComputePipelineCreateInfo { ComputePipelineCreateInfo( PipelineCreateFlags flags_ = PipelineCreateFlags(), PipelineShaderStageCreateInfo stage_ = PipelineShaderStageCreateInfo(), PipelineLayout layout_ = PipelineLayout(), Pipeline basePipelineHandle_ = Pipeline(), int32_t basePipelineIndex_ = 0 ) : sType( StructureType::eComputePipelineCreateInfo ) , pNext( nullptr ) , flags( flags_ ) , stage( stage_ ) , layout( layout_ ) , basePipelineHandle( basePipelineHandle_ ) , basePipelineIndex( basePipelineIndex_ ) { } ComputePipelineCreateInfo( VkComputePipelineCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(ComputePipelineCreateInfo) ); } ComputePipelineCreateInfo& operator=( VkComputePipelineCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(ComputePipelineCreateInfo) ); return *this; } ComputePipelineCreateInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } ComputePipelineCreateInfo& setFlags( PipelineCreateFlags flags_ ) { flags = flags_; return *this; } ComputePipelineCreateInfo& setStage( PipelineShaderStageCreateInfo stage_ ) { stage = stage_; return *this; } ComputePipelineCreateInfo& setLayout( PipelineLayout layout_ ) { layout = layout_; return *this; } ComputePipelineCreateInfo& setBasePipelineHandle( Pipeline basePipelineHandle_ ) { basePipelineHandle = basePipelineHandle_; return *this; } ComputePipelineCreateInfo& setBasePipelineIndex( int32_t basePipelineIndex_ ) { basePipelineIndex = basePipelineIndex_; return *this; } operator const VkComputePipelineCreateInfo&() const { return *reinterpret_cast(this); } bool operator==( ComputePipelineCreateInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( stage == rhs.stage ) && ( layout == rhs.layout ) && ( basePipelineHandle == rhs.basePipelineHandle ) && ( basePipelineIndex == rhs.basePipelineIndex ); } bool operator!=( ComputePipelineCreateInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; PipelineCreateFlags flags; PipelineShaderStageCreateInfo stage; PipelineLayout layout; Pipeline basePipelineHandle; int32_t basePipelineIndex; }; static_assert( sizeof( ComputePipelineCreateInfo ) == sizeof( VkComputePipelineCreateInfo ), "struct and wrapper have different size!" ); enum class ColorComponentFlagBits { eR = VK_COLOR_COMPONENT_R_BIT, eG = VK_COLOR_COMPONENT_G_BIT, eB = VK_COLOR_COMPONENT_B_BIT, eA = VK_COLOR_COMPONENT_A_BIT }; using ColorComponentFlags = Flags; VULKAN_HPP_INLINE ColorComponentFlags operator|( ColorComponentFlagBits bit0, ColorComponentFlagBits bit1 ) { return ColorComponentFlags( bit0 ) | bit1; } VULKAN_HPP_INLINE ColorComponentFlags operator~( ColorComponentFlagBits bits ) { return ~( ColorComponentFlags( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(ColorComponentFlagBits::eR) | VkFlags(ColorComponentFlagBits::eG) | VkFlags(ColorComponentFlagBits::eB) | VkFlags(ColorComponentFlagBits::eA) }; }; struct PipelineColorBlendAttachmentState { PipelineColorBlendAttachmentState( Bool32 blendEnable_ = 0, BlendFactor srcColorBlendFactor_ = BlendFactor::eZero, BlendFactor dstColorBlendFactor_ = BlendFactor::eZero, BlendOp colorBlendOp_ = BlendOp::eAdd, BlendFactor srcAlphaBlendFactor_ = BlendFactor::eZero, BlendFactor dstAlphaBlendFactor_ = BlendFactor::eZero, BlendOp alphaBlendOp_ = BlendOp::eAdd, ColorComponentFlags colorWriteMask_ = ColorComponentFlags() ) : blendEnable( blendEnable_ ) , srcColorBlendFactor( srcColorBlendFactor_ ) , dstColorBlendFactor( dstColorBlendFactor_ ) , colorBlendOp( colorBlendOp_ ) , srcAlphaBlendFactor( srcAlphaBlendFactor_ ) , dstAlphaBlendFactor( dstAlphaBlendFactor_ ) , alphaBlendOp( alphaBlendOp_ ) , colorWriteMask( colorWriteMask_ ) { } PipelineColorBlendAttachmentState( VkPipelineColorBlendAttachmentState const & rhs ) { memcpy( this, &rhs, sizeof(PipelineColorBlendAttachmentState) ); } PipelineColorBlendAttachmentState& operator=( VkPipelineColorBlendAttachmentState const & rhs ) { memcpy( this, &rhs, sizeof(PipelineColorBlendAttachmentState) ); return *this; } PipelineColorBlendAttachmentState& setBlendEnable( Bool32 blendEnable_ ) { blendEnable = blendEnable_; return *this; } PipelineColorBlendAttachmentState& setSrcColorBlendFactor( BlendFactor srcColorBlendFactor_ ) { srcColorBlendFactor = srcColorBlendFactor_; return *this; } PipelineColorBlendAttachmentState& setDstColorBlendFactor( BlendFactor dstColorBlendFactor_ ) { dstColorBlendFactor = dstColorBlendFactor_; return *this; } PipelineColorBlendAttachmentState& setColorBlendOp( BlendOp colorBlendOp_ ) { colorBlendOp = colorBlendOp_; return *this; } PipelineColorBlendAttachmentState& setSrcAlphaBlendFactor( BlendFactor srcAlphaBlendFactor_ ) { srcAlphaBlendFactor = srcAlphaBlendFactor_; return *this; } PipelineColorBlendAttachmentState& setDstAlphaBlendFactor( BlendFactor dstAlphaBlendFactor_ ) { dstAlphaBlendFactor = dstAlphaBlendFactor_; return *this; } PipelineColorBlendAttachmentState& setAlphaBlendOp( BlendOp alphaBlendOp_ ) { alphaBlendOp = alphaBlendOp_; return *this; } PipelineColorBlendAttachmentState& setColorWriteMask( ColorComponentFlags colorWriteMask_ ) { colorWriteMask = colorWriteMask_; return *this; } operator const VkPipelineColorBlendAttachmentState&() const { return *reinterpret_cast(this); } bool operator==( PipelineColorBlendAttachmentState const& rhs ) const { return ( blendEnable == rhs.blendEnable ) && ( srcColorBlendFactor == rhs.srcColorBlendFactor ) && ( dstColorBlendFactor == rhs.dstColorBlendFactor ) && ( colorBlendOp == rhs.colorBlendOp ) && ( srcAlphaBlendFactor == rhs.srcAlphaBlendFactor ) && ( dstAlphaBlendFactor == rhs.dstAlphaBlendFactor ) && ( alphaBlendOp == rhs.alphaBlendOp ) && ( colorWriteMask == rhs.colorWriteMask ); } bool operator!=( PipelineColorBlendAttachmentState const& rhs ) const { return !operator==( rhs ); } Bool32 blendEnable; BlendFactor srcColorBlendFactor; BlendFactor dstColorBlendFactor; BlendOp colorBlendOp; BlendFactor srcAlphaBlendFactor; BlendFactor dstAlphaBlendFactor; BlendOp alphaBlendOp; ColorComponentFlags colorWriteMask; }; static_assert( sizeof( PipelineColorBlendAttachmentState ) == sizeof( VkPipelineColorBlendAttachmentState ), "struct and wrapper have different size!" ); struct PipelineColorBlendStateCreateInfo { PipelineColorBlendStateCreateInfo( PipelineColorBlendStateCreateFlags flags_ = PipelineColorBlendStateCreateFlags(), Bool32 logicOpEnable_ = 0, LogicOp logicOp_ = LogicOp::eClear, uint32_t attachmentCount_ = 0, const PipelineColorBlendAttachmentState* pAttachments_ = nullptr, std::array const& blendConstants_ = { { 0, 0, 0, 0 } } ) : sType( StructureType::ePipelineColorBlendStateCreateInfo ) , pNext( nullptr ) , flags( flags_ ) , logicOpEnable( logicOpEnable_ ) , logicOp( logicOp_ ) , attachmentCount( attachmentCount_ ) , pAttachments( pAttachments_ ) { memcpy( &blendConstants, blendConstants_.data(), 4 * sizeof( float ) ); } PipelineColorBlendStateCreateInfo( VkPipelineColorBlendStateCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(PipelineColorBlendStateCreateInfo) ); } PipelineColorBlendStateCreateInfo& operator=( VkPipelineColorBlendStateCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(PipelineColorBlendStateCreateInfo) ); return *this; } PipelineColorBlendStateCreateInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } PipelineColorBlendStateCreateInfo& setFlags( PipelineColorBlendStateCreateFlags flags_ ) { flags = flags_; return *this; } PipelineColorBlendStateCreateInfo& setLogicOpEnable( Bool32 logicOpEnable_ ) { logicOpEnable = logicOpEnable_; return *this; } PipelineColorBlendStateCreateInfo& setLogicOp( LogicOp logicOp_ ) { logicOp = logicOp_; return *this; } PipelineColorBlendStateCreateInfo& setAttachmentCount( uint32_t attachmentCount_ ) { attachmentCount = attachmentCount_; return *this; } PipelineColorBlendStateCreateInfo& setPAttachments( const PipelineColorBlendAttachmentState* pAttachments_ ) { pAttachments = pAttachments_; return *this; } PipelineColorBlendStateCreateInfo& setBlendConstants( std::array blendConstants_ ) { memcpy( &blendConstants, blendConstants_.data(), 4 * sizeof( float ) ); return *this; } operator const VkPipelineColorBlendStateCreateInfo&() const { return *reinterpret_cast(this); } bool operator==( PipelineColorBlendStateCreateInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( logicOpEnable == rhs.logicOpEnable ) && ( logicOp == rhs.logicOp ) && ( attachmentCount == rhs.attachmentCount ) && ( pAttachments == rhs.pAttachments ) && ( memcmp( blendConstants, rhs.blendConstants, 4 * sizeof( float ) ) == 0 ); } bool operator!=( PipelineColorBlendStateCreateInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; PipelineColorBlendStateCreateFlags flags; Bool32 logicOpEnable; LogicOp logicOp; uint32_t attachmentCount; const PipelineColorBlendAttachmentState* pAttachments; float blendConstants[4]; }; static_assert( sizeof( PipelineColorBlendStateCreateInfo ) == sizeof( VkPipelineColorBlendStateCreateInfo ), "struct and wrapper have different size!" ); enum class FenceCreateFlagBits { eSignaled = VK_FENCE_CREATE_SIGNALED_BIT }; using FenceCreateFlags = Flags; VULKAN_HPP_INLINE FenceCreateFlags operator|( FenceCreateFlagBits bit0, FenceCreateFlagBits bit1 ) { return FenceCreateFlags( bit0 ) | bit1; } VULKAN_HPP_INLINE FenceCreateFlags operator~( FenceCreateFlagBits bits ) { return ~( FenceCreateFlags( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(FenceCreateFlagBits::eSignaled) }; }; struct FenceCreateInfo { FenceCreateInfo( FenceCreateFlags flags_ = FenceCreateFlags() ) : sType( StructureType::eFenceCreateInfo ) , pNext( nullptr ) , flags( flags_ ) { } FenceCreateInfo( VkFenceCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(FenceCreateInfo) ); } FenceCreateInfo& operator=( VkFenceCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(FenceCreateInfo) ); return *this; } FenceCreateInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } FenceCreateInfo& setFlags( FenceCreateFlags flags_ ) { flags = flags_; return *this; } operator const VkFenceCreateInfo&() const { return *reinterpret_cast(this); } bool operator==( FenceCreateInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ); } bool operator!=( FenceCreateInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; FenceCreateFlags flags; }; static_assert( sizeof( FenceCreateInfo ) == sizeof( VkFenceCreateInfo ), "struct and wrapper have different size!" ); enum class FormatFeatureFlagBits { eSampledImage = VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT, eStorageImage = VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT, eStorageImageAtomic = VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT, eUniformTexelBuffer = VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT, eStorageTexelBuffer = VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT, eStorageTexelBufferAtomic = VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT, eVertexBuffer = VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT, eColorAttachment = VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT, eColorAttachmentBlend = VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT, eDepthStencilAttachment = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT, eBlitSrc = VK_FORMAT_FEATURE_BLIT_SRC_BIT, eBlitDst = VK_FORMAT_FEATURE_BLIT_DST_BIT, eSampledImageFilterLinear = VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT, eSampledImageFilterCubicIMG = VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG, eTransferSrcKHR = VK_FORMAT_FEATURE_TRANSFER_SRC_BIT_KHR, eTransferDstKHR = VK_FORMAT_FEATURE_TRANSFER_DST_BIT_KHR }; using FormatFeatureFlags = Flags; VULKAN_HPP_INLINE FormatFeatureFlags operator|( FormatFeatureFlagBits bit0, FormatFeatureFlagBits bit1 ) { return FormatFeatureFlags( bit0 ) | bit1; } VULKAN_HPP_INLINE FormatFeatureFlags operator~( FormatFeatureFlagBits bits ) { return ~( FormatFeatureFlags( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(FormatFeatureFlagBits::eSampledImage) | VkFlags(FormatFeatureFlagBits::eStorageImage) | VkFlags(FormatFeatureFlagBits::eStorageImageAtomic) | VkFlags(FormatFeatureFlagBits::eUniformTexelBuffer) | VkFlags(FormatFeatureFlagBits::eStorageTexelBuffer) | VkFlags(FormatFeatureFlagBits::eStorageTexelBufferAtomic) | VkFlags(FormatFeatureFlagBits::eVertexBuffer) | VkFlags(FormatFeatureFlagBits::eColorAttachment) | VkFlags(FormatFeatureFlagBits::eColorAttachmentBlend) | VkFlags(FormatFeatureFlagBits::eDepthStencilAttachment) | VkFlags(FormatFeatureFlagBits::eBlitSrc) | VkFlags(FormatFeatureFlagBits::eBlitDst) | VkFlags(FormatFeatureFlagBits::eSampledImageFilterLinear) | VkFlags(FormatFeatureFlagBits::eSampledImageFilterCubicIMG) | VkFlags(FormatFeatureFlagBits::eTransferSrcKHR) | VkFlags(FormatFeatureFlagBits::eTransferDstKHR) }; }; struct FormatProperties { operator const VkFormatProperties&() const { return *reinterpret_cast(this); } bool operator==( FormatProperties const& rhs ) const { return ( linearTilingFeatures == rhs.linearTilingFeatures ) && ( optimalTilingFeatures == rhs.optimalTilingFeatures ) && ( bufferFeatures == rhs.bufferFeatures ); } bool operator!=( FormatProperties const& rhs ) const { return !operator==( rhs ); } FormatFeatureFlags linearTilingFeatures; FormatFeatureFlags optimalTilingFeatures; FormatFeatureFlags bufferFeatures; }; static_assert( sizeof( FormatProperties ) == sizeof( VkFormatProperties ), "struct and wrapper have different size!" ); struct FormatProperties2KHR { operator const VkFormatProperties2KHR&() const { return *reinterpret_cast(this); } bool operator==( FormatProperties2KHR const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( formatProperties == rhs.formatProperties ); } bool operator!=( FormatProperties2KHR const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: void* pNext; FormatProperties formatProperties; }; static_assert( sizeof( FormatProperties2KHR ) == sizeof( VkFormatProperties2KHR ), "struct and wrapper have different size!" ); enum class QueryControlFlagBits { ePrecise = VK_QUERY_CONTROL_PRECISE_BIT }; using QueryControlFlags = Flags; VULKAN_HPP_INLINE QueryControlFlags operator|( QueryControlFlagBits bit0, QueryControlFlagBits bit1 ) { return QueryControlFlags( bit0 ) | bit1; } VULKAN_HPP_INLINE QueryControlFlags operator~( QueryControlFlagBits bits ) { return ~( QueryControlFlags( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(QueryControlFlagBits::ePrecise) }; }; enum class QueryResultFlagBits { e64 = VK_QUERY_RESULT_64_BIT, eWait = VK_QUERY_RESULT_WAIT_BIT, eWithAvailability = VK_QUERY_RESULT_WITH_AVAILABILITY_BIT, ePartial = VK_QUERY_RESULT_PARTIAL_BIT }; using QueryResultFlags = Flags; VULKAN_HPP_INLINE QueryResultFlags operator|( QueryResultFlagBits bit0, QueryResultFlagBits bit1 ) { return QueryResultFlags( bit0 ) | bit1; } VULKAN_HPP_INLINE QueryResultFlags operator~( QueryResultFlagBits bits ) { return ~( QueryResultFlags( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(QueryResultFlagBits::e64) | VkFlags(QueryResultFlagBits::eWait) | VkFlags(QueryResultFlagBits::eWithAvailability) | VkFlags(QueryResultFlagBits::ePartial) }; }; enum class CommandBufferUsageFlagBits { eOneTimeSubmit = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, eRenderPassContinue = VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT, eSimultaneousUse = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT }; using CommandBufferUsageFlags = Flags; VULKAN_HPP_INLINE CommandBufferUsageFlags operator|( CommandBufferUsageFlagBits bit0, CommandBufferUsageFlagBits bit1 ) { return CommandBufferUsageFlags( bit0 ) | bit1; } VULKAN_HPP_INLINE CommandBufferUsageFlags operator~( CommandBufferUsageFlagBits bits ) { return ~( CommandBufferUsageFlags( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(CommandBufferUsageFlagBits::eOneTimeSubmit) | VkFlags(CommandBufferUsageFlagBits::eRenderPassContinue) | VkFlags(CommandBufferUsageFlagBits::eSimultaneousUse) }; }; enum class QueryPipelineStatisticFlagBits { eInputAssemblyVertices = VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_VERTICES_BIT, eInputAssemblyPrimitives = VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_PRIMITIVES_BIT, eVertexShaderInvocations = VK_QUERY_PIPELINE_STATISTIC_VERTEX_SHADER_INVOCATIONS_BIT, eGeometryShaderInvocations = VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_INVOCATIONS_BIT, eGeometryShaderPrimitives = VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_PRIMITIVES_BIT, eClippingInvocations = VK_QUERY_PIPELINE_STATISTIC_CLIPPING_INVOCATIONS_BIT, eClippingPrimitives = VK_QUERY_PIPELINE_STATISTIC_CLIPPING_PRIMITIVES_BIT, eFragmentShaderInvocations = VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT, eTessellationControlShaderPatches = VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_CONTROL_SHADER_PATCHES_BIT, eTessellationEvaluationShaderInvocations = VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_EVALUATION_SHADER_INVOCATIONS_BIT, eComputeShaderInvocations = VK_QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT }; using QueryPipelineStatisticFlags = Flags; VULKAN_HPP_INLINE QueryPipelineStatisticFlags operator|( QueryPipelineStatisticFlagBits bit0, QueryPipelineStatisticFlagBits bit1 ) { return QueryPipelineStatisticFlags( bit0 ) | bit1; } VULKAN_HPP_INLINE QueryPipelineStatisticFlags operator~( QueryPipelineStatisticFlagBits bits ) { return ~( QueryPipelineStatisticFlags( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(QueryPipelineStatisticFlagBits::eInputAssemblyVertices) | VkFlags(QueryPipelineStatisticFlagBits::eInputAssemblyPrimitives) | VkFlags(QueryPipelineStatisticFlagBits::eVertexShaderInvocations) | VkFlags(QueryPipelineStatisticFlagBits::eGeometryShaderInvocations) | VkFlags(QueryPipelineStatisticFlagBits::eGeometryShaderPrimitives) | VkFlags(QueryPipelineStatisticFlagBits::eClippingInvocations) | VkFlags(QueryPipelineStatisticFlagBits::eClippingPrimitives) | VkFlags(QueryPipelineStatisticFlagBits::eFragmentShaderInvocations) | VkFlags(QueryPipelineStatisticFlagBits::eTessellationControlShaderPatches) | VkFlags(QueryPipelineStatisticFlagBits::eTessellationEvaluationShaderInvocations) | VkFlags(QueryPipelineStatisticFlagBits::eComputeShaderInvocations) }; }; struct CommandBufferInheritanceInfo { CommandBufferInheritanceInfo( RenderPass renderPass_ = RenderPass(), uint32_t subpass_ = 0, Framebuffer framebuffer_ = Framebuffer(), Bool32 occlusionQueryEnable_ = 0, QueryControlFlags queryFlags_ = QueryControlFlags(), QueryPipelineStatisticFlags pipelineStatistics_ = QueryPipelineStatisticFlags() ) : sType( StructureType::eCommandBufferInheritanceInfo ) , pNext( nullptr ) , renderPass( renderPass_ ) , subpass( subpass_ ) , framebuffer( framebuffer_ ) , occlusionQueryEnable( occlusionQueryEnable_ ) , queryFlags( queryFlags_ ) , pipelineStatistics( pipelineStatistics_ ) { } CommandBufferInheritanceInfo( VkCommandBufferInheritanceInfo const & rhs ) { memcpy( this, &rhs, sizeof(CommandBufferInheritanceInfo) ); } CommandBufferInheritanceInfo& operator=( VkCommandBufferInheritanceInfo const & rhs ) { memcpy( this, &rhs, sizeof(CommandBufferInheritanceInfo) ); return *this; } CommandBufferInheritanceInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } CommandBufferInheritanceInfo& setRenderPass( RenderPass renderPass_ ) { renderPass = renderPass_; return *this; } CommandBufferInheritanceInfo& setSubpass( uint32_t subpass_ ) { subpass = subpass_; return *this; } CommandBufferInheritanceInfo& setFramebuffer( Framebuffer framebuffer_ ) { framebuffer = framebuffer_; return *this; } CommandBufferInheritanceInfo& setOcclusionQueryEnable( Bool32 occlusionQueryEnable_ ) { occlusionQueryEnable = occlusionQueryEnable_; return *this; } CommandBufferInheritanceInfo& setQueryFlags( QueryControlFlags queryFlags_ ) { queryFlags = queryFlags_; return *this; } CommandBufferInheritanceInfo& setPipelineStatistics( QueryPipelineStatisticFlags pipelineStatistics_ ) { pipelineStatistics = pipelineStatistics_; return *this; } operator const VkCommandBufferInheritanceInfo&() const { return *reinterpret_cast(this); } bool operator==( CommandBufferInheritanceInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( renderPass == rhs.renderPass ) && ( subpass == rhs.subpass ) && ( framebuffer == rhs.framebuffer ) && ( occlusionQueryEnable == rhs.occlusionQueryEnable ) && ( queryFlags == rhs.queryFlags ) && ( pipelineStatistics == rhs.pipelineStatistics ); } bool operator!=( CommandBufferInheritanceInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; RenderPass renderPass; uint32_t subpass; Framebuffer framebuffer; Bool32 occlusionQueryEnable; QueryControlFlags queryFlags; QueryPipelineStatisticFlags pipelineStatistics; }; static_assert( sizeof( CommandBufferInheritanceInfo ) == sizeof( VkCommandBufferInheritanceInfo ), "struct and wrapper have different size!" ); struct CommandBufferBeginInfo { CommandBufferBeginInfo( CommandBufferUsageFlags flags_ = CommandBufferUsageFlags(), const CommandBufferInheritanceInfo* pInheritanceInfo_ = nullptr ) : sType( StructureType::eCommandBufferBeginInfo ) , pNext( nullptr ) , flags( flags_ ) , pInheritanceInfo( pInheritanceInfo_ ) { } CommandBufferBeginInfo( VkCommandBufferBeginInfo const & rhs ) { memcpy( this, &rhs, sizeof(CommandBufferBeginInfo) ); } CommandBufferBeginInfo& operator=( VkCommandBufferBeginInfo const & rhs ) { memcpy( this, &rhs, sizeof(CommandBufferBeginInfo) ); return *this; } CommandBufferBeginInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } CommandBufferBeginInfo& setFlags( CommandBufferUsageFlags flags_ ) { flags = flags_; return *this; } CommandBufferBeginInfo& setPInheritanceInfo( const CommandBufferInheritanceInfo* pInheritanceInfo_ ) { pInheritanceInfo = pInheritanceInfo_; return *this; } operator const VkCommandBufferBeginInfo&() const { return *reinterpret_cast(this); } bool operator==( CommandBufferBeginInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( pInheritanceInfo == rhs.pInheritanceInfo ); } bool operator!=( CommandBufferBeginInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; CommandBufferUsageFlags flags; const CommandBufferInheritanceInfo* pInheritanceInfo; }; static_assert( sizeof( CommandBufferBeginInfo ) == sizeof( VkCommandBufferBeginInfo ), "struct and wrapper have different size!" ); struct QueryPoolCreateInfo { QueryPoolCreateInfo( QueryPoolCreateFlags flags_ = QueryPoolCreateFlags(), QueryType queryType_ = QueryType::eOcclusion, uint32_t queryCount_ = 0, QueryPipelineStatisticFlags pipelineStatistics_ = QueryPipelineStatisticFlags() ) : sType( StructureType::eQueryPoolCreateInfo ) , pNext( nullptr ) , flags( flags_ ) , queryType( queryType_ ) , queryCount( queryCount_ ) , pipelineStatistics( pipelineStatistics_ ) { } QueryPoolCreateInfo( VkQueryPoolCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(QueryPoolCreateInfo) ); } QueryPoolCreateInfo& operator=( VkQueryPoolCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(QueryPoolCreateInfo) ); return *this; } QueryPoolCreateInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } QueryPoolCreateInfo& setFlags( QueryPoolCreateFlags flags_ ) { flags = flags_; return *this; } QueryPoolCreateInfo& setQueryType( QueryType queryType_ ) { queryType = queryType_; return *this; } QueryPoolCreateInfo& setQueryCount( uint32_t queryCount_ ) { queryCount = queryCount_; return *this; } QueryPoolCreateInfo& setPipelineStatistics( QueryPipelineStatisticFlags pipelineStatistics_ ) { pipelineStatistics = pipelineStatistics_; return *this; } operator const VkQueryPoolCreateInfo&() const { return *reinterpret_cast(this); } bool operator==( QueryPoolCreateInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( queryType == rhs.queryType ) && ( queryCount == rhs.queryCount ) && ( pipelineStatistics == rhs.pipelineStatistics ); } bool operator!=( QueryPoolCreateInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; QueryPoolCreateFlags flags; QueryType queryType; uint32_t queryCount; QueryPipelineStatisticFlags pipelineStatistics; }; static_assert( sizeof( QueryPoolCreateInfo ) == sizeof( VkQueryPoolCreateInfo ), "struct and wrapper have different size!" ); enum class ImageAspectFlagBits { eColor = VK_IMAGE_ASPECT_COLOR_BIT, eDepth = VK_IMAGE_ASPECT_DEPTH_BIT, eStencil = VK_IMAGE_ASPECT_STENCIL_BIT, eMetadata = VK_IMAGE_ASPECT_METADATA_BIT }; using ImageAspectFlags = Flags; VULKAN_HPP_INLINE ImageAspectFlags operator|( ImageAspectFlagBits bit0, ImageAspectFlagBits bit1 ) { return ImageAspectFlags( bit0 ) | bit1; } VULKAN_HPP_INLINE ImageAspectFlags operator~( ImageAspectFlagBits bits ) { return ~( ImageAspectFlags( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(ImageAspectFlagBits::eColor) | VkFlags(ImageAspectFlagBits::eDepth) | VkFlags(ImageAspectFlagBits::eStencil) | VkFlags(ImageAspectFlagBits::eMetadata) }; }; struct ImageSubresource { ImageSubresource( ImageAspectFlags aspectMask_ = ImageAspectFlags(), uint32_t mipLevel_ = 0, uint32_t arrayLayer_ = 0 ) : aspectMask( aspectMask_ ) , mipLevel( mipLevel_ ) , arrayLayer( arrayLayer_ ) { } ImageSubresource( VkImageSubresource const & rhs ) { memcpy( this, &rhs, sizeof(ImageSubresource) ); } ImageSubresource& operator=( VkImageSubresource const & rhs ) { memcpy( this, &rhs, sizeof(ImageSubresource) ); return *this; } ImageSubresource& setAspectMask( ImageAspectFlags aspectMask_ ) { aspectMask = aspectMask_; return *this; } ImageSubresource& setMipLevel( uint32_t mipLevel_ ) { mipLevel = mipLevel_; return *this; } ImageSubresource& setArrayLayer( uint32_t arrayLayer_ ) { arrayLayer = arrayLayer_; return *this; } operator const VkImageSubresource&() const { return *reinterpret_cast(this); } bool operator==( ImageSubresource const& rhs ) const { return ( aspectMask == rhs.aspectMask ) && ( mipLevel == rhs.mipLevel ) && ( arrayLayer == rhs.arrayLayer ); } bool operator!=( ImageSubresource const& rhs ) const { return !operator==( rhs ); } ImageAspectFlags aspectMask; uint32_t mipLevel; uint32_t arrayLayer; }; static_assert( sizeof( ImageSubresource ) == sizeof( VkImageSubresource ), "struct and wrapper have different size!" ); struct ImageSubresourceLayers { ImageSubresourceLayers( ImageAspectFlags aspectMask_ = ImageAspectFlags(), uint32_t mipLevel_ = 0, uint32_t baseArrayLayer_ = 0, uint32_t layerCount_ = 0 ) : aspectMask( aspectMask_ ) , mipLevel( mipLevel_ ) , baseArrayLayer( baseArrayLayer_ ) , layerCount( layerCount_ ) { } ImageSubresourceLayers( VkImageSubresourceLayers const & rhs ) { memcpy( this, &rhs, sizeof(ImageSubresourceLayers) ); } ImageSubresourceLayers& operator=( VkImageSubresourceLayers const & rhs ) { memcpy( this, &rhs, sizeof(ImageSubresourceLayers) ); return *this; } ImageSubresourceLayers& setAspectMask( ImageAspectFlags aspectMask_ ) { aspectMask = aspectMask_; return *this; } ImageSubresourceLayers& setMipLevel( uint32_t mipLevel_ ) { mipLevel = mipLevel_; return *this; } ImageSubresourceLayers& setBaseArrayLayer( uint32_t baseArrayLayer_ ) { baseArrayLayer = baseArrayLayer_; return *this; } ImageSubresourceLayers& setLayerCount( uint32_t layerCount_ ) { layerCount = layerCount_; return *this; } operator const VkImageSubresourceLayers&() const { return *reinterpret_cast(this); } bool operator==( ImageSubresourceLayers const& rhs ) const { return ( aspectMask == rhs.aspectMask ) && ( mipLevel == rhs.mipLevel ) && ( baseArrayLayer == rhs.baseArrayLayer ) && ( layerCount == rhs.layerCount ); } bool operator!=( ImageSubresourceLayers const& rhs ) const { return !operator==( rhs ); } ImageAspectFlags aspectMask; uint32_t mipLevel; uint32_t baseArrayLayer; uint32_t layerCount; }; static_assert( sizeof( ImageSubresourceLayers ) == sizeof( VkImageSubresourceLayers ), "struct and wrapper have different size!" ); struct ImageSubresourceRange { ImageSubresourceRange( ImageAspectFlags aspectMask_ = ImageAspectFlags(), uint32_t baseMipLevel_ = 0, uint32_t levelCount_ = 0, uint32_t baseArrayLayer_ = 0, uint32_t layerCount_ = 0 ) : aspectMask( aspectMask_ ) , baseMipLevel( baseMipLevel_ ) , levelCount( levelCount_ ) , baseArrayLayer( baseArrayLayer_ ) , layerCount( layerCount_ ) { } ImageSubresourceRange( VkImageSubresourceRange const & rhs ) { memcpy( this, &rhs, sizeof(ImageSubresourceRange) ); } ImageSubresourceRange& operator=( VkImageSubresourceRange const & rhs ) { memcpy( this, &rhs, sizeof(ImageSubresourceRange) ); return *this; } ImageSubresourceRange& setAspectMask( ImageAspectFlags aspectMask_ ) { aspectMask = aspectMask_; return *this; } ImageSubresourceRange& setBaseMipLevel( uint32_t baseMipLevel_ ) { baseMipLevel = baseMipLevel_; return *this; } ImageSubresourceRange& setLevelCount( uint32_t levelCount_ ) { levelCount = levelCount_; return *this; } ImageSubresourceRange& setBaseArrayLayer( uint32_t baseArrayLayer_ ) { baseArrayLayer = baseArrayLayer_; return *this; } ImageSubresourceRange& setLayerCount( uint32_t layerCount_ ) { layerCount = layerCount_; return *this; } operator const VkImageSubresourceRange&() const { return *reinterpret_cast(this); } bool operator==( ImageSubresourceRange const& rhs ) const { return ( aspectMask == rhs.aspectMask ) && ( baseMipLevel == rhs.baseMipLevel ) && ( levelCount == rhs.levelCount ) && ( baseArrayLayer == rhs.baseArrayLayer ) && ( layerCount == rhs.layerCount ); } bool operator!=( ImageSubresourceRange const& rhs ) const { return !operator==( rhs ); } ImageAspectFlags aspectMask; uint32_t baseMipLevel; uint32_t levelCount; uint32_t baseArrayLayer; uint32_t layerCount; }; static_assert( sizeof( ImageSubresourceRange ) == sizeof( VkImageSubresourceRange ), "struct and wrapper have different size!" ); struct ImageMemoryBarrier { ImageMemoryBarrier( AccessFlags srcAccessMask_ = AccessFlags(), AccessFlags dstAccessMask_ = AccessFlags(), ImageLayout oldLayout_ = ImageLayout::eUndefined, ImageLayout newLayout_ = ImageLayout::eUndefined, uint32_t srcQueueFamilyIndex_ = 0, uint32_t dstQueueFamilyIndex_ = 0, Image image_ = Image(), ImageSubresourceRange subresourceRange_ = ImageSubresourceRange() ) : sType( StructureType::eImageMemoryBarrier ) , pNext( nullptr ) , srcAccessMask( srcAccessMask_ ) , dstAccessMask( dstAccessMask_ ) , oldLayout( oldLayout_ ) , newLayout( newLayout_ ) , srcQueueFamilyIndex( srcQueueFamilyIndex_ ) , dstQueueFamilyIndex( dstQueueFamilyIndex_ ) , image( image_ ) , subresourceRange( subresourceRange_ ) { } ImageMemoryBarrier( VkImageMemoryBarrier const & rhs ) { memcpy( this, &rhs, sizeof(ImageMemoryBarrier) ); } ImageMemoryBarrier& operator=( VkImageMemoryBarrier const & rhs ) { memcpy( this, &rhs, sizeof(ImageMemoryBarrier) ); return *this; } ImageMemoryBarrier& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } ImageMemoryBarrier& setSrcAccessMask( AccessFlags srcAccessMask_ ) { srcAccessMask = srcAccessMask_; return *this; } ImageMemoryBarrier& setDstAccessMask( AccessFlags dstAccessMask_ ) { dstAccessMask = dstAccessMask_; return *this; } ImageMemoryBarrier& setOldLayout( ImageLayout oldLayout_ ) { oldLayout = oldLayout_; return *this; } ImageMemoryBarrier& setNewLayout( ImageLayout newLayout_ ) { newLayout = newLayout_; return *this; } ImageMemoryBarrier& setSrcQueueFamilyIndex( uint32_t srcQueueFamilyIndex_ ) { srcQueueFamilyIndex = srcQueueFamilyIndex_; return *this; } ImageMemoryBarrier& setDstQueueFamilyIndex( uint32_t dstQueueFamilyIndex_ ) { dstQueueFamilyIndex = dstQueueFamilyIndex_; return *this; } ImageMemoryBarrier& setImage( Image image_ ) { image = image_; return *this; } ImageMemoryBarrier& setSubresourceRange( ImageSubresourceRange subresourceRange_ ) { subresourceRange = subresourceRange_; return *this; } operator const VkImageMemoryBarrier&() const { return *reinterpret_cast(this); } bool operator==( ImageMemoryBarrier const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( srcAccessMask == rhs.srcAccessMask ) && ( dstAccessMask == rhs.dstAccessMask ) && ( oldLayout == rhs.oldLayout ) && ( newLayout == rhs.newLayout ) && ( srcQueueFamilyIndex == rhs.srcQueueFamilyIndex ) && ( dstQueueFamilyIndex == rhs.dstQueueFamilyIndex ) && ( image == rhs.image ) && ( subresourceRange == rhs.subresourceRange ); } bool operator!=( ImageMemoryBarrier const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; AccessFlags srcAccessMask; AccessFlags dstAccessMask; ImageLayout oldLayout; ImageLayout newLayout; uint32_t srcQueueFamilyIndex; uint32_t dstQueueFamilyIndex; Image image; ImageSubresourceRange subresourceRange; }; static_assert( sizeof( ImageMemoryBarrier ) == sizeof( VkImageMemoryBarrier ), "struct and wrapper have different size!" ); struct ImageViewCreateInfo { ImageViewCreateInfo( ImageViewCreateFlags flags_ = ImageViewCreateFlags(), Image image_ = Image(), ImageViewType viewType_ = ImageViewType::e1D, Format format_ = Format::eUndefined, ComponentMapping components_ = ComponentMapping(), ImageSubresourceRange subresourceRange_ = ImageSubresourceRange() ) : sType( StructureType::eImageViewCreateInfo ) , pNext( nullptr ) , flags( flags_ ) , image( image_ ) , viewType( viewType_ ) , format( format_ ) , components( components_ ) , subresourceRange( subresourceRange_ ) { } ImageViewCreateInfo( VkImageViewCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(ImageViewCreateInfo) ); } ImageViewCreateInfo& operator=( VkImageViewCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(ImageViewCreateInfo) ); return *this; } ImageViewCreateInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } ImageViewCreateInfo& setFlags( ImageViewCreateFlags flags_ ) { flags = flags_; return *this; } ImageViewCreateInfo& setImage( Image image_ ) { image = image_; return *this; } ImageViewCreateInfo& setViewType( ImageViewType viewType_ ) { viewType = viewType_; return *this; } ImageViewCreateInfo& setFormat( Format format_ ) { format = format_; return *this; } ImageViewCreateInfo& setComponents( ComponentMapping components_ ) { components = components_; return *this; } ImageViewCreateInfo& setSubresourceRange( ImageSubresourceRange subresourceRange_ ) { subresourceRange = subresourceRange_; return *this; } operator const VkImageViewCreateInfo&() const { return *reinterpret_cast(this); } bool operator==( ImageViewCreateInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( image == rhs.image ) && ( viewType == rhs.viewType ) && ( format == rhs.format ) && ( components == rhs.components ) && ( subresourceRange == rhs.subresourceRange ); } bool operator!=( ImageViewCreateInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; ImageViewCreateFlags flags; Image image; ImageViewType viewType; Format format; ComponentMapping components; ImageSubresourceRange subresourceRange; }; static_assert( sizeof( ImageViewCreateInfo ) == sizeof( VkImageViewCreateInfo ), "struct and wrapper have different size!" ); struct ImageCopy { ImageCopy( ImageSubresourceLayers srcSubresource_ = ImageSubresourceLayers(), Offset3D srcOffset_ = Offset3D(), ImageSubresourceLayers dstSubresource_ = ImageSubresourceLayers(), Offset3D dstOffset_ = Offset3D(), Extent3D extent_ = Extent3D() ) : srcSubresource( srcSubresource_ ) , srcOffset( srcOffset_ ) , dstSubresource( dstSubresource_ ) , dstOffset( dstOffset_ ) , extent( extent_ ) { } ImageCopy( VkImageCopy const & rhs ) { memcpy( this, &rhs, sizeof(ImageCopy) ); } ImageCopy& operator=( VkImageCopy const & rhs ) { memcpy( this, &rhs, sizeof(ImageCopy) ); return *this; } ImageCopy& setSrcSubresource( ImageSubresourceLayers srcSubresource_ ) { srcSubresource = srcSubresource_; return *this; } ImageCopy& setSrcOffset( Offset3D srcOffset_ ) { srcOffset = srcOffset_; return *this; } ImageCopy& setDstSubresource( ImageSubresourceLayers dstSubresource_ ) { dstSubresource = dstSubresource_; return *this; } ImageCopy& setDstOffset( Offset3D dstOffset_ ) { dstOffset = dstOffset_; return *this; } ImageCopy& setExtent( Extent3D extent_ ) { extent = extent_; return *this; } operator const VkImageCopy&() const { return *reinterpret_cast(this); } bool operator==( ImageCopy const& rhs ) const { return ( srcSubresource == rhs.srcSubresource ) && ( srcOffset == rhs.srcOffset ) && ( dstSubresource == rhs.dstSubresource ) && ( dstOffset == rhs.dstOffset ) && ( extent == rhs.extent ); } bool operator!=( ImageCopy const& rhs ) const { return !operator==( rhs ); } ImageSubresourceLayers srcSubresource; Offset3D srcOffset; ImageSubresourceLayers dstSubresource; Offset3D dstOffset; Extent3D extent; }; static_assert( sizeof( ImageCopy ) == sizeof( VkImageCopy ), "struct and wrapper have different size!" ); struct ImageBlit { ImageBlit( ImageSubresourceLayers srcSubresource_ = ImageSubresourceLayers(), std::array const& srcOffsets_ = { { Offset3D(), Offset3D() } }, ImageSubresourceLayers dstSubresource_ = ImageSubresourceLayers(), std::array const& dstOffsets_ = { { Offset3D(), Offset3D() } } ) : srcSubresource( srcSubresource_ ) , dstSubresource( dstSubresource_ ) { memcpy( &srcOffsets, srcOffsets_.data(), 2 * sizeof( Offset3D ) ); memcpy( &dstOffsets, dstOffsets_.data(), 2 * sizeof( Offset3D ) ); } ImageBlit( VkImageBlit const & rhs ) { memcpy( this, &rhs, sizeof(ImageBlit) ); } ImageBlit& operator=( VkImageBlit const & rhs ) { memcpy( this, &rhs, sizeof(ImageBlit) ); return *this; } ImageBlit& setSrcSubresource( ImageSubresourceLayers srcSubresource_ ) { srcSubresource = srcSubresource_; return *this; } ImageBlit& setSrcOffsets( std::array srcOffsets_ ) { memcpy( &srcOffsets, srcOffsets_.data(), 2 * sizeof( Offset3D ) ); return *this; } ImageBlit& setDstSubresource( ImageSubresourceLayers dstSubresource_ ) { dstSubresource = dstSubresource_; return *this; } ImageBlit& setDstOffsets( std::array dstOffsets_ ) { memcpy( &dstOffsets, dstOffsets_.data(), 2 * sizeof( Offset3D ) ); return *this; } operator const VkImageBlit&() const { return *reinterpret_cast(this); } bool operator==( ImageBlit const& rhs ) const { return ( srcSubresource == rhs.srcSubresource ) && ( memcmp( srcOffsets, rhs.srcOffsets, 2 * sizeof( Offset3D ) ) == 0 ) && ( dstSubresource == rhs.dstSubresource ) && ( memcmp( dstOffsets, rhs.dstOffsets, 2 * sizeof( Offset3D ) ) == 0 ); } bool operator!=( ImageBlit const& rhs ) const { return !operator==( rhs ); } ImageSubresourceLayers srcSubresource; Offset3D srcOffsets[2]; ImageSubresourceLayers dstSubresource; Offset3D dstOffsets[2]; }; static_assert( sizeof( ImageBlit ) == sizeof( VkImageBlit ), "struct and wrapper have different size!" ); struct BufferImageCopy { BufferImageCopy( DeviceSize bufferOffset_ = 0, uint32_t bufferRowLength_ = 0, uint32_t bufferImageHeight_ = 0, ImageSubresourceLayers imageSubresource_ = ImageSubresourceLayers(), Offset3D imageOffset_ = Offset3D(), Extent3D imageExtent_ = Extent3D() ) : bufferOffset( bufferOffset_ ) , bufferRowLength( bufferRowLength_ ) , bufferImageHeight( bufferImageHeight_ ) , imageSubresource( imageSubresource_ ) , imageOffset( imageOffset_ ) , imageExtent( imageExtent_ ) { } BufferImageCopy( VkBufferImageCopy const & rhs ) { memcpy( this, &rhs, sizeof(BufferImageCopy) ); } BufferImageCopy& operator=( VkBufferImageCopy const & rhs ) { memcpy( this, &rhs, sizeof(BufferImageCopy) ); return *this; } BufferImageCopy& setBufferOffset( DeviceSize bufferOffset_ ) { bufferOffset = bufferOffset_; return *this; } BufferImageCopy& setBufferRowLength( uint32_t bufferRowLength_ ) { bufferRowLength = bufferRowLength_; return *this; } BufferImageCopy& setBufferImageHeight( uint32_t bufferImageHeight_ ) { bufferImageHeight = bufferImageHeight_; return *this; } BufferImageCopy& setImageSubresource( ImageSubresourceLayers imageSubresource_ ) { imageSubresource = imageSubresource_; return *this; } BufferImageCopy& setImageOffset( Offset3D imageOffset_ ) { imageOffset = imageOffset_; return *this; } BufferImageCopy& setImageExtent( Extent3D imageExtent_ ) { imageExtent = imageExtent_; return *this; } operator const VkBufferImageCopy&() const { return *reinterpret_cast(this); } bool operator==( BufferImageCopy const& rhs ) const { return ( bufferOffset == rhs.bufferOffset ) && ( bufferRowLength == rhs.bufferRowLength ) && ( bufferImageHeight == rhs.bufferImageHeight ) && ( imageSubresource == rhs.imageSubresource ) && ( imageOffset == rhs.imageOffset ) && ( imageExtent == rhs.imageExtent ); } bool operator!=( BufferImageCopy const& rhs ) const { return !operator==( rhs ); } DeviceSize bufferOffset; uint32_t bufferRowLength; uint32_t bufferImageHeight; ImageSubresourceLayers imageSubresource; Offset3D imageOffset; Extent3D imageExtent; }; static_assert( sizeof( BufferImageCopy ) == sizeof( VkBufferImageCopy ), "struct and wrapper have different size!" ); struct ImageResolve { ImageResolve( ImageSubresourceLayers srcSubresource_ = ImageSubresourceLayers(), Offset3D srcOffset_ = Offset3D(), ImageSubresourceLayers dstSubresource_ = ImageSubresourceLayers(), Offset3D dstOffset_ = Offset3D(), Extent3D extent_ = Extent3D() ) : srcSubresource( srcSubresource_ ) , srcOffset( srcOffset_ ) , dstSubresource( dstSubresource_ ) , dstOffset( dstOffset_ ) , extent( extent_ ) { } ImageResolve( VkImageResolve const & rhs ) { memcpy( this, &rhs, sizeof(ImageResolve) ); } ImageResolve& operator=( VkImageResolve const & rhs ) { memcpy( this, &rhs, sizeof(ImageResolve) ); return *this; } ImageResolve& setSrcSubresource( ImageSubresourceLayers srcSubresource_ ) { srcSubresource = srcSubresource_; return *this; } ImageResolve& setSrcOffset( Offset3D srcOffset_ ) { srcOffset = srcOffset_; return *this; } ImageResolve& setDstSubresource( ImageSubresourceLayers dstSubresource_ ) { dstSubresource = dstSubresource_; return *this; } ImageResolve& setDstOffset( Offset3D dstOffset_ ) { dstOffset = dstOffset_; return *this; } ImageResolve& setExtent( Extent3D extent_ ) { extent = extent_; return *this; } operator const VkImageResolve&() const { return *reinterpret_cast(this); } bool operator==( ImageResolve const& rhs ) const { return ( srcSubresource == rhs.srcSubresource ) && ( srcOffset == rhs.srcOffset ) && ( dstSubresource == rhs.dstSubresource ) && ( dstOffset == rhs.dstOffset ) && ( extent == rhs.extent ); } bool operator!=( ImageResolve const& rhs ) const { return !operator==( rhs ); } ImageSubresourceLayers srcSubresource; Offset3D srcOffset; ImageSubresourceLayers dstSubresource; Offset3D dstOffset; Extent3D extent; }; static_assert( sizeof( ImageResolve ) == sizeof( VkImageResolve ), "struct and wrapper have different size!" ); struct ClearAttachment { ClearAttachment( ImageAspectFlags aspectMask_ = ImageAspectFlags(), uint32_t colorAttachment_ = 0, ClearValue clearValue_ = ClearValue() ) : aspectMask( aspectMask_ ) , colorAttachment( colorAttachment_ ) , clearValue( clearValue_ ) { } ClearAttachment( VkClearAttachment const & rhs ) { memcpy( this, &rhs, sizeof(ClearAttachment) ); } ClearAttachment& operator=( VkClearAttachment const & rhs ) { memcpy( this, &rhs, sizeof(ClearAttachment) ); return *this; } ClearAttachment& setAspectMask( ImageAspectFlags aspectMask_ ) { aspectMask = aspectMask_; return *this; } ClearAttachment& setColorAttachment( uint32_t colorAttachment_ ) { colorAttachment = colorAttachment_; return *this; } ClearAttachment& setClearValue( ClearValue clearValue_ ) { clearValue = clearValue_; return *this; } operator const VkClearAttachment&() const { return *reinterpret_cast(this); } ImageAspectFlags aspectMask; uint32_t colorAttachment; ClearValue clearValue; }; static_assert( sizeof( ClearAttachment ) == sizeof( VkClearAttachment ), "struct and wrapper have different size!" ); enum class SparseImageFormatFlagBits { eSingleMiptail = VK_SPARSE_IMAGE_FORMAT_SINGLE_MIPTAIL_BIT, eAlignedMipSize = VK_SPARSE_IMAGE_FORMAT_ALIGNED_MIP_SIZE_BIT, eNonstandardBlockSize = VK_SPARSE_IMAGE_FORMAT_NONSTANDARD_BLOCK_SIZE_BIT }; using SparseImageFormatFlags = Flags; VULKAN_HPP_INLINE SparseImageFormatFlags operator|( SparseImageFormatFlagBits bit0, SparseImageFormatFlagBits bit1 ) { return SparseImageFormatFlags( bit0 ) | bit1; } VULKAN_HPP_INLINE SparseImageFormatFlags operator~( SparseImageFormatFlagBits bits ) { return ~( SparseImageFormatFlags( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(SparseImageFormatFlagBits::eSingleMiptail) | VkFlags(SparseImageFormatFlagBits::eAlignedMipSize) | VkFlags(SparseImageFormatFlagBits::eNonstandardBlockSize) }; }; struct SparseImageFormatProperties { operator const VkSparseImageFormatProperties&() const { return *reinterpret_cast(this); } bool operator==( SparseImageFormatProperties const& rhs ) const { return ( aspectMask == rhs.aspectMask ) && ( imageGranularity == rhs.imageGranularity ) && ( flags == rhs.flags ); } bool operator!=( SparseImageFormatProperties const& rhs ) const { return !operator==( rhs ); } ImageAspectFlags aspectMask; Extent3D imageGranularity; SparseImageFormatFlags flags; }; static_assert( sizeof( SparseImageFormatProperties ) == sizeof( VkSparseImageFormatProperties ), "struct and wrapper have different size!" ); struct SparseImageMemoryRequirements { operator const VkSparseImageMemoryRequirements&() const { return *reinterpret_cast(this); } bool operator==( SparseImageMemoryRequirements const& rhs ) const { return ( formatProperties == rhs.formatProperties ) && ( imageMipTailFirstLod == rhs.imageMipTailFirstLod ) && ( imageMipTailSize == rhs.imageMipTailSize ) && ( imageMipTailOffset == rhs.imageMipTailOffset ) && ( imageMipTailStride == rhs.imageMipTailStride ); } bool operator!=( SparseImageMemoryRequirements const& rhs ) const { return !operator==( rhs ); } SparseImageFormatProperties formatProperties; uint32_t imageMipTailFirstLod; DeviceSize imageMipTailSize; DeviceSize imageMipTailOffset; DeviceSize imageMipTailStride; }; static_assert( sizeof( SparseImageMemoryRequirements ) == sizeof( VkSparseImageMemoryRequirements ), "struct and wrapper have different size!" ); struct SparseImageFormatProperties2KHR { operator const VkSparseImageFormatProperties2KHR&() const { return *reinterpret_cast(this); } bool operator==( SparseImageFormatProperties2KHR const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( properties == rhs.properties ); } bool operator!=( SparseImageFormatProperties2KHR const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: void* pNext; SparseImageFormatProperties properties; }; static_assert( sizeof( SparseImageFormatProperties2KHR ) == sizeof( VkSparseImageFormatProperties2KHR ), "struct and wrapper have different size!" ); enum class SparseMemoryBindFlagBits { eMetadata = VK_SPARSE_MEMORY_BIND_METADATA_BIT }; using SparseMemoryBindFlags = Flags; VULKAN_HPP_INLINE SparseMemoryBindFlags operator|( SparseMemoryBindFlagBits bit0, SparseMemoryBindFlagBits bit1 ) { return SparseMemoryBindFlags( bit0 ) | bit1; } VULKAN_HPP_INLINE SparseMemoryBindFlags operator~( SparseMemoryBindFlagBits bits ) { return ~( SparseMemoryBindFlags( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(SparseMemoryBindFlagBits::eMetadata) }; }; struct SparseMemoryBind { SparseMemoryBind( DeviceSize resourceOffset_ = 0, DeviceSize size_ = 0, DeviceMemory memory_ = DeviceMemory(), DeviceSize memoryOffset_ = 0, SparseMemoryBindFlags flags_ = SparseMemoryBindFlags() ) : resourceOffset( resourceOffset_ ) , size( size_ ) , memory( memory_ ) , memoryOffset( memoryOffset_ ) , flags( flags_ ) { } SparseMemoryBind( VkSparseMemoryBind const & rhs ) { memcpy( this, &rhs, sizeof(SparseMemoryBind) ); } SparseMemoryBind& operator=( VkSparseMemoryBind const & rhs ) { memcpy( this, &rhs, sizeof(SparseMemoryBind) ); return *this; } SparseMemoryBind& setResourceOffset( DeviceSize resourceOffset_ ) { resourceOffset = resourceOffset_; return *this; } SparseMemoryBind& setSize( DeviceSize size_ ) { size = size_; return *this; } SparseMemoryBind& setMemory( DeviceMemory memory_ ) { memory = memory_; return *this; } SparseMemoryBind& setMemoryOffset( DeviceSize memoryOffset_ ) { memoryOffset = memoryOffset_; return *this; } SparseMemoryBind& setFlags( SparseMemoryBindFlags flags_ ) { flags = flags_; return *this; } operator const VkSparseMemoryBind&() const { return *reinterpret_cast(this); } bool operator==( SparseMemoryBind const& rhs ) const { return ( resourceOffset == rhs.resourceOffset ) && ( size == rhs.size ) && ( memory == rhs.memory ) && ( memoryOffset == rhs.memoryOffset ) && ( flags == rhs.flags ); } bool operator!=( SparseMemoryBind const& rhs ) const { return !operator==( rhs ); } DeviceSize resourceOffset; DeviceSize size; DeviceMemory memory; DeviceSize memoryOffset; SparseMemoryBindFlags flags; }; static_assert( sizeof( SparseMemoryBind ) == sizeof( VkSparseMemoryBind ), "struct and wrapper have different size!" ); struct SparseImageMemoryBind { SparseImageMemoryBind( ImageSubresource subresource_ = ImageSubresource(), Offset3D offset_ = Offset3D(), Extent3D extent_ = Extent3D(), DeviceMemory memory_ = DeviceMemory(), DeviceSize memoryOffset_ = 0, SparseMemoryBindFlags flags_ = SparseMemoryBindFlags() ) : subresource( subresource_ ) , offset( offset_ ) , extent( extent_ ) , memory( memory_ ) , memoryOffset( memoryOffset_ ) , flags( flags_ ) { } SparseImageMemoryBind( VkSparseImageMemoryBind const & rhs ) { memcpy( this, &rhs, sizeof(SparseImageMemoryBind) ); } SparseImageMemoryBind& operator=( VkSparseImageMemoryBind const & rhs ) { memcpy( this, &rhs, sizeof(SparseImageMemoryBind) ); return *this; } SparseImageMemoryBind& setSubresource( ImageSubresource subresource_ ) { subresource = subresource_; return *this; } SparseImageMemoryBind& setOffset( Offset3D offset_ ) { offset = offset_; return *this; } SparseImageMemoryBind& setExtent( Extent3D extent_ ) { extent = extent_; return *this; } SparseImageMemoryBind& setMemory( DeviceMemory memory_ ) { memory = memory_; return *this; } SparseImageMemoryBind& setMemoryOffset( DeviceSize memoryOffset_ ) { memoryOffset = memoryOffset_; return *this; } SparseImageMemoryBind& setFlags( SparseMemoryBindFlags flags_ ) { flags = flags_; return *this; } operator const VkSparseImageMemoryBind&() const { return *reinterpret_cast(this); } bool operator==( SparseImageMemoryBind const& rhs ) const { return ( subresource == rhs.subresource ) && ( offset == rhs.offset ) && ( extent == rhs.extent ) && ( memory == rhs.memory ) && ( memoryOffset == rhs.memoryOffset ) && ( flags == rhs.flags ); } bool operator!=( SparseImageMemoryBind const& rhs ) const { return !operator==( rhs ); } ImageSubresource subresource; Offset3D offset; Extent3D extent; DeviceMemory memory; DeviceSize memoryOffset; SparseMemoryBindFlags flags; }; static_assert( sizeof( SparseImageMemoryBind ) == sizeof( VkSparseImageMemoryBind ), "struct and wrapper have different size!" ); struct SparseBufferMemoryBindInfo { SparseBufferMemoryBindInfo( Buffer buffer_ = Buffer(), uint32_t bindCount_ = 0, const SparseMemoryBind* pBinds_ = nullptr ) : buffer( buffer_ ) , bindCount( bindCount_ ) , pBinds( pBinds_ ) { } SparseBufferMemoryBindInfo( VkSparseBufferMemoryBindInfo const & rhs ) { memcpy( this, &rhs, sizeof(SparseBufferMemoryBindInfo) ); } SparseBufferMemoryBindInfo& operator=( VkSparseBufferMemoryBindInfo const & rhs ) { memcpy( this, &rhs, sizeof(SparseBufferMemoryBindInfo) ); return *this; } SparseBufferMemoryBindInfo& setBuffer( Buffer buffer_ ) { buffer = buffer_; return *this; } SparseBufferMemoryBindInfo& setBindCount( uint32_t bindCount_ ) { bindCount = bindCount_; return *this; } SparseBufferMemoryBindInfo& setPBinds( const SparseMemoryBind* pBinds_ ) { pBinds = pBinds_; return *this; } operator const VkSparseBufferMemoryBindInfo&() const { return *reinterpret_cast(this); } bool operator==( SparseBufferMemoryBindInfo const& rhs ) const { return ( buffer == rhs.buffer ) && ( bindCount == rhs.bindCount ) && ( pBinds == rhs.pBinds ); } bool operator!=( SparseBufferMemoryBindInfo const& rhs ) const { return !operator==( rhs ); } Buffer buffer; uint32_t bindCount; const SparseMemoryBind* pBinds; }; static_assert( sizeof( SparseBufferMemoryBindInfo ) == sizeof( VkSparseBufferMemoryBindInfo ), "struct and wrapper have different size!" ); struct SparseImageOpaqueMemoryBindInfo { SparseImageOpaqueMemoryBindInfo( Image image_ = Image(), uint32_t bindCount_ = 0, const SparseMemoryBind* pBinds_ = nullptr ) : image( image_ ) , bindCount( bindCount_ ) , pBinds( pBinds_ ) { } SparseImageOpaqueMemoryBindInfo( VkSparseImageOpaqueMemoryBindInfo const & rhs ) { memcpy( this, &rhs, sizeof(SparseImageOpaqueMemoryBindInfo) ); } SparseImageOpaqueMemoryBindInfo& operator=( VkSparseImageOpaqueMemoryBindInfo const & rhs ) { memcpy( this, &rhs, sizeof(SparseImageOpaqueMemoryBindInfo) ); return *this; } SparseImageOpaqueMemoryBindInfo& setImage( Image image_ ) { image = image_; return *this; } SparseImageOpaqueMemoryBindInfo& setBindCount( uint32_t bindCount_ ) { bindCount = bindCount_; return *this; } SparseImageOpaqueMemoryBindInfo& setPBinds( const SparseMemoryBind* pBinds_ ) { pBinds = pBinds_; return *this; } operator const VkSparseImageOpaqueMemoryBindInfo&() const { return *reinterpret_cast(this); } bool operator==( SparseImageOpaqueMemoryBindInfo const& rhs ) const { return ( image == rhs.image ) && ( bindCount == rhs.bindCount ) && ( pBinds == rhs.pBinds ); } bool operator!=( SparseImageOpaqueMemoryBindInfo const& rhs ) const { return !operator==( rhs ); } Image image; uint32_t bindCount; const SparseMemoryBind* pBinds; }; static_assert( sizeof( SparseImageOpaqueMemoryBindInfo ) == sizeof( VkSparseImageOpaqueMemoryBindInfo ), "struct and wrapper have different size!" ); struct SparseImageMemoryBindInfo { SparseImageMemoryBindInfo( Image image_ = Image(), uint32_t bindCount_ = 0, const SparseImageMemoryBind* pBinds_ = nullptr ) : image( image_ ) , bindCount( bindCount_ ) , pBinds( pBinds_ ) { } SparseImageMemoryBindInfo( VkSparseImageMemoryBindInfo const & rhs ) { memcpy( this, &rhs, sizeof(SparseImageMemoryBindInfo) ); } SparseImageMemoryBindInfo& operator=( VkSparseImageMemoryBindInfo const & rhs ) { memcpy( this, &rhs, sizeof(SparseImageMemoryBindInfo) ); return *this; } SparseImageMemoryBindInfo& setImage( Image image_ ) { image = image_; return *this; } SparseImageMemoryBindInfo& setBindCount( uint32_t bindCount_ ) { bindCount = bindCount_; return *this; } SparseImageMemoryBindInfo& setPBinds( const SparseImageMemoryBind* pBinds_ ) { pBinds = pBinds_; return *this; } operator const VkSparseImageMemoryBindInfo&() const { return *reinterpret_cast(this); } bool operator==( SparseImageMemoryBindInfo const& rhs ) const { return ( image == rhs.image ) && ( bindCount == rhs.bindCount ) && ( pBinds == rhs.pBinds ); } bool operator!=( SparseImageMemoryBindInfo const& rhs ) const { return !operator==( rhs ); } Image image; uint32_t bindCount; const SparseImageMemoryBind* pBinds; }; static_assert( sizeof( SparseImageMemoryBindInfo ) == sizeof( VkSparseImageMemoryBindInfo ), "struct and wrapper have different size!" ); struct BindSparseInfo { BindSparseInfo( uint32_t waitSemaphoreCount_ = 0, const Semaphore* pWaitSemaphores_ = nullptr, uint32_t bufferBindCount_ = 0, const SparseBufferMemoryBindInfo* pBufferBinds_ = nullptr, uint32_t imageOpaqueBindCount_ = 0, const SparseImageOpaqueMemoryBindInfo* pImageOpaqueBinds_ = nullptr, uint32_t imageBindCount_ = 0, const SparseImageMemoryBindInfo* pImageBinds_ = nullptr, uint32_t signalSemaphoreCount_ = 0, const Semaphore* pSignalSemaphores_ = nullptr ) : sType( StructureType::eBindSparseInfo ) , pNext( nullptr ) , waitSemaphoreCount( waitSemaphoreCount_ ) , pWaitSemaphores( pWaitSemaphores_ ) , bufferBindCount( bufferBindCount_ ) , pBufferBinds( pBufferBinds_ ) , imageOpaqueBindCount( imageOpaqueBindCount_ ) , pImageOpaqueBinds( pImageOpaqueBinds_ ) , imageBindCount( imageBindCount_ ) , pImageBinds( pImageBinds_ ) , signalSemaphoreCount( signalSemaphoreCount_ ) , pSignalSemaphores( pSignalSemaphores_ ) { } BindSparseInfo( VkBindSparseInfo const & rhs ) { memcpy( this, &rhs, sizeof(BindSparseInfo) ); } BindSparseInfo& operator=( VkBindSparseInfo const & rhs ) { memcpy( this, &rhs, sizeof(BindSparseInfo) ); return *this; } BindSparseInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } BindSparseInfo& setWaitSemaphoreCount( uint32_t waitSemaphoreCount_ ) { waitSemaphoreCount = waitSemaphoreCount_; return *this; } BindSparseInfo& setPWaitSemaphores( const Semaphore* pWaitSemaphores_ ) { pWaitSemaphores = pWaitSemaphores_; return *this; } BindSparseInfo& setBufferBindCount( uint32_t bufferBindCount_ ) { bufferBindCount = bufferBindCount_; return *this; } BindSparseInfo& setPBufferBinds( const SparseBufferMemoryBindInfo* pBufferBinds_ ) { pBufferBinds = pBufferBinds_; return *this; } BindSparseInfo& setImageOpaqueBindCount( uint32_t imageOpaqueBindCount_ ) { imageOpaqueBindCount = imageOpaqueBindCount_; return *this; } BindSparseInfo& setPImageOpaqueBinds( const SparseImageOpaqueMemoryBindInfo* pImageOpaqueBinds_ ) { pImageOpaqueBinds = pImageOpaqueBinds_; return *this; } BindSparseInfo& setImageBindCount( uint32_t imageBindCount_ ) { imageBindCount = imageBindCount_; return *this; } BindSparseInfo& setPImageBinds( const SparseImageMemoryBindInfo* pImageBinds_ ) { pImageBinds = pImageBinds_; return *this; } BindSparseInfo& setSignalSemaphoreCount( uint32_t signalSemaphoreCount_ ) { signalSemaphoreCount = signalSemaphoreCount_; return *this; } BindSparseInfo& setPSignalSemaphores( const Semaphore* pSignalSemaphores_ ) { pSignalSemaphores = pSignalSemaphores_; return *this; } operator const VkBindSparseInfo&() const { return *reinterpret_cast(this); } bool operator==( BindSparseInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( waitSemaphoreCount == rhs.waitSemaphoreCount ) && ( pWaitSemaphores == rhs.pWaitSemaphores ) && ( bufferBindCount == rhs.bufferBindCount ) && ( pBufferBinds == rhs.pBufferBinds ) && ( imageOpaqueBindCount == rhs.imageOpaqueBindCount ) && ( pImageOpaqueBinds == rhs.pImageOpaqueBinds ) && ( imageBindCount == rhs.imageBindCount ) && ( pImageBinds == rhs.pImageBinds ) && ( signalSemaphoreCount == rhs.signalSemaphoreCount ) && ( pSignalSemaphores == rhs.pSignalSemaphores ); } bool operator!=( BindSparseInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; uint32_t waitSemaphoreCount; const Semaphore* pWaitSemaphores; uint32_t bufferBindCount; const SparseBufferMemoryBindInfo* pBufferBinds; uint32_t imageOpaqueBindCount; const SparseImageOpaqueMemoryBindInfo* pImageOpaqueBinds; uint32_t imageBindCount; const SparseImageMemoryBindInfo* pImageBinds; uint32_t signalSemaphoreCount; const Semaphore* pSignalSemaphores; }; static_assert( sizeof( BindSparseInfo ) == sizeof( VkBindSparseInfo ), "struct and wrapper have different size!" ); enum class PipelineStageFlagBits { eTopOfPipe = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, eDrawIndirect = VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT, eVertexInput = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, eVertexShader = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, eTessellationControlShader = VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT, eTessellationEvaluationShader = VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT, eGeometryShader = VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT, eFragmentShader = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, eEarlyFragmentTests = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, eLateFragmentTests = VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, eColorAttachmentOutput = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, eComputeShader = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, eTransfer = VK_PIPELINE_STAGE_TRANSFER_BIT, eBottomOfPipe = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, eHost = VK_PIPELINE_STAGE_HOST_BIT, eAllGraphics = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, eAllCommands = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, eCommandProcessNVX = VK_PIPELINE_STAGE_COMMAND_PROCESS_BIT_NVX }; using PipelineStageFlags = Flags; VULKAN_HPP_INLINE PipelineStageFlags operator|( PipelineStageFlagBits bit0, PipelineStageFlagBits bit1 ) { return PipelineStageFlags( bit0 ) | bit1; } VULKAN_HPP_INLINE PipelineStageFlags operator~( PipelineStageFlagBits bits ) { return ~( PipelineStageFlags( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(PipelineStageFlagBits::eTopOfPipe) | VkFlags(PipelineStageFlagBits::eDrawIndirect) | VkFlags(PipelineStageFlagBits::eVertexInput) | VkFlags(PipelineStageFlagBits::eVertexShader) | VkFlags(PipelineStageFlagBits::eTessellationControlShader) | VkFlags(PipelineStageFlagBits::eTessellationEvaluationShader) | VkFlags(PipelineStageFlagBits::eGeometryShader) | VkFlags(PipelineStageFlagBits::eFragmentShader) | VkFlags(PipelineStageFlagBits::eEarlyFragmentTests) | VkFlags(PipelineStageFlagBits::eLateFragmentTests) | VkFlags(PipelineStageFlagBits::eColorAttachmentOutput) | VkFlags(PipelineStageFlagBits::eComputeShader) | VkFlags(PipelineStageFlagBits::eTransfer) | VkFlags(PipelineStageFlagBits::eBottomOfPipe) | VkFlags(PipelineStageFlagBits::eHost) | VkFlags(PipelineStageFlagBits::eAllGraphics) | VkFlags(PipelineStageFlagBits::eAllCommands) | VkFlags(PipelineStageFlagBits::eCommandProcessNVX) }; }; enum class CommandPoolCreateFlagBits { eTransient = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, eResetCommandBuffer = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT }; using CommandPoolCreateFlags = Flags; VULKAN_HPP_INLINE CommandPoolCreateFlags operator|( CommandPoolCreateFlagBits bit0, CommandPoolCreateFlagBits bit1 ) { return CommandPoolCreateFlags( bit0 ) | bit1; } VULKAN_HPP_INLINE CommandPoolCreateFlags operator~( CommandPoolCreateFlagBits bits ) { return ~( CommandPoolCreateFlags( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(CommandPoolCreateFlagBits::eTransient) | VkFlags(CommandPoolCreateFlagBits::eResetCommandBuffer) }; }; struct CommandPoolCreateInfo { CommandPoolCreateInfo( CommandPoolCreateFlags flags_ = CommandPoolCreateFlags(), uint32_t queueFamilyIndex_ = 0 ) : sType( StructureType::eCommandPoolCreateInfo ) , pNext( nullptr ) , flags( flags_ ) , queueFamilyIndex( queueFamilyIndex_ ) { } CommandPoolCreateInfo( VkCommandPoolCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(CommandPoolCreateInfo) ); } CommandPoolCreateInfo& operator=( VkCommandPoolCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(CommandPoolCreateInfo) ); return *this; } CommandPoolCreateInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } CommandPoolCreateInfo& setFlags( CommandPoolCreateFlags flags_ ) { flags = flags_; return *this; } CommandPoolCreateInfo& setQueueFamilyIndex( uint32_t queueFamilyIndex_ ) { queueFamilyIndex = queueFamilyIndex_; return *this; } operator const VkCommandPoolCreateInfo&() const { return *reinterpret_cast(this); } bool operator==( CommandPoolCreateInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( queueFamilyIndex == rhs.queueFamilyIndex ); } bool operator!=( CommandPoolCreateInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; CommandPoolCreateFlags flags; uint32_t queueFamilyIndex; }; static_assert( sizeof( CommandPoolCreateInfo ) == sizeof( VkCommandPoolCreateInfo ), "struct and wrapper have different size!" ); enum class CommandPoolResetFlagBits { eReleaseResources = VK_COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT }; using CommandPoolResetFlags = Flags; VULKAN_HPP_INLINE CommandPoolResetFlags operator|( CommandPoolResetFlagBits bit0, CommandPoolResetFlagBits bit1 ) { return CommandPoolResetFlags( bit0 ) | bit1; } VULKAN_HPP_INLINE CommandPoolResetFlags operator~( CommandPoolResetFlagBits bits ) { return ~( CommandPoolResetFlags( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(CommandPoolResetFlagBits::eReleaseResources) }; }; enum class CommandBufferResetFlagBits { eReleaseResources = VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT }; using CommandBufferResetFlags = Flags; VULKAN_HPP_INLINE CommandBufferResetFlags operator|( CommandBufferResetFlagBits bit0, CommandBufferResetFlagBits bit1 ) { return CommandBufferResetFlags( bit0 ) | bit1; } VULKAN_HPP_INLINE CommandBufferResetFlags operator~( CommandBufferResetFlagBits bits ) { return ~( CommandBufferResetFlags( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(CommandBufferResetFlagBits::eReleaseResources) }; }; enum class SampleCountFlagBits { e1 = VK_SAMPLE_COUNT_1_BIT, e2 = VK_SAMPLE_COUNT_2_BIT, e4 = VK_SAMPLE_COUNT_4_BIT, e8 = VK_SAMPLE_COUNT_8_BIT, e16 = VK_SAMPLE_COUNT_16_BIT, e32 = VK_SAMPLE_COUNT_32_BIT, e64 = VK_SAMPLE_COUNT_64_BIT }; using SampleCountFlags = Flags; VULKAN_HPP_INLINE SampleCountFlags operator|( SampleCountFlagBits bit0, SampleCountFlagBits bit1 ) { return SampleCountFlags( bit0 ) | bit1; } VULKAN_HPP_INLINE SampleCountFlags operator~( SampleCountFlagBits bits ) { return ~( SampleCountFlags( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(SampleCountFlagBits::e1) | VkFlags(SampleCountFlagBits::e2) | VkFlags(SampleCountFlagBits::e4) | VkFlags(SampleCountFlagBits::e8) | VkFlags(SampleCountFlagBits::e16) | VkFlags(SampleCountFlagBits::e32) | VkFlags(SampleCountFlagBits::e64) }; }; struct ImageFormatProperties { operator const VkImageFormatProperties&() const { return *reinterpret_cast(this); } bool operator==( ImageFormatProperties const& rhs ) const { return ( maxExtent == rhs.maxExtent ) && ( maxMipLevels == rhs.maxMipLevels ) && ( maxArrayLayers == rhs.maxArrayLayers ) && ( sampleCounts == rhs.sampleCounts ) && ( maxResourceSize == rhs.maxResourceSize ); } bool operator!=( ImageFormatProperties const& rhs ) const { return !operator==( rhs ); } Extent3D maxExtent; uint32_t maxMipLevels; uint32_t maxArrayLayers; SampleCountFlags sampleCounts; DeviceSize maxResourceSize; }; static_assert( sizeof( ImageFormatProperties ) == sizeof( VkImageFormatProperties ), "struct and wrapper have different size!" ); struct ImageCreateInfo { ImageCreateInfo( ImageCreateFlags flags_ = ImageCreateFlags(), ImageType imageType_ = ImageType::e1D, Format format_ = Format::eUndefined, Extent3D extent_ = Extent3D(), uint32_t mipLevels_ = 0, uint32_t arrayLayers_ = 0, SampleCountFlagBits samples_ = SampleCountFlagBits::e1, ImageTiling tiling_ = ImageTiling::eOptimal, ImageUsageFlags usage_ = ImageUsageFlags(), SharingMode sharingMode_ = SharingMode::eExclusive, uint32_t queueFamilyIndexCount_ = 0, const uint32_t* pQueueFamilyIndices_ = nullptr, ImageLayout initialLayout_ = ImageLayout::eUndefined ) : sType( StructureType::eImageCreateInfo ) , pNext( nullptr ) , flags( flags_ ) , imageType( imageType_ ) , format( format_ ) , extent( extent_ ) , mipLevels( mipLevels_ ) , arrayLayers( arrayLayers_ ) , samples( samples_ ) , tiling( tiling_ ) , usage( usage_ ) , sharingMode( sharingMode_ ) , queueFamilyIndexCount( queueFamilyIndexCount_ ) , pQueueFamilyIndices( pQueueFamilyIndices_ ) , initialLayout( initialLayout_ ) { } ImageCreateInfo( VkImageCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(ImageCreateInfo) ); } ImageCreateInfo& operator=( VkImageCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(ImageCreateInfo) ); return *this; } ImageCreateInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } ImageCreateInfo& setFlags( ImageCreateFlags flags_ ) { flags = flags_; return *this; } ImageCreateInfo& setImageType( ImageType imageType_ ) { imageType = imageType_; return *this; } ImageCreateInfo& setFormat( Format format_ ) { format = format_; return *this; } ImageCreateInfo& setExtent( Extent3D extent_ ) { extent = extent_; return *this; } ImageCreateInfo& setMipLevels( uint32_t mipLevels_ ) { mipLevels = mipLevels_; return *this; } ImageCreateInfo& setArrayLayers( uint32_t arrayLayers_ ) { arrayLayers = arrayLayers_; return *this; } ImageCreateInfo& setSamples( SampleCountFlagBits samples_ ) { samples = samples_; return *this; } ImageCreateInfo& setTiling( ImageTiling tiling_ ) { tiling = tiling_; return *this; } ImageCreateInfo& setUsage( ImageUsageFlags usage_ ) { usage = usage_; return *this; } ImageCreateInfo& setSharingMode( SharingMode sharingMode_ ) { sharingMode = sharingMode_; return *this; } ImageCreateInfo& setQueueFamilyIndexCount( uint32_t queueFamilyIndexCount_ ) { queueFamilyIndexCount = queueFamilyIndexCount_; return *this; } ImageCreateInfo& setPQueueFamilyIndices( const uint32_t* pQueueFamilyIndices_ ) { pQueueFamilyIndices = pQueueFamilyIndices_; return *this; } ImageCreateInfo& setInitialLayout( ImageLayout initialLayout_ ) { initialLayout = initialLayout_; return *this; } operator const VkImageCreateInfo&() const { return *reinterpret_cast(this); } bool operator==( ImageCreateInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( imageType == rhs.imageType ) && ( format == rhs.format ) && ( extent == rhs.extent ) && ( mipLevels == rhs.mipLevels ) && ( arrayLayers == rhs.arrayLayers ) && ( samples == rhs.samples ) && ( tiling == rhs.tiling ) && ( usage == rhs.usage ) && ( sharingMode == rhs.sharingMode ) && ( queueFamilyIndexCount == rhs.queueFamilyIndexCount ) && ( pQueueFamilyIndices == rhs.pQueueFamilyIndices ) && ( initialLayout == rhs.initialLayout ); } bool operator!=( ImageCreateInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; ImageCreateFlags flags; ImageType imageType; Format format; Extent3D extent; uint32_t mipLevels; uint32_t arrayLayers; SampleCountFlagBits samples; ImageTiling tiling; ImageUsageFlags usage; SharingMode sharingMode; uint32_t queueFamilyIndexCount; const uint32_t* pQueueFamilyIndices; ImageLayout initialLayout; }; static_assert( sizeof( ImageCreateInfo ) == sizeof( VkImageCreateInfo ), "struct and wrapper have different size!" ); struct PipelineMultisampleStateCreateInfo { PipelineMultisampleStateCreateInfo( PipelineMultisampleStateCreateFlags flags_ = PipelineMultisampleStateCreateFlags(), SampleCountFlagBits rasterizationSamples_ = SampleCountFlagBits::e1, Bool32 sampleShadingEnable_ = 0, float minSampleShading_ = 0, const SampleMask* pSampleMask_ = nullptr, Bool32 alphaToCoverageEnable_ = 0, Bool32 alphaToOneEnable_ = 0 ) : sType( StructureType::ePipelineMultisampleStateCreateInfo ) , pNext( nullptr ) , flags( flags_ ) , rasterizationSamples( rasterizationSamples_ ) , sampleShadingEnable( sampleShadingEnable_ ) , minSampleShading( minSampleShading_ ) , pSampleMask( pSampleMask_ ) , alphaToCoverageEnable( alphaToCoverageEnable_ ) , alphaToOneEnable( alphaToOneEnable_ ) { } PipelineMultisampleStateCreateInfo( VkPipelineMultisampleStateCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(PipelineMultisampleStateCreateInfo) ); } PipelineMultisampleStateCreateInfo& operator=( VkPipelineMultisampleStateCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(PipelineMultisampleStateCreateInfo) ); return *this; } PipelineMultisampleStateCreateInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } PipelineMultisampleStateCreateInfo& setFlags( PipelineMultisampleStateCreateFlags flags_ ) { flags = flags_; return *this; } PipelineMultisampleStateCreateInfo& setRasterizationSamples( SampleCountFlagBits rasterizationSamples_ ) { rasterizationSamples = rasterizationSamples_; return *this; } PipelineMultisampleStateCreateInfo& setSampleShadingEnable( Bool32 sampleShadingEnable_ ) { sampleShadingEnable = sampleShadingEnable_; return *this; } PipelineMultisampleStateCreateInfo& setMinSampleShading( float minSampleShading_ ) { minSampleShading = minSampleShading_; return *this; } PipelineMultisampleStateCreateInfo& setPSampleMask( const SampleMask* pSampleMask_ ) { pSampleMask = pSampleMask_; return *this; } PipelineMultisampleStateCreateInfo& setAlphaToCoverageEnable( Bool32 alphaToCoverageEnable_ ) { alphaToCoverageEnable = alphaToCoverageEnable_; return *this; } PipelineMultisampleStateCreateInfo& setAlphaToOneEnable( Bool32 alphaToOneEnable_ ) { alphaToOneEnable = alphaToOneEnable_; return *this; } operator const VkPipelineMultisampleStateCreateInfo&() const { return *reinterpret_cast(this); } bool operator==( PipelineMultisampleStateCreateInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( rasterizationSamples == rhs.rasterizationSamples ) && ( sampleShadingEnable == rhs.sampleShadingEnable ) && ( minSampleShading == rhs.minSampleShading ) && ( pSampleMask == rhs.pSampleMask ) && ( alphaToCoverageEnable == rhs.alphaToCoverageEnable ) && ( alphaToOneEnable == rhs.alphaToOneEnable ); } bool operator!=( PipelineMultisampleStateCreateInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; PipelineMultisampleStateCreateFlags flags; SampleCountFlagBits rasterizationSamples; Bool32 sampleShadingEnable; float minSampleShading; const SampleMask* pSampleMask; Bool32 alphaToCoverageEnable; Bool32 alphaToOneEnable; }; static_assert( sizeof( PipelineMultisampleStateCreateInfo ) == sizeof( VkPipelineMultisampleStateCreateInfo ), "struct and wrapper have different size!" ); struct GraphicsPipelineCreateInfo { GraphicsPipelineCreateInfo( PipelineCreateFlags flags_ = PipelineCreateFlags(), uint32_t stageCount_ = 0, const PipelineShaderStageCreateInfo* pStages_ = nullptr, const PipelineVertexInputStateCreateInfo* pVertexInputState_ = nullptr, const PipelineInputAssemblyStateCreateInfo* pInputAssemblyState_ = nullptr, const PipelineTessellationStateCreateInfo* pTessellationState_ = nullptr, const PipelineViewportStateCreateInfo* pViewportState_ = nullptr, const PipelineRasterizationStateCreateInfo* pRasterizationState_ = nullptr, const PipelineMultisampleStateCreateInfo* pMultisampleState_ = nullptr, const PipelineDepthStencilStateCreateInfo* pDepthStencilState_ = nullptr, const PipelineColorBlendStateCreateInfo* pColorBlendState_ = nullptr, const PipelineDynamicStateCreateInfo* pDynamicState_ = nullptr, PipelineLayout layout_ = PipelineLayout(), RenderPass renderPass_ = RenderPass(), uint32_t subpass_ = 0, Pipeline basePipelineHandle_ = Pipeline(), int32_t basePipelineIndex_ = 0 ) : sType( StructureType::eGraphicsPipelineCreateInfo ) , pNext( nullptr ) , flags( flags_ ) , stageCount( stageCount_ ) , pStages( pStages_ ) , pVertexInputState( pVertexInputState_ ) , pInputAssemblyState( pInputAssemblyState_ ) , pTessellationState( pTessellationState_ ) , pViewportState( pViewportState_ ) , pRasterizationState( pRasterizationState_ ) , pMultisampleState( pMultisampleState_ ) , pDepthStencilState( pDepthStencilState_ ) , pColorBlendState( pColorBlendState_ ) , pDynamicState( pDynamicState_ ) , layout( layout_ ) , renderPass( renderPass_ ) , subpass( subpass_ ) , basePipelineHandle( basePipelineHandle_ ) , basePipelineIndex( basePipelineIndex_ ) { } GraphicsPipelineCreateInfo( VkGraphicsPipelineCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(GraphicsPipelineCreateInfo) ); } GraphicsPipelineCreateInfo& operator=( VkGraphicsPipelineCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(GraphicsPipelineCreateInfo) ); return *this; } GraphicsPipelineCreateInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } GraphicsPipelineCreateInfo& setFlags( PipelineCreateFlags flags_ ) { flags = flags_; return *this; } GraphicsPipelineCreateInfo& setStageCount( uint32_t stageCount_ ) { stageCount = stageCount_; return *this; } GraphicsPipelineCreateInfo& setPStages( const PipelineShaderStageCreateInfo* pStages_ ) { pStages = pStages_; return *this; } GraphicsPipelineCreateInfo& setPVertexInputState( const PipelineVertexInputStateCreateInfo* pVertexInputState_ ) { pVertexInputState = pVertexInputState_; return *this; } GraphicsPipelineCreateInfo& setPInputAssemblyState( const PipelineInputAssemblyStateCreateInfo* pInputAssemblyState_ ) { pInputAssemblyState = pInputAssemblyState_; return *this; } GraphicsPipelineCreateInfo& setPTessellationState( const PipelineTessellationStateCreateInfo* pTessellationState_ ) { pTessellationState = pTessellationState_; return *this; } GraphicsPipelineCreateInfo& setPViewportState( const PipelineViewportStateCreateInfo* pViewportState_ ) { pViewportState = pViewportState_; return *this; } GraphicsPipelineCreateInfo& setPRasterizationState( const PipelineRasterizationStateCreateInfo* pRasterizationState_ ) { pRasterizationState = pRasterizationState_; return *this; } GraphicsPipelineCreateInfo& setPMultisampleState( const PipelineMultisampleStateCreateInfo* pMultisampleState_ ) { pMultisampleState = pMultisampleState_; return *this; } GraphicsPipelineCreateInfo& setPDepthStencilState( const PipelineDepthStencilStateCreateInfo* pDepthStencilState_ ) { pDepthStencilState = pDepthStencilState_; return *this; } GraphicsPipelineCreateInfo& setPColorBlendState( const PipelineColorBlendStateCreateInfo* pColorBlendState_ ) { pColorBlendState = pColorBlendState_; return *this; } GraphicsPipelineCreateInfo& setPDynamicState( const PipelineDynamicStateCreateInfo* pDynamicState_ ) { pDynamicState = pDynamicState_; return *this; } GraphicsPipelineCreateInfo& setLayout( PipelineLayout layout_ ) { layout = layout_; return *this; } GraphicsPipelineCreateInfo& setRenderPass( RenderPass renderPass_ ) { renderPass = renderPass_; return *this; } GraphicsPipelineCreateInfo& setSubpass( uint32_t subpass_ ) { subpass = subpass_; return *this; } GraphicsPipelineCreateInfo& setBasePipelineHandle( Pipeline basePipelineHandle_ ) { basePipelineHandle = basePipelineHandle_; return *this; } GraphicsPipelineCreateInfo& setBasePipelineIndex( int32_t basePipelineIndex_ ) { basePipelineIndex = basePipelineIndex_; return *this; } operator const VkGraphicsPipelineCreateInfo&() const { return *reinterpret_cast(this); } bool operator==( GraphicsPipelineCreateInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( stageCount == rhs.stageCount ) && ( pStages == rhs.pStages ) && ( pVertexInputState == rhs.pVertexInputState ) && ( pInputAssemblyState == rhs.pInputAssemblyState ) && ( pTessellationState == rhs.pTessellationState ) && ( pViewportState == rhs.pViewportState ) && ( pRasterizationState == rhs.pRasterizationState ) && ( pMultisampleState == rhs.pMultisampleState ) && ( pDepthStencilState == rhs.pDepthStencilState ) && ( pColorBlendState == rhs.pColorBlendState ) && ( pDynamicState == rhs.pDynamicState ) && ( layout == rhs.layout ) && ( renderPass == rhs.renderPass ) && ( subpass == rhs.subpass ) && ( basePipelineHandle == rhs.basePipelineHandle ) && ( basePipelineIndex == rhs.basePipelineIndex ); } bool operator!=( GraphicsPipelineCreateInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; PipelineCreateFlags flags; uint32_t stageCount; const PipelineShaderStageCreateInfo* pStages; const PipelineVertexInputStateCreateInfo* pVertexInputState; const PipelineInputAssemblyStateCreateInfo* pInputAssemblyState; const PipelineTessellationStateCreateInfo* pTessellationState; const PipelineViewportStateCreateInfo* pViewportState; const PipelineRasterizationStateCreateInfo* pRasterizationState; const PipelineMultisampleStateCreateInfo* pMultisampleState; const PipelineDepthStencilStateCreateInfo* pDepthStencilState; const PipelineColorBlendStateCreateInfo* pColorBlendState; const PipelineDynamicStateCreateInfo* pDynamicState; PipelineLayout layout; RenderPass renderPass; uint32_t subpass; Pipeline basePipelineHandle; int32_t basePipelineIndex; }; static_assert( sizeof( GraphicsPipelineCreateInfo ) == sizeof( VkGraphicsPipelineCreateInfo ), "struct and wrapper have different size!" ); struct PhysicalDeviceLimits { operator const VkPhysicalDeviceLimits&() const { return *reinterpret_cast(this); } bool operator==( PhysicalDeviceLimits const& rhs ) const { return ( maxImageDimension1D == rhs.maxImageDimension1D ) && ( maxImageDimension2D == rhs.maxImageDimension2D ) && ( maxImageDimension3D == rhs.maxImageDimension3D ) && ( maxImageDimensionCube == rhs.maxImageDimensionCube ) && ( maxImageArrayLayers == rhs.maxImageArrayLayers ) && ( maxTexelBufferElements == rhs.maxTexelBufferElements ) && ( maxUniformBufferRange == rhs.maxUniformBufferRange ) && ( maxStorageBufferRange == rhs.maxStorageBufferRange ) && ( maxPushConstantsSize == rhs.maxPushConstantsSize ) && ( maxMemoryAllocationCount == rhs.maxMemoryAllocationCount ) && ( maxSamplerAllocationCount == rhs.maxSamplerAllocationCount ) && ( bufferImageGranularity == rhs.bufferImageGranularity ) && ( sparseAddressSpaceSize == rhs.sparseAddressSpaceSize ) && ( maxBoundDescriptorSets == rhs.maxBoundDescriptorSets ) && ( maxPerStageDescriptorSamplers == rhs.maxPerStageDescriptorSamplers ) && ( maxPerStageDescriptorUniformBuffers == rhs.maxPerStageDescriptorUniformBuffers ) && ( maxPerStageDescriptorStorageBuffers == rhs.maxPerStageDescriptorStorageBuffers ) && ( maxPerStageDescriptorSampledImages == rhs.maxPerStageDescriptorSampledImages ) && ( maxPerStageDescriptorStorageImages == rhs.maxPerStageDescriptorStorageImages ) && ( maxPerStageDescriptorInputAttachments == rhs.maxPerStageDescriptorInputAttachments ) && ( maxPerStageResources == rhs.maxPerStageResources ) && ( maxDescriptorSetSamplers == rhs.maxDescriptorSetSamplers ) && ( maxDescriptorSetUniformBuffers == rhs.maxDescriptorSetUniformBuffers ) && ( maxDescriptorSetUniformBuffersDynamic == rhs.maxDescriptorSetUniformBuffersDynamic ) && ( maxDescriptorSetStorageBuffers == rhs.maxDescriptorSetStorageBuffers ) && ( maxDescriptorSetStorageBuffersDynamic == rhs.maxDescriptorSetStorageBuffersDynamic ) && ( maxDescriptorSetSampledImages == rhs.maxDescriptorSetSampledImages ) && ( maxDescriptorSetStorageImages == rhs.maxDescriptorSetStorageImages ) && ( maxDescriptorSetInputAttachments == rhs.maxDescriptorSetInputAttachments ) && ( maxVertexInputAttributes == rhs.maxVertexInputAttributes ) && ( maxVertexInputBindings == rhs.maxVertexInputBindings ) && ( maxVertexInputAttributeOffset == rhs.maxVertexInputAttributeOffset ) && ( maxVertexInputBindingStride == rhs.maxVertexInputBindingStride ) && ( maxVertexOutputComponents == rhs.maxVertexOutputComponents ) && ( maxTessellationGenerationLevel == rhs.maxTessellationGenerationLevel ) && ( maxTessellationPatchSize == rhs.maxTessellationPatchSize ) && ( maxTessellationControlPerVertexInputComponents == rhs.maxTessellationControlPerVertexInputComponents ) && ( maxTessellationControlPerVertexOutputComponents == rhs.maxTessellationControlPerVertexOutputComponents ) && ( maxTessellationControlPerPatchOutputComponents == rhs.maxTessellationControlPerPatchOutputComponents ) && ( maxTessellationControlTotalOutputComponents == rhs.maxTessellationControlTotalOutputComponents ) && ( maxTessellationEvaluationInputComponents == rhs.maxTessellationEvaluationInputComponents ) && ( maxTessellationEvaluationOutputComponents == rhs.maxTessellationEvaluationOutputComponents ) && ( maxGeometryShaderInvocations == rhs.maxGeometryShaderInvocations ) && ( maxGeometryInputComponents == rhs.maxGeometryInputComponents ) && ( maxGeometryOutputComponents == rhs.maxGeometryOutputComponents ) && ( maxGeometryOutputVertices == rhs.maxGeometryOutputVertices ) && ( maxGeometryTotalOutputComponents == rhs.maxGeometryTotalOutputComponents ) && ( maxFragmentInputComponents == rhs.maxFragmentInputComponents ) && ( maxFragmentOutputAttachments == rhs.maxFragmentOutputAttachments ) && ( maxFragmentDualSrcAttachments == rhs.maxFragmentDualSrcAttachments ) && ( maxFragmentCombinedOutputResources == rhs.maxFragmentCombinedOutputResources ) && ( maxComputeSharedMemorySize == rhs.maxComputeSharedMemorySize ) && ( memcmp( maxComputeWorkGroupCount, rhs.maxComputeWorkGroupCount, 3 * sizeof( uint32_t ) ) == 0 ) && ( maxComputeWorkGroupInvocations == rhs.maxComputeWorkGroupInvocations ) && ( memcmp( maxComputeWorkGroupSize, rhs.maxComputeWorkGroupSize, 3 * sizeof( uint32_t ) ) == 0 ) && ( subPixelPrecisionBits == rhs.subPixelPrecisionBits ) && ( subTexelPrecisionBits == rhs.subTexelPrecisionBits ) && ( mipmapPrecisionBits == rhs.mipmapPrecisionBits ) && ( maxDrawIndexedIndexValue == rhs.maxDrawIndexedIndexValue ) && ( maxDrawIndirectCount == rhs.maxDrawIndirectCount ) && ( maxSamplerLodBias == rhs.maxSamplerLodBias ) && ( maxSamplerAnisotropy == rhs.maxSamplerAnisotropy ) && ( maxViewports == rhs.maxViewports ) && ( memcmp( maxViewportDimensions, rhs.maxViewportDimensions, 2 * sizeof( uint32_t ) ) == 0 ) && ( memcmp( viewportBoundsRange, rhs.viewportBoundsRange, 2 * sizeof( float ) ) == 0 ) && ( viewportSubPixelBits == rhs.viewportSubPixelBits ) && ( minMemoryMapAlignment == rhs.minMemoryMapAlignment ) && ( minTexelBufferOffsetAlignment == rhs.minTexelBufferOffsetAlignment ) && ( minUniformBufferOffsetAlignment == rhs.minUniformBufferOffsetAlignment ) && ( minStorageBufferOffsetAlignment == rhs.minStorageBufferOffsetAlignment ) && ( minTexelOffset == rhs.minTexelOffset ) && ( maxTexelOffset == rhs.maxTexelOffset ) && ( minTexelGatherOffset == rhs.minTexelGatherOffset ) && ( maxTexelGatherOffset == rhs.maxTexelGatherOffset ) && ( minInterpolationOffset == rhs.minInterpolationOffset ) && ( maxInterpolationOffset == rhs.maxInterpolationOffset ) && ( subPixelInterpolationOffsetBits == rhs.subPixelInterpolationOffsetBits ) && ( maxFramebufferWidth == rhs.maxFramebufferWidth ) && ( maxFramebufferHeight == rhs.maxFramebufferHeight ) && ( maxFramebufferLayers == rhs.maxFramebufferLayers ) && ( framebufferColorSampleCounts == rhs.framebufferColorSampleCounts ) && ( framebufferDepthSampleCounts == rhs.framebufferDepthSampleCounts ) && ( framebufferStencilSampleCounts == rhs.framebufferStencilSampleCounts ) && ( framebufferNoAttachmentsSampleCounts == rhs.framebufferNoAttachmentsSampleCounts ) && ( maxColorAttachments == rhs.maxColorAttachments ) && ( sampledImageColorSampleCounts == rhs.sampledImageColorSampleCounts ) && ( sampledImageIntegerSampleCounts == rhs.sampledImageIntegerSampleCounts ) && ( sampledImageDepthSampleCounts == rhs.sampledImageDepthSampleCounts ) && ( sampledImageStencilSampleCounts == rhs.sampledImageStencilSampleCounts ) && ( storageImageSampleCounts == rhs.storageImageSampleCounts ) && ( maxSampleMaskWords == rhs.maxSampleMaskWords ) && ( timestampComputeAndGraphics == rhs.timestampComputeAndGraphics ) && ( timestampPeriod == rhs.timestampPeriod ) && ( maxClipDistances == rhs.maxClipDistances ) && ( maxCullDistances == rhs.maxCullDistances ) && ( maxCombinedClipAndCullDistances == rhs.maxCombinedClipAndCullDistances ) && ( discreteQueuePriorities == rhs.discreteQueuePriorities ) && ( memcmp( pointSizeRange, rhs.pointSizeRange, 2 * sizeof( float ) ) == 0 ) && ( memcmp( lineWidthRange, rhs.lineWidthRange, 2 * sizeof( float ) ) == 0 ) && ( pointSizeGranularity == rhs.pointSizeGranularity ) && ( lineWidthGranularity == rhs.lineWidthGranularity ) && ( strictLines == rhs.strictLines ) && ( standardSampleLocations == rhs.standardSampleLocations ) && ( optimalBufferCopyOffsetAlignment == rhs.optimalBufferCopyOffsetAlignment ) && ( optimalBufferCopyRowPitchAlignment == rhs.optimalBufferCopyRowPitchAlignment ) && ( nonCoherentAtomSize == rhs.nonCoherentAtomSize ); } bool operator!=( PhysicalDeviceLimits const& rhs ) const { return !operator==( rhs ); } uint32_t maxImageDimension1D; uint32_t maxImageDimension2D; uint32_t maxImageDimension3D; uint32_t maxImageDimensionCube; uint32_t maxImageArrayLayers; uint32_t maxTexelBufferElements; uint32_t maxUniformBufferRange; uint32_t maxStorageBufferRange; uint32_t maxPushConstantsSize; uint32_t maxMemoryAllocationCount; uint32_t maxSamplerAllocationCount; DeviceSize bufferImageGranularity; DeviceSize sparseAddressSpaceSize; uint32_t maxBoundDescriptorSets; uint32_t maxPerStageDescriptorSamplers; uint32_t maxPerStageDescriptorUniformBuffers; uint32_t maxPerStageDescriptorStorageBuffers; uint32_t maxPerStageDescriptorSampledImages; uint32_t maxPerStageDescriptorStorageImages; uint32_t maxPerStageDescriptorInputAttachments; uint32_t maxPerStageResources; uint32_t maxDescriptorSetSamplers; uint32_t maxDescriptorSetUniformBuffers; uint32_t maxDescriptorSetUniformBuffersDynamic; uint32_t maxDescriptorSetStorageBuffers; uint32_t maxDescriptorSetStorageBuffersDynamic; uint32_t maxDescriptorSetSampledImages; uint32_t maxDescriptorSetStorageImages; uint32_t maxDescriptorSetInputAttachments; uint32_t maxVertexInputAttributes; uint32_t maxVertexInputBindings; uint32_t maxVertexInputAttributeOffset; uint32_t maxVertexInputBindingStride; uint32_t maxVertexOutputComponents; uint32_t maxTessellationGenerationLevel; uint32_t maxTessellationPatchSize; uint32_t maxTessellationControlPerVertexInputComponents; uint32_t maxTessellationControlPerVertexOutputComponents; uint32_t maxTessellationControlPerPatchOutputComponents; uint32_t maxTessellationControlTotalOutputComponents; uint32_t maxTessellationEvaluationInputComponents; uint32_t maxTessellationEvaluationOutputComponents; uint32_t maxGeometryShaderInvocations; uint32_t maxGeometryInputComponents; uint32_t maxGeometryOutputComponents; uint32_t maxGeometryOutputVertices; uint32_t maxGeometryTotalOutputComponents; uint32_t maxFragmentInputComponents; uint32_t maxFragmentOutputAttachments; uint32_t maxFragmentDualSrcAttachments; uint32_t maxFragmentCombinedOutputResources; uint32_t maxComputeSharedMemorySize; uint32_t maxComputeWorkGroupCount[3]; uint32_t maxComputeWorkGroupInvocations; uint32_t maxComputeWorkGroupSize[3]; uint32_t subPixelPrecisionBits; uint32_t subTexelPrecisionBits; uint32_t mipmapPrecisionBits; uint32_t maxDrawIndexedIndexValue; uint32_t maxDrawIndirectCount; float maxSamplerLodBias; float maxSamplerAnisotropy; uint32_t maxViewports; uint32_t maxViewportDimensions[2]; float viewportBoundsRange[2]; uint32_t viewportSubPixelBits; size_t minMemoryMapAlignment; DeviceSize minTexelBufferOffsetAlignment; DeviceSize minUniformBufferOffsetAlignment; DeviceSize minStorageBufferOffsetAlignment; int32_t minTexelOffset; uint32_t maxTexelOffset; int32_t minTexelGatherOffset; uint32_t maxTexelGatherOffset; float minInterpolationOffset; float maxInterpolationOffset; uint32_t subPixelInterpolationOffsetBits; uint32_t maxFramebufferWidth; uint32_t maxFramebufferHeight; uint32_t maxFramebufferLayers; SampleCountFlags framebufferColorSampleCounts; SampleCountFlags framebufferDepthSampleCounts; SampleCountFlags framebufferStencilSampleCounts; SampleCountFlags framebufferNoAttachmentsSampleCounts; uint32_t maxColorAttachments; SampleCountFlags sampledImageColorSampleCounts; SampleCountFlags sampledImageIntegerSampleCounts; SampleCountFlags sampledImageDepthSampleCounts; SampleCountFlags sampledImageStencilSampleCounts; SampleCountFlags storageImageSampleCounts; uint32_t maxSampleMaskWords; Bool32 timestampComputeAndGraphics; float timestampPeriod; uint32_t maxClipDistances; uint32_t maxCullDistances; uint32_t maxCombinedClipAndCullDistances; uint32_t discreteQueuePriorities; float pointSizeRange[2]; float lineWidthRange[2]; float pointSizeGranularity; float lineWidthGranularity; Bool32 strictLines; Bool32 standardSampleLocations; DeviceSize optimalBufferCopyOffsetAlignment; DeviceSize optimalBufferCopyRowPitchAlignment; DeviceSize nonCoherentAtomSize; }; static_assert( sizeof( PhysicalDeviceLimits ) == sizeof( VkPhysicalDeviceLimits ), "struct and wrapper have different size!" ); struct PhysicalDeviceProperties { operator const VkPhysicalDeviceProperties&() const { return *reinterpret_cast(this); } bool operator==( PhysicalDeviceProperties const& rhs ) const { return ( apiVersion == rhs.apiVersion ) && ( driverVersion == rhs.driverVersion ) && ( vendorID == rhs.vendorID ) && ( deviceID == rhs.deviceID ) && ( deviceType == rhs.deviceType ) && ( memcmp( deviceName, rhs.deviceName, VK_MAX_PHYSICAL_DEVICE_NAME_SIZE * sizeof( char ) ) == 0 ) && ( memcmp( pipelineCacheUUID, rhs.pipelineCacheUUID, VK_UUID_SIZE * sizeof( uint8_t ) ) == 0 ) && ( limits == rhs.limits ) && ( sparseProperties == rhs.sparseProperties ); } bool operator!=( PhysicalDeviceProperties const& rhs ) const { return !operator==( rhs ); } uint32_t apiVersion; uint32_t driverVersion; uint32_t vendorID; uint32_t deviceID; PhysicalDeviceType deviceType; char deviceName[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE]; uint8_t pipelineCacheUUID[VK_UUID_SIZE]; PhysicalDeviceLimits limits; PhysicalDeviceSparseProperties sparseProperties; }; static_assert( sizeof( PhysicalDeviceProperties ) == sizeof( VkPhysicalDeviceProperties ), "struct and wrapper have different size!" ); struct PhysicalDeviceProperties2KHR { operator const VkPhysicalDeviceProperties2KHR&() const { return *reinterpret_cast(this); } bool operator==( PhysicalDeviceProperties2KHR const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( properties == rhs.properties ); } bool operator!=( PhysicalDeviceProperties2KHR const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: void* pNext; PhysicalDeviceProperties properties; }; static_assert( sizeof( PhysicalDeviceProperties2KHR ) == sizeof( VkPhysicalDeviceProperties2KHR ), "struct and wrapper have different size!" ); struct ImageFormatProperties2KHR { operator const VkImageFormatProperties2KHR&() const { return *reinterpret_cast(this); } bool operator==( ImageFormatProperties2KHR const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( imageFormatProperties == rhs.imageFormatProperties ); } bool operator!=( ImageFormatProperties2KHR const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: void* pNext; ImageFormatProperties imageFormatProperties; }; static_assert( sizeof( ImageFormatProperties2KHR ) == sizeof( VkImageFormatProperties2KHR ), "struct and wrapper have different size!" ); struct PhysicalDeviceSparseImageFormatInfo2KHR { PhysicalDeviceSparseImageFormatInfo2KHR( Format format_ = Format::eUndefined, ImageType type_ = ImageType::e1D, SampleCountFlagBits samples_ = SampleCountFlagBits::e1, ImageUsageFlags usage_ = ImageUsageFlags(), ImageTiling tiling_ = ImageTiling::eOptimal ) : sType( StructureType::ePhysicalDeviceSparseImageFormatInfo2KHR ) , pNext( nullptr ) , format( format_ ) , type( type_ ) , samples( samples_ ) , usage( usage_ ) , tiling( tiling_ ) { } PhysicalDeviceSparseImageFormatInfo2KHR( VkPhysicalDeviceSparseImageFormatInfo2KHR const & rhs ) { memcpy( this, &rhs, sizeof(PhysicalDeviceSparseImageFormatInfo2KHR) ); } PhysicalDeviceSparseImageFormatInfo2KHR& operator=( VkPhysicalDeviceSparseImageFormatInfo2KHR const & rhs ) { memcpy( this, &rhs, sizeof(PhysicalDeviceSparseImageFormatInfo2KHR) ); return *this; } PhysicalDeviceSparseImageFormatInfo2KHR& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } PhysicalDeviceSparseImageFormatInfo2KHR& setFormat( Format format_ ) { format = format_; return *this; } PhysicalDeviceSparseImageFormatInfo2KHR& setType( ImageType type_ ) { type = type_; return *this; } PhysicalDeviceSparseImageFormatInfo2KHR& setSamples( SampleCountFlagBits samples_ ) { samples = samples_; return *this; } PhysicalDeviceSparseImageFormatInfo2KHR& setUsage( ImageUsageFlags usage_ ) { usage = usage_; return *this; } PhysicalDeviceSparseImageFormatInfo2KHR& setTiling( ImageTiling tiling_ ) { tiling = tiling_; return *this; } operator const VkPhysicalDeviceSparseImageFormatInfo2KHR&() const { return *reinterpret_cast(this); } bool operator==( PhysicalDeviceSparseImageFormatInfo2KHR const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( format == rhs.format ) && ( type == rhs.type ) && ( samples == rhs.samples ) && ( usage == rhs.usage ) && ( tiling == rhs.tiling ); } bool operator!=( PhysicalDeviceSparseImageFormatInfo2KHR const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; Format format; ImageType type; SampleCountFlagBits samples; ImageUsageFlags usage; ImageTiling tiling; }; static_assert( sizeof( PhysicalDeviceSparseImageFormatInfo2KHR ) == sizeof( VkPhysicalDeviceSparseImageFormatInfo2KHR ), "struct and wrapper have different size!" ); enum class AttachmentDescriptionFlagBits { eMayAlias = VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT }; using AttachmentDescriptionFlags = Flags; VULKAN_HPP_INLINE AttachmentDescriptionFlags operator|( AttachmentDescriptionFlagBits bit0, AttachmentDescriptionFlagBits bit1 ) { return AttachmentDescriptionFlags( bit0 ) | bit1; } VULKAN_HPP_INLINE AttachmentDescriptionFlags operator~( AttachmentDescriptionFlagBits bits ) { return ~( AttachmentDescriptionFlags( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(AttachmentDescriptionFlagBits::eMayAlias) }; }; struct AttachmentDescription { AttachmentDescription( AttachmentDescriptionFlags flags_ = AttachmentDescriptionFlags(), Format format_ = Format::eUndefined, SampleCountFlagBits samples_ = SampleCountFlagBits::e1, AttachmentLoadOp loadOp_ = AttachmentLoadOp::eLoad, AttachmentStoreOp storeOp_ = AttachmentStoreOp::eStore, AttachmentLoadOp stencilLoadOp_ = AttachmentLoadOp::eLoad, AttachmentStoreOp stencilStoreOp_ = AttachmentStoreOp::eStore, ImageLayout initialLayout_ = ImageLayout::eUndefined, ImageLayout finalLayout_ = ImageLayout::eUndefined ) : flags( flags_ ) , format( format_ ) , samples( samples_ ) , loadOp( loadOp_ ) , storeOp( storeOp_ ) , stencilLoadOp( stencilLoadOp_ ) , stencilStoreOp( stencilStoreOp_ ) , initialLayout( initialLayout_ ) , finalLayout( finalLayout_ ) { } AttachmentDescription( VkAttachmentDescription const & rhs ) { memcpy( this, &rhs, sizeof(AttachmentDescription) ); } AttachmentDescription& operator=( VkAttachmentDescription const & rhs ) { memcpy( this, &rhs, sizeof(AttachmentDescription) ); return *this; } AttachmentDescription& setFlags( AttachmentDescriptionFlags flags_ ) { flags = flags_; return *this; } AttachmentDescription& setFormat( Format format_ ) { format = format_; return *this; } AttachmentDescription& setSamples( SampleCountFlagBits samples_ ) { samples = samples_; return *this; } AttachmentDescription& setLoadOp( AttachmentLoadOp loadOp_ ) { loadOp = loadOp_; return *this; } AttachmentDescription& setStoreOp( AttachmentStoreOp storeOp_ ) { storeOp = storeOp_; return *this; } AttachmentDescription& setStencilLoadOp( AttachmentLoadOp stencilLoadOp_ ) { stencilLoadOp = stencilLoadOp_; return *this; } AttachmentDescription& setStencilStoreOp( AttachmentStoreOp stencilStoreOp_ ) { stencilStoreOp = stencilStoreOp_; return *this; } AttachmentDescription& setInitialLayout( ImageLayout initialLayout_ ) { initialLayout = initialLayout_; return *this; } AttachmentDescription& setFinalLayout( ImageLayout finalLayout_ ) { finalLayout = finalLayout_; return *this; } operator const VkAttachmentDescription&() const { return *reinterpret_cast(this); } bool operator==( AttachmentDescription const& rhs ) const { return ( flags == rhs.flags ) && ( format == rhs.format ) && ( samples == rhs.samples ) && ( loadOp == rhs.loadOp ) && ( storeOp == rhs.storeOp ) && ( stencilLoadOp == rhs.stencilLoadOp ) && ( stencilStoreOp == rhs.stencilStoreOp ) && ( initialLayout == rhs.initialLayout ) && ( finalLayout == rhs.finalLayout ); } bool operator!=( AttachmentDescription const& rhs ) const { return !operator==( rhs ); } AttachmentDescriptionFlags flags; Format format; SampleCountFlagBits samples; AttachmentLoadOp loadOp; AttachmentStoreOp storeOp; AttachmentLoadOp stencilLoadOp; AttachmentStoreOp stencilStoreOp; ImageLayout initialLayout; ImageLayout finalLayout; }; static_assert( sizeof( AttachmentDescription ) == sizeof( VkAttachmentDescription ), "struct and wrapper have different size!" ); enum class StencilFaceFlagBits { eFront = VK_STENCIL_FACE_FRONT_BIT, eBack = VK_STENCIL_FACE_BACK_BIT, eVkStencilFrontAndBack = VK_STENCIL_FRONT_AND_BACK }; using StencilFaceFlags = Flags; VULKAN_HPP_INLINE StencilFaceFlags operator|( StencilFaceFlagBits bit0, StencilFaceFlagBits bit1 ) { return StencilFaceFlags( bit0 ) | bit1; } VULKAN_HPP_INLINE StencilFaceFlags operator~( StencilFaceFlagBits bits ) { return ~( StencilFaceFlags( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(StencilFaceFlagBits::eFront) | VkFlags(StencilFaceFlagBits::eBack) | VkFlags(StencilFaceFlagBits::eVkStencilFrontAndBack) }; }; enum class DescriptorPoolCreateFlagBits { eFreeDescriptorSet = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT }; using DescriptorPoolCreateFlags = Flags; VULKAN_HPP_INLINE DescriptorPoolCreateFlags operator|( DescriptorPoolCreateFlagBits bit0, DescriptorPoolCreateFlagBits bit1 ) { return DescriptorPoolCreateFlags( bit0 ) | bit1; } VULKAN_HPP_INLINE DescriptorPoolCreateFlags operator~( DescriptorPoolCreateFlagBits bits ) { return ~( DescriptorPoolCreateFlags( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(DescriptorPoolCreateFlagBits::eFreeDescriptorSet) }; }; struct DescriptorPoolCreateInfo { DescriptorPoolCreateInfo( DescriptorPoolCreateFlags flags_ = DescriptorPoolCreateFlags(), uint32_t maxSets_ = 0, uint32_t poolSizeCount_ = 0, const DescriptorPoolSize* pPoolSizes_ = nullptr ) : sType( StructureType::eDescriptorPoolCreateInfo ) , pNext( nullptr ) , flags( flags_ ) , maxSets( maxSets_ ) , poolSizeCount( poolSizeCount_ ) , pPoolSizes( pPoolSizes_ ) { } DescriptorPoolCreateInfo( VkDescriptorPoolCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(DescriptorPoolCreateInfo) ); } DescriptorPoolCreateInfo& operator=( VkDescriptorPoolCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(DescriptorPoolCreateInfo) ); return *this; } DescriptorPoolCreateInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } DescriptorPoolCreateInfo& setFlags( DescriptorPoolCreateFlags flags_ ) { flags = flags_; return *this; } DescriptorPoolCreateInfo& setMaxSets( uint32_t maxSets_ ) { maxSets = maxSets_; return *this; } DescriptorPoolCreateInfo& setPoolSizeCount( uint32_t poolSizeCount_ ) { poolSizeCount = poolSizeCount_; return *this; } DescriptorPoolCreateInfo& setPPoolSizes( const DescriptorPoolSize* pPoolSizes_ ) { pPoolSizes = pPoolSizes_; return *this; } operator const VkDescriptorPoolCreateInfo&() const { return *reinterpret_cast(this); } bool operator==( DescriptorPoolCreateInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( maxSets == rhs.maxSets ) && ( poolSizeCount == rhs.poolSizeCount ) && ( pPoolSizes == rhs.pPoolSizes ); } bool operator!=( DescriptorPoolCreateInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; DescriptorPoolCreateFlags flags; uint32_t maxSets; uint32_t poolSizeCount; const DescriptorPoolSize* pPoolSizes; }; static_assert( sizeof( DescriptorPoolCreateInfo ) == sizeof( VkDescriptorPoolCreateInfo ), "struct and wrapper have different size!" ); enum class DependencyFlagBits { eByRegion = VK_DEPENDENCY_BY_REGION_BIT, eViewLocalKHX = VK_DEPENDENCY_VIEW_LOCAL_BIT_KHX, eDeviceGroupKHX = VK_DEPENDENCY_DEVICE_GROUP_BIT_KHX }; using DependencyFlags = Flags; VULKAN_HPP_INLINE DependencyFlags operator|( DependencyFlagBits bit0, DependencyFlagBits bit1 ) { return DependencyFlags( bit0 ) | bit1; } VULKAN_HPP_INLINE DependencyFlags operator~( DependencyFlagBits bits ) { return ~( DependencyFlags( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(DependencyFlagBits::eByRegion) | VkFlags(DependencyFlagBits::eViewLocalKHX) | VkFlags(DependencyFlagBits::eDeviceGroupKHX) }; }; struct SubpassDependency { SubpassDependency( uint32_t srcSubpass_ = 0, uint32_t dstSubpass_ = 0, PipelineStageFlags srcStageMask_ = PipelineStageFlags(), PipelineStageFlags dstStageMask_ = PipelineStageFlags(), AccessFlags srcAccessMask_ = AccessFlags(), AccessFlags dstAccessMask_ = AccessFlags(), DependencyFlags dependencyFlags_ = DependencyFlags() ) : srcSubpass( srcSubpass_ ) , dstSubpass( dstSubpass_ ) , srcStageMask( srcStageMask_ ) , dstStageMask( dstStageMask_ ) , srcAccessMask( srcAccessMask_ ) , dstAccessMask( dstAccessMask_ ) , dependencyFlags( dependencyFlags_ ) { } SubpassDependency( VkSubpassDependency const & rhs ) { memcpy( this, &rhs, sizeof(SubpassDependency) ); } SubpassDependency& operator=( VkSubpassDependency const & rhs ) { memcpy( this, &rhs, sizeof(SubpassDependency) ); return *this; } SubpassDependency& setSrcSubpass( uint32_t srcSubpass_ ) { srcSubpass = srcSubpass_; return *this; } SubpassDependency& setDstSubpass( uint32_t dstSubpass_ ) { dstSubpass = dstSubpass_; return *this; } SubpassDependency& setSrcStageMask( PipelineStageFlags srcStageMask_ ) { srcStageMask = srcStageMask_; return *this; } SubpassDependency& setDstStageMask( PipelineStageFlags dstStageMask_ ) { dstStageMask = dstStageMask_; return *this; } SubpassDependency& setSrcAccessMask( AccessFlags srcAccessMask_ ) { srcAccessMask = srcAccessMask_; return *this; } SubpassDependency& setDstAccessMask( AccessFlags dstAccessMask_ ) { dstAccessMask = dstAccessMask_; return *this; } SubpassDependency& setDependencyFlags( DependencyFlags dependencyFlags_ ) { dependencyFlags = dependencyFlags_; return *this; } operator const VkSubpassDependency&() const { return *reinterpret_cast(this); } bool operator==( SubpassDependency const& rhs ) const { return ( srcSubpass == rhs.srcSubpass ) && ( dstSubpass == rhs.dstSubpass ) && ( srcStageMask == rhs.srcStageMask ) && ( dstStageMask == rhs.dstStageMask ) && ( srcAccessMask == rhs.srcAccessMask ) && ( dstAccessMask == rhs.dstAccessMask ) && ( dependencyFlags == rhs.dependencyFlags ); } bool operator!=( SubpassDependency const& rhs ) const { return !operator==( rhs ); } uint32_t srcSubpass; uint32_t dstSubpass; PipelineStageFlags srcStageMask; PipelineStageFlags dstStageMask; AccessFlags srcAccessMask; AccessFlags dstAccessMask; DependencyFlags dependencyFlags; }; static_assert( sizeof( SubpassDependency ) == sizeof( VkSubpassDependency ), "struct and wrapper have different size!" ); enum class PresentModeKHR { eImmediate = VK_PRESENT_MODE_IMMEDIATE_KHR, eMailbox = VK_PRESENT_MODE_MAILBOX_KHR, eFifo = VK_PRESENT_MODE_FIFO_KHR, eFifoRelaxed = VK_PRESENT_MODE_FIFO_RELAXED_KHR, eSharedDemandRefresh = VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR, eSharedContinuousRefresh = VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR }; enum class ColorSpaceKHR { eSrgbNonlinear = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, eDisplayP3NonlinearEXT = VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT, eExtendedSrgbLinearEXT = VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT, eDciP3LinearEXT = VK_COLOR_SPACE_DCI_P3_LINEAR_EXT, eDciP3NonlinearEXT = VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT, eBt709LinearEXT = VK_COLOR_SPACE_BT709_LINEAR_EXT, eBt709NonlinearEXT = VK_COLOR_SPACE_BT709_NONLINEAR_EXT, eBt2020LinearEXT = VK_COLOR_SPACE_BT2020_LINEAR_EXT, eHdr10St2084EXT = VK_COLOR_SPACE_HDR10_ST2084_EXT, eDolbyvisionEXT = VK_COLOR_SPACE_DOLBYVISION_EXT, eHdr10HlgEXT = VK_COLOR_SPACE_HDR10_HLG_EXT, eAdobergbLinearEXT = VK_COLOR_SPACE_ADOBERGB_LINEAR_EXT, eAdobergbNonlinearEXT = VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT, ePassThroughEXT = VK_COLOR_SPACE_PASS_THROUGH_EXT }; struct SurfaceFormatKHR { operator const VkSurfaceFormatKHR&() const { return *reinterpret_cast(this); } bool operator==( SurfaceFormatKHR const& rhs ) const { return ( format == rhs.format ) && ( colorSpace == rhs.colorSpace ); } bool operator!=( SurfaceFormatKHR const& rhs ) const { return !operator==( rhs ); } Format format; ColorSpaceKHR colorSpace; }; static_assert( sizeof( SurfaceFormatKHR ) == sizeof( VkSurfaceFormatKHR ), "struct and wrapper have different size!" ); struct SurfaceFormat2KHR { operator const VkSurfaceFormat2KHR&() const { return *reinterpret_cast(this); } bool operator==( SurfaceFormat2KHR const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( surfaceFormat == rhs.surfaceFormat ); } bool operator!=( SurfaceFormat2KHR const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: void* pNext; SurfaceFormatKHR surfaceFormat; }; static_assert( sizeof( SurfaceFormat2KHR ) == sizeof( VkSurfaceFormat2KHR ), "struct and wrapper have different size!" ); enum class DisplayPlaneAlphaFlagBitsKHR { eOpaque = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR, eGlobal = VK_DISPLAY_PLANE_ALPHA_GLOBAL_BIT_KHR, ePerPixel = VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_BIT_KHR, ePerPixelPremultiplied = VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_PREMULTIPLIED_BIT_KHR }; using DisplayPlaneAlphaFlagsKHR = Flags; VULKAN_HPP_INLINE DisplayPlaneAlphaFlagsKHR operator|( DisplayPlaneAlphaFlagBitsKHR bit0, DisplayPlaneAlphaFlagBitsKHR bit1 ) { return DisplayPlaneAlphaFlagsKHR( bit0 ) | bit1; } VULKAN_HPP_INLINE DisplayPlaneAlphaFlagsKHR operator~( DisplayPlaneAlphaFlagBitsKHR bits ) { return ~( DisplayPlaneAlphaFlagsKHR( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(DisplayPlaneAlphaFlagBitsKHR::eOpaque) | VkFlags(DisplayPlaneAlphaFlagBitsKHR::eGlobal) | VkFlags(DisplayPlaneAlphaFlagBitsKHR::ePerPixel) | VkFlags(DisplayPlaneAlphaFlagBitsKHR::ePerPixelPremultiplied) }; }; struct DisplayPlaneCapabilitiesKHR { operator const VkDisplayPlaneCapabilitiesKHR&() const { return *reinterpret_cast(this); } bool operator==( DisplayPlaneCapabilitiesKHR const& rhs ) const { return ( supportedAlpha == rhs.supportedAlpha ) && ( minSrcPosition == rhs.minSrcPosition ) && ( maxSrcPosition == rhs.maxSrcPosition ) && ( minSrcExtent == rhs.minSrcExtent ) && ( maxSrcExtent == rhs.maxSrcExtent ) && ( minDstPosition == rhs.minDstPosition ) && ( maxDstPosition == rhs.maxDstPosition ) && ( minDstExtent == rhs.minDstExtent ) && ( maxDstExtent == rhs.maxDstExtent ); } bool operator!=( DisplayPlaneCapabilitiesKHR const& rhs ) const { return !operator==( rhs ); } DisplayPlaneAlphaFlagsKHR supportedAlpha; Offset2D minSrcPosition; Offset2D maxSrcPosition; Extent2D minSrcExtent; Extent2D maxSrcExtent; Offset2D minDstPosition; Offset2D maxDstPosition; Extent2D minDstExtent; Extent2D maxDstExtent; }; static_assert( sizeof( DisplayPlaneCapabilitiesKHR ) == sizeof( VkDisplayPlaneCapabilitiesKHR ), "struct and wrapper have different size!" ); enum class CompositeAlphaFlagBitsKHR { eOpaque = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, ePreMultiplied = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR, ePostMultiplied = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR, eInherit = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR }; using CompositeAlphaFlagsKHR = Flags; VULKAN_HPP_INLINE CompositeAlphaFlagsKHR operator|( CompositeAlphaFlagBitsKHR bit0, CompositeAlphaFlagBitsKHR bit1 ) { return CompositeAlphaFlagsKHR( bit0 ) | bit1; } VULKAN_HPP_INLINE CompositeAlphaFlagsKHR operator~( CompositeAlphaFlagBitsKHR bits ) { return ~( CompositeAlphaFlagsKHR( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(CompositeAlphaFlagBitsKHR::eOpaque) | VkFlags(CompositeAlphaFlagBitsKHR::ePreMultiplied) | VkFlags(CompositeAlphaFlagBitsKHR::ePostMultiplied) | VkFlags(CompositeAlphaFlagBitsKHR::eInherit) }; }; enum class SurfaceTransformFlagBitsKHR { eIdentity = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR, eRotate90 = VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR, eRotate180 = VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR, eRotate270 = VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR, eHorizontalMirror = VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR, eHorizontalMirrorRotate90 = VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR, eHorizontalMirrorRotate180 = VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR, eHorizontalMirrorRotate270 = VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR, eInherit = VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR }; using SurfaceTransformFlagsKHR = Flags; VULKAN_HPP_INLINE SurfaceTransformFlagsKHR operator|( SurfaceTransformFlagBitsKHR bit0, SurfaceTransformFlagBitsKHR bit1 ) { return SurfaceTransformFlagsKHR( bit0 ) | bit1; } VULKAN_HPP_INLINE SurfaceTransformFlagsKHR operator~( SurfaceTransformFlagBitsKHR bits ) { return ~( SurfaceTransformFlagsKHR( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(SurfaceTransformFlagBitsKHR::eIdentity) | VkFlags(SurfaceTransformFlagBitsKHR::eRotate90) | VkFlags(SurfaceTransformFlagBitsKHR::eRotate180) | VkFlags(SurfaceTransformFlagBitsKHR::eRotate270) | VkFlags(SurfaceTransformFlagBitsKHR::eHorizontalMirror) | VkFlags(SurfaceTransformFlagBitsKHR::eHorizontalMirrorRotate90) | VkFlags(SurfaceTransformFlagBitsKHR::eHorizontalMirrorRotate180) | VkFlags(SurfaceTransformFlagBitsKHR::eHorizontalMirrorRotate270) | VkFlags(SurfaceTransformFlagBitsKHR::eInherit) }; }; struct DisplayPropertiesKHR { operator const VkDisplayPropertiesKHR&() const { return *reinterpret_cast(this); } bool operator==( DisplayPropertiesKHR const& rhs ) const { return ( display == rhs.display ) && ( displayName == rhs.displayName ) && ( physicalDimensions == rhs.physicalDimensions ) && ( physicalResolution == rhs.physicalResolution ) && ( supportedTransforms == rhs.supportedTransforms ) && ( planeReorderPossible == rhs.planeReorderPossible ) && ( persistentContent == rhs.persistentContent ); } bool operator!=( DisplayPropertiesKHR const& rhs ) const { return !operator==( rhs ); } DisplayKHR display; const char* displayName; Extent2D physicalDimensions; Extent2D physicalResolution; SurfaceTransformFlagsKHR supportedTransforms; Bool32 planeReorderPossible; Bool32 persistentContent; }; static_assert( sizeof( DisplayPropertiesKHR ) == sizeof( VkDisplayPropertiesKHR ), "struct and wrapper have different size!" ); struct DisplaySurfaceCreateInfoKHR { DisplaySurfaceCreateInfoKHR( DisplaySurfaceCreateFlagsKHR flags_ = DisplaySurfaceCreateFlagsKHR(), DisplayModeKHR displayMode_ = DisplayModeKHR(), uint32_t planeIndex_ = 0, uint32_t planeStackIndex_ = 0, SurfaceTransformFlagBitsKHR transform_ = SurfaceTransformFlagBitsKHR::eIdentity, float globalAlpha_ = 0, DisplayPlaneAlphaFlagBitsKHR alphaMode_ = DisplayPlaneAlphaFlagBitsKHR::eOpaque, Extent2D imageExtent_ = Extent2D() ) : sType( StructureType::eDisplaySurfaceCreateInfoKHR ) , pNext( nullptr ) , flags( flags_ ) , displayMode( displayMode_ ) , planeIndex( planeIndex_ ) , planeStackIndex( planeStackIndex_ ) , transform( transform_ ) , globalAlpha( globalAlpha_ ) , alphaMode( alphaMode_ ) , imageExtent( imageExtent_ ) { } DisplaySurfaceCreateInfoKHR( VkDisplaySurfaceCreateInfoKHR const & rhs ) { memcpy( this, &rhs, sizeof(DisplaySurfaceCreateInfoKHR) ); } DisplaySurfaceCreateInfoKHR& operator=( VkDisplaySurfaceCreateInfoKHR const & rhs ) { memcpy( this, &rhs, sizeof(DisplaySurfaceCreateInfoKHR) ); return *this; } DisplaySurfaceCreateInfoKHR& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } DisplaySurfaceCreateInfoKHR& setFlags( DisplaySurfaceCreateFlagsKHR flags_ ) { flags = flags_; return *this; } DisplaySurfaceCreateInfoKHR& setDisplayMode( DisplayModeKHR displayMode_ ) { displayMode = displayMode_; return *this; } DisplaySurfaceCreateInfoKHR& setPlaneIndex( uint32_t planeIndex_ ) { planeIndex = planeIndex_; return *this; } DisplaySurfaceCreateInfoKHR& setPlaneStackIndex( uint32_t planeStackIndex_ ) { planeStackIndex = planeStackIndex_; return *this; } DisplaySurfaceCreateInfoKHR& setTransform( SurfaceTransformFlagBitsKHR transform_ ) { transform = transform_; return *this; } DisplaySurfaceCreateInfoKHR& setGlobalAlpha( float globalAlpha_ ) { globalAlpha = globalAlpha_; return *this; } DisplaySurfaceCreateInfoKHR& setAlphaMode( DisplayPlaneAlphaFlagBitsKHR alphaMode_ ) { alphaMode = alphaMode_; return *this; } DisplaySurfaceCreateInfoKHR& setImageExtent( Extent2D imageExtent_ ) { imageExtent = imageExtent_; return *this; } operator const VkDisplaySurfaceCreateInfoKHR&() const { return *reinterpret_cast(this); } bool operator==( DisplaySurfaceCreateInfoKHR const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( displayMode == rhs.displayMode ) && ( planeIndex == rhs.planeIndex ) && ( planeStackIndex == rhs.planeStackIndex ) && ( transform == rhs.transform ) && ( globalAlpha == rhs.globalAlpha ) && ( alphaMode == rhs.alphaMode ) && ( imageExtent == rhs.imageExtent ); } bool operator!=( DisplaySurfaceCreateInfoKHR const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; DisplaySurfaceCreateFlagsKHR flags; DisplayModeKHR displayMode; uint32_t planeIndex; uint32_t planeStackIndex; SurfaceTransformFlagBitsKHR transform; float globalAlpha; DisplayPlaneAlphaFlagBitsKHR alphaMode; Extent2D imageExtent; }; static_assert( sizeof( DisplaySurfaceCreateInfoKHR ) == sizeof( VkDisplaySurfaceCreateInfoKHR ), "struct and wrapper have different size!" ); struct SurfaceCapabilitiesKHR { operator const VkSurfaceCapabilitiesKHR&() const { return *reinterpret_cast(this); } bool operator==( SurfaceCapabilitiesKHR const& rhs ) const { return ( minImageCount == rhs.minImageCount ) && ( maxImageCount == rhs.maxImageCount ) && ( currentExtent == rhs.currentExtent ) && ( minImageExtent == rhs.minImageExtent ) && ( maxImageExtent == rhs.maxImageExtent ) && ( maxImageArrayLayers == rhs.maxImageArrayLayers ) && ( supportedTransforms == rhs.supportedTransforms ) && ( currentTransform == rhs.currentTransform ) && ( supportedCompositeAlpha == rhs.supportedCompositeAlpha ) && ( supportedUsageFlags == rhs.supportedUsageFlags ); } bool operator!=( SurfaceCapabilitiesKHR const& rhs ) const { return !operator==( rhs ); } uint32_t minImageCount; uint32_t maxImageCount; Extent2D currentExtent; Extent2D minImageExtent; Extent2D maxImageExtent; uint32_t maxImageArrayLayers; SurfaceTransformFlagsKHR supportedTransforms; SurfaceTransformFlagBitsKHR currentTransform; CompositeAlphaFlagsKHR supportedCompositeAlpha; ImageUsageFlags supportedUsageFlags; }; static_assert( sizeof( SurfaceCapabilitiesKHR ) == sizeof( VkSurfaceCapabilitiesKHR ), "struct and wrapper have different size!" ); struct SurfaceCapabilities2KHR { operator const VkSurfaceCapabilities2KHR&() const { return *reinterpret_cast(this); } bool operator==( SurfaceCapabilities2KHR const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( surfaceCapabilities == rhs.surfaceCapabilities ); } bool operator!=( SurfaceCapabilities2KHR const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: void* pNext; SurfaceCapabilitiesKHR surfaceCapabilities; }; static_assert( sizeof( SurfaceCapabilities2KHR ) == sizeof( VkSurfaceCapabilities2KHR ), "struct and wrapper have different size!" ); enum class DebugReportFlagBitsEXT { eInformation = VK_DEBUG_REPORT_INFORMATION_BIT_EXT, eWarning = VK_DEBUG_REPORT_WARNING_BIT_EXT, ePerformanceWarning = VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT, eError = VK_DEBUG_REPORT_ERROR_BIT_EXT, eDebug = VK_DEBUG_REPORT_DEBUG_BIT_EXT }; using DebugReportFlagsEXT = Flags; VULKAN_HPP_INLINE DebugReportFlagsEXT operator|( DebugReportFlagBitsEXT bit0, DebugReportFlagBitsEXT bit1 ) { return DebugReportFlagsEXT( bit0 ) | bit1; } VULKAN_HPP_INLINE DebugReportFlagsEXT operator~( DebugReportFlagBitsEXT bits ) { return ~( DebugReportFlagsEXT( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(DebugReportFlagBitsEXT::eInformation) | VkFlags(DebugReportFlagBitsEXT::eWarning) | VkFlags(DebugReportFlagBitsEXT::ePerformanceWarning) | VkFlags(DebugReportFlagBitsEXT::eError) | VkFlags(DebugReportFlagBitsEXT::eDebug) }; }; struct DebugReportCallbackCreateInfoEXT { DebugReportCallbackCreateInfoEXT( DebugReportFlagsEXT flags_ = DebugReportFlagsEXT(), PFN_vkDebugReportCallbackEXT pfnCallback_ = nullptr, void* pUserData_ = nullptr ) : sType( StructureType::eDebugReportCallbackCreateInfoEXT ) , pNext( nullptr ) , flags( flags_ ) , pfnCallback( pfnCallback_ ) , pUserData( pUserData_ ) { } DebugReportCallbackCreateInfoEXT( VkDebugReportCallbackCreateInfoEXT const & rhs ) { memcpy( this, &rhs, sizeof(DebugReportCallbackCreateInfoEXT) ); } DebugReportCallbackCreateInfoEXT& operator=( VkDebugReportCallbackCreateInfoEXT const & rhs ) { memcpy( this, &rhs, sizeof(DebugReportCallbackCreateInfoEXT) ); return *this; } DebugReportCallbackCreateInfoEXT& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } DebugReportCallbackCreateInfoEXT& setFlags( DebugReportFlagsEXT flags_ ) { flags = flags_; return *this; } DebugReportCallbackCreateInfoEXT& setPfnCallback( PFN_vkDebugReportCallbackEXT pfnCallback_ ) { pfnCallback = pfnCallback_; return *this; } DebugReportCallbackCreateInfoEXT& setPUserData( void* pUserData_ ) { pUserData = pUserData_; return *this; } operator const VkDebugReportCallbackCreateInfoEXT&() const { return *reinterpret_cast(this); } bool operator==( DebugReportCallbackCreateInfoEXT const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( pfnCallback == rhs.pfnCallback ) && ( pUserData == rhs.pUserData ); } bool operator!=( DebugReportCallbackCreateInfoEXT const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; DebugReportFlagsEXT flags; PFN_vkDebugReportCallbackEXT pfnCallback; void* pUserData; }; static_assert( sizeof( DebugReportCallbackCreateInfoEXT ) == sizeof( VkDebugReportCallbackCreateInfoEXT ), "struct and wrapper have different size!" ); enum class DebugReportObjectTypeEXT { eUnknown = VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, eInstance = VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT, ePhysicalDevice = VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT, eDevice = VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT, eQueue = VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT, eSemaphore = VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT, eCommandBuffer = VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, eFence = VK_DEBUG_REPORT_OBJECT_TYPE_FENCE_EXT, eDeviceMemory = VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT, eBuffer = VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT, eImage = VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, eEvent = VK_DEBUG_REPORT_OBJECT_TYPE_EVENT_EXT, eQueryPool = VK_DEBUG_REPORT_OBJECT_TYPE_QUERY_POOL_EXT, eBufferView = VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_VIEW_EXT, eImageView = VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT, eShaderModule = VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT, ePipelineCache = VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_CACHE_EXT, ePipelineLayout = VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_LAYOUT_EXT, eRenderPass = VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT, ePipeline = VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT, eDescriptorSetLayout = VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT_EXT, eSampler = VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_EXT, eDescriptorPool = VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_POOL_EXT, eDescriptorSet = VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT, eFramebuffer = VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT, eCommandPool = VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT, eSurfaceKhr = VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT, eSwapchainKhr = VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT, eDebugReport = VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT, eDisplayKhr = VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_KHR_EXT, eDisplayModeKhr = VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_MODE_KHR_EXT, eObjectTableNvx = VK_DEBUG_REPORT_OBJECT_TYPE_OBJECT_TABLE_NVX_EXT, eIndirectCommandsLayoutNvx = VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT, eDescriptorUpdateTemplateKHR = VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_KHR_EXT }; struct DebugMarkerObjectNameInfoEXT { DebugMarkerObjectNameInfoEXT( DebugReportObjectTypeEXT objectType_ = DebugReportObjectTypeEXT::eUnknown, uint64_t object_ = 0, const char* pObjectName_ = nullptr ) : sType( StructureType::eDebugMarkerObjectNameInfoEXT ) , pNext( nullptr ) , objectType( objectType_ ) , object( object_ ) , pObjectName( pObjectName_ ) { } DebugMarkerObjectNameInfoEXT( VkDebugMarkerObjectNameInfoEXT const & rhs ) { memcpy( this, &rhs, sizeof(DebugMarkerObjectNameInfoEXT) ); } DebugMarkerObjectNameInfoEXT& operator=( VkDebugMarkerObjectNameInfoEXT const & rhs ) { memcpy( this, &rhs, sizeof(DebugMarkerObjectNameInfoEXT) ); return *this; } DebugMarkerObjectNameInfoEXT& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } DebugMarkerObjectNameInfoEXT& setObjectType( DebugReportObjectTypeEXT objectType_ ) { objectType = objectType_; return *this; } DebugMarkerObjectNameInfoEXT& setObject( uint64_t object_ ) { object = object_; return *this; } DebugMarkerObjectNameInfoEXT& setPObjectName( const char* pObjectName_ ) { pObjectName = pObjectName_; return *this; } operator const VkDebugMarkerObjectNameInfoEXT&() const { return *reinterpret_cast(this); } bool operator==( DebugMarkerObjectNameInfoEXT const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( objectType == rhs.objectType ) && ( object == rhs.object ) && ( pObjectName == rhs.pObjectName ); } bool operator!=( DebugMarkerObjectNameInfoEXT const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; DebugReportObjectTypeEXT objectType; uint64_t object; const char* pObjectName; }; static_assert( sizeof( DebugMarkerObjectNameInfoEXT ) == sizeof( VkDebugMarkerObjectNameInfoEXT ), "struct and wrapper have different size!" ); struct DebugMarkerObjectTagInfoEXT { DebugMarkerObjectTagInfoEXT( DebugReportObjectTypeEXT objectType_ = DebugReportObjectTypeEXT::eUnknown, uint64_t object_ = 0, uint64_t tagName_ = 0, size_t tagSize_ = 0, const void* pTag_ = nullptr ) : sType( StructureType::eDebugMarkerObjectTagInfoEXT ) , pNext( nullptr ) , objectType( objectType_ ) , object( object_ ) , tagName( tagName_ ) , tagSize( tagSize_ ) , pTag( pTag_ ) { } DebugMarkerObjectTagInfoEXT( VkDebugMarkerObjectTagInfoEXT const & rhs ) { memcpy( this, &rhs, sizeof(DebugMarkerObjectTagInfoEXT) ); } DebugMarkerObjectTagInfoEXT& operator=( VkDebugMarkerObjectTagInfoEXT const & rhs ) { memcpy( this, &rhs, sizeof(DebugMarkerObjectTagInfoEXT) ); return *this; } DebugMarkerObjectTagInfoEXT& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } DebugMarkerObjectTagInfoEXT& setObjectType( DebugReportObjectTypeEXT objectType_ ) { objectType = objectType_; return *this; } DebugMarkerObjectTagInfoEXT& setObject( uint64_t object_ ) { object = object_; return *this; } DebugMarkerObjectTagInfoEXT& setTagName( uint64_t tagName_ ) { tagName = tagName_; return *this; } DebugMarkerObjectTagInfoEXT& setTagSize( size_t tagSize_ ) { tagSize = tagSize_; return *this; } DebugMarkerObjectTagInfoEXT& setPTag( const void* pTag_ ) { pTag = pTag_; return *this; } operator const VkDebugMarkerObjectTagInfoEXT&() const { return *reinterpret_cast(this); } bool operator==( DebugMarkerObjectTagInfoEXT const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( objectType == rhs.objectType ) && ( object == rhs.object ) && ( tagName == rhs.tagName ) && ( tagSize == rhs.tagSize ) && ( pTag == rhs.pTag ); } bool operator!=( DebugMarkerObjectTagInfoEXT const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; DebugReportObjectTypeEXT objectType; uint64_t object; uint64_t tagName; size_t tagSize; const void* pTag; }; static_assert( sizeof( DebugMarkerObjectTagInfoEXT ) == sizeof( VkDebugMarkerObjectTagInfoEXT ), "struct and wrapper have different size!" ); enum class DebugReportErrorEXT { eNone = VK_DEBUG_REPORT_ERROR_NONE_EXT, eCallbackRef = VK_DEBUG_REPORT_ERROR_CALLBACK_REF_EXT }; enum class RasterizationOrderAMD { eStrict = VK_RASTERIZATION_ORDER_STRICT_AMD, eRelaxed = VK_RASTERIZATION_ORDER_RELAXED_AMD }; struct PipelineRasterizationStateRasterizationOrderAMD { PipelineRasterizationStateRasterizationOrderAMD( RasterizationOrderAMD rasterizationOrder_ = RasterizationOrderAMD::eStrict ) : sType( StructureType::ePipelineRasterizationStateRasterizationOrderAMD ) , pNext( nullptr ) , rasterizationOrder( rasterizationOrder_ ) { } PipelineRasterizationStateRasterizationOrderAMD( VkPipelineRasterizationStateRasterizationOrderAMD const & rhs ) { memcpy( this, &rhs, sizeof(PipelineRasterizationStateRasterizationOrderAMD) ); } PipelineRasterizationStateRasterizationOrderAMD& operator=( VkPipelineRasterizationStateRasterizationOrderAMD const & rhs ) { memcpy( this, &rhs, sizeof(PipelineRasterizationStateRasterizationOrderAMD) ); return *this; } PipelineRasterizationStateRasterizationOrderAMD& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } PipelineRasterizationStateRasterizationOrderAMD& setRasterizationOrder( RasterizationOrderAMD rasterizationOrder_ ) { rasterizationOrder = rasterizationOrder_; return *this; } operator const VkPipelineRasterizationStateRasterizationOrderAMD&() const { return *reinterpret_cast(this); } bool operator==( PipelineRasterizationStateRasterizationOrderAMD const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( rasterizationOrder == rhs.rasterizationOrder ); } bool operator!=( PipelineRasterizationStateRasterizationOrderAMD const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; RasterizationOrderAMD rasterizationOrder; }; static_assert( sizeof( PipelineRasterizationStateRasterizationOrderAMD ) == sizeof( VkPipelineRasterizationStateRasterizationOrderAMD ), "struct and wrapper have different size!" ); enum class ExternalMemoryHandleTypeFlagBitsNV { eOpaqueWin32 = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_NV, eOpaqueWin32Kmt = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_NV, eD3D11Image = VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_IMAGE_BIT_NV, eD3D11ImageKmt = VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_IMAGE_KMT_BIT_NV }; using ExternalMemoryHandleTypeFlagsNV = Flags; VULKAN_HPP_INLINE ExternalMemoryHandleTypeFlagsNV operator|( ExternalMemoryHandleTypeFlagBitsNV bit0, ExternalMemoryHandleTypeFlagBitsNV bit1 ) { return ExternalMemoryHandleTypeFlagsNV( bit0 ) | bit1; } VULKAN_HPP_INLINE ExternalMemoryHandleTypeFlagsNV operator~( ExternalMemoryHandleTypeFlagBitsNV bits ) { return ~( ExternalMemoryHandleTypeFlagsNV( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(ExternalMemoryHandleTypeFlagBitsNV::eOpaqueWin32) | VkFlags(ExternalMemoryHandleTypeFlagBitsNV::eOpaqueWin32Kmt) | VkFlags(ExternalMemoryHandleTypeFlagBitsNV::eD3D11Image) | VkFlags(ExternalMemoryHandleTypeFlagBitsNV::eD3D11ImageKmt) }; }; struct ExternalMemoryImageCreateInfoNV { ExternalMemoryImageCreateInfoNV( ExternalMemoryHandleTypeFlagsNV handleTypes_ = ExternalMemoryHandleTypeFlagsNV() ) : sType( StructureType::eExternalMemoryImageCreateInfoNV ) , pNext( nullptr ) , handleTypes( handleTypes_ ) { } ExternalMemoryImageCreateInfoNV( VkExternalMemoryImageCreateInfoNV const & rhs ) { memcpy( this, &rhs, sizeof(ExternalMemoryImageCreateInfoNV) ); } ExternalMemoryImageCreateInfoNV& operator=( VkExternalMemoryImageCreateInfoNV const & rhs ) { memcpy( this, &rhs, sizeof(ExternalMemoryImageCreateInfoNV) ); return *this; } ExternalMemoryImageCreateInfoNV& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } ExternalMemoryImageCreateInfoNV& setHandleTypes( ExternalMemoryHandleTypeFlagsNV handleTypes_ ) { handleTypes = handleTypes_; return *this; } operator const VkExternalMemoryImageCreateInfoNV&() const { return *reinterpret_cast(this); } bool operator==( ExternalMemoryImageCreateInfoNV const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( handleTypes == rhs.handleTypes ); } bool operator!=( ExternalMemoryImageCreateInfoNV const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; ExternalMemoryHandleTypeFlagsNV handleTypes; }; static_assert( sizeof( ExternalMemoryImageCreateInfoNV ) == sizeof( VkExternalMemoryImageCreateInfoNV ), "struct and wrapper have different size!" ); struct ExportMemoryAllocateInfoNV { ExportMemoryAllocateInfoNV( ExternalMemoryHandleTypeFlagsNV handleTypes_ = ExternalMemoryHandleTypeFlagsNV() ) : sType( StructureType::eExportMemoryAllocateInfoNV ) , pNext( nullptr ) , handleTypes( handleTypes_ ) { } ExportMemoryAllocateInfoNV( VkExportMemoryAllocateInfoNV const & rhs ) { memcpy( this, &rhs, sizeof(ExportMemoryAllocateInfoNV) ); } ExportMemoryAllocateInfoNV& operator=( VkExportMemoryAllocateInfoNV const & rhs ) { memcpy( this, &rhs, sizeof(ExportMemoryAllocateInfoNV) ); return *this; } ExportMemoryAllocateInfoNV& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } ExportMemoryAllocateInfoNV& setHandleTypes( ExternalMemoryHandleTypeFlagsNV handleTypes_ ) { handleTypes = handleTypes_; return *this; } operator const VkExportMemoryAllocateInfoNV&() const { return *reinterpret_cast(this); } bool operator==( ExportMemoryAllocateInfoNV const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( handleTypes == rhs.handleTypes ); } bool operator!=( ExportMemoryAllocateInfoNV const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; ExternalMemoryHandleTypeFlagsNV handleTypes; }; static_assert( sizeof( ExportMemoryAllocateInfoNV ) == sizeof( VkExportMemoryAllocateInfoNV ), "struct and wrapper have different size!" ); #ifdef VK_USE_PLATFORM_WIN32_KHR struct ImportMemoryWin32HandleInfoNV { ImportMemoryWin32HandleInfoNV( ExternalMemoryHandleTypeFlagsNV handleType_ = ExternalMemoryHandleTypeFlagsNV(), HANDLE handle_ = 0 ) : sType( StructureType::eImportMemoryWin32HandleInfoNV ) , pNext( nullptr ) , handleType( handleType_ ) , handle( handle_ ) { } ImportMemoryWin32HandleInfoNV( VkImportMemoryWin32HandleInfoNV const & rhs ) { memcpy( this, &rhs, sizeof(ImportMemoryWin32HandleInfoNV) ); } ImportMemoryWin32HandleInfoNV& operator=( VkImportMemoryWin32HandleInfoNV const & rhs ) { memcpy( this, &rhs, sizeof(ImportMemoryWin32HandleInfoNV) ); return *this; } ImportMemoryWin32HandleInfoNV& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } ImportMemoryWin32HandleInfoNV& setHandleType( ExternalMemoryHandleTypeFlagsNV handleType_ ) { handleType = handleType_; return *this; } ImportMemoryWin32HandleInfoNV& setHandle( HANDLE handle_ ) { handle = handle_; return *this; } operator const VkImportMemoryWin32HandleInfoNV&() const { return *reinterpret_cast(this); } bool operator==( ImportMemoryWin32HandleInfoNV const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( handleType == rhs.handleType ) && ( handle == rhs.handle ); } bool operator!=( ImportMemoryWin32HandleInfoNV const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; ExternalMemoryHandleTypeFlagsNV handleType; HANDLE handle; }; static_assert( sizeof( ImportMemoryWin32HandleInfoNV ) == sizeof( VkImportMemoryWin32HandleInfoNV ), "struct and wrapper have different size!" ); #endif /*VK_USE_PLATFORM_WIN32_KHR*/ enum class ExternalMemoryFeatureFlagBitsNV { eDedicatedOnly = VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_NV, eExportable = VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_NV, eImportable = VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_NV }; using ExternalMemoryFeatureFlagsNV = Flags; VULKAN_HPP_INLINE ExternalMemoryFeatureFlagsNV operator|( ExternalMemoryFeatureFlagBitsNV bit0, ExternalMemoryFeatureFlagBitsNV bit1 ) { return ExternalMemoryFeatureFlagsNV( bit0 ) | bit1; } VULKAN_HPP_INLINE ExternalMemoryFeatureFlagsNV operator~( ExternalMemoryFeatureFlagBitsNV bits ) { return ~( ExternalMemoryFeatureFlagsNV( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(ExternalMemoryFeatureFlagBitsNV::eDedicatedOnly) | VkFlags(ExternalMemoryFeatureFlagBitsNV::eExportable) | VkFlags(ExternalMemoryFeatureFlagBitsNV::eImportable) }; }; struct ExternalImageFormatPropertiesNV { operator const VkExternalImageFormatPropertiesNV&() const { return *reinterpret_cast(this); } bool operator==( ExternalImageFormatPropertiesNV const& rhs ) const { return ( imageFormatProperties == rhs.imageFormatProperties ) && ( externalMemoryFeatures == rhs.externalMemoryFeatures ) && ( exportFromImportedHandleTypes == rhs.exportFromImportedHandleTypes ) && ( compatibleHandleTypes == rhs.compatibleHandleTypes ); } bool operator!=( ExternalImageFormatPropertiesNV const& rhs ) const { return !operator==( rhs ); } ImageFormatProperties imageFormatProperties; ExternalMemoryFeatureFlagsNV externalMemoryFeatures; ExternalMemoryHandleTypeFlagsNV exportFromImportedHandleTypes; ExternalMemoryHandleTypeFlagsNV compatibleHandleTypes; }; static_assert( sizeof( ExternalImageFormatPropertiesNV ) == sizeof( VkExternalImageFormatPropertiesNV ), "struct and wrapper have different size!" ); enum class ValidationCheckEXT { eAll = VK_VALIDATION_CHECK_ALL_EXT }; struct ValidationFlagsEXT { ValidationFlagsEXT( uint32_t disabledValidationCheckCount_ = 0, ValidationCheckEXT* pDisabledValidationChecks_ = nullptr ) : sType( StructureType::eValidationFlagsEXT ) , pNext( nullptr ) , disabledValidationCheckCount( disabledValidationCheckCount_ ) , pDisabledValidationChecks( pDisabledValidationChecks_ ) { } ValidationFlagsEXT( VkValidationFlagsEXT const & rhs ) { memcpy( this, &rhs, sizeof(ValidationFlagsEXT) ); } ValidationFlagsEXT& operator=( VkValidationFlagsEXT const & rhs ) { memcpy( this, &rhs, sizeof(ValidationFlagsEXT) ); return *this; } ValidationFlagsEXT& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } ValidationFlagsEXT& setDisabledValidationCheckCount( uint32_t disabledValidationCheckCount_ ) { disabledValidationCheckCount = disabledValidationCheckCount_; return *this; } ValidationFlagsEXT& setPDisabledValidationChecks( ValidationCheckEXT* pDisabledValidationChecks_ ) { pDisabledValidationChecks = pDisabledValidationChecks_; return *this; } operator const VkValidationFlagsEXT&() const { return *reinterpret_cast(this); } bool operator==( ValidationFlagsEXT const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( disabledValidationCheckCount == rhs.disabledValidationCheckCount ) && ( pDisabledValidationChecks == rhs.pDisabledValidationChecks ); } bool operator!=( ValidationFlagsEXT const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; uint32_t disabledValidationCheckCount; ValidationCheckEXT* pDisabledValidationChecks; }; static_assert( sizeof( ValidationFlagsEXT ) == sizeof( VkValidationFlagsEXT ), "struct and wrapper have different size!" ); enum class IndirectCommandsLayoutUsageFlagBitsNVX { eUnorderedSequences = VK_INDIRECT_COMMANDS_LAYOUT_USAGE_UNORDERED_SEQUENCES_BIT_NVX, eSparseSequences = VK_INDIRECT_COMMANDS_LAYOUT_USAGE_SPARSE_SEQUENCES_BIT_NVX, eEmptyExecutions = VK_INDIRECT_COMMANDS_LAYOUT_USAGE_EMPTY_EXECUTIONS_BIT_NVX, eIndexedSequences = VK_INDIRECT_COMMANDS_LAYOUT_USAGE_INDEXED_SEQUENCES_BIT_NVX }; using IndirectCommandsLayoutUsageFlagsNVX = Flags; VULKAN_HPP_INLINE IndirectCommandsLayoutUsageFlagsNVX operator|( IndirectCommandsLayoutUsageFlagBitsNVX bit0, IndirectCommandsLayoutUsageFlagBitsNVX bit1 ) { return IndirectCommandsLayoutUsageFlagsNVX( bit0 ) | bit1; } VULKAN_HPP_INLINE IndirectCommandsLayoutUsageFlagsNVX operator~( IndirectCommandsLayoutUsageFlagBitsNVX bits ) { return ~( IndirectCommandsLayoutUsageFlagsNVX( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(IndirectCommandsLayoutUsageFlagBitsNVX::eUnorderedSequences) | VkFlags(IndirectCommandsLayoutUsageFlagBitsNVX::eSparseSequences) | VkFlags(IndirectCommandsLayoutUsageFlagBitsNVX::eEmptyExecutions) | VkFlags(IndirectCommandsLayoutUsageFlagBitsNVX::eIndexedSequences) }; }; enum class ObjectEntryUsageFlagBitsNVX { eGraphics = VK_OBJECT_ENTRY_USAGE_GRAPHICS_BIT_NVX, eCompute = VK_OBJECT_ENTRY_USAGE_COMPUTE_BIT_NVX }; using ObjectEntryUsageFlagsNVX = Flags; VULKAN_HPP_INLINE ObjectEntryUsageFlagsNVX operator|( ObjectEntryUsageFlagBitsNVX bit0, ObjectEntryUsageFlagBitsNVX bit1 ) { return ObjectEntryUsageFlagsNVX( bit0 ) | bit1; } VULKAN_HPP_INLINE ObjectEntryUsageFlagsNVX operator~( ObjectEntryUsageFlagBitsNVX bits ) { return ~( ObjectEntryUsageFlagsNVX( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(ObjectEntryUsageFlagBitsNVX::eGraphics) | VkFlags(ObjectEntryUsageFlagBitsNVX::eCompute) }; }; enum class IndirectCommandsTokenTypeNVX { eVkIndirectCommandsTokenPipeline = VK_INDIRECT_COMMANDS_TOKEN_PIPELINE_NVX, eVkIndirectCommandsTokenDescriptorSet = VK_INDIRECT_COMMANDS_TOKEN_DESCRIPTOR_SET_NVX, eVkIndirectCommandsTokenIndexBuffer = VK_INDIRECT_COMMANDS_TOKEN_INDEX_BUFFER_NVX, eVkIndirectCommandsTokenVertexBuffer = VK_INDIRECT_COMMANDS_TOKEN_VERTEX_BUFFER_NVX, eVkIndirectCommandsTokenPushConstant = VK_INDIRECT_COMMANDS_TOKEN_PUSH_CONSTANT_NVX, eVkIndirectCommandsTokenDrawIndexed = VK_INDIRECT_COMMANDS_TOKEN_DRAW_INDEXED_NVX, eVkIndirectCommandsTokenDraw = VK_INDIRECT_COMMANDS_TOKEN_DRAW_NVX, eVkIndirectCommandsTokenDispatch = VK_INDIRECT_COMMANDS_TOKEN_DISPATCH_NVX }; struct IndirectCommandsTokenNVX { IndirectCommandsTokenNVX( IndirectCommandsTokenTypeNVX tokenType_ = IndirectCommandsTokenTypeNVX::eVkIndirectCommandsTokenPipeline, Buffer buffer_ = Buffer(), DeviceSize offset_ = 0 ) : tokenType( tokenType_ ) , buffer( buffer_ ) , offset( offset_ ) { } IndirectCommandsTokenNVX( VkIndirectCommandsTokenNVX const & rhs ) { memcpy( this, &rhs, sizeof(IndirectCommandsTokenNVX) ); } IndirectCommandsTokenNVX& operator=( VkIndirectCommandsTokenNVX const & rhs ) { memcpy( this, &rhs, sizeof(IndirectCommandsTokenNVX) ); return *this; } IndirectCommandsTokenNVX& setTokenType( IndirectCommandsTokenTypeNVX tokenType_ ) { tokenType = tokenType_; return *this; } IndirectCommandsTokenNVX& setBuffer( Buffer buffer_ ) { buffer = buffer_; return *this; } IndirectCommandsTokenNVX& setOffset( DeviceSize offset_ ) { offset = offset_; return *this; } operator const VkIndirectCommandsTokenNVX&() const { return *reinterpret_cast(this); } bool operator==( IndirectCommandsTokenNVX const& rhs ) const { return ( tokenType == rhs.tokenType ) && ( buffer == rhs.buffer ) && ( offset == rhs.offset ); } bool operator!=( IndirectCommandsTokenNVX const& rhs ) const { return !operator==( rhs ); } IndirectCommandsTokenTypeNVX tokenType; Buffer buffer; DeviceSize offset; }; static_assert( sizeof( IndirectCommandsTokenNVX ) == sizeof( VkIndirectCommandsTokenNVX ), "struct and wrapper have different size!" ); struct IndirectCommandsLayoutTokenNVX { IndirectCommandsLayoutTokenNVX( IndirectCommandsTokenTypeNVX tokenType_ = IndirectCommandsTokenTypeNVX::eVkIndirectCommandsTokenPipeline, uint32_t bindingUnit_ = 0, uint32_t dynamicCount_ = 0, uint32_t divisor_ = 0 ) : tokenType( tokenType_ ) , bindingUnit( bindingUnit_ ) , dynamicCount( dynamicCount_ ) , divisor( divisor_ ) { } IndirectCommandsLayoutTokenNVX( VkIndirectCommandsLayoutTokenNVX const & rhs ) { memcpy( this, &rhs, sizeof(IndirectCommandsLayoutTokenNVX) ); } IndirectCommandsLayoutTokenNVX& operator=( VkIndirectCommandsLayoutTokenNVX const & rhs ) { memcpy( this, &rhs, sizeof(IndirectCommandsLayoutTokenNVX) ); return *this; } IndirectCommandsLayoutTokenNVX& setTokenType( IndirectCommandsTokenTypeNVX tokenType_ ) { tokenType = tokenType_; return *this; } IndirectCommandsLayoutTokenNVX& setBindingUnit( uint32_t bindingUnit_ ) { bindingUnit = bindingUnit_; return *this; } IndirectCommandsLayoutTokenNVX& setDynamicCount( uint32_t dynamicCount_ ) { dynamicCount = dynamicCount_; return *this; } IndirectCommandsLayoutTokenNVX& setDivisor( uint32_t divisor_ ) { divisor = divisor_; return *this; } operator const VkIndirectCommandsLayoutTokenNVX&() const { return *reinterpret_cast(this); } bool operator==( IndirectCommandsLayoutTokenNVX const& rhs ) const { return ( tokenType == rhs.tokenType ) && ( bindingUnit == rhs.bindingUnit ) && ( dynamicCount == rhs.dynamicCount ) && ( divisor == rhs.divisor ); } bool operator!=( IndirectCommandsLayoutTokenNVX const& rhs ) const { return !operator==( rhs ); } IndirectCommandsTokenTypeNVX tokenType; uint32_t bindingUnit; uint32_t dynamicCount; uint32_t divisor; }; static_assert( sizeof( IndirectCommandsLayoutTokenNVX ) == sizeof( VkIndirectCommandsLayoutTokenNVX ), "struct and wrapper have different size!" ); struct IndirectCommandsLayoutCreateInfoNVX { IndirectCommandsLayoutCreateInfoNVX( PipelineBindPoint pipelineBindPoint_ = PipelineBindPoint::eGraphics, IndirectCommandsLayoutUsageFlagsNVX flags_ = IndirectCommandsLayoutUsageFlagsNVX(), uint32_t tokenCount_ = 0, const IndirectCommandsLayoutTokenNVX* pTokens_ = nullptr ) : sType( StructureType::eIndirectCommandsLayoutCreateInfoNVX ) , pNext( nullptr ) , pipelineBindPoint( pipelineBindPoint_ ) , flags( flags_ ) , tokenCount( tokenCount_ ) , pTokens( pTokens_ ) { } IndirectCommandsLayoutCreateInfoNVX( VkIndirectCommandsLayoutCreateInfoNVX const & rhs ) { memcpy( this, &rhs, sizeof(IndirectCommandsLayoutCreateInfoNVX) ); } IndirectCommandsLayoutCreateInfoNVX& operator=( VkIndirectCommandsLayoutCreateInfoNVX const & rhs ) { memcpy( this, &rhs, sizeof(IndirectCommandsLayoutCreateInfoNVX) ); return *this; } IndirectCommandsLayoutCreateInfoNVX& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } IndirectCommandsLayoutCreateInfoNVX& setPipelineBindPoint( PipelineBindPoint pipelineBindPoint_ ) { pipelineBindPoint = pipelineBindPoint_; return *this; } IndirectCommandsLayoutCreateInfoNVX& setFlags( IndirectCommandsLayoutUsageFlagsNVX flags_ ) { flags = flags_; return *this; } IndirectCommandsLayoutCreateInfoNVX& setTokenCount( uint32_t tokenCount_ ) { tokenCount = tokenCount_; return *this; } IndirectCommandsLayoutCreateInfoNVX& setPTokens( const IndirectCommandsLayoutTokenNVX* pTokens_ ) { pTokens = pTokens_; return *this; } operator const VkIndirectCommandsLayoutCreateInfoNVX&() const { return *reinterpret_cast(this); } bool operator==( IndirectCommandsLayoutCreateInfoNVX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( pipelineBindPoint == rhs.pipelineBindPoint ) && ( flags == rhs.flags ) && ( tokenCount == rhs.tokenCount ) && ( pTokens == rhs.pTokens ); } bool operator!=( IndirectCommandsLayoutCreateInfoNVX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; PipelineBindPoint pipelineBindPoint; IndirectCommandsLayoutUsageFlagsNVX flags; uint32_t tokenCount; const IndirectCommandsLayoutTokenNVX* pTokens; }; static_assert( sizeof( IndirectCommandsLayoutCreateInfoNVX ) == sizeof( VkIndirectCommandsLayoutCreateInfoNVX ), "struct and wrapper have different size!" ); enum class ObjectEntryTypeNVX { eVkObjectEntryDescriptorSet = VK_OBJECT_ENTRY_DESCRIPTOR_SET_NVX, eVkObjectEntryPipeline = VK_OBJECT_ENTRY_PIPELINE_NVX, eVkObjectEntryIndexBuffer = VK_OBJECT_ENTRY_INDEX_BUFFER_NVX, eVkObjectEntryVertexBuffer = VK_OBJECT_ENTRY_VERTEX_BUFFER_NVX, eVkObjectEntryPushConstant = VK_OBJECT_ENTRY_PUSH_CONSTANT_NVX }; struct ObjectTableCreateInfoNVX { ObjectTableCreateInfoNVX( uint32_t objectCount_ = 0, const ObjectEntryTypeNVX* pObjectEntryTypes_ = nullptr, const uint32_t* pObjectEntryCounts_ = nullptr, const ObjectEntryUsageFlagsNVX* pObjectEntryUsageFlags_ = nullptr, uint32_t maxUniformBuffersPerDescriptor_ = 0, uint32_t maxStorageBuffersPerDescriptor_ = 0, uint32_t maxStorageImagesPerDescriptor_ = 0, uint32_t maxSampledImagesPerDescriptor_ = 0, uint32_t maxPipelineLayouts_ = 0 ) : sType( StructureType::eObjectTableCreateInfoNVX ) , pNext( nullptr ) , objectCount( objectCount_ ) , pObjectEntryTypes( pObjectEntryTypes_ ) , pObjectEntryCounts( pObjectEntryCounts_ ) , pObjectEntryUsageFlags( pObjectEntryUsageFlags_ ) , maxUniformBuffersPerDescriptor( maxUniformBuffersPerDescriptor_ ) , maxStorageBuffersPerDescriptor( maxStorageBuffersPerDescriptor_ ) , maxStorageImagesPerDescriptor( maxStorageImagesPerDescriptor_ ) , maxSampledImagesPerDescriptor( maxSampledImagesPerDescriptor_ ) , maxPipelineLayouts( maxPipelineLayouts_ ) { } ObjectTableCreateInfoNVX( VkObjectTableCreateInfoNVX const & rhs ) { memcpy( this, &rhs, sizeof(ObjectTableCreateInfoNVX) ); } ObjectTableCreateInfoNVX& operator=( VkObjectTableCreateInfoNVX const & rhs ) { memcpy( this, &rhs, sizeof(ObjectTableCreateInfoNVX) ); return *this; } ObjectTableCreateInfoNVX& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } ObjectTableCreateInfoNVX& setObjectCount( uint32_t objectCount_ ) { objectCount = objectCount_; return *this; } ObjectTableCreateInfoNVX& setPObjectEntryTypes( const ObjectEntryTypeNVX* pObjectEntryTypes_ ) { pObjectEntryTypes = pObjectEntryTypes_; return *this; } ObjectTableCreateInfoNVX& setPObjectEntryCounts( const uint32_t* pObjectEntryCounts_ ) { pObjectEntryCounts = pObjectEntryCounts_; return *this; } ObjectTableCreateInfoNVX& setPObjectEntryUsageFlags( const ObjectEntryUsageFlagsNVX* pObjectEntryUsageFlags_ ) { pObjectEntryUsageFlags = pObjectEntryUsageFlags_; return *this; } ObjectTableCreateInfoNVX& setMaxUniformBuffersPerDescriptor( uint32_t maxUniformBuffersPerDescriptor_ ) { maxUniformBuffersPerDescriptor = maxUniformBuffersPerDescriptor_; return *this; } ObjectTableCreateInfoNVX& setMaxStorageBuffersPerDescriptor( uint32_t maxStorageBuffersPerDescriptor_ ) { maxStorageBuffersPerDescriptor = maxStorageBuffersPerDescriptor_; return *this; } ObjectTableCreateInfoNVX& setMaxStorageImagesPerDescriptor( uint32_t maxStorageImagesPerDescriptor_ ) { maxStorageImagesPerDescriptor = maxStorageImagesPerDescriptor_; return *this; } ObjectTableCreateInfoNVX& setMaxSampledImagesPerDescriptor( uint32_t maxSampledImagesPerDescriptor_ ) { maxSampledImagesPerDescriptor = maxSampledImagesPerDescriptor_; return *this; } ObjectTableCreateInfoNVX& setMaxPipelineLayouts( uint32_t maxPipelineLayouts_ ) { maxPipelineLayouts = maxPipelineLayouts_; return *this; } operator const VkObjectTableCreateInfoNVX&() const { return *reinterpret_cast(this); } bool operator==( ObjectTableCreateInfoNVX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( objectCount == rhs.objectCount ) && ( pObjectEntryTypes == rhs.pObjectEntryTypes ) && ( pObjectEntryCounts == rhs.pObjectEntryCounts ) && ( pObjectEntryUsageFlags == rhs.pObjectEntryUsageFlags ) && ( maxUniformBuffersPerDescriptor == rhs.maxUniformBuffersPerDescriptor ) && ( maxStorageBuffersPerDescriptor == rhs.maxStorageBuffersPerDescriptor ) && ( maxStorageImagesPerDescriptor == rhs.maxStorageImagesPerDescriptor ) && ( maxSampledImagesPerDescriptor == rhs.maxSampledImagesPerDescriptor ) && ( maxPipelineLayouts == rhs.maxPipelineLayouts ); } bool operator!=( ObjectTableCreateInfoNVX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; uint32_t objectCount; const ObjectEntryTypeNVX* pObjectEntryTypes; const uint32_t* pObjectEntryCounts; const ObjectEntryUsageFlagsNVX* pObjectEntryUsageFlags; uint32_t maxUniformBuffersPerDescriptor; uint32_t maxStorageBuffersPerDescriptor; uint32_t maxStorageImagesPerDescriptor; uint32_t maxSampledImagesPerDescriptor; uint32_t maxPipelineLayouts; }; static_assert( sizeof( ObjectTableCreateInfoNVX ) == sizeof( VkObjectTableCreateInfoNVX ), "struct and wrapper have different size!" ); struct ObjectTableEntryNVX { ObjectTableEntryNVX( ObjectEntryTypeNVX type_ = ObjectEntryTypeNVX::eVkObjectEntryDescriptorSet, ObjectEntryUsageFlagsNVX flags_ = ObjectEntryUsageFlagsNVX() ) : type( type_ ) , flags( flags_ ) { } ObjectTableEntryNVX( VkObjectTableEntryNVX const & rhs ) { memcpy( this, &rhs, sizeof(ObjectTableEntryNVX) ); } ObjectTableEntryNVX& operator=( VkObjectTableEntryNVX const & rhs ) { memcpy( this, &rhs, sizeof(ObjectTableEntryNVX) ); return *this; } ObjectTableEntryNVX& setType( ObjectEntryTypeNVX type_ ) { type = type_; return *this; } ObjectTableEntryNVX& setFlags( ObjectEntryUsageFlagsNVX flags_ ) { flags = flags_; return *this; } operator const VkObjectTableEntryNVX&() const { return *reinterpret_cast(this); } bool operator==( ObjectTableEntryNVX const& rhs ) const { return ( type == rhs.type ) && ( flags == rhs.flags ); } bool operator!=( ObjectTableEntryNVX const& rhs ) const { return !operator==( rhs ); } ObjectEntryTypeNVX type; ObjectEntryUsageFlagsNVX flags; }; static_assert( sizeof( ObjectTableEntryNVX ) == sizeof( VkObjectTableEntryNVX ), "struct and wrapper have different size!" ); struct ObjectTablePipelineEntryNVX { ObjectTablePipelineEntryNVX( ObjectEntryTypeNVX type_ = ObjectEntryTypeNVX::eVkObjectEntryDescriptorSet, ObjectEntryUsageFlagsNVX flags_ = ObjectEntryUsageFlagsNVX(), Pipeline pipeline_ = Pipeline() ) : type( type_ ) , flags( flags_ ) , pipeline( pipeline_ ) { } ObjectTablePipelineEntryNVX( VkObjectTablePipelineEntryNVX const & rhs ) { memcpy( this, &rhs, sizeof(ObjectTablePipelineEntryNVX) ); } ObjectTablePipelineEntryNVX& operator=( VkObjectTablePipelineEntryNVX const & rhs ) { memcpy( this, &rhs, sizeof(ObjectTablePipelineEntryNVX) ); return *this; } ObjectTablePipelineEntryNVX& setType( ObjectEntryTypeNVX type_ ) { type = type_; return *this; } ObjectTablePipelineEntryNVX& setFlags( ObjectEntryUsageFlagsNVX flags_ ) { flags = flags_; return *this; } ObjectTablePipelineEntryNVX& setPipeline( Pipeline pipeline_ ) { pipeline = pipeline_; return *this; } operator const VkObjectTablePipelineEntryNVX&() const { return *reinterpret_cast(this); } bool operator==( ObjectTablePipelineEntryNVX const& rhs ) const { return ( type == rhs.type ) && ( flags == rhs.flags ) && ( pipeline == rhs.pipeline ); } bool operator!=( ObjectTablePipelineEntryNVX const& rhs ) const { return !operator==( rhs ); } ObjectEntryTypeNVX type; ObjectEntryUsageFlagsNVX flags; Pipeline pipeline; }; static_assert( sizeof( ObjectTablePipelineEntryNVX ) == sizeof( VkObjectTablePipelineEntryNVX ), "struct and wrapper have different size!" ); struct ObjectTableDescriptorSetEntryNVX { ObjectTableDescriptorSetEntryNVX( ObjectEntryTypeNVX type_ = ObjectEntryTypeNVX::eVkObjectEntryDescriptorSet, ObjectEntryUsageFlagsNVX flags_ = ObjectEntryUsageFlagsNVX(), PipelineLayout pipelineLayout_ = PipelineLayout(), DescriptorSet descriptorSet_ = DescriptorSet() ) : type( type_ ) , flags( flags_ ) , pipelineLayout( pipelineLayout_ ) , descriptorSet( descriptorSet_ ) { } ObjectTableDescriptorSetEntryNVX( VkObjectTableDescriptorSetEntryNVX const & rhs ) { memcpy( this, &rhs, sizeof(ObjectTableDescriptorSetEntryNVX) ); } ObjectTableDescriptorSetEntryNVX& operator=( VkObjectTableDescriptorSetEntryNVX const & rhs ) { memcpy( this, &rhs, sizeof(ObjectTableDescriptorSetEntryNVX) ); return *this; } ObjectTableDescriptorSetEntryNVX& setType( ObjectEntryTypeNVX type_ ) { type = type_; return *this; } ObjectTableDescriptorSetEntryNVX& setFlags( ObjectEntryUsageFlagsNVX flags_ ) { flags = flags_; return *this; } ObjectTableDescriptorSetEntryNVX& setPipelineLayout( PipelineLayout pipelineLayout_ ) { pipelineLayout = pipelineLayout_; return *this; } ObjectTableDescriptorSetEntryNVX& setDescriptorSet( DescriptorSet descriptorSet_ ) { descriptorSet = descriptorSet_; return *this; } operator const VkObjectTableDescriptorSetEntryNVX&() const { return *reinterpret_cast(this); } bool operator==( ObjectTableDescriptorSetEntryNVX const& rhs ) const { return ( type == rhs.type ) && ( flags == rhs.flags ) && ( pipelineLayout == rhs.pipelineLayout ) && ( descriptorSet == rhs.descriptorSet ); } bool operator!=( ObjectTableDescriptorSetEntryNVX const& rhs ) const { return !operator==( rhs ); } ObjectEntryTypeNVX type; ObjectEntryUsageFlagsNVX flags; PipelineLayout pipelineLayout; DescriptorSet descriptorSet; }; static_assert( sizeof( ObjectTableDescriptorSetEntryNVX ) == sizeof( VkObjectTableDescriptorSetEntryNVX ), "struct and wrapper have different size!" ); struct ObjectTableVertexBufferEntryNVX { ObjectTableVertexBufferEntryNVX( ObjectEntryTypeNVX type_ = ObjectEntryTypeNVX::eVkObjectEntryDescriptorSet, ObjectEntryUsageFlagsNVX flags_ = ObjectEntryUsageFlagsNVX(), Buffer buffer_ = Buffer() ) : type( type_ ) , flags( flags_ ) , buffer( buffer_ ) { } ObjectTableVertexBufferEntryNVX( VkObjectTableVertexBufferEntryNVX const & rhs ) { memcpy( this, &rhs, sizeof(ObjectTableVertexBufferEntryNVX) ); } ObjectTableVertexBufferEntryNVX& operator=( VkObjectTableVertexBufferEntryNVX const & rhs ) { memcpy( this, &rhs, sizeof(ObjectTableVertexBufferEntryNVX) ); return *this; } ObjectTableVertexBufferEntryNVX& setType( ObjectEntryTypeNVX type_ ) { type = type_; return *this; } ObjectTableVertexBufferEntryNVX& setFlags( ObjectEntryUsageFlagsNVX flags_ ) { flags = flags_; return *this; } ObjectTableVertexBufferEntryNVX& setBuffer( Buffer buffer_ ) { buffer = buffer_; return *this; } operator const VkObjectTableVertexBufferEntryNVX&() const { return *reinterpret_cast(this); } bool operator==( ObjectTableVertexBufferEntryNVX const& rhs ) const { return ( type == rhs.type ) && ( flags == rhs.flags ) && ( buffer == rhs.buffer ); } bool operator!=( ObjectTableVertexBufferEntryNVX const& rhs ) const { return !operator==( rhs ); } ObjectEntryTypeNVX type; ObjectEntryUsageFlagsNVX flags; Buffer buffer; }; static_assert( sizeof( ObjectTableVertexBufferEntryNVX ) == sizeof( VkObjectTableVertexBufferEntryNVX ), "struct and wrapper have different size!" ); struct ObjectTableIndexBufferEntryNVX { ObjectTableIndexBufferEntryNVX( ObjectEntryTypeNVX type_ = ObjectEntryTypeNVX::eVkObjectEntryDescriptorSet, ObjectEntryUsageFlagsNVX flags_ = ObjectEntryUsageFlagsNVX(), Buffer buffer_ = Buffer(), IndexType indexType_ = IndexType::eUint16 ) : type( type_ ) , flags( flags_ ) , buffer( buffer_ ) , indexType( indexType_ ) { } ObjectTableIndexBufferEntryNVX( VkObjectTableIndexBufferEntryNVX const & rhs ) { memcpy( this, &rhs, sizeof(ObjectTableIndexBufferEntryNVX) ); } ObjectTableIndexBufferEntryNVX& operator=( VkObjectTableIndexBufferEntryNVX const & rhs ) { memcpy( this, &rhs, sizeof(ObjectTableIndexBufferEntryNVX) ); return *this; } ObjectTableIndexBufferEntryNVX& setType( ObjectEntryTypeNVX type_ ) { type = type_; return *this; } ObjectTableIndexBufferEntryNVX& setFlags( ObjectEntryUsageFlagsNVX flags_ ) { flags = flags_; return *this; } ObjectTableIndexBufferEntryNVX& setBuffer( Buffer buffer_ ) { buffer = buffer_; return *this; } ObjectTableIndexBufferEntryNVX& setIndexType( IndexType indexType_ ) { indexType = indexType_; return *this; } operator const VkObjectTableIndexBufferEntryNVX&() const { return *reinterpret_cast(this); } bool operator==( ObjectTableIndexBufferEntryNVX const& rhs ) const { return ( type == rhs.type ) && ( flags == rhs.flags ) && ( buffer == rhs.buffer ) && ( indexType == rhs.indexType ); } bool operator!=( ObjectTableIndexBufferEntryNVX const& rhs ) const { return !operator==( rhs ); } ObjectEntryTypeNVX type; ObjectEntryUsageFlagsNVX flags; Buffer buffer; IndexType indexType; }; static_assert( sizeof( ObjectTableIndexBufferEntryNVX ) == sizeof( VkObjectTableIndexBufferEntryNVX ), "struct and wrapper have different size!" ); struct ObjectTablePushConstantEntryNVX { ObjectTablePushConstantEntryNVX( ObjectEntryTypeNVX type_ = ObjectEntryTypeNVX::eVkObjectEntryDescriptorSet, ObjectEntryUsageFlagsNVX flags_ = ObjectEntryUsageFlagsNVX(), PipelineLayout pipelineLayout_ = PipelineLayout(), ShaderStageFlags stageFlags_ = ShaderStageFlags() ) : type( type_ ) , flags( flags_ ) , pipelineLayout( pipelineLayout_ ) , stageFlags( stageFlags_ ) { } ObjectTablePushConstantEntryNVX( VkObjectTablePushConstantEntryNVX const & rhs ) { memcpy( this, &rhs, sizeof(ObjectTablePushConstantEntryNVX) ); } ObjectTablePushConstantEntryNVX& operator=( VkObjectTablePushConstantEntryNVX const & rhs ) { memcpy( this, &rhs, sizeof(ObjectTablePushConstantEntryNVX) ); return *this; } ObjectTablePushConstantEntryNVX& setType( ObjectEntryTypeNVX type_ ) { type = type_; return *this; } ObjectTablePushConstantEntryNVX& setFlags( ObjectEntryUsageFlagsNVX flags_ ) { flags = flags_; return *this; } ObjectTablePushConstantEntryNVX& setPipelineLayout( PipelineLayout pipelineLayout_ ) { pipelineLayout = pipelineLayout_; return *this; } ObjectTablePushConstantEntryNVX& setStageFlags( ShaderStageFlags stageFlags_ ) { stageFlags = stageFlags_; return *this; } operator const VkObjectTablePushConstantEntryNVX&() const { return *reinterpret_cast(this); } bool operator==( ObjectTablePushConstantEntryNVX const& rhs ) const { return ( type == rhs.type ) && ( flags == rhs.flags ) && ( pipelineLayout == rhs.pipelineLayout ) && ( stageFlags == rhs.stageFlags ); } bool operator!=( ObjectTablePushConstantEntryNVX const& rhs ) const { return !operator==( rhs ); } ObjectEntryTypeNVX type; ObjectEntryUsageFlagsNVX flags; PipelineLayout pipelineLayout; ShaderStageFlags stageFlags; }; static_assert( sizeof( ObjectTablePushConstantEntryNVX ) == sizeof( VkObjectTablePushConstantEntryNVX ), "struct and wrapper have different size!" ); enum class DescriptorSetLayoutCreateFlagBits { ePushDescriptorKHR = VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR }; using DescriptorSetLayoutCreateFlags = Flags; VULKAN_HPP_INLINE DescriptorSetLayoutCreateFlags operator|( DescriptorSetLayoutCreateFlagBits bit0, DescriptorSetLayoutCreateFlagBits bit1 ) { return DescriptorSetLayoutCreateFlags( bit0 ) | bit1; } VULKAN_HPP_INLINE DescriptorSetLayoutCreateFlags operator~( DescriptorSetLayoutCreateFlagBits bits ) { return ~( DescriptorSetLayoutCreateFlags( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(DescriptorSetLayoutCreateFlagBits::ePushDescriptorKHR) }; }; struct DescriptorSetLayoutCreateInfo { DescriptorSetLayoutCreateInfo( DescriptorSetLayoutCreateFlags flags_ = DescriptorSetLayoutCreateFlags(), uint32_t bindingCount_ = 0, const DescriptorSetLayoutBinding* pBindings_ = nullptr ) : sType( StructureType::eDescriptorSetLayoutCreateInfo ) , pNext( nullptr ) , flags( flags_ ) , bindingCount( bindingCount_ ) , pBindings( pBindings_ ) { } DescriptorSetLayoutCreateInfo( VkDescriptorSetLayoutCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(DescriptorSetLayoutCreateInfo) ); } DescriptorSetLayoutCreateInfo& operator=( VkDescriptorSetLayoutCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(DescriptorSetLayoutCreateInfo) ); return *this; } DescriptorSetLayoutCreateInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } DescriptorSetLayoutCreateInfo& setFlags( DescriptorSetLayoutCreateFlags flags_ ) { flags = flags_; return *this; } DescriptorSetLayoutCreateInfo& setBindingCount( uint32_t bindingCount_ ) { bindingCount = bindingCount_; return *this; } DescriptorSetLayoutCreateInfo& setPBindings( const DescriptorSetLayoutBinding* pBindings_ ) { pBindings = pBindings_; return *this; } operator const VkDescriptorSetLayoutCreateInfo&() const { return *reinterpret_cast(this); } bool operator==( DescriptorSetLayoutCreateInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( bindingCount == rhs.bindingCount ) && ( pBindings == rhs.pBindings ); } bool operator!=( DescriptorSetLayoutCreateInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; DescriptorSetLayoutCreateFlags flags; uint32_t bindingCount; const DescriptorSetLayoutBinding* pBindings; }; static_assert( sizeof( DescriptorSetLayoutCreateInfo ) == sizeof( VkDescriptorSetLayoutCreateInfo ), "struct and wrapper have different size!" ); enum class ExternalMemoryHandleTypeFlagBitsKHX { eOpaqueFd = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHX, eOpaqueWin32 = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHX, eOpaqueWin32Kmt = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHX, eD3D11Texture = VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT_KHX, eD3D11TextureKmt = VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_KMT_BIT_KHX, eD3D12Heap = VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_HEAP_BIT_KHX, eD3D12Resource = VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT_KHX }; using ExternalMemoryHandleTypeFlagsKHX = Flags; VULKAN_HPP_INLINE ExternalMemoryHandleTypeFlagsKHX operator|( ExternalMemoryHandleTypeFlagBitsKHX bit0, ExternalMemoryHandleTypeFlagBitsKHX bit1 ) { return ExternalMemoryHandleTypeFlagsKHX( bit0 ) | bit1; } VULKAN_HPP_INLINE ExternalMemoryHandleTypeFlagsKHX operator~( ExternalMemoryHandleTypeFlagBitsKHX bits ) { return ~( ExternalMemoryHandleTypeFlagsKHX( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(ExternalMemoryHandleTypeFlagBitsKHX::eOpaqueFd) | VkFlags(ExternalMemoryHandleTypeFlagBitsKHX::eOpaqueWin32) | VkFlags(ExternalMemoryHandleTypeFlagBitsKHX::eOpaqueWin32Kmt) | VkFlags(ExternalMemoryHandleTypeFlagBitsKHX::eD3D11Texture) | VkFlags(ExternalMemoryHandleTypeFlagBitsKHX::eD3D11TextureKmt) | VkFlags(ExternalMemoryHandleTypeFlagBitsKHX::eD3D12Heap) | VkFlags(ExternalMemoryHandleTypeFlagBitsKHX::eD3D12Resource) }; }; struct PhysicalDeviceExternalImageFormatInfoKHX { PhysicalDeviceExternalImageFormatInfoKHX( ExternalMemoryHandleTypeFlagBitsKHX handleType_ = ExternalMemoryHandleTypeFlagBitsKHX::eOpaqueFd ) : sType( StructureType::ePhysicalDeviceExternalImageFormatInfoKHX ) , pNext( nullptr ) , handleType( handleType_ ) { } PhysicalDeviceExternalImageFormatInfoKHX( VkPhysicalDeviceExternalImageFormatInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(PhysicalDeviceExternalImageFormatInfoKHX) ); } PhysicalDeviceExternalImageFormatInfoKHX& operator=( VkPhysicalDeviceExternalImageFormatInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(PhysicalDeviceExternalImageFormatInfoKHX) ); return *this; } PhysicalDeviceExternalImageFormatInfoKHX& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } PhysicalDeviceExternalImageFormatInfoKHX& setHandleType( ExternalMemoryHandleTypeFlagBitsKHX handleType_ ) { handleType = handleType_; return *this; } operator const VkPhysicalDeviceExternalImageFormatInfoKHX&() const { return *reinterpret_cast(this); } bool operator==( PhysicalDeviceExternalImageFormatInfoKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( handleType == rhs.handleType ); } bool operator!=( PhysicalDeviceExternalImageFormatInfoKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; ExternalMemoryHandleTypeFlagBitsKHX handleType; }; static_assert( sizeof( PhysicalDeviceExternalImageFormatInfoKHX ) == sizeof( VkPhysicalDeviceExternalImageFormatInfoKHX ), "struct and wrapper have different size!" ); struct PhysicalDeviceExternalBufferInfoKHX { PhysicalDeviceExternalBufferInfoKHX( BufferCreateFlags flags_ = BufferCreateFlags(), BufferUsageFlags usage_ = BufferUsageFlags(), ExternalMemoryHandleTypeFlagBitsKHX handleType_ = ExternalMemoryHandleTypeFlagBitsKHX::eOpaqueFd ) : sType( StructureType::ePhysicalDeviceExternalBufferInfoKHX ) , pNext( nullptr ) , flags( flags_ ) , usage( usage_ ) , handleType( handleType_ ) { } PhysicalDeviceExternalBufferInfoKHX( VkPhysicalDeviceExternalBufferInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(PhysicalDeviceExternalBufferInfoKHX) ); } PhysicalDeviceExternalBufferInfoKHX& operator=( VkPhysicalDeviceExternalBufferInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(PhysicalDeviceExternalBufferInfoKHX) ); return *this; } PhysicalDeviceExternalBufferInfoKHX& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } PhysicalDeviceExternalBufferInfoKHX& setFlags( BufferCreateFlags flags_ ) { flags = flags_; return *this; } PhysicalDeviceExternalBufferInfoKHX& setUsage( BufferUsageFlags usage_ ) { usage = usage_; return *this; } PhysicalDeviceExternalBufferInfoKHX& setHandleType( ExternalMemoryHandleTypeFlagBitsKHX handleType_ ) { handleType = handleType_; return *this; } operator const VkPhysicalDeviceExternalBufferInfoKHX&() const { return *reinterpret_cast(this); } bool operator==( PhysicalDeviceExternalBufferInfoKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( usage == rhs.usage ) && ( handleType == rhs.handleType ); } bool operator!=( PhysicalDeviceExternalBufferInfoKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; BufferCreateFlags flags; BufferUsageFlags usage; ExternalMemoryHandleTypeFlagBitsKHX handleType; }; static_assert( sizeof( PhysicalDeviceExternalBufferInfoKHX ) == sizeof( VkPhysicalDeviceExternalBufferInfoKHX ), "struct and wrapper have different size!" ); struct ExternalMemoryImageCreateInfoKHX { ExternalMemoryImageCreateInfoKHX( ExternalMemoryHandleTypeFlagsKHX handleTypes_ = ExternalMemoryHandleTypeFlagsKHX() ) : sType( StructureType::eExternalMemoryImageCreateInfoKHX ) , pNext( nullptr ) , handleTypes( handleTypes_ ) { } ExternalMemoryImageCreateInfoKHX( VkExternalMemoryImageCreateInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(ExternalMemoryImageCreateInfoKHX) ); } ExternalMemoryImageCreateInfoKHX& operator=( VkExternalMemoryImageCreateInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(ExternalMemoryImageCreateInfoKHX) ); return *this; } ExternalMemoryImageCreateInfoKHX& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } ExternalMemoryImageCreateInfoKHX& setHandleTypes( ExternalMemoryHandleTypeFlagsKHX handleTypes_ ) { handleTypes = handleTypes_; return *this; } operator const VkExternalMemoryImageCreateInfoKHX&() const { return *reinterpret_cast(this); } bool operator==( ExternalMemoryImageCreateInfoKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( handleTypes == rhs.handleTypes ); } bool operator!=( ExternalMemoryImageCreateInfoKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; ExternalMemoryHandleTypeFlagsKHX handleTypes; }; static_assert( sizeof( ExternalMemoryImageCreateInfoKHX ) == sizeof( VkExternalMemoryImageCreateInfoKHX ), "struct and wrapper have different size!" ); struct ExternalMemoryBufferCreateInfoKHX { ExternalMemoryBufferCreateInfoKHX( ExternalMemoryHandleTypeFlagsKHX handleTypes_ = ExternalMemoryHandleTypeFlagsKHX() ) : sType( StructureType::eExternalMemoryBufferCreateInfoKHX ) , pNext( nullptr ) , handleTypes( handleTypes_ ) { } ExternalMemoryBufferCreateInfoKHX( VkExternalMemoryBufferCreateInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(ExternalMemoryBufferCreateInfoKHX) ); } ExternalMemoryBufferCreateInfoKHX& operator=( VkExternalMemoryBufferCreateInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(ExternalMemoryBufferCreateInfoKHX) ); return *this; } ExternalMemoryBufferCreateInfoKHX& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } ExternalMemoryBufferCreateInfoKHX& setHandleTypes( ExternalMemoryHandleTypeFlagsKHX handleTypes_ ) { handleTypes = handleTypes_; return *this; } operator const VkExternalMemoryBufferCreateInfoKHX&() const { return *reinterpret_cast(this); } bool operator==( ExternalMemoryBufferCreateInfoKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( handleTypes == rhs.handleTypes ); } bool operator!=( ExternalMemoryBufferCreateInfoKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; ExternalMemoryHandleTypeFlagsKHX handleTypes; }; static_assert( sizeof( ExternalMemoryBufferCreateInfoKHX ) == sizeof( VkExternalMemoryBufferCreateInfoKHX ), "struct and wrapper have different size!" ); struct ExportMemoryAllocateInfoKHX { ExportMemoryAllocateInfoKHX( ExternalMemoryHandleTypeFlagsKHX handleTypes_ = ExternalMemoryHandleTypeFlagsKHX() ) : sType( StructureType::eExportMemoryAllocateInfoKHX ) , pNext( nullptr ) , handleTypes( handleTypes_ ) { } ExportMemoryAllocateInfoKHX( VkExportMemoryAllocateInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(ExportMemoryAllocateInfoKHX) ); } ExportMemoryAllocateInfoKHX& operator=( VkExportMemoryAllocateInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(ExportMemoryAllocateInfoKHX) ); return *this; } ExportMemoryAllocateInfoKHX& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } ExportMemoryAllocateInfoKHX& setHandleTypes( ExternalMemoryHandleTypeFlagsKHX handleTypes_ ) { handleTypes = handleTypes_; return *this; } operator const VkExportMemoryAllocateInfoKHX&() const { return *reinterpret_cast(this); } bool operator==( ExportMemoryAllocateInfoKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( handleTypes == rhs.handleTypes ); } bool operator!=( ExportMemoryAllocateInfoKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; ExternalMemoryHandleTypeFlagsKHX handleTypes; }; static_assert( sizeof( ExportMemoryAllocateInfoKHX ) == sizeof( VkExportMemoryAllocateInfoKHX ), "struct and wrapper have different size!" ); #ifdef VK_USE_PLATFORM_WIN32_KHX struct ImportMemoryWin32HandleInfoKHX { ImportMemoryWin32HandleInfoKHX( ExternalMemoryHandleTypeFlagBitsKHX handleType_ = ExternalMemoryHandleTypeFlagBitsKHX::eOpaqueFd, HANDLE handle_ = 0 ) : sType( StructureType::eImportMemoryWin32HandleInfoKHX ) , pNext( nullptr ) , handleType( handleType_ ) , handle( handle_ ) { } ImportMemoryWin32HandleInfoKHX( VkImportMemoryWin32HandleInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(ImportMemoryWin32HandleInfoKHX) ); } ImportMemoryWin32HandleInfoKHX& operator=( VkImportMemoryWin32HandleInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(ImportMemoryWin32HandleInfoKHX) ); return *this; } ImportMemoryWin32HandleInfoKHX& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } ImportMemoryWin32HandleInfoKHX& setHandleType( ExternalMemoryHandleTypeFlagBitsKHX handleType_ ) { handleType = handleType_; return *this; } ImportMemoryWin32HandleInfoKHX& setHandle( HANDLE handle_ ) { handle = handle_; return *this; } operator const VkImportMemoryWin32HandleInfoKHX&() const { return *reinterpret_cast(this); } bool operator==( ImportMemoryWin32HandleInfoKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( handleType == rhs.handleType ) && ( handle == rhs.handle ); } bool operator!=( ImportMemoryWin32HandleInfoKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; ExternalMemoryHandleTypeFlagBitsKHX handleType; HANDLE handle; }; static_assert( sizeof( ImportMemoryWin32HandleInfoKHX ) == sizeof( VkImportMemoryWin32HandleInfoKHX ), "struct and wrapper have different size!" ); #endif /*VK_USE_PLATFORM_WIN32_KHX*/ struct ImportMemoryFdInfoKHX { ImportMemoryFdInfoKHX( ExternalMemoryHandleTypeFlagBitsKHX handleType_ = ExternalMemoryHandleTypeFlagBitsKHX::eOpaqueFd, int fd_ = 0 ) : sType( StructureType::eImportMemoryFdInfoKHX ) , pNext( nullptr ) , handleType( handleType_ ) , fd( fd_ ) { } ImportMemoryFdInfoKHX( VkImportMemoryFdInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(ImportMemoryFdInfoKHX) ); } ImportMemoryFdInfoKHX& operator=( VkImportMemoryFdInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(ImportMemoryFdInfoKHX) ); return *this; } ImportMemoryFdInfoKHX& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } ImportMemoryFdInfoKHX& setHandleType( ExternalMemoryHandleTypeFlagBitsKHX handleType_ ) { handleType = handleType_; return *this; } ImportMemoryFdInfoKHX& setFd( int fd_ ) { fd = fd_; return *this; } operator const VkImportMemoryFdInfoKHX&() const { return *reinterpret_cast(this); } bool operator==( ImportMemoryFdInfoKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( handleType == rhs.handleType ) && ( fd == rhs.fd ); } bool operator!=( ImportMemoryFdInfoKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; ExternalMemoryHandleTypeFlagBitsKHX handleType; int fd; }; static_assert( sizeof( ImportMemoryFdInfoKHX ) == sizeof( VkImportMemoryFdInfoKHX ), "struct and wrapper have different size!" ); enum class ExternalMemoryFeatureFlagBitsKHX { eDedicatedOnly = VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_KHX, eExportable = VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_KHX, eImportable = VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHX }; using ExternalMemoryFeatureFlagsKHX = Flags; VULKAN_HPP_INLINE ExternalMemoryFeatureFlagsKHX operator|( ExternalMemoryFeatureFlagBitsKHX bit0, ExternalMemoryFeatureFlagBitsKHX bit1 ) { return ExternalMemoryFeatureFlagsKHX( bit0 ) | bit1; } VULKAN_HPP_INLINE ExternalMemoryFeatureFlagsKHX operator~( ExternalMemoryFeatureFlagBitsKHX bits ) { return ~( ExternalMemoryFeatureFlagsKHX( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(ExternalMemoryFeatureFlagBitsKHX::eDedicatedOnly) | VkFlags(ExternalMemoryFeatureFlagBitsKHX::eExportable) | VkFlags(ExternalMemoryFeatureFlagBitsKHX::eImportable) }; }; struct ExternalMemoryPropertiesKHX { operator const VkExternalMemoryPropertiesKHX&() const { return *reinterpret_cast(this); } bool operator==( ExternalMemoryPropertiesKHX const& rhs ) const { return ( externalMemoryFeatures == rhs.externalMemoryFeatures ) && ( exportFromImportedHandleTypes == rhs.exportFromImportedHandleTypes ) && ( compatibleHandleTypes == rhs.compatibleHandleTypes ); } bool operator!=( ExternalMemoryPropertiesKHX const& rhs ) const { return !operator==( rhs ); } ExternalMemoryFeatureFlagsKHX externalMemoryFeatures; ExternalMemoryHandleTypeFlagsKHX exportFromImportedHandleTypes; ExternalMemoryHandleTypeFlagsKHX compatibleHandleTypes; }; static_assert( sizeof( ExternalMemoryPropertiesKHX ) == sizeof( VkExternalMemoryPropertiesKHX ), "struct and wrapper have different size!" ); struct ExternalImageFormatPropertiesKHX { operator const VkExternalImageFormatPropertiesKHX&() const { return *reinterpret_cast(this); } bool operator==( ExternalImageFormatPropertiesKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( externalMemoryProperties == rhs.externalMemoryProperties ); } bool operator!=( ExternalImageFormatPropertiesKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: void* pNext; ExternalMemoryPropertiesKHX externalMemoryProperties; }; static_assert( sizeof( ExternalImageFormatPropertiesKHX ) == sizeof( VkExternalImageFormatPropertiesKHX ), "struct and wrapper have different size!" ); struct ExternalBufferPropertiesKHX { operator const VkExternalBufferPropertiesKHX&() const { return *reinterpret_cast(this); } bool operator==( ExternalBufferPropertiesKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( externalMemoryProperties == rhs.externalMemoryProperties ); } bool operator!=( ExternalBufferPropertiesKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: void* pNext; ExternalMemoryPropertiesKHX externalMemoryProperties; }; static_assert( sizeof( ExternalBufferPropertiesKHX ) == sizeof( VkExternalBufferPropertiesKHX ), "struct and wrapper have different size!" ); enum class ExternalSemaphoreHandleTypeFlagBitsKHX { eOpaqueFd = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHX, eOpaqueWin32 = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHX, eOpaqueWin32Kmt = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHX, eD3D12Fence = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT_KHX, eFenceFd = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FENCE_FD_BIT_KHX }; using ExternalSemaphoreHandleTypeFlagsKHX = Flags; VULKAN_HPP_INLINE ExternalSemaphoreHandleTypeFlagsKHX operator|( ExternalSemaphoreHandleTypeFlagBitsKHX bit0, ExternalSemaphoreHandleTypeFlagBitsKHX bit1 ) { return ExternalSemaphoreHandleTypeFlagsKHX( bit0 ) | bit1; } VULKAN_HPP_INLINE ExternalSemaphoreHandleTypeFlagsKHX operator~( ExternalSemaphoreHandleTypeFlagBitsKHX bits ) { return ~( ExternalSemaphoreHandleTypeFlagsKHX( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(ExternalSemaphoreHandleTypeFlagBitsKHX::eOpaqueFd) | VkFlags(ExternalSemaphoreHandleTypeFlagBitsKHX::eOpaqueWin32) | VkFlags(ExternalSemaphoreHandleTypeFlagBitsKHX::eOpaqueWin32Kmt) | VkFlags(ExternalSemaphoreHandleTypeFlagBitsKHX::eD3D12Fence) | VkFlags(ExternalSemaphoreHandleTypeFlagBitsKHX::eFenceFd) }; }; struct PhysicalDeviceExternalSemaphoreInfoKHX { PhysicalDeviceExternalSemaphoreInfoKHX( ExternalSemaphoreHandleTypeFlagBitsKHX handleType_ = ExternalSemaphoreHandleTypeFlagBitsKHX::eOpaqueFd ) : sType( StructureType::ePhysicalDeviceExternalSemaphoreInfoKHX ) , pNext( nullptr ) , handleType( handleType_ ) { } PhysicalDeviceExternalSemaphoreInfoKHX( VkPhysicalDeviceExternalSemaphoreInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(PhysicalDeviceExternalSemaphoreInfoKHX) ); } PhysicalDeviceExternalSemaphoreInfoKHX& operator=( VkPhysicalDeviceExternalSemaphoreInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(PhysicalDeviceExternalSemaphoreInfoKHX) ); return *this; } PhysicalDeviceExternalSemaphoreInfoKHX& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } PhysicalDeviceExternalSemaphoreInfoKHX& setHandleType( ExternalSemaphoreHandleTypeFlagBitsKHX handleType_ ) { handleType = handleType_; return *this; } operator const VkPhysicalDeviceExternalSemaphoreInfoKHX&() const { return *reinterpret_cast(this); } bool operator==( PhysicalDeviceExternalSemaphoreInfoKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( handleType == rhs.handleType ); } bool operator!=( PhysicalDeviceExternalSemaphoreInfoKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; ExternalSemaphoreHandleTypeFlagBitsKHX handleType; }; static_assert( sizeof( PhysicalDeviceExternalSemaphoreInfoKHX ) == sizeof( VkPhysicalDeviceExternalSemaphoreInfoKHX ), "struct and wrapper have different size!" ); struct ExportSemaphoreCreateInfoKHX { ExportSemaphoreCreateInfoKHX( ExternalSemaphoreHandleTypeFlagsKHX handleTypes_ = ExternalSemaphoreHandleTypeFlagsKHX() ) : sType( StructureType::eExportSemaphoreCreateInfoKHX ) , pNext( nullptr ) , handleTypes( handleTypes_ ) { } ExportSemaphoreCreateInfoKHX( VkExportSemaphoreCreateInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(ExportSemaphoreCreateInfoKHX) ); } ExportSemaphoreCreateInfoKHX& operator=( VkExportSemaphoreCreateInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(ExportSemaphoreCreateInfoKHX) ); return *this; } ExportSemaphoreCreateInfoKHX& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } ExportSemaphoreCreateInfoKHX& setHandleTypes( ExternalSemaphoreHandleTypeFlagsKHX handleTypes_ ) { handleTypes = handleTypes_; return *this; } operator const VkExportSemaphoreCreateInfoKHX&() const { return *reinterpret_cast(this); } bool operator==( ExportSemaphoreCreateInfoKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( handleTypes == rhs.handleTypes ); } bool operator!=( ExportSemaphoreCreateInfoKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; ExternalSemaphoreHandleTypeFlagsKHX handleTypes; }; static_assert( sizeof( ExportSemaphoreCreateInfoKHX ) == sizeof( VkExportSemaphoreCreateInfoKHX ), "struct and wrapper have different size!" ); #ifdef VK_USE_PLATFORM_WIN32_KHX struct ImportSemaphoreWin32HandleInfoKHX { ImportSemaphoreWin32HandleInfoKHX( Semaphore semaphore_ = Semaphore(), ExternalSemaphoreHandleTypeFlagsKHX handleType_ = ExternalSemaphoreHandleTypeFlagsKHX(), HANDLE handle_ = 0 ) : sType( StructureType::eImportSemaphoreWin32HandleInfoKHX ) , pNext( nullptr ) , semaphore( semaphore_ ) , handleType( handleType_ ) , handle( handle_ ) { } ImportSemaphoreWin32HandleInfoKHX( VkImportSemaphoreWin32HandleInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(ImportSemaphoreWin32HandleInfoKHX) ); } ImportSemaphoreWin32HandleInfoKHX& operator=( VkImportSemaphoreWin32HandleInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(ImportSemaphoreWin32HandleInfoKHX) ); return *this; } ImportSemaphoreWin32HandleInfoKHX& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } ImportSemaphoreWin32HandleInfoKHX& setSemaphore( Semaphore semaphore_ ) { semaphore = semaphore_; return *this; } ImportSemaphoreWin32HandleInfoKHX& setHandleType( ExternalSemaphoreHandleTypeFlagsKHX handleType_ ) { handleType = handleType_; return *this; } ImportSemaphoreWin32HandleInfoKHX& setHandle( HANDLE handle_ ) { handle = handle_; return *this; } operator const VkImportSemaphoreWin32HandleInfoKHX&() const { return *reinterpret_cast(this); } bool operator==( ImportSemaphoreWin32HandleInfoKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( semaphore == rhs.semaphore ) && ( handleType == rhs.handleType ) && ( handle == rhs.handle ); } bool operator!=( ImportSemaphoreWin32HandleInfoKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; Semaphore semaphore; ExternalSemaphoreHandleTypeFlagsKHX handleType; HANDLE handle; }; static_assert( sizeof( ImportSemaphoreWin32HandleInfoKHX ) == sizeof( VkImportSemaphoreWin32HandleInfoKHX ), "struct and wrapper have different size!" ); #endif /*VK_USE_PLATFORM_WIN32_KHX*/ struct ImportSemaphoreFdInfoKHX { ImportSemaphoreFdInfoKHX( Semaphore semaphore_ = Semaphore(), ExternalSemaphoreHandleTypeFlagBitsKHX handleType_ = ExternalSemaphoreHandleTypeFlagBitsKHX::eOpaqueFd, int fd_ = 0 ) : sType( StructureType::eImportSemaphoreFdInfoKHX ) , pNext( nullptr ) , semaphore( semaphore_ ) , handleType( handleType_ ) , fd( fd_ ) { } ImportSemaphoreFdInfoKHX( VkImportSemaphoreFdInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(ImportSemaphoreFdInfoKHX) ); } ImportSemaphoreFdInfoKHX& operator=( VkImportSemaphoreFdInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(ImportSemaphoreFdInfoKHX) ); return *this; } ImportSemaphoreFdInfoKHX& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } ImportSemaphoreFdInfoKHX& setSemaphore( Semaphore semaphore_ ) { semaphore = semaphore_; return *this; } ImportSemaphoreFdInfoKHX& setHandleType( ExternalSemaphoreHandleTypeFlagBitsKHX handleType_ ) { handleType = handleType_; return *this; } ImportSemaphoreFdInfoKHX& setFd( int fd_ ) { fd = fd_; return *this; } operator const VkImportSemaphoreFdInfoKHX&() const { return *reinterpret_cast(this); } bool operator==( ImportSemaphoreFdInfoKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( semaphore == rhs.semaphore ) && ( handleType == rhs.handleType ) && ( fd == rhs.fd ); } bool operator!=( ImportSemaphoreFdInfoKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; Semaphore semaphore; ExternalSemaphoreHandleTypeFlagBitsKHX handleType; int fd; }; static_assert( sizeof( ImportSemaphoreFdInfoKHX ) == sizeof( VkImportSemaphoreFdInfoKHX ), "struct and wrapper have different size!" ); enum class ExternalSemaphoreFeatureFlagBitsKHX { eExportable = VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT_KHX, eImportable = VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT_KHX }; using ExternalSemaphoreFeatureFlagsKHX = Flags; VULKAN_HPP_INLINE ExternalSemaphoreFeatureFlagsKHX operator|( ExternalSemaphoreFeatureFlagBitsKHX bit0, ExternalSemaphoreFeatureFlagBitsKHX bit1 ) { return ExternalSemaphoreFeatureFlagsKHX( bit0 ) | bit1; } VULKAN_HPP_INLINE ExternalSemaphoreFeatureFlagsKHX operator~( ExternalSemaphoreFeatureFlagBitsKHX bits ) { return ~( ExternalSemaphoreFeatureFlagsKHX( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(ExternalSemaphoreFeatureFlagBitsKHX::eExportable) | VkFlags(ExternalSemaphoreFeatureFlagBitsKHX::eImportable) }; }; struct ExternalSemaphorePropertiesKHX { operator const VkExternalSemaphorePropertiesKHX&() const { return *reinterpret_cast(this); } bool operator==( ExternalSemaphorePropertiesKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( exportFromImportedHandleTypes == rhs.exportFromImportedHandleTypes ) && ( compatibleHandleTypes == rhs.compatibleHandleTypes ) && ( externalSemaphoreFeatures == rhs.externalSemaphoreFeatures ); } bool operator!=( ExternalSemaphorePropertiesKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: void* pNext; ExternalSemaphoreHandleTypeFlagsKHX exportFromImportedHandleTypes; ExternalSemaphoreHandleTypeFlagsKHX compatibleHandleTypes; ExternalSemaphoreFeatureFlagsKHX externalSemaphoreFeatures; }; static_assert( sizeof( ExternalSemaphorePropertiesKHX ) == sizeof( VkExternalSemaphorePropertiesKHX ), "struct and wrapper have different size!" ); enum class SurfaceCounterFlagBitsEXT { eVblank = VK_SURFACE_COUNTER_VBLANK_EXT }; using SurfaceCounterFlagsEXT = Flags; VULKAN_HPP_INLINE SurfaceCounterFlagsEXT operator|( SurfaceCounterFlagBitsEXT bit0, SurfaceCounterFlagBitsEXT bit1 ) { return SurfaceCounterFlagsEXT( bit0 ) | bit1; } VULKAN_HPP_INLINE SurfaceCounterFlagsEXT operator~( SurfaceCounterFlagBitsEXT bits ) { return ~( SurfaceCounterFlagsEXT( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(SurfaceCounterFlagBitsEXT::eVblank) }; }; struct SurfaceCapabilities2EXT { operator const VkSurfaceCapabilities2EXT&() const { return *reinterpret_cast(this); } bool operator==( SurfaceCapabilities2EXT const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( minImageCount == rhs.minImageCount ) && ( maxImageCount == rhs.maxImageCount ) && ( currentExtent == rhs.currentExtent ) && ( minImageExtent == rhs.minImageExtent ) && ( maxImageExtent == rhs.maxImageExtent ) && ( maxImageArrayLayers == rhs.maxImageArrayLayers ) && ( supportedTransforms == rhs.supportedTransforms ) && ( currentTransform == rhs.currentTransform ) && ( supportedCompositeAlpha == rhs.supportedCompositeAlpha ) && ( supportedUsageFlags == rhs.supportedUsageFlags ) && ( supportedSurfaceCounters == rhs.supportedSurfaceCounters ); } bool operator!=( SurfaceCapabilities2EXT const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: void* pNext; uint32_t minImageCount; uint32_t maxImageCount; Extent2D currentExtent; Extent2D minImageExtent; Extent2D maxImageExtent; uint32_t maxImageArrayLayers; SurfaceTransformFlagsKHR supportedTransforms; SurfaceTransformFlagBitsKHR currentTransform; CompositeAlphaFlagsKHR supportedCompositeAlpha; ImageUsageFlags supportedUsageFlags; SurfaceCounterFlagsEXT supportedSurfaceCounters; }; static_assert( sizeof( SurfaceCapabilities2EXT ) == sizeof( VkSurfaceCapabilities2EXT ), "struct and wrapper have different size!" ); struct SwapchainCounterCreateInfoEXT { SwapchainCounterCreateInfoEXT( SurfaceCounterFlagsEXT surfaceCounters_ = SurfaceCounterFlagsEXT() ) : sType( StructureType::eSwapchainCounterCreateInfoEXT ) , pNext( nullptr ) , surfaceCounters( surfaceCounters_ ) { } SwapchainCounterCreateInfoEXT( VkSwapchainCounterCreateInfoEXT const & rhs ) { memcpy( this, &rhs, sizeof(SwapchainCounterCreateInfoEXT) ); } SwapchainCounterCreateInfoEXT& operator=( VkSwapchainCounterCreateInfoEXT const & rhs ) { memcpy( this, &rhs, sizeof(SwapchainCounterCreateInfoEXT) ); return *this; } SwapchainCounterCreateInfoEXT& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } SwapchainCounterCreateInfoEXT& setSurfaceCounters( SurfaceCounterFlagsEXT surfaceCounters_ ) { surfaceCounters = surfaceCounters_; return *this; } operator const VkSwapchainCounterCreateInfoEXT&() const { return *reinterpret_cast(this); } bool operator==( SwapchainCounterCreateInfoEXT const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( surfaceCounters == rhs.surfaceCounters ); } bool operator!=( SwapchainCounterCreateInfoEXT const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; SurfaceCounterFlagsEXT surfaceCounters; }; static_assert( sizeof( SwapchainCounterCreateInfoEXT ) == sizeof( VkSwapchainCounterCreateInfoEXT ), "struct and wrapper have different size!" ); enum class DisplayPowerStateEXT { eOff = VK_DISPLAY_POWER_STATE_OFF_EXT, eSuspend = VK_DISPLAY_POWER_STATE_SUSPEND_EXT, eOn = VK_DISPLAY_POWER_STATE_ON_EXT }; struct DisplayPowerInfoEXT { DisplayPowerInfoEXT( DisplayPowerStateEXT powerState_ = DisplayPowerStateEXT::eOff ) : sType( StructureType::eDisplayPowerInfoEXT ) , pNext( nullptr ) , powerState( powerState_ ) { } DisplayPowerInfoEXT( VkDisplayPowerInfoEXT const & rhs ) { memcpy( this, &rhs, sizeof(DisplayPowerInfoEXT) ); } DisplayPowerInfoEXT& operator=( VkDisplayPowerInfoEXT const & rhs ) { memcpy( this, &rhs, sizeof(DisplayPowerInfoEXT) ); return *this; } DisplayPowerInfoEXT& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } DisplayPowerInfoEXT& setPowerState( DisplayPowerStateEXT powerState_ ) { powerState = powerState_; return *this; } operator const VkDisplayPowerInfoEXT&() const { return *reinterpret_cast(this); } bool operator==( DisplayPowerInfoEXT const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( powerState == rhs.powerState ); } bool operator!=( DisplayPowerInfoEXT const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; DisplayPowerStateEXT powerState; }; static_assert( sizeof( DisplayPowerInfoEXT ) == sizeof( VkDisplayPowerInfoEXT ), "struct and wrapper have different size!" ); enum class DeviceEventTypeEXT { eDisplayHotplug = VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT }; struct DeviceEventInfoEXT { DeviceEventInfoEXT( DeviceEventTypeEXT deviceEvent_ = DeviceEventTypeEXT::eDisplayHotplug ) : sType( StructureType::eDeviceEventInfoEXT ) , pNext( nullptr ) , deviceEvent( deviceEvent_ ) { } DeviceEventInfoEXT( VkDeviceEventInfoEXT const & rhs ) { memcpy( this, &rhs, sizeof(DeviceEventInfoEXT) ); } DeviceEventInfoEXT& operator=( VkDeviceEventInfoEXT const & rhs ) { memcpy( this, &rhs, sizeof(DeviceEventInfoEXT) ); return *this; } DeviceEventInfoEXT& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } DeviceEventInfoEXT& setDeviceEvent( DeviceEventTypeEXT deviceEvent_ ) { deviceEvent = deviceEvent_; return *this; } operator const VkDeviceEventInfoEXT&() const { return *reinterpret_cast(this); } bool operator==( DeviceEventInfoEXT const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( deviceEvent == rhs.deviceEvent ); } bool operator!=( DeviceEventInfoEXT const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; DeviceEventTypeEXT deviceEvent; }; static_assert( sizeof( DeviceEventInfoEXT ) == sizeof( VkDeviceEventInfoEXT ), "struct and wrapper have different size!" ); enum class DisplayEventTypeEXT { eFirstPixelOut = VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT }; struct DisplayEventInfoEXT { DisplayEventInfoEXT( DisplayEventTypeEXT displayEvent_ = DisplayEventTypeEXT::eFirstPixelOut ) : sType( StructureType::eDisplayEventInfoEXT ) , pNext( nullptr ) , displayEvent( displayEvent_ ) { } DisplayEventInfoEXT( VkDisplayEventInfoEXT const & rhs ) { memcpy( this, &rhs, sizeof(DisplayEventInfoEXT) ); } DisplayEventInfoEXT& operator=( VkDisplayEventInfoEXT const & rhs ) { memcpy( this, &rhs, sizeof(DisplayEventInfoEXT) ); return *this; } DisplayEventInfoEXT& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } DisplayEventInfoEXT& setDisplayEvent( DisplayEventTypeEXT displayEvent_ ) { displayEvent = displayEvent_; return *this; } operator const VkDisplayEventInfoEXT&() const { return *reinterpret_cast(this); } bool operator==( DisplayEventInfoEXT const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( displayEvent == rhs.displayEvent ); } bool operator!=( DisplayEventInfoEXT const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; DisplayEventTypeEXT displayEvent; }; static_assert( sizeof( DisplayEventInfoEXT ) == sizeof( VkDisplayEventInfoEXT ), "struct and wrapper have different size!" ); enum class PeerMemoryFeatureFlagBitsKHX { eCopySrc = VK_PEER_MEMORY_FEATURE_COPY_SRC_BIT_KHX, eCopyDst = VK_PEER_MEMORY_FEATURE_COPY_DST_BIT_KHX, eGenericSrc = VK_PEER_MEMORY_FEATURE_GENERIC_SRC_BIT_KHX, eGenericDst = VK_PEER_MEMORY_FEATURE_GENERIC_DST_BIT_KHX }; using PeerMemoryFeatureFlagsKHX = Flags; VULKAN_HPP_INLINE PeerMemoryFeatureFlagsKHX operator|( PeerMemoryFeatureFlagBitsKHX bit0, PeerMemoryFeatureFlagBitsKHX bit1 ) { return PeerMemoryFeatureFlagsKHX( bit0 ) | bit1; } VULKAN_HPP_INLINE PeerMemoryFeatureFlagsKHX operator~( PeerMemoryFeatureFlagBitsKHX bits ) { return ~( PeerMemoryFeatureFlagsKHX( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(PeerMemoryFeatureFlagBitsKHX::eCopySrc) | VkFlags(PeerMemoryFeatureFlagBitsKHX::eCopyDst) | VkFlags(PeerMemoryFeatureFlagBitsKHX::eGenericSrc) | VkFlags(PeerMemoryFeatureFlagBitsKHX::eGenericDst) }; }; enum class MemoryAllocateFlagBitsKHX { eDeviceMask = VK_MEMORY_ALLOCATE_DEVICE_MASK_BIT_KHX }; using MemoryAllocateFlagsKHX = Flags; VULKAN_HPP_INLINE MemoryAllocateFlagsKHX operator|( MemoryAllocateFlagBitsKHX bit0, MemoryAllocateFlagBitsKHX bit1 ) { return MemoryAllocateFlagsKHX( bit0 ) | bit1; } VULKAN_HPP_INLINE MemoryAllocateFlagsKHX operator~( MemoryAllocateFlagBitsKHX bits ) { return ~( MemoryAllocateFlagsKHX( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(MemoryAllocateFlagBitsKHX::eDeviceMask) }; }; struct MemoryAllocateFlagsInfoKHX { MemoryAllocateFlagsInfoKHX( MemoryAllocateFlagsKHX flags_ = MemoryAllocateFlagsKHX(), uint32_t deviceMask_ = 0 ) : sType( StructureType::eMemoryAllocateFlagsInfoKHX ) , pNext( nullptr ) , flags( flags_ ) , deviceMask( deviceMask_ ) { } MemoryAllocateFlagsInfoKHX( VkMemoryAllocateFlagsInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(MemoryAllocateFlagsInfoKHX) ); } MemoryAllocateFlagsInfoKHX& operator=( VkMemoryAllocateFlagsInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(MemoryAllocateFlagsInfoKHX) ); return *this; } MemoryAllocateFlagsInfoKHX& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } MemoryAllocateFlagsInfoKHX& setFlags( MemoryAllocateFlagsKHX flags_ ) { flags = flags_; return *this; } MemoryAllocateFlagsInfoKHX& setDeviceMask( uint32_t deviceMask_ ) { deviceMask = deviceMask_; return *this; } operator const VkMemoryAllocateFlagsInfoKHX&() const { return *reinterpret_cast(this); } bool operator==( MemoryAllocateFlagsInfoKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( deviceMask == rhs.deviceMask ); } bool operator!=( MemoryAllocateFlagsInfoKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; MemoryAllocateFlagsKHX flags; uint32_t deviceMask; }; static_assert( sizeof( MemoryAllocateFlagsInfoKHX ) == sizeof( VkMemoryAllocateFlagsInfoKHX ), "struct and wrapper have different size!" ); enum class DeviceGroupPresentModeFlagBitsKHX { eLocal = VK_DEVICE_GROUP_PRESENT_MODE_LOCAL_BIT_KHX, eRemote = VK_DEVICE_GROUP_PRESENT_MODE_REMOTE_BIT_KHX, eSum = VK_DEVICE_GROUP_PRESENT_MODE_SUM_BIT_KHX, eLocalMultiDevice = VK_DEVICE_GROUP_PRESENT_MODE_LOCAL_MULTI_DEVICE_BIT_KHX }; using DeviceGroupPresentModeFlagsKHX = Flags; VULKAN_HPP_INLINE DeviceGroupPresentModeFlagsKHX operator|( DeviceGroupPresentModeFlagBitsKHX bit0, DeviceGroupPresentModeFlagBitsKHX bit1 ) { return DeviceGroupPresentModeFlagsKHX( bit0 ) | bit1; } VULKAN_HPP_INLINE DeviceGroupPresentModeFlagsKHX operator~( DeviceGroupPresentModeFlagBitsKHX bits ) { return ~( DeviceGroupPresentModeFlagsKHX( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(DeviceGroupPresentModeFlagBitsKHX::eLocal) | VkFlags(DeviceGroupPresentModeFlagBitsKHX::eRemote) | VkFlags(DeviceGroupPresentModeFlagBitsKHX::eSum) | VkFlags(DeviceGroupPresentModeFlagBitsKHX::eLocalMultiDevice) }; }; struct DeviceGroupPresentCapabilitiesKHX { operator const VkDeviceGroupPresentCapabilitiesKHX&() const { return *reinterpret_cast(this); } bool operator==( DeviceGroupPresentCapabilitiesKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( memcmp( presentMask, rhs.presentMask, VK_MAX_DEVICE_GROUP_SIZE_KHX * sizeof( uint32_t ) ) == 0 ) && ( modes == rhs.modes ); } bool operator!=( DeviceGroupPresentCapabilitiesKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; uint32_t presentMask[VK_MAX_DEVICE_GROUP_SIZE_KHX]; DeviceGroupPresentModeFlagsKHX modes; }; static_assert( sizeof( DeviceGroupPresentCapabilitiesKHX ) == sizeof( VkDeviceGroupPresentCapabilitiesKHX ), "struct and wrapper have different size!" ); struct DeviceGroupPresentInfoKHX { DeviceGroupPresentInfoKHX( uint32_t swapchainCount_ = 0, const uint32_t* pDeviceMasks_ = nullptr, DeviceGroupPresentModeFlagBitsKHX mode_ = DeviceGroupPresentModeFlagBitsKHX::eLocal ) : sType( StructureType::eDeviceGroupPresentInfoKHX ) , pNext( nullptr ) , swapchainCount( swapchainCount_ ) , pDeviceMasks( pDeviceMasks_ ) , mode( mode_ ) { } DeviceGroupPresentInfoKHX( VkDeviceGroupPresentInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(DeviceGroupPresentInfoKHX) ); } DeviceGroupPresentInfoKHX& operator=( VkDeviceGroupPresentInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(DeviceGroupPresentInfoKHX) ); return *this; } DeviceGroupPresentInfoKHX& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } DeviceGroupPresentInfoKHX& setSwapchainCount( uint32_t swapchainCount_ ) { swapchainCount = swapchainCount_; return *this; } DeviceGroupPresentInfoKHX& setPDeviceMasks( const uint32_t* pDeviceMasks_ ) { pDeviceMasks = pDeviceMasks_; return *this; } DeviceGroupPresentInfoKHX& setMode( DeviceGroupPresentModeFlagBitsKHX mode_ ) { mode = mode_; return *this; } operator const VkDeviceGroupPresentInfoKHX&() const { return *reinterpret_cast(this); } bool operator==( DeviceGroupPresentInfoKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( swapchainCount == rhs.swapchainCount ) && ( pDeviceMasks == rhs.pDeviceMasks ) && ( mode == rhs.mode ); } bool operator!=( DeviceGroupPresentInfoKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; uint32_t swapchainCount; const uint32_t* pDeviceMasks; DeviceGroupPresentModeFlagBitsKHX mode; }; static_assert( sizeof( DeviceGroupPresentInfoKHX ) == sizeof( VkDeviceGroupPresentInfoKHX ), "struct and wrapper have different size!" ); struct DeviceGroupSwapchainCreateInfoKHX { DeviceGroupSwapchainCreateInfoKHX( DeviceGroupPresentModeFlagsKHX modes_ = DeviceGroupPresentModeFlagsKHX() ) : sType( StructureType::eDeviceGroupSwapchainCreateInfoKHX ) , pNext( nullptr ) , modes( modes_ ) { } DeviceGroupSwapchainCreateInfoKHX( VkDeviceGroupSwapchainCreateInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(DeviceGroupSwapchainCreateInfoKHX) ); } DeviceGroupSwapchainCreateInfoKHX& operator=( VkDeviceGroupSwapchainCreateInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(DeviceGroupSwapchainCreateInfoKHX) ); return *this; } DeviceGroupSwapchainCreateInfoKHX& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } DeviceGroupSwapchainCreateInfoKHX& setModes( DeviceGroupPresentModeFlagsKHX modes_ ) { modes = modes_; return *this; } operator const VkDeviceGroupSwapchainCreateInfoKHX&() const { return *reinterpret_cast(this); } bool operator==( DeviceGroupSwapchainCreateInfoKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( modes == rhs.modes ); } bool operator!=( DeviceGroupSwapchainCreateInfoKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; DeviceGroupPresentModeFlagsKHX modes; }; static_assert( sizeof( DeviceGroupSwapchainCreateInfoKHX ) == sizeof( VkDeviceGroupSwapchainCreateInfoKHX ), "struct and wrapper have different size!" ); enum class SwapchainCreateFlagBitsKHR { eBindSfrKHX = VK_SWAPCHAIN_CREATE_BIND_SFR_BIT_KHX }; using SwapchainCreateFlagsKHR = Flags; VULKAN_HPP_INLINE SwapchainCreateFlagsKHR operator|( SwapchainCreateFlagBitsKHR bit0, SwapchainCreateFlagBitsKHR bit1 ) { return SwapchainCreateFlagsKHR( bit0 ) | bit1; } VULKAN_HPP_INLINE SwapchainCreateFlagsKHR operator~( SwapchainCreateFlagBitsKHR bits ) { return ~( SwapchainCreateFlagsKHR( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(SwapchainCreateFlagBitsKHR::eBindSfrKHX) }; }; struct SwapchainCreateInfoKHR { SwapchainCreateInfoKHR( SwapchainCreateFlagsKHR flags_ = SwapchainCreateFlagsKHR(), SurfaceKHR surface_ = SurfaceKHR(), uint32_t minImageCount_ = 0, Format imageFormat_ = Format::eUndefined, ColorSpaceKHR imageColorSpace_ = ColorSpaceKHR::eSrgbNonlinear, Extent2D imageExtent_ = Extent2D(), uint32_t imageArrayLayers_ = 0, ImageUsageFlags imageUsage_ = ImageUsageFlags(), SharingMode imageSharingMode_ = SharingMode::eExclusive, uint32_t queueFamilyIndexCount_ = 0, const uint32_t* pQueueFamilyIndices_ = nullptr, SurfaceTransformFlagBitsKHR preTransform_ = SurfaceTransformFlagBitsKHR::eIdentity, CompositeAlphaFlagBitsKHR compositeAlpha_ = CompositeAlphaFlagBitsKHR::eOpaque, PresentModeKHR presentMode_ = PresentModeKHR::eImmediate, Bool32 clipped_ = 0, SwapchainKHR oldSwapchain_ = SwapchainKHR() ) : sType( StructureType::eSwapchainCreateInfoKHR ) , pNext( nullptr ) , flags( flags_ ) , surface( surface_ ) , minImageCount( minImageCount_ ) , imageFormat( imageFormat_ ) , imageColorSpace( imageColorSpace_ ) , imageExtent( imageExtent_ ) , imageArrayLayers( imageArrayLayers_ ) , imageUsage( imageUsage_ ) , imageSharingMode( imageSharingMode_ ) , queueFamilyIndexCount( queueFamilyIndexCount_ ) , pQueueFamilyIndices( pQueueFamilyIndices_ ) , preTransform( preTransform_ ) , compositeAlpha( compositeAlpha_ ) , presentMode( presentMode_ ) , clipped( clipped_ ) , oldSwapchain( oldSwapchain_ ) { } SwapchainCreateInfoKHR( VkSwapchainCreateInfoKHR const & rhs ) { memcpy( this, &rhs, sizeof(SwapchainCreateInfoKHR) ); } SwapchainCreateInfoKHR& operator=( VkSwapchainCreateInfoKHR const & rhs ) { memcpy( this, &rhs, sizeof(SwapchainCreateInfoKHR) ); return *this; } SwapchainCreateInfoKHR& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } SwapchainCreateInfoKHR& setFlags( SwapchainCreateFlagsKHR flags_ ) { flags = flags_; return *this; } SwapchainCreateInfoKHR& setSurface( SurfaceKHR surface_ ) { surface = surface_; return *this; } SwapchainCreateInfoKHR& setMinImageCount( uint32_t minImageCount_ ) { minImageCount = minImageCount_; return *this; } SwapchainCreateInfoKHR& setImageFormat( Format imageFormat_ ) { imageFormat = imageFormat_; return *this; } SwapchainCreateInfoKHR& setImageColorSpace( ColorSpaceKHR imageColorSpace_ ) { imageColorSpace = imageColorSpace_; return *this; } SwapchainCreateInfoKHR& setImageExtent( Extent2D imageExtent_ ) { imageExtent = imageExtent_; return *this; } SwapchainCreateInfoKHR& setImageArrayLayers( uint32_t imageArrayLayers_ ) { imageArrayLayers = imageArrayLayers_; return *this; } SwapchainCreateInfoKHR& setImageUsage( ImageUsageFlags imageUsage_ ) { imageUsage = imageUsage_; return *this; } SwapchainCreateInfoKHR& setImageSharingMode( SharingMode imageSharingMode_ ) { imageSharingMode = imageSharingMode_; return *this; } SwapchainCreateInfoKHR& setQueueFamilyIndexCount( uint32_t queueFamilyIndexCount_ ) { queueFamilyIndexCount = queueFamilyIndexCount_; return *this; } SwapchainCreateInfoKHR& setPQueueFamilyIndices( const uint32_t* pQueueFamilyIndices_ ) { pQueueFamilyIndices = pQueueFamilyIndices_; return *this; } SwapchainCreateInfoKHR& setPreTransform( SurfaceTransformFlagBitsKHR preTransform_ ) { preTransform = preTransform_; return *this; } SwapchainCreateInfoKHR& setCompositeAlpha( CompositeAlphaFlagBitsKHR compositeAlpha_ ) { compositeAlpha = compositeAlpha_; return *this; } SwapchainCreateInfoKHR& setPresentMode( PresentModeKHR presentMode_ ) { presentMode = presentMode_; return *this; } SwapchainCreateInfoKHR& setClipped( Bool32 clipped_ ) { clipped = clipped_; return *this; } SwapchainCreateInfoKHR& setOldSwapchain( SwapchainKHR oldSwapchain_ ) { oldSwapchain = oldSwapchain_; return *this; } operator const VkSwapchainCreateInfoKHR&() const { return *reinterpret_cast(this); } bool operator==( SwapchainCreateInfoKHR const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( surface == rhs.surface ) && ( minImageCount == rhs.minImageCount ) && ( imageFormat == rhs.imageFormat ) && ( imageColorSpace == rhs.imageColorSpace ) && ( imageExtent == rhs.imageExtent ) && ( imageArrayLayers == rhs.imageArrayLayers ) && ( imageUsage == rhs.imageUsage ) && ( imageSharingMode == rhs.imageSharingMode ) && ( queueFamilyIndexCount == rhs.queueFamilyIndexCount ) && ( pQueueFamilyIndices == rhs.pQueueFamilyIndices ) && ( preTransform == rhs.preTransform ) && ( compositeAlpha == rhs.compositeAlpha ) && ( presentMode == rhs.presentMode ) && ( clipped == rhs.clipped ) && ( oldSwapchain == rhs.oldSwapchain ); } bool operator!=( SwapchainCreateInfoKHR const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; SwapchainCreateFlagsKHR flags; SurfaceKHR surface; uint32_t minImageCount; Format imageFormat; ColorSpaceKHR imageColorSpace; Extent2D imageExtent; uint32_t imageArrayLayers; ImageUsageFlags imageUsage; SharingMode imageSharingMode; uint32_t queueFamilyIndexCount; const uint32_t* pQueueFamilyIndices; SurfaceTransformFlagBitsKHR preTransform; CompositeAlphaFlagBitsKHR compositeAlpha; PresentModeKHR presentMode; Bool32 clipped; SwapchainKHR oldSwapchain; }; static_assert( sizeof( SwapchainCreateInfoKHR ) == sizeof( VkSwapchainCreateInfoKHR ), "struct and wrapper have different size!" ); enum class ViewportCoordinateSwizzleNV { ePositiveX = VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_X_NV, eNegativeX = VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_X_NV, ePositiveY = VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_Y_NV, eNegativeY = VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_Y_NV, ePositiveZ = VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_Z_NV, eNegativeZ = VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_Z_NV, ePositiveW = VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_W_NV, eNegativeW = VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_W_NV }; struct ViewportSwizzleNV { ViewportSwizzleNV( ViewportCoordinateSwizzleNV x_ = ViewportCoordinateSwizzleNV::ePositiveX, ViewportCoordinateSwizzleNV y_ = ViewportCoordinateSwizzleNV::ePositiveX, ViewportCoordinateSwizzleNV z_ = ViewportCoordinateSwizzleNV::ePositiveX, ViewportCoordinateSwizzleNV w_ = ViewportCoordinateSwizzleNV::ePositiveX ) : x( x_ ) , y( y_ ) , z( z_ ) , w( w_ ) { } ViewportSwizzleNV( VkViewportSwizzleNV const & rhs ) { memcpy( this, &rhs, sizeof(ViewportSwizzleNV) ); } ViewportSwizzleNV& operator=( VkViewportSwizzleNV const & rhs ) { memcpy( this, &rhs, sizeof(ViewportSwizzleNV) ); return *this; } ViewportSwizzleNV& setX( ViewportCoordinateSwizzleNV x_ ) { x = x_; return *this; } ViewportSwizzleNV& setY( ViewportCoordinateSwizzleNV y_ ) { y = y_; return *this; } ViewportSwizzleNV& setZ( ViewportCoordinateSwizzleNV z_ ) { z = z_; return *this; } ViewportSwizzleNV& setW( ViewportCoordinateSwizzleNV w_ ) { w = w_; return *this; } operator const VkViewportSwizzleNV&() const { return *reinterpret_cast(this); } bool operator==( ViewportSwizzleNV const& rhs ) const { return ( x == rhs.x ) && ( y == rhs.y ) && ( z == rhs.z ) && ( w == rhs.w ); } bool operator!=( ViewportSwizzleNV const& rhs ) const { return !operator==( rhs ); } ViewportCoordinateSwizzleNV x; ViewportCoordinateSwizzleNV y; ViewportCoordinateSwizzleNV z; ViewportCoordinateSwizzleNV w; }; static_assert( sizeof( ViewportSwizzleNV ) == sizeof( VkViewportSwizzleNV ), "struct and wrapper have different size!" ); struct PipelineViewportSwizzleStateCreateInfoNV { PipelineViewportSwizzleStateCreateInfoNV( PipelineViewportSwizzleStateCreateFlagsNV flags_ = PipelineViewportSwizzleStateCreateFlagsNV(), uint32_t viewportCount_ = 0, const ViewportSwizzleNV* pViewportSwizzles_ = nullptr ) : sType( StructureType::ePipelineViewportSwizzleStateCreateInfoNV ) , pNext( nullptr ) , flags( flags_ ) , viewportCount( viewportCount_ ) , pViewportSwizzles( pViewportSwizzles_ ) { } PipelineViewportSwizzleStateCreateInfoNV( VkPipelineViewportSwizzleStateCreateInfoNV const & rhs ) { memcpy( this, &rhs, sizeof(PipelineViewportSwizzleStateCreateInfoNV) ); } PipelineViewportSwizzleStateCreateInfoNV& operator=( VkPipelineViewportSwizzleStateCreateInfoNV const & rhs ) { memcpy( this, &rhs, sizeof(PipelineViewportSwizzleStateCreateInfoNV) ); return *this; } PipelineViewportSwizzleStateCreateInfoNV& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } PipelineViewportSwizzleStateCreateInfoNV& setFlags( PipelineViewportSwizzleStateCreateFlagsNV flags_ ) { flags = flags_; return *this; } PipelineViewportSwizzleStateCreateInfoNV& setViewportCount( uint32_t viewportCount_ ) { viewportCount = viewportCount_; return *this; } PipelineViewportSwizzleStateCreateInfoNV& setPViewportSwizzles( const ViewportSwizzleNV* pViewportSwizzles_ ) { pViewportSwizzles = pViewportSwizzles_; return *this; } operator const VkPipelineViewportSwizzleStateCreateInfoNV&() const { return *reinterpret_cast(this); } bool operator==( PipelineViewportSwizzleStateCreateInfoNV const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( viewportCount == rhs.viewportCount ) && ( pViewportSwizzles == rhs.pViewportSwizzles ); } bool operator!=( PipelineViewportSwizzleStateCreateInfoNV const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; PipelineViewportSwizzleStateCreateFlagsNV flags; uint32_t viewportCount; const ViewportSwizzleNV* pViewportSwizzles; }; static_assert( sizeof( PipelineViewportSwizzleStateCreateInfoNV ) == sizeof( VkPipelineViewportSwizzleStateCreateInfoNV ), "struct and wrapper have different size!" ); enum class DiscardRectangleModeEXT { eInclusive = VK_DISCARD_RECTANGLE_MODE_INCLUSIVE_EXT, eExclusive = VK_DISCARD_RECTANGLE_MODE_EXCLUSIVE_EXT }; struct PipelineDiscardRectangleStateCreateInfoEXT { PipelineDiscardRectangleStateCreateInfoEXT( PipelineDiscardRectangleStateCreateFlagsEXT flags_ = PipelineDiscardRectangleStateCreateFlagsEXT(), DiscardRectangleModeEXT discardRectangleMode_ = DiscardRectangleModeEXT::eInclusive, uint32_t discardRectangleCount_ = 0, const Rect2D* pDiscardRectangles_ = nullptr ) : sType( StructureType::ePipelineDiscardRectangleStateCreateInfoEXT ) , pNext( nullptr ) , flags( flags_ ) , discardRectangleMode( discardRectangleMode_ ) , discardRectangleCount( discardRectangleCount_ ) , pDiscardRectangles( pDiscardRectangles_ ) { } PipelineDiscardRectangleStateCreateInfoEXT( VkPipelineDiscardRectangleStateCreateInfoEXT const & rhs ) { memcpy( this, &rhs, sizeof(PipelineDiscardRectangleStateCreateInfoEXT) ); } PipelineDiscardRectangleStateCreateInfoEXT& operator=( VkPipelineDiscardRectangleStateCreateInfoEXT const & rhs ) { memcpy( this, &rhs, sizeof(PipelineDiscardRectangleStateCreateInfoEXT) ); return *this; } PipelineDiscardRectangleStateCreateInfoEXT& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } PipelineDiscardRectangleStateCreateInfoEXT& setFlags( PipelineDiscardRectangleStateCreateFlagsEXT flags_ ) { flags = flags_; return *this; } PipelineDiscardRectangleStateCreateInfoEXT& setDiscardRectangleMode( DiscardRectangleModeEXT discardRectangleMode_ ) { discardRectangleMode = discardRectangleMode_; return *this; } PipelineDiscardRectangleStateCreateInfoEXT& setDiscardRectangleCount( uint32_t discardRectangleCount_ ) { discardRectangleCount = discardRectangleCount_; return *this; } PipelineDiscardRectangleStateCreateInfoEXT& setPDiscardRectangles( const Rect2D* pDiscardRectangles_ ) { pDiscardRectangles = pDiscardRectangles_; return *this; } operator const VkPipelineDiscardRectangleStateCreateInfoEXT&() const { return *reinterpret_cast(this); } bool operator==( PipelineDiscardRectangleStateCreateInfoEXT const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( discardRectangleMode == rhs.discardRectangleMode ) && ( discardRectangleCount == rhs.discardRectangleCount ) && ( pDiscardRectangles == rhs.pDiscardRectangles ); } bool operator!=( PipelineDiscardRectangleStateCreateInfoEXT const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; PipelineDiscardRectangleStateCreateFlagsEXT flags; DiscardRectangleModeEXT discardRectangleMode; uint32_t discardRectangleCount; const Rect2D* pDiscardRectangles; }; static_assert( sizeof( PipelineDiscardRectangleStateCreateInfoEXT ) == sizeof( VkPipelineDiscardRectangleStateCreateInfoEXT ), "struct and wrapper have different size!" ); enum class SubpassDescriptionFlagBits { ePerViewAttributesNVX = VK_SUBPASS_DESCRIPTION_PER_VIEW_ATTRIBUTES_BIT_NVX, ePerViewPositionXOnlyNVX = VK_SUBPASS_DESCRIPTION_PER_VIEW_POSITION_X_ONLY_BIT_NVX }; using SubpassDescriptionFlags = Flags; VULKAN_HPP_INLINE SubpassDescriptionFlags operator|( SubpassDescriptionFlagBits bit0, SubpassDescriptionFlagBits bit1 ) { return SubpassDescriptionFlags( bit0 ) | bit1; } VULKAN_HPP_INLINE SubpassDescriptionFlags operator~( SubpassDescriptionFlagBits bits ) { return ~( SubpassDescriptionFlags( bits ) ); } template <> struct FlagTraits { enum { allFlags = VkFlags(SubpassDescriptionFlagBits::ePerViewAttributesNVX) | VkFlags(SubpassDescriptionFlagBits::ePerViewPositionXOnlyNVX) }; }; struct SubpassDescription { SubpassDescription( SubpassDescriptionFlags flags_ = SubpassDescriptionFlags(), PipelineBindPoint pipelineBindPoint_ = PipelineBindPoint::eGraphics, uint32_t inputAttachmentCount_ = 0, const AttachmentReference* pInputAttachments_ = nullptr, uint32_t colorAttachmentCount_ = 0, const AttachmentReference* pColorAttachments_ = nullptr, const AttachmentReference* pResolveAttachments_ = nullptr, const AttachmentReference* pDepthStencilAttachment_ = nullptr, uint32_t preserveAttachmentCount_ = 0, const uint32_t* pPreserveAttachments_ = nullptr ) : flags( flags_ ) , pipelineBindPoint( pipelineBindPoint_ ) , inputAttachmentCount( inputAttachmentCount_ ) , pInputAttachments( pInputAttachments_ ) , colorAttachmentCount( colorAttachmentCount_ ) , pColorAttachments( pColorAttachments_ ) , pResolveAttachments( pResolveAttachments_ ) , pDepthStencilAttachment( pDepthStencilAttachment_ ) , preserveAttachmentCount( preserveAttachmentCount_ ) , pPreserveAttachments( pPreserveAttachments_ ) { } SubpassDescription( VkSubpassDescription const & rhs ) { memcpy( this, &rhs, sizeof(SubpassDescription) ); } SubpassDescription& operator=( VkSubpassDescription const & rhs ) { memcpy( this, &rhs, sizeof(SubpassDescription) ); return *this; } SubpassDescription& setFlags( SubpassDescriptionFlags flags_ ) { flags = flags_; return *this; } SubpassDescription& setPipelineBindPoint( PipelineBindPoint pipelineBindPoint_ ) { pipelineBindPoint = pipelineBindPoint_; return *this; } SubpassDescription& setInputAttachmentCount( uint32_t inputAttachmentCount_ ) { inputAttachmentCount = inputAttachmentCount_; return *this; } SubpassDescription& setPInputAttachments( const AttachmentReference* pInputAttachments_ ) { pInputAttachments = pInputAttachments_; return *this; } SubpassDescription& setColorAttachmentCount( uint32_t colorAttachmentCount_ ) { colorAttachmentCount = colorAttachmentCount_; return *this; } SubpassDescription& setPColorAttachments( const AttachmentReference* pColorAttachments_ ) { pColorAttachments = pColorAttachments_; return *this; } SubpassDescription& setPResolveAttachments( const AttachmentReference* pResolveAttachments_ ) { pResolveAttachments = pResolveAttachments_; return *this; } SubpassDescription& setPDepthStencilAttachment( const AttachmentReference* pDepthStencilAttachment_ ) { pDepthStencilAttachment = pDepthStencilAttachment_; return *this; } SubpassDescription& setPreserveAttachmentCount( uint32_t preserveAttachmentCount_ ) { preserveAttachmentCount = preserveAttachmentCount_; return *this; } SubpassDescription& setPPreserveAttachments( const uint32_t* pPreserveAttachments_ ) { pPreserveAttachments = pPreserveAttachments_; return *this; } operator const VkSubpassDescription&() const { return *reinterpret_cast(this); } bool operator==( SubpassDescription const& rhs ) const { return ( flags == rhs.flags ) && ( pipelineBindPoint == rhs.pipelineBindPoint ) && ( inputAttachmentCount == rhs.inputAttachmentCount ) && ( pInputAttachments == rhs.pInputAttachments ) && ( colorAttachmentCount == rhs.colorAttachmentCount ) && ( pColorAttachments == rhs.pColorAttachments ) && ( pResolveAttachments == rhs.pResolveAttachments ) && ( pDepthStencilAttachment == rhs.pDepthStencilAttachment ) && ( preserveAttachmentCount == rhs.preserveAttachmentCount ) && ( pPreserveAttachments == rhs.pPreserveAttachments ); } bool operator!=( SubpassDescription const& rhs ) const { return !operator==( rhs ); } SubpassDescriptionFlags flags; PipelineBindPoint pipelineBindPoint; uint32_t inputAttachmentCount; const AttachmentReference* pInputAttachments; uint32_t colorAttachmentCount; const AttachmentReference* pColorAttachments; const AttachmentReference* pResolveAttachments; const AttachmentReference* pDepthStencilAttachment; uint32_t preserveAttachmentCount; const uint32_t* pPreserveAttachments; }; static_assert( sizeof( SubpassDescription ) == sizeof( VkSubpassDescription ), "struct and wrapper have different size!" ); struct RenderPassCreateInfo { RenderPassCreateInfo( RenderPassCreateFlags flags_ = RenderPassCreateFlags(), uint32_t attachmentCount_ = 0, const AttachmentDescription* pAttachments_ = nullptr, uint32_t subpassCount_ = 0, const SubpassDescription* pSubpasses_ = nullptr, uint32_t dependencyCount_ = 0, const SubpassDependency* pDependencies_ = nullptr ) : sType( StructureType::eRenderPassCreateInfo ) , pNext( nullptr ) , flags( flags_ ) , attachmentCount( attachmentCount_ ) , pAttachments( pAttachments_ ) , subpassCount( subpassCount_ ) , pSubpasses( pSubpasses_ ) , dependencyCount( dependencyCount_ ) , pDependencies( pDependencies_ ) { } RenderPassCreateInfo( VkRenderPassCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(RenderPassCreateInfo) ); } RenderPassCreateInfo& operator=( VkRenderPassCreateInfo const & rhs ) { memcpy( this, &rhs, sizeof(RenderPassCreateInfo) ); return *this; } RenderPassCreateInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } RenderPassCreateInfo& setFlags( RenderPassCreateFlags flags_ ) { flags = flags_; return *this; } RenderPassCreateInfo& setAttachmentCount( uint32_t attachmentCount_ ) { attachmentCount = attachmentCount_; return *this; } RenderPassCreateInfo& setPAttachments( const AttachmentDescription* pAttachments_ ) { pAttachments = pAttachments_; return *this; } RenderPassCreateInfo& setSubpassCount( uint32_t subpassCount_ ) { subpassCount = subpassCount_; return *this; } RenderPassCreateInfo& setPSubpasses( const SubpassDescription* pSubpasses_ ) { pSubpasses = pSubpasses_; return *this; } RenderPassCreateInfo& setDependencyCount( uint32_t dependencyCount_ ) { dependencyCount = dependencyCount_; return *this; } RenderPassCreateInfo& setPDependencies( const SubpassDependency* pDependencies_ ) { pDependencies = pDependencies_; return *this; } operator const VkRenderPassCreateInfo&() const { return *reinterpret_cast(this); } bool operator==( RenderPassCreateInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( flags == rhs.flags ) && ( attachmentCount == rhs.attachmentCount ) && ( pAttachments == rhs.pAttachments ) && ( subpassCount == rhs.subpassCount ) && ( pSubpasses == rhs.pSubpasses ) && ( dependencyCount == rhs.dependencyCount ) && ( pDependencies == rhs.pDependencies ); } bool operator!=( RenderPassCreateInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; RenderPassCreateFlags flags; uint32_t attachmentCount; const AttachmentDescription* pAttachments; uint32_t subpassCount; const SubpassDescription* pSubpasses; uint32_t dependencyCount; const SubpassDependency* pDependencies; }; static_assert( sizeof( RenderPassCreateInfo ) == sizeof( VkRenderPassCreateInfo ), "struct and wrapper have different size!" ); Result enumerateInstanceLayerProperties( uint32_t* pPropertyCount, LayerProperties* pProperties ); #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template > typename ResultValueType>::type enumerateInstanceLayerProperties(); #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result enumerateInstanceLayerProperties( uint32_t* pPropertyCount, LayerProperties* pProperties ) { return static_cast( vkEnumerateInstanceLayerProperties( pPropertyCount, reinterpret_cast( pProperties ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template VULKAN_HPP_INLINE typename ResultValueType>::type enumerateInstanceLayerProperties() { std::vector properties; uint32_t propertyCount; Result result; do { result = static_cast( vkEnumerateInstanceLayerProperties( &propertyCount, nullptr ) ); if ( ( result == Result::eSuccess ) && propertyCount ) { properties.resize( propertyCount ); result = static_cast( vkEnumerateInstanceLayerProperties( &propertyCount, reinterpret_cast( properties.data() ) ) ); } } while ( result == Result::eIncomplete ); assert( propertyCount <= properties.size() ); properties.resize( propertyCount ); return createResultValue( result, properties, "vk::enumerateInstanceLayerProperties" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result enumerateInstanceExtensionProperties( const char* pLayerName, uint32_t* pPropertyCount, ExtensionProperties* pProperties ); #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template > typename ResultValueType>::type enumerateInstanceExtensionProperties( Optional layerName = nullptr ); #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result enumerateInstanceExtensionProperties( const char* pLayerName, uint32_t* pPropertyCount, ExtensionProperties* pProperties ) { return static_cast( vkEnumerateInstanceExtensionProperties( pLayerName, pPropertyCount, reinterpret_cast( pProperties ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template VULKAN_HPP_INLINE typename ResultValueType>::type enumerateInstanceExtensionProperties( Optional layerName ) { std::vector properties; uint32_t propertyCount; Result result; do { result = static_cast( vkEnumerateInstanceExtensionProperties( layerName ? layerName->c_str() : nullptr, &propertyCount, nullptr ) ); if ( ( result == Result::eSuccess ) && propertyCount ) { properties.resize( propertyCount ); result = static_cast( vkEnumerateInstanceExtensionProperties( layerName ? layerName->c_str() : nullptr, &propertyCount, reinterpret_cast( properties.data() ) ) ); } } while ( result == Result::eIncomplete ); assert( propertyCount <= properties.size() ); properties.resize( propertyCount ); return createResultValue( result, properties, "vk::enumerateInstanceExtensionProperties" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ // forward declarations struct CmdProcessCommandsInfoNVX; class CommandBuffer { public: CommandBuffer() : m_commandBuffer(VK_NULL_HANDLE) {} CommandBuffer( std::nullptr_t ) : m_commandBuffer(VK_NULL_HANDLE) {} VULKAN_HPP_TYPESAFE_EXPLICIT CommandBuffer(VkCommandBuffer commandBuffer) : m_commandBuffer(commandBuffer) {} #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) CommandBuffer& operator=(VkCommandBuffer commandBuffer) { m_commandBuffer = commandBuffer; return *this; } #endif CommandBuffer& operator=( std::nullptr_t ) { m_commandBuffer = VK_NULL_HANDLE; return *this; } bool operator==(CommandBuffer const &rhs) const { return m_commandBuffer == rhs.m_commandBuffer; } bool operator!=(CommandBuffer const &rhs) const { return m_commandBuffer != rhs.m_commandBuffer; } bool operator<(CommandBuffer const &rhs) const { return m_commandBuffer < rhs.m_commandBuffer; } Result begin( const CommandBufferBeginInfo* pBeginInfo ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type begin( const CommandBufferBeginInfo & beginInfo ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE Result end() const; #else ResultValueType::type end() const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE Result reset( CommandBufferResetFlags flags ) const; #else ResultValueType::type reset( CommandBufferResetFlags flags ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void bindPipeline( PipelineBindPoint pipelineBindPoint, Pipeline pipeline ) const; void setViewport( uint32_t firstViewport, uint32_t viewportCount, const Viewport* pViewports ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void setViewport( uint32_t firstViewport, ArrayProxy viewports ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void setScissor( uint32_t firstScissor, uint32_t scissorCount, const Rect2D* pScissors ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void setScissor( uint32_t firstScissor, ArrayProxy scissors ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void setLineWidth( float lineWidth ) const; void setDepthBias( float depthBiasConstantFactor, float depthBiasClamp, float depthBiasSlopeFactor ) const; void setBlendConstants( const float blendConstants[4] ) const; void setDepthBounds( float minDepthBounds, float maxDepthBounds ) const; void setStencilCompareMask( StencilFaceFlags faceMask, uint32_t compareMask ) const; void setStencilWriteMask( StencilFaceFlags faceMask, uint32_t writeMask ) const; void setStencilReference( StencilFaceFlags faceMask, uint32_t reference ) const; void bindDescriptorSets( PipelineBindPoint pipelineBindPoint, PipelineLayout layout, uint32_t firstSet, uint32_t descriptorSetCount, const DescriptorSet* pDescriptorSets, uint32_t dynamicOffsetCount, const uint32_t* pDynamicOffsets ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void bindDescriptorSets( PipelineBindPoint pipelineBindPoint, PipelineLayout layout, uint32_t firstSet, ArrayProxy descriptorSets, ArrayProxy dynamicOffsets ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void bindIndexBuffer( Buffer buffer, DeviceSize offset, IndexType indexType ) const; void bindVertexBuffers( uint32_t firstBinding, uint32_t bindingCount, const Buffer* pBuffers, const DeviceSize* pOffsets ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void bindVertexBuffers( uint32_t firstBinding, ArrayProxy buffers, ArrayProxy offsets ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void draw( uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance ) const; void drawIndexed( uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance ) const; void drawIndirect( Buffer buffer, DeviceSize offset, uint32_t drawCount, uint32_t stride ) const; void drawIndexedIndirect( Buffer buffer, DeviceSize offset, uint32_t drawCount, uint32_t stride ) const; void dispatch( uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ ) const; void dispatchIndirect( Buffer buffer, DeviceSize offset ) const; void copyBuffer( Buffer srcBuffer, Buffer dstBuffer, uint32_t regionCount, const BufferCopy* pRegions ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void copyBuffer( Buffer srcBuffer, Buffer dstBuffer, ArrayProxy regions ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void copyImage( Image srcImage, ImageLayout srcImageLayout, Image dstImage, ImageLayout dstImageLayout, uint32_t regionCount, const ImageCopy* pRegions ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void copyImage( Image srcImage, ImageLayout srcImageLayout, Image dstImage, ImageLayout dstImageLayout, ArrayProxy regions ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void blitImage( Image srcImage, ImageLayout srcImageLayout, Image dstImage, ImageLayout dstImageLayout, uint32_t regionCount, const ImageBlit* pRegions, Filter filter ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void blitImage( Image srcImage, ImageLayout srcImageLayout, Image dstImage, ImageLayout dstImageLayout, ArrayProxy regions, Filter filter ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void copyBufferToImage( Buffer srcBuffer, Image dstImage, ImageLayout dstImageLayout, uint32_t regionCount, const BufferImageCopy* pRegions ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void copyBufferToImage( Buffer srcBuffer, Image dstImage, ImageLayout dstImageLayout, ArrayProxy regions ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void copyImageToBuffer( Image srcImage, ImageLayout srcImageLayout, Buffer dstBuffer, uint32_t regionCount, const BufferImageCopy* pRegions ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void copyImageToBuffer( Image srcImage, ImageLayout srcImageLayout, Buffer dstBuffer, ArrayProxy regions ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void updateBuffer( Buffer dstBuffer, DeviceSize dstOffset, DeviceSize dataSize, const void* pData ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template void updateBuffer( Buffer dstBuffer, DeviceSize dstOffset, ArrayProxy data ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void fillBuffer( Buffer dstBuffer, DeviceSize dstOffset, DeviceSize size, uint32_t data ) const; void clearColorImage( Image image, ImageLayout imageLayout, const ClearColorValue* pColor, uint32_t rangeCount, const ImageSubresourceRange* pRanges ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void clearColorImage( Image image, ImageLayout imageLayout, const ClearColorValue & color, ArrayProxy ranges ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void clearDepthStencilImage( Image image, ImageLayout imageLayout, const ClearDepthStencilValue* pDepthStencil, uint32_t rangeCount, const ImageSubresourceRange* pRanges ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void clearDepthStencilImage( Image image, ImageLayout imageLayout, const ClearDepthStencilValue & depthStencil, ArrayProxy ranges ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void clearAttachments( uint32_t attachmentCount, const ClearAttachment* pAttachments, uint32_t rectCount, const ClearRect* pRects ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void clearAttachments( ArrayProxy attachments, ArrayProxy rects ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void resolveImage( Image srcImage, ImageLayout srcImageLayout, Image dstImage, ImageLayout dstImageLayout, uint32_t regionCount, const ImageResolve* pRegions ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void resolveImage( Image srcImage, ImageLayout srcImageLayout, Image dstImage, ImageLayout dstImageLayout, ArrayProxy regions ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void setEvent( Event event, PipelineStageFlags stageMask ) const; void resetEvent( Event event, PipelineStageFlags stageMask ) const; void waitEvents( uint32_t eventCount, const Event* pEvents, PipelineStageFlags srcStageMask, PipelineStageFlags dstStageMask, uint32_t memoryBarrierCount, const MemoryBarrier* pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const BufferMemoryBarrier* pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const ImageMemoryBarrier* pImageMemoryBarriers ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void waitEvents( ArrayProxy events, PipelineStageFlags srcStageMask, PipelineStageFlags dstStageMask, ArrayProxy memoryBarriers, ArrayProxy bufferMemoryBarriers, ArrayProxy imageMemoryBarriers ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void pipelineBarrier( PipelineStageFlags srcStageMask, PipelineStageFlags dstStageMask, DependencyFlags dependencyFlags, uint32_t memoryBarrierCount, const MemoryBarrier* pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const BufferMemoryBarrier* pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const ImageMemoryBarrier* pImageMemoryBarriers ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void pipelineBarrier( PipelineStageFlags srcStageMask, PipelineStageFlags dstStageMask, DependencyFlags dependencyFlags, ArrayProxy memoryBarriers, ArrayProxy bufferMemoryBarriers, ArrayProxy imageMemoryBarriers ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void beginQuery( QueryPool queryPool, uint32_t query, QueryControlFlags flags ) const; void endQuery( QueryPool queryPool, uint32_t query ) const; void resetQueryPool( QueryPool queryPool, uint32_t firstQuery, uint32_t queryCount ) const; void writeTimestamp( PipelineStageFlagBits pipelineStage, QueryPool queryPool, uint32_t query ) const; void copyQueryPoolResults( QueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, Buffer dstBuffer, DeviceSize dstOffset, DeviceSize stride, QueryResultFlags flags ) const; void pushConstants( PipelineLayout layout, ShaderStageFlags stageFlags, uint32_t offset, uint32_t size, const void* pValues ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template void pushConstants( PipelineLayout layout, ShaderStageFlags stageFlags, uint32_t offset, ArrayProxy values ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void beginRenderPass( const RenderPassBeginInfo* pRenderPassBegin, SubpassContents contents ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void beginRenderPass( const RenderPassBeginInfo & renderPassBegin, SubpassContents contents ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void nextSubpass( SubpassContents contents ) const; void endRenderPass() const; void executeCommands( uint32_t commandBufferCount, const CommandBuffer* pCommandBuffers ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void executeCommands( ArrayProxy commandBuffers ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void debugMarkerBeginEXT( DebugMarkerMarkerInfoEXT* pMarkerInfo ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE DebugMarkerMarkerInfoEXT debugMarkerBeginEXT() const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void debugMarkerEndEXT() const; void debugMarkerInsertEXT( DebugMarkerMarkerInfoEXT* pMarkerInfo ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE DebugMarkerMarkerInfoEXT debugMarkerInsertEXT() const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void drawIndirectCountAMD( Buffer buffer, DeviceSize offset, Buffer countBuffer, DeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride ) const; void drawIndexedIndirectCountAMD( Buffer buffer, DeviceSize offset, Buffer countBuffer, DeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride ) const; void processCommandsNVX( const CmdProcessCommandsInfoNVX* pProcessCommandsInfo ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void processCommandsNVX( const CmdProcessCommandsInfoNVX & processCommandsInfo ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void reserveSpaceForCommandsNVX( const CmdReserveSpaceForCommandsInfoNVX* pReserveSpaceInfo ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void reserveSpaceForCommandsNVX( const CmdReserveSpaceForCommandsInfoNVX & reserveSpaceInfo ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void pushDescriptorSetKHR( PipelineBindPoint pipelineBindPoint, PipelineLayout layout, uint32_t set, uint32_t descriptorWriteCount, const WriteDescriptorSet* pDescriptorWrites ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void pushDescriptorSetKHR( PipelineBindPoint pipelineBindPoint, PipelineLayout layout, uint32_t set, ArrayProxy descriptorWrites ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void setDeviceMaskKHX( uint32_t deviceMask ) const; void dispatchBaseKHX( uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ ) const; void pushDescriptorSetWithTemplateKHR( DescriptorUpdateTemplateKHR descriptorUpdateTemplate, PipelineLayout layout, uint32_t set, const void* pData ) const; void setViewportWScalingNV( uint32_t firstViewport, uint32_t viewportCount, const ViewportWScalingNV* pViewportWScalings ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void setViewportWScalingNV( uint32_t firstViewport, ArrayProxy viewportWScalings ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void setDiscardRectangleEXT( uint32_t firstDiscardRectangle, uint32_t discardRectangleCount, const Rect2D* pDiscardRectangles ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void setDiscardRectangleEXT( uint32_t firstDiscardRectangle, ArrayProxy discardRectangles ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_TYPESAFE_EXPLICIT operator VkCommandBuffer() const { return m_commandBuffer; } explicit operator bool() const { return m_commandBuffer != VK_NULL_HANDLE; } bool operator!() const { return m_commandBuffer == VK_NULL_HANDLE; } private: VkCommandBuffer m_commandBuffer; }; static_assert( sizeof( CommandBuffer ) == sizeof( VkCommandBuffer ), "handle and wrapper have different size!" ); VULKAN_HPP_INLINE Result CommandBuffer::begin( const CommandBufferBeginInfo* pBeginInfo ) const { return static_cast( vkBeginCommandBuffer( m_commandBuffer, reinterpret_cast( pBeginInfo ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type CommandBuffer::begin( const CommandBufferBeginInfo & beginInfo ) const { Result result = static_cast( vkBeginCommandBuffer( m_commandBuffer, reinterpret_cast( &beginInfo ) ) ); return createResultValue( result, "vk::CommandBuffer::begin" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE Result CommandBuffer::end() const { return static_cast( vkEndCommandBuffer( m_commandBuffer ) ); } #else VULKAN_HPP_INLINE ResultValueType::type CommandBuffer::end() const { Result result = static_cast( vkEndCommandBuffer( m_commandBuffer ) ); return createResultValue( result, "vk::CommandBuffer::end" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE Result CommandBuffer::reset( CommandBufferResetFlags flags ) const { return static_cast( vkResetCommandBuffer( m_commandBuffer, static_cast( flags ) ) ); } #else VULKAN_HPP_INLINE ResultValueType::type CommandBuffer::reset( CommandBufferResetFlags flags ) const { Result result = static_cast( vkResetCommandBuffer( m_commandBuffer, static_cast( flags ) ) ); return createResultValue( result, "vk::CommandBuffer::reset" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void CommandBuffer::bindPipeline( PipelineBindPoint pipelineBindPoint, Pipeline pipeline ) const { vkCmdBindPipeline( m_commandBuffer, static_cast( pipelineBindPoint ), static_cast( pipeline ) ); } VULKAN_HPP_INLINE void CommandBuffer::setViewport( uint32_t firstViewport, uint32_t viewportCount, const Viewport* pViewports ) const { vkCmdSetViewport( m_commandBuffer, firstViewport, viewportCount, reinterpret_cast( pViewports ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void CommandBuffer::setViewport( uint32_t firstViewport, ArrayProxy viewports ) const { vkCmdSetViewport( m_commandBuffer, firstViewport, viewports.size() , reinterpret_cast( viewports.data() ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void CommandBuffer::setScissor( uint32_t firstScissor, uint32_t scissorCount, const Rect2D* pScissors ) const { vkCmdSetScissor( m_commandBuffer, firstScissor, scissorCount, reinterpret_cast( pScissors ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void CommandBuffer::setScissor( uint32_t firstScissor, ArrayProxy scissors ) const { vkCmdSetScissor( m_commandBuffer, firstScissor, scissors.size() , reinterpret_cast( scissors.data() ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void CommandBuffer::setLineWidth( float lineWidth ) const { vkCmdSetLineWidth( m_commandBuffer, lineWidth ); } VULKAN_HPP_INLINE void CommandBuffer::setDepthBias( float depthBiasConstantFactor, float depthBiasClamp, float depthBiasSlopeFactor ) const { vkCmdSetDepthBias( m_commandBuffer, depthBiasConstantFactor, depthBiasClamp, depthBiasSlopeFactor ); } VULKAN_HPP_INLINE void CommandBuffer::setBlendConstants( const float blendConstants[4] ) const { vkCmdSetBlendConstants( m_commandBuffer, blendConstants ); } VULKAN_HPP_INLINE void CommandBuffer::setDepthBounds( float minDepthBounds, float maxDepthBounds ) const { vkCmdSetDepthBounds( m_commandBuffer, minDepthBounds, maxDepthBounds ); } VULKAN_HPP_INLINE void CommandBuffer::setStencilCompareMask( StencilFaceFlags faceMask, uint32_t compareMask ) const { vkCmdSetStencilCompareMask( m_commandBuffer, static_cast( faceMask ), compareMask ); } VULKAN_HPP_INLINE void CommandBuffer::setStencilWriteMask( StencilFaceFlags faceMask, uint32_t writeMask ) const { vkCmdSetStencilWriteMask( m_commandBuffer, static_cast( faceMask ), writeMask ); } VULKAN_HPP_INLINE void CommandBuffer::setStencilReference( StencilFaceFlags faceMask, uint32_t reference ) const { vkCmdSetStencilReference( m_commandBuffer, static_cast( faceMask ), reference ); } VULKAN_HPP_INLINE void CommandBuffer::bindDescriptorSets( PipelineBindPoint pipelineBindPoint, PipelineLayout layout, uint32_t firstSet, uint32_t descriptorSetCount, const DescriptorSet* pDescriptorSets, uint32_t dynamicOffsetCount, const uint32_t* pDynamicOffsets ) const { vkCmdBindDescriptorSets( m_commandBuffer, static_cast( pipelineBindPoint ), static_cast( layout ), firstSet, descriptorSetCount, reinterpret_cast( pDescriptorSets ), dynamicOffsetCount, pDynamicOffsets ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void CommandBuffer::bindDescriptorSets( PipelineBindPoint pipelineBindPoint, PipelineLayout layout, uint32_t firstSet, ArrayProxy descriptorSets, ArrayProxy dynamicOffsets ) const { vkCmdBindDescriptorSets( m_commandBuffer, static_cast( pipelineBindPoint ), static_cast( layout ), firstSet, descriptorSets.size() , reinterpret_cast( descriptorSets.data() ), dynamicOffsets.size() , dynamicOffsets.data() ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void CommandBuffer::bindIndexBuffer( Buffer buffer, DeviceSize offset, IndexType indexType ) const { vkCmdBindIndexBuffer( m_commandBuffer, static_cast( buffer ), offset, static_cast( indexType ) ); } VULKAN_HPP_INLINE void CommandBuffer::bindVertexBuffers( uint32_t firstBinding, uint32_t bindingCount, const Buffer* pBuffers, const DeviceSize* pOffsets ) const { vkCmdBindVertexBuffers( m_commandBuffer, firstBinding, bindingCount, reinterpret_cast( pBuffers ), pOffsets ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void CommandBuffer::bindVertexBuffers( uint32_t firstBinding, ArrayProxy buffers, ArrayProxy offsets ) const { #ifdef VULKAN_HPP_NO_EXCEPTIONS assert( buffers.size() == offsets.size() ); #else if ( buffers.size() != offsets.size() ) { throw std::logic_error( "vk::CommandBuffer::bindVertexBuffers: buffers.size() != offsets.size()" ); } #endif // VULKAN_HPP_NO_EXCEPTIONS vkCmdBindVertexBuffers( m_commandBuffer, firstBinding, buffers.size() , reinterpret_cast( buffers.data() ), offsets.data() ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void CommandBuffer::draw( uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance ) const { vkCmdDraw( m_commandBuffer, vertexCount, instanceCount, firstVertex, firstInstance ); } VULKAN_HPP_INLINE void CommandBuffer::drawIndexed( uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance ) const { vkCmdDrawIndexed( m_commandBuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstance ); } VULKAN_HPP_INLINE void CommandBuffer::drawIndirect( Buffer buffer, DeviceSize offset, uint32_t drawCount, uint32_t stride ) const { vkCmdDrawIndirect( m_commandBuffer, static_cast( buffer ), offset, drawCount, stride ); } VULKAN_HPP_INLINE void CommandBuffer::drawIndexedIndirect( Buffer buffer, DeviceSize offset, uint32_t drawCount, uint32_t stride ) const { vkCmdDrawIndexedIndirect( m_commandBuffer, static_cast( buffer ), offset, drawCount, stride ); } VULKAN_HPP_INLINE void CommandBuffer::dispatch( uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ ) const { vkCmdDispatch( m_commandBuffer, groupCountX, groupCountY, groupCountZ ); } VULKAN_HPP_INLINE void CommandBuffer::dispatchIndirect( Buffer buffer, DeviceSize offset ) const { vkCmdDispatchIndirect( m_commandBuffer, static_cast( buffer ), offset ); } VULKAN_HPP_INLINE void CommandBuffer::copyBuffer( Buffer srcBuffer, Buffer dstBuffer, uint32_t regionCount, const BufferCopy* pRegions ) const { vkCmdCopyBuffer( m_commandBuffer, static_cast( srcBuffer ), static_cast( dstBuffer ), regionCount, reinterpret_cast( pRegions ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void CommandBuffer::copyBuffer( Buffer srcBuffer, Buffer dstBuffer, ArrayProxy regions ) const { vkCmdCopyBuffer( m_commandBuffer, static_cast( srcBuffer ), static_cast( dstBuffer ), regions.size() , reinterpret_cast( regions.data() ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void CommandBuffer::copyImage( Image srcImage, ImageLayout srcImageLayout, Image dstImage, ImageLayout dstImageLayout, uint32_t regionCount, const ImageCopy* pRegions ) const { vkCmdCopyImage( m_commandBuffer, static_cast( srcImage ), static_cast( srcImageLayout ), static_cast( dstImage ), static_cast( dstImageLayout ), regionCount, reinterpret_cast( pRegions ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void CommandBuffer::copyImage( Image srcImage, ImageLayout srcImageLayout, Image dstImage, ImageLayout dstImageLayout, ArrayProxy regions ) const { vkCmdCopyImage( m_commandBuffer, static_cast( srcImage ), static_cast( srcImageLayout ), static_cast( dstImage ), static_cast( dstImageLayout ), regions.size() , reinterpret_cast( regions.data() ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void CommandBuffer::blitImage( Image srcImage, ImageLayout srcImageLayout, Image dstImage, ImageLayout dstImageLayout, uint32_t regionCount, const ImageBlit* pRegions, Filter filter ) const { vkCmdBlitImage( m_commandBuffer, static_cast( srcImage ), static_cast( srcImageLayout ), static_cast( dstImage ), static_cast( dstImageLayout ), regionCount, reinterpret_cast( pRegions ), static_cast( filter ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void CommandBuffer::blitImage( Image srcImage, ImageLayout srcImageLayout, Image dstImage, ImageLayout dstImageLayout, ArrayProxy regions, Filter filter ) const { vkCmdBlitImage( m_commandBuffer, static_cast( srcImage ), static_cast( srcImageLayout ), static_cast( dstImage ), static_cast( dstImageLayout ), regions.size() , reinterpret_cast( regions.data() ), static_cast( filter ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void CommandBuffer::copyBufferToImage( Buffer srcBuffer, Image dstImage, ImageLayout dstImageLayout, uint32_t regionCount, const BufferImageCopy* pRegions ) const { vkCmdCopyBufferToImage( m_commandBuffer, static_cast( srcBuffer ), static_cast( dstImage ), static_cast( dstImageLayout ), regionCount, reinterpret_cast( pRegions ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void CommandBuffer::copyBufferToImage( Buffer srcBuffer, Image dstImage, ImageLayout dstImageLayout, ArrayProxy regions ) const { vkCmdCopyBufferToImage( m_commandBuffer, static_cast( srcBuffer ), static_cast( dstImage ), static_cast( dstImageLayout ), regions.size() , reinterpret_cast( regions.data() ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void CommandBuffer::copyImageToBuffer( Image srcImage, ImageLayout srcImageLayout, Buffer dstBuffer, uint32_t regionCount, const BufferImageCopy* pRegions ) const { vkCmdCopyImageToBuffer( m_commandBuffer, static_cast( srcImage ), static_cast( srcImageLayout ), static_cast( dstBuffer ), regionCount, reinterpret_cast( pRegions ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void CommandBuffer::copyImageToBuffer( Image srcImage, ImageLayout srcImageLayout, Buffer dstBuffer, ArrayProxy regions ) const { vkCmdCopyImageToBuffer( m_commandBuffer, static_cast( srcImage ), static_cast( srcImageLayout ), static_cast( dstBuffer ), regions.size() , reinterpret_cast( regions.data() ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void CommandBuffer::updateBuffer( Buffer dstBuffer, DeviceSize dstOffset, DeviceSize dataSize, const void* pData ) const { vkCmdUpdateBuffer( m_commandBuffer, static_cast( dstBuffer ), dstOffset, dataSize, pData ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template VULKAN_HPP_INLINE void CommandBuffer::updateBuffer( Buffer dstBuffer, DeviceSize dstOffset, ArrayProxy data ) const { vkCmdUpdateBuffer( m_commandBuffer, static_cast( dstBuffer ), dstOffset, data.size() * sizeof( T ) , reinterpret_cast( data.data() ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void CommandBuffer::fillBuffer( Buffer dstBuffer, DeviceSize dstOffset, DeviceSize size, uint32_t data ) const { vkCmdFillBuffer( m_commandBuffer, static_cast( dstBuffer ), dstOffset, size, data ); } VULKAN_HPP_INLINE void CommandBuffer::clearColorImage( Image image, ImageLayout imageLayout, const ClearColorValue* pColor, uint32_t rangeCount, const ImageSubresourceRange* pRanges ) const { vkCmdClearColorImage( m_commandBuffer, static_cast( image ), static_cast( imageLayout ), reinterpret_cast( pColor ), rangeCount, reinterpret_cast( pRanges ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void CommandBuffer::clearColorImage( Image image, ImageLayout imageLayout, const ClearColorValue & color, ArrayProxy ranges ) const { vkCmdClearColorImage( m_commandBuffer, static_cast( image ), static_cast( imageLayout ), reinterpret_cast( &color ), ranges.size() , reinterpret_cast( ranges.data() ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void CommandBuffer::clearDepthStencilImage( Image image, ImageLayout imageLayout, const ClearDepthStencilValue* pDepthStencil, uint32_t rangeCount, const ImageSubresourceRange* pRanges ) const { vkCmdClearDepthStencilImage( m_commandBuffer, static_cast( image ), static_cast( imageLayout ), reinterpret_cast( pDepthStencil ), rangeCount, reinterpret_cast( pRanges ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void CommandBuffer::clearDepthStencilImage( Image image, ImageLayout imageLayout, const ClearDepthStencilValue & depthStencil, ArrayProxy ranges ) const { vkCmdClearDepthStencilImage( m_commandBuffer, static_cast( image ), static_cast( imageLayout ), reinterpret_cast( &depthStencil ), ranges.size() , reinterpret_cast( ranges.data() ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void CommandBuffer::clearAttachments( uint32_t attachmentCount, const ClearAttachment* pAttachments, uint32_t rectCount, const ClearRect* pRects ) const { vkCmdClearAttachments( m_commandBuffer, attachmentCount, reinterpret_cast( pAttachments ), rectCount, reinterpret_cast( pRects ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void CommandBuffer::clearAttachments( ArrayProxy attachments, ArrayProxy rects ) const { vkCmdClearAttachments( m_commandBuffer, attachments.size() , reinterpret_cast( attachments.data() ), rects.size() , reinterpret_cast( rects.data() ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void CommandBuffer::resolveImage( Image srcImage, ImageLayout srcImageLayout, Image dstImage, ImageLayout dstImageLayout, uint32_t regionCount, const ImageResolve* pRegions ) const { vkCmdResolveImage( m_commandBuffer, static_cast( srcImage ), static_cast( srcImageLayout ), static_cast( dstImage ), static_cast( dstImageLayout ), regionCount, reinterpret_cast( pRegions ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void CommandBuffer::resolveImage( Image srcImage, ImageLayout srcImageLayout, Image dstImage, ImageLayout dstImageLayout, ArrayProxy regions ) const { vkCmdResolveImage( m_commandBuffer, static_cast( srcImage ), static_cast( srcImageLayout ), static_cast( dstImage ), static_cast( dstImageLayout ), regions.size() , reinterpret_cast( regions.data() ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void CommandBuffer::setEvent( Event event, PipelineStageFlags stageMask ) const { vkCmdSetEvent( m_commandBuffer, static_cast( event ), static_cast( stageMask ) ); } VULKAN_HPP_INLINE void CommandBuffer::resetEvent( Event event, PipelineStageFlags stageMask ) const { vkCmdResetEvent( m_commandBuffer, static_cast( event ), static_cast( stageMask ) ); } VULKAN_HPP_INLINE void CommandBuffer::waitEvents( uint32_t eventCount, const Event* pEvents, PipelineStageFlags srcStageMask, PipelineStageFlags dstStageMask, uint32_t memoryBarrierCount, const MemoryBarrier* pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const BufferMemoryBarrier* pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const ImageMemoryBarrier* pImageMemoryBarriers ) const { vkCmdWaitEvents( m_commandBuffer, eventCount, reinterpret_cast( pEvents ), static_cast( srcStageMask ), static_cast( dstStageMask ), memoryBarrierCount, reinterpret_cast( pMemoryBarriers ), bufferMemoryBarrierCount, reinterpret_cast( pBufferMemoryBarriers ), imageMemoryBarrierCount, reinterpret_cast( pImageMemoryBarriers ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void CommandBuffer::waitEvents( ArrayProxy events, PipelineStageFlags srcStageMask, PipelineStageFlags dstStageMask, ArrayProxy memoryBarriers, ArrayProxy bufferMemoryBarriers, ArrayProxy imageMemoryBarriers ) const { vkCmdWaitEvents( m_commandBuffer, events.size() , reinterpret_cast( events.data() ), static_cast( srcStageMask ), static_cast( dstStageMask ), memoryBarriers.size() , reinterpret_cast( memoryBarriers.data() ), bufferMemoryBarriers.size() , reinterpret_cast( bufferMemoryBarriers.data() ), imageMemoryBarriers.size() , reinterpret_cast( imageMemoryBarriers.data() ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void CommandBuffer::pipelineBarrier( PipelineStageFlags srcStageMask, PipelineStageFlags dstStageMask, DependencyFlags dependencyFlags, uint32_t memoryBarrierCount, const MemoryBarrier* pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const BufferMemoryBarrier* pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const ImageMemoryBarrier* pImageMemoryBarriers ) const { vkCmdPipelineBarrier( m_commandBuffer, static_cast( srcStageMask ), static_cast( dstStageMask ), static_cast( dependencyFlags ), memoryBarrierCount, reinterpret_cast( pMemoryBarriers ), bufferMemoryBarrierCount, reinterpret_cast( pBufferMemoryBarriers ), imageMemoryBarrierCount, reinterpret_cast( pImageMemoryBarriers ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void CommandBuffer::pipelineBarrier( PipelineStageFlags srcStageMask, PipelineStageFlags dstStageMask, DependencyFlags dependencyFlags, ArrayProxy memoryBarriers, ArrayProxy bufferMemoryBarriers, ArrayProxy imageMemoryBarriers ) const { vkCmdPipelineBarrier( m_commandBuffer, static_cast( srcStageMask ), static_cast( dstStageMask ), static_cast( dependencyFlags ), memoryBarriers.size() , reinterpret_cast( memoryBarriers.data() ), bufferMemoryBarriers.size() , reinterpret_cast( bufferMemoryBarriers.data() ), imageMemoryBarriers.size() , reinterpret_cast( imageMemoryBarriers.data() ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void CommandBuffer::beginQuery( QueryPool queryPool, uint32_t query, QueryControlFlags flags ) const { vkCmdBeginQuery( m_commandBuffer, static_cast( queryPool ), query, static_cast( flags ) ); } VULKAN_HPP_INLINE void CommandBuffer::endQuery( QueryPool queryPool, uint32_t query ) const { vkCmdEndQuery( m_commandBuffer, static_cast( queryPool ), query ); } VULKAN_HPP_INLINE void CommandBuffer::resetQueryPool( QueryPool queryPool, uint32_t firstQuery, uint32_t queryCount ) const { vkCmdResetQueryPool( m_commandBuffer, static_cast( queryPool ), firstQuery, queryCount ); } VULKAN_HPP_INLINE void CommandBuffer::writeTimestamp( PipelineStageFlagBits pipelineStage, QueryPool queryPool, uint32_t query ) const { vkCmdWriteTimestamp( m_commandBuffer, static_cast( pipelineStage ), static_cast( queryPool ), query ); } VULKAN_HPP_INLINE void CommandBuffer::copyQueryPoolResults( QueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, Buffer dstBuffer, DeviceSize dstOffset, DeviceSize stride, QueryResultFlags flags ) const { vkCmdCopyQueryPoolResults( m_commandBuffer, static_cast( queryPool ), firstQuery, queryCount, static_cast( dstBuffer ), dstOffset, stride, static_cast( flags ) ); } VULKAN_HPP_INLINE void CommandBuffer::pushConstants( PipelineLayout layout, ShaderStageFlags stageFlags, uint32_t offset, uint32_t size, const void* pValues ) const { vkCmdPushConstants( m_commandBuffer, static_cast( layout ), static_cast( stageFlags ), offset, size, pValues ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template VULKAN_HPP_INLINE void CommandBuffer::pushConstants( PipelineLayout layout, ShaderStageFlags stageFlags, uint32_t offset, ArrayProxy values ) const { vkCmdPushConstants( m_commandBuffer, static_cast( layout ), static_cast( stageFlags ), offset, values.size() * sizeof( T ) , reinterpret_cast( values.data() ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void CommandBuffer::beginRenderPass( const RenderPassBeginInfo* pRenderPassBegin, SubpassContents contents ) const { vkCmdBeginRenderPass( m_commandBuffer, reinterpret_cast( pRenderPassBegin ), static_cast( contents ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void CommandBuffer::beginRenderPass( const RenderPassBeginInfo & renderPassBegin, SubpassContents contents ) const { vkCmdBeginRenderPass( m_commandBuffer, reinterpret_cast( &renderPassBegin ), static_cast( contents ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void CommandBuffer::nextSubpass( SubpassContents contents ) const { vkCmdNextSubpass( m_commandBuffer, static_cast( contents ) ); } VULKAN_HPP_INLINE void CommandBuffer::endRenderPass() const { vkCmdEndRenderPass( m_commandBuffer ); } VULKAN_HPP_INLINE void CommandBuffer::executeCommands( uint32_t commandBufferCount, const CommandBuffer* pCommandBuffers ) const { vkCmdExecuteCommands( m_commandBuffer, commandBufferCount, reinterpret_cast( pCommandBuffers ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void CommandBuffer::executeCommands( ArrayProxy commandBuffers ) const { vkCmdExecuteCommands( m_commandBuffer, commandBuffers.size() , reinterpret_cast( commandBuffers.data() ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void CommandBuffer::debugMarkerBeginEXT( DebugMarkerMarkerInfoEXT* pMarkerInfo ) const { vkCmdDebugMarkerBeginEXT( m_commandBuffer, reinterpret_cast( pMarkerInfo ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE DebugMarkerMarkerInfoEXT CommandBuffer::debugMarkerBeginEXT() const { DebugMarkerMarkerInfoEXT markerInfo; vkCmdDebugMarkerBeginEXT( m_commandBuffer, reinterpret_cast( &markerInfo ) ); return markerInfo; } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void CommandBuffer::debugMarkerEndEXT() const { vkCmdDebugMarkerEndEXT( m_commandBuffer ); } VULKAN_HPP_INLINE void CommandBuffer::debugMarkerInsertEXT( DebugMarkerMarkerInfoEXT* pMarkerInfo ) const { vkCmdDebugMarkerInsertEXT( m_commandBuffer, reinterpret_cast( pMarkerInfo ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE DebugMarkerMarkerInfoEXT CommandBuffer::debugMarkerInsertEXT() const { DebugMarkerMarkerInfoEXT markerInfo; vkCmdDebugMarkerInsertEXT( m_commandBuffer, reinterpret_cast( &markerInfo ) ); return markerInfo; } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void CommandBuffer::drawIndirectCountAMD( Buffer buffer, DeviceSize offset, Buffer countBuffer, DeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride ) const { vkCmdDrawIndirectCountAMD( m_commandBuffer, static_cast( buffer ), offset, static_cast( countBuffer ), countBufferOffset, maxDrawCount, stride ); } VULKAN_HPP_INLINE void CommandBuffer::drawIndexedIndirectCountAMD( Buffer buffer, DeviceSize offset, Buffer countBuffer, DeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride ) const { vkCmdDrawIndexedIndirectCountAMD( m_commandBuffer, static_cast( buffer ), offset, static_cast( countBuffer ), countBufferOffset, maxDrawCount, stride ); } VULKAN_HPP_INLINE void CommandBuffer::processCommandsNVX( const CmdProcessCommandsInfoNVX* pProcessCommandsInfo ) const { vkCmdProcessCommandsNVX( m_commandBuffer, reinterpret_cast( pProcessCommandsInfo ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void CommandBuffer::processCommandsNVX( const CmdProcessCommandsInfoNVX & processCommandsInfo ) const { vkCmdProcessCommandsNVX( m_commandBuffer, reinterpret_cast( &processCommandsInfo ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void CommandBuffer::reserveSpaceForCommandsNVX( const CmdReserveSpaceForCommandsInfoNVX* pReserveSpaceInfo ) const { vkCmdReserveSpaceForCommandsNVX( m_commandBuffer, reinterpret_cast( pReserveSpaceInfo ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void CommandBuffer::reserveSpaceForCommandsNVX( const CmdReserveSpaceForCommandsInfoNVX & reserveSpaceInfo ) const { vkCmdReserveSpaceForCommandsNVX( m_commandBuffer, reinterpret_cast( &reserveSpaceInfo ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void CommandBuffer::pushDescriptorSetKHR( PipelineBindPoint pipelineBindPoint, PipelineLayout layout, uint32_t set, uint32_t descriptorWriteCount, const WriteDescriptorSet* pDescriptorWrites ) const { vkCmdPushDescriptorSetKHR( m_commandBuffer, static_cast( pipelineBindPoint ), static_cast( layout ), set, descriptorWriteCount, reinterpret_cast( pDescriptorWrites ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void CommandBuffer::pushDescriptorSetKHR( PipelineBindPoint pipelineBindPoint, PipelineLayout layout, uint32_t set, ArrayProxy descriptorWrites ) const { vkCmdPushDescriptorSetKHR( m_commandBuffer, static_cast( pipelineBindPoint ), static_cast( layout ), set, descriptorWrites.size() , reinterpret_cast( descriptorWrites.data() ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void CommandBuffer::setDeviceMaskKHX( uint32_t deviceMask ) const { vkCmdSetDeviceMaskKHX( m_commandBuffer, deviceMask ); } VULKAN_HPP_INLINE void CommandBuffer::dispatchBaseKHX( uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ ) const { vkCmdDispatchBaseKHX( m_commandBuffer, baseGroupX, baseGroupY, baseGroupZ, groupCountX, groupCountY, groupCountZ ); } VULKAN_HPP_INLINE void CommandBuffer::pushDescriptorSetWithTemplateKHR( DescriptorUpdateTemplateKHR descriptorUpdateTemplate, PipelineLayout layout, uint32_t set, const void* pData ) const { vkCmdPushDescriptorSetWithTemplateKHR( m_commandBuffer, static_cast( descriptorUpdateTemplate ), static_cast( layout ), set, pData ); } VULKAN_HPP_INLINE void CommandBuffer::setViewportWScalingNV( uint32_t firstViewport, uint32_t viewportCount, const ViewportWScalingNV* pViewportWScalings ) const { vkCmdSetViewportWScalingNV( m_commandBuffer, firstViewport, viewportCount, reinterpret_cast( pViewportWScalings ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void CommandBuffer::setViewportWScalingNV( uint32_t firstViewport, ArrayProxy viewportWScalings ) const { vkCmdSetViewportWScalingNV( m_commandBuffer, firstViewport, viewportWScalings.size() , reinterpret_cast( viewportWScalings.data() ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void CommandBuffer::setDiscardRectangleEXT( uint32_t firstDiscardRectangle, uint32_t discardRectangleCount, const Rect2D* pDiscardRectangles ) const { vkCmdSetDiscardRectangleEXT( m_commandBuffer, firstDiscardRectangle, discardRectangleCount, reinterpret_cast( pDiscardRectangles ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void CommandBuffer::setDiscardRectangleEXT( uint32_t firstDiscardRectangle, ArrayProxy discardRectangles ) const { vkCmdSetDiscardRectangleEXT( m_commandBuffer, firstDiscardRectangle, discardRectangles.size() , reinterpret_cast( discardRectangles.data() ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ struct SubmitInfo { SubmitInfo( uint32_t waitSemaphoreCount_ = 0, const Semaphore* pWaitSemaphores_ = nullptr, const PipelineStageFlags* pWaitDstStageMask_ = nullptr, uint32_t commandBufferCount_ = 0, const CommandBuffer* pCommandBuffers_ = nullptr, uint32_t signalSemaphoreCount_ = 0, const Semaphore* pSignalSemaphores_ = nullptr ) : sType( StructureType::eSubmitInfo ) , pNext( nullptr ) , waitSemaphoreCount( waitSemaphoreCount_ ) , pWaitSemaphores( pWaitSemaphores_ ) , pWaitDstStageMask( pWaitDstStageMask_ ) , commandBufferCount( commandBufferCount_ ) , pCommandBuffers( pCommandBuffers_ ) , signalSemaphoreCount( signalSemaphoreCount_ ) , pSignalSemaphores( pSignalSemaphores_ ) { } SubmitInfo( VkSubmitInfo const & rhs ) { memcpy( this, &rhs, sizeof(SubmitInfo) ); } SubmitInfo& operator=( VkSubmitInfo const & rhs ) { memcpy( this, &rhs, sizeof(SubmitInfo) ); return *this; } SubmitInfo& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } SubmitInfo& setWaitSemaphoreCount( uint32_t waitSemaphoreCount_ ) { waitSemaphoreCount = waitSemaphoreCount_; return *this; } SubmitInfo& setPWaitSemaphores( const Semaphore* pWaitSemaphores_ ) { pWaitSemaphores = pWaitSemaphores_; return *this; } SubmitInfo& setPWaitDstStageMask( const PipelineStageFlags* pWaitDstStageMask_ ) { pWaitDstStageMask = pWaitDstStageMask_; return *this; } SubmitInfo& setCommandBufferCount( uint32_t commandBufferCount_ ) { commandBufferCount = commandBufferCount_; return *this; } SubmitInfo& setPCommandBuffers( const CommandBuffer* pCommandBuffers_ ) { pCommandBuffers = pCommandBuffers_; return *this; } SubmitInfo& setSignalSemaphoreCount( uint32_t signalSemaphoreCount_ ) { signalSemaphoreCount = signalSemaphoreCount_; return *this; } SubmitInfo& setPSignalSemaphores( const Semaphore* pSignalSemaphores_ ) { pSignalSemaphores = pSignalSemaphores_; return *this; } operator const VkSubmitInfo&() const { return *reinterpret_cast(this); } bool operator==( SubmitInfo const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( waitSemaphoreCount == rhs.waitSemaphoreCount ) && ( pWaitSemaphores == rhs.pWaitSemaphores ) && ( pWaitDstStageMask == rhs.pWaitDstStageMask ) && ( commandBufferCount == rhs.commandBufferCount ) && ( pCommandBuffers == rhs.pCommandBuffers ) && ( signalSemaphoreCount == rhs.signalSemaphoreCount ) && ( pSignalSemaphores == rhs.pSignalSemaphores ); } bool operator!=( SubmitInfo const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; uint32_t waitSemaphoreCount; const Semaphore* pWaitSemaphores; const PipelineStageFlags* pWaitDstStageMask; uint32_t commandBufferCount; const CommandBuffer* pCommandBuffers; uint32_t signalSemaphoreCount; const Semaphore* pSignalSemaphores; }; static_assert( sizeof( SubmitInfo ) == sizeof( VkSubmitInfo ), "struct and wrapper have different size!" ); class Queue { public: Queue() : m_queue(VK_NULL_HANDLE) {} Queue( std::nullptr_t ) : m_queue(VK_NULL_HANDLE) {} VULKAN_HPP_TYPESAFE_EXPLICIT Queue(VkQueue queue) : m_queue(queue) {} #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) Queue& operator=(VkQueue queue) { m_queue = queue; return *this; } #endif Queue& operator=( std::nullptr_t ) { m_queue = VK_NULL_HANDLE; return *this; } bool operator==(Queue const &rhs) const { return m_queue == rhs.m_queue; } bool operator!=(Queue const &rhs) const { return m_queue != rhs.m_queue; } bool operator<(Queue const &rhs) const { return m_queue < rhs.m_queue; } Result submit( uint32_t submitCount, const SubmitInfo* pSubmits, Fence fence ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type submit( ArrayProxy submits, Fence fence ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE Result waitIdle() const; #else ResultValueType::type waitIdle() const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result bindSparse( uint32_t bindInfoCount, const BindSparseInfo* pBindInfo, Fence fence ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type bindSparse( ArrayProxy bindInfo, Fence fence ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result presentKHR( const PresentInfoKHR* pPresentInfo ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE Result presentKHR( const PresentInfoKHR & presentInfo ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_TYPESAFE_EXPLICIT operator VkQueue() const { return m_queue; } explicit operator bool() const { return m_queue != VK_NULL_HANDLE; } bool operator!() const { return m_queue == VK_NULL_HANDLE; } private: VkQueue m_queue; }; static_assert( sizeof( Queue ) == sizeof( VkQueue ), "handle and wrapper have different size!" ); VULKAN_HPP_INLINE Result Queue::submit( uint32_t submitCount, const SubmitInfo* pSubmits, Fence fence ) const { return static_cast( vkQueueSubmit( m_queue, submitCount, reinterpret_cast( pSubmits ), static_cast( fence ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Queue::submit( ArrayProxy submits, Fence fence ) const { Result result = static_cast( vkQueueSubmit( m_queue, submits.size() , reinterpret_cast( submits.data() ), static_cast( fence ) ) ); return createResultValue( result, "vk::Queue::submit" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE Result Queue::waitIdle() const { return static_cast( vkQueueWaitIdle( m_queue ) ); } #else VULKAN_HPP_INLINE ResultValueType::type Queue::waitIdle() const { Result result = static_cast( vkQueueWaitIdle( m_queue ) ); return createResultValue( result, "vk::Queue::waitIdle" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Queue::bindSparse( uint32_t bindInfoCount, const BindSparseInfo* pBindInfo, Fence fence ) const { return static_cast( vkQueueBindSparse( m_queue, bindInfoCount, reinterpret_cast( pBindInfo ), static_cast( fence ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Queue::bindSparse( ArrayProxy bindInfo, Fence fence ) const { Result result = static_cast( vkQueueBindSparse( m_queue, bindInfo.size() , reinterpret_cast( bindInfo.data() ), static_cast( fence ) ) ); return createResultValue( result, "vk::Queue::bindSparse" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Queue::presentKHR( const PresentInfoKHR* pPresentInfo ) const { return static_cast( vkQueuePresentKHR( m_queue, reinterpret_cast( pPresentInfo ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE Result Queue::presentKHR( const PresentInfoKHR & presentInfo ) const { Result result = static_cast( vkQueuePresentKHR( m_queue, reinterpret_cast( &presentInfo ) ) ); return createResultValue( result, "vk::Queue::presentKHR", { Result::eSuccess, Result::eSuboptimalKHR } ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifndef VULKAN_HPP_NO_SMART_HANDLE class BufferDeleter; using UniqueBuffer = UniqueHandle; class BufferViewDeleter; using UniqueBufferView = UniqueHandle; class CommandBufferDeleter; using UniqueCommandBuffer = UniqueHandle; class CommandPoolDeleter; using UniqueCommandPool = UniqueHandle; class DescriptorPoolDeleter; using UniqueDescriptorPool = UniqueHandle; class DescriptorSetDeleter; using UniqueDescriptorSet = UniqueHandle; class DescriptorSetLayoutDeleter; using UniqueDescriptorSetLayout = UniqueHandle; class DescriptorUpdateTemplateKHRDeleter; using UniqueDescriptorUpdateTemplateKHR = UniqueHandle; class DeviceMemoryDeleter; using UniqueDeviceMemory = UniqueHandle; class EventDeleter; using UniqueEvent = UniqueHandle; class FenceDeleter; using UniqueFence = UniqueHandle; class FramebufferDeleter; using UniqueFramebuffer = UniqueHandle; class ImageDeleter; using UniqueImage = UniqueHandle; class ImageViewDeleter; using UniqueImageView = UniqueHandle; class IndirectCommandsLayoutNVXDeleter; using UniqueIndirectCommandsLayoutNVX = UniqueHandle; class ObjectTableNVXDeleter; using UniqueObjectTableNVX = UniqueHandle; class PipelineDeleter; using UniquePipeline = UniqueHandle; class PipelineCacheDeleter; using UniquePipelineCache = UniqueHandle; class PipelineLayoutDeleter; using UniquePipelineLayout = UniqueHandle; class QueryPoolDeleter; using UniqueQueryPool = UniqueHandle; class RenderPassDeleter; using UniqueRenderPass = UniqueHandle; class SamplerDeleter; using UniqueSampler = UniqueHandle; class SemaphoreDeleter; using UniqueSemaphore = UniqueHandle; class ShaderModuleDeleter; using UniqueShaderModule = UniqueHandle; class SwapchainKHRDeleter; using UniqueSwapchainKHR = UniqueHandle; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ class Device { public: Device() : m_device(VK_NULL_HANDLE) {} Device( std::nullptr_t ) : m_device(VK_NULL_HANDLE) {} VULKAN_HPP_TYPESAFE_EXPLICIT Device(VkDevice device) : m_device(device) {} #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) Device& operator=(VkDevice device) { m_device = device; return *this; } #endif Device& operator=( std::nullptr_t ) { m_device = VK_NULL_HANDLE; return *this; } bool operator==(Device const &rhs) const { return m_device == rhs.m_device; } bool operator!=(Device const &rhs) const { return m_device != rhs.m_device; } bool operator<(Device const &rhs) const { return m_device < rhs.m_device; } PFN_vkVoidFunction getProcAddr( const char* pName ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE PFN_vkVoidFunction getProcAddr( const std::string & name ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void destroy( const AllocationCallbacks* pAllocator ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void destroy( Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void getQueue( uint32_t queueFamilyIndex, uint32_t queueIndex, Queue* pQueue ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE Queue getQueue( uint32_t queueFamilyIndex, uint32_t queueIndex ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE Result waitIdle() const; #else ResultValueType::type waitIdle() const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result allocateMemory( const MemoryAllocateInfo* pAllocateInfo, const AllocationCallbacks* pAllocator, DeviceMemory* pMemory ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type allocateMemory( const MemoryAllocateInfo & allocateInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE UniqueDeviceMemory allocateMemoryUnique( const MemoryAllocateInfo & allocateInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void freeMemory( DeviceMemory memory, const AllocationCallbacks* pAllocator ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void freeMemory( DeviceMemory memory, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result mapMemory( DeviceMemory memory, DeviceSize offset, DeviceSize size, MemoryMapFlags flags, void** ppData ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type mapMemory( DeviceMemory memory, DeviceSize offset, DeviceSize size, MemoryMapFlags flags = MemoryMapFlags() ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void unmapMemory( DeviceMemory memory ) const; Result flushMappedMemoryRanges( uint32_t memoryRangeCount, const MappedMemoryRange* pMemoryRanges ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type flushMappedMemoryRanges( ArrayProxy memoryRanges ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result invalidateMappedMemoryRanges( uint32_t memoryRangeCount, const MappedMemoryRange* pMemoryRanges ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type invalidateMappedMemoryRanges( ArrayProxy memoryRanges ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void getMemoryCommitment( DeviceMemory memory, DeviceSize* pCommittedMemoryInBytes ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE DeviceSize getMemoryCommitment( DeviceMemory memory ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void getBufferMemoryRequirements( Buffer buffer, MemoryRequirements* pMemoryRequirements ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE MemoryRequirements getBufferMemoryRequirements( Buffer buffer ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE Result bindBufferMemory( Buffer buffer, DeviceMemory memory, DeviceSize memoryOffset ) const; #else ResultValueType::type bindBufferMemory( Buffer buffer, DeviceMemory memory, DeviceSize memoryOffset ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void getImageMemoryRequirements( Image image, MemoryRequirements* pMemoryRequirements ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE MemoryRequirements getImageMemoryRequirements( Image image ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE Result bindImageMemory( Image image, DeviceMemory memory, DeviceSize memoryOffset ) const; #else ResultValueType::type bindImageMemory( Image image, DeviceMemory memory, DeviceSize memoryOffset ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void getImageSparseMemoryRequirements( Image image, uint32_t* pSparseMemoryRequirementCount, SparseImageMemoryRequirements* pSparseMemoryRequirements ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template > std::vector getImageSparseMemoryRequirements( Image image ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result createFence( const FenceCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, Fence* pFence ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type createFence( const FenceCreateInfo & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE UniqueFence createFenceUnique( const FenceCreateInfo & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void destroyFence( Fence fence, const AllocationCallbacks* pAllocator ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void destroyFence( Fence fence, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result resetFences( uint32_t fenceCount, const Fence* pFences ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type resetFences( ArrayProxy fences ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result getFenceStatus( Fence fence ) const; Result waitForFences( uint32_t fenceCount, const Fence* pFences, Bool32 waitAll, uint64_t timeout ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE Result waitForFences( ArrayProxy fences, Bool32 waitAll, uint64_t timeout ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result createSemaphore( const SemaphoreCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, Semaphore* pSemaphore ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type createSemaphore( const SemaphoreCreateInfo & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE UniqueSemaphore createSemaphoreUnique( const SemaphoreCreateInfo & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void destroySemaphore( Semaphore semaphore, const AllocationCallbacks* pAllocator ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void destroySemaphore( Semaphore semaphore, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result createEvent( const EventCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, Event* pEvent ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type createEvent( const EventCreateInfo & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE UniqueEvent createEventUnique( const EventCreateInfo & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void destroyEvent( Event event, const AllocationCallbacks* pAllocator ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void destroyEvent( Event event, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result getEventStatus( Event event ) const; #ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE Result setEvent( Event event ) const; #else ResultValueType::type setEvent( Event event ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE Result resetEvent( Event event ) const; #else ResultValueType::type resetEvent( Event event ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result createQueryPool( const QueryPoolCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, QueryPool* pQueryPool ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type createQueryPool( const QueryPoolCreateInfo & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE UniqueQueryPool createQueryPoolUnique( const QueryPoolCreateInfo & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void destroyQueryPool( QueryPool queryPool, const AllocationCallbacks* pAllocator ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void destroyQueryPool( QueryPool queryPool, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result getQueryPoolResults( QueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, size_t dataSize, void* pData, DeviceSize stride, QueryResultFlags flags ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template Result getQueryPoolResults( QueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, ArrayProxy data, DeviceSize stride, QueryResultFlags flags ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result createBuffer( const BufferCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, Buffer* pBuffer ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type createBuffer( const BufferCreateInfo & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE UniqueBuffer createBufferUnique( const BufferCreateInfo & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void destroyBuffer( Buffer buffer, const AllocationCallbacks* pAllocator ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void destroyBuffer( Buffer buffer, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result createBufferView( const BufferViewCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, BufferView* pView ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type createBufferView( const BufferViewCreateInfo & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE UniqueBufferView createBufferViewUnique( const BufferViewCreateInfo & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void destroyBufferView( BufferView bufferView, const AllocationCallbacks* pAllocator ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void destroyBufferView( BufferView bufferView, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result createImage( const ImageCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, Image* pImage ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type createImage( const ImageCreateInfo & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE UniqueImage createImageUnique( const ImageCreateInfo & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void destroyImage( Image image, const AllocationCallbacks* pAllocator ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void destroyImage( Image image, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void getImageSubresourceLayout( Image image, const ImageSubresource* pSubresource, SubresourceLayout* pLayout ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE SubresourceLayout getImageSubresourceLayout( Image image, const ImageSubresource & subresource ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result createImageView( const ImageViewCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, ImageView* pView ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type createImageView( const ImageViewCreateInfo & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE UniqueImageView createImageViewUnique( const ImageViewCreateInfo & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void destroyImageView( ImageView imageView, const AllocationCallbacks* pAllocator ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void destroyImageView( ImageView imageView, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result createShaderModule( const ShaderModuleCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, ShaderModule* pShaderModule ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type createShaderModule( const ShaderModuleCreateInfo & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE UniqueShaderModule createShaderModuleUnique( const ShaderModuleCreateInfo & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void destroyShaderModule( ShaderModule shaderModule, const AllocationCallbacks* pAllocator ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void destroyShaderModule( ShaderModule shaderModule, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result createPipelineCache( const PipelineCacheCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, PipelineCache* pPipelineCache ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type createPipelineCache( const PipelineCacheCreateInfo & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE UniquePipelineCache createPipelineCacheUnique( const PipelineCacheCreateInfo & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void destroyPipelineCache( PipelineCache pipelineCache, const AllocationCallbacks* pAllocator ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void destroyPipelineCache( PipelineCache pipelineCache, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result getPipelineCacheData( PipelineCache pipelineCache, size_t* pDataSize, void* pData ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template > typename ResultValueType>::type getPipelineCacheData( PipelineCache pipelineCache ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result mergePipelineCaches( PipelineCache dstCache, uint32_t srcCacheCount, const PipelineCache* pSrcCaches ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type mergePipelineCaches( PipelineCache dstCache, ArrayProxy srcCaches ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result createGraphicsPipelines( PipelineCache pipelineCache, uint32_t createInfoCount, const GraphicsPipelineCreateInfo* pCreateInfos, const AllocationCallbacks* pAllocator, Pipeline* pPipelines ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template > typename ResultValueType>::type createGraphicsPipelines( PipelineCache pipelineCache, ArrayProxy createInfos, Optional allocator = nullptr ) const; ResultValueType::type createGraphicsPipeline( PipelineCache pipelineCache, const GraphicsPipelineCreateInfo & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE template > std::vector createGraphicsPipelinesUnique( PipelineCache pipelineCache, ArrayProxy createInfos, Optional allocator = nullptr ) const; UniquePipeline createGraphicsPipelineUnique( PipelineCache pipelineCache, const GraphicsPipelineCreateInfo & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result createComputePipelines( PipelineCache pipelineCache, uint32_t createInfoCount, const ComputePipelineCreateInfo* pCreateInfos, const AllocationCallbacks* pAllocator, Pipeline* pPipelines ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template > typename ResultValueType>::type createComputePipelines( PipelineCache pipelineCache, ArrayProxy createInfos, Optional allocator = nullptr ) const; ResultValueType::type createComputePipeline( PipelineCache pipelineCache, const ComputePipelineCreateInfo & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE template > std::vector createComputePipelinesUnique( PipelineCache pipelineCache, ArrayProxy createInfos, Optional allocator = nullptr ) const; UniquePipeline createComputePipelineUnique( PipelineCache pipelineCache, const ComputePipelineCreateInfo & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void destroyPipeline( Pipeline pipeline, const AllocationCallbacks* pAllocator ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void destroyPipeline( Pipeline pipeline, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result createPipelineLayout( const PipelineLayoutCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, PipelineLayout* pPipelineLayout ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type createPipelineLayout( const PipelineLayoutCreateInfo & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE UniquePipelineLayout createPipelineLayoutUnique( const PipelineLayoutCreateInfo & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void destroyPipelineLayout( PipelineLayout pipelineLayout, const AllocationCallbacks* pAllocator ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void destroyPipelineLayout( PipelineLayout pipelineLayout, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result createSampler( const SamplerCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, Sampler* pSampler ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type createSampler( const SamplerCreateInfo & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE UniqueSampler createSamplerUnique( const SamplerCreateInfo & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void destroySampler( Sampler sampler, const AllocationCallbacks* pAllocator ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void destroySampler( Sampler sampler, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result createDescriptorSetLayout( const DescriptorSetLayoutCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, DescriptorSetLayout* pSetLayout ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type createDescriptorSetLayout( const DescriptorSetLayoutCreateInfo & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE UniqueDescriptorSetLayout createDescriptorSetLayoutUnique( const DescriptorSetLayoutCreateInfo & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void destroyDescriptorSetLayout( DescriptorSetLayout descriptorSetLayout, const AllocationCallbacks* pAllocator ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void destroyDescriptorSetLayout( DescriptorSetLayout descriptorSetLayout, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result createDescriptorPool( const DescriptorPoolCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, DescriptorPool* pDescriptorPool ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type createDescriptorPool( const DescriptorPoolCreateInfo & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE UniqueDescriptorPool createDescriptorPoolUnique( const DescriptorPoolCreateInfo & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void destroyDescriptorPool( DescriptorPool descriptorPool, const AllocationCallbacks* pAllocator ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void destroyDescriptorPool( DescriptorPool descriptorPool, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE Result resetDescriptorPool( DescriptorPool descriptorPool, DescriptorPoolResetFlags flags ) const; #else ResultValueType::type resetDescriptorPool( DescriptorPool descriptorPool, DescriptorPoolResetFlags flags = DescriptorPoolResetFlags() ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result allocateDescriptorSets( const DescriptorSetAllocateInfo* pAllocateInfo, DescriptorSet* pDescriptorSets ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template > typename ResultValueType>::type allocateDescriptorSets( const DescriptorSetAllocateInfo & allocateInfo ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE template > std::vector allocateDescriptorSetsUnique( const DescriptorSetAllocateInfo & allocateInfo ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result freeDescriptorSets( DescriptorPool descriptorPool, uint32_t descriptorSetCount, const DescriptorSet* pDescriptorSets ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type freeDescriptorSets( DescriptorPool descriptorPool, ArrayProxy descriptorSets ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void updateDescriptorSets( uint32_t descriptorWriteCount, const WriteDescriptorSet* pDescriptorWrites, uint32_t descriptorCopyCount, const CopyDescriptorSet* pDescriptorCopies ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void updateDescriptorSets( ArrayProxy descriptorWrites, ArrayProxy descriptorCopies ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result createFramebuffer( const FramebufferCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, Framebuffer* pFramebuffer ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type createFramebuffer( const FramebufferCreateInfo & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE UniqueFramebuffer createFramebufferUnique( const FramebufferCreateInfo & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void destroyFramebuffer( Framebuffer framebuffer, const AllocationCallbacks* pAllocator ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void destroyFramebuffer( Framebuffer framebuffer, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result createRenderPass( const RenderPassCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, RenderPass* pRenderPass ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type createRenderPass( const RenderPassCreateInfo & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE UniqueRenderPass createRenderPassUnique( const RenderPassCreateInfo & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void destroyRenderPass( RenderPass renderPass, const AllocationCallbacks* pAllocator ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void destroyRenderPass( RenderPass renderPass, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void getRenderAreaGranularity( RenderPass renderPass, Extent2D* pGranularity ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE Extent2D getRenderAreaGranularity( RenderPass renderPass ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result createCommandPool( const CommandPoolCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, CommandPool* pCommandPool ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type createCommandPool( const CommandPoolCreateInfo & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE UniqueCommandPool createCommandPoolUnique( const CommandPoolCreateInfo & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void destroyCommandPool( CommandPool commandPool, const AllocationCallbacks* pAllocator ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void destroyCommandPool( CommandPool commandPool, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE Result resetCommandPool( CommandPool commandPool, CommandPoolResetFlags flags ) const; #else ResultValueType::type resetCommandPool( CommandPool commandPool, CommandPoolResetFlags flags ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result allocateCommandBuffers( const CommandBufferAllocateInfo* pAllocateInfo, CommandBuffer* pCommandBuffers ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template > typename ResultValueType>::type allocateCommandBuffers( const CommandBufferAllocateInfo & allocateInfo ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE template > std::vector allocateCommandBuffersUnique( const CommandBufferAllocateInfo & allocateInfo ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void freeCommandBuffers( CommandPool commandPool, uint32_t commandBufferCount, const CommandBuffer* pCommandBuffers ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void freeCommandBuffers( CommandPool commandPool, ArrayProxy commandBuffers ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result createSharedSwapchainsKHR( uint32_t swapchainCount, const SwapchainCreateInfoKHR* pCreateInfos, const AllocationCallbacks* pAllocator, SwapchainKHR* pSwapchains ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template > typename ResultValueType>::type createSharedSwapchainsKHR( ArrayProxy createInfos, Optional allocator = nullptr ) const; ResultValueType::type createSharedSwapchainKHR( const SwapchainCreateInfoKHR & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE template > std::vector createSharedSwapchainsKHRUnique( ArrayProxy createInfos, Optional allocator = nullptr ) const; UniqueSwapchainKHR createSharedSwapchainKHRUnique( const SwapchainCreateInfoKHR & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result createSwapchainKHR( const SwapchainCreateInfoKHR* pCreateInfo, const AllocationCallbacks* pAllocator, SwapchainKHR* pSwapchain ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type createSwapchainKHR( const SwapchainCreateInfoKHR & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE UniqueSwapchainKHR createSwapchainKHRUnique( const SwapchainCreateInfoKHR & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void destroySwapchainKHR( SwapchainKHR swapchain, const AllocationCallbacks* pAllocator ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void destroySwapchainKHR( SwapchainKHR swapchain, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result getSwapchainImagesKHR( SwapchainKHR swapchain, uint32_t* pSwapchainImageCount, Image* pSwapchainImages ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template > typename ResultValueType>::type getSwapchainImagesKHR( SwapchainKHR swapchain ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result acquireNextImageKHR( SwapchainKHR swapchain, uint64_t timeout, Semaphore semaphore, Fence fence, uint32_t* pImageIndex ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValue acquireNextImageKHR( SwapchainKHR swapchain, uint64_t timeout, Semaphore semaphore, Fence fence ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result debugMarkerSetObjectNameEXT( DebugMarkerObjectNameInfoEXT* pNameInfo ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type debugMarkerSetObjectNameEXT() const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result debugMarkerSetObjectTagEXT( DebugMarkerObjectTagInfoEXT* pTagInfo ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type debugMarkerSetObjectTagEXT() const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VK_USE_PLATFORM_WIN32_KHR Result getMemoryWin32HandleNV( DeviceMemory memory, ExternalMemoryHandleTypeFlagsNV handleType, HANDLE* pHandle ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type getMemoryWin32HandleNV( DeviceMemory memory, ExternalMemoryHandleTypeFlagsNV handleType ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_WIN32_KHR*/ Result createIndirectCommandsLayoutNVX( const IndirectCommandsLayoutCreateInfoNVX* pCreateInfo, const AllocationCallbacks* pAllocator, IndirectCommandsLayoutNVX* pIndirectCommandsLayout ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type createIndirectCommandsLayoutNVX( const IndirectCommandsLayoutCreateInfoNVX & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE UniqueIndirectCommandsLayoutNVX createIndirectCommandsLayoutNVXUnique( const IndirectCommandsLayoutCreateInfoNVX & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void destroyIndirectCommandsLayoutNVX( IndirectCommandsLayoutNVX indirectCommandsLayout, const AllocationCallbacks* pAllocator ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void destroyIndirectCommandsLayoutNVX( IndirectCommandsLayoutNVX indirectCommandsLayout, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result createObjectTableNVX( const ObjectTableCreateInfoNVX* pCreateInfo, const AllocationCallbacks* pAllocator, ObjectTableNVX* pObjectTable ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type createObjectTableNVX( const ObjectTableCreateInfoNVX & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE UniqueObjectTableNVX createObjectTableNVXUnique( const ObjectTableCreateInfoNVX & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void destroyObjectTableNVX( ObjectTableNVX objectTable, const AllocationCallbacks* pAllocator ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void destroyObjectTableNVX( ObjectTableNVX objectTable, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result registerObjectsNVX( ObjectTableNVX objectTable, uint32_t objectCount, const ObjectTableEntryNVX* const* ppObjectTableEntries, const uint32_t* pObjectIndices ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type registerObjectsNVX( ObjectTableNVX objectTable, ArrayProxy pObjectTableEntries, ArrayProxy objectIndices ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result unregisterObjectsNVX( ObjectTableNVX objectTable, uint32_t objectCount, const ObjectEntryTypeNVX* pObjectEntryTypes, const uint32_t* pObjectIndices ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type unregisterObjectsNVX( ObjectTableNVX objectTable, ArrayProxy objectEntryTypes, ArrayProxy objectIndices ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE void trimCommandPoolKHR( CommandPool commandPool, CommandPoolTrimFlagsKHR flags ) const; #else void trimCommandPoolKHR( CommandPool commandPool, CommandPoolTrimFlagsKHR flags = CommandPoolTrimFlagsKHR() ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VK_USE_PLATFORM_WIN32_KHX Result getMemoryWin32HandleKHX( DeviceMemory memory, ExternalMemoryHandleTypeFlagBitsKHX handleType, HANDLE* pHandle ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type getMemoryWin32HandleKHX( DeviceMemory memory, ExternalMemoryHandleTypeFlagBitsKHX handleType ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_WIN32_KHX*/ #ifdef VK_USE_PLATFORM_WIN32_KHX Result getMemoryWin32HandlePropertiesKHX( ExternalMemoryHandleTypeFlagBitsKHX handleType, HANDLE handle, MemoryWin32HandlePropertiesKHX* pMemoryWin32HandleProperties ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type getMemoryWin32HandlePropertiesKHX( ExternalMemoryHandleTypeFlagBitsKHX handleType, HANDLE handle ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_WIN32_KHX*/ Result getMemoryFdKHX( DeviceMemory memory, ExternalMemoryHandleTypeFlagBitsKHX handleType, int* pFd ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type getMemoryFdKHX( DeviceMemory memory, ExternalMemoryHandleTypeFlagBitsKHX handleType ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result getMemoryFdPropertiesKHX( ExternalMemoryHandleTypeFlagBitsKHX handleType, int fd, MemoryFdPropertiesKHX* pMemoryFdProperties ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type getMemoryFdPropertiesKHX( ExternalMemoryHandleTypeFlagBitsKHX handleType, int fd ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VK_USE_PLATFORM_WIN32_KHX Result getSemaphoreWin32HandleKHX( Semaphore semaphore, ExternalSemaphoreHandleTypeFlagBitsKHX handleType, HANDLE* pHandle ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type getSemaphoreWin32HandleKHX( Semaphore semaphore, ExternalSemaphoreHandleTypeFlagBitsKHX handleType ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_WIN32_KHX*/ #ifdef VK_USE_PLATFORM_WIN32_KHX Result importSemaphoreWin32HandleKHX( const ImportSemaphoreWin32HandleInfoKHX* pImportSemaphoreWin32HandleInfo ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type importSemaphoreWin32HandleKHX( const ImportSemaphoreWin32HandleInfoKHX & importSemaphoreWin32HandleInfo ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_WIN32_KHX*/ Result getSemaphoreFdKHX( Semaphore semaphore, ExternalSemaphoreHandleTypeFlagBitsKHX handleType, int* pFd ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type getSemaphoreFdKHX( Semaphore semaphore, ExternalSemaphoreHandleTypeFlagBitsKHX handleType ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result importSemaphoreFdKHX( const ImportSemaphoreFdInfoKHX* pImportSemaphoreFdInfo ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type importSemaphoreFdKHX( const ImportSemaphoreFdInfoKHX & importSemaphoreFdInfo ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result displayPowerControlEXT( DisplayKHR display, const DisplayPowerInfoEXT* pDisplayPowerInfo ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type displayPowerControlEXT( DisplayKHR display, const DisplayPowerInfoEXT & displayPowerInfo ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result registerEventEXT( const DeviceEventInfoEXT* pDeviceEventInfo, const AllocationCallbacks* pAllocator, Fence* pFence ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type registerEventEXT( const DeviceEventInfoEXT & deviceEventInfo, const AllocationCallbacks & allocator ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result registerDisplayEventEXT( DisplayKHR display, const DisplayEventInfoEXT* pDisplayEventInfo, const AllocationCallbacks* pAllocator, Fence* pFence ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type registerDisplayEventEXT( DisplayKHR display, const DisplayEventInfoEXT & displayEventInfo, const AllocationCallbacks & allocator ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result getSwapchainCounterEXT( SwapchainKHR swapchain, SurfaceCounterFlagBitsEXT counter, uint64_t* pCounterValue ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValue getSwapchainCounterEXT( SwapchainKHR swapchain, SurfaceCounterFlagBitsEXT counter ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void getGroupPeerMemoryFeaturesKHX( uint32_t heapIndex, uint32_t localDeviceIndex, uint32_t remoteDeviceIndex, PeerMemoryFeatureFlagsKHX* pPeerMemoryFeatures ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE PeerMemoryFeatureFlagsKHX getGroupPeerMemoryFeaturesKHX( uint32_t heapIndex, uint32_t localDeviceIndex, uint32_t remoteDeviceIndex ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result bindBufferMemory2KHX( uint32_t bindInfoCount, const BindBufferMemoryInfoKHX* pBindInfos ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type bindBufferMemory2KHX( ArrayProxy bindInfos ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result bindImageMemory2KHX( uint32_t bindInfoCount, const BindImageMemoryInfoKHX* pBindInfos ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type bindImageMemory2KHX( ArrayProxy bindInfos ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result getGroupPresentCapabilitiesKHX( DeviceGroupPresentCapabilitiesKHX* pDeviceGroupPresentCapabilities ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type getGroupPresentCapabilitiesKHX() const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result getGroupSurfacePresentModesKHX( SurfaceKHR surface, DeviceGroupPresentModeFlagsKHX* pModes ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type getGroupSurfacePresentModesKHX( SurfaceKHR surface ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result acquireNextImage2KHX( const AcquireNextImageInfoKHX* pAcquireInfo, uint32_t* pImageIndex ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValue acquireNextImage2KHX( const AcquireNextImageInfoKHX & acquireInfo ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result createDescriptorUpdateTemplateKHR( const DescriptorUpdateTemplateCreateInfoKHR* pCreateInfo, const AllocationCallbacks* pAllocator, DescriptorUpdateTemplateKHR* pDescriptorUpdateTemplate ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type createDescriptorUpdateTemplateKHR( const DescriptorUpdateTemplateCreateInfoKHR & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE UniqueDescriptorUpdateTemplateKHR createDescriptorUpdateTemplateKHRUnique( const DescriptorUpdateTemplateCreateInfoKHR & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void destroyDescriptorUpdateTemplateKHR( DescriptorUpdateTemplateKHR descriptorUpdateTemplate, const AllocationCallbacks* pAllocator ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void destroyDescriptorUpdateTemplateKHR( DescriptorUpdateTemplateKHR descriptorUpdateTemplate, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void updateDescriptorSetWithTemplateKHR( DescriptorSet descriptorSet, DescriptorUpdateTemplateKHR descriptorUpdateTemplate, const void* pData ) const; void setHdrMetadataEXT( uint32_t swapchainCount, const SwapchainKHR* pSwapchains, const HdrMetadataEXT* pMetadata ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void setHdrMetadataEXT( ArrayProxy swapchains, ArrayProxy metadata ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result getSwapchainStatusKHR( SwapchainKHR swapchain ) const; Result getRefreshCycleDurationGOOGLE( SwapchainKHR swapchain, RefreshCycleDurationGOOGLE* pDisplayTimingProperties ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type getRefreshCycleDurationGOOGLE( SwapchainKHR swapchain ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result getPastPresentationTimingGOOGLE( SwapchainKHR swapchain, uint32_t* pPresentationTimingCount, PastPresentationTimingGOOGLE* pPresentationTimings ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template > typename ResultValueType>::type getPastPresentationTimingGOOGLE( SwapchainKHR swapchain ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_TYPESAFE_EXPLICIT operator VkDevice() const { return m_device; } explicit operator bool() const { return m_device != VK_NULL_HANDLE; } bool operator!() const { return m_device == VK_NULL_HANDLE; } private: VkDevice m_device; }; static_assert( sizeof( Device ) == sizeof( VkDevice ), "handle and wrapper have different size!" ); #ifndef VULKAN_HPP_NO_SMART_HANDLE class BufferDeleter { public: BufferDeleter( Device device = Device(), Optional allocator = nullptr ) : m_device( device ) , m_allocator( allocator ) {} void operator()( Buffer buffer ) { m_device.destroyBuffer( buffer, m_allocator ); } private: Device m_device; Optional m_allocator; }; class BufferViewDeleter { public: BufferViewDeleter( Device device = Device(), Optional allocator = nullptr ) : m_device( device ) , m_allocator( allocator ) {} void operator()( BufferView bufferView ) { m_device.destroyBufferView( bufferView, m_allocator ); } private: Device m_device; Optional m_allocator; }; class CommandBufferDeleter { public: CommandBufferDeleter( Device device = Device(), CommandPool commandPool = CommandPool() ) : m_device( device ) , m_commandPool( commandPool ) {} void operator()( CommandBuffer commandBuffer ) { m_device.freeCommandBuffers( m_commandPool, commandBuffer ); } private: Device m_device; CommandPool m_commandPool; }; class CommandPoolDeleter { public: CommandPoolDeleter( Device device = Device(), Optional allocator = nullptr ) : m_device( device ) , m_allocator( allocator ) {} void operator()( CommandPool commandPool ) { m_device.destroyCommandPool( commandPool, m_allocator ); } private: Device m_device; Optional m_allocator; }; class DescriptorPoolDeleter { public: DescriptorPoolDeleter( Device device = Device(), Optional allocator = nullptr ) : m_device( device ) , m_allocator( allocator ) {} void operator()( DescriptorPool descriptorPool ) { m_device.destroyDescriptorPool( descriptorPool, m_allocator ); } private: Device m_device; Optional m_allocator; }; class DescriptorSetDeleter { public: DescriptorSetDeleter( Device device = Device(), DescriptorPool descriptorPool = DescriptorPool() ) : m_device( device ) , m_descriptorPool( descriptorPool ) {} void operator()( DescriptorSet descriptorSet ) { m_device.freeDescriptorSets( m_descriptorPool, descriptorSet ); } private: Device m_device; DescriptorPool m_descriptorPool; }; class DescriptorSetLayoutDeleter { public: DescriptorSetLayoutDeleter( Device device = Device(), Optional allocator = nullptr ) : m_device( device ) , m_allocator( allocator ) {} void operator()( DescriptorSetLayout descriptorSetLayout ) { m_device.destroyDescriptorSetLayout( descriptorSetLayout, m_allocator ); } private: Device m_device; Optional m_allocator; }; class DescriptorUpdateTemplateKHRDeleter { public: DescriptorUpdateTemplateKHRDeleter( Device device = Device(), Optional allocator = nullptr ) : m_device( device ) , m_allocator( allocator ) {} void operator()( DescriptorUpdateTemplateKHR descriptorUpdateTemplateKHR ) { m_device.destroyDescriptorUpdateTemplateKHR( descriptorUpdateTemplateKHR, m_allocator ); } private: Device m_device; Optional m_allocator; }; class DeviceMemoryDeleter { public: DeviceMemoryDeleter( Device device = Device(), Optional allocator = nullptr ) : m_device( device ) , m_allocator( allocator ) {} void operator()( DeviceMemory deviceMemory ) { m_device.freeMemory( deviceMemory, m_allocator ); } private: Device m_device; Optional m_allocator; }; class EventDeleter { public: EventDeleter( Device device = Device(), Optional allocator = nullptr ) : m_device( device ) , m_allocator( allocator ) {} void operator()( Event event ) { m_device.destroyEvent( event, m_allocator ); } private: Device m_device; Optional m_allocator; }; class FenceDeleter { public: FenceDeleter( Device device = Device(), Optional allocator = nullptr ) : m_device( device ) , m_allocator( allocator ) {} void operator()( Fence fence ) { m_device.destroyFence( fence, m_allocator ); } private: Device m_device; Optional m_allocator; }; class FramebufferDeleter { public: FramebufferDeleter( Device device = Device(), Optional allocator = nullptr ) : m_device( device ) , m_allocator( allocator ) {} void operator()( Framebuffer framebuffer ) { m_device.destroyFramebuffer( framebuffer, m_allocator ); } private: Device m_device; Optional m_allocator; }; class ImageDeleter { public: ImageDeleter( Device device = Device(), Optional allocator = nullptr ) : m_device( device ) , m_allocator( allocator ) {} void operator()( Image image ) { m_device.destroyImage( image, m_allocator ); } private: Device m_device; Optional m_allocator; }; class ImageViewDeleter { public: ImageViewDeleter( Device device = Device(), Optional allocator = nullptr ) : m_device( device ) , m_allocator( allocator ) {} void operator()( ImageView imageView ) { m_device.destroyImageView( imageView, m_allocator ); } private: Device m_device; Optional m_allocator; }; class IndirectCommandsLayoutNVXDeleter { public: IndirectCommandsLayoutNVXDeleter( Device device = Device(), Optional allocator = nullptr ) : m_device( device ) , m_allocator( allocator ) {} void operator()( IndirectCommandsLayoutNVX indirectCommandsLayoutNVX ) { m_device.destroyIndirectCommandsLayoutNVX( indirectCommandsLayoutNVX, m_allocator ); } private: Device m_device; Optional m_allocator; }; class ObjectTableNVXDeleter { public: ObjectTableNVXDeleter( Device device = Device(), Optional allocator = nullptr ) : m_device( device ) , m_allocator( allocator ) {} void operator()( ObjectTableNVX objectTableNVX ) { m_device.destroyObjectTableNVX( objectTableNVX, m_allocator ); } private: Device m_device; Optional m_allocator; }; class PipelineDeleter { public: PipelineDeleter( Device device = Device(), Optional allocator = nullptr ) : m_device( device ) , m_allocator( allocator ) {} void operator()( Pipeline pipeline ) { m_device.destroyPipeline( pipeline, m_allocator ); } private: Device m_device; Optional m_allocator; }; class PipelineCacheDeleter { public: PipelineCacheDeleter( Device device = Device(), Optional allocator = nullptr ) : m_device( device ) , m_allocator( allocator ) {} void operator()( PipelineCache pipelineCache ) { m_device.destroyPipelineCache( pipelineCache, m_allocator ); } private: Device m_device; Optional m_allocator; }; class PipelineLayoutDeleter { public: PipelineLayoutDeleter( Device device = Device(), Optional allocator = nullptr ) : m_device( device ) , m_allocator( allocator ) {} void operator()( PipelineLayout pipelineLayout ) { m_device.destroyPipelineLayout( pipelineLayout, m_allocator ); } private: Device m_device; Optional m_allocator; }; class QueryPoolDeleter { public: QueryPoolDeleter( Device device = Device(), Optional allocator = nullptr ) : m_device( device ) , m_allocator( allocator ) {} void operator()( QueryPool queryPool ) { m_device.destroyQueryPool( queryPool, m_allocator ); } private: Device m_device; Optional m_allocator; }; class RenderPassDeleter { public: RenderPassDeleter( Device device = Device(), Optional allocator = nullptr ) : m_device( device ) , m_allocator( allocator ) {} void operator()( RenderPass renderPass ) { m_device.destroyRenderPass( renderPass, m_allocator ); } private: Device m_device; Optional m_allocator; }; class SamplerDeleter { public: SamplerDeleter( Device device = Device(), Optional allocator = nullptr ) : m_device( device ) , m_allocator( allocator ) {} void operator()( Sampler sampler ) { m_device.destroySampler( sampler, m_allocator ); } private: Device m_device; Optional m_allocator; }; class SemaphoreDeleter { public: SemaphoreDeleter( Device device = Device(), Optional allocator = nullptr ) : m_device( device ) , m_allocator( allocator ) {} void operator()( Semaphore semaphore ) { m_device.destroySemaphore( semaphore, m_allocator ); } private: Device m_device; Optional m_allocator; }; class ShaderModuleDeleter { public: ShaderModuleDeleter( Device device = Device(), Optional allocator = nullptr ) : m_device( device ) , m_allocator( allocator ) {} void operator()( ShaderModule shaderModule ) { m_device.destroyShaderModule( shaderModule, m_allocator ); } private: Device m_device; Optional m_allocator; }; class SwapchainKHRDeleter { public: SwapchainKHRDeleter( Device device = Device(), Optional allocator = nullptr ) : m_device( device ) , m_allocator( allocator ) {} void operator()( SwapchainKHR swapchainKHR ) { m_device.destroySwapchainKHR( swapchainKHR, m_allocator ); } private: Device m_device; Optional m_allocator; }; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ VULKAN_HPP_INLINE PFN_vkVoidFunction Device::getProcAddr( const char* pName ) const { return vkGetDeviceProcAddr( m_device, pName ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE PFN_vkVoidFunction Device::getProcAddr( const std::string & name ) const { return vkGetDeviceProcAddr( m_device, name.c_str() ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::destroy( const AllocationCallbacks* pAllocator ) const { vkDestroyDevice( m_device, reinterpret_cast( pAllocator ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void Device::destroy( Optional allocator ) const { vkDestroyDevice( m_device, reinterpret_cast( static_cast( allocator ) ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::getQueue( uint32_t queueFamilyIndex, uint32_t queueIndex, Queue* pQueue ) const { vkGetDeviceQueue( m_device, queueFamilyIndex, queueIndex, reinterpret_cast( pQueue ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE Queue Device::getQueue( uint32_t queueFamilyIndex, uint32_t queueIndex ) const { Queue queue; vkGetDeviceQueue( m_device, queueFamilyIndex, queueIndex, reinterpret_cast( &queue ) ); return queue; } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE Result Device::waitIdle() const { return static_cast( vkDeviceWaitIdle( m_device ) ); } #else VULKAN_HPP_INLINE ResultValueType::type Device::waitIdle() const { Result result = static_cast( vkDeviceWaitIdle( m_device ) ); return createResultValue( result, "vk::Device::waitIdle" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::allocateMemory( const MemoryAllocateInfo* pAllocateInfo, const AllocationCallbacks* pAllocator, DeviceMemory* pMemory ) const { return static_cast( vkAllocateMemory( m_device, reinterpret_cast( pAllocateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pMemory ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::allocateMemory( const MemoryAllocateInfo & allocateInfo, Optional allocator ) const { DeviceMemory memory; Result result = static_cast( vkAllocateMemory( m_device, reinterpret_cast( &allocateInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &memory ) ) ); return createResultValue( result, memory, "vk::Device::allocateMemory" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE VULKAN_HPP_INLINE UniqueDeviceMemory Device::allocateMemoryUnique( const MemoryAllocateInfo & allocateInfo, Optional allocator ) const { DeviceMemoryDeleter deleter( *this, allocator ); return UniqueDeviceMemory( allocateMemory( allocateInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::freeMemory( DeviceMemory memory, const AllocationCallbacks* pAllocator ) const { vkFreeMemory( m_device, static_cast( memory ), reinterpret_cast( pAllocator ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void Device::freeMemory( DeviceMemory memory, Optional allocator ) const { vkFreeMemory( m_device, static_cast( memory ), reinterpret_cast( static_cast( allocator ) ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::mapMemory( DeviceMemory memory, DeviceSize offset, DeviceSize size, MemoryMapFlags flags, void** ppData ) const { return static_cast( vkMapMemory( m_device, static_cast( memory ), offset, size, static_cast( flags ), ppData ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::mapMemory( DeviceMemory memory, DeviceSize offset, DeviceSize size, MemoryMapFlags flags ) const { void* pData; Result result = static_cast( vkMapMemory( m_device, static_cast( memory ), offset, size, static_cast( flags ), &pData ) ); return createResultValue( result, pData, "vk::Device::mapMemory" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::unmapMemory( DeviceMemory memory ) const { vkUnmapMemory( m_device, static_cast( memory ) ); } VULKAN_HPP_INLINE Result Device::flushMappedMemoryRanges( uint32_t memoryRangeCount, const MappedMemoryRange* pMemoryRanges ) const { return static_cast( vkFlushMappedMemoryRanges( m_device, memoryRangeCount, reinterpret_cast( pMemoryRanges ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::flushMappedMemoryRanges( ArrayProxy memoryRanges ) const { Result result = static_cast( vkFlushMappedMemoryRanges( m_device, memoryRanges.size() , reinterpret_cast( memoryRanges.data() ) ) ); return createResultValue( result, "vk::Device::flushMappedMemoryRanges" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::invalidateMappedMemoryRanges( uint32_t memoryRangeCount, const MappedMemoryRange* pMemoryRanges ) const { return static_cast( vkInvalidateMappedMemoryRanges( m_device, memoryRangeCount, reinterpret_cast( pMemoryRanges ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::invalidateMappedMemoryRanges( ArrayProxy memoryRanges ) const { Result result = static_cast( vkInvalidateMappedMemoryRanges( m_device, memoryRanges.size() , reinterpret_cast( memoryRanges.data() ) ) ); return createResultValue( result, "vk::Device::invalidateMappedMemoryRanges" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::getMemoryCommitment( DeviceMemory memory, DeviceSize* pCommittedMemoryInBytes ) const { vkGetDeviceMemoryCommitment( m_device, static_cast( memory ), pCommittedMemoryInBytes ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE DeviceSize Device::getMemoryCommitment( DeviceMemory memory ) const { DeviceSize committedMemoryInBytes; vkGetDeviceMemoryCommitment( m_device, static_cast( memory ), &committedMemoryInBytes ); return committedMemoryInBytes; } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::getBufferMemoryRequirements( Buffer buffer, MemoryRequirements* pMemoryRequirements ) const { vkGetBufferMemoryRequirements( m_device, static_cast( buffer ), reinterpret_cast( pMemoryRequirements ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE MemoryRequirements Device::getBufferMemoryRequirements( Buffer buffer ) const { MemoryRequirements memoryRequirements; vkGetBufferMemoryRequirements( m_device, static_cast( buffer ), reinterpret_cast( &memoryRequirements ) ); return memoryRequirements; } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE Result Device::bindBufferMemory( Buffer buffer, DeviceMemory memory, DeviceSize memoryOffset ) const { return static_cast( vkBindBufferMemory( m_device, static_cast( buffer ), static_cast( memory ), memoryOffset ) ); } #else VULKAN_HPP_INLINE ResultValueType::type Device::bindBufferMemory( Buffer buffer, DeviceMemory memory, DeviceSize memoryOffset ) const { Result result = static_cast( vkBindBufferMemory( m_device, static_cast( buffer ), static_cast( memory ), memoryOffset ) ); return createResultValue( result, "vk::Device::bindBufferMemory" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::getImageMemoryRequirements( Image image, MemoryRequirements* pMemoryRequirements ) const { vkGetImageMemoryRequirements( m_device, static_cast( image ), reinterpret_cast( pMemoryRequirements ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE MemoryRequirements Device::getImageMemoryRequirements( Image image ) const { MemoryRequirements memoryRequirements; vkGetImageMemoryRequirements( m_device, static_cast( image ), reinterpret_cast( &memoryRequirements ) ); return memoryRequirements; } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE Result Device::bindImageMemory( Image image, DeviceMemory memory, DeviceSize memoryOffset ) const { return static_cast( vkBindImageMemory( m_device, static_cast( image ), static_cast( memory ), memoryOffset ) ); } #else VULKAN_HPP_INLINE ResultValueType::type Device::bindImageMemory( Image image, DeviceMemory memory, DeviceSize memoryOffset ) const { Result result = static_cast( vkBindImageMemory( m_device, static_cast( image ), static_cast( memory ), memoryOffset ) ); return createResultValue( result, "vk::Device::bindImageMemory" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::getImageSparseMemoryRequirements( Image image, uint32_t* pSparseMemoryRequirementCount, SparseImageMemoryRequirements* pSparseMemoryRequirements ) const { vkGetImageSparseMemoryRequirements( m_device, static_cast( image ), pSparseMemoryRequirementCount, reinterpret_cast( pSparseMemoryRequirements ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template VULKAN_HPP_INLINE std::vector Device::getImageSparseMemoryRequirements( Image image ) const { std::vector sparseMemoryRequirements; uint32_t sparseMemoryRequirementCount; vkGetImageSparseMemoryRequirements( m_device, static_cast( image ), &sparseMemoryRequirementCount, nullptr ); sparseMemoryRequirements.resize( sparseMemoryRequirementCount ); vkGetImageSparseMemoryRequirements( m_device, static_cast( image ), &sparseMemoryRequirementCount, reinterpret_cast( sparseMemoryRequirements.data() ) ); return sparseMemoryRequirements; } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::createFence( const FenceCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, Fence* pFence ) const { return static_cast( vkCreateFence( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pFence ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::createFence( const FenceCreateInfo & createInfo, Optional allocator ) const { Fence fence; Result result = static_cast( vkCreateFence( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &fence ) ) ); return createResultValue( result, fence, "vk::Device::createFence" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE VULKAN_HPP_INLINE UniqueFence Device::createFenceUnique( const FenceCreateInfo & createInfo, Optional allocator ) const { FenceDeleter deleter( *this, allocator ); return UniqueFence( createFence( createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::destroyFence( Fence fence, const AllocationCallbacks* pAllocator ) const { vkDestroyFence( m_device, static_cast( fence ), reinterpret_cast( pAllocator ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void Device::destroyFence( Fence fence, Optional allocator ) const { vkDestroyFence( m_device, static_cast( fence ), reinterpret_cast( static_cast( allocator ) ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::resetFences( uint32_t fenceCount, const Fence* pFences ) const { return static_cast( vkResetFences( m_device, fenceCount, reinterpret_cast( pFences ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::resetFences( ArrayProxy fences ) const { Result result = static_cast( vkResetFences( m_device, fences.size() , reinterpret_cast( fences.data() ) ) ); return createResultValue( result, "vk::Device::resetFences" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE Result Device::getFenceStatus( Fence fence ) const { return static_cast( vkGetFenceStatus( m_device, static_cast( fence ) ) ); } #else VULKAN_HPP_INLINE Result Device::getFenceStatus( Fence fence ) const { Result result = static_cast( vkGetFenceStatus( m_device, static_cast( fence ) ) ); return createResultValue( result, "vk::Device::getFenceStatus", { Result::eSuccess, Result::eNotReady } ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::waitForFences( uint32_t fenceCount, const Fence* pFences, Bool32 waitAll, uint64_t timeout ) const { return static_cast( vkWaitForFences( m_device, fenceCount, reinterpret_cast( pFences ), waitAll, timeout ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE Result Device::waitForFences( ArrayProxy fences, Bool32 waitAll, uint64_t timeout ) const { Result result = static_cast( vkWaitForFences( m_device, fences.size() , reinterpret_cast( fences.data() ), waitAll, timeout ) ); return createResultValue( result, "vk::Device::waitForFences", { Result::eSuccess, Result::eTimeout } ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::createSemaphore( const SemaphoreCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, Semaphore* pSemaphore ) const { return static_cast( vkCreateSemaphore( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pSemaphore ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::createSemaphore( const SemaphoreCreateInfo & createInfo, Optional allocator ) const { Semaphore semaphore; Result result = static_cast( vkCreateSemaphore( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &semaphore ) ) ); return createResultValue( result, semaphore, "vk::Device::createSemaphore" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE VULKAN_HPP_INLINE UniqueSemaphore Device::createSemaphoreUnique( const SemaphoreCreateInfo & createInfo, Optional allocator ) const { SemaphoreDeleter deleter( *this, allocator ); return UniqueSemaphore( createSemaphore( createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::destroySemaphore( Semaphore semaphore, const AllocationCallbacks* pAllocator ) const { vkDestroySemaphore( m_device, static_cast( semaphore ), reinterpret_cast( pAllocator ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void Device::destroySemaphore( Semaphore semaphore, Optional allocator ) const { vkDestroySemaphore( m_device, static_cast( semaphore ), reinterpret_cast( static_cast( allocator ) ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::createEvent( const EventCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, Event* pEvent ) const { return static_cast( vkCreateEvent( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pEvent ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::createEvent( const EventCreateInfo & createInfo, Optional allocator ) const { Event event; Result result = static_cast( vkCreateEvent( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &event ) ) ); return createResultValue( result, event, "vk::Device::createEvent" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE VULKAN_HPP_INLINE UniqueEvent Device::createEventUnique( const EventCreateInfo & createInfo, Optional allocator ) const { EventDeleter deleter( *this, allocator ); return UniqueEvent( createEvent( createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::destroyEvent( Event event, const AllocationCallbacks* pAllocator ) const { vkDestroyEvent( m_device, static_cast( event ), reinterpret_cast( pAllocator ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void Device::destroyEvent( Event event, Optional allocator ) const { vkDestroyEvent( m_device, static_cast( event ), reinterpret_cast( static_cast( allocator ) ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE Result Device::getEventStatus( Event event ) const { return static_cast( vkGetEventStatus( m_device, static_cast( event ) ) ); } #else VULKAN_HPP_INLINE Result Device::getEventStatus( Event event ) const { Result result = static_cast( vkGetEventStatus( m_device, static_cast( event ) ) ); return createResultValue( result, "vk::Device::getEventStatus", { Result::eEventSet, Result::eEventReset } ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE Result Device::setEvent( Event event ) const { return static_cast( vkSetEvent( m_device, static_cast( event ) ) ); } #else VULKAN_HPP_INLINE ResultValueType::type Device::setEvent( Event event ) const { Result result = static_cast( vkSetEvent( m_device, static_cast( event ) ) ); return createResultValue( result, "vk::Device::setEvent" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE Result Device::resetEvent( Event event ) const { return static_cast( vkResetEvent( m_device, static_cast( event ) ) ); } #else VULKAN_HPP_INLINE ResultValueType::type Device::resetEvent( Event event ) const { Result result = static_cast( vkResetEvent( m_device, static_cast( event ) ) ); return createResultValue( result, "vk::Device::resetEvent" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::createQueryPool( const QueryPoolCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, QueryPool* pQueryPool ) const { return static_cast( vkCreateQueryPool( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pQueryPool ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::createQueryPool( const QueryPoolCreateInfo & createInfo, Optional allocator ) const { QueryPool queryPool; Result result = static_cast( vkCreateQueryPool( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &queryPool ) ) ); return createResultValue( result, queryPool, "vk::Device::createQueryPool" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE VULKAN_HPP_INLINE UniqueQueryPool Device::createQueryPoolUnique( const QueryPoolCreateInfo & createInfo, Optional allocator ) const { QueryPoolDeleter deleter( *this, allocator ); return UniqueQueryPool( createQueryPool( createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::destroyQueryPool( QueryPool queryPool, const AllocationCallbacks* pAllocator ) const { vkDestroyQueryPool( m_device, static_cast( queryPool ), reinterpret_cast( pAllocator ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void Device::destroyQueryPool( QueryPool queryPool, Optional allocator ) const { vkDestroyQueryPool( m_device, static_cast( queryPool ), reinterpret_cast( static_cast( allocator ) ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::getQueryPoolResults( QueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, size_t dataSize, void* pData, DeviceSize stride, QueryResultFlags flags ) const { return static_cast( vkGetQueryPoolResults( m_device, static_cast( queryPool ), firstQuery, queryCount, dataSize, pData, stride, static_cast( flags ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template VULKAN_HPP_INLINE Result Device::getQueryPoolResults( QueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, ArrayProxy data, DeviceSize stride, QueryResultFlags flags ) const { Result result = static_cast( vkGetQueryPoolResults( m_device, static_cast( queryPool ), firstQuery, queryCount, data.size() * sizeof( T ) , reinterpret_cast( data.data() ), stride, static_cast( flags ) ) ); return createResultValue( result, "vk::Device::getQueryPoolResults", { Result::eSuccess, Result::eNotReady } ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::createBuffer( const BufferCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, Buffer* pBuffer ) const { return static_cast( vkCreateBuffer( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pBuffer ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::createBuffer( const BufferCreateInfo & createInfo, Optional allocator ) const { Buffer buffer; Result result = static_cast( vkCreateBuffer( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &buffer ) ) ); return createResultValue( result, buffer, "vk::Device::createBuffer" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE VULKAN_HPP_INLINE UniqueBuffer Device::createBufferUnique( const BufferCreateInfo & createInfo, Optional allocator ) const { BufferDeleter deleter( *this, allocator ); return UniqueBuffer( createBuffer( createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::destroyBuffer( Buffer buffer, const AllocationCallbacks* pAllocator ) const { vkDestroyBuffer( m_device, static_cast( buffer ), reinterpret_cast( pAllocator ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void Device::destroyBuffer( Buffer buffer, Optional allocator ) const { vkDestroyBuffer( m_device, static_cast( buffer ), reinterpret_cast( static_cast( allocator ) ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::createBufferView( const BufferViewCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, BufferView* pView ) const { return static_cast( vkCreateBufferView( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pView ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::createBufferView( const BufferViewCreateInfo & createInfo, Optional allocator ) const { BufferView view; Result result = static_cast( vkCreateBufferView( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &view ) ) ); return createResultValue( result, view, "vk::Device::createBufferView" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE VULKAN_HPP_INLINE UniqueBufferView Device::createBufferViewUnique( const BufferViewCreateInfo & createInfo, Optional allocator ) const { BufferViewDeleter deleter( *this, allocator ); return UniqueBufferView( createBufferView( createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::destroyBufferView( BufferView bufferView, const AllocationCallbacks* pAllocator ) const { vkDestroyBufferView( m_device, static_cast( bufferView ), reinterpret_cast( pAllocator ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void Device::destroyBufferView( BufferView bufferView, Optional allocator ) const { vkDestroyBufferView( m_device, static_cast( bufferView ), reinterpret_cast( static_cast( allocator ) ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::createImage( const ImageCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, Image* pImage ) const { return static_cast( vkCreateImage( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pImage ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::createImage( const ImageCreateInfo & createInfo, Optional allocator ) const { Image image; Result result = static_cast( vkCreateImage( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &image ) ) ); return createResultValue( result, image, "vk::Device::createImage" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE VULKAN_HPP_INLINE UniqueImage Device::createImageUnique( const ImageCreateInfo & createInfo, Optional allocator ) const { ImageDeleter deleter( *this, allocator ); return UniqueImage( createImage( createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::destroyImage( Image image, const AllocationCallbacks* pAllocator ) const { vkDestroyImage( m_device, static_cast( image ), reinterpret_cast( pAllocator ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void Device::destroyImage( Image image, Optional allocator ) const { vkDestroyImage( m_device, static_cast( image ), reinterpret_cast( static_cast( allocator ) ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::getImageSubresourceLayout( Image image, const ImageSubresource* pSubresource, SubresourceLayout* pLayout ) const { vkGetImageSubresourceLayout( m_device, static_cast( image ), reinterpret_cast( pSubresource ), reinterpret_cast( pLayout ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE SubresourceLayout Device::getImageSubresourceLayout( Image image, const ImageSubresource & subresource ) const { SubresourceLayout layout; vkGetImageSubresourceLayout( m_device, static_cast( image ), reinterpret_cast( &subresource ), reinterpret_cast( &layout ) ); return layout; } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::createImageView( const ImageViewCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, ImageView* pView ) const { return static_cast( vkCreateImageView( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pView ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::createImageView( const ImageViewCreateInfo & createInfo, Optional allocator ) const { ImageView view; Result result = static_cast( vkCreateImageView( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &view ) ) ); return createResultValue( result, view, "vk::Device::createImageView" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE VULKAN_HPP_INLINE UniqueImageView Device::createImageViewUnique( const ImageViewCreateInfo & createInfo, Optional allocator ) const { ImageViewDeleter deleter( *this, allocator ); return UniqueImageView( createImageView( createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::destroyImageView( ImageView imageView, const AllocationCallbacks* pAllocator ) const { vkDestroyImageView( m_device, static_cast( imageView ), reinterpret_cast( pAllocator ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void Device::destroyImageView( ImageView imageView, Optional allocator ) const { vkDestroyImageView( m_device, static_cast( imageView ), reinterpret_cast( static_cast( allocator ) ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::createShaderModule( const ShaderModuleCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, ShaderModule* pShaderModule ) const { return static_cast( vkCreateShaderModule( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pShaderModule ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::createShaderModule( const ShaderModuleCreateInfo & createInfo, Optional allocator ) const { ShaderModule shaderModule; Result result = static_cast( vkCreateShaderModule( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &shaderModule ) ) ); return createResultValue( result, shaderModule, "vk::Device::createShaderModule" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE VULKAN_HPP_INLINE UniqueShaderModule Device::createShaderModuleUnique( const ShaderModuleCreateInfo & createInfo, Optional allocator ) const { ShaderModuleDeleter deleter( *this, allocator ); return UniqueShaderModule( createShaderModule( createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::destroyShaderModule( ShaderModule shaderModule, const AllocationCallbacks* pAllocator ) const { vkDestroyShaderModule( m_device, static_cast( shaderModule ), reinterpret_cast( pAllocator ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void Device::destroyShaderModule( ShaderModule shaderModule, Optional allocator ) const { vkDestroyShaderModule( m_device, static_cast( shaderModule ), reinterpret_cast( static_cast( allocator ) ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::createPipelineCache( const PipelineCacheCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, PipelineCache* pPipelineCache ) const { return static_cast( vkCreatePipelineCache( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pPipelineCache ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::createPipelineCache( const PipelineCacheCreateInfo & createInfo, Optional allocator ) const { PipelineCache pipelineCache; Result result = static_cast( vkCreatePipelineCache( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &pipelineCache ) ) ); return createResultValue( result, pipelineCache, "vk::Device::createPipelineCache" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE VULKAN_HPP_INLINE UniquePipelineCache Device::createPipelineCacheUnique( const PipelineCacheCreateInfo & createInfo, Optional allocator ) const { PipelineCacheDeleter deleter( *this, allocator ); return UniquePipelineCache( createPipelineCache( createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::destroyPipelineCache( PipelineCache pipelineCache, const AllocationCallbacks* pAllocator ) const { vkDestroyPipelineCache( m_device, static_cast( pipelineCache ), reinterpret_cast( pAllocator ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void Device::destroyPipelineCache( PipelineCache pipelineCache, Optional allocator ) const { vkDestroyPipelineCache( m_device, static_cast( pipelineCache ), reinterpret_cast( static_cast( allocator ) ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::getPipelineCacheData( PipelineCache pipelineCache, size_t* pDataSize, void* pData ) const { return static_cast( vkGetPipelineCacheData( m_device, static_cast( pipelineCache ), pDataSize, pData ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template VULKAN_HPP_INLINE typename ResultValueType>::type Device::getPipelineCacheData( PipelineCache pipelineCache ) const { std::vector data; size_t dataSize; Result result; do { result = static_cast( vkGetPipelineCacheData( m_device, static_cast( pipelineCache ), &dataSize, nullptr ) ); if ( ( result == Result::eSuccess ) && dataSize ) { data.resize( dataSize ); result = static_cast( vkGetPipelineCacheData( m_device, static_cast( pipelineCache ), &dataSize, reinterpret_cast( data.data() ) ) ); } } while ( result == Result::eIncomplete ); assert( dataSize <= data.size() ); data.resize( dataSize ); return createResultValue( result, data, "vk::Device::getPipelineCacheData" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::mergePipelineCaches( PipelineCache dstCache, uint32_t srcCacheCount, const PipelineCache* pSrcCaches ) const { return static_cast( vkMergePipelineCaches( m_device, static_cast( dstCache ), srcCacheCount, reinterpret_cast( pSrcCaches ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::mergePipelineCaches( PipelineCache dstCache, ArrayProxy srcCaches ) const { Result result = static_cast( vkMergePipelineCaches( m_device, static_cast( dstCache ), srcCaches.size() , reinterpret_cast( srcCaches.data() ) ) ); return createResultValue( result, "vk::Device::mergePipelineCaches" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::createGraphicsPipelines( PipelineCache pipelineCache, uint32_t createInfoCount, const GraphicsPipelineCreateInfo* pCreateInfos, const AllocationCallbacks* pAllocator, Pipeline* pPipelines ) const { return static_cast( vkCreateGraphicsPipelines( m_device, static_cast( pipelineCache ), createInfoCount, reinterpret_cast( pCreateInfos ), reinterpret_cast( pAllocator ), reinterpret_cast( pPipelines ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template VULKAN_HPP_INLINE typename ResultValueType>::type Device::createGraphicsPipelines( PipelineCache pipelineCache, ArrayProxy createInfos, Optional allocator ) const { std::vector pipelines( createInfos.size() ); Result result = static_cast( vkCreateGraphicsPipelines( m_device, static_cast( pipelineCache ), createInfos.size() , reinterpret_cast( createInfos.data() ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( pipelines.data() ) ) ); return createResultValue( result, pipelines, "vk::Device::createGraphicsPipelines" ); } VULKAN_HPP_INLINE ResultValueType::type Device::createGraphicsPipeline( PipelineCache pipelineCache, const GraphicsPipelineCreateInfo & createInfo, Optional allocator ) const { Pipeline pipeline; Result result = static_cast( vkCreateGraphicsPipelines( m_device, static_cast( pipelineCache ), 1 , reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &pipeline ) ) ); return createResultValue( result, pipeline, "vk::Device::createGraphicsPipeline" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE template VULKAN_HPP_INLINE std::vector Device::createGraphicsPipelinesUnique( PipelineCache pipelineCache, ArrayProxy createInfos, Optional allocator ) const { PipelineDeleter deleter( *this, allocator ); std::vector pipelines = createGraphicsPipelines( pipelineCache, createInfos, allocator ); std::vector uniquePipelines; uniquePipelines.reserve( pipelines.size() ); for ( auto pipeline : pipelines ) { uniquePipelines.push_back( UniquePipeline( pipeline, deleter ) ); } return uniquePipelines; } VULKAN_HPP_INLINE UniquePipeline Device::createGraphicsPipelineUnique( PipelineCache pipelineCache, const GraphicsPipelineCreateInfo & createInfo, Optional allocator ) const { PipelineDeleter deleter( *this, allocator ); return UniquePipeline( createGraphicsPipeline( pipelineCache, createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::createComputePipelines( PipelineCache pipelineCache, uint32_t createInfoCount, const ComputePipelineCreateInfo* pCreateInfos, const AllocationCallbacks* pAllocator, Pipeline* pPipelines ) const { return static_cast( vkCreateComputePipelines( m_device, static_cast( pipelineCache ), createInfoCount, reinterpret_cast( pCreateInfos ), reinterpret_cast( pAllocator ), reinterpret_cast( pPipelines ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template VULKAN_HPP_INLINE typename ResultValueType>::type Device::createComputePipelines( PipelineCache pipelineCache, ArrayProxy createInfos, Optional allocator ) const { std::vector pipelines( createInfos.size() ); Result result = static_cast( vkCreateComputePipelines( m_device, static_cast( pipelineCache ), createInfos.size() , reinterpret_cast( createInfos.data() ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( pipelines.data() ) ) ); return createResultValue( result, pipelines, "vk::Device::createComputePipelines" ); } VULKAN_HPP_INLINE ResultValueType::type Device::createComputePipeline( PipelineCache pipelineCache, const ComputePipelineCreateInfo & createInfo, Optional allocator ) const { Pipeline pipeline; Result result = static_cast( vkCreateComputePipelines( m_device, static_cast( pipelineCache ), 1 , reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &pipeline ) ) ); return createResultValue( result, pipeline, "vk::Device::createComputePipeline" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE template VULKAN_HPP_INLINE std::vector Device::createComputePipelinesUnique( PipelineCache pipelineCache, ArrayProxy createInfos, Optional allocator ) const { PipelineDeleter deleter( *this, allocator ); std::vector pipelines = createComputePipelines( pipelineCache, createInfos, allocator ); std::vector uniquePipelines; uniquePipelines.reserve( pipelines.size() ); for ( auto pipeline : pipelines ) { uniquePipelines.push_back( UniquePipeline( pipeline, deleter ) ); } return uniquePipelines; } VULKAN_HPP_INLINE UniquePipeline Device::createComputePipelineUnique( PipelineCache pipelineCache, const ComputePipelineCreateInfo & createInfo, Optional allocator ) const { PipelineDeleter deleter( *this, allocator ); return UniquePipeline( createComputePipeline( pipelineCache, createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::destroyPipeline( Pipeline pipeline, const AllocationCallbacks* pAllocator ) const { vkDestroyPipeline( m_device, static_cast( pipeline ), reinterpret_cast( pAllocator ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void Device::destroyPipeline( Pipeline pipeline, Optional allocator ) const { vkDestroyPipeline( m_device, static_cast( pipeline ), reinterpret_cast( static_cast( allocator ) ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::createPipelineLayout( const PipelineLayoutCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, PipelineLayout* pPipelineLayout ) const { return static_cast( vkCreatePipelineLayout( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pPipelineLayout ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::createPipelineLayout( const PipelineLayoutCreateInfo & createInfo, Optional allocator ) const { PipelineLayout pipelineLayout; Result result = static_cast( vkCreatePipelineLayout( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &pipelineLayout ) ) ); return createResultValue( result, pipelineLayout, "vk::Device::createPipelineLayout" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE VULKAN_HPP_INLINE UniquePipelineLayout Device::createPipelineLayoutUnique( const PipelineLayoutCreateInfo & createInfo, Optional allocator ) const { PipelineLayoutDeleter deleter( *this, allocator ); return UniquePipelineLayout( createPipelineLayout( createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::destroyPipelineLayout( PipelineLayout pipelineLayout, const AllocationCallbacks* pAllocator ) const { vkDestroyPipelineLayout( m_device, static_cast( pipelineLayout ), reinterpret_cast( pAllocator ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void Device::destroyPipelineLayout( PipelineLayout pipelineLayout, Optional allocator ) const { vkDestroyPipelineLayout( m_device, static_cast( pipelineLayout ), reinterpret_cast( static_cast( allocator ) ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::createSampler( const SamplerCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, Sampler* pSampler ) const { return static_cast( vkCreateSampler( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pSampler ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::createSampler( const SamplerCreateInfo & createInfo, Optional allocator ) const { Sampler sampler; Result result = static_cast( vkCreateSampler( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &sampler ) ) ); return createResultValue( result, sampler, "vk::Device::createSampler" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE VULKAN_HPP_INLINE UniqueSampler Device::createSamplerUnique( const SamplerCreateInfo & createInfo, Optional allocator ) const { SamplerDeleter deleter( *this, allocator ); return UniqueSampler( createSampler( createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::destroySampler( Sampler sampler, const AllocationCallbacks* pAllocator ) const { vkDestroySampler( m_device, static_cast( sampler ), reinterpret_cast( pAllocator ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void Device::destroySampler( Sampler sampler, Optional allocator ) const { vkDestroySampler( m_device, static_cast( sampler ), reinterpret_cast( static_cast( allocator ) ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::createDescriptorSetLayout( const DescriptorSetLayoutCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, DescriptorSetLayout* pSetLayout ) const { return static_cast( vkCreateDescriptorSetLayout( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pSetLayout ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::createDescriptorSetLayout( const DescriptorSetLayoutCreateInfo & createInfo, Optional allocator ) const { DescriptorSetLayout setLayout; Result result = static_cast( vkCreateDescriptorSetLayout( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &setLayout ) ) ); return createResultValue( result, setLayout, "vk::Device::createDescriptorSetLayout" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE VULKAN_HPP_INLINE UniqueDescriptorSetLayout Device::createDescriptorSetLayoutUnique( const DescriptorSetLayoutCreateInfo & createInfo, Optional allocator ) const { DescriptorSetLayoutDeleter deleter( *this, allocator ); return UniqueDescriptorSetLayout( createDescriptorSetLayout( createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::destroyDescriptorSetLayout( DescriptorSetLayout descriptorSetLayout, const AllocationCallbacks* pAllocator ) const { vkDestroyDescriptorSetLayout( m_device, static_cast( descriptorSetLayout ), reinterpret_cast( pAllocator ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void Device::destroyDescriptorSetLayout( DescriptorSetLayout descriptorSetLayout, Optional allocator ) const { vkDestroyDescriptorSetLayout( m_device, static_cast( descriptorSetLayout ), reinterpret_cast( static_cast( allocator ) ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::createDescriptorPool( const DescriptorPoolCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, DescriptorPool* pDescriptorPool ) const { return static_cast( vkCreateDescriptorPool( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pDescriptorPool ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::createDescriptorPool( const DescriptorPoolCreateInfo & createInfo, Optional allocator ) const { DescriptorPool descriptorPool; Result result = static_cast( vkCreateDescriptorPool( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &descriptorPool ) ) ); return createResultValue( result, descriptorPool, "vk::Device::createDescriptorPool" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE VULKAN_HPP_INLINE UniqueDescriptorPool Device::createDescriptorPoolUnique( const DescriptorPoolCreateInfo & createInfo, Optional allocator ) const { DescriptorPoolDeleter deleter( *this, allocator ); return UniqueDescriptorPool( createDescriptorPool( createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::destroyDescriptorPool( DescriptorPool descriptorPool, const AllocationCallbacks* pAllocator ) const { vkDestroyDescriptorPool( m_device, static_cast( descriptorPool ), reinterpret_cast( pAllocator ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void Device::destroyDescriptorPool( DescriptorPool descriptorPool, Optional allocator ) const { vkDestroyDescriptorPool( m_device, static_cast( descriptorPool ), reinterpret_cast( static_cast( allocator ) ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE Result Device::resetDescriptorPool( DescriptorPool descriptorPool, DescriptorPoolResetFlags flags ) const { return static_cast( vkResetDescriptorPool( m_device, static_cast( descriptorPool ), static_cast( flags ) ) ); } #else VULKAN_HPP_INLINE ResultValueType::type Device::resetDescriptorPool( DescriptorPool descriptorPool, DescriptorPoolResetFlags flags ) const { Result result = static_cast( vkResetDescriptorPool( m_device, static_cast( descriptorPool ), static_cast( flags ) ) ); return createResultValue( result, "vk::Device::resetDescriptorPool" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::allocateDescriptorSets( const DescriptorSetAllocateInfo* pAllocateInfo, DescriptorSet* pDescriptorSets ) const { return static_cast( vkAllocateDescriptorSets( m_device, reinterpret_cast( pAllocateInfo ), reinterpret_cast( pDescriptorSets ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template VULKAN_HPP_INLINE typename ResultValueType>::type Device::allocateDescriptorSets( const DescriptorSetAllocateInfo & allocateInfo ) const { std::vector descriptorSets( allocateInfo.descriptorSetCount ); Result result = static_cast( vkAllocateDescriptorSets( m_device, reinterpret_cast( &allocateInfo ), reinterpret_cast( descriptorSets.data() ) ) ); return createResultValue( result, descriptorSets, "vk::Device::allocateDescriptorSets" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE template VULKAN_HPP_INLINE std::vector Device::allocateDescriptorSetsUnique( const DescriptorSetAllocateInfo & allocateInfo ) const { DescriptorSetDeleter deleter( *this, allocateInfo.descriptorPool ); std::vector descriptorSets = allocateDescriptorSets( allocateInfo ); std::vector uniqueDescriptorSets; uniqueDescriptorSets.reserve( descriptorSets.size() ); for ( auto descriptorSet : descriptorSets ) { uniqueDescriptorSets.push_back( UniqueDescriptorSet( descriptorSet, deleter ) ); } return uniqueDescriptorSets; } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::freeDescriptorSets( DescriptorPool descriptorPool, uint32_t descriptorSetCount, const DescriptorSet* pDescriptorSets ) const { return static_cast( vkFreeDescriptorSets( m_device, static_cast( descriptorPool ), descriptorSetCount, reinterpret_cast( pDescriptorSets ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::freeDescriptorSets( DescriptorPool descriptorPool, ArrayProxy descriptorSets ) const { Result result = static_cast( vkFreeDescriptorSets( m_device, static_cast( descriptorPool ), descriptorSets.size() , reinterpret_cast( descriptorSets.data() ) ) ); return createResultValue( result, "vk::Device::freeDescriptorSets" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::updateDescriptorSets( uint32_t descriptorWriteCount, const WriteDescriptorSet* pDescriptorWrites, uint32_t descriptorCopyCount, const CopyDescriptorSet* pDescriptorCopies ) const { vkUpdateDescriptorSets( m_device, descriptorWriteCount, reinterpret_cast( pDescriptorWrites ), descriptorCopyCount, reinterpret_cast( pDescriptorCopies ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void Device::updateDescriptorSets( ArrayProxy descriptorWrites, ArrayProxy descriptorCopies ) const { vkUpdateDescriptorSets( m_device, descriptorWrites.size() , reinterpret_cast( descriptorWrites.data() ), descriptorCopies.size() , reinterpret_cast( descriptorCopies.data() ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::createFramebuffer( const FramebufferCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, Framebuffer* pFramebuffer ) const { return static_cast( vkCreateFramebuffer( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pFramebuffer ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::createFramebuffer( const FramebufferCreateInfo & createInfo, Optional allocator ) const { Framebuffer framebuffer; Result result = static_cast( vkCreateFramebuffer( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &framebuffer ) ) ); return createResultValue( result, framebuffer, "vk::Device::createFramebuffer" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE VULKAN_HPP_INLINE UniqueFramebuffer Device::createFramebufferUnique( const FramebufferCreateInfo & createInfo, Optional allocator ) const { FramebufferDeleter deleter( *this, allocator ); return UniqueFramebuffer( createFramebuffer( createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::destroyFramebuffer( Framebuffer framebuffer, const AllocationCallbacks* pAllocator ) const { vkDestroyFramebuffer( m_device, static_cast( framebuffer ), reinterpret_cast( pAllocator ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void Device::destroyFramebuffer( Framebuffer framebuffer, Optional allocator ) const { vkDestroyFramebuffer( m_device, static_cast( framebuffer ), reinterpret_cast( static_cast( allocator ) ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::createRenderPass( const RenderPassCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, RenderPass* pRenderPass ) const { return static_cast( vkCreateRenderPass( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pRenderPass ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::createRenderPass( const RenderPassCreateInfo & createInfo, Optional allocator ) const { RenderPass renderPass; Result result = static_cast( vkCreateRenderPass( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &renderPass ) ) ); return createResultValue( result, renderPass, "vk::Device::createRenderPass" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE VULKAN_HPP_INLINE UniqueRenderPass Device::createRenderPassUnique( const RenderPassCreateInfo & createInfo, Optional allocator ) const { RenderPassDeleter deleter( *this, allocator ); return UniqueRenderPass( createRenderPass( createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::destroyRenderPass( RenderPass renderPass, const AllocationCallbacks* pAllocator ) const { vkDestroyRenderPass( m_device, static_cast( renderPass ), reinterpret_cast( pAllocator ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void Device::destroyRenderPass( RenderPass renderPass, Optional allocator ) const { vkDestroyRenderPass( m_device, static_cast( renderPass ), reinterpret_cast( static_cast( allocator ) ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::getRenderAreaGranularity( RenderPass renderPass, Extent2D* pGranularity ) const { vkGetRenderAreaGranularity( m_device, static_cast( renderPass ), reinterpret_cast( pGranularity ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE Extent2D Device::getRenderAreaGranularity( RenderPass renderPass ) const { Extent2D granularity; vkGetRenderAreaGranularity( m_device, static_cast( renderPass ), reinterpret_cast( &granularity ) ); return granularity; } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::createCommandPool( const CommandPoolCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, CommandPool* pCommandPool ) const { return static_cast( vkCreateCommandPool( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pCommandPool ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::createCommandPool( const CommandPoolCreateInfo & createInfo, Optional allocator ) const { CommandPool commandPool; Result result = static_cast( vkCreateCommandPool( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &commandPool ) ) ); return createResultValue( result, commandPool, "vk::Device::createCommandPool" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE VULKAN_HPP_INLINE UniqueCommandPool Device::createCommandPoolUnique( const CommandPoolCreateInfo & createInfo, Optional allocator ) const { CommandPoolDeleter deleter( *this, allocator ); return UniqueCommandPool( createCommandPool( createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::destroyCommandPool( CommandPool commandPool, const AllocationCallbacks* pAllocator ) const { vkDestroyCommandPool( m_device, static_cast( commandPool ), reinterpret_cast( pAllocator ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void Device::destroyCommandPool( CommandPool commandPool, Optional allocator ) const { vkDestroyCommandPool( m_device, static_cast( commandPool ), reinterpret_cast( static_cast( allocator ) ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE Result Device::resetCommandPool( CommandPool commandPool, CommandPoolResetFlags flags ) const { return static_cast( vkResetCommandPool( m_device, static_cast( commandPool ), static_cast( flags ) ) ); } #else VULKAN_HPP_INLINE ResultValueType::type Device::resetCommandPool( CommandPool commandPool, CommandPoolResetFlags flags ) const { Result result = static_cast( vkResetCommandPool( m_device, static_cast( commandPool ), static_cast( flags ) ) ); return createResultValue( result, "vk::Device::resetCommandPool" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::allocateCommandBuffers( const CommandBufferAllocateInfo* pAllocateInfo, CommandBuffer* pCommandBuffers ) const { return static_cast( vkAllocateCommandBuffers( m_device, reinterpret_cast( pAllocateInfo ), reinterpret_cast( pCommandBuffers ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template VULKAN_HPP_INLINE typename ResultValueType>::type Device::allocateCommandBuffers( const CommandBufferAllocateInfo & allocateInfo ) const { std::vector commandBuffers( allocateInfo.commandBufferCount ); Result result = static_cast( vkAllocateCommandBuffers( m_device, reinterpret_cast( &allocateInfo ), reinterpret_cast( commandBuffers.data() ) ) ); return createResultValue( result, commandBuffers, "vk::Device::allocateCommandBuffers" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE template VULKAN_HPP_INLINE std::vector Device::allocateCommandBuffersUnique( const CommandBufferAllocateInfo & allocateInfo ) const { CommandBufferDeleter deleter( *this, allocateInfo.commandPool ); std::vector commandBuffers = allocateCommandBuffers( allocateInfo ); std::vector uniqueCommandBuffers; uniqueCommandBuffers.reserve( commandBuffers.size() ); for ( auto commandBuffer : commandBuffers ) { uniqueCommandBuffers.push_back( UniqueCommandBuffer( commandBuffer, deleter ) ); } return uniqueCommandBuffers; } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::freeCommandBuffers( CommandPool commandPool, uint32_t commandBufferCount, const CommandBuffer* pCommandBuffers ) const { vkFreeCommandBuffers( m_device, static_cast( commandPool ), commandBufferCount, reinterpret_cast( pCommandBuffers ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void Device::freeCommandBuffers( CommandPool commandPool, ArrayProxy commandBuffers ) const { vkFreeCommandBuffers( m_device, static_cast( commandPool ), commandBuffers.size() , reinterpret_cast( commandBuffers.data() ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::createSharedSwapchainsKHR( uint32_t swapchainCount, const SwapchainCreateInfoKHR* pCreateInfos, const AllocationCallbacks* pAllocator, SwapchainKHR* pSwapchains ) const { return static_cast( vkCreateSharedSwapchainsKHR( m_device, swapchainCount, reinterpret_cast( pCreateInfos ), reinterpret_cast( pAllocator ), reinterpret_cast( pSwapchains ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template VULKAN_HPP_INLINE typename ResultValueType>::type Device::createSharedSwapchainsKHR( ArrayProxy createInfos, Optional allocator ) const { std::vector swapchains( createInfos.size() ); Result result = static_cast( vkCreateSharedSwapchainsKHR( m_device, createInfos.size() , reinterpret_cast( createInfos.data() ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( swapchains.data() ) ) ); return createResultValue( result, swapchains, "vk::Device::createSharedSwapchainsKHR" ); } VULKAN_HPP_INLINE ResultValueType::type Device::createSharedSwapchainKHR( const SwapchainCreateInfoKHR & createInfo, Optional allocator ) const { SwapchainKHR swapchain; Result result = static_cast( vkCreateSharedSwapchainsKHR( m_device, 1 , reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &swapchain ) ) ); return createResultValue( result, swapchain, "vk::Device::createSharedSwapchainKHR" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE template VULKAN_HPP_INLINE std::vector Device::createSharedSwapchainsKHRUnique( ArrayProxy createInfos, Optional allocator ) const { SwapchainKHRDeleter deleter( *this, allocator ); std::vector swapchainKHRs = createSharedSwapchainsKHR( createInfos, allocator ); std::vector uniqueSwapchainKHRs; uniqueSwapchainKHRs.reserve( swapchainKHRs.size() ); for ( auto swapchainKHR : swapchainKHRs ) { uniqueSwapchainKHRs.push_back( UniqueSwapchainKHR( swapchainKHR, deleter ) ); } return uniqueSwapchainKHRs; } VULKAN_HPP_INLINE UniqueSwapchainKHR Device::createSharedSwapchainKHRUnique( const SwapchainCreateInfoKHR & createInfo, Optional allocator ) const { SwapchainKHRDeleter deleter( *this, allocator ); return UniqueSwapchainKHR( createSharedSwapchainKHR( createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::createSwapchainKHR( const SwapchainCreateInfoKHR* pCreateInfo, const AllocationCallbacks* pAllocator, SwapchainKHR* pSwapchain ) const { return static_cast( vkCreateSwapchainKHR( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pSwapchain ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::createSwapchainKHR( const SwapchainCreateInfoKHR & createInfo, Optional allocator ) const { SwapchainKHR swapchain; Result result = static_cast( vkCreateSwapchainKHR( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &swapchain ) ) ); return createResultValue( result, swapchain, "vk::Device::createSwapchainKHR" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE VULKAN_HPP_INLINE UniqueSwapchainKHR Device::createSwapchainKHRUnique( const SwapchainCreateInfoKHR & createInfo, Optional allocator ) const { SwapchainKHRDeleter deleter( *this, allocator ); return UniqueSwapchainKHR( createSwapchainKHR( createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::destroySwapchainKHR( SwapchainKHR swapchain, const AllocationCallbacks* pAllocator ) const { vkDestroySwapchainKHR( m_device, static_cast( swapchain ), reinterpret_cast( pAllocator ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void Device::destroySwapchainKHR( SwapchainKHR swapchain, Optional allocator ) const { vkDestroySwapchainKHR( m_device, static_cast( swapchain ), reinterpret_cast( static_cast( allocator ) ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::getSwapchainImagesKHR( SwapchainKHR swapchain, uint32_t* pSwapchainImageCount, Image* pSwapchainImages ) const { return static_cast( vkGetSwapchainImagesKHR( m_device, static_cast( swapchain ), pSwapchainImageCount, reinterpret_cast( pSwapchainImages ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template VULKAN_HPP_INLINE typename ResultValueType>::type Device::getSwapchainImagesKHR( SwapchainKHR swapchain ) const { std::vector swapchainImages; uint32_t swapchainImageCount; Result result; do { result = static_cast( vkGetSwapchainImagesKHR( m_device, static_cast( swapchain ), &swapchainImageCount, nullptr ) ); if ( ( result == Result::eSuccess ) && swapchainImageCount ) { swapchainImages.resize( swapchainImageCount ); result = static_cast( vkGetSwapchainImagesKHR( m_device, static_cast( swapchain ), &swapchainImageCount, reinterpret_cast( swapchainImages.data() ) ) ); } } while ( result == Result::eIncomplete ); assert( swapchainImageCount <= swapchainImages.size() ); swapchainImages.resize( swapchainImageCount ); return createResultValue( result, swapchainImages, "vk::Device::getSwapchainImagesKHR" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::acquireNextImageKHR( SwapchainKHR swapchain, uint64_t timeout, Semaphore semaphore, Fence fence, uint32_t* pImageIndex ) const { return static_cast( vkAcquireNextImageKHR( m_device, static_cast( swapchain ), timeout, static_cast( semaphore ), static_cast( fence ), pImageIndex ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValue Device::acquireNextImageKHR( SwapchainKHR swapchain, uint64_t timeout, Semaphore semaphore, Fence fence ) const { uint32_t imageIndex; Result result = static_cast( vkAcquireNextImageKHR( m_device, static_cast( swapchain ), timeout, static_cast( semaphore ), static_cast( fence ), &imageIndex ) ); return createResultValue( result, imageIndex, "vk::Device::acquireNextImageKHR", { Result::eSuccess, Result::eTimeout, Result::eNotReady, Result::eSuboptimalKHR } ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::debugMarkerSetObjectNameEXT( DebugMarkerObjectNameInfoEXT* pNameInfo ) const { return static_cast( vkDebugMarkerSetObjectNameEXT( m_device, reinterpret_cast( pNameInfo ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::debugMarkerSetObjectNameEXT() const { DebugMarkerObjectNameInfoEXT nameInfo; Result result = static_cast( vkDebugMarkerSetObjectNameEXT( m_device, reinterpret_cast( &nameInfo ) ) ); return createResultValue( result, nameInfo, "vk::Device::debugMarkerSetObjectNameEXT" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::debugMarkerSetObjectTagEXT( DebugMarkerObjectTagInfoEXT* pTagInfo ) const { return static_cast( vkDebugMarkerSetObjectTagEXT( m_device, reinterpret_cast( pTagInfo ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::debugMarkerSetObjectTagEXT() const { DebugMarkerObjectTagInfoEXT tagInfo; Result result = static_cast( vkDebugMarkerSetObjectTagEXT( m_device, reinterpret_cast( &tagInfo ) ) ); return createResultValue( result, tagInfo, "vk::Device::debugMarkerSetObjectTagEXT" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VK_USE_PLATFORM_WIN32_KHR VULKAN_HPP_INLINE Result Device::getMemoryWin32HandleNV( DeviceMemory memory, ExternalMemoryHandleTypeFlagsNV handleType, HANDLE* pHandle ) const { return static_cast( vkGetMemoryWin32HandleNV( m_device, static_cast( memory ), static_cast( handleType ), pHandle ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::getMemoryWin32HandleNV( DeviceMemory memory, ExternalMemoryHandleTypeFlagsNV handleType ) const { HANDLE handle; Result result = static_cast( vkGetMemoryWin32HandleNV( m_device, static_cast( memory ), static_cast( handleType ), &handle ) ); return createResultValue( result, handle, "vk::Device::getMemoryWin32HandleNV" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_WIN32_KHR*/ VULKAN_HPP_INLINE Result Device::createIndirectCommandsLayoutNVX( const IndirectCommandsLayoutCreateInfoNVX* pCreateInfo, const AllocationCallbacks* pAllocator, IndirectCommandsLayoutNVX* pIndirectCommandsLayout ) const { return static_cast( vkCreateIndirectCommandsLayoutNVX( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pIndirectCommandsLayout ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::createIndirectCommandsLayoutNVX( const IndirectCommandsLayoutCreateInfoNVX & createInfo, Optional allocator ) const { IndirectCommandsLayoutNVX indirectCommandsLayout; Result result = static_cast( vkCreateIndirectCommandsLayoutNVX( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &indirectCommandsLayout ) ) ); return createResultValue( result, indirectCommandsLayout, "vk::Device::createIndirectCommandsLayoutNVX" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE VULKAN_HPP_INLINE UniqueIndirectCommandsLayoutNVX Device::createIndirectCommandsLayoutNVXUnique( const IndirectCommandsLayoutCreateInfoNVX & createInfo, Optional allocator ) const { IndirectCommandsLayoutNVXDeleter deleter( *this, allocator ); return UniqueIndirectCommandsLayoutNVX( createIndirectCommandsLayoutNVX( createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::destroyIndirectCommandsLayoutNVX( IndirectCommandsLayoutNVX indirectCommandsLayout, const AllocationCallbacks* pAllocator ) const { vkDestroyIndirectCommandsLayoutNVX( m_device, static_cast( indirectCommandsLayout ), reinterpret_cast( pAllocator ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void Device::destroyIndirectCommandsLayoutNVX( IndirectCommandsLayoutNVX indirectCommandsLayout, Optional allocator ) const { vkDestroyIndirectCommandsLayoutNVX( m_device, static_cast( indirectCommandsLayout ), reinterpret_cast( static_cast( allocator ) ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::createObjectTableNVX( const ObjectTableCreateInfoNVX* pCreateInfo, const AllocationCallbacks* pAllocator, ObjectTableNVX* pObjectTable ) const { return static_cast( vkCreateObjectTableNVX( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pObjectTable ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::createObjectTableNVX( const ObjectTableCreateInfoNVX & createInfo, Optional allocator ) const { ObjectTableNVX objectTable; Result result = static_cast( vkCreateObjectTableNVX( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &objectTable ) ) ); return createResultValue( result, objectTable, "vk::Device::createObjectTableNVX" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE VULKAN_HPP_INLINE UniqueObjectTableNVX Device::createObjectTableNVXUnique( const ObjectTableCreateInfoNVX & createInfo, Optional allocator ) const { ObjectTableNVXDeleter deleter( *this, allocator ); return UniqueObjectTableNVX( createObjectTableNVX( createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::destroyObjectTableNVX( ObjectTableNVX objectTable, const AllocationCallbacks* pAllocator ) const { vkDestroyObjectTableNVX( m_device, static_cast( objectTable ), reinterpret_cast( pAllocator ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void Device::destroyObjectTableNVX( ObjectTableNVX objectTable, Optional allocator ) const { vkDestroyObjectTableNVX( m_device, static_cast( objectTable ), reinterpret_cast( static_cast( allocator ) ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::registerObjectsNVX( ObjectTableNVX objectTable, uint32_t objectCount, const ObjectTableEntryNVX* const* ppObjectTableEntries, const uint32_t* pObjectIndices ) const { return static_cast( vkRegisterObjectsNVX( m_device, static_cast( objectTable ), objectCount, reinterpret_cast( ppObjectTableEntries ), pObjectIndices ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::registerObjectsNVX( ObjectTableNVX objectTable, ArrayProxy pObjectTableEntries, ArrayProxy objectIndices ) const { #ifdef VULKAN_HPP_NO_EXCEPTIONS assert( pObjectTableEntries.size() == objectIndices.size() ); #else if ( pObjectTableEntries.size() != objectIndices.size() ) { throw std::logic_error( "vk::Device::registerObjectsNVX: pObjectTableEntries.size() != objectIndices.size()" ); } #endif // VULKAN_HPP_NO_EXCEPTIONS Result result = static_cast( vkRegisterObjectsNVX( m_device, static_cast( objectTable ), pObjectTableEntries.size() , reinterpret_cast( pObjectTableEntries.data() ), objectIndices.data() ) ); return createResultValue( result, "vk::Device::registerObjectsNVX" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::unregisterObjectsNVX( ObjectTableNVX objectTable, uint32_t objectCount, const ObjectEntryTypeNVX* pObjectEntryTypes, const uint32_t* pObjectIndices ) const { return static_cast( vkUnregisterObjectsNVX( m_device, static_cast( objectTable ), objectCount, reinterpret_cast( pObjectEntryTypes ), pObjectIndices ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::unregisterObjectsNVX( ObjectTableNVX objectTable, ArrayProxy objectEntryTypes, ArrayProxy objectIndices ) const { #ifdef VULKAN_HPP_NO_EXCEPTIONS assert( objectEntryTypes.size() == objectIndices.size() ); #else if ( objectEntryTypes.size() != objectIndices.size() ) { throw std::logic_error( "vk::Device::unregisterObjectsNVX: objectEntryTypes.size() != objectIndices.size()" ); } #endif // VULKAN_HPP_NO_EXCEPTIONS Result result = static_cast( vkUnregisterObjectsNVX( m_device, static_cast( objectTable ), objectEntryTypes.size() , reinterpret_cast( objectEntryTypes.data() ), objectIndices.data() ) ); return createResultValue( result, "vk::Device::unregisterObjectsNVX" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::trimCommandPoolKHR( CommandPool commandPool, CommandPoolTrimFlagsKHR flags ) const { vkTrimCommandPoolKHR( m_device, static_cast( commandPool ), static_cast( flags ) ); } #ifdef VK_USE_PLATFORM_WIN32_KHX VULKAN_HPP_INLINE Result Device::getMemoryWin32HandleKHX( DeviceMemory memory, ExternalMemoryHandleTypeFlagBitsKHX handleType, HANDLE* pHandle ) const { return static_cast( vkGetMemoryWin32HandleKHX( m_device, static_cast( memory ), static_cast( handleType ), pHandle ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::getMemoryWin32HandleKHX( DeviceMemory memory, ExternalMemoryHandleTypeFlagBitsKHX handleType ) const { HANDLE handle; Result result = static_cast( vkGetMemoryWin32HandleKHX( m_device, static_cast( memory ), static_cast( handleType ), &handle ) ); return createResultValue( result, handle, "vk::Device::getMemoryWin32HandleKHX" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_WIN32_KHX*/ #ifdef VK_USE_PLATFORM_WIN32_KHX VULKAN_HPP_INLINE Result Device::getMemoryWin32HandlePropertiesKHX( ExternalMemoryHandleTypeFlagBitsKHX handleType, HANDLE handle, MemoryWin32HandlePropertiesKHX* pMemoryWin32HandleProperties ) const { return static_cast( vkGetMemoryWin32HandlePropertiesKHX( m_device, static_cast( handleType ), handle, reinterpret_cast( pMemoryWin32HandleProperties ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::getMemoryWin32HandlePropertiesKHX( ExternalMemoryHandleTypeFlagBitsKHX handleType, HANDLE handle ) const { MemoryWin32HandlePropertiesKHX memoryWin32HandleProperties; Result result = static_cast( vkGetMemoryWin32HandlePropertiesKHX( m_device, static_cast( handleType ), handle, reinterpret_cast( &memoryWin32HandleProperties ) ) ); return createResultValue( result, memoryWin32HandleProperties, "vk::Device::getMemoryWin32HandlePropertiesKHX" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_WIN32_KHX*/ VULKAN_HPP_INLINE Result Device::getMemoryFdKHX( DeviceMemory memory, ExternalMemoryHandleTypeFlagBitsKHX handleType, int* pFd ) const { return static_cast( vkGetMemoryFdKHX( m_device, static_cast( memory ), static_cast( handleType ), pFd ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::getMemoryFdKHX( DeviceMemory memory, ExternalMemoryHandleTypeFlagBitsKHX handleType ) const { int fd; Result result = static_cast( vkGetMemoryFdKHX( m_device, static_cast( memory ), static_cast( handleType ), &fd ) ); return createResultValue( result, fd, "vk::Device::getMemoryFdKHX" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::getMemoryFdPropertiesKHX( ExternalMemoryHandleTypeFlagBitsKHX handleType, int fd, MemoryFdPropertiesKHX* pMemoryFdProperties ) const { return static_cast( vkGetMemoryFdPropertiesKHX( m_device, static_cast( handleType ), fd, reinterpret_cast( pMemoryFdProperties ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::getMemoryFdPropertiesKHX( ExternalMemoryHandleTypeFlagBitsKHX handleType, int fd ) const { MemoryFdPropertiesKHX memoryFdProperties; Result result = static_cast( vkGetMemoryFdPropertiesKHX( m_device, static_cast( handleType ), fd, reinterpret_cast( &memoryFdProperties ) ) ); return createResultValue( result, memoryFdProperties, "vk::Device::getMemoryFdPropertiesKHX" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VK_USE_PLATFORM_WIN32_KHX VULKAN_HPP_INLINE Result Device::getSemaphoreWin32HandleKHX( Semaphore semaphore, ExternalSemaphoreHandleTypeFlagBitsKHX handleType, HANDLE* pHandle ) const { return static_cast( vkGetSemaphoreWin32HandleKHX( m_device, static_cast( semaphore ), static_cast( handleType ), pHandle ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::getSemaphoreWin32HandleKHX( Semaphore semaphore, ExternalSemaphoreHandleTypeFlagBitsKHX handleType ) const { HANDLE handle; Result result = static_cast( vkGetSemaphoreWin32HandleKHX( m_device, static_cast( semaphore ), static_cast( handleType ), &handle ) ); return createResultValue( result, handle, "vk::Device::getSemaphoreWin32HandleKHX" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_WIN32_KHX*/ #ifdef VK_USE_PLATFORM_WIN32_KHX VULKAN_HPP_INLINE Result Device::importSemaphoreWin32HandleKHX( const ImportSemaphoreWin32HandleInfoKHX* pImportSemaphoreWin32HandleInfo ) const { return static_cast( vkImportSemaphoreWin32HandleKHX( m_device, reinterpret_cast( pImportSemaphoreWin32HandleInfo ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::importSemaphoreWin32HandleKHX( const ImportSemaphoreWin32HandleInfoKHX & importSemaphoreWin32HandleInfo ) const { Result result = static_cast( vkImportSemaphoreWin32HandleKHX( m_device, reinterpret_cast( &importSemaphoreWin32HandleInfo ) ) ); return createResultValue( result, "vk::Device::importSemaphoreWin32HandleKHX" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_WIN32_KHX*/ VULKAN_HPP_INLINE Result Device::getSemaphoreFdKHX( Semaphore semaphore, ExternalSemaphoreHandleTypeFlagBitsKHX handleType, int* pFd ) const { return static_cast( vkGetSemaphoreFdKHX( m_device, static_cast( semaphore ), static_cast( handleType ), pFd ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::getSemaphoreFdKHX( Semaphore semaphore, ExternalSemaphoreHandleTypeFlagBitsKHX handleType ) const { int fd; Result result = static_cast( vkGetSemaphoreFdKHX( m_device, static_cast( semaphore ), static_cast( handleType ), &fd ) ); return createResultValue( result, fd, "vk::Device::getSemaphoreFdKHX" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::importSemaphoreFdKHX( const ImportSemaphoreFdInfoKHX* pImportSemaphoreFdInfo ) const { return static_cast( vkImportSemaphoreFdKHX( m_device, reinterpret_cast( pImportSemaphoreFdInfo ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::importSemaphoreFdKHX( const ImportSemaphoreFdInfoKHX & importSemaphoreFdInfo ) const { Result result = static_cast( vkImportSemaphoreFdKHX( m_device, reinterpret_cast( &importSemaphoreFdInfo ) ) ); return createResultValue( result, "vk::Device::importSemaphoreFdKHX" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::displayPowerControlEXT( DisplayKHR display, const DisplayPowerInfoEXT* pDisplayPowerInfo ) const { return static_cast( vkDisplayPowerControlEXT( m_device, static_cast( display ), reinterpret_cast( pDisplayPowerInfo ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::displayPowerControlEXT( DisplayKHR display, const DisplayPowerInfoEXT & displayPowerInfo ) const { Result result = static_cast( vkDisplayPowerControlEXT( m_device, static_cast( display ), reinterpret_cast( &displayPowerInfo ) ) ); return createResultValue( result, "vk::Device::displayPowerControlEXT" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::registerEventEXT( const DeviceEventInfoEXT* pDeviceEventInfo, const AllocationCallbacks* pAllocator, Fence* pFence ) const { return static_cast( vkRegisterDeviceEventEXT( m_device, reinterpret_cast( pDeviceEventInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pFence ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::registerEventEXT( const DeviceEventInfoEXT & deviceEventInfo, const AllocationCallbacks & allocator ) const { Fence fence; Result result = static_cast( vkRegisterDeviceEventEXT( m_device, reinterpret_cast( &deviceEventInfo ), reinterpret_cast( &allocator ), reinterpret_cast( &fence ) ) ); return createResultValue( result, fence, "vk::Device::registerEventEXT" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::registerDisplayEventEXT( DisplayKHR display, const DisplayEventInfoEXT* pDisplayEventInfo, const AllocationCallbacks* pAllocator, Fence* pFence ) const { return static_cast( vkRegisterDisplayEventEXT( m_device, static_cast( display ), reinterpret_cast( pDisplayEventInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pFence ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::registerDisplayEventEXT( DisplayKHR display, const DisplayEventInfoEXT & displayEventInfo, const AllocationCallbacks & allocator ) const { Fence fence; Result result = static_cast( vkRegisterDisplayEventEXT( m_device, static_cast( display ), reinterpret_cast( &displayEventInfo ), reinterpret_cast( &allocator ), reinterpret_cast( &fence ) ) ); return createResultValue( result, fence, "vk::Device::registerDisplayEventEXT" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::getSwapchainCounterEXT( SwapchainKHR swapchain, SurfaceCounterFlagBitsEXT counter, uint64_t* pCounterValue ) const { return static_cast( vkGetSwapchainCounterEXT( m_device, static_cast( swapchain ), static_cast( counter ), pCounterValue ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValue Device::getSwapchainCounterEXT( SwapchainKHR swapchain, SurfaceCounterFlagBitsEXT counter ) const { uint64_t counterValue; Result result = static_cast( vkGetSwapchainCounterEXT( m_device, static_cast( swapchain ), static_cast( counter ), &counterValue ) ); return createResultValue( result, counterValue, "vk::Device::getSwapchainCounterEXT", { Result::eSuccess, Result::eErrorDeviceLost, Result::eErrorOutOfDateKHR } ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::getGroupPeerMemoryFeaturesKHX( uint32_t heapIndex, uint32_t localDeviceIndex, uint32_t remoteDeviceIndex, PeerMemoryFeatureFlagsKHX* pPeerMemoryFeatures ) const { vkGetDeviceGroupPeerMemoryFeaturesKHX( m_device, heapIndex, localDeviceIndex, remoteDeviceIndex, reinterpret_cast( pPeerMemoryFeatures ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE PeerMemoryFeatureFlagsKHX Device::getGroupPeerMemoryFeaturesKHX( uint32_t heapIndex, uint32_t localDeviceIndex, uint32_t remoteDeviceIndex ) const { PeerMemoryFeatureFlagsKHX peerMemoryFeatures; vkGetDeviceGroupPeerMemoryFeaturesKHX( m_device, heapIndex, localDeviceIndex, remoteDeviceIndex, reinterpret_cast( &peerMemoryFeatures ) ); return peerMemoryFeatures; } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::bindBufferMemory2KHX( uint32_t bindInfoCount, const BindBufferMemoryInfoKHX* pBindInfos ) const { return static_cast( vkBindBufferMemory2KHX( m_device, bindInfoCount, reinterpret_cast( pBindInfos ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::bindBufferMemory2KHX( ArrayProxy bindInfos ) const { Result result = static_cast( vkBindBufferMemory2KHX( m_device, bindInfos.size() , reinterpret_cast( bindInfos.data() ) ) ); return createResultValue( result, "vk::Device::bindBufferMemory2KHX" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::bindImageMemory2KHX( uint32_t bindInfoCount, const BindImageMemoryInfoKHX* pBindInfos ) const { return static_cast( vkBindImageMemory2KHX( m_device, bindInfoCount, reinterpret_cast( pBindInfos ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::bindImageMemory2KHX( ArrayProxy bindInfos ) const { Result result = static_cast( vkBindImageMemory2KHX( m_device, bindInfos.size() , reinterpret_cast( bindInfos.data() ) ) ); return createResultValue( result, "vk::Device::bindImageMemory2KHX" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::getGroupPresentCapabilitiesKHX( DeviceGroupPresentCapabilitiesKHX* pDeviceGroupPresentCapabilities ) const { return static_cast( vkGetDeviceGroupPresentCapabilitiesKHX( m_device, reinterpret_cast( pDeviceGroupPresentCapabilities ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::getGroupPresentCapabilitiesKHX() const { DeviceGroupPresentCapabilitiesKHX deviceGroupPresentCapabilities; Result result = static_cast( vkGetDeviceGroupPresentCapabilitiesKHX( m_device, reinterpret_cast( &deviceGroupPresentCapabilities ) ) ); return createResultValue( result, deviceGroupPresentCapabilities, "vk::Device::getGroupPresentCapabilitiesKHX" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::getGroupSurfacePresentModesKHX( SurfaceKHR surface, DeviceGroupPresentModeFlagsKHX* pModes ) const { return static_cast( vkGetDeviceGroupSurfacePresentModesKHX( m_device, static_cast( surface ), reinterpret_cast( pModes ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::getGroupSurfacePresentModesKHX( SurfaceKHR surface ) const { DeviceGroupPresentModeFlagsKHX modes; Result result = static_cast( vkGetDeviceGroupSurfacePresentModesKHX( m_device, static_cast( surface ), reinterpret_cast( &modes ) ) ); return createResultValue( result, modes, "vk::Device::getGroupSurfacePresentModesKHX" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::acquireNextImage2KHX( const AcquireNextImageInfoKHX* pAcquireInfo, uint32_t* pImageIndex ) const { return static_cast( vkAcquireNextImage2KHX( m_device, reinterpret_cast( pAcquireInfo ), pImageIndex ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValue Device::acquireNextImage2KHX( const AcquireNextImageInfoKHX & acquireInfo ) const { uint32_t imageIndex; Result result = static_cast( vkAcquireNextImage2KHX( m_device, reinterpret_cast( &acquireInfo ), &imageIndex ) ); return createResultValue( result, imageIndex, "vk::Device::acquireNextImage2KHX", { Result::eSuccess, Result::eTimeout, Result::eNotReady, Result::eSuboptimalKHR } ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::createDescriptorUpdateTemplateKHR( const DescriptorUpdateTemplateCreateInfoKHR* pCreateInfo, const AllocationCallbacks* pAllocator, DescriptorUpdateTemplateKHR* pDescriptorUpdateTemplate ) const { return static_cast( vkCreateDescriptorUpdateTemplateKHR( m_device, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pDescriptorUpdateTemplate ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::createDescriptorUpdateTemplateKHR( const DescriptorUpdateTemplateCreateInfoKHR & createInfo, Optional allocator ) const { DescriptorUpdateTemplateKHR descriptorUpdateTemplate; Result result = static_cast( vkCreateDescriptorUpdateTemplateKHR( m_device, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &descriptorUpdateTemplate ) ) ); return createResultValue( result, descriptorUpdateTemplate, "vk::Device::createDescriptorUpdateTemplateKHR" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE VULKAN_HPP_INLINE UniqueDescriptorUpdateTemplateKHR Device::createDescriptorUpdateTemplateKHRUnique( const DescriptorUpdateTemplateCreateInfoKHR & createInfo, Optional allocator ) const { DescriptorUpdateTemplateKHRDeleter deleter( *this, allocator ); return UniqueDescriptorUpdateTemplateKHR( createDescriptorUpdateTemplateKHR( createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::destroyDescriptorUpdateTemplateKHR( DescriptorUpdateTemplateKHR descriptorUpdateTemplate, const AllocationCallbacks* pAllocator ) const { vkDestroyDescriptorUpdateTemplateKHR( m_device, static_cast( descriptorUpdateTemplate ), reinterpret_cast( pAllocator ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void Device::destroyDescriptorUpdateTemplateKHR( DescriptorUpdateTemplateKHR descriptorUpdateTemplate, Optional allocator ) const { vkDestroyDescriptorUpdateTemplateKHR( m_device, static_cast( descriptorUpdateTemplate ), reinterpret_cast( static_cast( allocator ) ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Device::updateDescriptorSetWithTemplateKHR( DescriptorSet descriptorSet, DescriptorUpdateTemplateKHR descriptorUpdateTemplate, const void* pData ) const { vkUpdateDescriptorSetWithTemplateKHR( m_device, static_cast( descriptorSet ), static_cast( descriptorUpdateTemplate ), pData ); } VULKAN_HPP_INLINE void Device::setHdrMetadataEXT( uint32_t swapchainCount, const SwapchainKHR* pSwapchains, const HdrMetadataEXT* pMetadata ) const { vkSetHdrMetadataEXT( m_device, swapchainCount, reinterpret_cast( pSwapchains ), reinterpret_cast( pMetadata ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void Device::setHdrMetadataEXT( ArrayProxy swapchains, ArrayProxy metadata ) const { #ifdef VULKAN_HPP_NO_EXCEPTIONS assert( swapchains.size() == metadata.size() ); #else if ( swapchains.size() != metadata.size() ) { throw std::logic_error( "vk::Device::setHdrMetadataEXT: swapchains.size() != metadata.size()" ); } #endif // VULKAN_HPP_NO_EXCEPTIONS vkSetHdrMetadataEXT( m_device, swapchains.size() , reinterpret_cast( swapchains.data() ), reinterpret_cast( metadata.data() ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE Result Device::getSwapchainStatusKHR( SwapchainKHR swapchain ) const { return static_cast( vkGetSwapchainStatusKHR( m_device, static_cast( swapchain ) ) ); } #else VULKAN_HPP_INLINE Result Device::getSwapchainStatusKHR( SwapchainKHR swapchain ) const { Result result = static_cast( vkGetSwapchainStatusKHR( m_device, static_cast( swapchain ) ) ); return createResultValue( result, "vk::Device::getSwapchainStatusKHR", { Result::eSuccess, Result::eSuboptimalKHR } ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::getRefreshCycleDurationGOOGLE( SwapchainKHR swapchain, RefreshCycleDurationGOOGLE* pDisplayTimingProperties ) const { return static_cast( vkGetRefreshCycleDurationGOOGLE( m_device, static_cast( swapchain ), reinterpret_cast( pDisplayTimingProperties ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Device::getRefreshCycleDurationGOOGLE( SwapchainKHR swapchain ) const { RefreshCycleDurationGOOGLE displayTimingProperties; Result result = static_cast( vkGetRefreshCycleDurationGOOGLE( m_device, static_cast( swapchain ), reinterpret_cast( &displayTimingProperties ) ) ); return createResultValue( result, displayTimingProperties, "vk::Device::getRefreshCycleDurationGOOGLE" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Device::getPastPresentationTimingGOOGLE( SwapchainKHR swapchain, uint32_t* pPresentationTimingCount, PastPresentationTimingGOOGLE* pPresentationTimings ) const { return static_cast( vkGetPastPresentationTimingGOOGLE( m_device, static_cast( swapchain ), pPresentationTimingCount, reinterpret_cast( pPresentationTimings ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template VULKAN_HPP_INLINE typename ResultValueType>::type Device::getPastPresentationTimingGOOGLE( SwapchainKHR swapchain ) const { std::vector presentationTimings; uint32_t presentationTimingCount; Result result = static_cast( vkGetPastPresentationTimingGOOGLE( m_device, static_cast( swapchain ), &presentationTimingCount, nullptr ) ); if ( ( result == Result::eSuccess ) && presentationTimingCount ) { presentationTimings.resize( presentationTimingCount ); result = static_cast( vkGetPastPresentationTimingGOOGLE( m_device, static_cast( swapchain ), &presentationTimingCount, reinterpret_cast( presentationTimings.data() ) ) ); } return createResultValue( result, presentationTimings, "vk::Device::getPastPresentationTimingGOOGLE" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifndef VULKAN_HPP_NO_SMART_HANDLE class DeviceDeleter; using UniqueDevice = UniqueHandle; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ class PhysicalDevice { public: PhysicalDevice() : m_physicalDevice(VK_NULL_HANDLE) {} PhysicalDevice( std::nullptr_t ) : m_physicalDevice(VK_NULL_HANDLE) {} VULKAN_HPP_TYPESAFE_EXPLICIT PhysicalDevice(VkPhysicalDevice physicalDevice) : m_physicalDevice(physicalDevice) {} #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) PhysicalDevice& operator=(VkPhysicalDevice physicalDevice) { m_physicalDevice = physicalDevice; return *this; } #endif PhysicalDevice& operator=( std::nullptr_t ) { m_physicalDevice = VK_NULL_HANDLE; return *this; } bool operator==(PhysicalDevice const &rhs) const { return m_physicalDevice == rhs.m_physicalDevice; } bool operator!=(PhysicalDevice const &rhs) const { return m_physicalDevice != rhs.m_physicalDevice; } bool operator<(PhysicalDevice const &rhs) const { return m_physicalDevice < rhs.m_physicalDevice; } void getProperties( PhysicalDeviceProperties* pProperties ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE PhysicalDeviceProperties getProperties() const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void getQueueFamilyProperties( uint32_t* pQueueFamilyPropertyCount, QueueFamilyProperties* pQueueFamilyProperties ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template > std::vector getQueueFamilyProperties() const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void getMemoryProperties( PhysicalDeviceMemoryProperties* pMemoryProperties ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE PhysicalDeviceMemoryProperties getMemoryProperties() const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void getFeatures( PhysicalDeviceFeatures* pFeatures ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE PhysicalDeviceFeatures getFeatures() const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void getFormatProperties( Format format, FormatProperties* pFormatProperties ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE FormatProperties getFormatProperties( Format format ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result getImageFormatProperties( Format format, ImageType type, ImageTiling tiling, ImageUsageFlags usage, ImageCreateFlags flags, ImageFormatProperties* pImageFormatProperties ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type getImageFormatProperties( Format format, ImageType type, ImageTiling tiling, ImageUsageFlags usage, ImageCreateFlags flags ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result createDevice( const DeviceCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, Device* pDevice ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type createDevice( const DeviceCreateInfo & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE UniqueDevice createDeviceUnique( const DeviceCreateInfo & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result enumerateDeviceLayerProperties( uint32_t* pPropertyCount, LayerProperties* pProperties ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template > typename ResultValueType>::type enumerateDeviceLayerProperties() const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result enumerateDeviceExtensionProperties( const char* pLayerName, uint32_t* pPropertyCount, ExtensionProperties* pProperties ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template > typename ResultValueType>::type enumerateDeviceExtensionProperties( Optional layerName = nullptr ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void getSparseImageFormatProperties( Format format, ImageType type, SampleCountFlagBits samples, ImageUsageFlags usage, ImageTiling tiling, uint32_t* pPropertyCount, SparseImageFormatProperties* pProperties ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template > std::vector getSparseImageFormatProperties( Format format, ImageType type, SampleCountFlagBits samples, ImageUsageFlags usage, ImageTiling tiling ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result getDisplayPropertiesKHR( uint32_t* pPropertyCount, DisplayPropertiesKHR* pProperties ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template > typename ResultValueType>::type getDisplayPropertiesKHR() const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result getDisplayPlanePropertiesKHR( uint32_t* pPropertyCount, DisplayPlanePropertiesKHR* pProperties ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template > typename ResultValueType>::type getDisplayPlanePropertiesKHR() const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result getDisplayPlaneSupportedDisplaysKHR( uint32_t planeIndex, uint32_t* pDisplayCount, DisplayKHR* pDisplays ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template > typename ResultValueType>::type getDisplayPlaneSupportedDisplaysKHR( uint32_t planeIndex ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result getDisplayModePropertiesKHR( DisplayKHR display, uint32_t* pPropertyCount, DisplayModePropertiesKHR* pProperties ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template > typename ResultValueType>::type getDisplayModePropertiesKHR( DisplayKHR display ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result createDisplayModeKHR( DisplayKHR display, const DisplayModeCreateInfoKHR* pCreateInfo, const AllocationCallbacks* pAllocator, DisplayModeKHR* pMode ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type createDisplayModeKHR( DisplayKHR display, const DisplayModeCreateInfoKHR & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result getDisplayPlaneCapabilitiesKHR( DisplayModeKHR mode, uint32_t planeIndex, DisplayPlaneCapabilitiesKHR* pCapabilities ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type getDisplayPlaneCapabilitiesKHR( DisplayModeKHR mode, uint32_t planeIndex ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VK_USE_PLATFORM_MIR_KHR Bool32 getMirPresentationSupportKHR( uint32_t queueFamilyIndex, MirConnection* connection ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE Bool32 getMirPresentationSupportKHR( uint32_t queueFamilyIndex, MirConnection & connection ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_MIR_KHR*/ Result getSurfaceSupportKHR( uint32_t queueFamilyIndex, SurfaceKHR surface, Bool32* pSupported ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type getSurfaceSupportKHR( uint32_t queueFamilyIndex, SurfaceKHR surface ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result getSurfaceCapabilitiesKHR( SurfaceKHR surface, SurfaceCapabilitiesKHR* pSurfaceCapabilities ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type getSurfaceCapabilitiesKHR( SurfaceKHR surface ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result getSurfaceFormatsKHR( SurfaceKHR surface, uint32_t* pSurfaceFormatCount, SurfaceFormatKHR* pSurfaceFormats ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template > typename ResultValueType>::type getSurfaceFormatsKHR( SurfaceKHR surface ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result getSurfacePresentModesKHR( SurfaceKHR surface, uint32_t* pPresentModeCount, PresentModeKHR* pPresentModes ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template > typename ResultValueType>::type getSurfacePresentModesKHR( SurfaceKHR surface ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VK_USE_PLATFORM_WAYLAND_KHR Bool32 getWaylandPresentationSupportKHR( uint32_t queueFamilyIndex, struct wl_display* display ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE Bool32 getWaylandPresentationSupportKHR( uint32_t queueFamilyIndex, struct wl_display & display ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_WAYLAND_KHR*/ #ifdef VK_USE_PLATFORM_WIN32_KHR Bool32 getWin32PresentationSupportKHR( uint32_t queueFamilyIndex ) const; #endif /*VK_USE_PLATFORM_WIN32_KHR*/ #ifdef VK_USE_PLATFORM_XLIB_KHR Bool32 getXlibPresentationSupportKHR( uint32_t queueFamilyIndex, Display* dpy, VisualID visualID ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE Bool32 getXlibPresentationSupportKHR( uint32_t queueFamilyIndex, Display & dpy, VisualID visualID ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_XLIB_KHR*/ #ifdef VK_USE_PLATFORM_XCB_KHR Bool32 getXcbPresentationSupportKHR( uint32_t queueFamilyIndex, xcb_connection_t* connection, xcb_visualid_t visual_id ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE Bool32 getXcbPresentationSupportKHR( uint32_t queueFamilyIndex, xcb_connection_t & connection, xcb_visualid_t visual_id ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_XCB_KHR*/ Result getExternalImageFormatPropertiesNV( Format format, ImageType type, ImageTiling tiling, ImageUsageFlags usage, ImageCreateFlags flags, ExternalMemoryHandleTypeFlagsNV externalHandleType, ExternalImageFormatPropertiesNV* pExternalImageFormatProperties ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type getExternalImageFormatPropertiesNV( Format format, ImageType type, ImageTiling tiling, ImageUsageFlags usage, ImageCreateFlags flags, ExternalMemoryHandleTypeFlagsNV externalHandleType ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void getGeneratedCommandsPropertiesNVX( DeviceGeneratedCommandsFeaturesNVX* pFeatures, DeviceGeneratedCommandsLimitsNVX* pLimits ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE DeviceGeneratedCommandsLimitsNVX getGeneratedCommandsPropertiesNVX( DeviceGeneratedCommandsFeaturesNVX & features ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void getFeatures2KHR( PhysicalDeviceFeatures2KHR* pFeatures ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE PhysicalDeviceFeatures2KHR getFeatures2KHR() const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void getProperties2KHR( PhysicalDeviceProperties2KHR* pProperties ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE PhysicalDeviceProperties2KHR getProperties2KHR() const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void getFormatProperties2KHR( Format format, FormatProperties2KHR* pFormatProperties ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE FormatProperties2KHR getFormatProperties2KHR( Format format ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result getImageFormatProperties2KHR( const PhysicalDeviceImageFormatInfo2KHR* pImageFormatInfo, ImageFormatProperties2KHR* pImageFormatProperties ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type getImageFormatProperties2KHR( const PhysicalDeviceImageFormatInfo2KHR & imageFormatInfo ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void getQueueFamilyProperties2KHR( uint32_t* pQueueFamilyPropertyCount, QueueFamilyProperties2KHR* pQueueFamilyProperties ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template > std::vector getQueueFamilyProperties2KHR() const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void getMemoryProperties2KHR( PhysicalDeviceMemoryProperties2KHR* pMemoryProperties ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE PhysicalDeviceMemoryProperties2KHR getMemoryProperties2KHR() const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void getSparseImageFormatProperties2KHR( const PhysicalDeviceSparseImageFormatInfo2KHR* pFormatInfo, uint32_t* pPropertyCount, SparseImageFormatProperties2KHR* pProperties ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template > std::vector getSparseImageFormatProperties2KHR( const PhysicalDeviceSparseImageFormatInfo2KHR & formatInfo ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void getExternalBufferPropertiesKHX( const PhysicalDeviceExternalBufferInfoKHX* pExternalBufferInfo, ExternalBufferPropertiesKHX* pExternalBufferProperties ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ExternalBufferPropertiesKHX getExternalBufferPropertiesKHX( const PhysicalDeviceExternalBufferInfoKHX & externalBufferInfo ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void getExternalSemaphorePropertiesKHX( const PhysicalDeviceExternalSemaphoreInfoKHX* pExternalSemaphoreInfo, ExternalSemaphorePropertiesKHX* pExternalSemaphoreProperties ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ExternalSemaphorePropertiesKHX getExternalSemaphorePropertiesKHX( const PhysicalDeviceExternalSemaphoreInfoKHX & externalSemaphoreInfo ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE Result releaseDisplayEXT( DisplayKHR display ) const; #else ResultValueType::type releaseDisplayEXT( DisplayKHR display ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT Result acquireXlibDisplayEXT( Display* dpy, DisplayKHR display ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type acquireXlibDisplayEXT( DisplayKHR display ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_XLIB_XRANDR_EXT*/ #ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT Result getRandROutputDisplayEXT( Display* dpy, RROutput rrOutput, DisplayKHR* pDisplay ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type getRandROutputDisplayEXT( Display & dpy, RROutput rrOutput ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_XLIB_XRANDR_EXT*/ Result getSurfaceCapabilities2EXT( SurfaceKHR surface, SurfaceCapabilities2EXT* pSurfaceCapabilities ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type getSurfaceCapabilities2EXT( SurfaceKHR surface ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result getPresentRectanglesKHX( SurfaceKHR surface, uint32_t* pRectCount, Rect2D* pRects ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template > typename ResultValueType>::type getPresentRectanglesKHX( SurfaceKHR surface ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result getSurfaceCapabilities2KHR( const PhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, SurfaceCapabilities2KHR* pSurfaceCapabilities ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE Result getSurfaceCapabilities2KHR( const PhysicalDeviceSurfaceInfo2KHR & surfaceInfo ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result getSurfaceFormats2KHR( const PhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, uint32_t* pSurfaceFormatCount, SurfaceFormat2KHR* pSurfaceFormats ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template > typename ResultValueType>::type getSurfaceFormats2KHR( const PhysicalDeviceSurfaceInfo2KHR & surfaceInfo ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_TYPESAFE_EXPLICIT operator VkPhysicalDevice() const { return m_physicalDevice; } explicit operator bool() const { return m_physicalDevice != VK_NULL_HANDLE; } bool operator!() const { return m_physicalDevice == VK_NULL_HANDLE; } private: VkPhysicalDevice m_physicalDevice; }; static_assert( sizeof( PhysicalDevice ) == sizeof( VkPhysicalDevice ), "handle and wrapper have different size!" ); #ifndef VULKAN_HPP_NO_SMART_HANDLE class DeviceDeleter { public: DeviceDeleter( Optional allocator = nullptr ) : m_allocator( allocator ) {} void operator()( Device device ) { device.destroy( m_allocator ); } private: Optional m_allocator; }; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ VULKAN_HPP_INLINE void PhysicalDevice::getProperties( PhysicalDeviceProperties* pProperties ) const { vkGetPhysicalDeviceProperties( m_physicalDevice, reinterpret_cast( pProperties ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE PhysicalDeviceProperties PhysicalDevice::getProperties() const { PhysicalDeviceProperties properties; vkGetPhysicalDeviceProperties( m_physicalDevice, reinterpret_cast( &properties ) ); return properties; } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void PhysicalDevice::getQueueFamilyProperties( uint32_t* pQueueFamilyPropertyCount, QueueFamilyProperties* pQueueFamilyProperties ) const { vkGetPhysicalDeviceQueueFamilyProperties( m_physicalDevice, pQueueFamilyPropertyCount, reinterpret_cast( pQueueFamilyProperties ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template VULKAN_HPP_INLINE std::vector PhysicalDevice::getQueueFamilyProperties() const { std::vector queueFamilyProperties; uint32_t queueFamilyPropertyCount; vkGetPhysicalDeviceQueueFamilyProperties( m_physicalDevice, &queueFamilyPropertyCount, nullptr ); queueFamilyProperties.resize( queueFamilyPropertyCount ); vkGetPhysicalDeviceQueueFamilyProperties( m_physicalDevice, &queueFamilyPropertyCount, reinterpret_cast( queueFamilyProperties.data() ) ); return queueFamilyProperties; } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void PhysicalDevice::getMemoryProperties( PhysicalDeviceMemoryProperties* pMemoryProperties ) const { vkGetPhysicalDeviceMemoryProperties( m_physicalDevice, reinterpret_cast( pMemoryProperties ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE PhysicalDeviceMemoryProperties PhysicalDevice::getMemoryProperties() const { PhysicalDeviceMemoryProperties memoryProperties; vkGetPhysicalDeviceMemoryProperties( m_physicalDevice, reinterpret_cast( &memoryProperties ) ); return memoryProperties; } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void PhysicalDevice::getFeatures( PhysicalDeviceFeatures* pFeatures ) const { vkGetPhysicalDeviceFeatures( m_physicalDevice, reinterpret_cast( pFeatures ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE PhysicalDeviceFeatures PhysicalDevice::getFeatures() const { PhysicalDeviceFeatures features; vkGetPhysicalDeviceFeatures( m_physicalDevice, reinterpret_cast( &features ) ); return features; } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void PhysicalDevice::getFormatProperties( Format format, FormatProperties* pFormatProperties ) const { vkGetPhysicalDeviceFormatProperties( m_physicalDevice, static_cast( format ), reinterpret_cast( pFormatProperties ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE FormatProperties PhysicalDevice::getFormatProperties( Format format ) const { FormatProperties formatProperties; vkGetPhysicalDeviceFormatProperties( m_physicalDevice, static_cast( format ), reinterpret_cast( &formatProperties ) ); return formatProperties; } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result PhysicalDevice::getImageFormatProperties( Format format, ImageType type, ImageTiling tiling, ImageUsageFlags usage, ImageCreateFlags flags, ImageFormatProperties* pImageFormatProperties ) const { return static_cast( vkGetPhysicalDeviceImageFormatProperties( m_physicalDevice, static_cast( format ), static_cast( type ), static_cast( tiling ), static_cast( usage ), static_cast( flags ), reinterpret_cast( pImageFormatProperties ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type PhysicalDevice::getImageFormatProperties( Format format, ImageType type, ImageTiling tiling, ImageUsageFlags usage, ImageCreateFlags flags ) const { ImageFormatProperties imageFormatProperties; Result result = static_cast( vkGetPhysicalDeviceImageFormatProperties( m_physicalDevice, static_cast( format ), static_cast( type ), static_cast( tiling ), static_cast( usage ), static_cast( flags ), reinterpret_cast( &imageFormatProperties ) ) ); return createResultValue( result, imageFormatProperties, "vk::PhysicalDevice::getImageFormatProperties" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result PhysicalDevice::createDevice( const DeviceCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, Device* pDevice ) const { return static_cast( vkCreateDevice( m_physicalDevice, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pDevice ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type PhysicalDevice::createDevice( const DeviceCreateInfo & createInfo, Optional allocator ) const { Device device; Result result = static_cast( vkCreateDevice( m_physicalDevice, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &device ) ) ); return createResultValue( result, device, "vk::PhysicalDevice::createDevice" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE VULKAN_HPP_INLINE UniqueDevice PhysicalDevice::createDeviceUnique( const DeviceCreateInfo & createInfo, Optional allocator ) const { DeviceDeleter deleter( allocator ); return UniqueDevice( createDevice( createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result PhysicalDevice::enumerateDeviceLayerProperties( uint32_t* pPropertyCount, LayerProperties* pProperties ) const { return static_cast( vkEnumerateDeviceLayerProperties( m_physicalDevice, pPropertyCount, reinterpret_cast( pProperties ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template VULKAN_HPP_INLINE typename ResultValueType>::type PhysicalDevice::enumerateDeviceLayerProperties() const { std::vector properties; uint32_t propertyCount; Result result; do { result = static_cast( vkEnumerateDeviceLayerProperties( m_physicalDevice, &propertyCount, nullptr ) ); if ( ( result == Result::eSuccess ) && propertyCount ) { properties.resize( propertyCount ); result = static_cast( vkEnumerateDeviceLayerProperties( m_physicalDevice, &propertyCount, reinterpret_cast( properties.data() ) ) ); } } while ( result == Result::eIncomplete ); assert( propertyCount <= properties.size() ); properties.resize( propertyCount ); return createResultValue( result, properties, "vk::PhysicalDevice::enumerateDeviceLayerProperties" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result PhysicalDevice::enumerateDeviceExtensionProperties( const char* pLayerName, uint32_t* pPropertyCount, ExtensionProperties* pProperties ) const { return static_cast( vkEnumerateDeviceExtensionProperties( m_physicalDevice, pLayerName, pPropertyCount, reinterpret_cast( pProperties ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template VULKAN_HPP_INLINE typename ResultValueType>::type PhysicalDevice::enumerateDeviceExtensionProperties( Optional layerName ) const { std::vector properties; uint32_t propertyCount; Result result; do { result = static_cast( vkEnumerateDeviceExtensionProperties( m_physicalDevice, layerName ? layerName->c_str() : nullptr, &propertyCount, nullptr ) ); if ( ( result == Result::eSuccess ) && propertyCount ) { properties.resize( propertyCount ); result = static_cast( vkEnumerateDeviceExtensionProperties( m_physicalDevice, layerName ? layerName->c_str() : nullptr, &propertyCount, reinterpret_cast( properties.data() ) ) ); } } while ( result == Result::eIncomplete ); assert( propertyCount <= properties.size() ); properties.resize( propertyCount ); return createResultValue( result, properties, "vk::PhysicalDevice::enumerateDeviceExtensionProperties" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void PhysicalDevice::getSparseImageFormatProperties( Format format, ImageType type, SampleCountFlagBits samples, ImageUsageFlags usage, ImageTiling tiling, uint32_t* pPropertyCount, SparseImageFormatProperties* pProperties ) const { vkGetPhysicalDeviceSparseImageFormatProperties( m_physicalDevice, static_cast( format ), static_cast( type ), static_cast( samples ), static_cast( usage ), static_cast( tiling ), pPropertyCount, reinterpret_cast( pProperties ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template VULKAN_HPP_INLINE std::vector PhysicalDevice::getSparseImageFormatProperties( Format format, ImageType type, SampleCountFlagBits samples, ImageUsageFlags usage, ImageTiling tiling ) const { std::vector properties; uint32_t propertyCount; vkGetPhysicalDeviceSparseImageFormatProperties( m_physicalDevice, static_cast( format ), static_cast( type ), static_cast( samples ), static_cast( usage ), static_cast( tiling ), &propertyCount, nullptr ); properties.resize( propertyCount ); vkGetPhysicalDeviceSparseImageFormatProperties( m_physicalDevice, static_cast( format ), static_cast( type ), static_cast( samples ), static_cast( usage ), static_cast( tiling ), &propertyCount, reinterpret_cast( properties.data() ) ); return properties; } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result PhysicalDevice::getDisplayPropertiesKHR( uint32_t* pPropertyCount, DisplayPropertiesKHR* pProperties ) const { return static_cast( vkGetPhysicalDeviceDisplayPropertiesKHR( m_physicalDevice, pPropertyCount, reinterpret_cast( pProperties ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template VULKAN_HPP_INLINE typename ResultValueType>::type PhysicalDevice::getDisplayPropertiesKHR() const { std::vector properties; uint32_t propertyCount; Result result; do { result = static_cast( vkGetPhysicalDeviceDisplayPropertiesKHR( m_physicalDevice, &propertyCount, nullptr ) ); if ( ( result == Result::eSuccess ) && propertyCount ) { properties.resize( propertyCount ); result = static_cast( vkGetPhysicalDeviceDisplayPropertiesKHR( m_physicalDevice, &propertyCount, reinterpret_cast( properties.data() ) ) ); } } while ( result == Result::eIncomplete ); assert( propertyCount <= properties.size() ); properties.resize( propertyCount ); return createResultValue( result, properties, "vk::PhysicalDevice::getDisplayPropertiesKHR" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result PhysicalDevice::getDisplayPlanePropertiesKHR( uint32_t* pPropertyCount, DisplayPlanePropertiesKHR* pProperties ) const { return static_cast( vkGetPhysicalDeviceDisplayPlanePropertiesKHR( m_physicalDevice, pPropertyCount, reinterpret_cast( pProperties ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template VULKAN_HPP_INLINE typename ResultValueType>::type PhysicalDevice::getDisplayPlanePropertiesKHR() const { std::vector properties; uint32_t propertyCount; Result result; do { result = static_cast( vkGetPhysicalDeviceDisplayPlanePropertiesKHR( m_physicalDevice, &propertyCount, nullptr ) ); if ( ( result == Result::eSuccess ) && propertyCount ) { properties.resize( propertyCount ); result = static_cast( vkGetPhysicalDeviceDisplayPlanePropertiesKHR( m_physicalDevice, &propertyCount, reinterpret_cast( properties.data() ) ) ); } } while ( result == Result::eIncomplete ); assert( propertyCount <= properties.size() ); properties.resize( propertyCount ); return createResultValue( result, properties, "vk::PhysicalDevice::getDisplayPlanePropertiesKHR" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result PhysicalDevice::getDisplayPlaneSupportedDisplaysKHR( uint32_t planeIndex, uint32_t* pDisplayCount, DisplayKHR* pDisplays ) const { return static_cast( vkGetDisplayPlaneSupportedDisplaysKHR( m_physicalDevice, planeIndex, pDisplayCount, reinterpret_cast( pDisplays ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template VULKAN_HPP_INLINE typename ResultValueType>::type PhysicalDevice::getDisplayPlaneSupportedDisplaysKHR( uint32_t planeIndex ) const { std::vector displays; uint32_t displayCount; Result result; do { result = static_cast( vkGetDisplayPlaneSupportedDisplaysKHR( m_physicalDevice, planeIndex, &displayCount, nullptr ) ); if ( ( result == Result::eSuccess ) && displayCount ) { displays.resize( displayCount ); result = static_cast( vkGetDisplayPlaneSupportedDisplaysKHR( m_physicalDevice, planeIndex, &displayCount, reinterpret_cast( displays.data() ) ) ); } } while ( result == Result::eIncomplete ); assert( displayCount <= displays.size() ); displays.resize( displayCount ); return createResultValue( result, displays, "vk::PhysicalDevice::getDisplayPlaneSupportedDisplaysKHR" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result PhysicalDevice::getDisplayModePropertiesKHR( DisplayKHR display, uint32_t* pPropertyCount, DisplayModePropertiesKHR* pProperties ) const { return static_cast( vkGetDisplayModePropertiesKHR( m_physicalDevice, static_cast( display ), pPropertyCount, reinterpret_cast( pProperties ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template VULKAN_HPP_INLINE typename ResultValueType>::type PhysicalDevice::getDisplayModePropertiesKHR( DisplayKHR display ) const { std::vector properties; uint32_t propertyCount; Result result; do { result = static_cast( vkGetDisplayModePropertiesKHR( m_physicalDevice, static_cast( display ), &propertyCount, nullptr ) ); if ( ( result == Result::eSuccess ) && propertyCount ) { properties.resize( propertyCount ); result = static_cast( vkGetDisplayModePropertiesKHR( m_physicalDevice, static_cast( display ), &propertyCount, reinterpret_cast( properties.data() ) ) ); } } while ( result == Result::eIncomplete ); assert( propertyCount <= properties.size() ); properties.resize( propertyCount ); return createResultValue( result, properties, "vk::PhysicalDevice::getDisplayModePropertiesKHR" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result PhysicalDevice::createDisplayModeKHR( DisplayKHR display, const DisplayModeCreateInfoKHR* pCreateInfo, const AllocationCallbacks* pAllocator, DisplayModeKHR* pMode ) const { return static_cast( vkCreateDisplayModeKHR( m_physicalDevice, static_cast( display ), reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pMode ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type PhysicalDevice::createDisplayModeKHR( DisplayKHR display, const DisplayModeCreateInfoKHR & createInfo, Optional allocator ) const { DisplayModeKHR mode; Result result = static_cast( vkCreateDisplayModeKHR( m_physicalDevice, static_cast( display ), reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &mode ) ) ); return createResultValue( result, mode, "vk::PhysicalDevice::createDisplayModeKHR" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result PhysicalDevice::getDisplayPlaneCapabilitiesKHR( DisplayModeKHR mode, uint32_t planeIndex, DisplayPlaneCapabilitiesKHR* pCapabilities ) const { return static_cast( vkGetDisplayPlaneCapabilitiesKHR( m_physicalDevice, static_cast( mode ), planeIndex, reinterpret_cast( pCapabilities ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type PhysicalDevice::getDisplayPlaneCapabilitiesKHR( DisplayModeKHR mode, uint32_t planeIndex ) const { DisplayPlaneCapabilitiesKHR capabilities; Result result = static_cast( vkGetDisplayPlaneCapabilitiesKHR( m_physicalDevice, static_cast( mode ), planeIndex, reinterpret_cast( &capabilities ) ) ); return createResultValue( result, capabilities, "vk::PhysicalDevice::getDisplayPlaneCapabilitiesKHR" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VK_USE_PLATFORM_MIR_KHR VULKAN_HPP_INLINE Bool32 PhysicalDevice::getMirPresentationSupportKHR( uint32_t queueFamilyIndex, MirConnection* connection ) const { return vkGetPhysicalDeviceMirPresentationSupportKHR( m_physicalDevice, queueFamilyIndex, connection ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE Bool32 PhysicalDevice::getMirPresentationSupportKHR( uint32_t queueFamilyIndex, MirConnection & connection ) const { return vkGetPhysicalDeviceMirPresentationSupportKHR( m_physicalDevice, queueFamilyIndex, &connection ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_MIR_KHR*/ VULKAN_HPP_INLINE Result PhysicalDevice::getSurfaceSupportKHR( uint32_t queueFamilyIndex, SurfaceKHR surface, Bool32* pSupported ) const { return static_cast( vkGetPhysicalDeviceSurfaceSupportKHR( m_physicalDevice, queueFamilyIndex, static_cast( surface ), pSupported ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type PhysicalDevice::getSurfaceSupportKHR( uint32_t queueFamilyIndex, SurfaceKHR surface ) const { Bool32 supported; Result result = static_cast( vkGetPhysicalDeviceSurfaceSupportKHR( m_physicalDevice, queueFamilyIndex, static_cast( surface ), &supported ) ); return createResultValue( result, supported, "vk::PhysicalDevice::getSurfaceSupportKHR" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result PhysicalDevice::getSurfaceCapabilitiesKHR( SurfaceKHR surface, SurfaceCapabilitiesKHR* pSurfaceCapabilities ) const { return static_cast( vkGetPhysicalDeviceSurfaceCapabilitiesKHR( m_physicalDevice, static_cast( surface ), reinterpret_cast( pSurfaceCapabilities ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type PhysicalDevice::getSurfaceCapabilitiesKHR( SurfaceKHR surface ) const { SurfaceCapabilitiesKHR surfaceCapabilities; Result result = static_cast( vkGetPhysicalDeviceSurfaceCapabilitiesKHR( m_physicalDevice, static_cast( surface ), reinterpret_cast( &surfaceCapabilities ) ) ); return createResultValue( result, surfaceCapabilities, "vk::PhysicalDevice::getSurfaceCapabilitiesKHR" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result PhysicalDevice::getSurfaceFormatsKHR( SurfaceKHR surface, uint32_t* pSurfaceFormatCount, SurfaceFormatKHR* pSurfaceFormats ) const { return static_cast( vkGetPhysicalDeviceSurfaceFormatsKHR( m_physicalDevice, static_cast( surface ), pSurfaceFormatCount, reinterpret_cast( pSurfaceFormats ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template VULKAN_HPP_INLINE typename ResultValueType>::type PhysicalDevice::getSurfaceFormatsKHR( SurfaceKHR surface ) const { std::vector surfaceFormats; uint32_t surfaceFormatCount; Result result; do { result = static_cast( vkGetPhysicalDeviceSurfaceFormatsKHR( m_physicalDevice, static_cast( surface ), &surfaceFormatCount, nullptr ) ); if ( ( result == Result::eSuccess ) && surfaceFormatCount ) { surfaceFormats.resize( surfaceFormatCount ); result = static_cast( vkGetPhysicalDeviceSurfaceFormatsKHR( m_physicalDevice, static_cast( surface ), &surfaceFormatCount, reinterpret_cast( surfaceFormats.data() ) ) ); } } while ( result == Result::eIncomplete ); assert( surfaceFormatCount <= surfaceFormats.size() ); surfaceFormats.resize( surfaceFormatCount ); return createResultValue( result, surfaceFormats, "vk::PhysicalDevice::getSurfaceFormatsKHR" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result PhysicalDevice::getSurfacePresentModesKHR( SurfaceKHR surface, uint32_t* pPresentModeCount, PresentModeKHR* pPresentModes ) const { return static_cast( vkGetPhysicalDeviceSurfacePresentModesKHR( m_physicalDevice, static_cast( surface ), pPresentModeCount, reinterpret_cast( pPresentModes ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template VULKAN_HPP_INLINE typename ResultValueType>::type PhysicalDevice::getSurfacePresentModesKHR( SurfaceKHR surface ) const { std::vector presentModes; uint32_t presentModeCount; Result result; do { result = static_cast( vkGetPhysicalDeviceSurfacePresentModesKHR( m_physicalDevice, static_cast( surface ), &presentModeCount, nullptr ) ); if ( ( result == Result::eSuccess ) && presentModeCount ) { presentModes.resize( presentModeCount ); result = static_cast( vkGetPhysicalDeviceSurfacePresentModesKHR( m_physicalDevice, static_cast( surface ), &presentModeCount, reinterpret_cast( presentModes.data() ) ) ); } } while ( result == Result::eIncomplete ); assert( presentModeCount <= presentModes.size() ); presentModes.resize( presentModeCount ); return createResultValue( result, presentModes, "vk::PhysicalDevice::getSurfacePresentModesKHR" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VK_USE_PLATFORM_WAYLAND_KHR VULKAN_HPP_INLINE Bool32 PhysicalDevice::getWaylandPresentationSupportKHR( uint32_t queueFamilyIndex, struct wl_display* display ) const { return vkGetPhysicalDeviceWaylandPresentationSupportKHR( m_physicalDevice, queueFamilyIndex, display ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE Bool32 PhysicalDevice::getWaylandPresentationSupportKHR( uint32_t queueFamilyIndex, struct wl_display & display ) const { return vkGetPhysicalDeviceWaylandPresentationSupportKHR( m_physicalDevice, queueFamilyIndex, &display ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_WAYLAND_KHR*/ #ifdef VK_USE_PLATFORM_WIN32_KHR VULKAN_HPP_INLINE Bool32 PhysicalDevice::getWin32PresentationSupportKHR( uint32_t queueFamilyIndex ) const { return vkGetPhysicalDeviceWin32PresentationSupportKHR( m_physicalDevice, queueFamilyIndex ); } #endif /*VK_USE_PLATFORM_WIN32_KHR*/ #ifdef VK_USE_PLATFORM_XLIB_KHR VULKAN_HPP_INLINE Bool32 PhysicalDevice::getXlibPresentationSupportKHR( uint32_t queueFamilyIndex, Display* dpy, VisualID visualID ) const { return vkGetPhysicalDeviceXlibPresentationSupportKHR( m_physicalDevice, queueFamilyIndex, dpy, visualID ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE Bool32 PhysicalDevice::getXlibPresentationSupportKHR( uint32_t queueFamilyIndex, Display & dpy, VisualID visualID ) const { return vkGetPhysicalDeviceXlibPresentationSupportKHR( m_physicalDevice, queueFamilyIndex, &dpy, visualID ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_XLIB_KHR*/ #ifdef VK_USE_PLATFORM_XCB_KHR VULKAN_HPP_INLINE Bool32 PhysicalDevice::getXcbPresentationSupportKHR( uint32_t queueFamilyIndex, xcb_connection_t* connection, xcb_visualid_t visual_id ) const { return vkGetPhysicalDeviceXcbPresentationSupportKHR( m_physicalDevice, queueFamilyIndex, connection, visual_id ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE Bool32 PhysicalDevice::getXcbPresentationSupportKHR( uint32_t queueFamilyIndex, xcb_connection_t & connection, xcb_visualid_t visual_id ) const { return vkGetPhysicalDeviceXcbPresentationSupportKHR( m_physicalDevice, queueFamilyIndex, &connection, visual_id ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_XCB_KHR*/ VULKAN_HPP_INLINE Result PhysicalDevice::getExternalImageFormatPropertiesNV( Format format, ImageType type, ImageTiling tiling, ImageUsageFlags usage, ImageCreateFlags flags, ExternalMemoryHandleTypeFlagsNV externalHandleType, ExternalImageFormatPropertiesNV* pExternalImageFormatProperties ) const { return static_cast( vkGetPhysicalDeviceExternalImageFormatPropertiesNV( m_physicalDevice, static_cast( format ), static_cast( type ), static_cast( tiling ), static_cast( usage ), static_cast( flags ), static_cast( externalHandleType ), reinterpret_cast( pExternalImageFormatProperties ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type PhysicalDevice::getExternalImageFormatPropertiesNV( Format format, ImageType type, ImageTiling tiling, ImageUsageFlags usage, ImageCreateFlags flags, ExternalMemoryHandleTypeFlagsNV externalHandleType ) const { ExternalImageFormatPropertiesNV externalImageFormatProperties; Result result = static_cast( vkGetPhysicalDeviceExternalImageFormatPropertiesNV( m_physicalDevice, static_cast( format ), static_cast( type ), static_cast( tiling ), static_cast( usage ), static_cast( flags ), static_cast( externalHandleType ), reinterpret_cast( &externalImageFormatProperties ) ) ); return createResultValue( result, externalImageFormatProperties, "vk::PhysicalDevice::getExternalImageFormatPropertiesNV" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void PhysicalDevice::getGeneratedCommandsPropertiesNVX( DeviceGeneratedCommandsFeaturesNVX* pFeatures, DeviceGeneratedCommandsLimitsNVX* pLimits ) const { vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX( m_physicalDevice, reinterpret_cast( pFeatures ), reinterpret_cast( pLimits ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE DeviceGeneratedCommandsLimitsNVX PhysicalDevice::getGeneratedCommandsPropertiesNVX( DeviceGeneratedCommandsFeaturesNVX & features ) const { DeviceGeneratedCommandsLimitsNVX limits; vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX( m_physicalDevice, reinterpret_cast( &features ), reinterpret_cast( &limits ) ); return limits; } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void PhysicalDevice::getFeatures2KHR( PhysicalDeviceFeatures2KHR* pFeatures ) const { vkGetPhysicalDeviceFeatures2KHR( m_physicalDevice, reinterpret_cast( pFeatures ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE PhysicalDeviceFeatures2KHR PhysicalDevice::getFeatures2KHR() const { PhysicalDeviceFeatures2KHR features; vkGetPhysicalDeviceFeatures2KHR( m_physicalDevice, reinterpret_cast( &features ) ); return features; } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void PhysicalDevice::getProperties2KHR( PhysicalDeviceProperties2KHR* pProperties ) const { vkGetPhysicalDeviceProperties2KHR( m_physicalDevice, reinterpret_cast( pProperties ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE PhysicalDeviceProperties2KHR PhysicalDevice::getProperties2KHR() const { PhysicalDeviceProperties2KHR properties; vkGetPhysicalDeviceProperties2KHR( m_physicalDevice, reinterpret_cast( &properties ) ); return properties; } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void PhysicalDevice::getFormatProperties2KHR( Format format, FormatProperties2KHR* pFormatProperties ) const { vkGetPhysicalDeviceFormatProperties2KHR( m_physicalDevice, static_cast( format ), reinterpret_cast( pFormatProperties ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE FormatProperties2KHR PhysicalDevice::getFormatProperties2KHR( Format format ) const { FormatProperties2KHR formatProperties; vkGetPhysicalDeviceFormatProperties2KHR( m_physicalDevice, static_cast( format ), reinterpret_cast( &formatProperties ) ); return formatProperties; } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result PhysicalDevice::getImageFormatProperties2KHR( const PhysicalDeviceImageFormatInfo2KHR* pImageFormatInfo, ImageFormatProperties2KHR* pImageFormatProperties ) const { return static_cast( vkGetPhysicalDeviceImageFormatProperties2KHR( m_physicalDevice, reinterpret_cast( pImageFormatInfo ), reinterpret_cast( pImageFormatProperties ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type PhysicalDevice::getImageFormatProperties2KHR( const PhysicalDeviceImageFormatInfo2KHR & imageFormatInfo ) const { ImageFormatProperties2KHR imageFormatProperties; Result result = static_cast( vkGetPhysicalDeviceImageFormatProperties2KHR( m_physicalDevice, reinterpret_cast( &imageFormatInfo ), reinterpret_cast( &imageFormatProperties ) ) ); return createResultValue( result, imageFormatProperties, "vk::PhysicalDevice::getImageFormatProperties2KHR" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void PhysicalDevice::getQueueFamilyProperties2KHR( uint32_t* pQueueFamilyPropertyCount, QueueFamilyProperties2KHR* pQueueFamilyProperties ) const { vkGetPhysicalDeviceQueueFamilyProperties2KHR( m_physicalDevice, pQueueFamilyPropertyCount, reinterpret_cast( pQueueFamilyProperties ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template VULKAN_HPP_INLINE std::vector PhysicalDevice::getQueueFamilyProperties2KHR() const { std::vector queueFamilyProperties; uint32_t queueFamilyPropertyCount; vkGetPhysicalDeviceQueueFamilyProperties2KHR( m_physicalDevice, &queueFamilyPropertyCount, nullptr ); queueFamilyProperties.resize( queueFamilyPropertyCount ); vkGetPhysicalDeviceQueueFamilyProperties2KHR( m_physicalDevice, &queueFamilyPropertyCount, reinterpret_cast( queueFamilyProperties.data() ) ); return queueFamilyProperties; } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void PhysicalDevice::getMemoryProperties2KHR( PhysicalDeviceMemoryProperties2KHR* pMemoryProperties ) const { vkGetPhysicalDeviceMemoryProperties2KHR( m_physicalDevice, reinterpret_cast( pMemoryProperties ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE PhysicalDeviceMemoryProperties2KHR PhysicalDevice::getMemoryProperties2KHR() const { PhysicalDeviceMemoryProperties2KHR memoryProperties; vkGetPhysicalDeviceMemoryProperties2KHR( m_physicalDevice, reinterpret_cast( &memoryProperties ) ); return memoryProperties; } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void PhysicalDevice::getSparseImageFormatProperties2KHR( const PhysicalDeviceSparseImageFormatInfo2KHR* pFormatInfo, uint32_t* pPropertyCount, SparseImageFormatProperties2KHR* pProperties ) const { vkGetPhysicalDeviceSparseImageFormatProperties2KHR( m_physicalDevice, reinterpret_cast( pFormatInfo ), pPropertyCount, reinterpret_cast( pProperties ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template VULKAN_HPP_INLINE std::vector PhysicalDevice::getSparseImageFormatProperties2KHR( const PhysicalDeviceSparseImageFormatInfo2KHR & formatInfo ) const { std::vector properties; uint32_t propertyCount; vkGetPhysicalDeviceSparseImageFormatProperties2KHR( m_physicalDevice, reinterpret_cast( &formatInfo ), &propertyCount, nullptr ); properties.resize( propertyCount ); vkGetPhysicalDeviceSparseImageFormatProperties2KHR( m_physicalDevice, reinterpret_cast( &formatInfo ), &propertyCount, reinterpret_cast( properties.data() ) ); return properties; } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void PhysicalDevice::getExternalBufferPropertiesKHX( const PhysicalDeviceExternalBufferInfoKHX* pExternalBufferInfo, ExternalBufferPropertiesKHX* pExternalBufferProperties ) const { vkGetPhysicalDeviceExternalBufferPropertiesKHX( m_physicalDevice, reinterpret_cast( pExternalBufferInfo ), reinterpret_cast( pExternalBufferProperties ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ExternalBufferPropertiesKHX PhysicalDevice::getExternalBufferPropertiesKHX( const PhysicalDeviceExternalBufferInfoKHX & externalBufferInfo ) const { ExternalBufferPropertiesKHX externalBufferProperties; vkGetPhysicalDeviceExternalBufferPropertiesKHX( m_physicalDevice, reinterpret_cast( &externalBufferInfo ), reinterpret_cast( &externalBufferProperties ) ); return externalBufferProperties; } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void PhysicalDevice::getExternalSemaphorePropertiesKHX( const PhysicalDeviceExternalSemaphoreInfoKHX* pExternalSemaphoreInfo, ExternalSemaphorePropertiesKHX* pExternalSemaphoreProperties ) const { vkGetPhysicalDeviceExternalSemaphorePropertiesKHX( m_physicalDevice, reinterpret_cast( pExternalSemaphoreInfo ), reinterpret_cast( pExternalSemaphoreProperties ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ExternalSemaphorePropertiesKHX PhysicalDevice::getExternalSemaphorePropertiesKHX( const PhysicalDeviceExternalSemaphoreInfoKHX & externalSemaphoreInfo ) const { ExternalSemaphorePropertiesKHX externalSemaphoreProperties; vkGetPhysicalDeviceExternalSemaphorePropertiesKHX( m_physicalDevice, reinterpret_cast( &externalSemaphoreInfo ), reinterpret_cast( &externalSemaphoreProperties ) ); return externalSemaphoreProperties; } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE Result PhysicalDevice::releaseDisplayEXT( DisplayKHR display ) const { return static_cast( vkReleaseDisplayEXT( m_physicalDevice, static_cast( display ) ) ); } #else VULKAN_HPP_INLINE ResultValueType::type PhysicalDevice::releaseDisplayEXT( DisplayKHR display ) const { Result result = static_cast( vkReleaseDisplayEXT( m_physicalDevice, static_cast( display ) ) ); return createResultValue( result, "vk::PhysicalDevice::releaseDisplayEXT" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT VULKAN_HPP_INLINE Result PhysicalDevice::acquireXlibDisplayEXT( Display* dpy, DisplayKHR display ) const { return static_cast( vkAcquireXlibDisplayEXT( m_physicalDevice, dpy, static_cast( display ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type PhysicalDevice::acquireXlibDisplayEXT( DisplayKHR display ) const { Display dpy; Result result = static_cast( vkAcquireXlibDisplayEXT( m_physicalDevice, &dpy, static_cast( display ) ) ); return createResultValue( result, dpy, "vk::PhysicalDevice::acquireXlibDisplayEXT" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_XLIB_XRANDR_EXT*/ #ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT VULKAN_HPP_INLINE Result PhysicalDevice::getRandROutputDisplayEXT( Display* dpy, RROutput rrOutput, DisplayKHR* pDisplay ) const { return static_cast( vkGetRandROutputDisplayEXT( m_physicalDevice, dpy, rrOutput, reinterpret_cast( pDisplay ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type PhysicalDevice::getRandROutputDisplayEXT( Display & dpy, RROutput rrOutput ) const { DisplayKHR display; Result result = static_cast( vkGetRandROutputDisplayEXT( m_physicalDevice, &dpy, rrOutput, reinterpret_cast( &display ) ) ); return createResultValue( result, display, "vk::PhysicalDevice::getRandROutputDisplayEXT" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_XLIB_XRANDR_EXT*/ VULKAN_HPP_INLINE Result PhysicalDevice::getSurfaceCapabilities2EXT( SurfaceKHR surface, SurfaceCapabilities2EXT* pSurfaceCapabilities ) const { return static_cast( vkGetPhysicalDeviceSurfaceCapabilities2EXT( m_physicalDevice, static_cast( surface ), reinterpret_cast( pSurfaceCapabilities ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type PhysicalDevice::getSurfaceCapabilities2EXT( SurfaceKHR surface ) const { SurfaceCapabilities2EXT surfaceCapabilities; Result result = static_cast( vkGetPhysicalDeviceSurfaceCapabilities2EXT( m_physicalDevice, static_cast( surface ), reinterpret_cast( &surfaceCapabilities ) ) ); return createResultValue( result, surfaceCapabilities, "vk::PhysicalDevice::getSurfaceCapabilities2EXT" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result PhysicalDevice::getPresentRectanglesKHX( SurfaceKHR surface, uint32_t* pRectCount, Rect2D* pRects ) const { return static_cast( vkGetPhysicalDevicePresentRectanglesKHX( m_physicalDevice, static_cast( surface ), pRectCount, reinterpret_cast( pRects ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template VULKAN_HPP_INLINE typename ResultValueType>::type PhysicalDevice::getPresentRectanglesKHX( SurfaceKHR surface ) const { std::vector rects; uint32_t rectCount; Result result; do { result = static_cast( vkGetPhysicalDevicePresentRectanglesKHX( m_physicalDevice, static_cast( surface ), &rectCount, nullptr ) ); if ( ( result == Result::eSuccess ) && rectCount ) { rects.resize( rectCount ); result = static_cast( vkGetPhysicalDevicePresentRectanglesKHX( m_physicalDevice, static_cast( surface ), &rectCount, reinterpret_cast( rects.data() ) ) ); } } while ( result == Result::eIncomplete ); assert( rectCount <= rects.size() ); rects.resize( rectCount ); return createResultValue( result, rects, "vk::PhysicalDevice::getPresentRectanglesKHX" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result PhysicalDevice::getSurfaceCapabilities2KHR( const PhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, SurfaceCapabilities2KHR* pSurfaceCapabilities ) const { return static_cast( vkGetPhysicalDeviceSurfaceCapabilities2KHR( m_physicalDevice, reinterpret_cast( pSurfaceInfo ), reinterpret_cast( pSurfaceCapabilities ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE #if 0 // LunarG 1.0.48 header update workaround VULKAN_HPP_INLINE Result PhysicalDevice::getSurfaceCapabilities2KHR( const PhysicalDeviceSurfaceInfo2KHR & surfaceInfo ) const { Result result = static_cast( vkGetPhysicalDeviceSurfaceCapabilities2KHR( m_physicalDevice, reinterpret_cast( &surfaceInfo ), reinterpret_cast( &surfaceCapabilities ) ) ); return createResultValue( result, surfaceCapabilities, "vk::PhysicalDevice::getSurfaceCapabilities2KHR" ); } #endif #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result PhysicalDevice::getSurfaceFormats2KHR( const PhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, uint32_t* pSurfaceFormatCount, SurfaceFormat2KHR* pSurfaceFormats ) const { return static_cast( vkGetPhysicalDeviceSurfaceFormats2KHR( m_physicalDevice, reinterpret_cast( pSurfaceInfo ), pSurfaceFormatCount, reinterpret_cast( pSurfaceFormats ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template VULKAN_HPP_INLINE typename ResultValueType>::type PhysicalDevice::getSurfaceFormats2KHR( const PhysicalDeviceSurfaceInfo2KHR & surfaceInfo ) const { std::vector surfaceFormats; uint32_t surfaceFormatCount; Result result; do { result = static_cast( vkGetPhysicalDeviceSurfaceFormats2KHR( m_physicalDevice, reinterpret_cast( &surfaceInfo ), &surfaceFormatCount, nullptr ) ); if ( ( result == Result::eSuccess ) && surfaceFormatCount ) { surfaceFormats.resize( surfaceFormatCount ); result = static_cast( vkGetPhysicalDeviceSurfaceFormats2KHR( m_physicalDevice, reinterpret_cast( &surfaceInfo ), &surfaceFormatCount, reinterpret_cast( surfaceFormats.data() ) ) ); } } while ( result == Result::eIncomplete ); assert( surfaceFormatCount <= surfaceFormats.size() ); surfaceFormats.resize( surfaceFormatCount ); return createResultValue( result, surfaceFormats, "vk::PhysicalDevice::getSurfaceFormats2KHR" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ struct CmdProcessCommandsInfoNVX { CmdProcessCommandsInfoNVX( ObjectTableNVX objectTable_ = ObjectTableNVX(), IndirectCommandsLayoutNVX indirectCommandsLayout_ = IndirectCommandsLayoutNVX(), uint32_t indirectCommandsTokenCount_ = 0, const IndirectCommandsTokenNVX* pIndirectCommandsTokens_ = nullptr, uint32_t maxSequencesCount_ = 0, CommandBuffer targetCommandBuffer_ = CommandBuffer(), Buffer sequencesCountBuffer_ = Buffer(), DeviceSize sequencesCountOffset_ = 0, Buffer sequencesIndexBuffer_ = Buffer(), DeviceSize sequencesIndexOffset_ = 0 ) : sType( StructureType::eCmdProcessCommandsInfoNVX ) , pNext( nullptr ) , objectTable( objectTable_ ) , indirectCommandsLayout( indirectCommandsLayout_ ) , indirectCommandsTokenCount( indirectCommandsTokenCount_ ) , pIndirectCommandsTokens( pIndirectCommandsTokens_ ) , maxSequencesCount( maxSequencesCount_ ) , targetCommandBuffer( targetCommandBuffer_ ) , sequencesCountBuffer( sequencesCountBuffer_ ) , sequencesCountOffset( sequencesCountOffset_ ) , sequencesIndexBuffer( sequencesIndexBuffer_ ) , sequencesIndexOffset( sequencesIndexOffset_ ) { } CmdProcessCommandsInfoNVX( VkCmdProcessCommandsInfoNVX const & rhs ) { memcpy( this, &rhs, sizeof(CmdProcessCommandsInfoNVX) ); } CmdProcessCommandsInfoNVX& operator=( VkCmdProcessCommandsInfoNVX const & rhs ) { memcpy( this, &rhs, sizeof(CmdProcessCommandsInfoNVX) ); return *this; } CmdProcessCommandsInfoNVX& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } CmdProcessCommandsInfoNVX& setObjectTable( ObjectTableNVX objectTable_ ) { objectTable = objectTable_; return *this; } CmdProcessCommandsInfoNVX& setIndirectCommandsLayout( IndirectCommandsLayoutNVX indirectCommandsLayout_ ) { indirectCommandsLayout = indirectCommandsLayout_; return *this; } CmdProcessCommandsInfoNVX& setIndirectCommandsTokenCount( uint32_t indirectCommandsTokenCount_ ) { indirectCommandsTokenCount = indirectCommandsTokenCount_; return *this; } CmdProcessCommandsInfoNVX& setPIndirectCommandsTokens( const IndirectCommandsTokenNVX* pIndirectCommandsTokens_ ) { pIndirectCommandsTokens = pIndirectCommandsTokens_; return *this; } CmdProcessCommandsInfoNVX& setMaxSequencesCount( uint32_t maxSequencesCount_ ) { maxSequencesCount = maxSequencesCount_; return *this; } CmdProcessCommandsInfoNVX& setTargetCommandBuffer( CommandBuffer targetCommandBuffer_ ) { targetCommandBuffer = targetCommandBuffer_; return *this; } CmdProcessCommandsInfoNVX& setSequencesCountBuffer( Buffer sequencesCountBuffer_ ) { sequencesCountBuffer = sequencesCountBuffer_; return *this; } CmdProcessCommandsInfoNVX& setSequencesCountOffset( DeviceSize sequencesCountOffset_ ) { sequencesCountOffset = sequencesCountOffset_; return *this; } CmdProcessCommandsInfoNVX& setSequencesIndexBuffer( Buffer sequencesIndexBuffer_ ) { sequencesIndexBuffer = sequencesIndexBuffer_; return *this; } CmdProcessCommandsInfoNVX& setSequencesIndexOffset( DeviceSize sequencesIndexOffset_ ) { sequencesIndexOffset = sequencesIndexOffset_; return *this; } operator const VkCmdProcessCommandsInfoNVX&() const { return *reinterpret_cast(this); } bool operator==( CmdProcessCommandsInfoNVX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( objectTable == rhs.objectTable ) && ( indirectCommandsLayout == rhs.indirectCommandsLayout ) && ( indirectCommandsTokenCount == rhs.indirectCommandsTokenCount ) && ( pIndirectCommandsTokens == rhs.pIndirectCommandsTokens ) && ( maxSequencesCount == rhs.maxSequencesCount ) && ( targetCommandBuffer == rhs.targetCommandBuffer ) && ( sequencesCountBuffer == rhs.sequencesCountBuffer ) && ( sequencesCountOffset == rhs.sequencesCountOffset ) && ( sequencesIndexBuffer == rhs.sequencesIndexBuffer ) && ( sequencesIndexOffset == rhs.sequencesIndexOffset ); } bool operator!=( CmdProcessCommandsInfoNVX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; ObjectTableNVX objectTable; IndirectCommandsLayoutNVX indirectCommandsLayout; uint32_t indirectCommandsTokenCount; const IndirectCommandsTokenNVX* pIndirectCommandsTokens; uint32_t maxSequencesCount; CommandBuffer targetCommandBuffer; Buffer sequencesCountBuffer; DeviceSize sequencesCountOffset; Buffer sequencesIndexBuffer; DeviceSize sequencesIndexOffset; }; static_assert( sizeof( CmdProcessCommandsInfoNVX ) == sizeof( VkCmdProcessCommandsInfoNVX ), "struct and wrapper have different size!" ); struct PhysicalDeviceGroupPropertiesKHX { operator const VkPhysicalDeviceGroupPropertiesKHX&() const { return *reinterpret_cast(this); } bool operator==( PhysicalDeviceGroupPropertiesKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( physicalDeviceCount == rhs.physicalDeviceCount ) && ( memcmp( physicalDevices, rhs.physicalDevices, VK_MAX_DEVICE_GROUP_SIZE_KHX * sizeof( PhysicalDevice ) ) == 0 ) && ( subsetAllocation == rhs.subsetAllocation ); } bool operator!=( PhysicalDeviceGroupPropertiesKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: void* pNext; uint32_t physicalDeviceCount; PhysicalDevice physicalDevices[VK_MAX_DEVICE_GROUP_SIZE_KHX]; Bool32 subsetAllocation; }; static_assert( sizeof( PhysicalDeviceGroupPropertiesKHX ) == sizeof( VkPhysicalDeviceGroupPropertiesKHX ), "struct and wrapper have different size!" ); #ifndef VULKAN_HPP_NO_SMART_HANDLE class DebugReportCallbackEXTDeleter; using UniqueDebugReportCallbackEXT = UniqueHandle; class SurfaceKHRDeleter; using UniqueSurfaceKHR = UniqueHandle; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ class Instance { public: Instance() : m_instance(VK_NULL_HANDLE) {} Instance( std::nullptr_t ) : m_instance(VK_NULL_HANDLE) {} VULKAN_HPP_TYPESAFE_EXPLICIT Instance(VkInstance instance) : m_instance(instance) {} #if defined(VULKAN_HPP_TYPESAFE_CONVERSION) Instance& operator=(VkInstance instance) { m_instance = instance; return *this; } #endif Instance& operator=( std::nullptr_t ) { m_instance = VK_NULL_HANDLE; return *this; } bool operator==(Instance const &rhs) const { return m_instance == rhs.m_instance; } bool operator!=(Instance const &rhs) const { return m_instance != rhs.m_instance; } bool operator<(Instance const &rhs) const { return m_instance < rhs.m_instance; } void destroy( const AllocationCallbacks* pAllocator ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void destroy( Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result enumeratePhysicalDevices( uint32_t* pPhysicalDeviceCount, PhysicalDevice* pPhysicalDevices ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template > typename ResultValueType>::type enumeratePhysicalDevices() const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ PFN_vkVoidFunction getProcAddr( const char* pName ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE PFN_vkVoidFunction getProcAddr( const std::string & name ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VK_USE_PLATFORM_ANDROID_KHR Result createAndroidSurfaceKHR( const AndroidSurfaceCreateInfoKHR* pCreateInfo, const AllocationCallbacks* pAllocator, SurfaceKHR* pSurface ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type createAndroidSurfaceKHR( const AndroidSurfaceCreateInfoKHR & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE UniqueSurfaceKHR createAndroidSurfaceKHRUnique( const AndroidSurfaceCreateInfoKHR & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_ANDROID_KHR*/ Result createDisplayPlaneSurfaceKHR( const DisplaySurfaceCreateInfoKHR* pCreateInfo, const AllocationCallbacks* pAllocator, SurfaceKHR* pSurface ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type createDisplayPlaneSurfaceKHR( const DisplaySurfaceCreateInfoKHR & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE UniqueSurfaceKHR createDisplayPlaneSurfaceKHRUnique( const DisplaySurfaceCreateInfoKHR & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VK_USE_PLATFORM_MIR_KHR Result createMirSurfaceKHR( const MirSurfaceCreateInfoKHR* pCreateInfo, const AllocationCallbacks* pAllocator, SurfaceKHR* pSurface ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type createMirSurfaceKHR( const MirSurfaceCreateInfoKHR & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE UniqueSurfaceKHR createMirSurfaceKHRUnique( const MirSurfaceCreateInfoKHR & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_MIR_KHR*/ void destroySurfaceKHR( SurfaceKHR surface, const AllocationCallbacks* pAllocator ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void destroySurfaceKHR( SurfaceKHR surface, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VK_USE_PLATFORM_VI_NN Result createViSurfaceNN( const ViSurfaceCreateInfoNN* pCreateInfo, const AllocationCallbacks* pAllocator, SurfaceKHR* pSurface ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type createViSurfaceNN( const ViSurfaceCreateInfoNN & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE UniqueSurfaceKHR createViSurfaceNNUnique( const ViSurfaceCreateInfoNN & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_VI_NN*/ #ifdef VK_USE_PLATFORM_WAYLAND_KHR Result createWaylandSurfaceKHR( const WaylandSurfaceCreateInfoKHR* pCreateInfo, const AllocationCallbacks* pAllocator, SurfaceKHR* pSurface ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type createWaylandSurfaceKHR( const WaylandSurfaceCreateInfoKHR & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE UniqueSurfaceKHR createWaylandSurfaceKHRUnique( const WaylandSurfaceCreateInfoKHR & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_WAYLAND_KHR*/ #ifdef VK_USE_PLATFORM_WIN32_KHR Result createWin32SurfaceKHR( const Win32SurfaceCreateInfoKHR* pCreateInfo, const AllocationCallbacks* pAllocator, SurfaceKHR* pSurface ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type createWin32SurfaceKHR( const Win32SurfaceCreateInfoKHR & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE UniqueSurfaceKHR createWin32SurfaceKHRUnique( const Win32SurfaceCreateInfoKHR & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_WIN32_KHR*/ #ifdef VK_USE_PLATFORM_XLIB_KHR Result createXlibSurfaceKHR( const XlibSurfaceCreateInfoKHR* pCreateInfo, const AllocationCallbacks* pAllocator, SurfaceKHR* pSurface ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type createXlibSurfaceKHR( const XlibSurfaceCreateInfoKHR & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE UniqueSurfaceKHR createXlibSurfaceKHRUnique( const XlibSurfaceCreateInfoKHR & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_XLIB_KHR*/ #ifdef VK_USE_PLATFORM_XCB_KHR Result createXcbSurfaceKHR( const XcbSurfaceCreateInfoKHR* pCreateInfo, const AllocationCallbacks* pAllocator, SurfaceKHR* pSurface ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type createXcbSurfaceKHR( const XcbSurfaceCreateInfoKHR & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE UniqueSurfaceKHR createXcbSurfaceKHRUnique( const XcbSurfaceCreateInfoKHR & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_XCB_KHR*/ Result createDebugReportCallbackEXT( const DebugReportCallbackCreateInfoEXT* pCreateInfo, const AllocationCallbacks* pAllocator, DebugReportCallbackEXT* pCallback ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type createDebugReportCallbackEXT( const DebugReportCallbackCreateInfoEXT & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE UniqueDebugReportCallbackEXT createDebugReportCallbackEXTUnique( const DebugReportCallbackCreateInfoEXT & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void destroyDebugReportCallbackEXT( DebugReportCallbackEXT callback, const AllocationCallbacks* pAllocator ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void destroyDebugReportCallbackEXT( DebugReportCallbackEXT callback, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ void debugReportMessageEXT( DebugReportFlagsEXT flags, DebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE void debugReportMessageEXT( DebugReportFlagsEXT flags, DebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const std::string & layerPrefix, const std::string & message ) const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ Result enumeratePhysicalDeviceGroupsKHX( uint32_t* pPhysicalDeviceGroupCount, PhysicalDeviceGroupPropertiesKHX* pPhysicalDeviceGroupProperties ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template > typename ResultValueType>::type enumeratePhysicalDeviceGroupsKHX() const; #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VK_USE_PLATFORM_IOS_MVK Result createIOSSurfaceMVK( const IOSSurfaceCreateInfoMVK* pCreateInfo, const AllocationCallbacks* pAllocator, SurfaceKHR* pSurface ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type createIOSSurfaceMVK( const IOSSurfaceCreateInfoMVK & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE UniqueSurfaceKHR createIOSSurfaceMVKUnique( const IOSSurfaceCreateInfoMVK & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_IOS_MVK*/ #ifdef VK_USE_PLATFORM_MACOS_MVK Result createMacOSSurfaceMVK( const MacOSSurfaceCreateInfoMVK* pCreateInfo, const AllocationCallbacks* pAllocator, SurfaceKHR* pSurface ) const; #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type createMacOSSurfaceMVK( const MacOSSurfaceCreateInfoMVK & createInfo, Optional allocator = nullptr ) const; #ifndef VULKAN_HPP_NO_SMART_HANDLE UniqueSurfaceKHR createMacOSSurfaceMVKUnique( const MacOSSurfaceCreateInfoMVK & createInfo, Optional allocator = nullptr ) const; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_MACOS_MVK*/ VULKAN_HPP_TYPESAFE_EXPLICIT operator VkInstance() const { return m_instance; } explicit operator bool() const { return m_instance != VK_NULL_HANDLE; } bool operator!() const { return m_instance == VK_NULL_HANDLE; } private: VkInstance m_instance; }; static_assert( sizeof( Instance ) == sizeof( VkInstance ), "handle and wrapper have different size!" ); #ifndef VULKAN_HPP_NO_SMART_HANDLE class DebugReportCallbackEXTDeleter { public: DebugReportCallbackEXTDeleter( Instance instance = Instance(), Optional allocator = nullptr ) : m_instance( instance ) , m_allocator( allocator ) {} void operator()( DebugReportCallbackEXT debugReportCallbackEXT ) { m_instance.destroyDebugReportCallbackEXT( debugReportCallbackEXT, m_allocator ); } private: Instance m_instance; Optional m_allocator; }; class SurfaceKHRDeleter { public: SurfaceKHRDeleter( Instance instance = Instance(), Optional allocator = nullptr ) : m_instance( instance ) , m_allocator( allocator ) {} void operator()( SurfaceKHR surfaceKHR ) { m_instance.destroySurfaceKHR( surfaceKHR, m_allocator ); } private: Instance m_instance; Optional m_allocator; }; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ VULKAN_HPP_INLINE void Instance::destroy( const AllocationCallbacks* pAllocator ) const { vkDestroyInstance( m_instance, reinterpret_cast( pAllocator ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void Instance::destroy( Optional allocator ) const { vkDestroyInstance( m_instance, reinterpret_cast( static_cast( allocator ) ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Instance::enumeratePhysicalDevices( uint32_t* pPhysicalDeviceCount, PhysicalDevice* pPhysicalDevices ) const { return static_cast( vkEnumeratePhysicalDevices( m_instance, pPhysicalDeviceCount, reinterpret_cast( pPhysicalDevices ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template VULKAN_HPP_INLINE typename ResultValueType>::type Instance::enumeratePhysicalDevices() const { std::vector physicalDevices; uint32_t physicalDeviceCount; Result result; do { result = static_cast( vkEnumeratePhysicalDevices( m_instance, &physicalDeviceCount, nullptr ) ); if ( ( result == Result::eSuccess ) && physicalDeviceCount ) { physicalDevices.resize( physicalDeviceCount ); result = static_cast( vkEnumeratePhysicalDevices( m_instance, &physicalDeviceCount, reinterpret_cast( physicalDevices.data() ) ) ); } } while ( result == Result::eIncomplete ); assert( physicalDeviceCount <= physicalDevices.size() ); physicalDevices.resize( physicalDeviceCount ); return createResultValue( result, physicalDevices, "vk::Instance::enumeratePhysicalDevices" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE PFN_vkVoidFunction Instance::getProcAddr( const char* pName ) const { return vkGetInstanceProcAddr( m_instance, pName ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE PFN_vkVoidFunction Instance::getProcAddr( const std::string & name ) const { return vkGetInstanceProcAddr( m_instance, name.c_str() ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VK_USE_PLATFORM_ANDROID_KHR VULKAN_HPP_INLINE Result Instance::createAndroidSurfaceKHR( const AndroidSurfaceCreateInfoKHR* pCreateInfo, const AllocationCallbacks* pAllocator, SurfaceKHR* pSurface ) const { return static_cast( vkCreateAndroidSurfaceKHR( m_instance, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pSurface ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Instance::createAndroidSurfaceKHR( const AndroidSurfaceCreateInfoKHR & createInfo, Optional allocator ) const { SurfaceKHR surface; Result result = static_cast( vkCreateAndroidSurfaceKHR( m_instance, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &surface ) ) ); return createResultValue( result, surface, "vk::Instance::createAndroidSurfaceKHR" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE VULKAN_HPP_INLINE UniqueSurfaceKHR Instance::createAndroidSurfaceKHRUnique( const AndroidSurfaceCreateInfoKHR & createInfo, Optional allocator ) const { SurfaceKHRDeleter deleter( *this, allocator ); return UniqueSurfaceKHR( createAndroidSurfaceKHR( createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_ANDROID_KHR*/ VULKAN_HPP_INLINE Result Instance::createDisplayPlaneSurfaceKHR( const DisplaySurfaceCreateInfoKHR* pCreateInfo, const AllocationCallbacks* pAllocator, SurfaceKHR* pSurface ) const { return static_cast( vkCreateDisplayPlaneSurfaceKHR( m_instance, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pSurface ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Instance::createDisplayPlaneSurfaceKHR( const DisplaySurfaceCreateInfoKHR & createInfo, Optional allocator ) const { SurfaceKHR surface; Result result = static_cast( vkCreateDisplayPlaneSurfaceKHR( m_instance, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &surface ) ) ); return createResultValue( result, surface, "vk::Instance::createDisplayPlaneSurfaceKHR" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE VULKAN_HPP_INLINE UniqueSurfaceKHR Instance::createDisplayPlaneSurfaceKHRUnique( const DisplaySurfaceCreateInfoKHR & createInfo, Optional allocator ) const { SurfaceKHRDeleter deleter( *this, allocator ); return UniqueSurfaceKHR( createDisplayPlaneSurfaceKHR( createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VK_USE_PLATFORM_MIR_KHR VULKAN_HPP_INLINE Result Instance::createMirSurfaceKHR( const MirSurfaceCreateInfoKHR* pCreateInfo, const AllocationCallbacks* pAllocator, SurfaceKHR* pSurface ) const { return static_cast( vkCreateMirSurfaceKHR( m_instance, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pSurface ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Instance::createMirSurfaceKHR( const MirSurfaceCreateInfoKHR & createInfo, Optional allocator ) const { SurfaceKHR surface; Result result = static_cast( vkCreateMirSurfaceKHR( m_instance, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &surface ) ) ); return createResultValue( result, surface, "vk::Instance::createMirSurfaceKHR" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE VULKAN_HPP_INLINE UniqueSurfaceKHR Instance::createMirSurfaceKHRUnique( const MirSurfaceCreateInfoKHR & createInfo, Optional allocator ) const { SurfaceKHRDeleter deleter( *this, allocator ); return UniqueSurfaceKHR( createMirSurfaceKHR( createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_MIR_KHR*/ VULKAN_HPP_INLINE void Instance::destroySurfaceKHR( SurfaceKHR surface, const AllocationCallbacks* pAllocator ) const { vkDestroySurfaceKHR( m_instance, static_cast( surface ), reinterpret_cast( pAllocator ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void Instance::destroySurfaceKHR( SurfaceKHR surface, Optional allocator ) const { vkDestroySurfaceKHR( m_instance, static_cast( surface ), reinterpret_cast( static_cast( allocator ) ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VK_USE_PLATFORM_VI_NN VULKAN_HPP_INLINE Result Instance::createViSurfaceNN( const ViSurfaceCreateInfoNN* pCreateInfo, const AllocationCallbacks* pAllocator, SurfaceKHR* pSurface ) const { return static_cast( vkCreateViSurfaceNN( m_instance, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pSurface ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Instance::createViSurfaceNN( const ViSurfaceCreateInfoNN & createInfo, Optional allocator ) const { SurfaceKHR surface; Result result = static_cast( vkCreateViSurfaceNN( m_instance, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &surface ) ) ); return createResultValue( result, surface, "vk::Instance::createViSurfaceNN" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE VULKAN_HPP_INLINE UniqueSurfaceKHR Instance::createViSurfaceNNUnique( const ViSurfaceCreateInfoNN & createInfo, Optional allocator ) const { SurfaceKHRDeleter deleter( *this, allocator ); return UniqueSurfaceKHR( createViSurfaceNN( createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_VI_NN*/ #ifdef VK_USE_PLATFORM_WAYLAND_KHR VULKAN_HPP_INLINE Result Instance::createWaylandSurfaceKHR( const WaylandSurfaceCreateInfoKHR* pCreateInfo, const AllocationCallbacks* pAllocator, SurfaceKHR* pSurface ) const { return static_cast( vkCreateWaylandSurfaceKHR( m_instance, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pSurface ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Instance::createWaylandSurfaceKHR( const WaylandSurfaceCreateInfoKHR & createInfo, Optional allocator ) const { SurfaceKHR surface; Result result = static_cast( vkCreateWaylandSurfaceKHR( m_instance, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &surface ) ) ); return createResultValue( result, surface, "vk::Instance::createWaylandSurfaceKHR" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE VULKAN_HPP_INLINE UniqueSurfaceKHR Instance::createWaylandSurfaceKHRUnique( const WaylandSurfaceCreateInfoKHR & createInfo, Optional allocator ) const { SurfaceKHRDeleter deleter( *this, allocator ); return UniqueSurfaceKHR( createWaylandSurfaceKHR( createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_WAYLAND_KHR*/ #ifdef VK_USE_PLATFORM_WIN32_KHR VULKAN_HPP_INLINE Result Instance::createWin32SurfaceKHR( const Win32SurfaceCreateInfoKHR* pCreateInfo, const AllocationCallbacks* pAllocator, SurfaceKHR* pSurface ) const { return static_cast( vkCreateWin32SurfaceKHR( m_instance, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pSurface ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Instance::createWin32SurfaceKHR( const Win32SurfaceCreateInfoKHR & createInfo, Optional allocator ) const { SurfaceKHR surface; Result result = static_cast( vkCreateWin32SurfaceKHR( m_instance, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &surface ) ) ); return createResultValue( result, surface, "vk::Instance::createWin32SurfaceKHR" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE VULKAN_HPP_INLINE UniqueSurfaceKHR Instance::createWin32SurfaceKHRUnique( const Win32SurfaceCreateInfoKHR & createInfo, Optional allocator ) const { SurfaceKHRDeleter deleter( *this, allocator ); return UniqueSurfaceKHR( createWin32SurfaceKHR( createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_WIN32_KHR*/ #ifdef VK_USE_PLATFORM_XLIB_KHR VULKAN_HPP_INLINE Result Instance::createXlibSurfaceKHR( const XlibSurfaceCreateInfoKHR* pCreateInfo, const AllocationCallbacks* pAllocator, SurfaceKHR* pSurface ) const { return static_cast( vkCreateXlibSurfaceKHR( m_instance, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pSurface ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Instance::createXlibSurfaceKHR( const XlibSurfaceCreateInfoKHR & createInfo, Optional allocator ) const { SurfaceKHR surface; Result result = static_cast( vkCreateXlibSurfaceKHR( m_instance, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &surface ) ) ); return createResultValue( result, surface, "vk::Instance::createXlibSurfaceKHR" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE VULKAN_HPP_INLINE UniqueSurfaceKHR Instance::createXlibSurfaceKHRUnique( const XlibSurfaceCreateInfoKHR & createInfo, Optional allocator ) const { SurfaceKHRDeleter deleter( *this, allocator ); return UniqueSurfaceKHR( createXlibSurfaceKHR( createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_XLIB_KHR*/ #ifdef VK_USE_PLATFORM_XCB_KHR VULKAN_HPP_INLINE Result Instance::createXcbSurfaceKHR( const XcbSurfaceCreateInfoKHR* pCreateInfo, const AllocationCallbacks* pAllocator, SurfaceKHR* pSurface ) const { return static_cast( vkCreateXcbSurfaceKHR( m_instance, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pSurface ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Instance::createXcbSurfaceKHR( const XcbSurfaceCreateInfoKHR & createInfo, Optional allocator ) const { SurfaceKHR surface; Result result = static_cast( vkCreateXcbSurfaceKHR( m_instance, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &surface ) ) ); return createResultValue( result, surface, "vk::Instance::createXcbSurfaceKHR" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE VULKAN_HPP_INLINE UniqueSurfaceKHR Instance::createXcbSurfaceKHRUnique( const XcbSurfaceCreateInfoKHR & createInfo, Optional allocator ) const { SurfaceKHRDeleter deleter( *this, allocator ); return UniqueSurfaceKHR( createXcbSurfaceKHR( createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_XCB_KHR*/ VULKAN_HPP_INLINE Result Instance::createDebugReportCallbackEXT( const DebugReportCallbackCreateInfoEXT* pCreateInfo, const AllocationCallbacks* pAllocator, DebugReportCallbackEXT* pCallback ) const { return static_cast( vkCreateDebugReportCallbackEXT( m_instance, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pCallback ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Instance::createDebugReportCallbackEXT( const DebugReportCallbackCreateInfoEXT & createInfo, Optional allocator ) const { DebugReportCallbackEXT callback; Result result = static_cast( vkCreateDebugReportCallbackEXT( m_instance, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &callback ) ) ); return createResultValue( result, callback, "vk::Instance::createDebugReportCallbackEXT" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE VULKAN_HPP_INLINE UniqueDebugReportCallbackEXT Instance::createDebugReportCallbackEXTUnique( const DebugReportCallbackCreateInfoEXT & createInfo, Optional allocator ) const { DebugReportCallbackEXTDeleter deleter( *this, allocator ); return UniqueDebugReportCallbackEXT( createDebugReportCallbackEXT( createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Instance::destroyDebugReportCallbackEXT( DebugReportCallbackEXT callback, const AllocationCallbacks* pAllocator ) const { vkDestroyDebugReportCallbackEXT( m_instance, static_cast( callback ), reinterpret_cast( pAllocator ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void Instance::destroyDebugReportCallbackEXT( DebugReportCallbackEXT callback, Optional allocator ) const { vkDestroyDebugReportCallbackEXT( m_instance, static_cast( callback ), reinterpret_cast( static_cast( allocator ) ) ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE void Instance::debugReportMessageEXT( DebugReportFlagsEXT flags, DebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage ) const { vkDebugReportMessageEXT( m_instance, static_cast( flags ), static_cast( objectType ), object, location, messageCode, pLayerPrefix, pMessage ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE void Instance::debugReportMessageEXT( DebugReportFlagsEXT flags, DebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const std::string & layerPrefix, const std::string & message ) const { #ifdef VULKAN_HPP_NO_EXCEPTIONS assert( layerPrefix.size() == message.size() ); #else if ( layerPrefix.size() != message.size() ) { throw std::logic_error( "vk::Instance::debugReportMessageEXT: layerPrefix.size() != message.size()" ); } #endif // VULKAN_HPP_NO_EXCEPTIONS vkDebugReportMessageEXT( m_instance, static_cast( flags ), static_cast( objectType ), object, location, messageCode, layerPrefix.c_str(), message.c_str() ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE Result Instance::enumeratePhysicalDeviceGroupsKHX( uint32_t* pPhysicalDeviceGroupCount, PhysicalDeviceGroupPropertiesKHX* pPhysicalDeviceGroupProperties ) const { return static_cast( vkEnumeratePhysicalDeviceGroupsKHX( m_instance, pPhysicalDeviceGroupCount, reinterpret_cast( pPhysicalDeviceGroupProperties ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE template VULKAN_HPP_INLINE typename ResultValueType>::type Instance::enumeratePhysicalDeviceGroupsKHX() const { std::vector physicalDeviceGroupProperties; uint32_t physicalDeviceGroupCount; Result result; do { result = static_cast( vkEnumeratePhysicalDeviceGroupsKHX( m_instance, &physicalDeviceGroupCount, nullptr ) ); if ( ( result == Result::eSuccess ) && physicalDeviceGroupCount ) { physicalDeviceGroupProperties.resize( physicalDeviceGroupCount ); result = static_cast( vkEnumeratePhysicalDeviceGroupsKHX( m_instance, &physicalDeviceGroupCount, reinterpret_cast( physicalDeviceGroupProperties.data() ) ) ); } } while ( result == Result::eIncomplete ); assert( physicalDeviceGroupCount <= physicalDeviceGroupProperties.size() ); physicalDeviceGroupProperties.resize( physicalDeviceGroupCount ); return createResultValue( result, physicalDeviceGroupProperties, "vk::Instance::enumeratePhysicalDeviceGroupsKHX" ); } #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifdef VK_USE_PLATFORM_IOS_MVK VULKAN_HPP_INLINE Result Instance::createIOSSurfaceMVK( const IOSSurfaceCreateInfoMVK* pCreateInfo, const AllocationCallbacks* pAllocator, SurfaceKHR* pSurface ) const { return static_cast( vkCreateIOSSurfaceMVK( m_instance, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pSurface ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Instance::createIOSSurfaceMVK( const IOSSurfaceCreateInfoMVK & createInfo, Optional allocator ) const { SurfaceKHR surface; Result result = static_cast( vkCreateIOSSurfaceMVK( m_instance, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &surface ) ) ); return createResultValue( result, surface, "vk::Instance::createIOSSurfaceMVK" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE VULKAN_HPP_INLINE UniqueSurfaceKHR Instance::createIOSSurfaceMVKUnique( const IOSSurfaceCreateInfoMVK & createInfo, Optional allocator ) const { SurfaceKHRDeleter deleter( *this, allocator ); return UniqueSurfaceKHR( createIOSSurfaceMVK( createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_IOS_MVK*/ #ifdef VK_USE_PLATFORM_MACOS_MVK VULKAN_HPP_INLINE Result Instance::createMacOSSurfaceMVK( const MacOSSurfaceCreateInfoMVK* pCreateInfo, const AllocationCallbacks* pAllocator, SurfaceKHR* pSurface ) const { return static_cast( vkCreateMacOSSurfaceMVK( m_instance, reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pSurface ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type Instance::createMacOSSurfaceMVK( const MacOSSurfaceCreateInfoMVK & createInfo, Optional allocator ) const { SurfaceKHR surface; Result result = static_cast( vkCreateMacOSSurfaceMVK( m_instance, reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &surface ) ) ); return createResultValue( result, surface, "vk::Instance::createMacOSSurfaceMVK" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE VULKAN_HPP_INLINE UniqueSurfaceKHR Instance::createMacOSSurfaceMVKUnique( const MacOSSurfaceCreateInfoMVK & createInfo, Optional allocator ) const { SurfaceKHRDeleter deleter( *this, allocator ); return UniqueSurfaceKHR( createMacOSSurfaceMVK( createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #endif /*VK_USE_PLATFORM_MACOS_MVK*/ struct DeviceGroupDeviceCreateInfoKHX { DeviceGroupDeviceCreateInfoKHX( uint32_t physicalDeviceCount_ = 0, const PhysicalDevice* pPhysicalDevices_ = nullptr ) : sType( StructureType::eDeviceGroupDeviceCreateInfoKHX ) , pNext( nullptr ) , physicalDeviceCount( physicalDeviceCount_ ) , pPhysicalDevices( pPhysicalDevices_ ) { } DeviceGroupDeviceCreateInfoKHX( VkDeviceGroupDeviceCreateInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(DeviceGroupDeviceCreateInfoKHX) ); } DeviceGroupDeviceCreateInfoKHX& operator=( VkDeviceGroupDeviceCreateInfoKHX const & rhs ) { memcpy( this, &rhs, sizeof(DeviceGroupDeviceCreateInfoKHX) ); return *this; } DeviceGroupDeviceCreateInfoKHX& setPNext( const void* pNext_ ) { pNext = pNext_; return *this; } DeviceGroupDeviceCreateInfoKHX& setPhysicalDeviceCount( uint32_t physicalDeviceCount_ ) { physicalDeviceCount = physicalDeviceCount_; return *this; } DeviceGroupDeviceCreateInfoKHX& setPPhysicalDevices( const PhysicalDevice* pPhysicalDevices_ ) { pPhysicalDevices = pPhysicalDevices_; return *this; } operator const VkDeviceGroupDeviceCreateInfoKHX&() const { return *reinterpret_cast(this); } bool operator==( DeviceGroupDeviceCreateInfoKHX const& rhs ) const { return ( sType == rhs.sType ) && ( pNext == rhs.pNext ) && ( physicalDeviceCount == rhs.physicalDeviceCount ) && ( pPhysicalDevices == rhs.pPhysicalDevices ); } bool operator!=( DeviceGroupDeviceCreateInfoKHX const& rhs ) const { return !operator==( rhs ); } private: StructureType sType; public: const void* pNext; uint32_t physicalDeviceCount; const PhysicalDevice* pPhysicalDevices; }; static_assert( sizeof( DeviceGroupDeviceCreateInfoKHX ) == sizeof( VkDeviceGroupDeviceCreateInfoKHX ), "struct and wrapper have different size!" ); #ifndef VULKAN_HPP_NO_SMART_HANDLE class InstanceDeleter; using UniqueInstance = UniqueHandle; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ Result createInstance( const InstanceCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, Instance* pInstance ); #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE ResultValueType::type createInstance( const InstanceCreateInfo & createInfo, Optional allocator = nullptr ); #ifndef VULKAN_HPP_NO_SMART_HANDLE UniqueInstance createInstanceUnique( const InstanceCreateInfo & createInfo, Optional allocator = nullptr ); #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ #ifndef VULKAN_HPP_NO_SMART_HANDLE class InstanceDeleter { public: InstanceDeleter( Optional allocator = nullptr ) : m_allocator( allocator ) {} void operator()( Instance instance ) { instance.destroy( m_allocator ); } private: Optional m_allocator; }; #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ VULKAN_HPP_INLINE Result createInstance( const InstanceCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, Instance* pInstance ) { return static_cast( vkCreateInstance( reinterpret_cast( pCreateInfo ), reinterpret_cast( pAllocator ), reinterpret_cast( pInstance ) ) ); } #ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE VULKAN_HPP_INLINE ResultValueType::type createInstance( const InstanceCreateInfo & createInfo, Optional allocator ) { Instance instance; Result result = static_cast( vkCreateInstance( reinterpret_cast( &createInfo ), reinterpret_cast( static_cast( allocator ) ), reinterpret_cast( &instance ) ) ); return createResultValue( result, instance, "vk::createInstance" ); } #ifndef VULKAN_HPP_NO_SMART_HANDLE VULKAN_HPP_INLINE UniqueInstance createInstanceUnique( const InstanceCreateInfo & createInfo, Optional allocator ) { InstanceDeleter deleter( allocator ); return UniqueInstance( createInstance( createInfo, allocator ), deleter ); } #endif /*VULKAN_HPP_NO_SMART_HANDLE*/ #endif /*VULKAN_HPP_DISABLE_ENHANCED_MODE*/ VULKAN_HPP_INLINE std::string to_string(FramebufferCreateFlagBits) { return "(void)"; } VULKAN_HPP_INLINE std::string to_string(FramebufferCreateFlags) { return "{}"; } VULKAN_HPP_INLINE std::string to_string(QueryPoolCreateFlagBits) { return "(void)"; } VULKAN_HPP_INLINE std::string to_string(QueryPoolCreateFlags) { return "{}"; } VULKAN_HPP_INLINE std::string to_string(RenderPassCreateFlagBits) { return "(void)"; } VULKAN_HPP_INLINE std::string to_string(RenderPassCreateFlags) { return "{}"; } VULKAN_HPP_INLINE std::string to_string(SamplerCreateFlagBits) { return "(void)"; } VULKAN_HPP_INLINE std::string to_string(SamplerCreateFlags) { return "{}"; } VULKAN_HPP_INLINE std::string to_string(PipelineLayoutCreateFlagBits) { return "(void)"; } VULKAN_HPP_INLINE std::string to_string(PipelineLayoutCreateFlags) { return "{}"; } VULKAN_HPP_INLINE std::string to_string(PipelineCacheCreateFlagBits) { return "(void)"; } VULKAN_HPP_INLINE std::string to_string(PipelineCacheCreateFlags) { return "{}"; } VULKAN_HPP_INLINE std::string to_string(PipelineDepthStencilStateCreateFlagBits) { return "(void)"; } VULKAN_HPP_INLINE std::string to_string(PipelineDepthStencilStateCreateFlags) { return "{}"; } VULKAN_HPP_INLINE std::string to_string(PipelineDynamicStateCreateFlagBits) { return "(void)"; } VULKAN_HPP_INLINE std::string to_string(PipelineDynamicStateCreateFlags) { return "{}"; } VULKAN_HPP_INLINE std::string to_string(PipelineColorBlendStateCreateFlagBits) { return "(void)"; } VULKAN_HPP_INLINE std::string to_string(PipelineColorBlendStateCreateFlags) { return "{}"; } VULKAN_HPP_INLINE std::string to_string(PipelineMultisampleStateCreateFlagBits) { return "(void)"; } VULKAN_HPP_INLINE std::string to_string(PipelineMultisampleStateCreateFlags) { return "{}"; } VULKAN_HPP_INLINE std::string to_string(PipelineRasterizationStateCreateFlagBits) { return "(void)"; } VULKAN_HPP_INLINE std::string to_string(PipelineRasterizationStateCreateFlags) { return "{}"; } VULKAN_HPP_INLINE std::string to_string(PipelineViewportStateCreateFlagBits) { return "(void)"; } VULKAN_HPP_INLINE std::string to_string(PipelineViewportStateCreateFlags) { return "{}"; } VULKAN_HPP_INLINE std::string to_string(PipelineTessellationStateCreateFlagBits) { return "(void)"; } VULKAN_HPP_INLINE std::string to_string(PipelineTessellationStateCreateFlags) { return "{}"; } VULKAN_HPP_INLINE std::string to_string(PipelineInputAssemblyStateCreateFlagBits) { return "(void)"; } VULKAN_HPP_INLINE std::string to_string(PipelineInputAssemblyStateCreateFlags) { return "{}"; } VULKAN_HPP_INLINE std::string to_string(PipelineVertexInputStateCreateFlagBits) { return "(void)"; } VULKAN_HPP_INLINE std::string to_string(PipelineVertexInputStateCreateFlags) { return "{}"; } VULKAN_HPP_INLINE std::string to_string(PipelineShaderStageCreateFlagBits) { return "(void)"; } VULKAN_HPP_INLINE std::string to_string(PipelineShaderStageCreateFlags) { return "{}"; } VULKAN_HPP_INLINE std::string to_string(BufferViewCreateFlagBits) { return "(void)"; } VULKAN_HPP_INLINE std::string to_string(BufferViewCreateFlags) { return "{}"; } VULKAN_HPP_INLINE std::string to_string(InstanceCreateFlagBits) { return "(void)"; } VULKAN_HPP_INLINE std::string to_string(InstanceCreateFlags) { return "{}"; } VULKAN_HPP_INLINE std::string to_string(DeviceCreateFlagBits) { return "(void)"; } VULKAN_HPP_INLINE std::string to_string(DeviceCreateFlags) { return "{}"; } VULKAN_HPP_INLINE std::string to_string(DeviceQueueCreateFlagBits) { return "(void)"; } VULKAN_HPP_INLINE std::string to_string(DeviceQueueCreateFlags) { return "{}"; } VULKAN_HPP_INLINE std::string to_string(ImageViewCreateFlagBits) { return "(void)"; } VULKAN_HPP_INLINE std::string to_string(ImageViewCreateFlags) { return "{}"; } VULKAN_HPP_INLINE std::string to_string(SemaphoreCreateFlagBits) { return "(void)"; } VULKAN_HPP_INLINE std::string to_string(SemaphoreCreateFlags) { return "{}"; } VULKAN_HPP_INLINE std::string to_string(ShaderModuleCreateFlagBits) { return "(void)"; } VULKAN_HPP_INLINE std::string to_string(ShaderModuleCreateFlags) { return "{}"; } VULKAN_HPP_INLINE std::string to_string(EventCreateFlagBits) { return "(void)"; } VULKAN_HPP_INLINE std::string to_string(EventCreateFlags) { return "{}"; } VULKAN_HPP_INLINE std::string to_string(MemoryMapFlagBits) { return "(void)"; } VULKAN_HPP_INLINE std::string to_string(MemoryMapFlags) { return "{}"; } VULKAN_HPP_INLINE std::string to_string(DescriptorPoolResetFlagBits) { return "(void)"; } VULKAN_HPP_INLINE std::string to_string(DescriptorPoolResetFlags) { return "{}"; } VULKAN_HPP_INLINE std::string to_string(DescriptorUpdateTemplateCreateFlagBitsKHR) { return "(void)"; } VULKAN_HPP_INLINE std::string to_string(DescriptorUpdateTemplateCreateFlagsKHR) { return "{}"; } VULKAN_HPP_INLINE std::string to_string(DisplayModeCreateFlagBitsKHR) { return "(void)"; } VULKAN_HPP_INLINE std::string to_string(DisplayModeCreateFlagsKHR) { return "{}"; } VULKAN_HPP_INLINE std::string to_string(DisplaySurfaceCreateFlagBitsKHR) { return "(void)"; } VULKAN_HPP_INLINE std::string to_string(DisplaySurfaceCreateFlagsKHR) { return "{}"; } #ifdef VK_USE_PLATFORM_ANDROID_KHR VULKAN_HPP_INLINE std::string to_string(AndroidSurfaceCreateFlagBitsKHR) { return "(void)"; } #endif /*VK_USE_PLATFORM_ANDROID_KHR*/ #ifdef VK_USE_PLATFORM_ANDROID_KHR VULKAN_HPP_INLINE std::string to_string(AndroidSurfaceCreateFlagsKHR) { return "{}"; } #endif /*VK_USE_PLATFORM_ANDROID_KHR*/ #ifdef VK_USE_PLATFORM_MIR_KHR VULKAN_HPP_INLINE std::string to_string(MirSurfaceCreateFlagBitsKHR) { return "(void)"; } #endif /*VK_USE_PLATFORM_MIR_KHR*/ #ifdef VK_USE_PLATFORM_MIR_KHR VULKAN_HPP_INLINE std::string to_string(MirSurfaceCreateFlagsKHR) { return "{}"; } #endif /*VK_USE_PLATFORM_MIR_KHR*/ #ifdef VK_USE_PLATFORM_VI_NN VULKAN_HPP_INLINE std::string to_string(ViSurfaceCreateFlagBitsNN) { return "(void)"; } #endif /*VK_USE_PLATFORM_VI_NN*/ #ifdef VK_USE_PLATFORM_VI_NN VULKAN_HPP_INLINE std::string to_string(ViSurfaceCreateFlagsNN) { return "{}"; } #endif /*VK_USE_PLATFORM_VI_NN*/ #ifdef VK_USE_PLATFORM_WAYLAND_KHR VULKAN_HPP_INLINE std::string to_string(WaylandSurfaceCreateFlagBitsKHR) { return "(void)"; } #endif /*VK_USE_PLATFORM_WAYLAND_KHR*/ #ifdef VK_USE_PLATFORM_WAYLAND_KHR VULKAN_HPP_INLINE std::string to_string(WaylandSurfaceCreateFlagsKHR) { return "{}"; } #endif /*VK_USE_PLATFORM_WAYLAND_KHR*/ #ifdef VK_USE_PLATFORM_WIN32_KHR VULKAN_HPP_INLINE std::string to_string(Win32SurfaceCreateFlagBitsKHR) { return "(void)"; } #endif /*VK_USE_PLATFORM_WIN32_KHR*/ #ifdef VK_USE_PLATFORM_WIN32_KHR VULKAN_HPP_INLINE std::string to_string(Win32SurfaceCreateFlagsKHR) { return "{}"; } #endif /*VK_USE_PLATFORM_WIN32_KHR*/ #ifdef VK_USE_PLATFORM_XLIB_KHR VULKAN_HPP_INLINE std::string to_string(XlibSurfaceCreateFlagBitsKHR) { return "(void)"; } #endif /*VK_USE_PLATFORM_XLIB_KHR*/ #ifdef VK_USE_PLATFORM_XLIB_KHR VULKAN_HPP_INLINE std::string to_string(XlibSurfaceCreateFlagsKHR) { return "{}"; } #endif /*VK_USE_PLATFORM_XLIB_KHR*/ #ifdef VK_USE_PLATFORM_XCB_KHR VULKAN_HPP_INLINE std::string to_string(XcbSurfaceCreateFlagBitsKHR) { return "(void)"; } #endif /*VK_USE_PLATFORM_XCB_KHR*/ #ifdef VK_USE_PLATFORM_XCB_KHR VULKAN_HPP_INLINE std::string to_string(XcbSurfaceCreateFlagsKHR) { return "{}"; } #endif /*VK_USE_PLATFORM_XCB_KHR*/ #ifdef VK_USE_PLATFORM_IOS_MVK VULKAN_HPP_INLINE std::string to_string(IOSSurfaceCreateFlagBitsMVK) { return "(void)"; } #endif /*VK_USE_PLATFORM_IOS_MVK*/ #ifdef VK_USE_PLATFORM_IOS_MVK VULKAN_HPP_INLINE std::string to_string(IOSSurfaceCreateFlagsMVK) { return "{}"; } #endif /*VK_USE_PLATFORM_IOS_MVK*/ #ifdef VK_USE_PLATFORM_MACOS_MVK VULKAN_HPP_INLINE std::string to_string(MacOSSurfaceCreateFlagBitsMVK) { return "(void)"; } #endif /*VK_USE_PLATFORM_MACOS_MVK*/ #ifdef VK_USE_PLATFORM_MACOS_MVK VULKAN_HPP_INLINE std::string to_string(MacOSSurfaceCreateFlagsMVK) { return "{}"; } #endif /*VK_USE_PLATFORM_MACOS_MVK*/ VULKAN_HPP_INLINE std::string to_string(CommandPoolTrimFlagBitsKHR) { return "(void)"; } VULKAN_HPP_INLINE std::string to_string(CommandPoolTrimFlagsKHR) { return "{}"; } VULKAN_HPP_INLINE std::string to_string(PipelineViewportSwizzleStateCreateFlagBitsNV) { return "(void)"; } VULKAN_HPP_INLINE std::string to_string(PipelineViewportSwizzleStateCreateFlagsNV) { return "{}"; } VULKAN_HPP_INLINE std::string to_string(PipelineDiscardRectangleStateCreateFlagBitsEXT) { return "(void)"; } VULKAN_HPP_INLINE std::string to_string(PipelineDiscardRectangleStateCreateFlagsEXT) { return "{}"; } VULKAN_HPP_INLINE std::string to_string(ImageLayout value) { switch (value) { case ImageLayout::eUndefined: return "Undefined"; case ImageLayout::eGeneral: return "General"; case ImageLayout::eColorAttachmentOptimal: return "ColorAttachmentOptimal"; case ImageLayout::eDepthStencilAttachmentOptimal: return "DepthStencilAttachmentOptimal"; case ImageLayout::eDepthStencilReadOnlyOptimal: return "DepthStencilReadOnlyOptimal"; case ImageLayout::eShaderReadOnlyOptimal: return "ShaderReadOnlyOptimal"; case ImageLayout::eTransferSrcOptimal: return "TransferSrcOptimal"; case ImageLayout::eTransferDstOptimal: return "TransferDstOptimal"; case ImageLayout::ePreinitialized: return "Preinitialized"; case ImageLayout::ePresentSrcKHR: return "PresentSrcKHR"; case ImageLayout::eSharedPresentKHR: return "SharedPresentKHR"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(AttachmentLoadOp value) { switch (value) { case AttachmentLoadOp::eLoad: return "Load"; case AttachmentLoadOp::eClear: return "Clear"; case AttachmentLoadOp::eDontCare: return "DontCare"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(AttachmentStoreOp value) { switch (value) { case AttachmentStoreOp::eStore: return "Store"; case AttachmentStoreOp::eDontCare: return "DontCare"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(ImageType value) { switch (value) { case ImageType::e1D: return "1D"; case ImageType::e2D: return "2D"; case ImageType::e3D: return "3D"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(ImageTiling value) { switch (value) { case ImageTiling::eOptimal: return "Optimal"; case ImageTiling::eLinear: return "Linear"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(ImageViewType value) { switch (value) { case ImageViewType::e1D: return "1D"; case ImageViewType::e2D: return "2D"; case ImageViewType::e3D: return "3D"; case ImageViewType::eCube: return "Cube"; case ImageViewType::e1DArray: return "1DArray"; case ImageViewType::e2DArray: return "2DArray"; case ImageViewType::eCubeArray: return "CubeArray"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(CommandBufferLevel value) { switch (value) { case CommandBufferLevel::ePrimary: return "Primary"; case CommandBufferLevel::eSecondary: return "Secondary"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(ComponentSwizzle value) { switch (value) { case ComponentSwizzle::eIdentity: return "Identity"; case ComponentSwizzle::eZero: return "Zero"; case ComponentSwizzle::eOne: return "One"; case ComponentSwizzle::eR: return "R"; case ComponentSwizzle::eG: return "G"; case ComponentSwizzle::eB: return "B"; case ComponentSwizzle::eA: return "A"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(DescriptorType value) { switch (value) { case DescriptorType::eSampler: return "Sampler"; case DescriptorType::eCombinedImageSampler: return "CombinedImageSampler"; case DescriptorType::eSampledImage: return "SampledImage"; case DescriptorType::eStorageImage: return "StorageImage"; case DescriptorType::eUniformTexelBuffer: return "UniformTexelBuffer"; case DescriptorType::eStorageTexelBuffer: return "StorageTexelBuffer"; case DescriptorType::eUniformBuffer: return "UniformBuffer"; case DescriptorType::eStorageBuffer: return "StorageBuffer"; case DescriptorType::eUniformBufferDynamic: return "UniformBufferDynamic"; case DescriptorType::eStorageBufferDynamic: return "StorageBufferDynamic"; case DescriptorType::eInputAttachment: return "InputAttachment"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(QueryType value) { switch (value) { case QueryType::eOcclusion: return "Occlusion"; case QueryType::ePipelineStatistics: return "PipelineStatistics"; case QueryType::eTimestamp: return "Timestamp"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(BorderColor value) { switch (value) { case BorderColor::eFloatTransparentBlack: return "FloatTransparentBlack"; case BorderColor::eIntTransparentBlack: return "IntTransparentBlack"; case BorderColor::eFloatOpaqueBlack: return "FloatOpaqueBlack"; case BorderColor::eIntOpaqueBlack: return "IntOpaqueBlack"; case BorderColor::eFloatOpaqueWhite: return "FloatOpaqueWhite"; case BorderColor::eIntOpaqueWhite: return "IntOpaqueWhite"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(PipelineBindPoint value) { switch (value) { case PipelineBindPoint::eGraphics: return "Graphics"; case PipelineBindPoint::eCompute: return "Compute"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(PipelineCacheHeaderVersion value) { switch (value) { case PipelineCacheHeaderVersion::eOne: return "One"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(PrimitiveTopology value) { switch (value) { case PrimitiveTopology::ePointList: return "PointList"; case PrimitiveTopology::eLineList: return "LineList"; case PrimitiveTopology::eLineStrip: return "LineStrip"; case PrimitiveTopology::eTriangleList: return "TriangleList"; case PrimitiveTopology::eTriangleStrip: return "TriangleStrip"; case PrimitiveTopology::eTriangleFan: return "TriangleFan"; case PrimitiveTopology::eLineListWithAdjacency: return "LineListWithAdjacency"; case PrimitiveTopology::eLineStripWithAdjacency: return "LineStripWithAdjacency"; case PrimitiveTopology::eTriangleListWithAdjacency: return "TriangleListWithAdjacency"; case PrimitiveTopology::eTriangleStripWithAdjacency: return "TriangleStripWithAdjacency"; case PrimitiveTopology::ePatchList: return "PatchList"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(SharingMode value) { switch (value) { case SharingMode::eExclusive: return "Exclusive"; case SharingMode::eConcurrent: return "Concurrent"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(IndexType value) { switch (value) { case IndexType::eUint16: return "Uint16"; case IndexType::eUint32: return "Uint32"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(Filter value) { switch (value) { case Filter::eNearest: return "Nearest"; case Filter::eLinear: return "Linear"; case Filter::eCubicIMG: return "CubicIMG"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(SamplerMipmapMode value) { switch (value) { case SamplerMipmapMode::eNearest: return "Nearest"; case SamplerMipmapMode::eLinear: return "Linear"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(SamplerAddressMode value) { switch (value) { case SamplerAddressMode::eRepeat: return "Repeat"; case SamplerAddressMode::eMirroredRepeat: return "MirroredRepeat"; case SamplerAddressMode::eClampToEdge: return "ClampToEdge"; case SamplerAddressMode::eClampToBorder: return "ClampToBorder"; case SamplerAddressMode::eMirrorClampToEdge: return "MirrorClampToEdge"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(CompareOp value) { switch (value) { case CompareOp::eNever: return "Never"; case CompareOp::eLess: return "Less"; case CompareOp::eEqual: return "Equal"; case CompareOp::eLessOrEqual: return "LessOrEqual"; case CompareOp::eGreater: return "Greater"; case CompareOp::eNotEqual: return "NotEqual"; case CompareOp::eGreaterOrEqual: return "GreaterOrEqual"; case CompareOp::eAlways: return "Always"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(PolygonMode value) { switch (value) { case PolygonMode::eFill: return "Fill"; case PolygonMode::eLine: return "Line"; case PolygonMode::ePoint: return "Point"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(CullModeFlagBits value) { switch (value) { case CullModeFlagBits::eNone: return "None"; case CullModeFlagBits::eFront: return "Front"; case CullModeFlagBits::eBack: return "Back"; case CullModeFlagBits::eFrontAndBack: return "FrontAndBack"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(CullModeFlags value) { if (!value) return "{}"; std::string result; if (value & CullModeFlagBits::eNone) result += "None | "; if (value & CullModeFlagBits::eFront) result += "Front | "; if (value & CullModeFlagBits::eBack) result += "Back | "; if (value & CullModeFlagBits::eFrontAndBack) result += "FrontAndBack | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(FrontFace value) { switch (value) { case FrontFace::eCounterClockwise: return "CounterClockwise"; case FrontFace::eClockwise: return "Clockwise"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(BlendFactor value) { switch (value) { case BlendFactor::eZero: return "Zero"; case BlendFactor::eOne: return "One"; case BlendFactor::eSrcColor: return "SrcColor"; case BlendFactor::eOneMinusSrcColor: return "OneMinusSrcColor"; case BlendFactor::eDstColor: return "DstColor"; case BlendFactor::eOneMinusDstColor: return "OneMinusDstColor"; case BlendFactor::eSrcAlpha: return "SrcAlpha"; case BlendFactor::eOneMinusSrcAlpha: return "OneMinusSrcAlpha"; case BlendFactor::eDstAlpha: return "DstAlpha"; case BlendFactor::eOneMinusDstAlpha: return "OneMinusDstAlpha"; case BlendFactor::eConstantColor: return "ConstantColor"; case BlendFactor::eOneMinusConstantColor: return "OneMinusConstantColor"; case BlendFactor::eConstantAlpha: return "ConstantAlpha"; case BlendFactor::eOneMinusConstantAlpha: return "OneMinusConstantAlpha"; case BlendFactor::eSrcAlphaSaturate: return "SrcAlphaSaturate"; case BlendFactor::eSrc1Color: return "Src1Color"; case BlendFactor::eOneMinusSrc1Color: return "OneMinusSrc1Color"; case BlendFactor::eSrc1Alpha: return "Src1Alpha"; case BlendFactor::eOneMinusSrc1Alpha: return "OneMinusSrc1Alpha"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(BlendOp value) { switch (value) { case BlendOp::eAdd: return "Add"; case BlendOp::eSubtract: return "Subtract"; case BlendOp::eReverseSubtract: return "ReverseSubtract"; case BlendOp::eMin: return "Min"; case BlendOp::eMax: return "Max"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(StencilOp value) { switch (value) { case StencilOp::eKeep: return "Keep"; case StencilOp::eZero: return "Zero"; case StencilOp::eReplace: return "Replace"; case StencilOp::eIncrementAndClamp: return "IncrementAndClamp"; case StencilOp::eDecrementAndClamp: return "DecrementAndClamp"; case StencilOp::eInvert: return "Invert"; case StencilOp::eIncrementAndWrap: return "IncrementAndWrap"; case StencilOp::eDecrementAndWrap: return "DecrementAndWrap"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(LogicOp value) { switch (value) { case LogicOp::eClear: return "Clear"; case LogicOp::eAnd: return "And"; case LogicOp::eAndReverse: return "AndReverse"; case LogicOp::eCopy: return "Copy"; case LogicOp::eAndInverted: return "AndInverted"; case LogicOp::eNoOp: return "NoOp"; case LogicOp::eXor: return "Xor"; case LogicOp::eOr: return "Or"; case LogicOp::eNor: return "Nor"; case LogicOp::eEquivalent: return "Equivalent"; case LogicOp::eInvert: return "Invert"; case LogicOp::eOrReverse: return "OrReverse"; case LogicOp::eCopyInverted: return "CopyInverted"; case LogicOp::eOrInverted: return "OrInverted"; case LogicOp::eNand: return "Nand"; case LogicOp::eSet: return "Set"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(InternalAllocationType value) { switch (value) { case InternalAllocationType::eExecutable: return "Executable"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(SystemAllocationScope value) { switch (value) { case SystemAllocationScope::eCommand: return "Command"; case SystemAllocationScope::eObject: return "Object"; case SystemAllocationScope::eCache: return "Cache"; case SystemAllocationScope::eDevice: return "Device"; case SystemAllocationScope::eInstance: return "Instance"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(PhysicalDeviceType value) { switch (value) { case PhysicalDeviceType::eOther: return "Other"; case PhysicalDeviceType::eIntegratedGpu: return "IntegratedGpu"; case PhysicalDeviceType::eDiscreteGpu: return "DiscreteGpu"; case PhysicalDeviceType::eVirtualGpu: return "VirtualGpu"; case PhysicalDeviceType::eCpu: return "Cpu"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(VertexInputRate value) { switch (value) { case VertexInputRate::eVertex: return "Vertex"; case VertexInputRate::eInstance: return "Instance"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(Format value) { switch (value) { case Format::eUndefined: return "Undefined"; case Format::eR4G4UnormPack8: return "R4G4UnormPack8"; case Format::eR4G4B4A4UnormPack16: return "R4G4B4A4UnormPack16"; case Format::eB4G4R4A4UnormPack16: return "B4G4R4A4UnormPack16"; case Format::eR5G6B5UnormPack16: return "R5G6B5UnormPack16"; case Format::eB5G6R5UnormPack16: return "B5G6R5UnormPack16"; case Format::eR5G5B5A1UnormPack16: return "R5G5B5A1UnormPack16"; case Format::eB5G5R5A1UnormPack16: return "B5G5R5A1UnormPack16"; case Format::eA1R5G5B5UnormPack16: return "A1R5G5B5UnormPack16"; case Format::eR8Unorm: return "R8Unorm"; case Format::eR8Snorm: return "R8Snorm"; case Format::eR8Uscaled: return "R8Uscaled"; case Format::eR8Sscaled: return "R8Sscaled"; case Format::eR8Uint: return "R8Uint"; case Format::eR8Sint: return "R8Sint"; case Format::eR8Srgb: return "R8Srgb"; case Format::eR8G8Unorm: return "R8G8Unorm"; case Format::eR8G8Snorm: return "R8G8Snorm"; case Format::eR8G8Uscaled: return "R8G8Uscaled"; case Format::eR8G8Sscaled: return "R8G8Sscaled"; case Format::eR8G8Uint: return "R8G8Uint"; case Format::eR8G8Sint: return "R8G8Sint"; case Format::eR8G8Srgb: return "R8G8Srgb"; case Format::eR8G8B8Unorm: return "R8G8B8Unorm"; case Format::eR8G8B8Snorm: return "R8G8B8Snorm"; case Format::eR8G8B8Uscaled: return "R8G8B8Uscaled"; case Format::eR8G8B8Sscaled: return "R8G8B8Sscaled"; case Format::eR8G8B8Uint: return "R8G8B8Uint"; case Format::eR8G8B8Sint: return "R8G8B8Sint"; case Format::eR8G8B8Srgb: return "R8G8B8Srgb"; case Format::eB8G8R8Unorm: return "B8G8R8Unorm"; case Format::eB8G8R8Snorm: return "B8G8R8Snorm"; case Format::eB8G8R8Uscaled: return "B8G8R8Uscaled"; case Format::eB8G8R8Sscaled: return "B8G8R8Sscaled"; case Format::eB8G8R8Uint: return "B8G8R8Uint"; case Format::eB8G8R8Sint: return "B8G8R8Sint"; case Format::eB8G8R8Srgb: return "B8G8R8Srgb"; case Format::eR8G8B8A8Unorm: return "R8G8B8A8Unorm"; case Format::eR8G8B8A8Snorm: return "R8G8B8A8Snorm"; case Format::eR8G8B8A8Uscaled: return "R8G8B8A8Uscaled"; case Format::eR8G8B8A8Sscaled: return "R8G8B8A8Sscaled"; case Format::eR8G8B8A8Uint: return "R8G8B8A8Uint"; case Format::eR8G8B8A8Sint: return "R8G8B8A8Sint"; case Format::eR8G8B8A8Srgb: return "R8G8B8A8Srgb"; case Format::eB8G8R8A8Unorm: return "B8G8R8A8Unorm"; case Format::eB8G8R8A8Snorm: return "B8G8R8A8Snorm"; case Format::eB8G8R8A8Uscaled: return "B8G8R8A8Uscaled"; case Format::eB8G8R8A8Sscaled: return "B8G8R8A8Sscaled"; case Format::eB8G8R8A8Uint: return "B8G8R8A8Uint"; case Format::eB8G8R8A8Sint: return "B8G8R8A8Sint"; case Format::eB8G8R8A8Srgb: return "B8G8R8A8Srgb"; case Format::eA8B8G8R8UnormPack32: return "A8B8G8R8UnormPack32"; case Format::eA8B8G8R8SnormPack32: return "A8B8G8R8SnormPack32"; case Format::eA8B8G8R8UscaledPack32: return "A8B8G8R8UscaledPack32"; case Format::eA8B8G8R8SscaledPack32: return "A8B8G8R8SscaledPack32"; case Format::eA8B8G8R8UintPack32: return "A8B8G8R8UintPack32"; case Format::eA8B8G8R8SintPack32: return "A8B8G8R8SintPack32"; case Format::eA8B8G8R8SrgbPack32: return "A8B8G8R8SrgbPack32"; case Format::eA2R10G10B10UnormPack32: return "A2R10G10B10UnormPack32"; case Format::eA2R10G10B10SnormPack32: return "A2R10G10B10SnormPack32"; case Format::eA2R10G10B10UscaledPack32: return "A2R10G10B10UscaledPack32"; case Format::eA2R10G10B10SscaledPack32: return "A2R10G10B10SscaledPack32"; case Format::eA2R10G10B10UintPack32: return "A2R10G10B10UintPack32"; case Format::eA2R10G10B10SintPack32: return "A2R10G10B10SintPack32"; case Format::eA2B10G10R10UnormPack32: return "A2B10G10R10UnormPack32"; case Format::eA2B10G10R10SnormPack32: return "A2B10G10R10SnormPack32"; case Format::eA2B10G10R10UscaledPack32: return "A2B10G10R10UscaledPack32"; case Format::eA2B10G10R10SscaledPack32: return "A2B10G10R10SscaledPack32"; case Format::eA2B10G10R10UintPack32: return "A2B10G10R10UintPack32"; case Format::eA2B10G10R10SintPack32: return "A2B10G10R10SintPack32"; case Format::eR16Unorm: return "R16Unorm"; case Format::eR16Snorm: return "R16Snorm"; case Format::eR16Uscaled: return "R16Uscaled"; case Format::eR16Sscaled: return "R16Sscaled"; case Format::eR16Uint: return "R16Uint"; case Format::eR16Sint: return "R16Sint"; case Format::eR16Sfloat: return "R16Sfloat"; case Format::eR16G16Unorm: return "R16G16Unorm"; case Format::eR16G16Snorm: return "R16G16Snorm"; case Format::eR16G16Uscaled: return "R16G16Uscaled"; case Format::eR16G16Sscaled: return "R16G16Sscaled"; case Format::eR16G16Uint: return "R16G16Uint"; case Format::eR16G16Sint: return "R16G16Sint"; case Format::eR16G16Sfloat: return "R16G16Sfloat"; case Format::eR16G16B16Unorm: return "R16G16B16Unorm"; case Format::eR16G16B16Snorm: return "R16G16B16Snorm"; case Format::eR16G16B16Uscaled: return "R16G16B16Uscaled"; case Format::eR16G16B16Sscaled: return "R16G16B16Sscaled"; case Format::eR16G16B16Uint: return "R16G16B16Uint"; case Format::eR16G16B16Sint: return "R16G16B16Sint"; case Format::eR16G16B16Sfloat: return "R16G16B16Sfloat"; case Format::eR16G16B16A16Unorm: return "R16G16B16A16Unorm"; case Format::eR16G16B16A16Snorm: return "R16G16B16A16Snorm"; case Format::eR16G16B16A16Uscaled: return "R16G16B16A16Uscaled"; case Format::eR16G16B16A16Sscaled: return "R16G16B16A16Sscaled"; case Format::eR16G16B16A16Uint: return "R16G16B16A16Uint"; case Format::eR16G16B16A16Sint: return "R16G16B16A16Sint"; case Format::eR16G16B16A16Sfloat: return "R16G16B16A16Sfloat"; case Format::eR32Uint: return "R32Uint"; case Format::eR32Sint: return "R32Sint"; case Format::eR32Sfloat: return "R32Sfloat"; case Format::eR32G32Uint: return "R32G32Uint"; case Format::eR32G32Sint: return "R32G32Sint"; case Format::eR32G32Sfloat: return "R32G32Sfloat"; case Format::eR32G32B32Uint: return "R32G32B32Uint"; case Format::eR32G32B32Sint: return "R32G32B32Sint"; case Format::eR32G32B32Sfloat: return "R32G32B32Sfloat"; case Format::eR32G32B32A32Uint: return "R32G32B32A32Uint"; case Format::eR32G32B32A32Sint: return "R32G32B32A32Sint"; case Format::eR32G32B32A32Sfloat: return "R32G32B32A32Sfloat"; case Format::eR64Uint: return "R64Uint"; case Format::eR64Sint: return "R64Sint"; case Format::eR64Sfloat: return "R64Sfloat"; case Format::eR64G64Uint: return "R64G64Uint"; case Format::eR64G64Sint: return "R64G64Sint"; case Format::eR64G64Sfloat: return "R64G64Sfloat"; case Format::eR64G64B64Uint: return "R64G64B64Uint"; case Format::eR64G64B64Sint: return "R64G64B64Sint"; case Format::eR64G64B64Sfloat: return "R64G64B64Sfloat"; case Format::eR64G64B64A64Uint: return "R64G64B64A64Uint"; case Format::eR64G64B64A64Sint: return "R64G64B64A64Sint"; case Format::eR64G64B64A64Sfloat: return "R64G64B64A64Sfloat"; case Format::eB10G11R11UfloatPack32: return "B10G11R11UfloatPack32"; case Format::eE5B9G9R9UfloatPack32: return "E5B9G9R9UfloatPack32"; case Format::eD16Unorm: return "D16Unorm"; case Format::eX8D24UnormPack32: return "X8D24UnormPack32"; case Format::eD32Sfloat: return "D32Sfloat"; case Format::eS8Uint: return "S8Uint"; case Format::eD16UnormS8Uint: return "D16UnormS8Uint"; case Format::eD24UnormS8Uint: return "D24UnormS8Uint"; case Format::eD32SfloatS8Uint: return "D32SfloatS8Uint"; case Format::eBc1RgbUnormBlock: return "Bc1RgbUnormBlock"; case Format::eBc1RgbSrgbBlock: return "Bc1RgbSrgbBlock"; case Format::eBc1RgbaUnormBlock: return "Bc1RgbaUnormBlock"; case Format::eBc1RgbaSrgbBlock: return "Bc1RgbaSrgbBlock"; case Format::eBc2UnormBlock: return "Bc2UnormBlock"; case Format::eBc2SrgbBlock: return "Bc2SrgbBlock"; case Format::eBc3UnormBlock: return "Bc3UnormBlock"; case Format::eBc3SrgbBlock: return "Bc3SrgbBlock"; case Format::eBc4UnormBlock: return "Bc4UnormBlock"; case Format::eBc4SnormBlock: return "Bc4SnormBlock"; case Format::eBc5UnormBlock: return "Bc5UnormBlock"; case Format::eBc5SnormBlock: return "Bc5SnormBlock"; case Format::eBc6HUfloatBlock: return "Bc6HUfloatBlock"; case Format::eBc6HSfloatBlock: return "Bc6HSfloatBlock"; case Format::eBc7UnormBlock: return "Bc7UnormBlock"; case Format::eBc7SrgbBlock: return "Bc7SrgbBlock"; case Format::eEtc2R8G8B8UnormBlock: return "Etc2R8G8B8UnormBlock"; case Format::eEtc2R8G8B8SrgbBlock: return "Etc2R8G8B8SrgbBlock"; case Format::eEtc2R8G8B8A1UnormBlock: return "Etc2R8G8B8A1UnormBlock"; case Format::eEtc2R8G8B8A1SrgbBlock: return "Etc2R8G8B8A1SrgbBlock"; case Format::eEtc2R8G8B8A8UnormBlock: return "Etc2R8G8B8A8UnormBlock"; case Format::eEtc2R8G8B8A8SrgbBlock: return "Etc2R8G8B8A8SrgbBlock"; case Format::eEacR11UnormBlock: return "EacR11UnormBlock"; case Format::eEacR11SnormBlock: return "EacR11SnormBlock"; case Format::eEacR11G11UnormBlock: return "EacR11G11UnormBlock"; case Format::eEacR11G11SnormBlock: return "EacR11G11SnormBlock"; case Format::eAstc4x4UnormBlock: return "Astc4x4UnormBlock"; case Format::eAstc4x4SrgbBlock: return "Astc4x4SrgbBlock"; case Format::eAstc5x4UnormBlock: return "Astc5x4UnormBlock"; case Format::eAstc5x4SrgbBlock: return "Astc5x4SrgbBlock"; case Format::eAstc5x5UnormBlock: return "Astc5x5UnormBlock"; case Format::eAstc5x5SrgbBlock: return "Astc5x5SrgbBlock"; case Format::eAstc6x5UnormBlock: return "Astc6x5UnormBlock"; case Format::eAstc6x5SrgbBlock: return "Astc6x5SrgbBlock"; case Format::eAstc6x6UnormBlock: return "Astc6x6UnormBlock"; case Format::eAstc6x6SrgbBlock: return "Astc6x6SrgbBlock"; case Format::eAstc8x5UnormBlock: return "Astc8x5UnormBlock"; case Format::eAstc8x5SrgbBlock: return "Astc8x5SrgbBlock"; case Format::eAstc8x6UnormBlock: return "Astc8x6UnormBlock"; case Format::eAstc8x6SrgbBlock: return "Astc8x6SrgbBlock"; case Format::eAstc8x8UnormBlock: return "Astc8x8UnormBlock"; case Format::eAstc8x8SrgbBlock: return "Astc8x8SrgbBlock"; case Format::eAstc10x5UnormBlock: return "Astc10x5UnormBlock"; case Format::eAstc10x5SrgbBlock: return "Astc10x5SrgbBlock"; case Format::eAstc10x6UnormBlock: return "Astc10x6UnormBlock"; case Format::eAstc10x6SrgbBlock: return "Astc10x6SrgbBlock"; case Format::eAstc10x8UnormBlock: return "Astc10x8UnormBlock"; case Format::eAstc10x8SrgbBlock: return "Astc10x8SrgbBlock"; case Format::eAstc10x10UnormBlock: return "Astc10x10UnormBlock"; case Format::eAstc10x10SrgbBlock: return "Astc10x10SrgbBlock"; case Format::eAstc12x10UnormBlock: return "Astc12x10UnormBlock"; case Format::eAstc12x10SrgbBlock: return "Astc12x10SrgbBlock"; case Format::eAstc12x12UnormBlock: return "Astc12x12UnormBlock"; case Format::eAstc12x12SrgbBlock: return "Astc12x12SrgbBlock"; case Format::ePvrtc12BppUnormBlockIMG: return "Pvrtc12BppUnormBlockIMG"; case Format::ePvrtc14BppUnormBlockIMG: return "Pvrtc14BppUnormBlockIMG"; case Format::ePvrtc22BppUnormBlockIMG: return "Pvrtc22BppUnormBlockIMG"; case Format::ePvrtc24BppUnormBlockIMG: return "Pvrtc24BppUnormBlockIMG"; case Format::ePvrtc12BppSrgbBlockIMG: return "Pvrtc12BppSrgbBlockIMG"; case Format::ePvrtc14BppSrgbBlockIMG: return "Pvrtc14BppSrgbBlockIMG"; case Format::ePvrtc22BppSrgbBlockIMG: return "Pvrtc22BppSrgbBlockIMG"; case Format::ePvrtc24BppSrgbBlockIMG: return "Pvrtc24BppSrgbBlockIMG"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(StructureType value) { switch (value) { case StructureType::eApplicationInfo: return "ApplicationInfo"; case StructureType::eInstanceCreateInfo: return "InstanceCreateInfo"; case StructureType::eDeviceQueueCreateInfo: return "DeviceQueueCreateInfo"; case StructureType::eDeviceCreateInfo: return "DeviceCreateInfo"; case StructureType::eSubmitInfo: return "SubmitInfo"; case StructureType::eMemoryAllocateInfo: return "MemoryAllocateInfo"; case StructureType::eMappedMemoryRange: return "MappedMemoryRange"; case StructureType::eBindSparseInfo: return "BindSparseInfo"; case StructureType::eFenceCreateInfo: return "FenceCreateInfo"; case StructureType::eSemaphoreCreateInfo: return "SemaphoreCreateInfo"; case StructureType::eEventCreateInfo: return "EventCreateInfo"; case StructureType::eQueryPoolCreateInfo: return "QueryPoolCreateInfo"; case StructureType::eBufferCreateInfo: return "BufferCreateInfo"; case StructureType::eBufferViewCreateInfo: return "BufferViewCreateInfo"; case StructureType::eImageCreateInfo: return "ImageCreateInfo"; case StructureType::eImageViewCreateInfo: return "ImageViewCreateInfo"; case StructureType::eShaderModuleCreateInfo: return "ShaderModuleCreateInfo"; case StructureType::ePipelineCacheCreateInfo: return "PipelineCacheCreateInfo"; case StructureType::ePipelineShaderStageCreateInfo: return "PipelineShaderStageCreateInfo"; case StructureType::ePipelineVertexInputStateCreateInfo: return "PipelineVertexInputStateCreateInfo"; case StructureType::ePipelineInputAssemblyStateCreateInfo: return "PipelineInputAssemblyStateCreateInfo"; case StructureType::ePipelineTessellationStateCreateInfo: return "PipelineTessellationStateCreateInfo"; case StructureType::ePipelineViewportStateCreateInfo: return "PipelineViewportStateCreateInfo"; case StructureType::ePipelineRasterizationStateCreateInfo: return "PipelineRasterizationStateCreateInfo"; case StructureType::ePipelineMultisampleStateCreateInfo: return "PipelineMultisampleStateCreateInfo"; case StructureType::ePipelineDepthStencilStateCreateInfo: return "PipelineDepthStencilStateCreateInfo"; case StructureType::ePipelineColorBlendStateCreateInfo: return "PipelineColorBlendStateCreateInfo"; case StructureType::ePipelineDynamicStateCreateInfo: return "PipelineDynamicStateCreateInfo"; case StructureType::eGraphicsPipelineCreateInfo: return "GraphicsPipelineCreateInfo"; case StructureType::eComputePipelineCreateInfo: return "ComputePipelineCreateInfo"; case StructureType::ePipelineLayoutCreateInfo: return "PipelineLayoutCreateInfo"; case StructureType::eSamplerCreateInfo: return "SamplerCreateInfo"; case StructureType::eDescriptorSetLayoutCreateInfo: return "DescriptorSetLayoutCreateInfo"; case StructureType::eDescriptorPoolCreateInfo: return "DescriptorPoolCreateInfo"; case StructureType::eDescriptorSetAllocateInfo: return "DescriptorSetAllocateInfo"; case StructureType::eWriteDescriptorSet: return "WriteDescriptorSet"; case StructureType::eCopyDescriptorSet: return "CopyDescriptorSet"; case StructureType::eFramebufferCreateInfo: return "FramebufferCreateInfo"; case StructureType::eRenderPassCreateInfo: return "RenderPassCreateInfo"; case StructureType::eCommandPoolCreateInfo: return "CommandPoolCreateInfo"; case StructureType::eCommandBufferAllocateInfo: return "CommandBufferAllocateInfo"; case StructureType::eCommandBufferInheritanceInfo: return "CommandBufferInheritanceInfo"; case StructureType::eCommandBufferBeginInfo: return "CommandBufferBeginInfo"; case StructureType::eRenderPassBeginInfo: return "RenderPassBeginInfo"; case StructureType::eBufferMemoryBarrier: return "BufferMemoryBarrier"; case StructureType::eImageMemoryBarrier: return "ImageMemoryBarrier"; case StructureType::eMemoryBarrier: return "MemoryBarrier"; case StructureType::eLoaderInstanceCreateInfo: return "LoaderInstanceCreateInfo"; case StructureType::eLoaderDeviceCreateInfo: return "LoaderDeviceCreateInfo"; case StructureType::eSwapchainCreateInfoKHR: return "SwapchainCreateInfoKHR"; case StructureType::ePresentInfoKHR: return "PresentInfoKHR"; case StructureType::eDisplayModeCreateInfoKHR: return "DisplayModeCreateInfoKHR"; case StructureType::eDisplaySurfaceCreateInfoKHR: return "DisplaySurfaceCreateInfoKHR"; case StructureType::eDisplayPresentInfoKHR: return "DisplayPresentInfoKHR"; case StructureType::eXlibSurfaceCreateInfoKHR: return "XlibSurfaceCreateInfoKHR"; case StructureType::eXcbSurfaceCreateInfoKHR: return "XcbSurfaceCreateInfoKHR"; case StructureType::eWaylandSurfaceCreateInfoKHR: return "WaylandSurfaceCreateInfoKHR"; case StructureType::eMirSurfaceCreateInfoKHR: return "MirSurfaceCreateInfoKHR"; case StructureType::eAndroidSurfaceCreateInfoKHR: return "AndroidSurfaceCreateInfoKHR"; case StructureType::eWin32SurfaceCreateInfoKHR: return "Win32SurfaceCreateInfoKHR"; case StructureType::eDebugReportCallbackCreateInfoEXT: return "DebugReportCallbackCreateInfoEXT"; case StructureType::ePipelineRasterizationStateRasterizationOrderAMD: return "PipelineRasterizationStateRasterizationOrderAMD"; case StructureType::eDebugMarkerObjectNameInfoEXT: return "DebugMarkerObjectNameInfoEXT"; case StructureType::eDebugMarkerObjectTagInfoEXT: return "DebugMarkerObjectTagInfoEXT"; case StructureType::eDebugMarkerMarkerInfoEXT: return "DebugMarkerMarkerInfoEXT"; case StructureType::eDedicatedAllocationImageCreateInfoNV: return "DedicatedAllocationImageCreateInfoNV"; case StructureType::eDedicatedAllocationBufferCreateInfoNV: return "DedicatedAllocationBufferCreateInfoNV"; case StructureType::eDedicatedAllocationMemoryAllocateInfoNV: return "DedicatedAllocationMemoryAllocateInfoNV"; case StructureType::eRenderPassMultiviewCreateInfoKHX: return "RenderPassMultiviewCreateInfoKHX"; case StructureType::ePhysicalDeviceMultiviewFeaturesKHX: return "PhysicalDeviceMultiviewFeaturesKHX"; case StructureType::ePhysicalDeviceMultiviewPropertiesKHX: return "PhysicalDeviceMultiviewPropertiesKHX"; case StructureType::eExternalMemoryImageCreateInfoNV: return "ExternalMemoryImageCreateInfoNV"; case StructureType::eExportMemoryAllocateInfoNV: return "ExportMemoryAllocateInfoNV"; case StructureType::eImportMemoryWin32HandleInfoNV: return "ImportMemoryWin32HandleInfoNV"; case StructureType::eExportMemoryWin32HandleInfoNV: return "ExportMemoryWin32HandleInfoNV"; case StructureType::eWin32KeyedMutexAcquireReleaseInfoNV: return "Win32KeyedMutexAcquireReleaseInfoNV"; case StructureType::ePhysicalDeviceFeatures2KHR: return "PhysicalDeviceFeatures2KHR"; case StructureType::ePhysicalDeviceProperties2KHR: return "PhysicalDeviceProperties2KHR"; case StructureType::eFormatProperties2KHR: return "FormatProperties2KHR"; case StructureType::eImageFormatProperties2KHR: return "ImageFormatProperties2KHR"; case StructureType::ePhysicalDeviceImageFormatInfo2KHR: return "PhysicalDeviceImageFormatInfo2KHR"; case StructureType::eQueueFamilyProperties2KHR: return "QueueFamilyProperties2KHR"; case StructureType::ePhysicalDeviceMemoryProperties2KHR: return "PhysicalDeviceMemoryProperties2KHR"; case StructureType::eSparseImageFormatProperties2KHR: return "SparseImageFormatProperties2KHR"; case StructureType::ePhysicalDeviceSparseImageFormatInfo2KHR: return "PhysicalDeviceSparseImageFormatInfo2KHR"; case StructureType::eMemoryAllocateFlagsInfoKHX: return "MemoryAllocateFlagsInfoKHX"; case StructureType::eBindBufferMemoryInfoKHX: return "BindBufferMemoryInfoKHX"; case StructureType::eBindImageMemoryInfoKHX: return "BindImageMemoryInfoKHX"; case StructureType::eDeviceGroupRenderPassBeginInfoKHX: return "DeviceGroupRenderPassBeginInfoKHX"; case StructureType::eDeviceGroupCommandBufferBeginInfoKHX: return "DeviceGroupCommandBufferBeginInfoKHX"; case StructureType::eDeviceGroupSubmitInfoKHX: return "DeviceGroupSubmitInfoKHX"; case StructureType::eDeviceGroupBindSparseInfoKHX: return "DeviceGroupBindSparseInfoKHX"; case StructureType::eDeviceGroupPresentCapabilitiesKHX: return "DeviceGroupPresentCapabilitiesKHX"; case StructureType::eImageSwapchainCreateInfoKHX: return "ImageSwapchainCreateInfoKHX"; case StructureType::eBindImageMemorySwapchainInfoKHX: return "BindImageMemorySwapchainInfoKHX"; case StructureType::eAcquireNextImageInfoKHX: return "AcquireNextImageInfoKHX"; case StructureType::eDeviceGroupPresentInfoKHX: return "DeviceGroupPresentInfoKHX"; case StructureType::eDeviceGroupSwapchainCreateInfoKHX: return "DeviceGroupSwapchainCreateInfoKHX"; case StructureType::eValidationFlagsEXT: return "ValidationFlagsEXT"; case StructureType::eViSurfaceCreateInfoNN: return "ViSurfaceCreateInfoNN"; case StructureType::ePhysicalDeviceGroupPropertiesKHX: return "PhysicalDeviceGroupPropertiesKHX"; case StructureType::eDeviceGroupDeviceCreateInfoKHX: return "DeviceGroupDeviceCreateInfoKHX"; case StructureType::ePhysicalDeviceExternalImageFormatInfoKHX: return "PhysicalDeviceExternalImageFormatInfoKHX"; case StructureType::eExternalImageFormatPropertiesKHX: return "ExternalImageFormatPropertiesKHX"; case StructureType::ePhysicalDeviceExternalBufferInfoKHX: return "PhysicalDeviceExternalBufferInfoKHX"; case StructureType::eExternalBufferPropertiesKHX: return "ExternalBufferPropertiesKHX"; case StructureType::ePhysicalDeviceIdPropertiesKHX: return "PhysicalDeviceIdPropertiesKHX"; case StructureType::eExternalMemoryBufferCreateInfoKHX: return "ExternalMemoryBufferCreateInfoKHX"; case StructureType::eExternalMemoryImageCreateInfoKHX: return "ExternalMemoryImageCreateInfoKHX"; case StructureType::eExportMemoryAllocateInfoKHX: return "ExportMemoryAllocateInfoKHX"; case StructureType::eImportMemoryWin32HandleInfoKHX: return "ImportMemoryWin32HandleInfoKHX"; case StructureType::eExportMemoryWin32HandleInfoKHX: return "ExportMemoryWin32HandleInfoKHX"; case StructureType::eMemoryWin32HandlePropertiesKHX: return "MemoryWin32HandlePropertiesKHX"; case StructureType::eImportMemoryFdInfoKHX: return "ImportMemoryFdInfoKHX"; case StructureType::eMemoryFdPropertiesKHX: return "MemoryFdPropertiesKHX"; case StructureType::eWin32KeyedMutexAcquireReleaseInfoKHX: return "Win32KeyedMutexAcquireReleaseInfoKHX"; case StructureType::ePhysicalDeviceExternalSemaphoreInfoKHX: return "PhysicalDeviceExternalSemaphoreInfoKHX"; case StructureType::eExternalSemaphorePropertiesKHX: return "ExternalSemaphorePropertiesKHX"; case StructureType::eExportSemaphoreCreateInfoKHX: return "ExportSemaphoreCreateInfoKHX"; case StructureType::eImportSemaphoreWin32HandleInfoKHX: return "ImportSemaphoreWin32HandleInfoKHX"; case StructureType::eExportSemaphoreWin32HandleInfoKHX: return "ExportSemaphoreWin32HandleInfoKHX"; case StructureType::eD3D12FenceSubmitInfoKHX: return "D3D12FenceSubmitInfoKHX"; case StructureType::eImportSemaphoreFdInfoKHX: return "ImportSemaphoreFdInfoKHX"; case StructureType::ePhysicalDevicePushDescriptorPropertiesKHR: return "PhysicalDevicePushDescriptorPropertiesKHR"; case StructureType::ePresentRegionsKHR: return "PresentRegionsKHR"; case StructureType::eDescriptorUpdateTemplateCreateInfoKHR: return "DescriptorUpdateTemplateCreateInfoKHR"; case StructureType::eObjectTableCreateInfoNVX: return "ObjectTableCreateInfoNVX"; case StructureType::eIndirectCommandsLayoutCreateInfoNVX: return "IndirectCommandsLayoutCreateInfoNVX"; case StructureType::eCmdProcessCommandsInfoNVX: return "CmdProcessCommandsInfoNVX"; case StructureType::eCmdReserveSpaceForCommandsInfoNVX: return "CmdReserveSpaceForCommandsInfoNVX"; case StructureType::eDeviceGeneratedCommandsLimitsNVX: return "DeviceGeneratedCommandsLimitsNVX"; case StructureType::eDeviceGeneratedCommandsFeaturesNVX: return "DeviceGeneratedCommandsFeaturesNVX"; case StructureType::ePipelineViewportWScalingStateCreateInfoNV: return "PipelineViewportWScalingStateCreateInfoNV"; case StructureType::eSurfaceCapabilities2EXT: return "SurfaceCapabilities2EXT"; case StructureType::eDisplayPowerInfoEXT: return "DisplayPowerInfoEXT"; case StructureType::eDeviceEventInfoEXT: return "DeviceEventInfoEXT"; case StructureType::eDisplayEventInfoEXT: return "DisplayEventInfoEXT"; case StructureType::eSwapchainCounterCreateInfoEXT: return "SwapchainCounterCreateInfoEXT"; case StructureType::ePresentTimesInfoGOOGLE: return "PresentTimesInfoGOOGLE"; case StructureType::ePhysicalDeviceMultiviewPerViewAttributesPropertiesNVX: return "PhysicalDeviceMultiviewPerViewAttributesPropertiesNVX"; case StructureType::ePipelineViewportSwizzleStateCreateInfoNV: return "PipelineViewportSwizzleStateCreateInfoNV"; case StructureType::ePhysicalDeviceDiscardRectanglePropertiesEXT: return "PhysicalDeviceDiscardRectanglePropertiesEXT"; case StructureType::ePipelineDiscardRectangleStateCreateInfoEXT: return "PipelineDiscardRectangleStateCreateInfoEXT"; case StructureType::eHdrMetadataEXT: return "HdrMetadataEXT"; case StructureType::eSharedPresentSurfaceCapabilitiesKHR: return "SharedPresentSurfaceCapabilitiesKHR"; case StructureType::ePhysicalDeviceSurfaceInfo2KHR: return "PhysicalDeviceSurfaceInfo2KHR"; case StructureType::eSurfaceCapabilities2KHR: return "SurfaceCapabilities2KHR"; case StructureType::eSurfaceFormat2KHR: return "SurfaceFormat2KHR"; case StructureType::eIosSurfaceCreateInfoMVK: return "IosSurfaceCreateInfoMVK"; case StructureType::eMacosSurfaceCreateInfoMVK: return "MacosSurfaceCreateInfoMVK"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(SubpassContents value) { switch (value) { case SubpassContents::eInline: return "Inline"; case SubpassContents::eSecondaryCommandBuffers: return "SecondaryCommandBuffers"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(DynamicState value) { switch (value) { case DynamicState::eViewport: return "Viewport"; case DynamicState::eScissor: return "Scissor"; case DynamicState::eLineWidth: return "LineWidth"; case DynamicState::eDepthBias: return "DepthBias"; case DynamicState::eBlendConstants: return "BlendConstants"; case DynamicState::eDepthBounds: return "DepthBounds"; case DynamicState::eStencilCompareMask: return "StencilCompareMask"; case DynamicState::eStencilWriteMask: return "StencilWriteMask"; case DynamicState::eStencilReference: return "StencilReference"; case DynamicState::eViewportWScalingNV: return "ViewportWScalingNV"; case DynamicState::eDiscardRectangleEXT: return "DiscardRectangleEXT"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(DescriptorUpdateTemplateTypeKHR value) { switch (value) { case DescriptorUpdateTemplateTypeKHR::eDescriptorSet: return "DescriptorSet"; case DescriptorUpdateTemplateTypeKHR::ePushDescriptors: return "PushDescriptors"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(ObjectType value) { switch (value) { case ObjectType::eUnknown: return "Unknown"; case ObjectType::eInstance: return "Instance"; case ObjectType::ePhysicalDevice: return "PhysicalDevice"; case ObjectType::eDevice: return "Device"; case ObjectType::eQueue: return "Queue"; case ObjectType::eSemaphore: return "Semaphore"; case ObjectType::eCommandBuffer: return "CommandBuffer"; case ObjectType::eFence: return "Fence"; case ObjectType::eDeviceMemory: return "DeviceMemory"; case ObjectType::eBuffer: return "Buffer"; case ObjectType::eImage: return "Image"; case ObjectType::eEvent: return "Event"; case ObjectType::eQueryPool: return "QueryPool"; case ObjectType::eBufferView: return "BufferView"; case ObjectType::eImageView: return "ImageView"; case ObjectType::eShaderModule: return "ShaderModule"; case ObjectType::ePipelineCache: return "PipelineCache"; case ObjectType::ePipelineLayout: return "PipelineLayout"; case ObjectType::eRenderPass: return "RenderPass"; case ObjectType::ePipeline: return "Pipeline"; case ObjectType::eDescriptorSetLayout: return "DescriptorSetLayout"; case ObjectType::eSampler: return "Sampler"; case ObjectType::eDescriptorPool: return "DescriptorPool"; case ObjectType::eDescriptorSet: return "DescriptorSet"; case ObjectType::eFramebuffer: return "Framebuffer"; case ObjectType::eCommandPool: return "CommandPool"; case ObjectType::eSurfaceKHR: return "SurfaceKHR"; case ObjectType::eSwapchainKHR: return "SwapchainKHR"; case ObjectType::eDisplayKHR: return "DisplayKHR"; case ObjectType::eDisplayModeKHR: return "DisplayModeKHR"; case ObjectType::eDebugReportCallbackEXT: return "DebugReportCallbackEXT"; case ObjectType::eDescriptorUpdateTemplateKHR: return "DescriptorUpdateTemplateKHR"; case ObjectType::eObjectTableNVX: return "ObjectTableNVX"; case ObjectType::eIndirectCommandsLayoutNVX: return "IndirectCommandsLayoutNVX"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(QueueFlagBits value) { switch (value) { case QueueFlagBits::eGraphics: return "Graphics"; case QueueFlagBits::eCompute: return "Compute"; case QueueFlagBits::eTransfer: return "Transfer"; case QueueFlagBits::eSparseBinding: return "SparseBinding"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(QueueFlags value) { if (!value) return "{}"; std::string result; if (value & QueueFlagBits::eGraphics) result += "Graphics | "; if (value & QueueFlagBits::eCompute) result += "Compute | "; if (value & QueueFlagBits::eTransfer) result += "Transfer | "; if (value & QueueFlagBits::eSparseBinding) result += "SparseBinding | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(MemoryPropertyFlagBits value) { switch (value) { case MemoryPropertyFlagBits::eDeviceLocal: return "DeviceLocal"; case MemoryPropertyFlagBits::eHostVisible: return "HostVisible"; case MemoryPropertyFlagBits::eHostCoherent: return "HostCoherent"; case MemoryPropertyFlagBits::eHostCached: return "HostCached"; case MemoryPropertyFlagBits::eLazilyAllocated: return "LazilyAllocated"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(MemoryPropertyFlags value) { if (!value) return "{}"; std::string result; if (value & MemoryPropertyFlagBits::eDeviceLocal) result += "DeviceLocal | "; if (value & MemoryPropertyFlagBits::eHostVisible) result += "HostVisible | "; if (value & MemoryPropertyFlagBits::eHostCoherent) result += "HostCoherent | "; if (value & MemoryPropertyFlagBits::eHostCached) result += "HostCached | "; if (value & MemoryPropertyFlagBits::eLazilyAllocated) result += "LazilyAllocated | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(MemoryHeapFlagBits value) { switch (value) { case MemoryHeapFlagBits::eDeviceLocal: return "DeviceLocal"; case MemoryHeapFlagBits::eMultiInstanceKHX: return "MultiInstanceKHX"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(MemoryHeapFlags value) { if (!value) return "{}"; std::string result; if (value & MemoryHeapFlagBits::eDeviceLocal) result += "DeviceLocal | "; if (value & MemoryHeapFlagBits::eMultiInstanceKHX) result += "MultiInstanceKHX | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(AccessFlagBits value) { switch (value) { case AccessFlagBits::eIndirectCommandRead: return "IndirectCommandRead"; case AccessFlagBits::eIndexRead: return "IndexRead"; case AccessFlagBits::eVertexAttributeRead: return "VertexAttributeRead"; case AccessFlagBits::eUniformRead: return "UniformRead"; case AccessFlagBits::eInputAttachmentRead: return "InputAttachmentRead"; case AccessFlagBits::eShaderRead: return "ShaderRead"; case AccessFlagBits::eShaderWrite: return "ShaderWrite"; case AccessFlagBits::eColorAttachmentRead: return "ColorAttachmentRead"; case AccessFlagBits::eColorAttachmentWrite: return "ColorAttachmentWrite"; case AccessFlagBits::eDepthStencilAttachmentRead: return "DepthStencilAttachmentRead"; case AccessFlagBits::eDepthStencilAttachmentWrite: return "DepthStencilAttachmentWrite"; case AccessFlagBits::eTransferRead: return "TransferRead"; case AccessFlagBits::eTransferWrite: return "TransferWrite"; case AccessFlagBits::eHostRead: return "HostRead"; case AccessFlagBits::eHostWrite: return "HostWrite"; case AccessFlagBits::eMemoryRead: return "MemoryRead"; case AccessFlagBits::eMemoryWrite: return "MemoryWrite"; case AccessFlagBits::eCommandProcessReadNVX: return "CommandProcessReadNVX"; case AccessFlagBits::eCommandProcessWriteNVX: return "CommandProcessWriteNVX"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(AccessFlags value) { if (!value) return "{}"; std::string result; if (value & AccessFlagBits::eIndirectCommandRead) result += "IndirectCommandRead | "; if (value & AccessFlagBits::eIndexRead) result += "IndexRead | "; if (value & AccessFlagBits::eVertexAttributeRead) result += "VertexAttributeRead | "; if (value & AccessFlagBits::eUniformRead) result += "UniformRead | "; if (value & AccessFlagBits::eInputAttachmentRead) result += "InputAttachmentRead | "; if (value & AccessFlagBits::eShaderRead) result += "ShaderRead | "; if (value & AccessFlagBits::eShaderWrite) result += "ShaderWrite | "; if (value & AccessFlagBits::eColorAttachmentRead) result += "ColorAttachmentRead | "; if (value & AccessFlagBits::eColorAttachmentWrite) result += "ColorAttachmentWrite | "; if (value & AccessFlagBits::eDepthStencilAttachmentRead) result += "DepthStencilAttachmentRead | "; if (value & AccessFlagBits::eDepthStencilAttachmentWrite) result += "DepthStencilAttachmentWrite | "; if (value & AccessFlagBits::eTransferRead) result += "TransferRead | "; if (value & AccessFlagBits::eTransferWrite) result += "TransferWrite | "; if (value & AccessFlagBits::eHostRead) result += "HostRead | "; if (value & AccessFlagBits::eHostWrite) result += "HostWrite | "; if (value & AccessFlagBits::eMemoryRead) result += "MemoryRead | "; if (value & AccessFlagBits::eMemoryWrite) result += "MemoryWrite | "; if (value & AccessFlagBits::eCommandProcessReadNVX) result += "CommandProcessReadNVX | "; if (value & AccessFlagBits::eCommandProcessWriteNVX) result += "CommandProcessWriteNVX | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(BufferUsageFlagBits value) { switch (value) { case BufferUsageFlagBits::eTransferSrc: return "TransferSrc"; case BufferUsageFlagBits::eTransferDst: return "TransferDst"; case BufferUsageFlagBits::eUniformTexelBuffer: return "UniformTexelBuffer"; case BufferUsageFlagBits::eStorageTexelBuffer: return "StorageTexelBuffer"; case BufferUsageFlagBits::eUniformBuffer: return "UniformBuffer"; case BufferUsageFlagBits::eStorageBuffer: return "StorageBuffer"; case BufferUsageFlagBits::eIndexBuffer: return "IndexBuffer"; case BufferUsageFlagBits::eVertexBuffer: return "VertexBuffer"; case BufferUsageFlagBits::eIndirectBuffer: return "IndirectBuffer"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(BufferUsageFlags value) { if (!value) return "{}"; std::string result; if (value & BufferUsageFlagBits::eTransferSrc) result += "TransferSrc | "; if (value & BufferUsageFlagBits::eTransferDst) result += "TransferDst | "; if (value & BufferUsageFlagBits::eUniformTexelBuffer) result += "UniformTexelBuffer | "; if (value & BufferUsageFlagBits::eStorageTexelBuffer) result += "StorageTexelBuffer | "; if (value & BufferUsageFlagBits::eUniformBuffer) result += "UniformBuffer | "; if (value & BufferUsageFlagBits::eStorageBuffer) result += "StorageBuffer | "; if (value & BufferUsageFlagBits::eIndexBuffer) result += "IndexBuffer | "; if (value & BufferUsageFlagBits::eVertexBuffer) result += "VertexBuffer | "; if (value & BufferUsageFlagBits::eIndirectBuffer) result += "IndirectBuffer | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(BufferCreateFlagBits value) { switch (value) { case BufferCreateFlagBits::eSparseBinding: return "SparseBinding"; case BufferCreateFlagBits::eSparseResidency: return "SparseResidency"; case BufferCreateFlagBits::eSparseAliased: return "SparseAliased"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(BufferCreateFlags value) { if (!value) return "{}"; std::string result; if (value & BufferCreateFlagBits::eSparseBinding) result += "SparseBinding | "; if (value & BufferCreateFlagBits::eSparseResidency) result += "SparseResidency | "; if (value & BufferCreateFlagBits::eSparseAliased) result += "SparseAliased | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(ShaderStageFlagBits value) { switch (value) { case ShaderStageFlagBits::eVertex: return "Vertex"; case ShaderStageFlagBits::eTessellationControl: return "TessellationControl"; case ShaderStageFlagBits::eTessellationEvaluation: return "TessellationEvaluation"; case ShaderStageFlagBits::eGeometry: return "Geometry"; case ShaderStageFlagBits::eFragment: return "Fragment"; case ShaderStageFlagBits::eCompute: return "Compute"; case ShaderStageFlagBits::eAllGraphics: return "AllGraphics"; case ShaderStageFlagBits::eAll: return "All"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(ShaderStageFlags value) { if (!value) return "{}"; std::string result; if (value & ShaderStageFlagBits::eVertex) result += "Vertex | "; if (value & ShaderStageFlagBits::eTessellationControl) result += "TessellationControl | "; if (value & ShaderStageFlagBits::eTessellationEvaluation) result += "TessellationEvaluation | "; if (value & ShaderStageFlagBits::eGeometry) result += "Geometry | "; if (value & ShaderStageFlagBits::eFragment) result += "Fragment | "; if (value & ShaderStageFlagBits::eCompute) result += "Compute | "; if (value & ShaderStageFlagBits::eAllGraphics) result += "AllGraphics | "; if (value & ShaderStageFlagBits::eAll) result += "All | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(ImageUsageFlagBits value) { switch (value) { case ImageUsageFlagBits::eTransferSrc: return "TransferSrc"; case ImageUsageFlagBits::eTransferDst: return "TransferDst"; case ImageUsageFlagBits::eSampled: return "Sampled"; case ImageUsageFlagBits::eStorage: return "Storage"; case ImageUsageFlagBits::eColorAttachment: return "ColorAttachment"; case ImageUsageFlagBits::eDepthStencilAttachment: return "DepthStencilAttachment"; case ImageUsageFlagBits::eTransientAttachment: return "TransientAttachment"; case ImageUsageFlagBits::eInputAttachment: return "InputAttachment"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(ImageUsageFlags value) { if (!value) return "{}"; std::string result; if (value & ImageUsageFlagBits::eTransferSrc) result += "TransferSrc | "; if (value & ImageUsageFlagBits::eTransferDst) result += "TransferDst | "; if (value & ImageUsageFlagBits::eSampled) result += "Sampled | "; if (value & ImageUsageFlagBits::eStorage) result += "Storage | "; if (value & ImageUsageFlagBits::eColorAttachment) result += "ColorAttachment | "; if (value & ImageUsageFlagBits::eDepthStencilAttachment) result += "DepthStencilAttachment | "; if (value & ImageUsageFlagBits::eTransientAttachment) result += "TransientAttachment | "; if (value & ImageUsageFlagBits::eInputAttachment) result += "InputAttachment | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(ImageCreateFlagBits value) { switch (value) { case ImageCreateFlagBits::eSparseBinding: return "SparseBinding"; case ImageCreateFlagBits::eSparseResidency: return "SparseResidency"; case ImageCreateFlagBits::eSparseAliased: return "SparseAliased"; case ImageCreateFlagBits::eMutableFormat: return "MutableFormat"; case ImageCreateFlagBits::eCubeCompatible: return "CubeCompatible"; case ImageCreateFlagBits::eBindSfrKHX: return "BindSfrKHX"; case ImageCreateFlagBits::e2DArrayCompatibleKHR: return "2DArrayCompatibleKHR"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(ImageCreateFlags value) { if (!value) return "{}"; std::string result; if (value & ImageCreateFlagBits::eSparseBinding) result += "SparseBinding | "; if (value & ImageCreateFlagBits::eSparseResidency) result += "SparseResidency | "; if (value & ImageCreateFlagBits::eSparseAliased) result += "SparseAliased | "; if (value & ImageCreateFlagBits::eMutableFormat) result += "MutableFormat | "; if (value & ImageCreateFlagBits::eCubeCompatible) result += "CubeCompatible | "; if (value & ImageCreateFlagBits::eBindSfrKHX) result += "BindSfrKHX | "; if (value & ImageCreateFlagBits::e2DArrayCompatibleKHR) result += "2DArrayCompatibleKHR | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(PipelineCreateFlagBits value) { switch (value) { case PipelineCreateFlagBits::eDisableOptimization: return "DisableOptimization"; case PipelineCreateFlagBits::eAllowDerivatives: return "AllowDerivatives"; case PipelineCreateFlagBits::eDerivative: return "Derivative"; case PipelineCreateFlagBits::eViewIndexFromDeviceIndexKHX: return "ViewIndexFromDeviceIndexKHX"; case PipelineCreateFlagBits::eDispatchBaseKHX: return "DispatchBaseKHX"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(PipelineCreateFlags value) { if (!value) return "{}"; std::string result; if (value & PipelineCreateFlagBits::eDisableOptimization) result += "DisableOptimization | "; if (value & PipelineCreateFlagBits::eAllowDerivatives) result += "AllowDerivatives | "; if (value & PipelineCreateFlagBits::eDerivative) result += "Derivative | "; if (value & PipelineCreateFlagBits::eViewIndexFromDeviceIndexKHX) result += "ViewIndexFromDeviceIndexKHX | "; if (value & PipelineCreateFlagBits::eDispatchBaseKHX) result += "DispatchBaseKHX | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(ColorComponentFlagBits value) { switch (value) { case ColorComponentFlagBits::eR: return "R"; case ColorComponentFlagBits::eG: return "G"; case ColorComponentFlagBits::eB: return "B"; case ColorComponentFlagBits::eA: return "A"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(ColorComponentFlags value) { if (!value) return "{}"; std::string result; if (value & ColorComponentFlagBits::eR) result += "R | "; if (value & ColorComponentFlagBits::eG) result += "G | "; if (value & ColorComponentFlagBits::eB) result += "B | "; if (value & ColorComponentFlagBits::eA) result += "A | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(FenceCreateFlagBits value) { switch (value) { case FenceCreateFlagBits::eSignaled: return "Signaled"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(FenceCreateFlags value) { if (!value) return "{}"; std::string result; if (value & FenceCreateFlagBits::eSignaled) result += "Signaled | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(FormatFeatureFlagBits value) { switch (value) { case FormatFeatureFlagBits::eSampledImage: return "SampledImage"; case FormatFeatureFlagBits::eStorageImage: return "StorageImage"; case FormatFeatureFlagBits::eStorageImageAtomic: return "StorageImageAtomic"; case FormatFeatureFlagBits::eUniformTexelBuffer: return "UniformTexelBuffer"; case FormatFeatureFlagBits::eStorageTexelBuffer: return "StorageTexelBuffer"; case FormatFeatureFlagBits::eStorageTexelBufferAtomic: return "StorageTexelBufferAtomic"; case FormatFeatureFlagBits::eVertexBuffer: return "VertexBuffer"; case FormatFeatureFlagBits::eColorAttachment: return "ColorAttachment"; case FormatFeatureFlagBits::eColorAttachmentBlend: return "ColorAttachmentBlend"; case FormatFeatureFlagBits::eDepthStencilAttachment: return "DepthStencilAttachment"; case FormatFeatureFlagBits::eBlitSrc: return "BlitSrc"; case FormatFeatureFlagBits::eBlitDst: return "BlitDst"; case FormatFeatureFlagBits::eSampledImageFilterLinear: return "SampledImageFilterLinear"; case FormatFeatureFlagBits::eSampledImageFilterCubicIMG: return "SampledImageFilterCubicIMG"; case FormatFeatureFlagBits::eTransferSrcKHR: return "TransferSrcKHR"; case FormatFeatureFlagBits::eTransferDstKHR: return "TransferDstKHR"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(FormatFeatureFlags value) { if (!value) return "{}"; std::string result; if (value & FormatFeatureFlagBits::eSampledImage) result += "SampledImage | "; if (value & FormatFeatureFlagBits::eStorageImage) result += "StorageImage | "; if (value & FormatFeatureFlagBits::eStorageImageAtomic) result += "StorageImageAtomic | "; if (value & FormatFeatureFlagBits::eUniformTexelBuffer) result += "UniformTexelBuffer | "; if (value & FormatFeatureFlagBits::eStorageTexelBuffer) result += "StorageTexelBuffer | "; if (value & FormatFeatureFlagBits::eStorageTexelBufferAtomic) result += "StorageTexelBufferAtomic | "; if (value & FormatFeatureFlagBits::eVertexBuffer) result += "VertexBuffer | "; if (value & FormatFeatureFlagBits::eColorAttachment) result += "ColorAttachment | "; if (value & FormatFeatureFlagBits::eColorAttachmentBlend) result += "ColorAttachmentBlend | "; if (value & FormatFeatureFlagBits::eDepthStencilAttachment) result += "DepthStencilAttachment | "; if (value & FormatFeatureFlagBits::eBlitSrc) result += "BlitSrc | "; if (value & FormatFeatureFlagBits::eBlitDst) result += "BlitDst | "; if (value & FormatFeatureFlagBits::eSampledImageFilterLinear) result += "SampledImageFilterLinear | "; if (value & FormatFeatureFlagBits::eSampledImageFilterCubicIMG) result += "SampledImageFilterCubicIMG | "; if (value & FormatFeatureFlagBits::eTransferSrcKHR) result += "TransferSrcKHR | "; if (value & FormatFeatureFlagBits::eTransferDstKHR) result += "TransferDstKHR | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(QueryControlFlagBits value) { switch (value) { case QueryControlFlagBits::ePrecise: return "Precise"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(QueryControlFlags value) { if (!value) return "{}"; std::string result; if (value & QueryControlFlagBits::ePrecise) result += "Precise | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(QueryResultFlagBits value) { switch (value) { case QueryResultFlagBits::e64: return "64"; case QueryResultFlagBits::eWait: return "Wait"; case QueryResultFlagBits::eWithAvailability: return "WithAvailability"; case QueryResultFlagBits::ePartial: return "Partial"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(QueryResultFlags value) { if (!value) return "{}"; std::string result; if (value & QueryResultFlagBits::e64) result += "64 | "; if (value & QueryResultFlagBits::eWait) result += "Wait | "; if (value & QueryResultFlagBits::eWithAvailability) result += "WithAvailability | "; if (value & QueryResultFlagBits::ePartial) result += "Partial | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(CommandBufferUsageFlagBits value) { switch (value) { case CommandBufferUsageFlagBits::eOneTimeSubmit: return "OneTimeSubmit"; case CommandBufferUsageFlagBits::eRenderPassContinue: return "RenderPassContinue"; case CommandBufferUsageFlagBits::eSimultaneousUse: return "SimultaneousUse"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(CommandBufferUsageFlags value) { if (!value) return "{}"; std::string result; if (value & CommandBufferUsageFlagBits::eOneTimeSubmit) result += "OneTimeSubmit | "; if (value & CommandBufferUsageFlagBits::eRenderPassContinue) result += "RenderPassContinue | "; if (value & CommandBufferUsageFlagBits::eSimultaneousUse) result += "SimultaneousUse | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(QueryPipelineStatisticFlagBits value) { switch (value) { case QueryPipelineStatisticFlagBits::eInputAssemblyVertices: return "InputAssemblyVertices"; case QueryPipelineStatisticFlagBits::eInputAssemblyPrimitives: return "InputAssemblyPrimitives"; case QueryPipelineStatisticFlagBits::eVertexShaderInvocations: return "VertexShaderInvocations"; case QueryPipelineStatisticFlagBits::eGeometryShaderInvocations: return "GeometryShaderInvocations"; case QueryPipelineStatisticFlagBits::eGeometryShaderPrimitives: return "GeometryShaderPrimitives"; case QueryPipelineStatisticFlagBits::eClippingInvocations: return "ClippingInvocations"; case QueryPipelineStatisticFlagBits::eClippingPrimitives: return "ClippingPrimitives"; case QueryPipelineStatisticFlagBits::eFragmentShaderInvocations: return "FragmentShaderInvocations"; case QueryPipelineStatisticFlagBits::eTessellationControlShaderPatches: return "TessellationControlShaderPatches"; case QueryPipelineStatisticFlagBits::eTessellationEvaluationShaderInvocations: return "TessellationEvaluationShaderInvocations"; case QueryPipelineStatisticFlagBits::eComputeShaderInvocations: return "ComputeShaderInvocations"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(QueryPipelineStatisticFlags value) { if (!value) return "{}"; std::string result; if (value & QueryPipelineStatisticFlagBits::eInputAssemblyVertices) result += "InputAssemblyVertices | "; if (value & QueryPipelineStatisticFlagBits::eInputAssemblyPrimitives) result += "InputAssemblyPrimitives | "; if (value & QueryPipelineStatisticFlagBits::eVertexShaderInvocations) result += "VertexShaderInvocations | "; if (value & QueryPipelineStatisticFlagBits::eGeometryShaderInvocations) result += "GeometryShaderInvocations | "; if (value & QueryPipelineStatisticFlagBits::eGeometryShaderPrimitives) result += "GeometryShaderPrimitives | "; if (value & QueryPipelineStatisticFlagBits::eClippingInvocations) result += "ClippingInvocations | "; if (value & QueryPipelineStatisticFlagBits::eClippingPrimitives) result += "ClippingPrimitives | "; if (value & QueryPipelineStatisticFlagBits::eFragmentShaderInvocations) result += "FragmentShaderInvocations | "; if (value & QueryPipelineStatisticFlagBits::eTessellationControlShaderPatches) result += "TessellationControlShaderPatches | "; if (value & QueryPipelineStatisticFlagBits::eTessellationEvaluationShaderInvocations) result += "TessellationEvaluationShaderInvocations | "; if (value & QueryPipelineStatisticFlagBits::eComputeShaderInvocations) result += "ComputeShaderInvocations | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(ImageAspectFlagBits value) { switch (value) { case ImageAspectFlagBits::eColor: return "Color"; case ImageAspectFlagBits::eDepth: return "Depth"; case ImageAspectFlagBits::eStencil: return "Stencil"; case ImageAspectFlagBits::eMetadata: return "Metadata"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(ImageAspectFlags value) { if (!value) return "{}"; std::string result; if (value & ImageAspectFlagBits::eColor) result += "Color | "; if (value & ImageAspectFlagBits::eDepth) result += "Depth | "; if (value & ImageAspectFlagBits::eStencil) result += "Stencil | "; if (value & ImageAspectFlagBits::eMetadata) result += "Metadata | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(SparseImageFormatFlagBits value) { switch (value) { case SparseImageFormatFlagBits::eSingleMiptail: return "SingleMiptail"; case SparseImageFormatFlagBits::eAlignedMipSize: return "AlignedMipSize"; case SparseImageFormatFlagBits::eNonstandardBlockSize: return "NonstandardBlockSize"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(SparseImageFormatFlags value) { if (!value) return "{}"; std::string result; if (value & SparseImageFormatFlagBits::eSingleMiptail) result += "SingleMiptail | "; if (value & SparseImageFormatFlagBits::eAlignedMipSize) result += "AlignedMipSize | "; if (value & SparseImageFormatFlagBits::eNonstandardBlockSize) result += "NonstandardBlockSize | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(SparseMemoryBindFlagBits value) { switch (value) { case SparseMemoryBindFlagBits::eMetadata: return "Metadata"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(SparseMemoryBindFlags value) { if (!value) return "{}"; std::string result; if (value & SparseMemoryBindFlagBits::eMetadata) result += "Metadata | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(PipelineStageFlagBits value) { switch (value) { case PipelineStageFlagBits::eTopOfPipe: return "TopOfPipe"; case PipelineStageFlagBits::eDrawIndirect: return "DrawIndirect"; case PipelineStageFlagBits::eVertexInput: return "VertexInput"; case PipelineStageFlagBits::eVertexShader: return "VertexShader"; case PipelineStageFlagBits::eTessellationControlShader: return "TessellationControlShader"; case PipelineStageFlagBits::eTessellationEvaluationShader: return "TessellationEvaluationShader"; case PipelineStageFlagBits::eGeometryShader: return "GeometryShader"; case PipelineStageFlagBits::eFragmentShader: return "FragmentShader"; case PipelineStageFlagBits::eEarlyFragmentTests: return "EarlyFragmentTests"; case PipelineStageFlagBits::eLateFragmentTests: return "LateFragmentTests"; case PipelineStageFlagBits::eColorAttachmentOutput: return "ColorAttachmentOutput"; case PipelineStageFlagBits::eComputeShader: return "ComputeShader"; case PipelineStageFlagBits::eTransfer: return "Transfer"; case PipelineStageFlagBits::eBottomOfPipe: return "BottomOfPipe"; case PipelineStageFlagBits::eHost: return "Host"; case PipelineStageFlagBits::eAllGraphics: return "AllGraphics"; case PipelineStageFlagBits::eAllCommands: return "AllCommands"; case PipelineStageFlagBits::eCommandProcessNVX: return "CommandProcessNVX"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(PipelineStageFlags value) { if (!value) return "{}"; std::string result; if (value & PipelineStageFlagBits::eTopOfPipe) result += "TopOfPipe | "; if (value & PipelineStageFlagBits::eDrawIndirect) result += "DrawIndirect | "; if (value & PipelineStageFlagBits::eVertexInput) result += "VertexInput | "; if (value & PipelineStageFlagBits::eVertexShader) result += "VertexShader | "; if (value & PipelineStageFlagBits::eTessellationControlShader) result += "TessellationControlShader | "; if (value & PipelineStageFlagBits::eTessellationEvaluationShader) result += "TessellationEvaluationShader | "; if (value & PipelineStageFlagBits::eGeometryShader) result += "GeometryShader | "; if (value & PipelineStageFlagBits::eFragmentShader) result += "FragmentShader | "; if (value & PipelineStageFlagBits::eEarlyFragmentTests) result += "EarlyFragmentTests | "; if (value & PipelineStageFlagBits::eLateFragmentTests) result += "LateFragmentTests | "; if (value & PipelineStageFlagBits::eColorAttachmentOutput) result += "ColorAttachmentOutput | "; if (value & PipelineStageFlagBits::eComputeShader) result += "ComputeShader | "; if (value & PipelineStageFlagBits::eTransfer) result += "Transfer | "; if (value & PipelineStageFlagBits::eBottomOfPipe) result += "BottomOfPipe | "; if (value & PipelineStageFlagBits::eHost) result += "Host | "; if (value & PipelineStageFlagBits::eAllGraphics) result += "AllGraphics | "; if (value & PipelineStageFlagBits::eAllCommands) result += "AllCommands | "; if (value & PipelineStageFlagBits::eCommandProcessNVX) result += "CommandProcessNVX | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(CommandPoolCreateFlagBits value) { switch (value) { case CommandPoolCreateFlagBits::eTransient: return "Transient"; case CommandPoolCreateFlagBits::eResetCommandBuffer: return "ResetCommandBuffer"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(CommandPoolCreateFlags value) { if (!value) return "{}"; std::string result; if (value & CommandPoolCreateFlagBits::eTransient) result += "Transient | "; if (value & CommandPoolCreateFlagBits::eResetCommandBuffer) result += "ResetCommandBuffer | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(CommandPoolResetFlagBits value) { switch (value) { case CommandPoolResetFlagBits::eReleaseResources: return "ReleaseResources"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(CommandPoolResetFlags value) { if (!value) return "{}"; std::string result; if (value & CommandPoolResetFlagBits::eReleaseResources) result += "ReleaseResources | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(CommandBufferResetFlagBits value) { switch (value) { case CommandBufferResetFlagBits::eReleaseResources: return "ReleaseResources"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(CommandBufferResetFlags value) { if (!value) return "{}"; std::string result; if (value & CommandBufferResetFlagBits::eReleaseResources) result += "ReleaseResources | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(SampleCountFlagBits value) { switch (value) { case SampleCountFlagBits::e1: return "1"; case SampleCountFlagBits::e2: return "2"; case SampleCountFlagBits::e4: return "4"; case SampleCountFlagBits::e8: return "8"; case SampleCountFlagBits::e16: return "16"; case SampleCountFlagBits::e32: return "32"; case SampleCountFlagBits::e64: return "64"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(SampleCountFlags value) { if (!value) return "{}"; std::string result; if (value & SampleCountFlagBits::e1) result += "1 | "; if (value & SampleCountFlagBits::e2) result += "2 | "; if (value & SampleCountFlagBits::e4) result += "4 | "; if (value & SampleCountFlagBits::e8) result += "8 | "; if (value & SampleCountFlagBits::e16) result += "16 | "; if (value & SampleCountFlagBits::e32) result += "32 | "; if (value & SampleCountFlagBits::e64) result += "64 | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(AttachmentDescriptionFlagBits value) { switch (value) { case AttachmentDescriptionFlagBits::eMayAlias: return "MayAlias"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(AttachmentDescriptionFlags value) { if (!value) return "{}"; std::string result; if (value & AttachmentDescriptionFlagBits::eMayAlias) result += "MayAlias | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(StencilFaceFlagBits value) { switch (value) { case StencilFaceFlagBits::eFront: return "Front"; case StencilFaceFlagBits::eBack: return "Back"; case StencilFaceFlagBits::eVkStencilFrontAndBack: return "VkStencilFrontAndBack"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(StencilFaceFlags value) { if (!value) return "{}"; std::string result; if (value & StencilFaceFlagBits::eFront) result += "Front | "; if (value & StencilFaceFlagBits::eBack) result += "Back | "; if (value & StencilFaceFlagBits::eVkStencilFrontAndBack) result += "VkStencilFrontAndBack | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(DescriptorPoolCreateFlagBits value) { switch (value) { case DescriptorPoolCreateFlagBits::eFreeDescriptorSet: return "FreeDescriptorSet"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(DescriptorPoolCreateFlags value) { if (!value) return "{}"; std::string result; if (value & DescriptorPoolCreateFlagBits::eFreeDescriptorSet) result += "FreeDescriptorSet | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(DependencyFlagBits value) { switch (value) { case DependencyFlagBits::eByRegion: return "ByRegion"; case DependencyFlagBits::eViewLocalKHX: return "ViewLocalKHX"; case DependencyFlagBits::eDeviceGroupKHX: return "DeviceGroupKHX"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(DependencyFlags value) { if (!value) return "{}"; std::string result; if (value & DependencyFlagBits::eByRegion) result += "ByRegion | "; if (value & DependencyFlagBits::eViewLocalKHX) result += "ViewLocalKHX | "; if (value & DependencyFlagBits::eDeviceGroupKHX) result += "DeviceGroupKHX | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(PresentModeKHR value) { switch (value) { case PresentModeKHR::eImmediate: return "Immediate"; case PresentModeKHR::eMailbox: return "Mailbox"; case PresentModeKHR::eFifo: return "Fifo"; case PresentModeKHR::eFifoRelaxed: return "FifoRelaxed"; case PresentModeKHR::eSharedDemandRefresh: return "SharedDemandRefresh"; case PresentModeKHR::eSharedContinuousRefresh: return "SharedContinuousRefresh"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(ColorSpaceKHR value) { switch (value) { case ColorSpaceKHR::eSrgbNonlinear: return "SrgbNonlinear"; case ColorSpaceKHR::eDisplayP3NonlinearEXT: return "DisplayP3NonlinearEXT"; case ColorSpaceKHR::eExtendedSrgbLinearEXT: return "ExtendedSrgbLinearEXT"; case ColorSpaceKHR::eDciP3LinearEXT: return "DciP3LinearEXT"; case ColorSpaceKHR::eDciP3NonlinearEXT: return "DciP3NonlinearEXT"; case ColorSpaceKHR::eBt709LinearEXT: return "Bt709LinearEXT"; case ColorSpaceKHR::eBt709NonlinearEXT: return "Bt709NonlinearEXT"; case ColorSpaceKHR::eBt2020LinearEXT: return "Bt2020LinearEXT"; case ColorSpaceKHR::eHdr10St2084EXT: return "Hdr10St2084EXT"; case ColorSpaceKHR::eDolbyvisionEXT: return "DolbyvisionEXT"; case ColorSpaceKHR::eHdr10HlgEXT: return "Hdr10HlgEXT"; case ColorSpaceKHR::eAdobergbLinearEXT: return "AdobergbLinearEXT"; case ColorSpaceKHR::eAdobergbNonlinearEXT: return "AdobergbNonlinearEXT"; case ColorSpaceKHR::ePassThroughEXT: return "PassThroughEXT"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(DisplayPlaneAlphaFlagBitsKHR value) { switch (value) { case DisplayPlaneAlphaFlagBitsKHR::eOpaque: return "Opaque"; case DisplayPlaneAlphaFlagBitsKHR::eGlobal: return "Global"; case DisplayPlaneAlphaFlagBitsKHR::ePerPixel: return "PerPixel"; case DisplayPlaneAlphaFlagBitsKHR::ePerPixelPremultiplied: return "PerPixelPremultiplied"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(DisplayPlaneAlphaFlagsKHR value) { if (!value) return "{}"; std::string result; if (value & DisplayPlaneAlphaFlagBitsKHR::eOpaque) result += "Opaque | "; if (value & DisplayPlaneAlphaFlagBitsKHR::eGlobal) result += "Global | "; if (value & DisplayPlaneAlphaFlagBitsKHR::ePerPixel) result += "PerPixel | "; if (value & DisplayPlaneAlphaFlagBitsKHR::ePerPixelPremultiplied) result += "PerPixelPremultiplied | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(CompositeAlphaFlagBitsKHR value) { switch (value) { case CompositeAlphaFlagBitsKHR::eOpaque: return "Opaque"; case CompositeAlphaFlagBitsKHR::ePreMultiplied: return "PreMultiplied"; case CompositeAlphaFlagBitsKHR::ePostMultiplied: return "PostMultiplied"; case CompositeAlphaFlagBitsKHR::eInherit: return "Inherit"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(CompositeAlphaFlagsKHR value) { if (!value) return "{}"; std::string result; if (value & CompositeAlphaFlagBitsKHR::eOpaque) result += "Opaque | "; if (value & CompositeAlphaFlagBitsKHR::ePreMultiplied) result += "PreMultiplied | "; if (value & CompositeAlphaFlagBitsKHR::ePostMultiplied) result += "PostMultiplied | "; if (value & CompositeAlphaFlagBitsKHR::eInherit) result += "Inherit | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(SurfaceTransformFlagBitsKHR value) { switch (value) { case SurfaceTransformFlagBitsKHR::eIdentity: return "Identity"; case SurfaceTransformFlagBitsKHR::eRotate90: return "Rotate90"; case SurfaceTransformFlagBitsKHR::eRotate180: return "Rotate180"; case SurfaceTransformFlagBitsKHR::eRotate270: return "Rotate270"; case SurfaceTransformFlagBitsKHR::eHorizontalMirror: return "HorizontalMirror"; case SurfaceTransformFlagBitsKHR::eHorizontalMirrorRotate90: return "HorizontalMirrorRotate90"; case SurfaceTransformFlagBitsKHR::eHorizontalMirrorRotate180: return "HorizontalMirrorRotate180"; case SurfaceTransformFlagBitsKHR::eHorizontalMirrorRotate270: return "HorizontalMirrorRotate270"; case SurfaceTransformFlagBitsKHR::eInherit: return "Inherit"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(SurfaceTransformFlagsKHR value) { if (!value) return "{}"; std::string result; if (value & SurfaceTransformFlagBitsKHR::eIdentity) result += "Identity | "; if (value & SurfaceTransformFlagBitsKHR::eRotate90) result += "Rotate90 | "; if (value & SurfaceTransformFlagBitsKHR::eRotate180) result += "Rotate180 | "; if (value & SurfaceTransformFlagBitsKHR::eRotate270) result += "Rotate270 | "; if (value & SurfaceTransformFlagBitsKHR::eHorizontalMirror) result += "HorizontalMirror | "; if (value & SurfaceTransformFlagBitsKHR::eHorizontalMirrorRotate90) result += "HorizontalMirrorRotate90 | "; if (value & SurfaceTransformFlagBitsKHR::eHorizontalMirrorRotate180) result += "HorizontalMirrorRotate180 | "; if (value & SurfaceTransformFlagBitsKHR::eHorizontalMirrorRotate270) result += "HorizontalMirrorRotate270 | "; if (value & SurfaceTransformFlagBitsKHR::eInherit) result += "Inherit | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(DebugReportFlagBitsEXT value) { switch (value) { case DebugReportFlagBitsEXT::eInformation: return "Information"; case DebugReportFlagBitsEXT::eWarning: return "Warning"; case DebugReportFlagBitsEXT::ePerformanceWarning: return "PerformanceWarning"; case DebugReportFlagBitsEXT::eError: return "Error"; case DebugReportFlagBitsEXT::eDebug: return "Debug"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(DebugReportFlagsEXT value) { if (!value) return "{}"; std::string result; if (value & DebugReportFlagBitsEXT::eInformation) result += "Information | "; if (value & DebugReportFlagBitsEXT::eWarning) result += "Warning | "; if (value & DebugReportFlagBitsEXT::ePerformanceWarning) result += "PerformanceWarning | "; if (value & DebugReportFlagBitsEXT::eError) result += "Error | "; if (value & DebugReportFlagBitsEXT::eDebug) result += "Debug | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(DebugReportObjectTypeEXT value) { switch (value) { case DebugReportObjectTypeEXT::eUnknown: return "Unknown"; case DebugReportObjectTypeEXT::eInstance: return "Instance"; case DebugReportObjectTypeEXT::ePhysicalDevice: return "PhysicalDevice"; case DebugReportObjectTypeEXT::eDevice: return "Device"; case DebugReportObjectTypeEXT::eQueue: return "Queue"; case DebugReportObjectTypeEXT::eSemaphore: return "Semaphore"; case DebugReportObjectTypeEXT::eCommandBuffer: return "CommandBuffer"; case DebugReportObjectTypeEXT::eFence: return "Fence"; case DebugReportObjectTypeEXT::eDeviceMemory: return "DeviceMemory"; case DebugReportObjectTypeEXT::eBuffer: return "Buffer"; case DebugReportObjectTypeEXT::eImage: return "Image"; case DebugReportObjectTypeEXT::eEvent: return "Event"; case DebugReportObjectTypeEXT::eQueryPool: return "QueryPool"; case DebugReportObjectTypeEXT::eBufferView: return "BufferView"; case DebugReportObjectTypeEXT::eImageView: return "ImageView"; case DebugReportObjectTypeEXT::eShaderModule: return "ShaderModule"; case DebugReportObjectTypeEXT::ePipelineCache: return "PipelineCache"; case DebugReportObjectTypeEXT::ePipelineLayout: return "PipelineLayout"; case DebugReportObjectTypeEXT::eRenderPass: return "RenderPass"; case DebugReportObjectTypeEXT::ePipeline: return "Pipeline"; case DebugReportObjectTypeEXT::eDescriptorSetLayout: return "DescriptorSetLayout"; case DebugReportObjectTypeEXT::eSampler: return "Sampler"; case DebugReportObjectTypeEXT::eDescriptorPool: return "DescriptorPool"; case DebugReportObjectTypeEXT::eDescriptorSet: return "DescriptorSet"; case DebugReportObjectTypeEXT::eFramebuffer: return "Framebuffer"; case DebugReportObjectTypeEXT::eCommandPool: return "CommandPool"; case DebugReportObjectTypeEXT::eSurfaceKhr: return "SurfaceKhr"; case DebugReportObjectTypeEXT::eSwapchainKhr: return "SwapchainKhr"; case DebugReportObjectTypeEXT::eDebugReport: return "DebugReport"; case DebugReportObjectTypeEXT::eDisplayKhr: return "DisplayKhr"; case DebugReportObjectTypeEXT::eDisplayModeKhr: return "DisplayModeKhr"; case DebugReportObjectTypeEXT::eObjectTableNvx: return "ObjectTableNvx"; case DebugReportObjectTypeEXT::eIndirectCommandsLayoutNvx: return "IndirectCommandsLayoutNvx"; case DebugReportObjectTypeEXT::eDescriptorUpdateTemplateKHR: return "DescriptorUpdateTemplateKHR"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(DebugReportErrorEXT value) { switch (value) { case DebugReportErrorEXT::eNone: return "None"; case DebugReportErrorEXT::eCallbackRef: return "CallbackRef"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(RasterizationOrderAMD value) { switch (value) { case RasterizationOrderAMD::eStrict: return "Strict"; case RasterizationOrderAMD::eRelaxed: return "Relaxed"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(ExternalMemoryHandleTypeFlagBitsNV value) { switch (value) { case ExternalMemoryHandleTypeFlagBitsNV::eOpaqueWin32: return "OpaqueWin32"; case ExternalMemoryHandleTypeFlagBitsNV::eOpaqueWin32Kmt: return "OpaqueWin32Kmt"; case ExternalMemoryHandleTypeFlagBitsNV::eD3D11Image: return "D3D11Image"; case ExternalMemoryHandleTypeFlagBitsNV::eD3D11ImageKmt: return "D3D11ImageKmt"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(ExternalMemoryHandleTypeFlagsNV value) { if (!value) return "{}"; std::string result; if (value & ExternalMemoryHandleTypeFlagBitsNV::eOpaqueWin32) result += "OpaqueWin32 | "; if (value & ExternalMemoryHandleTypeFlagBitsNV::eOpaqueWin32Kmt) result += "OpaqueWin32Kmt | "; if (value & ExternalMemoryHandleTypeFlagBitsNV::eD3D11Image) result += "D3D11Image | "; if (value & ExternalMemoryHandleTypeFlagBitsNV::eD3D11ImageKmt) result += "D3D11ImageKmt | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(ExternalMemoryFeatureFlagBitsNV value) { switch (value) { case ExternalMemoryFeatureFlagBitsNV::eDedicatedOnly: return "DedicatedOnly"; case ExternalMemoryFeatureFlagBitsNV::eExportable: return "Exportable"; case ExternalMemoryFeatureFlagBitsNV::eImportable: return "Importable"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(ExternalMemoryFeatureFlagsNV value) { if (!value) return "{}"; std::string result; if (value & ExternalMemoryFeatureFlagBitsNV::eDedicatedOnly) result += "DedicatedOnly | "; if (value & ExternalMemoryFeatureFlagBitsNV::eExportable) result += "Exportable | "; if (value & ExternalMemoryFeatureFlagBitsNV::eImportable) result += "Importable | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(ValidationCheckEXT value) { switch (value) { case ValidationCheckEXT::eAll: return "All"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(IndirectCommandsLayoutUsageFlagBitsNVX value) { switch (value) { case IndirectCommandsLayoutUsageFlagBitsNVX::eUnorderedSequences: return "UnorderedSequences"; case IndirectCommandsLayoutUsageFlagBitsNVX::eSparseSequences: return "SparseSequences"; case IndirectCommandsLayoutUsageFlagBitsNVX::eEmptyExecutions: return "EmptyExecutions"; case IndirectCommandsLayoutUsageFlagBitsNVX::eIndexedSequences: return "IndexedSequences"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(IndirectCommandsLayoutUsageFlagsNVX value) { if (!value) return "{}"; std::string result; if (value & IndirectCommandsLayoutUsageFlagBitsNVX::eUnorderedSequences) result += "UnorderedSequences | "; if (value & IndirectCommandsLayoutUsageFlagBitsNVX::eSparseSequences) result += "SparseSequences | "; if (value & IndirectCommandsLayoutUsageFlagBitsNVX::eEmptyExecutions) result += "EmptyExecutions | "; if (value & IndirectCommandsLayoutUsageFlagBitsNVX::eIndexedSequences) result += "IndexedSequences | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(ObjectEntryUsageFlagBitsNVX value) { switch (value) { case ObjectEntryUsageFlagBitsNVX::eGraphics: return "Graphics"; case ObjectEntryUsageFlagBitsNVX::eCompute: return "Compute"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(ObjectEntryUsageFlagsNVX value) { if (!value) return "{}"; std::string result; if (value & ObjectEntryUsageFlagBitsNVX::eGraphics) result += "Graphics | "; if (value & ObjectEntryUsageFlagBitsNVX::eCompute) result += "Compute | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(IndirectCommandsTokenTypeNVX value) { switch (value) { case IndirectCommandsTokenTypeNVX::eVkIndirectCommandsTokenPipeline: return "VkIndirectCommandsTokenPipeline"; case IndirectCommandsTokenTypeNVX::eVkIndirectCommandsTokenDescriptorSet: return "VkIndirectCommandsTokenDescriptorSet"; case IndirectCommandsTokenTypeNVX::eVkIndirectCommandsTokenIndexBuffer: return "VkIndirectCommandsTokenIndexBuffer"; case IndirectCommandsTokenTypeNVX::eVkIndirectCommandsTokenVertexBuffer: return "VkIndirectCommandsTokenVertexBuffer"; case IndirectCommandsTokenTypeNVX::eVkIndirectCommandsTokenPushConstant: return "VkIndirectCommandsTokenPushConstant"; case IndirectCommandsTokenTypeNVX::eVkIndirectCommandsTokenDrawIndexed: return "VkIndirectCommandsTokenDrawIndexed"; case IndirectCommandsTokenTypeNVX::eVkIndirectCommandsTokenDraw: return "VkIndirectCommandsTokenDraw"; case IndirectCommandsTokenTypeNVX::eVkIndirectCommandsTokenDispatch: return "VkIndirectCommandsTokenDispatch"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(ObjectEntryTypeNVX value) { switch (value) { case ObjectEntryTypeNVX::eVkObjectEntryDescriptorSet: return "VkObjectEntryDescriptorSet"; case ObjectEntryTypeNVX::eVkObjectEntryPipeline: return "VkObjectEntryPipeline"; case ObjectEntryTypeNVX::eVkObjectEntryIndexBuffer: return "VkObjectEntryIndexBuffer"; case ObjectEntryTypeNVX::eVkObjectEntryVertexBuffer: return "VkObjectEntryVertexBuffer"; case ObjectEntryTypeNVX::eVkObjectEntryPushConstant: return "VkObjectEntryPushConstant"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(DescriptorSetLayoutCreateFlagBits value) { switch (value) { case DescriptorSetLayoutCreateFlagBits::ePushDescriptorKHR: return "PushDescriptorKHR"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(DescriptorSetLayoutCreateFlags value) { if (!value) return "{}"; std::string result; if (value & DescriptorSetLayoutCreateFlagBits::ePushDescriptorKHR) result += "PushDescriptorKHR | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(ExternalMemoryHandleTypeFlagBitsKHX value) { switch (value) { case ExternalMemoryHandleTypeFlagBitsKHX::eOpaqueFd: return "OpaqueFd"; case ExternalMemoryHandleTypeFlagBitsKHX::eOpaqueWin32: return "OpaqueWin32"; case ExternalMemoryHandleTypeFlagBitsKHX::eOpaqueWin32Kmt: return "OpaqueWin32Kmt"; case ExternalMemoryHandleTypeFlagBitsKHX::eD3D11Texture: return "D3D11Texture"; case ExternalMemoryHandleTypeFlagBitsKHX::eD3D11TextureKmt: return "D3D11TextureKmt"; case ExternalMemoryHandleTypeFlagBitsKHX::eD3D12Heap: return "D3D12Heap"; case ExternalMemoryHandleTypeFlagBitsKHX::eD3D12Resource: return "D3D12Resource"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(ExternalMemoryHandleTypeFlagsKHX value) { if (!value) return "{}"; std::string result; if (value & ExternalMemoryHandleTypeFlagBitsKHX::eOpaqueFd) result += "OpaqueFd | "; if (value & ExternalMemoryHandleTypeFlagBitsKHX::eOpaqueWin32) result += "OpaqueWin32 | "; if (value & ExternalMemoryHandleTypeFlagBitsKHX::eOpaqueWin32Kmt) result += "OpaqueWin32Kmt | "; if (value & ExternalMemoryHandleTypeFlagBitsKHX::eD3D11Texture) result += "D3D11Texture | "; if (value & ExternalMemoryHandleTypeFlagBitsKHX::eD3D11TextureKmt) result += "D3D11TextureKmt | "; if (value & ExternalMemoryHandleTypeFlagBitsKHX::eD3D12Heap) result += "D3D12Heap | "; if (value & ExternalMemoryHandleTypeFlagBitsKHX::eD3D12Resource) result += "D3D12Resource | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(ExternalMemoryFeatureFlagBitsKHX value) { switch (value) { case ExternalMemoryFeatureFlagBitsKHX::eDedicatedOnly: return "DedicatedOnly"; case ExternalMemoryFeatureFlagBitsKHX::eExportable: return "Exportable"; case ExternalMemoryFeatureFlagBitsKHX::eImportable: return "Importable"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(ExternalMemoryFeatureFlagsKHX value) { if (!value) return "{}"; std::string result; if (value & ExternalMemoryFeatureFlagBitsKHX::eDedicatedOnly) result += "DedicatedOnly | "; if (value & ExternalMemoryFeatureFlagBitsKHX::eExportable) result += "Exportable | "; if (value & ExternalMemoryFeatureFlagBitsKHX::eImportable) result += "Importable | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(ExternalSemaphoreHandleTypeFlagBitsKHX value) { switch (value) { case ExternalSemaphoreHandleTypeFlagBitsKHX::eOpaqueFd: return "OpaqueFd"; case ExternalSemaphoreHandleTypeFlagBitsKHX::eOpaqueWin32: return "OpaqueWin32"; case ExternalSemaphoreHandleTypeFlagBitsKHX::eOpaqueWin32Kmt: return "OpaqueWin32Kmt"; case ExternalSemaphoreHandleTypeFlagBitsKHX::eD3D12Fence: return "D3D12Fence"; case ExternalSemaphoreHandleTypeFlagBitsKHX::eFenceFd: return "FenceFd"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(ExternalSemaphoreHandleTypeFlagsKHX value) { if (!value) return "{}"; std::string result; if (value & ExternalSemaphoreHandleTypeFlagBitsKHX::eOpaqueFd) result += "OpaqueFd | "; if (value & ExternalSemaphoreHandleTypeFlagBitsKHX::eOpaqueWin32) result += "OpaqueWin32 | "; if (value & ExternalSemaphoreHandleTypeFlagBitsKHX::eOpaqueWin32Kmt) result += "OpaqueWin32Kmt | "; if (value & ExternalSemaphoreHandleTypeFlagBitsKHX::eD3D12Fence) result += "D3D12Fence | "; if (value & ExternalSemaphoreHandleTypeFlagBitsKHX::eFenceFd) result += "FenceFd | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(ExternalSemaphoreFeatureFlagBitsKHX value) { switch (value) { case ExternalSemaphoreFeatureFlagBitsKHX::eExportable: return "Exportable"; case ExternalSemaphoreFeatureFlagBitsKHX::eImportable: return "Importable"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(ExternalSemaphoreFeatureFlagsKHX value) { if (!value) return "{}"; std::string result; if (value & ExternalSemaphoreFeatureFlagBitsKHX::eExportable) result += "Exportable | "; if (value & ExternalSemaphoreFeatureFlagBitsKHX::eImportable) result += "Importable | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(SurfaceCounterFlagBitsEXT value) { switch (value) { case SurfaceCounterFlagBitsEXT::eVblank: return "Vblank"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(SurfaceCounterFlagsEXT value) { if (!value) return "{}"; std::string result; if (value & SurfaceCounterFlagBitsEXT::eVblank) result += "Vblank | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(DisplayPowerStateEXT value) { switch (value) { case DisplayPowerStateEXT::eOff: return "Off"; case DisplayPowerStateEXT::eSuspend: return "Suspend"; case DisplayPowerStateEXT::eOn: return "On"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(DeviceEventTypeEXT value) { switch (value) { case DeviceEventTypeEXT::eDisplayHotplug: return "DisplayHotplug"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(DisplayEventTypeEXT value) { switch (value) { case DisplayEventTypeEXT::eFirstPixelOut: return "FirstPixelOut"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(PeerMemoryFeatureFlagBitsKHX value) { switch (value) { case PeerMemoryFeatureFlagBitsKHX::eCopySrc: return "CopySrc"; case PeerMemoryFeatureFlagBitsKHX::eCopyDst: return "CopyDst"; case PeerMemoryFeatureFlagBitsKHX::eGenericSrc: return "GenericSrc"; case PeerMemoryFeatureFlagBitsKHX::eGenericDst: return "GenericDst"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(PeerMemoryFeatureFlagsKHX value) { if (!value) return "{}"; std::string result; if (value & PeerMemoryFeatureFlagBitsKHX::eCopySrc) result += "CopySrc | "; if (value & PeerMemoryFeatureFlagBitsKHX::eCopyDst) result += "CopyDst | "; if (value & PeerMemoryFeatureFlagBitsKHX::eGenericSrc) result += "GenericSrc | "; if (value & PeerMemoryFeatureFlagBitsKHX::eGenericDst) result += "GenericDst | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(MemoryAllocateFlagBitsKHX value) { switch (value) { case MemoryAllocateFlagBitsKHX::eDeviceMask: return "DeviceMask"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(MemoryAllocateFlagsKHX value) { if (!value) return "{}"; std::string result; if (value & MemoryAllocateFlagBitsKHX::eDeviceMask) result += "DeviceMask | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(DeviceGroupPresentModeFlagBitsKHX value) { switch (value) { case DeviceGroupPresentModeFlagBitsKHX::eLocal: return "Local"; case DeviceGroupPresentModeFlagBitsKHX::eRemote: return "Remote"; case DeviceGroupPresentModeFlagBitsKHX::eSum: return "Sum"; case DeviceGroupPresentModeFlagBitsKHX::eLocalMultiDevice: return "LocalMultiDevice"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(DeviceGroupPresentModeFlagsKHX value) { if (!value) return "{}"; std::string result; if (value & DeviceGroupPresentModeFlagBitsKHX::eLocal) result += "Local | "; if (value & DeviceGroupPresentModeFlagBitsKHX::eRemote) result += "Remote | "; if (value & DeviceGroupPresentModeFlagBitsKHX::eSum) result += "Sum | "; if (value & DeviceGroupPresentModeFlagBitsKHX::eLocalMultiDevice) result += "LocalMultiDevice | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(SwapchainCreateFlagBitsKHR value) { switch (value) { case SwapchainCreateFlagBitsKHR::eBindSfrKHX: return "BindSfrKHX"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(SwapchainCreateFlagsKHR value) { if (!value) return "{}"; std::string result; if (value & SwapchainCreateFlagBitsKHR::eBindSfrKHX) result += "BindSfrKHX | "; return "{" + result.substr(0, result.size() - 3) + "}"; } VULKAN_HPP_INLINE std::string to_string(ViewportCoordinateSwizzleNV value) { switch (value) { case ViewportCoordinateSwizzleNV::ePositiveX: return "PositiveX"; case ViewportCoordinateSwizzleNV::eNegativeX: return "NegativeX"; case ViewportCoordinateSwizzleNV::ePositiveY: return "PositiveY"; case ViewportCoordinateSwizzleNV::eNegativeY: return "NegativeY"; case ViewportCoordinateSwizzleNV::ePositiveZ: return "PositiveZ"; case ViewportCoordinateSwizzleNV::eNegativeZ: return "NegativeZ"; case ViewportCoordinateSwizzleNV::ePositiveW: return "PositiveW"; case ViewportCoordinateSwizzleNV::eNegativeW: return "NegativeW"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(DiscardRectangleModeEXT value) { switch (value) { case DiscardRectangleModeEXT::eInclusive: return "Inclusive"; case DiscardRectangleModeEXT::eExclusive: return "Exclusive"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(SubpassDescriptionFlagBits value) { switch (value) { case SubpassDescriptionFlagBits::ePerViewAttributesNVX: return "PerViewAttributesNVX"; case SubpassDescriptionFlagBits::ePerViewPositionXOnlyNVX: return "PerViewPositionXOnlyNVX"; default: return "invalid"; } } VULKAN_HPP_INLINE std::string to_string(SubpassDescriptionFlags value) { if (!value) return "{}"; std::string result; if (value & SubpassDescriptionFlagBits::ePerViewAttributesNVX) result += "PerViewAttributesNVX | "; if (value & SubpassDescriptionFlagBits::ePerViewPositionXOnlyNVX) result += "PerViewPositionXOnlyNVX | "; return "{" + result.substr(0, result.size() - 3) + "}"; } } // namespace vk #endif ================================================ FILE: src/engine/server/server.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // server.h #include "../../game/q_shared.h" #include "../qcommon/qcommon.h" #include "../../game/g_public.h" #include "../../game/bg_public.h" //============================================================================= #define PERS_SCORE 0 // !!! MUST NOT CHANGE, SERVER AND // GAME BOTH REFERENCE !!! #define MAX_ENT_CLUSTERS 16 typedef struct svEntity_s { struct worldSector_s *worldSector; struct svEntity_s *nextEntityInWorldSector; entityState_t baseline; // for delta compression of initial sighting int numClusters; // if -1, use headnode instead int clusternums[MAX_ENT_CLUSTERS]; int lastCluster; // if all the clusters don't fit in clusternums int areanum, areanum2; int snapshotCounter; // used to prevent double adding from portal views } svEntity_t; typedef enum { SS_DEAD, // no map loaded SS_LOADING, // spawning level entities SS_GAME // actively running } serverState_t; typedef struct { serverState_t state; qboolean restarting; // if true, send configstring changes during SS_LOADING int serverId; // changes each server start int restartedServerId; // serverId before a map_restart int checksumFeed; // the feed key that we use to compute the pure checksum strings // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=475 // the serverId associated with the current checksumFeed (always <= serverId) int checksumFeedServerId; int snapshotCounter; // incremented for each snapshot built int timeResidual; // <= 1000 / sv_frame->value int nextFrameTime; // when time > nextFrameTime, process world struct cmodel_s *models[MAX_MODELS]; char *configstrings[MAX_CONFIGSTRINGS]; svEntity_t svEntities[MAX_GENTITIES]; char *entityParsePoint; // used during game VM init // the game virtual machine will update these on init and changes sharedEntity_t *gentities; int gentitySize; int num_entities; // current number, <= MAX_GENTITIES playerState_t *gameClients; int gameClientSize; // will be > sizeof(playerState_t) due to game private data int restartTime; } server_t; typedef struct { int areabytes; byte areabits[MAX_MAP_AREA_BYTES]; // portalarea visibility bits playerState_t ps; int num_entities; int first_entity; // into the circular sv_packet_entities[] // the entities MUST be in increasing state number // order, otherwise the delta compression will fail int messageSent; // time the message was transmitted int messageAcked; // time the message was acked int messageSize; // used to rate drop packets } clientSnapshot_t; typedef enum { CS_FREE, // can be reused for a new connection CS_ZOMBIE, // client has been disconnected, but don't reuse // connection for a couple seconds CS_CONNECTED, // has been assigned to a client_t, but no gamestate yet CS_PRIMED, // gamestate has been sent, but client hasn't sent a usercmd CS_ACTIVE // client is fully in game } clientState_t; typedef struct netchan_buffer_s { msg_t msg; byte msgBuffer[MAX_MSGLEN]; struct netchan_buffer_s *next; } netchan_buffer_t; typedef struct client_s { clientState_t state; char userinfo[MAX_INFO_STRING]; // name, etc char reliableCommands[MAX_RELIABLE_COMMANDS][MAX_STRING_CHARS]; int reliableSequence; // last added reliable message, not necesarily sent or acknowledged yet int reliableAcknowledge; // last acknowledged reliable message int reliableSent; // last sent reliable message, not necesarily acknowledged yet int messageAcknowledge; int gamestateMessageNum; // netchan->outgoingSequence of gamestate int challenge; usercmd_t lastUsercmd; int lastMessageNum; // for delta compression int lastClientCommand; // reliable client message sequence char lastClientCommandString[MAX_STRING_CHARS]; sharedEntity_t *gentity; // SV_GentityNum(clientnum) char name[MAX_NAME_LENGTH]; // extracted from userinfo, high bits masked // downloading char downloadName[MAX_QPATH]; // if not empty string, we are downloading fileHandle_t download; // file being downloaded int downloadSize; // total bytes (can't use EOF because of paks) int downloadCount; // bytes sent int downloadClientBlock; // last block we sent to the client, awaiting ack int downloadCurrentBlock; // current block number int downloadXmitBlock; // last block we xmited unsigned char *downloadBlocks[MAX_DOWNLOAD_WINDOW]; // the buffers for the download blocks int downloadBlockSize[MAX_DOWNLOAD_WINDOW]; qboolean downloadEOF; // We have sent the EOF block int downloadSendTime; // time we last got an ack from the client int deltaMessage; // frame last client usercmd message int nextReliableTime; // svs.time when another reliable command will be allowed int lastPacketTime; // svs.time when packet was last received int lastConnectTime; // svs.time when connection started int nextSnapshotTime; // send another snapshot when svs.time >= nextSnapshotTime qboolean rateDelayed; // true if nextSnapshotTime was set based on rate instead of snapshotMsec int timeoutCount; // must timeout a few frames in a row so debugging doesn't break clientSnapshot_t frames[PACKET_BACKUP]; // updates can be delta'd from here int ping; int rate; // bytes / second int snapshotMsec; // requests a snapshot every snapshotMsec unless rate choked int pureAuthentic; qboolean gotCP; // TTimo - additional flag to distinguish between a bad pure checksum, and no cp command at all netchan_t netchan; // TTimo // queuing outgoing fragmented messages to send them properly, without udp packet bursts // in case large fragmented messages are stacking up // buffer them into this queue, and hand them out to netchan as needed netchan_buffer_t *netchan_start_queue; netchan_buffer_t **netchan_end_queue; } client_t; //============================================================================= // MAX_CHALLENGES is made large to prevent a denial // of service attack that could cycle all of them // out before legitimate users connected #define MAX_CHALLENGES 1024 #define AUTHORIZE_TIMEOUT 5000 typedef struct { netadr_t adr; int challenge; int time; // time the last packet was sent to the autherize server int pingTime; // time the challenge response was sent to client int firstTime; // time the adr was first used, for authorize timeout checks qboolean connected; } challenge_t; #define MAX_MASTERS 8 // max recipients for heartbeat packets // this structure will be cleared only when the game dll changes typedef struct { qboolean initialized; // sv_init has completed int time; // will be strictly increasing across level changes int snapFlagServerBit; // ^= SNAPFLAG_SERVERCOUNT every SV_SpawnServer() client_t *clients; // [sv_maxclients->integer]; int numSnapshotEntities; // sv_maxclients->integer*PACKET_BACKUP*MAX_PACKET_ENTITIES int nextSnapshotEntities; // next snapshotEntities to use entityState_t *snapshotEntities; // [numSnapshotEntities] int nextHeartbeatTime; challenge_t challenges[MAX_CHALLENGES]; // to prevent invalid IPs from connecting netadr_t redirectAddress; // for rcon return messages netadr_t authorizeAddress; // for rcon return messages } serverStatic_t; //============================================================================= extern serverStatic_t svs; // persistant server info across maps extern server_t sv; // cleared each map extern vm_t *gvm; // game virtual machine #define MAX_MASTER_SERVERS 5 extern cvar_t *sv_fps; extern cvar_t *sv_timeout; extern cvar_t *sv_zombietime; extern cvar_t *sv_rconPassword; extern cvar_t *sv_privatePassword; extern cvar_t *sv_allowDownload; extern cvar_t *sv_maxclients; extern cvar_t *sv_privateClients; extern cvar_t *sv_hostname; extern cvar_t *sv_master[MAX_MASTER_SERVERS]; extern cvar_t *sv_reconnectlimit; extern cvar_t *sv_showloss; extern cvar_t *sv_padPackets; extern cvar_t *sv_killserver; extern cvar_t *sv_mapname; extern cvar_t *sv_mapChecksum; extern cvar_t *sv_serverid; extern cvar_t *sv_maxRate; extern cvar_t *sv_minPing; extern cvar_t *sv_maxPing; extern cvar_t *sv_gametype; extern cvar_t *sv_pure; extern cvar_t *sv_floodProtect; extern cvar_t *sv_lanForceRate; extern cvar_t *sv_strictAuth; //=========================================================== // // sv_main.c // void SV_FinalMessage (char *message); void QDECL SV_SendServerCommand( client_t *cl, const char *fmt, ...); void SV_AddOperatorCommands (void); void SV_RemoveOperatorCommands (void); void SV_MasterHeartbeat (void); void SV_MasterShutdown (void); // // sv_init.c // void SV_SetConfigstring( int index, const char *val ); void SV_GetConfigstring( int index, char *buffer, int bufferSize ); void SV_SetUserinfo( int index, const char *val ); void SV_GetUserinfo( int index, char *buffer, int bufferSize ); void SV_ChangeMaxClients( void ); void SV_SpawnServer( char *server, qboolean killBots ); // // sv_client.c // void SV_GetChallenge( netadr_t from ); void SV_DirectConnect( netadr_t from ); void SV_AuthorizeIpPacket( netadr_t from ); void SV_ExecuteClientMessage( client_t *cl, msg_t *msg ); void SV_UserinfoChanged( client_t *cl ); void SV_ClientEnterWorld( client_t *client, usercmd_t *cmd ); void SV_DropClient( client_t *drop, const char *reason ); void SV_ExecuteClientCommand( client_t *cl, const char *s, qboolean clientOK ); void SV_ClientThink (client_t *cl, usercmd_t *cmd); void SV_WriteDownloadToClient( client_t *cl , msg_t *msg ); // // sv_ccmds.c // void SV_Heartbeat_f( void ); // // sv_snapshot.c // void SV_AddServerCommand( client_t *client, const char *cmd ); void SV_UpdateServerCommandsToClient( client_t *client, msg_t *msg ); void SV_WriteFrameToClient (client_t *client, msg_t *msg); void SV_SendMessageToClient( msg_t *msg, client_t *client ); void SV_SendClientMessages( void ); void SV_SendClientSnapshot( client_t *client ); // // sv_game.c // int SV_NumForGentity( sharedEntity_t *ent ); sharedEntity_t *SV_GentityNum( int num ); playerState_t *SV_GameClientNum( int num ); svEntity_t *SV_SvEntityForGentity( sharedEntity_t *gEnt ); sharedEntity_t *SV_GEntityForSvEntity( svEntity_t *svEnt ); void SV_InitGameProgs ( void ); void SV_ShutdownGameProgs ( void ); void SV_RestartGameProgs( void ); qboolean SV_inPVS (const vec3_t p1, const vec3_t p2); // // sv_bot.c // void SV_BotFrame( int time ); int SV_BotAllocateClient(void); void SV_BotFreeClient( int clientNum ); void SV_BotInitCvars(void); int SV_BotLibSetup( void ); int SV_BotLibShutdown( void ); int SV_BotGetSnapshotEntity( int client, int ent ); int SV_BotGetConsoleMessage( int client, char *buf, int size ); int BotImport_DebugPolygonCreate(int color, int numPoints, vec3_t *points); void BotImport_DebugPolygonDelete(int id); //============================================================ // // high level object sorting to reduce interaction tests // void SV_ClearWorld (void); // called after the world model has been loaded, before linking any entities void SV_UnlinkEntity( sharedEntity_t *ent ); // call before removing an entity, and before trying to move one, // so it doesn't clip against itself void SV_LinkEntity( sharedEntity_t *ent ); // Needs to be called any time an entity changes origin, mins, maxs, // or solid. Automatically unlinks if needed. // sets ent->v.absmin and ent->v.absmax // sets ent->leafnums[] for pvs determination even if the entity // is not solid clipHandle_t SV_ClipHandleForEntity( const sharedEntity_t *ent ); void SV_SectorList_f( void ); int SV_AreaEntities( const vec3_t mins, const vec3_t maxs, int *entityList, int maxcount ); // fills in a table of entity numbers with entities that have bounding boxes // that intersect the given area. It is possible for a non-axial bmodel // to be returned that doesn't actually intersect the area on an exact // test. // returns the number of pointers filled in // The world entity is never returned in this list. int SV_PointContents( const vec3_t p, int passEntityNum ); // returns the CONTENTS_* value from the world and all entities at the given point. void SV_Trace( trace_t *results, const vec3_t start, vec3_t mins, vec3_t maxs, const vec3_t end, int passEntityNum, int contentmask, int capsule ); // mins and maxs are relative // if the entire move stays in a solid volume, trace.allsolid will be set, // trace.startsolid will be set, and trace.fraction will be 0 // if the starting point is in a solid, it will be allowed to move out // to an open area // passEntityNum is explicitly excluded from clipping checks (normally ENTITYNUM_NONE) void SV_ClipToEntity( trace_t *trace, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int entityNum, int contentmask, int capsule ); // clip to a specific entity // // sv_net_chan.c // void SV_Netchan_Transmit( client_t *client, msg_t *msg); void SV_Netchan_TransmitNextFragment( client_t *client ); qboolean SV_Netchan_Process( client_t *client, msg_t *msg ); ================================================ FILE: src/engine/server/sv_bot.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // sv_bot.c #include "server.h" #include "../../game/botlib.h" typedef struct bot_debugpoly_s { int inuse; int color; int numPoints; vec3_t points[128]; } bot_debugpoly_t; static bot_debugpoly_t *debugpolygons; int bot_maxdebugpolys; extern botlib_export_t *botlib_export; int bot_enable; /* ================== SV_BotAllocateClient ================== */ int SV_BotAllocateClient(void) { int i; client_t *cl; // find a client slot for ( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) { if ( cl->state == CS_FREE ) { break; } } if ( i == sv_maxclients->integer ) { return -1; } cl->gentity = SV_GentityNum( i ); cl->gentity->s.number = i; cl->state = CS_ACTIVE; cl->lastPacketTime = svs.time; cl->netchan.remoteAddress.type = NA_BOT; cl->rate = 16384; return i; } /* ================== SV_BotFreeClient ================== */ void SV_BotFreeClient( int clientNum ) { client_t *cl; if ( clientNum < 0 || clientNum >= sv_maxclients->integer ) { Com_Error( ERR_DROP, "SV_BotFreeClient: bad clientNum: %i", clientNum ); } cl = &svs.clients[clientNum]; cl->state = CS_FREE; cl->name[0] = 0; if ( cl->gentity ) { cl->gentity->r.svFlags &= ~SVF_BOT; } } /* ================== BotDrawDebugPolygons ================== */ void BotDrawDebugPolygons(void (*drawPoly)(int color, int numPoints, float *points), int value) { static cvar_t *bot_debug, *bot_groundonly, *bot_reachability, *bot_highlightarea; bot_debugpoly_t *poly; int i, parm0; if (!debugpolygons) return; //bot debugging if (!bot_debug) bot_debug = Cvar_Get("bot_debug", "0", 0); // if (bot_enable && bot_debug->integer) { //show reachabilities if (!bot_reachability) bot_reachability = Cvar_Get("bot_reachability", "0", 0); //show ground faces only if (!bot_groundonly) bot_groundonly = Cvar_Get("bot_groundonly", "1", 0); //get the hightlight area if (!bot_highlightarea) bot_highlightarea = Cvar_Get("bot_highlightarea", "0", 0); // parm0 = 0; if (svs.clients[0].lastUsercmd.buttons & BUTTON_ATTACK) parm0 |= 1; if (bot_reachability->integer) parm0 |= 2; if (bot_groundonly->integer) parm0 |= 4; botlib_export->BotLibVarSet("bot_highlightarea", bot_highlightarea->string); botlib_export->Test(parm0, NULL, svs.clients[0].gentity->r.currentOrigin, svs.clients[0].gentity->r.currentAngles); } //end if //draw all debug polys for (i = 0; i < bot_maxdebugpolys; i++) { poly = &debugpolygons[i]; if (!poly->inuse) continue; drawPoly(poly->color, poly->numPoints, (float *) poly->points); //Com_Printf("poly %i, numpoints = %d\n", i, poly->numPoints); } } /* ================== BotImport_Print ================== */ void QDECL BotImport_Print(int type, char *fmt, ...) { char str[2048]; va_list ap; va_start(ap, fmt); vsprintf(str, fmt, ap); va_end(ap); switch(type) { case PRT_MESSAGE: { Com_Printf("%s", str); break; } case PRT_WARNING: { Com_Printf(S_COLOR_YELLOW "Warning: %s", str); break; } case PRT_ERROR: { Com_Printf(S_COLOR_RED "Error: %s", str); break; } case PRT_FATAL: { Com_Printf(S_COLOR_RED "Fatal: %s", str); break; } case PRT_EXIT: { Com_Error(ERR_DROP, S_COLOR_RED "Exit: %s", str); break; } default: { Com_Printf("unknown print type\n"); break; } } } /* ================== BotImport_Trace ================== */ void BotImport_Trace(bsp_trace_t *bsptrace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask) { trace_t trace; SV_Trace(&trace, start, mins, maxs, end, passent, contentmask, qfalse); //copy the trace information bsptrace->allsolid = trace.allsolid; bsptrace->startsolid = trace.startsolid; bsptrace->fraction = trace.fraction; VectorCopy(trace.endpos, bsptrace->endpos); bsptrace->plane.dist = trace.plane.dist; VectorCopy(trace.plane.normal, bsptrace->plane.normal); bsptrace->plane.signbits = trace.plane.signbits; bsptrace->plane.type = trace.plane.type; bsptrace->surface.value = trace.surfaceFlags; bsptrace->ent = trace.entityNum; bsptrace->exp_dist = 0; bsptrace->sidenum = 0; bsptrace->contents = 0; } /* ================== BotImport_EntityTrace ================== */ void BotImport_EntityTrace(bsp_trace_t *bsptrace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int entnum, int contentmask) { trace_t trace; SV_ClipToEntity(&trace, start, mins, maxs, end, entnum, contentmask, qfalse); //copy the trace information bsptrace->allsolid = trace.allsolid; bsptrace->startsolid = trace.startsolid; bsptrace->fraction = trace.fraction; VectorCopy(trace.endpos, bsptrace->endpos); bsptrace->plane.dist = trace.plane.dist; VectorCopy(trace.plane.normal, bsptrace->plane.normal); bsptrace->plane.signbits = trace.plane.signbits; bsptrace->plane.type = trace.plane.type; bsptrace->surface.value = trace.surfaceFlags; bsptrace->ent = trace.entityNum; bsptrace->exp_dist = 0; bsptrace->sidenum = 0; bsptrace->contents = 0; } /* ================== BotImport_PointContents ================== */ int BotImport_PointContents(vec3_t point) { return SV_PointContents(point, -1); } /* ================== BotImport_inPVS ================== */ int BotImport_inPVS(vec3_t p1, vec3_t p2) { return SV_inPVS (p1, p2); } /* ================== BotImport_BSPEntityData ================== */ char *BotImport_BSPEntityData(void) { return CM_EntityString(); } /* ================== BotImport_BSPModelMinsMaxsOrigin ================== */ void BotImport_BSPModelMinsMaxsOrigin(int modelnum, vec3_t angles, vec3_t outmins, vec3_t outmaxs, vec3_t origin) { clipHandle_t h; vec3_t mins, maxs; float max; int i; h = CM_InlineModel(modelnum); CM_ModelBounds(h, mins, maxs); //if the model is rotated if ((angles[0] || angles[1] || angles[2])) { // expand for rotation max = RadiusFromBounds(mins, maxs); for (i = 0; i < 3; i++) { mins[i] = -max; maxs[i] = max; } } if (outmins) VectorCopy(mins, outmins); if (outmaxs) VectorCopy(maxs, outmaxs); if (origin) VectorClear(origin); } /* ================== BotImport_GetMemory ================== */ void *BotImport_GetMemory(int size) { void *ptr; ptr = Z_TagMalloc( size, TAG_BOTLIB ); return ptr; } /* ================== BotImport_FreeMemory ================== */ void BotImport_FreeMemory(void *ptr) { Z_Free(ptr); } /* ================= BotImport_HunkAlloc ================= */ void *BotImport_HunkAlloc( int size ) { if( Hunk_CheckMark() ) { Com_Error( ERR_DROP, "SV_Bot_HunkAlloc: Alloc with marks already set\n" ); } return Hunk_Alloc( size, h_high ); } /* ================== BotImport_DebugPolygonCreate ================== */ int BotImport_DebugPolygonCreate(int color, int numPoints, vec3_t *points) { bot_debugpoly_t *poly; int i; if (!debugpolygons) return 0; for (i = 1; i < bot_maxdebugpolys; i++) { if (!debugpolygons[i].inuse) break; } if (i >= bot_maxdebugpolys) return 0; poly = &debugpolygons[i]; poly->inuse = qtrue; poly->color = color; poly->numPoints = numPoints; Com_Memcpy(poly->points, points, numPoints * sizeof(vec3_t)); // return i; } /* ================== BotImport_DebugPolygonShow ================== */ void BotImport_DebugPolygonShow(int id, int color, int numPoints, vec3_t *points) { bot_debugpoly_t *poly; if (!debugpolygons) return; poly = &debugpolygons[id]; poly->inuse = qtrue; poly->color = color; poly->numPoints = numPoints; Com_Memcpy(poly->points, points, numPoints * sizeof(vec3_t)); } /* ================== BotImport_DebugPolygonDelete ================== */ void BotImport_DebugPolygonDelete(int id) { if (!debugpolygons) return; debugpolygons[id].inuse = qfalse; } /* ================== BotImport_DebugLineCreate ================== */ int BotImport_DebugLineCreate(void) { vec3_t points[1]; return BotImport_DebugPolygonCreate(0, 0, points); } /* ================== BotImport_DebugLineDelete ================== */ void BotImport_DebugLineDelete(int line) { BotImport_DebugPolygonDelete(line); } /* ================== BotImport_DebugLineShow ================== */ void BotImport_DebugLineShow(int line, vec3_t start, vec3_t end, int color) { vec3_t points[4], dir, cross, up = {0, 0, 1}; float dot; VectorCopy(start, points[0]); VectorCopy(start, points[1]); //points[1][2] -= 2; VectorCopy(end, points[2]); //points[2][2] -= 2; VectorCopy(end, points[3]); VectorSubtract(end, start, dir); VectorNormalize(dir); dot = DotProduct(dir, up); if (dot > 0.99 || dot < -0.99) VectorSet(cross, 1, 0, 0); else CrossProduct(dir, up, cross); VectorNormalize(cross); VectorMA(points[0], 2, cross, points[0]); VectorMA(points[1], -2, cross, points[1]); VectorMA(points[2], -2, cross, points[2]); VectorMA(points[3], 2, cross, points[3]); BotImport_DebugPolygonShow(line, color, 4, points); } /* ================== SV_BotClientCommand ================== */ void BotClientCommand( int client, char *command ) { SV_ExecuteClientCommand( &svs.clients[client], command, qtrue ); } /* ================== SV_BotFrame ================== */ void SV_BotFrame( int time ) { if (!bot_enable) return; //NOTE: maybe the game is already shutdown if (!gvm) return; VM_Call( gvm, BOTAI_START_FRAME, time ); } /* =============== SV_BotLibSetup =============== */ int SV_BotLibSetup( void ) { if (!bot_enable) { return 0; } if ( !botlib_export ) { Com_Printf( S_COLOR_RED "Error: SV_BotLibSetup without SV_BotInitBotLib\n" ); return -1; } return botlib_export->BotLibSetup(); } /* =============== SV_ShutdownBotLib Called when either the entire server is being killed, or it is changing to a different game directory. =============== */ int SV_BotLibShutdown( void ) { if ( !botlib_export ) { return -1; } return botlib_export->BotLibShutdown(); } /* ================== SV_BotInitCvars ================== */ void SV_BotInitCvars(void) { Cvar_Get("bot_enable", "1", 0); //enable the bot Cvar_Get("bot_developer", "0", CVAR_CHEAT); //bot developer mode Cvar_Get("bot_debug", "0", CVAR_CHEAT); //enable bot debugging Cvar_Get("bot_maxdebugpolys", "2", 0); //maximum number of debug polys Cvar_Get("bot_groundonly", "1", 0); //only show ground faces of areas Cvar_Get("bot_reachability", "0", 0); //show all reachabilities to other areas Cvar_Get("bot_visualizejumppads", "0", CVAR_CHEAT); //show jumppads Cvar_Get("bot_forceclustering", "0", 0); //force cluster calculations Cvar_Get("bot_forcereachability", "0", 0); //force reachability calculations Cvar_Get("bot_forcewrite", "0", 0); //force writing aas file Cvar_Get("bot_aasoptimize", "0", 0); //no aas file optimisation Cvar_Get("bot_saveroutingcache", "0", 0); //save routing cache Cvar_Get("bot_thinktime", "100", CVAR_CHEAT); //msec the bots thinks Cvar_Get("bot_reloadcharacters", "0", 0); //reload the bot characters each time Cvar_Get("bot_testichat", "0", 0); //test ichats Cvar_Get("bot_testrchat", "0", 0); //test rchats Cvar_Get("bot_testsolid", "0", CVAR_CHEAT); //test for solid areas Cvar_Get("bot_testclusters", "0", CVAR_CHEAT); //test the AAS clusters Cvar_Get("bot_fastchat", "0", 0); //fast chatting bots Cvar_Get("bot_nochat", "0", 0); //disable chats Cvar_Get("bot_pause", "0", CVAR_CHEAT); //pause the bots thinking Cvar_Get("bot_report", "0", CVAR_CHEAT); //get a full report in ctf Cvar_Get("bot_grapple", "0", 0); //enable grapple Cvar_Get("bot_rocketjump", "1", 0); //enable rocket jumping Cvar_Get("bot_challenge", "0", 0); //challenging bot Cvar_Get("bot_minplayers", "0", 0); //minimum players in a team or the game Cvar_Get("bot_interbreedchar", "", CVAR_CHEAT); //bot character used for interbreeding Cvar_Get("bot_interbreedbots", "10", CVAR_CHEAT); //number of bots used for interbreeding Cvar_Get("bot_interbreedcycle", "20", CVAR_CHEAT); //bot interbreeding cycle Cvar_Get("bot_interbreedwrite", "", CVAR_CHEAT); //write interbreeded bots to this file } /* ================== SV_BotInitBotLib ================== */ void SV_BotInitBotLib(void) { botlib_import_t botlib_import; if ( !Cvar_VariableValue("fs_restrict") && !Sys_CheckCD() ) { Com_Error( ERR_NEED_CD, "Game CD not in drive" ); } if (debugpolygons) Z_Free(debugpolygons); bot_maxdebugpolys = Cvar_VariableIntegerValue("bot_maxdebugpolys"); debugpolygons = (bot_debugpoly_t*) Z_Malloc(sizeof(bot_debugpoly_t)* bot_maxdebugpolys); botlib_import.Print = BotImport_Print; botlib_import.Trace = BotImport_Trace; botlib_import.EntityTrace = BotImport_EntityTrace; botlib_import.PointContents = BotImport_PointContents; botlib_import.inPVS = BotImport_inPVS; botlib_import.BSPEntityData = BotImport_BSPEntityData; botlib_import.BSPModelMinsMaxsOrigin = BotImport_BSPModelMinsMaxsOrigin; botlib_import.BotClientCommand = BotClientCommand; //memory management botlib_import.GetMemory = BotImport_GetMemory; botlib_import.FreeMemory = BotImport_FreeMemory; botlib_import.AvailableMemory = Z_AvailableMemory; botlib_import.HunkAlloc = BotImport_HunkAlloc; // file system access botlib_import.FS_FOpenFile = FS_FOpenFileByMode; botlib_import.FS_Read = FS_Read2; botlib_import.FS_Write = FS_Write; botlib_import.FS_FCloseFile = FS_FCloseFile; botlib_import.FS_Seek = FS_Seek; //debug lines botlib_import.DebugLineCreate = BotImport_DebugLineCreate; botlib_import.DebugLineDelete = BotImport_DebugLineDelete; botlib_import.DebugLineShow = BotImport_DebugLineShow; //debug polygons botlib_import.DebugPolygonCreate = BotImport_DebugPolygonCreate; botlib_import.DebugPolygonDelete = BotImport_DebugPolygonDelete; botlib_export = (botlib_export_t *)GetBotLibAPI( BOTLIB_API_VERSION, &botlib_import ); assert(botlib_export); // bk001129 - somehow we end up with a zero import. } // // * * * BOT AI CODE IS BELOW THIS POINT * * * // /* ================== SV_BotGetConsoleMessage ================== */ int SV_BotGetConsoleMessage( int client, char *buf, int size ) { client_t *cl; int index; cl = &svs.clients[client]; cl->lastPacketTime = svs.time; if ( cl->reliableAcknowledge == cl->reliableSequence ) { return qfalse; } cl->reliableAcknowledge++; index = cl->reliableAcknowledge & ( MAX_RELIABLE_COMMANDS - 1 ); if ( !cl->reliableCommands[index][0] ) { return qfalse; } Q_strncpyz( buf, cl->reliableCommands[index], size ); return qtrue; } #if 0 /* ================== EntityInPVS ================== */ int EntityInPVS( int client, int entityNum ) { client_t *cl; clientSnapshot_t *frame; int i; cl = &svs.clients[client]; frame = &cl->frames[cl->netchan.outgoingSequence & PACKET_MASK]; for ( i = 0; i < frame->num_entities; i++ ) { if ( svs.snapshotEntities[(frame->first_entity + i) % svs.numSnapshotEntities].number == entityNum ) { return qtrue; } } return qfalse; } #endif /* ================== SV_BotGetSnapshotEntity ================== */ int SV_BotGetSnapshotEntity( int client, int sequence ) { client_t *cl; clientSnapshot_t *frame; cl = &svs.clients[client]; frame = &cl->frames[cl->netchan.outgoingSequence & PACKET_MASK]; if (sequence < 0 || sequence >= frame->num_entities) { return -1; } return svs.snapshotEntities[(frame->first_entity + sequence) % svs.numSnapshotEntities].number; } ================================================ FILE: src/engine/server/sv_ccmds.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "server.h" /* =============================================================================== OPERATOR CONSOLE ONLY COMMANDS These commands can only be entered from stdin or by a remote operator datagram =============================================================================== */ /* ================== SV_GetPlayerByName Returns the player with name from Cmd_Argv(1) ================== */ static client_t *SV_GetPlayerByName( void ) { client_t *cl; int i; char *s; char cleanName[64]; // make sure server is running if ( !com_sv_running->integer ) { return NULL; } if ( Cmd_Argc() < 2 ) { Com_Printf( "No player specified.\n" ); return NULL; } s = Cmd_Argv(1); // check for a name match for ( i=0, cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++ ) { if ( !cl->state ) { continue; } if ( !Q_stricmp( cl->name, s ) ) { return cl; } Q_strncpyz( cleanName, cl->name, sizeof(cleanName) ); Q_CleanStr( cleanName ); if ( !Q_stricmp( cleanName, s ) ) { return cl; } } Com_Printf( "Player %s is not on the server\n", s ); return NULL; } /* ================== SV_GetPlayerByNum Returns the player with idnum from Cmd_Argv(1) ================== */ static client_t *SV_GetPlayerByNum( void ) { client_t *cl; int i; int idnum; char *s; // make sure server is running if ( !com_sv_running->integer ) { return NULL; } if ( Cmd_Argc() < 2 ) { Com_Printf( "No player specified.\n" ); return NULL; } s = Cmd_Argv(1); for (i = 0; s[i]; i++) { if (s[i] < '0' || s[i] > '9') { Com_Printf( "Bad slot number: %s\n", s); return NULL; } } idnum = atoi( s ); if ( idnum < 0 || idnum >= sv_maxclients->integer ) { Com_Printf( "Bad client slot: %i\n", idnum ); return NULL; } cl = &svs.clients[idnum]; if ( !cl->state ) { Com_Printf( "Client %i is not active\n", idnum ); return NULL; } return cl; return NULL; } //========================================================= /* ================== SV_Map_f Restart the server on a different map ================== */ static void SV_Map_f( void ) { char *cmd; char *map; qboolean killBots, cheat; char expanded[MAX_QPATH]; char mapname[MAX_QPATH]; map = Cmd_Argv(1); if ( !map ) { return; } // make sure the level exists before trying to change, so that // a typo at the server console won't end the game Com_sprintf (expanded, sizeof(expanded), "maps/%s.bsp", map); if ( FS_ReadFile (expanded, NULL) == -1 ) { Com_Printf ("Can't find map %s\n", expanded); return; } // force latched values to get set Cvar_Get ("g_gametype", "0", CVAR_SERVERINFO | CVAR_USERINFO | CVAR_LATCH ); cmd = Cmd_Argv(0); if( Q_stricmpn( cmd, "sp", 2 ) == 0 ) { Cvar_SetValue( "g_gametype", GT_SINGLE_PLAYER ); Cvar_SetValue( "g_doWarmup", 0 ); // may not set sv_maxclients directly, always set latched Cvar_SetLatched( "sv_maxclients", "8" ); cmd += 2; cheat = qfalse; killBots = qtrue; } else { if ( !Q_stricmp( cmd, "devmap" ) || !Q_stricmp( cmd, "spdevmap" ) ) { cheat = qtrue; killBots = qtrue; } else { cheat = qfalse; killBots = qfalse; } if( sv_gametype->integer == GT_SINGLE_PLAYER ) { Cvar_SetValue( "g_gametype", GT_FFA ); } } // save the map name here cause on a map restart we reload the q3config.cfg // and thus nuke the arguments of the map command Q_strncpyz(mapname, map, sizeof(mapname)); // start up the map SV_SpawnServer( mapname, killBots ); // set the cheat value // if the level was started with "map ", then // cheats will not be allowed. If started with "devmap " // then cheats will be allowed if ( cheat ) { Cvar_Set( "sv_cheats", "1" ); } else { Cvar_Set( "sv_cheats", "0" ); } } /* ================ SV_MapRestart_f Completely restarts a level, but doesn't send a new gamestate to the clients. This allows fair starts with variable load times. ================ */ static void SV_MapRestart_f( void ) { int i; client_t *client; char *denied; qboolean isBot; int delay; // make sure we aren't restarting twice in the same frame if ( com_frameTime == sv.serverId ) { return; } // make sure server is running if ( !com_sv_running->integer ) { Com_Printf( "Server is not running.\n" ); return; } if ( sv.restartTime ) { return; } if (Cmd_Argc() > 1 ) { delay = atoi( Cmd_Argv(1) ); } else { delay = 5; } if( delay && !Cvar_VariableValue("g_doWarmup") ) { sv.restartTime = svs.time + delay * 1000; SV_SetConfigstring( CS_WARMUP, va("%i", sv.restartTime) ); return; } // check for changes in variables that can't just be restarted // check for maxclients change if ( sv_maxclients->modified || sv_gametype->modified ) { char mapname[MAX_QPATH]; Com_Printf( "variable change -- restarting.\n" ); // restart the map the slow way Q_strncpyz( mapname, Cvar_VariableString( "mapname" ), sizeof( mapname ) ); SV_SpawnServer( mapname, qfalse ); return; } // toggle the server bit so clients can detect that a // map_restart has happened svs.snapFlagServerBit ^= SNAPFLAG_SERVERCOUNT; // generate a new serverid // TTimo - don't update restartedserverId there, otherwise we won't deal correctly with multiple map_restart sv.serverId = com_frameTime; Cvar_Set( "sv_serverid", va("%i", sv.serverId ) ); // reset all the vm data in place without changing memory allocation // note that we do NOT set sv.state = SS_LOADING, so configstrings that // had been changed from their default values will generate broadcast updates sv.state = SS_LOADING; sv.restarting = qtrue; SV_RestartGameProgs(); // run a few frames to allow everything to settle for ( i = 0 ;i < 3 ; i++ ) { VM_Call( gvm, GAME_RUN_FRAME, svs.time ); svs.time += 100; } sv.state = SS_GAME; sv.restarting = qfalse; // connect and begin all the clients for (i=0 ; iinteger ; i++) { client = &svs.clients[i]; // send the new gamestate to all connected clients if ( client->state < CS_CONNECTED) { continue; } if ( client->netchan.remoteAddress.type == NA_BOT ) { isBot = qtrue; } else { isBot = qfalse; } // add the map_restart command SV_AddServerCommand( client, "map_restart\n" ); // connect the client again, without the firstTime flag denied = (char*) VM_ExplicitArgPtr( gvm, VM_Call( gvm, GAME_CLIENT_CONNECT, i, qfalse, isBot ) ); if ( denied ) { // this generally shouldn't happen, because the client // was connected before the level change SV_DropClient( client, denied ); Com_Printf( "SV_MapRestart_f(%d): dropped client %i - denied!\n", delay, i ); // bk010125 continue; } client->state = CS_ACTIVE; SV_ClientEnterWorld( client, &client->lastUsercmd ); } // run another frame to allow things to look at all the players VM_Call( gvm, GAME_RUN_FRAME, svs.time ); svs.time += 100; } //=============================================================== /* ================== SV_Kick_f Kick a user off of the server FIXME: move to game ================== */ static void SV_Kick_f( void ) { client_t *cl; int i; // make sure server is running if ( !com_sv_running->integer ) { Com_Printf( "Server is not running.\n" ); return; } if ( Cmd_Argc() != 2 ) { Com_Printf ("Usage: kick \nkick all = kick everyone\nkick allbots = kick all bots\n"); return; } cl = SV_GetPlayerByName(); if ( !cl ) { if ( !Q_stricmp(Cmd_Argv(1), "all") ) { for ( i=0, cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++ ) { if ( !cl->state ) { continue; } if( cl->netchan.remoteAddress.type == NA_LOOPBACK ) { continue; } SV_DropClient( cl, "was kicked" ); cl->lastPacketTime = svs.time; // in case there is a funny zombie } } else if ( !Q_stricmp(Cmd_Argv(1), "allbots") ) { for ( i=0, cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++ ) { if ( !cl->state ) { continue; } if( cl->netchan.remoteAddress.type != NA_BOT ) { continue; } SV_DropClient( cl, "was kicked" ); cl->lastPacketTime = svs.time; // in case there is a funny zombie } } return; } if( cl->netchan.remoteAddress.type == NA_LOOPBACK ) { SV_SendServerCommand(NULL, "print \"%s\"", "Cannot kick host player\n"); return; } SV_DropClient( cl, "was kicked" ); cl->lastPacketTime = svs.time; // in case there is a funny zombie } /* ================== SV_Ban_f Ban a user from being able to play on this server through the auth server ================== */ static void SV_Ban_f( void ) { client_t *cl; // make sure server is running if ( !com_sv_running->integer ) { Com_Printf( "Server is not running.\n" ); return; } if ( Cmd_Argc() != 2 ) { Com_Printf ("Usage: banUser \n"); return; } cl = SV_GetPlayerByName(); if (!cl) { return; } if( cl->netchan.remoteAddress.type == NA_LOOPBACK ) { SV_SendServerCommand(NULL, "print \"%s\"", "Cannot kick host player\n"); return; } // look up the authorize server's IP if ( !svs.authorizeAddress.ip[0] && svs.authorizeAddress.type != NA_BAD ) { Com_Printf( "Resolving %s\n", AUTHORIZE_SERVER_NAME ); if ( !NET_StringToAdr( AUTHORIZE_SERVER_NAME, &svs.authorizeAddress ) ) { Com_Printf( "Couldn't resolve address\n" ); return; } svs.authorizeAddress.port = BigShort( PORT_AUTHORIZE ); Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", AUTHORIZE_SERVER_NAME, svs.authorizeAddress.ip[0], svs.authorizeAddress.ip[1], svs.authorizeAddress.ip[2], svs.authorizeAddress.ip[3], BigShort( svs.authorizeAddress.port ) ); } // otherwise send their ip to the authorize server if ( svs.authorizeAddress.type != NA_BAD ) { NET_OutOfBandPrint( NS_SERVER, svs.authorizeAddress, "banUser %i.%i.%i.%i", cl->netchan.remoteAddress.ip[0], cl->netchan.remoteAddress.ip[1], cl->netchan.remoteAddress.ip[2], cl->netchan.remoteAddress.ip[3] ); Com_Printf("%s was banned from coming back\n", cl->name); } } /* ================== SV_BanNum_f Ban a user from being able to play on this server through the auth server ================== */ static void SV_BanNum_f( void ) { client_t *cl; // make sure server is running if ( !com_sv_running->integer ) { Com_Printf( "Server is not running.\n" ); return; } if ( Cmd_Argc() != 2 ) { Com_Printf ("Usage: banClient \n"); return; } cl = SV_GetPlayerByNum(); if ( !cl ) { return; } if( cl->netchan.remoteAddress.type == NA_LOOPBACK ) { SV_SendServerCommand(NULL, "print \"%s\"", "Cannot kick host player\n"); return; } // look up the authorize server's IP if ( !svs.authorizeAddress.ip[0] && svs.authorizeAddress.type != NA_BAD ) { Com_Printf( "Resolving %s\n", AUTHORIZE_SERVER_NAME ); if ( !NET_StringToAdr( AUTHORIZE_SERVER_NAME, &svs.authorizeAddress ) ) { Com_Printf( "Couldn't resolve address\n" ); return; } svs.authorizeAddress.port = BigShort( PORT_AUTHORIZE ); Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", AUTHORIZE_SERVER_NAME, svs.authorizeAddress.ip[0], svs.authorizeAddress.ip[1], svs.authorizeAddress.ip[2], svs.authorizeAddress.ip[3], BigShort( svs.authorizeAddress.port ) ); } // otherwise send their ip to the authorize server if ( svs.authorizeAddress.type != NA_BAD ) { NET_OutOfBandPrint( NS_SERVER, svs.authorizeAddress, "banUser %i.%i.%i.%i", cl->netchan.remoteAddress.ip[0], cl->netchan.remoteAddress.ip[1], cl->netchan.remoteAddress.ip[2], cl->netchan.remoteAddress.ip[3] ); Com_Printf("%s was banned from coming back\n", cl->name); } } /* ================== SV_KickNum_f Kick a user off of the server FIXME: move to game ================== */ static void SV_KickNum_f( void ) { client_t *cl; // make sure server is running if ( !com_sv_running->integer ) { Com_Printf( "Server is not running.\n" ); return; } if ( Cmd_Argc() != 2 ) { Com_Printf ("Usage: kicknum \n"); return; } cl = SV_GetPlayerByNum(); if ( !cl ) { return; } if( cl->netchan.remoteAddress.type == NA_LOOPBACK ) { SV_SendServerCommand(NULL, "print \"%s\"", "Cannot kick host player\n"); return; } SV_DropClient( cl, "was kicked" ); cl->lastPacketTime = svs.time; // in case there is a funny zombie } /* ================ SV_Status_f ================ */ static void SV_Status_f( void ) { int i, j, l; client_t *cl; playerState_t *ps; const char *s; int ping; // make sure server is running if ( !com_sv_running->integer ) { Com_Printf( "Server is not running.\n" ); return; } Com_Printf ("map: %s\n", sv_mapname->string ); Com_Printf ("num score ping name lastmsg address qport rate\n"); Com_Printf ("--- ----- ---- --------------- ------- --------------------- ----- -----\n"); for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) { if (!cl->state) continue; Com_Printf ("%3i ", i); ps = SV_GameClientNum( i ); Com_Printf ("%5i ", ps->persistant[PERS_SCORE]); if (cl->state == CS_CONNECTED) Com_Printf ("CNCT "); else if (cl->state == CS_ZOMBIE) Com_Printf ("ZMBI "); else { ping = cl->ping < 9999 ? cl->ping : 9999; Com_Printf ("%4i ", ping); } Com_Printf ("%s", cl->name); // TTimo adding a ^7 to reset the color // NOTE: colored names in status breaks the padding (WONTFIX) Com_Printf ("^7"); l = 16 - (int)strlen(cl->name); for (j=0 ; jlastPacketTime ); s = NET_AdrToString( cl->netchan.remoteAddress ); Com_Printf ("%s", s); l = 22 - (int)strlen(s); for (j=0 ; jnetchan.qport); Com_Printf (" %5i", cl->rate); Com_Printf ("\n"); } Com_Printf ("\n"); } /* ================== SV_ConSay_f ================== */ static void SV_ConSay_f(void) { char *p; char text[1024]; // make sure server is running if ( !com_sv_running->integer ) { Com_Printf( "Server is not running.\n" ); return; } if ( Cmd_Argc () < 2 ) { return; } strcpy (text, "console: "); p = Cmd_Args(); if ( *p == '"' ) { p++; p[strlen(p)-1] = 0; } strcat(text, p); SV_SendServerCommand(NULL, "chat \"%s\n\"", text); } /* ================== SV_Heartbeat_f Also called by SV_DropClient, SV_DirectConnect, and SV_SpawnServer ================== */ void SV_Heartbeat_f( void ) { svs.nextHeartbeatTime = -9999999; } /* =========== SV_Serverinfo_f Examine the serverinfo string =========== */ static void SV_Serverinfo_f( void ) { Com_Printf ("Server info settings:\n"); Info_Print ( Cvar_InfoString( CVAR_SERVERINFO ) ); } /* =========== SV_Systeminfo_f Examine or change the serverinfo string =========== */ static void SV_Systeminfo_f( void ) { Com_Printf ("System info settings:\n"); Info_Print ( Cvar_InfoString( CVAR_SYSTEMINFO ) ); } /* =========== SV_DumpUser_f Examine all a users info strings FIXME: move to game =========== */ static void SV_DumpUser_f( void ) { client_t *cl; // make sure server is running if ( !com_sv_running->integer ) { Com_Printf( "Server is not running.\n" ); return; } if ( Cmd_Argc() != 2 ) { Com_Printf ("Usage: info \n"); return; } cl = SV_GetPlayerByName(); if ( !cl ) { return; } Com_Printf( "userinfo\n" ); Com_Printf( "--------\n" ); Info_Print( cl->userinfo ); } /* ================= SV_KillServer ================= */ static void SV_KillServer_f( void ) { SV_Shutdown( "killserver" ); } //=========================================================== /* ================== SV_AddOperatorCommands ================== */ void SV_AddOperatorCommands( void ) { static qboolean initialized; if ( initialized ) { return; } initialized = qtrue; Cmd_AddCommand ("heartbeat", SV_Heartbeat_f); Cmd_AddCommand ("kick", SV_Kick_f); Cmd_AddCommand ("banUser", SV_Ban_f); Cmd_AddCommand ("banClient", SV_BanNum_f); Cmd_AddCommand ("clientkick", SV_KickNum_f); Cmd_AddCommand ("status", SV_Status_f); Cmd_AddCommand ("serverinfo", SV_Serverinfo_f); Cmd_AddCommand ("systeminfo", SV_Systeminfo_f); Cmd_AddCommand ("dumpuser", SV_DumpUser_f); Cmd_AddCommand ("map_restart", SV_MapRestart_f); Cmd_AddCommand ("sectorlist", SV_SectorList_f); Cmd_AddCommand ("map", SV_Map_f); #ifndef PRE_RELEASE_DEMO Cmd_AddCommand ("devmap", SV_Map_f); Cmd_AddCommand ("spmap", SV_Map_f); Cmd_AddCommand ("spdevmap", SV_Map_f); #endif Cmd_AddCommand ("killserver", SV_KillServer_f); if( com_dedicated->integer ) { Cmd_AddCommand ("say", SV_ConSay_f); } } /* ================== SV_RemoveOperatorCommands ================== */ void SV_RemoveOperatorCommands( void ) { #if 0 // removing these won't let the server start again Cmd_RemoveCommand ("heartbeat"); Cmd_RemoveCommand ("kick"); Cmd_RemoveCommand ("banUser"); Cmd_RemoveCommand ("banClient"); Cmd_RemoveCommand ("status"); Cmd_RemoveCommand ("serverinfo"); Cmd_RemoveCommand ("systeminfo"); Cmd_RemoveCommand ("dumpuser"); Cmd_RemoveCommand ("map_restart"); Cmd_RemoveCommand ("sectorlist"); Cmd_RemoveCommand ("say"); #endif } ================================================ FILE: src/engine/server/sv_client.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // sv_client.c -- server code for dealing with clients #include "server.h" static void SV_CloseDownload( client_t *cl ); /* ================= SV_GetChallenge A "getchallenge" OOB command has been received Returns a challenge number that can be used in a subsequent connectResponse command. We do this to prevent denial of service attacks that flood the server with invalid connection IPs. With a challenge, they must give a valid IP address. If we are authorizing, a challenge request will cause a packet to be sent to the authorize server. When an authorizeip is returned, a challenge response will be sent to that ip. ================= */ void SV_GetChallenge( netadr_t from ) { int i; int oldest; int oldestTime; challenge_t *challenge; // ignore if we are in single player if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive")) { return; } oldest = 0; oldestTime = 0x7fffffff; // see if we already have a challenge for this ip challenge = &svs.challenges[0]; for (i = 0 ; i < MAX_CHALLENGES ; i++, challenge++) { if ( !challenge->connected && NET_CompareAdr( from, challenge->adr ) ) { break; } if ( challenge->time < oldestTime ) { oldestTime = challenge->time; oldest = i; } } if (i == MAX_CHALLENGES) { // this is the first time this client has asked for a challenge challenge = &svs.challenges[oldest]; challenge->challenge = ( (rand() << 16) ^ rand() ) ^ svs.time; challenge->adr = from; challenge->firstTime = svs.time; challenge->time = svs.time; challenge->connected = qfalse; i = oldest; } // if they are on a lan address, send the challengeResponse immediately if ( Sys_IsLANAddress( from ) ) { challenge->pingTime = svs.time; NET_OutOfBandPrint( NS_SERVER, from, "challengeResponse %i", challenge->challenge ); return; } // look up the authorize server's IP if ( !svs.authorizeAddress.ip[0] && svs.authorizeAddress.type != NA_BAD ) { Com_Printf( "Resolving %s\n", AUTHORIZE_SERVER_NAME ); if ( !NET_StringToAdr( AUTHORIZE_SERVER_NAME, &svs.authorizeAddress ) ) { Com_Printf( "Couldn't resolve address\n" ); return; } svs.authorizeAddress.port = BigShort( PORT_AUTHORIZE ); Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", AUTHORIZE_SERVER_NAME, svs.authorizeAddress.ip[0], svs.authorizeAddress.ip[1], svs.authorizeAddress.ip[2], svs.authorizeAddress.ip[3], BigShort( svs.authorizeAddress.port ) ); } // if they have been challenging for a long time and we // haven't heard anything from the authorize server, go ahead and // let them in, assuming the id server is down if ( svs.time - challenge->firstTime > AUTHORIZE_TIMEOUT ) { Com_DPrintf( "authorize server timed out\n" ); challenge->pingTime = svs.time; NET_OutOfBandPrint( NS_SERVER, challenge->adr, "challengeResponse %i", challenge->challenge ); return; } // otherwise send their ip to the authorize server if ( svs.authorizeAddress.type != NA_BAD ) { cvar_t *fs; char game[1024]; Com_DPrintf( "sending getIpAuthorize for %s\n", NET_AdrToString( from )); strcpy(game, BASEGAME); fs = Cvar_Get ("fs_game", "", CVAR_INIT|CVAR_SYSTEMINFO ); if (fs && fs->string[0] != 0) { strcpy(game, fs->string); } // the 0 is for backwards compatibility with obsolete sv_allowanonymous flags // getIpAuthorize 0 NET_OutOfBandPrint( NS_SERVER, svs.authorizeAddress, "getIpAuthorize %i %i.%i.%i.%i %s 0 %s", svs.challenges[i].challenge, from.ip[0], from.ip[1], from.ip[2], from.ip[3], game, sv_strictAuth->string ); } } /* ==================== SV_AuthorizeIpPacket A packet has been returned from the authorize server. If we have a challenge adr for that ip, send the challengeResponse to it ==================== */ void SV_AuthorizeIpPacket( netadr_t from ) { int challenge; int i; char *s; char *r; char ret[1024]; if ( !NET_CompareBaseAdr( from, svs.authorizeAddress ) ) { Com_Printf( "SV_AuthorizeIpPacket: not from authorize server\n" ); return; } challenge = atoi( Cmd_Argv( 1 ) ); for (i = 0 ; i < MAX_CHALLENGES ; i++) { if ( svs.challenges[i].challenge == challenge ) { break; } } if ( i == MAX_CHALLENGES ) { Com_Printf( "SV_AuthorizeIpPacket: challenge not found\n" ); return; } // send a packet back to the original client svs.challenges[i].pingTime = svs.time; s = Cmd_Argv( 2 ); r = Cmd_Argv( 3 ); // reason if ( !Q_stricmp( s, "demo" ) ) { if ( Cvar_VariableValue( "fs_restrict" ) ) { // a demo client connecting to a demo server NET_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, "challengeResponse %i", svs.challenges[i].challenge ); return; } // they are a demo client trying to connect to a real server NET_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, "print\nServer is not a demo server\n" ); // clear the challenge record so it won't timeout and let them through Com_Memset( &svs.challenges[i], 0, sizeof( svs.challenges[i] ) ); return; } if ( !Q_stricmp( s, "accept" ) ) { NET_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, "challengeResponse %i", svs.challenges[i].challenge ); return; } if ( !Q_stricmp( s, "unknown" ) ) { if (!r) { NET_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, "print\nAwaiting CD key authorization\n" ); } else { sprintf(ret, "print\n%s\n", r); NET_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, ret ); } // clear the challenge record so it won't timeout and let them through Com_Memset( &svs.challenges[i], 0, sizeof( svs.challenges[i] ) ); return; } // authorization failed if (!r) { NET_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, "print\nSomeone is using this CD Key\n" ); } else { sprintf(ret, "print\n%s\n", r); NET_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, ret ); } // clear the challenge record so it won't timeout and let them through Com_Memset( &svs.challenges[i], 0, sizeof( svs.challenges[i] ) ); } /* ================== SV_DirectConnect A "connect" OOB command has been received ================== */ #define PB_MESSAGE "PunkBuster Anti-Cheat software must be installed " \ "and Enabled in order to join this server. An updated game patch can be downloaded from " \ "www.idsoftware.com" void SV_DirectConnect( netadr_t from ) { char userinfo[MAX_INFO_STRING]; int i; client_t *cl, *newcl; MAC_STATIC client_t temp; sharedEntity_t *ent; int clientNum; int version; int qport; int challenge; char *password; int startIndex; intptr_t denied; int count; Com_DPrintf ("SVC_DirectConnect ()\n"); Q_strncpyz( userinfo, Cmd_Argv(1), sizeof(userinfo) ); version = atoi( Info_ValueForKey( userinfo, "protocol" ) ); if ( version != PROTOCOL_VERSION ) { NET_OutOfBandPrint( NS_SERVER, from, "print\nServer uses protocol version %i.\n", PROTOCOL_VERSION ); Com_DPrintf (" rejected connect from version %i\n", version); return; } challenge = atoi( Info_ValueForKey( userinfo, "challenge" ) ); qport = atoi( Info_ValueForKey( userinfo, "qport" ) ); // quick reject for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) { if ( cl->state == CS_FREE ) { continue; } if ( NET_CompareBaseAdr( from, cl->netchan.remoteAddress ) && ( cl->netchan.qport == qport || from.port == cl->netchan.remoteAddress.port ) ) { if (( svs.time - cl->lastConnectTime) < (sv_reconnectlimit->integer * 1000)) { Com_DPrintf ("%s:reconnect rejected : too soon\n", NET_AdrToString (from)); return; } break; } } // see if the challenge is valid (LAN clients don't need to challenge) if ( !NET_IsLocalAddress (from) ) { int ping; for (i=0 ; ivalue && ping < sv_minPing->value ) { // don't let them keep trying until they get a big delay NET_OutOfBandPrint( NS_SERVER, from, "print\nServer is for high pings only\n" ); Com_DPrintf ("Client %i rejected on a too low ping\n", i); // reset the address otherwise their ping will keep increasing // with each connect message and they'd eventually be able to connect svs.challenges[i].adr.port = 0; return; } if ( sv_maxPing->value && ping > sv_maxPing->value ) { NET_OutOfBandPrint( NS_SERVER, from, "print\nServer is for low pings only\n" ); Com_DPrintf ("Client %i rejected on a too high ping\n", i); return; } } } else { // force the "ip" info key to "localhost" Info_SetValueForKey( userinfo, "ip", "localhost" ); } newcl = &temp; Com_Memset (newcl, 0, sizeof(client_t)); // if there is already a slot for this ip, reuse it for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) { if ( cl->state == CS_FREE ) { continue; } if ( NET_CompareBaseAdr( from, cl->netchan.remoteAddress ) && ( cl->netchan.qport == qport || from.port == cl->netchan.remoteAddress.port ) ) { Com_Printf ("%s:reconnect\n", NET_AdrToString (from)); newcl = cl; // this doesn't work because it nukes the players userinfo // // disconnect the client from the game first so any flags the // // player might have are dropped // VM_Call( gvm, GAME_CLIENT_DISCONNECT, newcl - svs.clients ); // goto gotnewcl; } } // find a client slot // if "sv_privateClients" is set > 0, then that number // of client slots will be reserved for connections that // have "password" set to the value of "sv_privatePassword" // Info requests will report the maxclients as if the private // slots didn't exist, to prevent people from trying to connect // to a full server. // This is to allow us to reserve a couple slots here on our // servers so we can play without having to kick people. // check for privateClient password password = Info_ValueForKey( userinfo, "password" ); if ( !strcmp( password, sv_privatePassword->string ) ) { startIndex = 0; } else { // skip past the reserved slots startIndex = sv_privateClients->integer; } newcl = NULL; for ( i = startIndex; i < sv_maxclients->integer ; i++ ) { cl = &svs.clients[i]; if (cl->state == CS_FREE) { newcl = cl; break; } } if ( !newcl ) { if ( NET_IsLocalAddress( from ) ) { count = 0; for ( i = startIndex; i < sv_maxclients->integer ; i++ ) { cl = &svs.clients[i]; if (cl->netchan.remoteAddress.type == NA_BOT) { count++; } } // if they're all bots if (count >= sv_maxclients->integer - startIndex) { SV_DropClient(&svs.clients[sv_maxclients->integer - 1], "only bots on server"); newcl = &svs.clients[sv_maxclients->integer - 1]; } else { Com_Error( ERR_FATAL, "server is full on local connect\n" ); return; } } else { NET_OutOfBandPrint( NS_SERVER, from, "print\nServer is full.\n" ); Com_DPrintf ("Rejected a connection.\n"); return; } } // we got a newcl, so reset the reliableSequence and reliableAcknowledge cl->reliableAcknowledge = 0; cl->reliableSequence = 0; gotnewcl: // build a new connection // accept the new client // this is the only place a client_t is ever initialized *newcl = temp; clientNum = newcl - svs.clients; ent = SV_GentityNum( clientNum ); newcl->gentity = ent; // save the challenge newcl->challenge = challenge; // save the address Netchan_Setup (NS_SERVER, &newcl->netchan , from, qport); // init the netchan queue newcl->netchan_end_queue = &newcl->netchan_start_queue; // save the userinfo Q_strncpyz( newcl->userinfo, userinfo, sizeof(newcl->userinfo) ); // get the game a chance to reject this connection or modify the userinfo denied = VM_Call( gvm, GAME_CLIENT_CONNECT, clientNum, qtrue, qfalse ); // firstTime = qtrue if ( denied ) { // we can't just use VM_ArgPtr, because that is only valid inside a VM_Call const char* str = (const char*)VM_ExplicitArgPtr( gvm, denied ); NET_OutOfBandPrint( NS_SERVER, from, "print\n%s\n", str ); Com_DPrintf ("Game rejected a connection: %s.\n", str); return; } SV_UserinfoChanged( newcl ); // send the connect packet to the client NET_OutOfBandPrint( NS_SERVER, from, "connectResponse" ); Com_DPrintf( "Going from CS_FREE to CS_CONNECTED for %s\n", newcl->name ); newcl->state = CS_CONNECTED; newcl->nextSnapshotTime = svs.time; newcl->lastPacketTime = svs.time; newcl->lastConnectTime = svs.time; // when we receive the first packet from the client, we will // notice that it is from a different serverid and that the // gamestate message was not just sent, forcing a retransmit newcl->gamestateMessageNum = -1; // if this was the first client on the server, or the last client // the server can hold, send a heartbeat to the master. count = 0; for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) { if ( svs.clients[i].state >= CS_CONNECTED ) { count++; } } if ( count == 1 || count == sv_maxclients->integer ) { SV_Heartbeat_f(); } } /* ===================== SV_DropClient Called when the player is totally leaving the server, either willingly or unwillingly. This is NOT called if the entire server is quiting or crashing -- SV_FinalMessage() will handle that ===================== */ void SV_DropClient( client_t *drop, const char *reason ) { int i; challenge_t *challenge; if ( drop->state == CS_ZOMBIE ) { return; // already dropped } if ( !drop->gentity || !(drop->gentity->r.svFlags & SVF_BOT) ) { // see if we already have a challenge for this ip challenge = &svs.challenges[0]; for (i = 0 ; i < MAX_CHALLENGES ; i++, challenge++) { if ( NET_CompareAdr( drop->netchan.remoteAddress, challenge->adr ) ) { challenge->connected = qfalse; break; } } } // Kill any download SV_CloseDownload( drop ); // tell everyone why they got dropped SV_SendServerCommand( NULL, "print \"%s" S_COLOR_WHITE " %s\n\"", drop->name, reason ); Com_DPrintf( "Going to CS_ZOMBIE for %s\n", drop->name ); drop->state = CS_ZOMBIE; // become free in a few seconds if (drop->download) { FS_FCloseFile( drop->download ); drop->download = 0; } // call the prog function for removing a client // this will remove the body, among other things VM_Call( gvm, GAME_CLIENT_DISCONNECT, drop - svs.clients ); // add the disconnect command SV_SendServerCommand( drop, "disconnect \"%s\"", reason); if ( drop->netchan.remoteAddress.type == NA_BOT ) { SV_BotFreeClient( drop - svs.clients ); } // nuke user info SV_SetUserinfo( drop - svs.clients, "" ); // if this was the last client on the server, send a heartbeat // to the master so it is known the server is empty // send a heartbeat now so the master will get up to date info // if there is already a slot for this ip, reuse it for (i=0 ; i < sv_maxclients->integer ; i++ ) { if ( svs.clients[i].state >= CS_CONNECTED ) { break; } } if ( i == sv_maxclients->integer ) { SV_Heartbeat_f(); } } /* ================ SV_SendClientGameState Sends the first message from the server to a connected client. This will be sent on the initial connection and upon each new map load. It will be resent if the client acknowledges a later message but has the wrong gamestate. ================ */ void SV_SendClientGameState( client_t *client ) { int start; entityState_t *base, nullstate; msg_t msg; byte msgBuffer[MAX_MSGLEN]; Com_DPrintf ("SV_SendClientGameState() for %s\n", client->name); Com_DPrintf( "Going from CS_CONNECTED to CS_PRIMED for %s\n", client->name ); client->state = CS_PRIMED; client->pureAuthentic = 0; client->gotCP = qfalse; // when we receive the first packet from the client, we will // notice that it is from a different serverid and that the // gamestate message was not just sent, forcing a retransmit client->gamestateMessageNum = client->netchan.outgoingSequence; MSG_Init( &msg, msgBuffer, sizeof( msgBuffer ) ); // NOTE, MRE: all server->client messages now acknowledge // let the client know which reliable clientCommands we have received MSG_WriteLong( &msg, client->lastClientCommand ); // send any server commands waiting to be sent first. // we have to do this cause we send the client->reliableSequence // with a gamestate and it sets the clc.serverCommandSequence at // the client side SV_UpdateServerCommandsToClient( client, &msg ); // send the gamestate MSG_WriteByte( &msg, svc_gamestate ); MSG_WriteLong( &msg, client->reliableSequence ); // write the configstrings for ( start = 0 ; start < MAX_CONFIGSTRINGS ; start++ ) { if (sv.configstrings[start][0]) { MSG_WriteByte( &msg, svc_configstring ); MSG_WriteShort( &msg, start ); MSG_WriteBigString( &msg, sv.configstrings[start] ); } } // write the baselines Com_Memset( &nullstate, 0, sizeof( nullstate ) ); for ( start = 0 ; start < MAX_GENTITIES; start++ ) { base = &sv.svEntities[start].baseline; if ( !base->number ) { continue; } MSG_WriteByte( &msg, svc_baseline ); MSG_WriteDeltaEntity( &msg, &nullstate, base, qtrue ); } MSG_WriteByte( &msg, svc_EOF ); MSG_WriteLong( &msg, client - svs.clients); // write the checksum feed MSG_WriteLong( &msg, sv.checksumFeed); // deliver this to the client SV_SendMessageToClient( &msg, client ); } /* ================== SV_ClientEnterWorld ================== */ void SV_ClientEnterWorld( client_t *client, usercmd_t *cmd ) { int clientNum; sharedEntity_t *ent; Com_DPrintf( "Going from CS_PRIMED to CS_ACTIVE for %s\n", client->name ); client->state = CS_ACTIVE; // set up the entity for the client clientNum = client - svs.clients; ent = SV_GentityNum( clientNum ); ent->s.number = clientNum; client->gentity = ent; client->deltaMessage = -1; client->nextSnapshotTime = svs.time; // generate a snapshot immediately client->lastUsercmd = *cmd; // call the game begin function VM_Call( gvm, GAME_CLIENT_BEGIN, client - svs.clients ); } /* ============================================================ CLIENT COMMAND EXECUTION ============================================================ */ /* ================== SV_CloseDownload clear/free any download vars ================== */ static void SV_CloseDownload( client_t *cl ) { int i; // EOF if (cl->download) { FS_FCloseFile( cl->download ); } cl->download = 0; *cl->downloadName = 0; // Free the temporary buffer space for (i = 0; i < MAX_DOWNLOAD_WINDOW; i++) { if (cl->downloadBlocks[i]) { Z_Free( cl->downloadBlocks[i] ); cl->downloadBlocks[i] = NULL; } } } /* ================== SV_StopDownload_f Abort a download if in progress ================== */ void SV_StopDownload_f( client_t *cl ) { if (*cl->downloadName) Com_DPrintf( "clientDownload: %d : file \"%s\" aborted\n", cl - svs.clients, cl->downloadName ); SV_CloseDownload( cl ); } /* ================== SV_DoneDownload_f Downloads are finished ================== */ void SV_DoneDownload_f( client_t *cl ) { Com_DPrintf( "clientDownload: %s Done\n", cl->name); // resend the game state to update any clients that entered during the download SV_SendClientGameState(cl); } /* ================== SV_NextDownload_f The argument will be the last acknowledged block from the client, it should be the same as cl->downloadClientBlock ================== */ void SV_NextDownload_f( client_t *cl ) { int block = atoi( Cmd_Argv(1) ); if (block == cl->downloadClientBlock) { Com_DPrintf( "clientDownload: %d : client acknowledge of block %d\n", cl - svs.clients, block ); // Find out if we are done. A zero-length block indicates EOF if (cl->downloadBlockSize[cl->downloadClientBlock % MAX_DOWNLOAD_WINDOW] == 0) { Com_Printf( "clientDownload: %d : file \"%s\" completed\n", cl - svs.clients, cl->downloadName ); SV_CloseDownload( cl ); return; } cl->downloadSendTime = svs.time; cl->downloadClientBlock++; return; } // We aren't getting an acknowledge for the correct block, drop the client // FIXME: this is bad... the client will never parse the disconnect message // because the cgame isn't loaded yet SV_DropClient( cl, "broken download" ); } /* ================== SV_BeginDownload_f ================== */ void SV_BeginDownload_f( client_t *cl ) { // Kill any existing download SV_CloseDownload( cl ); // cl->downloadName is non-zero now, SV_WriteDownloadToClient will see this and open // the file itself Q_strncpyz( cl->downloadName, Cmd_Argv(1), sizeof(cl->downloadName) ); } /* ================== SV_WriteDownloadToClient Check to see if the client wants a file, open it if needed and start pumping the client Fill up msg with data ================== */ void SV_WriteDownloadToClient( client_t *cl , msg_t *msg ) { int curindex; int rate; int blockspersnap; int idPack, missionPack; char errorMessage[1024]; if (!*cl->downloadName) return; // Nothing being downloaded if (!cl->download) { // We open the file here Com_Printf( "clientDownload: %d : begining \"%s\"\n", cl - svs.clients, cl->downloadName ); missionPack = FS_idPak(cl->downloadName, "missionpack"); idPack = missionPack || FS_idPak(cl->downloadName, "baseq3"); if ( !sv_allowDownload->integer || idPack || ( cl->downloadSize = FS_SV_FOpenFileRead( cl->downloadName, &cl->download ) ) <= 0 ) { // cannot auto-download file if (idPack) { Com_Printf("clientDownload: %d : \"%s\" cannot download id pk3 files\n", cl - svs.clients, cl->downloadName); if (missionPack) { Com_sprintf(errorMessage, sizeof(errorMessage), "Cannot autodownload Team Arena file \"%s\"\n" "The Team Arena mission pack can be found in your local game store.", cl->downloadName); } else { Com_sprintf(errorMessage, sizeof(errorMessage), "Cannot autodownload id pk3 file \"%s\"", cl->downloadName); } } else if ( !sv_allowDownload->integer ) { Com_Printf("clientDownload: %d : \"%s\" download disabled", cl - svs.clients, cl->downloadName); if (sv_pure->integer) { Com_sprintf(errorMessage, sizeof(errorMessage), "Could not download \"%s\" because autodownloading is disabled on the server.\n\n" "You will need to get this file elsewhere before you " "can connect to this pure server.\n", cl->downloadName); } else { Com_sprintf(errorMessage, sizeof(errorMessage), "Could not download \"%s\" because autodownloading is disabled on the server.\n\n" "The server you are connecting to is not a pure server, " "set autodownload to No in your settings and you might be " "able to join the game anyway.\n", cl->downloadName); } } else { // NOTE TTimo this is NOT supposed to happen unless bug in our filesystem scheme? // if the pk3 is referenced, it must have been found somewhere in the filesystem Com_Printf("clientDownload: %d : \"%s\" file not found on server\n", cl - svs.clients, cl->downloadName); Com_sprintf(errorMessage, sizeof(errorMessage), "File \"%s\" not found on server for autodownloading.\n", cl->downloadName); } MSG_WriteByte( msg, svc_download ); MSG_WriteShort( msg, 0 ); // client is expecting block zero MSG_WriteLong( msg, -1 ); // illegal file size MSG_WriteString( msg, errorMessage ); *cl->downloadName = 0; return; } // Init cl->downloadCurrentBlock = cl->downloadClientBlock = cl->downloadXmitBlock = 0; cl->downloadCount = 0; cl->downloadEOF = qfalse; } // Perform any reads that we need to while (cl->downloadCurrentBlock - cl->downloadClientBlock < MAX_DOWNLOAD_WINDOW && cl->downloadSize != cl->downloadCount) { curindex = (cl->downloadCurrentBlock % MAX_DOWNLOAD_WINDOW); if (!cl->downloadBlocks[curindex]) cl->downloadBlocks[curindex] = (unsigned char*) Z_Malloc( MAX_DOWNLOAD_BLKSIZE ); cl->downloadBlockSize[curindex] = FS_Read( cl->downloadBlocks[curindex], MAX_DOWNLOAD_BLKSIZE, cl->download ); if (cl->downloadBlockSize[curindex] < 0) { // EOF right now cl->downloadCount = cl->downloadSize; break; } cl->downloadCount += cl->downloadBlockSize[curindex]; // Load in next block cl->downloadCurrentBlock++; } // Check to see if we have eof condition and add the EOF block if (cl->downloadCount == cl->downloadSize && !cl->downloadEOF && cl->downloadCurrentBlock - cl->downloadClientBlock < MAX_DOWNLOAD_WINDOW) { cl->downloadBlockSize[cl->downloadCurrentBlock % MAX_DOWNLOAD_WINDOW] = 0; cl->downloadCurrentBlock++; cl->downloadEOF = qtrue; // We have added the EOF block } // Loop up to window size times based on how many blocks we can fit in the // client snapMsec and rate // based on the rate, how many bytes can we fit in the snapMsec time of the client // normal rate / snapshotMsec calculation rate = cl->rate; if ( sv_maxRate->integer ) { if ( sv_maxRate->integer < 1000 ) { Cvar_Set( "sv_MaxRate", "1000" ); } if ( sv_maxRate->integer < rate ) { rate = sv_maxRate->integer; } } if (!rate) { blockspersnap = 1; } else { blockspersnap = ( (rate * cl->snapshotMsec) / 1000 + MAX_DOWNLOAD_BLKSIZE ) / MAX_DOWNLOAD_BLKSIZE; } if (blockspersnap < 0) blockspersnap = 1; while (blockspersnap--) { // Write out the next section of the file, if we have already reached our window, // automatically start retransmitting if (cl->downloadClientBlock == cl->downloadCurrentBlock) return; // Nothing to transmit if (cl->downloadXmitBlock == cl->downloadCurrentBlock) { // We have transmitted the complete window, should we start resending? //FIXME: This uses a hardcoded one second timeout for lost blocks //the timeout should be based on client rate somehow if (svs.time - cl->downloadSendTime > 1000) cl->downloadXmitBlock = cl->downloadClientBlock; else return; } // Send current block curindex = (cl->downloadXmitBlock % MAX_DOWNLOAD_WINDOW); MSG_WriteByte( msg, svc_download ); MSG_WriteShort( msg, cl->downloadXmitBlock ); // block zero is special, contains file size if ( cl->downloadXmitBlock == 0 ) MSG_WriteLong( msg, cl->downloadSize ); MSG_WriteShort( msg, cl->downloadBlockSize[curindex] ); // Write the block if ( cl->downloadBlockSize[curindex] ) { MSG_WriteData( msg, cl->downloadBlocks[curindex], cl->downloadBlockSize[curindex] ); } Com_DPrintf( "clientDownload: %d : writing block %d\n", cl - svs.clients, cl->downloadXmitBlock ); // Move on to the next block // It will get sent with next snap shot. The rate will keep us in line. cl->downloadXmitBlock++; cl->downloadSendTime = svs.time; } } /* ================= SV_Disconnect_f The client is going to disconnect, so remove the connection immediately FIXME: move to game? ================= */ static void SV_Disconnect_f( client_t *cl ) { SV_DropClient( cl, "disconnected" ); } /* ================= SV_VerifyPaks_f If we are pure, disconnect the client if they do no meet the following conditions: 1. the first two checksums match our view of cgame and ui 2. there are no any additional checksums that we do not have This routine would be a bit simpler with a goto but i abstained ================= */ static void SV_VerifyPaks_f( client_t *cl ) { int nChkSum1, nChkSum2, nClientPaks, nServerPaks, i, j, nCurArg; int nClientChkSum[1024]; int nServerChkSum[1024]; const char *pPaks, *pArg; qboolean bGood = qtrue; // if we are pure, we "expect" the client to load certain things from // certain pk3 files, namely we want the client to have loaded the // ui and cgame that we think should be loaded based on the pure setting // if ( sv_pure->integer != 0 ) { bGood = qtrue; nChkSum1 = nChkSum2 = 0; // we run the game, so determine which cgame and ui the client "should" be running bGood = (qboolean) (FS_FileIsInPAK("vm/cgame.qvm", &nChkSum1) == 1); if (bGood) bGood = (qboolean) (FS_FileIsInPAK("vm/ui.qvm", &nChkSum2) == 1); nClientPaks = Cmd_Argc(); // start at arg 2 ( skip serverId cl_paks ) nCurArg = 1; pArg = Cmd_Argv(nCurArg++); if(!pArg) { bGood = qfalse; } else { // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=475 // we may get incoming cp sequences from a previous checksumFeed, which we need to ignore // since serverId is a frame count, it always goes up if (atoi(pArg) < sv.checksumFeedServerId) { Com_DPrintf("ignoring outdated cp command from client %s\n", cl->name); return; } } // we basically use this while loop to avoid using 'goto' :) while (bGood) { // must be at least 6: "cl_paks cgame ui @ firstref ... numChecksums" // numChecksums is encoded if (nClientPaks < 6) { bGood = qfalse; break; } // verify first to be the cgame checksum pArg = Cmd_Argv(nCurArg++); if (!pArg || *pArg == '@' || atoi(pArg) != nChkSum1 ) { bGood = qfalse; break; } // verify the second to be the ui checksum pArg = Cmd_Argv(nCurArg++); if (!pArg || *pArg == '@' || atoi(pArg) != nChkSum2 ) { bGood = qfalse; break; } // should be sitting at the delimeter now pArg = Cmd_Argv(nCurArg++); if (*pArg != '@') { bGood = qfalse; break; } // store checksums since tokenization is not re-entrant for (i = 0; nCurArg < nClientPaks; i++) { nClientChkSum[i] = atoi(Cmd_Argv(nCurArg++)); } // store number to compare against (minus one cause the last is the number of checksums) nClientPaks = i - 1; // make sure none of the client check sums are the same // so the client can't send 5 the same checksums for (i = 0; i < nClientPaks; i++) { for (j = 0; j < nClientPaks; j++) { if (i == j) continue; if (nClientChkSum[i] == nClientChkSum[j]) { bGood = qfalse; break; } } if (bGood == qfalse) break; } if (bGood == qfalse) break; // get the pure checksums of the pk3 files loaded by the server pPaks = FS_LoadedPakPureChecksums(); Cmd_TokenizeString( pPaks ); nServerPaks = Cmd_Argc(); if (nServerPaks > 1024) nServerPaks = 1024; for (i = 0; i < nServerPaks; i++) { nServerChkSum[i] = atoi(Cmd_Argv(i)); } // check if the client has provided any pure checksums of pk3 files not loaded by the server for (i = 0; i < nClientPaks; i++) { for (j = 0; j < nServerPaks; j++) { if (nClientChkSum[i] == nServerChkSum[j]) { break; } } if (j >= nServerPaks) { bGood = qfalse; break; } } if ( bGood == qfalse ) { break; } // check if the number of checksums was correct nChkSum1 = sv.checksumFeed; for (i = 0; i < nClientPaks; i++) { nChkSum1 ^= nClientChkSum[i]; } nChkSum1 ^= nClientPaks; if (nChkSum1 != nClientChkSum[nClientPaks]) { bGood = qfalse; break; } // break out break; } cl->gotCP = qtrue; if (bGood) { cl->pureAuthentic = 1; } else { cl->pureAuthentic = 0; cl->nextSnapshotTime = -1; cl->state = CS_ACTIVE; SV_SendClientSnapshot( cl ); SV_DropClient( cl, "Unpure client detected. Invalid .PK3 files referenced!" ); } } } /* ================= SV_ResetPureClient_f ================= */ static void SV_ResetPureClient_f( client_t *cl ) { cl->pureAuthentic = 0; cl->gotCP = qfalse; } /* ================= SV_UserinfoChanged Pull specific info from a newly changed userinfo string into a more C friendly form. ================= */ void SV_UserinfoChanged( client_t *cl ) { char *val; int i; // name for C code Q_strncpyz( cl->name, Info_ValueForKey (cl->userinfo, "name"), sizeof(cl->name) ); // rate command // if the client is on the same subnet as the server and we aren't running an // internet public server, assume they don't need a rate choke if ( Sys_IsLANAddress( cl->netchan.remoteAddress ) && com_dedicated->integer != 2 && sv_lanForceRate->integer == 1) { cl->rate = 99999; // lans should not rate limit } else { val = Info_ValueForKey (cl->userinfo, "rate"); if (strlen(val)) { i = atoi(val); cl->rate = i; if (cl->rate < 1000) { cl->rate = 1000; } else if (cl->rate > 90000) { cl->rate = 90000; } } else { cl->rate = 3000; } } val = Info_ValueForKey (cl->userinfo, "handicap"); if (strlen(val)) { i = atoi(val); if (i<=0 || i>100 || (int)strlen(val) > 4) { Info_SetValueForKey( cl->userinfo, "handicap", "100" ); } } // snaps command val = Info_ValueForKey (cl->userinfo, "snaps"); if (strlen(val)) { i = atoi(val); if ( i < 1 ) { i = 1; } else if ( i > 30 ) { i = 30; } cl->snapshotMsec = 1000/i; } else { cl->snapshotMsec = 50; } // TTimo // maintain the IP information // this is set in SV_DirectConnect (directly on the server, not transmitted), may be lost when client updates it's userinfo // the banning code relies on this being consistently present val = Info_ValueForKey (cl->userinfo, "ip"); if (!val[0]) { //Com_DPrintf("Maintain IP in userinfo for '%s'\n", cl->name); if ( !NET_IsLocalAddress(cl->netchan.remoteAddress) ) Info_SetValueForKey( cl->userinfo, "ip", NET_AdrToString( cl->netchan.remoteAddress ) ); else // force the "ip" info key to "localhost" for local clients Info_SetValueForKey( cl->userinfo, "ip", "localhost" ); } } /* ================== SV_UpdateUserinfo_f ================== */ static void SV_UpdateUserinfo_f( client_t *cl ) { Q_strncpyz( cl->userinfo, Cmd_Argv(1), sizeof(cl->userinfo) ); SV_UserinfoChanged( cl ); // call prog code to allow overrides VM_Call( gvm, GAME_CLIENT_USERINFO_CHANGED, cl - svs.clients ); } typedef struct { char *name; void (*func)( client_t *cl ); } ucmd_t; static ucmd_t ucmds[] = { {"userinfo", SV_UpdateUserinfo_f}, {"disconnect", SV_Disconnect_f}, {"cp", SV_VerifyPaks_f}, {"vdr", SV_ResetPureClient_f}, {"download", SV_BeginDownload_f}, {"nextdl", SV_NextDownload_f}, {"stopdl", SV_StopDownload_f}, {"donedl", SV_DoneDownload_f}, {NULL, NULL} }; /* ================== SV_ExecuteClientCommand Also called by bot code ================== */ void SV_ExecuteClientCommand( client_t *cl, const char *s, qboolean clientOK ) { ucmd_t *u; qboolean bProcessed = qfalse; Cmd_TokenizeString( s ); // see if it is a server level command for (u=ucmds ; u->name ; u++) { if (!strcmp (Cmd_Argv(0), u->name) ) { u->func( cl ); bProcessed = qtrue; break; } } if (clientOK) { // pass unknown strings to the game if (!u->name && sv.state == SS_GAME) { VM_Call( gvm, GAME_CLIENT_COMMAND, cl - svs.clients ); } } else if (!bProcessed) Com_DPrintf( "client text ignored for %s: %s\n", cl->name, Cmd_Argv(0) ); } /* =============== SV_ClientCommand =============== */ static qboolean SV_ClientCommand( client_t *cl, msg_t *msg ) { int seq; const char *s; qboolean clientOk = qtrue; seq = MSG_ReadLong( msg ); s = MSG_ReadString( msg ); // see if we have already executed it if ( cl->lastClientCommand >= seq ) { return qtrue; } Com_DPrintf( "clientCommand: %s : %i : %s\n", cl->name, seq, s ); // drop the connection if we have somehow lost commands if ( seq > cl->lastClientCommand + 1 ) { Com_Printf( "Client %s lost %i clientCommands\n", cl->name, seq - cl->lastClientCommand + 1 ); SV_DropClient( cl, "Lost reliable commands" ); return qfalse; } // malicious users may try using too many string commands // to lag other players. If we decide that we want to stall // the command, we will stop processing the rest of the packet, // including the usercmd. This causes flooders to lag themselves // but not other people // We don't do this when the client hasn't been active yet since its // normal to spam a lot of commands when downloading if ( !com_cl_running->integer && cl->state >= CS_ACTIVE && sv_floodProtect->integer && svs.time < cl->nextReliableTime ) { // ignore any other text messages from this client but let them keep playing // TTimo - moved the ignored verbose to the actual processing in SV_ExecuteClientCommand, only printing if the core doesn't intercept clientOk = qfalse; } // don't allow another command for one second cl->nextReliableTime = svs.time + 1000; SV_ExecuteClientCommand( cl, s, clientOk ); cl->lastClientCommand = seq; Com_sprintf(cl->lastClientCommandString, sizeof(cl->lastClientCommandString), "%s", s); return qtrue; // continue procesing } //================================================================================== /* ================== SV_ClientThink Also called by bot code ================== */ void SV_ClientThink (client_t *cl, usercmd_t *cmd) { cl->lastUsercmd = *cmd; if ( cl->state != CS_ACTIVE ) { return; // may have been kicked during the last usercmd } VM_Call( gvm, GAME_CLIENT_THINK, cl - svs.clients ); } /* ================== SV_UserMove The message usually contains all the movement commands that were in the last three packets, so that the information in dropped packets can be recovered. On very fast clients, there may be multiple usercmd packed into each of the backup packets. ================== */ static void SV_UserMove( client_t *cl, msg_t *msg, qboolean delta ) { int i, key; int cmdCount; usercmd_t nullcmd; usercmd_t cmds[MAX_PACKET_USERCMDS]; usercmd_t *cmd, *oldcmd; if ( delta ) { cl->deltaMessage = cl->messageAcknowledge; } else { cl->deltaMessage = -1; } cmdCount = MSG_ReadByte( msg ); if ( cmdCount < 1 ) { Com_Printf( "cmdCount < 1\n" ); return; } if ( cmdCount > MAX_PACKET_USERCMDS ) { Com_Printf( "cmdCount > MAX_PACKET_USERCMDS\n" ); return; } // use the checksum feed in the key key = sv.checksumFeed; // also use the message acknowledge key ^= cl->messageAcknowledge; // also use the last acknowledged server command in the key key ^= Com_HashKey(cl->reliableCommands[ cl->reliableAcknowledge & (MAX_RELIABLE_COMMANDS-1) ], 32); Com_Memset( &nullcmd, 0, sizeof(nullcmd) ); oldcmd = &nullcmd; for ( i = 0 ; i < cmdCount ; i++ ) { cmd = &cmds[i]; MSG_ReadDeltaUsercmdKey( msg, key, oldcmd, cmd ); oldcmd = cmd; } // save time for ping calculation cl->frames[ cl->messageAcknowledge & PACKET_MASK ].messageAcked = svs.time; // TTimo // catch the no-cp-yet situation before SV_ClientEnterWorld // if CS_ACTIVE, then it's time to trigger a new gamestate emission // if not, then we are getting remaining parasite usermove commands, which we should ignore if (sv_pure->integer != 0 && cl->pureAuthentic == 0 && !cl->gotCP) { if (cl->state == CS_ACTIVE) { // we didn't get a cp yet, don't assume anything and just send the gamestate all over again Com_DPrintf( "%s: didn't get cp command, resending gamestate\n", cl->name, cl->state ); SV_SendClientGameState( cl ); } return; } // if this is the first usercmd we have received // this gamestate, put the client into the world if ( cl->state == CS_PRIMED ) { SV_ClientEnterWorld( cl, &cmds[0] ); // the moves can be processed normaly } // a bad cp command was sent, drop the client if (sv_pure->integer != 0 && cl->pureAuthentic == 0) { SV_DropClient( cl, "Cannot validate pure client!"); return; } if ( cl->state != CS_ACTIVE ) { cl->deltaMessage = -1; return; } // usually, the first couple commands will be duplicates // of ones we have previously received, but the servertimes // in the commands will cause them to be immediately discarded for ( i = 0 ; i < cmdCount ; i++ ) { // if this is a cmd from before a map_restart ignore it if ( cmds[i].serverTime > cmds[cmdCount-1].serverTime ) { continue; } // extremely lagged or cmd from before a map_restart //if ( cmds[i].serverTime > svs.time + 3000 ) { // continue; //} // don't execute if this is an old cmd which is already executed // these old cmds are included when cl_packetdup > 0 if ( cmds[i].serverTime <= cl->lastUsercmd.serverTime ) { continue; } SV_ClientThink (cl, &cmds[ i ]); } } /* =========================================================================== USER CMD EXECUTION =========================================================================== */ /* =================== SV_ExecuteClientMessage Parse a client packet =================== */ void SV_ExecuteClientMessage( client_t *cl, msg_t *msg ) { int c; int serverId; MSG_Bitstream(msg); serverId = MSG_ReadLong( msg ); cl->messageAcknowledge = MSG_ReadLong( msg ); if (cl->messageAcknowledge < 0) { // usually only hackers create messages like this // it is more annoying for them to let them hanging #ifndef NDEBUG SV_DropClient( cl, "DEBUG: illegible client message" ); #endif return; } cl->reliableAcknowledge = MSG_ReadLong( msg ); // NOTE: when the client message is fux0red the acknowledgement numbers // can be out of range, this could cause the server to send thousands of server // commands which the server thinks are not yet acknowledged in SV_UpdateServerCommandsToClient if (cl->reliableAcknowledge < cl->reliableSequence - MAX_RELIABLE_COMMANDS) { // usually only hackers create messages like this // it is more annoying for them to let them hanging #ifndef NDEBUG SV_DropClient( cl, "DEBUG: illegible client message" ); #endif cl->reliableAcknowledge = cl->reliableSequence; return; } // if this is a usercmd from a previous gamestate, // ignore it or retransmit the current gamestate // // if the client was downloading, let it stay at whatever serverId and // gamestate it was at. This allows it to keep downloading even when // the gamestate changes. After the download is finished, we'll // notice and send it a new game state // // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=536 // don't drop as long as previous command was a nextdl, after a dl is done, downloadName is set back to "" // but we still need to read the next message to move to next download or send gamestate // I don't like this hack though, it must have been working fine at some point, suspecting the fix is somewhere else if ( serverId != sv.serverId && !*cl->downloadName && !strstr(cl->lastClientCommandString, "nextdl") ) { if ( serverId >= sv.restartedServerId && serverId < sv.serverId ) { // TTimo - use a comparison here to catch multiple map_restart // they just haven't caught the map_restart yet Com_DPrintf("%s : ignoring pre map_restart / outdated client message\n", cl->name); return; } // if we can tell that the client has dropped the last // gamestate we sent them, resend it if ( cl->messageAcknowledge > cl->gamestateMessageNum ) { Com_DPrintf( "%s : dropped gamestate, resending\n", cl->name ); SV_SendClientGameState( cl ); } return; } // read optional clientCommand strings do { c = MSG_ReadByte( msg ); if ( c == clc_EOF ) { break; } if ( c != clc_clientCommand ) { break; } if ( !SV_ClientCommand( cl, msg ) ) { return; // we couldn't execute it because of the flood protection } if (cl->state == CS_ZOMBIE) { return; // disconnect command } } while ( 1 ); // read the usercmd_t if ( c == clc_move ) { SV_UserMove( cl, msg, qtrue ); } else if ( c == clc_moveNoDelta ) { SV_UserMove( cl, msg, qfalse ); } else if ( c != clc_EOF ) { Com_Printf( "WARNING: bad command byte for client %i\n", cl - svs.clients ); } // if ( msg->readcount != msg->cursize ) { // Com_Printf( "WARNING: Junk at end of packet for client %i\n", cl - svs.clients ); // } } ================================================ FILE: src/engine/server/sv_game.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // sv_game.c -- interface to the game dll #include "server.h" #include "../../game/botlib.h" botlib_export_t *botlib_export; void SV_GameError( const char *string ) { Com_Error( ERR_DROP, "%s", string ); } void SV_GamePrint( const char *string ) { Com_Printf( "%s", string ); } // these functions must be used instead of pointer arithmetic, because // the game allocates gentities with private information after the server shared part int SV_NumForGentity( sharedEntity_t *ent ) { int num; num = ( (byte *)ent - (byte *)sv.gentities ) / sv.gentitySize; return num; } sharedEntity_t *SV_GentityNum( int num ) { sharedEntity_t *ent; ent = (sharedEntity_t *)((byte *)sv.gentities + sv.gentitySize*(num)); return ent; } playerState_t *SV_GameClientNum( int num ) { playerState_t *ps; ps = (playerState_t *)((byte *)sv.gameClients + sv.gameClientSize*(num)); return ps; } svEntity_t *SV_SvEntityForGentity( sharedEntity_t *gEnt ) { if ( !gEnt || gEnt->s.number < 0 || gEnt->s.number >= MAX_GENTITIES ) { Com_Error( ERR_DROP, "SV_SvEntityForGentity: bad gEnt" ); } return &sv.svEntities[ gEnt->s.number ]; } sharedEntity_t *SV_GEntityForSvEntity( svEntity_t *svEnt ) { int num; num = svEnt - sv.svEntities; return SV_GentityNum( num ); } /* =============== SV_GameSendServerCommand Sends a command string to a client =============== */ void SV_GameSendServerCommand( int clientNum, const char *text ) { if ( clientNum == -1 ) { SV_SendServerCommand( NULL, "%s", text ); } else { if ( clientNum < 0 || clientNum >= sv_maxclients->integer ) { return; } SV_SendServerCommand( svs.clients + clientNum, "%s", text ); } } /* =============== SV_GameDropClient Disconnects the client with a message =============== */ void SV_GameDropClient( int clientNum, const char *reason ) { if ( clientNum < 0 || clientNum >= sv_maxclients->integer ) { return; } SV_DropClient( svs.clients + clientNum, reason ); } /* ================= SV_SetBrushModel sets mins and maxs for inline bmodels ================= */ void SV_SetBrushModel( sharedEntity_t *ent, const char *name ) { clipHandle_t h; vec3_t mins, maxs; if (!name) { Com_Error( ERR_DROP, "SV_SetBrushModel: NULL" ); } if (name[0] != '*') { Com_Error( ERR_DROP, "SV_SetBrushModel: %s isn't a brush model", name ); } ent->s.modelindex = atoi( name + 1 ); h = CM_InlineModel( ent->s.modelindex ); CM_ModelBounds( h, mins, maxs ); VectorCopy (mins, ent->r.mins); VectorCopy (maxs, ent->r.maxs); ent->r.bmodel = qtrue; ent->r.contents = -1; // we don't know exactly what is in the brushes SV_LinkEntity( ent ); // FIXME: remove } /* ================= SV_inPVS Also checks portalareas so that doors block sight ================= */ qboolean SV_inPVS (const vec3_t p1, const vec3_t p2) { int leafnum; int cluster; int area1, area2; byte *mask; leafnum = CM_PointLeafnum (p1); cluster = CM_LeafCluster (leafnum); area1 = CM_LeafArea (leafnum); mask = CM_ClusterPVS (cluster); leafnum = CM_PointLeafnum (p2); cluster = CM_LeafCluster (leafnum); area2 = CM_LeafArea (leafnum); if ( mask && (!(mask[cluster>>3] & (1<<(cluster&7)) ) ) ) return qfalse; if (!CM_AreasConnected (area1, area2)) return qfalse; // a door blocks sight return qtrue; } /* ================= SV_inPVSIgnorePortals Does NOT check portalareas ================= */ qboolean SV_inPVSIgnorePortals( const vec3_t p1, const vec3_t p2) { int leafnum; int cluster; int area1, area2; byte *mask; leafnum = CM_PointLeafnum (p1); cluster = CM_LeafCluster (leafnum); area1 = CM_LeafArea (leafnum); mask = CM_ClusterPVS (cluster); leafnum = CM_PointLeafnum (p2); cluster = CM_LeafCluster (leafnum); area2 = CM_LeafArea (leafnum); if ( mask && (!(mask[cluster>>3] & (1<<(cluster&7)) ) ) ) return qfalse; return qtrue; } /* ======================== SV_AdjustAreaPortalState ======================== */ void SV_AdjustAreaPortalState( sharedEntity_t *ent, qboolean open ) { svEntity_t *svEnt; svEnt = SV_SvEntityForGentity( ent ); if ( svEnt->areanum2 == -1 ) { return; } CM_AdjustAreaPortalState( svEnt->areanum, svEnt->areanum2, open ); } /* ================== SV_GameAreaEntities ================== */ qboolean SV_EntityContact( vec3_t mins, vec3_t maxs, const sharedEntity_t *gEnt, int capsule ) { const float *origin, *angles; clipHandle_t ch; trace_t trace; // check for exact collision origin = gEnt->r.currentOrigin; angles = gEnt->r.currentAngles; ch = SV_ClipHandleForEntity( gEnt ); CM_TransformedBoxTrace ( &trace, vec3_origin, vec3_origin, mins, maxs, ch, -1, origin, angles, capsule ); return trace.startsolid; } /* =============== SV_GetServerinfo =============== */ void SV_GetServerinfo( char *buffer, int bufferSize ) { if ( bufferSize < 1 ) { Com_Error( ERR_DROP, "SV_GetServerinfo: bufferSize == %i", bufferSize ); } Q_strncpyz( buffer, Cvar_InfoString( CVAR_SERVERINFO ), bufferSize ); } /* =============== SV_LocateGameData =============== */ void SV_LocateGameData( sharedEntity_t *gEnts, int numGEntities, int sizeofGEntity_t, playerState_t *clients, int sizeofGameClient ) { sv.gentities = gEnts; sv.gentitySize = sizeofGEntity_t; sv.num_entities = numGEntities; sv.gameClients = clients; sv.gameClientSize = sizeofGameClient; } /* =============== SV_GetUsercmd =============== */ void SV_GetUsercmd( int clientNum, usercmd_t *cmd ) { if ( clientNum < 0 || clientNum >= sv_maxclients->integer ) { Com_Error( ERR_DROP, "SV_GetUsercmd: bad clientNum:%i", clientNum ); } *cmd = svs.clients[clientNum].lastUsercmd; } //============================================== static int FloatAsInt( float f ) { union { int i; float f; } temp; temp.f = f; return temp.i; } /* ==================== SV_GameSystemCalls The module is making a system call ==================== */ #define VMA(x) VM_ArgPtr(args[x]) #define VMF(x) (*(float*)&args[x]) intptr_t SV_GameSystemCalls( intptr_t *args ) { switch( args[0] ) { case G_PRINT: Com_Printf( "%s", VMA(1) ); return 0; case G_ERROR: Com_Error( ERR_DROP, "%s", VMA(1) ); return 0; case G_MILLISECONDS: return Sys_Milliseconds(); case G_CVAR_REGISTER: Cvar_Register( (vmCvar_t*) VMA(1), (const char*) VMA(2), (const char*) VMA(3), args[4] ); return 0; case G_CVAR_UPDATE: Cvar_Update( (vmCvar_t*) VMA(1) ); return 0; case G_CVAR_SET: Cvar_Set( (const char *)VMA(1), (const char *)VMA(2) ); return 0; case G_CVAR_VARIABLE_INTEGER_VALUE: return Cvar_VariableIntegerValue( (const char *)VMA(1) ); case G_CVAR_VARIABLE_STRING_BUFFER: Cvar_VariableStringBuffer( (const char*) VMA(1), (char*) VMA(2), args[3] ); return 0; case G_ARGC: return Cmd_Argc(); case G_ARGV: Cmd_ArgvBuffer( args[1], (char*) VMA(2), args[3] ); return 0; case G_SEND_CONSOLE_COMMAND: Cbuf_ExecuteText( args[1], (const char*) VMA(2) ); return 0; case G_FS_FOPEN_FILE: return FS_FOpenFileByMode( (const char*) VMA(1), (fileHandle_t*) VMA(2), (fsMode_t) args[3] ); case G_FS_READ: FS_Read2( VMA(1), args[2], args[3] ); return 0; case G_FS_WRITE: FS_Write( VMA(1), args[2], args[3] ); return 0; case G_FS_FCLOSE_FILE: FS_FCloseFile( args[1] ); return 0; case G_FS_GETFILELIST: return FS_GetFileList( (const char*) VMA(1), (const char*) VMA(2), (char*) VMA(3), args[4] ); case G_FS_SEEK: return FS_Seek( args[1], args[2], args[3] ); case G_LOCATE_GAME_DATA: SV_LocateGameData( (sharedEntity_t*) VMA(1), args[2], args[3], (playerState_t*) VMA(4), args[5] ); return 0; case G_DROP_CLIENT: SV_GameDropClient( args[1], (const char*) VMA(2) ); return 0; case G_SEND_SERVER_COMMAND: SV_GameSendServerCommand( args[1], (const char*) VMA(2) ); return 0; case G_LINKENTITY: SV_LinkEntity( (sharedEntity_t*) VMA(1) ); return 0; case G_UNLINKENTITY: SV_UnlinkEntity( (sharedEntity_t*) VMA(1) ); return 0; case G_ENTITIES_IN_BOX: return SV_AreaEntities( (const vec_t*) VMA(1), (const vec_t*) VMA(2), (int*) VMA(3), args[4] ); case G_ENTITY_CONTACT: return SV_EntityContact((vec_t*) VMA(1), (vec_t*) VMA(2), (const sharedEntity_t*) VMA(3), /*int capsule*/ qfalse); case G_ENTITY_CONTACTCAPSULE: return SV_EntityContact( (vec_t*) VMA(1), (vec_t*) VMA(2), (const sharedEntity_t*) VMA(3), /*int capsule*/ qtrue ); case G_TRACE: SV_Trace((trace_t*)VMA(1), (const vec_t*)VMA(2), (vec_t*) VMA(3), (vec_t*)VMA(4), (const vec_t*) VMA(5), args[6], args[7], /*int capsule*/ qfalse); return 0; case G_TRACECAPSULE: SV_Trace((trace_t*)VMA(1), (const vec_t*)VMA(2), (vec_t*)VMA(3), (vec_t*)VMA(4), (const vec_t*)VMA(5), args[6], args[7], /*int capsule*/ qtrue); return 0; case G_POINT_CONTENTS: return SV_PointContents( (const vec_t*) VMA(1), args[2] ); case G_SET_BRUSH_MODEL: SV_SetBrushModel( (sharedEntity_t*) VMA(1), (const char*) VMA(2) ); return 0; case G_IN_PVS: return SV_inPVS( (const vec_t*) VMA(1), (const vec_t*) VMA(2) ); case G_IN_PVS_IGNORE_PORTALS: return SV_inPVSIgnorePortals( (const vec_t*) VMA(1), (const vec_t*) VMA(2) ); case G_SET_CONFIGSTRING: SV_SetConfigstring( args[1], (const char*) VMA(2) ); return 0; case G_GET_CONFIGSTRING: SV_GetConfigstring( args[1], (char*) VMA(2), args[3] ); return 0; case G_SET_USERINFO: SV_SetUserinfo( args[1], (const char*) VMA(2) ); return 0; case G_GET_USERINFO: SV_GetUserinfo( args[1], (char*) VMA(2), args[3] ); return 0; case G_GET_SERVERINFO: SV_GetServerinfo( (char*) VMA(1), args[2] ); return 0; case G_ADJUST_AREA_PORTAL_STATE: SV_AdjustAreaPortalState( (sharedEntity_t*) VMA(1), (qboolean) args[2] ); return 0; case G_AREAS_CONNECTED: return CM_AreasConnected( args[1], args[2] ); case G_BOT_ALLOCATE_CLIENT: return SV_BotAllocateClient(); case G_BOT_FREE_CLIENT: SV_BotFreeClient( args[1] ); return 0; case G_GET_USERCMD: SV_GetUsercmd( args[1], (usercmd_t*) VMA(2) ); return 0; case G_GET_ENTITY_TOKEN: { const char *s; s = COM_Parse( &sv.entityParsePoint ); Q_strncpyz( (char*) VMA(1), s, args[2] ); if ( !sv.entityParsePoint && !s[0] ) { return qfalse; } else { return qtrue; } } case G_DEBUG_POLYGON_CREATE: return BotImport_DebugPolygonCreate( args[1], args[2], (vec3_t*) VMA(3) ); case G_DEBUG_POLYGON_DELETE: BotImport_DebugPolygonDelete( args[1] ); return 0; case G_REAL_TIME: return Com_RealTime( (qtime_t*) VMA(1) ); case G_SNAPVECTOR: Sys_SnapVector( (float*) VMA(1) ); return 0; //==================================== case BOTLIB_SETUP: return SV_BotLibSetup(); case BOTLIB_SHUTDOWN: return SV_BotLibShutdown(); case BOTLIB_LIBVAR_SET: return botlib_export->BotLibVarSet( (char*) VMA(1), (char*) VMA(2) ); case BOTLIB_LIBVAR_GET: return botlib_export->BotLibVarGet( (char*) VMA(1), (char*) VMA(2), args[3] ); case BOTLIB_PC_ADD_GLOBAL_DEFINE: return botlib_export->PC_AddGlobalDefine( (char*) VMA(1) ); case BOTLIB_PC_LOAD_SOURCE: return botlib_export->PC_LoadSourceHandle( (const char*) VMA(1) ); case BOTLIB_PC_FREE_SOURCE: return botlib_export->PC_FreeSourceHandle( args[1] ); case BOTLIB_PC_READ_TOKEN: return botlib_export->PC_ReadTokenHandle( args[1], (pc_token_t*) VMA(2) ); case BOTLIB_PC_SOURCE_FILE_AND_LINE: return botlib_export->PC_SourceFileAndLine( args[1], (char*) VMA(2), (int*) VMA(3) ); case BOTLIB_START_FRAME: return botlib_export->BotLibStartFrame( VMF(1) ); case BOTLIB_LOAD_MAP: return botlib_export->BotLibLoadMap( (const char*) VMA(1) ); case BOTLIB_UPDATENTITY: return botlib_export->BotLibUpdateEntity( args[1], (bot_entitystate_t*) VMA(2) ); case BOTLIB_TEST: return botlib_export->Test( args[1], (char*) VMA(2), (vec_t*) VMA(3), (vec_t*) VMA(4) ); case BOTLIB_GET_SNAPSHOT_ENTITY: return SV_BotGetSnapshotEntity( args[1], args[2] ); case BOTLIB_GET_CONSOLE_MESSAGE: return SV_BotGetConsoleMessage( args[1], (char*) VMA(2), args[3] ); case BOTLIB_USER_COMMAND: SV_ClientThink( &svs.clients[args[1]], (usercmd_t*) VMA(2) ); return 0; case BOTLIB_AAS_BBOX_AREAS: return botlib_export->aas.AAS_BBoxAreas( (vec_t*) VMA(1), (vec_t*) VMA(2), (int*) VMA(3), args[4] ); case BOTLIB_AAS_AREA_INFO: return botlib_export->aas.AAS_AreaInfo( args[1], (aas_areainfo_s*) VMA(2) ); case BOTLIB_AAS_ALTERNATIVE_ROUTE_GOAL: return botlib_export->aas.AAS_AlternativeRouteGoals( (vec_t*) VMA(1), args[2], (vec_t*) VMA(3), args[4], args[5], (aas_altroutegoal_s*) VMA(6), args[7], args[8] ); case BOTLIB_AAS_ENTITY_INFO: botlib_export->aas.AAS_EntityInfo( args[1], (aas_entityinfo_s*) VMA(2) ); return 0; case BOTLIB_AAS_INITIALIZED: return botlib_export->aas.AAS_Initialized(); case BOTLIB_AAS_PRESENCE_TYPE_BOUNDING_BOX: botlib_export->aas.AAS_PresenceTypeBoundingBox( args[1], (vec_t*) VMA(2), (vec_t*) VMA(3) ); return 0; case BOTLIB_AAS_TIME: return FloatAsInt( botlib_export->aas.AAS_Time() ); case BOTLIB_AAS_POINT_AREA_NUM: return botlib_export->aas.AAS_PointAreaNum((vec_t*) VMA(1)); case BOTLIB_AAS_POINT_REACHABILITY_AREA_INDEX: return botlib_export->aas.AAS_PointReachabilityAreaIndex((vec_t*) VMA(1)); case BOTLIB_AAS_TRACE_AREAS: return botlib_export->aas.AAS_TraceAreas((vec_t*)VMA(1), (vec_t*) VMA(2), (int*) VMA(3), (vec3_t*) VMA(4), args[5]); case BOTLIB_AAS_POINT_CONTENTS: return botlib_export->aas.AAS_PointContents((vec_t*) VMA(1)); case BOTLIB_AAS_NEXT_BSP_ENTITY: return botlib_export->aas.AAS_NextBSPEntity( args[1] ); case BOTLIB_AAS_VALUE_FOR_BSP_EPAIR_KEY: return botlib_export->aas.AAS_ValueForBSPEpairKey( args[1], (char*) VMA(2), (char*) VMA(3), args[4] ); case BOTLIB_AAS_VECTOR_FOR_BSP_EPAIR_KEY: return botlib_export->aas.AAS_VectorForBSPEpairKey(args[1], (char*)VMA(2), (vec_t*) VMA(3)); case BOTLIB_AAS_FLOAT_FOR_BSP_EPAIR_KEY: return botlib_export->aas.AAS_FloatForBSPEpairKey( args[1], (char*) VMA(2), (float*) VMA(3) ); case BOTLIB_AAS_INT_FOR_BSP_EPAIR_KEY: return botlib_export->aas.AAS_IntForBSPEpairKey( args[1], (char*) VMA(2), (int*) VMA(3) ); case BOTLIB_AAS_AREA_REACHABILITY: return botlib_export->aas.AAS_AreaReachability( args[1] ); case BOTLIB_AAS_AREA_TRAVEL_TIME_TO_GOAL_AREA: return botlib_export->aas.AAS_AreaTravelTimeToGoalArea(args[1], (vec_t*) VMA(2), args[3], args[4]); case BOTLIB_AAS_ENABLE_ROUTING_AREA: return botlib_export->aas.AAS_EnableRoutingArea( args[1], args[2] ); case BOTLIB_AAS_PREDICT_ROUTE: return botlib_export->aas.AAS_PredictRoute((aas_predictroute_s*)VMA(1), args[2], (vec_t*) VMA(3), args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11]); case BOTLIB_AAS_SWIMMING: return botlib_export->aas.AAS_Swimming((vec_t*) VMA(1)); case BOTLIB_AAS_PREDICT_CLIENT_MOVEMENT: return botlib_export->aas.AAS_PredictClientMovement((aas_clientmove_s*)VMA(1), args[2], (vec_t*) VMA(3), args[4], args[5], (vec_t*)VMA(6), (vec_t*) VMA(7), args[8], args[9], VMF(10), args[11], args[12], args[13]); case BOTLIB_EA_SAY: botlib_export->ea.EA_Say( args[1], (char*) VMA(2) ); return 0; case BOTLIB_EA_SAY_TEAM: botlib_export->ea.EA_SayTeam( args[1], (char*) VMA(2) ); return 0; case BOTLIB_EA_COMMAND: botlib_export->ea.EA_Command( args[1], (char*) VMA(2) ); return 0; case BOTLIB_EA_ACTION: botlib_export->ea.EA_Action( args[1], args[2] ); break; case BOTLIB_EA_GESTURE: botlib_export->ea.EA_Gesture( args[1] ); return 0; case BOTLIB_EA_TALK: botlib_export->ea.EA_Talk( args[1] ); return 0; case BOTLIB_EA_ATTACK: botlib_export->ea.EA_Attack( args[1] ); return 0; case BOTLIB_EA_USE: botlib_export->ea.EA_Use( args[1] ); return 0; case BOTLIB_EA_RESPAWN: botlib_export->ea.EA_Respawn( args[1] ); return 0; case BOTLIB_EA_CROUCH: botlib_export->ea.EA_Crouch( args[1] ); return 0; case BOTLIB_EA_MOVE_UP: botlib_export->ea.EA_MoveUp( args[1] ); return 0; case BOTLIB_EA_MOVE_DOWN: botlib_export->ea.EA_MoveDown( args[1] ); return 0; case BOTLIB_EA_MOVE_FORWARD: botlib_export->ea.EA_MoveForward( args[1] ); return 0; case BOTLIB_EA_MOVE_BACK: botlib_export->ea.EA_MoveBack( args[1] ); return 0; case BOTLIB_EA_MOVE_LEFT: botlib_export->ea.EA_MoveLeft( args[1] ); return 0; case BOTLIB_EA_MOVE_RIGHT: botlib_export->ea.EA_MoveRight( args[1] ); return 0; case BOTLIB_EA_SELECT_WEAPON: botlib_export->ea.EA_SelectWeapon( args[1], args[2] ); return 0; case BOTLIB_EA_JUMP: botlib_export->ea.EA_Jump( args[1] ); return 0; case BOTLIB_EA_DELAYED_JUMP: botlib_export->ea.EA_DelayedJump( args[1] ); return 0; case BOTLIB_EA_MOVE: botlib_export->ea.EA_Move(args[1], (vec_t*) VMA(2), VMF(3)); return 0; case BOTLIB_EA_VIEW: botlib_export->ea.EA_View(args[1], (vec_t*) VMA(2)); return 0; case BOTLIB_EA_END_REGULAR: botlib_export->ea.EA_EndRegular( args[1], VMF(2) ); return 0; case BOTLIB_EA_GET_INPUT: botlib_export->ea.EA_GetInput( args[1], VMF(2), (bot_input_t*) VMA(3) ); return 0; case BOTLIB_EA_RESET_INPUT: botlib_export->ea.EA_ResetInput( args[1] ); return 0; case BOTLIB_AI_LOAD_CHARACTER: return botlib_export->ai.BotLoadCharacter( (char*) VMA(1), VMF(2) ); case BOTLIB_AI_FREE_CHARACTER: botlib_export->ai.BotFreeCharacter( args[1] ); return 0; case BOTLIB_AI_CHARACTERISTIC_FLOAT: return FloatAsInt( botlib_export->ai.Characteristic_Float( args[1], args[2] ) ); case BOTLIB_AI_CHARACTERISTIC_BFLOAT: return FloatAsInt( botlib_export->ai.Characteristic_BFloat( args[1], args[2], VMF(3), VMF(4) ) ); case BOTLIB_AI_CHARACTERISTIC_INTEGER: return botlib_export->ai.Characteristic_Integer( args[1], args[2] ); case BOTLIB_AI_CHARACTERISTIC_BINTEGER: return botlib_export->ai.Characteristic_BInteger( args[1], args[2], args[3], args[4] ); case BOTLIB_AI_CHARACTERISTIC_STRING: botlib_export->ai.Characteristic_String( args[1], args[2], (char*) VMA(3), args[4] ); return 0; case BOTLIB_AI_ALLOC_CHAT_STATE: return botlib_export->ai.BotAllocChatState(); case BOTLIB_AI_FREE_CHAT_STATE: botlib_export->ai.BotFreeChatState( args[1] ); return 0; case BOTLIB_AI_QUEUE_CONSOLE_MESSAGE: botlib_export->ai.BotQueueConsoleMessage( args[1], args[2], (char*) VMA(3) ); return 0; case BOTLIB_AI_REMOVE_CONSOLE_MESSAGE: botlib_export->ai.BotRemoveConsoleMessage( args[1], args[2] ); return 0; case BOTLIB_AI_NEXT_CONSOLE_MESSAGE: return botlib_export->ai.BotNextConsoleMessage( args[1], (bot_consolemessage_s*) VMA(2) ); case BOTLIB_AI_NUM_CONSOLE_MESSAGE: return botlib_export->ai.BotNumConsoleMessages( args[1] ); case BOTLIB_AI_INITIAL_CHAT: botlib_export->ai.BotInitialChat( args[1], (char*) VMA(2), args[3], (char*) VMA(4), (char*) VMA(5), (char*) VMA(6), (char*) VMA(7), (char*) VMA(8), (char*) VMA(9), (char*) VMA(10), (char*) VMA(11) ); return 0; case BOTLIB_AI_NUM_INITIAL_CHATS: return botlib_export->ai.BotNumInitialChats( args[1], (char*) VMA(2) ); case BOTLIB_AI_REPLY_CHAT: return botlib_export->ai.BotReplyChat( args[1], (char*) VMA(2), args[3], args[4], (char*) VMA(5), (char*) VMA(6), (char*) VMA(7), (char*) VMA(8), (char*) VMA(9), (char*) VMA(10), (char*) VMA(11), (char*) VMA(12) ); case BOTLIB_AI_CHAT_LENGTH: return botlib_export->ai.BotChatLength( args[1] ); case BOTLIB_AI_ENTER_CHAT: botlib_export->ai.BotEnterChat( args[1], args[2], args[3] ); return 0; case BOTLIB_AI_GET_CHAT_MESSAGE: botlib_export->ai.BotGetChatMessage( args[1], (char*) VMA(2), args[3] ); return 0; case BOTLIB_AI_STRING_CONTAINS: return botlib_export->ai.StringContains((char*)VMA(1), (char*) VMA(2), args[3]); case BOTLIB_AI_FIND_MATCH: return botlib_export->ai.BotFindMatch((char*) VMA(1), (bot_match_s*) VMA(2), args[3]); case BOTLIB_AI_MATCH_VARIABLE: botlib_export->ai.BotMatchVariable( (bot_match_s*) VMA(1), args[2], (char*) VMA(3), args[4] ); return 0; case BOTLIB_AI_UNIFY_WHITE_SPACES: botlib_export->ai.UnifyWhiteSpaces((char*) VMA(1)); return 0; case BOTLIB_AI_REPLACE_SYNONYMS: botlib_export->ai.BotReplaceSynonyms((char*) VMA(1), args[2]); return 0; case BOTLIB_AI_LOAD_CHAT_FILE: return botlib_export->ai.BotLoadChatFile(args[1], (char*)VMA(2), (char*) VMA(3)); case BOTLIB_AI_SET_CHAT_GENDER: botlib_export->ai.BotSetChatGender( args[1], args[2] ); return 0; case BOTLIB_AI_SET_CHAT_NAME: botlib_export->ai.BotSetChatName(args[1], (char*) VMA(2), args[3]); return 0; case BOTLIB_AI_RESET_GOAL_STATE: botlib_export->ai.BotResetGoalState( args[1] ); return 0; case BOTLIB_AI_RESET_AVOID_GOALS: botlib_export->ai.BotResetAvoidGoals( args[1] ); return 0; case BOTLIB_AI_REMOVE_FROM_AVOID_GOALS: botlib_export->ai.BotRemoveFromAvoidGoals( args[1], args[2] ); return 0; case BOTLIB_AI_PUSH_GOAL: botlib_export->ai.BotPushGoal(args[1], (bot_goal_s*) VMA(2)); return 0; case BOTLIB_AI_POP_GOAL: botlib_export->ai.BotPopGoal( args[1] ); return 0; case BOTLIB_AI_EMPTY_GOAL_STACK: botlib_export->ai.BotEmptyGoalStack( args[1] ); return 0; case BOTLIB_AI_DUMP_AVOID_GOALS: botlib_export->ai.BotDumpAvoidGoals( args[1] ); return 0; case BOTLIB_AI_DUMP_GOAL_STACK: botlib_export->ai.BotDumpGoalStack( args[1] ); return 0; case BOTLIB_AI_GOAL_NAME: botlib_export->ai.BotGoalName( args[1], (char*) VMA(2), args[3] ); return 0; case BOTLIB_AI_GET_TOP_GOAL: return botlib_export->ai.BotGetTopGoal( args[1], (bot_goal_s*) VMA(2) ); case BOTLIB_AI_GET_SECOND_GOAL: return botlib_export->ai.BotGetSecondGoal(args[1], (bot_goal_s*) VMA(2)); case BOTLIB_AI_CHOOSE_LTG_ITEM: return botlib_export->ai.BotChooseLTGItem( args[1], (vec_t*) VMA(2), (int*) VMA(3), args[4] ); case BOTLIB_AI_CHOOSE_NBG_ITEM: return botlib_export->ai.BotChooseNBGItem( args[1], (vec_t*) VMA(2), (int*) VMA(3), args[4], (bot_goal_s*) VMA(5), VMF(6) ); case BOTLIB_AI_TOUCHING_GOAL: return botlib_export->ai.BotTouchingGoal((vec_t*)VMA(1), (bot_goal_s*) VMA(2)); case BOTLIB_AI_ITEM_GOAL_IN_VIS_BUT_NOT_VISIBLE: return botlib_export->ai.BotItemGoalInVisButNotVisible(args[1], (vec_t*)VMA(2), (vec_t*)VMA(3), (bot_goal_s*) VMA(4)); case BOTLIB_AI_GET_LEVEL_ITEM_GOAL: return botlib_export->ai.BotGetLevelItemGoal(args[1], (char*)VMA(2), (bot_goal_s*) VMA(3)); case BOTLIB_AI_GET_NEXT_CAMP_SPOT_GOAL: return botlib_export->ai.BotGetNextCampSpotGoal(args[1], (bot_goal_s*) VMA(2)); case BOTLIB_AI_GET_MAP_LOCATION_GOAL: return botlib_export->ai.BotGetMapLocationGoal((char*)VMA(1), (bot_goal_s*) VMA(2)); case BOTLIB_AI_AVOID_GOAL_TIME: return FloatAsInt( botlib_export->ai.BotAvoidGoalTime( args[1], args[2] ) ); case BOTLIB_AI_SET_AVOID_GOAL_TIME: botlib_export->ai.BotSetAvoidGoalTime( args[1], args[2], VMF(3)); return 0; case BOTLIB_AI_INIT_LEVEL_ITEMS: botlib_export->ai.BotInitLevelItems(); return 0; case BOTLIB_AI_UPDATE_ENTITY_ITEMS: botlib_export->ai.BotUpdateEntityItems(); return 0; case BOTLIB_AI_LOAD_ITEM_WEIGHTS: return botlib_export->ai.BotLoadItemWeights( args[1], (char*) VMA(2) ); case BOTLIB_AI_FREE_ITEM_WEIGHTS: botlib_export->ai.BotFreeItemWeights( args[1] ); return 0; case BOTLIB_AI_INTERBREED_GOAL_FUZZY_LOGIC: botlib_export->ai.BotInterbreedGoalFuzzyLogic( args[1], args[2], args[3] ); return 0; case BOTLIB_AI_SAVE_GOAL_FUZZY_LOGIC: botlib_export->ai.BotSaveGoalFuzzyLogic( args[1], (char*) VMA(2) ); return 0; case BOTLIB_AI_MUTATE_GOAL_FUZZY_LOGIC: botlib_export->ai.BotMutateGoalFuzzyLogic( args[1], VMF(2) ); return 0; case BOTLIB_AI_ALLOC_GOAL_STATE: return botlib_export->ai.BotAllocGoalState( args[1] ); case BOTLIB_AI_FREE_GOAL_STATE: botlib_export->ai.BotFreeGoalState( args[1] ); return 0; case BOTLIB_AI_RESET_MOVE_STATE: botlib_export->ai.BotResetMoveState( args[1] ); return 0; case BOTLIB_AI_ADD_AVOID_SPOT: botlib_export->ai.BotAddAvoidSpot( args[1], (vec_t*) VMA(2), VMF(3), args[4] ); return 0; case BOTLIB_AI_MOVE_TO_GOAL: botlib_export->ai.BotMoveToGoal((bot_moveresult_s*)VMA(1), args[2], (bot_goal_s*) VMA(3), args[4]); return 0; case BOTLIB_AI_MOVE_IN_DIRECTION: return botlib_export->ai.BotMoveInDirection( args[1], (vec_t*) VMA(2), VMF(3), args[4] ); case BOTLIB_AI_RESET_AVOID_REACH: botlib_export->ai.BotResetAvoidReach( args[1] ); return 0; case BOTLIB_AI_RESET_LAST_AVOID_REACH: botlib_export->ai.BotResetLastAvoidReach( args[1] ); return 0; case BOTLIB_AI_REACHABILITY_AREA: return botlib_export->ai.BotReachabilityArea( (vec_t*) VMA(1), args[2] ); case BOTLIB_AI_MOVEMENT_VIEW_TARGET: return botlib_export->ai.BotMovementViewTarget(args[1], (bot_goal_s*) VMA(2), args[3], VMF(4), (vec_t*) VMA(5)); case BOTLIB_AI_PREDICT_VISIBLE_POSITION: return botlib_export->ai.BotPredictVisiblePosition((vec_t*)VMA(1), args[2], (bot_goal_s*) VMA(3), args[4], (vec_t*) VMA(5)); case BOTLIB_AI_ALLOC_MOVE_STATE: return botlib_export->ai.BotAllocMoveState(); case BOTLIB_AI_FREE_MOVE_STATE: botlib_export->ai.BotFreeMoveState( args[1] ); return 0; case BOTLIB_AI_INIT_MOVE_STATE: botlib_export->ai.BotInitMoveState( args[1], (bot_initmove_s*) VMA(2) ); return 0; case BOTLIB_AI_CHOOSE_BEST_FIGHT_WEAPON: return botlib_export->ai.BotChooseBestFightWeapon( args[1], (int*) VMA(2) ); case BOTLIB_AI_GET_WEAPON_INFO: botlib_export->ai.BotGetWeaponInfo( args[1], args[2], (weaponinfo_s*) VMA(3) ); return 0; case BOTLIB_AI_LOAD_WEAPON_WEIGHTS: return botlib_export->ai.BotLoadWeaponWeights( args[1], (char*) VMA(2) ); case BOTLIB_AI_ALLOC_WEAPON_STATE: return botlib_export->ai.BotAllocWeaponState(); case BOTLIB_AI_FREE_WEAPON_STATE: botlib_export->ai.BotFreeWeaponState( args[1] ); return 0; case BOTLIB_AI_RESET_WEAPON_STATE: botlib_export->ai.BotResetWeaponState( args[1] ); return 0; case BOTLIB_AI_GENETIC_PARENTS_AND_CHILD_SELECTION: return botlib_export->ai.GeneticParentsAndChildSelection(args[1], (float*) VMA(2), (int*) VMA(3), (int*) VMA(4), (int*) VMA(5)); case TRAP_MEMSET: Com_Memset( VMA(1), args[2], args[3] ); return 0; case TRAP_MEMCPY: Com_Memcpy( VMA(1), VMA(2), args[3] ); return 0; case TRAP_STRNCPY: strncpy( (char*)VMA(1), (const char*)VMA(2), args[3] ); return args[1]; case TRAP_SIN: return FloatAsInt( sin( VMF(1) ) ); case TRAP_COS: return FloatAsInt( cos( VMF(1) ) ); case TRAP_ATAN2: return FloatAsInt( atan2( VMF(1), VMF(2) ) ); case TRAP_SQRT: return FloatAsInt( sqrt( VMF(1) ) ); case TRAP_MATRIXMULTIPLY: MatrixMultiply((float(*)[3]) VMA(1), (float(*)[3]) VMA(2), (float(*)[3]) VMA(3)); return 0; case TRAP_ANGLEVECTORS: AngleVectors( (const vec_t*) VMA(1), (vec_t*) VMA(2), (vec_t*) VMA(3), (vec_t*) VMA(4) ); return 0; case TRAP_PERPENDICULARVECTOR: PerpendicularVector( (vec_t*) VMA(1), (vec_t*) VMA(2) ); return 0; case TRAP_FLOOR: return FloatAsInt( floor( VMF(1) ) ); case TRAP_CEIL: return FloatAsInt( ceil( VMF(1) ) ); default: Com_Error( ERR_DROP, "Bad game system trap: %i", args[0] ); } return -1; } /* =============== SV_ShutdownGameProgs Called every time a map changes =============== */ void SV_ShutdownGameProgs( void ) { if ( !gvm ) { return; } VM_Call( gvm, GAME_SHUTDOWN, qfalse ); VM_Free( gvm ); gvm = NULL; } /* ================== SV_InitGameVM Called for both a full init and a restart ================== */ static void SV_InitGameVM( qboolean restart ) { int i; // start the entity parsing at the beginning sv.entityParsePoint = CM_EntityString(); // clear all gentity pointers that might still be set from // a previous level // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=522 // now done before GAME_INIT call for ( i = 0 ; i < sv_maxclients->integer ; i++ ) { svs.clients[i].gentity = NULL; } // use the current msec count for a random seed // init for this gamestate VM_Call( gvm, GAME_INIT, svs.time, Com_Milliseconds(), restart ); } /* =================== SV_RestartGameProgs Called on a map_restart, but not on a normal map change =================== */ void SV_RestartGameProgs( void ) { if ( !gvm ) { return; } VM_Call( gvm, GAME_SHUTDOWN, qtrue ); // do a restart instead of a free gvm = VM_Restart( gvm ); if ( !gvm ) { // bk001212 - as done below Com_Error( ERR_FATAL, "VM_Restart on game failed" ); } SV_InitGameVM( qtrue ); } /* =============== SV_InitGameProgs Called on a normal map change, not on a map_restart =============== */ void SV_InitGameProgs( void ) { cvar_t *var; //FIXME these are temp while I make bots run in vm extern int bot_enable; var = Cvar_Get( "bot_enable", "1", CVAR_LATCH ); if ( var ) { bot_enable = var->integer; } else { bot_enable = 0; } // load the dll or bytecode gvm = VM_Create( "qagame", SV_GameSystemCalls, (vmInterpret_t) (int) Cvar_VariableValue( "vm_game" ) ); if ( !gvm ) { Com_Error( ERR_FATAL, "VM_Create on game failed" ); } SV_InitGameVM( qfalse ); } /* ==================== SV_GameCommand See if the current console command is claimed by the game ==================== */ qboolean SV_GameCommand( void ) { if ( sv.state != SS_GAME ) { return qfalse; } return (qboolean) VM_Call( gvm, GAME_CONSOLE_COMMAND ); } ================================================ FILE: src/engine/server/sv_init.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "server.h" /* =============== SV_SetConfigstring =============== */ void SV_SetConfigstring (int index, const char *val) { int len, i; int maxChunkSize = MAX_STRING_CHARS - 24; client_t *client; if ( index < 0 || index >= MAX_CONFIGSTRINGS ) { Com_Error (ERR_DROP, "SV_SetConfigstring: bad index %i\n", index); } if ( !val ) { val = ""; } // don't bother broadcasting an update if no change if ( !strcmp( val, sv.configstrings[ index ] ) ) { return; } // change the string in sv Z_Free( sv.configstrings[index] ); sv.configstrings[index] = CopyString( val ); // send it to all the clients if we aren't // spawning a new server if ( sv.state == SS_GAME || sv.restarting ) { // send the data to all relevent clients for (i = 0, client = svs.clients; i < sv_maxclients->integer ; i++, client++) { if ( client->state < CS_PRIMED ) { continue; } // do not always send server info to all clients if ( index == CS_SERVERINFO && client->gentity && (client->gentity->r.svFlags & SVF_NOSERVERINFO) ) { continue; } len = (int)strlen( val ); if( len >= maxChunkSize ) { int sent = 0; int remaining = len; char *cmd; char buf[MAX_STRING_CHARS]; while (remaining > 0 ) { if ( sent == 0 ) { cmd = "bcs0"; } else if( remaining < maxChunkSize ) { cmd = "bcs2"; } else { cmd = "bcs1"; } Q_strncpyz( buf, &val[sent], maxChunkSize ); SV_SendServerCommand( client, "%s %i \"%s\"\n", cmd, index, buf ); sent += (maxChunkSize - 1); remaining -= (maxChunkSize - 1); } } else { // standard cs, just send it SV_SendServerCommand( client, "cs %i \"%s\"\n", index, val ); } } } } /* =============== SV_GetConfigstring =============== */ void SV_GetConfigstring( int index, char *buffer, int bufferSize ) { if ( bufferSize < 1 ) { Com_Error( ERR_DROP, "SV_GetConfigstring: bufferSize == %i", bufferSize ); } if ( index < 0 || index >= MAX_CONFIGSTRINGS ) { Com_Error (ERR_DROP, "SV_GetConfigstring: bad index %i\n", index); } if ( !sv.configstrings[index] ) { buffer[0] = 0; return; } Q_strncpyz( buffer, sv.configstrings[index], bufferSize ); } /* =============== SV_SetUserinfo =============== */ void SV_SetUserinfo( int index, const char *val ) { if ( index < 0 || index >= sv_maxclients->integer ) { Com_Error (ERR_DROP, "SV_SetUserinfo: bad index %i\n", index); } if ( !val ) { val = ""; } Q_strncpyz( svs.clients[index].userinfo, val, sizeof( svs.clients[ index ].userinfo ) ); Q_strncpyz( svs.clients[index].name, Info_ValueForKey( val, "name" ), sizeof(svs.clients[index].name) ); } /* =============== SV_GetUserinfo =============== */ void SV_GetUserinfo( int index, char *buffer, int bufferSize ) { if ( bufferSize < 1 ) { Com_Error( ERR_DROP, "SV_GetUserinfo: bufferSize == %i", bufferSize ); } if ( index < 0 || index >= sv_maxclients->integer ) { Com_Error (ERR_DROP, "SV_GetUserinfo: bad index %i\n", index); } Q_strncpyz( buffer, svs.clients[ index ].userinfo, bufferSize ); } /* ================ SV_CreateBaseline Entity baselines are used to compress non-delta messages to the clients -- only the fields that differ from the baseline will be transmitted ================ */ void SV_CreateBaseline( void ) { sharedEntity_t *svent; int entnum; for ( entnum = 1; entnum < sv.num_entities ; entnum++ ) { svent = SV_GentityNum(entnum); if (!svent->r.linked) { continue; } svent->s.number = entnum; // // take current state as baseline // sv.svEntities[entnum].baseline = svent->s; } } /* =============== SV_BoundMaxClients =============== */ void SV_BoundMaxClients( int minimum ) { // get the current maxclients value Cvar_Get( "sv_maxclients", "8", 0 ); sv_maxclients->modified = qfalse; if ( sv_maxclients->integer < minimum ) { Cvar_Set( "sv_maxclients", va("%i", minimum) ); } else if ( sv_maxclients->integer > MAX_CLIENTS ) { Cvar_Set( "sv_maxclients", va("%i", MAX_CLIENTS) ); } } /* =============== SV_Startup Called when a host starts a map when it wasn't running one before. Successive map or map_restart commands will NOT cause this to be called, unless the game is exited to the menu system first. =============== */ void SV_Startup( void ) { if ( svs.initialized ) { Com_Error( ERR_FATAL, "SV_Startup: svs.initialized" ); } SV_BoundMaxClients( 1 ); svs.clients = (client_t*) Z_Malloc (sizeof(client_t) * sv_maxclients->integer ); if ( com_dedicated->integer ) { svs.numSnapshotEntities = sv_maxclients->integer * PACKET_BACKUP * 64; } else { // we don't need nearly as many when playing locally svs.numSnapshotEntities = sv_maxclients->integer * 4 * 64; } svs.initialized = qtrue; Cvar_Set( "sv_running", "1" ); } /* ================== SV_ChangeMaxClients ================== */ void SV_ChangeMaxClients( void ) { int oldMaxClients; int i; client_t *oldClients; int count; // get the highest client number in use count = 0; for ( i = 0 ; i < sv_maxclients->integer ; i++ ) { if ( svs.clients[i].state >= CS_CONNECTED ) { if (i > count) count = i; } } count++; oldMaxClients = sv_maxclients->integer; // never go below the highest client number in use SV_BoundMaxClients( count ); // if still the same if ( sv_maxclients->integer == oldMaxClients ) { return; } oldClients = (client_t*) Hunk_AllocateTempMemory( count * sizeof(client_t) ); // copy the clients to hunk memory for ( i = 0 ; i < count ; i++ ) { if ( svs.clients[i].state >= CS_CONNECTED ) { oldClients[i] = svs.clients[i]; } else { Com_Memset(&oldClients[i], 0, sizeof(client_t)); } } // free old clients arrays Z_Free( svs.clients ); // allocate new clients svs.clients = (client_t*) Z_Malloc ( sv_maxclients->integer * sizeof(client_t) ); Com_Memset( svs.clients, 0, sv_maxclients->integer * sizeof(client_t) ); // copy the clients over for ( i = 0 ; i < count ; i++ ) { if ( oldClients[i].state >= CS_CONNECTED ) { svs.clients[i] = oldClients[i]; } } // free the old clients on the hunk Hunk_FreeTempMemory( oldClients ); // allocate new snapshot entities if ( com_dedicated->integer ) { svs.numSnapshotEntities = sv_maxclients->integer * PACKET_BACKUP * 64; } else { // we don't need nearly as many when playing locally svs.numSnapshotEntities = sv_maxclients->integer * 4 * 64; } } /* ================ SV_ClearServer ================ */ void SV_ClearServer(void) { int i; for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) { if ( sv.configstrings[i] ) { Z_Free( sv.configstrings[i] ); } } Com_Memset (&sv, 0, sizeof(sv)); } /* ================ SV_TouchCGame touch the cgame.vm so that a pure client can load it if it's in a seperate pk3 ================ */ void SV_TouchCGame(void) { fileHandle_t f; char filename[MAX_QPATH]; Com_sprintf( filename, sizeof(filename), "vm/%s.qvm", "cgame" ); FS_FOpenFileRead( filename, &f, qfalse ); if ( f ) { FS_FCloseFile( f ); } } /* ================ SV_SpawnServer Change the server to a new map, taking all connected clients along with it. This is NOT called for map_restart ================ */ void SV_SpawnServer( char *server, qboolean killBots ) { int i; int checksum; qboolean isBot; char systemInfo[16384]; const char *p; // shut down the existing game if it is running SV_ShutdownGameProgs(); Com_Printf ("------ Server Initialization ------\n"); Com_Printf ("Server: %s\n",server); // if not running a dedicated server CL_MapLoading will connect the client to the server // also print some status stuff CL_MapLoading(); // make sure all the client stuff is unloaded CL_ShutdownAll(); // clear the whole hunk because we're (re)loading the server Hunk_Clear(); // clear collision map data CM_ClearMap(); // init client structures and svs.numSnapshotEntities if ( !Cvar_VariableValue("sv_running") ) { SV_Startup(); } else { // check for maxclients change if ( sv_maxclients->modified ) { SV_ChangeMaxClients(); } } // clear pak references FS_ClearPakReferences(0); // allocate the snapshot entities on the hunk svs.snapshotEntities = (entityState_t*) Hunk_Alloc( sizeof(entityState_t)*svs.numSnapshotEntities, h_high ); svs.nextSnapshotEntities = 0; // toggle the server bit so clients can detect that a // server has changed svs.snapFlagServerBit ^= SNAPFLAG_SERVERCOUNT; // set nextmap to the same map, but it may be overriden // by the game startup or another console command Cvar_Set( "nextmap", "map_restart 0"); // Cvar_Set( "nextmap", va("map %s", server) ); // wipe the entire per-level structure SV_ClearServer(); for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) { sv.configstrings[i] = CopyString(""); } // make sure we are not paused Cvar_Set("cl_paused", "0"); // get a new checksum feed and restart the file system srand(Com_Milliseconds()); sv.checksumFeed = ( ((int) rand() << 16) ^ rand() ) ^ Com_Milliseconds(); FS_Restart( sv.checksumFeed ); CM_LoadMap( va("maps/%s.bsp", server), qfalse, &checksum ); // set serverinfo visible name Cvar_Set( "mapname", server ); Cvar_Set( "sv_mapChecksum", va("%i",checksum) ); // serverid should be different each time sv.serverId = com_frameTime; sv.restartedServerId = sv.serverId; // I suppose the init here is just to be safe sv.checksumFeedServerId = sv.serverId; Cvar_Set( "sv_serverid", va("%i", sv.serverId ) ); // clear physics interaction links SV_ClearWorld (); // media configstring setting should be done during // the loading stage, so connected clients don't have // to load during actual gameplay sv.state = SS_LOADING; // load and spawn all other entities SV_InitGameProgs(); // don't allow a map_restart if game is modified sv_gametype->modified = qfalse; // run a few frames to allow everything to settle for ( i = 0 ;i < 3 ; i++ ) { VM_Call( gvm, GAME_RUN_FRAME, svs.time ); SV_BotFrame( svs.time ); svs.time += 100; } // create a baseline for more efficient communications SV_CreateBaseline (); for (i=0 ; iinteger ; i++) { // send the new gamestate to all connected clients if (svs.clients[i].state >= CS_CONNECTED) { char *denied; if ( svs.clients[i].netchan.remoteAddress.type == NA_BOT ) { if ( killBots ) { SV_DropClient( &svs.clients[i], "" ); continue; } isBot = qtrue; } else { isBot = qfalse; } // connect the client again denied = (char*) VM_ExplicitArgPtr( gvm, VM_Call( gvm, GAME_CLIENT_CONNECT, i, qfalse, isBot ) ); // firstTime = qfalse if ( denied ) { // this generally shouldn't happen, because the client // was connected before the level change SV_DropClient( &svs.clients[i], denied ); } else { if( !isBot ) { // when we get the next packet from a connected client, // the new gamestate will be sent svs.clients[i].state = CS_CONNECTED; } else { client_t *client; sharedEntity_t *ent; client = &svs.clients[i]; client->state = CS_ACTIVE; ent = SV_GentityNum( i ); ent->s.number = i; client->gentity = ent; client->deltaMessage = -1; client->nextSnapshotTime = svs.time; // generate a snapshot immediately VM_Call( gvm, GAME_CLIENT_BEGIN, i ); } } } } // run another frame to allow things to look at all the players VM_Call( gvm, GAME_RUN_FRAME, svs.time ); SV_BotFrame( svs.time ); svs.time += 100; if ( sv_pure->integer ) { // the server sends these to the clients so they will only // load pk3s also loaded at the server p = FS_LoadedPakChecksums(); Cvar_Set( "sv_paks", p ); if (strlen(p) == 0) { Com_Printf( "WARNING: sv_pure set but no PK3 files loaded\n" ); } p = FS_LoadedPakNames(); Cvar_Set( "sv_pakNames", p ); // if a dedicated pure server we need to touch the cgame because it could be in a // seperate pk3 file and the client will need to load the latest cgame.qvm if ( com_dedicated->integer ) { SV_TouchCGame(); } } else { Cvar_Set( "sv_paks", "" ); Cvar_Set( "sv_pakNames", "" ); } // the server sends these to the clients so they can figure // out which pk3s should be auto-downloaded p = FS_ReferencedPakChecksums(); Cvar_Set( "sv_referencedPaks", p ); p = FS_ReferencedPakNames(); Cvar_Set( "sv_referencedPakNames", p ); // save systeminfo and serverinfo strings Q_strncpyz( systemInfo, Cvar_InfoString_Big( CVAR_SYSTEMINFO ), sizeof( systemInfo ) ); cvar_modifiedFlags &= ~CVAR_SYSTEMINFO; SV_SetConfigstring( CS_SYSTEMINFO, systemInfo ); SV_SetConfigstring( CS_SERVERINFO, Cvar_InfoString( CVAR_SERVERINFO ) ); cvar_modifiedFlags &= ~CVAR_SERVERINFO; // any media configstring setting now should issue a warning // and any configstring changes should be reliably transmitted // to all clients sv.state = SS_GAME; // send a heartbeat now so the master will get up to date info SV_Heartbeat_f(); Hunk_SetMark(); Com_Printf ("-----------------------------------\n"); } /* =============== SV_Init Only called at main exe startup, not for each game =============== */ void SV_BotInitBotLib(void); void SV_Init (void) { SV_AddOperatorCommands (); // serverinfo vars Cvar_Get ("dmflags", "0", CVAR_SERVERINFO); Cvar_Get ("fraglimit", "20", CVAR_SERVERINFO); Cvar_Get ("timelimit", "0", CVAR_SERVERINFO); sv_gametype = Cvar_Get ("g_gametype", "0", CVAR_SERVERINFO | CVAR_LATCH ); Cvar_Get ("sv_keywords", "", CVAR_SERVERINFO); Cvar_Get ("protocol", va("%i", PROTOCOL_VERSION), CVAR_SERVERINFO | CVAR_ROM); sv_mapname = Cvar_Get ("mapname", "nomap", CVAR_SERVERINFO | CVAR_ROM); sv_privateClients = Cvar_Get ("sv_privateClients", "0", CVAR_SERVERINFO); sv_hostname = Cvar_Get ("sv_hostname", "noname", CVAR_SERVERINFO | CVAR_ARCHIVE ); sv_maxclients = Cvar_Get ("sv_maxclients", "8", CVAR_SERVERINFO | CVAR_LATCH); sv_maxRate = Cvar_Get ("sv_maxRate", "0", CVAR_ARCHIVE | CVAR_SERVERINFO ); sv_minPing = Cvar_Get ("sv_minPing", "0", CVAR_ARCHIVE | CVAR_SERVERINFO ); sv_maxPing = Cvar_Get ("sv_maxPing", "0", CVAR_ARCHIVE | CVAR_SERVERINFO ); sv_floodProtect = Cvar_Get ("sv_floodProtect", "1", CVAR_ARCHIVE | CVAR_SERVERINFO ); // systeminfo Cvar_Get ("sv_cheats", "1", CVAR_SYSTEMINFO | CVAR_ROM ); sv_serverid = Cvar_Get ("sv_serverid", "0", CVAR_SYSTEMINFO | CVAR_ROM ); #ifndef DLL_ONLY // bk010216 - for DLL-only servers sv_pure = Cvar_Get ("sv_pure", "1", CVAR_SYSTEMINFO ); #else sv_pure = Cvar_Get ("sv_pure", "0", CVAR_SYSTEMINFO | CVAR_INIT | CVAR_ROM ); #endif Cvar_Get ("sv_paks", "", CVAR_SYSTEMINFO | CVAR_ROM ); Cvar_Get ("sv_pakNames", "", CVAR_SYSTEMINFO | CVAR_ROM ); Cvar_Get ("sv_referencedPaks", "", CVAR_SYSTEMINFO | CVAR_ROM ); Cvar_Get ("sv_referencedPakNames", "", CVAR_SYSTEMINFO | CVAR_ROM ); // server vars sv_rconPassword = Cvar_Get ("rconPassword", "", CVAR_TEMP ); sv_privatePassword = Cvar_Get ("sv_privatePassword", "", CVAR_TEMP ); sv_fps = Cvar_Get ("sv_fps", "20", CVAR_TEMP ); sv_timeout = Cvar_Get ("sv_timeout", "200", CVAR_TEMP ); sv_zombietime = Cvar_Get ("sv_zombietime", "2", CVAR_TEMP ); Cvar_Get ("nextmap", "", CVAR_TEMP ); sv_allowDownload = Cvar_Get ("sv_allowDownload", "0", CVAR_SERVERINFO); sv_master[0] = Cvar_Get ("sv_master1", MASTER_SERVER_NAME, 0 ); sv_master[1] = Cvar_Get ("sv_master2", "", CVAR_ARCHIVE ); sv_master[2] = Cvar_Get ("sv_master3", "", CVAR_ARCHIVE ); sv_master[3] = Cvar_Get ("sv_master4", "", CVAR_ARCHIVE ); sv_master[4] = Cvar_Get ("sv_master5", "", CVAR_ARCHIVE ); sv_reconnectlimit = Cvar_Get ("sv_reconnectlimit", "3", 0); sv_showloss = Cvar_Get ("sv_showloss", "0", 0); sv_padPackets = Cvar_Get ("sv_padPackets", "0", 0); sv_killserver = Cvar_Get ("sv_killserver", "0", 0); sv_mapChecksum = Cvar_Get ("sv_mapChecksum", "", CVAR_ROM); sv_lanForceRate = Cvar_Get ("sv_lanForceRate", "1", CVAR_ARCHIVE ); sv_strictAuth = Cvar_Get ("sv_strictAuth", "1", CVAR_ARCHIVE ); // initialize bot cvars so they are listed and can be set before loading the botlib SV_BotInitCvars(); // init the botlib here because we need the pre-compiler in the UI SV_BotInitBotLib(); } /* ================== SV_FinalMessage Used by SV_Shutdown to send a final message to all connected clients before the server goes down. The messages are sent immediately, not just stuck on the outgoing message list, because the server is going to totally exit after returning from this function. ================== */ void SV_FinalMessage( char *message ) { int i, j; client_t *cl; // send it twice, ignoring rate for ( j = 0 ; j < 2 ; j++ ) { for (i=0, cl = svs.clients ; i < sv_maxclients->integer ; i++, cl++) { if (cl->state >= CS_CONNECTED) { // don't send a disconnect to a local client if ( cl->netchan.remoteAddress.type != NA_LOOPBACK ) { SV_SendServerCommand( cl, "print \"%s\"", message ); SV_SendServerCommand( cl, "disconnect" ); } // force a snapshot to be sent cl->nextSnapshotTime = -1; SV_SendClientSnapshot( cl ); } } } } /* ================ SV_Shutdown Called when each game quits, before Sys_Quit or Sys_Error ================ */ void SV_Shutdown( char *finalmsg ) { if ( !com_sv_running || !com_sv_running->integer ) { return; } Com_Printf( "----- Server Shutdown -----\n" ); if ( svs.clients && !com_errorEntered ) { SV_FinalMessage( finalmsg ); } SV_RemoveOperatorCommands(); SV_MasterShutdown(); SV_ShutdownGameProgs(); // free current level SV_ClearServer(); // free server static data if ( svs.clients ) { Z_Free( svs.clients ); } Com_Memset( &svs, 0, sizeof( svs ) ); Cvar_Set( "sv_running", "0" ); Cvar_Set("ui_singlePlayerActive", "0"); Com_Printf( "---------------------------\n" ); // disconnect any local clients CL_Disconnect( qfalse ); } ================================================ FILE: src/engine/server/sv_main.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "server.h" serverStatic_t svs; // persistant server info server_t sv; // local server vm_t *gvm = NULL; // game virtual machine // bk001212 init cvar_t *sv_fps; // time rate for running non-clients cvar_t *sv_timeout; // seconds without any message cvar_t *sv_zombietime; // seconds to sink messages after disconnect cvar_t *sv_rconPassword; // password for remote server commands cvar_t *sv_privatePassword; // password for the privateClient slots cvar_t *sv_allowDownload; cvar_t *sv_maxclients; cvar_t *sv_privateClients; // number of clients reserved for password cvar_t *sv_hostname; cvar_t *sv_master[MAX_MASTER_SERVERS]; // master server ip address cvar_t *sv_reconnectlimit; // minimum seconds between connect messages cvar_t *sv_showloss; // report when usercmds are lost cvar_t *sv_padPackets; // add nop bytes to messages cvar_t *sv_killserver; // menu system can set to 1 to shut server down cvar_t *sv_mapname; cvar_t *sv_mapChecksum; cvar_t *sv_serverid; cvar_t *sv_maxRate; cvar_t *sv_minPing; cvar_t *sv_maxPing; cvar_t *sv_gametype; cvar_t *sv_pure; cvar_t *sv_floodProtect; cvar_t *sv_lanForceRate; // dedicated 1 (LAN) server forces local client rates to 99999 (bug #491) cvar_t *sv_strictAuth; /* ============================================================================= EVENT MESSAGES ============================================================================= */ /* =============== SV_ExpandNewlines Converts newlines to "\n" so a line prints nicer =============== */ char *SV_ExpandNewlines( char *in ) { static char string[1024]; int l; l = 0; while ( *in && l < sizeof(string) - 3 ) { if ( *in == '\n' ) { string[l++] = '\\'; string[l++] = 'n'; } else { string[l++] = *in; } in++; } string[l] = 0; return string; } /* ====================== SV_ReplacePendingServerCommands This is ugly ====================== */ int SV_ReplacePendingServerCommands( client_t *client, const char *cmd ) { int i, index, csnum1, csnum2; for ( i = client->reliableSent+1; i <= client->reliableSequence; i++ ) { index = i & ( MAX_RELIABLE_COMMANDS - 1 ); // if ( !Q_strncmp(cmd, client->reliableCommands[ index ], (int)strlen("cs")) ) { sscanf(cmd, "cs %i", &csnum1); sscanf(client->reliableCommands[ index ], "cs %i", &csnum2); if ( csnum1 == csnum2 ) { Q_strncpyz( client->reliableCommands[ index ], cmd, sizeof( client->reliableCommands[ index ] ) ); /* if ( client->netchan.remoteAddress.type != NA_BOT ) { Com_Printf( "WARNING: client %i removed double pending config string %i: %s\n", client-svs.clients, csnum1, cmd ); } */ return qtrue; } } } return qfalse; } /* ====================== SV_AddServerCommand The given command will be transmitted to the client, and is guaranteed to not have future snapshot_t executed before it is executed ====================== */ void SV_AddServerCommand( client_t *client, const char *cmd ) { int index, i; // this is very ugly but it's also a waste to for instance send multiple config string updates // for the same config string index in one snapshot // if ( SV_ReplacePendingServerCommands( client, cmd ) ) { // return; // } client->reliableSequence++; // if we would be losing an old command that hasn't been acknowledged, // we must drop the connection // we check == instead of >= so a broadcast print added by SV_DropClient() // doesn't cause a recursive drop client if ( client->reliableSequence - client->reliableAcknowledge == MAX_RELIABLE_COMMANDS + 1 ) { Com_Printf( "===== pending server commands =====\n" ); for ( i = client->reliableAcknowledge + 1 ; i <= client->reliableSequence ; i++ ) { Com_Printf( "cmd %5d: %s\n", i, client->reliableCommands[ i & (MAX_RELIABLE_COMMANDS-1) ] ); } Com_Printf( "cmd %5d: %s\n", i, cmd ); SV_DropClient( client, "Server command overflow" ); return; } index = client->reliableSequence & ( MAX_RELIABLE_COMMANDS - 1 ); Q_strncpyz( client->reliableCommands[ index ], cmd, sizeof( client->reliableCommands[ index ] ) ); } /* ================= SV_SendServerCommand Sends a reliable command string to be interpreted by the client game module: "cp", "print", "chat", etc A NULL client will broadcast to all clients ================= */ void QDECL SV_SendServerCommand(client_t *cl, const char *fmt, ...) { va_list argptr; byte message[MAX_MSGLEN]; client_t *client; int j; va_start (argptr,fmt); Q_vsnprintf ((char *)message, sizeof(message), fmt,argptr); va_end (argptr); if ( cl != NULL ) { SV_AddServerCommand( cl, (char *)message ); return; } // hack to echo broadcast prints to console if ( com_dedicated->integer && !strncmp( (char *)message, "print", 5) ) { Com_Printf ("broadcast: %s\n", SV_ExpandNewlines((char *)message) ); } // send the data to all relevent clients for (j = 0, client = svs.clients; j < sv_maxclients->integer ; j++, client++) { if ( client->state < CS_PRIMED ) { continue; } SV_AddServerCommand( client, (char *)message ); } } /* ============================================================================== MASTER SERVER FUNCTIONS ============================================================================== */ /* ================ SV_MasterHeartbeat Send a message to the masters every few minutes to let it know we are alive, and log information. We will also have a heartbeat sent when a server changes from empty to non-empty, and full to non-full, but not on every player enter or exit. ================ */ #define HEARTBEAT_MSEC 300*1000 #define HEARTBEAT_GAME "QuakeArena-1" void SV_MasterHeartbeat( void ) { static netadr_t adr[MAX_MASTER_SERVERS]; int i; // "dedicated 1" is for lan play, "dedicated 2" is for inet public play if ( !com_dedicated || com_dedicated->integer != 2 ) { return; // only dedicated servers send heartbeats } // if not time yet, don't send anything if ( svs.time < svs.nextHeartbeatTime ) { return; } svs.nextHeartbeatTime = svs.time + HEARTBEAT_MSEC; // send to group masters for ( i = 0 ; i < MAX_MASTER_SERVERS ; i++ ) { if ( !sv_master[i]->string[0] ) { continue; } // see if we haven't already resolved the name // resolving usually causes hitches on win95, so only // do it when needed if ( sv_master[i]->modified ) { sv_master[i]->modified = qfalse; Com_Printf( "Resolving %s\n", sv_master[i]->string ); if ( !NET_StringToAdr( sv_master[i]->string, &adr[i] ) ) { // if the address failed to resolve, clear it // so we don't take repeated dns hits Com_Printf( "Couldn't resolve address: %s\n", sv_master[i]->string ); Cvar_Set( sv_master[i]->name, "" ); sv_master[i]->modified = qfalse; continue; } if ( !strstr( ":", sv_master[i]->string ) ) { adr[i].port = BigShort( PORT_MASTER ); } Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", sv_master[i]->string, adr[i].ip[0], adr[i].ip[1], adr[i].ip[2], adr[i].ip[3], BigShort( adr[i].port ) ); } Com_Printf ("Sending heartbeat to %s\n", sv_master[i]->string ); // this command should be changed if the server info / status format // ever incompatably changes NET_OutOfBandPrint( NS_SERVER, adr[i], "heartbeat %s\n", HEARTBEAT_GAME ); } } /* ================= SV_MasterShutdown Informs all masters that this server is going down ================= */ void SV_MasterShutdown( void ) { // send a hearbeat right now svs.nextHeartbeatTime = -9999; SV_MasterHeartbeat(); // send it again to minimize chance of drops svs.nextHeartbeatTime = -9999; SV_MasterHeartbeat(); // when the master tries to poll the server, it won't respond, so // it will be removed from the list } /* ============================================================================== CONNECTIONLESS COMMANDS ============================================================================== */ /* ================ SVC_Status Responds with all the info that qplug or qspy can see about the server and all connected players. Used for getting detailed information after the simple info query. ================ */ void SVC_Status( netadr_t from ) { char player[1024]; char status[MAX_MSGLEN]; int i; client_t *cl; playerState_t *ps; int statusLength; int playerLength; char infostring[MAX_INFO_STRING]; // ignore if we are in single player if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER ) { return; } strcpy( infostring, Cvar_InfoString( CVAR_SERVERINFO ) ); // echo back the parameter to status. so master servers can use it as a challenge // to prevent timed spoofed reply packets that add ghost servers Info_SetValueForKey( infostring, "challenge", Cmd_Argv(1) ); // add "demo" to the sv_keywords if restricted if ( Cvar_VariableValue( "fs_restrict" ) ) { char keywords[MAX_INFO_STRING]; Com_sprintf( keywords, sizeof( keywords ), "demo %s", Info_ValueForKey( infostring, "sv_keywords" ) ); Info_SetValueForKey( infostring, "sv_keywords", keywords ); } status[0] = 0; statusLength = 0; for (i=0 ; i < sv_maxclients->integer ; i++) { cl = &svs.clients[i]; if ( cl->state >= CS_CONNECTED ) { ps = SV_GameClientNum( i ); Com_sprintf (player, sizeof(player), "%i %i \"%s\"\n", ps->persistant[PERS_SCORE], cl->ping, cl->name); playerLength = (int)strlen(player); if (statusLength + playerLength >= sizeof(status) ) { break; // can't hold any more } strcpy (status + statusLength, player); statusLength += playerLength; } } NET_OutOfBandPrint( NS_SERVER, from, "statusResponse\n%s\n%s", infostring, status ); } /* ================ SVC_Info Responds with a short info message that should be enough to determine if a user is interested in a server to do a full status ================ */ void SVC_Info( netadr_t from ) { int i, count; char *gamedir; char infostring[MAX_INFO_STRING]; // ignore if we are in single player if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive")) { return; } // don't count privateclients count = 0; for ( i = sv_privateClients->integer ; i < sv_maxclients->integer ; i++ ) { if ( svs.clients[i].state >= CS_CONNECTED ) { count++; } } infostring[0] = 0; // echo back the parameter to status. so servers can use it as a challenge // to prevent timed spoofed reply packets that add ghost servers Info_SetValueForKey( infostring, "challenge", Cmd_Argv(1) ); Info_SetValueForKey( infostring, "protocol", va("%i", PROTOCOL_VERSION) ); Info_SetValueForKey( infostring, "hostname", sv_hostname->string ); Info_SetValueForKey( infostring, "mapname", sv_mapname->string ); Info_SetValueForKey( infostring, "clients", va("%i", count) ); Info_SetValueForKey( infostring, "sv_maxclients", va("%i", sv_maxclients->integer - sv_privateClients->integer ) ); Info_SetValueForKey( infostring, "gametype", va("%i", sv_gametype->integer ) ); Info_SetValueForKey( infostring, "pure", va("%i", sv_pure->integer ) ); if( sv_minPing->integer ) { Info_SetValueForKey( infostring, "minPing", va("%i", sv_minPing->integer) ); } if( sv_maxPing->integer ) { Info_SetValueForKey( infostring, "maxPing", va("%i", sv_maxPing->integer) ); } gamedir = Cvar_VariableString( "fs_game" ); if( *gamedir ) { Info_SetValueForKey( infostring, "game", gamedir ); } NET_OutOfBandPrint( NS_SERVER, from, "infoResponse\n%s", infostring ); } /* ================ SVC_FlushRedirect ================ */ void SV_FlushRedirect( char *outputbuf ) { NET_OutOfBandPrint( NS_SERVER, svs.redirectAddress, "print\n%s", outputbuf ); } /* =============== SVC_RemoteCommand An rcon packet arrived from the network. Shift down the remaining args Redirect all printfs =============== */ void SVC_RemoteCommand( netadr_t from, msg_t *msg ) { qboolean valid; unsigned int time; char remaining[1024]; // TTimo - scaled down to accumulate, but not overflow anything network wise, print wise etc. // (OOB messages are the bottleneck here) #define SV_OUTPUTBUF_LENGTH (1024 - 16) char sv_outputbuf[SV_OUTPUTBUF_LENGTH]; static unsigned int lasttime = 0; char *cmd_aux; // TTimo - https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=534 time = Com_Milliseconds(); if (time<(lasttime+500)) { return; } lasttime = time; if ( !strlen( sv_rconPassword->string ) || strcmp (Cmd_Argv(1), sv_rconPassword->string) ) { valid = qfalse; Com_Printf ("Bad rcon from %s:\n%s\n", NET_AdrToString (from), Cmd_Argv(2) ); } else { valid = qtrue; Com_Printf ("Rcon from %s:\n%s\n", NET_AdrToString (from), Cmd_Argv(2) ); } // start redirecting all print outputs to the packet svs.redirectAddress = from; Com_BeginRedirect (sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect); if ( !strlen( sv_rconPassword->string ) ) { Com_Printf ("No rconpassword set on the server.\n"); } else if ( !valid ) { Com_Printf ("Bad rconpassword.\n"); } else { remaining[0] = 0; // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=543 // get the command directly, "rcon " to avoid quoting issues // extract the command by walking // since the cmd formatting can fuckup (amount of spaces), using a dumb step by step parsing cmd_aux = Cmd_Cmd(); cmd_aux+=4; while(cmd_aux[0]==' ') cmd_aux++; while(cmd_aux[0] && cmd_aux[0]!=' ') // password cmd_aux++; while(cmd_aux[0]==' ') cmd_aux++; Q_strcat( remaining, sizeof(remaining), cmd_aux); Cmd_ExecuteString (remaining); } Com_EndRedirect (); } /* ================= SV_ConnectionlessPacket A connectionless packet has four leading 0xff characters to distinguish it from a game channel. Clients that are in the game can still send connectionless packets. ================= */ void SV_ConnectionlessPacket( netadr_t from, msg_t *msg ) { char *s; char *c; MSG_BeginReadingOOB( msg ); MSG_ReadLong( msg ); // skip the -1 marker if (!Q_strncmp("connect", (const char*) &msg->data[4], 7)) { Huff_Decompress(msg, 12); } s = MSG_ReadStringLine( msg ); Cmd_TokenizeString( s ); c = Cmd_Argv(0); Com_DPrintf ("SV packet %s : %s\n", NET_AdrToString(from), c); if (!Q_stricmp(c, "getstatus")) { SVC_Status( from ); } else if (!Q_stricmp(c, "getinfo")) { SVC_Info( from ); } else if (!Q_stricmp(c, "getchallenge")) { SV_GetChallenge( from ); } else if (!Q_stricmp(c, "connect")) { SV_DirectConnect( from ); } else if (!Q_stricmp(c, "ipAuthorize")) { SV_AuthorizeIpPacket( from ); } else if (!Q_stricmp(c, "rcon")) { SVC_RemoteCommand( from, msg ); } else if (!Q_stricmp(c, "disconnect")) { // if a client starts up a local server, we may see some spurious // server disconnect messages when their new server sees our final // sequenced messages to the old client } else { Com_DPrintf ("bad connectionless packet from %s:\n%s\n" , NET_AdrToString (from), s); } } //============================================================================ /* ================= SV_ReadPackets ================= */ void SV_PacketEvent( netadr_t from, msg_t *msg ) { int i; client_t *cl; int qport; // check for connectionless packet (0xffffffff) first if ( msg->cursize >= 4 && *(int *)msg->data == -1) { SV_ConnectionlessPacket( from, msg ); return; } // read the qport out of the message so we can fix up // stupid address translating routers MSG_BeginReadingOOB( msg ); MSG_ReadLong( msg ); // sequence number qport = MSG_ReadShort( msg ) & 0xffff; // find which client the message is from for (i=0, cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) { if (cl->state == CS_FREE) { continue; } if ( !NET_CompareBaseAdr( from, cl->netchan.remoteAddress ) ) { continue; } // it is possible to have multiple clients from a single IP // address, so they are differentiated by the qport variable if (cl->netchan.qport != qport) { continue; } // the IP port can't be used to differentiate them, because // some address translating routers periodically change UDP // port assignments if (cl->netchan.remoteAddress.port != from.port) { Com_Printf( "SV_PacketEvent: fixing up a translated port\n" ); cl->netchan.remoteAddress.port = from.port; } // make sure it is a valid, in sequence packet if (SV_Netchan_Process(cl, msg)) { // zombie clients still need to do the Netchan_Process // to make sure they don't need to retransmit the final // reliable message, but they don't do any other processing if (cl->state != CS_ZOMBIE) { cl->lastPacketTime = svs.time; // don't timeout SV_ExecuteClientMessage( cl, msg ); } } return; } // if we received a sequenced packet from an address we don't recognize, // send an out of band disconnect packet to it NET_OutOfBandPrint( NS_SERVER, from, "disconnect" ); } /* =================== SV_CalcPings Updates the cl->ping variables =================== */ void SV_CalcPings( void ) { int i, j; client_t *cl; int total, count; int delta; playerState_t *ps; for (i=0 ; i < sv_maxclients->integer ; i++) { cl = &svs.clients[i]; if ( cl->state != CS_ACTIVE ) { cl->ping = 999; continue; } if ( !cl->gentity ) { cl->ping = 999; continue; } if ( cl->gentity->r.svFlags & SVF_BOT ) { cl->ping = 0; continue; } total = 0; count = 0; for ( j = 0 ; j < PACKET_BACKUP ; j++ ) { if ( cl->frames[j].messageAcked <= 0 ) { continue; } delta = cl->frames[j].messageAcked - cl->frames[j].messageSent; count++; total += delta; } if (!count) { cl->ping = 999; } else { cl->ping = total/count; if ( cl->ping > 999 ) { cl->ping = 999; } } // let the game dll know about the ping ps = SV_GameClientNum( i ); ps->ping = cl->ping; } } /* ================== SV_CheckTimeouts If a packet has not been received from a client for timeout->integer seconds, drop the conneciton. Server time is used instead of realtime to avoid dropping the local client while debugging. When a client is normally dropped, the client_t goes into a zombie state for a few seconds to make sure any final reliable message gets resent if necessary ================== */ void SV_CheckTimeouts( void ) { int i; client_t *cl; int droppoint; int zombiepoint; droppoint = svs.time - 1000 * sv_timeout->integer; zombiepoint = svs.time - 1000 * sv_zombietime->integer; for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) { // message times may be wrong across a changelevel if (cl->lastPacketTime > svs.time) { cl->lastPacketTime = svs.time; } if (cl->state == CS_ZOMBIE && cl->lastPacketTime < zombiepoint) { // using the client id cause the cl->name is empty at this point Com_DPrintf( "Going from CS_ZOMBIE to CS_FREE for client %d\n", i ); cl->state = CS_FREE; // can now be reused continue; } if ( cl->state >= CS_CONNECTED && cl->lastPacketTime < droppoint) { // wait several frames so a debugger session doesn't // cause a timeout if ( ++cl->timeoutCount > 5 ) { SV_DropClient (cl, "timed out"); cl->state = CS_FREE; // don't bother with zombie state } } else { cl->timeoutCount = 0; } } } /* ================== SV_CheckPaused ================== */ qboolean SV_CheckPaused( void ) { int count; client_t *cl; int i; if ( !cl_paused->integer ) { return qfalse; } // only pause if there is just a single client connected count = 0; for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) { if ( cl->state >= CS_CONNECTED && cl->netchan.remoteAddress.type != NA_BOT ) { count++; } } if ( count > 1 ) { // don't pause if (sv_paused->integer) Cvar_Set("sv_paused", "0"); return qfalse; } if (!sv_paused->integer) Cvar_Set("sv_paused", "1"); return qtrue; } /* ================== SV_Frame Player movement occurs as a result of packet events, which happen before SV_Frame is called ================== */ void SV_Frame( int msec ) { int frameMsec; int startTime; // the menu kills the server with this cvar if ( sv_killserver->integer ) { SV_Shutdown ("Server was killed.\n"); Cvar_Set( "sv_killserver", "0" ); return; } if ( !com_sv_running->integer ) { return; } // allow pause if only the local client is connected if ( SV_CheckPaused() ) { return; } // if it isn't time for the next frame, do nothing if ( sv_fps->integer < 1 ) { Cvar_Set( "sv_fps", "10" ); } frameMsec = 1000 / sv_fps->integer ; sv.timeResidual += msec; if (!com_dedicated->integer) SV_BotFrame( svs.time + sv.timeResidual ); if ( com_dedicated->integer && sv.timeResidual < frameMsec ) { // NET_Sleep will give the OS time slices until either get a packet // or time enough for a server frame has gone by NET_Sleep(frameMsec - sv.timeResidual); return; } // if time is about to hit the 32nd bit, kick all clients // and clear sv.time, rather // than checking for negative time wraparound everywhere. // 2giga-milliseconds = 23 days, so it won't be too often if ( svs.time > 0x70000000 ) { SV_Shutdown( "Restarting server due to time wrapping" ); Cbuf_AddText( "vstr nextmap\n" ); return; } // this can happen considerably earlier when lots of clients play and the map doesn't change if ( svs.nextSnapshotEntities >= 0x7FFFFFFE - svs.numSnapshotEntities ) { SV_Shutdown( "Restarting server due to numSnapshotEntities wrapping" ); Cbuf_AddText( "vstr nextmap\n" ); return; } if( sv.restartTime && svs.time >= sv.restartTime ) { sv.restartTime = 0; Cbuf_AddText( "map_restart 0\n" ); return; } // update infostrings if anything has been changed if ( cvar_modifiedFlags & CVAR_SERVERINFO ) { SV_SetConfigstring( CS_SERVERINFO, Cvar_InfoString( CVAR_SERVERINFO ) ); cvar_modifiedFlags &= ~CVAR_SERVERINFO; } if ( cvar_modifiedFlags & CVAR_SYSTEMINFO ) { SV_SetConfigstring( CS_SYSTEMINFO, Cvar_InfoString_Big( CVAR_SYSTEMINFO ) ); cvar_modifiedFlags &= ~CVAR_SYSTEMINFO; } if ( com_speeds->integer ) { startTime = Sys_Milliseconds (); } else { startTime = 0; // quite a compiler warning } // update ping based on the all received frames SV_CalcPings(); if (com_dedicated->integer) SV_BotFrame( svs.time ); // run the game simulation in chunks while ( sv.timeResidual >= frameMsec ) { sv.timeResidual -= frameMsec; svs.time += frameMsec; // let everything in the world think and move VM_Call( gvm, GAME_RUN_FRAME, svs.time ); } if ( com_speeds->integer ) { time_game = Sys_Milliseconds () - startTime; } // check timeouts SV_CheckTimeouts(); // send messages back to the clients SV_SendClientMessages(); // send a heartbeat to the master if needed SV_MasterHeartbeat(); } //============================================================================ ================================================ FILE: src/engine/server/sv_net_chan.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "../../game/q_shared.h" #include "../qcommon/qcommon.h" #include "server.h" /* ============== SV_Netchan_Encode // first four bytes of the data are always: long reliableAcknowledge; ============== */ static void SV_Netchan_Encode( client_t *client, msg_t *msg ) { long reliableAcknowledge, i, index; byte key, *string; int srdc, sbit, soob; if ( msg->cursize < SV_ENCODE_START ) { return; } srdc = msg->readcount; sbit = msg->bit; soob = msg->oob; msg->bit = 0; msg->readcount = 0; msg->oob = (qboolean) 0; reliableAcknowledge = MSG_ReadLong(msg); msg->oob = (qboolean) soob; msg->bit = sbit; msg->readcount = srdc; string = (byte *)client->lastClientCommandString; index = 0; // xor the client challenge with the netchan sequence number key = client->challenge ^ client->netchan.outgoingSequence; for (i = SV_ENCODE_START; i < msg->cursize; i++) { // modify the key with the last received and with this message acknowledged client command if (!string[index]) index = 0; if (string[index] > 127 || string[index] == '%') { key ^= '.' << (i & 1); } else { key ^= string[index] << (i & 1); } index++; // encode the data with this key *(msg->data + i) = *(msg->data + i) ^ key; } } /* ============== SV_Netchan_Decode // first 12 bytes of the data are always: long serverId; long messageAcknowledge; long reliableAcknowledge; ============== */ static void SV_Netchan_Decode( client_t *client, msg_t *msg ) { int serverId, messageAcknowledge, reliableAcknowledge; int i, index, srdc, sbit, soob; byte key, *string; srdc = msg->readcount; sbit = msg->bit; soob = msg->oob; msg->oob = (qboolean) 0; serverId = MSG_ReadLong(msg); messageAcknowledge = MSG_ReadLong(msg); reliableAcknowledge = MSG_ReadLong(msg); msg->oob = (qboolean) soob; msg->bit = sbit; msg->readcount = srdc; string = (byte *)client->reliableCommands[ reliableAcknowledge & (MAX_RELIABLE_COMMANDS-1) ]; index = 0; // key = client->challenge ^ serverId ^ messageAcknowledge; for (i = msg->readcount + SV_DECODE_START; i < msg->cursize; i++) { // modify the key with the last sent and acknowledged server command if (!string[index]) index = 0; if (string[index] > 127 || string[index] == '%') { key ^= '.' << (i & 1); } else { key ^= string[index] << (i & 1); } index++; // decode the data with this key *(msg->data + i) = *(msg->data + i) ^ key; } } /* ================= SV_Netchan_TransmitNextFragment ================= */ void SV_Netchan_TransmitNextFragment( client_t *client ) { Netchan_TransmitNextFragment( &client->netchan ); if (!client->netchan.unsentFragments) { // make sure the netchan queue has been properly initialized (you never know) if (!client->netchan_end_queue) { Com_Error(ERR_DROP, "netchan queue is not properly initialized in SV_Netchan_TransmitNextFragment\n"); } // the last fragment was transmitted, check wether we have queued messages if (client->netchan_start_queue) { netchan_buffer_t *netbuf; Com_DPrintf("#462 Netchan_TransmitNextFragment: popping a queued message for transmit\n"); netbuf = client->netchan_start_queue; SV_Netchan_Encode( client, &netbuf->msg ); Netchan_Transmit( &client->netchan, netbuf->msg.cursize, netbuf->msg.data ); // pop from queue client->netchan_start_queue = netbuf->next; if (!client->netchan_start_queue) { Com_DPrintf("#462 Netchan_TransmitNextFragment: emptied queue\n"); client->netchan_end_queue = &client->netchan_start_queue; } else Com_DPrintf("#462 Netchan_TransmitNextFragment: remaining queued message\n"); Z_Free(netbuf); } } } /* =============== SV_Netchan_Transmit TTimo https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=462 if there are some unsent fragments (which may happen if the snapshots and the gamestate are fragmenting, and collide on send for instance) then buffer them and make sure they get sent in correct order ================ */ void SV_Netchan_Transmit( client_t *client, msg_t *msg) { //int length, const byte *data ) { MSG_WriteByte( msg, svc_EOF ); if (client->netchan.unsentFragments) { netchan_buffer_t *netbuf; Com_DPrintf("#462 SV_Netchan_Transmit: unsent fragments, stacked\n"); netbuf = (netchan_buffer_t *)Z_Malloc(sizeof(netchan_buffer_t)); // store the msg, we can't store it encoded, as the encoding depends on stuff we still have to finish sending MSG_Copy(&netbuf->msg, netbuf->msgBuffer, sizeof( netbuf->msgBuffer ), msg); netbuf->next = NULL; // insert it in the queue, the message will be encoded and sent later *client->netchan_end_queue = netbuf; client->netchan_end_queue = &(*client->netchan_end_queue)->next; // emit the next fragment of the current message for now Netchan_TransmitNextFragment(&client->netchan); } else { SV_Netchan_Encode( client, msg ); Netchan_Transmit( &client->netchan, msg->cursize, msg->data ); } } /* ================= Netchan_SV_Process ================= */ qboolean SV_Netchan_Process( client_t *client, msg_t *msg ) { int ret; ret = Netchan_Process( &client->netchan, msg ); if (!ret) return qfalse; SV_Netchan_Decode( client, msg ); return qtrue; } ================================================ FILE: src/engine/server/sv_snapshot.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "server.h" /* ============================================================================= Delta encode a client frame onto the network channel A normal server packet will look like: 4 sequence number (high bit set if an oversize fragment) 1 svc_snapshot 4 last client reliable command 4 serverTime 1 lastframe for delta compression 1 snapFlags 1 areaBytes ============================================================================= */ /* ============= SV_EmitPacketEntities Writes a delta update of an entityState_t list to the message. ============= */ static void SV_EmitPacketEntities( clientSnapshot_t *from, clientSnapshot_t *to, msg_t *msg ) { entityState_t *oldent, *newent; int oldindex, newindex; int oldnum, newnum; int from_num_entities; // generate the delta update if ( !from ) { from_num_entities = 0; } else { from_num_entities = from->num_entities; } newent = NULL; oldent = NULL; newindex = 0; oldindex = 0; while ( newindex < to->num_entities || oldindex < from_num_entities ) { if ( newindex >= to->num_entities ) { newnum = 9999; } else { newent = &svs.snapshotEntities[(to->first_entity+newindex) % svs.numSnapshotEntities]; newnum = newent->number; } if ( oldindex >= from_num_entities ) { oldnum = 9999; } else { oldent = &svs.snapshotEntities[(from->first_entity+oldindex) % svs.numSnapshotEntities]; oldnum = oldent->number; } if ( newnum == oldnum ) { // delta update from old position // because the force parm is qfalse, this will not result // in any bytes being emited if the entity has not changed at all MSG_WriteDeltaEntity (msg, oldent, newent, qfalse ); oldindex++; newindex++; continue; } if ( newnum < oldnum ) { // this is a new entity, send it from the baseline MSG_WriteDeltaEntity (msg, &sv.svEntities[newnum].baseline, newent, qtrue ); newindex++; continue; } if ( newnum > oldnum ) { // the old entity isn't present in the new message MSG_WriteDeltaEntity (msg, oldent, NULL, qtrue ); oldindex++; continue; } } MSG_WriteBits( msg, (MAX_GENTITIES-1), GENTITYNUM_BITS ); // end of packetentities } /* ================== SV_WriteSnapshotToClient ================== */ static void SV_WriteSnapshotToClient( client_t *client, msg_t *msg ) { clientSnapshot_t *frame, *oldframe; int lastframe; int i; int snapFlags; // this is the snapshot we are creating frame = &client->frames[ client->netchan.outgoingSequence & PACKET_MASK ]; // try to use a previous frame as the source for delta compressing the snapshot if ( client->deltaMessage <= 0 || client->state != CS_ACTIVE ) { // client is asking for a retransmit oldframe = NULL; lastframe = 0; } else if ( client->netchan.outgoingSequence - client->deltaMessage >= (PACKET_BACKUP - 3) ) { // client hasn't gotten a good message through in a long time Com_DPrintf ("%s: Delta request from out of date packet.\n", client->name); oldframe = NULL; lastframe = 0; } else { // we have a valid snapshot to delta from oldframe = &client->frames[ client->deltaMessage & PACKET_MASK ]; lastframe = client->netchan.outgoingSequence - client->deltaMessage; // the snapshot's entities may still have rolled off the buffer, though if ( oldframe->first_entity <= svs.nextSnapshotEntities - svs.numSnapshotEntities ) { Com_DPrintf ("%s: Delta request from out of date entities.\n", client->name); oldframe = NULL; lastframe = 0; } } MSG_WriteByte (msg, svc_snapshot); // NOTE, MRE: now sent at the start of every message from server to client // let the client know which reliable clientCommands we have received //MSG_WriteLong( msg, client->lastClientCommand ); // send over the current server time so the client can drift // its view of time to try to match MSG_WriteLong (msg, svs.time); // what we are delta'ing from MSG_WriteByte (msg, lastframe); snapFlags = svs.snapFlagServerBit; if ( client->rateDelayed ) { snapFlags |= SNAPFLAG_RATE_DELAYED; } if ( client->state != CS_ACTIVE ) { snapFlags |= SNAPFLAG_NOT_ACTIVE; } MSG_WriteByte (msg, snapFlags); // send over the areabits MSG_WriteByte (msg, frame->areabytes); MSG_WriteData (msg, frame->areabits, frame->areabytes); // delta encode the playerstate if ( oldframe ) { MSG_WriteDeltaPlayerstate( msg, &oldframe->ps, &frame->ps ); } else { MSG_WriteDeltaPlayerstate( msg, NULL, &frame->ps ); } // delta encode the entities SV_EmitPacketEntities (oldframe, frame, msg); // padding for rate debugging if ( sv_padPackets->integer ) { for ( i = 0 ; i < sv_padPackets->integer ; i++ ) { MSG_WriteByte (msg, svc_nop); } } } /* ================== SV_UpdateServerCommandsToClient (re)send all server commands the client hasn't acknowledged yet ================== */ void SV_UpdateServerCommandsToClient( client_t *client, msg_t *msg ) { int i; // write any unacknowledged serverCommands for ( i = client->reliableAcknowledge + 1 ; i <= client->reliableSequence ; i++ ) { MSG_WriteByte( msg, svc_serverCommand ); MSG_WriteLong( msg, i ); MSG_WriteString( msg, client->reliableCommands[ i & (MAX_RELIABLE_COMMANDS-1) ] ); } client->reliableSent = client->reliableSequence; } /* ============================================================================= Build a client snapshot structure ============================================================================= */ #define MAX_SNAPSHOT_ENTITIES 1024 typedef struct { int numSnapshotEntities; int snapshotEntities[MAX_SNAPSHOT_ENTITIES]; } snapshotEntityNumbers_t; /* ======================= SV_QsortEntityNumbers ======================= */ static int QDECL SV_QsortEntityNumbers( const void *a, const void *b ) { int *ea, *eb; ea = (int *)a; eb = (int *)b; if ( *ea == *eb ) { Com_Error( ERR_DROP, "SV_QsortEntityStates: duplicated entity" ); } if ( *ea < *eb ) { return -1; } return 1; } /* =============== SV_AddEntToSnapshot =============== */ static void SV_AddEntToSnapshot( svEntity_t *svEnt, sharedEntity_t *gEnt, snapshotEntityNumbers_t *eNums ) { // if we have already added this entity to this snapshot, don't add again if ( svEnt->snapshotCounter == sv.snapshotCounter ) { return; } svEnt->snapshotCounter = sv.snapshotCounter; // if we are full, silently discard entities if ( eNums->numSnapshotEntities == MAX_SNAPSHOT_ENTITIES ) { return; } eNums->snapshotEntities[ eNums->numSnapshotEntities ] = gEnt->s.number; eNums->numSnapshotEntities++; } /* =============== SV_AddEntitiesVisibleFromPoint =============== */ static void SV_AddEntitiesVisibleFromPoint( vec3_t origin, clientSnapshot_t *frame, snapshotEntityNumbers_t *eNums, qboolean portal ) { int e, i; sharedEntity_t *ent; svEntity_t *svEnt; int l; int clientarea, clientcluster; int leafnum; int c_fullsend; byte *clientpvs; byte *bitvector; // during an error shutdown message we may need to transmit // the shutdown message after the server has shutdown, so // specfically check for it if ( !sv.state ) { return; } leafnum = CM_PointLeafnum (origin); clientarea = CM_LeafArea (leafnum); clientcluster = CM_LeafCluster (leafnum); // calculate the visible areas frame->areabytes = CM_WriteAreaBits( frame->areabits, clientarea ); clientpvs = CM_ClusterPVS (clientcluster); c_fullsend = 0; for ( e = 0 ; e < sv.num_entities ; e++ ) { ent = SV_GentityNum(e); // never send entities that aren't linked in if ( !ent->r.linked ) { continue; } if (ent->s.number != e) { Com_DPrintf ("FIXING ENT->S.NUMBER!!!\n"); ent->s.number = e; } // entities can be flagged to explicitly not be sent to the client if ( ent->r.svFlags & SVF_NOCLIENT ) { continue; } // entities can be flagged to be sent to only one client if ( ent->r.svFlags & SVF_SINGLECLIENT ) { if ( ent->r.singleClient != frame->ps.clientNum ) { continue; } } // entities can be flagged to be sent to everyone but one client if ( ent->r.svFlags & SVF_NOTSINGLECLIENT ) { if ( ent->r.singleClient == frame->ps.clientNum ) { continue; } } // entities can be flagged to be sent to a given mask of clients if ( ent->r.svFlags & SVF_CLIENTMASK ) { if (frame->ps.clientNum >= 32) Com_Error( ERR_DROP, "SVF_CLIENTMASK: cientNum > 32\n" ); if (~ent->r.singleClient & (1 << frame->ps.clientNum)) continue; } svEnt = SV_SvEntityForGentity( ent ); // don't double add an entity through portals if ( svEnt->snapshotCounter == sv.snapshotCounter ) { continue; } // broadcast entities are always sent if ( ent->r.svFlags & SVF_BROADCAST ) { SV_AddEntToSnapshot( svEnt, ent, eNums ); continue; } // ignore if not touching a PV leaf // check area if ( !CM_AreasConnected( clientarea, svEnt->areanum ) ) { // doors can legally straddle two areas, so // we may need to check another one if ( !CM_AreasConnected( clientarea, svEnt->areanum2 ) ) { continue; // blocked by a door } } bitvector = clientpvs; // check individual leafs if ( !svEnt->numClusters ) { continue; } l = 0; for ( i=0 ; i < svEnt->numClusters ; i++ ) { l = svEnt->clusternums[i]; if ( bitvector[l >> 3] & (1 << (l&7) ) ) { break; } } // if we haven't found it to be visible, // check overflow clusters that coudln't be stored if ( i == svEnt->numClusters ) { if ( svEnt->lastCluster ) { for ( ; l <= svEnt->lastCluster ; l++ ) { if ( bitvector[l >> 3] & (1 << (l&7) ) ) { break; } } if ( l == svEnt->lastCluster ) { continue; // not visible } } else { continue; } } // add it SV_AddEntToSnapshot( svEnt, ent, eNums ); // if its a portal entity, add everything visible from its camera position if ( ent->r.svFlags & SVF_PORTAL ) { if ( ent->s.generic1 ) { vec3_t dir; VectorSubtract(ent->s.origin, origin, dir); if ( VectorLengthSquared(dir) > (float) ent->s.generic1 * ent->s.generic1 ) { continue; } } SV_AddEntitiesVisibleFromPoint( ent->s.origin2, frame, eNums, qtrue ); } } } /* ============= SV_BuildClientSnapshot Decides which entities are going to be visible to the client, and copies off the playerstate and areabits. This properly handles multiple recursive portals, but the render currently doesn't. For viewing through other player's eyes, clent can be something other than client->gentity ============= */ static void SV_BuildClientSnapshot( client_t *client ) { vec3_t org; clientSnapshot_t *frame; snapshotEntityNumbers_t entityNumbers; int i; sharedEntity_t *ent; entityState_t *state; svEntity_t *svEnt; sharedEntity_t *clent; int clientNum; playerState_t *ps; // bump the counter used to prevent double adding sv.snapshotCounter++; // this is the frame we are creating frame = &client->frames[ client->netchan.outgoingSequence & PACKET_MASK ]; // clear everything in this snapshot entityNumbers.numSnapshotEntities = 0; Com_Memset( frame->areabits, 0, sizeof( frame->areabits ) ); // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=62 frame->num_entities = 0; clent = client->gentity; if ( !clent || client->state == CS_ZOMBIE ) { return; } // grab the current playerState_t ps = SV_GameClientNum( client - svs.clients ); frame->ps = *ps; // never send client's own entity, because it can // be regenerated from the playerstate clientNum = frame->ps.clientNum; if ( clientNum < 0 || clientNum >= MAX_GENTITIES ) { Com_Error( ERR_DROP, "SV_SvEntityForGentity: bad gEnt" ); } svEnt = &sv.svEntities[ clientNum ]; svEnt->snapshotCounter = sv.snapshotCounter; // find the client's viewpoint VectorCopy( ps->origin, org ); org[2] += ps->viewheight; // add all the entities directly visible to the eye, which // may include portal entities that merge other viewpoints SV_AddEntitiesVisibleFromPoint( org, frame, &entityNumbers, qfalse ); // if there were portals visible, there may be out of order entities // in the list which will need to be resorted for the delta compression // to work correctly. This also catches the error condition // of an entity being included twice. qsort( entityNumbers.snapshotEntities, entityNumbers.numSnapshotEntities, sizeof( entityNumbers.snapshotEntities[0] ), SV_QsortEntityNumbers ); // now that all viewpoint's areabits have been OR'd together, invert // all of them to make it a mask vector, which is what the renderer wants for ( i = 0 ; i < MAX_MAP_AREA_BYTES/4 ; i++ ) { ((int *)frame->areabits)[i] = ((int *)frame->areabits)[i] ^ -1; } // copy the entity states out frame->num_entities = 0; frame->first_entity = svs.nextSnapshotEntities; for ( i = 0 ; i < entityNumbers.numSnapshotEntities ; i++ ) { ent = SV_GentityNum(entityNumbers.snapshotEntities[i]); state = &svs.snapshotEntities[svs.nextSnapshotEntities % svs.numSnapshotEntities]; *state = ent->s; svs.nextSnapshotEntities++; // this should never hit, map should always be restarted first in SV_Frame if ( svs.nextSnapshotEntities >= 0x7FFFFFFE ) { Com_Error(ERR_FATAL, "svs.nextSnapshotEntities wrapped"); } frame->num_entities++; } } /* ==================== SV_RateMsec Return the number of msec a given size message is supposed to take to clear, based on the current rate ==================== */ #define HEADER_RATE_BYTES 48 // include our header, IP header, and some overhead static int SV_RateMsec( client_t *client, int messageSize ) { int rate; int rateMsec; // individual messages will never be larger than fragment size if ( messageSize > 1500 ) { messageSize = 1500; } rate = client->rate; if ( sv_maxRate->integer ) { if ( sv_maxRate->integer < 1000 ) { Cvar_Set( "sv_MaxRate", "1000" ); } if ( sv_maxRate->integer < rate ) { rate = sv_maxRate->integer; } } rateMsec = ( messageSize + HEADER_RATE_BYTES ) * 1000 / rate; return rateMsec; } /* ======================= SV_SendMessageToClient Called by SV_SendClientSnapshot and SV_SendClientGameState ======================= */ void SV_SendMessageToClient( msg_t *msg, client_t *client ) { int rateMsec; // record information about the message client->frames[client->netchan.outgoingSequence & PACKET_MASK].messageSize = msg->cursize; client->frames[client->netchan.outgoingSequence & PACKET_MASK].messageSent = svs.time; client->frames[client->netchan.outgoingSequence & PACKET_MASK].messageAcked = -1; // send the datagram SV_Netchan_Transmit( client, msg ); //msg->cursize, msg->data ); // set nextSnapshotTime based on rate and requested number of updates // local clients get snapshots every frame // TTimo - https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=491 // added sv_lanForceRate check if ( client->netchan.remoteAddress.type == NA_LOOPBACK || (sv_lanForceRate->integer && Sys_IsLANAddress (client->netchan.remoteAddress)) ) { client->nextSnapshotTime = svs.time - 1; return; } // normal rate / snapshotMsec calculation rateMsec = SV_RateMsec( client, msg->cursize ); if ( rateMsec < client->snapshotMsec ) { // never send more packets than this, no matter what the rate is at rateMsec = client->snapshotMsec; client->rateDelayed = qfalse; } else { client->rateDelayed = qtrue; } client->nextSnapshotTime = svs.time + rateMsec; // don't pile up empty snapshots while connecting if ( client->state != CS_ACTIVE ) { // a gigantic connection message may have already put the nextSnapshotTime // more than a second away, so don't shorten it // do shorten if client is downloading if ( !*client->downloadName && client->nextSnapshotTime < svs.time + 1000 ) { client->nextSnapshotTime = svs.time + 1000; } } } /* ======================= SV_SendClientSnapshot Also called by SV_FinalMessage ======================= */ void SV_SendClientSnapshot( client_t *client ) { byte msg_buf[MAX_MSGLEN]; msg_t msg; // build the snapshot SV_BuildClientSnapshot( client ); // bots need to have their snapshots build, but // the query them directly without needing to be sent if ( client->gentity && client->gentity->r.svFlags & SVF_BOT ) { return; } MSG_Init (&msg, msg_buf, sizeof(msg_buf)); msg.allowoverflow = qtrue; // NOTE, MRE: all server->client messages now acknowledge // let the client know which reliable clientCommands we have received MSG_WriteLong( &msg, client->lastClientCommand ); // (re)send any reliable server commands SV_UpdateServerCommandsToClient( client, &msg ); // send over all the relevant entityState_t // and the playerState_t SV_WriteSnapshotToClient( client, &msg ); // Add any download data if the client is downloading SV_WriteDownloadToClient( client, &msg ); // check for overflow if ( msg.overflowed ) { Com_Printf ("WARNING: msg overflowed for %s\n", client->name); MSG_Clear (&msg); } SV_SendMessageToClient( &msg, client ); } /* ======================= SV_SendClientMessages ======================= */ void SV_SendClientMessages( void ) { int i; client_t *c; // send a message to each connected client for (i=0, c = svs.clients ; i < sv_maxclients->integer ; i++, c++) { if (!c->state) { continue; // not connected } if ( svs.time < c->nextSnapshotTime ) { continue; // not time yet } // send additional message fragments if the last message // was too large to send at once if ( c->netchan.unsentFragments ) { c->nextSnapshotTime = svs.time + SV_RateMsec( c, c->netchan.unsentLength - c->netchan.unsentFragmentStart ); SV_Netchan_TransmitNextFragment( c ); continue; } // generate and send a new message SV_SendClientSnapshot( c ); } } ================================================ FILE: src/engine/server/sv_world.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // world.c -- world query functions #include "server.h" /* ================ SV_ClipHandleForEntity Returns a headnode that can be used for testing or clipping to a given entity. If the entity is a bsp model, the headnode will be returned, otherwise a custom box tree will be constructed. ================ */ clipHandle_t SV_ClipHandleForEntity( const sharedEntity_t *ent ) { if ( ent->r.bmodel ) { // explicit hulls in the BSP model return CM_InlineModel( ent->s.modelindex ); } if ( ent->r.svFlags & SVF_CAPSULE ) { // create a temp capsule from bounding box sizes return CM_TempBoxModel( ent->r.mins, ent->r.maxs, qtrue ); } // create a temp tree from bounding box sizes return CM_TempBoxModel( ent->r.mins, ent->r.maxs, qfalse ); } /* =============================================================================== ENTITY CHECKING To avoid linearly searching through lists of entities during environment testing, the world is carved up with an evenly spaced, axially aligned bsp tree. Entities are kept in chains either at the final leafs, or at the first node that splits them, which prevents having to deal with multiple fragments of a single entity. =============================================================================== */ typedef struct worldSector_s { int axis; // -1 = leaf node float dist; struct worldSector_s *children[2]; svEntity_t *entities; } worldSector_t; #define AREA_DEPTH 4 #define AREA_NODES 64 worldSector_t sv_worldSectors[AREA_NODES]; int sv_numworldSectors; /* =============== SV_SectorList_f =============== */ void SV_SectorList_f( void ) { int i, c; worldSector_t *sec; svEntity_t *ent; for ( i = 0 ; i < AREA_NODES ; i++ ) { sec = &sv_worldSectors[i]; c = 0; for ( ent = sec->entities ; ent ; ent = ent->nextEntityInWorldSector ) { c++; } Com_Printf( "sector %i: %i entities\n", i, c ); } } /* =============== SV_CreateworldSector Builds a uniformly subdivided tree for the given world size =============== */ worldSector_t *SV_CreateworldSector( int depth, vec3_t mins, vec3_t maxs ) { worldSector_t *anode; vec3_t size; vec3_t mins1, maxs1, mins2, maxs2; anode = &sv_worldSectors[sv_numworldSectors]; sv_numworldSectors++; if (depth == AREA_DEPTH) { anode->axis = -1; anode->children[0] = anode->children[1] = NULL; return anode; } VectorSubtract (maxs, mins, size); if (size[0] > size[1]) { anode->axis = 0; } else { anode->axis = 1; } anode->dist = 0.5 * (maxs[anode->axis] + mins[anode->axis]); VectorCopy (mins, mins1); VectorCopy (mins, mins2); VectorCopy (maxs, maxs1); VectorCopy (maxs, maxs2); maxs1[anode->axis] = mins2[anode->axis] = anode->dist; anode->children[0] = SV_CreateworldSector (depth+1, mins2, maxs2); anode->children[1] = SV_CreateworldSector (depth+1, mins1, maxs1); return anode; } /* =============== SV_ClearWorld =============== */ void SV_ClearWorld( void ) { clipHandle_t h; vec3_t mins, maxs; Com_Memset( sv_worldSectors, 0, sizeof(sv_worldSectors) ); sv_numworldSectors = 0; // get world map bounds h = CM_InlineModel( 0 ); CM_ModelBounds( h, mins, maxs ); SV_CreateworldSector( 0, mins, maxs ); } /* =============== SV_UnlinkEntity =============== */ void SV_UnlinkEntity( sharedEntity_t *gEnt ) { svEntity_t *ent; svEntity_t *scan; worldSector_t *ws; ent = SV_SvEntityForGentity( gEnt ); gEnt->r.linked = qfalse; ws = ent->worldSector; if ( !ws ) { return; // not linked in anywhere } ent->worldSector = NULL; if ( ws->entities == ent ) { ws->entities = ent->nextEntityInWorldSector; return; } for ( scan = ws->entities ; scan ; scan = scan->nextEntityInWorldSector ) { if ( scan->nextEntityInWorldSector == ent ) { scan->nextEntityInWorldSector = ent->nextEntityInWorldSector; return; } } Com_Printf( "WARNING: SV_UnlinkEntity: not found in worldSector\n" ); } /* =============== SV_LinkEntity =============== */ #define MAX_TOTAL_ENT_LEAFS 128 void SV_LinkEntity( sharedEntity_t *gEnt ) { worldSector_t *node; int leafs[MAX_TOTAL_ENT_LEAFS]; int cluster; int num_leafs; int i, j, k; int area; int lastLeaf; float *origin, *angles; svEntity_t *ent; ent = SV_SvEntityForGentity( gEnt ); if ( ent->worldSector ) { SV_UnlinkEntity( gEnt ); // unlink from old position } // encode the size into the entityState_t for client prediction if ( gEnt->r.bmodel ) { gEnt->s.solid = SOLID_BMODEL; // a solid_box will never create this value } else if ( gEnt->r.contents & ( CONTENTS_SOLID | CONTENTS_BODY ) ) { // assume that x/y are equal and symetric i = gEnt->r.maxs[0]; if (i<1) i = 1; if (i>255) i = 255; // z is not symetric j = (-gEnt->r.mins[2]); if (j<1) j = 1; if (j>255) j = 255; // and z maxs can be negative... k = (gEnt->r.maxs[2]+32); if (k<1) k = 1; if (k>255) k = 255; gEnt->s.solid = (k<<16) | (j<<8) | i; } else { gEnt->s.solid = 0; } // get the position origin = gEnt->r.currentOrigin; angles = gEnt->r.currentAngles; // set the abs box if ( gEnt->r.bmodel && (angles[0] || angles[1] || angles[2]) ) { // expand for rotation float max; int i; max = RadiusFromBounds( gEnt->r.mins, gEnt->r.maxs ); for (i=0 ; i<3 ; i++) { gEnt->r.absmin[i] = origin[i] - max; gEnt->r.absmax[i] = origin[i] + max; } } else { // normal VectorAdd (origin, gEnt->r.mins, gEnt->r.absmin); VectorAdd (origin, gEnt->r.maxs, gEnt->r.absmax); } // because movement is clipped an epsilon away from an actual edge, // we must fully check even when bounding boxes don't quite touch gEnt->r.absmin[0] -= 1; gEnt->r.absmin[1] -= 1; gEnt->r.absmin[2] -= 1; gEnt->r.absmax[0] += 1; gEnt->r.absmax[1] += 1; gEnt->r.absmax[2] += 1; // link to PVS leafs ent->numClusters = 0; ent->lastCluster = 0; ent->areanum = -1; ent->areanum2 = -1; //get all leafs, including solids num_leafs = CM_BoxLeafnums( gEnt->r.absmin, gEnt->r.absmax, leafs, MAX_TOTAL_ENT_LEAFS, &lastLeaf ); // if none of the leafs were inside the map, the // entity is outside the world and can be considered unlinked if ( !num_leafs ) { return; } // set areas, even from clusters that don't fit in the entity array for (i=0 ; iareanum != -1 && ent->areanum != area) { if (ent->areanum2 != -1 && ent->areanum2 != area && sv.state == SS_LOADING) { Com_DPrintf ("Object %i touching 3 areas at %f %f %f\n", gEnt->s.number, gEnt->r.absmin[0], gEnt->r.absmin[1], gEnt->r.absmin[2]); } ent->areanum2 = area; } else { ent->areanum = area; } } } // store as many explicit clusters as we can ent->numClusters = 0; for (i=0 ; i < num_leafs ; i++) { cluster = CM_LeafCluster( leafs[i] ); if ( cluster != -1 ) { ent->clusternums[ent->numClusters++] = cluster; if ( ent->numClusters == MAX_ENT_CLUSTERS ) { break; } } } // store off a last cluster if we need to if ( i != num_leafs ) { ent->lastCluster = CM_LeafCluster( lastLeaf ); } gEnt->r.linkcount++; // find the first world sector node that the ent's box crosses node = sv_worldSectors; while (1) { if (node->axis == -1) break; if ( gEnt->r.absmin[node->axis] > node->dist) node = node->children[0]; else if ( gEnt->r.absmax[node->axis] < node->dist) node = node->children[1]; else break; // crosses the node } // link it in ent->worldSector = node; ent->nextEntityInWorldSector = node->entities; node->entities = ent; gEnt->r.linked = qtrue; } /* ============================================================================ AREA QUERY Fills in a list of all entities who's absmin / absmax intersects the given bounds. This does NOT mean that they actually touch in the case of bmodels. ============================================================================ */ typedef struct { const float *mins; const float *maxs; int *list; int count, maxcount; } areaParms_t; /* ==================== SV_AreaEntities_r ==================== */ void SV_AreaEntities_r( worldSector_t *node, areaParms_t *ap ) { svEntity_t *check, *next; sharedEntity_t *gcheck; int count; count = 0; for ( check = node->entities ; check ; check = next ) { next = check->nextEntityInWorldSector; gcheck = SV_GEntityForSvEntity( check ); if ( gcheck->r.absmin[0] > ap->maxs[0] || gcheck->r.absmin[1] > ap->maxs[1] || gcheck->r.absmin[2] > ap->maxs[2] || gcheck->r.absmax[0] < ap->mins[0] || gcheck->r.absmax[1] < ap->mins[1] || gcheck->r.absmax[2] < ap->mins[2]) { continue; } if ( ap->count == ap->maxcount ) { Com_Printf ("SV_AreaEntities: MAXCOUNT\n"); return; } ap->list[ap->count] = check - sv.svEntities; ap->count++; } if (node->axis == -1) { return; // terminal node } // recurse down both sides if ( ap->maxs[node->axis] > node->dist ) { SV_AreaEntities_r ( node->children[0], ap ); } if ( ap->mins[node->axis] < node->dist ) { SV_AreaEntities_r ( node->children[1], ap ); } } /* ================ SV_AreaEntities ================ */ int SV_AreaEntities( const vec3_t mins, const vec3_t maxs, int *entityList, int maxcount ) { areaParms_t ap; ap.mins = mins; ap.maxs = maxs; ap.list = entityList; ap.count = 0; ap.maxcount = maxcount; SV_AreaEntities_r( sv_worldSectors, &ap ); return ap.count; } //=========================================================================== typedef struct { vec3_t boxmins, boxmaxs;// enclose the test object along entire move const float *mins; const float *maxs; // size of the moving object const float *start; vec3_t end; trace_t trace; int passEntityNum; int contentmask; int capsule; } moveclip_t; /* ==================== SV_ClipToEntity ==================== */ void SV_ClipToEntity( trace_t *trace, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int entityNum, int contentmask, int capsule ) { sharedEntity_t *touch; clipHandle_t clipHandle; float *origin, *angles; touch = SV_GentityNum( entityNum ); Com_Memset(trace, 0, sizeof(trace_t)); // if it doesn't have any brushes of a type we // are looking for, ignore it if ( ! ( contentmask & touch->r.contents ) ) { trace->fraction = 1.0; return; } // might intersect, so do an exact clip clipHandle = SV_ClipHandleForEntity (touch); origin = touch->r.currentOrigin; angles = touch->r.currentAngles; if ( !touch->r.bmodel ) { angles = vec3_origin; // boxes don't rotate } CM_TransformedBoxTrace ( trace, (float *)start, (float *)end, (float *)mins, (float *)maxs, clipHandle, contentmask, origin, angles, capsule); if ( trace->fraction < 1 ) { trace->entityNum = touch->s.number; } } /* ==================== SV_ClipMoveToEntities ==================== */ void SV_ClipMoveToEntities( moveclip_t *clip ) { int i, num; int touchlist[MAX_GENTITIES]; sharedEntity_t *touch; int passOwnerNum; trace_t trace; clipHandle_t clipHandle; float *origin, *angles; num = SV_AreaEntities( clip->boxmins, clip->boxmaxs, touchlist, MAX_GENTITIES); if ( clip->passEntityNum != ENTITYNUM_NONE ) { passOwnerNum = ( SV_GentityNum( clip->passEntityNum ) )->r.ownerNum; if ( passOwnerNum == ENTITYNUM_NONE ) { passOwnerNum = -1; } } else { passOwnerNum = -1; } for ( i=0 ; itrace.allsolid ) { return; } touch = SV_GentityNum( touchlist[i] ); // see if we should ignore this entity if ( clip->passEntityNum != ENTITYNUM_NONE ) { if ( touchlist[i] == clip->passEntityNum ) { continue; // don't clip against the pass entity } if ( touch->r.ownerNum == clip->passEntityNum ) { continue; // don't clip against own missiles } if ( touch->r.ownerNum == passOwnerNum ) { continue; // don't clip against other missiles from our owner } } // if it doesn't have any brushes of a type we // are looking for, ignore it if ( ! ( clip->contentmask & touch->r.contents ) ) { continue; } // might intersect, so do an exact clip clipHandle = SV_ClipHandleForEntity (touch); origin = touch->r.currentOrigin; angles = touch->r.currentAngles; if ( !touch->r.bmodel ) { angles = vec3_origin; // boxes don't rotate } CM_TransformedBoxTrace ( &trace, (float *)clip->start, (float *)clip->end, (float *)clip->mins, (float *)clip->maxs, clipHandle, clip->contentmask, origin, angles, clip->capsule); if ( trace.allsolid ) { clip->trace.allsolid = qtrue; trace.entityNum = touch->s.number; } else if ( trace.startsolid ) { clip->trace.startsolid = qtrue; trace.entityNum = touch->s.number; } if ( trace.fraction < clip->trace.fraction ) { qboolean oldStart; // make sure we keep a startsolid from a previous trace oldStart = clip->trace.startsolid; trace.entityNum = touch->s.number; clip->trace = trace; //clip->trace.startsolid |= oldStart; if (oldStart == qtrue) { clip->trace.startsolid = qtrue; } } } } /* ================== SV_Trace Moves the given mins/maxs volume through the world from start to end. passEntityNum and entities owned by passEntityNum are explicitly not checked. ================== */ void SV_Trace( trace_t *results, const vec3_t start, vec3_t mins, vec3_t maxs, const vec3_t end, int passEntityNum, int contentmask, int capsule ) { moveclip_t clip; int i; if ( !mins ) { mins = vec3_origin; } if ( !maxs ) { maxs = vec3_origin; } Com_Memset ( &clip, 0, sizeof ( moveclip_t ) ); // clip to world CM_BoxTrace( &clip.trace, start, end, mins, maxs, 0, contentmask, capsule ); clip.trace.entityNum = clip.trace.fraction != 1.0 ? ENTITYNUM_WORLD : ENTITYNUM_NONE; if ( clip.trace.fraction == 0 ) { *results = clip.trace; return; // blocked immediately by the world } clip.contentmask = contentmask; clip.start = start; // VectorCopy( clip.trace.endpos, clip.end ); VectorCopy( end, clip.end ); clip.mins = mins; clip.maxs = maxs; clip.passEntityNum = passEntityNum; clip.capsule = capsule; // create the bounding box of the entire move // we can limit it to the part of the move not // already clipped off by the world, which can be // a significant savings for line of sight and shot traces for ( i=0 ; i<3 ; i++ ) { if ( end[i] > start[i] ) { clip.boxmins[i] = clip.start[i] + clip.mins[i] - 1; clip.boxmaxs[i] = clip.end[i] + clip.maxs[i] + 1; } else { clip.boxmins[i] = clip.end[i] + clip.mins[i] - 1; clip.boxmaxs[i] = clip.start[i] + clip.maxs[i] + 1; } } // clip to other solid entities SV_ClipMoveToEntities ( &clip ); *results = clip.trace; } /* ============= SV_PointContents ============= */ int SV_PointContents( const vec3_t p, int passEntityNum ) { int touch[MAX_GENTITIES]; sharedEntity_t *hit; int i, num; int contents, c2; clipHandle_t clipHandle; float *angles; // get base contents from world contents = CM_PointContents( p, 0 ); // or in contents from all the other entities num = SV_AreaEntities( p, p, touch, MAX_GENTITIES ); for ( i=0 ; is.angles; if ( !hit->r.bmodel ) { angles = vec3_origin; // boxes don't rotate } c2 = CM_TransformedPointContents (p, clipHandle, hit->s.origin, hit->s.angles); contents |= c2; } return contents; } ================================================ FILE: src/game/ai_chat.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: ai_chat.c * * desc: Quake3 bot AI * * $Archive: /MissionPack/code/game/ai_chat.c $ * *****************************************************************************/ #include "g_local.h" #include "botlib.h" #include "be_aas.h" #include "be_ea.h" #include "be_ai_char.h" #include "be_ai_chat.h" #include "be_ai_gen.h" #include "be_ai_goal.h" #include "be_ai_move.h" #include "be_ai_weap.h" // #include "ai_main.h" #include "ai_dmq3.h" #include "ai_chat.h" #include "ai_cmd.h" #include "ai_dmnet.h" // #include "chars.h" //characteristics #include "inv.h" //indexes into the inventory #include "syn.h" //synonyms #include "match.h" //string matching types and vars #define TIME_BETWEENCHATTING 25 /* ================== BotNumActivePlayers ================== */ int BotNumActivePlayers(void) { int i, num; char buf[MAX_INFO_STRING]; static int maxclients; if (!maxclients) maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); num = 0; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; //skip spectators if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue; // num++; } return num; } /* ================== BotIsFirstInRankings ================== */ int BotIsFirstInRankings(bot_state_t *bs) { int i, score; char buf[MAX_INFO_STRING]; static int maxclients; playerState_t ps; if (!maxclients) maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); score = bs->cur_ps.persistant[PERS_SCORE]; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; //skip spectators if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue; // BotAI_GetClientState(i, &ps); if (score < ps.persistant[PERS_SCORE]) return qfalse; } return qtrue; } /* ================== BotIsLastInRankings ================== */ int BotIsLastInRankings(bot_state_t *bs) { int i, score; char buf[MAX_INFO_STRING]; static int maxclients; playerState_t ps; if (!maxclients) maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); score = bs->cur_ps.persistant[PERS_SCORE]; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; //skip spectators if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue; // BotAI_GetClientState(i, &ps); if (score > ps.persistant[PERS_SCORE]) return qfalse; } return qtrue; } /* ================== BotFirstClientInRankings ================== */ char *BotFirstClientInRankings(void) { int i, bestscore, bestclient; char buf[MAX_INFO_STRING]; static char name[32]; static int maxclients; playerState_t ps; if (!maxclients) maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); bestscore = -999999; bestclient = 0; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; //skip spectators if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue; // BotAI_GetClientState(i, &ps); if (ps.persistant[PERS_SCORE] > bestscore) { bestscore = ps.persistant[PERS_SCORE]; bestclient = i; } } EasyClientName(bestclient, name, 32); return name; } /* ================== BotLastClientInRankings ================== */ char *BotLastClientInRankings(void) { int i, worstscore, bestclient; char buf[MAX_INFO_STRING]; static char name[32]; static int maxclients; playerState_t ps; if (!maxclients) maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); worstscore = 999999; bestclient = 0; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; //skip spectators if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue; // BotAI_GetClientState(i, &ps); if (ps.persistant[PERS_SCORE] < worstscore) { worstscore = ps.persistant[PERS_SCORE]; bestclient = i; } } EasyClientName(bestclient, name, 32); return name; } /* ================== BotRandomOpponentName ================== */ char *BotRandomOpponentName(bot_state_t *bs) { int i, count; char buf[MAX_INFO_STRING]; int opponents[MAX_CLIENTS], numopponents; static int maxclients; static char name[32]; if (!maxclients) maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); numopponents = 0; opponents[0] = 0; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { if (i == bs->client) continue; // trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; //skip spectators if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue; //skip team mates if (BotSameTeam(bs, i)) continue; // opponents[numopponents] = i; numopponents++; } count = random() * numopponents; for (i = 0; i < numopponents; i++) { count--; if (count <= 0) { EasyClientName(opponents[i], name, sizeof(name)); return name; } } EasyClientName(opponents[0], name, sizeof(name)); return name; } /* ================== BotMapTitle ================== */ char *BotMapTitle(void) { char info[1024]; static char mapname[128]; trap_GetServerinfo(info, sizeof(info)); strncpy(mapname, Info_ValueForKey( info, "mapname" ), sizeof(mapname)-1); mapname[sizeof(mapname)-1] = '\0'; return mapname; } /* ================== BotWeaponNameForMeansOfDeath ================== */ char *BotWeaponNameForMeansOfDeath(int mod) { switch(mod) { case MOD_SHOTGUN: return "Shotgun"; case MOD_GAUNTLET: return "Gauntlet"; case MOD_MACHINEGUN: return "Machinegun"; case MOD_GRENADE: case MOD_GRENADE_SPLASH: return "Grenade Launcher"; case MOD_ROCKET: case MOD_ROCKET_SPLASH: return "Rocket Launcher"; case MOD_PLASMA: case MOD_PLASMA_SPLASH: return "Plasmagun"; case MOD_RAILGUN: return "Railgun"; case MOD_LIGHTNING: return "Lightning Gun"; case MOD_BFG: case MOD_BFG_SPLASH: return "BFG10K"; case MOD_GRAPPLE: return "Grapple"; default: return "[unknown weapon]"; } } /* ================== BotRandomWeaponName ================== */ char *BotRandomWeaponName(void) { int rnd = random() * 8.9; switch(rnd) { case 0: return "Gauntlet"; case 1: return "Shotgun"; case 2: return "Machinegun"; case 3: return "Grenade Launcher"; case 4: return "Rocket Launcher"; case 5: return "Plasmagun"; case 6: return "Railgun"; case 7: return "Lightning Gun"; default: return "BFG10K"; } } /* ================== BotVisibleEnemies ================== */ int BotVisibleEnemies(bot_state_t *bs) { float vis; int i; aas_entityinfo_t entinfo; for (i = 0; i < MAX_CLIENTS; i++) { if (i == bs->client) continue; // BotEntityInfo(i, &entinfo); // if (!entinfo.valid) continue; //if the enemy isn't dead and the enemy isn't the bot self if (EntityIsDead(&entinfo) || entinfo.number == bs->entitynum) continue; //if the enemy is invisible and not shooting if (EntityIsInvisible(&entinfo) && !EntityIsShooting(&entinfo)) { continue; } //if on the same team if (BotSameTeam(bs, i)) continue; //check if the enemy is visible vis = BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, i); if (vis > 0) return qtrue; } return qfalse; } /* ================== BotValidChatPosition ================== */ int BotValidChatPosition(bot_state_t *bs) { vec3_t point, start, end, mins, maxs; bsp_trace_t trace; //if the bot is dead all positions are valid if (BotIsDead(bs)) return qtrue; //never start chatting with a powerup if (bs->inventory[INVENTORY_QUAD] || bs->inventory[INVENTORY_HASTE] || bs->inventory[INVENTORY_INVISIBILITY] || bs->inventory[INVENTORY_REGEN] || bs->inventory[INVENTORY_FLIGHT]) return qfalse; //must be on the ground //if (bs->cur_ps.groundEntityNum != ENTITYNUM_NONE) return qfalse; //do not chat if in lava or slime VectorCopy(bs->origin, point); point[2] -= 24; if (trap_PointContents(point,bs->entitynum) & (CONTENTS_LAVA|CONTENTS_SLIME)) return qfalse; //do not chat if under water VectorCopy(bs->origin, point); point[2] += 32; if (trap_PointContents(point,bs->entitynum) & MASK_WATER) return qfalse; //must be standing on the world entity VectorCopy(bs->origin, start); VectorCopy(bs->origin, end); start[2] += 1; end[2] -= 10; trap_AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, mins, maxs); BotAI_Trace(&trace, start, mins, maxs, end, bs->client, MASK_SOLID); if (trace.ent != ENTITYNUM_WORLD) return qfalse; //the bot is in a position where it can chat return qtrue; } /* ================== BotChat_EnterGame ================== */ int BotChat_EnterGame(bot_state_t *bs) { char name[32]; float rnd; if (bot_nochat.integer) return qfalse; if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse; //don't chat in teamplay if (TeamPlayIsOn()) return qfalse; // don't chat in tournament mode if (gametype == GT_TOURNAMENT) return qfalse; rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_ENTEREXITGAME, 0, 1); if (!bot_fastchat.integer) { if (random() > rnd) return qfalse; } if (BotNumActivePlayers() <= 1) return qfalse; if (!BotValidChatPosition(bs)) return qfalse; BotAI_BotInitialChat(bs, "game_enter", EasyClientName(bs->client, name, 32), // 0 BotRandomOpponentName(bs), // 1 "[invalid var]", // 2 "[invalid var]", // 3 BotMapTitle(), // 4 NULL); bs->lastchat_time = FloatTime(); bs->chatto = CHAT_ALL; return qtrue; } /* ================== BotChat_ExitGame ================== */ int BotChat_ExitGame(bot_state_t *bs) { char name[32]; float rnd; if (bot_nochat.integer) return qfalse; if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse; //don't chat in teamplay if (TeamPlayIsOn()) return qfalse; // don't chat in tournament mode if (gametype == GT_TOURNAMENT) return qfalse; rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_ENTEREXITGAME, 0, 1); if (!bot_fastchat.integer) { if (random() > rnd) return qfalse; } if (BotNumActivePlayers() <= 1) return qfalse; // BotAI_BotInitialChat(bs, "game_exit", EasyClientName(bs->client, name, 32), // 0 BotRandomOpponentName(bs), // 1 "[invalid var]", // 2 "[invalid var]", // 3 BotMapTitle(), // 4 NULL); bs->lastchat_time = FloatTime(); bs->chatto = CHAT_ALL; return qtrue; } /* ================== BotChat_StartLevel ================== */ int BotChat_StartLevel(bot_state_t *bs) { char name[32]; float rnd; if (bot_nochat.integer) return qfalse; if (BotIsObserver(bs)) return qfalse; if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse; //don't chat in teamplay if (TeamPlayIsOn()) { trap_EA_Command(bs->client, "vtaunt"); return qfalse; } // don't chat in tournament mode if (gametype == GT_TOURNAMENT) return qfalse; rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_STARTENDLEVEL, 0, 1); if (!bot_fastchat.integer) { if (random() > rnd) return qfalse; } if (BotNumActivePlayers() <= 1) return qfalse; BotAI_BotInitialChat(bs, "level_start", EasyClientName(bs->client, name, 32), // 0 NULL); bs->lastchat_time = FloatTime(); bs->chatto = CHAT_ALL; return qtrue; } /* ================== BotChat_EndLevel ================== */ int BotChat_EndLevel(bot_state_t *bs) { char name[32]; float rnd; if (bot_nochat.integer) return qfalse; if (BotIsObserver(bs)) return qfalse; if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse; // teamplay if (TeamPlayIsOn()) { if (BotIsFirstInRankings(bs)) { trap_EA_Command(bs->client, "vtaunt"); } return qtrue; } // don't chat in tournament mode if (gametype == GT_TOURNAMENT) return qfalse; rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_STARTENDLEVEL, 0, 1); if (!bot_fastchat.integer) { if (random() > rnd) return qfalse; } if (BotNumActivePlayers() <= 1) return qfalse; // if (BotIsFirstInRankings(bs)) { BotAI_BotInitialChat(bs, "level_end_victory", EasyClientName(bs->client, name, 32), // 0 BotRandomOpponentName(bs), // 1 "[invalid var]", // 2 BotLastClientInRankings(), // 3 BotMapTitle(), // 4 NULL); } else if (BotIsLastInRankings(bs)) { BotAI_BotInitialChat(bs, "level_end_lose", EasyClientName(bs->client, name, 32), // 0 BotRandomOpponentName(bs), // 1 BotFirstClientInRankings(), // 2 "[invalid var]", // 3 BotMapTitle(), // 4 NULL); } else { BotAI_BotInitialChat(bs, "level_end", EasyClientName(bs->client, name, 32), // 0 BotRandomOpponentName(bs), // 1 BotFirstClientInRankings(), // 2 BotLastClientInRankings(), // 3 BotMapTitle(), // 4 NULL); } bs->lastchat_time = FloatTime(); bs->chatto = CHAT_ALL; return qtrue; } /* ================== BotChat_Death ================== */ int BotChat_Death(bot_state_t *bs) { char name[32]; float rnd; if (bot_nochat.integer) return qfalse; if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse; rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_DEATH, 0, 1); // don't chat in tournament mode if (gametype == GT_TOURNAMENT) return qfalse; //if fast chatting is off if (!bot_fastchat.integer) { if (random() > rnd) return qfalse; } if (BotNumActivePlayers() <= 1) return qfalse; // if (bs->lastkilledby >= 0 && bs->lastkilledby < MAX_CLIENTS) EasyClientName(bs->lastkilledby, name, 32); else strcpy(name, "[world]"); // if (TeamPlayIsOn() && BotSameTeam(bs, bs->lastkilledby)) { if (bs->lastkilledby == bs->client) return qfalse; BotAI_BotInitialChat(bs, "death_teammate", name, NULL); bs->chatto = CHAT_TEAM; } else { //teamplay if (TeamPlayIsOn()) { trap_EA_Command(bs->client, "vtaunt"); return qtrue; } // if (bs->botdeathtype == MOD_WATER) BotAI_BotInitialChat(bs, "death_drown", BotRandomOpponentName(bs), NULL); else if (bs->botdeathtype == MOD_SLIME) BotAI_BotInitialChat(bs, "death_slime", BotRandomOpponentName(bs), NULL); else if (bs->botdeathtype == MOD_LAVA) BotAI_BotInitialChat(bs, "death_lava", BotRandomOpponentName(bs), NULL); else if (bs->botdeathtype == MOD_FALLING) BotAI_BotInitialChat(bs, "death_cratered", BotRandomOpponentName(bs), NULL); else if (bs->botsuicide || //all other suicides by own weapon bs->botdeathtype == MOD_CRUSH || bs->botdeathtype == MOD_SUICIDE || bs->botdeathtype == MOD_TARGET_LASER || bs->botdeathtype == MOD_TRIGGER_HURT || bs->botdeathtype == MOD_UNKNOWN) BotAI_BotInitialChat(bs, "death_suicide", BotRandomOpponentName(bs), NULL); else if (bs->botdeathtype == MOD_TELEFRAG) BotAI_BotInitialChat(bs, "death_telefrag", name, NULL); else { if ((bs->botdeathtype == MOD_GAUNTLET || bs->botdeathtype == MOD_RAILGUN || bs->botdeathtype == MOD_BFG || bs->botdeathtype == MOD_BFG_SPLASH) && random() < 0.5) { if (bs->botdeathtype == MOD_GAUNTLET) BotAI_BotInitialChat(bs, "death_gauntlet", name, // 0 BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1 NULL); else if (bs->botdeathtype == MOD_RAILGUN) BotAI_BotInitialChat(bs, "death_rail", name, // 0 BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1 NULL); else BotAI_BotInitialChat(bs, "death_bfg", name, // 0 BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1 NULL); } //choose between insult and praise else if (random() < trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_INSULT, 0, 1)) { BotAI_BotInitialChat(bs, "death_insult", name, // 0 BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1 NULL); } else { BotAI_BotInitialChat(bs, "death_praise", name, // 0 BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1 NULL); } } bs->chatto = CHAT_ALL; } bs->lastchat_time = FloatTime(); return qtrue; } /* ================== BotChat_Kill ================== */ int BotChat_Kill(bot_state_t *bs) { char name[32]; float rnd; if (bot_nochat.integer) return qfalse; if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse; rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_KILL, 0, 1); // don't chat in tournament mode if (gametype == GT_TOURNAMENT) return qfalse; //if fast chat is off if (!bot_fastchat.integer) { if (random() > rnd) return qfalse; } if (bs->lastkilledplayer == bs->client) return qfalse; if (BotNumActivePlayers() <= 1) return qfalse; if (!BotValidChatPosition(bs)) return qfalse; // if (BotVisibleEnemies(bs)) return qfalse; // EasyClientName(bs->lastkilledplayer, name, 32); // bs->chatto = CHAT_ALL; if (TeamPlayIsOn() && BotSameTeam(bs, bs->lastkilledplayer)) { BotAI_BotInitialChat(bs, "kill_teammate", name, NULL); bs->chatto = CHAT_TEAM; } else { //don't chat in teamplay if (TeamPlayIsOn()) { trap_EA_Command(bs->client, "vtaunt"); return qfalse; // don't wait } // if (bs->enemydeathtype == MOD_GAUNTLET) { BotAI_BotInitialChat(bs, "kill_gauntlet", name, NULL); } else if (bs->enemydeathtype == MOD_RAILGUN) { BotAI_BotInitialChat(bs, "kill_rail", name, NULL); } else if (bs->enemydeathtype == MOD_TELEFRAG) { BotAI_BotInitialChat(bs, "kill_telefrag", name, NULL); } //choose between insult and praise else if (random() < trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_INSULT, 0, 1)) { BotAI_BotInitialChat(bs, "kill_insult", name, NULL); } else { BotAI_BotInitialChat(bs, "kill_praise", name, NULL); } } bs->lastchat_time = FloatTime(); return qtrue; } /* ================== BotChat_EnemySuicide ================== */ int BotChat_EnemySuicide(bot_state_t *bs) { char name[32]; float rnd; if (bot_nochat.integer) return qfalse; if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse; if (BotNumActivePlayers() <= 1) return qfalse; // rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_KILL, 0, 1); //don't chat in teamplay if (TeamPlayIsOn()) return qfalse; // don't chat in tournament mode if (gametype == GT_TOURNAMENT) return qfalse; //if fast chat is off if (!bot_fastchat.integer) { if (random() > rnd) return qfalse; } if (!BotValidChatPosition(bs)) return qfalse; // if (BotVisibleEnemies(bs)) return qfalse; // if (bs->enemy >= 0) EasyClientName(bs->enemy, name, 32); else strcpy(name, ""); BotAI_BotInitialChat(bs, "enemy_suicide", name, NULL); bs->lastchat_time = FloatTime(); bs->chatto = CHAT_ALL; return qtrue; } /* ================== BotChat_HitTalking ================== */ int BotChat_HitTalking(bot_state_t *bs) { char name[32], *weap; int lasthurt_client; float rnd; if (bot_nochat.integer) return qfalse; if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse; if (BotNumActivePlayers() <= 1) return qfalse; lasthurt_client = g_entities[bs->client].client->lasthurt_client; if (!lasthurt_client) return qfalse; if (lasthurt_client == bs->client) return qfalse; // if (lasthurt_client < 0 || lasthurt_client >= MAX_CLIENTS) return qfalse; // rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_HITTALKING, 0, 1); //don't chat in teamplay if (TeamPlayIsOn()) return qfalse; // don't chat in tournament mode if (gametype == GT_TOURNAMENT) return qfalse; //if fast chat is off if (!bot_fastchat.integer) { if (random() > rnd * 0.5) return qfalse; } if (!BotValidChatPosition(bs)) return qfalse; // ClientName(g_entities[bs->client].client->lasthurt_client, name, sizeof(name)); weap = BotWeaponNameForMeansOfDeath(g_entities[bs->client].client->lasthurt_client); // BotAI_BotInitialChat(bs, "hit_talking", name, weap, NULL); bs->lastchat_time = FloatTime(); bs->chatto = CHAT_ALL; return qtrue; } /* ================== BotChat_HitNoDeath ================== */ int BotChat_HitNoDeath(bot_state_t *bs) { char name[32], *weap; float rnd; int lasthurt_client; aas_entityinfo_t entinfo; lasthurt_client = g_entities[bs->client].client->lasthurt_client; if (!lasthurt_client) return qfalse; if (lasthurt_client == bs->client) return qfalse; // if (lasthurt_client < 0 || lasthurt_client >= MAX_CLIENTS) return qfalse; // if (bot_nochat.integer) return qfalse; if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse; if (BotNumActivePlayers() <= 1) return qfalse; rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_HITNODEATH, 0, 1); //don't chat in teamplay if (TeamPlayIsOn()) return qfalse; // don't chat in tournament mode if (gametype == GT_TOURNAMENT) return qfalse; //if fast chat is off if (!bot_fastchat.integer) { if (random() > rnd * 0.5) return qfalse; } if (!BotValidChatPosition(bs)) return qfalse; // if (BotVisibleEnemies(bs)) return qfalse; // BotEntityInfo(bs->enemy, &entinfo); if (EntityIsShooting(&entinfo)) return qfalse; // ClientName(lasthurt_client, name, sizeof(name)); weap = BotWeaponNameForMeansOfDeath(g_entities[bs->client].client->lasthurt_mod); // BotAI_BotInitialChat(bs, "hit_nodeath", name, weap, NULL); bs->lastchat_time = FloatTime(); bs->chatto = CHAT_ALL; return qtrue; } /* ================== BotChat_HitNoKill ================== */ int BotChat_HitNoKill(bot_state_t *bs) { char name[32], *weap; float rnd; aas_entityinfo_t entinfo; if (bot_nochat.integer) return qfalse; if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse; if (BotNumActivePlayers() <= 1) return qfalse; rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_HITNOKILL, 0, 1); //don't chat in teamplay if (TeamPlayIsOn()) return qfalse; // don't chat in tournament mode if (gametype == GT_TOURNAMENT) return qfalse; //if fast chat is off if (!bot_fastchat.integer) { if (random() > rnd * 0.5) return qfalse; } if (!BotValidChatPosition(bs)) return qfalse; // if (BotVisibleEnemies(bs)) return qfalse; // BotEntityInfo(bs->enemy, &entinfo); if (EntityIsShooting(&entinfo)) return qfalse; // ClientName(bs->enemy, name, sizeof(name)); weap = BotWeaponNameForMeansOfDeath(g_entities[bs->enemy].client->lasthurt_mod); // BotAI_BotInitialChat(bs, "hit_nokill", name, weap, NULL); bs->lastchat_time = FloatTime(); bs->chatto = CHAT_ALL; return qtrue; } /* ================== BotChat_Random ================== */ int BotChat_Random(bot_state_t *bs) { float rnd; char name[32]; if (bot_nochat.integer) return qfalse; if (BotIsObserver(bs)) return qfalse; if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse; // don't chat in tournament mode if (gametype == GT_TOURNAMENT) return qfalse; //don't chat when doing something important :) if (bs->ltgtype == LTG_TEAMHELP || bs->ltgtype == LTG_TEAMACCOMPANY || bs->ltgtype == LTG_RUSHBASE) return qfalse; // rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_RANDOM, 0, 1); if (random() > bs->thinktime * 0.1) return qfalse; if (!bot_fastchat.integer) { if (random() > rnd) return qfalse; if (random() > 0.25) return qfalse; } if (BotNumActivePlayers() <= 1) return qfalse; // if (!BotValidChatPosition(bs)) return qfalse; // if (BotVisibleEnemies(bs)) return qfalse; // if (bs->lastkilledplayer == bs->client) { strcpy(name, BotRandomOpponentName(bs)); } else { EasyClientName(bs->lastkilledplayer, name, sizeof(name)); } if (TeamPlayIsOn()) { trap_EA_Command(bs->client, "vtaunt"); return qfalse; // don't wait } // if (random() < trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_MISC, 0, 1)) { BotAI_BotInitialChat(bs, "random_misc", BotRandomOpponentName(bs), // 0 name, // 1 "[invalid var]", // 2 "[invalid var]", // 3 BotMapTitle(), // 4 BotRandomWeaponName(), // 5 NULL); } else { BotAI_BotInitialChat(bs, "random_insult", BotRandomOpponentName(bs), // 0 name, // 1 "[invalid var]", // 2 "[invalid var]", // 3 BotMapTitle(), // 4 BotRandomWeaponName(), // 5 NULL); } bs->lastchat_time = FloatTime(); bs->chatto = CHAT_ALL; return qtrue; } /* ================== BotChatTime ================== */ float BotChatTime(bot_state_t *bs) { int cpm; cpm = trap_Characteristic_BInteger(bs->character, CHARACTERISTIC_CHAT_CPM, 1, 4000); return 2.0; //(float) trap_BotChatLength(bs->cs) * 30 / cpm; } /* ================== BotChatTest ================== */ void BotChatTest(bot_state_t *bs) { char name[32]; char *weap; int num, i; num = trap_BotNumInitialChats(bs->cs, "game_enter"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "game_enter", EasyClientName(bs->client, name, 32), // 0 BotRandomOpponentName(bs), // 1 "[invalid var]", // 2 "[invalid var]", // 3 BotMapTitle(), // 4 NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "game_exit"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "game_exit", EasyClientName(bs->client, name, 32), // 0 BotRandomOpponentName(bs), // 1 "[invalid var]", // 2 "[invalid var]", // 3 BotMapTitle(), // 4 NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "level_start"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "level_start", EasyClientName(bs->client, name, 32), // 0 NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "level_end_victory"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "level_end_victory", EasyClientName(bs->client, name, 32), // 0 BotRandomOpponentName(bs), // 1 BotFirstClientInRankings(), // 2 BotLastClientInRankings(), // 3 BotMapTitle(), // 4 NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "level_end_lose"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "level_end_lose", EasyClientName(bs->client, name, 32), // 0 BotRandomOpponentName(bs), // 1 BotFirstClientInRankings(), // 2 BotLastClientInRankings(), // 3 BotMapTitle(), // 4 NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "level_end"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "level_end", EasyClientName(bs->client, name, 32), // 0 BotRandomOpponentName(bs), // 1 BotFirstClientInRankings(), // 2 BotLastClientInRankings(), // 3 BotMapTitle(), // 4 NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } EasyClientName(bs->lastkilledby, name, sizeof(name)); num = trap_BotNumInitialChats(bs->cs, "death_drown"); for (i = 0; i < num; i++) { // BotAI_BotInitialChat(bs, "death_drown", name, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "death_slime"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "death_slime", name, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "death_lava"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "death_lava", name, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "death_cratered"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "death_cratered", name, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "death_suicide"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "death_suicide", name, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "death_telefrag"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "death_telefrag", name, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "death_gauntlet"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "death_gauntlet", name, // 0 BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1 NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "death_rail"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "death_rail", name, // 0 BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1 NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "death_bfg"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "death_bfg", name, // 0 BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1 NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "death_insult"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "death_insult", name, // 0 BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1 NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "death_praise"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "death_praise", name, // 0 BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1 NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } // EasyClientName(bs->lastkilledplayer, name, 32); // num = trap_BotNumInitialChats(bs->cs, "kill_gauntlet"); for (i = 0; i < num; i++) { // BotAI_BotInitialChat(bs, "kill_gauntlet", name, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "kill_rail"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "kill_rail", name, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "kill_telefrag"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "kill_telefrag", name, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "kill_insult"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "kill_insult", name, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "kill_praise"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "kill_praise", name, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "enemy_suicide"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "enemy_suicide", name, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } ClientName(g_entities[bs->client].client->lasthurt_client, name, sizeof(name)); weap = BotWeaponNameForMeansOfDeath(g_entities[bs->client].client->lasthurt_client); num = trap_BotNumInitialChats(bs->cs, "hit_talking"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "hit_talking", name, weap, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "hit_nodeath"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "hit_nodeath", name, weap, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "hit_nokill"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "hit_nokill", name, weap, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } // if (bs->lastkilledplayer == bs->client) { strcpy(name, BotRandomOpponentName(bs)); } else { EasyClientName(bs->lastkilledplayer, name, sizeof(name)); } // num = trap_BotNumInitialChats(bs->cs, "random_misc"); for (i = 0; i < num; i++) { // BotAI_BotInitialChat(bs, "random_misc", BotRandomOpponentName(bs), // 0 name, // 1 "[invalid var]", // 2 "[invalid var]", // 3 BotMapTitle(), // 4 BotRandomWeaponName(), // 5 NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } num = trap_BotNumInitialChats(bs->cs, "random_insult"); for (i = 0; i < num; i++) { BotAI_BotInitialChat(bs, "random_insult", BotRandomOpponentName(bs), // 0 name, // 1 "[invalid var]", // 2 "[invalid var]", // 3 BotMapTitle(), // 4 BotRandomWeaponName(), // 5 NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } } ================================================ FILE: src/game/ai_chat.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: ai_chat.h * * desc: Quake3 bot AI * * $Archive: /source/code/botai/ai_chat.c $ * *****************************************************************************/ // int BotChat_EnterGame(bot_state_t *bs); // int BotChat_ExitGame(bot_state_t *bs); // int BotChat_StartLevel(bot_state_t *bs); // int BotChat_EndLevel(bot_state_t *bs); // int BotChat_HitTalking(bot_state_t *bs); // int BotChat_HitNoDeath(bot_state_t *bs); // int BotChat_HitNoKill(bot_state_t *bs); // int BotChat_Death(bot_state_t *bs); // int BotChat_Kill(bot_state_t *bs); // int BotChat_EnemySuicide(bot_state_t *bs); // int BotChat_Random(bot_state_t *bs); // time the selected chat takes to type in float BotChatTime(bot_state_t *bs); // returns true if the bot can chat at the current position int BotValidChatPosition(bot_state_t *bs); // test the initial bot chats void BotChatTest(bot_state_t *bs); ================================================ FILE: src/game/ai_cmd.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: ai_cmd.c * * desc: Quake3 bot AI * * $Archive: /MissionPack/code/game/ai_cmd.c $ * *****************************************************************************/ #include "g_local.h" #include "botlib.h" #include "be_aas.h" #include "be_ea.h" #include "be_ai_char.h" #include "be_ai_chat.h" #include "be_ai_gen.h" #include "be_ai_goal.h" #include "be_ai_move.h" #include "be_ai_weap.h" // #include "ai_main.h" #include "ai_dmq3.h" #include "ai_chat.h" #include "ai_cmd.h" #include "ai_dmnet.h" #include "ai_team.h" // #include "chars.h" //characteristics #include "inv.h" //indexes into the inventory #include "syn.h" //synonyms #include "match.h" //string matching types and vars // for the voice chats #include "menudef.h" int notleader[MAX_CLIENTS]; #ifdef DEBUG /* ================== BotPrintTeamGoal ================== */ void BotPrintTeamGoal(bot_state_t *bs) { char netname[MAX_NETNAME]; float t; ClientName(bs->client, netname, sizeof(netname)); t = bs->teamgoal_time - FloatTime(); switch(bs->ltgtype) { case LTG_TEAMHELP: { BotAI_Print(PRT_MESSAGE, "%s: I'm gonna help a team mate for %1.0f secs\n", netname, t); break; } case LTG_TEAMACCOMPANY: { BotAI_Print(PRT_MESSAGE, "%s: I'm gonna accompany a team mate for %1.0f secs\n", netname, t); break; } case LTG_GETFLAG: { BotAI_Print(PRT_MESSAGE, "%s: I'm gonna get the flag for %1.0f secs\n", netname, t); break; } case LTG_RUSHBASE: { BotAI_Print(PRT_MESSAGE, "%s: I'm gonna rush to the base for %1.0f secs\n", netname, t); break; } case LTG_RETURNFLAG: { BotAI_Print(PRT_MESSAGE, "%s: I'm gonna try to return the flag for %1.0f secs\n", netname, t); break; } case LTG_DEFENDKEYAREA: { BotAI_Print(PRT_MESSAGE, "%s: I'm gonna defend a key area for %1.0f secs\n", netname, t); break; } case LTG_GETITEM: { BotAI_Print(PRT_MESSAGE, "%s: I'm gonna get an item for %1.0f secs\n", netname, t); break; } case LTG_KILL: { BotAI_Print(PRT_MESSAGE, "%s: I'm gonna kill someone for %1.0f secs\n", netname, t); break; } case LTG_CAMP: case LTG_CAMPORDER: { BotAI_Print(PRT_MESSAGE, "%s: I'm gonna camp for %1.0f secs\n", netname, t); break; } case LTG_PATROL: { BotAI_Print(PRT_MESSAGE, "%s: I'm gonna patrol for %1.0f secs\n", netname, t); break; } default: { if (bs->ctfroam_time > FloatTime()) { t = bs->ctfroam_time - FloatTime(); BotAI_Print(PRT_MESSAGE, "%s: I'm gonna roam for %1.0f secs\n", netname, t); } else { BotAI_Print(PRT_MESSAGE, "%s: I've got a regular goal\n", netname); } } } } #endif //DEBUG /* ================== BotGetItemTeamGoal FIXME: add stuff like "upper rocket launcher" "the rl near the railgun", "lower grenade launcher" etc. ================== */ int BotGetItemTeamGoal(char *goalname, bot_goal_t *goal) { int i; if (!strlen(goalname)) return qfalse; i = -1; do { i = trap_BotGetLevelItemGoal(i, goalname, goal); if (i > 0) { //do NOT defend dropped items if (goal->flags & GFL_DROPPED) continue; return qtrue; } } while(i > 0); return qfalse; } /* ================== BotGetMessageTeamGoal ================== */ int BotGetMessageTeamGoal(bot_state_t *bs, char *goalname, bot_goal_t *goal) { bot_waypoint_t *cp; if (BotGetItemTeamGoal(goalname, goal)) return qtrue; cp = BotFindWayPoint(bs->checkpoints, goalname); if (cp) { memcpy(goal, &cp->goal, sizeof(bot_goal_t)); return qtrue; } return qfalse; } /* ================== BotGetTime ================== */ float BotGetTime(bot_match_t *match) { bot_match_t timematch; char timestring[MAX_MESSAGE_SIZE]; float t; //if the matched string has a time if (match->subtype & ST_TIME) { //get the time string trap_BotMatchVariable(match, TIME, timestring, MAX_MESSAGE_SIZE); //match it to find out if the time is in seconds or minutes if (trap_BotFindMatch(timestring, &timematch, MTCONTEXT_TIME)) { if (timematch.type == MSG_FOREVER) { t = 99999999.0f; } else if (timematch.type == MSG_FORAWHILE) { t = 10 * 60; // 10 minutes } else if (timematch.type == MSG_FORALONGTIME) { t = 30 * 60; // 30 minutes } else { trap_BotMatchVariable(&timematch, TIME, timestring, MAX_MESSAGE_SIZE); if (timematch.type == MSG_MINUTES) t = atof(timestring) * 60; else if (timematch.type == MSG_SECONDS) t = atof(timestring); else t = 0; } //if there's a valid time if (t > 0) return FloatTime() + t; } } return 0; } /* ================== FindClientByName ================== */ int FindClientByName(char *name) { int i; char buf[MAX_INFO_STRING]; static int maxclients; if (!maxclients) maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { ClientName(i, buf, sizeof(buf)); if (!Q_stricmp(buf, name)) return i; } for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { ClientName(i, buf, sizeof(buf)); if (stristr(buf, name)) return i; } return -1; } /* ================== FindEnemyByName ================== */ int FindEnemyByName(bot_state_t *bs, char *name) { int i; char buf[MAX_INFO_STRING]; static int maxclients; if (!maxclients) maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { if (BotSameTeam(bs, i)) continue; ClientName(i, buf, sizeof(buf)); if (!Q_stricmp(buf, name)) return i; } for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { if (BotSameTeam(bs, i)) continue; ClientName(i, buf, sizeof(buf)); if (stristr(buf, name)) return i; } return -1; } /* ================== NumPlayersOnSameTeam ================== */ int NumPlayersOnSameTeam(bot_state_t *bs) { int i, num; char buf[MAX_INFO_STRING]; static int maxclients; if (!maxclients) maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); num = 0; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { trap_GetConfigstring(CS_PLAYERS+i, buf, MAX_INFO_STRING); if (strlen(buf)) { if (BotSameTeam(bs, i+1)) num++; } } return num; } /* ================== TeamPlayIsOn ================== */ int BotGetPatrolWaypoints(bot_state_t *bs, bot_match_t *match) { char keyarea[MAX_MESSAGE_SIZE]; int patrolflags; bot_waypoint_t *wp, *newwp, *newpatrolpoints; bot_match_t keyareamatch; bot_goal_t goal; newpatrolpoints = NULL; patrolflags = 0; // trap_BotMatchVariable(match, KEYAREA, keyarea, MAX_MESSAGE_SIZE); // while(1) { if (!trap_BotFindMatch(keyarea, &keyareamatch, MTCONTEXT_PATROLKEYAREA)) { trap_EA_SayTeam(bs->client, "what do you say?"); BotFreeWaypoints(newpatrolpoints); bs->patrolpoints = NULL; return qfalse; } trap_BotMatchVariable(&keyareamatch, KEYAREA, keyarea, MAX_MESSAGE_SIZE); if (!BotGetMessageTeamGoal(bs, keyarea, &goal)) { //BotAI_BotInitialChat(bs, "cannotfind", keyarea, NULL); //trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); BotFreeWaypoints(newpatrolpoints); bs->patrolpoints = NULL; return qfalse; } //create a new waypoint newwp = BotCreateWayPoint(keyarea, goal.origin, goal.areanum); if (!newwp) break; //add the waypoint to the patrol points newwp->next = NULL; for (wp = newpatrolpoints; wp && wp->next; wp = wp->next); if (!wp) { newpatrolpoints = newwp; newwp->prev = NULL; } else { wp->next = newwp; newwp->prev = wp; } // if (keyareamatch.subtype & ST_BACK) { patrolflags = PATROL_LOOP; break; } else if (keyareamatch.subtype & ST_REVERSE) { patrolflags = PATROL_REVERSE; break; } else if (keyareamatch.subtype & ST_MORE) { trap_BotMatchVariable(&keyareamatch, MORE, keyarea, MAX_MESSAGE_SIZE); } else { break; } } // if (!newpatrolpoints || !newpatrolpoints->next) { trap_EA_SayTeam(bs->client, "I need more key points to patrol\n"); BotFreeWaypoints(newpatrolpoints); newpatrolpoints = NULL; return qfalse; } // BotFreeWaypoints(bs->patrolpoints); bs->patrolpoints = newpatrolpoints; // bs->curpatrolpoint = bs->patrolpoints; bs->patrolflags = patrolflags; // return qtrue; } /* ================== BotAddressedToBot ================== */ int BotAddressedToBot(bot_state_t *bs, bot_match_t *match) { char addressedto[MAX_MESSAGE_SIZE]; char netname[MAX_MESSAGE_SIZE]; char name[MAX_MESSAGE_SIZE]; char botname[128]; int client; bot_match_t addresseematch; trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); client = ClientOnSameTeamFromName(bs, netname); if (client < 0) return qfalse; //if the message is addressed to someone if (match->subtype & ST_ADDRESSED) { trap_BotMatchVariable(match, ADDRESSEE, addressedto, sizeof(addressedto)); //the name of this bot ClientName(bs->client, botname, 128); // while(trap_BotFindMatch(addressedto, &addresseematch, MTCONTEXT_ADDRESSEE)) { if (addresseematch.type == MSG_EVERYONE) { return qtrue; } else if (addresseematch.type == MSG_MULTIPLENAMES) { trap_BotMatchVariable(&addresseematch, TEAMMATE, name, sizeof(name)); if (strlen(name)) { if (stristr(botname, name)) return qtrue; if (stristr(bs->subteam, name)) return qtrue; } trap_BotMatchVariable(&addresseematch, MORE, addressedto, MAX_MESSAGE_SIZE); } else { trap_BotMatchVariable(&addresseematch, TEAMMATE, name, MAX_MESSAGE_SIZE); if (strlen(name)) { if (stristr(botname, name)) return qtrue; if (stristr(bs->subteam, name)) return qtrue; } break; } } //Com_sprintf(buf, sizeof(buf), "not addressed to me but %s", addressedto); //trap_EA_Say(bs->client, buf); return qfalse; } else { bot_match_t tellmatch; tellmatch.type = 0; //if this message wasn't directed solely to this bot if (!trap_BotFindMatch(match->string, &tellmatch, MTCONTEXT_REPLYCHAT) || tellmatch.type != MSG_CHATTELL) { //make sure not everyone reacts to this message if (random() > (float ) 1.0 / (NumPlayersOnSameTeam(bs)-1)) return qfalse; } } return qtrue; } /* ================== BotGPSToPosition ================== */ int BotGPSToPosition(char *buf, vec3_t position) { int i, j = 0; int num, sign; for (i = 0; i < 3; i++) { num = 0; while(buf[j] == ' ') j++; if (buf[j] == '-') { j++; sign = -1; } else { sign = 1; } while (buf[j]) { if (buf[j] >= '0' && buf[j] <= '9') { num = num * 10 + buf[j] - '0'; j++; } else { j++; break; } } BotAI_Print(PRT_MESSAGE, "%d\n", sign * num); position[i] = (float) sign * num; } return qtrue; } /* ================== BotMatch_HelpAccompany ================== */ void BotMatch_HelpAccompany(bot_state_t *bs, bot_match_t *match) { int client, other, areanum; char teammate[MAX_MESSAGE_SIZE]; char netname[MAX_MESSAGE_SIZE]; char itemname[MAX_MESSAGE_SIZE]; bot_match_t teammatematch; aas_entityinfo_t entinfo; if (!TeamPlayIsOn()) return; //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; //get the team mate name trap_BotMatchVariable(match, TEAMMATE, teammate, sizeof(teammate)); //get the client to help if (trap_BotFindMatch(teammate, &teammatematch, MTCONTEXT_TEAMMATE) && //if someone asks for him or herself teammatematch.type == MSG_ME) { //get the netname trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); client = ClientFromName(netname); other = qfalse; } else { //asked for someone else client = FindClientByName(teammate); //if this is the bot self if (client == bs->client) { other = qfalse; } else if (!BotSameTeam(bs, client)) { //FIXME: say "I don't help the enemy" return; } else { other = qtrue; } } //if the bot doesn't know who to help (FindClientByName returned -1) if (client < 0) { if (other) BotAI_BotInitialChat(bs, "whois", teammate, NULL); else BotAI_BotInitialChat(bs, "whois", netname, NULL); client = ClientFromName(netname); trap_BotEnterChat(bs->cs, client, CHAT_TELL); return; } //don't help or accompany yourself if (client == bs->client) { return; } // bs->teamgoal.entitynum = -1; BotEntityInfo(client, &entinfo); //if info is valid (in PVS) if (entinfo.valid) { areanum = BotPointAreaNum(entinfo.origin); if (areanum) {// && trap_AAS_AreaReachability(areanum)) { bs->teamgoal.entitynum = client; bs->teamgoal.areanum = areanum; VectorCopy(entinfo.origin, bs->teamgoal.origin); VectorSet(bs->teamgoal.mins, -8, -8, -8); VectorSet(bs->teamgoal.maxs, 8, 8, 8); } } //if no teamgoal yet if (bs->teamgoal.entitynum < 0) { //if near an item if (match->subtype & ST_NEARITEM) { //get the match variable trap_BotMatchVariable(match, ITEM, itemname, sizeof(itemname)); // if (!BotGetMessageTeamGoal(bs, itemname, &bs->teamgoal)) { //BotAI_BotInitialChat(bs, "cannotfind", itemname, NULL); //trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM); return; } } } // if (bs->teamgoal.entitynum < 0) { if (other) BotAI_BotInitialChat(bs, "whereis", teammate, NULL); else BotAI_BotInitialChat(bs, "whereareyou", netname, NULL); client = ClientFromName(netname); trap_BotEnterChat(bs->cs, client, CHAT_TEAM); return; } //the team mate bs->teammate = client; // trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); // client = ClientFromName(netname); //the team mate who ordered bs->decisionmaker = client; bs->ordered = qtrue; bs->order_time = FloatTime(); //last time the team mate was assumed visible bs->teammatevisible_time = FloatTime(); //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //get the team goal time bs->teamgoal_time = BotGetTime(match); //set the ltg type if (match->type == MSG_HELP) { bs->ltgtype = LTG_TEAMHELP; if (!bs->teamgoal_time) bs->teamgoal_time = FloatTime() + TEAM_HELP_TIME; } else { bs->ltgtype = LTG_TEAMACCOMPANY; if (!bs->teamgoal_time) bs->teamgoal_time = FloatTime() + TEAM_ACCOMPANY_TIME; bs->formation_dist = 3.5 * 32; //3.5 meter bs->arrive_time = 0; // BotSetTeamStatus(bs); // remember last ordered task BotRememberLastOrderedTask(bs); } #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotMatch_DefendKeyArea ================== */ void BotMatch_DefendKeyArea(bot_state_t *bs, bot_match_t *match) { char itemname[MAX_MESSAGE_SIZE]; char netname[MAX_MESSAGE_SIZE]; int client; if (!TeamPlayIsOn()) return; //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; //get the match variable trap_BotMatchVariable(match, KEYAREA, itemname, sizeof(itemname)); // if (!BotGetMessageTeamGoal(bs, itemname, &bs->teamgoal)) { //BotAI_BotInitialChat(bs, "cannotfind", itemname, NULL); //trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM); return; } // trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); // client = ClientFromName(netname); //the team mate who ordered bs->decisionmaker = client; bs->ordered = qtrue; bs->order_time = FloatTime(); //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //set the ltg type bs->ltgtype = LTG_DEFENDKEYAREA; //get the team goal time bs->teamgoal_time = BotGetTime(match); //set the team goal time if (!bs->teamgoal_time) bs->teamgoal_time = FloatTime() + TEAM_DEFENDKEYAREA_TIME; //away from defending bs->defendaway_time = 0; // BotSetTeamStatus(bs); // remember last ordered task BotRememberLastOrderedTask(bs); #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotMatch_GetItem ================== */ void BotMatch_GetItem(bot_state_t *bs, bot_match_t *match) { char itemname[MAX_MESSAGE_SIZE]; char netname[MAX_MESSAGE_SIZE]; int client; if (!TeamPlayIsOn()) return; //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; //get the match variable trap_BotMatchVariable(match, ITEM, itemname, sizeof(itemname)); // if (!BotGetMessageTeamGoal(bs, itemname, &bs->teamgoal)) { //BotAI_BotInitialChat(bs, "cannotfind", itemname, NULL); //trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM); return; } trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); client = ClientOnSameTeamFromName(bs, netname); // bs->decisionmaker = client; bs->ordered = qtrue; bs->order_time = FloatTime(); //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //set the ltg type bs->ltgtype = LTG_GETITEM; //set the team goal time bs->teamgoal_time = FloatTime() + TEAM_GETITEM_TIME; // BotSetTeamStatus(bs); #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotMatch_Camp ================== */ void BotMatch_Camp(bot_state_t *bs, bot_match_t *match) { int client, areanum; char netname[MAX_MESSAGE_SIZE]; char itemname[MAX_MESSAGE_SIZE]; aas_entityinfo_t entinfo; if (!TeamPlayIsOn()) return; //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; // trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); //asked for someone else client = FindClientByName(netname); //if there's no valid client with this name if (client < 0) { BotAI_BotInitialChat(bs, "whois", netname, NULL); trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM); return; } //get the match variable trap_BotMatchVariable(match, KEYAREA, itemname, sizeof(itemname)); //in CTF it could be the base if (match->subtype & ST_THERE) { //camp at the spot the bot is currently standing bs->teamgoal.entitynum = bs->entitynum; bs->teamgoal.areanum = bs->areanum; VectorCopy(bs->origin, bs->teamgoal.origin); VectorSet(bs->teamgoal.mins, -8, -8, -8); VectorSet(bs->teamgoal.maxs, 8, 8, 8); } else if (match->subtype & ST_HERE) { //if this is the bot self if (client == bs->client) return; // bs->teamgoal.entitynum = -1; BotEntityInfo(client, &entinfo); //if info is valid (in PVS) if (entinfo.valid) { areanum = BotPointAreaNum(entinfo.origin); if (areanum) {// && trap_AAS_AreaReachability(areanum)) { //NOTE: just assume the bot knows where the person is //if (BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, client)) { bs->teamgoal.entitynum = client; bs->teamgoal.areanum = areanum; VectorCopy(entinfo.origin, bs->teamgoal.origin); VectorSet(bs->teamgoal.mins, -8, -8, -8); VectorSet(bs->teamgoal.maxs, 8, 8, 8); //} } } //if the other is not visible if (bs->teamgoal.entitynum < 0) { BotAI_BotInitialChat(bs, "whereareyou", netname, NULL); client = ClientFromName(netname); trap_BotEnterChat(bs->cs, client, CHAT_TELL); return; } } else if (!BotGetMessageTeamGoal(bs, itemname, &bs->teamgoal)) { //BotAI_BotInitialChat(bs, "cannotfind", itemname, NULL); //client = ClientFromName(netname); //trap_BotEnterChat(bs->cs, client, CHAT_TELL); return; } // bs->decisionmaker = client; bs->ordered = qtrue; bs->order_time = FloatTime(); //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //set the ltg type bs->ltgtype = LTG_CAMPORDER; //get the team goal time bs->teamgoal_time = BotGetTime(match); //set the team goal time if (!bs->teamgoal_time) bs->teamgoal_time = FloatTime() + TEAM_CAMP_TIME; //not arrived yet bs->arrive_time = 0; // BotSetTeamStatus(bs); // remember last ordered task BotRememberLastOrderedTask(bs); #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotMatch_Patrol ================== */ void BotMatch_Patrol(bot_state_t *bs, bot_match_t *match) { char netname[MAX_MESSAGE_SIZE]; int client; if (!TeamPlayIsOn()) return; //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; //get the patrol waypoints if (!BotGetPatrolWaypoints(bs, match)) return; // trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); // client = FindClientByName(netname); // bs->decisionmaker = client; bs->ordered = qtrue; bs->order_time = FloatTime(); //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //set the ltg type bs->ltgtype = LTG_PATROL; //get the team goal time bs->teamgoal_time = BotGetTime(match); //set the team goal time if not set already if (!bs->teamgoal_time) bs->teamgoal_time = FloatTime() + TEAM_PATROL_TIME; // BotSetTeamStatus(bs); // remember last ordered task BotRememberLastOrderedTask(bs); #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotMatch_GetFlag ================== */ void BotMatch_GetFlag(bot_state_t *bs, bot_match_t *match) { char netname[MAX_MESSAGE_SIZE]; int client; if (gametype == GT_CTF) { if (!ctf_redflag.areanum || !ctf_blueflag.areanum) return; } else { return; } //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; // trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); // client = FindClientByName(netname); // bs->decisionmaker = client; bs->ordered = qtrue; bs->order_time = FloatTime(); //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //set the ltg type bs->ltgtype = LTG_GETFLAG; //set the team goal time bs->teamgoal_time = FloatTime() + CTF_GETFLAG_TIME; // get an alternate route in ctf if (gametype == GT_CTF) { //get an alternative route goal towards the enemy base BotGetAlternateRouteGoal(bs, BotOppositeTeam(bs)); } // BotSetTeamStatus(bs); // remember last ordered task BotRememberLastOrderedTask(bs); #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotMatch_AttackEnemyBase ================== */ void BotMatch_AttackEnemyBase(bot_state_t *bs, bot_match_t *match) { char netname[MAX_MESSAGE_SIZE]; int client; if (gametype == GT_CTF) { BotMatch_GetFlag(bs, match); } else { return; } //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; // trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); // client = FindClientByName(netname); // bs->decisionmaker = client; bs->ordered = qtrue; bs->order_time = FloatTime(); //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //set the ltg type bs->ltgtype = LTG_ATTACKENEMYBASE; //set the team goal time bs->teamgoal_time = FloatTime() + TEAM_ATTACKENEMYBASE_TIME; bs->attackaway_time = 0; // BotSetTeamStatus(bs); // remember last ordered task BotRememberLastOrderedTask(bs); #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotMatch_RushBase ================== */ void BotMatch_RushBase(bot_state_t *bs, bot_match_t *match) { char netname[MAX_MESSAGE_SIZE]; int client; if (gametype == GT_CTF) { if (!ctf_redflag.areanum || !ctf_blueflag.areanum) return; } else { return; } //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; // trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); // client = FindClientByName(netname); // bs->decisionmaker = client; bs->ordered = qtrue; bs->order_time = FloatTime(); //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //set the ltg type bs->ltgtype = LTG_RUSHBASE; //set the team goal time bs->teamgoal_time = FloatTime() + CTF_RUSHBASE_TIME; bs->rushbaseaway_time = 0; // BotSetTeamStatus(bs); #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotMatch_TaskPreference ================== */ void BotMatch_TaskPreference(bot_state_t *bs, bot_match_t *match) { char netname[MAX_NETNAME]; char teammatename[MAX_MESSAGE_SIZE]; int teammate, preference; ClientName(bs->client, netname, sizeof(netname)); if (Q_stricmp(netname, bs->teamleader) != 0) return; trap_BotMatchVariable(match, NETNAME, teammatename, sizeof(teammatename)); teammate = ClientFromName(teammatename); if (teammate < 0) return; preference = BotGetTeamMateTaskPreference(bs, teammate); switch(match->subtype) { case ST_DEFENDER: { preference &= ~TEAMTP_ATTACKER; preference |= TEAMTP_DEFENDER; break; } case ST_ATTACKER: { preference &= ~TEAMTP_DEFENDER; preference |= TEAMTP_ATTACKER; break; } case ST_ROAMER: { preference &= ~(TEAMTP_ATTACKER|TEAMTP_DEFENDER); break; } } BotSetTeamMateTaskPreference(bs, teammate, preference); // EasyClientName(teammate, teammatename, sizeof(teammatename)); BotAI_BotInitialChat(bs, "keepinmind", teammatename, NULL); trap_BotEnterChat(bs->cs, teammate, CHAT_TELL); BotVoiceChatOnly(bs, teammate, VOICECHAT_YES); trap_EA_Action(bs->client, ACTION_AFFIRMATIVE); } /* ================== BotMatch_ReturnFlag ================== */ void BotMatch_ReturnFlag(bot_state_t *bs, bot_match_t *match) { char netname[MAX_MESSAGE_SIZE]; int client; //if not in CTF mode if ( gametype != GT_CTF ) return; //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; // trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); // client = FindClientByName(netname); // bs->decisionmaker = client; bs->ordered = qtrue; bs->order_time = FloatTime(); //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //set the ltg type bs->ltgtype = LTG_RETURNFLAG; //set the team goal time bs->teamgoal_time = FloatTime() + CTF_RETURNFLAG_TIME; bs->rushbaseaway_time = 0; // BotSetTeamStatus(bs); #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotMatch_JoinSubteam ================== */ void BotMatch_JoinSubteam(bot_state_t *bs, bot_match_t *match) { char teammate[MAX_MESSAGE_SIZE]; char netname[MAX_MESSAGE_SIZE]; int client; if (!TeamPlayIsOn()) return; //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; //get the sub team name trap_BotMatchVariable(match, TEAMNAME, teammate, sizeof(teammate)); //set the sub team name strncpy(bs->subteam, teammate, 32); bs->subteam[31] = '\0'; // trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); BotAI_BotInitialChat(bs, "joinedteam", teammate, NULL); client = ClientFromName(netname); trap_BotEnterChat(bs->cs, client, CHAT_TELL); } /* ================== BotMatch_LeaveSubteam ================== */ void BotMatch_LeaveSubteam(bot_state_t *bs, bot_match_t *match) { char netname[MAX_MESSAGE_SIZE]; int client; if (!TeamPlayIsOn()) return; //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; // if (strlen(bs->subteam)) { BotAI_BotInitialChat(bs, "leftteam", bs->subteam, NULL); trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); client = ClientFromName(netname); trap_BotEnterChat(bs->cs, client, CHAT_TELL); } //end if strcpy(bs->subteam, ""); } /* ================== BotMatch_LeaveSubteam ================== */ void BotMatch_WhichTeam(bot_state_t *bs, bot_match_t *match) { if (!TeamPlayIsOn()) return; //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; // if (strlen(bs->subteam)) { BotAI_BotInitialChat(bs, "inteam", bs->subteam, NULL); } else { BotAI_BotInitialChat(bs, "noteam", NULL); } trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM); } /* ================== BotMatch_CheckPoint ================== */ void BotMatch_CheckPoint(bot_state_t *bs, bot_match_t *match) { int areanum, client; char buf[MAX_MESSAGE_SIZE]; char netname[MAX_MESSAGE_SIZE]; vec3_t position; bot_waypoint_t *cp; if (!TeamPlayIsOn()) return; // trap_BotMatchVariable(match, POSITION, buf, MAX_MESSAGE_SIZE); VectorClear(position); // trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); client = ClientFromName(netname); //BotGPSToPosition(buf, position); sscanf(buf, "%f %f %f", &position[0], &position[1], &position[2]); position[2] += 0.5; areanum = BotPointAreaNum(position); if (!areanum) { if (BotAddressedToBot(bs, match)) { BotAI_BotInitialChat(bs, "checkpoint_invalid", NULL); trap_BotEnterChat(bs->cs, client, CHAT_TELL); } return; } // trap_BotMatchVariable(match, NAME, buf, MAX_MESSAGE_SIZE); //check if there already exists a checkpoint with this name cp = BotFindWayPoint(bs->checkpoints, buf); if (cp) { if (cp->next) cp->next->prev = cp->prev; if (cp->prev) cp->prev->next = cp->next; else bs->checkpoints = cp->next; cp->inuse = qfalse; } //create a new check point cp = BotCreateWayPoint(buf, position, areanum); //add the check point to the bot's known chech points cp->next = bs->checkpoints; if (bs->checkpoints) bs->checkpoints->prev = cp; bs->checkpoints = cp; // if (BotAddressedToBot(bs, match)) { Com_sprintf(buf, sizeof(buf), "%1.0f %1.0f %1.0f", cp->goal.origin[0], cp->goal.origin[1], cp->goal.origin[2]); BotAI_BotInitialChat(bs, "checkpoint_confirm", cp->name, buf, NULL); trap_BotEnterChat(bs->cs, client, CHAT_TELL); } } /* ================== BotMatch_FormationSpace ================== */ void BotMatch_FormationSpace(bot_state_t *bs, bot_match_t *match) { char buf[MAX_MESSAGE_SIZE]; float space; if (!TeamPlayIsOn()) return; //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; // trap_BotMatchVariable(match, NUMBER, buf, MAX_MESSAGE_SIZE); //if it's the distance in feet if (match->subtype & ST_FEET) space = 0.3048 * 32 * atof(buf); //else it's in meters else space = 32 * atof(buf); //check if the formation intervening space is valid if (space < 48 || space > 500) space = 100; bs->formation_dist = space; } /* ================== BotMatch_Dismiss ================== */ void BotMatch_Dismiss(bot_state_t *bs, bot_match_t *match) { char netname[MAX_MESSAGE_SIZE]; int client; if (!TeamPlayIsOn()) return; //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); client = ClientFromName(netname); // bs->decisionmaker = client; // bs->ltgtype = 0; bs->lead_time = 0; bs->lastgoal_ltgtype = 0; // BotAI_BotInitialChat(bs, "dismissed", NULL); trap_BotEnterChat(bs->cs, client, CHAT_TELL); } /* ================== BotMatch_Suicide ================== */ void BotMatch_Suicide(bot_state_t *bs, bot_match_t *match) { char netname[MAX_MESSAGE_SIZE]; int client; if (!TeamPlayIsOn()) return; //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; // trap_EA_Command(bs->client, "kill"); // trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); client = ClientFromName(netname); // BotVoiceChat(bs, client, VOICECHAT_TAUNT); trap_EA_Action(bs->client, ACTION_AFFIRMATIVE); } /* ================== BotMatch_StartTeamLeaderShip ================== */ void BotMatch_StartTeamLeaderShip(bot_state_t *bs, bot_match_t *match) { int client; char teammate[MAX_MESSAGE_SIZE]; if (!TeamPlayIsOn()) return; //if chats for him or herself if (match->subtype & ST_I) { //get the team mate that will be the team leader trap_BotMatchVariable(match, NETNAME, teammate, sizeof(teammate)); strncpy(bs->teamleader, teammate, sizeof(bs->teamleader)); bs->teamleader[sizeof(bs->teamleader)] = '\0'; } //chats for someone else else { //get the team mate that will be the team leader trap_BotMatchVariable(match, TEAMMATE, teammate, sizeof(teammate)); client = FindClientByName(teammate); if (client >= 0) ClientName(client, bs->teamleader, sizeof(bs->teamleader)); } } /* ================== BotMatch_StopTeamLeaderShip ================== */ void BotMatch_StopTeamLeaderShip(bot_state_t *bs, bot_match_t *match) { int client; char teammate[MAX_MESSAGE_SIZE]; char netname[MAX_MESSAGE_SIZE]; if (!TeamPlayIsOn()) return; //get the team mate that stops being the team leader trap_BotMatchVariable(match, TEAMMATE, teammate, sizeof(teammate)); //if chats for him or herself if (match->subtype & ST_I) { trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); client = FindClientByName(netname); } //chats for someone else else { client = FindClientByName(teammate); } //end else if (client >= 0) { if (!Q_stricmp(bs->teamleader, ClientName(client, netname, sizeof(netname)))) { bs->teamleader[0] = '\0'; notleader[client] = qtrue; } } } /* ================== BotMatch_WhoIsTeamLeader ================== */ void BotMatch_WhoIsTeamLeader(bot_state_t *bs, bot_match_t *match) { char netname[MAX_MESSAGE_SIZE]; if (!TeamPlayIsOn()) return; ClientName(bs->client, netname, sizeof(netname)); //if this bot IS the team leader if (!Q_stricmp(netname, bs->teamleader)) { trap_EA_SayTeam(bs->client, "I'm the team leader\n"); } } /* ================== BotMatch_WhatAreYouDoing ================== */ void BotMatch_WhatAreYouDoing(bot_state_t *bs, bot_match_t *match) { char netname[MAX_MESSAGE_SIZE]; char goalname[MAX_MESSAGE_SIZE]; int client; //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; // switch(bs->ltgtype) { case LTG_TEAMHELP: { EasyClientName(bs->teammate, netname, sizeof(netname)); BotAI_BotInitialChat(bs, "helping", netname, NULL); break; } case LTG_TEAMACCOMPANY: { EasyClientName(bs->teammate, netname, sizeof(netname)); BotAI_BotInitialChat(bs, "accompanying", netname, NULL); break; } case LTG_DEFENDKEYAREA: { trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname)); BotAI_BotInitialChat(bs, "defending", goalname, NULL); break; } case LTG_GETITEM: { trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname)); BotAI_BotInitialChat(bs, "gettingitem", goalname, NULL); break; } case LTG_KILL: { ClientName(bs->teamgoal.entitynum, netname, sizeof(netname)); BotAI_BotInitialChat(bs, "killing", netname, NULL); break; } case LTG_CAMP: case LTG_CAMPORDER: { BotAI_BotInitialChat(bs, "camping", NULL); break; } case LTG_PATROL: { BotAI_BotInitialChat(bs, "patrolling", NULL); break; } case LTG_GETFLAG: { BotAI_BotInitialChat(bs, "capturingflag", NULL); break; } case LTG_RUSHBASE: { BotAI_BotInitialChat(bs, "rushingbase", NULL); break; } case LTG_RETURNFLAG: { BotAI_BotInitialChat(bs, "returningflag", NULL); break; } default: { BotAI_BotInitialChat(bs, "roaming", NULL); break; } } //chat what the bot is doing trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); client = ClientFromName(netname); trap_BotEnterChat(bs->cs, client, CHAT_TELL); } /* ================== BotMatch_WhatIsMyCommand ================== */ void BotMatch_WhatIsMyCommand(bot_state_t *bs, bot_match_t *match) { char netname[MAX_NETNAME]; ClientName(bs->client, netname, sizeof(netname)); if (Q_stricmp(netname, bs->teamleader) != 0) return; bs->forceorders = qtrue; } /* ================== BotNearestVisibleItem ================== */ float BotNearestVisibleItem(bot_state_t *bs, char *itemname, bot_goal_t *goal) { int i; char name[64]; bot_goal_t tmpgoal; float dist, bestdist; vec3_t dir; bsp_trace_t trace; bestdist = 999999; i = -1; do { i = trap_BotGetLevelItemGoal(i, itemname, &tmpgoal); trap_BotGoalName(tmpgoal.number, name, sizeof(name)); if (Q_stricmp(itemname, name) != 0) continue; VectorSubtract(tmpgoal.origin, bs->origin, dir); dist = VectorLength(dir); if (dist < bestdist) { //trace from start to end BotAI_Trace(&trace, bs->eye, NULL, NULL, tmpgoal.origin, bs->client, CONTENTS_SOLID|CONTENTS_PLAYERCLIP); if (trace.fraction >= 1.0) { bestdist = dist; memcpy(goal, &tmpgoal, sizeof(bot_goal_t)); } } } while(i > 0); return bestdist; } /* ================== BotMatch_WhereAreYou ================== */ void BotMatch_WhereAreYou(bot_state_t *bs, bot_match_t *match) { float dist, bestdist; int i, bestitem, redtt, bluett, client; bot_goal_t goal; char netname[MAX_MESSAGE_SIZE]; char *nearbyitems[] = { "Shotgun", "Grenade Launcher", "Rocket Launcher", "Plasmagun", "Railgun", "Lightning Gun", "BFG10K", "Quad Damage", "Regeneration", "Battle Suit", "Speed", "Invisibility", "Flight", "Armor", "Heavy Armor", "Red Flag", "Blue Flag", NULL }; // if (!TeamPlayIsOn()) return; //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; bestitem = -1; bestdist = 999999; for (i = 0; nearbyitems[i]; i++) { dist = BotNearestVisibleItem(bs, nearbyitems[i], &goal); if (dist < bestdist) { bestdist = dist; bestitem = i; } } if (bestitem != -1) { if (gametype == GT_CTF ) { redtt = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, ctf_redflag.areanum, TFL_DEFAULT); bluett = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, ctf_blueflag.areanum, TFL_DEFAULT); if (redtt < (redtt + bluett) * 0.4) { BotAI_BotInitialChat(bs, "teamlocation", nearbyitems[bestitem], "red", NULL); } else if (bluett < (redtt + bluett) * 0.4) { BotAI_BotInitialChat(bs, "teamlocation", nearbyitems[bestitem], "blue", NULL); } else { BotAI_BotInitialChat(bs, "location", nearbyitems[bestitem], NULL); } } else { BotAI_BotInitialChat(bs, "location", nearbyitems[bestitem], NULL); } trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); client = ClientFromName(netname); trap_BotEnterChat(bs->cs, client, CHAT_TELL); } } /* ================== BotMatch_LeadTheWay ================== */ void BotMatch_LeadTheWay(bot_state_t *bs, bot_match_t *match) { aas_entityinfo_t entinfo; char netname[MAX_MESSAGE_SIZE], teammate[MAX_MESSAGE_SIZE]; int client, areanum, other; if (!TeamPlayIsOn()) return; //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; //if someone asks for someone else if (match->subtype & ST_SOMEONE) { //get the team mate name trap_BotMatchVariable(match, TEAMMATE, teammate, sizeof(teammate)); client = FindClientByName(teammate); //if this is the bot self if (client == bs->client) { other = qfalse; } else if (!BotSameTeam(bs, client)) { //FIXME: say "I don't help the enemy" return; } else { other = qtrue; } } else { //get the netname trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); client = ClientFromName(netname); other = qfalse; } //if the bot doesn't know who to help (FindClientByName returned -1) if (client < 0) { BotAI_BotInitialChat(bs, "whois", netname, NULL); trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM); return; } // bs->lead_teamgoal.entitynum = -1; BotEntityInfo(client, &entinfo); //if info is valid (in PVS) if (entinfo.valid) { areanum = BotPointAreaNum(entinfo.origin); if (areanum) { // && trap_AAS_AreaReachability(areanum)) { bs->lead_teamgoal.entitynum = client; bs->lead_teamgoal.areanum = areanum; VectorCopy(entinfo.origin, bs->lead_teamgoal.origin); VectorSet(bs->lead_teamgoal.mins, -8, -8, -8); VectorSet(bs->lead_teamgoal.maxs, 8, 8, 8); } } if (bs->teamgoal.entitynum < 0) { if (other) BotAI_BotInitialChat(bs, "whereis", teammate, NULL); else BotAI_BotInitialChat(bs, "whereareyou", netname, NULL); trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM); return; } bs->lead_teammate = client; bs->lead_time = FloatTime() + TEAM_LEAD_TIME; bs->leadvisible_time = 0; bs->leadmessage_time = -(FloatTime() + 2 * random()); } /* ================== BotMatch_Kill ================== */ void BotMatch_Kill(bot_state_t *bs, bot_match_t *match) { char enemy[MAX_MESSAGE_SIZE]; char netname[MAX_MESSAGE_SIZE]; int client; if (!TeamPlayIsOn()) return; //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; trap_BotMatchVariable(match, ENEMY, enemy, sizeof(enemy)); // client = FindEnemyByName(bs, enemy); if (client < 0) { BotAI_BotInitialChat(bs, "whois", enemy, NULL); trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); client = ClientFromName(netname); trap_BotEnterChat(bs->cs, client, CHAT_TELL); return; } bs->teamgoal.entitynum = client; //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //set the ltg type bs->ltgtype = LTG_KILL; //set the team goal time bs->teamgoal_time = FloatTime() + TEAM_KILL_SOMEONE; // BotSetTeamStatus(bs); #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotMatch_CTF ================== */ void BotMatch_CTF(bot_state_t *bs, bot_match_t *match) { char flag[128], netname[MAX_NETNAME]; if (gametype == GT_CTF) { trap_BotMatchVariable(match, FLAG, flag, sizeof(flag)); if (match->subtype & ST_GOTFLAG) { if (!Q_stricmp(flag, "red")) { bs->redflagstatus = 1; if (BotTeam(bs) == TEAM_BLUE) { trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); bs->flagcarrier = ClientFromName(netname); } } else { bs->blueflagstatus = 1; if (BotTeam(bs) == TEAM_RED) { trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); bs->flagcarrier = ClientFromName(netname); } } bs->flagstatuschanged = 1; bs->lastflagcapture_time = FloatTime(); } else if (match->subtype & ST_CAPTUREDFLAG) { bs->redflagstatus = 0; bs->blueflagstatus = 0; bs->flagcarrier = 0; bs->flagstatuschanged = 1; } else if (match->subtype & ST_RETURNEDFLAG) { if (!Q_stricmp(flag, "red")) bs->redflagstatus = 0; else bs->blueflagstatus = 0; bs->flagstatuschanged = 1; } } } void BotMatch_EnterGame(bot_state_t *bs, bot_match_t *match) { int client; char netname[MAX_NETNAME]; trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); client = FindClientByName(netname); if (client >= 0) { notleader[client] = qfalse; } //NOTE: eliza chats will catch this //Com_sprintf(buf, sizeof(buf), "heya %s", netname); //EA_Say(bs->client, buf); } void BotMatch_NewLeader(bot_state_t *bs, bot_match_t *match) { int client; char netname[MAX_NETNAME]; trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); client = FindClientByName(netname); if (!BotSameTeam(bs, client)) return; Q_strncpyz(bs->teamleader, netname, sizeof(bs->teamleader)); } /* ================== BotMatchMessage ================== */ int BotMatchMessage(bot_state_t *bs, char *message) { bot_match_t match; match.type = 0; //if it is an unknown message if (!trap_BotFindMatch(message, &match, MTCONTEXT_MISC |MTCONTEXT_INITIALTEAMCHAT |MTCONTEXT_CTF)) { return qfalse; } //react to the found message switch(match.type) { case MSG_HELP: //someone calling for help case MSG_ACCOMPANY: //someone calling for company { BotMatch_HelpAccompany(bs, &match); break; } case MSG_DEFENDKEYAREA: //teamplay defend a key area { BotMatch_DefendKeyArea(bs, &match); break; } case MSG_CAMP: //camp somewhere { BotMatch_Camp(bs, &match); break; } case MSG_PATROL: //patrol between several key areas { BotMatch_Patrol(bs, &match); break; } //CTF & 1FCTF case MSG_GETFLAG: //ctf get the enemy flag { BotMatch_GetFlag(bs, &match); break; } //CTF & 1FCTF & Harvester case MSG_RUSHBASE: //ctf rush to the base { BotMatch_RushBase(bs, &match); break; } //CTF & 1FCTF case MSG_RETURNFLAG: { BotMatch_ReturnFlag(bs, &match); break; } //CTF & 1FCTF & Obelisk & Harvester case MSG_TASKPREFERENCE: { BotMatch_TaskPreference(bs, &match); break; } //CTF & 1FCTF case MSG_CTF: { BotMatch_CTF(bs, &match); break; } case MSG_GETITEM: { BotMatch_GetItem(bs, &match); break; } case MSG_JOINSUBTEAM: //join a sub team { BotMatch_JoinSubteam(bs, &match); break; } case MSG_LEAVESUBTEAM: //leave a sub team { BotMatch_LeaveSubteam(bs, &match); break; } case MSG_WHICHTEAM: { BotMatch_WhichTeam(bs, &match); break; } case MSG_CHECKPOINT: //remember a check point { BotMatch_CheckPoint(bs, &match); break; } case MSG_CREATENEWFORMATION: //start the creation of a new formation { trap_EA_SayTeam(bs->client, "the part of my brain to create formations has been damaged"); break; } case MSG_FORMATIONPOSITION: //tell someone his/her position in the formation { trap_EA_SayTeam(bs->client, "the part of my brain to create formations has been damaged"); break; } case MSG_FORMATIONSPACE: //set the formation space { BotMatch_FormationSpace(bs, &match); break; } case MSG_DOFORMATION: //form a certain formation { break; } case MSG_DISMISS: //dismiss someone { BotMatch_Dismiss(bs, &match); break; } case MSG_STARTTEAMLEADERSHIP: //someone will become the team leader { BotMatch_StartTeamLeaderShip(bs, &match); break; } case MSG_STOPTEAMLEADERSHIP: //someone will stop being the team leader { BotMatch_StopTeamLeaderShip(bs, &match); break; } case MSG_WHOISTEAMLAEDER: { BotMatch_WhoIsTeamLeader(bs, &match); break; } case MSG_WHATAREYOUDOING: //ask a bot what he/she is doing { BotMatch_WhatAreYouDoing(bs, &match); break; } case MSG_WHATISMYCOMMAND: { BotMatch_WhatIsMyCommand(bs, &match); break; } case MSG_WHEREAREYOU: { BotMatch_WhereAreYou(bs, &match); break; } case MSG_LEADTHEWAY: { BotMatch_LeadTheWay(bs, &match); break; } case MSG_KILL: { BotMatch_Kill(bs, &match); break; } case MSG_ENTERGAME: //someone entered the game { BotMatch_EnterGame(bs, &match); break; } case MSG_NEWLEADER: { BotMatch_NewLeader(bs, &match); break; } case MSG_WAIT: { break; } case MSG_SUICIDE: { BotMatch_Suicide(bs, &match); break; } default: { BotAI_Print(PRT_MESSAGE, "unknown match type\n"); break; } } return qtrue; } ================================================ FILE: src/game/ai_cmd.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: ai_cmd.h * * desc: Quake3 bot AI * * $Archive: /source/code/botai/ai_chat.c $ * *****************************************************************************/ extern int notleader[MAX_CLIENTS]; int BotMatchMessage(bot_state_t *bs, char *message); void BotPrintTeamGoal(bot_state_t *bs); ================================================ FILE: src/game/ai_dmnet.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: ai_dmnet.c * * desc: Quake3 bot AI * * $Archive: /MissionPack/code/game/ai_dmnet.c $ * *****************************************************************************/ #include "g_local.h" #include "botlib.h" #include "be_aas.h" #include "be_ea.h" #include "be_ai_char.h" #include "be_ai_chat.h" #include "be_ai_gen.h" #include "be_ai_goal.h" #include "be_ai_move.h" #include "be_ai_weap.h" // #include "ai_main.h" #include "ai_dmq3.h" #include "ai_chat.h" #include "ai_cmd.h" #include "ai_dmnet.h" #include "ai_team.h" //data file headers #include "chars.h" //characteristics #include "inv.h" //indexes into the inventory #include "syn.h" //synonyms #include "match.h" //string matching types and vars // for the voice chats #include "menudef.h" //goal flag, see be_ai_goal.h for the other GFL_* #define GFL_AIR 128 int numnodeswitches; char nodeswitch[MAX_NODESWITCHES+1][144]; #define LOOKAHEAD_DISTANCE 300 /* ================== BotResetNodeSwitches ================== */ void BotResetNodeSwitches(void) { numnodeswitches = 0; } /* ================== BotDumpNodeSwitches ================== */ void BotDumpNodeSwitches(bot_state_t *bs) { int i; char netname[MAX_NETNAME]; ClientName(bs->client, netname, sizeof(netname)); BotAI_Print(PRT_MESSAGE, "%s at %1.1f switched more than %d AI nodes\n", netname, FloatTime(), MAX_NODESWITCHES); for (i = 0; i < numnodeswitches; i++) { BotAI_Print(PRT_MESSAGE, nodeswitch[i]); } BotAI_Print(PRT_FATAL, ""); } /* ================== BotRecordNodeSwitch ================== */ void BotRecordNodeSwitch(bot_state_t *bs, char *node, char *str, char *s) { char netname[MAX_NETNAME]; ClientName(bs->client, netname, sizeof(netname)); Com_sprintf(nodeswitch[numnodeswitches], 144, "%s at %2.1f entered %s: %s from %s\n", netname, FloatTime(), node, str, s); #ifdef DEBUG if (0) { BotAI_Print(PRT_MESSAGE, nodeswitch[numnodeswitches]); } #endif //DEBUG numnodeswitches++; } /* ================== BotGetAirGoal ================== */ int BotGetAirGoal(bot_state_t *bs, bot_goal_t *goal) { bsp_trace_t bsptrace; vec3_t end, mins = {-15, -15, -2}, maxs = {15, 15, 2}; int areanum; //trace up until we hit solid VectorCopy(bs->origin, end); end[2] += 1000; BotAI_Trace(&bsptrace, bs->origin, mins, maxs, end, bs->entitynum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP); //trace down until we hit water VectorCopy(bsptrace.endpos, end); BotAI_Trace(&bsptrace, end, mins, maxs, bs->origin, bs->entitynum, CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA); //if we found the water surface if (bsptrace.fraction > 0) { areanum = BotPointAreaNum(bsptrace.endpos); if (areanum) { VectorCopy(bsptrace.endpos, goal->origin); goal->origin[2] -= 2; goal->areanum = areanum; goal->mins[0] = -15; goal->mins[1] = -15; goal->mins[2] = -1; goal->maxs[0] = 15; goal->maxs[1] = 15; goal->maxs[2] = 1; goal->flags = GFL_AIR; goal->number = 0; goal->iteminfo = 0; goal->entitynum = 0; return qtrue; } } return qfalse; } /* ================== BotGoForAir ================== */ int BotGoForAir(bot_state_t *bs, int tfl, bot_goal_t *ltg, float range) { bot_goal_t goal; //if the bot needs air if (bs->lastair_time < FloatTime() - 6) { // #ifdef DEBUG //BotAI_Print(PRT_MESSAGE, "going for air\n"); #endif //DEBUG //if we can find an air goal if (BotGetAirGoal(bs, &goal)) { trap_BotPushGoal(bs->gs, &goal); return qtrue; } else { //get a nearby goal outside the water while(trap_BotChooseNBGItem(bs->gs, bs->origin, bs->inventory, tfl, ltg, range)) { trap_BotGetTopGoal(bs->gs, &goal); //if the goal is not in water if (!(trap_AAS_PointContents(goal.origin) & (CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA))) { return qtrue; } trap_BotPopGoal(bs->gs); } trap_BotResetAvoidGoals(bs->gs); } } return qfalse; } /* ================== BotNearbyGoal ================== */ int BotNearbyGoal(bot_state_t *bs, int tfl, bot_goal_t *ltg, float range) { int ret; //check if the bot should go for air if (BotGoForAir(bs, tfl, ltg, range)) return qtrue; //if the bot is carrying the enemy flag if (BotCTFCarryingFlag(bs)) { //if the bot is just a few secs away from the base if (trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, bs->teamgoal.areanum, TFL_DEFAULT) < 300) { //make the range really small range = 50; } } // ret = trap_BotChooseNBGItem(bs->gs, bs->origin, bs->inventory, tfl, ltg, range); /* if (ret) { char buf[128]; //get the goal at the top of the stack trap_BotGetTopGoal(bs->gs, &goal); trap_BotGoalName(goal.number, buf, sizeof(buf)); BotAI_Print(PRT_MESSAGE, "%1.1f: new nearby goal %s\n", FloatTime(), buf); } */ return ret; } /* ================== BotReachedGoal ================== */ int BotReachedGoal(bot_state_t *bs, bot_goal_t *goal) { if (goal->flags & GFL_ITEM) { //if touching the goal if (trap_BotTouchingGoal(bs->origin, goal)) { if (!(goal->flags & GFL_DROPPED)) { trap_BotSetAvoidGoalTime(bs->gs, goal->number, -1); } return qtrue; } //if the goal isn't there if (trap_BotItemGoalInVisButNotVisible(bs->entitynum, bs->eye, bs->viewangles, goal)) { /* float avoidtime; int t; avoidtime = trap_BotAvoidGoalTime(bs->gs, goal->number); if (avoidtime > 0) { t = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, goal->areanum, bs->tfl); if ((float) t * 0.009 < avoidtime) return qtrue; } */ return qtrue; } //if in the goal area and below or above the goal and not swimming if (bs->areanum == goal->areanum) { if (bs->origin[0] > goal->origin[0] + goal->mins[0] && bs->origin[0] < goal->origin[0] + goal->maxs[0]) { if (bs->origin[1] > goal->origin[1] + goal->mins[1] && bs->origin[1] < goal->origin[1] + goal->maxs[1]) { if (!trap_AAS_Swimming(bs->origin)) { return qtrue; } } } } } else if (goal->flags & GFL_AIR) { //if touching the goal if (trap_BotTouchingGoal(bs->origin, goal)) return qtrue; //if the bot got air if (bs->lastair_time > FloatTime() - 1) return qtrue; } else { //if touching the goal if (trap_BotTouchingGoal(bs->origin, goal)) return qtrue; } return qfalse; } /* ================== BotGetItemLongTermGoal ================== */ int BotGetItemLongTermGoal(bot_state_t *bs, int tfl, bot_goal_t *goal) { //if the bot has no goal if (!trap_BotGetTopGoal(bs->gs, goal)) { //BotAI_Print(PRT_MESSAGE, "no ltg on stack\n"); bs->ltg_time = 0; } //if the bot touches the current goal else if (BotReachedGoal(bs, goal)) { BotChooseWeapon(bs); bs->ltg_time = 0; } //if it is time to find a new long term goal if (bs->ltg_time < FloatTime()) { //pop the current goal from the stack trap_BotPopGoal(bs->gs); //BotAI_Print(PRT_MESSAGE, "%s: choosing new ltg\n", ClientName(bs->client, netname, sizeof(netname))); //choose a new goal //BotAI_Print(PRT_MESSAGE, "%6.1f client %d: BotChooseLTGItem\n", FloatTime(), bs->client); if (trap_BotChooseLTGItem(bs->gs, bs->origin, bs->inventory, tfl)) { /* char buf[128]; //get the goal at the top of the stack trap_BotGetTopGoal(bs->gs, goal); trap_BotGoalName(goal->number, buf, sizeof(buf)); BotAI_Print(PRT_MESSAGE, "%1.1f: new long term goal %s\n", FloatTime(), buf); */ bs->ltg_time = FloatTime() + 20; } else {//the bot gets sorta stuck with all the avoid timings, shouldn't happen though // #ifdef DEBUG char netname[128]; BotAI_Print(PRT_MESSAGE, "%s: no valid ltg (probably stuck)\n", ClientName(bs->client, netname, sizeof(netname))); #endif //trap_BotDumpAvoidGoals(bs->gs); //reset the avoid goals and the avoid reach trap_BotResetAvoidGoals(bs->gs); trap_BotResetAvoidReach(bs->ms); } //get the goal at the top of the stack return trap_BotGetTopGoal(bs->gs, goal); } return qtrue; } /* ================== BotGetLongTermGoal we could also create a seperate AI node for every long term goal type however this saves us a lot of code ================== */ int BotGetLongTermGoal(bot_state_t *bs, int tfl, int retreat, bot_goal_t *goal) { vec3_t target, dir, dir2; char netname[MAX_NETNAME]; char buf[MAX_MESSAGE_SIZE]; int areanum; float croucher; aas_entityinfo_t entinfo, botinfo; bot_waypoint_t *wp; if (bs->ltgtype == LTG_TEAMHELP && !retreat) { //check for bot typing status message if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { BotAI_BotInitialChat(bs, "help_start", EasyClientName(bs->teammate, netname, sizeof(netname)), NULL); trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL); BotVoiceChatOnly(bs, bs->decisionmaker, VOICECHAT_YES); trap_EA_Action(bs->client, ACTION_AFFIRMATIVE); bs->teammessage_time = 0; } //if trying to help the team mate for more than a minute if (bs->teamgoal_time < FloatTime()) bs->ltgtype = 0; //if the team mate IS visible for quite some time if (bs->teammatevisible_time < FloatTime() - 10) bs->ltgtype = 0; //get entity information of the companion BotEntityInfo(bs->teammate, &entinfo); //if the team mate is visible if (BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, bs->teammate)) { //if close just stand still there VectorSubtract(entinfo.origin, bs->origin, dir); if (VectorLengthSquared(dir) < Square(100)) { trap_BotResetAvoidReach(bs->ms); return qfalse; } } else { //last time the bot was NOT visible bs->teammatevisible_time = FloatTime(); } //if the entity information is valid (entity in PVS) if (entinfo.valid) { areanum = BotPointAreaNum(entinfo.origin); if (areanum && trap_AAS_AreaReachability(areanum)) { //update team goal bs->teamgoal.entitynum = bs->teammate; bs->teamgoal.areanum = areanum; VectorCopy(entinfo.origin, bs->teamgoal.origin); VectorSet(bs->teamgoal.mins, -8, -8, -8); VectorSet(bs->teamgoal.maxs, 8, 8, 8); } } memcpy(goal, &bs->teamgoal, sizeof(bot_goal_t)); return qtrue; } //if the bot accompanies someone if (bs->ltgtype == LTG_TEAMACCOMPANY && !retreat) { //check for bot typing status message if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { BotAI_BotInitialChat(bs, "accompany_start", EasyClientName(bs->teammate, netname, sizeof(netname)), NULL); trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL); BotVoiceChatOnly(bs, bs->decisionmaker, VOICECHAT_YES); trap_EA_Action(bs->client, ACTION_AFFIRMATIVE); bs->teammessage_time = 0; } //if accompanying the companion for 3 minutes if (bs->teamgoal_time < FloatTime()) { BotAI_BotInitialChat(bs, "accompany_stop", EasyClientName(bs->teammate, netname, sizeof(netname)), NULL); trap_BotEnterChat(bs->cs, bs->teammate, CHAT_TELL); bs->ltgtype = 0; } //get entity information of the companion BotEntityInfo(bs->teammate, &entinfo); //if the companion is visible if (BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, bs->teammate)) { //update visible time bs->teammatevisible_time = FloatTime(); VectorSubtract(entinfo.origin, bs->origin, dir); if (VectorLengthSquared(dir) < Square(bs->formation_dist)) { // // if the client being followed bumps into this bot then // the bot should back up BotEntityInfo(bs->entitynum, &botinfo); // if the followed client is not standing ontop of the bot if (botinfo.origin[2] + botinfo.maxs[2] > entinfo.origin[2] + entinfo.mins[2]) { // if the bounding boxes touch each other if (botinfo.origin[0] + botinfo.maxs[0] > entinfo.origin[0] + entinfo.mins[0] - 4&& botinfo.origin[0] + botinfo.mins[0] < entinfo.origin[0] + entinfo.maxs[0] + 4) { if (botinfo.origin[1] + botinfo.maxs[1] > entinfo.origin[1] + entinfo.mins[1] - 4 && botinfo.origin[1] + botinfo.mins[1] < entinfo.origin[1] + entinfo.maxs[1] + 4) { if (botinfo.origin[2] + botinfo.maxs[2] > entinfo.origin[2] + entinfo.mins[2] - 4 && botinfo.origin[2] + botinfo.mins[2] < entinfo.origin[2] + entinfo.maxs[2] + 4) { // if the followed client looks in the direction of this bot AngleVectors(entinfo.angles, dir, NULL, NULL); dir[2] = 0; VectorNormalize(dir); //VectorSubtract(entinfo.origin, entinfo.lastvisorigin, dir); VectorSubtract(bs->origin, entinfo.origin, dir2); VectorNormalize(dir2); if (DotProduct(dir, dir2) > 0.7) { // back up BotSetupForMovement(bs); trap_BotMoveInDirection(bs->ms, dir2, 400, MOVE_WALK); } } } } } //check if the bot wants to crouch //don't crouch if crouched less than 5 seconds ago if (bs->attackcrouch_time < FloatTime() - 5) { croucher = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CROUCHER, 0, 1); if (random() < bs->thinktime * croucher) { bs->attackcrouch_time = FloatTime() + 5 + croucher * 15; } } //don't crouch when swimming if (trap_AAS_Swimming(bs->origin)) bs->attackcrouch_time = FloatTime() - 1; //if not arrived yet or arived some time ago if (bs->arrive_time < FloatTime() - 2) { //if not arrived yet if (!bs->arrive_time) { trap_EA_Gesture(bs->client); BotAI_BotInitialChat(bs, "accompany_arrive", EasyClientName(bs->teammate, netname, sizeof(netname)), NULL); trap_BotEnterChat(bs->cs, bs->teammate, CHAT_TELL); bs->arrive_time = FloatTime(); } //if the bot wants to crouch else if (bs->attackcrouch_time > FloatTime()) { trap_EA_Crouch(bs->client); } //else do some model taunts else if (random() < bs->thinktime * 0.05) { //do a gesture :) trap_EA_Gesture(bs->client); } } //if just arrived look at the companion if (bs->arrive_time > FloatTime() - 2) { VectorSubtract(entinfo.origin, bs->origin, dir); vectoangles(dir, bs->ideal_viewangles); bs->ideal_viewangles[2] *= 0.5; } //else look strategically around for enemies else if (random() < bs->thinktime * 0.8) { BotRoamGoal(bs, target); VectorSubtract(target, bs->origin, dir); vectoangles(dir, bs->ideal_viewangles); bs->ideal_viewangles[2] *= 0.5; } //check if the bot wants to go for air if (BotGoForAir(bs, bs->tfl, &bs->teamgoal, 400)) { trap_BotResetLastAvoidReach(bs->ms); //get the goal at the top of the stack //trap_BotGetTopGoal(bs->gs, &tmpgoal); //trap_BotGoalName(tmpgoal.number, buf, 144); //BotAI_Print(PRT_MESSAGE, "new nearby goal %s\n", buf); //time the bot gets to pick up the nearby goal item bs->nbg_time = FloatTime() + 8; AIEnter_Seek_NBG(bs, "BotLongTermGoal: go for air"); return qfalse; } // trap_BotResetAvoidReach(bs->ms); return qfalse; } } //if the entity information is valid (entity in PVS) if (entinfo.valid) { areanum = BotPointAreaNum(entinfo.origin); if (areanum && trap_AAS_AreaReachability(areanum)) { //update team goal bs->teamgoal.entitynum = bs->teammate; bs->teamgoal.areanum = areanum; VectorCopy(entinfo.origin, bs->teamgoal.origin); VectorSet(bs->teamgoal.mins, -8, -8, -8); VectorSet(bs->teamgoal.maxs, 8, 8, 8); } } //the goal the bot should go for memcpy(goal, &bs->teamgoal, sizeof(bot_goal_t)); //if the companion is NOT visible for too long if (bs->teammatevisible_time < FloatTime() - 60) { BotAI_BotInitialChat(bs, "accompany_cannotfind", EasyClientName(bs->teammate, netname, sizeof(netname)), NULL); trap_BotEnterChat(bs->cs, bs->teammate, CHAT_TELL); bs->ltgtype = 0; // just to make sure the bot won't spam this message bs->teammatevisible_time = FloatTime(); } return qtrue; } // if (bs->ltgtype == LTG_DEFENDKEYAREA) { if (trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, bs->teamgoal.areanum, TFL_DEFAULT) > bs->defendaway_range) { bs->defendaway_time = 0; } } //if defending a key area if (bs->ltgtype == LTG_DEFENDKEYAREA && !retreat && bs->defendaway_time < FloatTime()) { //check for bot typing status message if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { trap_BotGoalName(bs->teamgoal.number, buf, sizeof(buf)); BotAI_BotInitialChat(bs, "defend_start", buf, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); BotVoiceChatOnly(bs, -1, VOICECHAT_ONDEFENSE); bs->teammessage_time = 0; } //set the bot goal memcpy(goal, &bs->teamgoal, sizeof(bot_goal_t)); //stop after 2 minutes if (bs->teamgoal_time < FloatTime()) { trap_BotGoalName(bs->teamgoal.number, buf, sizeof(buf)); BotAI_BotInitialChat(bs, "defend_stop", buf, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); bs->ltgtype = 0; } //if very close... go away for some time VectorSubtract(goal->origin, bs->origin, dir); if (VectorLengthSquared(dir) < Square(70)) { trap_BotResetAvoidReach(bs->ms); bs->defendaway_time = FloatTime() + 3 + 3 * random(); if (BotHasPersistantPowerupAndWeapon(bs)) { bs->defendaway_range = 100; } else { bs->defendaway_range = 350; } } return qtrue; } //going to kill someone if (bs->ltgtype == LTG_KILL && !retreat) { //check for bot typing status message if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { EasyClientName(bs->teamgoal.entitynum, buf, sizeof(buf)); BotAI_BotInitialChat(bs, "kill_start", buf, NULL); trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL); bs->teammessage_time = 0; } // if (bs->lastkilledplayer == bs->teamgoal.entitynum) { EasyClientName(bs->teamgoal.entitynum, buf, sizeof(buf)); BotAI_BotInitialChat(bs, "kill_done", buf, NULL); trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL); bs->lastkilledplayer = -1; bs->ltgtype = 0; } // if (bs->teamgoal_time < FloatTime()) { bs->ltgtype = 0; } //just roam around return BotGetItemLongTermGoal(bs, tfl, goal); } //get an item if (bs->ltgtype == LTG_GETITEM && !retreat) { //check for bot typing status message if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { trap_BotGoalName(bs->teamgoal.number, buf, sizeof(buf)); BotAI_BotInitialChat(bs, "getitem_start", buf, NULL); trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL); BotVoiceChatOnly(bs, bs->decisionmaker, VOICECHAT_YES); trap_EA_Action(bs->client, ACTION_AFFIRMATIVE); bs->teammessage_time = 0; } //set the bot goal memcpy(goal, &bs->teamgoal, sizeof(bot_goal_t)); //stop after some time if (bs->teamgoal_time < FloatTime()) { bs->ltgtype = 0; } // if (trap_BotItemGoalInVisButNotVisible(bs->entitynum, bs->eye, bs->viewangles, goal)) { trap_BotGoalName(bs->teamgoal.number, buf, sizeof(buf)); BotAI_BotInitialChat(bs, "getitem_notthere", buf, NULL); trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL); bs->ltgtype = 0; } else if (BotReachedGoal(bs, goal)) { trap_BotGoalName(bs->teamgoal.number, buf, sizeof(buf)); BotAI_BotInitialChat(bs, "getitem_gotit", buf, NULL); trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL); bs->ltgtype = 0; } return qtrue; } //if camping somewhere if ((bs->ltgtype == LTG_CAMP || bs->ltgtype == LTG_CAMPORDER) && !retreat) { //check for bot typing status message if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { if (bs->ltgtype == LTG_CAMPORDER) { BotAI_BotInitialChat(bs, "camp_start", EasyClientName(bs->teammate, netname, sizeof(netname)), NULL); trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL); BotVoiceChatOnly(bs, bs->decisionmaker, VOICECHAT_YES); trap_EA_Action(bs->client, ACTION_AFFIRMATIVE); } bs->teammessage_time = 0; } //set the bot goal memcpy(goal, &bs->teamgoal, sizeof(bot_goal_t)); // if (bs->teamgoal_time < FloatTime()) { if (bs->ltgtype == LTG_CAMPORDER) { BotAI_BotInitialChat(bs, "camp_stop", NULL); trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL); } bs->ltgtype = 0; } //if really near the camp spot VectorSubtract(goal->origin, bs->origin, dir); if (VectorLengthSquared(dir) < Square(60)) { //if not arrived yet if (!bs->arrive_time) { if (bs->ltgtype == LTG_CAMPORDER) { BotAI_BotInitialChat(bs, "camp_arrive", EasyClientName(bs->teammate, netname, sizeof(netname)), NULL); trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL); BotVoiceChatOnly(bs, bs->decisionmaker, VOICECHAT_INPOSITION); } bs->arrive_time = FloatTime(); } //look strategically around for enemies if (random() < bs->thinktime * 0.8) { BotRoamGoal(bs, target); VectorSubtract(target, bs->origin, dir); vectoangles(dir, bs->ideal_viewangles); bs->ideal_viewangles[2] *= 0.5; } //check if the bot wants to crouch //don't crouch if crouched less than 5 seconds ago if (bs->attackcrouch_time < FloatTime() - 5) { croucher = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CROUCHER, 0, 1); if (random() < bs->thinktime * croucher) { bs->attackcrouch_time = FloatTime() + 5 + croucher * 15; } } //if the bot wants to crouch if (bs->attackcrouch_time > FloatTime()) { trap_EA_Crouch(bs->client); } //don't crouch when swimming if (trap_AAS_Swimming(bs->origin)) bs->attackcrouch_time = FloatTime() - 1; //make sure the bot is not gonna drown if (trap_PointContents(bs->eye,bs->entitynum) & (CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA)) { if (bs->ltgtype == LTG_CAMPORDER) { BotAI_BotInitialChat(bs, "camp_stop", NULL); trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL); // if (bs->lastgoal_ltgtype == LTG_CAMPORDER) { bs->lastgoal_ltgtype = 0; } } bs->ltgtype = 0; } // if (bs->camp_range > 0) { //FIXME: move around a bit } // trap_BotResetAvoidReach(bs->ms); return qfalse; } return qtrue; } //patrolling along several waypoints if (bs->ltgtype == LTG_PATROL && !retreat) { //check for bot typing status message if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { strcpy(buf, ""); for (wp = bs->patrolpoints; wp; wp = wp->next) { strcat(buf, wp->name); if (wp->next) strcat(buf, " to "); } BotAI_BotInitialChat(bs, "patrol_start", buf, NULL); trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL); BotVoiceChatOnly(bs, bs->decisionmaker, VOICECHAT_YES); trap_EA_Action(bs->client, ACTION_AFFIRMATIVE); bs->teammessage_time = 0; } // if (!bs->curpatrolpoint) { bs->ltgtype = 0; return qfalse; } //if the bot touches the current goal if (trap_BotTouchingGoal(bs->origin, &bs->curpatrolpoint->goal)) { if (bs->patrolflags & PATROL_BACK) { if (bs->curpatrolpoint->prev) { bs->curpatrolpoint = bs->curpatrolpoint->prev; } else { bs->curpatrolpoint = bs->curpatrolpoint->next; bs->patrolflags &= ~PATROL_BACK; } } else { if (bs->curpatrolpoint->next) { bs->curpatrolpoint = bs->curpatrolpoint->next; } else { bs->curpatrolpoint = bs->curpatrolpoint->prev; bs->patrolflags |= PATROL_BACK; } } } //stop after 5 minutes if (bs->teamgoal_time < FloatTime()) { BotAI_BotInitialChat(bs, "patrol_stop", NULL); trap_BotEnterChat(bs->cs, bs->decisionmaker, CHAT_TELL); bs->ltgtype = 0; } if (!bs->curpatrolpoint) { bs->ltgtype = 0; return qfalse; } memcpy(goal, &bs->curpatrolpoint->goal, sizeof(bot_goal_t)); return qtrue; } #ifdef CTF if (gametype == GT_CTF) { //if going for enemy flag if (bs->ltgtype == LTG_GETFLAG) { //check for bot typing status message if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { BotAI_BotInitialChat(bs, "captureflag_start", NULL); trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); BotVoiceChatOnly(bs, -1, VOICECHAT_ONGETFLAG); bs->teammessage_time = 0; } // switch(BotTeam(bs)) { case TEAM_RED: memcpy(goal, &ctf_blueflag, sizeof(bot_goal_t)); break; case TEAM_BLUE: memcpy(goal, &ctf_redflag, sizeof(bot_goal_t)); break; default: bs->ltgtype = 0; return qfalse; } //if touching the flag if (trap_BotTouchingGoal(bs->origin, goal)) { // make sure the bot knows the flag isn't there anymore switch(BotTeam(bs)) { case TEAM_RED: bs->blueflagstatus = 1; break; case TEAM_BLUE: bs->redflagstatus = 1; break; } bs->ltgtype = 0; } //stop after 3 minutes if (bs->teamgoal_time < FloatTime()) { bs->ltgtype = 0; } BotAlternateRoute(bs, goal); return qtrue; } //if rushing to the base if (bs->ltgtype == LTG_RUSHBASE && bs->rushbaseaway_time < FloatTime()) { switch(BotTeam(bs)) { case TEAM_RED: memcpy(goal, &ctf_redflag, sizeof(bot_goal_t)); break; case TEAM_BLUE: memcpy(goal, &ctf_blueflag, sizeof(bot_goal_t)); break; default: bs->ltgtype = 0; return qfalse; } //if not carrying the flag anymore if (!BotCTFCarryingFlag(bs)) bs->ltgtype = 0; //quit rushing after 2 minutes if (bs->teamgoal_time < FloatTime()) bs->ltgtype = 0; //if touching the base flag the bot should loose the enemy flag if (trap_BotTouchingGoal(bs->origin, goal)) { //if the bot is still carrying the enemy flag then the //base flag is gone, now just walk near the base a bit if (BotCTFCarryingFlag(bs)) { trap_BotResetAvoidReach(bs->ms); bs->rushbaseaway_time = FloatTime() + 5 + 10 * random(); //FIXME: add chat to tell the others to get back the flag } else { bs->ltgtype = 0; } } BotAlternateRoute(bs, goal); return qtrue; } //returning flag if (bs->ltgtype == LTG_RETURNFLAG) { //check for bot typing status message if (bs->teammessage_time && bs->teammessage_time < FloatTime()) { BotAI_BotInitialChat(bs, "returnflag_start", NULL); trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); BotVoiceChatOnly(bs, -1, VOICECHAT_ONRETURNFLAG); bs->teammessage_time = 0; } // switch(BotTeam(bs)) { case TEAM_RED: memcpy(goal, &ctf_blueflag, sizeof(bot_goal_t)); break; case TEAM_BLUE: memcpy(goal, &ctf_redflag, sizeof(bot_goal_t)); break; default: bs->ltgtype = 0; return qfalse; } //if touching the flag if (trap_BotTouchingGoal(bs->origin, goal)) bs->ltgtype = 0; //stop after 3 minutes if (bs->teamgoal_time < FloatTime()) { bs->ltgtype = 0; } BotAlternateRoute(bs, goal); return qtrue; } } #endif //CTF //normal goal stuff return BotGetItemLongTermGoal(bs, tfl, goal); } /* ================== BotLongTermGoal ================== */ int BotLongTermGoal(bot_state_t *bs, int tfl, int retreat, bot_goal_t *goal) { aas_entityinfo_t entinfo; char teammate[MAX_MESSAGE_SIZE]; float squaredist; int areanum; vec3_t dir; //FIXME: also have air long term goals? // //if the bot is leading someone and not retreating if (bs->lead_time > 0 && !retreat) { if (bs->lead_time < FloatTime()) { BotAI_BotInitialChat(bs, "lead_stop", EasyClientName(bs->lead_teammate, teammate, sizeof(teammate)), NULL); trap_BotEnterChat(bs->cs, bs->teammate, CHAT_TELL); bs->lead_time = 0; return BotGetLongTermGoal(bs, tfl, retreat, goal); } // if (bs->leadmessage_time < 0 && -bs->leadmessage_time < FloatTime()) { BotAI_BotInitialChat(bs, "followme", EasyClientName(bs->lead_teammate, teammate, sizeof(teammate)), NULL); trap_BotEnterChat(bs->cs, bs->teammate, CHAT_TELL); bs->leadmessage_time = FloatTime(); } //get entity information of the companion BotEntityInfo(bs->lead_teammate, &entinfo); // if (entinfo.valid) { areanum = BotPointAreaNum(entinfo.origin); if (areanum && trap_AAS_AreaReachability(areanum)) { //update team goal bs->lead_teamgoal.entitynum = bs->lead_teammate; bs->lead_teamgoal.areanum = areanum; VectorCopy(entinfo.origin, bs->lead_teamgoal.origin); VectorSet(bs->lead_teamgoal.mins, -8, -8, -8); VectorSet(bs->lead_teamgoal.maxs, 8, 8, 8); } } //if the team mate is visible if (BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, bs->lead_teammate)) { bs->leadvisible_time = FloatTime(); } //if the team mate is not visible for 1 seconds if (bs->leadvisible_time < FloatTime() - 1) { bs->leadbackup_time = FloatTime() + 2; } //distance towards the team mate VectorSubtract(bs->origin, bs->lead_teamgoal.origin, dir); squaredist = VectorLengthSquared(dir); //if backing up towards the team mate if (bs->leadbackup_time > FloatTime()) { if (bs->leadmessage_time < FloatTime() - 20) { BotAI_BotInitialChat(bs, "followme", EasyClientName(bs->lead_teammate, teammate, sizeof(teammate)), NULL); trap_BotEnterChat(bs->cs, bs->teammate, CHAT_TELL); bs->leadmessage_time = FloatTime(); } //if very close to the team mate if (squaredist < Square(100)) { bs->leadbackup_time = 0; } //the bot should go back to the team mate memcpy(goal, &bs->lead_teamgoal, sizeof(bot_goal_t)); return qtrue; } else { //if quite distant from the team mate if (squaredist > Square(500)) { if (bs->leadmessage_time < FloatTime() - 20) { BotAI_BotInitialChat(bs, "followme", EasyClientName(bs->lead_teammate, teammate, sizeof(teammate)), NULL); trap_BotEnterChat(bs->cs, bs->teammate, CHAT_TELL); bs->leadmessage_time = FloatTime(); } //look at the team mate VectorSubtract(entinfo.origin, bs->origin, dir); vectoangles(dir, bs->ideal_viewangles); bs->ideal_viewangles[2] *= 0.5; //just wait for the team mate return qfalse; } } } return BotGetLongTermGoal(bs, tfl, retreat, goal); } /* ================== AIEnter_Intermission ================== */ void AIEnter_Intermission(bot_state_t *bs, char *s) { BotRecordNodeSwitch(bs, "intermission", "", s); //reset the bot state BotResetState(bs); //check for end level chat if (BotChat_EndLevel(bs)) { trap_BotEnterChat(bs->cs, 0, bs->chatto); } bs->ainode = AINode_Intermission; } /* ================== AINode_Intermission ================== */ int AINode_Intermission(bot_state_t *bs) { //if the intermission ended if (!BotIntermission(bs)) { if (BotChat_StartLevel(bs)) { bs->stand_time = FloatTime() + BotChatTime(bs); } else { bs->stand_time = FloatTime() + 2; } AIEnter_Stand(bs, "intermission: chat"); } return qtrue; } /* ================== AIEnter_Observer ================== */ void AIEnter_Observer(bot_state_t *bs, char *s) { BotRecordNodeSwitch(bs, "observer", "", s); //reset the bot state BotResetState(bs); bs->ainode = AINode_Observer; } /* ================== AINode_Observer ================== */ int AINode_Observer(bot_state_t *bs) { //if the bot left observer mode if (!BotIsObserver(bs)) { AIEnter_Stand(bs, "observer: left observer"); } return qtrue; } /* ================== AIEnter_Stand ================== */ void AIEnter_Stand(bot_state_t *bs, char *s) { BotRecordNodeSwitch(bs, "stand", "", s); bs->standfindenemy_time = FloatTime() + 1; bs->ainode = AINode_Stand; } /* ================== AINode_Stand ================== */ int AINode_Stand(bot_state_t *bs) { //if the bot's health decreased if (bs->lastframe_health > bs->inventory[INVENTORY_HEALTH]) { if (BotChat_HitTalking(bs)) { bs->standfindenemy_time = FloatTime() + BotChatTime(bs) + 0.1; bs->stand_time = FloatTime() + BotChatTime(bs) + 0.1; } } if (bs->standfindenemy_time < FloatTime()) { if (BotFindEnemy(bs, -1)) { AIEnter_Battle_Fight(bs, "stand: found enemy"); return qfalse; } bs->standfindenemy_time = FloatTime() + 1; } // put up chat icon trap_EA_Talk(bs->client); // when done standing if (bs->stand_time < FloatTime()) { trap_BotEnterChat(bs->cs, 0, bs->chatto); AIEnter_Seek_LTG(bs, "stand: time out"); return qfalse; } // return qtrue; } /* ================== AIEnter_Respawn ================== */ void AIEnter_Respawn(bot_state_t *bs, char *s) { BotRecordNodeSwitch(bs, "respawn", "", s); //reset some states trap_BotResetMoveState(bs->ms); trap_BotResetGoalState(bs->gs); trap_BotResetAvoidGoals(bs->gs); trap_BotResetAvoidReach(bs->ms); //if the bot wants to chat if (BotChat_Death(bs)) { bs->respawn_time = FloatTime() + BotChatTime(bs); bs->respawnchat_time = FloatTime(); } else { bs->respawn_time = FloatTime() + 1 + random(); bs->respawnchat_time = 0; } //set respawn state bs->respawn_wait = qfalse; bs->ainode = AINode_Respawn; } /* ================== AINode_Respawn ================== */ int AINode_Respawn(bot_state_t *bs) { // if waiting for the actual respawn if (bs->respawn_wait) { if (!BotIsDead(bs)) { AIEnter_Seek_LTG(bs, "respawn: respawned"); } else { trap_EA_Respawn(bs->client); } } else if (bs->respawn_time < FloatTime()) { // wait until respawned bs->respawn_wait = qtrue; // elementary action respawn trap_EA_Respawn(bs->client); // if (bs->respawnchat_time) { trap_BotEnterChat(bs->cs, 0, bs->chatto); bs->enemy = -1; } } if (bs->respawnchat_time && bs->respawnchat_time < FloatTime() - 0.5) { trap_EA_Talk(bs->client); } // return qtrue; } /* ================== BotSelectActivateWeapon ================== */ int BotSelectActivateWeapon(bot_state_t *bs) { // if (bs->inventory[INVENTORY_MACHINEGUN] > 0 && bs->inventory[INVENTORY_BULLETS] > 0) return WEAPONINDEX_MACHINEGUN; else if (bs->inventory[INVENTORY_SHOTGUN] > 0 && bs->inventory[INVENTORY_SHELLS] > 0) return WEAPONINDEX_SHOTGUN; else if (bs->inventory[INVENTORY_PLASMAGUN] > 0 && bs->inventory[INVENTORY_CELLS] > 0) return WEAPONINDEX_PLASMAGUN; else if (bs->inventory[INVENTORY_LIGHTNING] > 0 && bs->inventory[INVENTORY_LIGHTNINGAMMO] > 0) return WEAPONINDEX_LIGHTNING; else if (bs->inventory[INVENTORY_RAILGUN] > 0 && bs->inventory[INVENTORY_SLUGS] > 0) return WEAPONINDEX_RAILGUN; else if (bs->inventory[INVENTORY_ROCKETLAUNCHER] > 0 && bs->inventory[INVENTORY_ROCKETS] > 0) return WEAPONINDEX_ROCKET_LAUNCHER; else if (bs->inventory[INVENTORY_BFG10K] > 0 && bs->inventory[INVENTORY_BFGAMMO] > 0) return WEAPONINDEX_BFG; else { return -1; } } /* ================== BotClearPath try to deactivate obstacles like proximity mines on the bot's path ================== */ void BotClearPath(bot_state_t *bs, bot_moveresult_t *moveresult) { int i, bestmine; float dist, bestdist; vec3_t target, dir; bsp_trace_t bsptrace; entityState_t state; // if there is a dead body wearing kamikze nearby if (bs->kamikazebody) { // if the bot's view angles and weapon are not used for movement if ( !(moveresult->flags & (MOVERESULT_MOVEMENTVIEW | MOVERESULT_MOVEMENTWEAPON)) ) { // BotAI_GetEntityState(bs->kamikazebody, &state); VectorCopy(state.pos.trBase, target); target[2] += 8; VectorSubtract(target, bs->eye, dir); vectoangles(dir, moveresult->ideal_viewangles); // moveresult->weapon = BotSelectActivateWeapon(bs); if (moveresult->weapon == -1) { // FIXME: run away! moveresult->weapon = 0; } if (moveresult->weapon) { // moveresult->flags |= MOVERESULT_MOVEMENTWEAPON | MOVERESULT_MOVEMENTVIEW; // if holding the right weapon if (bs->cur_ps.weapon == moveresult->weapon) { // if the bot is pretty close with it's aim if (InFieldOfVision(bs->viewangles, 20, moveresult->ideal_viewangles)) { // BotAI_Trace(&bsptrace, bs->eye, NULL, NULL, target, bs->entitynum, MASK_SHOT); // if the mine is visible from the current position if (bsptrace.fraction >= 1.0 || bsptrace.ent == state.number) { // shoot at the mine trap_EA_Attack(bs->client); } } } } } } if (moveresult->flags & MOVERESULT_BLOCKEDBYAVOIDSPOT) { bs->blockedbyavoidspot_time = FloatTime() + 5; } // if blocked by an avoid spot and the view angles and weapon are used for movement if (bs->blockedbyavoidspot_time > FloatTime() && !(moveresult->flags & (MOVERESULT_MOVEMENTVIEW | MOVERESULT_MOVEMENTWEAPON)) ) { bestdist = 300; bestmine = -1; for (i = 0; i < bs->numproxmines; i++) { BotAI_GetEntityState(bs->proxmines[i], &state); VectorSubtract(state.pos.trBase, bs->origin, dir); dist = VectorLength(dir); if (dist < bestdist) { bestdist = dist; bestmine = i; } } if (bestmine != -1) { // // state->generic1 == TEAM_RED || state->generic1 == TEAM_BLUE // // deactivate prox mines in the bot's path by shooting // rockets or plasma cells etc. at them BotAI_GetEntityState(bs->proxmines[bestmine], &state); VectorCopy(state.pos.trBase, target); target[2] += 2; VectorSubtract(target, bs->eye, dir); vectoangles(dir, moveresult->ideal_viewangles); // if the bot has a weapon that does splash damage if (bs->inventory[INVENTORY_PLASMAGUN] > 0 && bs->inventory[INVENTORY_CELLS] > 0) moveresult->weapon = WEAPONINDEX_PLASMAGUN; else if (bs->inventory[INVENTORY_ROCKETLAUNCHER] > 0 && bs->inventory[INVENTORY_ROCKETS] > 0) moveresult->weapon = WEAPONINDEX_ROCKET_LAUNCHER; else if (bs->inventory[INVENTORY_BFG10K] > 0 && bs->inventory[INVENTORY_BFGAMMO] > 0) moveresult->weapon = WEAPONINDEX_BFG; else { moveresult->weapon = 0; } if (moveresult->weapon) { // moveresult->flags |= MOVERESULT_MOVEMENTWEAPON | MOVERESULT_MOVEMENTVIEW; // if holding the right weapon if (bs->cur_ps.weapon == moveresult->weapon) { // if the bot is pretty close with it's aim if (InFieldOfVision(bs->viewangles, 20, moveresult->ideal_viewangles)) { // BotAI_Trace(&bsptrace, bs->eye, NULL, NULL, target, bs->entitynum, MASK_SHOT); // if the mine is visible from the current position if (bsptrace.fraction >= 1.0 || bsptrace.ent == state.number) { // shoot at the mine trap_EA_Attack(bs->client); } } } } } } } /* ================== AIEnter_Seek_ActivateEntity ================== */ void AIEnter_Seek_ActivateEntity(bot_state_t *bs, char *s) { BotRecordNodeSwitch(bs, "activate entity", "", s); bs->ainode = AINode_Seek_ActivateEntity; } /* ================== AINode_Seek_Activate_Entity ================== */ int AINode_Seek_ActivateEntity(bot_state_t *bs) { bot_goal_t *goal; vec3_t target, dir, ideal_viewangles; bot_moveresult_t moveresult; int targetvisible; bsp_trace_t bsptrace; aas_entityinfo_t entinfo; if (BotIsObserver(bs)) { BotClearActivateGoalStack(bs); AIEnter_Observer(bs, "active entity: observer"); return qfalse; } //if in the intermission if (BotIntermission(bs)) { BotClearActivateGoalStack(bs); AIEnter_Intermission(bs, "activate entity: intermission"); return qfalse; } //respawn if dead if (BotIsDead(bs)) { BotClearActivateGoalStack(bs); AIEnter_Respawn(bs, "activate entity: bot dead"); return qfalse; } // bs->tfl = TFL_DEFAULT; if (bot_grapple.integer) bs->tfl |= TFL_GRAPPLEHOOK; // if in lava or slime the bot should be able to get out if (BotInLavaOrSlime(bs)) bs->tfl |= TFL_LAVA|TFL_SLIME; // map specific code BotMapScripts(bs); // no enemy bs->enemy = -1; // if the bot has no activate goal if (!bs->activatestack) { BotClearActivateGoalStack(bs); AIEnter_Seek_NBG(bs, "activate entity: no goal"); return qfalse; } // goal = &bs->activatestack->goal; // initialize target being visible to false targetvisible = qfalse; // if the bot has to shoot at a target to activate something if (bs->activatestack->shoot) { // BotAI_Trace(&bsptrace, bs->eye, NULL, NULL, bs->activatestack->target, bs->entitynum, MASK_SHOT); // if the shootable entity is visible from the current position if (bsptrace.fraction >= 1.0 || bsptrace.ent == goal->entitynum) { targetvisible = qtrue; // if holding the right weapon if (bs->cur_ps.weapon == bs->activatestack->weapon) { VectorSubtract(bs->activatestack->target, bs->eye, dir); vectoangles(dir, ideal_viewangles); // if the bot is pretty close with it's aim if (InFieldOfVision(bs->viewangles, 20, ideal_viewangles)) { trap_EA_Attack(bs->client); } } } } // if the shoot target is visible if (targetvisible) { // get the entity info of the entity the bot is shooting at BotEntityInfo(goal->entitynum, &entinfo); // if the entity the bot shoots at moved if (!VectorCompare(bs->activatestack->origin, entinfo.origin)) { #ifdef DEBUG BotAI_Print(PRT_MESSAGE, "hit shootable button or trigger\n"); #endif //DEBUG bs->activatestack->time = 0; } // if the activate goal has been activated or the bot takes too long if (bs->activatestack->time < FloatTime()) { BotPopFromActivateGoalStack(bs); // if there are more activate goals on the stack if (bs->activatestack) { bs->activatestack->time = FloatTime() + 10; return qfalse; } AIEnter_Seek_NBG(bs, "activate entity: time out"); return qfalse; } memset(&moveresult, 0, sizeof(bot_moveresult_t)); } else { // if the bot has no goal if (!goal) { bs->activatestack->time = 0; } // if the bot does not have a shoot goal else if (!bs->activatestack->shoot) { //if the bot touches the current goal if (trap_BotTouchingGoal(bs->origin, goal)) { #ifdef DEBUG BotAI_Print(PRT_MESSAGE, "touched button or trigger\n"); #endif //DEBUG bs->activatestack->time = 0; } } // if the activate goal has been activated or the bot takes too long if (bs->activatestack->time < FloatTime()) { BotPopFromActivateGoalStack(bs); // if there are more activate goals on the stack if (bs->activatestack) { bs->activatestack->time = FloatTime() + 10; return qfalse; } AIEnter_Seek_NBG(bs, "activate entity: activated"); return qfalse; } //predict obstacles if (BotAIPredictObstacles(bs, goal)) return qfalse; //initialize the movement state BotSetupForMovement(bs); //move towards the goal trap_BotMoveToGoal(&moveresult, bs->ms, goal, bs->tfl); //if the movement failed if (moveresult.failure) { //reset the avoid reach, otherwise bot is stuck in current area trap_BotResetAvoidReach(bs->ms); // bs->activatestack->time = 0; } //check if the bot is blocked BotAIBlocked(bs, &moveresult, qtrue); } // BotClearPath(bs, &moveresult); // if the bot has to shoot to activate if (bs->activatestack->shoot) { // if the view angles aren't yet used for the movement if (!(moveresult.flags & MOVERESULT_MOVEMENTVIEW)) { VectorSubtract(bs->activatestack->target, bs->eye, dir); vectoangles(dir, moveresult.ideal_viewangles); moveresult.flags |= MOVERESULT_MOVEMENTVIEW; } // if there's no weapon yet used for the movement if (!(moveresult.flags & MOVERESULT_MOVEMENTWEAPON)) { moveresult.flags |= MOVERESULT_MOVEMENTWEAPON; // bs->activatestack->weapon = BotSelectActivateWeapon(bs); if (bs->activatestack->weapon == -1) { //FIXME: find a decent weapon first bs->activatestack->weapon = 0; } moveresult.weapon = bs->activatestack->weapon; } } // if the ideal view angles are set for movement if (moveresult.flags & (MOVERESULT_MOVEMENTVIEWSET|MOVERESULT_MOVEMENTVIEW|MOVERESULT_SWIMVIEW)) { VectorCopy(moveresult.ideal_viewangles, bs->ideal_viewangles); } // if waiting for something else if (moveresult.flags & MOVERESULT_WAITING) { if (random() < bs->thinktime * 0.8) { BotRoamGoal(bs, target); VectorSubtract(target, bs->origin, dir); vectoangles(dir, bs->ideal_viewangles); bs->ideal_viewangles[2] *= 0.5; } } else if (!(bs->flags & BFL_IDEALVIEWSET)) { if (trap_BotMovementViewTarget(bs->ms, goal, bs->tfl, 300, target)) { VectorSubtract(target, bs->origin, dir); vectoangles(dir, bs->ideal_viewangles); } else { vectoangles(moveresult.movedir, bs->ideal_viewangles); } bs->ideal_viewangles[2] *= 0.5; } // if the weapon is used for the bot movement if (moveresult.flags & MOVERESULT_MOVEMENTWEAPON) bs->weaponnum = moveresult.weapon; // if there is an enemy if (BotFindEnemy(bs, -1)) { if (BotWantsToRetreat(bs)) { //keep the current long term goal and retreat AIEnter_Battle_NBG(bs, "activate entity: found enemy"); } else { trap_BotResetLastAvoidReach(bs->ms); //empty the goal stack trap_BotEmptyGoalStack(bs->gs); //go fight AIEnter_Battle_Fight(bs, "activate entity: found enemy"); } BotClearActivateGoalStack(bs); } return qtrue; } /* ================== AIEnter_Seek_NBG ================== */ void AIEnter_Seek_NBG(bot_state_t *bs, char *s) { bot_goal_t goal; char buf[144]; if (trap_BotGetTopGoal(bs->gs, &goal)) { trap_BotGoalName(goal.number, buf, 144); BotRecordNodeSwitch(bs, "seek NBG", buf, s); } else { BotRecordNodeSwitch(bs, "seek NBG", "no goal", s); } bs->ainode = AINode_Seek_NBG; } /* ================== AINode_Seek_NBG ================== */ int AINode_Seek_NBG(bot_state_t *bs) { bot_goal_t goal; vec3_t target, dir; bot_moveresult_t moveresult; if (BotIsObserver(bs)) { AIEnter_Observer(bs, "seek nbg: observer"); return qfalse; } //if in the intermission if (BotIntermission(bs)) { AIEnter_Intermission(bs, "seek nbg: intermision"); return qfalse; } //respawn if dead if (BotIsDead(bs)) { AIEnter_Respawn(bs, "seek nbg: bot dead"); return qfalse; } // bs->tfl = TFL_DEFAULT; if (bot_grapple.integer) bs->tfl |= TFL_GRAPPLEHOOK; //if in lava or slime the bot should be able to get out if (BotInLavaOrSlime(bs)) bs->tfl |= TFL_LAVA|TFL_SLIME; // if (BotCanAndWantsToRocketJump(bs)) { bs->tfl |= TFL_ROCKETJUMP; } //map specific code BotMapScripts(bs); //no enemy bs->enemy = -1; //if the bot has no goal if (!trap_BotGetTopGoal(bs->gs, &goal)) bs->nbg_time = 0; //if the bot touches the current goal else if (BotReachedGoal(bs, &goal)) { BotChooseWeapon(bs); bs->nbg_time = 0; } // if (bs->nbg_time < FloatTime()) { //pop the current goal from the stack trap_BotPopGoal(bs->gs); //check for new nearby items right away //NOTE: we canNOT reset the check_time to zero because it would create an endless loop of node switches bs->check_time = FloatTime() + 0.05; //go back to seek ltg AIEnter_Seek_LTG(bs, "seek nbg: time out"); return qfalse; } //predict obstacles if (BotAIPredictObstacles(bs, &goal)) return qfalse; //initialize the movement state BotSetupForMovement(bs); //move towards the goal trap_BotMoveToGoal(&moveresult, bs->ms, &goal, bs->tfl); //if the movement failed if (moveresult.failure) { //reset the avoid reach, otherwise bot is stuck in current area trap_BotResetAvoidReach(bs->ms); bs->nbg_time = 0; } //check if the bot is blocked BotAIBlocked(bs, &moveresult, qtrue); // BotClearPath(bs, &moveresult); //if the viewangles are used for the movement if (moveresult.flags & (MOVERESULT_MOVEMENTVIEWSET|MOVERESULT_MOVEMENTVIEW|MOVERESULT_SWIMVIEW)) { VectorCopy(moveresult.ideal_viewangles, bs->ideal_viewangles); } //if waiting for something else if (moveresult.flags & MOVERESULT_WAITING) { if (random() < bs->thinktime * 0.8) { BotRoamGoal(bs, target); VectorSubtract(target, bs->origin, dir); vectoangles(dir, bs->ideal_viewangles); bs->ideal_viewangles[2] *= 0.5; } } else if (!(bs->flags & BFL_IDEALVIEWSET)) { if (!trap_BotGetSecondGoal(bs->gs, &goal)) trap_BotGetTopGoal(bs->gs, &goal); if (trap_BotMovementViewTarget(bs->ms, &goal, bs->tfl, 300, target)) { VectorSubtract(target, bs->origin, dir); vectoangles(dir, bs->ideal_viewangles); } //FIXME: look at cluster portals? else vectoangles(moveresult.movedir, bs->ideal_viewangles); bs->ideal_viewangles[2] *= 0.5; } //if the weapon is used for the bot movement if (moveresult.flags & MOVERESULT_MOVEMENTWEAPON) bs->weaponnum = moveresult.weapon; //if there is an enemy if (BotFindEnemy(bs, -1)) { if (BotWantsToRetreat(bs)) { //keep the current long term goal and retreat AIEnter_Battle_NBG(bs, "seek nbg: found enemy"); } else { trap_BotResetLastAvoidReach(bs->ms); //empty the goal stack trap_BotEmptyGoalStack(bs->gs); //go fight AIEnter_Battle_Fight(bs, "seek nbg: found enemy"); } } return qtrue; } /* ================== AIEnter_Seek_LTG ================== */ void AIEnter_Seek_LTG(bot_state_t *bs, char *s) { bot_goal_t goal; char buf[144]; if (trap_BotGetTopGoal(bs->gs, &goal)) { trap_BotGoalName(goal.number, buf, 144); BotRecordNodeSwitch(bs, "seek LTG", buf, s); } else { BotRecordNodeSwitch(bs, "seek LTG", "no goal", s); } bs->ainode = AINode_Seek_LTG; } /* ================== AINode_Seek_LTG ================== */ int AINode_Seek_LTG(bot_state_t *bs) { bot_goal_t goal; vec3_t target, dir; bot_moveresult_t moveresult; int range; //char buf[128]; //bot_goal_t tmpgoal; if (BotIsObserver(bs)) { AIEnter_Observer(bs, "seek ltg: observer"); return qfalse; } //if in the intermission if (BotIntermission(bs)) { AIEnter_Intermission(bs, "seek ltg: intermission"); return qfalse; } //respawn if dead if (BotIsDead(bs)) { AIEnter_Respawn(bs, "seek ltg: bot dead"); return qfalse; } // if (BotChat_Random(bs)) { bs->stand_time = FloatTime() + BotChatTime(bs); AIEnter_Stand(bs, "seek ltg: random chat"); return qfalse; } // bs->tfl = TFL_DEFAULT; if (bot_grapple.integer) bs->tfl |= TFL_GRAPPLEHOOK; //if in lava or slime the bot should be able to get out if (BotInLavaOrSlime(bs)) bs->tfl |= TFL_LAVA|TFL_SLIME; // if (BotCanAndWantsToRocketJump(bs)) { bs->tfl |= TFL_ROCKETJUMP; } //map specific code BotMapScripts(bs); //no enemy bs->enemy = -1; // if (bs->killedenemy_time > FloatTime() - 2) { if (random() < bs->thinktime * 1) { trap_EA_Gesture(bs->client); } } //if there is an enemy if (BotFindEnemy(bs, -1)) { if (BotWantsToRetreat(bs)) { //keep the current long term goal and retreat AIEnter_Battle_Retreat(bs, "seek ltg: found enemy"); return qfalse; } else { trap_BotResetLastAvoidReach(bs->ms); //empty the goal stack trap_BotEmptyGoalStack(bs->gs); //go fight AIEnter_Battle_Fight(bs, "seek ltg: found enemy"); return qfalse; } } // BotTeamGoals(bs, qfalse); //get the current long term goal if (!BotLongTermGoal(bs, bs->tfl, qfalse, &goal)) { return qtrue; } //check for nearby goals periodicly if (bs->check_time < FloatTime()) { bs->check_time = FloatTime() + 0.5; //check if the bot wants to camp BotWantsToCamp(bs); // if (bs->ltgtype == LTG_DEFENDKEYAREA) range = 400; else range = 150; // #ifdef CTF if (gametype == GT_CTF) { //if carrying a flag the bot shouldn't be distracted too much if (BotCTFCarryingFlag(bs)) range = 50; } #endif //CTF // if (BotNearbyGoal(bs, bs->tfl, &goal, range)) { trap_BotResetLastAvoidReach(bs->ms); //get the goal at the top of the stack //trap_BotGetTopGoal(bs->gs, &tmpgoal); //trap_BotGoalName(tmpgoal.number, buf, 144); //BotAI_Print(PRT_MESSAGE, "new nearby goal %s\n", buf); //time the bot gets to pick up the nearby goal item bs->nbg_time = FloatTime() + 4 + range * 0.01; AIEnter_Seek_NBG(bs, "ltg seek: nbg"); return qfalse; } } //predict obstacles if (BotAIPredictObstacles(bs, &goal)) return qfalse; //initialize the movement state BotSetupForMovement(bs); //move towards the goal trap_BotMoveToGoal(&moveresult, bs->ms, &goal, bs->tfl); //if the movement failed if (moveresult.failure) { //reset the avoid reach, otherwise bot is stuck in current area trap_BotResetAvoidReach(bs->ms); //BotAI_Print(PRT_MESSAGE, "movement failure %d\n", moveresult.traveltype); bs->ltg_time = 0; } // BotAIBlocked(bs, &moveresult, qtrue); // BotClearPath(bs, &moveresult); //if the viewangles are used for the movement if (moveresult.flags & (MOVERESULT_MOVEMENTVIEWSET|MOVERESULT_MOVEMENTVIEW|MOVERESULT_SWIMVIEW)) { VectorCopy(moveresult.ideal_viewangles, bs->ideal_viewangles); } //if waiting for something else if (moveresult.flags & MOVERESULT_WAITING) { if (random() < bs->thinktime * 0.8) { BotRoamGoal(bs, target); VectorSubtract(target, bs->origin, dir); vectoangles(dir, bs->ideal_viewangles); bs->ideal_viewangles[2] *= 0.5; } } else if (!(bs->flags & BFL_IDEALVIEWSET)) { if (trap_BotMovementViewTarget(bs->ms, &goal, bs->tfl, 300, target)) { VectorSubtract(target, bs->origin, dir); vectoangles(dir, bs->ideal_viewangles); } //FIXME: look at cluster portals? else if (VectorLengthSquared(moveresult.movedir)) { vectoangles(moveresult.movedir, bs->ideal_viewangles); } else if (random() < bs->thinktime * 0.8) { BotRoamGoal(bs, target); VectorSubtract(target, bs->origin, dir); vectoangles(dir, bs->ideal_viewangles); bs->ideal_viewangles[2] *= 0.5; } bs->ideal_viewangles[2] *= 0.5; } //if the weapon is used for the bot movement if (moveresult.flags & MOVERESULT_MOVEMENTWEAPON) bs->weaponnum = moveresult.weapon; // return qtrue; } /* ================== AIEnter_Battle_Fight ================== */ void AIEnter_Battle_Fight(bot_state_t *bs, char *s) { BotRecordNodeSwitch(bs, "battle fight", "", s); trap_BotResetLastAvoidReach(bs->ms); bs->ainode = AINode_Battle_Fight; } /* ================== AIEnter_Battle_Fight ================== */ void AIEnter_Battle_SuicidalFight(bot_state_t *bs, char *s) { BotRecordNodeSwitch(bs, "battle fight", "", s); trap_BotResetLastAvoidReach(bs->ms); bs->ainode = AINode_Battle_Fight; bs->flags |= BFL_FIGHTSUICIDAL; } /* ================== AINode_Battle_Fight ================== */ int AINode_Battle_Fight(bot_state_t *bs) { int areanum; vec3_t target; aas_entityinfo_t entinfo; bot_moveresult_t moveresult; if (BotIsObserver(bs)) { AIEnter_Observer(bs, "battle fight: observer"); return qfalse; } //if in the intermission if (BotIntermission(bs)) { AIEnter_Intermission(bs, "battle fight: intermission"); return qfalse; } //respawn if dead if (BotIsDead(bs)) { AIEnter_Respawn(bs, "battle fight: bot dead"); return qfalse; } //if there is another better enemy if (BotFindEnemy(bs, bs->enemy)) { #ifdef DEBUG BotAI_Print(PRT_MESSAGE, "found new better enemy\n"); #endif } //if no enemy if (bs->enemy < 0) { AIEnter_Seek_LTG(bs, "battle fight: no enemy"); return qfalse; } // BotEntityInfo(bs->enemy, &entinfo); //if the enemy is dead if (bs->enemydeath_time) { if (bs->enemydeath_time < FloatTime() - 1.0) { bs->enemydeath_time = 0; if (bs->enemysuicide) { BotChat_EnemySuicide(bs); } if (bs->lastkilledplayer == bs->enemy && BotChat_Kill(bs)) { bs->stand_time = FloatTime() + BotChatTime(bs); AIEnter_Stand(bs, "battle fight: enemy dead"); } else { bs->ltg_time = 0; AIEnter_Seek_LTG(bs, "battle fight: enemy dead"); } return qfalse; } } else { if (EntityIsDead(&entinfo)) { bs->enemydeath_time = FloatTime(); } } //if the enemy is invisible and not shooting the bot looses track easily if (EntityIsInvisible(&entinfo) && !EntityIsShooting(&entinfo)) { if (random() < 0.2) { AIEnter_Seek_LTG(bs, "battle fight: invisible"); return qfalse; } } // VectorCopy(entinfo.origin, target); // if not a player enemy if (bs->enemy >= MAX_CLIENTS) { } //update the reachability area and origin if possible areanum = BotPointAreaNum(target); if (areanum && trap_AAS_AreaReachability(areanum)) { VectorCopy(target, bs->lastenemyorigin); bs->lastenemyareanum = areanum; } //update the attack inventory values BotUpdateBattleInventory(bs, bs->enemy); //if the bot's health decreased if (bs->lastframe_health > bs->inventory[INVENTORY_HEALTH]) { if (BotChat_HitNoDeath(bs)) { bs->stand_time = FloatTime() + BotChatTime(bs); AIEnter_Stand(bs, "battle fight: chat health decreased"); return qfalse; } } //if the bot hit someone if (bs->cur_ps.persistant[PERS_HITS] > bs->lasthitcount) { if (BotChat_HitNoKill(bs)) { bs->stand_time = FloatTime() + BotChatTime(bs); AIEnter_Stand(bs, "battle fight: chat hit someone"); return qfalse; } } //if the enemy is not visible if (!BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy)) { if (BotWantsToChase(bs)) { AIEnter_Battle_Chase(bs, "battle fight: enemy out of sight"); return qfalse; } else { AIEnter_Seek_LTG(bs, "battle fight: enemy out of sight"); return qfalse; } } //use holdable items BotBattleUseItems(bs); // bs->tfl = TFL_DEFAULT; if (bot_grapple.integer) bs->tfl |= TFL_GRAPPLEHOOK; //if in lava or slime the bot should be able to get out if (BotInLavaOrSlime(bs)) bs->tfl |= TFL_LAVA|TFL_SLIME; // if (BotCanAndWantsToRocketJump(bs)) { bs->tfl |= TFL_ROCKETJUMP; } //choose the best weapon to fight with BotChooseWeapon(bs); //do attack movements moveresult = BotAttackMove(bs, bs->tfl); //if the movement failed if (moveresult.failure) { //reset the avoid reach, otherwise bot is stuck in current area trap_BotResetAvoidReach(bs->ms); //BotAI_Print(PRT_MESSAGE, "movement failure %d\n", moveresult.traveltype); bs->ltg_time = 0; } // BotAIBlocked(bs, &moveresult, qfalse); //aim at the enemy BotAimAtEnemy(bs); //attack the enemy if possible BotCheckAttack(bs); //if the bot wants to retreat if (!(bs->flags & BFL_FIGHTSUICIDAL)) { if (BotWantsToRetreat(bs)) { AIEnter_Battle_Retreat(bs, "battle fight: wants to retreat"); return qtrue; } } return qtrue; } /* ================== AIEnter_Battle_Chase ================== */ void AIEnter_Battle_Chase(bot_state_t *bs, char *s) { BotRecordNodeSwitch(bs, "battle chase", "", s); bs->chase_time = FloatTime(); bs->ainode = AINode_Battle_Chase; } /* ================== AINode_Battle_Chase ================== */ int AINode_Battle_Chase(bot_state_t *bs) { bot_goal_t goal; vec3_t target, dir; bot_moveresult_t moveresult; float range; if (BotIsObserver(bs)) { AIEnter_Observer(bs, "battle chase: observer"); return qfalse; } //if in the intermission if (BotIntermission(bs)) { AIEnter_Intermission(bs, "battle chase: intermission"); return qfalse; } //respawn if dead if (BotIsDead(bs)) { AIEnter_Respawn(bs, "battle chase: bot dead"); return qfalse; } //if no enemy if (bs->enemy < 0) { AIEnter_Seek_LTG(bs, "battle chase: no enemy"); return qfalse; } //if the enemy is visible if (BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy)) { AIEnter_Battle_Fight(bs, "battle chase"); return qfalse; } //if there is another enemy if (BotFindEnemy(bs, -1)) { AIEnter_Battle_Fight(bs, "battle chase: better enemy"); return qfalse; } //there is no last enemy area if (!bs->lastenemyareanum) { AIEnter_Seek_LTG(bs, "battle chase: no enemy area"); return qfalse; } // bs->tfl = TFL_DEFAULT; if (bot_grapple.integer) bs->tfl |= TFL_GRAPPLEHOOK; //if in lava or slime the bot should be able to get out if (BotInLavaOrSlime(bs)) bs->tfl |= TFL_LAVA|TFL_SLIME; // if (BotCanAndWantsToRocketJump(bs)) { bs->tfl |= TFL_ROCKETJUMP; } //map specific code BotMapScripts(bs); //create the chase goal goal.entitynum = bs->enemy; goal.areanum = bs->lastenemyareanum; VectorCopy(bs->lastenemyorigin, goal.origin); VectorSet(goal.mins, -8, -8, -8); VectorSet(goal.maxs, 8, 8, 8); //if the last seen enemy spot is reached the enemy could not be found if (trap_BotTouchingGoal(bs->origin, &goal)) bs->chase_time = 0; //if there's no chase time left if (!bs->chase_time || bs->chase_time < FloatTime() - 10) { AIEnter_Seek_LTG(bs, "battle chase: time out"); return qfalse; } //check for nearby goals periodicly if (bs->check_time < FloatTime()) { bs->check_time = FloatTime() + 1; range = 150; // if (BotNearbyGoal(bs, bs->tfl, &goal, range)) { //the bot gets 5 seconds to pick up the nearby goal item bs->nbg_time = FloatTime() + 0.1 * range + 1; trap_BotResetLastAvoidReach(bs->ms); AIEnter_Battle_NBG(bs, "battle chase: nbg"); return qfalse; } } // BotUpdateBattleInventory(bs, bs->enemy); //initialize the movement state BotSetupForMovement(bs); //move towards the goal trap_BotMoveToGoal(&moveresult, bs->ms, &goal, bs->tfl); //if the movement failed if (moveresult.failure) { //reset the avoid reach, otherwise bot is stuck in current area trap_BotResetAvoidReach(bs->ms); //BotAI_Print(PRT_MESSAGE, "movement failure %d\n", moveresult.traveltype); bs->ltg_time = 0; } // BotAIBlocked(bs, &moveresult, qfalse); // if (moveresult.flags & (MOVERESULT_MOVEMENTVIEWSET|MOVERESULT_MOVEMENTVIEW|MOVERESULT_SWIMVIEW)) { VectorCopy(moveresult.ideal_viewangles, bs->ideal_viewangles); } else if (!(bs->flags & BFL_IDEALVIEWSET)) { if (bs->chase_time > FloatTime() - 2) { BotAimAtEnemy(bs); } else { if (trap_BotMovementViewTarget(bs->ms, &goal, bs->tfl, 300, target)) { VectorSubtract(target, bs->origin, dir); vectoangles(dir, bs->ideal_viewangles); } else { vectoangles(moveresult.movedir, bs->ideal_viewangles); } } bs->ideal_viewangles[2] *= 0.5; } //if the weapon is used for the bot movement if (moveresult.flags & MOVERESULT_MOVEMENTWEAPON) bs->weaponnum = moveresult.weapon; //if the bot is in the area the enemy was last seen in if (bs->areanum == bs->lastenemyareanum) bs->chase_time = 0; //if the bot wants to retreat (the bot could have been damage during the chase) if (BotWantsToRetreat(bs)) { AIEnter_Battle_Retreat(bs, "battle chase: wants to retreat"); return qtrue; } return qtrue; } /* ================== AIEnter_Battle_Retreat ================== */ void AIEnter_Battle_Retreat(bot_state_t *bs, char *s) { BotRecordNodeSwitch(bs, "battle retreat", "", s); bs->ainode = AINode_Battle_Retreat; } /* ================== AINode_Battle_Retreat ================== */ int AINode_Battle_Retreat(bot_state_t *bs) { bot_goal_t goal; aas_entityinfo_t entinfo; bot_moveresult_t moveresult; vec3_t target, dir; float attack_skill, range; int areanum; if (BotIsObserver(bs)) { AIEnter_Observer(bs, "battle retreat: observer"); return qfalse; } //if in the intermission if (BotIntermission(bs)) { AIEnter_Intermission(bs, "battle retreat: intermission"); return qfalse; } //respawn if dead if (BotIsDead(bs)) { AIEnter_Respawn(bs, "battle retreat: bot dead"); return qfalse; } //if no enemy if (bs->enemy < 0) { AIEnter_Seek_LTG(bs, "battle retreat: no enemy"); return qfalse; } // BotEntityInfo(bs->enemy, &entinfo); if (EntityIsDead(&entinfo)) { AIEnter_Seek_LTG(bs, "battle retreat: enemy dead"); return qfalse; } //if there is another better enemy if (BotFindEnemy(bs, bs->enemy)) { #ifdef DEBUG BotAI_Print(PRT_MESSAGE, "found new better enemy\n"); #endif } // bs->tfl = TFL_DEFAULT; if (bot_grapple.integer) bs->tfl |= TFL_GRAPPLEHOOK; //if in lava or slime the bot should be able to get out if (BotInLavaOrSlime(bs)) bs->tfl |= TFL_LAVA|TFL_SLIME; //map specific code BotMapScripts(bs); //update the attack inventory values BotUpdateBattleInventory(bs, bs->enemy); //if the bot doesn't want to retreat anymore... probably picked up some nice items if (BotWantsToChase(bs)) { //empty the goal stack, when chasing, only the enemy is the goal trap_BotEmptyGoalStack(bs->gs); //go chase the enemy AIEnter_Battle_Chase(bs, "battle retreat: wants to chase"); return qfalse; } //update the last time the enemy was visible if (BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy)) { bs->enemyvisible_time = FloatTime(); VectorCopy(entinfo.origin, target); // if not a player enemy if (bs->enemy >= MAX_CLIENTS) { } //update the reachability area and origin if possible areanum = BotPointAreaNum(target); if (areanum && trap_AAS_AreaReachability(areanum)) { VectorCopy(target, bs->lastenemyorigin); bs->lastenemyareanum = areanum; } } //if the enemy is NOT visible for 4 seconds if (bs->enemyvisible_time < FloatTime() - 4) { AIEnter_Seek_LTG(bs, "battle retreat: lost enemy"); return qfalse; } //else if the enemy is NOT visible else if (bs->enemyvisible_time < FloatTime()) { //if there is another enemy if (BotFindEnemy(bs, -1)) { AIEnter_Battle_Fight(bs, "battle retreat: another enemy"); return qfalse; } } // BotTeamGoals(bs, qtrue); //use holdable items BotBattleUseItems(bs); //get the current long term goal while retreating if (!BotLongTermGoal(bs, bs->tfl, qtrue, &goal)) { AIEnter_Battle_SuicidalFight(bs, "battle retreat: no way out"); return qfalse; } //check for nearby goals periodicly if (bs->check_time < FloatTime()) { bs->check_time = FloatTime() + 1; range = 150; #ifdef CTF if (gametype == GT_CTF) { //if carrying a flag the bot shouldn't be distracted too much if (BotCTFCarryingFlag(bs)) range = 50; } #endif //CTF // if (BotNearbyGoal(bs, bs->tfl, &goal, range)) { trap_BotResetLastAvoidReach(bs->ms); //time the bot gets to pick up the nearby goal item bs->nbg_time = FloatTime() + range / 100 + 1; AIEnter_Battle_NBG(bs, "battle retreat: nbg"); return qfalse; } } //initialize the movement state BotSetupForMovement(bs); //move towards the goal trap_BotMoveToGoal(&moveresult, bs->ms, &goal, bs->tfl); //if the movement failed if (moveresult.failure) { //reset the avoid reach, otherwise bot is stuck in current area trap_BotResetAvoidReach(bs->ms); //BotAI_Print(PRT_MESSAGE, "movement failure %d\n", moveresult.traveltype); bs->ltg_time = 0; } // BotAIBlocked(bs, &moveresult, qfalse); //choose the best weapon to fight with BotChooseWeapon(bs); //if the view is fixed for the movement if (moveresult.flags & (MOVERESULT_MOVEMENTVIEW|MOVERESULT_SWIMVIEW)) { VectorCopy(moveresult.ideal_viewangles, bs->ideal_viewangles); } else if (!(moveresult.flags & MOVERESULT_MOVEMENTVIEWSET) && !(bs->flags & BFL_IDEALVIEWSET) ) { attack_skill = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_ATTACK_SKILL, 0, 1); //if the bot is skilled anough if (attack_skill > 0.3) { BotAimAtEnemy(bs); } else { if (trap_BotMovementViewTarget(bs->ms, &goal, bs->tfl, 300, target)) { VectorSubtract(target, bs->origin, dir); vectoangles(dir, bs->ideal_viewangles); } else { vectoangles(moveresult.movedir, bs->ideal_viewangles); } bs->ideal_viewangles[2] *= 0.5; } } //if the weapon is used for the bot movement if (moveresult.flags & MOVERESULT_MOVEMENTWEAPON) bs->weaponnum = moveresult.weapon; //attack the enemy if possible BotCheckAttack(bs); // return qtrue; } /* ================== AIEnter_Battle_NBG ================== */ void AIEnter_Battle_NBG(bot_state_t *bs, char *s) { BotRecordNodeSwitch(bs, "battle NBG", "", s); bs->ainode = AINode_Battle_NBG; } /* ================== AINode_Battle_NBG ================== */ int AINode_Battle_NBG(bot_state_t *bs) { int areanum; bot_goal_t goal; aas_entityinfo_t entinfo; bot_moveresult_t moveresult; float attack_skill; vec3_t target, dir; if (BotIsObserver(bs)) { AIEnter_Observer(bs, "battle nbg: observer"); return qfalse; } //if in the intermission if (BotIntermission(bs)) { AIEnter_Intermission(bs, "battle nbg: intermission"); return qfalse; } //respawn if dead if (BotIsDead(bs)) { AIEnter_Respawn(bs, "battle nbg: bot dead"); return qfalse; } //if no enemy if (bs->enemy < 0) { AIEnter_Seek_NBG(bs, "battle nbg: no enemy"); return qfalse; } // BotEntityInfo(bs->enemy, &entinfo); if (EntityIsDead(&entinfo)) { AIEnter_Seek_NBG(bs, "battle nbg: enemy dead"); return qfalse; } // bs->tfl = TFL_DEFAULT; if (bot_grapple.integer) bs->tfl |= TFL_GRAPPLEHOOK; //if in lava or slime the bot should be able to get out if (BotInLavaOrSlime(bs)) bs->tfl |= TFL_LAVA|TFL_SLIME; // if (BotCanAndWantsToRocketJump(bs)) { bs->tfl |= TFL_ROCKETJUMP; } //map specific code BotMapScripts(bs); //update the last time the enemy was visible if (BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy)) { bs->enemyvisible_time = FloatTime(); VectorCopy(entinfo.origin, target); // if not a player enemy if (bs->enemy >= MAX_CLIENTS) { } //update the reachability area and origin if possible areanum = BotPointAreaNum(target); if (areanum && trap_AAS_AreaReachability(areanum)) { VectorCopy(target, bs->lastenemyorigin); bs->lastenemyareanum = areanum; } } //if the bot has no goal or touches the current goal if (!trap_BotGetTopGoal(bs->gs, &goal)) { bs->nbg_time = 0; } else if (BotReachedGoal(bs, &goal)) { bs->nbg_time = 0; } // if (bs->nbg_time < FloatTime()) { //pop the current goal from the stack trap_BotPopGoal(bs->gs); //if the bot still has a goal if (trap_BotGetTopGoal(bs->gs, &goal)) AIEnter_Battle_Retreat(bs, "battle nbg: time out"); else AIEnter_Battle_Fight(bs, "battle nbg: time out"); // return qfalse; } //initialize the movement state BotSetupForMovement(bs); //move towards the goal trap_BotMoveToGoal(&moveresult, bs->ms, &goal, bs->tfl); //if the movement failed if (moveresult.failure) { //reset the avoid reach, otherwise bot is stuck in current area trap_BotResetAvoidReach(bs->ms); //BotAI_Print(PRT_MESSAGE, "movement failure %d\n", moveresult.traveltype); bs->nbg_time = 0; } // BotAIBlocked(bs, &moveresult, qfalse); //update the attack inventory values BotUpdateBattleInventory(bs, bs->enemy); //choose the best weapon to fight with BotChooseWeapon(bs); //if the view is fixed for the movement if (moveresult.flags & (MOVERESULT_MOVEMENTVIEW|MOVERESULT_SWIMVIEW)) { VectorCopy(moveresult.ideal_viewangles, bs->ideal_viewangles); } else if (!(moveresult.flags & MOVERESULT_MOVEMENTVIEWSET) && !(bs->flags & BFL_IDEALVIEWSET)) { attack_skill = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_ATTACK_SKILL, 0, 1); //if the bot is skilled anough and the enemy is visible if (attack_skill > 0.3) { //&& BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy) BotAimAtEnemy(bs); } else { if (trap_BotMovementViewTarget(bs->ms, &goal, bs->tfl, 300, target)) { VectorSubtract(target, bs->origin, dir); vectoangles(dir, bs->ideal_viewangles); } else { vectoangles(moveresult.movedir, bs->ideal_viewangles); } bs->ideal_viewangles[2] *= 0.5; } } //if the weapon is used for the bot movement if (moveresult.flags & MOVERESULT_MOVEMENTWEAPON) bs->weaponnum = moveresult.weapon; //attack the enemy if possible BotCheckAttack(bs); // return qtrue; } ================================================ FILE: src/game/ai_dmnet.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: ai_dmnet.h * * desc: Quake3 bot AI * * $Archive: /source/code/botai/ai_chat.c $ * *****************************************************************************/ #define MAX_NODESWITCHES 50 void AIEnter_Intermission(bot_state_t *bs, char *s); void AIEnter_Observer(bot_state_t *bs, char *s); void AIEnter_Respawn(bot_state_t *bs, char *s); void AIEnter_Stand(bot_state_t *bs, char *s); void AIEnter_Seek_ActivateEntity(bot_state_t *bs, char *s); void AIEnter_Seek_NBG(bot_state_t *bs, char *s); void AIEnter_Seek_LTG(bot_state_t *bs, char *s); void AIEnter_Seek_Camp(bot_state_t *bs, char *s); void AIEnter_Battle_Fight(bot_state_t *bs, char *s); void AIEnter_Battle_Chase(bot_state_t *bs, char *s); void AIEnter_Battle_Retreat(bot_state_t *bs, char *s); void AIEnter_Battle_NBG(bot_state_t *bs, char *s); int AINode_Intermission(bot_state_t *bs); int AINode_Observer(bot_state_t *bs); int AINode_Respawn(bot_state_t *bs); int AINode_Stand(bot_state_t *bs); int AINode_Seek_ActivateEntity(bot_state_t *bs); int AINode_Seek_NBG(bot_state_t *bs); int AINode_Seek_LTG(bot_state_t *bs); int AINode_Battle_Fight(bot_state_t *bs); int AINode_Battle_Chase(bot_state_t *bs); int AINode_Battle_Retreat(bot_state_t *bs); int AINode_Battle_NBG(bot_state_t *bs); void BotResetNodeSwitches(void); void BotDumpNodeSwitches(bot_state_t *bs); ================================================ FILE: src/game/ai_dmq3.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: ai_dmq3.c * * desc: Quake3 bot AI * * $Archive: /MissionPack/code/game/ai_dmq3.c $ * *****************************************************************************/ #include "g_local.h" #include "botlib.h" #include "be_aas.h" #include "be_ea.h" #include "be_ai_char.h" #include "be_ai_chat.h" #include "be_ai_gen.h" #include "be_ai_goal.h" #include "be_ai_move.h" #include "be_ai_weap.h" // #include "ai_main.h" #include "ai_dmq3.h" #include "ai_chat.h" #include "ai_cmd.h" #include "ai_dmnet.h" #include "ai_team.h" // #include "chars.h" //characteristics #include "inv.h" //indexes into the inventory #include "syn.h" //synonyms #include "match.h" //string matching types and vars // for the voice chats #include "menudef.h" // sos001205 - for q3_ui also // from aasfile.h #define AREACONTENTS_MOVER 1024 #define AREACONTENTS_MODELNUMSHIFT 24 #define AREACONTENTS_MAXMODELNUM 0xFF #define AREACONTENTS_MODELNUM (AREACONTENTS_MAXMODELNUM << AREACONTENTS_MODELNUMSHIFT) #define IDEAL_ATTACKDIST 140 #define MAX_WAYPOINTS 128 // bot_waypoint_t botai_waypoints[MAX_WAYPOINTS]; bot_waypoint_t *botai_freewaypoints; //NOTE: not using a cvars which can be updated because the game should be reloaded anyway int gametype; //game type int maxclients; //maximum number of clients vmCvar_t bot_grapple; vmCvar_t bot_rocketjump; vmCvar_t bot_fastchat; vmCvar_t bot_nochat; vmCvar_t bot_testrchat; vmCvar_t bot_challenge; vmCvar_t bot_predictobstacles; vmCvar_t g_spSkill; extern vmCvar_t bot_developer; vec3_t lastteleport_origin; //last teleport event origin float lastteleport_time; //last teleport event time int max_bspmodelindex; //maximum BSP model index //CTF flag goals bot_goal_t ctf_redflag; bot_goal_t ctf_blueflag; #define MAX_ALTROUTEGOALS 32 int altroutegoals_setup; aas_altroutegoal_t red_altroutegoals[MAX_ALTROUTEGOALS]; int red_numaltroutegoals; aas_altroutegoal_t blue_altroutegoals[MAX_ALTROUTEGOALS]; int blue_numaltroutegoals; /* ================== BotSetUserInfo ================== */ void BotSetUserInfo(bot_state_t *bs, char *key, char *value) { char userinfo[MAX_INFO_STRING]; trap_GetUserinfo(bs->client, userinfo, sizeof(userinfo)); Info_SetValueForKey(userinfo, key, value); trap_SetUserinfo(bs->client, userinfo); ClientUserinfoChanged( bs->client ); } /* ================== BotCTFCarryingFlag ================== */ int BotCTFCarryingFlag(bot_state_t *bs) { if (gametype != GT_CTF) return CTF_FLAG_NONE; if (bs->inventory[INVENTORY_REDFLAG] > 0) return CTF_FLAG_RED; else if (bs->inventory[INVENTORY_BLUEFLAG] > 0) return CTF_FLAG_BLUE; return CTF_FLAG_NONE; } /* ================== BotTeam ================== */ int BotTeam(bot_state_t *bs) { char info[1024]; if (bs->client < 0 || bs->client >= MAX_CLIENTS) { //BotAI_Print(PRT_ERROR, "BotCTFTeam: client out of range\n"); return qfalse; } trap_GetConfigstring(CS_PLAYERS+bs->client, info, sizeof(info)); // if (atoi(Info_ValueForKey(info, "t")) == TEAM_RED) return TEAM_RED; else if (atoi(Info_ValueForKey(info, "t")) == TEAM_BLUE) return TEAM_BLUE; return TEAM_FREE; } /* ================== BotOppositeTeam ================== */ int BotOppositeTeam(bot_state_t *bs) { switch(BotTeam(bs)) { case TEAM_RED: return TEAM_BLUE; case TEAM_BLUE: return TEAM_RED; default: return TEAM_FREE; } } /* ================== BotEnemyFlag ================== */ bot_goal_t *BotEnemyFlag(bot_state_t *bs) { if (BotTeam(bs) == TEAM_RED) { return &ctf_blueflag; } else { return &ctf_redflag; } } /* ================== BotTeamFlag ================== */ bot_goal_t *BotTeamFlag(bot_state_t *bs) { if (BotTeam(bs) == TEAM_RED) { return &ctf_redflag; } else { return &ctf_blueflag; } } /* ================== EntityIsDead ================== */ qboolean EntityIsDead(aas_entityinfo_t *entinfo) { playerState_t ps; if (entinfo->number >= 0 && entinfo->number < MAX_CLIENTS) { //retrieve the current client state BotAI_GetClientState( entinfo->number, &ps ); if (ps.pm_type != PM_NORMAL) return qtrue; } return qfalse; } /* ================== EntityCarriesFlag ================== */ qboolean EntityCarriesFlag(aas_entityinfo_t *entinfo) { if ( entinfo->powerups & ( 1 << PW_REDFLAG ) ) return qtrue; if ( entinfo->powerups & ( 1 << PW_BLUEFLAG ) ) return qtrue; return qfalse; } /* ================== EntityIsInvisible ================== */ qboolean EntityIsInvisible(aas_entityinfo_t *entinfo) { // the flag is always visible if (EntityCarriesFlag(entinfo)) { return qfalse; } if (entinfo->powerups & (1 << PW_INVIS)) { return qtrue; } return qfalse; } /* ================== EntityIsShooting ================== */ qboolean EntityIsShooting(aas_entityinfo_t *entinfo) { if (entinfo->flags & EF_FIRING) { return qtrue; } return qfalse; } /* ================== EntityIsChatting ================== */ qboolean EntityIsChatting(aas_entityinfo_t *entinfo) { if (entinfo->flags & EF_TALK) { return qtrue; } return qfalse; } /* ================== EntityHasQuad ================== */ qboolean EntityHasQuad(aas_entityinfo_t *entinfo) { if (entinfo->powerups & (1 << PW_QUAD)) { return qtrue; } return qfalse; } /* ================== BotRememberLastOrderedTask ================== */ void BotRememberLastOrderedTask(bot_state_t *bs) { if (!bs->ordered) { return; } bs->lastgoal_decisionmaker = bs->decisionmaker; bs->lastgoal_ltgtype = bs->ltgtype; memcpy(&bs->lastgoal_teamgoal, &bs->teamgoal, sizeof(bot_goal_t)); bs->lastgoal_teammate = bs->teammate; } /* ================== BotSetTeamStatus ================== */ void BotSetTeamStatus(bot_state_t *bs) { } /* ================== BotSetLastOrderedTask ================== */ int BotSetLastOrderedTask(bot_state_t *bs) { if (gametype == GT_CTF) { // don't go back to returning the flag if it's at the base if ( bs->lastgoal_ltgtype == LTG_RETURNFLAG ) { if ( BotTeam(bs) == TEAM_RED ) { if ( bs->redflagstatus == 0 ) { bs->lastgoal_ltgtype = 0; } } else { if ( bs->blueflagstatus == 0 ) { bs->lastgoal_ltgtype = 0; } } } } if ( bs->lastgoal_ltgtype ) { bs->decisionmaker = bs->lastgoal_decisionmaker; bs->ordered = qtrue; bs->ltgtype = bs->lastgoal_ltgtype; memcpy(&bs->teamgoal, &bs->lastgoal_teamgoal, sizeof(bot_goal_t)); bs->teammate = bs->lastgoal_teammate; bs->teamgoal_time = FloatTime() + 300; BotSetTeamStatus(bs); // if ( gametype == GT_CTF ) { if ( bs->ltgtype == LTG_GETFLAG ) { bot_goal_t *tb, *eb; int tt, et; tb = BotTeamFlag(bs); eb = BotEnemyFlag(bs); tt = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, tb->areanum, TFL_DEFAULT); et = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, eb->areanum, TFL_DEFAULT); // if the travel time towards the enemy base is larger than towards our base if (et > tt) { //get an alternative route goal towards the enemy base BotGetAlternateRouteGoal(bs, BotOppositeTeam(bs)); } } } return qtrue; } return qfalse; } /* ================== BotRefuseOrder ================== */ void BotRefuseOrder(bot_state_t *bs) { if (!bs->ordered) return; // if the bot was ordered to do something if ( bs->order_time && bs->order_time > FloatTime() - 10 ) { trap_EA_Action(bs->client, ACTION_NEGATIVE); BotVoiceChat(bs, bs->decisionmaker, VOICECHAT_NO); bs->order_time = 0; } } /* ================== BotCTFSeekGoals ================== */ void BotCTFSeekGoals(bot_state_t *bs) { float rnd, l1, l2; int flagstatus, c; vec3_t dir; aas_entityinfo_t entinfo; //when carrying a flag in ctf the bot should rush to the base if (BotCTFCarryingFlag(bs)) { //if not already rushing to the base if (bs->ltgtype != LTG_RUSHBASE) { BotRefuseOrder(bs); bs->ltgtype = LTG_RUSHBASE; bs->teamgoal_time = FloatTime() + CTF_RUSHBASE_TIME; bs->rushbaseaway_time = 0; bs->decisionmaker = bs->client; bs->ordered = qfalse; // switch(BotTeam(bs)) { case TEAM_RED: VectorSubtract(bs->origin, ctf_blueflag.origin, dir); break; case TEAM_BLUE: VectorSubtract(bs->origin, ctf_redflag.origin, dir); break; default: VectorSet(dir, 999, 999, 999); break; } // if the bot picked up the flag very close to the enemy base if ( VectorLength(dir) < 128 ) { // get an alternative route goal through the enemy base BotGetAlternateRouteGoal(bs, BotOppositeTeam(bs)); } else { // don't use any alt route goal, just get the hell out of the base bs->altroutegoal.areanum = 0; } BotSetUserInfo(bs, "teamtask", va("%d", TEAMTASK_OFFENSE)); BotVoiceChat(bs, -1, VOICECHAT_IHAVEFLAG); } else if (bs->rushbaseaway_time > FloatTime()) { if (BotTeam(bs) == TEAM_RED) flagstatus = bs->redflagstatus; else flagstatus = bs->blueflagstatus; //if the flag is back if (flagstatus == 0) { bs->rushbaseaway_time = 0; } } return; } // if the bot decided to follow someone if ( bs->ltgtype == LTG_TEAMACCOMPANY && !bs->ordered ) { // if the team mate being accompanied no longer carries the flag BotEntityInfo(bs->teammate, &entinfo); if (!EntityCarriesFlag(&entinfo)) { bs->ltgtype = 0; } } // if (BotTeam(bs) == TEAM_RED) flagstatus = bs->redflagstatus * 2 + bs->blueflagstatus; else flagstatus = bs->blueflagstatus * 2 + bs->redflagstatus; //if our team has the enemy flag and our flag is at the base if (flagstatus == 1) { // if (bs->owndecision_time < FloatTime()) { //if Not defending the base already if (!(bs->ltgtype == LTG_DEFENDKEYAREA && (bs->teamgoal.number == ctf_redflag.number || bs->teamgoal.number == ctf_blueflag.number))) { //if there is a visible team mate flag carrier c = BotTeamFlagCarrierVisible(bs); if (c >= 0 && // and not already following the team mate flag carrier (bs->ltgtype != LTG_TEAMACCOMPANY || bs->teammate != c)) { // BotRefuseOrder(bs); //follow the flag carrier bs->decisionmaker = bs->client; bs->ordered = qfalse; //the team mate bs->teammate = c; //last time the team mate was visible bs->teammatevisible_time = FloatTime(); //no message bs->teammessage_time = 0; //no arrive message bs->arrive_time = 1; // BotVoiceChat(bs, bs->teammate, VOICECHAT_ONFOLLOW); //get the team goal time bs->teamgoal_time = FloatTime() + TEAM_ACCOMPANY_TIME; bs->ltgtype = LTG_TEAMACCOMPANY; bs->formation_dist = 3.5 * 32; //3.5 meter BotSetTeamStatus(bs); bs->owndecision_time = FloatTime() + 5; } } } return; } //if the enemy has our flag else if (flagstatus == 2) { // if (bs->owndecision_time < FloatTime()) { //if enemy flag carrier is visible c = BotEnemyFlagCarrierVisible(bs); if (c >= 0) { //FIXME: fight enemy flag carrier } //if not already doing something important if (bs->ltgtype != LTG_GETFLAG && bs->ltgtype != LTG_RETURNFLAG && bs->ltgtype != LTG_TEAMHELP && bs->ltgtype != LTG_TEAMACCOMPANY && bs->ltgtype != LTG_CAMPORDER && bs->ltgtype != LTG_PATROL && bs->ltgtype != LTG_GETITEM) { BotRefuseOrder(bs); bs->decisionmaker = bs->client; bs->ordered = qfalse; // if (random() < 0.5) { //go for the enemy flag bs->ltgtype = LTG_GETFLAG; } else { bs->ltgtype = LTG_RETURNFLAG; } //no team message bs->teammessage_time = 0; //set the time the bot will stop getting the flag bs->teamgoal_time = FloatTime() + CTF_GETFLAG_TIME; //get an alternative route goal towards the enemy base BotGetAlternateRouteGoal(bs, BotOppositeTeam(bs)); // BotSetTeamStatus(bs); bs->owndecision_time = FloatTime() + 5; } } return; } //if both flags Not at their bases else if (flagstatus == 3) { // if (bs->owndecision_time < FloatTime()) { // if not trying to return the flag and not following the team flag carrier if ( bs->ltgtype != LTG_RETURNFLAG && bs->ltgtype != LTG_TEAMACCOMPANY ) { // c = BotTeamFlagCarrierVisible(bs); // if there is a visible team mate flag carrier if (c >= 0) { BotRefuseOrder(bs); //follow the flag carrier bs->decisionmaker = bs->client; bs->ordered = qfalse; //the team mate bs->teammate = c; //last time the team mate was visible bs->teammatevisible_time = FloatTime(); //no message bs->teammessage_time = 0; //no arrive message bs->arrive_time = 1; // BotVoiceChat(bs, bs->teammate, VOICECHAT_ONFOLLOW); //get the team goal time bs->teamgoal_time = FloatTime() + TEAM_ACCOMPANY_TIME; bs->ltgtype = LTG_TEAMACCOMPANY; bs->formation_dist = 3.5 * 32; //3.5 meter // BotSetTeamStatus(bs); bs->owndecision_time = FloatTime() + 5; } else { BotRefuseOrder(bs); bs->decisionmaker = bs->client; bs->ordered = qfalse; //get the enemy flag bs->teammessage_time = FloatTime() + 2 * random(); //get the flag bs->ltgtype = LTG_RETURNFLAG; //set the time the bot will stop getting the flag bs->teamgoal_time = FloatTime() + CTF_RETURNFLAG_TIME; //get an alternative route goal towards the enemy base BotGetAlternateRouteGoal(bs, BotOppositeTeam(bs)); // BotSetTeamStatus(bs); bs->owndecision_time = FloatTime() + 5; } } } return; } // don't just do something wait for the bot team leader to give orders if (BotTeamLeader(bs)) { return; } // if the bot is ordered to do something if ( bs->lastgoal_ltgtype ) { bs->teamgoal_time += 60; } // if the bot decided to do something on it's own and has a last ordered goal if ( !bs->ordered && bs->lastgoal_ltgtype ) { bs->ltgtype = 0; } //if already a CTF or team goal if (bs->ltgtype == LTG_TEAMHELP || bs->ltgtype == LTG_TEAMACCOMPANY || bs->ltgtype == LTG_DEFENDKEYAREA || bs->ltgtype == LTG_GETFLAG || bs->ltgtype == LTG_RUSHBASE || bs->ltgtype == LTG_RETURNFLAG || bs->ltgtype == LTG_CAMPORDER || bs->ltgtype == LTG_PATROL || bs->ltgtype == LTG_GETITEM || bs->ltgtype == LTG_MAKELOVE_UNDER || bs->ltgtype == LTG_MAKELOVE_ONTOP) { return; } // if (BotSetLastOrderedTask(bs)) return; // if (bs->owndecision_time > FloatTime()) return;; //if the bot is roaming if (bs->ctfroam_time > FloatTime()) return; //if the bot has anough aggression to decide what to do if (BotAggression(bs) < 50) return; //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); // if (bs->teamtaskpreference & (TEAMTP_ATTACKER|TEAMTP_DEFENDER)) { if (bs->teamtaskpreference & TEAMTP_ATTACKER) { l1 = 0.7f; } else { l1 = 0.2f; } l2 = 0.9f; } else { l1 = 0.4f; l2 = 0.7f; } //get the flag or defend the base rnd = random(); if (rnd < l1 && ctf_redflag.areanum && ctf_blueflag.areanum) { bs->decisionmaker = bs->client; bs->ordered = qfalse; bs->ltgtype = LTG_GETFLAG; //set the time the bot will stop getting the flag bs->teamgoal_time = FloatTime() + CTF_GETFLAG_TIME; //get an alternative route goal towards the enemy base BotGetAlternateRouteGoal(bs, BotOppositeTeam(bs)); BotSetTeamStatus(bs); } else if (rnd < l2 && ctf_redflag.areanum && ctf_blueflag.areanum) { bs->decisionmaker = bs->client; bs->ordered = qfalse; // if (BotTeam(bs) == TEAM_RED) memcpy(&bs->teamgoal, &ctf_redflag, sizeof(bot_goal_t)); else memcpy(&bs->teamgoal, &ctf_blueflag, sizeof(bot_goal_t)); //set the ltg type bs->ltgtype = LTG_DEFENDKEYAREA; //set the time the bot stops defending the base bs->teamgoal_time = FloatTime() + TEAM_DEFENDKEYAREA_TIME; bs->defendaway_time = 0; BotSetTeamStatus(bs); } else { bs->ltgtype = 0; //set the time the bot will stop roaming bs->ctfroam_time = FloatTime() + CTF_ROAM_TIME; BotSetTeamStatus(bs); } bs->owndecision_time = FloatTime() + 5; #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotCTFRetreatGoals ================== */ void BotCTFRetreatGoals(bot_state_t *bs) { //when carrying a flag in ctf the bot should rush to the base if (BotCTFCarryingFlag(bs)) { //if not already rushing to the base if (bs->ltgtype != LTG_RUSHBASE) { BotRefuseOrder(bs); bs->ltgtype = LTG_RUSHBASE; bs->teamgoal_time = FloatTime() + CTF_RUSHBASE_TIME; bs->rushbaseaway_time = 0; bs->decisionmaker = bs->client; bs->ordered = qfalse; BotSetTeamStatus(bs); } } } /* ================== BotTeamGoals ================== */ void BotTeamGoals(bot_state_t *bs, int retreat) { if ( retreat ) { if (gametype == GT_CTF) { BotCTFRetreatGoals(bs); } } else { if (gametype == GT_CTF) { //decide what to do in CTF mode BotCTFSeekGoals(bs); } } // reset the order time which is used to see if // we decided to refuse an order bs->order_time = 0; } /* ================== BotPointAreaNum ================== */ int BotPointAreaNum(vec3_t origin) { int areanum, numareas, areas[10]; vec3_t end; areanum = trap_AAS_PointAreaNum(origin); if (areanum) return areanum; VectorCopy(origin, end); end[2] += 10; numareas = trap_AAS_TraceAreas(origin, end, areas, NULL, 10); if (numareas > 0) return areas[0]; return 0; } /* ================== ClientName ================== */ char *ClientName(int client, char *name, int size) { char buf[MAX_INFO_STRING]; if (client < 0 || client >= MAX_CLIENTS) { BotAI_Print(PRT_ERROR, "ClientName: client out of range\n"); return "[client out of range]"; } trap_GetConfigstring(CS_PLAYERS+client, buf, sizeof(buf)); strncpy(name, Info_ValueForKey(buf, "n"), size-1); name[size-1] = '\0'; Q_CleanStr( name ); return name; } /* ================== ClientSkin ================== */ char *ClientSkin(int client, char *skin, int size) { char buf[MAX_INFO_STRING]; if (client < 0 || client >= MAX_CLIENTS) { BotAI_Print(PRT_ERROR, "ClientSkin: client out of range\n"); return "[client out of range]"; } trap_GetConfigstring(CS_PLAYERS+client, buf, sizeof(buf)); strncpy(skin, Info_ValueForKey(buf, "model"), size-1); skin[size-1] = '\0'; return skin; } /* ================== ClientFromName ================== */ int ClientFromName(char *name) { int i; char buf[MAX_INFO_STRING]; static int maxclients; if (!maxclients) maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); Q_CleanStr( buf ); if (!Q_stricmp(Info_ValueForKey(buf, "n"), name)) return i; } return -1; } /* ================== ClientOnSameTeamFromName ================== */ int ClientOnSameTeamFromName(bot_state_t *bs, char *name) { int i; char buf[MAX_INFO_STRING]; static int maxclients; if (!maxclients) maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { if (!BotSameTeam(bs, i)) continue; trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); Q_CleanStr( buf ); if (!Q_stricmp(Info_ValueForKey(buf, "n"), name)) return i; } return -1; } /* ================== stristr ================== */ char *stristr(char *str, char *charset) { int i; while(*str) { for (i = 0; charset[i] && str[i]; i++) { if (toupper(charset[i]) != toupper(str[i])) break; } if (!charset[i]) return str; str++; } return NULL; } /* ================== EasyClientName ================== */ char *EasyClientName(int client, char *buf, int size) { int i; char *str1, *str2, *ptr, c; char name[128]; strcpy(name, ClientName(client, name, sizeof(name))); for (i = 0; name[i]; i++) name[i] &= 127; //remove all spaces for (ptr = strstr(name, " "); ptr; ptr = strstr(name, " ")) { memmove(ptr, ptr+1, (int)strlen(ptr+1)+1); } //check for [x] and ]x[ clan names str1 = strstr(name, "["); str2 = strstr(name, "]"); if (str1 && str2) { if (str2 > str1) memmove(str1, str2+1, (int)strlen(str2+1)+1); else memmove(str2, str1+1, (int)strlen(str1+1)+1); } //remove Mr prefix if ((name[0] == 'm' || name[0] == 'M') && (name[1] == 'r' || name[1] == 'R')) { memmove(name, name+2, (int)strlen(name+2)+1); } //only allow lower case alphabet characters ptr = name; while(*ptr) { c = *ptr; if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_') { ptr++; } else if (c >= 'A' && c <= 'Z') { *ptr += 'a' - 'A'; ptr++; } else { memmove(ptr, ptr+1, (int)strlen(ptr + 1)+1); } } strncpy(buf, name, size-1); buf[size-1] = '\0'; return buf; } /* ================== BotSynonymContext ================== */ int BotSynonymContext(bot_state_t *bs) { int context; context = CONTEXT_NORMAL|CONTEXT_NEARBYITEM|CONTEXT_NAMES; // if (gametype == GT_CTF ) { if (BotTeam(bs) == TEAM_RED) context |= CONTEXT_CTFREDTEAM; else context |= CONTEXT_CTFBLUETEAM; } return context; } /* ================== BotChooseWeapon ================== */ void BotChooseWeapon(bot_state_t *bs) { int newweaponnum; if (bs->cur_ps.weaponstate == WEAPON_RAISING || bs->cur_ps.weaponstate == WEAPON_DROPPING) { trap_EA_SelectWeapon(bs->client, bs->weaponnum); } else { newweaponnum = trap_BotChooseBestFightWeapon(bs->ws, bs->inventory); if (bs->weaponnum != newweaponnum) bs->weaponchange_time = FloatTime(); bs->weaponnum = newweaponnum; //BotAI_Print(PRT_MESSAGE, "bs->weaponnum = %d\n", bs->weaponnum); trap_EA_SelectWeapon(bs->client, bs->weaponnum); } } /* ================== BotSetupForMovement ================== */ void BotSetupForMovement(bot_state_t *bs) { bot_initmove_t initmove; memset(&initmove, 0, sizeof(bot_initmove_t)); VectorCopy(bs->cur_ps.origin, initmove.origin); VectorCopy(bs->cur_ps.velocity, initmove.velocity); VectorClear(initmove.viewoffset); initmove.viewoffset[2] += bs->cur_ps.viewheight; initmove.entitynum = bs->entitynum; initmove.client = bs->client; initmove.thinktime = bs->thinktime; //set the onground flag if (bs->cur_ps.groundEntityNum != ENTITYNUM_NONE) initmove.or_moveflags |= MFL_ONGROUND; //set the teleported flag if ((bs->cur_ps.pm_flags & PMF_TIME_KNOCKBACK) && (bs->cur_ps.pm_time > 0)) { initmove.or_moveflags |= MFL_TELEPORTED; } //set the waterjump flag if ((bs->cur_ps.pm_flags & PMF_TIME_WATERJUMP) && (bs->cur_ps.pm_time > 0)) { initmove.or_moveflags |= MFL_WATERJUMP; } //set presence type if (bs->cur_ps.pm_flags & PMF_DUCKED) initmove.presencetype = PRESENCE_CROUCH; else initmove.presencetype = PRESENCE_NORMAL; // if (bs->walker > 0.5) initmove.or_moveflags |= MFL_WALK; // VectorCopy(bs->viewangles, initmove.viewangles); // trap_BotInitMoveState(bs->ms, &initmove); } /* ================== BotCheckItemPickup ================== */ void BotCheckItemPickup(bot_state_t *bs, int *oldinventory) { } /* ================== BotUpdateInventory ================== */ void BotUpdateInventory(bot_state_t *bs) { int oldinventory[MAX_ITEMS]; memcpy(oldinventory, bs->inventory, sizeof(oldinventory)); //armor bs->inventory[INVENTORY_ARMOR] = bs->cur_ps.stats[STAT_ARMOR]; //weapons bs->inventory[INVENTORY_GAUNTLET] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_GAUNTLET)) != 0; bs->inventory[INVENTORY_SHOTGUN] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_SHOTGUN)) != 0; bs->inventory[INVENTORY_MACHINEGUN] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_MACHINEGUN)) != 0; bs->inventory[INVENTORY_GRENADELAUNCHER] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_GRENADE_LAUNCHER)) != 0; bs->inventory[INVENTORY_ROCKETLAUNCHER] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_ROCKET_LAUNCHER)) != 0; bs->inventory[INVENTORY_LIGHTNING] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_LIGHTNING)) != 0; bs->inventory[INVENTORY_RAILGUN] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_RAILGUN)) != 0; bs->inventory[INVENTORY_PLASMAGUN] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_PLASMAGUN)) != 0; bs->inventory[INVENTORY_BFG10K] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_BFG)) != 0; bs->inventory[INVENTORY_GRAPPLINGHOOK] = (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_GRAPPLING_HOOK)) != 0; //ammo bs->inventory[INVENTORY_SHELLS] = bs->cur_ps.ammo[WP_SHOTGUN]; bs->inventory[INVENTORY_BULLETS] = bs->cur_ps.ammo[WP_MACHINEGUN]; bs->inventory[INVENTORY_GRENADES] = bs->cur_ps.ammo[WP_GRENADE_LAUNCHER]; bs->inventory[INVENTORY_CELLS] = bs->cur_ps.ammo[WP_PLASMAGUN]; bs->inventory[INVENTORY_LIGHTNINGAMMO] = bs->cur_ps.ammo[WP_LIGHTNING]; bs->inventory[INVENTORY_ROCKETS] = bs->cur_ps.ammo[WP_ROCKET_LAUNCHER]; bs->inventory[INVENTORY_SLUGS] = bs->cur_ps.ammo[WP_RAILGUN]; bs->inventory[INVENTORY_BFGAMMO] = bs->cur_ps.ammo[WP_BFG]; //powerups bs->inventory[INVENTORY_HEALTH] = bs->cur_ps.stats[STAT_HEALTH]; bs->inventory[INVENTORY_TELEPORTER] = bs->cur_ps.stats[STAT_HOLDABLE_ITEM] == MODELINDEX_TELEPORTER; bs->inventory[INVENTORY_MEDKIT] = bs->cur_ps.stats[STAT_HOLDABLE_ITEM] == MODELINDEX_MEDKIT; bs->inventory[INVENTORY_QUAD] = bs->cur_ps.powerups[PW_QUAD] != 0; bs->inventory[INVENTORY_ENVIRONMENTSUIT] = bs->cur_ps.powerups[PW_BATTLESUIT] != 0; bs->inventory[INVENTORY_HASTE] = bs->cur_ps.powerups[PW_HASTE] != 0; bs->inventory[INVENTORY_INVISIBILITY] = bs->cur_ps.powerups[PW_INVIS] != 0; bs->inventory[INVENTORY_REGEN] = bs->cur_ps.powerups[PW_REGEN] != 0; bs->inventory[INVENTORY_FLIGHT] = bs->cur_ps.powerups[PW_FLIGHT] != 0; bs->inventory[INVENTORY_REDFLAG] = bs->cur_ps.powerups[PW_REDFLAG] != 0; bs->inventory[INVENTORY_BLUEFLAG] = bs->cur_ps.powerups[PW_BLUEFLAG] != 0; BotCheckItemPickup(bs, oldinventory); } /* ================== BotUpdateBattleInventory ================== */ void BotUpdateBattleInventory(bot_state_t *bs, int enemy) { vec3_t dir; aas_entityinfo_t entinfo; BotEntityInfo(enemy, &entinfo); VectorSubtract(entinfo.origin, bs->origin, dir); bs->inventory[ENEMY_HEIGHT] = (int) dir[2]; dir[2] = 0; bs->inventory[ENEMY_HORIZONTAL_DIST] = (int) VectorLength(dir); //FIXME: add num visible enemies and num visible team mates to the inventory } /* ================== BotBattleUseItems ================== */ void BotBattleUseItems(bot_state_t *bs) { if (bs->inventory[INVENTORY_HEALTH] < 40) { if (bs->inventory[INVENTORY_TELEPORTER] > 0) { if (!BotCTFCarryingFlag(bs) ) { trap_EA_Use(bs->client); } } } if (bs->inventory[INVENTORY_HEALTH] < 60) { if (bs->inventory[INVENTORY_MEDKIT] > 0) { trap_EA_Use(bs->client); } } } /* ================== BotSetTeleportTime ================== */ void BotSetTeleportTime(bot_state_t *bs) { if ((bs->cur_ps.eFlags ^ bs->last_eFlags) & EF_TELEPORT_BIT) { bs->teleport_time = FloatTime(); } bs->last_eFlags = bs->cur_ps.eFlags; } /* ================== BotIsDead ================== */ qboolean BotIsDead(bot_state_t *bs) { return (bs->cur_ps.pm_type == PM_DEAD); } /* ================== BotIsObserver ================== */ qboolean BotIsObserver(bot_state_t *bs) { char buf[MAX_INFO_STRING]; if (bs->cur_ps.pm_type == PM_SPECTATOR) return qtrue; trap_GetConfigstring(CS_PLAYERS+bs->client, buf, sizeof(buf)); if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) return qtrue; return qfalse; } /* ================== BotIntermission ================== */ qboolean BotIntermission(bot_state_t *bs) { //NOTE: we shouldn't be looking at the game code... if (level.intermissiontime) return qtrue; return (bs->cur_ps.pm_type == PM_FREEZE || bs->cur_ps.pm_type == PM_INTERMISSION); } /* ================== BotInLavaOrSlime ================== */ qboolean BotInLavaOrSlime(bot_state_t *bs) { vec3_t feet; VectorCopy(bs->origin, feet); feet[2] -= 23; return (trap_AAS_PointContents(feet) & (CONTENTS_LAVA|CONTENTS_SLIME)); } /* ================== BotCreateWayPoint ================== */ bot_waypoint_t *BotCreateWayPoint(char *name, vec3_t origin, int areanum) { bot_waypoint_t *wp; vec3_t waypointmins = {-8, -8, -8}, waypointmaxs = {8, 8, 8}; wp = botai_freewaypoints; if ( !wp ) { BotAI_Print( PRT_WARNING, "BotCreateWayPoint: Out of waypoints\n" ); return NULL; } botai_freewaypoints = botai_freewaypoints->next; Q_strncpyz( wp->name, name, sizeof(wp->name) ); VectorCopy(origin, wp->goal.origin); VectorCopy(waypointmins, wp->goal.mins); VectorCopy(waypointmaxs, wp->goal.maxs); wp->goal.areanum = areanum; wp->next = NULL; wp->prev = NULL; return wp; } /* ================== BotFindWayPoint ================== */ bot_waypoint_t *BotFindWayPoint(bot_waypoint_t *waypoints, char *name) { bot_waypoint_t *wp; for (wp = waypoints; wp; wp = wp->next) { if (!Q_stricmp(wp->name, name)) return wp; } return NULL; } /* ================== BotFreeWaypoints ================== */ void BotFreeWaypoints(bot_waypoint_t *wp) { bot_waypoint_t *nextwp; for (; wp; wp = nextwp) { nextwp = wp->next; wp->next = botai_freewaypoints; botai_freewaypoints = wp; } } /* ================== BotInitWaypoints ================== */ void BotInitWaypoints(void) { int i; botai_freewaypoints = NULL; for (i = 0; i < MAX_WAYPOINTS; i++) { botai_waypoints[i].next = botai_freewaypoints; botai_freewaypoints = &botai_waypoints[i]; } } /* ================== TeamPlayIsOn ================== */ int TeamPlayIsOn(void) { return ( gametype >= GT_TEAM ); } /* ================== BotAggression ================== */ float BotAggression(bot_state_t *bs) { //if the bot has quad if (bs->inventory[INVENTORY_QUAD]) { //if the bot is not holding the gauntlet or the enemy is really nearby if (bs->weaponnum != WP_GAUNTLET || bs->inventory[ENEMY_HORIZONTAL_DIST] < 80) { return 70; } } //if the enemy is located way higher than the bot if (bs->inventory[ENEMY_HEIGHT] > 200) return 0; //if the bot is very low on health if (bs->inventory[INVENTORY_HEALTH] < 60) return 0; //if the bot is low on health if (bs->inventory[INVENTORY_HEALTH] < 80) { //if the bot has insufficient armor if (bs->inventory[INVENTORY_ARMOR] < 40) return 0; } //if the bot can use the bfg if (bs->inventory[INVENTORY_BFG10K] > 0 && bs->inventory[INVENTORY_BFGAMMO] > 7) return 100; //if the bot can use the railgun if (bs->inventory[INVENTORY_RAILGUN] > 0 && bs->inventory[INVENTORY_SLUGS] > 5) return 95; //if the bot can use the lightning gun if (bs->inventory[INVENTORY_LIGHTNING] > 0 && bs->inventory[INVENTORY_LIGHTNINGAMMO] > 50) return 90; //if the bot can use the rocketlauncher if (bs->inventory[INVENTORY_ROCKETLAUNCHER] > 0 && bs->inventory[INVENTORY_ROCKETS] > 5) return 90; //if the bot can use the plasmagun if (bs->inventory[INVENTORY_PLASMAGUN] > 0 && bs->inventory[INVENTORY_CELLS] > 40) return 85; //if the bot can use the grenade launcher if (bs->inventory[INVENTORY_GRENADELAUNCHER] > 0 && bs->inventory[INVENTORY_GRENADES] > 10) return 80; //if the bot can use the shotgun if (bs->inventory[INVENTORY_SHOTGUN] > 0 && bs->inventory[INVENTORY_SHELLS] > 10) return 50; //otherwise the bot is not feeling too good return 0; } /* ================== BotFeelingBad ================== */ float BotFeelingBad(bot_state_t *bs) { if (bs->weaponnum == WP_GAUNTLET) { return 100; } if (bs->inventory[INVENTORY_HEALTH] < 40) { return 100; } if (bs->weaponnum == WP_MACHINEGUN) { return 90; } if (bs->inventory[INVENTORY_HEALTH] < 60) { return 80; } return 0; } /* ================== BotWantsToRetreat ================== */ int BotWantsToRetreat(bot_state_t *bs) { aas_entityinfo_t entinfo; if (gametype == GT_CTF) { //always retreat when carrying a CTF flag if (BotCTFCarryingFlag(bs)) return qtrue; } // if (bs->enemy >= 0) { //if the enemy is carrying a flag BotEntityInfo(bs->enemy, &entinfo); if (EntityCarriesFlag(&entinfo)) return qfalse; } //if the bot is getting the flag if (bs->ltgtype == LTG_GETFLAG) return qtrue; // if (BotAggression(bs) < 50) return qtrue; return qfalse; } /* ================== BotWantsToChase ================== */ int BotWantsToChase(bot_state_t *bs) { aas_entityinfo_t entinfo; if (gametype == GT_CTF) { //never chase when carrying a CTF flag if (BotCTFCarryingFlag(bs)) return qfalse; //always chase if the enemy is carrying a flag BotEntityInfo(bs->enemy, &entinfo); if (EntityCarriesFlag(&entinfo)) return qtrue; } //if the bot is getting the flag if (bs->ltgtype == LTG_GETFLAG) return qfalse; // if (BotAggression(bs) > 50) return qtrue; return qfalse; } /* ================== BotWantsToHelp ================== */ int BotWantsToHelp(bot_state_t *bs) { return qtrue; } /* ================== BotCanAndWantsToRocketJump ================== */ int BotCanAndWantsToRocketJump(bot_state_t *bs) { float rocketjumper; //if rocket jumping is disabled if (!bot_rocketjump.integer) return qfalse; //if no rocket launcher if (bs->inventory[INVENTORY_ROCKETLAUNCHER] <= 0) return qfalse; //if low on rockets if (bs->inventory[INVENTORY_ROCKETS] < 3) return qfalse; //never rocket jump with the Quad if (bs->inventory[INVENTORY_QUAD]) return qfalse; //if low on health if (bs->inventory[INVENTORY_HEALTH] < 60) return qfalse; //if not full health if (bs->inventory[INVENTORY_HEALTH] < 90) { //if the bot has insufficient armor if (bs->inventory[INVENTORY_ARMOR] < 40) return qfalse; } rocketjumper = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_WEAPONJUMPING, 0, 1); if (rocketjumper < 0.5) return qfalse; return qtrue; } /* ================== BotHasPersistantPowerupAndWeapon ================== */ int BotHasPersistantPowerupAndWeapon(bot_state_t *bs) { //if the bot is very low on health if (bs->inventory[INVENTORY_HEALTH] < 60) return qfalse; //if the bot is low on health if (bs->inventory[INVENTORY_HEALTH] < 80) { //if the bot has insufficient armor if (bs->inventory[INVENTORY_ARMOR] < 40) return qfalse; } //if the bot can use the bfg if (bs->inventory[INVENTORY_BFG10K] > 0 && bs->inventory[INVENTORY_BFGAMMO] > 7) return qtrue; //if the bot can use the railgun if (bs->inventory[INVENTORY_RAILGUN] > 0 && bs->inventory[INVENTORY_SLUGS] > 5) return qtrue; //if the bot can use the lightning gun if (bs->inventory[INVENTORY_LIGHTNING] > 0 && bs->inventory[INVENTORY_LIGHTNINGAMMO] > 50) return qtrue; //if the bot can use the rocketlauncher if (bs->inventory[INVENTORY_ROCKETLAUNCHER] > 0 && bs->inventory[INVENTORY_ROCKETS] > 5) return qtrue; // if (bs->inventory[INVENTORY_NAILGUN] > 0 && bs->inventory[INVENTORY_NAILS] > 5) return qtrue; // if (bs->inventory[INVENTORY_PROXLAUNCHER] > 0 && bs->inventory[INVENTORY_MINES] > 5) return qtrue; // if (bs->inventory[INVENTORY_CHAINGUN] > 0 && bs->inventory[INVENTORY_BELT] > 40) return qtrue; //if the bot can use the plasmagun if (bs->inventory[INVENTORY_PLASMAGUN] > 0 && bs->inventory[INVENTORY_CELLS] > 20) return qtrue; return qfalse; } /* ================== BotGoCamp ================== */ void BotGoCamp(bot_state_t *bs, bot_goal_t *goal) { float camper; bs->decisionmaker = bs->client; //set message time to zero so bot will NOT show any message bs->teammessage_time = 0; //set the ltg type bs->ltgtype = LTG_CAMP; //set the team goal memcpy(&bs->teamgoal, goal, sizeof(bot_goal_t)); //get the team goal time camper = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CAMPER, 0, 1); if (camper > 0.99) bs->teamgoal_time = FloatTime() + 99999; else bs->teamgoal_time = FloatTime() + 120 + 180 * camper + random() * 15; //set the last time the bot started camping bs->camp_time = FloatTime(); //the teammate that requested the camping bs->teammate = 0; //do NOT type arrive message bs->arrive_time = 1; } /* ================== BotWantsToCamp ================== */ int BotWantsToCamp(bot_state_t *bs) { float camper; int cs, traveltime, besttraveltime; bot_goal_t goal, bestgoal; camper = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CAMPER, 0, 1); if (camper < 0.1) return qfalse; //if the bot has a team goal if (bs->ltgtype == LTG_TEAMHELP || bs->ltgtype == LTG_TEAMACCOMPANY || bs->ltgtype == LTG_DEFENDKEYAREA || bs->ltgtype == LTG_GETFLAG || bs->ltgtype == LTG_RUSHBASE || bs->ltgtype == LTG_CAMP || bs->ltgtype == LTG_CAMPORDER || bs->ltgtype == LTG_PATROL) { return qfalse; } //if camped recently if (bs->camp_time > FloatTime() - 60 + 300 * (1-camper)) return qfalse; // if (random() > camper) { bs->camp_time = FloatTime(); return qfalse; } //if the bot isn't healthy anough if (BotAggression(bs) < 50) return qfalse; //the bot should have at least have the rocket launcher, the railgun or the bfg10k with some ammo if ((bs->inventory[INVENTORY_ROCKETLAUNCHER] <= 0 || bs->inventory[INVENTORY_ROCKETS < 10]) && (bs->inventory[INVENTORY_RAILGUN] <= 0 || bs->inventory[INVENTORY_SLUGS] < 10) && (bs->inventory[INVENTORY_BFG10K] <= 0 || bs->inventory[INVENTORY_BFGAMMO] < 10)) { return qfalse; } //find the closest camp spot besttraveltime = 99999; for (cs = trap_BotGetNextCampSpotGoal(0, &goal); cs; cs = trap_BotGetNextCampSpotGoal(cs, &goal)) { traveltime = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, goal.areanum, TFL_DEFAULT); if (traveltime && traveltime < besttraveltime) { besttraveltime = traveltime; memcpy(&bestgoal, &goal, sizeof(bot_goal_t)); } } if (besttraveltime > 150) return qfalse; //ok found a camp spot, go camp there BotGoCamp(bs, &bestgoal); bs->ordered = qfalse; // return qtrue; } /* ================== BotDontAvoid ================== */ void BotDontAvoid(bot_state_t *bs, char *itemname) { bot_goal_t goal; int num; num = trap_BotGetLevelItemGoal(-1, itemname, &goal); while(num >= 0) { trap_BotRemoveFromAvoidGoals(bs->gs, goal.number); num = trap_BotGetLevelItemGoal(num, itemname, &goal); } } /* ================== BotGoForPowerups ================== */ void BotGoForPowerups(bot_state_t *bs) { //don't avoid any of the powerups anymore BotDontAvoid(bs, "Quad Damage"); BotDontAvoid(bs, "Regeneration"); BotDontAvoid(bs, "Battle Suit"); BotDontAvoid(bs, "Speed"); BotDontAvoid(bs, "Invisibility"); //BotDontAvoid(bs, "Flight"); //reset the long term goal time so the bot will go for the powerup //NOTE: the long term goal type doesn't change bs->ltg_time = 0; } /* ================== BotRoamGoal ================== */ void BotRoamGoal(bot_state_t *bs, vec3_t goal) { int pc, i; float len, rnd; vec3_t dir, bestorg, belowbestorg; bsp_trace_t trace; for (i = 0; i < 10; i++) { //start at the bot origin VectorCopy(bs->origin, bestorg); rnd = random(); if (rnd > 0.25) { //add a random value to the x-coordinate if (random() < 0.5) bestorg[0] -= 800 * random() + 100; else bestorg[0] += 800 * random() + 100; } if (rnd < 0.75) { //add a random value to the y-coordinate if (random() < 0.5) bestorg[1] -= 800 * random() + 100; else bestorg[1] += 800 * random() + 100; } //add a random value to the z-coordinate (NOTE: 48 = maxjump?) bestorg[2] += 2 * 48 * crandom(); //trace a line from the origin to the roam target BotAI_Trace(&trace, bs->origin, NULL, NULL, bestorg, bs->entitynum, MASK_SOLID); //direction and length towards the roam target VectorSubtract(trace.endpos, bs->origin, dir); len = VectorNormalize(dir); //if the roam target is far away anough if (len > 200) { //the roam target is in the given direction before walls VectorScale(dir, len * trace.fraction - 40, dir); VectorAdd(bs->origin, dir, bestorg); //get the coordinates of the floor below the roam target belowbestorg[0] = bestorg[0]; belowbestorg[1] = bestorg[1]; belowbestorg[2] = bestorg[2] - 800; BotAI_Trace(&trace, bestorg, NULL, NULL, belowbestorg, bs->entitynum, MASK_SOLID); // if (!trace.startsolid) { trace.endpos[2]++; pc = trap_PointContents(trace.endpos, bs->entitynum); if (!(pc & (CONTENTS_LAVA | CONTENTS_SLIME))) { VectorCopy(bestorg, goal); return; } } } } VectorCopy(bestorg, goal); } /* ================== BotAttackMove ================== */ bot_moveresult_t BotAttackMove(bot_state_t *bs, int tfl) { int movetype, i, attackentity; float attack_skill, jumper, croucher, dist, strafechange_time; float attack_dist, attack_range; vec3_t forward, backward, sideward, hordir, up = {0, 0, 1}; aas_entityinfo_t entinfo; bot_moveresult_t moveresult; bot_goal_t goal; attackentity = bs->enemy; // if (bs->attackchase_time > FloatTime()) { //create the chase goal goal.entitynum = attackentity; goal.areanum = bs->lastenemyareanum; VectorCopy(bs->lastenemyorigin, goal.origin); VectorSet(goal.mins, -8, -8, -8); VectorSet(goal.maxs, 8, 8, 8); //initialize the movement state BotSetupForMovement(bs); //move towards the goal trap_BotMoveToGoal(&moveresult, bs->ms, &goal, tfl); return moveresult; } // memset(&moveresult, 0, sizeof(bot_moveresult_t)); // attack_skill = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_ATTACK_SKILL, 0, 1); jumper = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_JUMPER, 0, 1); croucher = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CROUCHER, 0, 1); //if the bot is really stupid if (attack_skill < 0.2) return moveresult; //initialize the movement state BotSetupForMovement(bs); //get the enemy entity info BotEntityInfo(attackentity, &entinfo); //direction towards the enemy VectorSubtract(entinfo.origin, bs->origin, forward); //the distance towards the enemy dist = VectorNormalize(forward); VectorNegate(forward, backward); //walk, crouch or jump movetype = MOVE_WALK; // if (bs->attackcrouch_time < FloatTime() - 1) { if (random() < jumper) { movetype = MOVE_JUMP; } //wait at least one second before crouching again else if (bs->attackcrouch_time < FloatTime() - 1 && random() < croucher) { bs->attackcrouch_time = FloatTime() + croucher * 5; } } if (bs->attackcrouch_time > FloatTime()) movetype = MOVE_CROUCH; //if the bot should jump if (movetype == MOVE_JUMP) { //if jumped last frame if (bs->attackjump_time > FloatTime()) { movetype = MOVE_WALK; } else { bs->attackjump_time = FloatTime() + 1; } } if (bs->cur_ps.weapon == WP_GAUNTLET) { attack_dist = 0; attack_range = 0; } else { attack_dist = IDEAL_ATTACKDIST; attack_range = 40; } //if the bot is stupid if (attack_skill <= 0.4) { //just walk to or away from the enemy if (dist > attack_dist + attack_range) { if (trap_BotMoveInDirection(bs->ms, forward, 400, movetype)) return moveresult; } if (dist < attack_dist - attack_range) { if (trap_BotMoveInDirection(bs->ms, backward, 400, movetype)) return moveresult; } return moveresult; } //increase the strafe time bs->attackstrafe_time += bs->thinktime; //get the strafe change time strafechange_time = 0.4 + (1 - attack_skill) * 0.2; if (attack_skill > 0.7) strafechange_time += crandom() * 0.2; //if the strafe direction should be changed if (bs->attackstrafe_time > strafechange_time) { //some magic number :) if (random() > 0.935) { //flip the strafe direction bs->flags ^= BFL_STRAFERIGHT; bs->attackstrafe_time = 0; } } // for (i = 0; i < 2; i++) { hordir[0] = forward[0]; hordir[1] = forward[1]; hordir[2] = 0; VectorNormalize(hordir); //get the sideward vector CrossProduct(hordir, up, sideward); //reverse the vector depending on the strafe direction if (bs->flags & BFL_STRAFERIGHT) VectorNegate(sideward, sideward); //randomly go back a little if (random() > 0.9) { VectorAdd(sideward, backward, sideward); } else { //walk forward or backward to get at the ideal attack distance if (dist > attack_dist + attack_range) { VectorAdd(sideward, forward, sideward); } else if (dist < attack_dist - attack_range) { VectorAdd(sideward, backward, sideward); } } //perform the movement if (trap_BotMoveInDirection(bs->ms, sideward, 400, movetype)) return moveresult; //movement failed, flip the strafe direction bs->flags ^= BFL_STRAFERIGHT; bs->attackstrafe_time = 0; } //bot couldn't do any usefull movement // bs->attackchase_time = AAS_Time() + 6; return moveresult; } /* ================== BotSameTeam ================== */ int BotSameTeam(bot_state_t *bs, int entnum) { char info1[1024], info2[1024]; if (bs->client < 0 || bs->client >= MAX_CLIENTS) { //BotAI_Print(PRT_ERROR, "BotSameTeam: client out of range\n"); return qfalse; } if (entnum < 0 || entnum >= MAX_CLIENTS) { //BotAI_Print(PRT_ERROR, "BotSameTeam: client out of range\n"); return qfalse; } if ( gametype >= GT_TEAM ) { trap_GetConfigstring(CS_PLAYERS+bs->client, info1, sizeof(info1)); trap_GetConfigstring(CS_PLAYERS+entnum, info2, sizeof(info2)); // if (atoi(Info_ValueForKey(info1, "t")) == atoi(Info_ValueForKey(info2, "t"))) return qtrue; } return qfalse; } /* ================== InFieldOfVision ================== */ qboolean InFieldOfVision(vec3_t viewangles, float fov, vec3_t angles) { int i; float diff, angle; for (i = 0; i < 2; i++) { angle = AngleMod(viewangles[i]); angles[i] = AngleMod(angles[i]); diff = angles[i] - angle; if (angles[i] > angle) { if (diff > 180.0) diff -= 360.0; } else { if (diff < -180.0) diff += 360.0; } if (diff > 0) { if (diff > fov * 0.5) return qfalse; } else { if (diff < -fov * 0.5) return qfalse; } } return qtrue; } /* ================== BotEntityVisible returns visibility in the range [0, 1] taking fog and water surfaces into account ================== */ float BotEntityVisible(int viewer, vec3_t eye, vec3_t viewangles, float fov, int ent) { int i, contents_mask, passent, hitent, infog, inwater, otherinfog, pc; float squaredfogdist, waterfactor, vis, bestvis; bsp_trace_t trace; aas_entityinfo_t entinfo; vec3_t dir, entangles, start, end, middle; //calculate middle of bounding box BotEntityInfo(ent, &entinfo); VectorAdd(entinfo.mins, entinfo.maxs, middle); VectorScale(middle, 0.5, middle); VectorAdd(entinfo.origin, middle, middle); //check if entity is within field of vision VectorSubtract(middle, eye, dir); vectoangles(dir, entangles); if (!InFieldOfVision(viewangles, fov, entangles)) return 0; // pc = trap_AAS_PointContents(eye); infog = (pc & CONTENTS_FOG); inwater = (pc & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER)); // bestvis = 0; for (i = 0; i < 3; i++) { //if the point is not in potential visible sight //if (!AAS_inPVS(eye, middle)) continue; // contents_mask = CONTENTS_SOLID|CONTENTS_PLAYERCLIP; passent = viewer; hitent = ent; VectorCopy(eye, start); VectorCopy(middle, end); //if the entity is in water, lava or slime if (trap_AAS_PointContents(middle) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER)) { contents_mask |= (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER); } //if eye is in water, lava or slime if (inwater) { if (!(contents_mask & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER))) { passent = ent; hitent = viewer; VectorCopy(middle, start); VectorCopy(eye, end); } contents_mask ^= (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER); } //trace from start to end BotAI_Trace(&trace, start, NULL, NULL, end, passent, contents_mask); //if water was hit waterfactor = 1.0; if (trace.contents & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER)) { //if the water surface is translucent if (1) { //trace through the water contents_mask &= ~(CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER); BotAI_Trace(&trace, trace.endpos, NULL, NULL, end, passent, contents_mask); waterfactor = 0.5; } } //if a full trace or the hitent was hit if (trace.fraction >= 1 || trace.ent == hitent) { //check for fog, assuming there's only one fog brush where //either the viewer or the entity is in or both are in otherinfog = (trap_AAS_PointContents(middle) & CONTENTS_FOG); if (infog && otherinfog) { VectorSubtract(trace.endpos, eye, dir); squaredfogdist = VectorLengthSquared(dir); } else if (infog) { VectorCopy(trace.endpos, start); BotAI_Trace(&trace, start, NULL, NULL, eye, viewer, CONTENTS_FOG); VectorSubtract(eye, trace.endpos, dir); squaredfogdist = VectorLengthSquared(dir); } else if (otherinfog) { VectorCopy(trace.endpos, end); BotAI_Trace(&trace, eye, NULL, NULL, end, viewer, CONTENTS_FOG); VectorSubtract(end, trace.endpos, dir); squaredfogdist = VectorLengthSquared(dir); } else { //if the entity and the viewer are not in fog assume there's no fog in between squaredfogdist = 0; } //decrease visibility with the view distance through fog vis = 1 / ((squaredfogdist * 0.001) < 1 ? 1 : (squaredfogdist * 0.001)); //if entering water visibility is reduced vis *= waterfactor; // if (vis > bestvis) bestvis = vis; //if pretty much no fog if (bestvis >= 0.95) return bestvis; } //check bottom and top of bounding box as well if (i == 0) middle[2] += entinfo.mins[2]; else if (i == 1) middle[2] += entinfo.maxs[2] - entinfo.mins[2]; } return bestvis; } /* ================== BotFindEnemy ================== */ int BotFindEnemy(bot_state_t *bs, int curenemy) { int i, healthdecrease; float f, alertness, easyfragger, vis; float squaredist, cursquaredist; aas_entityinfo_t entinfo, curenemyinfo; vec3_t dir, angles; alertness = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_ALERTNESS, 0, 1); easyfragger = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_EASY_FRAGGER, 0, 1); //check if the health decreased healthdecrease = bs->lasthealth > bs->inventory[INVENTORY_HEALTH]; //remember the current health value bs->lasthealth = bs->inventory[INVENTORY_HEALTH]; // if (curenemy >= 0) { BotEntityInfo(curenemy, &curenemyinfo); if (EntityCarriesFlag(&curenemyinfo)) return qfalse; VectorSubtract(curenemyinfo.origin, bs->origin, dir); cursquaredist = VectorLengthSquared(dir); } else { cursquaredist = 0; } // for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { if (i == bs->client) continue; //if it's the current enemy if (i == curenemy) continue; // BotEntityInfo(i, &entinfo); // if (!entinfo.valid) continue; //if the enemy isn't dead and the enemy isn't the bot self if (EntityIsDead(&entinfo) || entinfo.number == bs->entitynum) continue; //if the enemy is invisible and not shooting if (EntityIsInvisible(&entinfo) && !EntityIsShooting(&entinfo)) { continue; } //if not an easy fragger don't shoot at chatting players if (easyfragger < 0.5 && EntityIsChatting(&entinfo)) continue; // if (lastteleport_time > FloatTime() - 3) { VectorSubtract(entinfo.origin, lastteleport_origin, dir); if (VectorLengthSquared(dir) < Square(70)) continue; } //calculate the distance towards the enemy VectorSubtract(entinfo.origin, bs->origin, dir); squaredist = VectorLengthSquared(dir); //if this entity is not carrying a flag if (!EntityCarriesFlag(&entinfo)) { //if this enemy is further away than the current one if (curenemy >= 0 && squaredist > cursquaredist) continue; } //end if //if the bot has no if (squaredist > Square(900.0 + alertness * 4000.0)) continue; //if on the same team if (BotSameTeam(bs, i)) continue; //if the bot's health decreased or the enemy is shooting if (curenemy < 0 && (healthdecrease || EntityIsShooting(&entinfo))) f = 360; else f = 90 + 90 - (90 - (squaredist > Square(810) ? Square(810) : squaredist) / (810 * 9)); //check if the enemy is visible vis = BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, f, i); if (vis <= 0) continue; //if the enemy is quite far away, not shooting and the bot is not damaged if (curenemy < 0 && squaredist > Square(100) && !healthdecrease && !EntityIsShooting(&entinfo)) { //check if we can avoid this enemy VectorSubtract(bs->origin, entinfo.origin, dir); vectoangles(dir, angles); //if the bot isn't in the fov of the enemy if (!InFieldOfVision(entinfo.angles, 90, angles)) { //update some stuff for this enemy BotUpdateBattleInventory(bs, i); //if the bot doesn't really want to fight if (BotWantsToRetreat(bs)) continue; } } //found an enemy bs->enemy = entinfo.number; if (curenemy >= 0) bs->enemysight_time = FloatTime() - 2; else bs->enemysight_time = FloatTime(); bs->enemysuicide = qfalse; bs->enemydeath_time = 0; bs->enemyvisible_time = FloatTime(); return qtrue; } return qfalse; } /* ================== BotTeamFlagCarrierVisible ================== */ int BotTeamFlagCarrierVisible(bot_state_t *bs) { int i; float vis; aas_entityinfo_t entinfo; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { if (i == bs->client) continue; // BotEntityInfo(i, &entinfo); //if this player is active if (!entinfo.valid) continue; //if this player is carrying a flag if (!EntityCarriesFlag(&entinfo)) continue; //if the flag carrier is not on the same team if (!BotSameTeam(bs, i)) continue; //if the flag carrier is not visible vis = BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, i); if (vis <= 0) continue; // return i; } return -1; } /* ================== BotTeamFlagCarrier ================== */ int BotTeamFlagCarrier(bot_state_t *bs) { int i; aas_entityinfo_t entinfo; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { if (i == bs->client) continue; // BotEntityInfo(i, &entinfo); //if this player is active if (!entinfo.valid) continue; //if this player is carrying a flag if (!EntityCarriesFlag(&entinfo)) continue; //if the flag carrier is not on the same team if (!BotSameTeam(bs, i)) continue; // return i; } return -1; } /* ================== BotEnemyFlagCarrierVisible ================== */ int BotEnemyFlagCarrierVisible(bot_state_t *bs) { int i; float vis; aas_entityinfo_t entinfo; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { if (i == bs->client) continue; // BotEntityInfo(i, &entinfo); //if this player is active if (!entinfo.valid) continue; //if this player is carrying a flag if (!EntityCarriesFlag(&entinfo)) continue; //if the flag carrier is on the same team if (BotSameTeam(bs, i)) continue; //if the flag carrier is not visible vis = BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, i); if (vis <= 0) continue; // return i; } return -1; } /* ================== BotVisibleTeamMatesAndEnemies ================== */ void BotVisibleTeamMatesAndEnemies(bot_state_t *bs, int *teammates, int *enemies, float range) { int i; float vis; aas_entityinfo_t entinfo; vec3_t dir; if (teammates) *teammates = 0; if (enemies) *enemies = 0; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { if (i == bs->client) continue; // BotEntityInfo(i, &entinfo); //if this player is active if (!entinfo.valid) continue; //if this player is carrying a flag if (!EntityCarriesFlag(&entinfo)) continue; //if not within range VectorSubtract(entinfo.origin, bs->origin, dir); if (VectorLengthSquared(dir) > Square(range)) continue; //if the flag carrier is not visible vis = BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, i); if (vis <= 0) continue; //if the flag carrier is on the same team if (BotSameTeam(bs, i)) { if (teammates) (*teammates)++; } else { if (enemies) (*enemies)++; } } } /* ================== BotAimAtEnemy ================== */ void BotAimAtEnemy(bot_state_t *bs) { int i, enemyvisible; float dist, f, aim_skill, aim_accuracy, speed, reactiontime; vec3_t dir, bestorigin, end, start, groundtarget, cmdmove, enemyvelocity; vec3_t mins = {-4,-4,-4}, maxs = {4, 4, 4}; weaponinfo_t wi; aas_entityinfo_t entinfo; bot_goal_t goal; bsp_trace_t trace; vec3_t target; //if the bot has no enemy if (bs->enemy < 0) { return; } //get the enemy entity information BotEntityInfo(bs->enemy, &entinfo); //if this is not a player (should be an obelisk) if (bs->enemy >= MAX_CLIENTS) { //if the obelisk is visible VectorCopy(entinfo.origin, target); //aim at the obelisk VectorSubtract(target, bs->eye, dir); vectoangles(dir, bs->ideal_viewangles); //set the aim target before trying to attack VectorCopy(target, bs->aimtarget); return; } // //BotAI_Print(PRT_MESSAGE, "client %d: aiming at client %d\n", bs->entitynum, bs->enemy); // aim_skill = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_SKILL, 0, 1); aim_accuracy = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY, 0, 1); // if (aim_skill > 0.95) { //don't aim too early reactiontime = 0.5 * trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_REACTIONTIME, 0, 1); if (bs->enemysight_time > FloatTime() - reactiontime) return; if (bs->teleport_time > FloatTime() - reactiontime) return; } //get the weapon information trap_BotGetWeaponInfo(bs->ws, bs->weaponnum, &wi); //get the weapon specific aim accuracy and or aim skill if (wi.number == WP_MACHINEGUN) { aim_accuracy = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY_MACHINEGUN, 0, 1); } else if (wi.number == WP_SHOTGUN) { aim_accuracy = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY_SHOTGUN, 0, 1); } else if (wi.number == WP_GRENADE_LAUNCHER) { aim_accuracy = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY_GRENADELAUNCHER, 0, 1); aim_skill = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_SKILL_GRENADELAUNCHER, 0, 1); } else if (wi.number == WP_ROCKET_LAUNCHER) { aim_accuracy = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY_ROCKETLAUNCHER, 0, 1); aim_skill = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_SKILL_ROCKETLAUNCHER, 0, 1); } else if (wi.number == WP_LIGHTNING) { aim_accuracy = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY_LIGHTNING, 0, 1); } else if (wi.number == WP_RAILGUN) { aim_accuracy = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY_RAILGUN, 0, 1); } else if (wi.number == WP_PLASMAGUN) { aim_accuracy = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY_PLASMAGUN, 0, 1); aim_skill = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_SKILL_PLASMAGUN, 0, 1); } else if (wi.number == WP_BFG) { aim_accuracy = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY_BFG10K, 0, 1); aim_skill = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_SKILL_BFG10K, 0, 1); } // if (aim_accuracy <= 0) aim_accuracy = 0.0001f; //get the enemy entity information BotEntityInfo(bs->enemy, &entinfo); //if the enemy is invisible then shoot crappy most of the time if (EntityIsInvisible(&entinfo)) { if (random() > 0.1) aim_accuracy *= 0.4f; } // VectorSubtract(entinfo.origin, entinfo.lastvisorigin, enemyvelocity); VectorScale(enemyvelocity, 1 / entinfo.update_time, enemyvelocity); //enemy origin and velocity is remembered every 0.5 seconds if (bs->enemyposition_time < FloatTime()) { // bs->enemyposition_time = FloatTime() + 0.5; VectorCopy(enemyvelocity, bs->enemyvelocity); VectorCopy(entinfo.origin, bs->enemyorigin); } //if not extremely skilled if (aim_skill < 0.9) { VectorSubtract(entinfo.origin, bs->enemyorigin, dir); //if the enemy moved a bit if (VectorLengthSquared(dir) > Square(48)) { //if the enemy changed direction if (DotProduct(bs->enemyvelocity, enemyvelocity) < 0) { //aim accuracy should be worse now aim_accuracy *= 0.7f; } } } //check visibility of enemy enemyvisible = BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy); //if the enemy is visible if (enemyvisible) { // VectorCopy(entinfo.origin, bestorigin); bestorigin[2] += 8; //get the start point shooting from //NOTE: the x and y projectile start offsets are ignored VectorCopy(bs->origin, start); start[2] += bs->cur_ps.viewheight; start[2] += wi.offset[2]; // BotAI_Trace(&trace, start, mins, maxs, bestorigin, bs->entitynum, MASK_SHOT); //if the enemy is NOT hit if (trace.fraction <= 1 && trace.ent != entinfo.number) { bestorigin[2] += 16; } //if it is not an instant hit weapon the bot might want to predict the enemy if (wi.speed) { // VectorSubtract(bestorigin, bs->origin, dir); dist = VectorLength(dir); VectorSubtract(entinfo.origin, bs->enemyorigin, dir); //if the enemy is NOT pretty far away and strafing just small steps left and right if (!(dist > 100 && VectorLengthSquared(dir) < Square(32))) { //if skilled anough do exact prediction if (aim_skill > 0.8 && //if the weapon is ready to fire bs->cur_ps.weaponstate == WEAPON_READY) { aas_clientmove_t move; vec3_t origin; VectorSubtract(entinfo.origin, bs->origin, dir); //distance towards the enemy dist = VectorLength(dir); //direction the enemy is moving in VectorSubtract(entinfo.origin, entinfo.lastvisorigin, dir); // VectorScale(dir, 1 / entinfo.update_time, dir); // VectorCopy(entinfo.origin, origin); origin[2] += 1; // VectorClear(cmdmove); //AAS_ClearShownDebugLines(); trap_AAS_PredictClientMovement(&move, bs->enemy, origin, PRESENCE_CROUCH, qfalse, dir, cmdmove, 0, dist * 10 / wi.speed, 0.1f, 0, 0, qfalse); VectorCopy(move.endpos, bestorigin); //BotAI_Print(PRT_MESSAGE, "%1.1f predicted speed = %f, frames = %f\n", FloatTime(), VectorLength(dir), dist * 10 / wi.speed); } //if not that skilled do linear prediction else if (aim_skill > 0.4) { VectorSubtract(entinfo.origin, bs->origin, dir); //distance towards the enemy dist = VectorLength(dir); //direction the enemy is moving in VectorSubtract(entinfo.origin, entinfo.lastvisorigin, dir); dir[2] = 0; // speed = VectorNormalize(dir) / entinfo.update_time; //botimport.Print(PRT_MESSAGE, "speed = %f, wi->speed = %f\n", speed, wi->speed); //best spot to aim at VectorMA(entinfo.origin, (dist / wi.speed) * speed, dir, bestorigin); } } } //if the projectile does radial damage if (aim_skill > 0.6 && wi.proj.damagetype & DAMAGETYPE_RADIAL) { //if the enemy isn't standing significantly higher than the bot if (entinfo.origin[2] < bs->origin[2] + 16) { //try to aim at the ground in front of the enemy VectorCopy(entinfo.origin, end); end[2] -= 64; BotAI_Trace(&trace, entinfo.origin, NULL, NULL, end, entinfo.number, MASK_SHOT); // VectorCopy(bestorigin, groundtarget); if (trace.startsolid) groundtarget[2] = entinfo.origin[2] - 16; else groundtarget[2] = trace.endpos[2] - 8; //trace a line from projectile start to ground target BotAI_Trace(&trace, start, NULL, NULL, groundtarget, bs->entitynum, MASK_SHOT); //if hitpoint is not vertically too far from the ground target if (fabs(trace.endpos[2] - groundtarget[2]) < 50) { VectorSubtract(trace.endpos, groundtarget, dir); //if the hitpoint is near anough the ground target if (VectorLengthSquared(dir) < Square(60)) { VectorSubtract(trace.endpos, start, dir); //if the hitpoint is far anough from the bot if (VectorLengthSquared(dir) > Square(100)) { //check if the bot is visible from the ground target trace.endpos[2] += 1; BotAI_Trace(&trace, trace.endpos, NULL, NULL, entinfo.origin, entinfo.number, MASK_SHOT); if (trace.fraction >= 1) { //botimport.Print(PRT_MESSAGE, "%1.1f aiming at ground\n", AAS_Time()); VectorCopy(groundtarget, bestorigin); } } } } } } bestorigin[0] += 20 * crandom() * (1 - aim_accuracy); bestorigin[1] += 20 * crandom() * (1 - aim_accuracy); bestorigin[2] += 10 * crandom() * (1 - aim_accuracy); } else { // VectorCopy(bs->lastenemyorigin, bestorigin); bestorigin[2] += 8; //if the bot is skilled anough if (aim_skill > 0.5) { //do prediction shots around corners if (wi.number == WP_BFG || wi.number == WP_ROCKET_LAUNCHER || wi.number == WP_GRENADE_LAUNCHER) { //create the chase goal goal.entitynum = bs->client; goal.areanum = bs->areanum; VectorCopy(bs->eye, goal.origin); VectorSet(goal.mins, -8, -8, -8); VectorSet(goal.maxs, 8, 8, 8); // if (trap_BotPredictVisiblePosition(bs->lastenemyorigin, bs->lastenemyareanum, &goal, TFL_DEFAULT, target)) { VectorSubtract(target, bs->eye, dir); if (VectorLengthSquared(dir) > Square(80)) { VectorCopy(target, bestorigin); bestorigin[2] -= 20; } } aim_accuracy = 1; } } } // if (enemyvisible) { BotAI_Trace(&trace, bs->eye, NULL, NULL, bestorigin, bs->entitynum, MASK_SHOT); VectorCopy(trace.endpos, bs->aimtarget); } else { VectorCopy(bestorigin, bs->aimtarget); } //get aim direction VectorSubtract(bestorigin, bs->eye, dir); // if (wi.number == WP_MACHINEGUN || wi.number == WP_SHOTGUN || wi.number == WP_LIGHTNING || wi.number == WP_RAILGUN) { //distance towards the enemy dist = VectorLength(dir); if (dist > 150) dist = 150; f = 0.6 + dist / 150 * 0.4; aim_accuracy *= f; } //add some random stuff to the aim direction depending on the aim accuracy if (aim_accuracy < 0.8) { VectorNormalize(dir); for (i = 0; i < 3; i++) dir[i] += 0.3 * crandom() * (1 - aim_accuracy); } //set the ideal view angles vectoangles(dir, bs->ideal_viewangles); //take the weapon spread into account for lower skilled bots bs->ideal_viewangles[PITCH] += 6 * wi.vspread * crandom() * (1 - aim_accuracy); bs->ideal_viewangles[PITCH] = AngleMod(bs->ideal_viewangles[PITCH]); bs->ideal_viewangles[YAW] += 6 * wi.hspread * crandom() * (1 - aim_accuracy); bs->ideal_viewangles[YAW] = AngleMod(bs->ideal_viewangles[YAW]); //if the bots should be really challenging if (bot_challenge.integer) { //if the bot is really accurate and has the enemy in view for some time if (aim_accuracy > 0.9 && bs->enemysight_time < FloatTime() - 1) { //set the view angles directly if (bs->ideal_viewangles[PITCH] > 180) bs->ideal_viewangles[PITCH] -= 360; VectorCopy(bs->ideal_viewangles, bs->viewangles); trap_EA_View(bs->client, bs->viewangles); } } } /* ================== BotCheckAttack ================== */ void BotCheckAttack(bot_state_t *bs) { float points, reactiontime, fov, firethrottle; int attackentity; bsp_trace_t bsptrace; //float selfpreservation; vec3_t forward, right, start, end, dir, angles; weaponinfo_t wi; bsp_trace_t trace; aas_entityinfo_t entinfo; vec3_t mins = {-8, -8, -8}, maxs = {8, 8, 8}; attackentity = bs->enemy; // BotEntityInfo(attackentity, &entinfo); // if not attacking a player if (attackentity >= MAX_CLIENTS) { } // reactiontime = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_REACTIONTIME, 0, 1); if (bs->enemysight_time > FloatTime() - reactiontime) return; if (bs->teleport_time > FloatTime() - reactiontime) return; //if changing weapons if (bs->weaponchange_time > FloatTime() - 0.1) return; //check fire throttle characteristic if (bs->firethrottlewait_time > FloatTime()) return; firethrottle = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_FIRETHROTTLE, 0, 1); if (bs->firethrottleshoot_time < FloatTime()) { if (random() > firethrottle) { bs->firethrottlewait_time = FloatTime() + firethrottle; bs->firethrottleshoot_time = 0; } else { bs->firethrottleshoot_time = FloatTime() + 1 - firethrottle; bs->firethrottlewait_time = 0; } } // // VectorSubtract(bs->aimtarget, bs->eye, dir); // if (bs->weaponnum == WP_GAUNTLET) { if (VectorLengthSquared(dir) > Square(60)) { return; } } if (VectorLengthSquared(dir) < Square(100)) fov = 120; else fov = 50; // vectoangles(dir, angles); if (!InFieldOfVision(bs->viewangles, fov, angles)) return; BotAI_Trace(&bsptrace, bs->eye, NULL, NULL, bs->aimtarget, bs->client, CONTENTS_SOLID|CONTENTS_PLAYERCLIP); if (bsptrace.fraction < 1 && bsptrace.ent != attackentity) return; //get the weapon info trap_BotGetWeaponInfo(bs->ws, bs->weaponnum, &wi); //get the start point shooting from VectorCopy(bs->origin, start); start[2] += bs->cur_ps.viewheight; AngleVectors(bs->viewangles, forward, right, NULL); start[0] += forward[0] * wi.offset[0] + right[0] * wi.offset[1]; start[1] += forward[1] * wi.offset[0] + right[1] * wi.offset[1]; start[2] += forward[2] * wi.offset[0] + right[2] * wi.offset[1] + wi.offset[2]; //end point aiming at VectorMA(start, 1000, forward, end); //a little back to make sure not inside a very close enemy VectorMA(start, -12, forward, start); BotAI_Trace(&trace, start, mins, maxs, end, bs->entitynum, MASK_SHOT); //if the entity is a client if (trace.ent > 0 && trace.ent <= MAX_CLIENTS) { if (trace.ent != attackentity) { //if a teammate is hit if (BotSameTeam(bs, trace.ent)) return; } } //if won't hit the enemy or not attacking a player (obelisk) if (trace.ent != attackentity || attackentity >= MAX_CLIENTS) { //if the projectile does radial damage if (wi.proj.damagetype & DAMAGETYPE_RADIAL) { if (trace.fraction * 1000 < wi.proj.radius) { points = (wi.proj.damage - 0.5 * trace.fraction * 1000) * 0.5; if (points > 0) { return; } } //FIXME: check if a teammate gets radial damage } } //if fire has to be release to activate weapon if (wi.flags & WFL_FIRERELEASED) { if (bs->flags & BFL_ATTACKED) { trap_EA_Attack(bs->client); } } else { trap_EA_Attack(bs->client); } bs->flags ^= BFL_ATTACKED; } /* ================== BotMapScripts ================== */ void BotMapScripts(bot_state_t *bs) { char info[1024]; char mapname[128]; int i, shootbutton; float aim_accuracy; aas_entityinfo_t entinfo; vec3_t dir; trap_GetServerinfo(info, sizeof(info)); strncpy(mapname, Info_ValueForKey( info, "mapname" ), sizeof(mapname)-1); mapname[sizeof(mapname)-1] = '\0'; if (!Q_stricmp(mapname, "q3tourney6")) { vec3_t mins = {700, 204, 672}, maxs = {964, 468, 680}; vec3_t buttonorg = {304, 352, 920}; //NOTE: NEVER use the func_bobbing in q3tourney6 bs->tfl &= ~TFL_FUNCBOB; //if the bot is below the bounding box if (bs->origin[0] > mins[0] && bs->origin[0] < maxs[0]) { if (bs->origin[1] > mins[1] && bs->origin[1] < maxs[1]) { if (bs->origin[2] < mins[2]) { return; } } } shootbutton = qfalse; //if an enemy is below this bounding box then shoot the button for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { if (i == bs->client) continue; // BotEntityInfo(i, &entinfo); // if (!entinfo.valid) continue; //if the enemy isn't dead and the enemy isn't the bot self if (EntityIsDead(&entinfo) || entinfo.number == bs->entitynum) continue; // if (entinfo.origin[0] > mins[0] && entinfo.origin[0] < maxs[0]) { if (entinfo.origin[1] > mins[1] && entinfo.origin[1] < maxs[1]) { if (entinfo.origin[2] < mins[2]) { //if there's a team mate below the crusher if (BotSameTeam(bs, i)) { shootbutton = qfalse; break; } else { shootbutton = qtrue; } } } } } if (shootbutton) { bs->flags |= BFL_IDEALVIEWSET; VectorSubtract(buttonorg, bs->eye, dir); vectoangles(dir, bs->ideal_viewangles); aim_accuracy = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY, 0, 1); bs->ideal_viewangles[PITCH] += 8 * crandom() * (1 - aim_accuracy); bs->ideal_viewangles[PITCH] = AngleMod(bs->ideal_viewangles[PITCH]); bs->ideal_viewangles[YAW] += 8 * crandom() * (1 - aim_accuracy); bs->ideal_viewangles[YAW] = AngleMod(bs->ideal_viewangles[YAW]); // if (InFieldOfVision(bs->viewangles, 20, bs->ideal_viewangles)) { trap_EA_Attack(bs->client); } } } else if (!Q_stricmp(mapname, "mpq3tourney6")) { //NOTE: NEVER use the func_bobbing in mpq3tourney6 bs->tfl &= ~TFL_FUNCBOB; } } /* ================== BotSetMovedir ================== */ // bk001205 - made these static static vec3_t VEC_UP = {0, -1, 0}; static vec3_t MOVEDIR_UP = {0, 0, 1}; static vec3_t VEC_DOWN = {0, -2, 0}; static vec3_t MOVEDIR_DOWN = {0, 0, -1}; void BotSetMovedir(vec3_t angles, vec3_t movedir) { if (VectorCompare(angles, VEC_UP)) { VectorCopy(MOVEDIR_UP, movedir); } else if (VectorCompare(angles, VEC_DOWN)) { VectorCopy(MOVEDIR_DOWN, movedir); } else { AngleVectors(angles, movedir, NULL, NULL); } } /* ================== BotModelMinsMaxs this is ugly ================== */ int BotModelMinsMaxs(int modelindex, int eType, int contents, vec3_t mins, vec3_t maxs) { gentity_t *ent; int i; ent = &g_entities[0]; for (i = 0; i < level.num_entities; i++, ent++) { if ( !ent->inuse ) { continue; } if ( eType && ent->s.eType != eType) { continue; } if ( contents && ent->r.contents != contents) { continue; } if (ent->s.modelindex == modelindex) { if (mins) VectorAdd(ent->r.currentOrigin, ent->r.mins, mins); if (maxs) VectorAdd(ent->r.currentOrigin, ent->r.maxs, maxs); return i; } } if (mins) VectorClear(mins); if (maxs) VectorClear(maxs); return 0; } /* ================== BotFuncButtonGoal ================== */ int BotFuncButtonActivateGoal(bot_state_t *bs, int bspent, bot_activategoal_t *activategoal) { int i, areas[10], numareas, modelindex, entitynum; char model[128]; float lip, dist, health, angle; vec3_t size, start, end, mins, maxs, angles, points[10]; vec3_t movedir, origin, goalorigin, bboxmins, bboxmaxs; vec3_t extramins = {1, 1, 1}, extramaxs = {-1, -1, -1}; bsp_trace_t bsptrace; activategoal->shoot = qfalse; VectorClear(activategoal->target); //create a bot goal towards the button trap_AAS_ValueForBSPEpairKey(bspent, "model", model, sizeof(model)); if (!*model) return qfalse; modelindex = atoi(model+1); if (!modelindex) return qfalse; VectorClear(angles); entitynum = BotModelMinsMaxs(modelindex, ET_MOVER, 0, mins, maxs); //get the lip of the button trap_AAS_FloatForBSPEpairKey(bspent, "lip", &lip); if (!lip) lip = 4; //get the move direction from the angle trap_AAS_FloatForBSPEpairKey(bspent, "angle", &angle); VectorSet(angles, 0, angle, 0); BotSetMovedir(angles, movedir); //button size VectorSubtract(maxs, mins, size); //button origin VectorAdd(mins, maxs, origin); VectorScale(origin, 0.5, origin); //touch distance of the button dist = fabs(movedir[0]) * size[0] + fabs(movedir[1]) * size[1] + fabs(movedir[2]) * size[2]; dist *= 0.5; // trap_AAS_FloatForBSPEpairKey(bspent, "health", &health); //if the button is shootable if (health) { //calculate the shoot target VectorMA(origin, -dist, movedir, goalorigin); // VectorCopy(goalorigin, activategoal->target); activategoal->shoot = qtrue; // BotAI_Trace(&bsptrace, bs->eye, NULL, NULL, goalorigin, bs->entitynum, MASK_SHOT); // if the button is visible from the current position if (bsptrace.fraction >= 1.0 || bsptrace.ent == entitynum) { // activategoal->goal.entitynum = entitynum; //NOTE: this is the entity number of the shootable button activategoal->goal.number = 0; activategoal->goal.flags = 0; VectorCopy(bs->origin, activategoal->goal.origin); activategoal->goal.areanum = bs->areanum; VectorSet(activategoal->goal.mins, -8, -8, -8); VectorSet(activategoal->goal.maxs, 8, 8, 8); // return qtrue; } else { //create a goal from where the button is visible and shoot at the button from there //add bounding box size to the dist trap_AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, bboxmins, bboxmaxs); for (i = 0; i < 3; i++) { if (movedir[i] < 0) dist += fabs(movedir[i]) * fabs(bboxmaxs[i]); else dist += fabs(movedir[i]) * fabs(bboxmins[i]); } //calculate the goal origin VectorMA(origin, -dist, movedir, goalorigin); // VectorCopy(goalorigin, start); start[2] += 24; VectorCopy(start, end); end[2] -= 512; numareas = trap_AAS_TraceAreas(start, end, areas, points, 10); // for (i = numareas-1; i >= 0; i--) { if (trap_AAS_AreaReachability(areas[i])) { break; } } if (i < 0) { // FIXME: trace forward and maybe in other directions to find a valid area } if (i >= 0) { // VectorCopy(points[i], activategoal->goal.origin); activategoal->goal.areanum = areas[i]; VectorSet(activategoal->goal.mins, 8, 8, 8); VectorSet(activategoal->goal.maxs, -8, -8, -8); // for (i = 0; i < 3; i++) { if (movedir[i] < 0) activategoal->goal.maxs[i] += fabs(movedir[i]) * fabs(extramaxs[i]); else activategoal->goal.mins[i] += fabs(movedir[i]) * fabs(extramins[i]); } //end for // activategoal->goal.entitynum = entitynum; activategoal->goal.number = 0; activategoal->goal.flags = 0; return qtrue; } } return qfalse; } else { //add bounding box size to the dist trap_AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, bboxmins, bboxmaxs); for (i = 0; i < 3; i++) { if (movedir[i] < 0) dist += fabs(movedir[i]) * fabs(bboxmaxs[i]); else dist += fabs(movedir[i]) * fabs(bboxmins[i]); } //calculate the goal origin VectorMA(origin, -dist, movedir, goalorigin); // VectorCopy(goalorigin, start); start[2] += 24; VectorCopy(start, end); end[2] -= 100; numareas = trap_AAS_TraceAreas(start, end, areas, NULL, 10); // for (i = 0; i < numareas; i++) { if (trap_AAS_AreaReachability(areas[i])) { break; } } if (i < numareas) { // VectorCopy(origin, activategoal->goal.origin); activategoal->goal.areanum = areas[i]; VectorSubtract(mins, origin, activategoal->goal.mins); VectorSubtract(maxs, origin, activategoal->goal.maxs); // for (i = 0; i < 3; i++) { if (movedir[i] < 0) activategoal->goal.maxs[i] += fabs(movedir[i]) * fabs(extramaxs[i]); else activategoal->goal.mins[i] += fabs(movedir[i]) * fabs(extramins[i]); } //end for // activategoal->goal.entitynum = entitynum; activategoal->goal.number = 0; activategoal->goal.flags = 0; return qtrue; } } return qfalse; } /* ================== BotFuncDoorGoal ================== */ int BotFuncDoorActivateGoal(bot_state_t *bs, int bspent, bot_activategoal_t *activategoal) { int modelindex, entitynum; char model[MAX_INFO_STRING]; vec3_t mins, maxs, origin, angles; //shoot at the shootable door trap_AAS_ValueForBSPEpairKey(bspent, "model", model, sizeof(model)); if (!*model) return qfalse; modelindex = atoi(model+1); if (!modelindex) return qfalse; VectorClear(angles); entitynum = BotModelMinsMaxs(modelindex, ET_MOVER, 0, mins, maxs); //door origin VectorAdd(mins, maxs, origin); VectorScale(origin, 0.5, origin); VectorCopy(origin, activategoal->target); activategoal->shoot = qtrue; // activategoal->goal.entitynum = entitynum; //NOTE: this is the entity number of the shootable door activategoal->goal.number = 0; activategoal->goal.flags = 0; VectorCopy(bs->origin, activategoal->goal.origin); activategoal->goal.areanum = bs->areanum; VectorSet(activategoal->goal.mins, -8, -8, -8); VectorSet(activategoal->goal.maxs, 8, 8, 8); return qtrue; } /* ================== BotTriggerMultipleGoal ================== */ int BotTriggerMultipleActivateGoal(bot_state_t *bs, int bspent, bot_activategoal_t *activategoal) { int i, areas[10], numareas, modelindex, entitynum; char model[128]; vec3_t start, end, mins, maxs, angles; vec3_t origin, goalorigin; activategoal->shoot = qfalse; VectorClear(activategoal->target); //create a bot goal towards the trigger trap_AAS_ValueForBSPEpairKey(bspent, "model", model, sizeof(model)); if (!*model) return qfalse; modelindex = atoi(model+1); if (!modelindex) return qfalse; VectorClear(angles); entitynum = BotModelMinsMaxs(modelindex, 0, CONTENTS_TRIGGER, mins, maxs); //trigger origin VectorAdd(mins, maxs, origin); VectorScale(origin, 0.5, origin); VectorCopy(origin, goalorigin); // VectorCopy(goalorigin, start); start[2] += 24; VectorCopy(start, end); end[2] -= 100; numareas = trap_AAS_TraceAreas(start, end, areas, NULL, 10); // for (i = 0; i < numareas; i++) { if (trap_AAS_AreaReachability(areas[i])) { break; } } if (i < numareas) { VectorCopy(origin, activategoal->goal.origin); activategoal->goal.areanum = areas[i]; VectorSubtract(mins, origin, activategoal->goal.mins); VectorSubtract(maxs, origin, activategoal->goal.maxs); // activategoal->goal.entitynum = entitynum; activategoal->goal.number = 0; activategoal->goal.flags = 0; return qtrue; } return qfalse; } /* ================== BotPopFromActivateGoalStack ================== */ int BotPopFromActivateGoalStack(bot_state_t *bs) { if (!bs->activatestack) return qfalse; BotEnableActivateGoalAreas(bs->activatestack, qtrue); bs->activatestack->inuse = qfalse; bs->activatestack->justused_time = FloatTime(); bs->activatestack = bs->activatestack->next; return qtrue; } /* ================== BotPushOntoActivateGoalStack ================== */ int BotPushOntoActivateGoalStack(bot_state_t *bs, bot_activategoal_t *activategoal) { int i, best; float besttime; best = -1; besttime = FloatTime() + 9999; // for (i = 0; i < MAX_ACTIVATESTACK; i++) { if (!bs->activategoalheap[i].inuse) { if (bs->activategoalheap[i].justused_time < besttime) { besttime = bs->activategoalheap[i].justused_time; best = i; } } } if (best != -1) { memcpy(&bs->activategoalheap[best], activategoal, sizeof(bot_activategoal_t)); bs->activategoalheap[best].inuse = qtrue; bs->activategoalheap[best].next = bs->activatestack; bs->activatestack = &bs->activategoalheap[best]; return qtrue; } return qfalse; } /* ================== BotClearActivateGoalStack ================== */ void BotClearActivateGoalStack(bot_state_t *bs) { while(bs->activatestack) BotPopFromActivateGoalStack(bs); } /* ================== BotEnableActivateGoalAreas ================== */ void BotEnableActivateGoalAreas(bot_activategoal_t *activategoal, int enable) { int i; if (activategoal->areasdisabled == !enable) return; for (i = 0; i < activategoal->numareas; i++) trap_AAS_EnableRoutingArea( activategoal->areas[i], enable ); activategoal->areasdisabled = !enable; } /* ================== BotIsGoingToActivateEntity ================== */ int BotIsGoingToActivateEntity(bot_state_t *bs, int entitynum) { bot_activategoal_t *a; int i; for (a = bs->activatestack; a; a = a->next) { if (a->time < FloatTime()) continue; if (a->goal.entitynum == entitynum) return qtrue; } for (i = 0; i < MAX_ACTIVATESTACK; i++) { if (bs->activategoalheap[i].inuse) continue; // if (bs->activategoalheap[i].goal.entitynum == entitynum) { // if the bot went for this goal less than 2 seconds ago if (bs->activategoalheap[i].justused_time > FloatTime() - 2) return qtrue; } } return qfalse; } /* ================== BotGetActivateGoal returns the number of the bsp entity to activate goal->entitynum will be set to the game entity to activate ================== */ //#define OBSTACLEDEBUG int BotGetActivateGoal(bot_state_t *bs, int entitynum, bot_activategoal_t *activategoal) { int i, ent, cur_entities[10], spawnflags, modelindex, areas[MAX_ACTIVATEAREAS*2], numareas, t; char model[MAX_INFO_STRING], tmpmodel[128]; char target[128], classname[128]; float health; char targetname[10][128]; aas_entityinfo_t entinfo; aas_areainfo_t areainfo; vec3_t origin, angles, absmins, absmaxs; memset(activategoal, 0, sizeof(bot_activategoal_t)); BotEntityInfo(entitynum, &entinfo); Com_sprintf(model, sizeof( model ), "*%d", entinfo.modelindex); for (ent = trap_AAS_NextBSPEntity(0); ent; ent = trap_AAS_NextBSPEntity(ent)) { if (!trap_AAS_ValueForBSPEpairKey(ent, "model", tmpmodel, sizeof(tmpmodel))) continue; if (!strcmp(model, tmpmodel)) break; } if (!ent) { BotAI_Print(PRT_ERROR, "BotGetActivateGoal: no entity found with model %s\n", model); return 0; } trap_AAS_ValueForBSPEpairKey(ent, "classname", classname, sizeof(classname)); if (!classname) { BotAI_Print(PRT_ERROR, "BotGetActivateGoal: entity with model %s has no classname\n", model); return 0; } //if it is a door if (!strcmp(classname, "func_door")) { if (trap_AAS_FloatForBSPEpairKey(ent, "health", &health)) { //if the door has health then the door must be shot to open if (health) { BotFuncDoorActivateGoal(bs, ent, activategoal); return ent; } } // trap_AAS_IntForBSPEpairKey(ent, "spawnflags", &spawnflags); // if the door starts open then just wait for the door to return if ( spawnflags & 1 ) return 0; //get the door origin if (!trap_AAS_VectorForBSPEpairKey(ent, "origin", origin)) { VectorClear(origin); } //if the door is open or opening already if (!VectorCompare(origin, entinfo.origin)) return 0; // store all the areas the door is in trap_AAS_ValueForBSPEpairKey(ent, "model", model, sizeof(model)); if (*model) { modelindex = atoi(model+1); if (modelindex) { VectorClear(angles); BotModelMinsMaxs(modelindex, ET_MOVER, 0, absmins, absmaxs); // numareas = trap_AAS_BBoxAreas(absmins, absmaxs, areas, MAX_ACTIVATEAREAS*2); // store the areas with reachabilities first for (i = 0; i < numareas; i++) { if (activategoal->numareas >= MAX_ACTIVATEAREAS) break; if ( !trap_AAS_AreaReachability(areas[i]) ) { continue; } trap_AAS_AreaInfo(areas[i], &areainfo); if (areainfo.contents & AREACONTENTS_MOVER) { activategoal->areas[activategoal->numareas++] = areas[i]; } } // store any remaining areas for (i = 0; i < numareas; i++) { if (activategoal->numareas >= MAX_ACTIVATEAREAS) break; if ( trap_AAS_AreaReachability(areas[i]) ) { continue; } trap_AAS_AreaInfo(areas[i], &areainfo); if (areainfo.contents & AREACONTENTS_MOVER) { activategoal->areas[activategoal->numareas++] = areas[i]; } } } } } // if the bot is blocked by or standing on top of a button if (!strcmp(classname, "func_button")) { return 0; } // get the targetname so we can find an entity with a matching target if (!trap_AAS_ValueForBSPEpairKey(ent, "targetname", targetname[0], sizeof(targetname[0]))) { if (bot_developer.integer) { BotAI_Print(PRT_ERROR, "BotGetActivateGoal: entity with model \"%s\" has no targetname\n", model); } return 0; } // allow tree-like activation cur_entities[0] = trap_AAS_NextBSPEntity(0); for (i = 0; i >= 0 && i < 10;) { for (ent = cur_entities[i]; ent; ent = trap_AAS_NextBSPEntity(ent)) { if (!trap_AAS_ValueForBSPEpairKey(ent, "target", target, sizeof(target))) continue; if (!strcmp(targetname[i], target)) { cur_entities[i] = trap_AAS_NextBSPEntity(ent); break; } } if (!ent) { if (bot_developer.integer) { BotAI_Print(PRT_ERROR, "BotGetActivateGoal: no entity with target \"%s\"\n", targetname[i]); } i--; continue; } if (!trap_AAS_ValueForBSPEpairKey(ent, "classname", classname, sizeof(classname))) { if (bot_developer.integer) { BotAI_Print(PRT_ERROR, "BotGetActivateGoal: entity with target \"%s\" has no classname\n", targetname[i]); } continue; } // BSP button model if (!strcmp(classname, "func_button")) { // if (!BotFuncButtonActivateGoal(bs, ent, activategoal)) continue; // if the bot tries to activate this button already if ( bs->activatestack && bs->activatestack->inuse && bs->activatestack->goal.entitynum == activategoal->goal.entitynum && bs->activatestack->time > FloatTime() && bs->activatestack->start_time < FloatTime() - 2) continue; // if the bot is in a reachability area if ( trap_AAS_AreaReachability(bs->areanum) ) { // disable all areas the blocking entity is in BotEnableActivateGoalAreas( activategoal, qfalse ); // t = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, activategoal->goal.areanum, bs->tfl); // if the button is not reachable if (!t) { continue; } activategoal->time = FloatTime() + t * 0.01 + 5; } return ent; } // invisible trigger multiple box else if (!strcmp(classname, "trigger_multiple")) { // if (!BotTriggerMultipleActivateGoal(bs, ent, activategoal)) continue; // if the bot tries to activate this trigger already if ( bs->activatestack && bs->activatestack->inuse && bs->activatestack->goal.entitynum == activategoal->goal.entitynum && bs->activatestack->time > FloatTime() && bs->activatestack->start_time < FloatTime() - 2) continue; // if the bot is in a reachability area if ( trap_AAS_AreaReachability(bs->areanum) ) { // disable all areas the blocking entity is in BotEnableActivateGoalAreas( activategoal, qfalse ); // t = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, activategoal->goal.areanum, bs->tfl); // if the trigger is not reachable if (!t) { continue; } activategoal->time = FloatTime() + t * 0.01 + 5; } return ent; } else if (!strcmp(classname, "func_timer")) { // just skip the func_timer continue; } // the actual button or trigger might be linked through a target_relay or target_delay else if (!strcmp(classname, "target_relay") || !strcmp(classname, "target_delay")) { if (trap_AAS_ValueForBSPEpairKey(ent, "targetname", targetname[i+1], sizeof(targetname[0]))) { i++; cur_entities[i] = trap_AAS_NextBSPEntity(0); } } } #ifdef OBSTACLEDEBUG BotAI_Print(PRT_ERROR, "BotGetActivateGoal: no valid activator for entity with target \"%s\"\n", targetname[0]); #endif return 0; } /* ================== BotGoForActivateGoal ================== */ int BotGoForActivateGoal(bot_state_t *bs, bot_activategoal_t *activategoal) { aas_entityinfo_t activateinfo; activategoal->inuse = qtrue; if (!activategoal->time) activategoal->time = FloatTime() + 10; activategoal->start_time = FloatTime(); BotEntityInfo(activategoal->goal.entitynum, &activateinfo); VectorCopy(activateinfo.origin, activategoal->origin); // if (BotPushOntoActivateGoalStack(bs, activategoal)) { // enter the activate entity AI node AIEnter_Seek_ActivateEntity(bs, "BotGoForActivateGoal"); return qtrue; } else { // enable any routing areas that were disabled BotEnableActivateGoalAreas(activategoal, qtrue); return qfalse; } } /* ================== BotPrintActivateGoalInfo ================== */ void BotPrintActivateGoalInfo(bot_state_t *bs, bot_activategoal_t *activategoal, int bspent) { char netname[MAX_NETNAME]; char classname[128]; char buf[128]; ClientName(bs->client, netname, sizeof(netname)); trap_AAS_ValueForBSPEpairKey(bspent, "classname", classname, sizeof(classname)); if (activategoal->shoot) { Com_sprintf(buf, sizeof(buf), "%s: I have to shoot at a %s from %1.1f %1.1f %1.1f in area %d\n", netname, classname, activategoal->goal.origin[0], activategoal->goal.origin[1], activategoal->goal.origin[2], activategoal->goal.areanum); } else { Com_sprintf(buf, sizeof(buf), "%s: I have to activate a %s at %1.1f %1.1f %1.1f in area %d\n", netname, classname, activategoal->goal.origin[0], activategoal->goal.origin[1], activategoal->goal.origin[2], activategoal->goal.areanum); } trap_EA_Say(bs->client, buf); } /* ================== BotRandomMove ================== */ void BotRandomMove(bot_state_t *bs, bot_moveresult_t *moveresult) { vec3_t dir, angles; angles[0] = 0; angles[1] = random() * 360; angles[2] = 0; AngleVectors(angles, dir, NULL, NULL); trap_BotMoveInDirection(bs->ms, dir, 400, MOVE_WALK); moveresult->failure = qfalse; VectorCopy(dir, moveresult->movedir); } /* ================== BotAIBlocked Very basic handling of bots being blocked by other entities. Check what kind of entity is blocking the bot and try to activate it. If that's not an option then try to walk around or over the entity. Before the bot ends in this part of the AI it should predict which doors to open, which buttons to activate etc. ================== */ void BotAIBlocked(bot_state_t *bs, bot_moveresult_t *moveresult, int activate) { int movetype, bspent; vec3_t hordir, start, end, mins, maxs, sideward, angles, up = {0, 0, 1}; aas_entityinfo_t entinfo; bot_activategoal_t activategoal; // if the bot is not blocked by anything if (!moveresult->blocked) { bs->notblocked_time = FloatTime(); return; } // if stuck in a solid area if ( moveresult->type == RESULTTYPE_INSOLIDAREA ) { // move in a random direction in the hope to get out BotRandomMove(bs, moveresult); // return; } // get info for the entity that is blocking the bot BotEntityInfo(moveresult->blockentity, &entinfo); #ifdef OBSTACLEDEBUG ClientName(bs->client, netname, sizeof(netname)); BotAI_Print(PRT_MESSAGE, "%s: I'm blocked by model %d\n", netname, entinfo.modelindex); #endif // OBSTACLEDEBUG // if blocked by a bsp model and the bot wants to activate it if (activate && entinfo.modelindex > 0 && entinfo.modelindex <= max_bspmodelindex) { // find the bsp entity which should be activated in order to get the blocking entity out of the way bspent = BotGetActivateGoal(bs, entinfo.number, &activategoal); if (bspent) { // if (bs->activatestack && !bs->activatestack->inuse) bs->activatestack = NULL; // if not already trying to activate this entity if (!BotIsGoingToActivateEntity(bs, activategoal.goal.entitynum)) { // BotGoForActivateGoal(bs, &activategoal); } // if ontop of an obstacle or // if the bot is not in a reachability area it'll still // need some dynamic obstacle avoidance, otherwise return if (!(moveresult->flags & MOVERESULT_ONTOPOFOBSTACLE) && trap_AAS_AreaReachability(bs->areanum)) return; } else { // enable any routing areas that were disabled BotEnableActivateGoalAreas(&activategoal, qtrue); } } // just some basic dynamic obstacle avoidance code hordir[0] = moveresult->movedir[0]; hordir[1] = moveresult->movedir[1]; hordir[2] = 0; // if no direction just take a random direction if (VectorNormalize(hordir) < 0.1) { VectorSet(angles, 0, 360 * random(), 0); AngleVectors(angles, hordir, NULL, NULL); } // //if (moveresult->flags & MOVERESULT_ONTOPOFOBSTACLE) movetype = MOVE_JUMP; //else movetype = MOVE_WALK; // if there's an obstacle at the bot's feet and head then // the bot might be able to crouch through VectorCopy(bs->origin, start); start[2] += 18; VectorMA(start, 5, hordir, end); VectorSet(mins, -16, -16, -24); VectorSet(maxs, 16, 16, 4); // //bsptrace = AAS_Trace(start, mins, maxs, end, bs->entitynum, MASK_PLAYERSOLID); //if (bsptrace.fraction >= 1) movetype = MOVE_CROUCH; // get the sideward vector CrossProduct(hordir, up, sideward); // if (bs->flags & BFL_AVOIDRIGHT) VectorNegate(sideward, sideward); // try to crouch straight forward? if (movetype != MOVE_CROUCH || !trap_BotMoveInDirection(bs->ms, hordir, 400, movetype)) { // perform the movement if (!trap_BotMoveInDirection(bs->ms, sideward, 400, movetype)) { // flip the avoid direction flag bs->flags ^= BFL_AVOIDRIGHT; // flip the direction // VectorNegate(sideward, sideward); VectorMA(sideward, -1, hordir, sideward); // move in the other direction trap_BotMoveInDirection(bs->ms, sideward, 400, movetype); } } // if (bs->notblocked_time < FloatTime() - 0.4) { // just reset goals and hope the bot will go into another direction? // is this still needed?? if (bs->ainode == AINode_Seek_NBG) bs->nbg_time = 0; else if (bs->ainode == AINode_Seek_LTG) bs->ltg_time = 0; } } /* ================== BotAIPredictObstacles Predict the route towards the goal and check if the bot will be blocked by certain obstacles. When the bot has obstacles on it's path the bot should figure out if they can be removed by activating certain entities. ================== */ int BotAIPredictObstacles(bot_state_t *bs, bot_goal_t *goal) { int modelnum, entitynum, bspent; bot_activategoal_t activategoal; aas_predictroute_t route; if (!bot_predictobstacles.integer) return qfalse; // always predict when the goal change or at regular intervals if (bs->predictobstacles_goalareanum == goal->areanum && bs->predictobstacles_time > FloatTime() - 6) { return qfalse; } bs->predictobstacles_goalareanum = goal->areanum; bs->predictobstacles_time = FloatTime(); // predict at most 100 areas or 10 seconds ahead trap_AAS_PredictRoute(&route, bs->areanum, bs->origin, goal->areanum, bs->tfl, 100, 1000, RSE_USETRAVELTYPE|RSE_ENTERCONTENTS, AREACONTENTS_MOVER, TFL_BRIDGE, 0); // if bot has to travel through an area with a mover if (route.stopevent & RSE_ENTERCONTENTS) { // if the bot will run into a mover if (route.endcontents & AREACONTENTS_MOVER) { //NOTE: this only works with bspc 2.1 or higher modelnum = (route.endcontents & AREACONTENTS_MODELNUM) >> AREACONTENTS_MODELNUMSHIFT; if (modelnum) { // entitynum = BotModelMinsMaxs(modelnum, ET_MOVER, 0, NULL, NULL); if (entitynum) { //NOTE: BotGetActivateGoal already checks if the door is open or not bspent = BotGetActivateGoal(bs, entitynum, &activategoal); if (bspent) { // if (bs->activatestack && !bs->activatestack->inuse) bs->activatestack = NULL; // if not already trying to activate this entity if (!BotIsGoingToActivateEntity(bs, activategoal.goal.entitynum)) { // //BotAI_Print(PRT_MESSAGE, "blocked by mover model %d, entity %d ?\n", modelnum, entitynum); // BotGoForActivateGoal(bs, &activategoal); return qtrue; } else { // enable any routing areas that were disabled BotEnableActivateGoalAreas(&activategoal, qtrue); } } } } } } else if (route.stopevent & RSE_USETRAVELTYPE) { if (route.endtravelflags & TFL_BRIDGE) { //FIXME: check if the bridge is available to travel over } } return qfalse; } /* ================== BotCheckConsoleMessages ================== */ void BotCheckConsoleMessages(bot_state_t *bs) { char botname[MAX_NETNAME], message[MAX_MESSAGE_SIZE], netname[MAX_NETNAME], *ptr; float chat_reply; int context, handle; bot_consolemessage_t m; bot_match_t match; //the name of this bot ClientName(bs->client, botname, sizeof(botname)); // while((handle = trap_BotNextConsoleMessage(bs->cs, &m)) != 0) { //if the chat state is flooded with messages the bot will read them quickly if (trap_BotNumConsoleMessages(bs->cs) < 10) { //if it is a chat message the bot needs some time to read it if (m.type == CMS_CHAT && m.time > FloatTime() - (1 + random())) break; } // ptr = m.message; //if it is a chat message then don't unify white spaces and don't //replace synonyms in the netname if (m.type == CMS_CHAT) { // if (trap_BotFindMatch(m.message, &match, MTCONTEXT_REPLYCHAT)) { ptr = m.message + match.variables[MESSAGE].offset; } } //unify the white spaces in the message trap_UnifyWhiteSpaces(ptr); //replace synonyms in the right context context = BotSynonymContext(bs); trap_BotReplaceSynonyms(ptr, context); //if there's no match if (!BotMatchMessage(bs, m.message)) { //if it is a chat message if (m.type == CMS_CHAT && !bot_nochat.integer) { // if (!trap_BotFindMatch(m.message, &match, MTCONTEXT_REPLYCHAT)) { trap_BotRemoveConsoleMessage(bs->cs, handle); continue; } //don't use eliza chats with team messages if (match.subtype & ST_TEAM) { trap_BotRemoveConsoleMessage(bs->cs, handle); continue; } // trap_BotMatchVariable(&match, NETNAME, netname, sizeof(netname)); trap_BotMatchVariable(&match, MESSAGE, message, sizeof(message)); //if this is a message from the bot self if (bs->client == ClientFromName(netname)) { trap_BotRemoveConsoleMessage(bs->cs, handle); continue; } //unify the message trap_UnifyWhiteSpaces(message); // trap_Cvar_Update(&bot_testrchat); if (bot_testrchat.integer) { // trap_BotLibVarSet("bot_testrchat", "1"); //if bot replies with a chat message if (trap_BotReplyChat(bs->cs, message, context, CONTEXT_REPLY, NULL, NULL, NULL, NULL, NULL, NULL, botname, netname)) { BotAI_Print(PRT_MESSAGE, "------------------------\n"); } else { BotAI_Print(PRT_MESSAGE, "**** no valid reply ****\n"); } } //if at a valid chat position and not chatting already and not in teamplay else if (bs->ainode != AINode_Stand && BotValidChatPosition(bs) && !TeamPlayIsOn()) { chat_reply = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_REPLY, 0, 1); if (random() < 1.5 / (NumBots()+1) && random() < chat_reply) { //if bot replies with a chat message if (trap_BotReplyChat(bs->cs, message, context, CONTEXT_REPLY, NULL, NULL, NULL, NULL, NULL, NULL, botname, netname)) { //remove the console message trap_BotRemoveConsoleMessage(bs->cs, handle); bs->stand_time = FloatTime() + BotChatTime(bs); AIEnter_Stand(bs, "BotCheckConsoleMessages: reply chat"); //EA_Say(bs->client, bs->cs.chatmessage); break; } } } } } //remove the console message trap_BotRemoveConsoleMessage(bs->cs, handle); } } /* ================== BotCheckEvents ================== */ void BotCheckForGrenades(bot_state_t *bs, entityState_t *state) { // if this is not a grenade if (state->eType != ET_MISSILE || state->weapon != WP_GRENADE_LAUNCHER) return; // try to avoid the grenade trap_BotAddAvoidSpot(bs->ms, state->pos.trBase, 160, AVOID_ALWAYS); } /* ================== BotCheckEvents ================== */ void BotCheckEvents(bot_state_t *bs, entityState_t *state) { int event; char buf[128]; //NOTE: this sucks, we're accessing the gentity_t directly //but there's no other fast way to do it right now if (bs->entityeventTime[state->number] == g_entities[state->number].eventTime) { return; } bs->entityeventTime[state->number] = g_entities[state->number].eventTime; //if it's an event only entity if (state->eType > ET_EVENTS) { event = (state->eType - ET_EVENTS) & ~EV_EVENT_BITS; } else { event = state->event & ~EV_EVENT_BITS; } // switch(event) { //client obituary event case EV_OBITUARY: { int target, attacker, mod; target = state->otherEntityNum; attacker = state->otherEntityNum2; mod = state->eventParm; // if (target == bs->client) { bs->botdeathtype = mod; bs->lastkilledby = attacker; // if (target == attacker || target == ENTITYNUM_NONE || target == ENTITYNUM_WORLD) bs->botsuicide = qtrue; else bs->botsuicide = qfalse; // bs->num_deaths++; } //else if this client was killed by the bot else if (attacker == bs->client) { bs->enemydeathtype = mod; bs->lastkilledplayer = target; bs->killedenemy_time = FloatTime(); // bs->num_kills++; } else if (attacker == bs->enemy && target == attacker) { bs->enemysuicide = qtrue; } // break; } case EV_GLOBAL_SOUND: { if (state->eventParm < 0 || state->eventParm > MAX_SOUNDS) { BotAI_Print(PRT_ERROR, "EV_GLOBAL_SOUND: eventParm (%d) out of range\n", state->eventParm); break; } trap_GetConfigstring(CS_SOUNDS + state->eventParm, buf, sizeof(buf)); /* if (!strcmp(buf, "sound/teamplay/flagret_red.wav")) { //red flag is returned bs->redflagstatus = 0; bs->flagstatuschanged = qtrue; } else if (!strcmp(buf, "sound/teamplay/flagret_blu.wav")) { //blue flag is returned bs->blueflagstatus = 0; bs->flagstatuschanged = qtrue; } else*/ if (!strcmp(buf, "sound/items/poweruprespawn.wav")) { //powerup respawned... go get it BotGoForPowerups(bs); } break; } case EV_GLOBAL_TEAM_SOUND: { if (gametype == GT_CTF) { switch(state->eventParm) { case GTS_RED_CAPTURE: bs->blueflagstatus = 0; bs->redflagstatus = 0; bs->flagstatuschanged = qtrue; break; //see BotMatch_CTF case GTS_BLUE_CAPTURE: bs->blueflagstatus = 0; bs->redflagstatus = 0; bs->flagstatuschanged = qtrue; break; //see BotMatch_CTF case GTS_RED_RETURN: //blue flag is returned bs->blueflagstatus = 0; bs->flagstatuschanged = qtrue; break; case GTS_BLUE_RETURN: //red flag is returned bs->redflagstatus = 0; bs->flagstatuschanged = qtrue; break; case GTS_RED_TAKEN: //blue flag is taken bs->blueflagstatus = 1; bs->flagstatuschanged = qtrue; break; //see BotMatch_CTF case GTS_BLUE_TAKEN: //red flag is taken bs->redflagstatus = 1; bs->flagstatuschanged = qtrue; break; //see BotMatch_CTF } } break; } case EV_PLAYER_TELEPORT_IN: { VectorCopy(state->origin, lastteleport_origin); lastteleport_time = FloatTime(); break; } case EV_GENERAL_SOUND: { //if this sound is played on the bot if (state->number == bs->client) { if (state->eventParm < 0 || state->eventParm > MAX_SOUNDS) { BotAI_Print(PRT_ERROR, "EV_GENERAL_SOUND: eventParm (%d) out of range\n", state->eventParm); break; } //check out the sound trap_GetConfigstring(CS_SOUNDS + state->eventParm, buf, sizeof(buf)); //if falling into a death pit if (!strcmp(buf, "*falling1.wav")) { //if the bot has a personal teleporter if (bs->inventory[INVENTORY_TELEPORTER] > 0) { //use the holdable item trap_EA_Use(bs->client); } } } break; } case EV_FOOTSTEP: case EV_FOOTSTEP_METAL: case EV_FOOTSPLASH: case EV_FOOTWADE: case EV_SWIM: case EV_FALL_SHORT: case EV_FALL_MEDIUM: case EV_FALL_FAR: case EV_STEP_4: case EV_STEP_8: case EV_STEP_12: case EV_STEP_16: case EV_JUMP_PAD: case EV_JUMP: case EV_TAUNT: case EV_WATER_TOUCH: case EV_WATER_LEAVE: case EV_WATER_UNDER: case EV_WATER_CLEAR: case EV_ITEM_PICKUP: case EV_GLOBAL_ITEM_PICKUP: case EV_NOAMMO: case EV_CHANGE_WEAPON: case EV_FIRE_WEAPON: //FIXME: either add to sound queue or mark player as someone making noise break; case EV_USE_ITEM0: case EV_USE_ITEM1: case EV_USE_ITEM2: case EV_USE_ITEM3: case EV_USE_ITEM4: case EV_USE_ITEM5: case EV_USE_ITEM6: case EV_USE_ITEM7: case EV_USE_ITEM8: case EV_USE_ITEM9: case EV_USE_ITEM10: case EV_USE_ITEM11: case EV_USE_ITEM12: case EV_USE_ITEM13: case EV_USE_ITEM14: break; } } /* ================== BotCheckSnapshot ================== */ void BotCheckSnapshot(bot_state_t *bs) { int ent; entityState_t state; //remove all avoid spots trap_BotAddAvoidSpot(bs->ms, vec3_origin, 0, AVOID_CLEAR); //reset kamikaze body bs->kamikazebody = 0; //reset number of proxmines bs->numproxmines = 0; // ent = 0; while( ( ent = BotAI_GetSnapshotEntity( bs->client, ent, &state ) ) != -1 ) { //check the entity state for events BotCheckEvents(bs, &state); //check for grenades the bot should avoid BotCheckForGrenades(bs, &state); // } //check the player state for events BotAI_GetEntityState(bs->client, &state); //copy the player state events to the entity state state.event = bs->cur_ps.externalEvent; state.eventParm = bs->cur_ps.externalEventParm; // BotCheckEvents(bs, &state); } /* ================== BotCheckAir ================== */ void BotCheckAir(bot_state_t *bs) { if (bs->inventory[INVENTORY_ENVIRONMENTSUIT] <= 0) { if (trap_AAS_PointContents(bs->eye) & (CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA)) { return; } } bs->lastair_time = FloatTime(); } /* ================== BotAlternateRoute ================== */ bot_goal_t *BotAlternateRoute(bot_state_t *bs, bot_goal_t *goal) { int t; // if the bot has an alternative route goal if (bs->altroutegoal.areanum) { // if (bs->reachedaltroutegoal_time) return goal; // travel time towards alternative route goal t = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, bs->altroutegoal.areanum, bs->tfl); if (t && t < 20) { //BotAI_Print(PRT_MESSAGE, "reached alternate route goal\n"); bs->reachedaltroutegoal_time = FloatTime(); } memcpy(goal, &bs->altroutegoal, sizeof(bot_goal_t)); return &bs->altroutegoal; } return goal; } /* ================== BotGetAlternateRouteGoal ================== */ int BotGetAlternateRouteGoal(bot_state_t *bs, int base) { aas_altroutegoal_t *altroutegoals; bot_goal_t *goal; int numaltroutegoals, rnd; if (base == TEAM_RED) { altroutegoals = red_altroutegoals; numaltroutegoals = red_numaltroutegoals; } else { altroutegoals = blue_altroutegoals; numaltroutegoals = blue_numaltroutegoals; } if (!numaltroutegoals) return qfalse; rnd = (float) random() * numaltroutegoals; if (rnd >= numaltroutegoals) rnd = numaltroutegoals-1; goal = &bs->altroutegoal; goal->areanum = altroutegoals[rnd].areanum; VectorCopy(altroutegoals[rnd].origin, goal->origin); VectorSet(goal->mins, -8, -8, -8); VectorSet(goal->maxs, 8, 8, 8); goal->entitynum = 0; goal->iteminfo = 0; goal->number = 0; goal->flags = 0; // bs->reachedaltroutegoal_time = 0; return qtrue; } /* ================== BotSetupAlternateRouteGoals ================== */ void BotSetupAlternativeRouteGoals(void) { if (altroutegoals_setup) return; altroutegoals_setup = qtrue; } /* ================== BotDeathmatchAI ================== */ void BotDeathmatchAI(bot_state_t *bs, float thinktime) { char gender[144], name[144], buf[144]; char userinfo[MAX_INFO_STRING]; int i; //if the bot has just been setup if (bs->setupcount > 0) { bs->setupcount--; if (bs->setupcount > 0) return; //get the gender characteristic trap_Characteristic_String(bs->character, CHARACTERISTIC_GENDER, gender, sizeof(gender)); //set the bot gender trap_GetUserinfo(bs->client, userinfo, sizeof(userinfo)); Info_SetValueForKey(userinfo, "sex", gender); trap_SetUserinfo(bs->client, userinfo); //set the team if ( !bs->map_restart && g_gametype.integer != GT_TOURNAMENT ) { Com_sprintf(buf, sizeof(buf), "team %s", bs->settings.team); trap_EA_Command(bs->client, buf); } //set the chat gender if (gender[0] == 'm') trap_BotSetChatGender(bs->cs, CHAT_GENDERMALE); else if (gender[0] == 'f') trap_BotSetChatGender(bs->cs, CHAT_GENDERFEMALE); else trap_BotSetChatGender(bs->cs, CHAT_GENDERLESS); //set the chat name ClientName(bs->client, name, sizeof(name)); trap_BotSetChatName(bs->cs, name, bs->client); // bs->lastframe_health = bs->inventory[INVENTORY_HEALTH]; bs->lasthitcount = bs->cur_ps.persistant[PERS_HITS]; // bs->setupcount = 0; // BotSetupAlternativeRouteGoals(); } //no ideal view set bs->flags &= ~BFL_IDEALVIEWSET; // if (!BotIntermission(bs)) { //set the teleport time BotSetTeleportTime(bs); //update some inventory values BotUpdateInventory(bs); //check out the snapshot BotCheckSnapshot(bs); //check for air BotCheckAir(bs); } //check the console messages BotCheckConsoleMessages(bs); //if not in the intermission and not in observer mode if (!BotIntermission(bs) && !BotIsObserver(bs)) { //do team AI BotTeamAI(bs); } //if the bot has no ai node if (!bs->ainode) { AIEnter_Seek_LTG(bs, "BotDeathmatchAI: no ai node"); } //if the bot entered the game less than 8 seconds ago if (!bs->entergamechat && bs->entergame_time > FloatTime() - 8) { if (BotChat_EnterGame(bs)) { bs->stand_time = FloatTime() + BotChatTime(bs); AIEnter_Stand(bs, "BotDeathmatchAI: chat enter game"); } bs->entergamechat = qtrue; } //reset the node switches from the previous frame BotResetNodeSwitches(); //execute AI nodes for (i = 0; i < MAX_NODESWITCHES; i++) { if (bs->ainode(bs)) break; } //if the bot removed itself :) if (!bs->inuse) return; //if the bot executed too many AI nodes if (i >= MAX_NODESWITCHES) { trap_BotDumpGoalStack(bs->gs); trap_BotDumpAvoidGoals(bs->gs); BotDumpNodeSwitches(bs); ClientName(bs->client, name, sizeof(name)); BotAI_Print(PRT_ERROR, "%s at %1.1f switched more than %d AI nodes\n", name, FloatTime(), MAX_NODESWITCHES); } // bs->lastframe_health = bs->inventory[INVENTORY_HEALTH]; bs->lasthitcount = bs->cur_ps.persistant[PERS_HITS]; } /* ================== BotSetEntityNumForGoalWithModel ================== */ void BotSetEntityNumForGoalWithModel(bot_goal_t *goal, int eType, char *modelname) { gentity_t *ent; int i, modelindex; vec3_t dir; modelindex = G_ModelIndex( modelname ); ent = &g_entities[0]; for (i = 0; i < level.num_entities; i++, ent++) { if ( !ent->inuse ) { continue; } if ( eType && ent->s.eType != eType) { continue; } if (ent->s.modelindex != modelindex) { continue; } VectorSubtract(goal->origin, ent->s.origin, dir); if (VectorLengthSquared(dir) < Square(10)) { goal->entitynum = i; return; } } } /* ================== BotSetEntityNumForGoal ================== */ void BotSetEntityNumForGoal(bot_goal_t *goal, char *classname) { gentity_t *ent; int i; vec3_t dir; ent = &g_entities[0]; for (i = 0; i < level.num_entities; i++, ent++) { if ( !ent->inuse ) { continue; } if ( !Q_stricmp(ent->classname, classname) ) { continue; } VectorSubtract(goal->origin, ent->s.origin, dir); if (VectorLengthSquared(dir) < Square(10)) { goal->entitynum = i; return; } } } /* ================== BotGoalForBSPEntity ================== */ int BotGoalForBSPEntity( char *classname, bot_goal_t *goal ) { char value[MAX_INFO_STRING]; vec3_t origin, start, end; int ent, numareas, areas[10]; memset(goal, 0, sizeof(bot_goal_t)); for (ent = trap_AAS_NextBSPEntity(0); ent; ent = trap_AAS_NextBSPEntity(ent)) { if (!trap_AAS_ValueForBSPEpairKey(ent, "classname", value, sizeof(value))) continue; if (!strcmp(value, classname)) { if (!trap_AAS_VectorForBSPEpairKey(ent, "origin", origin)) return qfalse; VectorCopy(origin, goal->origin); VectorCopy(origin, start); start[2] -= 32; VectorCopy(origin, end); end[2] += 32; numareas = trap_AAS_TraceAreas(start, end, areas, NULL, 10); if (!numareas) return qfalse; goal->areanum = areas[0]; return qtrue; } } return qfalse; } /* ================== BotSetupDeathmatchAI ================== */ void BotSetupDeathmatchAI(void) { int ent, modelnum; char model[128]; gametype = trap_Cvar_VariableIntegerValue("g_gametype"); maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); trap_Cvar_Register(&bot_rocketjump, "bot_rocketjump", "1", 0); trap_Cvar_Register(&bot_grapple, "bot_grapple", "0", 0); trap_Cvar_Register(&bot_fastchat, "bot_fastchat", "0", 0); trap_Cvar_Register(&bot_nochat, "bot_nochat", "0", 0); trap_Cvar_Register(&bot_testrchat, "bot_testrchat", "0", 0); trap_Cvar_Register(&bot_challenge, "bot_challenge", "0", 0); trap_Cvar_Register(&bot_predictobstacles, "bot_predictobstacles", "1", 0); trap_Cvar_Register(&g_spSkill, "g_spSkill", "2", 0); // if (gametype == GT_CTF) { if (trap_BotGetLevelItemGoal(-1, "Red Flag", &ctf_redflag) < 0) BotAI_Print(PRT_WARNING, "CTF without Red Flag\n"); if (trap_BotGetLevelItemGoal(-1, "Blue Flag", &ctf_blueflag) < 0) BotAI_Print(PRT_WARNING, "CTF without Blue Flag\n"); } max_bspmodelindex = 0; for (ent = trap_AAS_NextBSPEntity(0); ent; ent = trap_AAS_NextBSPEntity(ent)) { if (!trap_AAS_ValueForBSPEpairKey(ent, "model", model, sizeof(model))) continue; if (model[0] == '*') { modelnum = atoi(model+1); if (modelnum > max_bspmodelindex) max_bspmodelindex = modelnum; } } //initialize the waypoint heap BotInitWaypoints(); } /* ================== BotShutdownDeathmatchAI ================== */ void BotShutdownDeathmatchAI(void) { altroutegoals_setup = qfalse; } ================================================ FILE: src/game/ai_dmq3.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: ai_dmq3.h * * desc: Quake3 bot AI * * $Archive: /source/code/botai/ai_chat.c $ * *****************************************************************************/ //setup the deathmatch AI void BotSetupDeathmatchAI(void); //shutdown the deathmatch AI void BotShutdownDeathmatchAI(void); //let the bot live within it's deathmatch AI net void BotDeathmatchAI(bot_state_t *bs, float thinktime); //free waypoints void BotFreeWaypoints(bot_waypoint_t *wp); //choose a weapon void BotChooseWeapon(bot_state_t *bs); //setup movement stuff void BotSetupForMovement(bot_state_t *bs); //update the inventory void BotUpdateInventory(bot_state_t *bs); //update the inventory during battle void BotUpdateBattleInventory(bot_state_t *bs, int enemy); //use holdable items during battle void BotBattleUseItems(bot_state_t *bs); //return true if the bot is dead qboolean BotIsDead(bot_state_t *bs); //returns true if the bot is in observer mode qboolean BotIsObserver(bot_state_t *bs); //returns true if the bot is in the intermission qboolean BotIntermission(bot_state_t *bs); //returns true if the bot is in lava or slime qboolean BotInLavaOrSlime(bot_state_t *bs); //returns true if the entity is dead qboolean EntityIsDead(aas_entityinfo_t *entinfo); //returns true if the entity is invisible qboolean EntityIsInvisible(aas_entityinfo_t *entinfo); //returns true if the entity is shooting qboolean EntityIsShooting(aas_entityinfo_t *entinfo); // set a user info key/value pair void BotSetUserInfo(bot_state_t *bs, char *key, char *value); // set the team status (offense, defense etc.) void BotSetTeamStatus(bot_state_t *bs); //returns the name of the client char *ClientName(int client, char *name, int size); //returns an simplyfied client name char *EasyClientName(int client, char *name, int size); //returns the skin used by the client char *ClientSkin(int client, char *skin, int size); // returns the appropriate synonym context for the current game type and situation int BotSynonymContext(bot_state_t *bs); // set last ordered task int BotSetLastOrderedTask(bot_state_t *bs); // selection of goals for teamplay void BotTeamGoals(bot_state_t *bs, int retreat); //returns the aggression of the bot in the range [0, 100] float BotAggression(bot_state_t *bs); //returns how bad the bot feels float BotFeelingBad(bot_state_t *bs); //returns true if the bot wants to retreat int BotWantsToRetreat(bot_state_t *bs); //returns true if the bot wants to chase int BotWantsToChase(bot_state_t *bs); //returns true if the bot wants to help int BotWantsToHelp(bot_state_t *bs); //returns true if the bot can and wants to rocketjump int BotCanAndWantsToRocketJump(bot_state_t *bs); // returns true if the bot has a persistant powerup and a weapon int BotHasPersistantPowerupAndWeapon(bot_state_t *bs); //returns true if the bot wants to and goes camping int BotWantsToCamp(bot_state_t *bs); //the bot will perform attack movements bot_moveresult_t BotAttackMove(bot_state_t *bs, int tfl); //returns true if the bot and the entity are in the same team int BotSameTeam(bot_state_t *bs, int entnum); //returns true if teamplay is on int TeamPlayIsOn(void); // returns the client number of the team mate flag carrier (-1 if none) int BotTeamFlagCarrier(bot_state_t *bs); //returns visible team mate flag carrier if available int BotTeamFlagCarrierVisible(bot_state_t *bs); //returns visible enemy flag carrier if available int BotEnemyFlagCarrierVisible(bot_state_t *bs); //get the number of visible teammates and enemies void BotVisibleTeamMatesAndEnemies(bot_state_t *bs, int *teammates, int *enemies, float range); //returns true if within the field of vision for the given angles qboolean InFieldOfVision(vec3_t viewangles, float fov, vec3_t angles); //returns true and sets the .enemy field when an enemy is found int BotFindEnemy(bot_state_t *bs, int curenemy); //returns a roam goal void BotRoamGoal(bot_state_t *bs, vec3_t goal); //returns entity visibility in the range [0, 1] float BotEntityVisible(int viewer, vec3_t eye, vec3_t viewangles, float fov, int ent); //the bot will aim at the current enemy void BotAimAtEnemy(bot_state_t *bs); //check if the bot should attack void BotCheckAttack(bot_state_t *bs); //AI when the bot is blocked void BotAIBlocked(bot_state_t *bs, bot_moveresult_t *moveresult, int activate); //AI to predict obstacles int BotAIPredictObstacles(bot_state_t *bs, bot_goal_t *goal); //enable or disable the areas the blocking entity is in void BotEnableActivateGoalAreas(bot_activategoal_t *activategoal, int enable); //pop an activate goal from the stack int BotPopFromActivateGoalStack(bot_state_t *bs); //clear the activate goal stack void BotClearActivateGoalStack(bot_state_t *bs); //returns the team the bot is in int BotTeam(bot_state_t *bs); //retuns the opposite team of the bot int BotOppositeTeam(bot_state_t *bs); //returns the flag the bot is carrying (CTFFLAG_?) int BotCTFCarryingFlag(bot_state_t *bs); //remember the last ordered task void BotRememberLastOrderedTask(bot_state_t *bs); //set ctf goals (defend base, get enemy flag) during seek void BotCTFSeekGoals(bot_state_t *bs); //set ctf goals (defend base, get enemy flag) during retreat void BotCTFRetreatGoals(bot_state_t *bs); //get a random alternate route goal towards the given base int BotGetAlternateRouteGoal(bot_state_t *bs, int base); //returns either the alternate route goal or the given goal bot_goal_t *BotAlternateRoute(bot_state_t *bs, bot_goal_t *goal); //create a new waypoint bot_waypoint_t *BotCreateWayPoint(char *name, vec3_t origin, int areanum); //find a waypoint with the given name bot_waypoint_t *BotFindWayPoint(bot_waypoint_t *waypoints, char *name); //strstr but case insensitive char *stristr(char *str, char *charset); //returns the number of the client with the given name int ClientFromName(char *name); int ClientOnSameTeamFromName(bot_state_t *bs, char *name); // int BotPointAreaNum(vec3_t origin); // void BotMapScripts(bot_state_t *bs); //ctf flags #define CTF_FLAG_NONE 0 #define CTF_FLAG_RED 1 #define CTF_FLAG_BLUE 2 //CTF skins #define CTF_SKIN_REDTEAM "red" #define CTF_SKIN_BLUETEAM "blue" extern int gametype; //game type extern int maxclients; //maximum number of clients extern vmCvar_t bot_grapple; extern vmCvar_t bot_rocketjump; extern vmCvar_t bot_fastchat; extern vmCvar_t bot_nochat; extern vmCvar_t bot_testrchat; extern vmCvar_t bot_challenge; extern bot_goal_t ctf_redflag; extern bot_goal_t ctf_blueflag; ================================================ FILE: src/game/ai_main.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: ai_main.c * * desc: Quake3 bot AI * * $Archive: /MissionPack/code/game/ai_main.c $ * *****************************************************************************/ #include "g_local.h" #include "q_shared.h" #include "botlib.h" //bot lib interface #include "be_aas.h" #include "be_ea.h" #include "be_ai_char.h" #include "be_ai_chat.h" #include "be_ai_gen.h" #include "be_ai_goal.h" #include "be_ai_move.h" #include "be_ai_weap.h" // #include "ai_main.h" #include "ai_dmq3.h" #include "ai_chat.h" #include "ai_cmd.h" #include "ai_dmnet.h" #include "ai_vcmd.h" // #include "chars.h" #include "inv.h" #include "syn.h" #define MAX_PATH 144 //bot states bot_state_t *botstates[MAX_CLIENTS]; //number of bots int numbots; //floating point time float floattime; //time to do a regular update float regularupdate_time; // int bot_interbreed; int bot_interbreedmatchcount; // vmCvar_t bot_thinktime; vmCvar_t bot_memorydump; vmCvar_t bot_saveroutingcache; vmCvar_t bot_pause; vmCvar_t bot_report; vmCvar_t bot_testsolid; vmCvar_t bot_testclusters; vmCvar_t bot_developer; vmCvar_t bot_interbreedchar; vmCvar_t bot_interbreedbots; vmCvar_t bot_interbreedcycle; vmCvar_t bot_interbreedwrite; void ExitLevel( void ); /* ================== BotAI_Print ================== */ void QDECL BotAI_Print(int type, char *fmt, ...) { char str[2048]; va_list ap; va_start(ap, fmt); vsprintf(str, fmt, ap); va_end(ap); switch(type) { case PRT_MESSAGE: { G_Printf("%s", str); break; } case PRT_WARNING: { G_Printf( S_COLOR_YELLOW "Warning: %s", str ); break; } case PRT_ERROR: { G_Printf( S_COLOR_RED "Error: %s", str ); break; } case PRT_FATAL: { G_Printf( S_COLOR_RED "Fatal: %s", str ); break; } case PRT_EXIT: { G_Error( S_COLOR_RED "Exit: %s", str ); break; } default: { G_Printf( "unknown print type\n" ); break; } } } /* ================== BotAI_Trace ================== */ void BotAI_Trace(bsp_trace_t *bsptrace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask) { trace_t trace; trap_Trace(&trace, start, mins, maxs, end, passent, contentmask); //copy the trace information bsptrace->allsolid = trace.allsolid; bsptrace->startsolid = trace.startsolid; bsptrace->fraction = trace.fraction; VectorCopy(trace.endpos, bsptrace->endpos); bsptrace->plane.dist = trace.plane.dist; VectorCopy(trace.plane.normal, bsptrace->plane.normal); bsptrace->plane.signbits = trace.plane.signbits; bsptrace->plane.type = trace.plane.type; bsptrace->surface.value = trace.surfaceFlags; bsptrace->ent = trace.entityNum; bsptrace->exp_dist = 0; bsptrace->sidenum = 0; bsptrace->contents = 0; } /* ================== BotAI_GetClientState ================== */ int BotAI_GetClientState( int clientNum, playerState_t *state ) { gentity_t *ent; ent = &g_entities[clientNum]; if ( !ent->inuse ) { return qfalse; } if ( !ent->client ) { return qfalse; } memcpy( state, &ent->client->ps, sizeof(playerState_t) ); return qtrue; } /* ================== BotAI_GetEntityState ================== */ int BotAI_GetEntityState( int entityNum, entityState_t *state ) { gentity_t *ent; ent = &g_entities[entityNum]; memset( state, 0, sizeof(entityState_t) ); if (!ent->inuse) return qfalse; if (!ent->r.linked) return qfalse; if (ent->r.svFlags & SVF_NOCLIENT) return qfalse; memcpy( state, &ent->s, sizeof(entityState_t) ); return qtrue; } /* ================== BotAI_GetSnapshotEntity ================== */ int BotAI_GetSnapshotEntity( int clientNum, int sequence, entityState_t *state ) { int entNum; entNum = trap_BotGetSnapshotEntity( clientNum, sequence ); if ( entNum == -1 ) { memset(state, 0, sizeof(entityState_t)); return -1; } BotAI_GetEntityState( entNum, state ); return sequence + 1; } /* ================== BotAI_BotInitialChat ================== */ void QDECL BotAI_BotInitialChat( bot_state_t *bs, char *type, ... ) { int i, mcontext; va_list ap; char *p; char *vars[MAX_MATCHVARIABLES]; memset(vars, 0, sizeof(vars)); va_start(ap, type); p = va_arg(ap, char *); for (i = 0; i < MAX_MATCHVARIABLES; i++) { if( !p ) { break; } vars[i] = p; p = va_arg(ap, char *); } va_end(ap); mcontext = BotSynonymContext(bs); trap_BotInitialChat( bs->cs, type, mcontext, vars[0], vars[1], vars[2], vars[3], vars[4], vars[5], vars[6], vars[7] ); } /* ================== BotTestAAS ================== */ void BotTestAAS(vec3_t origin) { int areanum; aas_areainfo_t info; trap_Cvar_Update(&bot_testsolid); trap_Cvar_Update(&bot_testclusters); if (bot_testsolid.integer) { if (!trap_AAS_Initialized()) return; areanum = BotPointAreaNum(origin); if (areanum) BotAI_Print(PRT_MESSAGE, "\remtpy area"); else BotAI_Print(PRT_MESSAGE, "\r^1SOLID area"); } else if (bot_testclusters.integer) { if (!trap_AAS_Initialized()) return; areanum = BotPointAreaNum(origin); if (!areanum) BotAI_Print(PRT_MESSAGE, "\r^1Solid! "); else { trap_AAS_AreaInfo(areanum, &info); BotAI_Print(PRT_MESSAGE, "\rarea %d, cluster %d ", areanum, info.cluster); } } } /* ================== BotReportStatus ================== */ void BotReportStatus(bot_state_t *bs) { char goalname[MAX_MESSAGE_SIZE]; char netname[MAX_MESSAGE_SIZE]; char *leader, flagstatus[32]; // ClientName(bs->client, netname, sizeof(netname)); if (Q_stricmp(netname, bs->teamleader) == 0) leader = "L"; else leader = " "; strcpy(flagstatus, " "); if (gametype == GT_CTF) { if (BotCTFCarryingFlag(bs)) { if (BotTeam(bs) == TEAM_RED) strcpy(flagstatus, S_COLOR_RED"F "); else strcpy(flagstatus, S_COLOR_BLUE"F "); } } switch(bs->ltgtype) { case LTG_TEAMHELP: { EasyClientName(bs->teammate, goalname, sizeof(goalname)); BotAI_Print(PRT_MESSAGE, "%-20s%s%s: helping %s\n", netname, leader, flagstatus, goalname); break; } case LTG_TEAMACCOMPANY: { EasyClientName(bs->teammate, goalname, sizeof(goalname)); BotAI_Print(PRT_MESSAGE, "%-20s%s%s: accompanying %s\n", netname, leader, flagstatus, goalname); break; } case LTG_DEFENDKEYAREA: { trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname)); BotAI_Print(PRT_MESSAGE, "%-20s%s%s: defending %s\n", netname, leader, flagstatus, goalname); break; } case LTG_GETITEM: { trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname)); BotAI_Print(PRT_MESSAGE, "%-20s%s%s: getting item %s\n", netname, leader, flagstatus, goalname); break; } case LTG_KILL: { ClientName(bs->teamgoal.entitynum, goalname, sizeof(goalname)); BotAI_Print(PRT_MESSAGE, "%-20s%s%s: killing %s\n", netname, leader, flagstatus, goalname); break; } case LTG_CAMP: case LTG_CAMPORDER: { BotAI_Print(PRT_MESSAGE, "%-20s%s%s: camping\n", netname, leader, flagstatus); break; } case LTG_PATROL: { BotAI_Print(PRT_MESSAGE, "%-20s%s%s: patrolling\n", netname, leader, flagstatus); break; } case LTG_GETFLAG: { BotAI_Print(PRT_MESSAGE, "%-20s%s%s: capturing flag\n", netname, leader, flagstatus); break; } case LTG_RUSHBASE: { BotAI_Print(PRT_MESSAGE, "%-20s%s%s: rushing base\n", netname, leader, flagstatus); break; } case LTG_RETURNFLAG: { BotAI_Print(PRT_MESSAGE, "%-20s%s%s: returning flag\n", netname, leader, flagstatus); break; } case LTG_ATTACKENEMYBASE: { BotAI_Print(PRT_MESSAGE, "%-20s%s%s: attacking the enemy base\n", netname, leader, flagstatus); break; } case LTG_HARVEST: { BotAI_Print(PRT_MESSAGE, "%-20s%s%s: harvesting\n", netname, leader, flagstatus); break; } default: { BotAI_Print(PRT_MESSAGE, "%-20s%s%s: roaming\n", netname, leader, flagstatus); break; } } } /* ================== BotTeamplayReport ================== */ void BotTeamplayReport(void) { int i; char buf[MAX_INFO_STRING]; BotAI_Print(PRT_MESSAGE, S_COLOR_RED"RED\n"); for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { // if ( !botstates[i] || !botstates[i]->inuse ) continue; // trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; //skip spectators if (atoi(Info_ValueForKey(buf, "t")) == TEAM_RED) { BotReportStatus(botstates[i]); } } BotAI_Print(PRT_MESSAGE, S_COLOR_BLUE"BLUE\n"); for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { // if ( !botstates[i] || !botstates[i]->inuse ) continue; // trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; //skip spectators if (atoi(Info_ValueForKey(buf, "t")) == TEAM_BLUE) { BotReportStatus(botstates[i]); } } } /* ================== BotSetInfoConfigString ================== */ void BotSetInfoConfigString(bot_state_t *bs) { char goalname[MAX_MESSAGE_SIZE]; char netname[MAX_MESSAGE_SIZE]; char action[MAX_MESSAGE_SIZE]; char *leader, carrying[32], *cs; bot_goal_t goal; // ClientName(bs->client, netname, sizeof(netname)); if (Q_stricmp(netname, bs->teamleader) == 0) leader = "L"; else leader = " "; strcpy(carrying, " "); if (gametype == GT_CTF) { if (BotCTFCarryingFlag(bs)) { strcpy(carrying, "F "); } } switch(bs->ltgtype) { case LTG_TEAMHELP: { EasyClientName(bs->teammate, goalname, sizeof(goalname)); Com_sprintf(action, sizeof(action), "helping %s", goalname); break; } case LTG_TEAMACCOMPANY: { EasyClientName(bs->teammate, goalname, sizeof(goalname)); Com_sprintf(action, sizeof(action), "accompanying %s", goalname); break; } case LTG_DEFENDKEYAREA: { trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname)); Com_sprintf(action, sizeof(action), "defending %s", goalname); break; } case LTG_GETITEM: { trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname)); Com_sprintf(action, sizeof(action), "getting item %s", goalname); break; } case LTG_KILL: { ClientName(bs->teamgoal.entitynum, goalname, sizeof(goalname)); Com_sprintf(action, sizeof(action), "killing %s", goalname); break; } case LTG_CAMP: case LTG_CAMPORDER: { Com_sprintf(action, sizeof(action), "camping"); break; } case LTG_PATROL: { Com_sprintf(action, sizeof(action), "patrolling"); break; } case LTG_GETFLAG: { Com_sprintf(action, sizeof(action), "capturing flag"); break; } case LTG_RUSHBASE: { Com_sprintf(action, sizeof(action), "rushing base"); break; } case LTG_RETURNFLAG: { Com_sprintf(action, sizeof(action), "returning flag"); break; } case LTG_ATTACKENEMYBASE: { Com_sprintf(action, sizeof(action), "attacking the enemy base"); break; } case LTG_HARVEST: { Com_sprintf(action, sizeof(action), "harvesting"); break; } default: { trap_BotGetTopGoal(bs->gs, &goal); trap_BotGoalName(goal.number, goalname, sizeof(goalname)); Com_sprintf(action, sizeof(action), "roaming %s", goalname); break; } } cs = va("l\\%s\\c\\%s\\a\\%s", leader, carrying, action); trap_SetConfigstring (CS_BOTINFO + bs->client, cs); } /* ============== BotUpdateInfoConfigStrings ============== */ void BotUpdateInfoConfigStrings(void) { int i; char buf[MAX_INFO_STRING]; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { // if ( !botstates[i] || !botstates[i]->inuse ) continue; // trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; BotSetInfoConfigString(botstates[i]); } } /* ============== BotInterbreedBots ============== */ void BotInterbreedBots(void) { float ranks[MAX_CLIENTS]; int parent1, parent2, child; int i; // get rankings for all the bots for (i = 0; i < MAX_CLIENTS; i++) { if ( botstates[i] && botstates[i]->inuse ) { ranks[i] = botstates[i]->num_kills * 2 - botstates[i]->num_deaths; } else { ranks[i] = -1; } } if (trap_GeneticParentsAndChildSelection(MAX_CLIENTS, ranks, &parent1, &parent2, &child)) { trap_BotInterbreedGoalFuzzyLogic(botstates[parent1]->gs, botstates[parent2]->gs, botstates[child]->gs); trap_BotMutateGoalFuzzyLogic(botstates[child]->gs, 1); } // reset the kills and deaths for (i = 0; i < MAX_CLIENTS; i++) { if (botstates[i] && botstates[i]->inuse) { botstates[i]->num_kills = 0; botstates[i]->num_deaths = 0; } } } /* ============== BotWriteInterbreeded ============== */ void BotWriteInterbreeded(char *filename) { float rank, bestrank; int i, bestbot; bestrank = 0; bestbot = -1; // get the best bot for (i = 0; i < MAX_CLIENTS; i++) { if ( botstates[i] && botstates[i]->inuse ) { rank = botstates[i]->num_kills * 2 - botstates[i]->num_deaths; } else { rank = -1; } if (rank > bestrank) { bestrank = rank; bestbot = i; } } if (bestbot >= 0) { //write out the new goal fuzzy logic trap_BotSaveGoalFuzzyLogic(botstates[bestbot]->gs, filename); } } /* ============== BotInterbreedEndMatch add link back into ExitLevel? ============== */ void BotInterbreedEndMatch(void) { if (!bot_interbreed) return; bot_interbreedmatchcount++; if (bot_interbreedmatchcount >= bot_interbreedcycle.integer) { bot_interbreedmatchcount = 0; // trap_Cvar_Update(&bot_interbreedwrite); if (strlen(bot_interbreedwrite.string)) { BotWriteInterbreeded(bot_interbreedwrite.string); trap_Cvar_Set("bot_interbreedwrite", ""); } BotInterbreedBots(); } } /* ============== BotInterbreeding ============== */ void BotInterbreeding(void) { int i; trap_Cvar_Update(&bot_interbreedchar); if (!strlen(bot_interbreedchar.string)) return; //make sure we are in tournament mode if (gametype != GT_TOURNAMENT) { trap_Cvar_Set("g_gametype", va("%d", GT_TOURNAMENT)); ExitLevel(); return; } //shutdown all the bots for (i = 0; i < MAX_CLIENTS; i++) { if (botstates[i] && botstates[i]->inuse) { BotAIShutdownClient(botstates[i]->client, qfalse); } } //make sure all item weight configs are reloaded and Not shared trap_BotLibVarSet("bot_reloadcharacters", "1"); //add a number of bots using the desired bot character for (i = 0; i < bot_interbreedbots.integer; i++) { trap_SendConsoleCommand( EXEC_INSERT, va("addbot %s 4 free %i %s%d\n", bot_interbreedchar.string, i * 50, bot_interbreedchar.string, i) ); } // trap_Cvar_Set("bot_interbreedchar", ""); bot_interbreed = qtrue; } /* ============== BotEntityInfo ============== */ void BotEntityInfo(int entnum, aas_entityinfo_t *info) { trap_AAS_EntityInfo(entnum, info); } /* ============== NumBots ============== */ int NumBots(void) { return numbots; } /* ============== BotTeamLeader ============== */ int BotTeamLeader(bot_state_t *bs) { int leader; leader = ClientFromName(bs->teamleader); if (leader < 0) return qfalse; if (!botstates[leader] || !botstates[leader]->inuse) return qfalse; return qtrue; } /* ============== AngleDifference ============== */ float AngleDifference(float ang1, float ang2) { float diff; diff = ang1 - ang2; if (ang1 > ang2) { if (diff > 180.0) diff -= 360.0; } else { if (diff < -180.0) diff += 360.0; } return diff; } /* ============== BotChangeViewAngle ============== */ float BotChangeViewAngle(float angle, float ideal_angle, float speed) { float move; angle = AngleMod(angle); ideal_angle = AngleMod(ideal_angle); if (angle == ideal_angle) return angle; move = ideal_angle - angle; if (ideal_angle > angle) { if (move > 180.0) move -= 360.0; } else { if (move < -180.0) move += 360.0; } if (move > 0) { if (move > speed) move = speed; } else { if (move < -speed) move = -speed; } return AngleMod(angle + move); } /* ============== BotChangeViewAngles ============== */ void BotChangeViewAngles(bot_state_t *bs, float thinktime) { float diff, factor, maxchange, anglespeed, disired_speed; int i; if (bs->ideal_viewangles[PITCH] > 180) bs->ideal_viewangles[PITCH] -= 360; // if (bs->enemy >= 0) { factor = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_VIEW_FACTOR, 0.01f, 1); maxchange = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_VIEW_MAXCHANGE, 1, 1800); } else { factor = 0.05f; maxchange = 360; } if (maxchange < 240) maxchange = 240; maxchange *= thinktime; for (i = 0; i < 2; i++) { // if (bot_challenge.integer) { //smooth slowdown view model diff = abs(AngleDifference(bs->viewangles[i], bs->ideal_viewangles[i])); anglespeed = diff * factor; if (anglespeed > maxchange) anglespeed = maxchange; bs->viewangles[i] = BotChangeViewAngle(bs->viewangles[i], bs->ideal_viewangles[i], anglespeed); } else { //over reaction view model bs->viewangles[i] = AngleMod(bs->viewangles[i]); bs->ideal_viewangles[i] = AngleMod(bs->ideal_viewangles[i]); diff = AngleDifference(bs->viewangles[i], bs->ideal_viewangles[i]); disired_speed = diff * factor; bs->viewanglespeed[i] += (bs->viewanglespeed[i] - disired_speed); if (bs->viewanglespeed[i] > 180) bs->viewanglespeed[i] = maxchange; if (bs->viewanglespeed[i] < -180) bs->viewanglespeed[i] = -maxchange; anglespeed = bs->viewanglespeed[i]; if (anglespeed > maxchange) anglespeed = maxchange; if (anglespeed < -maxchange) anglespeed = -maxchange; bs->viewangles[i] += anglespeed; bs->viewangles[i] = AngleMod(bs->viewangles[i]); //demping bs->viewanglespeed[i] *= 0.45 * (1 - factor); } //BotAI_Print(PRT_MESSAGE, "ideal_angles %f %f\n", bs->ideal_viewangles[0], bs->ideal_viewangles[1], bs->ideal_viewangles[2]);` //bs->viewangles[i] = bs->ideal_viewangles[i]; } //bs->viewangles[PITCH] = 0; if (bs->viewangles[PITCH] > 180) bs->viewangles[PITCH] -= 360; //elementary action: view trap_EA_View(bs->client, bs->viewangles); } /* ============== BotInputToUserCommand ============== */ void BotInputToUserCommand(bot_input_t *bi, usercmd_t *ucmd, int delta_angles[3], int time) { vec3_t angles, forward, right; short temp; int j; //clear the whole structure memset(ucmd, 0, sizeof(usercmd_t)); // //Com_Printf("dir = %f %f %f speed = %f\n", bi->dir[0], bi->dir[1], bi->dir[2], bi->speed); //the duration for the user command in milli seconds ucmd->serverTime = time; // if (bi->actionflags & ACTION_DELAYEDJUMP) { bi->actionflags |= ACTION_JUMP; bi->actionflags &= ~ACTION_DELAYEDJUMP; } //set the buttons if (bi->actionflags & ACTION_RESPAWN) ucmd->buttons = BUTTON_ATTACK; if (bi->actionflags & ACTION_ATTACK) ucmd->buttons |= BUTTON_ATTACK; if (bi->actionflags & ACTION_TALK) ucmd->buttons |= BUTTON_TALK; if (bi->actionflags & ACTION_GESTURE) ucmd->buttons |= BUTTON_GESTURE; if (bi->actionflags & ACTION_USE) ucmd->buttons |= BUTTON_USE_HOLDABLE; if (bi->actionflags & ACTION_WALK) ucmd->buttons |= BUTTON_WALKING; if (bi->actionflags & ACTION_AFFIRMATIVE) ucmd->buttons |= BUTTON_AFFIRMATIVE; if (bi->actionflags & ACTION_NEGATIVE) ucmd->buttons |= BUTTON_NEGATIVE; if (bi->actionflags & ACTION_GETFLAG) ucmd->buttons |= BUTTON_GETFLAG; if (bi->actionflags & ACTION_GUARDBASE) ucmd->buttons |= BUTTON_GUARDBASE; if (bi->actionflags & ACTION_PATROL) ucmd->buttons |= BUTTON_PATROL; if (bi->actionflags & ACTION_FOLLOWME) ucmd->buttons |= BUTTON_FOLLOWME; // ucmd->weapon = bi->weapon; //set the view angles //NOTE: the ucmd->angles are the angles WITHOUT the delta angles ucmd->angles[PITCH] = ANGLE2SHORT(bi->viewangles[PITCH]); ucmd->angles[YAW] = ANGLE2SHORT(bi->viewangles[YAW]); ucmd->angles[ROLL] = ANGLE2SHORT(bi->viewangles[ROLL]); //subtract the delta angles for (j = 0; j < 3; j++) { temp = ucmd->angles[j] - delta_angles[j]; /*NOTE: disabled because temp should be mod first if ( j == PITCH ) { // don't let the player look up or down more than 90 degrees if ( temp > 16000 ) temp = 16000; else if ( temp < -16000 ) temp = -16000; } */ ucmd->angles[j] = temp; } //NOTE: movement is relative to the REAL view angles //get the horizontal forward and right vector //get the pitch in the range [-180, 180] if (bi->dir[2]) angles[PITCH] = bi->viewangles[PITCH]; else angles[PITCH] = 0; angles[YAW] = bi->viewangles[YAW]; angles[ROLL] = 0; AngleVectors(angles, forward, right, NULL); //bot input speed is in the range [0, 400] bi->speed = bi->speed * 127 / 400; //set the view independent movement ucmd->forwardmove = DotProduct(forward, bi->dir) * bi->speed; ucmd->rightmove = DotProduct(right, bi->dir) * bi->speed; ucmd->upmove = abs(forward[2]) * bi->dir[2] * bi->speed; //normal keyboard movement if (bi->actionflags & ACTION_MOVEFORWARD) ucmd->forwardmove += 127; if (bi->actionflags & ACTION_MOVEBACK) ucmd->forwardmove -= 127; if (bi->actionflags & ACTION_MOVELEFT) ucmd->rightmove -= 127; if (bi->actionflags & ACTION_MOVERIGHT) ucmd->rightmove += 127; //jump/moveup if (bi->actionflags & ACTION_JUMP) ucmd->upmove += 127; //crouch/movedown if (bi->actionflags & ACTION_CROUCH) ucmd->upmove -= 127; // //Com_Printf("forward = %d right = %d up = %d\n", ucmd.forwardmove, ucmd.rightmove, ucmd.upmove); //Com_Printf("ucmd->serverTime = %d\n", ucmd->serverTime); } /* ============== BotUpdateInput ============== */ void BotUpdateInput(bot_state_t *bs, int time, int elapsed_time) { bot_input_t bi; int j; //add the delta angles to the bot's current view angles for (j = 0; j < 3; j++) { bs->viewangles[j] = AngleMod(bs->viewangles[j] + SHORT2ANGLE(bs->cur_ps.delta_angles[j])); } //change the bot view angles BotChangeViewAngles(bs, (float) elapsed_time / 1000); //retrieve the bot input trap_EA_GetInput(bs->client, (float) time / 1000, &bi); //respawn hack if (bi.actionflags & ACTION_RESPAWN) { if (bs->lastucmd.buttons & BUTTON_ATTACK) bi.actionflags &= ~(ACTION_RESPAWN|ACTION_ATTACK); } //convert the bot input to a usercmd BotInputToUserCommand(&bi, &bs->lastucmd, bs->cur_ps.delta_angles, time); //subtract the delta angles for (j = 0; j < 3; j++) { bs->viewangles[j] = AngleMod(bs->viewangles[j] - SHORT2ANGLE(bs->cur_ps.delta_angles[j])); } } /* ============== BotAIRegularUpdate ============== */ void BotAIRegularUpdate(void) { if (regularupdate_time < FloatTime()) { trap_BotUpdateEntityItems(); regularupdate_time = FloatTime() + 0.3; } } /* ============== RemoveColorEscapeSequences ============== */ void RemoveColorEscapeSequences( char *text ) { int i, l; l = 0; for ( i = 0; text[i]; i++ ) { if (Q_IsColorString(&text[i])) { i++; continue; } if (text[i] > 0x7E) continue; text[l++] = text[i]; } text[l] = '\0'; } /* ============== BotAI ============== */ int BotAI(int client, float thinktime) { bot_state_t *bs; char buf[1024], *args; int j; trap_EA_ResetInput(client); // bs = botstates[client]; if (!bs || !bs->inuse) { BotAI_Print(PRT_FATAL, "BotAI: client %d is not setup\n", client); return qfalse; } //retrieve the current client state BotAI_GetClientState( client, &bs->cur_ps ); //retrieve any waiting server commands while( trap_BotGetServerCommand(client, buf, sizeof(buf)) ) { //have buf point to the command and args to the command arguments args = strchr( buf, ' '); if (!args) continue; *args++ = '\0'; //remove color espace sequences from the arguments RemoveColorEscapeSequences( args ); if (!Q_stricmp(buf, "cp ")) { /*CenterPrintf*/ } else if (!Q_stricmp(buf, "cs")) { /*ConfigStringModified*/ } else if (!Q_stricmp(buf, "print")) { //remove first and last quote from the chat message memmove(args, args+1, (int)strlen(args)); args[strlen(args)-1] = '\0'; trap_BotQueueConsoleMessage(bs->cs, CMS_NORMAL, args); } else if (!Q_stricmp(buf, "chat")) { //remove first and last quote from the chat message memmove(args, args+1, (int)strlen(args)); args[strlen(args)-1] = '\0'; trap_BotQueueConsoleMessage(bs->cs, CMS_CHAT, args); } else if (!Q_stricmp(buf, "tchat")) { //remove first and last quote from the chat message memmove(args, args+1, (int)strlen(args)); args[strlen(args)-1] = '\0'; trap_BotQueueConsoleMessage(bs->cs, CMS_CHAT, args); } else if (!Q_stricmp(buf, "scores")) { /*FIXME: parse scores?*/ } else if (!Q_stricmp(buf, "clientLevelShot")) { /*ignore*/ } } //add the delta angles to the bot's current view angles for (j = 0; j < 3; j++) { bs->viewangles[j] = AngleMod(bs->viewangles[j] + SHORT2ANGLE(bs->cur_ps.delta_angles[j])); } //increase the local time of the bot bs->ltime += thinktime; // bs->thinktime = thinktime; //origin of the bot VectorCopy(bs->cur_ps.origin, bs->origin); //eye coordinates of the bot VectorCopy(bs->cur_ps.origin, bs->eye); bs->eye[2] += bs->cur_ps.viewheight; //get the area the bot is in bs->areanum = BotPointAreaNum(bs->origin); //the real AI BotDeathmatchAI(bs, thinktime); //set the weapon selection every AI frame trap_EA_SelectWeapon(bs->client, bs->weaponnum); //subtract the delta angles for (j = 0; j < 3; j++) { bs->viewangles[j] = AngleMod(bs->viewangles[j] - SHORT2ANGLE(bs->cur_ps.delta_angles[j])); } //everything was ok return qtrue; } /* ================== BotScheduleBotThink ================== */ void BotScheduleBotThink(void) { int i, botnum; botnum = 0; for( i = 0; i < MAX_CLIENTS; i++ ) { if( !botstates[i] || !botstates[i]->inuse ) { continue; } //initialize the bot think residual time botstates[i]->botthink_residual = bot_thinktime.integer * botnum / numbots; botnum++; } } /* ============== BotWriteSessionData ============== */ void BotWriteSessionData(bot_state_t *bs) { const char *s; const char *var; s = va( "%i %i %i %i %i %i %i %i" " %f %f %f" " %f %f %f" " %f %f %f", bs->lastgoal_decisionmaker, bs->lastgoal_ltgtype, bs->lastgoal_teammate, bs->lastgoal_teamgoal.areanum, bs->lastgoal_teamgoal.entitynum, bs->lastgoal_teamgoal.flags, bs->lastgoal_teamgoal.iteminfo, bs->lastgoal_teamgoal.number, bs->lastgoal_teamgoal.origin[0], bs->lastgoal_teamgoal.origin[1], bs->lastgoal_teamgoal.origin[2], bs->lastgoal_teamgoal.mins[0], bs->lastgoal_teamgoal.mins[1], bs->lastgoal_teamgoal.mins[2], bs->lastgoal_teamgoal.maxs[0], bs->lastgoal_teamgoal.maxs[1], bs->lastgoal_teamgoal.maxs[2] ); var = va( "botsession%i", bs->client ); trap_Cvar_Set( var, s ); } /* ============== BotReadSessionData ============== */ void BotReadSessionData(bot_state_t *bs) { char s[MAX_STRING_CHARS]; const char *var; var = va( "botsession%i", bs->client ); trap_Cvar_VariableStringBuffer( var, s, sizeof(s) ); sscanf(s, "%i %i %i %i %i %i %i %i" " %f %f %f" " %f %f %f" " %f %f %f", &bs->lastgoal_decisionmaker, &bs->lastgoal_ltgtype, &bs->lastgoal_teammate, &bs->lastgoal_teamgoal.areanum, &bs->lastgoal_teamgoal.entitynum, &bs->lastgoal_teamgoal.flags, &bs->lastgoal_teamgoal.iteminfo, &bs->lastgoal_teamgoal.number, &bs->lastgoal_teamgoal.origin[0], &bs->lastgoal_teamgoal.origin[1], &bs->lastgoal_teamgoal.origin[2], &bs->lastgoal_teamgoal.mins[0], &bs->lastgoal_teamgoal.mins[1], &bs->lastgoal_teamgoal.mins[2], &bs->lastgoal_teamgoal.maxs[0], &bs->lastgoal_teamgoal.maxs[1], &bs->lastgoal_teamgoal.maxs[2] ); } /* ============== BotAISetupClient ============== */ int BotAISetupClient(int client, struct bot_settings_s *settings, qboolean restart) { char filename[MAX_PATH], name[MAX_PATH], gender[MAX_PATH]; bot_state_t *bs; int errnum; if (!botstates[client]) botstates[client] = G_Alloc(sizeof(bot_state_t)); bs = botstates[client]; if (bs && bs->inuse) { BotAI_Print(PRT_FATAL, "BotAISetupClient: client %d already setup\n", client); return qfalse; } if (!trap_AAS_Initialized()) { BotAI_Print(PRT_FATAL, "AAS not initialized\n"); return qfalse; } //load the bot character bs->character = trap_BotLoadCharacter(settings->characterfile, settings->skill); if (!bs->character) { BotAI_Print(PRT_FATAL, "couldn't load skill %f from %s\n", settings->skill, settings->characterfile); return qfalse; } //copy the settings memcpy(&bs->settings, settings, sizeof(bot_settings_t)); //allocate a goal state bs->gs = trap_BotAllocGoalState(client); //load the item weights trap_Characteristic_String(bs->character, CHARACTERISTIC_ITEMWEIGHTS, filename, MAX_PATH); errnum = trap_BotLoadItemWeights(bs->gs, filename); if (errnum != BLERR_NOERROR) { trap_BotFreeGoalState(bs->gs); return qfalse; } //allocate a weapon state bs->ws = trap_BotAllocWeaponState(); //load the weapon weights trap_Characteristic_String(bs->character, CHARACTERISTIC_WEAPONWEIGHTS, filename, MAX_PATH); errnum = trap_BotLoadWeaponWeights(bs->ws, filename); if (errnum != BLERR_NOERROR) { trap_BotFreeGoalState(bs->gs); trap_BotFreeWeaponState(bs->ws); return qfalse; } //allocate a chat state bs->cs = trap_BotAllocChatState(); //load the chat file trap_Characteristic_String(bs->character, CHARACTERISTIC_CHAT_FILE, filename, MAX_PATH); trap_Characteristic_String(bs->character, CHARACTERISTIC_CHAT_NAME, name, MAX_PATH); errnum = trap_BotLoadChatFile(bs->cs, filename, name); if (errnum != BLERR_NOERROR) { trap_BotFreeChatState(bs->cs); trap_BotFreeGoalState(bs->gs); trap_BotFreeWeaponState(bs->ws); return qfalse; } //get the gender characteristic trap_Characteristic_String(bs->character, CHARACTERISTIC_GENDER, gender, MAX_PATH); //set the chat gender if (*gender == 'f' || *gender == 'F') trap_BotSetChatGender(bs->cs, CHAT_GENDERFEMALE); else if (*gender == 'm' || *gender == 'M') trap_BotSetChatGender(bs->cs, CHAT_GENDERMALE); else trap_BotSetChatGender(bs->cs, CHAT_GENDERLESS); bs->inuse = qtrue; bs->client = client; bs->entitynum = client; bs->setupcount = 4; bs->entergame_time = FloatTime(); bs->ms = trap_BotAllocMoveState(); bs->walker = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_WALKER, 0, 1); numbots++; if (trap_Cvar_VariableIntegerValue("bot_testichat")) { trap_BotLibVarSet("bot_testichat", "1"); BotChatTest(bs); } //NOTE: reschedule the bot thinking BotScheduleBotThink(); //if interbreeding start with a mutation if (bot_interbreed) { trap_BotMutateGoalFuzzyLogic(bs->gs, 1); } // if we kept the bot client if (restart) { BotReadSessionData(bs); } //bot has been setup succesfully return qtrue; } /* ============== BotAIShutdownClient ============== */ int BotAIShutdownClient(int client, qboolean restart) { bot_state_t *bs; bs = botstates[client]; if (!bs || !bs->inuse) { //BotAI_Print(PRT_ERROR, "BotAIShutdownClient: client %d already shutdown\n", client); return qfalse; } if (restart) { BotWriteSessionData(bs); } if (BotChat_ExitGame(bs)) { trap_BotEnterChat(bs->cs, bs->client, CHAT_ALL); } trap_BotFreeMoveState(bs->ms); //free the goal state` trap_BotFreeGoalState(bs->gs); //free the chat file trap_BotFreeChatState(bs->cs); //free the weapon weights trap_BotFreeWeaponState(bs->ws); //free the bot character trap_BotFreeCharacter(bs->character); // BotFreeWaypoints(bs->checkpoints); BotFreeWaypoints(bs->patrolpoints); //clear activate goal stack BotClearActivateGoalStack(bs); //clear the bot state memset(bs, 0, sizeof(bot_state_t)); //set the inuse flag to qfalse bs->inuse = qfalse; //there's one bot less numbots--; //everything went ok return qtrue; } /* ============== BotResetState called when a bot enters the intermission or observer mode and when the level is changed ============== */ void BotResetState(bot_state_t *bs) { int client, entitynum, inuse; int movestate, goalstate, chatstate, weaponstate; bot_settings_t settings; int character; playerState_t ps; //current player state float entergame_time; //save some things that should not be reset here memcpy(&settings, &bs->settings, sizeof(bot_settings_t)); memcpy(&ps, &bs->cur_ps, sizeof(playerState_t)); inuse = bs->inuse; client = bs->client; entitynum = bs->entitynum; character = bs->character; movestate = bs->ms; goalstate = bs->gs; chatstate = bs->cs; weaponstate = bs->ws; entergame_time = bs->entergame_time; //free checkpoints and patrol points BotFreeWaypoints(bs->checkpoints); BotFreeWaypoints(bs->patrolpoints); //reset the whole state memset(bs, 0, sizeof(bot_state_t)); //copy back some state stuff that should not be reset bs->ms = movestate; bs->gs = goalstate; bs->cs = chatstate; bs->ws = weaponstate; memcpy(&bs->cur_ps, &ps, sizeof(playerState_t)); memcpy(&bs->settings, &settings, sizeof(bot_settings_t)); bs->inuse = inuse; bs->client = client; bs->entitynum = entitynum; bs->character = character; bs->entergame_time = entergame_time; //reset several states if (bs->ms) trap_BotResetMoveState(bs->ms); if (bs->gs) trap_BotResetGoalState(bs->gs); if (bs->ws) trap_BotResetWeaponState(bs->ws); if (bs->gs) trap_BotResetAvoidGoals(bs->gs); if (bs->ms) trap_BotResetAvoidReach(bs->ms); } /* ============== BotAILoadMap ============== */ int BotAILoadMap( int restart ) { int i; vmCvar_t mapname; if (!restart) { trap_Cvar_Register( &mapname, "mapname", "", CVAR_SERVERINFO | CVAR_ROM ); trap_BotLibLoadMap( mapname.string ); } for (i = 0; i < MAX_CLIENTS; i++) { if (botstates[i] && botstates[i]->inuse) { BotResetState( botstates[i] ); botstates[i]->setupcount = 4; } } BotSetupDeathmatchAI(); return qtrue; } /* ================== BotAIStartFrame ================== */ int BotAIStartFrame(int time) { int i; gentity_t *ent; bot_entitystate_t state; int elapsed_time, thinktime; static int local_time; static int botlib_residual; static int lastbotthink_time; G_CheckBotSpawn(); trap_Cvar_Update(&bot_rocketjump); trap_Cvar_Update(&bot_grapple); trap_Cvar_Update(&bot_fastchat); trap_Cvar_Update(&bot_nochat); trap_Cvar_Update(&bot_testrchat); trap_Cvar_Update(&bot_thinktime); trap_Cvar_Update(&bot_memorydump); trap_Cvar_Update(&bot_saveroutingcache); trap_Cvar_Update(&bot_pause); trap_Cvar_Update(&bot_report); if (bot_report.integer) { // BotTeamplayReport(); // trap_Cvar_Set("bot_report", "0"); BotUpdateInfoConfigStrings(); } if (bot_pause.integer) { // execute bot user commands every frame for( i = 0; i < MAX_CLIENTS; i++ ) { if( !botstates[i] || !botstates[i]->inuse ) { continue; } if( g_entities[i].client->pers.connected != CON_CONNECTED ) { continue; } botstates[i]->lastucmd.forwardmove = 0; botstates[i]->lastucmd.rightmove = 0; botstates[i]->lastucmd.upmove = 0; botstates[i]->lastucmd.buttons = 0; botstates[i]->lastucmd.serverTime = time; trap_BotUserCommand(botstates[i]->client, &botstates[i]->lastucmd); } return qtrue; } if (bot_memorydump.integer) { trap_BotLibVarSet("memorydump", "1"); trap_Cvar_Set("bot_memorydump", "0"); } if (bot_saveroutingcache.integer) { trap_BotLibVarSet("saveroutingcache", "1"); trap_Cvar_Set("bot_saveroutingcache", "0"); } //check if bot interbreeding is activated BotInterbreeding(); //cap the bot think time if (bot_thinktime.integer > 200) { trap_Cvar_Set("bot_thinktime", "200"); } //if the bot think time changed we should reschedule the bots if (bot_thinktime.integer != lastbotthink_time) { lastbotthink_time = bot_thinktime.integer; BotScheduleBotThink(); } elapsed_time = time - local_time; local_time = time; botlib_residual += elapsed_time; if (elapsed_time > bot_thinktime.integer) thinktime = elapsed_time; else thinktime = bot_thinktime.integer; // update the bot library if ( botlib_residual >= thinktime ) { botlib_residual -= thinktime; trap_BotLibStartFrame((float) time / 1000); if (!trap_AAS_Initialized()) return qfalse; //update entities in the botlib for (i = 0; i < MAX_GENTITIES; i++) { ent = &g_entities[i]; if (!ent->inuse) { trap_BotLibUpdateEntity(i, NULL); continue; } if (!ent->r.linked) { trap_BotLibUpdateEntity(i, NULL); continue; } if (ent->r.svFlags & SVF_NOCLIENT) { trap_BotLibUpdateEntity(i, NULL); continue; } // do not update missiles if (ent->s.eType == ET_MISSILE && ent->s.weapon != WP_GRAPPLING_HOOK) { trap_BotLibUpdateEntity(i, NULL); continue; } // do not update event only entities if (ent->s.eType > ET_EVENTS) { trap_BotLibUpdateEntity(i, NULL); continue; } // memset(&state, 0, sizeof(bot_entitystate_t)); // VectorCopy(ent->r.currentOrigin, state.origin); if (i < MAX_CLIENTS) { VectorCopy(ent->s.apos.trBase, state.angles); } else { VectorCopy(ent->r.currentAngles, state.angles); } VectorCopy(ent->s.origin2, state.old_origin); VectorCopy(ent->r.mins, state.mins); VectorCopy(ent->r.maxs, state.maxs); state.type = ent->s.eType; state.flags = ent->s.eFlags; if (ent->r.bmodel) state.solid = SOLID_BSP; else state.solid = SOLID_BBOX; state.groundent = ent->s.groundEntityNum; state.modelindex = ent->s.modelindex; state.modelindex2 = ent->s.modelindex2; state.frame = ent->s.frame; state.event = ent->s.event; state.eventParm = ent->s.eventParm; state.powerups = ent->s.powerups; state.legsAnim = ent->s.legsAnim; state.torsoAnim = ent->s.torsoAnim; state.weapon = ent->s.weapon; // trap_BotLibUpdateEntity(i, &state); } BotAIRegularUpdate(); } floattime = trap_AAS_Time(); // execute scheduled bot AI for( i = 0; i < MAX_CLIENTS; i++ ) { if( !botstates[i] || !botstates[i]->inuse ) { continue; } // botstates[i]->botthink_residual += elapsed_time; // if ( botstates[i]->botthink_residual >= thinktime ) { botstates[i]->botthink_residual -= thinktime; if (!trap_AAS_Initialized()) return qfalse; if (g_entities[i].client->pers.connected == CON_CONNECTED) { BotAI(i, (float) thinktime / 1000); } } } // execute bot user commands every frame for( i = 0; i < MAX_CLIENTS; i++ ) { if( !botstates[i] || !botstates[i]->inuse ) { continue; } if( g_entities[i].client->pers.connected != CON_CONNECTED ) { continue; } BotUpdateInput(botstates[i], time, elapsed_time); trap_BotUserCommand(botstates[i]->client, &botstates[i]->lastucmd); } return qtrue; } /* ============== BotInitLibrary ============== */ int BotInitLibrary(void) { char buf[144]; //set the maxclients and maxentities library variables before calling BotSetupLibrary trap_Cvar_VariableStringBuffer("sv_maxclients", buf, sizeof(buf)); if (!strlen(buf)) strcpy(buf, "8"); trap_BotLibVarSet("maxclients", buf); Com_sprintf(buf, sizeof(buf), "%d", MAX_GENTITIES); trap_BotLibVarSet("maxentities", buf); //bsp checksum trap_Cvar_VariableStringBuffer("sv_mapChecksum", buf, sizeof(buf)); if (strlen(buf)) trap_BotLibVarSet("sv_mapChecksum", buf); //maximum number of aas links trap_Cvar_VariableStringBuffer("max_aaslinks", buf, sizeof(buf)); if (strlen(buf)) trap_BotLibVarSet("max_aaslinks", buf); //maximum number of items in a level trap_Cvar_VariableStringBuffer("max_levelitems", buf, sizeof(buf)); if (strlen(buf)) trap_BotLibVarSet("max_levelitems", buf); //game type trap_Cvar_VariableStringBuffer("g_gametype", buf, sizeof(buf)); if (!strlen(buf)) strcpy(buf, "0"); trap_BotLibVarSet("g_gametype", buf); //bot developer mode and log file trap_BotLibVarSet("bot_developer", bot_developer.string); trap_BotLibVarSet("log", buf); //no chatting trap_Cvar_VariableStringBuffer("bot_nochat", buf, sizeof(buf)); if (strlen(buf)) trap_BotLibVarSet("nochat", "0"); //visualize jump pads trap_Cvar_VariableStringBuffer("bot_visualizejumppads", buf, sizeof(buf)); if (strlen(buf)) trap_BotLibVarSet("bot_visualizejumppads", buf); //forced clustering calculations trap_Cvar_VariableStringBuffer("bot_forceclustering", buf, sizeof(buf)); if (strlen(buf)) trap_BotLibVarSet("forceclustering", buf); //forced reachability calculations trap_Cvar_VariableStringBuffer("bot_forcereachability", buf, sizeof(buf)); if (strlen(buf)) trap_BotLibVarSet("forcereachability", buf); //force writing of AAS to file trap_Cvar_VariableStringBuffer("bot_forcewrite", buf, sizeof(buf)); if (strlen(buf)) trap_BotLibVarSet("forcewrite", buf); //no AAS optimization trap_Cvar_VariableStringBuffer("bot_aasoptimize", buf, sizeof(buf)); if (strlen(buf)) trap_BotLibVarSet("aasoptimize", buf); // trap_Cvar_VariableStringBuffer("bot_saveroutingcache", buf, sizeof(buf)); if (strlen(buf)) trap_BotLibVarSet("saveroutingcache", buf); //reload instead of cache bot character files trap_Cvar_VariableStringBuffer("bot_reloadcharacters", buf, sizeof(buf)); if (!strlen(buf)) strcpy(buf, "0"); trap_BotLibVarSet("bot_reloadcharacters", buf); //base directory trap_Cvar_VariableStringBuffer("fs_basepath", buf, sizeof(buf)); if (strlen(buf)) trap_BotLibVarSet("basedir", buf); //game directory trap_Cvar_VariableStringBuffer("fs_game", buf, sizeof(buf)); if (strlen(buf)) trap_BotLibVarSet("gamedir", buf); //cd directory trap_Cvar_VariableStringBuffer("fs_cdpath", buf, sizeof(buf)); if (strlen(buf)) trap_BotLibVarSet("cddir", buf); //setup the bot library return trap_BotLibSetup(); } /* ============== BotAISetup ============== */ int BotAISetup( int restart ) { int errnum; trap_Cvar_Register(&bot_thinktime, "bot_thinktime", "100", CVAR_CHEAT); trap_Cvar_Register(&bot_memorydump, "bot_memorydump", "0", CVAR_CHEAT); trap_Cvar_Register(&bot_saveroutingcache, "bot_saveroutingcache", "0", CVAR_CHEAT); trap_Cvar_Register(&bot_pause, "bot_pause", "0", CVAR_CHEAT); trap_Cvar_Register(&bot_report, "bot_report", "0", CVAR_CHEAT); trap_Cvar_Register(&bot_testsolid, "bot_testsolid", "0", CVAR_CHEAT); trap_Cvar_Register(&bot_testclusters, "bot_testclusters", "0", CVAR_CHEAT); trap_Cvar_Register(&bot_developer, "bot_developer", "0", CVAR_CHEAT); trap_Cvar_Register(&bot_interbreedchar, "bot_interbreedchar", "", 0); trap_Cvar_Register(&bot_interbreedbots, "bot_interbreedbots", "10", 0); trap_Cvar_Register(&bot_interbreedcycle, "bot_interbreedcycle", "20", 0); trap_Cvar_Register(&bot_interbreedwrite, "bot_interbreedwrite", "", 0); //if the game is restarted for a tournament if (restart) { return qtrue; } //initialize the bot states memset( botstates, 0, sizeof(botstates) ); errnum = BotInitLibrary(); if (errnum != BLERR_NOERROR) return qfalse; return qtrue; } /* ============== BotAIShutdown ============== */ int BotAIShutdown( int restart ) { int i; //if the game is restarted for a tournament if ( restart ) { //shutdown all the bots in the botlib for (i = 0; i < MAX_CLIENTS; i++) { if (botstates[i] && botstates[i]->inuse) { BotAIShutdownClient(botstates[i]->client, restart); } } //don't shutdown the bot library } else { trap_BotLibShutdown(); } return qtrue; } ================================================ FILE: src/game/ai_main.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: ai_main.h * * desc: Quake3 bot AI * * $Archive: /source/code/botai/ai_chat.c $ * *****************************************************************************/ //#define DEBUG #define CTF #define MAX_ITEMS 256 //bot flags #define BFL_STRAFERIGHT 1 //strafe to the right #define BFL_ATTACKED 2 //bot has attacked last ai frame #define BFL_ATTACKJUMPED 4 //bot jumped during attack last frame #define BFL_AIMATENEMY 8 //bot aimed at the enemy this frame #define BFL_AVOIDRIGHT 16 //avoid obstacles by going to the right #define BFL_IDEALVIEWSET 32 //bot has ideal view angles set #define BFL_FIGHTSUICIDAL 64 //bot is in a suicidal fight //long term goal types #define LTG_TEAMHELP 1 //help a team mate #define LTG_TEAMACCOMPANY 2 //accompany a team mate #define LTG_DEFENDKEYAREA 3 //defend a key area #define LTG_GETFLAG 4 //get the enemy flag #define LTG_RUSHBASE 5 //rush to the base #define LTG_RETURNFLAG 6 //return the flag #define LTG_CAMP 7 //camp somewhere #define LTG_CAMPORDER 8 //ordered to camp somewhere #define LTG_PATROL 9 //patrol #define LTG_GETITEM 10 //get an item #define LTG_KILL 11 //kill someone #define LTG_HARVEST 12 //harvest skulls #define LTG_ATTACKENEMYBASE 13 //attack the enemy base #define LTG_MAKELOVE_UNDER 14 #define LTG_MAKELOVE_ONTOP 15 //some goal dedication times #define TEAM_HELP_TIME 60 //1 minute teamplay help time #define TEAM_ACCOMPANY_TIME 600 //10 minutes teamplay accompany time #define TEAM_DEFENDKEYAREA_TIME 600 //10 minutes ctf defend base time #define TEAM_CAMP_TIME 600 //10 minutes camping time #define TEAM_PATROL_TIME 600 //10 minutes patrolling time #define TEAM_LEAD_TIME 600 //10 minutes taking the lead #define TEAM_GETITEM_TIME 60 //1 minute #define TEAM_KILL_SOMEONE 180 //3 minute to kill someone #define TEAM_ATTACKENEMYBASE_TIME 600 //10 minutes #define TEAM_HARVEST_TIME 120 //2 minutes #define CTF_GETFLAG_TIME 600 //10 minutes ctf get flag time #define CTF_RUSHBASE_TIME 120 //2 minutes ctf rush base time #define CTF_RETURNFLAG_TIME 180 //3 minutes to return the flag #define CTF_ROAM_TIME 60 //1 minute ctf roam time //patrol flags #define PATROL_LOOP 1 #define PATROL_REVERSE 2 #define PATROL_BACK 4 //teamplay task preference #define TEAMTP_DEFENDER 1 #define TEAMTP_ATTACKER 2 //CTF strategy #define CTFS_AGRESSIVE 1 //copied from the aas file header #define PRESENCE_NONE 1 #define PRESENCE_NORMAL 2 #define PRESENCE_CROUCH 4 // #define MAX_PROXMINES 64 //check points typedef struct bot_waypoint_s { int inuse; char name[32]; bot_goal_t goal; struct bot_waypoint_s *next, *prev; } bot_waypoint_t; #define MAX_ACTIVATESTACK 8 #define MAX_ACTIVATEAREAS 32 typedef struct bot_activategoal_s { int inuse; bot_goal_t goal; //goal to activate (buttons etc.) float time; //time to activate something float start_time; //time starting to activate something float justused_time; //time the goal was used int shoot; //true if bot has to shoot to activate int weapon; //weapon to be used for activation vec3_t target; //target to shoot at to activate something vec3_t origin; //origin of the blocking entity to activate int areas[MAX_ACTIVATEAREAS]; //routing areas disabled by blocking entity int numareas; //number of disabled routing areas int areasdisabled; //true if the areas are disabled for the routing struct bot_activategoal_s *next; //next activate goal on stack } bot_activategoal_t; //bot state typedef struct bot_state_s { int inuse; //true if this state is used by a bot client int botthink_residual; //residual for the bot thinks int client; //client number of the bot int entitynum; //entity number of the bot playerState_t cur_ps; //current player state int last_eFlags; //last ps flags usercmd_t lastucmd; //usercmd from last frame int entityeventTime[1024]; //last entity event time // bot_settings_t settings; //several bot settings int (*ainode)(struct bot_state_s *bs); //current AI node float thinktime; //time the bot thinks this frame vec3_t origin; //origin of the bot vec3_t velocity; //velocity of the bot int presencetype; //presence type of the bot vec3_t eye; //eye coordinates of the bot int areanum; //the number of the area the bot is in int inventory[MAX_ITEMS]; //string with items amounts the bot has int tfl; //the travel flags the bot uses int flags; //several flags int respawn_wait; //wait until respawned int lasthealth; //health value previous frame int lastkilledplayer; //last killed player int lastkilledby; //player that last killed this bot int botdeathtype; //the death type of the bot int enemydeathtype; //the death type of the enemy int botsuicide; //true when the bot suicides int enemysuicide; //true when the enemy of the bot suicides int setupcount; //true when the bot has just been setup int map_restart; //true when the map is being restarted int entergamechat; //true when the bot used an enter game chat int num_deaths; //number of time this bot died int num_kills; //number of kills of this bot int revenge_enemy; //the revenge enemy int revenge_kills; //number of kills the enemy made int lastframe_health; //health value the last frame int lasthitcount; //number of hits last frame int chatto; //chat to all or team float walker; //walker charactertic float ltime; //local bot time float entergame_time; //time the bot entered the game float ltg_time; //long term goal time float nbg_time; //nearby goal time float respawn_time; //time the bot takes to respawn float respawnchat_time; //time the bot started a chat during respawn float chase_time; //time the bot will chase the enemy float enemyvisible_time; //time the enemy was last visible float check_time; //time to check for nearby items float stand_time; //time the bot is standing still float lastchat_time; //time the bot last selected a chat float kamikaze_time; //time to check for kamikaze usage float invulnerability_time; //time to check for invulnerability usage float standfindenemy_time; //time to find enemy while standing float attackstrafe_time; //time the bot is strafing in one dir float attackcrouch_time; //time the bot will stop crouching float attackchase_time; //time the bot chases during actual attack float attackjump_time; //time the bot jumped during attack float enemysight_time; //time before reacting to enemy float enemydeath_time; //time the enemy died float enemyposition_time; //time the position and velocity of the enemy were stored float defendaway_time; //time away while defending float defendaway_range; //max travel time away from defend area float rushbaseaway_time; //time away from rushing to the base float attackaway_time; //time away from attacking the enemy base float harvestaway_time; //time away from harvesting float ctfroam_time; //time the bot is roaming in ctf float killedenemy_time; //time the bot killed the enemy float arrive_time; //time arrived (at companion) float lastair_time; //last time the bot had air float teleport_time; //last time the bot teleported float camp_time; //last time camped float camp_range; //camp range float weaponchange_time; //time the bot started changing weapons float firethrottlewait_time; //amount of time to wait float firethrottleshoot_time; //amount of time to shoot float notblocked_time; //last time the bot was not blocked float blockedbyavoidspot_time; //time blocked by an avoid spot float predictobstacles_time; //last time the bot predicted obstacles int predictobstacles_goalareanum; //last goal areanum the bot predicted obstacles for vec3_t aimtarget; vec3_t enemyvelocity; //enemy velocity 0.5 secs ago during battle vec3_t enemyorigin; //enemy origin 0.5 secs ago during battle // int kamikazebody; //kamikaze body int proxmines[MAX_PROXMINES]; int numproxmines; // int character; //the bot character int ms; //move state of the bot int gs; //goal state of the bot int cs; //chat state of the bot int ws; //weapon state of the bot // int enemy; //enemy entity number int lastenemyareanum; //last reachability area the enemy was in vec3_t lastenemyorigin; //last origin of the enemy in the reachability area int weaponnum; //current weapon number vec3_t viewangles; //current view angles vec3_t ideal_viewangles; //ideal view angles vec3_t viewanglespeed; // int ltgtype; //long term goal type // team goals int teammate; //team mate involved in this team goal int decisionmaker; //player who decided to go for this goal int ordered; //true if ordered to do something float order_time; //time ordered to do something int owndecision_time; //time the bot made it's own decision bot_goal_t teamgoal; //the team goal bot_goal_t altroutegoal; //alternative route goal float reachedaltroutegoal_time; //time the bot reached the alt route goal float teammessage_time; //time to message team mates what the bot is doing float teamgoal_time; //time to stop helping team mate float teammatevisible_time; //last time the team mate was NOT visible int teamtaskpreference; //team task preference // last ordered team goal int lastgoal_decisionmaker; int lastgoal_ltgtype; int lastgoal_teammate; bot_goal_t lastgoal_teamgoal; // for leading team mates int lead_teammate; //team mate the bot is leading bot_goal_t lead_teamgoal; //team goal while leading float lead_time; //time leading someone float leadvisible_time; //last time the team mate was visible float leadmessage_time; //last time a messaged was sent to the team mate float leadbackup_time; //time backing up towards team mate // char teamleader[32]; //netname of the team leader float askteamleader_time; //time asked for team leader float becometeamleader_time; //time the bot will become the team leader float teamgiveorders_time; //time to give team orders float lastflagcapture_time; //last time a flag was captured int numteammates; //number of team mates int redflagstatus; //0 = at base, 1 = not at base int blueflagstatus; //0 = at base, 1 = not at base int neutralflagstatus; //0 = at base, 1 = our team has flag, 2 = enemy team has flag, 3 = enemy team dropped the flag int flagstatuschanged; //flag status changed int forceorders; //true if forced to give orders int flagcarrier; //team mate carrying the enemy flag int ctfstrategy; //ctf strategy char subteam[32]; //sub team name float formation_dist; //formation team mate intervening space char formation_teammate[16]; //netname of the team mate the bot uses for relative positioning float formation_angle; //angle relative to the formation team mate vec3_t formation_dir; //the direction the formation is moving in vec3_t formation_origin; //origin the bot uses for relative positioning bot_goal_t formation_goal; //formation goal bot_activategoal_t *activatestack; //first activate goal on the stack bot_activategoal_t activategoalheap[MAX_ACTIVATESTACK]; //activate goal heap bot_waypoint_t *checkpoints; //check points bot_waypoint_t *patrolpoints; //patrol points bot_waypoint_t *curpatrolpoint; //current patrol point the bot is going for int patrolflags; //patrol flags } bot_state_t; //resets the whole bot state void BotResetState(bot_state_t *bs); //returns the number of bots in the game int NumBots(void); //returns info about the entity void BotEntityInfo(int entnum, aas_entityinfo_t *info); extern float floattime; #define FloatTime() floattime // from the game source void QDECL BotAI_Print(int type, char *fmt, ...); void QDECL QDECL BotAI_BotInitialChat( bot_state_t *bs, char *type, ... ); void BotAI_Trace(bsp_trace_t *bsptrace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask); int BotAI_GetClientState( int clientNum, playerState_t *state ); int BotAI_GetEntityState( int entityNum, entityState_t *state ); int BotAI_GetSnapshotEntity( int clientNum, int sequence, entityState_t *state ); int BotTeamLeader(bot_state_t *bs); ================================================ FILE: src/game/ai_team.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: ai_team.c * * desc: Quake3 bot AI * * $Archive: /MissionPack/code/game/ai_team.c $ * *****************************************************************************/ #include "g_local.h" #include "botlib.h" #include "be_aas.h" #include "be_ea.h" #include "be_ai_char.h" #include "be_ai_chat.h" #include "be_ai_gen.h" #include "be_ai_goal.h" #include "be_ai_move.h" #include "be_ai_weap.h" // #include "ai_main.h" #include "ai_dmq3.h" #include "ai_chat.h" #include "ai_cmd.h" #include "ai_dmnet.h" #include "ai_team.h" #include "ai_vcmd.h" #include "match.h" // for the voice chats #include "menudef.h" //ctf task preferences for a client typedef struct bot_ctftaskpreference_s { char name[36]; int preference; } bot_ctftaskpreference_t; bot_ctftaskpreference_t ctftaskpreferences[MAX_CLIENTS]; /* ================== BotValidTeamLeader ================== */ int BotValidTeamLeader(bot_state_t *bs) { if (!strlen(bs->teamleader)) return qfalse; if (ClientFromName(bs->teamleader) == -1) return qfalse; return qtrue; } /* ================== BotNumTeamMates ================== */ int BotNumTeamMates(bot_state_t *bs) { int i, numplayers; char buf[MAX_INFO_STRING]; static int maxclients; if (!maxclients) maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); numplayers = 0; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; //skip spectators if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue; // if (BotSameTeam(bs, i)) { numplayers++; } } return numplayers; } /* ================== BotClientTravelTimeToGoal ================== */ int BotClientTravelTimeToGoal(int client, bot_goal_t *goal) { playerState_t ps; int areanum; BotAI_GetClientState(client, &ps); areanum = BotPointAreaNum(ps.origin); if (!areanum) return 1; return trap_AAS_AreaTravelTimeToGoalArea(areanum, ps.origin, goal->areanum, TFL_DEFAULT); } /* ================== BotSortTeamMatesByBaseTravelTime ================== */ int BotSortTeamMatesByBaseTravelTime(bot_state_t *bs, int *teammates, int maxteammates) { int i, j, k, numteammates, traveltime; char buf[MAX_INFO_STRING]; static int maxclients; int traveltimes[MAX_CLIENTS]; bot_goal_t *goal = NULL; if (gametype == GT_CTF || gametype == GT_1FCTF) { if (BotTeam(bs) == TEAM_RED) goal = &ctf_redflag; else goal = &ctf_blueflag; } if (!maxclients) maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); numteammates = 0; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; //skip spectators if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue; // if (BotSameTeam(bs, i)) { // traveltime = BotClientTravelTimeToGoal(i, goal); // for (j = 0; j < numteammates; j++) { if (traveltime < traveltimes[j]) { for (k = numteammates; k > j; k--) { traveltimes[k] = traveltimes[k-1]; teammates[k] = teammates[k-1]; } break; } } traveltimes[j] = traveltime; teammates[j] = i; numteammates++; if (numteammates >= maxteammates) break; } } return numteammates; } /* ================== BotSetTeamMateTaskPreference ================== */ void BotSetTeamMateTaskPreference(bot_state_t *bs, int teammate, int preference) { char teammatename[MAX_NETNAME]; ctftaskpreferences[teammate].preference = preference; ClientName(teammate, teammatename, sizeof(teammatename)); strcpy(ctftaskpreferences[teammate].name, teammatename); } /* ================== BotGetTeamMateTaskPreference ================== */ int BotGetTeamMateTaskPreference(bot_state_t *bs, int teammate) { char teammatename[MAX_NETNAME]; if (!ctftaskpreferences[teammate].preference) return 0; ClientName(teammate, teammatename, sizeof(teammatename)); if (Q_stricmp(teammatename, ctftaskpreferences[teammate].name)) return 0; return ctftaskpreferences[teammate].preference; } /* ================== BotSortTeamMatesByTaskPreference ================== */ int BotSortTeamMatesByTaskPreference(bot_state_t *bs, int *teammates, int numteammates) { int defenders[MAX_CLIENTS], numdefenders; int attackers[MAX_CLIENTS], numattackers; int roamers[MAX_CLIENTS], numroamers; int i, preference; numdefenders = numattackers = numroamers = 0; for (i = 0; i < numteammates; i++) { preference = BotGetTeamMateTaskPreference(bs, teammates[i]); if (preference & TEAMTP_DEFENDER) { defenders[numdefenders++] = teammates[i]; } else if (preference & TEAMTP_ATTACKER) { attackers[numattackers++] = teammates[i]; } else { roamers[numroamers++] = teammates[i]; } } numteammates = 0; //defenders at the front of the list memcpy(&teammates[numteammates], defenders, numdefenders * sizeof(int)); numteammates += numdefenders; //roamers in the middle memcpy(&teammates[numteammates], roamers, numroamers * sizeof(int)); numteammates += numroamers; //attacker in the back of the list memcpy(&teammates[numteammates], attackers, numattackers * sizeof(int)); numteammates += numattackers; return numteammates; } /* ================== BotSayTeamOrders ================== */ void BotSayTeamOrderAlways(bot_state_t *bs, int toclient) { char teamchat[MAX_MESSAGE_SIZE]; char buf[MAX_MESSAGE_SIZE]; char name[MAX_NETNAME]; //if the bot is talking to itself if (bs->client == toclient) { //don't show the message just put it in the console message queue trap_BotGetChatMessage(bs->cs, buf, sizeof(buf)); ClientName(bs->client, name, sizeof(name)); Com_sprintf(teamchat, sizeof(teamchat), EC"(%s"EC")"EC": %s", name, buf); trap_BotQueueConsoleMessage(bs->cs, CMS_CHAT, teamchat); } else { trap_BotEnterChat(bs->cs, toclient, CHAT_TELL); } } /* ================== BotSayTeamOrders ================== */ void BotSayTeamOrder(bot_state_t *bs, int toclient) { BotSayTeamOrderAlways(bs, toclient); } /* ================== BotVoiceChat ================== */ void BotVoiceChat(bot_state_t *bs, int toclient, char *voicechat) { } /* ================== BotVoiceChatOnly ================== */ void BotVoiceChatOnly(bot_state_t *bs, int toclient, char *voicechat) { } /* ================== BotSayVoiceTeamOrder ================== */ void BotSayVoiceTeamOrder(bot_state_t *bs, int toclient, char *voicechat) { } /* ================== BotCTFOrders ================== */ void BotCTFOrders_BothFlagsNotAtBase(bot_state_t *bs) { int numteammates, defenders, attackers, i, other; int teammates[MAX_CLIENTS]; char name[MAX_NETNAME], carriername[MAX_NETNAME]; numteammates = BotSortTeamMatesByBaseTravelTime(bs, teammates, sizeof(teammates)); BotSortTeamMatesByTaskPreference(bs, teammates, numteammates); //different orders based on the number of team mates switch(bs->numteammates) { case 1: break; case 2: { //tell the one not carrying the flag to attack the enemy base if (teammates[0] != bs->flagcarrier) other = teammates[0]; else other = teammates[1]; ClientName(other, name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, other); BotSayVoiceTeamOrder(bs, other, VOICECHAT_GETFLAG); break; } case 3: { //tell the one closest to the base not carrying the flag to accompany the flag carrier if (teammates[0] != bs->flagcarrier) other = teammates[0]; else other = teammates[1]; ClientName(other, name, sizeof(name)); if ( bs->flagcarrier != -1 ) { ClientName(bs->flagcarrier, carriername, sizeof(carriername)); if (bs->flagcarrier == bs->client) { BotAI_BotInitialChat(bs, "cmd_accompanyme", name, NULL); BotSayVoiceTeamOrder(bs, other, VOICECHAT_FOLLOWME); } else { BotAI_BotInitialChat(bs, "cmd_accompany", name, carriername, NULL); BotSayVoiceTeamOrder(bs, other, VOICECHAT_FOLLOWFLAGCARRIER); } } else { // BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayVoiceTeamOrder(bs, other, VOICECHAT_GETFLAG); } BotSayTeamOrder(bs, other); //tell the one furthest from the the base not carrying the flag to get the enemy flag if (teammates[2] != bs->flagcarrier) other = teammates[2]; else other = teammates[1]; ClientName(other, name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, other); BotSayVoiceTeamOrder(bs, other, VOICECHAT_RETURNFLAG); break; } default: { defenders = (int) (float) numteammates * 0.4 + 0.5; if (defenders > 4) defenders = 4; attackers = (int) (float) numteammates * 0.5 + 0.5; if (attackers > 5) attackers = 5; if (bs->flagcarrier != -1) { ClientName(bs->flagcarrier, carriername, sizeof(carriername)); for (i = 0; i < defenders; i++) { // if (teammates[i] == bs->flagcarrier) { continue; } // ClientName(teammates[i], name, sizeof(name)); if (bs->flagcarrier == bs->client) { BotAI_BotInitialChat(bs, "cmd_accompanyme", name, NULL); BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_FOLLOWME); } else { BotAI_BotInitialChat(bs, "cmd_accompany", name, carriername, NULL); BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_FOLLOWFLAGCARRIER); } BotSayTeamOrder(bs, teammates[i]); } } else { for (i = 0; i < defenders; i++) { // if (teammates[i] == bs->flagcarrier) { continue; } // ClientName(teammates[i], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_GETFLAG); BotSayTeamOrder(bs, teammates[i]); } } for (i = 0; i < attackers; i++) { // if (teammates[numteammates - i - 1] == bs->flagcarrier) { continue; } // ClientName(teammates[numteammates - i - 1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[numteammates - i - 1]); BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_RETURNFLAG); } // break; } } } /* ================== BotCTFOrders ================== */ void BotCTFOrders_FlagNotAtBase(bot_state_t *bs) { int numteammates, defenders, attackers, i; int teammates[MAX_CLIENTS]; char name[MAX_NETNAME]; numteammates = BotSortTeamMatesByBaseTravelTime(bs, teammates, sizeof(teammates)); BotSortTeamMatesByTaskPreference(bs, teammates, numteammates); //passive strategy if (!(bs->ctfstrategy & CTFS_AGRESSIVE)) { //different orders based on the number of team mates switch(bs->numteammates) { case 1: break; case 2: { //both will go for the enemy flag ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_GETFLAG); // ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_GETFLAG); break; } case 3: { //keep one near the base for when the flag is returned ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); //the other two get the flag ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_GETFLAG); // ClientName(teammates[2], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[2]); BotSayVoiceTeamOrder(bs, teammates[2], VOICECHAT_GETFLAG); break; } default: { //keep some people near the base for when the flag is returned defenders = (int) (float) numteammates * 0.3 + 0.5; if (defenders > 3) defenders = 3; attackers = (int) (float) numteammates * 0.7 + 0.5; if (attackers > 6) attackers = 6; for (i = 0; i < defenders; i++) { // ClientName(teammates[i], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[i]); BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); } for (i = 0; i < attackers; i++) { // ClientName(teammates[numteammates - i - 1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[numteammates - i - 1]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_GETFLAG); } // break; } } } else { //different orders based on the number of team mates switch(bs->numteammates) { case 1: break; case 2: { //both will go for the enemy flag ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_GETFLAG); // ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_GETFLAG); break; } case 3: { //everyone go for the flag ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_GETFLAG); // ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_GETFLAG); // ClientName(teammates[2], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[2]); BotSayVoiceTeamOrder(bs, teammates[2], VOICECHAT_GETFLAG); break; } default: { //keep some people near the base for when the flag is returned defenders = (int) (float) numteammates * 0.2 + 0.5; if (defenders > 2) defenders = 2; attackers = (int) (float) numteammates * 0.7 + 0.5; if (attackers > 7) attackers = 7; for (i = 0; i < defenders; i++) { // ClientName(teammates[i], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[i]); BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); } for (i = 0; i < attackers; i++) { // ClientName(teammates[numteammates - i - 1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[numteammates - i - 1]); BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_GETFLAG); } // break; } } } } /* ================== BotCTFOrders ================== */ void BotCTFOrders_EnemyFlagNotAtBase(bot_state_t *bs) { int numteammates, defenders, attackers, i, other; int teammates[MAX_CLIENTS]; char name[MAX_NETNAME], carriername[MAX_NETNAME]; numteammates = BotSortTeamMatesByBaseTravelTime(bs, teammates, sizeof(teammates)); BotSortTeamMatesByTaskPreference(bs, teammates, numteammates); //different orders based on the number of team mates switch(numteammates) { case 1: break; case 2: { //tell the one not carrying the flag to defend the base if (teammates[0] == bs->flagcarrier) other = teammates[1]; else other = teammates[0]; ClientName(other, name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, other); BotSayVoiceTeamOrder(bs, other, VOICECHAT_DEFEND); break; } case 3: { //tell the one closest to the base not carrying the flag to defend the base if (teammates[0] != bs->flagcarrier) other = teammates[0]; else other = teammates[1]; ClientName(other, name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, other); BotSayVoiceTeamOrder(bs, other, VOICECHAT_DEFEND); //tell the other also to defend the base if (teammates[2] != bs->flagcarrier) other = teammates[2]; else other = teammates[1]; ClientName(other, name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, other); BotSayVoiceTeamOrder(bs, other, VOICECHAT_DEFEND); break; } default: { //60% will defend the base defenders = (int) (float) numteammates * 0.6 + 0.5; if (defenders > 6) defenders = 6; //30% accompanies the flag carrier attackers = (int) (float) numteammates * 0.3 + 0.5; if (attackers > 3) attackers = 3; for (i = 0; i < defenders; i++) { // if (teammates[i] == bs->flagcarrier) { continue; } ClientName(teammates[i], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[i]); BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); } // if we have a flag carrier if ( bs->flagcarrier != -1 ) { ClientName(bs->flagcarrier, carriername, sizeof(carriername)); for (i = 0; i < attackers; i++) { // if (teammates[numteammates - i - 1] == bs->flagcarrier) { continue; } // ClientName(teammates[numteammates - i - 1], name, sizeof(name)); if (bs->flagcarrier == bs->client) { BotAI_BotInitialChat(bs, "cmd_accompanyme", name, NULL); BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_FOLLOWME); } else { BotAI_BotInitialChat(bs, "cmd_accompany", name, carriername, NULL); BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_FOLLOWFLAGCARRIER); } BotSayTeamOrder(bs, teammates[numteammates - i - 1]); } } else { for (i = 0; i < attackers; i++) { // if (teammates[numteammates - i - 1] == bs->flagcarrier) { continue; } // ClientName(teammates[numteammates - i - 1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_GETFLAG); BotSayTeamOrder(bs, teammates[numteammates - i - 1]); } } // break; } } } /* ================== BotCTFOrders ================== */ void BotCTFOrders_BothFlagsAtBase(bot_state_t *bs) { int numteammates, defenders, attackers, i; int teammates[MAX_CLIENTS]; char name[MAX_NETNAME]; //sort team mates by travel time to base numteammates = BotSortTeamMatesByBaseTravelTime(bs, teammates, sizeof(teammates)); //sort team mates by CTF preference BotSortTeamMatesByTaskPreference(bs, teammates, numteammates); //passive strategy if (!(bs->ctfstrategy & CTFS_AGRESSIVE)) { //different orders based on the number of team mates switch(numteammates) { case 1: break; case 2: { //the one closest to the base will defend the base ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); //the other will get the flag ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_GETFLAG); break; } case 3: { //the one closest to the base will defend the base ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); //the second one closest to the base will defend the base ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_DEFEND); //the other will get the flag ClientName(teammates[2], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[2]); BotSayVoiceTeamOrder(bs, teammates[2], VOICECHAT_GETFLAG); break; } default: { defenders = (int) (float) numteammates * 0.5 + 0.5; if (defenders > 5) defenders = 5; attackers = (int) (float) numteammates * 0.4 + 0.5; if (attackers > 4) attackers = 4; for (i = 0; i < defenders; i++) { // ClientName(teammates[i], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[i]); BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); } for (i = 0; i < attackers; i++) { // ClientName(teammates[numteammates - i - 1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[numteammates - i - 1]); BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_GETFLAG); } // break; } } } else { //different orders based on the number of team mates switch(numteammates) { case 1: break; case 2: { //the one closest to the base will defend the base ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); //the other will get the flag ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_GETFLAG); break; } case 3: { //the one closest to the base will defend the base ClientName(teammates[0], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[0]); BotSayVoiceTeamOrder(bs, teammates[0], VOICECHAT_DEFEND); //the others should go for the enemy flag ClientName(teammates[1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[1]); BotSayVoiceTeamOrder(bs, teammates[1], VOICECHAT_GETFLAG); // ClientName(teammates[2], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[2]); BotSayVoiceTeamOrder(bs, teammates[2], VOICECHAT_GETFLAG); break; } default: { defenders = (int) (float) numteammates * 0.4 + 0.5; if (defenders > 4) defenders = 4; attackers = (int) (float) numteammates * 0.5 + 0.5; if (attackers > 5) attackers = 5; for (i = 0; i < defenders; i++) { // ClientName(teammates[i], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_defendbase", name, NULL); BotSayTeamOrder(bs, teammates[i]); BotSayVoiceTeamOrder(bs, teammates[i], VOICECHAT_DEFEND); } for (i = 0; i < attackers; i++) { // ClientName(teammates[numteammates - i - 1], name, sizeof(name)); BotAI_BotInitialChat(bs, "cmd_getflag", name, NULL); BotSayTeamOrder(bs, teammates[numteammates - i - 1]); BotSayVoiceTeamOrder(bs, teammates[numteammates - i - 1], VOICECHAT_GETFLAG); } // break; } } } } /* ================== BotCTFOrders ================== */ void BotCTFOrders(bot_state_t *bs) { int flagstatus; // if (BotTeam(bs) == TEAM_RED) flagstatus = bs->redflagstatus * 2 + bs->blueflagstatus; else flagstatus = bs->blueflagstatus * 2 + bs->redflagstatus; // switch(flagstatus) { case 0: BotCTFOrders_BothFlagsAtBase(bs); break; case 1: BotCTFOrders_EnemyFlagNotAtBase(bs); break; case 2: BotCTFOrders_FlagNotAtBase(bs); break; case 3: BotCTFOrders_BothFlagsNotAtBase(bs); break; } } /* ================== BotCreateGroup ================== */ void BotCreateGroup(bot_state_t *bs, int *teammates, int groupsize) { char name[MAX_NETNAME], leadername[MAX_NETNAME]; int i; // the others in the group will follow the teammates[0] ClientName(teammates[0], leadername, sizeof(leadername)); for (i = 1; i < groupsize; i++) { ClientName(teammates[i], name, sizeof(name)); if (teammates[0] == bs->client) { BotAI_BotInitialChat(bs, "cmd_accompanyme", name, NULL); } else { BotAI_BotInitialChat(bs, "cmd_accompany", name, leadername, NULL); } BotSayTeamOrderAlways(bs, teammates[i]); } } /* ================== BotTeamOrders FIXME: defend key areas? ================== */ void BotTeamOrders(bot_state_t *bs) { int teammates[MAX_CLIENTS]; int numteammates, i; char buf[MAX_INFO_STRING]; static int maxclients; if (!maxclients) maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); numteammates = 0; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; //skip spectators if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue; // if (BotSameTeam(bs, i)) { teammates[numteammates] = i; numteammates++; } } // switch(numteammates) { case 1: break; case 2: { //nothing special break; } case 3: { //have one follow another and one free roaming BotCreateGroup(bs, teammates, 2); break; } case 4: { BotCreateGroup(bs, teammates, 2); //a group of 2 BotCreateGroup(bs, &teammates[2], 2); //a group of 2 break; } case 5: { BotCreateGroup(bs, teammates, 2); //a group of 2 BotCreateGroup(bs, &teammates[2], 3); //a group of 3 break; } default: { if (numteammates <= 10) { for (i = 0; i < numteammates / 2; i++) { BotCreateGroup(bs, &teammates[i*2], 2); //groups of 2 } } break; } } } /* ================== FindHumanTeamLeader ================== */ int FindHumanTeamLeader(bot_state_t *bs) { int i; for (i = 0; i < MAX_CLIENTS; i++) { if ( g_entities[i].inuse ) { // if this player is not a bot if ( !(g_entities[i].r.svFlags & SVF_BOT) ) { // if this player is ok with being the leader if (!notleader[i]) { // if this player is on the same team if ( BotSameTeam(bs, i) ) { ClientName(i, bs->teamleader, sizeof(bs->teamleader)); // if not yet ordered to do anything if ( !BotSetLastOrderedTask(bs) ) { // go on defense by default BotVoiceChat_Defend(bs, i, SAY_TELL); } return qtrue; } } } } } return qfalse; } /* ================== BotTeamAI ================== */ void BotTeamAI(bot_state_t *bs) { int numteammates; char netname[MAX_NETNAME]; // if ( gametype < GT_TEAM ) return; // make sure we've got a valid team leader if (!BotValidTeamLeader(bs)) { // if (!FindHumanTeamLeader(bs)) { // if (!bs->askteamleader_time && !bs->becometeamleader_time) { if (bs->entergame_time + 10 > FloatTime()) { bs->askteamleader_time = FloatTime() + 5 + random() * 10; } else { bs->becometeamleader_time = FloatTime() + 5 + random() * 10; } } if (bs->askteamleader_time && bs->askteamleader_time < FloatTime()) { // if asked for a team leader and no response BotAI_BotInitialChat(bs, "whoisteamleader", NULL); trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); bs->askteamleader_time = 0; bs->becometeamleader_time = FloatTime() + 8 + random() * 10; } if (bs->becometeamleader_time && bs->becometeamleader_time < FloatTime()) { BotAI_BotInitialChat(bs, "iamteamleader", NULL); trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); BotSayVoiceTeamOrder(bs, -1, VOICECHAT_STARTLEADER); ClientName(bs->client, netname, sizeof(netname)); strncpy(bs->teamleader, netname, sizeof(bs->teamleader)); bs->teamleader[sizeof(bs->teamleader)] = '\0'; bs->becometeamleader_time = 0; } return; } } bs->askteamleader_time = 0; bs->becometeamleader_time = 0; //return if this bot is NOT the team leader ClientName(bs->client, netname, sizeof(netname)); if (Q_stricmp(netname, bs->teamleader) != 0) return; // numteammates = BotNumTeamMates(bs); //give orders switch(gametype) { case GT_TEAM: { if (bs->numteammates != numteammates || bs->forceorders) { bs->teamgiveorders_time = FloatTime(); bs->numteammates = numteammates; bs->forceorders = qfalse; } //if it's time to give orders if (bs->teamgiveorders_time && bs->teamgiveorders_time < FloatTime() - 5) { BotTeamOrders(bs); //give orders again after 120 seconds bs->teamgiveorders_time = FloatTime() + 120; } break; } case GT_CTF: { //if the number of team mates changed or the flag status changed //or someone wants to know what to do if (bs->numteammates != numteammates || bs->flagstatuschanged || bs->forceorders) { bs->teamgiveorders_time = FloatTime(); bs->numteammates = numteammates; bs->flagstatuschanged = qfalse; bs->forceorders = qfalse; } //if there were no flag captures the last 3 minutes if (bs->lastflagcapture_time < FloatTime() - 240) { bs->lastflagcapture_time = FloatTime(); //randomly change the CTF strategy if (random() < 0.4) { bs->ctfstrategy ^= CTFS_AGRESSIVE; bs->teamgiveorders_time = FloatTime(); } } //if it's time to give orders if (bs->teamgiveorders_time && bs->teamgiveorders_time < FloatTime() - 3) { BotCTFOrders(bs); // bs->teamgiveorders_time = 0; } break; } } } ================================================ FILE: src/game/ai_team.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: ai_team.h * * desc: Quake3 bot AI * * $Archive: /source/code/botai/ai_chat.c $ * *****************************************************************************/ void BotTeamAI(bot_state_t *bs); int BotGetTeamMateTaskPreference(bot_state_t *bs, int teammate); void BotSetTeamMateTaskPreference(bot_state_t *bs, int teammate, int preference); void BotVoiceChat(bot_state_t *bs, int toclient, char *voicechat); void BotVoiceChatOnly(bot_state_t *bs, int toclient, char *voicechat); ================================================ FILE: src/game/ai_vcmd.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: ai_vcmd.c * * desc: Quake3 bot AI * * $Archive: /MissionPack/code/game/ai_vcmd.c $ * *****************************************************************************/ #include "g_local.h" #include "botlib.h" #include "be_aas.h" #include "be_ea.h" #include "be_ai_char.h" #include "be_ai_chat.h" #include "be_ai_gen.h" #include "be_ai_goal.h" #include "be_ai_move.h" #include "be_ai_weap.h" // #include "ai_main.h" #include "ai_dmq3.h" #include "ai_chat.h" #include "ai_cmd.h" #include "ai_dmnet.h" #include "ai_team.h" #include "ai_vcmd.h" // #include "chars.h" //characteristics #include "inv.h" //indexes into the inventory #include "syn.h" //synonyms #include "match.h" //string matching types and vars // for the voice chats #include "menudef.h" typedef struct voiceCommand_s { char *cmd; void (*func)(bot_state_t *bs, int client, int mode); } voiceCommand_t; /* ================== BotVoiceChat_GetFlag ================== */ void BotVoiceChat_GetFlag(bot_state_t *bs, int client, int mode) { // if (gametype == GT_CTF) { if (!ctf_redflag.areanum || !ctf_blueflag.areanum) return; } else { return; } // bs->decisionmaker = client; bs->ordered = qtrue; bs->order_time = FloatTime(); //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //set the ltg type bs->ltgtype = LTG_GETFLAG; //set the team goal time bs->teamgoal_time = FloatTime() + CTF_GETFLAG_TIME; // get an alternate route in ctf if (gametype == GT_CTF) { //get an alternative route goal towards the enemy base BotGetAlternateRouteGoal(bs, BotOppositeTeam(bs)); } // BotSetTeamStatus(bs); // remember last ordered task BotRememberLastOrderedTask(bs); #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotVoiceChat_Offense ================== */ void BotVoiceChat_Offense(bot_state_t *bs, int client, int mode) { if ( gametype == GT_CTF ) { BotVoiceChat_GetFlag(bs, client, mode); return; } { // bs->decisionmaker = client; bs->ordered = qtrue; bs->order_time = FloatTime(); //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //set the ltg type bs->ltgtype = LTG_ATTACKENEMYBASE; //set the team goal time bs->teamgoal_time = FloatTime() + TEAM_ATTACKENEMYBASE_TIME; bs->attackaway_time = 0; // BotSetTeamStatus(bs); // remember last ordered task BotRememberLastOrderedTask(bs); } #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotVoiceChat_Defend ================== */ void BotVoiceChat_Defend(bot_state_t *bs, int client, int mode) { if (gametype == GT_CTF ) { // switch(BotTeam(bs)) { case TEAM_RED: memcpy(&bs->teamgoal, &ctf_redflag, sizeof(bot_goal_t)); break; case TEAM_BLUE: memcpy(&bs->teamgoal, &ctf_blueflag, sizeof(bot_goal_t)); break; default: return; } } else { return; } // bs->decisionmaker = client; bs->ordered = qtrue; bs->order_time = FloatTime(); //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //set the ltg type bs->ltgtype = LTG_DEFENDKEYAREA; //get the team goal time bs->teamgoal_time = FloatTime() + TEAM_DEFENDKEYAREA_TIME; //away from defending bs->defendaway_time = 0; // BotSetTeamStatus(bs); // remember last ordered task BotRememberLastOrderedTask(bs); #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotVoiceChat_DefendFlag ================== */ void BotVoiceChat_DefendFlag(bot_state_t *bs, int client, int mode) { BotVoiceChat_Defend(bs, client, mode); } /* ================== BotVoiceChat_Patrol ================== */ void BotVoiceChat_Patrol(bot_state_t *bs, int client, int mode) { // bs->decisionmaker = client; // bs->ltgtype = 0; bs->lead_time = 0; bs->lastgoal_ltgtype = 0; // BotAI_BotInitialChat(bs, "dismissed", NULL); trap_BotEnterChat(bs->cs, client, CHAT_TELL); BotVoiceChatOnly(bs, -1, VOICECHAT_ONPATROL); // BotSetTeamStatus(bs); #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotVoiceChat_Camp ================== */ void BotVoiceChat_Camp(bot_state_t *bs, int client, int mode) { int areanum; aas_entityinfo_t entinfo; char netname[MAX_NETNAME]; // bs->teamgoal.entitynum = -1; BotEntityInfo(client, &entinfo); //if info is valid (in PVS) if (entinfo.valid) { areanum = BotPointAreaNum(entinfo.origin); if (areanum) { // && trap_AAS_AreaReachability(areanum)) { //NOTE: just assume the bot knows where the person is //if (BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, client)) { bs->teamgoal.entitynum = client; bs->teamgoal.areanum = areanum; VectorCopy(entinfo.origin, bs->teamgoal.origin); VectorSet(bs->teamgoal.mins, -8, -8, -8); VectorSet(bs->teamgoal.maxs, 8, 8, 8); //} } } //if the other is not visible if (bs->teamgoal.entitynum < 0) { BotAI_BotInitialChat(bs, "whereareyou", EasyClientName(client, netname, sizeof(netname)), NULL); trap_BotEnterChat(bs->cs, client, CHAT_TELL); return; } // bs->decisionmaker = client; bs->ordered = qtrue; bs->order_time = FloatTime(); //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //set the ltg type bs->ltgtype = LTG_CAMPORDER; //get the team goal time bs->teamgoal_time = FloatTime() + TEAM_CAMP_TIME; //the teammate that requested the camping bs->teammate = client; //not arrived yet bs->arrive_time = 0; // BotSetTeamStatus(bs); // remember last ordered task BotRememberLastOrderedTask(bs); #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotVoiceChat_FollowMe ================== */ void BotVoiceChat_FollowMe(bot_state_t *bs, int client, int mode) { int areanum; aas_entityinfo_t entinfo; char netname[MAX_NETNAME]; bs->teamgoal.entitynum = -1; BotEntityInfo(client, &entinfo); //if info is valid (in PVS) if (entinfo.valid) { areanum = BotPointAreaNum(entinfo.origin); if (areanum) { // && trap_AAS_AreaReachability(areanum)) { bs->teamgoal.entitynum = client; bs->teamgoal.areanum = areanum; VectorCopy(entinfo.origin, bs->teamgoal.origin); VectorSet(bs->teamgoal.mins, -8, -8, -8); VectorSet(bs->teamgoal.maxs, 8, 8, 8); } } //if the other is not visible if (bs->teamgoal.entitynum < 0) { BotAI_BotInitialChat(bs, "whereareyou", EasyClientName(client, netname, sizeof(netname)), NULL); trap_BotEnterChat(bs->cs, client, CHAT_TELL); return; } // bs->decisionmaker = client; bs->ordered = qtrue; bs->order_time = FloatTime(); //the team mate bs->teammate = client; //last time the team mate was assumed visible bs->teammatevisible_time = FloatTime(); //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //get the team goal time bs->teamgoal_time = FloatTime() + TEAM_ACCOMPANY_TIME; //set the ltg type bs->ltgtype = LTG_TEAMACCOMPANY; bs->formation_dist = 3.5 * 32; //3.5 meter bs->arrive_time = 0; // BotSetTeamStatus(bs); // remember last ordered task BotRememberLastOrderedTask(bs); #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotVoiceChat_FollowFlagCarrier ================== */ void BotVoiceChat_FollowFlagCarrier(bot_state_t *bs, int client, int mode) { int carrier; carrier = BotTeamFlagCarrier(bs); if (carrier >= 0) BotVoiceChat_FollowMe(bs, carrier, mode); #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotVoiceChat_ReturnFlag ================== */ void BotVoiceChat_ReturnFlag(bot_state_t *bs, int client, int mode) { //if not in CTF mode if ( gametype != GT_CTF ) { return; } // bs->decisionmaker = client; bs->ordered = qtrue; bs->order_time = FloatTime(); //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //set the ltg type bs->ltgtype = LTG_RETURNFLAG; //set the team goal time bs->teamgoal_time = FloatTime() + CTF_RETURNFLAG_TIME; bs->rushbaseaway_time = 0; BotSetTeamStatus(bs); #ifdef DEBUG BotPrintTeamGoal(bs); #endif //DEBUG } /* ================== BotVoiceChat_StartLeader ================== */ void BotVoiceChat_StartLeader(bot_state_t *bs, int client, int mode) { ClientName(client, bs->teamleader, sizeof(bs->teamleader)); } /* ================== BotVoiceChat_StopLeader ================== */ void BotVoiceChat_StopLeader(bot_state_t *bs, int client, int mode) { char netname[MAX_MESSAGE_SIZE]; if (!Q_stricmp(bs->teamleader, ClientName(client, netname, sizeof(netname)))) { bs->teamleader[0] = '\0'; notleader[client] = qtrue; } } /* ================== BotVoiceChat_WhoIsLeader ================== */ void BotVoiceChat_WhoIsLeader(bot_state_t *bs, int client, int mode) { char netname[MAX_MESSAGE_SIZE]; if (!TeamPlayIsOn()) return; ClientName(bs->client, netname, sizeof(netname)); //if this bot IS the team leader if (!Q_stricmp(netname, bs->teamleader)) { BotAI_BotInitialChat(bs, "iamteamleader", NULL); trap_BotEnterChat(bs->cs, 0, CHAT_TEAM); BotVoiceChatOnly(bs, -1, VOICECHAT_STARTLEADER); } } /* ================== BotVoiceChat_WantOnDefense ================== */ void BotVoiceChat_WantOnDefense(bot_state_t *bs, int client, int mode) { char netname[MAX_NETNAME]; int preference; preference = BotGetTeamMateTaskPreference(bs, client); preference &= ~TEAMTP_ATTACKER; preference |= TEAMTP_DEFENDER; BotSetTeamMateTaskPreference(bs, client, preference); // EasyClientName(client, netname, sizeof(netname)); BotAI_BotInitialChat(bs, "keepinmind", netname, NULL); trap_BotEnterChat(bs->cs, client, CHAT_TELL); BotVoiceChatOnly(bs, client, VOICECHAT_YES); trap_EA_Action(bs->client, ACTION_AFFIRMATIVE); } /* ================== BotVoiceChat_WantOnOffense ================== */ void BotVoiceChat_WantOnOffense(bot_state_t *bs, int client, int mode) { char netname[MAX_NETNAME]; int preference; preference = BotGetTeamMateTaskPreference(bs, client); preference &= ~TEAMTP_DEFENDER; preference |= TEAMTP_ATTACKER; BotSetTeamMateTaskPreference(bs, client, preference); // EasyClientName(client, netname, sizeof(netname)); BotAI_BotInitialChat(bs, "keepinmind", netname, NULL); trap_BotEnterChat(bs->cs, client, CHAT_TELL); BotVoiceChatOnly(bs, client, VOICECHAT_YES); trap_EA_Action(bs->client, ACTION_AFFIRMATIVE); } void BotVoiceChat_Dummy(bot_state_t *bs, int client, int mode) { } voiceCommand_t voiceCommands[] = { {VOICECHAT_GETFLAG, BotVoiceChat_GetFlag}, {VOICECHAT_OFFENSE, BotVoiceChat_Offense }, {VOICECHAT_DEFEND, BotVoiceChat_Defend }, {VOICECHAT_DEFENDFLAG, BotVoiceChat_DefendFlag }, {VOICECHAT_PATROL, BotVoiceChat_Patrol }, {VOICECHAT_CAMP, BotVoiceChat_Camp }, {VOICECHAT_FOLLOWME, BotVoiceChat_FollowMe }, {VOICECHAT_FOLLOWFLAGCARRIER, BotVoiceChat_FollowFlagCarrier }, {VOICECHAT_RETURNFLAG, BotVoiceChat_ReturnFlag }, {VOICECHAT_STARTLEADER, BotVoiceChat_StartLeader }, {VOICECHAT_STOPLEADER, BotVoiceChat_StopLeader }, {VOICECHAT_WHOISLEADER, BotVoiceChat_WhoIsLeader }, {VOICECHAT_WANTONDEFENSE, BotVoiceChat_WantOnDefense }, {VOICECHAT_WANTONOFFENSE, BotVoiceChat_WantOnOffense }, {NULL, BotVoiceChat_Dummy} }; int BotVoiceChatCommand(bot_state_t *bs, int mode, char *voiceChat) { int i, voiceOnly, clientNum, color; char *ptr, buf[MAX_MESSAGE_SIZE], *cmd; if (!TeamPlayIsOn()) { return qfalse; } if ( mode == SAY_ALL ) { return qfalse; // don't do anything with voice chats to everyone } Q_strncpyz(buf, voiceChat, sizeof(buf)); cmd = buf; for (ptr = cmd; *cmd && *cmd > ' '; cmd++); while (*cmd && *cmd <= ' ') *cmd++ = '\0'; voiceOnly = atoi(ptr); for (ptr = cmd; *cmd && *cmd > ' '; cmd++); while (*cmd && *cmd <= ' ') *cmd++ = '\0'; clientNum = atoi(ptr); for (ptr = cmd; *cmd && *cmd > ' '; cmd++); while (*cmd && *cmd <= ' ') *cmd++ = '\0'; color = atoi(ptr); if (!BotSameTeam(bs, clientNum)) { return qfalse; } for (i = 0; voiceCommands[i].cmd; i++) { if (!Q_stricmp(cmd, voiceCommands[i].cmd)) { voiceCommands[i].func(bs, clientNum, mode); return qtrue; } } return qfalse; } ================================================ FILE: src/game/ai_vcmd.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: ai_vcmd.h * * desc: Quake3 bot AI * * $Archive: /source/code/botai/ai_vcmd.c $ * *****************************************************************************/ int BotVoiceChatCommand(bot_state_t *bs, int mode, char *voicechat); void BotVoiceChat_Defend(bot_state_t *bs, int client, int mode); ================================================ FILE: src/game/be_aas.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: be_aas.h * * desc: Area Awareness System, stuff exported to the AI * * $Archive: /source/code/botlib/be_aas.h $ * *****************************************************************************/ #ifndef MAX_STRINGFIELD #define MAX_STRINGFIELD 80 #endif //travel flags #define TFL_INVALID 0x00000001 //traveling temporary not possible #define TFL_WALK 0x00000002 //walking #define TFL_CROUCH 0x00000004 //crouching #define TFL_BARRIERJUMP 0x00000008 //jumping onto a barrier #define TFL_JUMP 0x00000010 //jumping #define TFL_LADDER 0x00000020 //climbing a ladder #define TFL_WALKOFFLEDGE 0x00000080 //walking of a ledge #define TFL_SWIM 0x00000100 //swimming #define TFL_WATERJUMP 0x00000200 //jumping out of the water #define TFL_TELEPORT 0x00000400 //teleporting #define TFL_ELEVATOR 0x00000800 //elevator #define TFL_ROCKETJUMP 0x00001000 //rocket jumping #define TFL_BFGJUMP 0x00002000 //bfg jumping #define TFL_GRAPPLEHOOK 0x00004000 //grappling hook #define TFL_DOUBLEJUMP 0x00008000 //double jump #define TFL_RAMPJUMP 0x00010000 //ramp jump #define TFL_STRAFEJUMP 0x00020000 //strafe jump #define TFL_JUMPPAD 0x00040000 //jump pad #define TFL_AIR 0x00080000 //travel through air #define TFL_WATER 0x00100000 //travel through water #define TFL_SLIME 0x00200000 //travel through slime #define TFL_LAVA 0x00400000 //travel through lava #define TFL_DONOTENTER 0x00800000 //travel through donotenter area #define TFL_FUNCBOB 0x01000000 //func bobbing #define TFL_FLIGHT 0x02000000 //flight #define TFL_BRIDGE 0x04000000 //move over a bridge // #define TFL_NOTTEAM1 0x08000000 //not team 1 #define TFL_NOTTEAM2 0x10000000 //not team 2 //default travel flags #define TFL_DEFAULT TFL_WALK|TFL_CROUCH|TFL_BARRIERJUMP|\ TFL_JUMP|TFL_LADDER|\ TFL_WALKOFFLEDGE|TFL_SWIM|TFL_WATERJUMP|\ TFL_TELEPORT|TFL_ELEVATOR|\ TFL_AIR|TFL_WATER|TFL_JUMPPAD|TFL_FUNCBOB typedef enum { SOLID_NOT, // no interaction with other objects SOLID_TRIGGER, // only touch when inside, after moving SOLID_BBOX, // touch on edge SOLID_BSP // bsp clip, touch on edge } solid_t; //a trace is returned when a box is swept through the AAS world typedef struct aas_trace_s { qboolean startsolid; // if true, the initial point was in a solid area float fraction; // time completed, 1.0 = didn't hit anything vec3_t endpos; // final position int ent; // entity blocking the trace int lastarea; // last area the trace was in (zero if none) int area; // area blocking the trace (zero if none) int planenum; // number of the plane that was hit } aas_trace_t; /* Defined in botlib.h //bsp_trace_t hit surface typedef struct bsp_surface_s { char name[16]; int flags; int value; } bsp_surface_t; //a trace is returned when a box is swept through the BSP world typedef struct bsp_trace_s { qboolean allsolid; // if true, plane is not valid qboolean startsolid; // if true, the initial point was in a solid area float fraction; // time completed, 1.0 = didn't hit anything vec3_t endpos; // final position cplane_t plane; // surface normal at impact float exp_dist; // expanded plane distance int sidenum; // number of the brush side hit bsp_surface_t surface; // hit surface int contents; // contents on other side of surface hit int ent; // number of entity hit } bsp_trace_t; // */ //entity info typedef struct aas_entityinfo_s { int valid; // true if updated this frame int type; // entity type int flags; // entity flags float ltime; // local time float update_time; // time between last and current update int number; // number of the entity vec3_t origin; // origin of the entity vec3_t angles; // angles of the model vec3_t old_origin; // for lerping vec3_t lastvisorigin; // last visible origin vec3_t mins; // bounding box minimums vec3_t maxs; // bounding box maximums int groundent; // ground entity int solid; // solid type int modelindex; // model used int modelindex2; // weapons, CTF flags, etc int frame; // model frame number int event; // impulse events -- muzzle flashes, footsteps, etc int eventParm; // even parameter int powerups; // bit flags int weapon; // determines weapon and flash model, etc int legsAnim; // mask off ANIM_TOGGLEBIT int torsoAnim; // mask off ANIM_TOGGLEBIT } aas_entityinfo_t; // area info typedef struct aas_areainfo_s { int contents; int flags; int presencetype; int cluster; vec3_t mins; vec3_t maxs; vec3_t center; } aas_areainfo_t; // client movement prediction stop events, stop as soon as: #define SE_NONE 0 #define SE_HITGROUND 1 // the ground is hit #define SE_LEAVEGROUND 2 // there's no ground #define SE_ENTERWATER 4 // water is entered #define SE_ENTERSLIME 8 // slime is entered #define SE_ENTERLAVA 16 // lava is entered #define SE_HITGROUNDDAMAGE 32 // the ground is hit with damage #define SE_GAP 64 // there's a gap #define SE_TOUCHJUMPPAD 128 // touching a jump pad area #define SE_TOUCHTELEPORTER 256 // touching teleporter #define SE_ENTERAREA 512 // the given stoparea is entered #define SE_HITGROUNDAREA 1024 // a ground face in the area is hit #define SE_HITBOUNDINGBOX 2048 // hit the specified bounding box #define SE_TOUCHCLUSTERPORTAL 4096 // touching a cluster portal typedef struct aas_clientmove_s { vec3_t endpos; //position at the end of movement prediction int endarea; //area at end of movement prediction vec3_t velocity; //velocity at the end of movement prediction aas_trace_t trace; //last trace int presencetype; //presence type at end of movement prediction int stopevent; //event that made the prediction stop int endcontents; //contents at the end of movement prediction float time; //time predicted ahead int frames; //number of frames predicted ahead } aas_clientmove_t; // alternate route goals #define ALTROUTEGOAL_ALL 1 #define ALTROUTEGOAL_CLUSTERPORTALS 2 #define ALTROUTEGOAL_VIEWPORTALS 4 typedef struct aas_altroutegoal_s { vec3_t origin; int areanum; unsigned short starttraveltime; unsigned short goaltraveltime; unsigned short extratraveltime; } aas_altroutegoal_t; // route prediction stop events #define RSE_NONE 0 #define RSE_NOROUTE 1 //no route to goal #define RSE_USETRAVELTYPE 2 //stop as soon as on of the given travel types is used #define RSE_ENTERCONTENTS 4 //stop when entering the given contents #define RSE_ENTERAREA 8 //stop when entering the given area typedef struct aas_predictroute_s { vec3_t endpos; //position at the end of movement prediction int endarea; //area at end of movement prediction int stopevent; //event that made the prediction stop int endcontents; //contents at the end of movement prediction int endtravelflags; //end travel flags int numareas; //number of areas predicted ahead int time; //time predicted ahead (in hundreth of a sec) } aas_predictroute_t; ================================================ FILE: src/game/be_ai_char.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: be_ai_char.h * * desc: bot characters * * $Archive: /source/code/botlib/be_ai_char.h $ * *****************************************************************************/ //loads a bot character from a file int BotLoadCharacter(char *charfile, float skill); //frees a bot character void BotFreeCharacter(int character); //returns a float characteristic float Characteristic_Float(int character, int index); //returns a bounded float characteristic float Characteristic_BFloat(int character, int index, float min, float max); //returns an integer characteristic int Characteristic_Integer(int character, int index); //returns a bounded integer characteristic int Characteristic_BInteger(int character, int index, int min, int max); //returns a string characteristic void Characteristic_String(int character, int index, char *buf, int size); //free cached bot characters void BotShutdownCharacters(void); ================================================ FILE: src/game/be_ai_chat.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: be_ai_chat.h * * desc: char AI * * $Archive: /source/code/botlib/be_ai_chat.h $ * *****************************************************************************/ #define MAX_MESSAGE_SIZE 256 #define MAX_CHATTYPE_NAME 32 #define MAX_MATCHVARIABLES 8 #define CHAT_GENDERLESS 0 #define CHAT_GENDERFEMALE 1 #define CHAT_GENDERMALE 2 #define CHAT_ALL 0 #define CHAT_TEAM 1 #define CHAT_TELL 2 //a console message typedef struct bot_consolemessage_s { int handle; float time; //message time int type; //message type char message[MAX_MESSAGE_SIZE]; //message struct bot_consolemessage_s *prev, *next; //prev and next in list } bot_consolemessage_t; //match variable typedef struct bot_matchvariable_s { char offset; int length; } bot_matchvariable_t; //returned to AI when a match is found typedef struct bot_match_s { char string[MAX_MESSAGE_SIZE]; int type; int subtype; bot_matchvariable_t variables[MAX_MATCHVARIABLES]; } bot_match_t; //setup the chat AI int BotSetupChatAI(void); //shutdown the chat AI void BotShutdownChatAI(void); //returns the handle to a newly allocated chat state int BotAllocChatState(void); //frees the chatstate void BotFreeChatState(int handle); //adds a console message to the chat state void BotQueueConsoleMessage(int chatstate, int type, char *message); //removes the console message from the chat state void BotRemoveConsoleMessage(int chatstate, int handle); //returns the next console message from the state int BotNextConsoleMessage(int chatstate, bot_consolemessage_t *cm); //returns the number of console messages currently stored in the state int BotNumConsoleMessages(int chatstate); //selects a chat message of the given type void BotInitialChat(int chatstate, char *type, int mcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7); //returns the number of initial chat messages of the given type int BotNumInitialChats(int chatstate, char *type); //find and select a reply for the given message int BotReplyChat(int chatstate, char *message, int mcontext, int vcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7); //returns the length of the currently selected chat message int BotChatLength(int chatstate); //enters the selected chat message void BotEnterChat(int chatstate, int clientto, int sendto); //get the chat message ready to be output void BotGetChatMessage(int chatstate, char *buf, int size); //checks if the first string contains the second one, returns index into first string or -1 if not found int StringContains(char *str1, char *str2, int casesensitive); //finds a match for the given string using the match templates int BotFindMatch(char *str, bot_match_t *match, unsigned long int context); //returns a variable from a match void BotMatchVariable(bot_match_t *match, int variable, char *buf, int size); //unify all the white spaces in the string void UnifyWhiteSpaces(char *string); //replace all the context related synonyms in the string void BotReplaceSynonyms(char *string, unsigned long int context); //loads a chat file for the chat state int BotLoadChatFile(int chatstate, char *chatfile, char *chatname); //store the gender of the bot in the chat state void BotSetChatGender(int chatstate, int gender); //store the bot name in the chat state void BotSetChatName(int chatstate, char *name, int client); ================================================ FILE: src/game/be_ai_gen.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: be_ai_gen.h * * desc: genetic selection * * $Archive: /source/code/botlib/be_ai_gen.h $ * *****************************************************************************/ int GeneticParentsAndChildSelection(int numranks, float *ranks, int *parent1, int *parent2, int *child); ================================================ FILE: src/game/be_ai_goal.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: be_ai_goal.h * * desc: goal AI * * $Archive: /source/code/botlib/be_ai_goal.h $ * *****************************************************************************/ #define MAX_AVOIDGOALS 256 #define MAX_GOALSTACK 8 #define GFL_NONE 0 #define GFL_ITEM 1 #define GFL_ROAM 2 #define GFL_DROPPED 4 //a bot goal typedef struct bot_goal_s { vec3_t origin; //origin of the goal int areanum; //area number of the goal vec3_t mins, maxs; //mins and maxs of the goal int entitynum; //number of the goal entity int number; //goal number int flags; //goal flags int iteminfo; //item information } bot_goal_t; //reset the whole goal state, but keep the item weights void BotResetGoalState(int goalstate); //reset avoid goals void BotResetAvoidGoals(int goalstate); //remove the goal with the given number from the avoid goals void BotRemoveFromAvoidGoals(int goalstate, int number); //push a goal onto the goal stack void BotPushGoal(int goalstate, bot_goal_t *goal); //pop a goal from the goal stack void BotPopGoal(int goalstate); //empty the bot's goal stack void BotEmptyGoalStack(int goalstate); //dump the avoid goals void BotDumpAvoidGoals(int goalstate); //dump the goal stack void BotDumpGoalStack(int goalstate); //get the name name of the goal with the given number void BotGoalName(int number, char *name, int size); //get the top goal from the stack int BotGetTopGoal(int goalstate, bot_goal_t *goal); //get the second goal on the stack int BotGetSecondGoal(int goalstate, bot_goal_t *goal); //choose the best long term goal item for the bot int BotChooseLTGItem(int goalstate, vec3_t origin, int *inventory, int travelflags); //choose the best nearby goal item for the bot //the item may not be further away from the current bot position than maxtime //also the travel time from the nearby goal towards the long term goal may not //be larger than the travel time towards the long term goal from the current bot position int BotChooseNBGItem(int goalstate, vec3_t origin, int *inventory, int travelflags, bot_goal_t *ltg, float maxtime); //returns true if the bot touches the goal int BotTouchingGoal(vec3_t origin, bot_goal_t *goal); //returns true if the goal should be visible but isn't int BotItemGoalInVisButNotVisible(int viewer, vec3_t eye, vec3_t viewangles, bot_goal_t *goal); //search for a goal for the given classname, the index can be used //as a start point for the search when multiple goals are available with that same classname int BotGetLevelItemGoal(int index, char *classname, bot_goal_t *goal); //get the next camp spot in the map int BotGetNextCampSpotGoal(int num, bot_goal_t *goal); //get the map location with the given name int BotGetMapLocationGoal(char *name, bot_goal_t *goal); //returns the avoid goal time float BotAvoidGoalTime(int goalstate, int number); //set the avoid goal time void BotSetAvoidGoalTime(int goalstate, int number, float avoidtime); //initializes the items in the level void BotInitLevelItems(void); //regularly update dynamic entity items (dropped weapons, flags etc.) void BotUpdateEntityItems(void); //interbreed the goal fuzzy logic void BotInterbreedGoalFuzzyLogic(int parent1, int parent2, int child); //save the goal fuzzy logic to disk void BotSaveGoalFuzzyLogic(int goalstate, char *filename); //mutate the goal fuzzy logic void BotMutateGoalFuzzyLogic(int goalstate, float range); //loads item weights for the bot int BotLoadItemWeights(int goalstate, char *filename); //frees the item weights of the bot void BotFreeItemWeights(int goalstate); //returns the handle of a newly allocated goal state int BotAllocGoalState(int client); //free the given goal state void BotFreeGoalState(int handle); //setup the goal AI int BotSetupGoalAI(void); //shut down the goal AI void BotShutdownGoalAI(void); ================================================ FILE: src/game/be_ai_move.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: be_ai_move.h * * desc: movement AI * * $Archive: /source/code/botlib/be_ai_move.h $ * *****************************************************************************/ //movement types #define MOVE_WALK 1 #define MOVE_CROUCH 2 #define MOVE_JUMP 4 #define MOVE_GRAPPLE 8 #define MOVE_ROCKETJUMP 16 #define MOVE_BFGJUMP 32 //move flags #define MFL_BARRIERJUMP 1 //bot is performing a barrier jump #define MFL_ONGROUND 2 //bot is in the ground #define MFL_SWIMMING 4 //bot is swimming #define MFL_AGAINSTLADDER 8 //bot is against a ladder #define MFL_WATERJUMP 16 //bot is waterjumping #define MFL_TELEPORTED 32 //bot is being teleported #define MFL_GRAPPLEPULL 64 //bot is being pulled by the grapple #define MFL_ACTIVEGRAPPLE 128 //bot is using the grapple hook #define MFL_GRAPPLERESET 256 //bot has reset the grapple #define MFL_WALK 512 //bot should walk slowly // move result flags #define MOVERESULT_MOVEMENTVIEW 1 //bot uses view for movement #define MOVERESULT_SWIMVIEW 2 //bot uses view for swimming #define MOVERESULT_WAITING 4 //bot is waiting for something #define MOVERESULT_MOVEMENTVIEWSET 8 //bot has set the view in movement code #define MOVERESULT_MOVEMENTWEAPON 16 //bot uses weapon for movement #define MOVERESULT_ONTOPOFOBSTACLE 32 //bot is ontop of obstacle #define MOVERESULT_ONTOPOF_FUNCBOB 64 //bot is ontop of a func_bobbing #define MOVERESULT_ONTOPOF_ELEVATOR 128 //bot is ontop of an elevator (func_plat) #define MOVERESULT_BLOCKEDBYAVOIDSPOT 256 //bot is blocked by an avoid spot // #define MAX_AVOIDREACH 1 #define MAX_AVOIDSPOTS 32 // avoid spot types #define AVOID_CLEAR 0 //clear all avoid spots #define AVOID_ALWAYS 1 //avoid always #define AVOID_DONTBLOCK 2 //never totally block // restult types #define RESULTTYPE_ELEVATORUP 1 //elevator is up #define RESULTTYPE_WAITFORFUNCBOBBING 2 //waiting for func bobbing to arrive #define RESULTTYPE_BADGRAPPLEPATH 4 //grapple path is obstructed #define RESULTTYPE_INSOLIDAREA 8 //stuck in solid area, this is bad //structure used to initialize the movement state //the or_moveflags MFL_ONGROUND, MFL_TELEPORTED and MFL_WATERJUMP come from the playerstate typedef struct bot_initmove_s { vec3_t origin; //origin of the bot vec3_t velocity; //velocity of the bot vec3_t viewoffset; //view offset int entitynum; //entity number of the bot int client; //client number of the bot float thinktime; //time the bot thinks int presencetype; //presencetype of the bot vec3_t viewangles; //view angles of the bot int or_moveflags; //values ored to the movement flags } bot_initmove_t; //NOTE: the ideal_viewangles are only valid if MFL_MOVEMENTVIEW is set typedef struct bot_moveresult_s { int failure; //true if movement failed all together int type; //failure or blocked type int blocked; //true if blocked by an entity int blockentity; //entity blocking the bot int traveltype; //last executed travel type int flags; //result flags int weapon; //weapon used for movement vec3_t movedir; //movement direction vec3_t ideal_viewangles; //ideal viewangles for the movement } bot_moveresult_t; // bk001204: from code/botlib/be_ai_move.c // TTimo 04/12/2001 was moved here to avoid dup defines typedef struct bot_avoidspot_s { vec3_t origin; float radius; int type; } bot_avoidspot_t; //resets the whole move state void BotResetMoveState(int movestate); //moves the bot to the given goal void BotMoveToGoal(bot_moveresult_t *result, int movestate, bot_goal_t *goal, int travelflags); //moves the bot in the specified direction using the specified type of movement int BotMoveInDirection(int movestate, vec3_t dir, float speed, int type); //reset avoid reachability void BotResetAvoidReach(int movestate); //resets the last avoid reachability void BotResetLastAvoidReach(int movestate); //returns a reachability area if the origin is in one int BotReachabilityArea(vec3_t origin, int client); //view target based on movement int BotMovementViewTarget(int movestate, bot_goal_t *goal, int travelflags, float lookahead, vec3_t target); //predict the position of a player based on movement towards a goal int BotPredictVisiblePosition(vec3_t origin, int areanum, bot_goal_t *goal, int travelflags, vec3_t target); //returns the handle of a newly allocated movestate int BotAllocMoveState(void); //frees the movestate with the given handle void BotFreeMoveState(int handle); //initialize movement state before performing any movement void BotInitMoveState(int handle, bot_initmove_t *initmove); //add a spot to avoid (if type == AVOID_CLEAR all spots are removed) void BotAddAvoidSpot(int movestate, vec3_t origin, float radius, int type); //must be called every map change void BotSetBrushModelTypes(void); //setup movement AI int BotSetupMoveAI(void); //shutdown movement AI void BotShutdownMoveAI(void); ================================================ FILE: src/game/be_ai_weap.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: be_ai_weap.h * * desc: weapon AI * * $Archive: /source/code/botlib/be_ai_weap.h $ * *****************************************************************************/ //projectile flags #define PFL_WINDOWDAMAGE 1 //projectile damages through window #define PFL_RETURN 2 //set when projectile returns to owner //weapon flags #define WFL_FIRERELEASED 1 //set when projectile is fired with key-up event //damage types #define DAMAGETYPE_IMPACT 1 //damage on impact #define DAMAGETYPE_RADIAL 2 //radial damage #define DAMAGETYPE_VISIBLE 4 //damage to all entities visible to the projectile typedef struct projectileinfo_s { char name[MAX_STRINGFIELD]; char model[MAX_STRINGFIELD]; int flags; float gravity; int damage; float radius; int visdamage; int damagetype; int healthinc; float push; float detonation; float bounce; float bouncefric; float bouncestop; } projectileinfo_t; typedef struct weaponinfo_s { int valid; //true if the weapon info is valid int number; //number of the weapon char name[MAX_STRINGFIELD]; char model[MAX_STRINGFIELD]; int level; int weaponindex; int flags; char projectile[MAX_STRINGFIELD]; int numprojectiles; float hspread; float vspread; float speed; float acceleration; vec3_t recoil; vec3_t offset; vec3_t angleoffset; float extrazvelocity; int ammoamount; int ammoindex; float activate; float reload; float spinup; float spindown; projectileinfo_t proj; //pointer to the used projectile } weaponinfo_t; //setup the weapon AI int BotSetupWeaponAI(void); //shut down the weapon AI void BotShutdownWeaponAI(void); //returns the best weapon to fight with int BotChooseBestFightWeapon(int weaponstate, int *inventory); //returns the information of the current weapon void BotGetWeaponInfo(int weaponstate, int weapon, weaponinfo_t *weaponinfo); //loads the weapon weights int BotLoadWeaponWeights(int weaponstate, char *filename); //returns a handle to a newly allocated weapon state int BotAllocWeaponState(void); //frees the weapon state void BotFreeWeaponState(int weaponstate); //resets the whole weapon state void BotResetWeaponState(int weaponstate); ================================================ FILE: src/game/be_ea.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: be_ea.h * * desc: elementary actions * * $Archive: /source/code/botlib/be_ea.h $ * *****************************************************************************/ //ClientCommand elementary actions void EA_Say(int client, char *str); void EA_SayTeam(int client, char *str); void EA_Command(int client, char *command ); void EA_Action(int client, int action); void EA_Crouch(int client); void EA_Walk(int client); void EA_MoveUp(int client); void EA_MoveDown(int client); void EA_MoveForward(int client); void EA_MoveBack(int client); void EA_MoveLeft(int client); void EA_MoveRight(int client); void EA_Attack(int client); void EA_Respawn(int client); void EA_Talk(int client); void EA_Gesture(int client); void EA_Use(int client); //regular elementary actions void EA_SelectWeapon(int client, int weapon); void EA_Jump(int client); void EA_DelayedJump(int client); void EA_Move(int client, vec3_t dir, float speed); void EA_View(int client, vec3_t viewangles); //send regular input to the server void EA_EndRegular(int client, float thinktime); void EA_GetInput(int client, float thinktime, bot_input_t *input); void EA_ResetInput(int client); //setup and shutdown routines int EA_Setup(void); void EA_Shutdown(void); ================================================ FILE: src/game/bg_lib.c ================================================ // // // bg_lib,c -- standard C library replacement routines used by code // compiled for the virtual machine #include "q_shared.h" /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if defined(LIBC_SCCS) && !defined(lint) #if 0 static char sccsid[] = "@(#)qsort.c 8.1 (Berkeley) 6/4/93"; #endif static const char rcsid[] = #endif /* LIBC_SCCS and not lint */ // bk001127 - needed for DLL's #if !defined( Q3_VM ) typedef int cmp_t(const void *, const void *); #endif static char* med3(char *, char *, char *, cmp_t *); static void swapfunc(char *, char *, int, int); #ifndef min #define min(a, b) (a) < (b) ? a : b #endif /* * Qsort routine from Bentley & McIlroy's "Engineering a Sort Function". */ #define swapcode(TYPE, parmi, parmj, n) { \ long i = (n) / sizeof (TYPE); \ register TYPE *pi = (TYPE *) (parmi); \ register TYPE *pj = (TYPE *) (parmj); \ do { \ register TYPE t = *pi; \ *pi++ = *pj; \ *pj++ = t; \ } while (--i > 0); \ } #define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \ es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1; static void swapfunc(a, b, n, swaptype) char *a, *b; int n, swaptype; { if(swaptype <= 1) swapcode(long, a, b, n) else swapcode(char, a, b, n) } #define swap(a, b) \ if (swaptype == 0) { \ long t = *(long *)(a); \ *(long *)(a) = *(long *)(b); \ *(long *)(b) = t; \ } else \ swapfunc(a, b, es, swaptype) #define vecswap(a, b, n) if ((n) > 0) swapfunc(a, b, n, swaptype) static char * med3(a, b, c, cmp) char *a, *b, *c; cmp_t *cmp; { return cmp(a, b) < 0 ? (cmp(b, c) < 0 ? b : (cmp(a, c) < 0 ? c : a )) :(cmp(b, c) > 0 ? b : (cmp(a, c) < 0 ? a : c )); } void qsort(a, n, es, cmp) void *a; size_t n, es; cmp_t *cmp; { char *pa, *pb, *pc, *pd, *pl, *pm, *pn; int d, r, swaptype, swap_cnt; loop: SWAPINIT(a, es); swap_cnt = 0; if (n < 7) { for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es) for (pl = pm; pl > (char *)a && cmp(pl - es, pl) > 0; pl -= es) swap(pl, pl - es); return; } pm = (char *)a + (n / 2) * es; if (n > 7) { pl = a; pn = (char *)a + (n - 1) * es; if (n > 40) { d = (n / 8) * es; pl = med3(pl, pl + d, pl + 2 * d, cmp); pm = med3(pm - d, pm, pm + d, cmp); pn = med3(pn - 2 * d, pn - d, pn, cmp); } pm = med3(pl, pm, pn, cmp); } swap(a, pm); pa = pb = (char *)a + es; pc = pd = (char *)a + (n - 1) * es; for (;;) { while (pb <= pc && (r = cmp(pb, a)) <= 0) { if (r == 0) { swap_cnt = 1; swap(pa, pb); pa += es; } pb += es; } while (pb <= pc && (r = cmp(pc, a)) >= 0) { if (r == 0) { swap_cnt = 1; swap(pc, pd); pd -= es; } pc -= es; } if (pb > pc) break; swap(pb, pc); swap_cnt = 1; pb += es; pc -= es; } if (swap_cnt == 0) { /* Switch to insertion sort */ for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es) for (pl = pm; pl > (char *)a && cmp(pl - es, pl) > 0; pl -= es) swap(pl, pl - es); return; } pn = (char *)a + n * es; r = min(pa - (char *)a, pb - pa); vecswap(a, pb - r, r); r = min(pd - pc, pn - pd - es); vecswap(pb, pn - r, r); if ((r = pb - pa) > es) qsort(a, r / es, es, cmp); if ((r = pd - pc) > es) { /* Iterate rather than recurse to save stack space */ a = pn - r; n = r / es; goto loop; } /* qsort(pn - r, r / es, es, cmp);*/ } //================================================================================== // this file is excluded from release builds because of intrinsics // bk001211 - gcc errors on compiling strcpy: parse error before `__extension__' #if defined ( Q3_VM ) int strlen( const char *string ) { const char *s; s = string; while ( *s ) { s++; } return s - string; } char *strcat( char *strDestination, const char *strSource ) { char *s; s = strDestination; while ( *s ) { s++; } while ( *strSource ) { *s++ = *strSource++; } *s = 0; return strDestination; } char *strcpy( char *strDestination, const char *strSource ) { char *s; s = strDestination; while ( *strSource ) { *s++ = *strSource++; } *s = 0; return strDestination; } int strcmp( const char *string1, const char *string2 ) { while ( *string1 == *string2 && *string1 && *string2 ) { string1++; string2++; } return *string1 - *string2; } char *strchr( const char *string, int c ) { while ( *string ) { if ( *string == c ) { return ( char * )string; } string++; } return (char *)0; } char *strstr( const char *string, const char *strCharSet ) { while ( *string ) { int i; for ( i = 0 ; strCharSet[i] ; i++ ) { if ( string[i] != strCharSet[i] ) { break; } } if ( !strCharSet[i] ) { return (char *)string; } string++; } return (char *)0; } #endif // bk001211 // bk001120 - presumably needed for Mac //#if !defined(_MSC_VER) && !defined(__linux__) // bk001127 - undid undo #if defined ( Q3_VM ) int tolower( int c ) { if ( c >= 'A' && c <= 'Z' ) { c += 'a' - 'A'; } return c; } int toupper( int c ) { if ( c >= 'a' && c <= 'z' ) { c += 'A' - 'a'; } return c; } #endif //#ifndef _MSC_VER void *memmove( void *dest, const void *src, size_t count ) { int i; if ( dest > src ) { for ( i = count-1 ; i >= 0 ; i-- ) { ((char *)dest)[i] = ((char *)src)[i]; } } else { for ( i = 0 ; i < count ; i++ ) { ((char *)dest)[i] = ((char *)src)[i]; } } return dest; } #if 0 double floor( double x ) { return (int)(x + 0x40000000) - 0x40000000; } void *memset( void *dest, int c, size_t count ) { while ( count-- ) { ((char *)dest)[count] = c; } return dest; } void *memcpy( void *dest, const void *src, size_t count ) { while ( count-- ) { ((char *)dest)[count] = ((char *)src)[count]; } return dest; } char *strncpy( char *strDest, const char *strSource, size_t count ) { char *s; s = strDest; while ( *strSource && count ) { *s++ = *strSource++; count--; } while ( count-- ) { *s++ = 0; } return strDest; } double sqrt( double x ) { float y; float delta; float maxError; if ( x <= 0 ) { return 0; } // initial guess y = x / 2; // refine maxError = x * 0.001; do { delta = ( y * y ) - x; y -= delta / ( 2 * y ); } while ( delta > maxError || delta < -maxError ); return y; } float sintable[1024] = { 0.000000,0.001534,0.003068,0.004602,0.006136,0.007670,0.009204,0.010738, 0.012272,0.013805,0.015339,0.016873,0.018407,0.019940,0.021474,0.023008, 0.024541,0.026075,0.027608,0.029142,0.030675,0.032208,0.033741,0.035274, 0.036807,0.038340,0.039873,0.041406,0.042938,0.044471,0.046003,0.047535, 0.049068,0.050600,0.052132,0.053664,0.055195,0.056727,0.058258,0.059790, 0.061321,0.062852,0.064383,0.065913,0.067444,0.068974,0.070505,0.072035, 0.073565,0.075094,0.076624,0.078153,0.079682,0.081211,0.082740,0.084269, 0.085797,0.087326,0.088854,0.090381,0.091909,0.093436,0.094963,0.096490, 0.098017,0.099544,0.101070,0.102596,0.104122,0.105647,0.107172,0.108697, 0.110222,0.111747,0.113271,0.114795,0.116319,0.117842,0.119365,0.120888, 0.122411,0.123933,0.125455,0.126977,0.128498,0.130019,0.131540,0.133061, 0.134581,0.136101,0.137620,0.139139,0.140658,0.142177,0.143695,0.145213, 0.146730,0.148248,0.149765,0.151281,0.152797,0.154313,0.155828,0.157343, 0.158858,0.160372,0.161886,0.163400,0.164913,0.166426,0.167938,0.169450, 0.170962,0.172473,0.173984,0.175494,0.177004,0.178514,0.180023,0.181532, 0.183040,0.184548,0.186055,0.187562,0.189069,0.190575,0.192080,0.193586, 0.195090,0.196595,0.198098,0.199602,0.201105,0.202607,0.204109,0.205610, 0.207111,0.208612,0.210112,0.211611,0.213110,0.214609,0.216107,0.217604, 0.219101,0.220598,0.222094,0.223589,0.225084,0.226578,0.228072,0.229565, 0.231058,0.232550,0.234042,0.235533,0.237024,0.238514,0.240003,0.241492, 0.242980,0.244468,0.245955,0.247442,0.248928,0.250413,0.251898,0.253382, 0.254866,0.256349,0.257831,0.259313,0.260794,0.262275,0.263755,0.265234, 0.266713,0.268191,0.269668,0.271145,0.272621,0.274097,0.275572,0.277046, 0.278520,0.279993,0.281465,0.282937,0.284408,0.285878,0.287347,0.288816, 0.290285,0.291752,0.293219,0.294685,0.296151,0.297616,0.299080,0.300543, 0.302006,0.303468,0.304929,0.306390,0.307850,0.309309,0.310767,0.312225, 0.313682,0.315138,0.316593,0.318048,0.319502,0.320955,0.322408,0.323859, 0.325310,0.326760,0.328210,0.329658,0.331106,0.332553,0.334000,0.335445, 0.336890,0.338334,0.339777,0.341219,0.342661,0.344101,0.345541,0.346980, 0.348419,0.349856,0.351293,0.352729,0.354164,0.355598,0.357031,0.358463, 0.359895,0.361326,0.362756,0.364185,0.365613,0.367040,0.368467,0.369892, 0.371317,0.372741,0.374164,0.375586,0.377007,0.378428,0.379847,0.381266, 0.382683,0.384100,0.385516,0.386931,0.388345,0.389758,0.391170,0.392582, 0.393992,0.395401,0.396810,0.398218,0.399624,0.401030,0.402435,0.403838, 0.405241,0.406643,0.408044,0.409444,0.410843,0.412241,0.413638,0.415034, 0.416430,0.417824,0.419217,0.420609,0.422000,0.423390,0.424780,0.426168, 0.427555,0.428941,0.430326,0.431711,0.433094,0.434476,0.435857,0.437237, 0.438616,0.439994,0.441371,0.442747,0.444122,0.445496,0.446869,0.448241, 0.449611,0.450981,0.452350,0.453717,0.455084,0.456449,0.457813,0.459177, 0.460539,0.461900,0.463260,0.464619,0.465976,0.467333,0.468689,0.470043, 0.471397,0.472749,0.474100,0.475450,0.476799,0.478147,0.479494,0.480839, 0.482184,0.483527,0.484869,0.486210,0.487550,0.488889,0.490226,0.491563, 0.492898,0.494232,0.495565,0.496897,0.498228,0.499557,0.500885,0.502212, 0.503538,0.504863,0.506187,0.507509,0.508830,0.510150,0.511469,0.512786, 0.514103,0.515418,0.516732,0.518045,0.519356,0.520666,0.521975,0.523283, 0.524590,0.525895,0.527199,0.528502,0.529804,0.531104,0.532403,0.533701, 0.534998,0.536293,0.537587,0.538880,0.540171,0.541462,0.542751,0.544039, 0.545325,0.546610,0.547894,0.549177,0.550458,0.551738,0.553017,0.554294, 0.555570,0.556845,0.558119,0.559391,0.560662,0.561931,0.563199,0.564466, 0.565732,0.566996,0.568259,0.569521,0.570781,0.572040,0.573297,0.574553, 0.575808,0.577062,0.578314,0.579565,0.580814,0.582062,0.583309,0.584554, 0.585798,0.587040,0.588282,0.589521,0.590760,0.591997,0.593232,0.594466, 0.595699,0.596931,0.598161,0.599389,0.600616,0.601842,0.603067,0.604290, 0.605511,0.606731,0.607950,0.609167,0.610383,0.611597,0.612810,0.614022, 0.615232,0.616440,0.617647,0.618853,0.620057,0.621260,0.622461,0.623661, 0.624859,0.626056,0.627252,0.628446,0.629638,0.630829,0.632019,0.633207, 0.634393,0.635578,0.636762,0.637944,0.639124,0.640303,0.641481,0.642657, 0.643832,0.645005,0.646176,0.647346,0.648514,0.649681,0.650847,0.652011, 0.653173,0.654334,0.655493,0.656651,0.657807,0.658961,0.660114,0.661266, 0.662416,0.663564,0.664711,0.665856,0.667000,0.668142,0.669283,0.670422, 0.671559,0.672695,0.673829,0.674962,0.676093,0.677222,0.678350,0.679476, 0.680601,0.681724,0.682846,0.683965,0.685084,0.686200,0.687315,0.688429, 0.689541,0.690651,0.691759,0.692866,0.693971,0.695075,0.696177,0.697278, 0.698376,0.699473,0.700569,0.701663,0.702755,0.703845,0.704934,0.706021, 0.707107,0.708191,0.709273,0.710353,0.711432,0.712509,0.713585,0.714659, 0.715731,0.716801,0.717870,0.718937,0.720003,0.721066,0.722128,0.723188, 0.724247,0.725304,0.726359,0.727413,0.728464,0.729514,0.730563,0.731609, 0.732654,0.733697,0.734739,0.735779,0.736817,0.737853,0.738887,0.739920, 0.740951,0.741980,0.743008,0.744034,0.745058,0.746080,0.747101,0.748119, 0.749136,0.750152,0.751165,0.752177,0.753187,0.754195,0.755201,0.756206, 0.757209,0.758210,0.759209,0.760207,0.761202,0.762196,0.763188,0.764179, 0.765167,0.766154,0.767139,0.768122,0.769103,0.770083,0.771061,0.772036, 0.773010,0.773983,0.774953,0.775922,0.776888,0.777853,0.778817,0.779778, 0.780737,0.781695,0.782651,0.783605,0.784557,0.785507,0.786455,0.787402, 0.788346,0.789289,0.790230,0.791169,0.792107,0.793042,0.793975,0.794907, 0.795837,0.796765,0.797691,0.798615,0.799537,0.800458,0.801376,0.802293, 0.803208,0.804120,0.805031,0.805940,0.806848,0.807753,0.808656,0.809558, 0.810457,0.811355,0.812251,0.813144,0.814036,0.814926,0.815814,0.816701, 0.817585,0.818467,0.819348,0.820226,0.821103,0.821977,0.822850,0.823721, 0.824589,0.825456,0.826321,0.827184,0.828045,0.828904,0.829761,0.830616, 0.831470,0.832321,0.833170,0.834018,0.834863,0.835706,0.836548,0.837387, 0.838225,0.839060,0.839894,0.840725,0.841555,0.842383,0.843208,0.844032, 0.844854,0.845673,0.846491,0.847307,0.848120,0.848932,0.849742,0.850549, 0.851355,0.852159,0.852961,0.853760,0.854558,0.855354,0.856147,0.856939, 0.857729,0.858516,0.859302,0.860085,0.860867,0.861646,0.862424,0.863199, 0.863973,0.864744,0.865514,0.866281,0.867046,0.867809,0.868571,0.869330, 0.870087,0.870842,0.871595,0.872346,0.873095,0.873842,0.874587,0.875329, 0.876070,0.876809,0.877545,0.878280,0.879012,0.879743,0.880471,0.881197, 0.881921,0.882643,0.883363,0.884081,0.884797,0.885511,0.886223,0.886932, 0.887640,0.888345,0.889048,0.889750,0.890449,0.891146,0.891841,0.892534, 0.893224,0.893913,0.894599,0.895284,0.895966,0.896646,0.897325,0.898001, 0.898674,0.899346,0.900016,0.900683,0.901349,0.902012,0.902673,0.903332, 0.903989,0.904644,0.905297,0.905947,0.906596,0.907242,0.907886,0.908528, 0.909168,0.909806,0.910441,0.911075,0.911706,0.912335,0.912962,0.913587, 0.914210,0.914830,0.915449,0.916065,0.916679,0.917291,0.917901,0.918508, 0.919114,0.919717,0.920318,0.920917,0.921514,0.922109,0.922701,0.923291, 0.923880,0.924465,0.925049,0.925631,0.926210,0.926787,0.927363,0.927935, 0.928506,0.929075,0.929641,0.930205,0.930767,0.931327,0.931884,0.932440, 0.932993,0.933544,0.934093,0.934639,0.935184,0.935726,0.936266,0.936803, 0.937339,0.937872,0.938404,0.938932,0.939459,0.939984,0.940506,0.941026, 0.941544,0.942060,0.942573,0.943084,0.943593,0.944100,0.944605,0.945107, 0.945607,0.946105,0.946601,0.947094,0.947586,0.948075,0.948561,0.949046, 0.949528,0.950008,0.950486,0.950962,0.951435,0.951906,0.952375,0.952842, 0.953306,0.953768,0.954228,0.954686,0.955141,0.955594,0.956045,0.956494, 0.956940,0.957385,0.957826,0.958266,0.958703,0.959139,0.959572,0.960002, 0.960431,0.960857,0.961280,0.961702,0.962121,0.962538,0.962953,0.963366, 0.963776,0.964184,0.964590,0.964993,0.965394,0.965793,0.966190,0.966584, 0.966976,0.967366,0.967754,0.968139,0.968522,0.968903,0.969281,0.969657, 0.970031,0.970403,0.970772,0.971139,0.971504,0.971866,0.972226,0.972584, 0.972940,0.973293,0.973644,0.973993,0.974339,0.974684,0.975025,0.975365, 0.975702,0.976037,0.976370,0.976700,0.977028,0.977354,0.977677,0.977999, 0.978317,0.978634,0.978948,0.979260,0.979570,0.979877,0.980182,0.980485, 0.980785,0.981083,0.981379,0.981673,0.981964,0.982253,0.982539,0.982824, 0.983105,0.983385,0.983662,0.983937,0.984210,0.984480,0.984749,0.985014, 0.985278,0.985539,0.985798,0.986054,0.986308,0.986560,0.986809,0.987057, 0.987301,0.987544,0.987784,0.988022,0.988258,0.988491,0.988722,0.988950, 0.989177,0.989400,0.989622,0.989841,0.990058,0.990273,0.990485,0.990695, 0.990903,0.991108,0.991311,0.991511,0.991710,0.991906,0.992099,0.992291, 0.992480,0.992666,0.992850,0.993032,0.993212,0.993389,0.993564,0.993737, 0.993907,0.994075,0.994240,0.994404,0.994565,0.994723,0.994879,0.995033, 0.995185,0.995334,0.995481,0.995625,0.995767,0.995907,0.996045,0.996180, 0.996313,0.996443,0.996571,0.996697,0.996820,0.996941,0.997060,0.997176, 0.997290,0.997402,0.997511,0.997618,0.997723,0.997825,0.997925,0.998023, 0.998118,0.998211,0.998302,0.998390,0.998476,0.998559,0.998640,0.998719, 0.998795,0.998870,0.998941,0.999011,0.999078,0.999142,0.999205,0.999265, 0.999322,0.999378,0.999431,0.999481,0.999529,0.999575,0.999619,0.999660, 0.999699,0.999735,0.999769,0.999801,0.999831,0.999858,0.999882,0.999905, 0.999925,0.999942,0.999958,0.999971,0.999981,0.999989,0.999995,0.999999 }; double sin( double x ) { int index; int quad; index = 1024 * x / (M_PI * 0.5); quad = ( index >> 10 ) & 3; index &= 1023; switch ( quad ) { case 0: return sintable[index]; case 1: return sintable[1023-index]; case 2: return -sintable[index]; case 3: return -sintable[1023-index]; } return 0; } double cos( double x ) { int index; int quad; index = 1024 * x / (M_PI * 0.5); quad = ( index >> 10 ) & 3; index &= 1023; switch ( quad ) { case 3: return sintable[index]; case 0: return sintable[1023-index]; case 1: return -sintable[index]; case 2: return -sintable[1023-index]; } return 0; } /* void create_acostable( void ) { int i; FILE *fp; float a; fp = fopen("c:\\acostable.txt", "w"); fprintf(fp, "float acostable[] = {"); for (i = 0; i < 1024; i++) { if (!(i & 7)) fprintf(fp, "\n"); a = acos( (float) -1 + i / 512 ); fprintf(fp, "%1.8f,", a); } fprintf(fp, "\n}\n"); fclose(fp); } */ float acostable[] = { 3.14159265,3.07908248,3.05317551,3.03328655,3.01651113,3.00172442,2.98834964,2.97604422, 2.96458497,2.95381690,2.94362719,2.93393068,2.92466119,2.91576615,2.90720289,2.89893629, 2.89093699,2.88318015,2.87564455,2.86831188,2.86116621,2.85419358,2.84738169,2.84071962, 2.83419760,2.82780691,2.82153967,2.81538876,2.80934770,2.80341062,2.79757211,2.79182724, 2.78617145,2.78060056,2.77511069,2.76969824,2.76435988,2.75909250,2.75389319,2.74875926, 2.74368816,2.73867752,2.73372510,2.72882880,2.72398665,2.71919677,2.71445741,2.70976688, 2.70512362,2.70052613,2.69597298,2.69146283,2.68699438,2.68256642,2.67817778,2.67382735, 2.66951407,2.66523692,2.66099493,2.65678719,2.65261279,2.64847088,2.64436066,2.64028133, 2.63623214,2.63221238,2.62822133,2.62425835,2.62032277,2.61641398,2.61253138,2.60867440, 2.60484248,2.60103507,2.59725167,2.59349176,2.58975488,2.58604053,2.58234828,2.57867769, 2.57502832,2.57139977,2.56779164,2.56420354,2.56063509,2.55708594,2.55355572,2.55004409, 2.54655073,2.54307530,2.53961750,2.53617701,2.53275354,2.52934680,2.52595650,2.52258238, 2.51922417,2.51588159,2.51255441,2.50924238,2.50594525,2.50266278,2.49939476,2.49614096, 2.49290115,2.48967513,2.48646269,2.48326362,2.48007773,2.47690482,2.47374472,2.47059722, 2.46746215,2.46433933,2.46122860,2.45812977,2.45504269,2.45196720,2.44890314,2.44585034, 2.44280867,2.43977797,2.43675809,2.43374890,2.43075025,2.42776201,2.42478404,2.42181622, 2.41885841,2.41591048,2.41297232,2.41004380,2.40712480,2.40421521,2.40131491,2.39842379, 2.39554173,2.39266863,2.38980439,2.38694889,2.38410204,2.38126374,2.37843388,2.37561237, 2.37279910,2.36999400,2.36719697,2.36440790,2.36162673,2.35885335,2.35608768,2.35332964, 2.35057914,2.34783610,2.34510044,2.34237208,2.33965094,2.33693695,2.33423003,2.33153010, 2.32883709,2.32615093,2.32347155,2.32079888,2.31813284,2.31547337,2.31282041,2.31017388, 2.30753373,2.30489988,2.30227228,2.29965086,2.29703556,2.29442632,2.29182309,2.28922580, 2.28663439,2.28404881,2.28146900,2.27889490,2.27632647,2.27376364,2.27120637,2.26865460, 2.26610827,2.26356735,2.26103177,2.25850149,2.25597646,2.25345663,2.25094195,2.24843238, 2.24592786,2.24342836,2.24093382,2.23844420,2.23595946,2.23347956,2.23100444,2.22853408, 2.22606842,2.22360742,2.22115104,2.21869925,2.21625199,2.21380924,2.21137096,2.20893709, 2.20650761,2.20408248,2.20166166,2.19924511,2.19683280,2.19442469,2.19202074,2.18962092, 2.18722520,2.18483354,2.18244590,2.18006225,2.17768257,2.17530680,2.17293493,2.17056692, 2.16820274,2.16584236,2.16348574,2.16113285,2.15878367,2.15643816,2.15409630,2.15175805, 2.14942338,2.14709226,2.14476468,2.14244059,2.14011997,2.13780279,2.13548903,2.13317865, 2.13087163,2.12856795,2.12626757,2.12397047,2.12167662,2.11938600,2.11709859,2.11481435, 2.11253326,2.11025530,2.10798044,2.10570867,2.10343994,2.10117424,2.09891156,2.09665185, 2.09439510,2.09214129,2.08989040,2.08764239,2.08539725,2.08315496,2.08091550,2.07867884, 2.07644495,2.07421383,2.07198545,2.06975978,2.06753681,2.06531651,2.06309887,2.06088387, 2.05867147,2.05646168,2.05425445,2.05204979,2.04984765,2.04764804,2.04545092,2.04325628, 2.04106409,2.03887435,2.03668703,2.03450211,2.03231957,2.03013941,2.02796159,2.02578610, 2.02361292,2.02144204,2.01927344,2.01710710,2.01494300,2.01278113,2.01062146,2.00846399, 2.00630870,2.00415556,2.00200457,1.99985570,1.99770895,1.99556429,1.99342171,1.99128119, 1.98914271,1.98700627,1.98487185,1.98273942,1.98060898,1.97848051,1.97635399,1.97422942, 1.97210676,1.96998602,1.96786718,1.96575021,1.96363511,1.96152187,1.95941046,1.95730088, 1.95519310,1.95308712,1.95098292,1.94888050,1.94677982,1.94468089,1.94258368,1.94048818, 1.93839439,1.93630228,1.93421185,1.93212308,1.93003595,1.92795046,1.92586659,1.92378433, 1.92170367,1.91962459,1.91754708,1.91547113,1.91339673,1.91132385,1.90925250,1.90718266, 1.90511432,1.90304746,1.90098208,1.89891815,1.89685568,1.89479464,1.89273503,1.89067683, 1.88862003,1.88656463,1.88451060,1.88245794,1.88040664,1.87835668,1.87630806,1.87426076, 1.87221477,1.87017008,1.86812668,1.86608457,1.86404371,1.86200412,1.85996577,1.85792866, 1.85589277,1.85385809,1.85182462,1.84979234,1.84776125,1.84573132,1.84370256,1.84167495, 1.83964848,1.83762314,1.83559892,1.83357582,1.83155381,1.82953289,1.82751305,1.82549429, 1.82347658,1.82145993,1.81944431,1.81742973,1.81541617,1.81340362,1.81139207,1.80938151, 1.80737194,1.80536334,1.80335570,1.80134902,1.79934328,1.79733848,1.79533460,1.79333164, 1.79132959,1.78932843,1.78732817,1.78532878,1.78333027,1.78133261,1.77933581,1.77733985, 1.77534473,1.77335043,1.77135695,1.76936428,1.76737240,1.76538132,1.76339101,1.76140148, 1.75941271,1.75742470,1.75543743,1.75345090,1.75146510,1.74948002,1.74749565,1.74551198, 1.74352900,1.74154672,1.73956511,1.73758417,1.73560389,1.73362426,1.73164527,1.72966692, 1.72768920,1.72571209,1.72373560,1.72175971,1.71978441,1.71780969,1.71583556,1.71386199, 1.71188899,1.70991653,1.70794462,1.70597325,1.70400241,1.70203209,1.70006228,1.69809297, 1.69612416,1.69415584,1.69218799,1.69022062,1.68825372,1.68628727,1.68432127,1.68235571, 1.68039058,1.67842588,1.67646160,1.67449772,1.67253424,1.67057116,1.66860847,1.66664615, 1.66468420,1.66272262,1.66076139,1.65880050,1.65683996,1.65487975,1.65291986,1.65096028, 1.64900102,1.64704205,1.64508338,1.64312500,1.64116689,1.63920905,1.63725148,1.63529416, 1.63333709,1.63138026,1.62942366,1.62746728,1.62551112,1.62355517,1.62159943,1.61964388, 1.61768851,1.61573332,1.61377831,1.61182346,1.60986877,1.60791422,1.60595982,1.60400556, 1.60205142,1.60009739,1.59814349,1.59618968,1.59423597,1.59228235,1.59032882,1.58837536, 1.58642196,1.58446863,1.58251535,1.58056211,1.57860891,1.57665574,1.57470259,1.57274945, 1.57079633,1.56884320,1.56689007,1.56493692,1.56298375,1.56103055,1.55907731,1.55712403, 1.55517069,1.55321730,1.55126383,1.54931030,1.54735668,1.54540297,1.54344917,1.54149526, 1.53954124,1.53758710,1.53563283,1.53367843,1.53172389,1.52976919,1.52781434,1.52585933, 1.52390414,1.52194878,1.51999323,1.51803748,1.51608153,1.51412537,1.51216900,1.51021240, 1.50825556,1.50629849,1.50434117,1.50238360,1.50042576,1.49846765,1.49650927,1.49455060, 1.49259163,1.49063237,1.48867280,1.48671291,1.48475270,1.48279215,1.48083127,1.47887004, 1.47690845,1.47494650,1.47298419,1.47102149,1.46905841,1.46709493,1.46513106,1.46316677, 1.46120207,1.45923694,1.45727138,1.45530538,1.45333893,1.45137203,1.44940466,1.44743682, 1.44546850,1.44349969,1.44153038,1.43956057,1.43759024,1.43561940,1.43364803,1.43167612, 1.42970367,1.42773066,1.42575709,1.42378296,1.42180825,1.41983295,1.41785705,1.41588056, 1.41390346,1.41192573,1.40994738,1.40796840,1.40598877,1.40400849,1.40202755,1.40004594, 1.39806365,1.39608068,1.39409701,1.39211264,1.39012756,1.38814175,1.38615522,1.38416795, 1.38217994,1.38019117,1.37820164,1.37621134,1.37422025,1.37222837,1.37023570,1.36824222, 1.36624792,1.36425280,1.36225684,1.36026004,1.35826239,1.35626387,1.35426449,1.35226422, 1.35026307,1.34826101,1.34625805,1.34425418,1.34224937,1.34024364,1.33823695,1.33622932, 1.33422072,1.33221114,1.33020059,1.32818904,1.32617649,1.32416292,1.32214834,1.32013273, 1.31811607,1.31609837,1.31407960,1.31205976,1.31003885,1.30801684,1.30599373,1.30396951, 1.30194417,1.29991770,1.29789009,1.29586133,1.29383141,1.29180031,1.28976803,1.28773456, 1.28569989,1.28366400,1.28162688,1.27958854,1.27754894,1.27550809,1.27346597,1.27142257, 1.26937788,1.26733189,1.26528459,1.26323597,1.26118602,1.25913471,1.25708205,1.25502803, 1.25297262,1.25091583,1.24885763,1.24679802,1.24473698,1.24267450,1.24061058,1.23854519, 1.23647833,1.23440999,1.23234015,1.23026880,1.22819593,1.22612152,1.22404557,1.22196806, 1.21988898,1.21780832,1.21572606,1.21364219,1.21155670,1.20946958,1.20738080,1.20529037, 1.20319826,1.20110447,1.19900898,1.19691177,1.19481283,1.19271216,1.19060973,1.18850553, 1.18639955,1.18429178,1.18218219,1.18007079,1.17795754,1.17584244,1.17372548,1.17160663, 1.16948589,1.16736324,1.16523866,1.16311215,1.16098368,1.15885323,1.15672081,1.15458638, 1.15244994,1.15031147,1.14817095,1.14602836,1.14388370,1.14173695,1.13958808,1.13743709, 1.13528396,1.13312866,1.13097119,1.12881153,1.12664966,1.12448556,1.12231921,1.12015061, 1.11797973,1.11580656,1.11363107,1.11145325,1.10927308,1.10709055,1.10490563,1.10271831, 1.10052856,1.09833638,1.09614174,1.09394462,1.09174500,1.08954287,1.08733820,1.08513098, 1.08292118,1.08070879,1.07849378,1.07627614,1.07405585,1.07183287,1.06960721,1.06737882, 1.06514770,1.06291382,1.06067715,1.05843769,1.05619540,1.05395026,1.05170226,1.04945136, 1.04719755,1.04494080,1.04268110,1.04041841,1.03815271,1.03588399,1.03361221,1.03133735, 1.02905939,1.02677830,1.02449407,1.02220665,1.01991603,1.01762219,1.01532509,1.01302471, 1.01072102,1.00841400,1.00610363,1.00378986,1.00147268,0.99915206,0.99682798,0.99450039, 0.99216928,0.98983461,0.98749636,0.98515449,0.98280898,0.98045980,0.97810691,0.97575030, 0.97338991,0.97102573,0.96865772,0.96628585,0.96391009,0.96153040,0.95914675,0.95675912, 0.95436745,0.95197173,0.94957191,0.94716796,0.94475985,0.94234754,0.93993099,0.93751017, 0.93508504,0.93265556,0.93022170,0.92778341,0.92534066,0.92289341,0.92044161,0.91798524, 0.91552424,0.91305858,0.91058821,0.90811309,0.90563319,0.90314845,0.90065884,0.89816430, 0.89566479,0.89316028,0.89065070,0.88813602,0.88561619,0.88309116,0.88056088,0.87802531, 0.87548438,0.87293806,0.87038629,0.86782901,0.86526619,0.86269775,0.86012366,0.85754385, 0.85495827,0.85236686,0.84976956,0.84716633,0.84455709,0.84194179,0.83932037,0.83669277, 0.83405893,0.83141877,0.82877225,0.82611928,0.82345981,0.82079378,0.81812110,0.81544172, 0.81275556,0.81006255,0.80736262,0.80465570,0.80194171,0.79922057,0.79649221,0.79375655, 0.79101352,0.78826302,0.78550497,0.78273931,0.77996593,0.77718475,0.77439569,0.77159865, 0.76879355,0.76598029,0.76315878,0.76032891,0.75749061,0.75464376,0.75178826,0.74892402, 0.74605092,0.74316887,0.74027775,0.73737744,0.73446785,0.73154885,0.72862033,0.72568217, 0.72273425,0.71977644,0.71680861,0.71383064,0.71084240,0.70784376,0.70483456,0.70181469, 0.69878398,0.69574231,0.69268952,0.68962545,0.68654996,0.68346288,0.68036406,0.67725332, 0.67413051,0.67099544,0.66784794,0.66468783,0.66151492,0.65832903,0.65512997,0.65191753, 0.64869151,0.64545170,0.64219789,0.63892987,0.63564741,0.63235028,0.62903824,0.62571106, 0.62236849,0.61901027,0.61563615,0.61224585,0.60883911,0.60541564,0.60197515,0.59851735, 0.59504192,0.59154856,0.58803694,0.58450672,0.58095756,0.57738911,0.57380101,0.57019288, 0.56656433,0.56291496,0.55924437,0.55555212,0.55183778,0.54810089,0.54434099,0.54055758, 0.53675018,0.53291825,0.52906127,0.52517867,0.52126988,0.51733431,0.51337132,0.50938028, 0.50536051,0.50131132,0.49723200,0.49312177,0.48897987,0.48480547,0.48059772,0.47635573, 0.47207859,0.46776530,0.46341487,0.45902623,0.45459827,0.45012983,0.44561967,0.44106652, 0.43646903,0.43182577,0.42713525,0.42239588,0.41760600,0.41276385,0.40786755,0.40291513, 0.39790449,0.39283339,0.38769946,0.38250016,0.37723277,0.37189441,0.36648196,0.36099209, 0.35542120,0.34976542,0.34402054,0.33818204,0.33224495,0.32620390,0.32005298,0.31378574, 0.30739505,0.30087304,0.29421096,0.28739907,0.28042645,0.27328078,0.26594810,0.25841250, 0.25065566,0.24265636,0.23438976,0.22582651,0.21693146,0.20766198,0.19796546,0.18777575, 0.17700769,0.16554844,0.15324301,0.13986823,0.12508152,0.10830610,0.08841715,0.06251018, } double acos( double x ) { int index; if (x < -1) x = -1; if (x > 1) x = 1; index = (float) (1.0 + x) * 511.9; return acostable[index]; } double atan2( double y, double x ) { float base; float temp; float dir; float test; int i; if ( x < 0 ) { if ( y >= 0 ) { // quad 1 base = M_PI / 2; temp = x; x = y; y = -temp; } else { // quad 2 base = M_PI; x = -x; y = -y; } } else { if ( y < 0 ) { // quad 3 base = 3 * M_PI / 2; temp = x; x = -y; y = temp; } } if ( y > x ) { base += M_PI/2; temp = x; x = y; y = temp; dir = -1; } else { dir = 1; } // calcualte angle in octant 0 if ( x == 0 ) { return base; } y /= x; for ( i = 0 ; i < 512 ; i++ ) { test = sintable[i] / sintable[1023-i]; if ( test > y ) { break; } } return base + dir * i * ( M_PI/2048); } #endif #ifdef Q3_VM // bk001127 - guarded this tan replacement // ld: undefined versioned symbol name tan@@GLIBC_2.0 double tan( double x ) { return sin(x) / cos(x); } #endif static int randSeed = 0; void srand( unsigned seed ) { randSeed = seed; } int rand( void ) { randSeed = (69069 * randSeed + 1); return randSeed & 0x7fff; } double atof( const char *string ) { float sign; float value; int c; // skip whitespace while ( *string <= ' ' ) { if ( !*string ) { return 0; } string++; } // check sign switch ( *string ) { case '+': string++; sign = 1; break; case '-': string++; sign = -1; break; default: sign = 1; break; } // read digits value = 0; c = string[0]; if ( c != '.' ) { do { c = *string++; if ( c < '0' || c > '9' ) { break; } c -= '0'; value = value * 10 + c; } while ( 1 ); } else { string++; } // check for decimal point if ( c == '.' ) { double fraction; fraction = 0.1; do { c = *string++; if ( c < '0' || c > '9' ) { break; } c -= '0'; value += c * fraction; fraction *= 0.1; } while ( 1 ); } // not handling 10e10 notation... return value * sign; } double _atof( const char **stringPtr ) { const char *string; float sign; float value; int c = '0'; // bk001211 - uninitialized use possible string = *stringPtr; // skip whitespace while ( *string <= ' ' ) { if ( !*string ) { *stringPtr = string; return 0; } string++; } // check sign switch ( *string ) { case '+': string++; sign = 1; break; case '-': string++; sign = -1; break; default: sign = 1; break; } // read digits value = 0; if ( string[0] != '.' ) { do { c = *string++; if ( c < '0' || c > '9' ) { break; } c -= '0'; value = value * 10 + c; } while ( 1 ); } // check for decimal point if ( c == '.' ) { double fraction; fraction = 0.1; do { c = *string++; if ( c < '0' || c > '9' ) { break; } c -= '0'; value += c * fraction; fraction *= 0.1; } while ( 1 ); } // not handling 10e10 notation... *stringPtr = string; return value * sign; } // bk001120 - presumably needed for Mac //#if !defined ( _MSC_VER ) && ! defined ( __linux__ ) // bk001127 - undid undo #if defined ( Q3_VM ) int atoi( const char *string ) { int sign; int value; int c; // skip whitespace while ( *string <= ' ' ) { if ( !*string ) { return 0; } string++; } // check sign switch ( *string ) { case '+': string++; sign = 1; break; case '-': string++; sign = -1; break; default: sign = 1; break; } // read digits value = 0; do { c = *string++; if ( c < '0' || c > '9' ) { break; } c -= '0'; value = value * 10 + c; } while ( 1 ); // not handling 10e10 notation... return value * sign; } int _atoi( const char **stringPtr ) { int sign; int value; int c; const char *string; string = *stringPtr; // skip whitespace while ( *string <= ' ' ) { if ( !*string ) { return 0; } string++; } // check sign switch ( *string ) { case '+': string++; sign = 1; break; case '-': string++; sign = -1; break; default: sign = 1; break; } // read digits value = 0; do { c = *string++; if ( c < '0' || c > '9' ) { break; } c -= '0'; value = value * 10 + c; } while ( 1 ); // not handling 10e10 notation... *stringPtr = string; return value * sign; } int abs( int n ) { return n < 0 ? -n : n; } double fabs( double x ) { return x < 0 ? -x : x; } //========================================================= #define ALT 0x00000001 /* alternate form */ #define HEXPREFIX 0x00000002 /* add 0x or 0X prefix */ #define LADJUST 0x00000004 /* left adjustment */ #define LONGDBL 0x00000008 /* long double */ #define LONGINT 0x00000010 /* long integer */ #define QUADINT 0x00000020 /* quad integer */ #define SHORTINT 0x00000040 /* short integer */ #define ZEROPAD 0x00000080 /* zero (as opposed to blank) pad */ #define FPT 0x00000100 /* floating point number */ #define to_digit(c) ((c) - '0') #define is_digit(c) ((unsigned)to_digit(c) <= 9) #define to_char(n) ((n) + '0') void AddInt( char **buf_p, int val, int width, int flags ) { char text[32]; int digits; int signedVal; char *buf; digits = 0; signedVal = val; if ( val < 0 ) { val = -val; } do { text[digits++] = '0' + val % 10; val /= 10; } while ( val ); if ( signedVal < 0 ) { text[digits++] = '-'; } buf = *buf_p; if( !( flags & LADJUST ) ) { while ( digits < width ) { *buf++ = ( flags & ZEROPAD ) ? '0' : ' '; width--; } } while ( digits-- ) { *buf++ = text[digits]; width--; } if( flags & LADJUST ) { while ( width-- ) { *buf++ = ( flags & ZEROPAD ) ? '0' : ' '; } } *buf_p = buf; } void AddFloat( char **buf_p, float fval, int width, int prec ) { char text[32]; int digits; float signedVal; char *buf; int val; // get the sign signedVal = fval; if ( fval < 0 ) { fval = -fval; } // write the float number digits = 0; val = (int)fval; do { text[digits++] = '0' + val % 10; val /= 10; } while ( val ); if ( signedVal < 0 ) { text[digits++] = '-'; } buf = *buf_p; while ( digits < width ) { *buf++ = ' '; width--; } while ( digits-- ) { *buf++ = text[digits]; } *buf_p = buf; if (prec < 0) prec = 6; // write the fraction digits = 0; while (digits < prec) { fval -= (int) fval; fval *= 10.0; val = (int) fval; text[digits++] = '0' + val % 10; } if (digits > 0) { buf = *buf_p; *buf++ = '.'; for (prec = 0; prec < digits; prec++) { *buf++ = text[prec]; } *buf_p = buf; } } void AddString( char **buf_p, char *string, int width, int prec ) { int size; char *buf; buf = *buf_p; if ( string == NULL ) { string = "(null)"; prec = -1; } if ( prec >= 0 ) { for( size = 0; size < prec; size++ ) { if( string[size] == '\0' ) { break; } } } else { size = strlen( string ); } width -= size; while( size-- ) { *buf++ = *string++; } while( width-- > 0 ) { *buf++ = ' '; } *buf_p = buf; } /* vsprintf I'm not going to support a bunch of the more arcane stuff in here just to keep it simpler. For example, the '*' and '$' are not currently supported. I've tried to make it so that it will just parse and ignore formats we don't support. */ int vsprintf( char *buffer, const char *fmt, va_list argptr ) { int *arg; char *buf_p; char ch; int flags; int width; int prec; int n; char sign; buf_p = buffer; arg = (int *)argptr; while( qtrue ) { // run through the format string until we hit a '%' or '\0' for ( ch = *fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++ ) { *buf_p++ = ch; } if ( ch == '\0' ) { goto done; } // skip over the '%' fmt++; // reset formatting state flags = 0; width = 0; prec = -1; sign = '\0'; rflag: ch = *fmt++; reswitch: switch( ch ) { case '-': flags |= LADJUST; goto rflag; case '.': n = 0; while( is_digit( ( ch = *fmt++ ) ) ) { n = 10 * n + ( ch - '0' ); } prec = n < 0 ? -1 : n; goto reswitch; case '0': flags |= ZEROPAD; goto rflag; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': n = 0; do { n = 10 * n + ( ch - '0' ); ch = *fmt++; } while( is_digit( ch ) ); width = n; goto reswitch; case 'c': *buf_p++ = (char)*arg; arg++; break; case 'd': case 'i': AddInt( &buf_p, *arg, width, flags ); arg++; break; case 'f': AddFloat( &buf_p, *(double *)arg, width, prec ); #ifdef __LCC__ arg += 1; // everything is 32 bit in my compiler #else arg += 2; #endif break; case 's': AddString( &buf_p, (char *)*arg, width, prec ); arg++; break; case '%': *buf_p++ = ch; break; default: *buf_p++ = (char)*arg; arg++; break; } } done: *buf_p = 0; return buf_p - buffer; } /* this is really crappy */ int sscanf( const char *buffer, const char *fmt, ... ) { int cmd; int **arg; int count; arg = (int **)&fmt + 1; count = 0; while ( *fmt ) { if ( fmt[0] != '%' ) { fmt++; continue; } cmd = fmt[1]; fmt += 2; switch ( cmd ) { case 'i': case 'd': case 'u': **arg = _atoi( &buffer ); break; case 'f': *(float *)*arg = _atof( &buffer ); break; } arg++; } return count; } #endif ================================================ FILE: src/game/bg_lib.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // bg_lib.h -- standard C library replacement routines used by code // compiled for the virtual machine // This file is NOT included on native builds typedef int size_t; typedef char * va_list; #define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) ) #define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ) #define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) #define va_end(ap) ( ap = (va_list)0 ) #define CHAR_BIT 8 /* number of bits in a char */ #define SCHAR_MIN (-128) /* minimum signed char value */ #define SCHAR_MAX 127 /* maximum signed char value */ #define UCHAR_MAX 0xff /* maximum unsigned char value */ #define SHRT_MIN (-32768) /* minimum (signed) short value */ #define SHRT_MAX 32767 /* maximum (signed) short value */ #define USHRT_MAX 0xffff /* maximum unsigned short value */ #define INT_MIN (-2147483647 - 1) /* minimum (signed) int value */ #define INT_MAX 2147483647 /* maximum (signed) int value */ #define UINT_MAX 0xffffffff /* maximum unsigned int value */ #define LONG_MIN (-2147483647L - 1) /* minimum (signed) long value */ #define LONG_MAX 2147483647L /* maximum (signed) long value */ #define ULONG_MAX 0xffffffffUL /* maximum unsigned long value */ // Misc functions typedef int cmp_t(const void *, const void *); void qsort(void *a, size_t n, size_t es, cmp_t *cmp); void srand( unsigned seed ); int rand( void ); // String functions int strlen( const char *string ); char *strcat( char *strDestination, const char *strSource ); char *strcpy( char *strDestination, const char *strSource ); int strcmp( const char *string1, const char *string2 ); char *strchr( const char *string, int c ); char *strstr( const char *string, const char *strCharSet ); char *strncpy( char *strDest, const char *strSource, size_t count ); int tolower( int c ); int toupper( int c ); double atof( const char *string ); double _atof( const char **stringPtr ); int atoi( const char *string ); int _atoi( const char **stringPtr ); int vsprintf( char *buffer, const char *fmt, va_list argptr ); int sscanf( const char *buffer, const char *fmt, ... ); // Memory functions void *memmove( void *dest, const void *src, size_t count ); void *memset( void *dest, int c, size_t count ); void *memcpy( void *dest, const void *src, size_t count ); // Math functions double ceil( double x ); double floor( double x ); double sqrt( double x ); double sin( double x ); double cos( double x ); double atan2( double y, double x ); double tan( double x ); int abs( int n ); double fabs( double x ); double acos( double x ); ================================================ FILE: src/game/bg_local.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // bg_local.h -- local definitions for the bg (both games) files #define MIN_WALK_NORMAL 0.7f // can't walk on very steep slopes #define STEPSIZE 18 #define JUMP_VELOCITY 270 #define TIMER_LAND 130 #define TIMER_GESTURE (34*66+50) #define OVERCLIP 1.001f // all of the locals will be zeroed before each // pmove, just to make damn sure we don't have // any differences when running on client or server typedef struct { vec3_t forward, right, up; float frametime; int msec; qboolean walking; qboolean groundPlane; trace_t groundTrace; float impactSpeed; vec3_t previous_origin; vec3_t previous_velocity; int previous_waterlevel; } pml_t; extern pmove_t *pm; extern pml_t pml; // movement parameters extern float pm_stopspeed; extern float pm_duckScale; extern float pm_swimScale; extern float pm_wadeScale; extern float pm_accelerate; extern float pm_airaccelerate; extern float pm_wateraccelerate; extern float pm_flyaccelerate; extern float pm_friction; extern float pm_waterfriction; extern float pm_flightfriction; extern int c_pmove; void PM_ClipVelocity( vec3_t in, vec3_t normal, vec3_t out, float overbounce ); void PM_AddTouchEnt( int entityNum ); void PM_AddEvent( int newEvent ); qboolean PM_SlideMove( qboolean gravity ); void PM_StepSlideMove( qboolean gravity ); ================================================ FILE: src/game/bg_misc.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // bg_misc.c -- both games misc functions, all completely stateless #include "q_shared.h" #include "bg_public.h" /*QUAKED item_***** ( 0 0 0 ) (-16 -16 -16) (16 16 16) suspended DO NOT USE THIS CLASS, IT JUST HOLDS GENERAL INFORMATION. The suspended flag will allow items to hang in the air, otherwise they are dropped to the next surface. If an item is the target of another entity, it will not spawn in until fired. An item fires all of its targets when it is picked up. If the toucher can't carry it, the targets won't be fired. "notfree" if set to 1, don't spawn in free for all games "notteam" if set to 1, don't spawn in team games "notsingle" if set to 1, don't spawn in single player games "wait" override the default wait before respawning. -1 = never respawn automatically, which can be used with targeted spawning. "random" random number of plus or minus seconds varied from the respawn time "count" override quantity or duration on most items. */ gitem_t bg_itemlist[] = { { NULL, NULL, { NULL, NULL, 0, 0} , /* icon */ NULL, /* pickup */ NULL, 0, 0, 0, /* precache */ "", /* sounds */ "" }, // leave index 0 alone // // ARMOR // /*QUAKED item_armor_shard (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "item_armor_shard", "sound/misc/ar1_pkup.wav", { "models/powerups/armor/shard.md3", "models/powerups/armor/shard_sphere.md3", 0, 0} , /* icon */ "icons/iconr_shard", /* pickup */ "Armor Shard", 5, IT_ARMOR, 0, /* precache */ "", /* sounds */ "" }, /*QUAKED item_armor_combat (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "item_armor_combat", "sound/misc/ar2_pkup.wav", { "models/powerups/armor/armor_yel.md3", 0, 0, 0}, /* icon */ "icons/iconr_yellow", /* pickup */ "Armor", 50, IT_ARMOR, 0, /* precache */ "", /* sounds */ "" }, /*QUAKED item_armor_body (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "item_armor_body", "sound/misc/ar2_pkup.wav", { "models/powerups/armor/armor_red.md3", 0, 0, 0}, /* icon */ "icons/iconr_red", /* pickup */ "Heavy Armor", 100, IT_ARMOR, 0, /* precache */ "", /* sounds */ "" }, // // health // /*QUAKED item_health_small (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "item_health_small", "sound/items/s_health.wav", { "models/powerups/health/small_cross.md3", "models/powerups/health/small_sphere.md3", 0, 0 }, /* icon */ "icons/iconh_green", /* pickup */ "5 Health", 5, IT_HEALTH, 0, /* precache */ "", /* sounds */ "" }, /*QUAKED item_health (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "item_health", "sound/items/n_health.wav", { "models/powerups/health/medium_cross.md3", "models/powerups/health/medium_sphere.md3", 0, 0 }, /* icon */ "icons/iconh_yellow", /* pickup */ "25 Health", 25, IT_HEALTH, 0, /* precache */ "", /* sounds */ "" }, /*QUAKED item_health_large (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "item_health_large", "sound/items/l_health.wav", { "models/powerups/health/large_cross.md3", "models/powerups/health/large_sphere.md3", 0, 0 }, /* icon */ "icons/iconh_red", /* pickup */ "50 Health", 50, IT_HEALTH, 0, /* precache */ "", /* sounds */ "" }, /*QUAKED item_health_mega (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "item_health_mega", "sound/items/m_health.wav", { "models/powerups/health/mega_cross.md3", "models/powerups/health/mega_sphere.md3", 0, 0 }, /* icon */ "icons/iconh_mega", /* pickup */ "Mega Health", 100, IT_HEALTH, 0, /* precache */ "", /* sounds */ "" }, // // WEAPONS // /*QUAKED weapon_gauntlet (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "weapon_gauntlet", "sound/misc/w_pkup.wav", { "models/weapons2/gauntlet/gauntlet.md3", 0, 0, 0}, /* icon */ "icons/iconw_gauntlet", /* pickup */ "Gauntlet", 0, IT_WEAPON, WP_GAUNTLET, /* precache */ "", /* sounds */ "" }, /*QUAKED weapon_shotgun (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "weapon_shotgun", "sound/misc/w_pkup.wav", { "models/weapons2/shotgun/shotgun.md3", 0, 0, 0}, /* icon */ "icons/iconw_shotgun", /* pickup */ "Shotgun", 10, IT_WEAPON, WP_SHOTGUN, /* precache */ "", /* sounds */ "" }, /*QUAKED weapon_machinegun (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "weapon_machinegun", "sound/misc/w_pkup.wav", { "models/weapons2/machinegun/machinegun.md3", 0, 0, 0}, /* icon */ "icons/iconw_machinegun", /* pickup */ "Machinegun", 40, IT_WEAPON, WP_MACHINEGUN, /* precache */ "", /* sounds */ "" }, /*QUAKED weapon_grenadelauncher (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "weapon_grenadelauncher", "sound/misc/w_pkup.wav", { "models/weapons2/grenadel/grenadel.md3", 0, 0, 0}, /* icon */ "icons/iconw_grenade", /* pickup */ "Grenade Launcher", 10, IT_WEAPON, WP_GRENADE_LAUNCHER, /* precache */ "", /* sounds */ "sound/weapons/grenade/hgrenb1a.wav sound/weapons/grenade/hgrenb2a.wav" }, /*QUAKED weapon_rocketlauncher (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "weapon_rocketlauncher", "sound/misc/w_pkup.wav", { "models/weapons2/rocketl/rocketl.md3", 0, 0, 0}, /* icon */ "icons/iconw_rocket", /* pickup */ "Rocket Launcher", 10, IT_WEAPON, WP_ROCKET_LAUNCHER, /* precache */ "", /* sounds */ "" }, /*QUAKED weapon_lightning (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "weapon_lightning", "sound/misc/w_pkup.wav", { "models/weapons2/lightning/lightning.md3", 0, 0, 0}, /* icon */ "icons/iconw_lightning", /* pickup */ "Lightning Gun", 100, IT_WEAPON, WP_LIGHTNING, /* precache */ "", /* sounds */ "" }, /*QUAKED weapon_railgun (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "weapon_railgun", "sound/misc/w_pkup.wav", { "models/weapons2/railgun/railgun.md3", 0, 0, 0}, /* icon */ "icons/iconw_railgun", /* pickup */ "Railgun", 10, IT_WEAPON, WP_RAILGUN, /* precache */ "", /* sounds */ "" }, /*QUAKED weapon_plasmagun (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "weapon_plasmagun", "sound/misc/w_pkup.wav", { "models/weapons2/plasma/plasma.md3", 0, 0, 0}, /* icon */ "icons/iconw_plasma", /* pickup */ "Plasma Gun", 50, IT_WEAPON, WP_PLASMAGUN, /* precache */ "", /* sounds */ "" }, /*QUAKED weapon_bfg (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "weapon_bfg", "sound/misc/w_pkup.wav", { "models/weapons2/bfg/bfg.md3", 0, 0, 0}, /* icon */ "icons/iconw_bfg", /* pickup */ "BFG10K", 20, IT_WEAPON, WP_BFG, /* precache */ "", /* sounds */ "" }, /*QUAKED weapon_grapplinghook (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "weapon_grapplinghook", "sound/misc/w_pkup.wav", { "models/weapons2/grapple/grapple.md3", 0, 0, 0}, /* icon */ "icons/iconw_grapple", /* pickup */ "Grappling Hook", 0, IT_WEAPON, WP_GRAPPLING_HOOK, /* precache */ "", /* sounds */ "" }, // // AMMO ITEMS // /*QUAKED ammo_shells (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "ammo_shells", "sound/misc/am_pkup.wav", { "models/powerups/ammo/shotgunam.md3", 0, 0, 0}, /* icon */ "icons/icona_shotgun", /* pickup */ "Shells", 10, IT_AMMO, WP_SHOTGUN, /* precache */ "", /* sounds */ "" }, /*QUAKED ammo_bullets (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "ammo_bullets", "sound/misc/am_pkup.wav", { "models/powerups/ammo/machinegunam.md3", 0, 0, 0}, /* icon */ "icons/icona_machinegun", /* pickup */ "Bullets", 50, IT_AMMO, WP_MACHINEGUN, /* precache */ "", /* sounds */ "" }, /*QUAKED ammo_grenades (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "ammo_grenades", "sound/misc/am_pkup.wav", { "models/powerups/ammo/grenadeam.md3", 0, 0, 0}, /* icon */ "icons/icona_grenade", /* pickup */ "Grenades", 5, IT_AMMO, WP_GRENADE_LAUNCHER, /* precache */ "", /* sounds */ "" }, /*QUAKED ammo_cells (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "ammo_cells", "sound/misc/am_pkup.wav", { "models/powerups/ammo/plasmaam.md3", 0, 0, 0}, /* icon */ "icons/icona_plasma", /* pickup */ "Cells", 30, IT_AMMO, WP_PLASMAGUN, /* precache */ "", /* sounds */ "" }, /*QUAKED ammo_lightning (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "ammo_lightning", "sound/misc/am_pkup.wav", { "models/powerups/ammo/lightningam.md3", 0, 0, 0}, /* icon */ "icons/icona_lightning", /* pickup */ "Lightning", 60, IT_AMMO, WP_LIGHTNING, /* precache */ "", /* sounds */ "" }, /*QUAKED ammo_rockets (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "ammo_rockets", "sound/misc/am_pkup.wav", { "models/powerups/ammo/rocketam.md3", 0, 0, 0}, /* icon */ "icons/icona_rocket", /* pickup */ "Rockets", 5, IT_AMMO, WP_ROCKET_LAUNCHER, /* precache */ "", /* sounds */ "" }, /*QUAKED ammo_slugs (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "ammo_slugs", "sound/misc/am_pkup.wav", { "models/powerups/ammo/railgunam.md3", 0, 0, 0}, /* icon */ "icons/icona_railgun", /* pickup */ "Slugs", 10, IT_AMMO, WP_RAILGUN, /* precache */ "", /* sounds */ "" }, /*QUAKED ammo_bfg (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "ammo_bfg", "sound/misc/am_pkup.wav", { "models/powerups/ammo/bfgam.md3", 0, 0, 0}, /* icon */ "icons/icona_bfg", /* pickup */ "Bfg Ammo", 15, IT_AMMO, WP_BFG, /* precache */ "", /* sounds */ "" }, // // HOLDABLE ITEMS // /*QUAKED holdable_teleporter (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "holdable_teleporter", "sound/items/holdable.wav", { "models/powerups/holdable/teleporter.md3", 0, 0, 0}, /* icon */ "icons/teleporter", /* pickup */ "Personal Teleporter", 60, IT_HOLDABLE, HI_TELEPORTER, /* precache */ "", /* sounds */ "" }, /*QUAKED holdable_medkit (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "holdable_medkit", "sound/items/holdable.wav", { "models/powerups/holdable/medkit.md3", "models/powerups/holdable/medkit_sphere.md3", 0, 0}, /* icon */ "icons/medkit", /* pickup */ "Medkit", 60, IT_HOLDABLE, HI_MEDKIT, /* precache */ "", /* sounds */ "sound/items/use_medkit.wav" }, // // POWERUP ITEMS // /*QUAKED item_quad (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "item_quad", "sound/items/quaddamage.wav", { "models/powerups/instant/quad.md3", "models/powerups/instant/quad_ring.md3", 0, 0 }, /* icon */ "icons/quad", /* pickup */ "Quad Damage", 30, IT_POWERUP, PW_QUAD, /* precache */ "", /* sounds */ "sound/items/damage2.wav sound/items/damage3.wav" }, /*QUAKED item_enviro (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "item_enviro", "sound/items/protect.wav", { "models/powerups/instant/enviro.md3", "models/powerups/instant/enviro_ring.md3", 0, 0 }, /* icon */ "icons/envirosuit", /* pickup */ "Battle Suit", 30, IT_POWERUP, PW_BATTLESUIT, /* precache */ "", /* sounds */ "sound/items/airout.wav sound/items/protect3.wav" }, /*QUAKED item_haste (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "item_haste", "sound/items/haste.wav", { "models/powerups/instant/haste.md3", "models/powerups/instant/haste_ring.md3", 0, 0 }, /* icon */ "icons/haste", /* pickup */ "Speed", 30, IT_POWERUP, PW_HASTE, /* precache */ "", /* sounds */ "" }, /*QUAKED item_invis (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "item_invis", "sound/items/invisibility.wav", { "models/powerups/instant/invis.md3", "models/powerups/instant/invis_ring.md3", 0, 0 }, /* icon */ "icons/invis", /* pickup */ "Invisibility", 30, IT_POWERUP, PW_INVIS, /* precache */ "", /* sounds */ "" }, /*QUAKED item_regen (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "item_regen", "sound/items/regeneration.wav", { "models/powerups/instant/regen.md3", "models/powerups/instant/regen_ring.md3", 0, 0 }, /* icon */ "icons/regen", /* pickup */ "Regeneration", 30, IT_POWERUP, PW_REGEN, /* precache */ "", /* sounds */ "sound/items/regen.wav" }, /*QUAKED item_flight (.3 .3 1) (-16 -16 -16) (16 16 16) suspended */ { "item_flight", "sound/items/flight.wav", { "models/powerups/instant/flight.md3", "models/powerups/instant/flight_ring.md3", 0, 0 }, /* icon */ "icons/flight", /* pickup */ "Flight", 60, IT_POWERUP, PW_FLIGHT, /* precache */ "", /* sounds */ "sound/items/flight.wav" }, /*QUAKED team_CTF_redflag (1 0 0) (-16 -16 -16) (16 16 16) Only in CTF games */ { "team_CTF_redflag", NULL, { "models/flags/r_flag.md3", 0, 0, 0 }, /* icon */ "icons/iconf_red1", /* pickup */ "Red Flag", 0, IT_TEAM, PW_REDFLAG, /* precache */ "", /* sounds */ "" }, /*QUAKED team_CTF_blueflag (0 0 1) (-16 -16 -16) (16 16 16) Only in CTF games */ { "team_CTF_blueflag", NULL, { "models/flags/b_flag.md3", 0, 0, 0 }, /* icon */ "icons/iconf_blu1", /* pickup */ "Blue Flag", 0, IT_TEAM, PW_BLUEFLAG, /* precache */ "", /* sounds */ "" }, // end of list marker {NULL} }; int bg_numItems = sizeof(bg_itemlist) / sizeof(bg_itemlist[0]) - 1; /* ============== BG_FindItemForPowerup ============== */ gitem_t *BG_FindItemForPowerup( powerup_t pw ) { int i; for ( i = 0 ; i < bg_numItems ; i++ ) { if ( (bg_itemlist[i].giType == IT_POWERUP || bg_itemlist[i].giType == IT_TEAM || bg_itemlist[i].giType == IT_PERSISTANT_POWERUP) && bg_itemlist[i].giTag == pw ) { return &bg_itemlist[i]; } } return NULL; } /* ============== BG_FindItemForHoldable ============== */ gitem_t *BG_FindItemForHoldable( holdable_t pw ) { int i; for ( i = 0 ; i < bg_numItems ; i++ ) { if ( bg_itemlist[i].giType == IT_HOLDABLE && bg_itemlist[i].giTag == pw ) { return &bg_itemlist[i]; } } Com_Error( ERR_DROP, "HoldableItem not found" ); return NULL; } /* =============== BG_FindItemForWeapon =============== */ gitem_t *BG_FindItemForWeapon( weapon_t weapon ) { gitem_t *it; for ( it = bg_itemlist + 1 ; it->classname ; it++) { if ( it->giType == IT_WEAPON && it->giTag == weapon ) { return it; } } Com_Error( ERR_DROP, "Couldn't find item for weapon %i", weapon); return NULL; } /* =============== BG_FindItem =============== */ gitem_t *BG_FindItem( const char *pickupName ) { gitem_t *it; for ( it = bg_itemlist + 1 ; it->classname ; it++ ) { if ( !Q_stricmp( it->pickup_name, pickupName ) ) return it; } return NULL; } /* ============ BG_PlayerTouchesItem Items can be picked up without actually touching their physical bounds to make grabbing them easier ============ */ qboolean BG_PlayerTouchesItem( playerState_t *ps, entityState_t *item, int atTime ) { vec3_t origin; BG_EvaluateTrajectory( &item->pos, atTime, origin ); // we are ignoring ducked differences here if ( ps->origin[0] - origin[0] > 44 || ps->origin[0] - origin[0] < -50 || ps->origin[1] - origin[1] > 36 || ps->origin[1] - origin[1] < -36 || ps->origin[2] - origin[2] > 36 || ps->origin[2] - origin[2] < -36 ) { return qfalse; } return qtrue; } /* ================ BG_CanItemBeGrabbed Returns false if the item should not be picked up. This needs to be the same for client side prediction and server use. ================ */ qboolean BG_CanItemBeGrabbed( int gametype, const entityState_t *ent, const playerState_t *ps ) { gitem_t *item; if ( ent->modelindex < 1 || ent->modelindex >= bg_numItems ) { Com_Error( ERR_DROP, "BG_CanItemBeGrabbed: index out of range" ); } item = &bg_itemlist[ent->modelindex]; switch( item->giType ) { case IT_WEAPON: return qtrue; // weapons are always picked up case IT_AMMO: if ( ps->ammo[ item->giTag ] >= 200 ) { return qfalse; // can't hold any more } return qtrue; case IT_ARMOR: if ( ps->stats[STAT_ARMOR] >= ps->stats[STAT_MAX_HEALTH] * 2 ) { return qfalse; } return qtrue; case IT_HEALTH: // small and mega healths will go over the max, otherwise // don't pick up if already at max if ( item->quantity == 5 || item->quantity == 100 ) { if ( ps->stats[STAT_HEALTH] >= ps->stats[STAT_MAX_HEALTH] * 2 ) { return qfalse; } return qtrue; } if ( ps->stats[STAT_HEALTH] >= ps->stats[STAT_MAX_HEALTH] ) { return qfalse; } return qtrue; case IT_POWERUP: return qtrue; // powerups are always picked up case IT_TEAM: // team items, such as flags if( gametype == GT_CTF ) { // ent->modelindex2 is non-zero on items if they are dropped // we need to know this because we can pick up our dropped flag (and return it) // but we can't pick up our flag at base if (ps->persistant[PERS_TEAM] == TEAM_RED) { if (item->giTag == PW_BLUEFLAG || (item->giTag == PW_REDFLAG && ent->modelindex2) || (item->giTag == PW_REDFLAG && ps->powerups[PW_BLUEFLAG]) ) return qtrue; } else if (ps->persistant[PERS_TEAM] == TEAM_BLUE) { if (item->giTag == PW_REDFLAG || (item->giTag == PW_BLUEFLAG && ent->modelindex2) || (item->giTag == PW_BLUEFLAG && ps->powerups[PW_REDFLAG]) ) return qtrue; } } return qfalse; case IT_HOLDABLE: // can only hold one item at a time if ( ps->stats[STAT_HOLDABLE_ITEM] ) { return qfalse; } return qtrue; case IT_BAD: Com_Error( ERR_DROP, "BG_CanItemBeGrabbed: IT_BAD" ); default: #ifndef Q3_VM #ifndef NDEBUG // bk0001204 Com_Printf("BG_CanItemBeGrabbed: unknown enum %d\n", item->giType ); #endif #endif break; } return qfalse; } //====================================================================== /* ================ BG_EvaluateTrajectory ================ */ void BG_EvaluateTrajectory( const trajectory_t *tr, int atTime, vec3_t result ) { float deltaTime; float phase; switch( tr->trType ) { case TR_STATIONARY: case TR_INTERPOLATE: VectorCopy( tr->trBase, result ); break; case TR_LINEAR: deltaTime = ( atTime - tr->trTime ) * 0.001; // milliseconds to seconds VectorMA( tr->trBase, deltaTime, tr->trDelta, result ); break; case TR_SINE: deltaTime = ( atTime - tr->trTime ) / (float) tr->trDuration; phase = sin( deltaTime * M_PI * 2 ); VectorMA( tr->trBase, phase, tr->trDelta, result ); break; case TR_LINEAR_STOP: if ( atTime > tr->trTime + tr->trDuration ) { atTime = tr->trTime + tr->trDuration; } deltaTime = ( atTime - tr->trTime ) * 0.001; // milliseconds to seconds if ( deltaTime < 0 ) { deltaTime = 0; } VectorMA( tr->trBase, deltaTime, tr->trDelta, result ); break; case TR_GRAVITY: deltaTime = ( atTime - tr->trTime ) * 0.001; // milliseconds to seconds VectorMA( tr->trBase, deltaTime, tr->trDelta, result ); result[2] -= 0.5 * DEFAULT_GRAVITY * deltaTime * deltaTime; // FIXME: local gravity... break; default: Com_Error( ERR_DROP, "BG_EvaluateTrajectory: unknown trType: %i", tr->trTime ); break; } } /* ================ BG_EvaluateTrajectoryDelta For determining velocity at a given time ================ */ void BG_EvaluateTrajectoryDelta( const trajectory_t *tr, int atTime, vec3_t result ) { float deltaTime; float phase; switch( tr->trType ) { case TR_STATIONARY: case TR_INTERPOLATE: VectorClear( result ); break; case TR_LINEAR: VectorCopy( tr->trDelta, result ); break; case TR_SINE: deltaTime = ( atTime - tr->trTime ) / (float) tr->trDuration; phase = cos( deltaTime * M_PI * 2 ); // derivative of sin = cos phase *= 0.5; VectorScale( tr->trDelta, phase, result ); break; case TR_LINEAR_STOP: if ( atTime > tr->trTime + tr->trDuration ) { VectorClear( result ); return; } VectorCopy( tr->trDelta, result ); break; case TR_GRAVITY: deltaTime = ( atTime - tr->trTime ) * 0.001; // milliseconds to seconds VectorCopy( tr->trDelta, result ); result[2] -= DEFAULT_GRAVITY * deltaTime; // FIXME: local gravity... break; default: Com_Error( ERR_DROP, "BG_EvaluateTrajectoryDelta: unknown trType: %i", tr->trTime ); break; } } char *eventnames[] = { "EV_NONE", "EV_FOOTSTEP", "EV_FOOTSTEP_METAL", "EV_FOOTSPLASH", "EV_FOOTWADE", "EV_SWIM", "EV_STEP_4", "EV_STEP_8", "EV_STEP_12", "EV_STEP_16", "EV_FALL_SHORT", "EV_FALL_MEDIUM", "EV_FALL_FAR", "EV_JUMP_PAD", // boing sound at origin", jump sound on player "EV_JUMP", "EV_WATER_TOUCH", // foot touches "EV_WATER_LEAVE", // foot leaves "EV_WATER_UNDER", // head touches "EV_WATER_CLEAR", // head leaves "EV_ITEM_PICKUP", // normal item pickups are predictable "EV_GLOBAL_ITEM_PICKUP", // powerup / team sounds are broadcast to everyone "EV_NOAMMO", "EV_CHANGE_WEAPON", "EV_FIRE_WEAPON", "EV_USE_ITEM0", "EV_USE_ITEM1", "EV_USE_ITEM2", "EV_USE_ITEM3", "EV_USE_ITEM4", "EV_USE_ITEM5", "EV_USE_ITEM6", "EV_USE_ITEM7", "EV_USE_ITEM8", "EV_USE_ITEM9", "EV_USE_ITEM10", "EV_USE_ITEM11", "EV_USE_ITEM12", "EV_USE_ITEM13", "EV_USE_ITEM14", "EV_USE_ITEM15", "EV_ITEM_RESPAWN", "EV_ITEM_POP", "EV_PLAYER_TELEPORT_IN", "EV_PLAYER_TELEPORT_OUT", "EV_GRENADE_BOUNCE", // eventParm will be the soundindex "EV_GENERAL_SOUND", "EV_GLOBAL_SOUND", // no attenuation "EV_GLOBAL_TEAM_SOUND", "EV_BULLET_HIT_FLESH", "EV_BULLET_HIT_WALL", "EV_MISSILE_HIT", "EV_MISSILE_MISS", "EV_MISSILE_MISS_METAL", "EV_RAILTRAIL", "EV_SHOTGUN", "EV_BULLET", // otherEntity is the shooter "EV_PAIN", "EV_DEATH1", "EV_DEATH2", "EV_DEATH3", "EV_OBITUARY", "EV_POWERUP_QUAD", "EV_POWERUP_BATTLESUIT", "EV_POWERUP_REGEN", "EV_GIB_PLAYER", // gib a previously living player "EV_SCOREPLUM", // score plum "EV_DEBUG_LINE", "EV_STOPLOOPINGSOUND", "EV_TAUNT" }; /* =============== BG_AddPredictableEventToPlayerstate Handles the sequence numbers =============== */ void trap_Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize ); void BG_AddPredictableEventToPlayerstate( int newEvent, int eventParm, playerState_t *ps ) { #ifdef _DEBUG { char buf[256]; trap_Cvar_VariableStringBuffer("showevents", buf, sizeof(buf)); if ( atof(buf) != 0 ) { #ifdef QAGAME Com_Printf(" game event svt %5d -> %5d: num = %20s parm %d\n", ps->pmove_framecount/*ps->commandTime*/, ps->eventSequence, eventnames[newEvent], eventParm); #else Com_Printf("Cgame event svt %5d -> %5d: num = %20s parm %d\n", ps->pmove_framecount/*ps->commandTime*/, ps->eventSequence, eventnames[newEvent], eventParm); #endif } } #endif ps->events[ps->eventSequence & (MAX_PS_EVENTS-1)] = newEvent; ps->eventParms[ps->eventSequence & (MAX_PS_EVENTS-1)] = eventParm; ps->eventSequence++; } /* ======================== BG_TouchJumpPad ======================== */ void BG_TouchJumpPad( playerState_t *ps, entityState_t *jumppad ) { vec3_t angles; float p; int effectNum; // spectators don't use jump pads if ( ps->pm_type != PM_NORMAL ) { return; } // flying characters don't hit bounce pads if ( ps->powerups[PW_FLIGHT] ) { return; } // if we didn't hit this same jumppad the previous frame // then don't play the event sound again if we are in a fat trigger if ( ps->jumppad_ent != jumppad->number ) { vectoangles( jumppad->origin2, angles); p = fabs( AngleNormalize180( angles[PITCH] ) ); if( p < 45 ) { effectNum = 0; } else { effectNum = 1; } BG_AddPredictableEventToPlayerstate( EV_JUMP_PAD, effectNum, ps ); } // remember hitting this jumppad this frame ps->jumppad_ent = jumppad->number; ps->jumppad_frame = ps->pmove_framecount; // give the player the velocity from the jumppad VectorCopy( jumppad->origin2, ps->velocity ); } /* ======================== BG_PlayerStateToEntityState This is done after each set of usercmd_t on the server, and after local prediction on the client ======================== */ void BG_PlayerStateToEntityState( playerState_t *ps, entityState_t *s, qboolean snap ) { int i; if ( ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_SPECTATOR ) { s->eType = ET_INVISIBLE; } else if ( ps->stats[STAT_HEALTH] <= GIB_HEALTH ) { s->eType = ET_INVISIBLE; } else { s->eType = ET_PLAYER; } s->number = ps->clientNum; s->pos.trType = TR_INTERPOLATE; VectorCopy( ps->origin, s->pos.trBase ); if ( snap ) { SnapVector( s->pos.trBase ); } // set the trDelta for flag direction VectorCopy( ps->velocity, s->pos.trDelta ); s->apos.trType = TR_INTERPOLATE; VectorCopy( ps->viewangles, s->apos.trBase ); if ( snap ) { SnapVector( s->apos.trBase ); } s->angles2[YAW] = ps->movementDir; s->legsAnim = ps->legsAnim; s->torsoAnim = ps->torsoAnim; s->clientNum = ps->clientNum; // ET_PLAYER looks here instead of at number // so corpses can also reference the proper config s->eFlags = ps->eFlags; if ( ps->stats[STAT_HEALTH] <= 0 ) { s->eFlags |= EF_DEAD; } else { s->eFlags &= ~EF_DEAD; } if ( ps->externalEvent ) { s->event = ps->externalEvent; s->eventParm = ps->externalEventParm; } else if ( ps->entityEventSequence < ps->eventSequence ) { int seq; if ( ps->entityEventSequence < ps->eventSequence - MAX_PS_EVENTS) { ps->entityEventSequence = ps->eventSequence - MAX_PS_EVENTS; } seq = ps->entityEventSequence & (MAX_PS_EVENTS-1); s->event = ps->events[ seq ] | ( ( ps->entityEventSequence & 3 ) << 8 ); s->eventParm = ps->eventParms[ seq ]; ps->entityEventSequence++; } s->weapon = ps->weapon; s->groundEntityNum = ps->groundEntityNum; s->powerups = 0; for ( i = 0 ; i < MAX_POWERUPS ; i++ ) { if ( ps->powerups[ i ] ) { s->powerups |= 1 << i; } } s->loopSound = ps->loopSound; s->generic1 = ps->generic1; } /* ======================== BG_PlayerStateToEntityStateExtraPolate This is done after each set of usercmd_t on the server, and after local prediction on the client ======================== */ void BG_PlayerStateToEntityStateExtraPolate( playerState_t *ps, entityState_t *s, int time, qboolean snap ) { int i; if ( ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_SPECTATOR ) { s->eType = ET_INVISIBLE; } else if ( ps->stats[STAT_HEALTH] <= GIB_HEALTH ) { s->eType = ET_INVISIBLE; } else { s->eType = ET_PLAYER; } s->number = ps->clientNum; s->pos.trType = TR_LINEAR_STOP; VectorCopy( ps->origin, s->pos.trBase ); if ( snap ) { SnapVector( s->pos.trBase ); } // set the trDelta for flag direction and linear prediction VectorCopy( ps->velocity, s->pos.trDelta ); // set the time for linear prediction s->pos.trTime = time; // set maximum extra polation time s->pos.trDuration = 50; // 1000 / sv_fps (default = 20) s->apos.trType = TR_INTERPOLATE; VectorCopy( ps->viewangles, s->apos.trBase ); if ( snap ) { SnapVector( s->apos.trBase ); } s->angles2[YAW] = ps->movementDir; s->legsAnim = ps->legsAnim; s->torsoAnim = ps->torsoAnim; s->clientNum = ps->clientNum; // ET_PLAYER looks here instead of at number // so corpses can also reference the proper config s->eFlags = ps->eFlags; if ( ps->stats[STAT_HEALTH] <= 0 ) { s->eFlags |= EF_DEAD; } else { s->eFlags &= ~EF_DEAD; } if ( ps->externalEvent ) { s->event = ps->externalEvent; s->eventParm = ps->externalEventParm; } else if ( ps->entityEventSequence < ps->eventSequence ) { int seq; if ( ps->entityEventSequence < ps->eventSequence - MAX_PS_EVENTS) { ps->entityEventSequence = ps->eventSequence - MAX_PS_EVENTS; } seq = ps->entityEventSequence & (MAX_PS_EVENTS-1); s->event = ps->events[ seq ] | ( ( ps->entityEventSequence & 3 ) << 8 ); s->eventParm = ps->eventParms[ seq ]; ps->entityEventSequence++; } s->weapon = ps->weapon; s->groundEntityNum = ps->groundEntityNum; s->powerups = 0; for ( i = 0 ; i < MAX_POWERUPS ; i++ ) { if ( ps->powerups[ i ] ) { s->powerups |= 1 << i; } } s->loopSound = ps->loopSound; s->generic1 = ps->generic1; } ================================================ FILE: src/game/bg_pmove.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // bg_pmove.c -- both games player movement code // takes a playerstate and a usercmd as input and returns a modifed playerstate #include "q_shared.h" #include "bg_public.h" #include "bg_local.h" pmove_t *pm; pml_t pml; // movement parameters float pm_stopspeed = 100.0f; float pm_duckScale = 0.25f; float pm_swimScale = 0.50f; float pm_wadeScale = 0.70f; float pm_accelerate = 10.0f; float pm_airaccelerate = 1.0f; float pm_wateraccelerate = 4.0f; float pm_flyaccelerate = 8.0f; float pm_friction = 6.0f; float pm_waterfriction = 1.0f; float pm_flightfriction = 3.0f; float pm_spectatorfriction = 5.0f; int c_pmove = 0; /* =============== PM_AddEvent =============== */ void PM_AddEvent( int newEvent ) { BG_AddPredictableEventToPlayerstate( newEvent, 0, pm->ps ); } /* =============== PM_AddTouchEnt =============== */ void PM_AddTouchEnt( int entityNum ) { int i; if ( entityNum == ENTITYNUM_WORLD ) { return; } if ( pm->numtouch == MAXTOUCH ) { return; } // see if it is already added for ( i = 0 ; i < pm->numtouch ; i++ ) { if ( pm->touchents[ i ] == entityNum ) { return; } } // add it pm->touchents[pm->numtouch] = entityNum; pm->numtouch++; } /* =================== PM_StartTorsoAnim =================== */ static void PM_StartTorsoAnim( int anim ) { if ( pm->ps->pm_type >= PM_DEAD ) { return; } pm->ps->torsoAnim = ( ( pm->ps->torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; } static void PM_StartLegsAnim( int anim ) { if ( pm->ps->pm_type >= PM_DEAD ) { return; } if ( pm->ps->legsTimer > 0 ) { return; // a high priority animation is running } pm->ps->legsAnim = ( ( pm->ps->legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; } static void PM_ContinueLegsAnim( int anim ) { if ( ( pm->ps->legsAnim & ~ANIM_TOGGLEBIT ) == anim ) { return; } if ( pm->ps->legsTimer > 0 ) { return; // a high priority animation is running } PM_StartLegsAnim( anim ); } static void PM_ContinueTorsoAnim( int anim ) { if ( ( pm->ps->torsoAnim & ~ANIM_TOGGLEBIT ) == anim ) { return; } if ( pm->ps->torsoTimer > 0 ) { return; // a high priority animation is running } PM_StartTorsoAnim( anim ); } static void PM_ForceLegsAnim( int anim ) { pm->ps->legsTimer = 0; PM_StartLegsAnim( anim ); } /* ================== PM_ClipVelocity Slide off of the impacting surface ================== */ void PM_ClipVelocity( vec3_t in, vec3_t normal, vec3_t out, float overbounce ) { float backoff; float change; int i; backoff = DotProduct (in, normal); if ( backoff < 0 ) { backoff *= overbounce; } else { backoff /= overbounce; } for ( i=0 ; i<3 ; i++ ) { change = normal[i]*backoff; out[i] = in[i] - change; } } /* ================== PM_Friction Handles both ground friction and water friction ================== */ static void PM_Friction( void ) { vec3_t vec; float *vel; float speed, newspeed, control; float drop; vel = pm->ps->velocity; VectorCopy( vel, vec ); if ( pml.walking ) { vec[2] = 0; // ignore slope movement } speed = VectorLength(vec); if (speed < 1) { vel[0] = 0; vel[1] = 0; // allow sinking underwater // FIXME: still have z friction underwater? return; } drop = 0; // apply ground friction if ( pm->waterlevel <= 1 ) { if ( pml.walking && !(pml.groundTrace.surfaceFlags & SURF_SLICK) ) { // if getting knocked back, no friction if ( ! (pm->ps->pm_flags & PMF_TIME_KNOCKBACK) ) { control = speed < pm_stopspeed ? pm_stopspeed : speed; drop += control*pm_friction*pml.frametime; } } } // apply water friction even if just wading if ( pm->waterlevel ) { drop += speed*pm_waterfriction*pm->waterlevel*pml.frametime; } // apply flying friction if ( pm->ps->powerups[PW_FLIGHT]) { drop += speed*pm_flightfriction*pml.frametime; } if ( pm->ps->pm_type == PM_SPECTATOR) { drop += speed*pm_spectatorfriction*pml.frametime; } // scale the velocity newspeed = speed - drop; if (newspeed < 0) { newspeed = 0; } newspeed /= speed; vel[0] = vel[0] * newspeed; vel[1] = vel[1] * newspeed; vel[2] = vel[2] * newspeed; } /* ============== PM_Accelerate Handles user intended acceleration ============== */ static void PM_Accelerate( vec3_t wishdir, float wishspeed, float accel ) { #if 1 // q2 style int i; float addspeed, accelspeed, currentspeed; currentspeed = DotProduct (pm->ps->velocity, wishdir); addspeed = wishspeed - currentspeed; if (addspeed <= 0) { return; } accelspeed = accel*pml.frametime*wishspeed; if (accelspeed > addspeed) { accelspeed = addspeed; } for (i=0 ; i<3 ; i++) { pm->ps->velocity[i] += accelspeed*wishdir[i]; } #else // proper way (avoids strafe jump maxspeed bug), but feels bad vec3_t wishVelocity; vec3_t pushDir; float pushLen; float canPush; VectorScale( wishdir, wishspeed, wishVelocity ); VectorSubtract( wishVelocity, pm->ps->velocity, pushDir ); pushLen = VectorNormalize( pushDir ); canPush = accel*pml.frametime*wishspeed; if (canPush > pushLen) { canPush = pushLen; } VectorMA( pm->ps->velocity, canPush, pushDir, pm->ps->velocity ); #endif } /* ============ PM_CmdScale Returns the scale factor to apply to cmd movements This allows the clients to use axial -127 to 127 values for all directions without getting a sqrt(2) distortion in speed. ============ */ static float PM_CmdScale( usercmd_t *cmd ) { int max; float total; float scale; max = abs( cmd->forwardmove ); if ( abs( cmd->rightmove ) > max ) { max = abs( cmd->rightmove ); } if ( abs( cmd->upmove ) > max ) { max = abs( cmd->upmove ); } if ( !max ) { return 0; } total = sqrt( cmd->forwardmove * cmd->forwardmove + cmd->rightmove * cmd->rightmove + cmd->upmove * cmd->upmove ); scale = (float)pm->ps->speed * max / ( 127.0 * total ); return scale; } /* ================ PM_SetMovementDir Determine the rotation of the legs reletive to the facing dir ================ */ static void PM_SetMovementDir( void ) { if ( pm->cmd.forwardmove || pm->cmd.rightmove ) { if ( pm->cmd.rightmove == 0 && pm->cmd.forwardmove > 0 ) { pm->ps->movementDir = 0; } else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove > 0 ) { pm->ps->movementDir = 1; } else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove == 0 ) { pm->ps->movementDir = 2; } else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove < 0 ) { pm->ps->movementDir = 3; } else if ( pm->cmd.rightmove == 0 && pm->cmd.forwardmove < 0 ) { pm->ps->movementDir = 4; } else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove < 0 ) { pm->ps->movementDir = 5; } else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove == 0 ) { pm->ps->movementDir = 6; } else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove > 0 ) { pm->ps->movementDir = 7; } } else { // if they aren't actively going directly sideways, // change the animation to the diagonal so they // don't stop too crooked if ( pm->ps->movementDir == 2 ) { pm->ps->movementDir = 1; } else if ( pm->ps->movementDir == 6 ) { pm->ps->movementDir = 7; } } } /* ============= PM_CheckJump ============= */ static qboolean PM_CheckJump( void ) { if ( pm->ps->pm_flags & PMF_RESPAWNED ) { return qfalse; // don't allow jump until all buttons are up } if ( pm->cmd.upmove < 10 ) { // not holding jump return qfalse; } // must wait for jump to be released if ( pm->ps->pm_flags & PMF_JUMP_HELD ) { // clear upmove so cmdscale doesn't lower running speed pm->cmd.upmove = 0; return qfalse; } pml.groundPlane = qfalse; // jumping away pml.walking = qfalse; pm->ps->pm_flags |= PMF_JUMP_HELD; pm->ps->groundEntityNum = ENTITYNUM_NONE; pm->ps->velocity[2] = JUMP_VELOCITY; PM_AddEvent( EV_JUMP ); if ( pm->cmd.forwardmove >= 0 ) { PM_ForceLegsAnim( LEGS_JUMP ); pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP; } else { PM_ForceLegsAnim( LEGS_JUMPB ); pm->ps->pm_flags |= PMF_BACKWARDS_JUMP; } return qtrue; } /* ============= PM_CheckWaterJump ============= */ static qboolean PM_CheckWaterJump( void ) { vec3_t spot; int cont; vec3_t flatforward; if (pm->ps->pm_time) { return qfalse; } // check for water jump if ( pm->waterlevel != 2 ) { return qfalse; } flatforward[0] = pml.forward[0]; flatforward[1] = pml.forward[1]; flatforward[2] = 0; VectorNormalize (flatforward); VectorMA (pm->ps->origin, 30, flatforward, spot); spot[2] += 4; cont = pm->pointcontents (spot, pm->ps->clientNum ); if ( !(cont & CONTENTS_SOLID) ) { return qfalse; } spot[2] += 16; cont = pm->pointcontents (spot, pm->ps->clientNum ); if ( cont ) { return qfalse; } // jump out of water VectorScale (pml.forward, 200, pm->ps->velocity); pm->ps->velocity[2] = 350; pm->ps->pm_flags |= PMF_TIME_WATERJUMP; pm->ps->pm_time = 2000; return qtrue; } //============================================================================ /* =================== PM_WaterJumpMove Flying out of the water =================== */ static void PM_WaterJumpMove( void ) { // waterjump has no control, but falls PM_StepSlideMove( qtrue ); pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime; if (pm->ps->velocity[2] < 0) { // cancel as soon as we are falling down again pm->ps->pm_flags &= ~PMF_ALL_TIMES; pm->ps->pm_time = 0; } } /* =================== PM_WaterMove =================== */ static void PM_WaterMove( void ) { int i; vec3_t wishvel; float wishspeed; vec3_t wishdir; float scale; float vel; if ( PM_CheckWaterJump() ) { PM_WaterJumpMove(); return; } #if 0 // jump = head for surface if ( pm->cmd.upmove >= 10 ) { if (pm->ps->velocity[2] > -300) { if ( pm->watertype == CONTENTS_WATER ) { pm->ps->velocity[2] = 100; } else if (pm->watertype == CONTENTS_SLIME) { pm->ps->velocity[2] = 80; } else { pm->ps->velocity[2] = 50; } } } #endif PM_Friction (); scale = PM_CmdScale( &pm->cmd ); // // user intentions // if ( !scale ) { wishvel[0] = 0; wishvel[1] = 0; wishvel[2] = -60; // sink towards bottom } else { for (i=0 ; i<3 ; i++) wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove; wishvel[2] += scale * pm->cmd.upmove; } VectorCopy (wishvel, wishdir); wishspeed = VectorNormalize(wishdir); if ( wishspeed > pm->ps->speed * pm_swimScale ) { wishspeed = pm->ps->speed * pm_swimScale; } PM_Accelerate (wishdir, wishspeed, pm_wateraccelerate); // make sure we can go up slopes easily under water if ( pml.groundPlane && DotProduct( pm->ps->velocity, pml.groundTrace.plane.normal ) < 0 ) { vel = VectorLength(pm->ps->velocity); // slide along the ground plane PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, pm->ps->velocity, OVERCLIP ); VectorNormalize(pm->ps->velocity); VectorScale(pm->ps->velocity, vel, pm->ps->velocity); } PM_SlideMove( qfalse ); } /* =================== PM_FlyMove Only with the flight powerup =================== */ static void PM_FlyMove( void ) { int i; vec3_t wishvel; float wishspeed; vec3_t wishdir; float scale; // normal slowdown PM_Friction (); scale = PM_CmdScale( &pm->cmd ); // // user intentions // if ( !scale ) { wishvel[0] = 0; wishvel[1] = 0; wishvel[2] = 0; } else { for (i=0 ; i<3 ; i++) { wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove; } wishvel[2] += scale * pm->cmd.upmove; } VectorCopy (wishvel, wishdir); wishspeed = VectorNormalize(wishdir); PM_Accelerate (wishdir, wishspeed, pm_flyaccelerate); PM_StepSlideMove( qfalse ); } /* =================== PM_AirMove =================== */ static void PM_AirMove( void ) { int i; vec3_t wishvel; float fmove, smove; vec3_t wishdir; float wishspeed; float scale; usercmd_t cmd; PM_Friction(); fmove = pm->cmd.forwardmove; smove = pm->cmd.rightmove; cmd = pm->cmd; scale = PM_CmdScale( &cmd ); // set the movementDir so clients can rotate the legs for strafing PM_SetMovementDir(); // project moves down to flat plane pml.forward[2] = 0; pml.right[2] = 0; VectorNormalize (pml.forward); VectorNormalize (pml.right); for ( i = 0 ; i < 2 ; i++ ) { wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove; } wishvel[2] = 0; VectorCopy (wishvel, wishdir); wishspeed = VectorNormalize(wishdir); wishspeed *= scale; // not on ground, so little effect on velocity PM_Accelerate (wishdir, wishspeed, pm_airaccelerate); // we may have a ground plane that is very steep, even // though we don't have a groundentity // slide along the steep plane if ( pml.groundPlane ) { PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, pm->ps->velocity, OVERCLIP ); } #if 0 //ZOID: If we are on the grapple, try stair-stepping //this allows a player to use the grapple to pull himself //over a ledge if (pm->ps->pm_flags & PMF_GRAPPLE_PULL) PM_StepSlideMove ( qtrue ); else PM_SlideMove ( qtrue ); #endif PM_StepSlideMove ( qtrue ); } /* =================== PM_GrappleMove =================== */ static void PM_GrappleMove( void ) { vec3_t vel, v; float vlen; VectorScale(pml.forward, -16, v); VectorAdd(pm->ps->grapplePoint, v, v); VectorSubtract(v, pm->ps->origin, vel); vlen = VectorLength(vel); VectorNormalize( vel ); if (vlen <= 100) VectorScale(vel, 10 * vlen, vel); else VectorScale(vel, 800, vel); VectorCopy(vel, pm->ps->velocity); pml.groundPlane = qfalse; } /* =================== PM_WalkMove =================== */ static void PM_WalkMove( void ) { int i; vec3_t wishvel; float fmove, smove; vec3_t wishdir; float wishspeed; float scale; usercmd_t cmd; float accelerate; float vel; if ( pm->waterlevel > 2 && DotProduct( pml.forward, pml.groundTrace.plane.normal ) > 0 ) { // begin swimming PM_WaterMove(); return; } if ( PM_CheckJump () ) { // jumped away if ( pm->waterlevel > 1 ) { PM_WaterMove(); } else { PM_AirMove(); } return; } PM_Friction (); fmove = pm->cmd.forwardmove; smove = pm->cmd.rightmove; cmd = pm->cmd; scale = PM_CmdScale( &cmd ); // set the movementDir so clients can rotate the legs for strafing PM_SetMovementDir(); // project moves down to flat plane pml.forward[2] = 0; pml.right[2] = 0; // project the forward and right directions onto the ground plane PM_ClipVelocity (pml.forward, pml.groundTrace.plane.normal, pml.forward, OVERCLIP ); PM_ClipVelocity (pml.right, pml.groundTrace.plane.normal, pml.right, OVERCLIP ); // VectorNormalize (pml.forward); VectorNormalize (pml.right); for ( i = 0 ; i < 3 ; i++ ) { wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove; } // when going up or down slopes the wish velocity should Not be zero // wishvel[2] = 0; VectorCopy (wishvel, wishdir); wishspeed = VectorNormalize(wishdir); wishspeed *= scale; // clamp the speed lower if ducking if ( pm->ps->pm_flags & PMF_DUCKED ) { if ( wishspeed > pm->ps->speed * pm_duckScale ) { wishspeed = pm->ps->speed * pm_duckScale; } } // clamp the speed lower if wading or walking on the bottom if ( pm->waterlevel ) { float waterScale; waterScale = pm->waterlevel / 3.0; waterScale = 1.0 - ( 1.0 - pm_swimScale ) * waterScale; if ( wishspeed > pm->ps->speed * waterScale ) { wishspeed = pm->ps->speed * waterScale; } } // when a player gets hit, they temporarily lose // full control, which allows them to be moved a bit if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) { accelerate = pm_airaccelerate; } else { accelerate = pm_accelerate; } PM_Accelerate (wishdir, wishspeed, accelerate); //Com_Printf("velocity = %1.1f %1.1f %1.1f\n", pm->ps->velocity[0], pm->ps->velocity[1], pm->ps->velocity[2]); //Com_Printf("velocity1 = %1.1f\n", VectorLength(pm->ps->velocity)); if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) { pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime; } else { // don't reset the z velocity for slopes // pm->ps->velocity[2] = 0; } vel = VectorLength(pm->ps->velocity); // slide along the ground plane PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, pm->ps->velocity, OVERCLIP ); // don't decrease velocity when going up or down a slope VectorNormalize(pm->ps->velocity); VectorScale(pm->ps->velocity, vel, pm->ps->velocity); // don't do anything if standing still if (!pm->ps->velocity[0] && !pm->ps->velocity[1]) { return; } PM_StepSlideMove( qfalse ); //Com_Printf("velocity2 = %1.1f\n", VectorLength(pm->ps->velocity)); } /* ============== PM_DeadMove ============== */ static void PM_DeadMove( void ) { float forward; if ( !pml.walking ) { return; } // extra friction forward = VectorLength (pm->ps->velocity); forward -= 20; if ( forward <= 0 ) { VectorClear (pm->ps->velocity); } else { VectorNormalize (pm->ps->velocity); VectorScale (pm->ps->velocity, forward, pm->ps->velocity); } } /* =============== PM_NoclipMove =============== */ static void PM_NoclipMove( void ) { float speed, drop, friction, control, newspeed; int i; vec3_t wishvel; float fmove, smove; vec3_t wishdir; float wishspeed; float scale; pm->ps->viewheight = DEFAULT_VIEWHEIGHT; // friction speed = VectorLength (pm->ps->velocity); if (speed < 1) { VectorCopy (vec3_origin, pm->ps->velocity); } else { drop = 0; friction = pm_friction*1.5; // extra friction control = speed < pm_stopspeed ? pm_stopspeed : speed; drop += control*friction*pml.frametime; // scale the velocity newspeed = speed - drop; if (newspeed < 0) newspeed = 0; newspeed /= speed; VectorScale (pm->ps->velocity, newspeed, pm->ps->velocity); } // accelerate scale = PM_CmdScale( &pm->cmd ); fmove = pm->cmd.forwardmove; smove = pm->cmd.rightmove; for (i=0 ; i<3 ; i++) wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove; wishvel[2] += pm->cmd.upmove; VectorCopy (wishvel, wishdir); wishspeed = VectorNormalize(wishdir); wishspeed *= scale; PM_Accelerate( wishdir, wishspeed, pm_accelerate ); // move VectorMA (pm->ps->origin, pml.frametime, pm->ps->velocity, pm->ps->origin); } //============================================================================ /* ================ PM_FootstepForSurface Returns an event number apropriate for the groundsurface ================ */ static int PM_FootstepForSurface( void ) { if ( pml.groundTrace.surfaceFlags & SURF_NOSTEPS ) { return 0; } if ( pml.groundTrace.surfaceFlags & SURF_METALSTEPS ) { return EV_FOOTSTEP_METAL; } return EV_FOOTSTEP; } /* ================= PM_CrashLand Check for hard landings that generate sound events ================= */ static void PM_CrashLand( void ) { float delta; float dist; float vel, acc; float t; float a, b, c, den; // decide which landing animation to use if ( pm->ps->pm_flags & PMF_BACKWARDS_JUMP ) { PM_ForceLegsAnim( LEGS_LANDB ); } else { PM_ForceLegsAnim( LEGS_LAND ); } pm->ps->legsTimer = TIMER_LAND; // calculate the exact velocity on landing dist = pm->ps->origin[2] - pml.previous_origin[2]; vel = pml.previous_velocity[2]; acc = -pm->ps->gravity; a = acc / 2; b = vel; c = -dist; den = b * b - 4 * a * c; if ( den < 0 ) { return; } t = (-b - sqrt( den ) ) / ( 2 * a ); delta = vel + t * acc; delta = delta*delta * 0.0001; // ducking while falling doubles damage if ( pm->ps->pm_flags & PMF_DUCKED ) { delta *= 2; } // never take falling damage if completely underwater if ( pm->waterlevel == 3 ) { return; } // reduce falling damage if there is standing water if ( pm->waterlevel == 2 ) { delta *= 0.25; } if ( pm->waterlevel == 1 ) { delta *= 0.5; } if ( delta < 1 ) { return; } // create a local entity event to play the sound // SURF_NODAMAGE is used for bounce pads where you don't ever // want to take damage or play a crunch sound if ( !(pml.groundTrace.surfaceFlags & SURF_NODAMAGE) ) { if ( delta > 60 ) { PM_AddEvent( EV_FALL_FAR ); } else if ( delta > 40 ) { // this is a pain grunt, so don't play it if dead if ( pm->ps->stats[STAT_HEALTH] > 0 ) { PM_AddEvent( EV_FALL_MEDIUM ); } } else if ( delta > 7 ) { PM_AddEvent( EV_FALL_SHORT ); } else { PM_AddEvent( PM_FootstepForSurface() ); } } // start footstep cycle over pm->ps->bobCycle = 0; } /* ============= PM_CheckStuck ============= */ /* void PM_CheckStuck(void) { trace_t trace; pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask); if (trace.allsolid) { //int shit = qtrue; } } */ /* ============= PM_CorrectAllSolid ============= */ static int PM_CorrectAllSolid( trace_t *trace ) { int i, j, k; vec3_t point; if ( pm->debugLevel ) { Com_Printf("%i:allsolid\n", c_pmove); } // jitter around for (i = -1; i <= 1; i++) { for (j = -1; j <= 1; j++) { for (k = -1; k <= 1; k++) { VectorCopy(pm->ps->origin, point); point[0] += (float) i; point[1] += (float) j; point[2] += (float) k; pm->trace (trace, point, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask); if ( !trace->allsolid ) { point[0] = pm->ps->origin[0]; point[1] = pm->ps->origin[1]; point[2] = pm->ps->origin[2] - 0.25; pm->trace (trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask); pml.groundTrace = *trace; return qtrue; } } } } pm->ps->groundEntityNum = ENTITYNUM_NONE; pml.groundPlane = qfalse; pml.walking = qfalse; return qfalse; } /* ============= PM_GroundTraceMissed The ground trace didn't hit a surface, so we are in freefall ============= */ static void PM_GroundTraceMissed( void ) { trace_t trace; vec3_t point; if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) { // we just transitioned into freefall if ( pm->debugLevel ) { Com_Printf("%i:lift\n", c_pmove); } // if they aren't in a jumping animation and the ground is a ways away, force into it // if we didn't do the trace, the player would be backflipping down staircases VectorCopy( pm->ps->origin, point ); point[2] -= 64; pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask); if ( trace.fraction == 1.0 ) { if ( pm->cmd.forwardmove >= 0 ) { PM_ForceLegsAnim( LEGS_JUMP ); pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP; } else { PM_ForceLegsAnim( LEGS_JUMPB ); pm->ps->pm_flags |= PMF_BACKWARDS_JUMP; } } } pm->ps->groundEntityNum = ENTITYNUM_NONE; pml.groundPlane = qfalse; pml.walking = qfalse; } /* ============= PM_GroundTrace ============= */ static void PM_GroundTrace( void ) { vec3_t point; trace_t trace; point[0] = pm->ps->origin[0]; point[1] = pm->ps->origin[1]; point[2] = pm->ps->origin[2] - 0.25; pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask); pml.groundTrace = trace; // do something corrective if the trace starts in a solid... if ( trace.allsolid ) { if ( !PM_CorrectAllSolid(&trace) ) return; } // if the trace didn't hit anything, we are in free fall if ( trace.fraction == 1.0 ) { PM_GroundTraceMissed(); pml.groundPlane = qfalse; pml.walking = qfalse; return; } // check if getting thrown off the ground if ( pm->ps->velocity[2] > 0 && DotProduct( pm->ps->velocity, trace.plane.normal ) > 10 ) { if ( pm->debugLevel ) { Com_Printf("%i:kickoff\n", c_pmove); } // go into jump animation if ( pm->cmd.forwardmove >= 0 ) { PM_ForceLegsAnim( LEGS_JUMP ); pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP; } else { PM_ForceLegsAnim( LEGS_JUMPB ); pm->ps->pm_flags |= PMF_BACKWARDS_JUMP; } pm->ps->groundEntityNum = ENTITYNUM_NONE; pml.groundPlane = qfalse; pml.walking = qfalse; return; } // slopes that are too steep will not be considered onground if ( trace.plane.normal[2] < MIN_WALK_NORMAL ) { if ( pm->debugLevel ) { Com_Printf("%i:steep\n", c_pmove); } // FIXME: if they can't slide down the slope, let them // walk (sharp crevices) pm->ps->groundEntityNum = ENTITYNUM_NONE; pml.groundPlane = qtrue; pml.walking = qfalse; return; } pml.groundPlane = qtrue; pml.walking = qtrue; // hitting solid ground will end a waterjump if (pm->ps->pm_flags & PMF_TIME_WATERJUMP) { pm->ps->pm_flags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND); pm->ps->pm_time = 0; } if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) { // just hit the ground if ( pm->debugLevel ) { Com_Printf("%i:Land\n", c_pmove); } PM_CrashLand(); // don't do landing time if we were just going down a slope if ( pml.previous_velocity[2] < -200 ) { // don't allow another jump for a little while pm->ps->pm_flags |= PMF_TIME_LAND; pm->ps->pm_time = 250; } } pm->ps->groundEntityNum = trace.entityNum; // don't reset the z velocity for slopes // pm->ps->velocity[2] = 0; PM_AddTouchEnt( trace.entityNum ); } /* ============= PM_SetWaterLevel FIXME: avoid this twice? certainly if not moving ============= */ static void PM_SetWaterLevel( void ) { vec3_t point; int cont; int sample1; int sample2; // // get waterlevel, accounting for ducking // pm->waterlevel = 0; pm->watertype = 0; point[0] = pm->ps->origin[0]; point[1] = pm->ps->origin[1]; point[2] = pm->ps->origin[2] + MINS_Z + 1; cont = pm->pointcontents( point, pm->ps->clientNum ); if ( cont & MASK_WATER ) { sample2 = pm->ps->viewheight - MINS_Z; sample1 = sample2 / 2; pm->watertype = cont; pm->waterlevel = 1; point[2] = pm->ps->origin[2] + MINS_Z + sample1; cont = pm->pointcontents (point, pm->ps->clientNum ); if ( cont & MASK_WATER ) { pm->waterlevel = 2; point[2] = pm->ps->origin[2] + MINS_Z + sample2; cont = pm->pointcontents (point, pm->ps->clientNum ); if ( cont & MASK_WATER ){ pm->waterlevel = 3; } } } } /* ============== PM_CheckDuck Sets mins, maxs, and pm->ps->viewheight ============== */ static void PM_CheckDuck (void) { trace_t trace; if ( pm->ps->powerups[PW_INVULNERABILITY] ) { if ( pm->ps->pm_flags & PMF_INVULEXPAND ) { // invulnerability sphere has a 42 units radius VectorSet( pm->mins, -42, -42, -42 ); VectorSet( pm->maxs, 42, 42, 42 ); } else { VectorSet( pm->mins, -15, -15, MINS_Z ); VectorSet( pm->maxs, 15, 15, 16 ); } pm->ps->pm_flags |= PMF_DUCKED; pm->ps->viewheight = CROUCH_VIEWHEIGHT; return; } pm->ps->pm_flags &= ~PMF_INVULEXPAND; pm->mins[0] = -15; pm->mins[1] = -15; pm->maxs[0] = 15; pm->maxs[1] = 15; pm->mins[2] = MINS_Z; if (pm->ps->pm_type == PM_DEAD) { pm->maxs[2] = -8; pm->ps->viewheight = DEAD_VIEWHEIGHT; return; } if (pm->cmd.upmove < 0) { // duck pm->ps->pm_flags |= PMF_DUCKED; } else { // stand up if possible if (pm->ps->pm_flags & PMF_DUCKED) { // try to stand up pm->maxs[2] = 32; pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask ); if (!trace.allsolid) pm->ps->pm_flags &= ~PMF_DUCKED; } } if (pm->ps->pm_flags & PMF_DUCKED) { pm->maxs[2] = 16; pm->ps->viewheight = CROUCH_VIEWHEIGHT; } else { pm->maxs[2] = 32; pm->ps->viewheight = DEFAULT_VIEWHEIGHT; } } //=================================================================== /* =============== PM_Footsteps =============== */ static void PM_Footsteps( void ) { float bobmove; int old; qboolean footstep; // // calculate speed and cycle to be used for // all cyclic walking effects // pm->xyspeed = sqrt( pm->ps->velocity[0] * pm->ps->velocity[0] + pm->ps->velocity[1] * pm->ps->velocity[1] ); if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) { if ( pm->ps->powerups[PW_INVULNERABILITY] ) { PM_ContinueLegsAnim( LEGS_IDLECR ); } // airborne leaves position in cycle intact, but doesn't advance if ( pm->waterlevel > 1 ) { PM_ContinueLegsAnim( LEGS_SWIM ); } return; } // if not trying to move if ( !pm->cmd.forwardmove && !pm->cmd.rightmove ) { if ( pm->xyspeed < 5 ) { pm->ps->bobCycle = 0; // start at beginning of cycle again if ( pm->ps->pm_flags & PMF_DUCKED ) { PM_ContinueLegsAnim( LEGS_IDLECR ); } else { PM_ContinueLegsAnim( LEGS_IDLE ); } } return; } footstep = qfalse; if ( pm->ps->pm_flags & PMF_DUCKED ) { bobmove = 0.5; // ducked characters bob much faster if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) { PM_ContinueLegsAnim( LEGS_BACKCR ); } else { PM_ContinueLegsAnim( LEGS_WALKCR ); } // ducked characters never play footsteps /* } else if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) { if ( !( pm->cmd.buttons & BUTTON_WALKING ) ) { bobmove = 0.4; // faster speeds bob faster footstep = qtrue; } else { bobmove = 0.3; } PM_ContinueLegsAnim( LEGS_BACK ); */ } else { if ( !( pm->cmd.buttons & BUTTON_WALKING ) ) { bobmove = 0.4f; // faster speeds bob faster if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) { PM_ContinueLegsAnim( LEGS_BACK ); } else { PM_ContinueLegsAnim( LEGS_RUN ); } footstep = qtrue; } else { bobmove = 0.3f; // walking bobs slow if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) { PM_ContinueLegsAnim( LEGS_BACKWALK ); } else { PM_ContinueLegsAnim( LEGS_WALK ); } } } // check for footstep / splash sounds old = pm->ps->bobCycle; pm->ps->bobCycle = (int)( old + bobmove * pml.msec ) & 255; // if we just crossed a cycle boundary, play an apropriate footstep event if ( ( ( old + 64 ) ^ ( pm->ps->bobCycle + 64 ) ) & 128 ) { if ( pm->waterlevel == 0 ) { // on ground will only play sounds if running if ( footstep && !pm->noFootsteps ) { PM_AddEvent( PM_FootstepForSurface() ); } } else if ( pm->waterlevel == 1 ) { // splashing PM_AddEvent( EV_FOOTSPLASH ); } else if ( pm->waterlevel == 2 ) { // wading / swimming at surface PM_AddEvent( EV_SWIM ); } else if ( pm->waterlevel == 3 ) { // no sound when completely underwater } } } /* ============== PM_WaterEvents Generate sound events for entering and leaving water ============== */ static void PM_WaterEvents( void ) { // FIXME? // // if just entered a water volume, play a sound // if (!pml.previous_waterlevel && pm->waterlevel) { PM_AddEvent( EV_WATER_TOUCH ); } // // if just completely exited a water volume, play a sound // if (pml.previous_waterlevel && !pm->waterlevel) { PM_AddEvent( EV_WATER_LEAVE ); } // // check for head just going under water // if (pml.previous_waterlevel != 3 && pm->waterlevel == 3) { PM_AddEvent( EV_WATER_UNDER ); } // // check for head just coming out of water // if (pml.previous_waterlevel == 3 && pm->waterlevel != 3) { PM_AddEvent( EV_WATER_CLEAR ); } } /* =============== PM_BeginWeaponChange =============== */ static void PM_BeginWeaponChange( int weapon ) { if ( weapon <= WP_NONE || weapon >= WP_NUM_WEAPONS ) { return; } if ( !( pm->ps->stats[STAT_WEAPONS] & ( 1 << weapon ) ) ) { return; } if ( pm->ps->weaponstate == WEAPON_DROPPING ) { return; } PM_AddEvent( EV_CHANGE_WEAPON ); pm->ps->weaponstate = WEAPON_DROPPING; pm->ps->weaponTime += 200; PM_StartTorsoAnim( TORSO_DROP ); } /* =============== PM_FinishWeaponChange =============== */ static void PM_FinishWeaponChange( void ) { int weapon; weapon = pm->cmd.weapon; if ( weapon < WP_NONE || weapon >= WP_NUM_WEAPONS ) { weapon = WP_NONE; } if ( !( pm->ps->stats[STAT_WEAPONS] & ( 1 << weapon ) ) ) { weapon = WP_NONE; } pm->ps->weapon = weapon; pm->ps->weaponstate = WEAPON_RAISING; pm->ps->weaponTime += 250; PM_StartTorsoAnim( TORSO_RAISE ); } /* ============== PM_TorsoAnimation ============== */ static void PM_TorsoAnimation( void ) { if ( pm->ps->weaponstate == WEAPON_READY ) { if ( pm->ps->weapon == WP_GAUNTLET ) { PM_ContinueTorsoAnim( TORSO_STAND2 ); } else { PM_ContinueTorsoAnim( TORSO_STAND ); } return; } } /* ============== PM_Weapon Generates weapon events and modifes the weapon counter ============== */ static void PM_Weapon( void ) { int addTime; // don't allow attack until all buttons are up if ( pm->ps->pm_flags & PMF_RESPAWNED ) { return; } // ignore if spectator if ( pm->ps->persistant[PERS_TEAM] == TEAM_SPECTATOR ) { return; } // check for dead player if ( pm->ps->stats[STAT_HEALTH] <= 0 ) { pm->ps->weapon = WP_NONE; return; } // check for item using if ( pm->cmd.buttons & BUTTON_USE_HOLDABLE ) { if ( ! ( pm->ps->pm_flags & PMF_USE_ITEM_HELD ) ) { if ( bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag == HI_MEDKIT && pm->ps->stats[STAT_HEALTH] >= (pm->ps->stats[STAT_MAX_HEALTH] + 25) ) { // don't use medkit if at max health } else { pm->ps->pm_flags |= PMF_USE_ITEM_HELD; PM_AddEvent( EV_USE_ITEM0 + bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag ); pm->ps->stats[STAT_HOLDABLE_ITEM] = 0; } return; } } else { pm->ps->pm_flags &= ~PMF_USE_ITEM_HELD; } // make weapon function if ( pm->ps->weaponTime > 0 ) { pm->ps->weaponTime -= pml.msec; } // check for weapon change // can't change if weapon is firing, but can change // again if lowering or raising if ( pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING ) { if ( pm->ps->weapon != pm->cmd.weapon ) { PM_BeginWeaponChange( pm->cmd.weapon ); } } if ( pm->ps->weaponTime > 0 ) { return; } // change weapon if time if ( pm->ps->weaponstate == WEAPON_DROPPING ) { PM_FinishWeaponChange(); return; } if ( pm->ps->weaponstate == WEAPON_RAISING ) { pm->ps->weaponstate = WEAPON_READY; if ( pm->ps->weapon == WP_GAUNTLET ) { PM_StartTorsoAnim( TORSO_STAND2 ); } else { PM_StartTorsoAnim( TORSO_STAND ); } return; } // check for fire if ( ! (pm->cmd.buttons & BUTTON_ATTACK) ) { pm->ps->weaponTime = 0; pm->ps->weaponstate = WEAPON_READY; return; } // start the animation even if out of ammo if ( pm->ps->weapon == WP_GAUNTLET ) { // the guantlet only "fires" when it actually hits something if ( !pm->gauntletHit ) { pm->ps->weaponTime = 0; pm->ps->weaponstate = WEAPON_READY; return; } PM_StartTorsoAnim( TORSO_ATTACK2 ); } else { PM_StartTorsoAnim( TORSO_ATTACK ); } pm->ps->weaponstate = WEAPON_FIRING; // check for out of ammo if ( ! pm->ps->ammo[ pm->ps->weapon ] ) { PM_AddEvent( EV_NOAMMO ); pm->ps->weaponTime += 500; return; } // take an ammo away if not infinite if ( pm->ps->ammo[ pm->ps->weapon ] != -1 ) { pm->ps->ammo[ pm->ps->weapon ]--; } // fire weapon PM_AddEvent( EV_FIRE_WEAPON ); switch( pm->ps->weapon ) { default: case WP_GAUNTLET: addTime = 400; break; case WP_LIGHTNING: addTime = 50; break; case WP_SHOTGUN: addTime = 1000; break; case WP_MACHINEGUN: addTime = 100; break; case WP_GRENADE_LAUNCHER: addTime = 800; break; case WP_ROCKET_LAUNCHER: addTime = 800; break; case WP_PLASMAGUN: addTime = 100; break; case WP_RAILGUN: addTime = 1500; break; case WP_BFG: addTime = 200; break; case WP_GRAPPLING_HOOK: addTime = 400; break; } if ( pm->ps->powerups[PW_HASTE] ) { addTime /= 1.3; } pm->ps->weaponTime += addTime; } /* ================ PM_Animate ================ */ static void PM_Animate( void ) { if ( pm->cmd.buttons & BUTTON_GESTURE ) { if ( pm->ps->torsoTimer == 0 ) { PM_StartTorsoAnim( TORSO_GESTURE ); pm->ps->torsoTimer = TIMER_GESTURE; PM_AddEvent( EV_TAUNT ); } } } /* ================ PM_DropTimers ================ */ static void PM_DropTimers( void ) { // drop misc timing counter if ( pm->ps->pm_time ) { if ( pml.msec >= pm->ps->pm_time ) { pm->ps->pm_flags &= ~PMF_ALL_TIMES; pm->ps->pm_time = 0; } else { pm->ps->pm_time -= pml.msec; } } // drop animation counter if ( pm->ps->legsTimer > 0 ) { pm->ps->legsTimer -= pml.msec; if ( pm->ps->legsTimer < 0 ) { pm->ps->legsTimer = 0; } } if ( pm->ps->torsoTimer > 0 ) { pm->ps->torsoTimer -= pml.msec; if ( pm->ps->torsoTimer < 0 ) { pm->ps->torsoTimer = 0; } } } /* ================ PM_UpdateViewAngles This can be used as another entry point when only the viewangles are being updated isntead of a full move ================ */ void PM_UpdateViewAngles( playerState_t *ps, const usercmd_t *cmd ) { short temp; int i; if ( ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_SPINTERMISSION) { return; // no view changes at all } if ( ps->pm_type != PM_SPECTATOR && ps->stats[STAT_HEALTH] <= 0 ) { return; // no view changes at all } // circularly clamp the angles with deltas for (i=0 ; i<3 ; i++) { temp = cmd->angles[i] + ps->delta_angles[i]; if ( i == PITCH ) { // don't let the player look up or down more than 90 degrees if ( temp > 16000 ) { ps->delta_angles[i] = 16000 - cmd->angles[i]; temp = 16000; } else if ( temp < -16000 ) { ps->delta_angles[i] = -16000 - cmd->angles[i]; temp = -16000; } } ps->viewangles[i] = SHORT2ANGLE(temp); } } /* ================ PmoveSingle ================ */ void trap_SnapVector( float *v ); void PmoveSingle (pmove_t *pmove) { pm = pmove; // this counter lets us debug movement problems with a journal // by setting a conditional breakpoint fot the previous frame c_pmove++; // clear results pm->numtouch = 0; pm->watertype = 0; pm->waterlevel = 0; if ( pm->ps->stats[STAT_HEALTH] <= 0 ) { pm->tracemask &= ~CONTENTS_BODY; // corpses can fly through bodies } // make sure walking button is clear if they are running, to avoid // proxy no-footsteps cheats if ( abs( pm->cmd.forwardmove ) > 64 || abs( pm->cmd.rightmove ) > 64 ) { pm->cmd.buttons &= ~BUTTON_WALKING; } // set the talk balloon flag if ( pm->cmd.buttons & BUTTON_TALK ) { pm->ps->eFlags |= EF_TALK; } else { pm->ps->eFlags &= ~EF_TALK; } // set the firing flag for continuous beam weapons if ( !(pm->ps->pm_flags & PMF_RESPAWNED) && pm->ps->pm_type != PM_INTERMISSION && ( pm->cmd.buttons & BUTTON_ATTACK ) && pm->ps->ammo[ pm->ps->weapon ] ) { pm->ps->eFlags |= EF_FIRING; } else { pm->ps->eFlags &= ~EF_FIRING; } // clear the respawned flag if attack and use are cleared if ( pm->ps->stats[STAT_HEALTH] > 0 && !( pm->cmd.buttons & (BUTTON_ATTACK | BUTTON_USE_HOLDABLE) ) ) { pm->ps->pm_flags &= ~PMF_RESPAWNED; } // if talk button is down, dissallow all other input // this is to prevent any possible intercept proxy from // adding fake talk balloons if ( pmove->cmd.buttons & BUTTON_TALK ) { // keep the talk button set tho for when the cmd.serverTime > 66 msec // and the same cmd is used multiple times in Pmove pmove->cmd.buttons = BUTTON_TALK; pmove->cmd.forwardmove = 0; pmove->cmd.rightmove = 0; pmove->cmd.upmove = 0; } // clear all pmove local vars memset (&pml, 0, sizeof(pml)); // determine the time pml.msec = pmove->cmd.serverTime - pm->ps->commandTime; if ( pml.msec < 1 ) { pml.msec = 1; } else if ( pml.msec > 200 ) { pml.msec = 200; } pm->ps->commandTime = pmove->cmd.serverTime; // save old org in case we get stuck VectorCopy (pm->ps->origin, pml.previous_origin); // save old velocity for crashlanding VectorCopy (pm->ps->velocity, pml.previous_velocity); pml.frametime = pml.msec * 0.001; // update the viewangles PM_UpdateViewAngles( pm->ps, &pm->cmd ); AngleVectors (pm->ps->viewangles, pml.forward, pml.right, pml.up); if ( pm->cmd.upmove < 10 ) { // not holding jump pm->ps->pm_flags &= ~PMF_JUMP_HELD; } // decide if backpedaling animations should be used if ( pm->cmd.forwardmove < 0 ) { pm->ps->pm_flags |= PMF_BACKWARDS_RUN; } else if ( pm->cmd.forwardmove > 0 || ( pm->cmd.forwardmove == 0 && pm->cmd.rightmove ) ) { pm->ps->pm_flags &= ~PMF_BACKWARDS_RUN; } if ( pm->ps->pm_type >= PM_DEAD ) { pm->cmd.forwardmove = 0; pm->cmd.rightmove = 0; pm->cmd.upmove = 0; } if ( pm->ps->pm_type == PM_SPECTATOR ) { PM_CheckDuck (); PM_FlyMove (); PM_DropTimers (); return; } if ( pm->ps->pm_type == PM_NOCLIP ) { PM_NoclipMove (); PM_DropTimers (); return; } if (pm->ps->pm_type == PM_FREEZE) { return; // no movement at all } if ( pm->ps->pm_type == PM_INTERMISSION || pm->ps->pm_type == PM_SPINTERMISSION) { return; // no movement at all } // set watertype, and waterlevel PM_SetWaterLevel(); pml.previous_waterlevel = pmove->waterlevel; // set mins, maxs, and viewheight PM_CheckDuck (); // set groundentity PM_GroundTrace(); if ( pm->ps->pm_type == PM_DEAD ) { PM_DeadMove (); } PM_DropTimers(); if ( pm->ps->powerups[PW_FLIGHT] ) { // flight powerup doesn't allow jump and has different friction PM_FlyMove(); } else if (pm->ps->pm_flags & PMF_GRAPPLE_PULL) { PM_GrappleMove(); // We can wiggle a bit PM_AirMove(); } else if (pm->ps->pm_flags & PMF_TIME_WATERJUMP) { PM_WaterJumpMove(); } else if ( pm->waterlevel > 1 ) { // swimming PM_WaterMove(); } else if ( pml.walking ) { // walking on ground PM_WalkMove(); } else { // airborne PM_AirMove(); } PM_Animate(); // set groundentity, watertype, and waterlevel PM_GroundTrace(); PM_SetWaterLevel(); // weapons PM_Weapon(); // torso animation PM_TorsoAnimation(); // footstep events / legs animations PM_Footsteps(); // entering / leaving water splashes PM_WaterEvents(); // snap some parts of playerstate to save network bandwidth trap_SnapVector( pm->ps->velocity ); } /* ================ Pmove Can be called by either the server or the client ================ */ void Pmove (pmove_t *pmove) { int finalTime; finalTime = pmove->cmd.serverTime; if ( finalTime < pmove->ps->commandTime ) { return; // should not happen } if ( finalTime > pmove->ps->commandTime + 1000 ) { pmove->ps->commandTime = finalTime - 1000; } pmove->ps->pmove_framecount = (pmove->ps->pmove_framecount+1) & ((1<ps->commandTime != finalTime ) { int msec; msec = finalTime - pmove->ps->commandTime; if ( pmove->pmove_fixed ) { if ( msec > pmove->pmove_msec ) { msec = pmove->pmove_msec; } } else { if ( msec > 66 ) { msec = 66; } } pmove->cmd.serverTime = pmove->ps->commandTime + msec; PmoveSingle( pmove ); if ( pmove->ps->pm_flags & PMF_JUMP_HELD ) { pmove->cmd.upmove = 20; } } //PM_CheckStuck(); } ================================================ FILE: src/game/bg_public.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // bg_public.h -- definitions shared by both the server game and client game modules // because games can change separately from the main system version, we need a // second version that must match between game and cgame #define GAME_VERSION "baseq3-1" #define DEFAULT_GRAVITY 800 #define GIB_HEALTH -40 #define ARMOR_PROTECTION 0.66 #define MAX_ITEMS 256 #define RANK_TIED_FLAG 0x4000 #define DEFAULT_SHOTGUN_SPREAD 700 #define DEFAULT_SHOTGUN_COUNT 11 #define ITEM_RADIUS 15 // item sizes are needed for client side pickup detection #define LIGHTNING_RANGE 768 #define SCORE_NOT_PRESENT -9999 // for the CS_SCORES[12] when only one player is present #define VOTE_TIME 30000 // 30 seconds before vote times out #define MINS_Z -24 #define DEFAULT_VIEWHEIGHT 26 #define CROUCH_VIEWHEIGHT 12 #define DEAD_VIEWHEIGHT -16 // // config strings are a general means of communicating variable length strings // from the server to all connected clients. // // CS_SERVERINFO and CS_SYSTEMINFO are defined in q_shared.h #define CS_MUSIC 2 #define CS_MESSAGE 3 // from the map worldspawn's message field #define CS_MOTD 4 // g_motd string for server message of the day #define CS_WARMUP 5 // server time when the match will be restarted #define CS_SCORES1 6 #define CS_SCORES2 7 #define CS_VOTE_TIME 8 #define CS_VOTE_STRING 9 #define CS_VOTE_YES 10 #define CS_VOTE_NO 11 #define CS_TEAMVOTE_TIME 12 #define CS_TEAMVOTE_STRING 14 #define CS_TEAMVOTE_YES 16 #define CS_TEAMVOTE_NO 18 #define CS_GAME_VERSION 20 #define CS_LEVEL_START_TIME 21 // so the timer only shows the current level #define CS_INTERMISSION 22 // when 1, fraglimit/timelimit has been hit and intermission will start in a second or two #define CS_FLAGSTATUS 23 // string indicating flag status in CTF #define CS_SHADERSTATE 24 #define CS_BOTINFO 25 #define CS_ITEMS 27 // string of 0's and 1's that tell which items are present #define CS_MODELS 32 #define CS_SOUNDS (CS_MODELS+MAX_MODELS) #define CS_PLAYERS (CS_SOUNDS+MAX_SOUNDS) #define CS_LOCATIONS (CS_PLAYERS+MAX_CLIENTS) #define CS_PARTICLES (CS_LOCATIONS+MAX_LOCATIONS) #define CS_MAX (CS_PARTICLES+MAX_LOCATIONS) #if (CS_MAX) > MAX_CONFIGSTRINGS #error overflow: (CS_MAX) > MAX_CONFIGSTRINGS #endif typedef enum { GT_FFA, // free for all GT_TOURNAMENT, // one on one tournament GT_SINGLE_PLAYER, // single player ffa //-- team games go after this -- GT_TEAM, // team deathmatch GT_CTF, // capture the flag GT_1FCTF, GT_OBELISK, GT_HARVESTER, GT_MAX_GAME_TYPE } gametype_t; typedef enum { GENDER_MALE, GENDER_FEMALE, GENDER_NEUTER } gender_t; /* =================================================================================== PMOVE MODULE The pmove code takes a player_state_t and a usercmd_t and generates a new player_state_t and some other output data. Used for local prediction on the client game and true movement on the server game. =================================================================================== */ typedef enum { PM_NORMAL, // can accelerate and turn PM_NOCLIP, // noclip movement PM_SPECTATOR, // still run into walls PM_DEAD, // no acceleration or turning, but free falling PM_FREEZE, // stuck in place with no control PM_INTERMISSION, // no movement or status bar PM_SPINTERMISSION // no movement or status bar } pmtype_t; typedef enum { WEAPON_READY, WEAPON_RAISING, WEAPON_DROPPING, WEAPON_FIRING } weaponstate_t; // pmove->pm_flags #define PMF_DUCKED 1 #define PMF_JUMP_HELD 2 #define PMF_BACKWARDS_JUMP 8 // go into backwards land #define PMF_BACKWARDS_RUN 16 // coast down to backwards run #define PMF_TIME_LAND 32 // pm_time is time before rejump #define PMF_TIME_KNOCKBACK 64 // pm_time is an air-accelerate only time #define PMF_TIME_WATERJUMP 256 // pm_time is waterjump #define PMF_RESPAWNED 512 // clear after attack and jump buttons come up #define PMF_USE_ITEM_HELD 1024 #define PMF_GRAPPLE_PULL 2048 // pull towards grapple location #define PMF_FOLLOW 4096 // spectate following another player #define PMF_SCOREBOARD 8192 // spectate as a scoreboard #define PMF_INVULEXPAND 16384 // invulnerability sphere set to full size #define PMF_ALL_TIMES (PMF_TIME_WATERJUMP|PMF_TIME_LAND|PMF_TIME_KNOCKBACK) #define MAXTOUCH 32 typedef struct { // state (in / out) playerState_t *ps; // command (in) usercmd_t cmd; int tracemask; // collide against these types of surfaces int debugLevel; // if set, diagnostic output will be printed qboolean noFootsteps; // if the game is setup for no footsteps by the server qboolean gauntletHit; // true if a gauntlet attack would actually hit something int framecount; // results (out) int numtouch; int touchents[MAXTOUCH]; vec3_t mins, maxs; // bounding box size int watertype; int waterlevel; float xyspeed; // for fixed msec Pmove int pmove_fixed; int pmove_msec; // callbacks to test the world // these will be different functions during game and cgame void (*trace)( trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int passEntityNum, int contentMask ); int (*pointcontents)( const vec3_t point, int passEntityNum ); } pmove_t; // if a full pmove isn't done on the client, you can just update the angles void PM_UpdateViewAngles( playerState_t *ps, const usercmd_t *cmd ); void Pmove (pmove_t *pmove); //=================================================================================== // player_state->stats[] indexes // NOTE: may not have more than 16 typedef enum { STAT_HEALTH, STAT_HOLDABLE_ITEM, STAT_WEAPONS, // 16 bit fields STAT_ARMOR, STAT_DEAD_YAW, // look this direction when dead (FIXME: get rid of?) STAT_CLIENTS_READY, // bit mask of clients wishing to exit the intermission (FIXME: configstring?) STAT_MAX_HEALTH // health / armor limit, changable by handicap } statIndex_t; // player_state->persistant[] indexes // these fields are the only part of player_state that isn't // cleared on respawn // NOTE: may not have more than 16 typedef enum { PERS_SCORE, // !!! MUST NOT CHANGE, SERVER AND GAME BOTH REFERENCE !!! PERS_HITS, // total points damage inflicted so damage beeps can sound on change PERS_RANK, // player rank or team rank PERS_TEAM, // player team PERS_SPAWN_COUNT, // incremented every respawn PERS_PLAYEREVENTS, // 16 bits that can be flipped for events PERS_ATTACKER, // clientnum of last damage inflicter PERS_ATTACKEE_ARMOR, // health/armor of last person we attacked PERS_KILLED, // count of the number of times you died // player awards tracking PERS_IMPRESSIVE_COUNT, // two railgun hits in a row PERS_EXCELLENT_COUNT, // two successive kills in a short amount of time PERS_DEFEND_COUNT, // defend awards PERS_ASSIST_COUNT, // assist awards PERS_GAUNTLET_FRAG_COUNT, // kills with the guantlet PERS_CAPTURES // captures } persEnum_t; // entityState_t->eFlags #define EF_DEAD 0x00000001 // don't draw a foe marker over players with EF_DEAD #define EF_TELEPORT_BIT 0x00000004 // toggled every time the origin abruptly changes #define EF_AWARD_EXCELLENT 0x00000008 // draw an excellent sprite #define EF_PLAYER_EVENT 0x00000010 #define EF_BOUNCE 0x00000010 // for missiles #define EF_BOUNCE_HALF 0x00000020 // for missiles #define EF_AWARD_GAUNTLET 0x00000040 // draw a gauntlet sprite #define EF_NODRAW 0x00000080 // may have an event, but no model (unspawned items) #define EF_FIRING 0x00000100 // for lightning gun #define EF_KAMIKAZE 0x00000200 #define EF_MOVER_STOP 0x00000400 // will push otherwise #define EF_AWARD_CAP 0x00000800 // draw the capture sprite #define EF_TALK 0x00001000 // draw a talk balloon #define EF_CONNECTION 0x00002000 // draw a connection trouble sprite #define EF_VOTED 0x00004000 // already cast a vote #define EF_AWARD_IMPRESSIVE 0x00008000 // draw an impressive sprite #define EF_AWARD_DEFEND 0x00010000 // draw a defend sprite #define EF_AWARD_ASSIST 0x00020000 // draw a assist sprite #define EF_AWARD_DENIED 0x00040000 // denied #define EF_TEAMVOTED 0x00080000 // already cast a team vote // NOTE: may not have more than 16 typedef enum { PW_NONE, PW_QUAD, PW_BATTLESUIT, PW_HASTE, PW_INVIS, PW_REGEN, PW_FLIGHT, PW_REDFLAG, PW_BLUEFLAG, PW_NEUTRALFLAG, PW_SCOUT, PW_GUARD, PW_DOUBLER, PW_AMMOREGEN, PW_INVULNERABILITY, PW_NUM_POWERUPS } powerup_t; typedef enum { HI_NONE, HI_TELEPORTER, HI_MEDKIT, HI_KAMIKAZE, HI_PORTAL, HI_INVULNERABILITY, HI_NUM_HOLDABLE } holdable_t; typedef enum { WP_NONE, WP_GAUNTLET, WP_MACHINEGUN, WP_SHOTGUN, WP_GRENADE_LAUNCHER, WP_ROCKET_LAUNCHER, WP_LIGHTNING, WP_RAILGUN, WP_PLASMAGUN, WP_BFG, WP_GRAPPLING_HOOK, WP_NUM_WEAPONS } weapon_t; // reward sounds (stored in ps->persistant[PERS_PLAYEREVENTS]) #define PLAYEREVENT_DENIEDREWARD 0x0001 #define PLAYEREVENT_GAUNTLETREWARD 0x0002 #define PLAYEREVENT_HOLYSHIT 0x0004 // entityState_t->event values // entity events are for effects that take place reletive // to an existing entities origin. Very network efficient. // two bits at the top of the entityState->event field // will be incremented with each change in the event so // that an identical event started twice in a row can // be distinguished. And off the value with ~EV_EVENT_BITS // to retrieve the actual event number #define EV_EVENT_BIT1 0x00000100 #define EV_EVENT_BIT2 0x00000200 #define EV_EVENT_BITS (EV_EVENT_BIT1|EV_EVENT_BIT2) #define EVENT_VALID_MSEC 300 typedef enum { EV_NONE, EV_FOOTSTEP, EV_FOOTSTEP_METAL, EV_FOOTSPLASH, EV_FOOTWADE, EV_SWIM, EV_STEP_4, EV_STEP_8, EV_STEP_12, EV_STEP_16, EV_FALL_SHORT, EV_FALL_MEDIUM, EV_FALL_FAR, EV_JUMP_PAD, // boing sound at origin, jump sound on player EV_JUMP, EV_WATER_TOUCH, // foot touches EV_WATER_LEAVE, // foot leaves EV_WATER_UNDER, // head touches EV_WATER_CLEAR, // head leaves EV_ITEM_PICKUP, // normal item pickups are predictable EV_GLOBAL_ITEM_PICKUP, // powerup / team sounds are broadcast to everyone EV_NOAMMO, EV_CHANGE_WEAPON, EV_FIRE_WEAPON, EV_USE_ITEM0, EV_USE_ITEM1, EV_USE_ITEM2, EV_USE_ITEM3, EV_USE_ITEM4, EV_USE_ITEM5, EV_USE_ITEM6, EV_USE_ITEM7, EV_USE_ITEM8, EV_USE_ITEM9, EV_USE_ITEM10, EV_USE_ITEM11, EV_USE_ITEM12, EV_USE_ITEM13, EV_USE_ITEM14, EV_USE_ITEM15, EV_ITEM_RESPAWN, EV_ITEM_POP, EV_PLAYER_TELEPORT_IN, EV_PLAYER_TELEPORT_OUT, EV_GRENADE_BOUNCE, // eventParm will be the soundindex EV_GENERAL_SOUND, EV_GLOBAL_SOUND, // no attenuation EV_GLOBAL_TEAM_SOUND, EV_BULLET_HIT_FLESH, EV_BULLET_HIT_WALL, EV_MISSILE_HIT, EV_MISSILE_MISS, EV_MISSILE_MISS_METAL, EV_RAILTRAIL, EV_SHOTGUN, EV_BULLET, // otherEntity is the shooter EV_PAIN, EV_DEATH1, EV_DEATH2, EV_DEATH3, EV_OBITUARY, EV_POWERUP_QUAD, EV_POWERUP_BATTLESUIT, EV_POWERUP_REGEN, EV_GIB_PLAYER, // gib a previously living player EV_SCOREPLUM, // score plum EV_DEBUG_LINE, EV_STOPLOOPINGSOUND, EV_TAUNT, EV_TAUNT_YES, EV_TAUNT_NO, EV_TAUNT_FOLLOWME, EV_TAUNT_GETFLAG, EV_TAUNT_GUARDBASE, EV_TAUNT_PATROL } entity_event_t; typedef enum { GTS_RED_CAPTURE, GTS_BLUE_CAPTURE, GTS_RED_RETURN, GTS_BLUE_RETURN, GTS_RED_TAKEN, GTS_BLUE_TAKEN, GTS_REDOBELISK_ATTACKED, GTS_BLUEOBELISK_ATTACKED, GTS_REDTEAM_SCORED, GTS_BLUETEAM_SCORED, GTS_REDTEAM_TOOK_LEAD, GTS_BLUETEAM_TOOK_LEAD, GTS_TEAMS_ARE_TIED, GTS_KAMIKAZE } global_team_sound_t; // animations typedef enum { BOTH_DEATH1, BOTH_DEAD1, BOTH_DEATH2, BOTH_DEAD2, BOTH_DEATH3, BOTH_DEAD3, TORSO_GESTURE, TORSO_ATTACK, TORSO_ATTACK2, TORSO_DROP, TORSO_RAISE, TORSO_STAND, TORSO_STAND2, LEGS_WALKCR, LEGS_WALK, LEGS_RUN, LEGS_BACK, LEGS_SWIM, LEGS_JUMP, LEGS_LAND, LEGS_JUMPB, LEGS_LANDB, LEGS_IDLE, LEGS_IDLECR, LEGS_TURN, TORSO_GETFLAG, TORSO_GUARDBASE, TORSO_PATROL, TORSO_FOLLOWME, TORSO_AFFIRMATIVE, TORSO_NEGATIVE, MAX_ANIMATIONS, LEGS_BACKCR, LEGS_BACKWALK, FLAG_RUN, FLAG_STAND, FLAG_STAND2RUN, MAX_TOTALANIMATIONS } animNumber_t; typedef struct animation_s { int firstFrame; int numFrames; int loopFrames; // 0 to numFrames int frameLerp; // msec between frames int initialLerp; // msec to get to first frame int reversed; // true if animation is reversed int flipflop; // true if animation should flipflop back to base } animation_t; // flip the togglebit every time an animation // changes so a restart of the same anim can be detected #define ANIM_TOGGLEBIT 128 typedef enum { TEAM_FREE, TEAM_RED, TEAM_BLUE, TEAM_SPECTATOR, TEAM_NUM_TEAMS } team_t; // Time between location updates #define TEAM_LOCATION_UPDATE_TIME 1000 // How many players on the overlay #define TEAM_MAXOVERLAY 32 //team task typedef enum { TEAMTASK_NONE, TEAMTASK_OFFENSE, TEAMTASK_DEFENSE, TEAMTASK_PATROL, TEAMTASK_FOLLOW, TEAMTASK_RETRIEVE, TEAMTASK_ESCORT, TEAMTASK_CAMP } teamtask_t; // means of death typedef enum { MOD_UNKNOWN, MOD_SHOTGUN, MOD_GAUNTLET, MOD_MACHINEGUN, MOD_GRENADE, MOD_GRENADE_SPLASH, MOD_ROCKET, MOD_ROCKET_SPLASH, MOD_PLASMA, MOD_PLASMA_SPLASH, MOD_RAILGUN, MOD_LIGHTNING, MOD_BFG, MOD_BFG_SPLASH, MOD_WATER, MOD_SLIME, MOD_LAVA, MOD_CRUSH, MOD_TELEFRAG, MOD_FALLING, MOD_SUICIDE, MOD_TARGET_LASER, MOD_TRIGGER_HURT, MOD_GRAPPLE } meansOfDeath_t; //--------------------------------------------------------- // gitem_t->type typedef enum { IT_BAD, IT_WEAPON, // EFX: rotate + upscale + minlight IT_AMMO, // EFX: rotate IT_ARMOR, // EFX: rotate + minlight IT_HEALTH, // EFX: static external sphere + rotating internal IT_POWERUP, // instant on, timer based // EFX: rotate + external ring that rotates IT_HOLDABLE, // single use, holdable item // EFX: rotate + bob IT_PERSISTANT_POWERUP, IT_TEAM } itemType_t; #define MAX_ITEM_MODELS 4 typedef struct gitem_s { char *classname; // spawning name char *pickup_sound; char *world_model[MAX_ITEM_MODELS]; char *icon; char *pickup_name; // for printing on pickup int quantity; // for ammo how much, or duration of powerup itemType_t giType; // IT_* flags int giTag; char *precaches; // string of all models and images this item will use char *sounds; // string of all sounds this item will use } gitem_t; // included in both the game dll and the client extern gitem_t bg_itemlist[]; extern int bg_numItems; gitem_t *BG_FindItem( const char *pickupName ); gitem_t *BG_FindItemForWeapon( weapon_t weapon ); gitem_t *BG_FindItemForPowerup( powerup_t pw ); gitem_t *BG_FindItemForHoldable( holdable_t pw ); #define ITEM_INDEX(x) ((x)-bg_itemlist) qboolean BG_CanItemBeGrabbed( int gametype, const entityState_t *ent, const playerState_t *ps ); // g_dmflags->integer flags #define DF_NO_FALLING 8 #define DF_FIXED_FOV 16 #define DF_NO_FOOTSTEPS 32 // content masks #define MASK_ALL (-1) #define MASK_SOLID (CONTENTS_SOLID) #define MASK_PLAYERSOLID (CONTENTS_SOLID|CONTENTS_PLAYERCLIP|CONTENTS_BODY) #define MASK_DEADSOLID (CONTENTS_SOLID|CONTENTS_PLAYERCLIP) #define MASK_WATER (CONTENTS_WATER|CONTENTS_LAVA|CONTENTS_SLIME) #define MASK_OPAQUE (CONTENTS_SOLID|CONTENTS_SLIME|CONTENTS_LAVA) #define MASK_SHOT (CONTENTS_SOLID|CONTENTS_BODY|CONTENTS_CORPSE) // // entityState_t->eType // typedef enum { ET_GENERAL, ET_PLAYER, ET_ITEM, ET_MISSILE, ET_MOVER, ET_BEAM, ET_PORTAL, ET_SPEAKER, ET_PUSH_TRIGGER, ET_TELEPORT_TRIGGER, ET_INVISIBLE, ET_GRAPPLE, // grapple hooked on wall ET_TEAM, ET_EVENTS // any of the EV_* events can be added freestanding // by setting eType to ET_EVENTS + eventNum // this avoids having to set eFlags and eventNum } entityType_t; void BG_EvaluateTrajectory( const trajectory_t *tr, int atTime, vec3_t result ); void BG_EvaluateTrajectoryDelta( const trajectory_t *tr, int atTime, vec3_t result ); void BG_AddPredictableEventToPlayerstate( int newEvent, int eventParm, playerState_t *ps ); void BG_TouchJumpPad( playerState_t *ps, entityState_t *jumppad ); void BG_PlayerStateToEntityState( playerState_t *ps, entityState_t *s, qboolean snap ); void BG_PlayerStateToEntityStateExtraPolate( playerState_t *ps, entityState_t *s, int time, qboolean snap ); qboolean BG_PlayerTouchesItem( playerState_t *ps, entityState_t *item, int atTime ); #define ARENAS_PER_TIER 4 #define MAX_ARENAS 1024 #define MAX_ARENAS_TEXT 8192 #define MAX_BOTS 1024 #define MAX_BOTS_TEXT 8192 // Kamikaze // 1st shockwave times #define KAMI_SHOCKWAVE_STARTTIME 0 #define KAMI_SHOCKWAVEFADE_STARTTIME 1500 #define KAMI_SHOCKWAVE_ENDTIME 2000 // explosion/implosion times #define KAMI_EXPLODE_STARTTIME 250 #define KAMI_IMPLODE_STARTTIME 2000 #define KAMI_IMPLODE_ENDTIME 2250 // 2nd shockwave times #define KAMI_SHOCKWAVE2_STARTTIME 2000 #define KAMI_SHOCKWAVE2FADE_STARTTIME 2500 #define KAMI_SHOCKWAVE2_ENDTIME 3000 // radius of the models without scaling #define KAMI_SHOCKWAVEMODEL_RADIUS 88 #define KAMI_BOOMSPHEREMODEL_RADIUS 72 // maximum radius of the models during the effect #define KAMI_SHOCKWAVE_MAXRADIUS 1320 #define KAMI_BOOMSPHERE_MAXRADIUS 720 #define KAMI_SHOCKWAVE2_MAXRADIUS 704 ================================================ FILE: src/game/bg_slidemove.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // bg_slidemove.c -- part of bg_pmove functionality #include "q_shared.h" #include "bg_public.h" #include "bg_local.h" /* input: origin, velocity, bounds, groundPlane, trace function output: origin, velocity, impacts, stairup boolean */ /* ================== PM_SlideMove Returns qtrue if the velocity was clipped in some way ================== */ #define MAX_CLIP_PLANES 5 qboolean PM_SlideMove( qboolean gravity ) { int bumpcount, numbumps; vec3_t dir; float d; int numplanes; vec3_t planes[MAX_CLIP_PLANES]; vec3_t primal_velocity; vec3_t clipVelocity; int i, j, k; trace_t trace; vec3_t end; float time_left; float into; vec3_t endVelocity; vec3_t endClipVelocity; numbumps = 4; VectorCopy (pm->ps->velocity, primal_velocity); if ( gravity ) { VectorCopy( pm->ps->velocity, endVelocity ); endVelocity[2] -= pm->ps->gravity * pml.frametime; pm->ps->velocity[2] = ( pm->ps->velocity[2] + endVelocity[2] ) * 0.5; primal_velocity[2] = endVelocity[2]; if ( pml.groundPlane ) { // slide along the ground plane PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, pm->ps->velocity, OVERCLIP ); } } time_left = pml.frametime; // never turn against the ground plane if ( pml.groundPlane ) { numplanes = 1; VectorCopy( pml.groundTrace.plane.normal, planes[0] ); } else { numplanes = 0; } // never turn against original velocity VectorNormalize2( pm->ps->velocity, planes[numplanes] ); numplanes++; for ( bumpcount=0 ; bumpcount < numbumps ; bumpcount++ ) { // calculate position we are trying to move to VectorMA( pm->ps->origin, time_left, pm->ps->velocity, end ); // see if we can make it there pm->trace ( &trace, pm->ps->origin, pm->mins, pm->maxs, end, pm->ps->clientNum, pm->tracemask); if (trace.allsolid) { // entity is completely trapped in another solid pm->ps->velocity[2] = 0; // don't build up falling damage, but allow sideways acceleration return qtrue; } if (trace.fraction > 0) { // actually covered some distance VectorCopy (trace.endpos, pm->ps->origin); } if (trace.fraction == 1) { break; // moved the entire distance } // save entity for contact PM_AddTouchEnt( trace.entityNum ); time_left -= time_left * trace.fraction; if (numplanes >= MAX_CLIP_PLANES) { // this shouldn't really happen VectorClear( pm->ps->velocity ); return qtrue; } // // if this is the same plane we hit before, nudge velocity // out along it, which fixes some epsilon issues with // non-axial planes // for ( i = 0 ; i < numplanes ; i++ ) { if ( DotProduct( trace.plane.normal, planes[i] ) > 0.99 ) { VectorAdd( trace.plane.normal, pm->ps->velocity, pm->ps->velocity ); break; } } if ( i < numplanes ) { continue; } VectorCopy (trace.plane.normal, planes[numplanes]); numplanes++; // // modify velocity so it parallels all of the clip planes // // find a plane that it enters for ( i = 0 ; i < numplanes ; i++ ) { into = DotProduct( pm->ps->velocity, planes[i] ); if ( into >= 0.1 ) { continue; // move doesn't interact with the plane } // see how hard we are hitting things if ( -into > pml.impactSpeed ) { pml.impactSpeed = -into; } // slide along the plane PM_ClipVelocity (pm->ps->velocity, planes[i], clipVelocity, OVERCLIP ); // slide along the plane PM_ClipVelocity (endVelocity, planes[i], endClipVelocity, OVERCLIP ); // see if there is a second plane that the new move enters for ( j = 0 ; j < numplanes ; j++ ) { if ( j == i ) { continue; } if ( DotProduct( clipVelocity, planes[j] ) >= 0.1 ) { continue; // move doesn't interact with the plane } // try clipping the move to the plane PM_ClipVelocity( clipVelocity, planes[j], clipVelocity, OVERCLIP ); PM_ClipVelocity( endClipVelocity, planes[j], endClipVelocity, OVERCLIP ); // see if it goes back into the first clip plane if ( DotProduct( clipVelocity, planes[i] ) >= 0 ) { continue; } // slide the original velocity along the crease CrossProduct (planes[i], planes[j], dir); VectorNormalize( dir ); d = DotProduct( dir, pm->ps->velocity ); VectorScale( dir, d, clipVelocity ); CrossProduct (planes[i], planes[j], dir); VectorNormalize( dir ); d = DotProduct( dir, endVelocity ); VectorScale( dir, d, endClipVelocity ); // see if there is a third plane the the new move enters for ( k = 0 ; k < numplanes ; k++ ) { if ( k == i || k == j ) { continue; } if ( DotProduct( clipVelocity, planes[k] ) >= 0.1 ) { continue; // move doesn't interact with the plane } // stop dead at a tripple plane interaction VectorClear( pm->ps->velocity ); return qtrue; } } // if we have fixed all interactions, try another move VectorCopy( clipVelocity, pm->ps->velocity ); VectorCopy( endClipVelocity, endVelocity ); break; } } if ( gravity ) { VectorCopy( endVelocity, pm->ps->velocity ); } // don't change velocity if in a timer (FIXME: is this correct?) if ( pm->ps->pm_time ) { VectorCopy( primal_velocity, pm->ps->velocity ); } return ( bumpcount != 0 ); } /* ================== PM_StepSlideMove ================== */ void PM_StepSlideMove( qboolean gravity ) { vec3_t start_o, start_v; vec3_t down_o, down_v; trace_t trace; // float down_dist, up_dist; // vec3_t delta, delta2; vec3_t up, down; float stepSize; VectorCopy (pm->ps->origin, start_o); VectorCopy (pm->ps->velocity, start_v); if ( PM_SlideMove( gravity ) == 0 ) { return; // we got exactly where we wanted to go first try } VectorCopy(start_o, down); down[2] -= STEPSIZE; pm->trace (&trace, start_o, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask); VectorSet(up, 0, 0, 1); // never step up when you still have up velocity if ( pm->ps->velocity[2] > 0 && (trace.fraction == 1.0 || DotProduct(trace.plane.normal, up) < 0.7)) { return; } VectorCopy (pm->ps->origin, down_o); VectorCopy (pm->ps->velocity, down_v); VectorCopy (start_o, up); up[2] += STEPSIZE; // test the player position if they were a stepheight higher pm->trace (&trace, start_o, pm->mins, pm->maxs, up, pm->ps->clientNum, pm->tracemask); if ( trace.allsolid ) { if ( pm->debugLevel ) { Com_Printf("%i:bend can't step\n", c_pmove); } return; // can't step up } stepSize = trace.endpos[2] - start_o[2]; // try slidemove from this position VectorCopy (trace.endpos, pm->ps->origin); VectorCopy (start_v, pm->ps->velocity); PM_SlideMove( gravity ); // push down the final amount VectorCopy (pm->ps->origin, down); down[2] -= stepSize; pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask); if ( !trace.allsolid ) { VectorCopy (trace.endpos, pm->ps->origin); } if ( trace.fraction < 1.0 ) { PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity, OVERCLIP ); } #if 0 // if the down trace can trace back to the original position directly, don't step pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, start_o, pm->ps->clientNum, pm->tracemask); if ( trace.fraction == 1.0 ) { // use the original move VectorCopy (down_o, pm->ps->origin); VectorCopy (down_v, pm->ps->velocity); if ( pm->debugLevel ) { Com_Printf("%i:bend\n", c_pmove); } } else #endif { // use the step move float delta; delta = pm->ps->origin[2] - start_o[2]; if ( delta > 2 ) { if ( delta < 7 ) { PM_AddEvent( EV_STEP_4 ); } else if ( delta < 11 ) { PM_AddEvent( EV_STEP_8 ); } else if ( delta < 15 ) { PM_AddEvent( EV_STEP_12 ); } else { PM_AddEvent( EV_STEP_16 ); } } if ( pm->debugLevel ) { Com_Printf("%i:stepped\n", c_pmove); } } } ================================================ FILE: src/game/botlib.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /***************************************************************************** * name: botlib.h * * desc: bot AI library * * $Archive: /source/code/game/botai.h $ * *****************************************************************************/ #define BOTLIB_API_VERSION 2 struct aas_clientmove_s; struct aas_entityinfo_s; struct aas_areainfo_s; struct aas_altroutegoal_s; struct aas_predictroute_s; struct bot_consolemessage_s; struct bot_match_s; struct bot_goal_s; struct bot_moveresult_s; struct bot_initmove_s; struct weaponinfo_s; #define BOTFILESBASEFOLDER "botfiles" //debug line colors #define LINECOLOR_NONE -1 #define LINECOLOR_RED 1//0xf2f2f0f0L #define LINECOLOR_GREEN 2//0xd0d1d2d3L #define LINECOLOR_BLUE 3//0xf3f3f1f1L #define LINECOLOR_YELLOW 4//0xdcdddedfL #define LINECOLOR_ORANGE 5//0xe0e1e2e3L //Print types #define PRT_MESSAGE 1 #define PRT_WARNING 2 #define PRT_ERROR 3 #define PRT_FATAL 4 #define PRT_EXIT 5 //console message types #define CMS_NORMAL 0 #define CMS_CHAT 1 //botlib error codes #define BLERR_NOERROR 0 //no error #define BLERR_LIBRARYNOTSETUP 1 //library not setup #define BLERR_INVALIDENTITYNUMBER 2 //invalid entity number #define BLERR_NOAASFILE 3 //no AAS file available #define BLERR_CANNOTOPENAASFILE 4 //cannot open AAS file #define BLERR_WRONGAASFILEID 5 //incorrect AAS file id #define BLERR_WRONGAASFILEVERSION 6 //incorrect AAS file version #define BLERR_CANNOTREADAASLUMP 7 //cannot read AAS file lump #define BLERR_CANNOTLOADICHAT 8 //cannot load initial chats #define BLERR_CANNOTLOADITEMWEIGHTS 9 //cannot load item weights #define BLERR_CANNOTLOADITEMCONFIG 10 //cannot load item config #define BLERR_CANNOTLOADWEAPONWEIGHTS 11 //cannot load weapon weights #define BLERR_CANNOTLOADWEAPONCONFIG 12 //cannot load weapon config //action flags #define ACTION_ATTACK 0x0000001 #define ACTION_USE 0x0000002 #define ACTION_RESPAWN 0x0000008 #define ACTION_JUMP 0x0000010 #define ACTION_MOVEUP 0x0000020 #define ACTION_CROUCH 0x0000080 #define ACTION_MOVEDOWN 0x0000100 #define ACTION_MOVEFORWARD 0x0000200 #define ACTION_MOVEBACK 0x0000800 #define ACTION_MOVELEFT 0x0001000 #define ACTION_MOVERIGHT 0x0002000 #define ACTION_DELAYEDJUMP 0x0008000 #define ACTION_TALK 0x0010000 #define ACTION_GESTURE 0x0020000 #define ACTION_WALK 0x0080000 #define ACTION_AFFIRMATIVE 0x0100000 #define ACTION_NEGATIVE 0x0200000 #define ACTION_GETFLAG 0x0800000 #define ACTION_GUARDBASE 0x1000000 #define ACTION_PATROL 0x2000000 #define ACTION_FOLLOWME 0x8000000 //the bot input, will be converted to an usercmd_t typedef struct bot_input_s { float thinktime; //time since last output (in seconds) vec3_t dir; //movement direction float speed; //speed in the range [0, 400] vec3_t viewangles; //the view angles int actionflags; //one of the ACTION_? flags int weapon; //weapon to use } bot_input_t; #ifndef BSPTRACE #define BSPTRACE //bsp_trace_t hit surface typedef struct bsp_surface_s { char name[16]; int flags; int value; } bsp_surface_t; //remove the bsp_trace_s structure definition l8r on //a trace is returned when a box is swept through the world typedef struct bsp_trace_s { qboolean allsolid; // if true, plane is not valid qboolean startsolid; // if true, the initial point was in a solid area float fraction; // time completed, 1.0 = didn't hit anything vec3_t endpos; // final position cplane_t plane; // surface normal at impact float exp_dist; // expanded plane distance int sidenum; // number of the brush side hit bsp_surface_t surface; // the hit point surface int contents; // contents on other side of surface hit int ent; // number of entity hit } bsp_trace_t; #endif // BSPTRACE //entity state typedef struct bot_entitystate_s { int type; // entity type int flags; // entity flags vec3_t origin; // origin of the entity vec3_t angles; // angles of the model vec3_t old_origin; // for lerping vec3_t mins; // bounding box minimums vec3_t maxs; // bounding box maximums int groundent; // ground entity int solid; // solid type int modelindex; // model used int modelindex2; // weapons, CTF flags, etc int frame; // model frame number int event; // impulse events -- muzzle flashes, footsteps, etc int eventParm; // even parameter int powerups; // bit flags int weapon; // determines weapon and flash model, etc int legsAnim; // mask off ANIM_TOGGLEBIT int torsoAnim; // mask off ANIM_TOGGLEBIT } bot_entitystate_t; //bot AI library exported functions typedef struct botlib_import_s { //print messages from the bot library void (QDECL *Print)(int type, char *fmt, ...); //trace a bbox through the world void (*Trace)(bsp_trace_t *trace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask); //trace a bbox against a specific entity void (*EntityTrace)(bsp_trace_t *trace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int entnum, int contentmask); //retrieve the contents at the given point int (*PointContents)(vec3_t point); //check if the point is in potential visible sight int (*inPVS)(vec3_t p1, vec3_t p2); //retrieve the BSP entity data lump char *(*BSPEntityData)(void); // void (*BSPModelMinsMaxsOrigin)(int modelnum, vec3_t angles, vec3_t mins, vec3_t maxs, vec3_t origin); //send a bot client command void (*BotClientCommand)(int client, char *command); //memory allocation void *(*GetMemory)(int size); // allocate from Zone void (*FreeMemory)(void *ptr); // free memory from Zone int (*AvailableMemory)(void); // available Zone memory void *(*HunkAlloc)(int size); // allocate from hunk //file system access int (*FS_FOpenFile)( const char *qpath, fileHandle_t *file, fsMode_t mode ); int (*FS_Read)( void *buffer, int len, fileHandle_t f ); int (*FS_Write)( const void *buffer, int len, fileHandle_t f ); void (*FS_FCloseFile)( fileHandle_t f ); int (*FS_Seek)( fileHandle_t f, long offset, int origin ); //debug visualisation stuff int (*DebugLineCreate)(void); void (*DebugLineDelete)(int line); void (*DebugLineShow)(int line, vec3_t start, vec3_t end, int color); // int (*DebugPolygonCreate)(int color, int numPoints, vec3_t *points); void (*DebugPolygonDelete)(int id); } botlib_import_t; typedef struct aas_export_s { //----------------------------------- // be_aas_entity.h //----------------------------------- void (*AAS_EntityInfo)(int entnum, struct aas_entityinfo_s *info); //----------------------------------- // be_aas_main.h //----------------------------------- int (*AAS_Initialized)(void); void (*AAS_PresenceTypeBoundingBox)(int presencetype, vec3_t mins, vec3_t maxs); float (*AAS_Time)(void); //-------------------------------------------- // be_aas_sample.c //-------------------------------------------- int (*AAS_PointAreaNum)(vec3_t point); int (*AAS_PointReachabilityAreaIndex)( vec3_t point ); int (*AAS_TraceAreas)(vec3_t start, vec3_t end, int *areas, vec3_t *points, int maxareas); int (*AAS_BBoxAreas)(vec3_t absmins, vec3_t absmaxs, int *areas, int maxareas); int (*AAS_AreaInfo)( int areanum, struct aas_areainfo_s *info ); //-------------------------------------------- // be_aas_bspq3.c //-------------------------------------------- int (*AAS_PointContents)(vec3_t point); int (*AAS_NextBSPEntity)(int ent); int (*AAS_ValueForBSPEpairKey)(int ent, char *key, char *value, int size); int (*AAS_VectorForBSPEpairKey)(int ent, char *key, vec3_t v); int (*AAS_FloatForBSPEpairKey)(int ent, char *key, float *value); int (*AAS_IntForBSPEpairKey)(int ent, char *key, int *value); //-------------------------------------------- // be_aas_reach.c //-------------------------------------------- int (*AAS_AreaReachability)(int areanum); //-------------------------------------------- // be_aas_route.c //-------------------------------------------- int (*AAS_AreaTravelTimeToGoalArea)(int areanum, vec3_t origin, int goalareanum, int travelflags); int (*AAS_EnableRoutingArea)(int areanum, int enable); int (*AAS_PredictRoute)(struct aas_predictroute_s *route, int areanum, vec3_t origin, int goalareanum, int travelflags, int maxareas, int maxtime, int stopevent, int stopcontents, int stoptfl, int stopareanum); //-------------------------------------------- // be_aas_altroute.c //-------------------------------------------- int (*AAS_AlternativeRouteGoals)(vec3_t start, int startareanum, vec3_t goal, int goalareanum, int travelflags, struct aas_altroutegoal_s *altroutegoals, int maxaltroutegoals, int type); //-------------------------------------------- // be_aas_move.c //-------------------------------------------- int (*AAS_Swimming)(vec3_t origin); int (*AAS_PredictClientMovement)(struct aas_clientmove_s *move, int entnum, vec3_t origin, int presencetype, int onground, vec3_t velocity, vec3_t cmdmove, int cmdframes, int maxframes, float frametime, int stopevent, int stopareanum, int visualize); } aas_export_t; typedef struct ea_export_s { //ClientCommand elementary actions void (*EA_Command)(int client, char *command ); void (*EA_Say)(int client, char *str); void (*EA_SayTeam)(int client, char *str); // void (*EA_Action)(int client, int action); void (*EA_Gesture)(int client); void (*EA_Talk)(int client); void (*EA_Attack)(int client); void (*EA_Use)(int client); void (*EA_Respawn)(int client); void (*EA_MoveUp)(int client); void (*EA_MoveDown)(int client); void (*EA_MoveForward)(int client); void (*EA_MoveBack)(int client); void (*EA_MoveLeft)(int client); void (*EA_MoveRight)(int client); void (*EA_Crouch)(int client); void (*EA_SelectWeapon)(int client, int weapon); void (*EA_Jump)(int client); void (*EA_DelayedJump)(int client); void (*EA_Move)(int client, vec3_t dir, float speed); void (*EA_View)(int client, vec3_t viewangles); //send regular input to the server void (*EA_EndRegular)(int client, float thinktime); void (*EA_GetInput)(int client, float thinktime, bot_input_t *input); void (*EA_ResetInput)(int client); } ea_export_t; typedef struct ai_export_s { //----------------------------------- // be_ai_char.h //----------------------------------- int (*BotLoadCharacter)(char *charfile, float skill); void (*BotFreeCharacter)(int character); float (*Characteristic_Float)(int character, int index); float (*Characteristic_BFloat)(int character, int index, float min, float max); int (*Characteristic_Integer)(int character, int index); int (*Characteristic_BInteger)(int character, int index, int min, int max); void (*Characteristic_String)(int character, int index, char *buf, int size); //----------------------------------- // be_ai_chat.h //----------------------------------- int (*BotAllocChatState)(void); void (*BotFreeChatState)(int handle); void (*BotQueueConsoleMessage)(int chatstate, int type, char *message); void (*BotRemoveConsoleMessage)(int chatstate, int handle); int (*BotNextConsoleMessage)(int chatstate, struct bot_consolemessage_s *cm); int (*BotNumConsoleMessages)(int chatstate); void (*BotInitialChat)(int chatstate, char *type, int mcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7); int (*BotNumInitialChats)(int chatstate, char *type); int (*BotReplyChat)(int chatstate, char *message, int mcontext, int vcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7); int (*BotChatLength)(int chatstate); void (*BotEnterChat)(int chatstate, int client, int sendto); void (*BotGetChatMessage)(int chatstate, char *buf, int size); int (*StringContains)(char *str1, char *str2, int casesensitive); int (*BotFindMatch)(char *str, struct bot_match_s *match, unsigned long int context); void (*BotMatchVariable)(struct bot_match_s *match, int variable, char *buf, int size); void (*UnifyWhiteSpaces)(char *string); void (*BotReplaceSynonyms)(char *string, unsigned long int context); int (*BotLoadChatFile)(int chatstate, char *chatfile, char *chatname); void (*BotSetChatGender)(int chatstate, int gender); void (*BotSetChatName)(int chatstate, char *name, int client); //----------------------------------- // be_ai_goal.h //----------------------------------- void (*BotResetGoalState)(int goalstate); void (*BotResetAvoidGoals)(int goalstate); void (*BotRemoveFromAvoidGoals)(int goalstate, int number); void (*BotPushGoal)(int goalstate, struct bot_goal_s *goal); void (*BotPopGoal)(int goalstate); void (*BotEmptyGoalStack)(int goalstate); void (*BotDumpAvoidGoals)(int goalstate); void (*BotDumpGoalStack)(int goalstate); void (*BotGoalName)(int number, char *name, int size); int (*BotGetTopGoal)(int goalstate, struct bot_goal_s *goal); int (*BotGetSecondGoal)(int goalstate, struct bot_goal_s *goal); int (*BotChooseLTGItem)(int goalstate, vec3_t origin, int *inventory, int travelflags); int (*BotChooseNBGItem)(int goalstate, vec3_t origin, int *inventory, int travelflags, struct bot_goal_s *ltg, float maxtime); int (*BotTouchingGoal)(vec3_t origin, struct bot_goal_s *goal); int (*BotItemGoalInVisButNotVisible)(int viewer, vec3_t eye, vec3_t viewangles, struct bot_goal_s *goal); int (*BotGetLevelItemGoal)(int index, char *classname, struct bot_goal_s *goal); int (*BotGetNextCampSpotGoal)(int num, struct bot_goal_s *goal); int (*BotGetMapLocationGoal)(char *name, struct bot_goal_s *goal); float (*BotAvoidGoalTime)(int goalstate, int number); void (*BotSetAvoidGoalTime)(int goalstate, int number, float avoidtime); void (*BotInitLevelItems)(void); void (*BotUpdateEntityItems)(void); int (*BotLoadItemWeights)(int goalstate, char *filename); void (*BotFreeItemWeights)(int goalstate); void (*BotInterbreedGoalFuzzyLogic)(int parent1, int parent2, int child); void (*BotSaveGoalFuzzyLogic)(int goalstate, char *filename); void (*BotMutateGoalFuzzyLogic)(int goalstate, float range); int (*BotAllocGoalState)(int client); void (*BotFreeGoalState)(int handle); //----------------------------------- // be_ai_move.h //----------------------------------- void (*BotResetMoveState)(int movestate); void (*BotMoveToGoal)(struct bot_moveresult_s *result, int movestate, struct bot_goal_s *goal, int travelflags); int (*BotMoveInDirection)(int movestate, vec3_t dir, float speed, int type); void (*BotResetAvoidReach)(int movestate); void (*BotResetLastAvoidReach)(int movestate); int (*BotReachabilityArea)(vec3_t origin, int testground); int (*BotMovementViewTarget)(int movestate, struct bot_goal_s *goal, int travelflags, float lookahead, vec3_t target); int (*BotPredictVisiblePosition)(vec3_t origin, int areanum, struct bot_goal_s *goal, int travelflags, vec3_t target); int (*BotAllocMoveState)(void); void (*BotFreeMoveState)(int handle); void (*BotInitMoveState)(int handle, struct bot_initmove_s *initmove); void (*BotAddAvoidSpot)(int movestate, vec3_t origin, float radius, int type); //----------------------------------- // be_ai_weap.h //----------------------------------- int (*BotChooseBestFightWeapon)(int weaponstate, int *inventory); void (*BotGetWeaponInfo)(int weaponstate, int weapon, struct weaponinfo_s *weaponinfo); int (*BotLoadWeaponWeights)(int weaponstate, char *filename); int (*BotAllocWeaponState)(void); void (*BotFreeWeaponState)(int weaponstate); void (*BotResetWeaponState)(int weaponstate); //----------------------------------- // be_ai_gen.h //----------------------------------- int (*GeneticParentsAndChildSelection)(int numranks, float *ranks, int *parent1, int *parent2, int *child); } ai_export_t; //bot AI library imported functions typedef struct botlib_export_s { //Area Awareness System functions aas_export_t aas; //Elementary Action functions ea_export_t ea; //AI functions ai_export_t ai; //setup the bot library, returns BLERR_ int (*BotLibSetup)(void); //shutdown the bot library, returns BLERR_ int (*BotLibShutdown)(void); //sets a library variable returns BLERR_ int (*BotLibVarSet)(char *var_name, char *value); //gets a library variable returns BLERR_ int (*BotLibVarGet)(char *var_name, char *value, int size); //sets a C-like define returns BLERR_ int (*PC_AddGlobalDefine)(char *string); int (*PC_LoadSourceHandle)(const char *filename); int (*PC_FreeSourceHandle)(int handle); int (*PC_ReadTokenHandle)(int handle, pc_token_t *pc_token); int (*PC_SourceFileAndLine)(int handle, char *filename, int *line); //start a frame in the bot library int (*BotLibStartFrame)(float time); //load a new map in the bot library int (*BotLibLoadMap)(const char *mapname); //entity updates int (*BotLibUpdateEntity)(int ent, bot_entitystate_t *state); //just for testing int (*Test)(int parm0, char *parm1, vec3_t parm2, vec3_t parm3); } botlib_export_t; //linking of bot library botlib_export_t *GetBotLibAPI( int apiVersion, botlib_import_t *import ); /* Library variables: name: default: module(s): description: "basedir" "" l_utils.c base directory "gamedir" "" l_utils.c game directory "cddir" "" l_utils.c CD directory "log" "0" l_log.c enable/disable creating a log file "maxclients" "4" be_interface.c maximum number of clients "maxentities" "1024" be_interface.c maximum number of entities "bot_developer" "0" be_interface.c bot developer mode "phys_friction" "6" be_aas_move.c ground friction "phys_stopspeed" "100" be_aas_move.c stop speed "phys_gravity" "800" be_aas_move.c gravity value "phys_waterfriction" "1" be_aas_move.c water friction "phys_watergravity" "400" be_aas_move.c gravity in water "phys_maxvelocity" "320" be_aas_move.c maximum velocity "phys_maxwalkvelocity" "320" be_aas_move.c maximum walk velocity "phys_maxcrouchvelocity" "100" be_aas_move.c maximum crouch velocity "phys_maxswimvelocity" "150" be_aas_move.c maximum swim velocity "phys_walkaccelerate" "10" be_aas_move.c walk acceleration "phys_airaccelerate" "1" be_aas_move.c air acceleration "phys_swimaccelerate" "4" be_aas_move.c swim acceleration "phys_maxstep" "18" be_aas_move.c maximum step height "phys_maxsteepness" "0.7" be_aas_move.c maximum floor steepness "phys_maxbarrier" "32" be_aas_move.c maximum barrier height "phys_maxwaterjump" "19" be_aas_move.c maximum waterjump height "phys_jumpvel" "270" be_aas_move.c jump z velocity "phys_falldelta5" "40" be_aas_move.c "phys_falldelta10" "60" be_aas_move.c "rs_waterjump" "400" be_aas_move.c "rs_teleport" "50" be_aas_move.c "rs_barrierjump" "100" be_aas_move.c "rs_startcrouch" "300" be_aas_move.c "rs_startgrapple" "500" be_aas_move.c "rs_startwalkoffledge" "70" be_aas_move.c "rs_startjump" "300" be_aas_move.c "rs_rocketjump" "500" be_aas_move.c "rs_bfgjump" "500" be_aas_move.c "rs_jumppad" "250" be_aas_move.c "rs_aircontrolledjumppad" "300" be_aas_move.c "rs_funcbob" "300" be_aas_move.c "rs_startelevator" "50" be_aas_move.c "rs_falldamage5" "300" be_aas_move.c "rs_falldamage10" "500" be_aas_move.c "rs_maxjumpfallheight" "450" be_aas_move.c "max_aaslinks" "4096" be_aas_sample.c maximum links in the AAS "max_routingcache" "4096" be_aas_route.c maximum routing cache size in KB "forceclustering" "0" be_aas_main.c force recalculation of clusters "forcereachability" "0" be_aas_main.c force recalculation of reachabilities "forcewrite" "0" be_aas_main.c force writing of aas file "aasoptimize" "0" be_aas_main.c enable aas optimization "sv_mapChecksum" "0" be_aas_main.c BSP file checksum "bot_visualizejumppads" "0" be_aas_reach.c visualize jump pads "bot_reloadcharacters" "0" - reload bot character files "ai_gametype" "0" be_ai_goal.c game type "droppedweight" "1000" be_ai_goal.c additional dropped item weight "weapindex_rocketlauncher" "5" be_ai_move.c rl weapon index for rocket jumping "weapindex_bfg10k" "9" be_ai_move.c bfg weapon index for bfg jumping "weapindex_grapple" "10" be_ai_move.c grapple weapon index for grappling "entitytypemissile" "3" be_ai_move.c ET_MISSILE "offhandgrapple" "0" be_ai_move.c enable off hand grapple hook "cmd_grappleon" "grappleon" be_ai_move.c command to activate off hand grapple "cmd_grappleoff" "grappleoff" be_ai_move.c command to deactivate off hand grapple "itemconfig" "items.c" be_ai_goal.c item configuration file "weaponconfig" "weapons.c" be_ai_weap.c weapon configuration file "synfile" "syn.c" be_ai_chat.c file with synonyms "rndfile" "rnd.c" be_ai_chat.c file with random strings "matchfile" "match.c" be_ai_chat.c file with match strings "nochat" "0" be_ai_chat.c disable chats "max_messages" "1024" be_ai_chat.c console message heap size "max_weaponinfo" "32" be_ai_weap.c maximum number of weapon info "max_projectileinfo" "32" be_ai_weap.c maximum number of projectile info "max_iteminfo" "256" be_ai_goal.c maximum number of item info "max_levelitems" "256" be_ai_goal.c maximum number of level items */ ================================================ FILE: src/game/chars.h ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ //======================================================== //======================================================== //name #define CHARACTERISTIC_NAME 0 //string //gender of the bot #define CHARACTERISTIC_GENDER 1 //string ("male", "female", "it") //attack skill // > 0.0 && < 0.2 = don't move // > 0.3 && < 1.0 = aim at enemy during retreat // > 0.0 && < 0.4 = only move forward/backward // >= 0.4 && < 1.0 = circle strafing // > 0.7 && < 1.0 = random strafe direction change #define CHARACTERISTIC_ATTACK_SKILL 2 //float [0, 1] //weapon weight file #define CHARACTERISTIC_WEAPONWEIGHTS 3 //string //view angle difference to angle change factor #define CHARACTERISTIC_VIEW_FACTOR 4 //float <0, 1] //maximum view angle change #define CHARACTERISTIC_VIEW_MAXCHANGE 5 //float [1, 360] //reaction time in seconds #define CHARACTERISTIC_REACTIONTIME 6 //float [0, 5] //accuracy when aiming #define CHARACTERISTIC_AIM_ACCURACY 7 //float [0, 1] //weapon specific aim accuracy #define CHARACTERISTIC_AIM_ACCURACY_MACHINEGUN 8 //float [0, 1] #define CHARACTERISTIC_AIM_ACCURACY_SHOTGUN 9 //float [0, 1] #define CHARACTERISTIC_AIM_ACCURACY_ROCKETLAUNCHER 10 //float [0, 1] #define CHARACTERISTIC_AIM_ACCURACY_GRENADELAUNCHER 11 //float [0, 1] #define CHARACTERISTIC_AIM_ACCURACY_LIGHTNING 12 #define CHARACTERISTIC_AIM_ACCURACY_PLASMAGUN 13 //float [0, 1] #define CHARACTERISTIC_AIM_ACCURACY_RAILGUN 14 #define CHARACTERISTIC_AIM_ACCURACY_BFG10K 15 //float [0, 1] //skill when aiming // > 0.0 && < 0.9 = aim is affected by enemy movement // > 0.4 && <= 0.8 = enemy linear leading // > 0.8 && <= 1.0 = enemy exact movement leading // > 0.5 && <= 1.0 = prediction shots when enemy is not visible // > 0.6 && <= 1.0 = splash damage by shooting nearby geometry #define CHARACTERISTIC_AIM_SKILL 16 //float [0, 1] //weapon specific aim skill #define CHARACTERISTIC_AIM_SKILL_ROCKETLAUNCHER 17 //float [0, 1] #define CHARACTERISTIC_AIM_SKILL_GRENADELAUNCHER 18 //float [0, 1] #define CHARACTERISTIC_AIM_SKILL_PLASMAGUN 19 //float [0, 1] #define CHARACTERISTIC_AIM_SKILL_BFG10K 20 //float [0, 1] //======================================================== //chat //======================================================== //file with chats #define CHARACTERISTIC_CHAT_FILE 21 //string //name of the chat character #define CHARACTERISTIC_CHAT_NAME 22 //string //characters per minute type speed #define CHARACTERISTIC_CHAT_CPM 23 //integer [1, 4000] //tendency to insult/praise #define CHARACTERISTIC_CHAT_INSULT 24 //float [0, 1] //tendency to chat misc #define CHARACTERISTIC_CHAT_MISC 25 //float [0, 1] //tendency to chat at start or end of level #define CHARACTERISTIC_CHAT_STARTENDLEVEL 26 //float [0, 1] //tendency to chat entering or exiting the game #define CHARACTERISTIC_CHAT_ENTEREXITGAME 27 //float [0, 1] //tendency to chat when killed someone #define CHARACTERISTIC_CHAT_KILL 28 //float [0, 1] //tendency to chat when died #define CHARACTERISTIC_CHAT_DEATH 29 //float [0, 1] //tendency to chat when enemy suicides #define CHARACTERISTIC_CHAT_ENEMYSUICIDE 30 //float [0, 1] //tendency to chat when hit while talking #define CHARACTERISTIC_CHAT_HITTALKING 31 //float [0, 1] //tendency to chat when bot was hit but didn't dye #define CHARACTERISTIC_CHAT_HITNODEATH 32 //float [0, 1] //tendency to chat when bot hit the enemy but enemy didn't dye #define CHARACTERISTIC_CHAT_HITNOKILL 33 //float [0, 1] //tendency to randomly chat #define CHARACTERISTIC_CHAT_RANDOM 34 //float [0, 1] //tendency to reply #define CHARACTERISTIC_CHAT_REPLY 35 //float [0, 1] //======================================================== //movement //======================================================== //tendency to crouch #define CHARACTERISTIC_CROUCHER 36 //float [0, 1] //tendency to jump #define CHARACTERISTIC_JUMPER 37 //float [0, 1] //tendency to walk #define CHARACTERISTIC_WALKER 48 //float [0, 1] //tendency to jump using a weapon #define CHARACTERISTIC_WEAPONJUMPING 38 //float [0, 1] //tendency to use the grapple hook when available #define CHARACTERISTIC_GRAPPLE_USER 39 //float [0, 1] //use this!! //======================================================== //goal //======================================================== //item weight file #define CHARACTERISTIC_ITEMWEIGHTS 40 //string //the aggression of the bot #define CHARACTERISTIC_AGGRESSION 41 //float [0, 1] //the self preservation of the bot (rockets near walls etc.) #define CHARACTERISTIC_SELFPRESERVATION 42 //float [0, 1] //how likely the bot is to take revenge #define CHARACTERISTIC_VENGEFULNESS 43 //float [0, 1] //use this!! //tendency to camp #define CHARACTERISTIC_CAMPER 44 //float [0, 1] //======================================================== //======================================================== //tendency to get easy frags #define CHARACTERISTIC_EASY_FRAGGER 45 //float [0, 1] //how alert the bot is (view distance) #define CHARACTERISTIC_ALERTNESS 46 //float [0, 1] //how much the bot fires it's weapon #define CHARACTERISTIC_FIRETHROTTLE 47 //float [0, 1] ================================================ FILE: src/game/g_active.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #include "g_local.h" /* =============== G_DamageFeedback Called just before a snapshot is sent to the given player. Totals up all damage and generates both the player_state_t damage values to that client for pain blends and kicks, and global pain sound events for all clients. =============== */ void P_DamageFeedback( gentity_t *player ) { gclient_t *client; float count; vec3_t angles; client = player->client; if ( client->ps.pm_type == PM_DEAD ) { return; } // total points of damage shot at the player this frame count = client->damage_blood + client->damage_armor; if ( count == 0 ) { return; // didn't take any damage } if ( count > 255 ) { count = 255; } // send the information to the client // world damage (falling, slime, etc) uses a special code // to make the blend blob centered instead of positional if ( client->damage_fromWorld ) { client->ps.damagePitch = 255; client->ps.damageYaw = 255; client->damage_fromWorld = qfalse; } else { vectoangles( client->damage_from, angles ); client->ps.damagePitch = angles[PITCH]/360.0 * 256; client->ps.damageYaw = angles[YAW]/360.0 * 256; } // play an apropriate pain sound if ( (level.time > player->pain_debounce_time) && !(player->flags & FL_GODMODE) ) { player->pain_debounce_time = level.time + 700; G_AddEvent( player, EV_PAIN, player->health ); client->ps.damageEvent++; } client->ps.damageCount = count; // // clear totals // client->damage_blood = 0; client->damage_armor = 0; client->damage_knockback = 0; } /* ============= P_WorldEffects Check for lava / slime contents and drowning ============= */ void P_WorldEffects( gentity_t *ent ) { qboolean envirosuit; int waterlevel; if ( ent->client->noclip ) { ent->client->airOutTime = level.time + 12000; // don't need air return; } waterlevel = ent->waterlevel; envirosuit = ent->client->ps.powerups[PW_BATTLESUIT] > level.time; // // check for drowning // if ( waterlevel == 3 ) { // envirosuit give air if ( envirosuit ) { ent->client->airOutTime = level.time + 10000; } // if out of air, start drowning if ( ent->client->airOutTime < level.time) { // drown! ent->client->airOutTime += 1000; if ( ent->health > 0 ) { // take more damage the longer underwater ent->damage += 2; if (ent->damage > 15) ent->damage = 15; // play a gurp sound instead of a normal pain sound if (ent->health <= ent->damage) { G_Sound(ent, CHAN_VOICE, G_SoundIndex("*drown.wav")); } else if (rand()&1) { G_Sound(ent, CHAN_VOICE, G_SoundIndex("sound/player/gurp1.wav")); } else { G_Sound(ent, CHAN_VOICE, G_SoundIndex("sound/player/gurp2.wav")); } // don't play a normal pain sound ent->pain_debounce_time = level.time + 200; G_Damage (ent, NULL, NULL, NULL, NULL, ent->damage, DAMAGE_NO_ARMOR, MOD_WATER); } } } else { ent->client->airOutTime = level.time + 12000; ent->damage = 2; } // // check for sizzle damage (move to pmove?) // if (waterlevel && (ent->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) ) { if (ent->health > 0 && ent->pain_debounce_time <= level.time ) { if ( envirosuit ) { G_AddEvent( ent, EV_POWERUP_BATTLESUIT, 0 ); } else { if (ent->watertype & CONTENTS_LAVA) { G_Damage (ent, NULL, NULL, NULL, NULL, 30*waterlevel, 0, MOD_LAVA); } if (ent->watertype & CONTENTS_SLIME) { G_Damage (ent, NULL, NULL, NULL, NULL, 10*waterlevel, 0, MOD_SLIME); } } } } } /* =============== G_SetClientSound =============== */ void G_SetClientSound( gentity_t *ent ) { if (ent->waterlevel && (ent->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) ) { ent->client->ps.loopSound = level.snd_fry; } else { ent->client->ps.loopSound = 0; } } //============================================================== /* ============== ClientImpacts ============== */ void ClientImpacts( gentity_t *ent, pmove_t *pm ) { int i, j; trace_t trace; gentity_t *other; memset( &trace, 0, sizeof( trace ) ); for (i=0 ; inumtouch ; i++) { for (j=0 ; jtouchents[j] == pm->touchents[i] ) { break; } } if (j != i) { continue; // duplicated } other = &g_entities[ pm->touchents[i] ]; if ( ( ent->r.svFlags & SVF_BOT ) && ( ent->touch ) ) { ent->touch( ent, other, &trace ); } if ( !other->touch ) { continue; } other->touch( other, ent, &trace ); } } /* ============ G_TouchTriggers Find all trigger entities that ent's current position touches. Spectators will only interact with teleporters. ============ */ void G_TouchTriggers( gentity_t *ent ) { int i, num; int touch[MAX_GENTITIES]; gentity_t *hit; trace_t trace; vec3_t mins, maxs; static vec3_t range = { 40, 40, 52 }; if ( !ent->client ) { return; } // dead clients don't activate triggers! if ( ent->client->ps.stats[STAT_HEALTH] <= 0 ) { return; } VectorSubtract( ent->client->ps.origin, range, mins ); VectorAdd( ent->client->ps.origin, range, maxs ); num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES ); // can't use ent->absmin, because that has a one unit pad VectorAdd( ent->client->ps.origin, ent->r.mins, mins ); VectorAdd( ent->client->ps.origin, ent->r.maxs, maxs ); for ( i=0 ; itouch && !ent->touch ) { continue; } if ( !( hit->r.contents & CONTENTS_TRIGGER ) ) { continue; } // ignore most entities if a spectator if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) { if ( hit->s.eType != ET_TELEPORT_TRIGGER && // this is ugly but adding a new ET_? type will // most likely cause network incompatibilities hit->touch != Touch_DoorTrigger) { continue; } } // use seperate code for determining if an item is picked up // so you don't have to actually contact its bounding box if ( hit->s.eType == ET_ITEM ) { if ( !BG_PlayerTouchesItem( &ent->client->ps, &hit->s, level.time ) ) { continue; } } else { if ( !trap_EntityContact( mins, maxs, hit ) ) { continue; } } memset( &trace, 0, sizeof(trace) ); if ( hit->touch ) { hit->touch (hit, ent, &trace); } if ( ( ent->r.svFlags & SVF_BOT ) && ( ent->touch ) ) { ent->touch( ent, hit, &trace ); } } // if we didn't touch a jump pad this pmove frame if ( ent->client->ps.jumppad_frame != ent->client->ps.pmove_framecount ) { ent->client->ps.jumppad_frame = 0; ent->client->ps.jumppad_ent = 0; } } /* ================= SpectatorThink ================= */ void SpectatorThink( gentity_t *ent, usercmd_t *ucmd ) { pmove_t pm; gclient_t *client; client = ent->client; if ( client->sess.spectatorState != SPECTATOR_FOLLOW ) { client->ps.pm_type = PM_SPECTATOR; client->ps.speed = 400; // faster than normal // set up for pmove memset (&pm, 0, sizeof(pm)); pm.ps = &client->ps; pm.cmd = *ucmd; pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY; // spectators can fly through bodies pm.trace = trap_Trace; pm.pointcontents = trap_PointContents; // perform a pmove Pmove (&pm); // save results of pmove VectorCopy( client->ps.origin, ent->s.origin ); G_TouchTriggers( ent ); trap_UnlinkEntity( ent ); } client->oldbuttons = client->buttons; client->buttons = ucmd->buttons; // attack button cycles through spectators if ( ( client->buttons & BUTTON_ATTACK ) && ! ( client->oldbuttons & BUTTON_ATTACK ) ) { Cmd_FollowCycle_f( ent, 1 ); } } /* ================= ClientInactivityTimer Returns qfalse if the client is dropped ================= */ qboolean ClientInactivityTimer( gclient_t *client ) { if ( ! g_inactivity.integer ) { // give everyone some time, so if the operator sets g_inactivity during // gameplay, everyone isn't kicked client->inactivityTime = level.time + 60 * 1000; client->inactivityWarning = qfalse; } else if ( client->pers.cmd.forwardmove || client->pers.cmd.rightmove || client->pers.cmd.upmove || (client->pers.cmd.buttons & BUTTON_ATTACK) ) { client->inactivityTime = level.time + g_inactivity.integer * 1000; client->inactivityWarning = qfalse; } else if ( !client->pers.localClient ) { if ( level.time > client->inactivityTime ) { trap_DropClient( client - level.clients, "Dropped due to inactivity" ); return qfalse; } if ( level.time > client->inactivityTime - 10000 && !client->inactivityWarning ) { client->inactivityWarning = qtrue; trap_SendServerCommand( client - level.clients, "cp \"Ten seconds until inactivity drop!\n\"" ); } } return qtrue; } /* ================== ClientTimerActions Actions that happen once a second ================== */ void ClientTimerActions( gentity_t *ent, int msec ) { gclient_t *client; client = ent->client; client->timeResidual += msec; while ( client->timeResidual >= 1000 ) { client->timeResidual -= 1000; // regenerate if ( client->ps.powerups[PW_REGEN] ) { if ( ent->health < client->ps.stats[STAT_MAX_HEALTH]) { ent->health += 15; if ( ent->health > client->ps.stats[STAT_MAX_HEALTH] * 1.1 ) { ent->health = client->ps.stats[STAT_MAX_HEALTH] * 1.1; } G_AddEvent( ent, EV_POWERUP_REGEN, 0 ); } else if ( ent->health < client->ps.stats[STAT_MAX_HEALTH] * 2) { ent->health += 5; if ( ent->health > client->ps.stats[STAT_MAX_HEALTH] * 2 ) { ent->health = client->ps.stats[STAT_MAX_HEALTH] * 2; } G_AddEvent( ent, EV_POWERUP_REGEN, 0 ); } } else { // count down health when over max if ( ent->health > client->ps.stats[STAT_MAX_HEALTH] ) { ent->health--; } } // count down armor when over max if ( client->ps.stats[STAT_ARMOR] > client->ps.stats[STAT_MAX_HEALTH] ) { client->ps.stats[STAT_ARMOR]--; } } } /* ==================== ClientIntermissionThink ==================== */ void ClientIntermissionThink( gclient_t *client ) { client->ps.eFlags &= ~EF_TALK; client->ps.eFlags &= ~EF_FIRING; // the level will exit when everyone wants to or after timeouts // swap and latch button actions client->oldbuttons = client->buttons; client->buttons = client->pers.cmd.buttons; if ( client->buttons & ( BUTTON_ATTACK | BUTTON_USE_HOLDABLE ) & ( client->oldbuttons ^ client->buttons ) ) { // this used to be an ^1 but once a player says ready, it should stick client->readyToExit = 1; } } /* ================ ClientEvents Events will be passed on to the clients for presentation, but any server game effects are handled here ================ */ void ClientEvents( gentity_t *ent, int oldEventSequence ) { int i, j; int event; gclient_t *client; int damage; vec3_t dir; vec3_t origin, angles; // qboolean fired; gitem_t *item; gentity_t *drop; client = ent->client; if ( oldEventSequence < client->ps.eventSequence - MAX_PS_EVENTS ) { oldEventSequence = client->ps.eventSequence - MAX_PS_EVENTS; } for ( i = oldEventSequence ; i < client->ps.eventSequence ; i++ ) { event = client->ps.events[ i & (MAX_PS_EVENTS-1) ]; switch ( event ) { case EV_FALL_MEDIUM: case EV_FALL_FAR: if ( ent->s.eType != ET_PLAYER ) { break; // not in the player model } if ( g_dmflags.integer & DF_NO_FALLING ) { break; } if ( event == EV_FALL_FAR ) { damage = 10; } else { damage = 5; } VectorSet (dir, 0, 0, 1); ent->pain_debounce_time = level.time + 200; // no normal pain sound G_Damage (ent, NULL, NULL, NULL, NULL, damage, 0, MOD_FALLING); break; case EV_FIRE_WEAPON: FireWeapon( ent ); break; case EV_USE_ITEM1: // teleporter // drop flags in CTF item = NULL; j = 0; if ( ent->client->ps.powerups[ PW_REDFLAG ] ) { item = BG_FindItemForPowerup( PW_REDFLAG ); j = PW_REDFLAG; } else if ( ent->client->ps.powerups[ PW_BLUEFLAG ] ) { item = BG_FindItemForPowerup( PW_BLUEFLAG ); j = PW_BLUEFLAG; } else if ( ent->client->ps.powerups[ PW_NEUTRALFLAG ] ) { item = BG_FindItemForPowerup( PW_NEUTRALFLAG ); j = PW_NEUTRALFLAG; } if ( item ) { drop = Drop_Item( ent, item, 0 ); // decide how many seconds it has left drop->count = ( ent->client->ps.powerups[ j ] - level.time ) / 1000; if ( drop->count < 1 ) { drop->count = 1; } ent->client->ps.powerups[ j ] = 0; } SelectSpawnPoint( ent->client->ps.origin, origin, angles ); TeleportPlayer( ent, origin, angles ); break; case EV_USE_ITEM2: // medkit ent->health = ent->client->ps.stats[STAT_MAX_HEALTH] + 25; break; default: break; } } } void BotTestSolid(vec3_t origin); /* ============== SendPendingPredictableEvents ============== */ void SendPendingPredictableEvents( playerState_t *ps ) { gentity_t *t; int event, seq; int extEvent, number; // if there are still events pending if ( ps->entityEventSequence < ps->eventSequence ) { // create a temporary entity for this event which is sent to everyone // except the client who generated the event seq = ps->entityEventSequence & (MAX_PS_EVENTS-1); event = ps->events[ seq ] | ( ( ps->entityEventSequence & 3 ) << 8 ); // set external event to zero before calling BG_PlayerStateToEntityState extEvent = ps->externalEvent; ps->externalEvent = 0; // create temporary entity for event t = G_TempEntity( ps->origin, event ); number = t->s.number; BG_PlayerStateToEntityState( ps, &t->s, qtrue ); t->s.number = number; t->s.eType = ET_EVENTS + event; t->s.eFlags |= EF_PLAYER_EVENT; t->s.otherEntityNum = ps->clientNum; // send to everyone except the client who generated the event t->r.svFlags |= SVF_NOTSINGLECLIENT; t->r.singleClient = ps->clientNum; // set back external event ps->externalEvent = extEvent; } } /* ============== ClientThink This will be called once for each client frame, which will usually be a couple times for each server frame on fast clients. If "g_synchronousClients 1" is set, this will be called exactly once for each server frame, which makes for smooth demo recording. ============== */ void ClientThink_real( gentity_t *ent ) { gclient_t *client; pmove_t pm; int oldEventSequence; int msec; usercmd_t *ucmd; client = ent->client; // don't think if the client is not yet connected (and thus not yet spawned in) if (client->pers.connected != CON_CONNECTED) { return; } // mark the time, so the connection sprite can be removed ucmd = &ent->client->pers.cmd; // sanity check the command time to prevent speedup cheating if ( ucmd->serverTime > level.time + 200 ) { ucmd->serverTime = level.time + 200; // G_Printf("serverTime <<<<<\n" ); } if ( ucmd->serverTime < level.time - 1000 ) { ucmd->serverTime = level.time - 1000; // G_Printf("serverTime >>>>>\n" ); } msec = ucmd->serverTime - client->ps.commandTime; // following others may result in bad times, but we still want // to check for follow toggles if ( msec < 1 && client->sess.spectatorState != SPECTATOR_FOLLOW ) { return; } if ( msec > 200 ) { msec = 200; } if ( pmove_msec.integer < 8 ) { trap_Cvar_Set("pmove_msec", "8"); } else if (pmove_msec.integer > 33) { trap_Cvar_Set("pmove_msec", "33"); } if ( pmove_fixed.integer || client->pers.pmoveFixed ) { ucmd->serverTime = ((ucmd->serverTime + pmove_msec.integer-1) / pmove_msec.integer) * pmove_msec.integer; //if (ucmd->serverTime - client->ps.commandTime <= 0) // return; } // // check for exiting intermission // if ( level.intermissiontime ) { ClientIntermissionThink( client ); return; } // spectators don't do much if ( client->sess.sessionTeam == TEAM_SPECTATOR ) { if ( client->sess.spectatorState == SPECTATOR_SCOREBOARD ) { return; } SpectatorThink( ent, ucmd ); return; } // check for inactivity timer, but never drop the local client of a non-dedicated server if ( !ClientInactivityTimer( client ) ) { return; } // clear the rewards if time if ( level.time > client->rewardTime ) { client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ); } if ( client->noclip ) { client->ps.pm_type = PM_NOCLIP; } else if ( client->ps.stats[STAT_HEALTH] <= 0 ) { client->ps.pm_type = PM_DEAD; } else { client->ps.pm_type = PM_NORMAL; } client->ps.gravity = g_gravity.value; // set speed client->ps.speed = g_speed.value; if ( client->ps.powerups[PW_HASTE] ) { client->ps.speed *= 1.3; } // Let go of the hook if we aren't firing if ( client->ps.weapon == WP_GRAPPLING_HOOK && client->hook && !( ucmd->buttons & BUTTON_ATTACK ) ) { Weapon_HookFree(client->hook); } // set up for pmove oldEventSequence = client->ps.eventSequence; memset (&pm, 0, sizeof(pm)); // check for the hit-scan gauntlet, don't let the action // go through as an attack unless it actually hits something if ( client->ps.weapon == WP_GAUNTLET && !( ucmd->buttons & BUTTON_TALK ) && ( ucmd->buttons & BUTTON_ATTACK ) && client->ps.weaponTime <= 0 ) { pm.gauntletHit = CheckGauntletAttack( ent ); } if ( ent->flags & FL_FORCE_GESTURE ) { ent->flags &= ~FL_FORCE_GESTURE; ent->client->pers.cmd.buttons |= BUTTON_GESTURE; } pm.ps = &client->ps; pm.cmd = *ucmd; if ( pm.ps->pm_type == PM_DEAD ) { pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY; } else if ( ent->r.svFlags & SVF_BOT ) { pm.tracemask = MASK_PLAYERSOLID | CONTENTS_BOTCLIP; } else { pm.tracemask = MASK_PLAYERSOLID; } pm.trace = trap_Trace; pm.pointcontents = trap_PointContents; pm.debugLevel = g_debugMove.integer; pm.noFootsteps = ( g_dmflags.integer & DF_NO_FOOTSTEPS ) > 0; pm.pmove_fixed = pmove_fixed.integer | client->pers.pmoveFixed; pm.pmove_msec = pmove_msec.integer; VectorCopy( client->ps.origin, client->oldOrigin ); Pmove (&pm); // save results of pmove if ( ent->client->ps.eventSequence != oldEventSequence ) { ent->eventTime = level.time; } if (g_smoothClients.integer) { BG_PlayerStateToEntityStateExtraPolate( &ent->client->ps, &ent->s, ent->client->ps.commandTime, qtrue ); } else { BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qtrue ); } SendPendingPredictableEvents( &ent->client->ps ); if ( !( ent->client->ps.eFlags & EF_FIRING ) ) { client->fireHeld = qfalse; // for grapple } // use the snapped origin for linking so it matches client predicted versions VectorCopy( ent->s.pos.trBase, ent->r.currentOrigin ); VectorCopy (pm.mins, ent->r.mins); VectorCopy (pm.maxs, ent->r.maxs); ent->waterlevel = pm.waterlevel; ent->watertype = pm.watertype; // execute client events ClientEvents( ent, oldEventSequence ); // link entity now, after any personal teleporters have been used trap_LinkEntity (ent); if ( !ent->client->noclip ) { G_TouchTriggers( ent ); } // NOTE: now copy the exact origin over otherwise clients can be snapped into solid VectorCopy( ent->client->ps.origin, ent->r.currentOrigin ); //test for solid areas in the AAS file BotTestAAS(ent->r.currentOrigin); // touch other objects ClientImpacts( ent, &pm ); // save results of triggers and client events if (ent->client->ps.eventSequence != oldEventSequence) { ent->eventTime = level.time; } // swap and latch button actions client->oldbuttons = client->buttons; client->buttons = ucmd->buttons; client->latched_buttons |= client->buttons & ~client->oldbuttons; // check for respawning if ( client->ps.stats[STAT_HEALTH] <= 0 ) { // wait for the attack button to be pressed if ( level.time > client->respawnTime ) { // forcerespawn is to prevent users from waiting out powerups if ( g_forcerespawn.integer > 0 && ( level.time - client->respawnTime ) > g_forcerespawn.integer * 1000 ) { respawn( ent ); return; } // pressing attack or use is the normal respawn method if ( ucmd->buttons & ( BUTTON_ATTACK | BUTTON_USE_HOLDABLE ) ) { respawn( ent ); } } return; } // perform once-a-second actions ClientTimerActions( ent, msec ); } /* ================== ClientThink A new command has arrived from the client ================== */ void ClientThink( int clientNum ) { gentity_t *ent; ent = g_entities + clientNum; trap_GetUsercmd( clientNum, &ent->client->pers.cmd ); // mark the time we got info, so we can display the // phone jack if they don't get any for a while ent->client->lastCmdTime = level.time; if ( !(ent->r.svFlags & SVF_BOT) && !g_synchronousClients.integer ) { ClientThink_real( ent ); } } void G_RunClient( gentity_t *ent ) { if ( !(ent->r.svFlags & SVF_BOT) && !g_synchronousClients.integer ) { return; } ent->client->pers.cmd.serverTime = level.time; ClientThink_real( ent ); } /* ================== SpectatorClientEndFrame ================== */ void SpectatorClientEndFrame( gentity_t *ent ) { gclient_t *cl; // if we are doing a chase cam or a remote view, grab the latest info if ( ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) { int clientNum, flags; clientNum = ent->client->sess.spectatorClient; // team follow1 and team follow2 go to whatever clients are playing if ( clientNum == -1 ) { clientNum = level.follow1; } else if ( clientNum == -2 ) { clientNum = level.follow2; } if ( clientNum >= 0 ) { cl = &level.clients[ clientNum ]; if ( cl->pers.connected == CON_CONNECTED && cl->sess.sessionTeam != TEAM_SPECTATOR ) { flags = (cl->ps.eFlags & ~(EF_VOTED | EF_TEAMVOTED)) | (ent->client->ps.eFlags & (EF_VOTED | EF_TEAMVOTED)); ent->client->ps = cl->ps; ent->client->ps.pm_flags |= PMF_FOLLOW; ent->client->ps.eFlags = flags; return; } else { // drop them to free spectators unless they are dedicated camera followers if ( ent->client->sess.spectatorClient >= 0 ) { ent->client->sess.spectatorState = SPECTATOR_FREE; ClientBegin( ent->client - level.clients ); } } } } if ( ent->client->sess.spectatorState == SPECTATOR_SCOREBOARD ) { ent->client->ps.pm_flags |= PMF_SCOREBOARD; } else { ent->client->ps.pm_flags &= ~PMF_SCOREBOARD; } } /* ============== ClientEndFrame Called at the end of each server frame for each connected client A fast client will have multiple ClientThink for each ClientEdFrame, while a slow client may have multiple ClientEndFrame between ClientThink. ============== */ void ClientEndFrame( gentity_t *ent ) { int i; clientPersistant_t *pers; if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) { SpectatorClientEndFrame( ent ); return; } pers = &ent->client->pers; // turn off any expired powerups for ( i = 0 ; i < MAX_POWERUPS ; i++ ) { if ( ent->client->ps.powerups[ i ] < level.time ) { ent->client->ps.powerups[ i ] = 0; } } // save network bandwidth #if 0 if ( !g_synchronousClients->integer && ent->client->ps.pm_type == PM_NORMAL ) { // FIXME: this must change eventually for non-sync demo recording VectorClear( ent->client->ps.viewangles ); } #endif // // If the end of unit layout is displayed, don't give // the player any normal movement attributes // if ( level.intermissiontime ) { return; } // burn from lava, etc P_WorldEffects (ent); // apply all the damage taken this frame P_DamageFeedback (ent); // add the EF_CONNECTION flag if we haven't gotten commands recently if ( level.time - ent->client->lastCmdTime > 1000 ) { ent->s.eFlags |= EF_CONNECTION; } else { ent->s.eFlags &= ~EF_CONNECTION; } ent->client->ps.stats[STAT_HEALTH] = ent->health; // FIXME: get rid of ent->health... G_SetClientSound (ent); // set the latest infor if (g_smoothClients.integer) { BG_PlayerStateToEntityStateExtraPolate( &ent->client->ps, &ent->s, ent->client->ps.commandTime, qtrue ); } else { BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qtrue ); } SendPendingPredictableEvents( &ent->client->ps ); // set the bit for the reachability area the client is currently in // i = trap_AAS_PointReachabilityAreaIndex( ent->client->ps.origin ); // ent->client->areabits[i >> 3] |= 1 << (i & 7); } ================================================ FILE: src/game/g_arenas.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // // g_arenas.c // #include "g_local.h" gentity_t *podium1; gentity_t *podium2; gentity_t *podium3; /* ================== UpdateTournamentInfo ================== */ void UpdateTournamentInfo( void ) { int i; gentity_t *player; int playerClientNum; int n, accuracy, perfect, msglen; int buflen; char buf[32]; char msg[MAX_STRING_CHARS]; // find the real player player = NULL; for (i = 0; i < level.maxclients; i++ ) { player = &g_entities[i]; if ( !player->inuse ) { continue; } if ( !( player->r.svFlags & SVF_BOT ) ) { break; } } // this should never happen! if ( !player || i == level.maxclients ) { return; } playerClientNum = i; CalculateRanks(); if ( level.clients[playerClientNum].sess.sessionTeam == TEAM_SPECTATOR ) { Com_sprintf( msg, sizeof(msg), "postgame %i %i 0 0 0 0 0 0", level.numNonSpectatorClients, playerClientNum ); } else { if( player->client->accuracy_shots ) { accuracy = player->client->accuracy_hits * 100 / player->client->accuracy_shots; } else { accuracy = 0; } perfect = ( level.clients[playerClientNum].ps.persistant[PERS_RANK] == 0 && player->client->ps.persistant[PERS_KILLED] == 0 ) ? 1 : 0; Com_sprintf( msg, sizeof(msg), "postgame %i %i %i %i %i %i %i %i", level.numNonSpectatorClients, playerClientNum, accuracy, player->client->ps.persistant[PERS_IMPRESSIVE_COUNT], player->client->ps.persistant[PERS_EXCELLENT_COUNT], player->client->ps.persistant[PERS_GAUNTLET_FRAG_COUNT], player->client->ps.persistant[PERS_SCORE], perfect ); } msglen = (int)strlen( msg ); for( i = 0; i < level.numNonSpectatorClients; i++ ) { n = level.sortedClients[i]; Com_sprintf( buf, sizeof(buf), " %i %i %i", n, level.clients[n].ps.persistant[PERS_RANK], level.clients[n].ps.persistant[PERS_SCORE] ); buflen = (int)strlen( buf ); if( msglen + buflen + 1 >= sizeof(msg) ) { break; } strcat( msg, buf ); } trap_SendConsoleCommand( EXEC_APPEND, msg ); } static gentity_t *SpawnModelOnVictoryPad( gentity_t *pad, vec3_t offset, gentity_t *ent, int place ) { gentity_t *body; vec3_t vec; vec3_t f, r, u; body = G_Spawn(); if ( !body ) { G_Printf( S_COLOR_RED "ERROR: out of gentities\n" ); return NULL; } body->classname = ent->client->pers.netname; body->client = ent->client; body->s = ent->s; body->s.eType = ET_PLAYER; // could be ET_INVISIBLE body->s.eFlags = 0; // clear EF_TALK, etc body->s.powerups = 0; // clear powerups body->s.loopSound = 0; // clear lava burning body->s.number = body - g_entities; body->timestamp = level.time; body->physicsObject = qtrue; body->physicsBounce = 0; // don't bounce body->s.event = 0; body->s.pos.trType = TR_STATIONARY; body->s.groundEntityNum = ENTITYNUM_WORLD; body->s.legsAnim = LEGS_IDLE; body->s.torsoAnim = TORSO_STAND; if( body->s.weapon == WP_NONE ) { body->s.weapon = WP_MACHINEGUN; } if( body->s.weapon == WP_GAUNTLET) { body->s.torsoAnim = TORSO_STAND2; } body->s.event = 0; body->r.svFlags = ent->r.svFlags; VectorCopy (ent->r.mins, body->r.mins); VectorCopy (ent->r.maxs, body->r.maxs); VectorCopy (ent->r.absmin, body->r.absmin); VectorCopy (ent->r.absmax, body->r.absmax); body->clipmask = CONTENTS_SOLID | CONTENTS_PLAYERCLIP; body->r.contents = CONTENTS_BODY; body->r.ownerNum = ent->r.ownerNum; body->takedamage = qfalse; VectorSubtract( level.intermission_origin, pad->r.currentOrigin, vec ); vectoangles( vec, body->s.apos.trBase ); body->s.apos.trBase[PITCH] = 0; body->s.apos.trBase[ROLL] = 0; AngleVectors( body->s.apos.trBase, f, r, u ); VectorMA( pad->r.currentOrigin, offset[0], f, vec ); VectorMA( vec, offset[1], r, vec ); VectorMA( vec, offset[2], u, vec ); G_SetOrigin( body, vec ); trap_LinkEntity (body); body->count = place; return body; } static void CelebrateStop( gentity_t *player ) { int anim; if( player->s.weapon == WP_GAUNTLET) { anim = TORSO_STAND2; } else { anim = TORSO_STAND; } player->s.torsoAnim = ( ( player->s.torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; } #define TIMER_GESTURE (34*66+50) static void CelebrateStart( gentity_t *player ) { player->s.torsoAnim = ( ( player->s.torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | TORSO_GESTURE; player->nextthink = level.time + TIMER_GESTURE; player->think = CelebrateStop; /* player->client->ps.events[player->client->ps.eventSequence & (MAX_PS_EVENTS-1)] = EV_TAUNT; player->client->ps.eventParms[player->client->ps.eventSequence & (MAX_PS_EVENTS-1)] = 0; player->client->ps.eventSequence++; */ G_AddEvent(player, EV_TAUNT, 0); } static vec3_t offsetFirst = {0, 0, 74}; static vec3_t offsetSecond = {-10, 60, 54}; static vec3_t offsetThird = {-19, -60, 45}; static void PodiumPlacementThink( gentity_t *podium ) { vec3_t vec; vec3_t origin; vec3_t f, r, u; podium->nextthink = level.time + 100; AngleVectors( level.intermission_angle, vec, NULL, NULL ); VectorMA( level.intermission_origin, trap_Cvar_VariableIntegerValue( "g_podiumDist" ), vec, origin ); origin[2] -= trap_Cvar_VariableIntegerValue( "g_podiumDrop" ); G_SetOrigin( podium, origin ); if( podium1 ) { VectorSubtract( level.intermission_origin, podium->r.currentOrigin, vec ); vectoangles( vec, podium1->s.apos.trBase ); podium1->s.apos.trBase[PITCH] = 0; podium1->s.apos.trBase[ROLL] = 0; AngleVectors( podium1->s.apos.trBase, f, r, u ); VectorMA( podium->r.currentOrigin, offsetFirst[0], f, vec ); VectorMA( vec, offsetFirst[1], r, vec ); VectorMA( vec, offsetFirst[2], u, vec ); G_SetOrigin( podium1, vec ); } if( podium2 ) { VectorSubtract( level.intermission_origin, podium->r.currentOrigin, vec ); vectoangles( vec, podium2->s.apos.trBase ); podium2->s.apos.trBase[PITCH] = 0; podium2->s.apos.trBase[ROLL] = 0; AngleVectors( podium2->s.apos.trBase, f, r, u ); VectorMA( podium->r.currentOrigin, offsetSecond[0], f, vec ); VectorMA( vec, offsetSecond[1], r, vec ); VectorMA( vec, offsetSecond[2], u, vec ); G_SetOrigin( podium2, vec ); } if( podium3 ) { VectorSubtract( level.intermission_origin, podium->r.currentOrigin, vec ); vectoangles( vec, podium3->s.apos.trBase ); podium3->s.apos.trBase[PITCH] = 0; podium3->s.apos.trBase[ROLL] = 0; AngleVectors( podium3->s.apos.trBase, f, r, u ); VectorMA( podium->r.currentOrigin, offsetThird[0], f, vec ); VectorMA( vec, offsetThird[1], r, vec ); VectorMA( vec, offsetThird[2], u, vec ); G_SetOrigin( podium3, vec ); } } static gentity_t *SpawnPodium( void ) { gentity_t *podium; vec3_t vec; vec3_t origin; podium = G_Spawn(); if ( !podium ) { return NULL; } podium->classname = "podium"; podium->s.eType = ET_GENERAL; podium->s.number = podium - g_entities; podium->clipmask = CONTENTS_SOLID; podium->r.contents = CONTENTS_SOLID; podium->s.modelindex = G_ModelIndex( SP_PODIUM_MODEL ); AngleVectors( level.intermission_angle, vec, NULL, NULL ); VectorMA( level.intermission_origin, trap_Cvar_VariableIntegerValue( "g_podiumDist" ), vec, origin ); origin[2] -= trap_Cvar_VariableIntegerValue( "g_podiumDrop" ); G_SetOrigin( podium, origin ); VectorSubtract( level.intermission_origin, podium->r.currentOrigin, vec ); podium->s.apos.trBase[YAW] = vectoyaw( vec ); trap_LinkEntity (podium); podium->think = PodiumPlacementThink; podium->nextthink = level.time + 100; return podium; } /* ================== SpawnModelsOnVictoryPads ================== */ void SpawnModelsOnVictoryPads( void ) { gentity_t *player; gentity_t *podium; podium1 = NULL; podium2 = NULL; podium3 = NULL; podium = SpawnPodium(); player = SpawnModelOnVictoryPad( podium, offsetFirst, &g_entities[level.sortedClients[0]], level.clients[ level.sortedClients[0] ].ps.persistant[PERS_RANK] &~ RANK_TIED_FLAG ); if ( player ) { player->nextthink = level.time + 2000; player->think = CelebrateStart; podium1 = player; } player = SpawnModelOnVictoryPad( podium, offsetSecond, &g_entities[level.sortedClients[1]], level.clients[ level.sortedClients[1] ].ps.persistant[PERS_RANK] &~ RANK_TIED_FLAG ); if ( player ) { podium2 = player; } if ( level.numNonSpectatorClients > 2 ) { player = SpawnModelOnVictoryPad( podium, offsetThird, &g_entities[level.sortedClients[2]], level.clients[ level.sortedClients[2] ].ps.persistant[PERS_RANK] &~ RANK_TIED_FLAG ); if ( player ) { podium3 = player; } } } /* =============== Svcmd_AbortPodium_f =============== */ void Svcmd_AbortPodium_f( void ) { if( g_gametype.integer != GT_SINGLE_PLAYER ) { return; } if( podium1 ) { podium1->nextthink = level.time; podium1->think = CelebrateStop; } } ================================================ FILE: src/game/g_bot.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // g_bot.c #include "g_local.h" static int g_numBots; static char *g_botInfos[MAX_BOTS]; int g_numArenas; static char *g_arenaInfos[MAX_ARENAS]; #define BOT_BEGIN_DELAY_BASE 2000 #define BOT_BEGIN_DELAY_INCREMENT 1500 #define BOT_SPAWN_QUEUE_DEPTH 16 typedef struct { int clientNum; int spawnTime; } botSpawnQueue_t; //static int botBeginDelay = 0; // bk001206 - unused, init static botSpawnQueue_t botSpawnQueue[BOT_SPAWN_QUEUE_DEPTH]; vmCvar_t bot_minplayers; extern gentity_t *podium1; extern gentity_t *podium2; extern gentity_t *podium3; float trap_Cvar_VariableValue( const char *var_name ) { char buf[128]; trap_Cvar_VariableStringBuffer(var_name, buf, sizeof(buf)); return atof(buf); } /* =============== G_ParseInfos =============== */ int G_ParseInfos( char *buf, int max, char *infos[] ) { char *token; int count; char key[MAX_TOKEN_CHARS]; char info[MAX_INFO_STRING]; count = 0; while ( 1 ) { token = COM_Parse( &buf ); if ( !token[0] ) { break; } if ( strcmp( token, "{" ) ) { Com_Printf( "Missing { in info file\n" ); break; } if ( count == max ) { Com_Printf( "Max infos exceeded\n" ); break; } info[0] = '\0'; while ( 1 ) { token = COM_ParseExt( &buf, qtrue ); if ( !token[0] ) { Com_Printf( "Unexpected end of info file\n" ); break; } if ( !strcmp( token, "}" ) ) { break; } Q_strncpyz( key, token, sizeof( key ) ); token = COM_ParseExt( &buf, qfalse ); if ( !token[0] ) { strcpy( token, "" ); } Info_SetValueForKey( info, key, token ); } //NOTE: extra space for arena number infos[count] = G_Alloc((int)strlen(info) + (int)strlen("\\num\\") + (int)strlen(va("%d", MAX_ARENAS)) + 1); if (infos[count]) { strcpy(infos[count], info); count++; } } return count; } /* =============== G_LoadArenasFromFile =============== */ static void G_LoadArenasFromFile( char *filename ) { int len; fileHandle_t f; char buf[MAX_ARENAS_TEXT]; len = trap_FS_FOpenFile( filename, &f, FS_READ ); if ( !f ) { trap_Printf( va( S_COLOR_RED "file not found: %s\n", filename ) ); return; } if ( len >= MAX_ARENAS_TEXT ) { trap_Printf( va( S_COLOR_RED "file too large: %s is %i, max allowed is %i", filename, len, MAX_ARENAS_TEXT ) ); trap_FS_FCloseFile( f ); return; } trap_FS_Read( buf, len, f ); buf[len] = 0; trap_FS_FCloseFile( f ); g_numArenas += G_ParseInfos( buf, MAX_ARENAS - g_numArenas, &g_arenaInfos[g_numArenas] ); } /* =============== G_LoadArenas =============== */ static void G_LoadArenas( void ) { int numdirs; vmCvar_t arenasFile; char filename[128]; char dirlist[1024]; char* dirptr; int i, n; int dirlen; g_numArenas = 0; trap_Cvar_Register( &arenasFile, "g_arenasFile", "", CVAR_INIT|CVAR_ROM ); if( *arenasFile.string ) { G_LoadArenasFromFile(arenasFile.string); } else { G_LoadArenasFromFile("scripts/arenas.txt"); } // get all arenas from .arena files numdirs = trap_FS_GetFileList("scripts", ".arena", dirlist, 1024 ); dirptr = dirlist; for (i = 0; i < numdirs; i++, dirptr += dirlen+1) { dirlen = (int)strlen(dirptr); strcpy(filename, "scripts/"); strcat(filename, dirptr); G_LoadArenasFromFile(filename); } trap_Printf( va( "%i arenas parsed\n", g_numArenas ) ); for( n = 0; n < g_numArenas; n++ ) { Info_SetValueForKey( g_arenaInfos[n], "num", va( "%i", n ) ); } } /* =============== G_GetArenaInfoByNumber =============== */ const char *G_GetArenaInfoByMap( const char *map ) { int n; for( n = 0; n < g_numArenas; n++ ) { if( Q_stricmp( Info_ValueForKey( g_arenaInfos[n], "map" ), map ) == 0 ) { return g_arenaInfos[n]; } } return NULL; } /* ================= PlayerIntroSound ================= */ static void PlayerIntroSound( const char *modelAndSkin ) { char model[MAX_QPATH]; char *skin; Q_strncpyz( model, modelAndSkin, sizeof(model) ); skin = Q_strrchr( model, '/' ); if ( skin ) { *skin++ = '\0'; } else { skin = model; } if( Q_stricmp( skin, "default" ) == 0 ) { skin = model; } trap_SendConsoleCommand( EXEC_APPEND, va( "play sound/player/announce/%s.wav\n", skin ) ); } /* =============== G_AddRandomBot =============== */ void G_AddRandomBot( int team ) { int i, n, num; float skill; char *value, netname[36], *teamstr; gclient_t *cl; num = 0; for ( n = 0; n < g_numBots ; n++ ) { value = Info_ValueForKey( g_botInfos[n], "name" ); // for ( i=0 ; i< g_maxclients.integer ; i++ ) { cl = level.clients + i; if ( cl->pers.connected != CON_CONNECTED ) { continue; } if ( !(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT) ) { continue; } if ( team >= 0 && cl->sess.sessionTeam != team ) { continue; } if ( !Q_stricmp( value, cl->pers.netname ) ) { break; } } if (i >= g_maxclients.integer) { num++; } } num = random() * num; for ( n = 0; n < g_numBots ; n++ ) { value = Info_ValueForKey( g_botInfos[n], "name" ); // for ( i=0 ; i< g_maxclients.integer ; i++ ) { cl = level.clients + i; if ( cl->pers.connected != CON_CONNECTED ) { continue; } if ( !(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT) ) { continue; } if ( team >= 0 && cl->sess.sessionTeam != team ) { continue; } if ( !Q_stricmp( value, cl->pers.netname ) ) { break; } } if (i >= g_maxclients.integer) { num--; if (num <= 0) { skill = trap_Cvar_VariableValue( "g_spSkill" ); if (team == TEAM_RED) teamstr = "red"; else if (team == TEAM_BLUE) teamstr = "blue"; else teamstr = ""; strncpy(netname, value, sizeof(netname)-1); netname[sizeof(netname)-1] = '\0'; Q_CleanStr(netname); trap_SendConsoleCommand( EXEC_INSERT, va("addbot %s %f %s %i\n", netname, skill, teamstr, 0) ); return; } } } } /* =============== G_RemoveRandomBot =============== */ int G_RemoveRandomBot( int team ) { int i; char netname[36]; gclient_t *cl; for ( i=0 ; i< g_maxclients.integer ; i++ ) { cl = level.clients + i; if ( cl->pers.connected != CON_CONNECTED ) { continue; } if ( !(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT) ) { continue; } if ( team >= 0 && cl->sess.sessionTeam != team ) { continue; } strcpy(netname, cl->pers.netname); Q_CleanStr(netname); trap_SendConsoleCommand( EXEC_INSERT, va("kick %s\n", netname) ); return qtrue; } return qfalse; } /* =============== G_CountHumanPlayers =============== */ int G_CountHumanPlayers( int team ) { int i, num; gclient_t *cl; num = 0; for ( i=0 ; i< g_maxclients.integer ; i++ ) { cl = level.clients + i; if ( cl->pers.connected != CON_CONNECTED ) { continue; } if ( g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT ) { continue; } if ( team >= 0 && cl->sess.sessionTeam != team ) { continue; } num++; } return num; } /* =============== G_CountBotPlayers =============== */ int G_CountBotPlayers( int team ) { int i, n, num; gclient_t *cl; num = 0; for ( i=0 ; i< g_maxclients.integer ; i++ ) { cl = level.clients + i; if ( cl->pers.connected != CON_CONNECTED ) { continue; } if ( !(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT) ) { continue; } if ( team >= 0 && cl->sess.sessionTeam != team ) { continue; } num++; } for( n = 0; n < BOT_SPAWN_QUEUE_DEPTH; n++ ) { if( !botSpawnQueue[n].spawnTime ) { continue; } if ( botSpawnQueue[n].spawnTime > level.time ) { continue; } num++; } return num; } /* =============== G_CheckMinimumPlayers =============== */ void G_CheckMinimumPlayers( void ) { int minplayers; int humanplayers, botplayers; static int checkminimumplayers_time; if (level.intermissiontime) return; //only check once each 10 seconds if (checkminimumplayers_time > level.time - 10000) { return; } checkminimumplayers_time = level.time; trap_Cvar_Update(&bot_minplayers); minplayers = bot_minplayers.integer; if (minplayers <= 0) return; if (g_gametype.integer >= GT_TEAM) { if (minplayers >= g_maxclients.integer / 2) { minplayers = (g_maxclients.integer / 2) -1; } humanplayers = G_CountHumanPlayers( TEAM_RED ); botplayers = G_CountBotPlayers( TEAM_RED ); // if (humanplayers + botplayers < minplayers) { G_AddRandomBot( TEAM_RED ); } else if (humanplayers + botplayers > minplayers && botplayers) { G_RemoveRandomBot( TEAM_RED ); } // humanplayers = G_CountHumanPlayers( TEAM_BLUE ); botplayers = G_CountBotPlayers( TEAM_BLUE ); // if (humanplayers + botplayers < minplayers) { G_AddRandomBot( TEAM_BLUE ); } else if (humanplayers + botplayers > minplayers && botplayers) { G_RemoveRandomBot( TEAM_BLUE ); } } else if (g_gametype.integer == GT_TOURNAMENT ) { if (minplayers >= g_maxclients.integer) { minplayers = g_maxclients.integer-1; } humanplayers = G_CountHumanPlayers( -1 ); botplayers = G_CountBotPlayers( -1 ); // if (humanplayers + botplayers < minplayers) { G_AddRandomBot( TEAM_FREE ); } else if (humanplayers + botplayers > minplayers && botplayers) { // try to remove spectators first if (!G_RemoveRandomBot( TEAM_SPECTATOR )) { // just remove the bot that is playing G_RemoveRandomBot( -1 ); } } } else if (g_gametype.integer == GT_FFA) { if (minplayers >= g_maxclients.integer) { minplayers = g_maxclients.integer-1; } humanplayers = G_CountHumanPlayers( TEAM_FREE ); botplayers = G_CountBotPlayers( TEAM_FREE ); // if (humanplayers + botplayers < minplayers) { G_AddRandomBot( TEAM_FREE ); } else if (humanplayers + botplayers > minplayers && botplayers) { G_RemoveRandomBot( TEAM_FREE ); } } } /* =============== G_CheckBotSpawn =============== */ void G_CheckBotSpawn( void ) { int n; char userinfo[MAX_INFO_VALUE]; G_CheckMinimumPlayers(); for( n = 0; n < BOT_SPAWN_QUEUE_DEPTH; n++ ) { if( !botSpawnQueue[n].spawnTime ) { continue; } if ( botSpawnQueue[n].spawnTime > level.time ) { continue; } ClientBegin( botSpawnQueue[n].clientNum ); botSpawnQueue[n].spawnTime = 0; if( g_gametype.integer == GT_SINGLE_PLAYER ) { trap_GetUserinfo( botSpawnQueue[n].clientNum, userinfo, sizeof(userinfo) ); PlayerIntroSound( Info_ValueForKey (userinfo, "model") ); } } } /* =============== AddBotToSpawnQueue =============== */ static void AddBotToSpawnQueue( int clientNum, int delay ) { int n; for( n = 0; n < BOT_SPAWN_QUEUE_DEPTH; n++ ) { if( !botSpawnQueue[n].spawnTime ) { botSpawnQueue[n].spawnTime = level.time + delay; botSpawnQueue[n].clientNum = clientNum; return; } } G_Printf( S_COLOR_YELLOW "Unable to delay spawn\n" ); ClientBegin( clientNum ); } /* =============== G_RemoveQueuedBotBegin Called on client disconnect to make sure the delayed spawn doesn't happen on a freed index =============== */ void G_RemoveQueuedBotBegin( int clientNum ) { int n; for( n = 0; n < BOT_SPAWN_QUEUE_DEPTH; n++ ) { if( botSpawnQueue[n].clientNum == clientNum ) { botSpawnQueue[n].spawnTime = 0; return; } } } /* =============== G_BotConnect =============== */ qboolean G_BotConnect( int clientNum, qboolean restart ) { bot_settings_t settings; char userinfo[MAX_INFO_STRING]; trap_GetUserinfo( clientNum, userinfo, sizeof(userinfo) ); Q_strncpyz( settings.characterfile, Info_ValueForKey( userinfo, "characterfile" ), sizeof(settings.characterfile) ); settings.skill = atof( Info_ValueForKey( userinfo, "skill" ) ); Q_strncpyz( settings.team, Info_ValueForKey( userinfo, "team" ), sizeof(settings.team) ); if (!BotAISetupClient( clientNum, &settings, restart )) { trap_DropClient( clientNum, "BotAISetupClient failed" ); return qfalse; } return qtrue; } /* =============== G_AddBot =============== */ static void G_AddBot( const char *name, float skill, const char *team, int delay, char *altname) { int clientNum; char *botinfo; gentity_t *bot; char *key; char *s; char *botname; char *model; char *headmodel; char userinfo[MAX_INFO_STRING]; // get the botinfo from bots.txt botinfo = G_GetBotInfoByName( name ); if ( !botinfo ) { G_Printf( S_COLOR_RED "Error: Bot '%s' not defined\n", name ); return; } // create the bot's userinfo userinfo[0] = '\0'; botname = Info_ValueForKey( botinfo, "funname" ); if( !botname[0] ) { botname = Info_ValueForKey( botinfo, "name" ); } // check for an alternative name if (altname && altname[0]) { botname = altname; } Info_SetValueForKey( userinfo, "name", botname ); Info_SetValueForKey( userinfo, "rate", "25000" ); Info_SetValueForKey( userinfo, "snaps", "20" ); Info_SetValueForKey( userinfo, "skill", va("%1.2f", skill) ); if ( skill >= 1 && skill < 2 ) { Info_SetValueForKey( userinfo, "handicap", "50" ); } else if ( skill >= 2 && skill < 3 ) { Info_SetValueForKey( userinfo, "handicap", "70" ); } else if ( skill >= 3 && skill < 4 ) { Info_SetValueForKey( userinfo, "handicap", "90" ); } key = "model"; model = Info_ValueForKey( botinfo, key ); if ( !*model ) { model = "visor/default"; } Info_SetValueForKey( userinfo, key, model ); key = "team_model"; Info_SetValueForKey( userinfo, key, model ); key = "headmodel"; headmodel = Info_ValueForKey( botinfo, key ); if ( !*headmodel ) { headmodel = model; } Info_SetValueForKey( userinfo, key, headmodel ); key = "team_headmodel"; Info_SetValueForKey( userinfo, key, headmodel ); key = "gender"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "male"; } Info_SetValueForKey( userinfo, "sex", s ); key = "color1"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "4"; } Info_SetValueForKey( userinfo, key, s ); key = "color2"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "5"; } Info_SetValueForKey( userinfo, key, s ); s = Info_ValueForKey(botinfo, "aifile"); if (!*s ) { trap_Printf( S_COLOR_RED "Error: bot has no aifile specified\n" ); return; } // have the server allocate a client slot clientNum = trap_BotAllocateClient(); if ( clientNum == -1 ) { G_Printf( S_COLOR_RED "Unable to add bot. All player slots are in use.\n" ); G_Printf( S_COLOR_RED "Start server with more 'open' slots (or check setting of sv_maxclients cvar).\n" ); return; } // initialize the bot settings if( !team || !*team ) { if( g_gametype.integer >= GT_TEAM ) { if( PickTeam(clientNum) == TEAM_RED) { team = "red"; } else { team = "blue"; } } else { team = "red"; } } Info_SetValueForKey( userinfo, "characterfile", Info_ValueForKey( botinfo, "aifile" ) ); Info_SetValueForKey( userinfo, "skill", va( "%5.2f", skill ) ); Info_SetValueForKey( userinfo, "team", team ); bot = &g_entities[ clientNum ]; bot->r.svFlags |= SVF_BOT; bot->inuse = qtrue; // register the userinfo trap_SetUserinfo( clientNum, userinfo ); // have it connect to the game as a normal client if ( ClientConnect( clientNum, qtrue, qtrue ) ) { return; } if( delay == 0 ) { ClientBegin( clientNum ); return; } AddBotToSpawnQueue( clientNum, delay ); } /* =============== Svcmd_AddBot_f =============== */ void Svcmd_AddBot_f( void ) { float skill; int delay; char name[MAX_TOKEN_CHARS]; char altname[MAX_TOKEN_CHARS]; char string[MAX_TOKEN_CHARS]; char team[MAX_TOKEN_CHARS]; // are bots enabled? if ( !trap_Cvar_VariableIntegerValue( "bot_enable" ) ) { return; } // name trap_Argv( 1, name, sizeof( name ) ); if ( !name[0] ) { trap_Printf( "Usage: Addbot [skill 1-5] [team] [msec delay] [altname]\n" ); return; } // skill trap_Argv( 2, string, sizeof( string ) ); if ( !string[0] ) { skill = 4; } else { skill = atof( string ); } // team trap_Argv( 3, team, sizeof( team ) ); // delay trap_Argv( 4, string, sizeof( string ) ); if ( !string[0] ) { delay = 0; } else { delay = atoi( string ); } // alternative name trap_Argv( 5, altname, sizeof( altname ) ); G_AddBot( name, skill, team, delay, altname ); // if this was issued during gameplay and we are playing locally, // go ahead and load the bot's media immediately if ( level.time - level.startTime > 1000 && trap_Cvar_VariableIntegerValue( "cl_running" ) ) { trap_SendServerCommand( -1, "loaddefered\n" ); // FIXME: spelled wrong, but not changing for demo } } /* =============== Svcmd_BotList_f =============== */ void Svcmd_BotList_f( void ) { int i; char name[MAX_TOKEN_CHARS]; char funname[MAX_TOKEN_CHARS]; char model[MAX_TOKEN_CHARS]; char aifile[MAX_TOKEN_CHARS]; trap_Printf("^1name model aifile funname\n"); for (i = 0; i < g_numBots; i++) { strcpy(name, Info_ValueForKey( g_botInfos[i], "name" )); if ( !*name ) { strcpy(name, "UnnamedPlayer"); } strcpy(funname, Info_ValueForKey( g_botInfos[i], "funname" )); if ( !*funname ) { strcpy(funname, ""); } strcpy(model, Info_ValueForKey( g_botInfos[i], "model" )); if ( !*model ) { strcpy(model, "visor/default"); } strcpy(aifile, Info_ValueForKey( g_botInfos[i], "aifile")); if (!*aifile ) { strcpy(aifile, "bots/default_c.c"); } trap_Printf(va("%-16s %-16s %-20s %-20s\n", name, model, aifile, funname)); } } /* =============== G_SpawnBots =============== */ static void G_SpawnBots( char *botList, int baseDelay ) { char *bot; char *p; float skill; int delay; char bots[MAX_INFO_VALUE]; podium1 = NULL; podium2 = NULL; podium3 = NULL; skill = trap_Cvar_VariableValue( "g_spSkill" ); if( skill < 1 ) { trap_Cvar_Set( "g_spSkill", "1" ); skill = 1; } else if ( skill > 5 ) { trap_Cvar_Set( "g_spSkill", "5" ); skill = 5; } Q_strncpyz( bots, botList, sizeof(bots) ); p = &bots[0]; delay = baseDelay; while( *p ) { //skip spaces while( *p && *p == ' ' ) { p++; } if( !p ) { break; } // mark start of bot name bot = p; // skip until space of null while( *p && *p != ' ' ) { p++; } if( *p ) { *p++ = 0; } // we must add the bot this way, calling G_AddBot directly at this stage // does "Bad Things" trap_SendConsoleCommand( EXEC_INSERT, va("addbot %s %f free %i\n", bot, skill, delay) ); delay += BOT_BEGIN_DELAY_INCREMENT; } } /* =============== G_LoadBotsFromFile =============== */ static void G_LoadBotsFromFile( char *filename ) { int len; fileHandle_t f; char buf[MAX_BOTS_TEXT]; len = trap_FS_FOpenFile( filename, &f, FS_READ ); if ( !f ) { trap_Printf( va( S_COLOR_RED "file not found: %s\n", filename ) ); return; } if ( len >= MAX_BOTS_TEXT ) { trap_Printf( va( S_COLOR_RED "file too large: %s is %i, max allowed is %i", filename, len, MAX_BOTS_TEXT ) ); trap_FS_FCloseFile( f ); return; } trap_FS_Read( buf, len, f ); buf[len] = 0; trap_FS_FCloseFile( f ); g_numBots += G_ParseInfos( buf, MAX_BOTS - g_numBots, &g_botInfos[g_numBots] ); } /* =============== G_LoadBots =============== */ static void G_LoadBots( void ) { vmCvar_t botsFile; int numdirs; char filename[128]; char dirlist[1024]; char* dirptr; int i; int dirlen; if ( !trap_Cvar_VariableIntegerValue( "bot_enable" ) ) { return; } g_numBots = 0; trap_Cvar_Register( &botsFile, "g_botsFile", "", CVAR_INIT|CVAR_ROM ); if( *botsFile.string ) { G_LoadBotsFromFile(botsFile.string); } else { G_LoadBotsFromFile("scripts/bots.txt"); } // get all bots from .bot files numdirs = trap_FS_GetFileList("scripts", ".bot", dirlist, 1024 ); dirptr = dirlist; for (i = 0; i < numdirs; i++, dirptr += dirlen+1) { dirlen = (int)strlen(dirptr); strcpy(filename, "scripts/"); strcat(filename, dirptr); G_LoadBotsFromFile(filename); } trap_Printf( va( "%i bots parsed\n", g_numBots ) ); } /* =============== G_GetBotInfoByNumber =============== */ char *G_GetBotInfoByNumber( int num ) { if( num < 0 || num >= g_numBots ) { trap_Printf( va( S_COLOR_RED "Invalid bot number: %i\n", num ) ); return NULL; } return g_botInfos[num]; } /* =============== G_GetBotInfoByName =============== */ char *G_GetBotInfoByName( const char *name ) { int n; char *value; for ( n = 0; n < g_numBots ; n++ ) { value = Info_ValueForKey( g_botInfos[n], "name" ); if ( !Q_stricmp( value, name ) ) { return g_botInfos[n]; } } return NULL; } /* =============== G_InitBots =============== */ void G_InitBots( qboolean restart ) { int fragLimit; int timeLimit; const char *arenainfo; char *strValue; int basedelay; char map[MAX_QPATH]; char serverinfo[MAX_INFO_STRING]; G_LoadBots(); G_LoadArenas(); trap_Cvar_Register( &bot_minplayers, "bot_minplayers", "0", CVAR_SERVERINFO ); if( g_gametype.integer == GT_SINGLE_PLAYER ) { trap_GetServerinfo( serverinfo, sizeof(serverinfo) ); Q_strncpyz( map, Info_ValueForKey( serverinfo, "mapname" ), sizeof(map) ); arenainfo = G_GetArenaInfoByMap( map ); if ( !arenainfo ) { return; } strValue = Info_ValueForKey( arenainfo, "fraglimit" ); fragLimit = atoi( strValue ); if ( fragLimit ) { trap_Cvar_Set( "fraglimit", strValue ); } else { trap_Cvar_Set( "fraglimit", "0" ); } strValue = Info_ValueForKey( arenainfo, "timelimit" ); timeLimit = atoi( strValue ); if ( timeLimit ) { trap_Cvar_Set( "timelimit", strValue ); } else { trap_Cvar_Set( "timelimit", "0" ); } if ( !fragLimit && !timeLimit ) { trap_Cvar_Set( "fraglimit", "10" ); trap_Cvar_Set( "timelimit", "0" ); } basedelay = BOT_BEGIN_DELAY_BASE; strValue = Info_ValueForKey( arenainfo, "special" ); if( Q_stricmp( strValue, "training" ) == 0 ) { basedelay += 10000; } if( !restart ) { G_SpawnBots( Info_ValueForKey( arenainfo, "bots" ), basedelay ); } } } ================================================ FILE: src/game/g_client.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #include "g_local.h" // g_client.c -- client functions that don't happen every frame static vec3_t playerMins = {-15, -15, -24}; static vec3_t playerMaxs = {15, 15, 32}; /*QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 32) initial potential spawning position for deathmatch games. The first time a player enters the game, they will be at an 'initial' spot. Targets will be fired when someone spawns in on them. "nobots" will prevent bots from using this spot. "nohumans" will prevent non-bots from using this spot. */ void SP_info_player_deathmatch( gentity_t *ent ) { int i; G_SpawnInt( "nobots", "0", &i); if ( i ) { ent->flags |= FL_NO_BOTS; } G_SpawnInt( "nohumans", "0", &i ); if ( i ) { ent->flags |= FL_NO_HUMANS; } } /*QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 32) equivelant to info_player_deathmatch */ void SP_info_player_start(gentity_t *ent) { ent->classname = "info_player_deathmatch"; SP_info_player_deathmatch( ent ); } /*QUAKED info_player_intermission (1 0 1) (-16 -16 -24) (16 16 32) The intermission will be viewed from this point. Target an info_notnull for the view direction. */ void SP_info_player_intermission( gentity_t *ent ) { } /* ======================================================================= SelectSpawnPoint ======================================================================= */ /* ================ SpotWouldTelefrag ================ */ qboolean SpotWouldTelefrag( gentity_t *spot ) { int i, num; int touch[MAX_GENTITIES]; gentity_t *hit; vec3_t mins, maxs; VectorAdd( spot->s.origin, playerMins, mins ); VectorAdd( spot->s.origin, playerMaxs, maxs ); num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES ); for (i=0 ; iclient && hit->client->ps.stats[STAT_HEALTH] > 0 ) { if ( hit->client) { return qtrue; } } return qfalse; } /* ================ SelectNearestDeathmatchSpawnPoint Find the spot that we DON'T want to use ================ */ #define MAX_SPAWN_POINTS 128 gentity_t *SelectNearestDeathmatchSpawnPoint( vec3_t from ) { gentity_t *spot; vec3_t delta; float dist, nearestDist; gentity_t *nearestSpot; nearestDist = 999999; nearestSpot = NULL; spot = NULL; while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) { VectorSubtract( spot->s.origin, from, delta ); dist = VectorLength( delta ); if ( dist < nearestDist ) { nearestDist = dist; nearestSpot = spot; } } return nearestSpot; } /* ================ SelectRandomDeathmatchSpawnPoint go to a random point that doesn't telefrag ================ */ #define MAX_SPAWN_POINTS 128 gentity_t *SelectRandomDeathmatchSpawnPoint( void ) { gentity_t *spot; int count; int selection; gentity_t *spots[MAX_SPAWN_POINTS]; count = 0; spot = NULL; while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) { if ( SpotWouldTelefrag( spot ) ) { continue; } spots[ count ] = spot; count++; } if ( !count ) { // no spots that won't telefrag return G_Find( NULL, FOFS(classname), "info_player_deathmatch"); } selection = rand() % count; return spots[ selection ]; } /* =========== SelectRandomFurthestSpawnPoint Chooses a player start, deathmatch start, etc ============ */ gentity_t *SelectRandomFurthestSpawnPoint ( vec3_t avoidPoint, vec3_t origin, vec3_t angles ) { gentity_t *spot; vec3_t delta; float dist; float list_dist[64]; gentity_t *list_spot[64]; int numSpots, rnd, i, j; numSpots = 0; spot = NULL; while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) { if ( SpotWouldTelefrag( spot ) ) { continue; } VectorSubtract( spot->s.origin, avoidPoint, delta ); dist = VectorLength( delta ); for (i = 0; i < numSpots; i++) { if ( dist > list_dist[i] ) { if ( numSpots >= 64 ) numSpots = 64-1; for (j = numSpots; j > i; j--) { list_dist[j] = list_dist[j-1]; list_spot[j] = list_spot[j-1]; } list_dist[i] = dist; list_spot[i] = spot; numSpots++; if (numSpots > 64) numSpots = 64; break; } } if (i >= numSpots && numSpots < 64) { list_dist[numSpots] = dist; list_spot[numSpots] = spot; numSpots++; } } if (!numSpots) { spot = G_Find( NULL, FOFS(classname), "info_player_deathmatch"); if (!spot) G_Error( "Couldn't find a spawn point" ); VectorCopy (spot->s.origin, origin); origin[2] += 9; VectorCopy (spot->s.angles, angles); return spot; } // select a random spot from the spawn points furthest away rnd = random() * (numSpots / 2); VectorCopy (list_spot[rnd]->s.origin, origin); origin[2] += 9; VectorCopy (list_spot[rnd]->s.angles, angles); return list_spot[rnd]; } /* =========== SelectSpawnPoint Chooses a player start, deathmatch start, etc ============ */ gentity_t *SelectSpawnPoint ( vec3_t avoidPoint, vec3_t origin, vec3_t angles ) { return SelectRandomFurthestSpawnPoint( avoidPoint, origin, angles ); /* gentity_t *spot; gentity_t *nearestSpot; nearestSpot = SelectNearestDeathmatchSpawnPoint( avoidPoint ); spot = SelectRandomDeathmatchSpawnPoint ( ); if ( spot == nearestSpot ) { // roll again if it would be real close to point of death spot = SelectRandomDeathmatchSpawnPoint ( ); if ( spot == nearestSpot ) { // last try spot = SelectRandomDeathmatchSpawnPoint ( ); } } // find a single player start spot if (!spot) { G_Error( "Couldn't find a spawn point" ); } VectorCopy (spot->s.origin, origin); origin[2] += 9; VectorCopy (spot->s.angles, angles); return spot; */ } /* =========== SelectInitialSpawnPoint Try to find a spawn point marked 'initial', otherwise use normal spawn selection. ============ */ gentity_t *SelectInitialSpawnPoint( vec3_t origin, vec3_t angles ) { gentity_t *spot; spot = NULL; while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) { if ( spot->spawnflags & 1 ) { break; } } if ( !spot || SpotWouldTelefrag( spot ) ) { return SelectSpawnPoint( vec3_origin, origin, angles ); } VectorCopy (spot->s.origin, origin); origin[2] += 9; VectorCopy (spot->s.angles, angles); return spot; } /* =========== SelectSpectatorSpawnPoint ============ */ gentity_t *SelectSpectatorSpawnPoint( vec3_t origin, vec3_t angles ) { FindIntermissionPoint(); VectorCopy( level.intermission_origin, origin ); VectorCopy( level.intermission_angle, angles ); return NULL; } /* ======================================================================= BODYQUE ======================================================================= */ /* =============== InitBodyQue =============== */ void InitBodyQue (void) { int i; gentity_t *ent; level.bodyQueIndex = 0; for (i=0; iclassname = "bodyque"; ent->neverFree = qtrue; level.bodyQue[i] = ent; } } /* ============= BodySink After sitting around for five seconds, fall into the ground and dissapear ============= */ void BodySink( gentity_t *ent ) { if ( level.time - ent->timestamp > 6500 ) { // the body ques are never actually freed, they are just unlinked trap_UnlinkEntity( ent ); ent->physicsObject = qfalse; return; } ent->nextthink = level.time + 100; ent->s.pos.trBase[2] -= 1; } /* ============= CopyToBodyQue A player is respawning, so make an entity that looks just like the existing corpse to leave behind. ============= */ void CopyToBodyQue( gentity_t *ent ) { gentity_t *body; int contents; trap_UnlinkEntity (ent); // if client is in a nodrop area, don't leave the body contents = trap_PointContents( ent->s.origin, -1 ); if ( contents & CONTENTS_NODROP ) { return; } // grab a body que and cycle to the next one body = level.bodyQue[ level.bodyQueIndex ]; level.bodyQueIndex = (level.bodyQueIndex + 1) % BODY_QUEUE_SIZE; trap_UnlinkEntity (body); body->s = ent->s; body->s.eFlags = EF_DEAD; // clear EF_TALK, etc body->s.powerups = 0; // clear powerups body->s.loopSound = 0; // clear lava burning body->s.number = body - g_entities; body->timestamp = level.time; body->physicsObject = qtrue; body->physicsBounce = 0; // don't bounce if ( body->s.groundEntityNum == ENTITYNUM_NONE ) { body->s.pos.trType = TR_GRAVITY; body->s.pos.trTime = level.time; VectorCopy( ent->client->ps.velocity, body->s.pos.trDelta ); } else { body->s.pos.trType = TR_STATIONARY; } body->s.event = 0; // change the animation to the last-frame only, so the sequence // doesn't repeat anew for the body switch ( body->s.legsAnim & ~ANIM_TOGGLEBIT ) { case BOTH_DEATH1: case BOTH_DEAD1: body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD1; break; case BOTH_DEATH2: case BOTH_DEAD2: body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD2; break; case BOTH_DEATH3: case BOTH_DEAD3: default: body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD3; break; } body->r.svFlags = ent->r.svFlags; VectorCopy (ent->r.mins, body->r.mins); VectorCopy (ent->r.maxs, body->r.maxs); VectorCopy (ent->r.absmin, body->r.absmin); VectorCopy (ent->r.absmax, body->r.absmax); body->clipmask = CONTENTS_SOLID | CONTENTS_PLAYERCLIP; body->r.contents = CONTENTS_CORPSE; body->r.ownerNum = ent->s.number; body->nextthink = level.time + 5000; body->think = BodySink; body->die = body_die; // don't take more damage if already gibbed if ( ent->health <= GIB_HEALTH ) { body->takedamage = qfalse; } else { body->takedamage = qtrue; } VectorCopy ( body->s.pos.trBase, body->r.currentOrigin ); trap_LinkEntity (body); } //====================================================================== /* ================== SetClientViewAngle ================== */ void SetClientViewAngle( gentity_t *ent, vec3_t angle ) { int i; // set the delta angle for (i=0 ; i<3 ; i++) { int cmdAngle; cmdAngle = ANGLE2SHORT(angle[i]); ent->client->ps.delta_angles[i] = cmdAngle - ent->client->pers.cmd.angles[i]; } VectorCopy( angle, ent->s.angles ); VectorCopy (ent->s.angles, ent->client->ps.viewangles); } /* ================ respawn ================ */ void respawn( gentity_t *ent ) { gentity_t *tent; CopyToBodyQue (ent); ClientSpawn(ent); // add a teleportation effect tent = G_TempEntity( ent->client->ps.origin, EV_PLAYER_TELEPORT_IN ); tent->s.clientNum = ent->s.clientNum; } /* ================ TeamCount Returns number of players on a team ================ */ team_t TeamCount( int ignoreClientNum, int team ) { int i; int count = 0; for ( i = 0 ; i < level.maxclients ; i++ ) { if ( i == ignoreClientNum ) { continue; } if ( level.clients[i].pers.connected == CON_DISCONNECTED ) { continue; } if ( level.clients[i].sess.sessionTeam == team ) { count++; } } return count; } /* ================ TeamLeader Returns the client number of the team leader ================ */ int TeamLeader( int team ) { int i; for ( i = 0 ; i < level.maxclients ; i++ ) { if ( level.clients[i].pers.connected == CON_DISCONNECTED ) { continue; } if ( level.clients[i].sess.sessionTeam == team ) { if ( level.clients[i].sess.teamLeader ) return i; } } return -1; } /* ================ PickTeam ================ */ team_t PickTeam( int ignoreClientNum ) { int counts[TEAM_NUM_TEAMS]; counts[TEAM_BLUE] = TeamCount( ignoreClientNum, TEAM_BLUE ); counts[TEAM_RED] = TeamCount( ignoreClientNum, TEAM_RED ); if ( counts[TEAM_BLUE] > counts[TEAM_RED] ) { return TEAM_RED; } if ( counts[TEAM_RED] > counts[TEAM_BLUE] ) { return TEAM_BLUE; } // equal team count, so join the team with the lowest score if ( level.teamScores[TEAM_BLUE] > level.teamScores[TEAM_RED] ) { return TEAM_RED; } return TEAM_BLUE; } /* =========== ForceClientSkin Forces a client's skin (for teamplay) =========== */ /* static void ForceClientSkin( gclient_t *client, char *model, const char *skin ) { char *p; if ((p = Q_strrchr(model, '/')) != 0) { *p = 0; } Q_strcat(model, MAX_QPATH, "/"); Q_strcat(model, MAX_QPATH, skin); } */ /* =========== ClientCheckName ============ */ static void ClientCleanName( const char *in, char *out, int outSize ) { int len, colorlessLen; char ch; char *p; int spaces; //save room for trailing null byte outSize--; len = 0; colorlessLen = 0; p = out; *p = 0; spaces = 0; while( 1 ) { ch = *in++; if( !ch ) { break; } // don't allow leading spaces if( !*p && ch == ' ' ) { continue; } // check colors if( ch == Q_COLOR_ESCAPE ) { // solo trailing carat is not a color prefix if( !*in ) { break; } // don't allow black in a name, period if( ColorIndex(*in) == 0 ) { in++; continue; } // make sure room in dest for both chars if( len > outSize - 2 ) { break; } *out++ = ch; *out++ = *in++; len += 2; continue; } // don't allow too many consecutive spaces if( ch == ' ' ) { spaces++; if( spaces > 3 ) { continue; } } else { spaces = 0; } if( len > outSize - 1 ) { break; } *out++ = ch; colorlessLen++; len++; } *out = 0; // don't allow empty names if( *p == 0 || colorlessLen == 0 ) { Q_strncpyz( p, "UnnamedPlayer", outSize ); } } /* =========== ClientUserInfoChanged Called from ClientConnect when the player first connects and directly by the server system when the player updates a userinfo variable. The game can override any of the settings and call trap_SetUserinfo if desired. ============ */ void ClientUserinfoChanged( int clientNum ) { gentity_t *ent; int teamTask, teamLeader, team, health; char *s; char model[MAX_QPATH]; char headModel[MAX_QPATH]; char oldname[MAX_STRING_CHARS]; gclient_t *client; char c1[MAX_INFO_STRING]; char c2[MAX_INFO_STRING]; char redTeam[MAX_INFO_STRING]; char blueTeam[MAX_INFO_STRING]; char userinfo[MAX_INFO_STRING]; ent = g_entities + clientNum; client = ent->client; trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) ); // check for malformed or illegal info strings if ( !Info_Validate(userinfo) ) { strcpy (userinfo, "\\name\\badinfo"); } // check for local client s = Info_ValueForKey( userinfo, "ip" ); if ( !strcmp( s, "localhost" ) ) { client->pers.localClient = qtrue; } // check the item prediction s = Info_ValueForKey( userinfo, "cg_predictItems" ); if ( !atoi( s ) ) { client->pers.predictItemPickup = qfalse; } else { client->pers.predictItemPickup = qtrue; } // set name Q_strncpyz ( oldname, client->pers.netname, sizeof( oldname ) ); s = Info_ValueForKey (userinfo, "name"); ClientCleanName( s, client->pers.netname, sizeof(client->pers.netname) ); if ( client->sess.sessionTeam == TEAM_SPECTATOR ) { if ( client->sess.spectatorState == SPECTATOR_SCOREBOARD ) { Q_strncpyz( client->pers.netname, "scoreboard", sizeof(client->pers.netname) ); } } if ( client->pers.connected == CON_CONNECTED ) { if ( strcmp( oldname, client->pers.netname ) ) { trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " renamed to %s\n\"", oldname, client->pers.netname) ); } } // set max health health = atoi( Info_ValueForKey( userinfo, "handicap" ) ); client->pers.maxHealth = health; if ( client->pers.maxHealth < 1 || client->pers.maxHealth > 100 ) { client->pers.maxHealth = 100; } client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth; // set model if( g_gametype.integer >= GT_TEAM ) { Q_strncpyz( model, Info_ValueForKey (userinfo, "team_model"), sizeof( model ) ); Q_strncpyz( headModel, Info_ValueForKey (userinfo, "team_headmodel"), sizeof( headModel ) ); } else { Q_strncpyz( model, Info_ValueForKey (userinfo, "model"), sizeof( model ) ); Q_strncpyz( headModel, Info_ValueForKey (userinfo, "headmodel"), sizeof( headModel ) ); } // bots set their team a few frames later if (g_gametype.integer >= GT_TEAM && g_entities[clientNum].r.svFlags & SVF_BOT) { s = Info_ValueForKey( userinfo, "team" ); if ( !Q_stricmp( s, "red" ) || !Q_stricmp( s, "r" ) ) { team = TEAM_RED; } else if ( !Q_stricmp( s, "blue" ) || !Q_stricmp( s, "b" ) ) { team = TEAM_BLUE; } else { // pick the team with the least number of players team = PickTeam( clientNum ); } } else { team = client->sess.sessionTeam; } /* NOTE: all client side now // team switch( team ) { case TEAM_RED: ForceClientSkin(client, model, "red"); // ForceClientSkin(client, headModel, "red"); break; case TEAM_BLUE: ForceClientSkin(client, model, "blue"); // ForceClientSkin(client, headModel, "blue"); break; } // don't ever use a default skin in teamplay, it would just waste memory // however bots will always join a team but they spawn in as spectator if ( g_gametype.integer >= GT_TEAM && team == TEAM_SPECTATOR) { ForceClientSkin(client, model, "red"); // ForceClientSkin(client, headModel, "red"); } */ // teamInfo s = Info_ValueForKey( userinfo, "teamoverlay" ); if ( ! *s || atoi( s ) != 0 ) { client->pers.teamInfo = qtrue; } else { client->pers.teamInfo = qfalse; } /* s = Info_ValueForKey( userinfo, "cg_pmove_fixed" ); if ( !*s || atoi( s ) == 0 ) { client->pers.pmoveFixed = qfalse; } else { client->pers.pmoveFixed = qtrue; } */ // team task (0 = none, 1 = offence, 2 = defence) teamTask = atoi(Info_ValueForKey(userinfo, "teamtask")); // team Leader (1 = leader, 0 is normal player) teamLeader = client->sess.teamLeader; // colors strcpy(c1, Info_ValueForKey( userinfo, "color1" )); strcpy(c2, Info_ValueForKey( userinfo, "color2" )); strcpy(redTeam, Info_ValueForKey( userinfo, "g_redteam" )); strcpy(blueTeam, Info_ValueForKey( userinfo, "g_blueteam" )); // send over a subset of the userinfo keys so other clients can // print scoreboards, display models, and play custom sounds if ( ent->r.svFlags & SVF_BOT ) { s = va("n\\%s\\t\\%i\\model\\%s\\hmodel\\%s\\c1\\%s\\c2\\%s\\hc\\%i\\w\\%i\\l\\%i\\skill\\%s\\tt\\%d\\tl\\%d", client->pers.netname, team, model, headModel, c1, c2, client->pers.maxHealth, client->sess.wins, client->sess.losses, Info_ValueForKey( userinfo, "skill" ), teamTask, teamLeader ); } else { s = va("n\\%s\\t\\%i\\model\\%s\\hmodel\\%s\\g_redteam\\%s\\g_blueteam\\%s\\c1\\%s\\c2\\%s\\hc\\%i\\w\\%i\\l\\%i\\tt\\%d\\tl\\%d", client->pers.netname, client->sess.sessionTeam, model, headModel, redTeam, blueTeam, c1, c2, client->pers.maxHealth, client->sess.wins, client->sess.losses, teamTask, teamLeader); } trap_SetConfigstring( CS_PLAYERS+clientNum, s ); // this is not the userinfo, more like the configstring actually G_LogPrintf( "ClientUserinfoChanged: %i %s\n", clientNum, s ); } /* =========== ClientConnect Called when a player begins connecting to the server. Called again for every map change or tournement restart. The session information will be valid after exit. Return NULL if the client should be allowed, otherwise return a string with the reason for denial. Otherwise, the client will be sent the current gamestate and will eventually get to ClientBegin. firstTime will be qtrue the very first time a client connects to the server machine, but qfalse on map changes and tournement restarts. ============ */ char *ClientConnect( int clientNum, qboolean firstTime, qboolean isBot ) { char *value; // char *areabits; gclient_t *client; char userinfo[MAX_INFO_STRING]; gentity_t *ent; ent = &g_entities[ clientNum ]; trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) ); // IP filtering // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=500 // recommanding PB based IP / GUID banning, the builtin system is pretty limited // check to see if they are on the banned IP list value = Info_ValueForKey (userinfo, "ip"); if ( G_FilterPacket( value ) ) { return "You are banned from this server."; } // we don't check password for bots and local client // NOTE: local client <-> "ip" "localhost" // this means this client is not running in our current process if ( !( ent->r.svFlags & SVF_BOT ) && (strcmp(value, "localhost") != 0)) { // check for a password value = Info_ValueForKey (userinfo, "password"); if ( g_password.string[0] && Q_stricmp( g_password.string, "none" ) && strcmp( g_password.string, value) != 0) { return "Invalid password"; } } // they can connect ent->client = level.clients + clientNum; client = ent->client; // areabits = client->areabits; memset( client, 0, sizeof(*client) ); client->pers.connected = CON_CONNECTING; // read or initialize the session data if ( firstTime || level.newSession ) { G_InitSessionData( client, userinfo ); } G_ReadSessionData( client ); if( isBot ) { ent->r.svFlags |= SVF_BOT; ent->inuse = qtrue; if( !G_BotConnect( clientNum, !firstTime ) ) { return "BotConnectfailed"; } } // get and distribute relevent paramters G_LogPrintf( "ClientConnect: %i\n", clientNum ); ClientUserinfoChanged( clientNum ); // don't do the "xxx connected" messages if they were caried over from previous level if ( firstTime ) { trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " connected\n\"", client->pers.netname) ); } if ( g_gametype.integer >= GT_TEAM && client->sess.sessionTeam != TEAM_SPECTATOR ) { BroadcastTeamChange( client, -1 ); } // count current clients and rank for scoreboard CalculateRanks(); // for statistics // client->areabits = areabits; // if ( !client->areabits ) // client->areabits = G_Alloc( (trap_AAS_PointReachabilityAreaIndex( NULL ) + 7) / 8 ); return NULL; } /* =========== ClientBegin called when a client has finished connecting, and is ready to be placed into the level. This will happen every level load, and on transition between teams, but doesn't happen on respawns ============ */ void ClientBegin( int clientNum ) { gentity_t *ent; gclient_t *client; gentity_t *tent; int flags; ent = g_entities + clientNum; client = level.clients + clientNum; if ( ent->r.linked ) { trap_UnlinkEntity( ent ); } G_InitGentity( ent ); ent->touch = 0; ent->pain = 0; ent->client = client; client->pers.connected = CON_CONNECTED; client->pers.enterTime = level.time; client->pers.teamState.state = TEAM_BEGIN; // save eflags around this, because changing teams will // cause this to happen with a valid entity, and we // want to make sure the teleport bit is set right // so the viewpoint doesn't interpolate through the // world to the new position flags = client->ps.eFlags; memset( &client->ps, 0, sizeof( client->ps ) ); client->ps.eFlags = flags; // locate ent at a spawn point ClientSpawn( ent ); if ( client->sess.sessionTeam != TEAM_SPECTATOR ) { // send event tent = G_TempEntity( ent->client->ps.origin, EV_PLAYER_TELEPORT_IN ); tent->s.clientNum = ent->s.clientNum; if ( g_gametype.integer != GT_TOURNAMENT ) { trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " entered the game\n\"", client->pers.netname) ); } } G_LogPrintf( "ClientBegin: %i\n", clientNum ); // count current clients and rank for scoreboard CalculateRanks(); } /* =========== ClientSpawn Called every time a client is placed fresh in the world: after the first ClientBegin, and after each respawn Initializes all non-persistant parts of playerState ============ */ void ClientSpawn(gentity_t *ent) { int index; vec3_t spawn_origin, spawn_angles; gclient_t *client; int i; clientPersistant_t saved; clientSession_t savedSess; int persistant[MAX_PERSISTANT]; gentity_t *spawnPoint; int flags; int savedPing; // char *savedAreaBits; int accuracy_hits, accuracy_shots; int eventSequence; char userinfo[MAX_INFO_STRING]; index = ent - g_entities; client = ent->client; // find a spawn point // do it before setting health back up, so farthest // ranging doesn't count this client if ( client->sess.sessionTeam == TEAM_SPECTATOR ) { spawnPoint = SelectSpectatorSpawnPoint ( spawn_origin, spawn_angles); } else if (g_gametype.integer >= GT_CTF ) { // all base oriented team games use the CTF spawn points spawnPoint = SelectCTFSpawnPoint ( client->sess.sessionTeam, client->pers.teamState.state, spawn_origin, spawn_angles); } else { do { // the first spawn should be at a good looking spot if ( !client->pers.initialSpawn && client->pers.localClient ) { client->pers.initialSpawn = qtrue; spawnPoint = SelectInitialSpawnPoint( spawn_origin, spawn_angles ); } else { // don't spawn near existing origin if possible spawnPoint = SelectSpawnPoint ( client->ps.origin, spawn_origin, spawn_angles); } // Tim needs to prevent bots from spawning at the initial point // on q3dm0... if ( ( spawnPoint->flags & FL_NO_BOTS ) && ( ent->r.svFlags & SVF_BOT ) ) { continue; // try again } // just to be symetric, we have a nohumans option... if ( ( spawnPoint->flags & FL_NO_HUMANS ) && !( ent->r.svFlags & SVF_BOT ) ) { continue; // try again } break; } while ( 1 ); } client->pers.teamState.state = TEAM_ACTIVE; // always clear the kamikaze flag ent->s.eFlags &= ~EF_KAMIKAZE; // toggle the teleport bit so the client knows to not lerp // and never clear the voted flag flags = ent->client->ps.eFlags & (EF_TELEPORT_BIT | EF_VOTED | EF_TEAMVOTED); flags ^= EF_TELEPORT_BIT; // clear everything but the persistant data saved = client->pers; savedSess = client->sess; savedPing = client->ps.ping; // savedAreaBits = client->areabits; accuracy_hits = client->accuracy_hits; accuracy_shots = client->accuracy_shots; for ( i = 0 ; i < MAX_PERSISTANT ; i++ ) { persistant[i] = client->ps.persistant[i]; } eventSequence = client->ps.eventSequence; memset (client, 0, sizeof(*client)); // bk FIXME: Com_Memset? client->pers = saved; client->sess = savedSess; client->ps.ping = savedPing; // client->areabits = savedAreaBits; client->accuracy_hits = accuracy_hits; client->accuracy_shots = accuracy_shots; client->lastkilled_client = -1; for ( i = 0 ; i < MAX_PERSISTANT ; i++ ) { client->ps.persistant[i] = persistant[i]; } client->ps.eventSequence = eventSequence; // increment the spawncount so the client will detect the respawn client->ps.persistant[PERS_SPAWN_COUNT]++; client->ps.persistant[PERS_TEAM] = client->sess.sessionTeam; client->airOutTime = level.time + 12000; trap_GetUserinfo( index, userinfo, sizeof(userinfo) ); // set max health client->pers.maxHealth = atoi( Info_ValueForKey( userinfo, "handicap" ) ); if ( client->pers.maxHealth < 1 || client->pers.maxHealth > 100 ) { client->pers.maxHealth = 100; } // clear entity values client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth; client->ps.eFlags = flags; ent->s.groundEntityNum = ENTITYNUM_NONE; ent->client = &level.clients[index]; ent->takedamage = qtrue; ent->inuse = qtrue; ent->classname = "player"; ent->r.contents = CONTENTS_BODY; ent->clipmask = MASK_PLAYERSOLID; ent->die = player_die; ent->waterlevel = 0; ent->watertype = 0; ent->flags = 0; VectorCopy (playerMins, ent->r.mins); VectorCopy (playerMaxs, ent->r.maxs); client->ps.clientNum = index; client->ps.stats[STAT_WEAPONS] = ( 1 << WP_MACHINEGUN ); if ( g_gametype.integer == GT_TEAM ) { client->ps.ammo[WP_MACHINEGUN] = 50; } else { client->ps.ammo[WP_MACHINEGUN] = 100; } client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_GAUNTLET ); client->ps.ammo[WP_GAUNTLET] = -1; client->ps.ammo[WP_GRAPPLING_HOOK] = -1; // health will count down towards max_health ent->health = client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH] + 25; G_SetOrigin( ent, spawn_origin ); VectorCopy( spawn_origin, client->ps.origin ); // the respawned flag will be cleared after the attack and jump keys come up client->ps.pm_flags |= PMF_RESPAWNED; trap_GetUsercmd( client - level.clients, &ent->client->pers.cmd ); SetClientViewAngle( ent, spawn_angles ); if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) { } else { G_KillBox( ent ); trap_LinkEntity (ent); // force the base weapon up client->ps.weapon = WP_MACHINEGUN; client->ps.weaponstate = WEAPON_READY; } // don't allow full run speed for a bit client->ps.pm_flags |= PMF_TIME_KNOCKBACK; client->ps.pm_time = 100; client->respawnTime = level.time; client->inactivityTime = level.time + g_inactivity.integer * 1000; client->latched_buttons = 0; // set default animations client->ps.torsoAnim = TORSO_STAND; client->ps.legsAnim = LEGS_IDLE; if ( level.intermissiontime ) { MoveClientToIntermission( ent ); } else { // fire the targets of the spawn point G_UseTargets( spawnPoint, ent ); // select the highest weapon number available, after any // spawn given items have fired client->ps.weapon = 1; for ( i = WP_NUM_WEAPONS - 1 ; i > 0 ; i-- ) { if ( client->ps.stats[STAT_WEAPONS] & ( 1 << i ) ) { client->ps.weapon = i; break; } } } // run a client frame to drop exactly to the floor, // initialize animations and other things client->ps.commandTime = level.time - 100; ent->client->pers.cmd.serverTime = level.time; ClientThink( ent-g_entities ); // positively link the client, even if the command times are weird if ( ent->client->sess.sessionTeam != TEAM_SPECTATOR ) { BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue ); VectorCopy( ent->client->ps.origin, ent->r.currentOrigin ); trap_LinkEntity( ent ); } // run the presend to set anything else ClientEndFrame( ent ); // clear entity state values BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue ); } /* =========== ClientDisconnect Called when a player drops from the server. Will not be called between levels. This should NOT be called directly by any game logic, call trap_DropClient(), which will call this and do server system housekeeping. ============ */ void ClientDisconnect( int clientNum ) { gentity_t *ent; gentity_t *tent; int i; // cleanup if we are kicking a bot that // hasn't spawned yet G_RemoveQueuedBotBegin( clientNum ); ent = g_entities + clientNum; if ( !ent->client ) { return; } // stop any following clients for ( i = 0 ; i < level.maxclients ; i++ ) { if ( level.clients[i].sess.sessionTeam == TEAM_SPECTATOR && level.clients[i].sess.spectatorState == SPECTATOR_FOLLOW && level.clients[i].sess.spectatorClient == clientNum ) { StopFollowing( &g_entities[i] ); } } // send effect if they were completely connected if ( ent->client->pers.connected == CON_CONNECTED && ent->client->sess.sessionTeam != TEAM_SPECTATOR ) { tent = G_TempEntity( ent->client->ps.origin, EV_PLAYER_TELEPORT_OUT ); tent->s.clientNum = ent->s.clientNum; // They don't get to take powerups with them! // Especially important for stuff like CTF flags TossClientItems( ent ); } G_LogPrintf( "ClientDisconnect: %i\n", clientNum ); // if we are playing in tourney mode and losing, give a win to the other player if ( (g_gametype.integer == GT_TOURNAMENT ) && !level.intermissiontime && !level.warmupTime && level.sortedClients[1] == clientNum ) { level.clients[ level.sortedClients[0] ].sess.wins++; ClientUserinfoChanged( level.sortedClients[0] ); } trap_UnlinkEntity (ent); ent->s.modelindex = 0; ent->inuse = qfalse; ent->classname = "disconnected"; ent->client->pers.connected = CON_DISCONNECTED; ent->client->ps.persistant[PERS_TEAM] = TEAM_FREE; ent->client->sess.sessionTeam = TEAM_FREE; trap_SetConfigstring( CS_PLAYERS + clientNum, ""); CalculateRanks(); if ( ent->r.svFlags & SVF_BOT ) { BotAIShutdownClient( clientNum, qfalse ); } } ================================================ FILE: src/game/g_cmds.c ================================================ /* =========================================================================== 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // #include "g_local.h" #include "menudef.h" // for the voice chats /* ================== DeathmatchScoreboardMessage ================== */ void DeathmatchScoreboardMessage( gentity_t *ent ) { char entry[1024]; char string[1400]; int stringlength; int i, j; gclient_t *cl; int numSorted, scoreFlags, accuracy, perfect; // send the latest information on all clients string[0] = 0; stringlength = 0; scoreFlags = 0; numSorted = level.numConnectedClients; for (i=0 ; i < numSorted ; i++) { int ping; cl = &level.clients[level.sortedClients[i]]; if ( cl->pers.connected == CON_CONNECTING ) { ping = -1; } else { ping = cl->ps.ping < 999 ? cl->ps.ping : 999; } if( cl->accuracy_shots ) { accuracy = cl->accuracy_hits * 100 / cl->accuracy_shots; } else { accuracy = 0; } perfect = ( cl->ps.persistant[PERS_RANK] == 0 && cl->ps.persistant[PERS_KILLED] == 0 ) ? 1 : 0; Com_sprintf (entry, sizeof(entry), " %i %i %i %i %i %i %i %i %i %i %i %i %i %i", level.sortedClients[i], cl->ps.persistant[PERS_SCORE], ping, (level.time - cl->pers.enterTime)/60000, scoreFlags, g_entities[level.sortedClients[i]].s.powerups, accuracy, cl->ps.persistant[PERS_IMPRESSIVE_COUNT], cl->ps.persistant[PERS_EXCELLENT_COUNT], cl->ps.persistant[PERS_GAUNTLET_FRAG_COUNT], cl->ps.persistant[PERS_DEFEND_COUNT], cl->ps.persistant[PERS_ASSIST_COUNT], perfect, cl->ps.persistant[PERS_CAPTURES]); j = (int)strlen(entry); if (stringlength + j > 1024) break; strcpy (string + stringlength, entry); stringlength += j; } trap_SendServerCommand( ent-g_entities, va("scores %i %i %i%s", i, level.teamScores[TEAM_RED], level.teamScores[TEAM_BLUE], string ) ); } /* ================== Cmd_Score_f Request current scoreboard information ================== */ void Cmd_Score_f( gentity_t *ent ) { DeathmatchScoreboardMessage( ent ); } /* ================== CheatsOk ================== */ qboolean CheatsOk( gentity_t *ent ) { if ( !g_cheats.integer ) { trap_SendServerCommand( ent-g_entities, va("print \"Cheats are not enabled on this server.\n\"")); return qfalse; } if ( ent->health <= 0 ) { trap_SendServerCommand( ent-g_entities, va("print \"You must be alive to use this command.\n\"")); return qfalse; } return qtrue; } /* ================== ConcatArgs ================== */ char *ConcatArgs( int start ) { int i, c, tlen; static char line[MAX_STRING_CHARS]; int len; char arg[MAX_STRING_CHARS]; len = 0; c = trap_Argc(); for ( i = start ; i < c ; i++ ) { trap_Argv( i, arg, sizeof( arg ) ); tlen = (int)strlen( arg ); if ( len + tlen >= MAX_STRING_CHARS - 1 ) { break; } memcpy( line + len, arg, tlen ); len += tlen; if ( i != c - 1 ) { line[len] = ' '; len++; } } line[len] = 0; return line; } /* ================== SanitizeString Remove case and control characters ================== */ void SanitizeString( char *in, char *out ) { while ( *in ) { if ( *in == 27 ) { in += 2; // skip color code continue; } if ( *in < 32 ) { in++; continue; } *out++ = tolower( *in++ ); } *out = 0; } /* ================== ClientNumberFromString Returns a player number for either a number or name string Returns -1 if invalid ================== */ int ClientNumberFromString( gentity_t *to, char *s ) { gclient_t *cl; int idnum; char s2[MAX_STRING_CHARS]; char n2[MAX_STRING_CHARS]; // numeric values are just slot numbers if (s[0] >= '0' && s[0] <= '9') { idnum = atoi( s ); if ( idnum < 0 || idnum >= level.maxclients ) { trap_SendServerCommand( to-g_entities, va("print \"Bad client slot: %i\n\"", idnum)); return -1; } cl = &level.clients[idnum]; if ( cl->pers.connected != CON_CONNECTED ) { trap_SendServerCommand( to-g_entities, va("print \"Client %i is not active\n\"", idnum)); return -1; } return idnum; } // check for a name match SanitizeString( s, s2 ); for ( idnum=0,cl=level.clients ; idnum < level.maxclients ; idnum++,cl++ ) { if ( cl->pers.connected != CON_CONNECTED ) { continue; } SanitizeString( cl->pers.netname, n2 ); if ( !strcmp( n2, s2 ) ) { return idnum; } } trap_SendServerCommand( to-g_entities, va("print \"User %s is not on the server\n\"", s)); return -1; } /* ================== Cmd_Give_f Give items to a client ================== */ void Cmd_Give_f (gentity_t *ent) { char *name; gitem_t *it; int i; qboolean give_all; gentity_t *it_ent; trace_t trace; if ( !CheatsOk( ent ) ) { return; } name = ConcatArgs( 1 ); if (Q_stricmp(name, "all") == 0) give_all = qtrue; else give_all = qfalse; if (give_all || Q_stricmp( name, "health") == 0) { ent->health = ent->client->ps.stats[STAT_MAX_HEALTH]; if (!give_all) return; } if (give_all || Q_stricmp(name, "weapons") == 0) { ent->client->ps.stats[STAT_WEAPONS] = (1 << WP_NUM_WEAPONS) - 1 - ( 1 << WP_GRAPPLING_HOOK ) - ( 1 << WP_NONE ); if (!give_all) return; } if (give_all || Q_stricmp(name, "ammo") == 0) { for ( i = 0 ; i < MAX_WEAPONS ; i++ ) { ent->client->ps.ammo[i] = 999; } if (!give_all) return; } if (give_all || Q_stricmp(name, "armor") == 0) { ent->client->ps.stats[STAT_ARMOR] = 200; if (!give_all) return; } if (Q_stricmp(name, "excellent") == 0) { ent->client->ps.persistant[PERS_EXCELLENT_COUNT]++; return; } if (Q_stricmp(name, "impressive") == 0) { ent->client->ps.persistant[PERS_IMPRESSIVE_COUNT]++; return; } if (Q_stricmp(name, "gauntletaward") == 0) { ent->client->ps.persistant[PERS_GAUNTLET_FRAG_COUNT]++; return; } if (Q_stricmp(name, "defend") == 0) { ent->client->ps.persistant[PERS_DEFEND_COUNT]++; return; } if (Q_stricmp(name, "assist") == 0) { ent->client->ps.persistant[PERS_ASSIST_COUNT]++; return; } // spawn a specific item right on the player if ( !give_all ) { it = BG_FindItem (name); if (!it) { return; } it_ent = G_Spawn(); VectorCopy( ent->r.currentOrigin, it_ent->s.origin ); it_ent->classname = it->classname; G_SpawnItem (it_ent, it); FinishSpawningItem(it_ent ); memset( &trace, 0, sizeof( trace ) ); Touch_Item (it_ent, ent, &trace); if (it_ent->inuse) { G_FreeEntity( it_ent ); } } } /* ================== Cmd_God_f Sets client to godmode argv(0) god ================== */ void Cmd_God_f (gentity_t *ent) { char *msg; if ( !CheatsOk( ent ) ) { return; } ent->flags ^= FL_GODMODE; if (!(ent->flags & FL_GODMODE) ) msg = "godmode OFF\n"; else msg = "godmode ON\n"; trap_SendServerCommand( ent-g_entities, va("print \"%s\"", msg)); } /* ================== Cmd_Notarget_f Sets client to notarget argv(0) notarget ================== */ void Cmd_Notarget_f( gentity_t *ent ) { char *msg; if ( !CheatsOk( ent ) ) { return; } ent->flags ^= FL_NOTARGET; if (!(ent->flags & FL_NOTARGET) ) msg = "notarget OFF\n"; else msg = "notarget ON\n"; trap_SendServerCommand( ent-g_entities, va("print \"%s\"", msg)); } /* ================== Cmd_Noclip_f argv(0) noclip ================== */ void Cmd_Noclip_f( gentity_t *ent ) { char *msg; if ( !CheatsOk( ent ) ) { return; } if ( ent->client->noclip ) { msg = "noclip OFF\n"; } else { msg = "noclip ON\n"; } ent->client->noclip = !ent->client->noclip; trap_SendServerCommand( ent-g_entities, va("print \"%s\"", msg)); } /* ================== Cmd_LevelShot_f This is just to help generate the level pictures for the menus. It goes to the intermission immediately and sends over a command to the client to resize the view, hide the scoreboard, and take a special screenshot ================== */ void Cmd_LevelShot_f( gentity_t *ent ) { if ( !CheatsOk( ent ) ) { return; } // doesn't work in single player if ( g_gametype.integer != 0 ) { trap_SendServerCommand( ent-g_entities, "print \"Must be in g_gametype 0 for levelshot\n\"" ); return; } BeginIntermission(); trap_SendServerCommand( ent-g_entities, "clientLevelShot" ); } /* ================== Cmd_LevelShot_f This is just to help generate the level pictures for the menus. It goes to the intermission immediately and sends over a command to the client to resize the view, hide the scoreboard, and take a special screenshot ================== */ void Cmd_TeamTask_f( gentity_t *ent ) { char userinfo[MAX_INFO_STRING]; char arg[MAX_TOKEN_CHARS]; int task; int client = ent->client - level.clients; if ( trap_Argc() != 2 ) { return; } trap_Argv( 1, arg, sizeof( arg ) ); task = atoi( arg ); trap_GetUserinfo(client, userinfo, sizeof(userinfo)); Info_SetValueForKey(userinfo, "teamtask", va("%d", task)); trap_SetUserinfo(client, userinfo); ClientUserinfoChanged(client); } /* ================= Cmd_Kill_f ================= */ void Cmd_Kill_f( gentity_t *ent ) { if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) { return; } if (ent->health <= 0) { return; } ent->flags &= ~FL_GODMODE; ent->client->ps.stats[STAT_HEALTH] = ent->health = -999; player_die (ent, ent, ent, 100000, MOD_SUICIDE); } /* ================= BroadCastTeamChange Let everyone know about a team change ================= */ void BroadcastTeamChange( gclient_t *client, int oldTeam ) { if ( client->sess.sessionTeam == TEAM_RED ) { trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the red team.\n\"", client->pers.netname) ); } else if ( client->sess.sessionTeam == TEAM_BLUE ) { trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the blue team.\n\"", client->pers.netname)); } else if ( client->sess.sessionTeam == TEAM_SPECTATOR && oldTeam != TEAM_SPECTATOR ) { trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the spectators.\n\"", client->pers.netname)); } else if ( client->sess.sessionTeam == TEAM_FREE ) { trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the battle.\n\"", client->pers.netname)); } } /* ================= SetTeam ================= */ void SetTeam( gentity_t *ent, char *s ) { int team, oldTeam; gclient_t *client; int clientNum; spectatorState_t specState; int specClient; int teamLeader; // // see what change is requested // client = ent->client; clientNum = client - level.clients; specClient = 0; specState = SPECTATOR_NOT; if ( !Q_stricmp( s, "scoreboard" ) || !Q_stricmp( s, "score" ) ) { team = TEAM_SPECTATOR; specState = SPECTATOR_SCOREBOARD; } else if ( !Q_stricmp( s, "follow1" ) ) { team = TEAM_SPECTATOR; specState = SPECTATOR_FOLLOW; specClient = -1; } else if ( !Q_stricmp( s, "follow2" ) ) { team = TEAM_SPECTATOR; specState = SPECTATOR_FOLLOW; specClient = -2; } else if ( !Q_stricmp( s, "spectator" ) || !Q_stricmp( s, "s" ) ) { team = TEAM_SPECTATOR; specState = SPECTATOR_FREE; } else if ( g_gametype.integer >= GT_TEAM ) { // if running a team game, assign player to one of the teams specState = SPECTATOR_NOT; if ( !Q_stricmp( s, "red" ) || !Q_stricmp( s, "r" ) ) { team = TEAM_RED; } else if ( !Q_stricmp( s, "blue" ) || !Q_stricmp( s, "b" ) ) { team = TEAM_BLUE; } else { // pick the team with the least number of players team = PickTeam( clientNum ); } if ( g_teamForceBalance.integer ) { int counts[TEAM_NUM_TEAMS]; counts[TEAM_BLUE] = TeamCount( ent->client->ps.clientNum, TEAM_BLUE ); counts[TEAM_RED] = TeamCount( ent->client->ps.clientNum, TEAM_RED ); // We allow a spread of two if ( team == TEAM_RED && counts[TEAM_RED] - counts[TEAM_BLUE] > 1 ) { trap_SendServerCommand( ent->client->ps.clientNum, "cp \"Red team has too many players.\n\"" ); return; // ignore the request } if ( team == TEAM_BLUE && counts[TEAM_BLUE] - counts[TEAM_RED] > 1 ) { trap_SendServerCommand( ent->client->ps.clientNum, "cp \"Blue team has too many players.\n\"" ); return; // ignore the request } // It's ok, the team we are switching to has less or same number of players } } else { // force them to spectators if there aren't any spots free team = TEAM_FREE; } // override decision if limiting the players if ( (g_gametype.integer == GT_TOURNAMENT) && level.numNonSpectatorClients >= 2 ) { team = TEAM_SPECTATOR; } else if ( g_maxGameClients.integer > 0 && level.numNonSpectatorClients >= g_maxGameClients.integer ) { team = TEAM_SPECTATOR; } // // decide if we will allow the change // oldTeam = client->sess.sessionTeam; if ( team == oldTeam && team != TEAM_SPECTATOR ) { return; } // // execute the team change // // if the player was dead leave the body if ( client->ps.stats[STAT_HEALTH] <= 0 ) { CopyToBodyQue(ent); } // he starts at 'base' client->pers.teamState.state = TEAM_BEGIN; if ( oldTeam != TEAM_SPECTATOR ) { // Kill him (makes sure he loses flags, etc) ent->flags &= ~FL_GODMODE; ent->client->ps.stats[STAT_HEALTH] = ent->health = 0; player_die (ent, ent, ent, 100000, MOD_SUICIDE); } // they go to the end of the line for tournements if ( team == TEAM_SPECTATOR ) { client->sess.spectatorTime = level.time; } client->sess.sessionTeam = team; client->sess.spectatorState = specState; client->sess.spectatorClient = specClient; client->sess.teamLeader = qfalse; if ( team == TEAM_RED || team == TEAM_BLUE ) { teamLeader = TeamLeader( team ); // if there is no team leader or the team leader is a bot and this client is not a bot if ( teamLeader == -1 || ( !(g_entities[clientNum].r.svFlags & SVF_BOT) && (g_entities[teamLeader].r.svFlags & SVF_BOT) ) ) { SetLeader( team, clientNum ); } } // make sure there is a team leader on the team the player came from if ( oldTeam == TEAM_RED || oldTeam == TEAM_BLUE ) { CheckTeamLeader( oldTeam ); } BroadcastTeamChange( client, oldTeam ); // get and distribute relevent paramters ClientUserinfoChanged( clientNum ); ClientBegin( clientNum ); } /* ================= StopFollowing If the client being followed leaves the game, or you just want to drop to free floating spectator mode ================= */ void StopFollowing( gentity_t *ent ) { ent->client->ps.persistant[ PERS_TEAM ] = TEAM_SPECTATOR; ent->client->sess.sessionTeam = TEAM_SPECTATOR; ent->client->sess.spectatorState = SPECTATOR_FREE; ent->client->ps.pm_flags &= ~PMF_FOLLOW; ent->r.svFlags &= ~SVF_BOT; ent->client->ps.clientNum = ent - g_entities; } /* ================= Cmd_Team_f ================= */ void Cmd_Team_f( gentity_t *ent ) { int oldTeam; char s[MAX_TOKEN_CHARS]; if ( trap_Argc() != 2 ) { oldTeam = ent->client->sess.sessionTeam; switch ( oldTeam ) { case TEAM_BLUE: trap_SendServerCommand( ent-g_entities, "print \"Blue team\n\"" ); break; case TEAM_RED: trap_SendServerCommand( ent-g_entities, "print \"Red team\n\"" ); break; case TEAM_FREE: trap_SendServerCommand( ent-g_entities, "print \"Free team\n\"" ); break; case TEAM_SPECTATOR: trap_SendServerCommand( ent-g_entities, "print \"Spectator team\n\"" ); break; } return; } if ( ent->client->switchTeamTime > level.time ) { trap_SendServerCommand( ent-g_entities, "print \"May not switch teams more than once per 5 seconds.\n\"" ); return; } // if they are playing a tournement game, count as a loss if ( (g_gametype.integer == GT_TOURNAMENT ) && ent->client->sess.sessionTeam == TEAM_FREE ) { ent->client->sess.losses++; } trap_Argv( 1, s, sizeof( s ) ); SetTeam( ent, s ); ent->client->switchTeamTime = level.time + 5000; } /* ================= Cmd_Follow_f ================= */ void Cmd_Follow_f( gentity_t *ent ) { int i; char arg[MAX_TOKEN_CHARS]; if ( trap_Argc() != 2 ) { if ( ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) { StopFollowing( ent ); } return; } trap_Argv( 1, arg, sizeof( arg ) ); i = ClientNumberFromString( ent, arg ); if ( i == -1 ) { return; } // can't follow self if ( &level.clients[ i ] == ent->client ) { return; } // can't follow another spectator if ( level.clients[ i ].sess.sessionTeam == TEAM_SPECTATOR ) { return; } // if they are playing a tournement game, count as a loss if ( (g_gametype.integer == GT_TOURNAMENT ) && ent->client->sess.sessionTeam == TEAM_FREE ) { ent->client->sess.losses++; } // first set them to spectator if ( ent->client->sess.sessionTeam != TEAM_SPECTATOR ) { SetTeam( ent, "spectator" ); } ent->client->sess.spectatorState = SPECTATOR_FOLLOW; ent->client->sess.spectatorClient = i; } /* ================= Cmd_FollowCycle_f ================= */ void Cmd_FollowCycle_f( gentity_t *ent, int dir ) { int clientnum; int original; // if they are playing a tournement game, count as a loss if ( (g_gametype.integer == GT_TOURNAMENT ) && ent->client->sess.sessionTeam == TEAM_FREE ) { ent->client->sess.losses++; } // first set them to spectator if ( ent->client->sess.spectatorState == SPECTATOR_NOT ) { SetTeam( ent, "spectator" ); } if ( dir != 1 && dir != -1 ) { G_Error( "Cmd_FollowCycle_f: bad dir %i", dir ); } clientnum = ent->client->sess.spectatorClient; original = clientnum; do { clientnum += dir; if ( clientnum >= level.maxclients ) { clientnum = 0; } if ( clientnum < 0 ) { clientnum = level.maxclients - 1; } // can only follow connected clients if ( level.clients[ clientnum ].pers.connected != CON_CONNECTED ) { continue; } // can't follow another spectator if ( level.clients[ clientnum ].sess.sessionTeam == TEAM_SPECTATOR ) { continue; } // this is good, we can use it ent->client->sess.spectatorClient = clientnum; ent->client->sess.spectatorState = SPECTATOR_FOLLOW; return; } while ( clientnum != original ); // leave it where it was } /* ================== G_Say ================== */ static void G_SayTo( gentity_t *ent, gentity_t *other, int mode, int color, const char *name, const char *message ) { if (!other) { return; } if (!other->inuse) { return; } if (!other->client) { return; } if ( other->client->pers.connected != CON_CONNECTED ) { return; } if ( mode == SAY_TEAM && !OnSameTeam(ent, other) ) { return; } // no chatting to players in tournements if ( (g_gametype.integer == GT_TOURNAMENT ) && other->client->sess.sessionTeam == TEAM_FREE && ent->client->sess.sessionTeam != TEAM_FREE ) { return; } trap_SendServerCommand( other-g_entities, va("%s \"%s%c%c%s\"", mode == SAY_TEAM ? "tchat" : "chat", name, Q_COLOR_ESCAPE, color, message)); } #define EC "\x19" void G_Say( gentity_t *ent, gentity_t *target, int mode, const char *chatText ) { int j; gentity_t *other; int color; char name[64]; // don't let text be too long for malicious reasons char text[MAX_SAY_TEXT]; char location[64]; if ( g_gametype.integer < GT_TEAM && mode == SAY_TEAM ) { mode = SAY_ALL; } switch ( mode ) { default: case SAY_ALL: G_LogPrintf( "say: %s: %s\n", ent->client->pers.netname, chatText ); Com_sprintf (name, sizeof(name), "%s%c%c"EC": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE ); color = COLOR_GREEN; break; case SAY_TEAM: G_LogPrintf( "sayteam: %s: %s\n", ent->client->pers.netname, chatText ); if (Team_GetLocationMsg(ent, location, sizeof(location))) Com_sprintf (name, sizeof(name), EC"(%s%c%c"EC") (%s)"EC": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE, location); else Com_sprintf (name, sizeof(name), EC"(%s%c%c"EC")"EC": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE ); color = COLOR_CYAN; break; case SAY_TELL: if (target && g_gametype.integer >= GT_TEAM && target->client->sess.sessionTeam == ent->client->sess.sessionTeam && Team_GetLocationMsg(ent, location, sizeof(location))) Com_sprintf (name, sizeof(name), EC"[%s%c%c"EC"] (%s)"EC": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE, location ); else Com_sprintf (name, sizeof(name), EC"[%s%c%c"EC"]"EC": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE ); color = COLOR_MAGENTA; break; } Q_strncpyz( text, chatText, sizeof(text) ); if ( target ) { G_SayTo( ent, target, mode, color, name, text ); return; } // echo the text to the console if ( g_dedicated.integer ) { G_Printf( "%s%s\n", name, text); } // send it to all the apropriate clients for (j = 0; j < level.maxclients; j++) { other = &g_entities[j]; G_SayTo( ent, other, mode, color, name, text ); } } /* ================== Cmd_Say_f ================== */ static void Cmd_Say_f( gentity_t *ent, int mode, qboolean arg0 ) { char *p; if ( trap_Argc () < 2 && !arg0 ) { return; } if (arg0) { p = ConcatArgs( 0 ); } else { p = ConcatArgs( 1 ); } G_Say( ent, NULL, mode, p ); } /* ================== Cmd_Tell_f ================== */ static void Cmd_Tell_f( gentity_t *ent ) { int targetNum; gentity_t *target; char *p; char arg[MAX_TOKEN_CHARS]; if ( trap_Argc () < 2 ) { return; } trap_Argv( 1, arg, sizeof( arg ) ); targetNum = atoi( arg ); if ( targetNum < 0 || targetNum >= level.maxclients ) { return; } target = &g_entities[targetNum]; if ( !target || !target->inuse || !target->client ) { return; } p = ConcatArgs( 2 ); G_LogPrintf( "tell: %s to %s: %s\n", ent->client->pers.netname, target->client->pers.netname, p ); G_Say( ent, target, SAY_TELL, p ); // don't tell to the player self if it was already directed to this player // also don't send the chat back to a bot if ( ent != target && !(ent->r.svFlags & SVF_BOT)) { G_Say( ent, ent, SAY_TELL, p ); } } static void G_VoiceTo( gentity_t *ent, gentity_t *other, int mode, const char *id, qboolean voiceonly ) { int color; char *cmd; if (!other) { return; } if (!other->inuse) { return; } if (!other->client) { return; } if ( mode == SAY_TEAM && !OnSameTeam(ent, other) ) { return; } // no chatting to players in tournements if ( (g_gametype.integer == GT_TOURNAMENT )) { return; } if (mode == SAY_TEAM) { color = COLOR_CYAN; cmd = "vtchat"; } else if (mode == SAY_TELL) { color = COLOR_MAGENTA; cmd = "vtell"; } else { color = COLOR_GREEN; cmd = "vchat"; } trap_SendServerCommand( other-g_entities, va("%s %d %d %d %s", cmd, voiceonly, ent->s.number, color, id)); } void G_Voice( gentity_t *ent, gentity_t *target, int mode, const char *id, qboolean voiceonly ) { int j; gentity_t *other; if ( g_gametype.integer < GT_TEAM && mode == SAY_TEAM ) { mode = SAY_ALL; } if ( target ) { G_VoiceTo( ent, target, mode, id, voiceonly ); return; } // echo the text to the console if ( g_dedicated.integer ) { G_Printf( "voice: %s %s\n", ent->client->pers.netname, id); } // send it to all the apropriate clients for (j = 0; j < level.maxclients; j++) { other = &g_entities[j]; G_VoiceTo( ent, other, mode, id, voiceonly ); } } /* ================== Cmd_Voice_f ================== */ static void Cmd_Voice_f( gentity_t *ent, int mode, qboolean arg0, qboolean voiceonly ) { char *p; if ( trap_Argc () < 2 && !arg0 ) { return; } if (arg0) { p = ConcatArgs( 0 ); } else { p = ConcatArgs( 1 ); } G_Voice( ent, NULL, mode, p, voiceonly ); } /* ================== Cmd_VoiceTell_f ================== */ static void Cmd_VoiceTell_f( gentity_t *ent, qboolean voiceonly ) { int targetNum; gentity_t *target; char *id; char arg[MAX_TOKEN_CHARS]; if ( trap_Argc () < 2 ) { return; } trap_Argv( 1, arg, sizeof( arg ) ); targetNum = atoi( arg ); if ( targetNum < 0 || targetNum >= level.maxclients ) { return; } target = &g_entities[targetNum]; if ( !target || !target->inuse || !target->client ) { return; } id = ConcatArgs( 2 ); G_LogPrintf( "vtell: %s to %s: %s\n", ent->client->pers.netname, target->client->pers.netname, id ); G_Voice( ent, target, SAY_TELL, id, voiceonly ); // don't tell to the player self if it was already directed to this player // also don't send the chat back to a bot if ( ent != target && !(ent->r.svFlags & SVF_BOT)) { G_Voice( ent, ent, SAY_TELL, id, voiceonly ); } } /* ================== Cmd_VoiceTaunt_f ================== */ static void Cmd_VoiceTaunt_f( gentity_t *ent ) { gentity_t *who; int i; if (!ent->client) { return; } // insult someone who just killed you if (ent->enemy && ent->enemy->client && ent->enemy->client->lastkilled_client == ent->s.number) { // i am a dead corpse if (!(ent->enemy->r.svFlags & SVF_BOT)) { G_Voice( ent, ent->enemy, SAY_TELL, VOICECHAT_DEATHINSULT, qfalse ); } if (!(ent->r.svFlags & SVF_BOT)) { G_Voice( ent, ent, SAY_TELL, VOICECHAT_DEATHINSULT, qfalse ); } ent->enemy = NULL; return; } // insult someone you just killed if (ent->client->lastkilled_client >= 0 && ent->client->lastkilled_client != ent->s.number) { who = g_entities + ent->client->lastkilled_client; if (who->client) { // who is the person I just killed if (who->client->lasthurt_mod == MOD_GAUNTLET) { if (!(who->r.svFlags & SVF_BOT)) { G_Voice( ent, who, SAY_TELL, VOICECHAT_KILLGAUNTLET, qfalse ); // and I killed them with a gauntlet } if (!(ent->r.svFlags & SVF_BOT)) { G_Voice( ent, ent, SAY_TELL, VOICECHAT_KILLGAUNTLET, qfalse ); } } else { if (!(who->r.svFlags & SVF_BOT)) { G_Voice( ent, who, SAY_TELL, VOICECHAT_KILLINSULT, qfalse ); // and I killed them with something else } if (!(ent->r.svFlags & SVF_BOT)) { G_Voice( ent, ent, SAY_TELL, VOICECHAT_KILLINSULT, qfalse ); } } ent->client->lastkilled_client = -1; return; } } if (g_gametype.integer >= GT_TEAM) { // praise a team mate who just got a reward for(i = 0; i < MAX_CLIENTS; i++) { who = g_entities + i; if (who->client && who != ent && who->client->sess.sessionTeam == ent->client->sess.sessionTeam) { if (who->client->rewardTime > level.time) { if (!(who->r.svFlags & SVF_BOT)) { G_Voice( ent, who, SAY_TELL, VOICECHAT_PRAISE, qfalse ); } if (!(ent->r.svFlags & SVF_BOT)) { G_Voice( ent, ent, SAY_TELL, VOICECHAT_PRAISE, qfalse ); } return; } } } } // just say something G_Voice( ent, NULL, SAY_ALL, VOICECHAT_TAUNT, qfalse ); } static char *gc_orders[] = { "hold your position", "hold this position", "come here", "cover me", "guard location", "search and destroy", "report" }; void Cmd_GameCommand_f( gentity_t *ent ) { int player; int order; char str[MAX_TOKEN_CHARS]; trap_Argv( 1, str, sizeof( str ) ); player = atoi( str ); trap_Argv( 2, str, sizeof( str ) ); order = atoi( str ); if ( player < 0 || player >= MAX_CLIENTS ) { return; } if ( order < 0 || order > sizeof(gc_orders)/sizeof(char *) ) { return; } G_Say( ent, &g_entities[player], SAY_TELL, gc_orders[order] ); G_Say( ent, ent, SAY_TELL, gc_orders[order] ); } /* ================== Cmd_Where_f ================== */ void Cmd_Where_f( gentity_t *ent ) { trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", vtos( ent->s.origin ) ) ); } static const char *gameNames[] = { "Free For All", "Tournament", "Single Player", "Team Deathmatch", "Capture the Flag", "One Flag CTF", "Overload", "Harvester" }; /* ================== Cmd_CallVote_f ================== */ void Cmd_CallVote_f( gentity_t *ent ) { int i; char arg1[MAX_STRING_TOKENS]; char arg2[MAX_STRING_TOKENS]; if ( !g_allowVote.integer ) { trap_SendServerCommand( ent-g_entities, "print \"Voting not allowed here.\n\"" ); return; } if ( level.voteTime ) { trap_SendServerCommand( ent-g_entities, "print \"A vote is already in progress.\n\"" ); return; } if ( ent->client->pers.voteCount >= MAX_VOTE_COUNT ) { trap_SendServerCommand( ent-g_entities, "print \"You have called the maximum number of votes.\n\"" ); return; } if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) { trap_SendServerCommand( ent-g_entities, "print \"Not allowed to call a vote as spectator.\n\"" ); return; } // make sure it is a valid command to vote on trap_Argv( 1, arg1, sizeof( arg1 ) ); trap_Argv( 2, arg2, sizeof( arg2 ) ); if( strchr( arg1, ';' ) || strchr( arg2, ';' ) ) { trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" ); return; } if ( !Q_stricmp( arg1, "map_restart" ) ) { } else if ( !Q_stricmp( arg1, "nextmap" ) ) { } else if ( !Q_stricmp( arg1, "map" ) ) { } else if ( !Q_stricmp( arg1, "g_gametype" ) ) { } else if ( !Q_stricmp( arg1, "kick" ) ) { } else if ( !Q_stricmp( arg1, "clientkick" ) ) { } else if ( !Q_stricmp( arg1, "g_doWarmup" ) ) { } else if ( !Q_stricmp( arg1, "timelimit" ) ) { } else if ( !Q_stricmp( arg1, "fraglimit" ) ) { } else { trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" ); trap_SendServerCommand( ent-g_entities, "print \"Vote commands are: map_restart, nextmap, map , g_gametype , kick , clientkick , g_doWarmup, timelimit