diff --git a/readme.md b/readme.md index afde0d2..4a4b477 100644 --- a/readme.md +++ b/readme.md @@ -6,6 +6,17 @@ YOU CAN RUN THE GAME WITHOUT THE SOURCE CODE RECREATION TO RUN THE EXPANSION PAC You will need a copy of the retail game(you can buy it on steam). +## Building the reconstructed engine and game DLL + +The build is centralized in `src/CMakeLists.txt`; it does not consume external +CSV source lists. From the repository root, run: + +```powershell +Set-Location src +cmake --preset windows-x86-debug +cmake --build --preset windows-x86-debug --target q4_runtime +``` + Come join us on discord! https://discord.gg/y2hp2S8c9Y @@ -20,4 +31,4 @@ https://github.com/jmarshall23 If you want to get ahold of me for work(I'm 16 year veteran of the game industry, just got laid off due to the AI surge), specifically I do graphics engineering, port work, low level platform work, technical director work, complex gameplay code, -etc. I've worked on over 20 games or so, my e-mail address is justinmarshall20@gmail.com \ No newline at end of file +etc. I've worked on over 20 games or so, my e-mail address is justinmarshall20@gmail.com diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 11a5cab..f8d47b6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,6 +2,8 @@ cmake_minimum_required(VERSION 3.25) project(Quake4Reconstruction LANGUAGES C CXX) +get_filename_component(Q4_RUNTIME_DIR "${PROJECT_SOURCE_DIR}/.." ABSOLUTE) + if(NOT WIN32) message(FATAL_ERROR "The initial reconstruction target is 32-bit Windows") endif() @@ -28,24 +30,796 @@ if(MSVC) set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") endif() +# The build is intentionally defined in this file. CSV files under +# docs/reconstruction are provenance reports, not build inputs. if(Q4_BUILD_IDLIB) - add_subdirectory(idlib) +# This is the exact first-party idlib compiland set recorded in quake4.pdb. +# Additional SDK files remain in the tree for the game DLLs but are not linked +# into the reconstructed executable until evidence assigns them to the target. +set(Q4_IDLIB_SOURCES + ${PROJECT_SOURCE_DIR}/idlib/BitMsg.cpp + ${PROJECT_SOURCE_DIR}/idlib/CmdArgs.cpp + ${PROJECT_SOURCE_DIR}/idlib/Dict.cpp + ${PROJECT_SOURCE_DIR}/idlib/Heap.cpp + ${PROJECT_SOURCE_DIR}/idlib/LangDict.cpp + ${PROJECT_SOURCE_DIR}/idlib/Lexer.cpp + ${PROJECT_SOURCE_DIR}/idlib/LexerFactory.cpp + ${PROJECT_SOURCE_DIR}/idlib/Lib.cpp + ${PROJECT_SOURCE_DIR}/idlib/mapfile.cpp + ${PROJECT_SOURCE_DIR}/idlib/Parser.cpp + ${PROJECT_SOURCE_DIR}/idlib/Str.cpp + ${PROJECT_SOURCE_DIR}/idlib/Timer.cpp + ${PROJECT_SOURCE_DIR}/idlib/Token.cpp + ${PROJECT_SOURCE_DIR}/idlib/bv/Bounds.cpp + ${PROJECT_SOURCE_DIR}/idlib/bv/Box.cpp + ${PROJECT_SOURCE_DIR}/idlib/bv/Frustum.cpp + ${PROJECT_SOURCE_DIR}/idlib/bv/Sphere.cpp + ${PROJECT_SOURCE_DIR}/idlib/containers/HashIndex.cpp + ${PROJECT_SOURCE_DIR}/idlib/geometry/JointTransform.cpp + ${PROJECT_SOURCE_DIR}/idlib/geometry/Surface.cpp + ${PROJECT_SOURCE_DIR}/idlib/geometry/Surface_Patch.cpp + ${PROJECT_SOURCE_DIR}/idlib/geometry/TraceModel.cpp + ${PROJECT_SOURCE_DIR}/idlib/geometry/Winding.cpp + ${PROJECT_SOURCE_DIR}/idlib/math/Angles.cpp + ${PROJECT_SOURCE_DIR}/idlib/math/Math.cpp + ${PROJECT_SOURCE_DIR}/idlib/math/Matrix.cpp + ${PROJECT_SOURCE_DIR}/idlib/math/Plane.cpp + ${PROJECT_SOURCE_DIR}/idlib/math/Polynomial.cpp + ${PROJECT_SOURCE_DIR}/idlib/math/Quat.cpp + ${PROJECT_SOURCE_DIR}/idlib/math/Radians.cpp + ${PROJECT_SOURCE_DIR}/idlib/math/Rotation.cpp + ${PROJECT_SOURCE_DIR}/idlib/math/Simd.cpp + ${PROJECT_SOURCE_DIR}/idlib/math/Simd_3DNow.cpp + ${PROJECT_SOURCE_DIR}/idlib/math/Simd_generic.cpp + ${PROJECT_SOURCE_DIR}/idlib/math/Simd_MMX.cpp + ${PROJECT_SOURCE_DIR}/idlib/math/Simd_SSE.cpp + ${PROJECT_SOURCE_DIR}/idlib/math/Simd_SSE2.cpp + ${PROJECT_SOURCE_DIR}/idlib/math/Simd_SSE3.cpp + ${PROJECT_SOURCE_DIR}/idlib/math/Vector.cpp + ${PROJECT_SOURCE_DIR}/idlib/hashing/CRC32.cpp + ${PROJECT_SOURCE_DIR}/idlib/hashing/MD4.cpp + ${PROJECT_SOURCE_DIR}/idlib/hashing/MD5.cpp +) + +add_library(q4_idlib STATIC ${Q4_IDLIB_SOURCES}) + +target_include_directories(q4_idlib + PUBLIC + ${PROJECT_SOURCE_DIR} + ${PROJECT_SOURCE_DIR}/idlib + PRIVATE + "C:/Program Files (x86)/Microsoft DirectX SDK (June 2010)/Include" +) + +target_compile_definitions(q4_idlib + PRIVATE + WIN32 + _WINDOWS + _LOAD_DLL + _USE_32BIT_TIME_T + Q4_NO_PUNKBUSTER + Q4_RECON_SEED + Q4_RECON_ENGINE_PRIVATE + Q4_RECON_SDK_MSGQUEUE + $<$:_DEBUG> + $<$:NDEBUG;_FINAL> +) + +target_precompile_headers(q4_idlib PRIVATE ${PROJECT_SOURCE_DIR}/idlib/precompiled.h) + +set_target_properties(q4_idlib PROPERTIES + OUTPUT_NAME idlib + FOLDER "Engine" +) + +# The Quake 4 SDK builds idlib a second time with Q4SDK for gamex86.dll. +# Several public classes (notably the SIMD hierarchy) are macro-sensitive, so +# linking the engine-private idlib into the game DLL gives the DLL a different +# vtable layout than the one seen by its game translation units. +set(Q4_GAME_IDLIB_SOURCES + ${Q4_IDLIB_SOURCES} + ${PROJECT_SOURCE_DIR}/idlib/geometry/Winding2D.cpp + ${PROJECT_SOURCE_DIR}/idlib/math/Lcp.cpp + ${PROJECT_SOURCE_DIR}/idlib/math/Ode.cpp +) + +add_library(q4_game_idlib STATIC ${Q4_GAME_IDLIB_SOURCES}) + +target_include_directories(q4_game_idlib + PUBLIC + ${PROJECT_SOURCE_DIR} + ${PROJECT_SOURCE_DIR}/idlib + PRIVATE + "C:/Program Files (x86)/Microsoft DirectX SDK (June 2010)/Include" +) + +target_compile_definitions(q4_game_idlib + PRIVATE + WIN32 + _WINDOWS + _USE_32BIT_TIME_T + Q4SDK + Q4_NO_PUNKBUSTER + $<$:_DEBUG> + $<$:NDEBUG;_FINAL> +) + +target_precompile_headers(q4_game_idlib PRIVATE ${PROJECT_SOURCE_DIR}/idlib/precompiled.h) + +set_target_properties(q4_game_idlib PROPERTIES + OUTPUT_NAME game_idlib + FOLDER "Game" +) + endif() if(Q4_BUILD_ENGINE_SEED) - add_subdirectory(engine) +function(q4_add_engine_object target) + add_library(${target} OBJECT ${ARGN}) + target_include_directories(${target} + PRIVATE + ${PROJECT_SOURCE_DIR} + ${PROJECT_SOURCE_DIR}/renderer/jpeg-6 + ${PROJECT_SOURCE_DIR}/sound/OggVorbis/ogg/include + ${PROJECT_SOURCE_DIR}/sound/OggVorbis/vorbis/include + "C:/Program Files (x86)/Microsoft DirectX SDK (June 2010)/Include" + ) + target_compile_definitions(${target} + PRIVATE + WIN32 + _WINDOWS + _LOAD_DLL + _USE_32BIT_TIME_T + Q4_NO_PUNKBUSTER + Q4_RECON_SEED + Q4_DISABLE_TOOLS + Q4_DISABLE_GL_LOGGING + Q4_DISABLE_TASKKEY_HOOK + $<$:_DEBUG> + $<$:NDEBUG;_FINAL> + ) + set_target_properties(${target} PROPERTIES FOLDER "Engine/Seed") +endfunction() + +file(GLOB_RECURSE Q4_AAS_SOURCES CONFIGURE_DEPENDS + ${PROJECT_SOURCE_DIR}/aas/*.cpp +) +set(Q4_AAS_FILE_SOURCE ${PROJECT_SOURCE_DIR}/aas/AASFile.cpp) +set(Q4_AAS_COMPILER_SOURCES + ${PROJECT_SOURCE_DIR}/aas/AASSettingsTools.cpp + ${PROJECT_SOURCE_DIR}/aas/AASTactical.cpp + ${PROJECT_SOURCE_DIR}/aas/AASBuild.cpp + ${PROJECT_SOURCE_DIR}/aas/AASBuild_file.cpp + ${PROJECT_SOURCE_DIR}/aas/AASBuild_gravity.cpp + ${PROJECT_SOURCE_DIR}/aas/AASBuild_ledge.cpp + ${PROJECT_SOURCE_DIR}/aas/AASBuild_merge.cpp + ${PROJECT_SOURCE_DIR}/aas/AASCluster.cpp + ${PROJECT_SOURCE_DIR}/aas/AASReach.cpp + ${PROJECT_SOURCE_DIR}/aas/Brush.cpp + ${PROJECT_SOURCE_DIR}/aas/BrushBSP.cpp +) +list(REMOVE_ITEM Q4_AAS_SOURCES ${Q4_AAS_COMPILER_SOURCES}) + +file(GLOB_RECURSE Q4_CM_SOURCES CONFIGURE_DEPENDS + ${PROJECT_SOURCE_DIR}/cm/*.cpp +) +set(Q4_CM_MODEL_SOURCE ${PROJECT_SOURCE_DIR}/cm/CollisionModel.cpp) +set(Q4_CM_LOAD_SOURCE ${PROJECT_SOURCE_DIR}/cm/collisionmodel_load.cpp) +set(Q4_CM_CONTACTS_SOURCE ${PROJECT_SOURCE_DIR}/cm/collisionmodel_contacts.cpp) +set(Q4_CM_CONTENTS_SOURCE ${PROJECT_SOURCE_DIR}/cm/collisionmodel_contents.cpp) +set(Q4_CM_TRACE_SOURCE ${PROJECT_SOURCE_DIR}/cm/collisionmodel_trace.cpp) +set(Q4_CM_TRANSLATE_SOURCE ${PROJECT_SOURCE_DIR}/cm/collisionmodel_translate.cpp) +list(REMOVE_ITEM Q4_CM_SOURCES ${Q4_CM_MODEL_SOURCE}) + +file(GLOB Q4_FRAMEWORK_TOP_SOURCES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/framework/*.cpp) +set(Q4_FRAMEWORK_PLAYER_MODEL_SOURCE ${PROJECT_SOURCE_DIR}/framework/DeclPlayerModel.cpp) +set(Q4_FRAMEWORK_ENGINE_TOP_SOURCES ${Q4_FRAMEWORK_TOP_SOURCES}) +# DeclPlayerModel is owned by gamex86.dll in the retail PDB. Keep it out of +# q4xp.exe so the reconstructed compiland and its allocator live in the same +# module that registers DECL_PLAYER_MODEL. +list(REMOVE_ITEM Q4_FRAMEWORK_ENGINE_TOP_SOURCES ${Q4_FRAMEWORK_PLAYER_MODEL_SOURCE}) +set(Q4_FRAMEWORK_SESSION_SOURCES + ${PROJECT_SOURCE_DIR}/framework/common.cpp + ${PROJECT_SOURCE_DIR}/framework/session.cpp + ${PROJECT_SOURCE_DIR}/framework/session_menu.cpp +) +set(Q4_FRAMEWORK_EDITFIELD_SOURCE ${PROJECT_SOURCE_DIR}/framework/editfield.cpp) +set(Q4_FRAMEWORK_CONSOLE_SOURCE ${PROJECT_SOURCE_DIR}/framework/console.cpp) +set(Q4_FRAMEWORK_USERCMD_SOURCE ${PROJECT_SOURCE_DIR}/framework/usercmdgen.cpp) +set(Q4_FRAMEWORK_CONSOLE_SOURCES + ${Q4_FRAMEWORK_EDITFIELD_SOURCE} + ${Q4_FRAMEWORK_CONSOLE_SOURCE} + ${Q4_FRAMEWORK_USERCMD_SOURCE} +) +file(GLOB Q4_FRAMEWORK_ASYNC_SOURCES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/framework/async/*.cpp) +set(Q4_FRAMEWORK_MSGCHANNEL_SOURCE ${PROJECT_SOURCE_DIR}/framework/async/msgchannel.cpp) +list(REMOVE_ITEM Q4_FRAMEWORK_ASYNC_SOURCES ${Q4_FRAMEWORK_MSGCHANNEL_SOURCE}) +set(Q4_FRAMEWORK_CORE_SOURCES ${Q4_FRAMEWORK_ENGINE_TOP_SOURCES}) +list(REMOVE_ITEM Q4_FRAMEWORK_CORE_SOURCES + ${Q4_FRAMEWORK_SESSION_SOURCES} + ${Q4_FRAMEWORK_CONSOLE_SOURCES} +) + +file(GLOB_RECURSE Q4_UI_SOURCES CONFIGURE_DEPENDS + ${PROJECT_SOURCE_DIR}/ui/*.cpp +) + +# Retail quake4.exe renderer compilation units. Keep this explicit: the PDB +# records one object for each entry and the list doubles as a boundary audit. +set(Q4_RENDERER_SOURCES + ${PROJECT_SOURCE_DIR}/renderer/Cinematic.cpp + ${PROJECT_SOURCE_DIR}/renderer/draw_arb.cpp + ${PROJECT_SOURCE_DIR}/renderer/draw_arb2.cpp + ${PROJECT_SOURCE_DIR}/renderer/draw_common.cpp + ${PROJECT_SOURCE_DIR}/renderer/draw_nv10.cpp + ${PROJECT_SOURCE_DIR}/renderer/draw_nv20.cpp + ${PROJECT_SOURCE_DIR}/renderer/draw_r200.cpp + ${PROJECT_SOURCE_DIR}/renderer/GuiModel.cpp + ${PROJECT_SOURCE_DIR}/renderer/Image_files.cpp + ${PROJECT_SOURCE_DIR}/renderer/Image_init.cpp + ${PROJECT_SOURCE_DIR}/renderer/Image_load.cpp + ${PROJECT_SOURCE_DIR}/renderer/Image_process.cpp + ${PROJECT_SOURCE_DIR}/renderer/Image_program.cpp + ${PROJECT_SOURCE_DIR}/renderer/Image_stats.cpp + ${PROJECT_SOURCE_DIR}/renderer/Interaction.cpp + ${PROJECT_SOURCE_DIR}/renderer/Material.cpp + ${PROJECT_SOURCE_DIR}/renderer/MegaTexture.cpp + ${PROJECT_SOURCE_DIR}/renderer/Model.cpp + ${PROJECT_SOURCE_DIR}/renderer/ModelDecal.cpp + ${PROJECT_SOURCE_DIR}/renderer/ModelManager.cpp + ${PROJECT_SOURCE_DIR}/renderer/ModelOverlay.cpp + ${PROJECT_SOURCE_DIR}/renderer/Model_ase.cpp + ${PROJECT_SOURCE_DIR}/renderer/Model_beam.cpp + ${PROJECT_SOURCE_DIR}/renderer/Model_liquid.cpp + ${PROJECT_SOURCE_DIR}/renderer/Model_lwo.cpp + ${PROJECT_SOURCE_DIR}/renderer/Model_ma.cpp + ${PROJECT_SOURCE_DIR}/renderer/Model_md3.cpp + ${PROJECT_SOURCE_DIR}/renderer/Model_md5.cpp + ${PROJECT_SOURCE_DIR}/renderer/Model_sprite.cpp + ${PROJECT_SOURCE_DIR}/renderer/RenderEntity.cpp + ${PROJECT_SOURCE_DIR}/renderer/RenderSystem.cpp + ${PROJECT_SOURCE_DIR}/renderer/RenderSystem_init.cpp + ${PROJECT_SOURCE_DIR}/renderer/RenderWorld.cpp + ${PROJECT_SOURCE_DIR}/renderer/RenderWorld_demo.cpp + ${PROJECT_SOURCE_DIR}/renderer/RenderWorld_load.cpp + ${PROJECT_SOURCE_DIR}/renderer/RenderWorld_portals.cpp + ${PROJECT_SOURCE_DIR}/renderer/Shaders.cpp + ${PROJECT_SOURCE_DIR}/renderer/rvGLSLShader.cpp + ${PROJECT_SOURCE_DIR}/renderer/rvIndexBuffer.cpp + ${PROJECT_SOURCE_DIR}/renderer/rvMesh.cpp + ${PROJECT_SOURCE_DIR}/renderer/rvPrimBatch.cpp + ${PROJECT_SOURCE_DIR}/renderer/rvRenderModelMD5R.cpp + ${PROJECT_SOURCE_DIR}/renderer/rvSpecial.cpp + ${PROJECT_SOURCE_DIR}/renderer/rvTexRenderTarget.cpp + ${PROJECT_SOURCE_DIR}/renderer/rvVertexBuffer.cpp + ${PROJECT_SOURCE_DIR}/renderer/rvVertexBufferCopy.cpp + ${PROJECT_SOURCE_DIR}/renderer/rvVertexFormat.cpp + ${PROJECT_SOURCE_DIR}/renderer/tr_backend.cpp + ${PROJECT_SOURCE_DIR}/renderer/tr_deform.cpp + ${PROJECT_SOURCE_DIR}/renderer/tr_font.cpp + ${PROJECT_SOURCE_DIR}/renderer/tr_guisurf.cpp + ${PROJECT_SOURCE_DIR}/renderer/tr_light.cpp + ${PROJECT_SOURCE_DIR}/renderer/tr_lightrun.cpp + ${PROJECT_SOURCE_DIR}/renderer/tr_main.cpp + ${PROJECT_SOURCE_DIR}/renderer/tr_orderIndexes.cpp + ${PROJECT_SOURCE_DIR}/renderer/tr_polytope.cpp + ${PROJECT_SOURCE_DIR}/renderer/tr_render.cpp + ${PROJECT_SOURCE_DIR}/renderer/tr_rendertools.cpp + ${PROJECT_SOURCE_DIR}/renderer/tr_shadowbounds.cpp + ${PROJECT_SOURCE_DIR}/renderer/tr_stencilshadow.cpp + ${PROJECT_SOURCE_DIR}/renderer/tr_subview.cpp + ${PROJECT_SOURCE_DIR}/renderer/tr_trace.cpp + ${PROJECT_SOURCE_DIR}/renderer/tr_trisurf.cpp + ${PROJECT_SOURCE_DIR}/renderer/tr_turboshadow.cpp + ${PROJECT_SOURCE_DIR}/renderer/VertexCache.cpp +) + +# Retail BSE object boundaries recovered from quake4.pdb. +set(Q4_BSE_SOURCES + ${PROJECT_SOURCE_DIR}/bse/BSE_Bounds.cpp + ${PROJECT_SOURCE_DIR}/bse/BSE_Effect.cpp + ${PROJECT_SOURCE_DIR}/bse/BSE_EffectTemplate.cpp + ${PROJECT_SOURCE_DIR}/bse/BSE_Electricity.cpp + ${PROJECT_SOURCE_DIR}/bse/BSE_Envelopes.cpp + ${PROJECT_SOURCE_DIR}/bse/BSE_Light.cpp + ${PROJECT_SOURCE_DIR}/bse/BSE_Manager.cpp + ${PROJECT_SOURCE_DIR}/bse/BSE_ParseParticle2.cpp + ${PROJECT_SOURCE_DIR}/bse/BSE_Particle.cpp + ${PROJECT_SOURCE_DIR}/bse/BSE_Render.cpp + ${PROJECT_SOURCE_DIR}/bse/BSE_Segment.cpp + ${PROJECT_SOURCE_DIR}/bse/BSE_SegmentTemplate.cpp + ${PROJECT_SOURCE_DIR}/bse/BSE_SpawnDomains.cpp +) +file(GLOB Q4_JPEG_SOURCES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/renderer/jpeg-6/*.c) + +file(GLOB_RECURSE Q4_SOUND_SOURCES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/sound/*.cpp) +file(GLOB_RECURSE Q4_OGGVORBIS_SOURCES CONFIGURE_DEPENDS + ${PROJECT_SOURCE_DIR}/sound/OggVorbis/*.c +) + +file(GLOB_RECURSE Q4_SYS_SOURCES CONFIGURE_DEPENDS + ${PROJECT_SOURCE_DIR}/sys/*.cpp +) + +q4_add_engine_object(q4_engine_aas_file ${Q4_AAS_FILE_SOURCE}) +q4_add_engine_object(q4_engine_aas ${Q4_AAS_SOURCES}) +target_compile_definitions(q4_engine_aas PRIVATE Q4_AAS_LEGACY_SEED) +q4_add_engine_object(q4_engine_cm_model ${Q4_CM_MODEL_SOURCE}) +target_compile_definitions(q4_engine_cm_model PRIVATE Q4_CM_MODEL_ONLY) +q4_add_engine_object(q4_engine_cm_load ${Q4_CM_LOAD_SOURCE}) +q4_add_engine_object(q4_engine_cm_contacts ${Q4_CM_CONTACTS_SOURCE}) +q4_add_engine_object(q4_engine_cm_contents ${Q4_CM_CONTENTS_SOURCE}) +q4_add_engine_object(q4_engine_cm_trace ${Q4_CM_TRACE_SOURCE}) +q4_add_engine_object(q4_engine_cm_translate ${Q4_CM_TRANSLATE_SOURCE}) +q4_add_engine_object(q4_engine_cm_seed ${Q4_CM_SOURCES}) +target_compile_definitions(q4_engine_cm_seed PRIVATE Q4_CM_LEGACY_SEED) +set(Q4_FRAMEWORK_CORE_TARGETS) +foreach(source IN LISTS Q4_FRAMEWORK_CORE_SOURCES) + get_filename_component(stem ${source} NAME_WE) + set(target q4_engine_framework_core_${stem}) + q4_add_engine_object(${target} ${source}) + target_compile_definitions(${target} PRIVATE Q4_RECON_ENGINE_PRIVATE Q4_RECON_SDK_MSGQUEUE) + list(APPEND Q4_FRAMEWORK_CORE_TARGETS ${target}) +endforeach() +add_custom_target(q4_engine_framework_core DEPENDS ${Q4_FRAMEWORK_CORE_TARGETS}) +set_target_properties(q4_engine_framework_core PROPERTIES FOLDER "Engine/Seed") +q4_add_engine_object(q4_engine_framework_editfield ${Q4_FRAMEWORK_EDITFIELD_SOURCE}) +q4_add_engine_object(q4_engine_framework_console_impl ${Q4_FRAMEWORK_CONSOLE_SOURCE}) +q4_add_engine_object(q4_engine_framework_usercmd ${Q4_FRAMEWORK_USERCMD_SOURCE}) +add_custom_target(q4_engine_framework_console + DEPENDS + q4_engine_framework_editfield + q4_engine_framework_console_impl + q4_engine_framework_usercmd +) +set_target_properties(q4_engine_framework_console PROPERTIES FOLDER "Engine/Seed") +set(Q4_FRAMEWORK_ASYNC_TARGETS) +foreach(source IN LISTS Q4_FRAMEWORK_ASYNC_SOURCES) + get_filename_component(stem ${source} NAME_WE) + set(target q4_engine_framework_async_${stem}) + q4_add_engine_object(${target} ${source}) + target_compile_definitions(${target} PRIVATE Q4_RECON_ENGINE_PRIVATE Q4_RECON_SDK_MSGQUEUE) + list(APPEND Q4_FRAMEWORK_ASYNC_TARGETS ${target}) +endforeach() +add_custom_target(q4_engine_framework_async DEPENDS ${Q4_FRAMEWORK_ASYNC_TARGETS}) +set_target_properties(q4_engine_framework_async PROPERTIES FOLDER "Engine/Seed") +q4_add_engine_object(q4_engine_framework_msgchannel ${Q4_FRAMEWORK_MSGCHANNEL_SOURCE}) +target_compile_definitions(q4_engine_framework_msgchannel PRIVATE Q4_RECON_SDK_MSGQUEUE) +q4_add_engine_object(q4_engine_framework_session ${Q4_FRAMEWORK_SESSION_SOURCES}) +foreach(target q4_engine_framework_editfield q4_engine_framework_console_impl q4_engine_framework_usercmd q4_engine_framework_session) + target_compile_definitions(${target} PRIVATE Q4_RECON_ENGINE_PRIVATE Q4_RECON_SDK_MSGQUEUE) +endforeach() +set(Q4_UI_TARGETS) +foreach(source IN LISTS Q4_UI_SOURCES) + get_filename_component(stem ${source} NAME_WE) + set(target q4_engine_ui_${stem}) + q4_add_engine_object(${target} ${source}) + target_compile_definitions(${target} PRIVATE Q4_RECON_ENGINE_PRIVATE Q4_RECON_SDK_MSGQUEUE) + list(APPEND Q4_UI_TARGETS ${target}) +endforeach() +add_custom_target(q4_engine_ui DEPENDS ${Q4_UI_TARGETS}) +set_target_properties(q4_engine_ui PROPERTIES FOLDER "Engine/Seed") + +set(Q4_RENDERER_TARGETS) +foreach(source IN LISTS Q4_RENDERER_SOURCES) + get_filename_component(stem ${source} NAME_WE) + set(target q4_engine_renderer_${stem}) + q4_add_engine_object(${target} ${source}) + target_compile_definitions(${target} PRIVATE Q4_RECON_ENGINE_PRIVATE Q4_RECON_SDK_MSGQUEUE) + list(APPEND Q4_RENDERER_TARGETS ${target}) +endforeach() +add_custom_target(q4_engine_renderer DEPENDS ${Q4_RENDERER_TARGETS}) +set_target_properties(q4_engine_renderer PROPERTIES FOLDER "Engine/Seed") +set(Q4_BSE_TARGETS) +foreach(source IN LISTS Q4_BSE_SOURCES) + get_filename_component(stem ${source} NAME_WE) + string(TOLOWER ${stem} stem_lower) + set(target q4_engine_bse_${stem_lower}) + q4_add_engine_object(${target} ${source}) + target_compile_definitions(${target} PRIVATE Q4_RECON_ENGINE_PRIVATE Q4_RECON_SDK_MSGQUEUE) + list(APPEND Q4_BSE_TARGETS ${target}) +endforeach() +add_custom_target(q4_engine_bse DEPENDS ${Q4_BSE_TARGETS}) +set_target_properties(q4_engine_bse PROPERTIES FOLDER "Engine/Seed") +q4_add_engine_object(q4_bse_abi ${PROJECT_SOURCE_DIR}/tests/abi/BSE_ABI.cpp) +target_compile_definitions(q4_bse_abi PRIVATE Q4_RECON_ENGINE_PRIVATE Q4_RECON_SDK_MSGQUEUE) +set_target_properties(q4_bse_abi PROPERTIES FOLDER "Reconstruction/ABI") +q4_add_engine_object(q4_renderer_raven_abi ${PROJECT_SOURCE_DIR}/tests/abi/RendererRaven_ABI.cpp) +target_compile_definitions(q4_renderer_raven_abi PRIVATE Q4_RECON_ENGINE_PRIVATE Q4_RECON_SDK_MSGQUEUE) +set_target_properties(q4_renderer_raven_abi PROPERTIES FOLDER "Reconstruction/ABI") +q4_add_engine_object(q4_thirdparty_jpeg ${Q4_JPEG_SOURCES}) +target_include_directories(q4_thirdparty_jpeg PRIVATE ${PROJECT_SOURCE_DIR}/renderer/jpeg-6) +set_target_properties(q4_thirdparty_jpeg PROPERTIES FOLDER "ThirdParty") +set(Q4_SOUND_TARGETS) +foreach(source IN LISTS Q4_SOUND_SOURCES) + get_filename_component(stem ${source} NAME_WE) + set(target q4_engine_sound_${stem}) + q4_add_engine_object(${target} ${source}) + target_include_directories(${target} PRIVATE ${PROJECT_SOURCE_DIR}/sound ${PROJECT_SOURCE_DIR}/sys/win32) + target_compile_definitions(${target} PRIVATE Q4_RECON_ENGINE_PRIVATE Q4_RECON_SDK_MSGQUEUE Q4_RECONSTRUCTED_SOUND) + list(APPEND Q4_SOUND_TARGETS ${target}) +endforeach() +add_custom_target(q4_engine_sound DEPENDS ${Q4_SOUND_TARGETS}) +set_target_properties(q4_engine_sound PROPERTIES FOLDER "Engine/Seed") +q4_add_engine_object(q4_thirdparty_oggvorbis ${Q4_OGGVORBIS_SOURCES}) +target_include_directories(q4_thirdparty_oggvorbis PRIVATE + ${PROJECT_SOURCE_DIR}/sound/OggVorbis + ${PROJECT_SOURCE_DIR}/sound/OggVorbis/ogg + ${PROJECT_SOURCE_DIR}/sound/OggVorbis/vorbis + ${PROJECT_SOURCE_DIR}/sound/OggVorbis/vorbissrc +) +set_target_properties(q4_thirdparty_oggvorbis PROPERTIES FOLDER "ThirdParty") +set(Q4_SYS_TARGETS) +foreach(source IN LISTS Q4_SYS_SOURCES) + get_filename_component(stem ${source} NAME_WE) + set(target q4_engine_sys_${stem}) + q4_add_engine_object(${target} ${source}) + target_compile_definitions(${target} PRIVATE Q4_RECON_ENGINE_PRIVATE Q4_RECON_SDK_MSGQUEUE) + list(APPEND Q4_SYS_TARGETS ${target}) +endforeach() +add_custom_target(q4_engine_sys DEPENDS ${Q4_SYS_TARGETS}) +set_target_properties(q4_engine_sys PROPERTIES FOLDER "Engine/Seed") + +add_custom_target(q4_engine_seed + DEPENDS + q4_engine_aas + q4_engine_aas_file + q4_engine_cm_model + q4_engine_cm_load + q4_engine_cm_contacts + q4_engine_cm_contents + q4_engine_cm_trace + q4_engine_cm_translate + q4_engine_cm_seed + q4_engine_framework_core + q4_engine_framework_console + q4_engine_framework_async + q4_engine_framework_msgchannel + q4_engine_framework_session + q4_engine_ui + q4_engine_renderer + q4_engine_bse + q4_thirdparty_jpeg + q4_engine_sound + q4_thirdparty_oggvorbis + q4_engine_sys +) +set_target_properties(q4_engine_seed PROPERTIES FOLDER "Engine") + +add_custom_target(q4_reconstruction_foundation + DEPENDS + q4_idlib + q4_engine_aas + q4_engine_aas_file + q4_engine_cm_model + q4_engine_cm_load + q4_engine_cm_contacts + q4_engine_cm_contents + q4_engine_cm_trace + q4_engine_cm_translate + q4_engine_cm_seed + q4_engine_framework_console + q4_engine_framework_core_cmdsystem + q4_engine_framework_core_compressor + q4_engine_framework_core_cvarsystem + q4_engine_framework_core_declaf + q4_engine_framework_core_declentitydef + q4_engine_framework_core_declmanager + q4_engine_framework_core_declpda + q4_engine_framework_core_declskin + q4_engine_framework_core_decltable + q4_engine_framework_core_demofile + q4_engine_framework_core_eventloop + q4_engine_framework_core_file + q4_engine_framework_core_filesystem + q4_engine_framework_core_keyinput + q4_engine_framework_core_unzip + q4_engine_framework_async_asyncnetwork + q4_engine_framework_async_networksystem + q4_engine_framework_msgchannel + q4_engine_ui + q4_thirdparty_jpeg + q4_thirdparty_oggvorbis +) +set_target_properties(q4_reconstruction_foundation PROPERTIES FOLDER "Reconstruction") + +# Full reconstructed Win32 engine integration target. The per-compiland +# object targets above remain the source-ownership/ABI audit surface; this +# target compiles the same recovered sources into a runnable executable. +# The editor and compilers live in the external q4_tools target and cross the +# reconstructed retail Tools API rather than being compiled into the engine. +# CURL remains disabled at this reconstruction frontier. +set(Q4_OPENAL_LOADER_SOURCES + ${PROJECT_SOURCE_DIR}/openal/idal.cpp +) + +add_executable(q4xp WIN32 + ${PROJECT_SOURCE_DIR}/sys/win32/rc/Quake4.rc + ${Q4_AAS_SOURCES} + ${Q4_CM_MODEL_SOURCE} + ${Q4_CM_SOURCES} + ${Q4_FRAMEWORK_ENGINE_TOP_SOURCES} + ${Q4_FRAMEWORK_ASYNC_SOURCES} + ${Q4_FRAMEWORK_MSGCHANNEL_SOURCE} + ${Q4_UI_SOURCES} + ${Q4_RENDERER_SOURCES} + ${Q4_BSE_SOURCES} + ${Q4_JPEG_SOURCES} + ${Q4_SOUND_SOURCES} + ${Q4_OGGVORBIS_SOURCES} + ${Q4_OPENAL_LOADER_SOURCES} + ${Q4_SYS_SOURCES} +) + +target_include_directories(q4xp PRIVATE + ${PROJECT_SOURCE_DIR} + ${PROJECT_SOURCE_DIR}/renderer/jpeg-6 + ${PROJECT_SOURCE_DIR}/sound + ${PROJECT_SOURCE_DIR}/sys/win32 + ${PROJECT_SOURCE_DIR}/sound/OggVorbis + ${PROJECT_SOURCE_DIR}/sound/OggVorbis/ogg + ${PROJECT_SOURCE_DIR}/sound/OggVorbis/ogg/include + ${PROJECT_SOURCE_DIR}/sound/OggVorbis/vorbis + ${PROJECT_SOURCE_DIR}/sound/OggVorbis/vorbis/include + ${PROJECT_SOURCE_DIR}/sound/OggVorbis/vorbissrc + "C:/Program Files (x86)/Microsoft DirectX SDK (June 2010)/Include" +) + +target_compile_definitions(q4xp PRIVATE + WIN32 + _WINDOWS + _LOAD_DLL + __DOOM_DLL__ + _USE_32BIT_TIME_T + Q4_NO_PUNKBUSTER + Q4_RECON_SEED + Q4_RECON_ENGINE_PRIVATE + Q4_RECON_SDK_MSGQUEUE + Q4_DISABLE_TASKKEY_HOOK + Q4_DISABLE_TOOLS + Q4_DISABLE_GL_LOGGING + Q4_RECONSTRUCTED_SOUND + ID_ENABLE_CURL=0 + $<$:_DEBUG> + $<$:NDEBUG;_FINAL> +) + +target_link_directories(q4xp PRIVATE + "C:/Program Files (x86)/Microsoft DirectX SDK (June 2010)/Lib/x86" + ${PROJECT_SOURCE_DIR}/openal/lib +) + +target_link_libraries(q4xp PRIVATE + q4_idlib + opengl32 + winmm + ws2_32 + iphlpapi + dbghelp + comctl32 + dinput8 + dsound + dxguid + version +) + +if(MSVC) + # Keep an address map and symbols beside the reconstructed executable so + # retail-startup crashes can be resolved without enabling the tools DLL. + target_compile_options(q4xp PRIVATE /Zi) + target_link_options(q4xp PRIVATE + /DEBUG:FULL + /PDB:${Q4_RUNTIME_DIR}/q4xp.pdb + /MAP:${Q4_RUNTIME_DIR}/q4xp.map + /STACK:16777216,4096 + ) +endif() + +set_target_properties(q4xp PROPERTIES + OUTPUT_NAME q4xp + RUNTIME_OUTPUT_DIRECTORY "${Q4_RUNTIME_DIR}" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${Q4_RUNTIME_DIR}" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${Q4_RUNTIME_DIR}" + PDB_OUTPUT_DIRECTORY "${Q4_RUNTIME_DIR}" + FOLDER "Engine" +) + endif() if(Q4_BUILD_GAME_DLL) - add_subdirectory(game) +file(GLOB_RECURSE Q4_GAME_SOURCES CONFIGURE_DEPENDS + ${PROJECT_SOURCE_DIR}/game/*.cpp +) + +# Callbacks.cpp is generated-style source text included by gamesys/Class.cpp; +# it is not a standalone translation unit in the Quake 4 SDK project. +list(REMOVE_ITEM Q4_GAME_SOURCES + ${PROJECT_SOURCE_DIR}/game/gamesys/Callbacks.cpp +) + +add_library(gamex86 SHARED + ${Q4_GAME_SOURCES} + ${PROJECT_SOURCE_DIR}/framework/DeclPlayerModel.cpp +) + +target_include_directories(gamex86 PRIVATE + ${PROJECT_SOURCE_DIR} + ${PROJECT_SOURCE_DIR}/game + "C:/Program Files (x86)/Microsoft DirectX SDK (June 2010)/Include" +) + +target_compile_definitions(gamex86 PRIVATE + WIN32 + _WINDOWS + _LOAD_DLL + _USE_32BIT_TIME_T + GAME_DLL + Q4SDK + Q4_RECON_RETAIL_UI_MANAGER_ABI + Q4_NO_PUNKBUSTER + $<$:_DEBUG> + $<$:NDEBUG;_FINAL> +) + +target_link_libraries(gamex86 PRIVATE q4_game_idlib winmm) + +if(MSVC) + target_compile_options(gamex86 PRIVATE /Zi) + target_link_options(gamex86 PRIVATE + /DEF:${PROJECT_SOURCE_DIR}/game/game.def + /DEBUG:FULL + /PDB:${Q4_RUNTIME_DIR}/q4base/gamex86.pdb + ) +endif() + +set_target_properties(gamex86 PROPERTIES + OUTPUT_NAME gamex86 + PREFIX "" + RUNTIME_OUTPUT_DIRECTORY "${Q4_RUNTIME_DIR}/q4base" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${Q4_RUNTIME_DIR}/q4base" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${Q4_RUNTIME_DIR}/q4base" + LIBRARY_OUTPUT_DIRECTORY "${Q4_RUNTIME_DIR}/q4base" + ARCHIVE_OUTPUT_DIRECTORY "${Q4_RUNTIME_DIR}/q4base" + PDB_OUTPUT_DIRECTORY "${Q4_RUNTIME_DIR}/q4base" + FOLDER "Game" +) + +endif() + +if(Q4_BUILD_ENGINE_SEED AND Q4_BUILD_GAME_DLL) + add_custom_target(q4_runtime + DEPENDS + q4xp + gamex86 + ) + set_target_properties(q4_runtime PROPERTIES FOLDER "Runtime") endif() if(Q4_BUILD_TOOLS_DLL) - add_subdirectory(tools) +file(GLOB Q4_RADIANT_SOURCES CONFIGURE_DEPENDS + ${PROJECT_SOURCE_DIR}/tools/radiant/*.cpp + ${PROJECT_SOURCE_DIR}/tools/common/*.cpp + ${PROJECT_SOURCE_DIR}/tools/comafx/*.cpp +) +set_source_files_properties(${Q4_RADIANT_SOURCES} PROPERTIES + COMPILE_OPTIONS "/FI${PROJECT_SOURCE_DIR}/tools/comafx/StdAfx.h" +) + +add_library(q4_tools SHARED + ${PROJECT_SOURCE_DIR}/tools/ToolsStub.cpp + ${PROJECT_SOURCE_DIR}/tools/ToolRenderAdapters.cpp + ${PROJECT_SOURCE_DIR}/tools/Toolsx86.def + ${PROJECT_SOURCE_DIR}/sys/win32/rc/Radiant.rc + ${PROJECT_SOURCE_DIR}/sys/win32/rc/Common.rc + ${PROJECT_SOURCE_DIR}/sys/win32/rc/PropTree.rc + ${PROJECT_SOURCE_DIR}/idlib/geometry/Surface_SweptSpline.cpp + ${PROJECT_SOURCE_DIR}/aas/AASSettingsTools.cpp + ${PROJECT_SOURCE_DIR}/aas/AASTactical.cpp + ${PROJECT_SOURCE_DIR}/aas/AASBuild.cpp + ${PROJECT_SOURCE_DIR}/aas/AASBuild_file.cpp + ${PROJECT_SOURCE_DIR}/aas/AASBuild_gravity.cpp + ${PROJECT_SOURCE_DIR}/aas/AASBuild_ledge.cpp + ${PROJECT_SOURCE_DIR}/aas/AASBuild_merge.cpp + ${PROJECT_SOURCE_DIR}/aas/AASCluster.cpp + ${PROJECT_SOURCE_DIR}/aas/AASReach.cpp + ${PROJECT_SOURCE_DIR}/aas/Brush.cpp + ${PROJECT_SOURCE_DIR}/aas/BrushBSP.cpp + ${PROJECT_SOURCE_DIR}/tools/compilers/dmap/dmap.cpp + ${PROJECT_SOURCE_DIR}/tools/compilers/dmap/facebsp.cpp + ${PROJECT_SOURCE_DIR}/tools/compilers/dmap/gldraw.cpp + ${PROJECT_SOURCE_DIR}/tools/compilers/dmap/glfile.cpp + ${PROJECT_SOURCE_DIR}/tools/compilers/dmap/leakfile.cpp + ${PROJECT_SOURCE_DIR}/tools/compilers/dmap/map.cpp + ${PROJECT_SOURCE_DIR}/tools/compilers/dmap/optimize.cpp + ${PROJECT_SOURCE_DIR}/tools/compilers/dmap/optimize_gcc.cpp + ${PROJECT_SOURCE_DIR}/tools/compilers/dmap/output.cpp + ${PROJECT_SOURCE_DIR}/tools/compilers/dmap/portals.cpp + ${PROJECT_SOURCE_DIR}/tools/compilers/dmap/shadowopt3.cpp + ${PROJECT_SOURCE_DIR}/tools/compilers/dmap/tritjunction.cpp + ${PROJECT_SOURCE_DIR}/tools/compilers/dmap/tritools.cpp + ${PROJECT_SOURCE_DIR}/tools/compilers/dmap/ubrush.cpp + ${PROJECT_SOURCE_DIR}/tools/compilers/dmap/usurface.cpp + ${Q4_RADIANT_SOURCES} +) + +if(CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION) + target_include_directories(q4_tools BEFORE PRIVATE + "C:/Program Files (x86)/Windows Kits/10/Include/${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}/shared" + ) +endif() + +target_include_directories(q4_tools PRIVATE + ${PROJECT_SOURCE_DIR} + "C:/Program Files (x86)/Microsoft DirectX SDK (June 2010)/Include" +) + +target_compile_definitions(q4_tools PRIVATE + WIN32 + _WINDOWS + ID_GL_HARDLINK + _LOAD_DLL + Q4_NO_PUNKBUSTER + Q4_RECON_ENGINE_PRIVATE + Q4_RECON_SDK_MSGQUEUE + $<$:_DEBUG> + $<$:NDEBUG;_FINAL> +) + +target_link_libraries(q4_tools PRIVATE + q4_idlib + user32 + gdi32 + comctl32 + comdlg32 + shell32 + opengl32 + glu32 + ws2_32 +) + +set_target_properties(q4_tools PROPERTIES + OUTPUT_NAME Toolsx86 + PREFIX "" + FOLDER "Tools" + MFC_FLAG 2 +) + +# Keep the 2005 retail Toolsx86.dll intact for binary comparison. The +# reconstructed engine deliberately loads this non-conflicting runtime alias. +add_custom_command(TARGET q4_tools POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + $ + "${Q4_RUNTIME_DIR}/ToolsReconstructedx86.dll" +) + endif() if(Q4_BUILD_RECON_TOOLS) - add_subdirectory(tools/reconstruction) +add_custom_target(reconstruction_manifests SOURCES + ${PROJECT_SOURCE_DIR}/tools/reconstruction/Export-EvidenceManifest.ps1 + ${PROJECT_SOURCE_DIR}/tools/reconstruction/Export-ReconstructionLedger.ps1 + ${PROJECT_SOURCE_DIR}/tools/reconstruction/Seed-DoomImplementations.ps1 + ${PROJECT_SOURCE_DIR}/tools/reconstruction/Seed-DoomHeaders.ps1 +) + +set_target_properties(reconstruction_manifests PROPERTIES FOLDER "Reconstruction") + +if(MSVC) + set(DIA_SDK_ROOT "${CMAKE_GENERATOR_INSTANCE}/DIA SDK") + if(EXISTS "${DIA_SDK_ROOT}/include/dia2.h") + add_executable(q4_pdb_inventory + ${PROJECT_SOURCE_DIR}/tools/reconstruction/pdb_inventory.cpp + ) + target_include_directories(q4_pdb_inventory PRIVATE "${DIA_SDK_ROOT}/include") + target_link_directories(q4_pdb_inventory PRIVATE "${DIA_SDK_ROOT}/lib") + target_link_libraries(q4_pdb_inventory PRIVATE diaguids ole32 oleaut32) + target_compile_definitions(q4_pdb_inventory PRIVATE UNICODE _UNICODE) + set_target_properties(q4_pdb_inventory PROPERTIES FOLDER "Reconstruction") + else() + message(WARNING "DIA SDK not found; q4_pdb_inventory will not be built") + endif() +endif() + endif() enable_testing() diff --git a/src/aas/AASBuild.cpp b/src/aas/AASBuild.cpp new file mode 100644 index 0000000..7465a8a --- /dev/null +++ b/src/aas/AASBuild.cpp @@ -0,0 +1,1031 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../idlib/precompiled.h" +#pragma hdrstop + +#include "AASBuild_local.h" + +#define BFL_PATCH 0x1000 + +//=============================================================== +// +// idAASBuild +// +//=============================================================== + +/* +============ +idAASBuild::idAASBuild +============ +*/ +idAASBuild::idAASBuild( void ) { + file = NULL; + procNodes = NULL; + numProcNodes = 0; + numGravitationalSubdivisions = 0; + numMergedLeafNodes = 0; + numLedgeSubdivisions = 0; + ledgeMap = NULL; +} + +/* +============ +idAASBuild::~idAASBuild +============ +*/ +idAASBuild::~idAASBuild( void ) { + Shutdown(); +} + +/* +================ +idAASBuild::Shutdown +================ +*/ +void idAASBuild::Shutdown( void ) { + aasSettings = NULL; + if ( file ) { + delete file; + file = NULL; + } + DeleteProcBSP(); + numGravitationalSubdivisions = 0; + numMergedLeafNodes = 0; + numLedgeSubdivisions = 0; + ledgeList.Clear(); + if ( ledgeMap ) { + delete ledgeMap; + ledgeMap = NULL; + } +} + +/* +================ +idAASBuild::ParseProcNodes +================ +*/ +void idAASBuild::ParseProcNodes( idLexer *src ) { + int i; + + src->ExpectTokenString( "{" ); + + idAASBuild::numProcNodes = src->ParseInt(); + if ( idAASBuild::numProcNodes < 0 ) { + src->Error( "idAASBuild::ParseProcNodes: bad numProcNodes" ); + } + idAASBuild::procNodes = (aasProcNode_t *)Mem_ClearedAlloc( idAASBuild::numProcNodes * sizeof( aasProcNode_t ) ); + + for ( i = 0; i < idAASBuild::numProcNodes; i++ ) { + aasProcNode_t *node; + + node = &(idAASBuild::procNodes[i]); + + src->Parse1DMatrix( 4, node->plane.ToFloatPtr() ); + node->children[0] = src->ParseInt(); + node->children[1] = src->ParseInt(); + } + + src->ExpectTokenString( "}" ); +} + +/* +================ +idAASBuild::LoadProcBSP +================ +*/ +bool idAASBuild::LoadProcBSP( const char *name, ID_TIME_T minFileTime ) { + idStr fileName; + idToken token; + idLexer *src; + + // load it + fileName = name; + fileName.SetFileExtension( PROC_FILE_EXT ); + src = new idLexer( fileName, LEXFL_NOSTRINGCONCAT | LEXFL_NODOLLARPRECOMPILE ); + if ( !src->IsLoaded() ) { + common->Warning("idAASBuild::LoadProcBSP: couldn't load %s", fileName.c_str() ); + delete src; + return false; + } + + // if the file is too old + if ( src->GetFileTime() < minFileTime ) { + delete src; + return false; + } + + if ( !src->ReadToken( &token ) || token.Icmp( PROC_FILE_ID ) ) { + common->Warning( "idAASBuild::LoadProcBSP: bad id '%s' instead of '%s'", token.c_str(), PROC_FILE_ID ); + delete src; + return false; + } + + // parse the file + while ( 1 ) { + if ( !src->ReadToken( &token ) ) { + break; + } + + if ( token == "model" ) { + src->SkipBracedSection(); + continue; + } + + if ( token == "shadowModel" ) { + src->SkipBracedSection(); + continue; + } + + if ( token == "interAreaPortals" ) { + src->SkipBracedSection(); + continue; + } + + if ( token == "nodes" ) { + idAASBuild::ParseProcNodes( src ); + break; + } + + src->Error( "idAASBuild::LoadProcBSP: bad token \"%s\"", token.c_str() ); + } + + delete src; + + return true; +} + +/* +============ +idAASBuild::DeleteProcBSP +============ +*/ +void idAASBuild::DeleteProcBSP( void ) { + if ( procNodes ) { + Mem_Free( procNodes ); + procNodes = NULL; + } + numProcNodes = 0; +} + +/* +============ +idAASBuild::ChoppedAwayByProcBSP +============ +*/ +bool idAASBuild::ChoppedAwayByProcBSP( int nodeNum, idFixedWinding *w, const idVec3 &normal, const idVec3 &origin, const float radius ) { + int res; + idFixedWinding back; + aasProcNode_t *node; + float dist; + + do { + node = idAASBuild::procNodes + nodeNum; + dist = node->plane.Normal() * origin + node->plane[3]; + if ( dist > radius ) { + res = SIDE_FRONT; + } + else if ( dist < -radius ) { + res = SIDE_BACK; + } + else { + res = w->Split( &back, node->plane, ON_EPSILON ); + } + if ( res == SIDE_FRONT ) { + nodeNum = node->children[0]; + } + else if ( res == SIDE_BACK ) { + nodeNum = node->children[1]; + } + else if ( res == SIDE_ON ) { + // continue with the side the winding faces + if ( node->plane.Normal() * normal > 0.0f ) { + nodeNum = node->children[0]; + } + else { + nodeNum = node->children[1]; + } + } + else { + // if either node is not solid + if ( node->children[0] < 0 || node->children[1] < 0 ) { + return false; + } + // only recurse if the node is not solid + if ( node->children[1] > 0 ) { + if ( !idAASBuild::ChoppedAwayByProcBSP( node->children[1], &back, normal, origin, radius ) ) { + return false; + } + } + nodeNum = node->children[0]; + } + } while ( nodeNum > 0 ); + if ( nodeNum < 0 ) { + return false; + } + return true; +} + +/* +============ +idAASBuild::ClipBrushSidesWithProcBSP +============ +*/ +void idAASBuild::ClipBrushSidesWithProcBSP( idBrushList &brushList ) { + int i, clippedSides; + idBrush *brush; + idFixedWinding neww; + idBounds bounds; + float radius; + idVec3 origin; + + // if the .proc file has no BSP tree + if ( idAASBuild::procNodes == NULL ) { + return; + } + + clippedSides = 0; + for ( brush = brushList.Head(); brush; brush = brush->Next() ) { + for ( i = 0; i < brush->GetNumSides(); i++ ) { + + if ( !brush->GetSide(i)->GetWinding() ) { + continue; + } + + // make a local copy of the winding + neww = *brush->GetSide(i)->GetWinding(); + neww.GetBounds( bounds ); + origin = (bounds[1] - bounds[0]) * 0.5f; + radius = origin.Length() + ON_EPSILON; + origin = bounds[0] + origin; + + if ( ChoppedAwayByProcBSP( 0, &neww, brush->GetSide(i)->GetPlane().Normal(), origin, radius ) ) { + brush->GetSide(i)->SetFlag( SFL_USED_SPLITTER ); + clippedSides++; + } + } + } + + common->Printf( "%6d brush sides clipped\n", clippedSides ); +} + +/* +============ +idAASBuild::ContentsForAAS +============ +*/ +int idAASBuild::ContentsForAAS( int contents ) { + int c; + + if ( contents & ( CONTENTS_SOLID|CONTENTS_AAS_SOLID|CONTENTS_MONSTERCLIP ) ) { + return AREACONTENTS_SOLID; + } + c = 0; + if ( contents & CONTENTS_WATER ) { + c |= AREACONTENTS_WATER; + } + if ( contents & CONTENTS_AREAPORTAL ) { + c |= AREACONTENTS_CLUSTERPORTAL; + } + if ( contents & CONTENTS_AAS_OBSTACLE ) { + c |= AREACONTENTS_OBSTACLE; + } + return c; +} + +/* +============ +idAASBuild::AddBrushForMapBrush +============ +*/ +idBrushList idAASBuild::AddBrushesForMapBrush( const idMapBrush *mapBrush, const idVec3 &origin, const idMat3 &axis, int entityNum, int primitiveNum, idBrushList brushList ) { + int contents, i; + idMapBrushSide *mapSide; + const idMaterial *mat; + idList sideList; + idBrush *brush; + idPlane plane; + + contents = 0; + for ( i = 0; i < mapBrush->GetNumSides(); i++ ) { + mapSide = mapBrush->GetSide(i); + mat = declManager->FindMaterial( mapSide->GetMaterial() ); + contents |= mat->GetContentFlags(); + plane = mapSide->GetPlane(); + plane.FixDegeneracies( DEGENERATE_DIST_EPSILON ); + sideList.Append( new idBrushSide( plane, -1 ) ); + } + + contents = ContentsForAAS( contents ); + if ( !contents ) { + for ( i = 0; i < sideList.Num(); i++ ) { + delete sideList[i]; + } + return brushList; + } + + brush = new idBrush(); + brush->SetContents( contents ); + + if ( !brush->FromSides( sideList ) ) { + common->Warning( "brush primitive %d on entity %d is degenerate", primitiveNum, entityNum ); + delete brush; + return brushList; + } + + brush->SetEntityNum( entityNum ); + brush->SetPrimitiveNum( primitiveNum ); + brush->Transform( origin, axis ); + brushList.AddToTail( brush ); + + return brushList; +} + +/* +============ +idAASBuild::AddBrushesForPatch +============ +*/ +idBrushList idAASBuild::AddBrushesForMapPatch( const idMapPatch *mapPatch, const idVec3 &origin, const idMat3 &axis, int entityNum, int primitiveNum, idBrushList brushList ) { + int i, j, contents, validBrushes; + float dot; + int v1, v2, v3, v4; + idFixedWinding w; + idPlane plane; + idVec3 d1, d2; + idBrush *brush; + idSurface_Patch mesh; + const idMaterial *mat; + + mat = declManager->FindMaterial( mapPatch->GetMaterial() ); + contents = ContentsForAAS( mat->GetContentFlags() ); + + if ( !contents ) { + return brushList; + } + + mesh = idSurface_Patch( *mapPatch ); + + // if the patch has an explicit number of subdivisions use it to avoid cracks + if ( mapPatch->GetExplicitlySubdivided() ) { + mesh.SubdivideExplicit( mapPatch->GetHorzSubdivisions(), mapPatch->GetVertSubdivisions(), false, true ); + } else { + mesh.Subdivide( DEFAULT_CURVE_MAX_ERROR_CD, DEFAULT_CURVE_MAX_ERROR_CD, DEFAULT_CURVE_MAX_LENGTH_CD, false ); + } + + validBrushes = 0; + + for ( i = 0; i < mesh.GetWidth() - 1; i++ ) { + for ( j = 0; j < mesh.GetHeight() - 1; j++ ) { + + v1 = j * mesh.GetWidth() + i; + v2 = v1 + 1; + v3 = v1 + mesh.GetWidth() + 1; + v4 = v1 + mesh.GetWidth(); + + d1 = mesh[v2].xyz - mesh[v1].xyz; + d2 = mesh[v3].xyz - mesh[v1].xyz; + plane.SetNormal( d1.Cross(d2) ); + if ( plane.Normalize() != 0.0f ) { + plane.FitThroughPoint( mesh[v1].xyz ); + dot = plane.Distance( mesh[v4].xyz ); + // if we can turn it into a quad + if ( idMath::Fabs(dot) < 0.1f ) { + w.Clear(); + w += mesh[v1].xyz; + w += mesh[v2].xyz; + w += mesh[v3].xyz; + w += mesh[v4].xyz; + + brush = new idBrush(); + brush->SetContents( contents ); + if ( brush->FromWinding( w, plane ) ) { + brush->SetEntityNum( entityNum ); + brush->SetPrimitiveNum( primitiveNum ); + brush->SetFlag( BFL_PATCH ); + brush->Transform( origin, axis ); + brushList.AddToTail( brush ); + validBrushes++; + } + else { + delete brush; + } + continue; + } + else { + // create one of the triangles + w.Clear(); + w += mesh[v1].xyz; + w += mesh[v2].xyz; + w += mesh[v3].xyz; + + brush = new idBrush(); + brush->SetContents( contents ); + if ( brush->FromWinding( w, plane ) ) { + brush->SetEntityNum( entityNum ); + brush->SetPrimitiveNum( primitiveNum ); + brush->SetFlag( BFL_PATCH ); + brush->Transform( origin, axis ); + brushList.AddToTail( brush ); + validBrushes++; + } + else { + delete brush; + } + } + } + // create the other triangle + d1 = mesh[v3].xyz - mesh[v1].xyz; + d2 = mesh[v4].xyz - mesh[v1].xyz; + plane.SetNormal( d1.Cross(d2) ); + if ( plane.Normalize() != 0.0f ) { + plane.FitThroughPoint( mesh[v1].xyz ); + + w.Clear(); + w += mesh[v1].xyz; + w += mesh[v3].xyz; + w += mesh[v4].xyz; + + brush = new idBrush(); + brush->SetContents( contents ); + if ( brush->FromWinding( w, plane ) ) { + brush->SetEntityNum( entityNum ); + brush->SetPrimitiveNum( primitiveNum ); + brush->SetFlag( BFL_PATCH ); + brush->Transform( origin, axis ); + brushList.AddToTail( brush ); + validBrushes++; + } + else { + delete brush; + } + } + } + } + + if ( !validBrushes ) { + common->Warning( "patch primitive %d on entity %d is completely degenerate", primitiveNum, entityNum ); + } + + return brushList; +} + +/* +============ +idAASBuild::AddBrushesForMapEntity +============ +*/ +idBrushList idAASBuild::AddBrushesForMapEntity( const idMapEntity *mapEnt, int entityNum, idBrushList brushList ) { + int i; + idVec3 origin; + idMat3 axis; + + if ( mapEnt->GetNumPrimitives() < 1 ) { + return brushList; + } + + mapEnt->epairs.GetVector( "origin", "0 0 0", origin ); + if ( !mapEnt->epairs.GetMatrix( "rotation", "1 0 0 0 1 0 0 0 1", axis ) ) { + float angle = mapEnt->epairs.GetFloat( "angle" ); + if ( angle != 0.0f ) { + axis = idAngles( 0.0f, angle, 0.0f ).ToMat3(); + } else { + axis.Identity(); + } + } + + for ( i = 0; i < mapEnt->GetNumPrimitives(); i++ ) { + idMapPrimitive *mapPrim; + + mapPrim = mapEnt->GetPrimitive(i); + if ( mapPrim->GetType() == idMapPrimitive::TYPE_BRUSH ) { + brushList = AddBrushesForMapBrush( static_cast(mapPrim), origin, axis, entityNum, i, brushList ); + continue; + } + if ( mapPrim->GetType() == idMapPrimitive::TYPE_PATCH ) { + if ( aasSettings->usePatches ) { + brushList = AddBrushesForMapPatch( static_cast(mapPrim), origin, axis, entityNum, i, brushList ); + } + continue; + } + } + + return brushList; +} + +/* +============ +idAASBuild::AddBrushesForMapFile +============ +*/ +idBrushList idAASBuild::AddBrushesForMapFile( const idMapFile * mapFile, idBrushList brushList ) { + int i; + + common->Printf( "[Brush Load]\n" ); + + brushList = AddBrushesForMapEntity( mapFile->GetEntity( 0 ), 0, brushList ); + + for ( i = 1; i < mapFile->GetNumEntities(); i++ ) { + const char *classname = mapFile->GetEntity( i )->epairs.GetString( "classname" ); + + if ( idStr::Icmp( classname, "func_aas_obstacle" ) == 0 ) { + brushList = AddBrushesForMapEntity( mapFile->GetEntity( i ), i, brushList ); + } + } + + common->Printf( "%6d brushes\n", brushList.Num() ); + + return brushList; +} + +/* +============ +idAASBuild::CheckForEntities +============ +*/ +bool idAASBuild::CheckForEntities( const idMapFile *mapFile, idStrList &entityClassNames ) const { + int i; + idStr classname; + + com_editors |= EDITOR_AAS; + + for ( i = 0; i < mapFile->GetNumEntities(); i++ ) { + if ( !mapFile->GetEntity(i)->epairs.GetString( "classname", "", classname ) ) { + continue; + } + + if ( aasSettings->ValidEntity( classname ) ) { + entityClassNames.AddUnique( classname ); + } + } + + com_editors &= ~EDITOR_AAS; + + return ( entityClassNames.Num() != 0 ); +} + +/* +============ +MergeAllowed +============ +*/ +bool MergeAllowed( idBrush *b1, idBrush *b2 ) { + return ( b1->GetContents() == b2->GetContents() && !( ( b1->GetFlags() | b2->GetFlags() ) & BFL_PATCH ) ); +} + +/* +============ +ExpandedChopAllowed +============ +*/ +bool ExpandedChopAllowed( idBrush *b1, idBrush *b2 ) { + return ( b1->GetContents() == b2->GetContents() ); +} + +/* +============ +ExpandedMergeAllowed +============ +*/ +bool ExpandedMergeAllowed( idBrush *b1, idBrush *b2 ) { + return ( b1->GetContents() == b2->GetContents() ); +} + +/* +============ +idAASBuild::ChangeMultipleBoundingBoxContents +============ +*/ +void idAASBuild::ChangeMultipleBoundingBoxContents_r( idBrushBSPNode *node, int mask ) { + while( node ) { + if ( !( node->GetContents() & mask ) ) { + node->SetContents( node->GetContents() & ~AREACONTENTS_SOLID ); + } + ChangeMultipleBoundingBoxContents_r( node->GetChild( 0 ), mask ); + node = node->GetChild( 1 ); + } +} + +/* +============ +idAASBuild::Build +============ +*/ +bool idAASBuild::Build( const idStr &fileName, const idAASSettings *settings ) { + int i, bit, mask, startTime; + idMapFile * mapFile; + idBrushList brushList; + idList expandedBrushes; + idBrush *b; + idBrushBSP bsp; + idStr name; + idAASReach reach; + idAASCluster cluster; + idStrList entityClassNames; + + startTime = Sys_Milliseconds(); + + Shutdown(); + + aasSettings = settings; + + name = fileName; + name.SetFileExtension( "map" ); + + mapFile = new idMapFile; + if ( !mapFile->Parse( name ) ) { + delete mapFile; + common->Error( "Couldn't load map file: '%s'", name.c_str() ); + return false; + } + + // check if this map has any entities that use this AAS file + if ( !CheckForEntities( mapFile, entityClassNames ) ) { + delete mapFile; + common->Printf( "no entities in map that use %s\n", settings->fileExtension.c_str() ); + return true; + } + + // load map file brushes + brushList = AddBrushesForMapFile( mapFile, brushList ); + + // if empty map + if ( brushList.Num() == 0 ) { + delete mapFile; + common->Error( "%s is empty", name.c_str() ); + return false; + } + + // merge as many brushes as possible before expansion + brushList.Merge( MergeAllowed ); + + // if there is a .proc file newer than the .map file + if ( LoadProcBSP( fileName, mapFile->GetFileTime() ) ) { + ClipBrushSidesWithProcBSP( brushList ); + DeleteProcBSP(); + } + + // make copies of the brush list + expandedBrushes.Append( &brushList ); + for ( i = 1; i < aasSettings->numBoundingBoxes; i++ ) { + expandedBrushes.Append( brushList.Copy() ); + } + + // expand brushes for the axial bounding boxes + mask = AREACONTENTS_SOLID; + for ( i = 0; i < expandedBrushes.Num(); i++ ) { + for ( b = expandedBrushes[i]->Head(); b; b = b->Next() ) { + b->ExpandForAxialBox( aasSettings->boundingBoxes[i] ); + bit = 1 << ( i + AREACONTENTS_BBOX_BIT ); + mask |= bit; + b->SetContents( b->GetContents() | bit ); + } + } + + // move all brushes back into the original list + for ( i = 1; i < aasSettings->numBoundingBoxes; i++ ) { + brushList.AddToTail( *expandedBrushes[i] ); + delete expandedBrushes[i]; + } + + if ( aasSettings->writeBrushMap ) { + bsp.WriteBrushMap( fileName, "_" + aasSettings->fileExtension, AREACONTENTS_SOLID ); + } + + // build BSP tree from brushes + bsp.Build( brushList, AREACONTENTS_SOLID, ExpandedChopAllowed, ExpandedMergeAllowed ); + + // only solid nodes with all bits set for all bounding boxes need to stay solid + ChangeMultipleBoundingBoxContents_r( bsp.GetRootNode(), mask ); + + // portalize the bsp tree + bsp.Portalize(); + + // remove subspaces not reachable by entities + if ( !bsp.RemoveOutside( mapFile, AREACONTENTS_SOLID, entityClassNames ) ) { + bsp.LeakFile( name ); + delete mapFile; + common->Printf( "%s has no outside", name.c_str() ); + return false; + } + + // gravitational subdivision + GravitationalSubdivision( bsp ); + + // merge portals where possible + bsp.MergePortals( AREACONTENTS_SOLID ); + + // melt portal windings + bsp.MeltPortals( AREACONTENTS_SOLID ); + + if ( aasSettings->writeBrushMap ) { + WriteLedgeMap( fileName, "_" + aasSettings->fileExtension + "_ledge" ); + } + + // ledge subdivisions + LedgeSubdivision( bsp ); + + // merge leaf nodes + MergeLeafNodes( bsp ); + + // merge portals where possible + bsp.MergePortals( AREACONTENTS_SOLID ); + + // melt portal windings + bsp.MeltPortals( AREACONTENTS_SOLID ); + + // store the file from the bsp tree + StoreFile( bsp ); + file->settings = *aasSettings; + + // calculate reachability + reach.Build( mapFile, file ); + + // build clusters + cluster.Build( file ); + + // optimize the file + if ( !aasSettings->noOptimize ) { + file->Optimize(); + } + + // write the file + name.SetFileExtension( aasSettings->fileExtension ); + file->Write( name, mapFile->GetGeometryCRC() ); + + // delete the map file + delete mapFile; + + common->Printf( "%6d seconds to create AAS\n", (Sys_Milliseconds() - startTime) / 1000 ); + + return true; +} + +/* +============ +idAASBuild::BuildReachability +============ +*/ +bool idAASBuild::BuildReachability( const idStr &fileName, const idAASSettings *settings ) { + int startTime; + idMapFile * mapFile; + idStr name; + idAASReach reach; + idAASCluster cluster; + + startTime = Sys_Milliseconds(); + + aasSettings = settings; + + name = fileName; + name.SetFileExtension( "map" ); + + mapFile = new idMapFile; + if ( !mapFile->Parse( name ) ) { + delete mapFile; + common->Error( "Couldn't load map file: '%s'", name.c_str() ); + return false; + } + + file = new idAASCompilerFile( AASFile->CreateNew() ); + + name.SetFileExtension( aasSettings->fileExtension ); + if ( !file->Load( name, 0 ) ) { + delete mapFile; + common->Error( "Couldn't load AAS file: '%s'", name.c_str() ); + return false; + } + + file->settings = *aasSettings; + + // calculate reachability + reach.Build( mapFile, file ); + + // build clusters + cluster.Build( file ); + + // write the file + file->Write( name, mapFile->GetGeometryCRC() ); + + // delete the map file + delete mapFile; + + common->Printf( "%6d seconds to calculate reachability\n", (Sys_Milliseconds() - startTime) / 1000 ); + + return true; +} + +/* +============ +ParseOptions +============ +*/ +int ParseOptions( const idCmdArgs &args, idAASSettings &settings ) { + int i; + idStr str; + + for ( i = 1; i < args.Argc(); i++ ) { + + str = args.Argv( i ); + str.StripLeading( '-' ); + + if ( str.Icmp( "usePatches" ) == 0 ) { + settings.usePatches = true; + common->Printf( "usePatches = true\n" ); + } else if ( str.Icmp( "writeBrushMap" ) == 0 ) { + settings.writeBrushMap = true; + common->Printf( "writeBrushMap = true\n" ); + } else if ( str.Icmp( "playerFlood" ) == 0 ) { + settings.playerFlood = true; + common->Printf( "playerFlood = true\n" ); + } else if ( str.Icmp( "noOptimize" ) == 0 ) { + settings.noOptimize = true; + common->Printf( "noOptimize = true\n" ); + } + } + return args.Argc() - 1; +} + +/* +============ +RunAAS_f +============ +*/ +void RunAAS_f( const idCmdArgs &args ) { + int i; + idAASBuild aas; + idAASSettings settings; + idStr mapName; + + if ( args.Argc() <= 1 ) { + common->Printf( "runAAS [options] \n" + "options:\n" + " -usePatches = use bezier patches for collision detection.\n" + " -writeBrushMap = write a brush map with the AAS geometry.\n" + " -playerFlood = use player spawn points as valid AAS positions.\n" ); + return; + } + + common->ClearWarnings( "compiling AAS" ); + + common->SetRefreshOnPrint( true ); + + // get the aas settings definitions + const idDict *dict = gameEdit->FindEntityDefDict( "aas_types", false ); + if ( !dict ) { + common->Error( "Unable to find entityDef for 'aas_types'" ); + } + + const idKeyValue *kv = dict->MatchPrefix( "type" ); + while( kv != NULL ) { + const idDict *settingsDict = gameEdit->FindEntityDefDict( kv->GetValue(), false ); + if ( !settingsDict ) { + common->Warning( "Unable to find '%s' in def/aas.def", kv->GetValue().c_str() ); + } else { + settings.FromDict( kv->GetValue(), settingsDict ); + i = ParseOptions( args, settings ); + mapName = args.Argv(i); + mapName.BackSlashesToSlashes(); + if ( mapName.Icmpn( "maps/", 4 ) != 0 ) { + mapName = "maps/" + mapName; + } + aas.Build( mapName, &settings ); + } + + kv = dict->MatchPrefix( "type", kv ); + if ( kv ) { + common->Printf( "=======================================================\n" ); + } + } + common->SetRefreshOnPrint( false ); + common->PrintWarnings(); +} + +/* +============ +RunAASDir_f +============ +*/ +void RunAASDir_f( const idCmdArgs &args ) { + int i; + idAASBuild aas; + idAASSettings settings; + idFileList *mapFiles; + + if ( args.Argc() <= 1 ) { + common->Printf( "runAASDir \n" ); + return; + } + + common->ClearWarnings( "compiling AAS" ); + + common->SetRefreshOnPrint( true ); + + // get the aas settings definitions + const idDict *dict = gameEdit->FindEntityDefDict( "aas_types", false ); + if ( !dict ) { + common->Error( "Unable to find entityDef for 'aas_types'" ); + } + + // scan for .map files + mapFiles = fileSystem->ListFiles( idStr("maps/") + args.Argv(1), ".map" ); + + // create AAS files for all the .map files + for ( i = 0; i < mapFiles->GetNumFiles(); i++ ) { + if ( i ) { + common->Printf( "=======================================================\n" ); + } + + const idKeyValue *kv = dict->MatchPrefix( "type" ); + while( kv != NULL ) { + const idDict *settingsDict = gameEdit->FindEntityDefDict( kv->GetValue(), false ); + if ( !settingsDict ) { + common->Warning( "Unable to find '%s' in def/aas.def", kv->GetValue().c_str() ); + } else { + settings.FromDict( kv->GetValue(), settingsDict ); + aas.Build( idStr( "maps/" ) + args.Argv( 1 ) + "/" + mapFiles->GetFile( i ), &settings ); + } + + kv = dict->MatchPrefix( "type", kv ); + if ( kv ) { + common->Printf( "=======================================================\n" ); + } + } + } + + fileSystem->FreeFileList( mapFiles ); + + common->SetRefreshOnPrint( false ); + common->PrintWarnings(); +} + +/* +============ +RunReach_f +============ +*/ +void RunReach_f( const idCmdArgs &args ) { + int i; + idAASBuild aas; + idAASSettings settings; + + if ( args.Argc() <= 1 ) { + common->Printf( "runReach [options] \n" ); + return; + } + + common->ClearWarnings( "calculating AAS reachability" ); + + common->SetRefreshOnPrint( true ); + + // get the aas settings definitions + const idDict *dict = gameEdit->FindEntityDefDict( "aas_types", false ); + if ( !dict ) { + common->Error( "Unable to find entityDef for 'aas_types'" ); + } + + const idKeyValue *kv = dict->MatchPrefix( "type" ); + while( kv != NULL ) { + const idDict *settingsDict = gameEdit->FindEntityDefDict( kv->GetValue(), false ); + if ( !settingsDict ) { + common->Warning( "Unable to find '%s' in def/aas.def", kv->GetValue().c_str() ); + } else { + settings.FromDict( kv->GetValue(), settingsDict ); + i = ParseOptions( args, settings ); + aas.BuildReachability( idStr("maps/") + args.Argv(i), &settings ); + } + + kv = dict->MatchPrefix( "type", kv ); + if ( kv ) { + common->Printf( "=======================================================\n" ); + } + } + + common->SetRefreshOnPrint( false ); + common->PrintWarnings(); +} diff --git a/src/aas/AASBuild_file.cpp b/src/aas/AASBuild_file.cpp new file mode 100644 index 0000000..63200f1 --- /dev/null +++ b/src/aas/AASBuild_file.cpp @@ -0,0 +1,485 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../idlib/precompiled.h" +#pragma hdrstop + +#include "AASBuild_local.h" + +#define VERTEX_HASH_BOXSIZE (1<<6) // must be power of 2 +#define VERTEX_HASH_SIZE (VERTEX_HASH_BOXSIZE*VERTEX_HASH_BOXSIZE) +#define EDGE_HASH_SIZE (1<<14) + +#define INTEGRAL_EPSILON 0.01f +#define VERTEX_EPSILON 0.1f + +#define AAS_PLANE_NORMAL_EPSILON 0.00001f +#define AAS_PLANE_DIST_EPSILON 0.01f + + +idHashIndex *aas_vertexHash; +idHashIndex *aas_edgeHash; +idBounds aas_vertexBounds; +int aas_vertexShift; + +/* +================ +idAASBuild::SetupHash +================ +*/ +void idAASBuild::SetupHash( void ) { + aas_vertexHash = new idHashIndex( VERTEX_HASH_SIZE, 1024 ); + aas_edgeHash = new idHashIndex( EDGE_HASH_SIZE, 1024 ); +} + +/* +================ +idAASBuild::ShutdownHash +================ +*/ +void idAASBuild::ShutdownHash( void ) { + delete aas_vertexHash; + delete aas_edgeHash; +} + +/* +================ +idAASBuild::ClearHash +================ +*/ +void idAASBuild::ClearHash( const idBounds &bounds ) { + int i; + float f, max; + + aas_vertexHash->Clear(); + aas_edgeHash->Clear(); + aas_vertexBounds = bounds; + + max = bounds[1].x - bounds[0].x; + f = bounds[1].y - bounds[0].y; + if ( f > max ) { + max = f; + } + aas_vertexShift = (float) max / VERTEX_HASH_BOXSIZE; + for ( i = 0; (1<> 2; + y = (((int) (vec[1] - aas_vertexBounds[0].y + 0.5)) + 2) >> 2; + return (x + y * VERTEX_HASH_BOXSIZE) & (VERTEX_HASH_SIZE-1); +} + +/* +================ +idAASBuild::GetVertex +================ +*/ +bool idAASBuild::GetVertex( const idVec3 &v, int *vertexNum ) { + int i, hashKey, vn; + aasVertex_t vert, *p; + + for (i = 0; i < 3; i++) { + if ( idMath::Fabs(v[i] - idMath::Rint(v[i])) < INTEGRAL_EPSILON ) { + vert[i] = idMath::Rint(v[i]); + } + else { + vert[i] = v[i]; + } + } + + hashKey = idAASBuild::HashVec( vert ); + + for ( vn = aas_vertexHash->First( hashKey ); vn >= 0; vn = aas_vertexHash->Next( vn ) ) { + p = &file->vertices[vn]; + // first compare z-axis because hash is based on x-y plane + if (idMath::Fabs( vert.z - p->z ) < VERTEX_EPSILON && + idMath::Fabs( vert.x - p->x ) < VERTEX_EPSILON && + idMath::Fabs( vert.y - p->y ) < VERTEX_EPSILON ) + { + *vertexNum = vn; + return true; + } + } + + *vertexNum = file->vertices.Num(); + aas_vertexHash->Add( hashKey, file->vertices.Num() ); + file->vertices.Append( vert ); + + return false; +} + +/* +================ +idAASBuild::GetEdge +================ +*/ +bool idAASBuild::GetEdge( const idVec3 &v1, const idVec3 &v2, int *edgeNum, int v1num ) { + int v2num, hashKey, e; + int *vertexNum; + aasEdge_t edge; + bool found; + + if ( v1num != -1 ) { + found = true; + } + else { + found = GetVertex( v1, &v1num ); + } + found &= GetVertex( v2, &v2num ); + // if both vertexes are the same or snapped onto each other + if ( v1num == v2num ) { + *edgeNum = 0; + return true; + } + hashKey = aas_edgeHash->GenerateKey( v1num, v2num ); + // if both vertexes where already stored + if ( found ) { + for ( e = aas_edgeHash->First( hashKey ); e >= 0; e = aas_edgeHash->Next( e ) ) { + + vertexNum = file->edges[e].vertexNum; + if ( vertexNum[0] == v2num ) { + if ( vertexNum[1] == v1num ) { + // negative for a reversed edge + *edgeNum = -e; + break; + } + } + else if ( vertexNum[0] == v1num ) { + if ( vertexNum[1] == v2num ) { + *edgeNum = e; + break; + } + } + } + // if edge found in hash + if ( e >= 0 ) { + return true; + } + } + + *edgeNum = file->edges.Num(); + aas_edgeHash->Add( hashKey, file->edges.Num() ); + + edge.vertexNum[0] = v1num; + edge.vertexNum[1] = v2num; + + file->edges.Append( edge ); + + return false; +} + +/* +================ +idAASBuild::GetFaceForPortal +================ +*/ +bool idAASBuild::GetFaceForPortal( idBrushBSPPortal *portal, int side, int *faceNum ) { + int i, j, v1num; + int numFaceEdges, faceEdges[MAX_POINTS_ON_WINDING]; + idWinding *w; + aasFace_t face; + + if ( portal->GetFaceNum() > 0 ) { + if ( side ) { + *faceNum = -portal->GetFaceNum(); + } + else { + *faceNum = portal->GetFaceNum(); + } + return true; + } + + w = portal->GetWinding(); + // turn the winding into a sequence of edges + numFaceEdges = 0; + v1num = -1; // first vertex unknown + for ( i = 0; i < w->GetNumPoints(); i++ ) { + + GetEdge( (*w)[i].ToVec3(), (*w)[(i+1)%w->GetNumPoints()].ToVec3(), &faceEdges[numFaceEdges], v1num ); + + if ( faceEdges[numFaceEdges] ) { + // last vertex of this edge is the first vertex of the next edge + v1num = file->edges[ abs(faceEdges[numFaceEdges]) ].vertexNum[ INTSIGNBITNOTSET(faceEdges[numFaceEdges]) ]; + + // this edge is valid so keep it + numFaceEdges++; + } + } + + // should have at least 3 edges + if ( numFaceEdges < 3 ) { + return false; + } + + // the polygon is invalid if some edge is found twice + for ( i = 0; i < numFaceEdges; i++ ) { + for ( j = i+1; j < numFaceEdges; j++ ) { + if ( faceEdges[i] == faceEdges[j] || faceEdges[i] == -faceEdges[j] ) { + return false; + } + } + } + + portal->SetFaceNum( file->faces.Num() ); + + face.planeNum = file->planeList.FindPlane( portal->GetPlane(), AAS_PLANE_NORMAL_EPSILON, AAS_PLANE_DIST_EPSILON ); + face.flags = portal->GetFlags(); + face.areas[0] = face.areas[1] = 0; + face.firstEdge = file->edgeIndex.Num(); + face.numEdges = numFaceEdges; + for ( i = 0; i < numFaceEdges; i++ ) { + file->edgeIndex.Append( faceEdges[i] ); + } + if ( side ) { + *faceNum = -file->faces.Num(); + } + else { + *faceNum = file->faces.Num(); + } + file->faces.Append( face ); + + return true; +} + +/* +================ +idAASBuild::GetAreaForLeafNode +================ +*/ +bool idAASBuild::GetAreaForLeafNode( idBrushBSPNode *node, int *areaNum ) { + int s, faceNum; + idBrushBSPPortal *p; + aasArea_t area; + + if ( node->GetAreaNum() ) { + *areaNum = -node->GetAreaNum(); + return true; + } + + area.flags = node->GetFlags(); + area.cluster = area.clusterAreaNum = 0; + area.contents = node->GetContents(); + area.firstFace = file->faceIndex.Num(); + area.numFaces = 0; + area.reach = NULL; + area.rev_reach = NULL; + + for ( p = node->GetPortals(); p; p = p->Next(s) ) { + s = (p->GetNode(1) == node); + + if ( !GetFaceForPortal( p, s, &faceNum ) ) { + continue; + } + + file->faceIndex.Append( faceNum ); + area.numFaces++; + + if ( faceNum > 0 ) { + file->faces[abs(faceNum)].areas[0] = file->areas.Num(); + } + else { + file->faces[abs(faceNum)].areas[1] = file->areas.Num(); + } + } + + if ( !area.numFaces ) { + *areaNum = 0; + return false; + } + + *areaNum = -file->areas.Num(); + node->SetAreaNum( file->areas.Num() ); + file->areas.Append( area ); + + DisplayRealTimeString( "\r%6d", file->areas.Num() ); + + return true; +} + +/* +================ +idAASBuild::StoreTree_r +================ +*/ +int idAASBuild::StoreTree_r( idBrushBSPNode *node ) { + int areaNum, nodeNum, child0, child1; + aasNode_t aasNode; + + if ( !node ) { + return 0; + } + + if ( node->GetContents() & AREACONTENTS_SOLID ) { + return 0; + } + + if ( !node->GetChild(0) && !node->GetChild(1) ) { + if ( GetAreaForLeafNode( node, &areaNum ) ) { + return areaNum; + } + return 0; + } + + aasNode.planeNum = file->planeList.FindPlane( node->GetPlane(), AAS_PLANE_NORMAL_EPSILON, AAS_PLANE_DIST_EPSILON ); + aasNode.children[0] = aasNode.children[1] = 0; + nodeNum = file->nodes.Num(); + file->nodes.Append( aasNode ); + + // !@#$%^ cause of some bug we cannot set the children directly with the StoreTree_r return value + child0 = StoreTree_r( node->GetChild(0) ); + file->nodes[nodeNum].children[0] = child0; + child1 = StoreTree_r( node->GetChild(1) ); + file->nodes[nodeNum].children[1] = child1; + + if ( !child0 && !child1 ) { + file->nodes.SetNum( file->nodes.Num()-1 ); + return 0; + } + + return nodeNum; +} + +/* +================ +idAASBuild::GetSizeEstimate_r +================ +*/ +void idAASBuild::GetSizeEstimate_r( idBrushBSPNode *parent, idBrushBSPNode *node, struct sizeEstimate_s &size ) { + idBrushBSPPortal *p; + int s; + + if ( !node ) { + return; + } + + if ( node->GetContents() & AREACONTENTS_SOLID ) { + return; + } + + if ( !node->GetChild(0) && !node->GetChild(1) ) { + // multiple branches of the bsp tree might point to the same leaf node + if ( node->GetParent() == parent ) { + size.numAreas++; + for ( p = node->GetPortals(); p; p = p->Next(s) ) { + s = (p->GetNode(1) == node); + size.numFaceIndexes++; + size.numEdgeIndexes += p->GetWinding()->GetNumPoints(); + } + } + } + else { + size.numNodes++; + } + + GetSizeEstimate_r( node, node->GetChild(0), size ); + GetSizeEstimate_r( node, node->GetChild(1), size ); +} + +/* +================ +idAASBuild::SetSizeEstimate +================ +*/ +void idAASBuild::SetSizeEstimate( const idBrushBSP &bsp, idAASCompilerFile *file ) { + sizeEstimate_t size; + + size.numEdgeIndexes = 1; + size.numFaceIndexes = 1; + size.numAreas = 1; + size.numNodes = 1; + + GetSizeEstimate_r( NULL, bsp.GetRootNode(), size ); + + file->SetSizes( size ); +} + +/* +================ +idAASBuild::StoreFile +================ +*/ +bool idAASBuild::StoreFile( const idBrushBSP &bsp ) { + aasEdge_t edge; + aasFace_t face; + aasArea_t area; + aasNode_t node; + + common->Printf( "[Store AAS]\n" ); + + SetupHash(); + ClearHash( bsp.GetTreeBounds() ); + + file = new idAASCompilerFile( AASFile->CreateNew() ); + + file->Clear(); + + SetSizeEstimate( bsp, file ); + + // the first edge is a dummy + memset( &edge, 0, sizeof( edge ) ); + file->edges.Append( edge ); + + // the first face is a dummy + memset( &face, 0, sizeof( face ) ); + file->faces.Append( face ); + + // the first area is a dummy + memset( &area, 0, sizeof( area ) ); + file->areas.Append( area ); + + // the first node is a dummy + memset( &node, 0, sizeof( node ) ); + file->nodes.Append( node ); + + // store the tree + StoreTree_r( bsp.GetRootNode() ); + + // calculate area bounds and a reachable point in the area + file->FinishAreas(); + + ShutdownHash(); + + common->Printf( "\r%6d areas\n", file->areas.Num() ); + + return true; +} diff --git a/src/aas/AASBuild_gravity.cpp b/src/aas/AASBuild_gravity.cpp new file mode 100644 index 0000000..cdfa53d --- /dev/null +++ b/src/aas/AASBuild_gravity.cpp @@ -0,0 +1,359 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../idlib/precompiled.h" +#pragma hdrstop + +#include "AASBuild_local.h" + + +/* +============ +idAASBuild::SetPortalFlags_r +============ +*/ +void idAASBuild::SetPortalFlags_r( idBrushBSPNode *node ) { + int s; + idBrushBSPPortal *p; + idVec3 normal; + + if ( !node ) { + return; + } + + if ( node->GetContents() & AREACONTENTS_SOLID ) { + return; + } + + if ( !node->GetChild(0) && !node->GetChild(1) ) { + for ( p = node->GetPortals(); p; p = p->Next(s) ) { + s = (p->GetNode(1) == node); + + // if solid at the other side of the portal + if ( p->GetNode(!s)->GetContents() & AREACONTENTS_SOLID ) { + if ( s ) { + normal = -p->GetPlane().Normal(); + } + else { + normal = p->GetPlane().Normal(); + } + if ( normal * aasSettings->invGravityDir > aasSettings->minFloorCos ) { + p->SetFlag( FACE_FLOOR ); + } + else { + p->SetFlag( FACE_SOLID ); + } + } + } + return; + } + + SetPortalFlags_r( node->GetChild(0) ); + SetPortalFlags_r( node->GetChild(1) ); +} + +/* +============ +idAASBuild::PortalIsGap +============ +*/ +bool idAASBuild::PortalIsGap( idBrushBSPPortal *portal, int side ) { + idVec3 normal; + + // if solid at the other side of the portal + if ( portal->GetNode(!side)->GetContents() & AREACONTENTS_SOLID ) { + return false; + } + + if ( side ) { + normal = -(portal->GetPlane().Normal()); + } + else { + normal = portal->GetPlane().Normal(); + } + if ( normal * aasSettings->invGravityDir > aasSettings->minFloorCos ) { + return true; + } + return false; +} + +/* +============ +idAASBuild::GravSubdivLeafNode +============ +*/ +#define FACE_CHECKED BIT(31) +#define GRAVSUBDIV_EPSILON 0.1f + +void idAASBuild::GravSubdivLeafNode( idBrushBSPNode *node ) { + int s1, s2, i, j, k, side1; + int numSplits, numSplitters; + idBrushBSPPortal *p1, *p2; + idWinding *w1, *w2; + idVec3 normal; + idPlane plane; + idPlaneSet planeList; + float d, min, max; + int *splitterOrder; + int *bestNumSplits; + int floor, gap, numFloorChecked; + + // if this leaf node is already classified it cannot have a combination of floor and gap portals + if ( node->GetFlags() & (AREA_FLOOR|AREA_GAP) ) { + return; + } + + floor = gap = 0; + + // check if the area has a floor + for ( p1 = node->GetPortals(); p1; p1 = p1->Next(s1) ) { + s1 = (p1->GetNode(1) == node); + + if ( p1->GetFlags() & FACE_FLOOR ) { + floor++; + } + } + + // find seperating planes between gap and floor portals + for ( p1 = node->GetPortals(); p1; p1 = p1->Next(s1) ) { + s1 = (p1->GetNode(1) == node); + + // if the portal is a gap seen from this side + if ( PortalIsGap( p1, s1 ) ) { + gap++; + // if the area doesn't have a floor + if ( !floor ) { + break; + } + } + else { + continue; + } + + numFloorChecked = 0; + + w1 = p1->GetWinding(); + + // test all edges of the gap + for ( i = 0; i < w1->GetNumPoints(); i++ ) { + + // create a plane through the edge of the gap parallel to the direction of gravity + normal = (*w1)[(i+1)%w1->GetNumPoints()].ToVec3() - (*w1)[i].ToVec3(); + normal = normal.Cross( aasSettings->invGravityDir ); + if ( normal.Normalize() < 0.2f ) { + continue; + } + plane.SetNormal( normal ); + plane.FitThroughPoint( (*w1)[i].ToVec3() ); + + // get the side of the plane the gap is on + side1 = w1->PlaneSide( plane, GRAVSUBDIV_EPSILON ); + if ( side1 == SIDE_ON ) { + break; + } + + // test if the plane through the edge of the gap seperates the gap from a floor portal + for ( p2 = node->GetPortals(); p2; p2 = p2->Next(s2) ) { + s2 = (p2->GetNode(1) == node); + + if ( !( p2->GetFlags() & FACE_FLOOR ) ) { + continue; + } + + if ( p2->GetFlags() & FACE_CHECKED ) { + continue; + } + + w2 = p2->GetWinding(); + + min = 2.0f * GRAVSUBDIV_EPSILON; + max = GRAVSUBDIV_EPSILON; + if ( side1 == SIDE_FRONT ) { + for ( j = 0; j < w2->GetNumPoints(); j++ ) { + d = plane.Distance( (*w2)[j].ToVec3() ); + if ( d >= GRAVSUBDIV_EPSILON ) { + break; // point at the same side of the plane as the gap + } + d = idMath::Fabs( d ); + if ( d < min ) { + min = d; + } + if ( d > max ) { + max = d; + } + } + } + else { + for ( j = 0; j < w2->GetNumPoints(); j++ ) { + d = plane.Distance( (*w2)[j].ToVec3() ); + if ( d <= -GRAVSUBDIV_EPSILON ) { + break; // point at the same side of the plane as the gap + } + d = idMath::Fabs( d ); + if ( d < min ) { + min = d; + } + if ( d > max ) { + max = d; + } + } + } + + // a point of the floor portal was found to be at the same side of the plane as the gap + if ( j < w2->GetNumPoints() ) { + continue; + } + + // if the floor portal touches the plane + if ( min < GRAVSUBDIV_EPSILON && max > GRAVSUBDIV_EPSILON ) { + planeList.FindPlane( plane, 0.00001f, 0.1f ); + } + + p2->SetFlag( FACE_CHECKED ); + numFloorChecked++; + + } + if ( numFloorChecked == floor ) { + break; + } + } + + for ( p2 = node->GetPortals(); p2; p2 = p2->Next(s2) ) { + s2 = (p2->GetNode(1) == node); + p2->RemoveFlag( FACE_CHECKED ); + } + } + + // if the leaf node does not have both floor and gap portals + if ( !( gap && floor) ) { + if ( floor ) { + node->SetFlag( AREA_FLOOR ); + } + else if ( gap ) { + node->SetFlag( AREA_GAP ); + } + return; + } + + // if no valid seperators found + if ( planeList.Num() == 0 ) { + // NOTE: this should never happend, if it does the leaf node has degenerate portals + return; + } + + splitterOrder = (int *) _alloca( planeList.Num() * sizeof( int ) ); + bestNumSplits = (int *) _alloca( planeList.Num() * sizeof( int ) ); + numSplitters = 0; + + // test all possible seperators and sort them from best to worst + for ( i = 0; i < planeList.Num(); i += 2 ) { + numSplits = 0; + + for ( p1 = node->GetPortals(); p1; p1 = p1->Next(s1) ) { + s1 = (p1->GetNode(1) == node); + if ( p1->GetWinding()->PlaneSide( planeList[i], 0.1f ) == SIDE_CROSS ) { + numSplits++; + } + } + + for ( j = 0; j < numSplitters; j++ ) { + if ( numSplits < bestNumSplits[j] ) { + for ( k = numSplitters; k > j; k-- ) { + bestNumSplits[k] = bestNumSplits[k-1]; + splitterOrder[k] = splitterOrder[k-1]; + } + bestNumSplits[j] = numSplits; + splitterOrder[j] = i; + numSplitters++; + break; + } + } + if ( j >= numSplitters ) { + bestNumSplits[j] = numSplits; + splitterOrder[j] = i; + numSplitters++; + } + } + + // try all seperators in order from best to worst + for ( i = 0; i < numSplitters; i++ ) { + if ( node->Split( planeList[splitterOrder[i]], -1 ) ) { + // we found a seperator that works + break; + } + } + if ( i >= numSplitters) { + return; + } + + DisplayRealTimeString( "\r%6d", ++numGravitationalSubdivisions ); + + // test children for further splits + GravSubdivLeafNode( node->GetChild(0) ); + GravSubdivLeafNode( node->GetChild(1) ); +} + +/* +============ +idAASBuild::GravSubdiv_r +============ +*/ +void idAASBuild::GravSubdiv_r( idBrushBSPNode *node ) { + + if ( !node ) { + return; + } + + if ( node->GetContents() & AREACONTENTS_SOLID ) { + return; + } + + if ( !node->GetChild(0) && !node->GetChild(1) ) { + GravSubdivLeafNode( node ); + return; + } + + GravSubdiv_r( node->GetChild(0) ); + GravSubdiv_r( node->GetChild(1) ); +} + +/* +============ +idAASBuild::GravitationalSubdivision +============ +*/ +void idAASBuild::GravitationalSubdivision( idBrushBSP &bsp ) { + numGravitationalSubdivisions = 0; + + common->Printf( "[Gravitational Subdivision]\n" ); + + SetPortalFlags_r( bsp.GetRootNode() ); + GravSubdiv_r( bsp.GetRootNode() ); + + common->Printf( "\r%6d subdivisions\n", numGravitationalSubdivisions ); +} diff --git a/src/aas/AASBuild_ledge.cpp b/src/aas/AASBuild_ledge.cpp new file mode 100644 index 0000000..363c876 --- /dev/null +++ b/src/aas/AASBuild_ledge.cpp @@ -0,0 +1,575 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../idlib/precompiled.h" +#pragma hdrstop + +#include "AASBuild_local.h" + +#define LEDGE_EPSILON 0.1f + +//=============================================================== +// +// idLedge +// +//=============================================================== + +/* +============ +idLedge::idLedge +============ +*/ +idLedge::idLedge( void ) { +} + +/* +============ +idLedge::idLedge +============ +*/ +idLedge::idLedge( const idVec3 &v1, const idVec3 &v2, const idVec3 &gravityDir, idBrushBSPNode *n ) { + start = v1; + end = v2; + node = n; + numPlanes = 4; + planes[0].SetNormal( (v1 - v2).Cross( gravityDir ) ); + planes[0].Normalize(); + planes[0].FitThroughPoint( v1 ); + planes[1].SetNormal( (v1 - v2).Cross( planes[0].Normal() ) ); + planes[1].Normalize(); + planes[1].FitThroughPoint( v1 ); + planes[2].SetNormal( v1 - v2 ); + planes[2].Normalize(); + planes[2].FitThroughPoint( v1 ); + planes[3].SetNormal( v2 - v1 ); + planes[3].Normalize(); + planes[3].FitThroughPoint( v2 ); +} + +/* +============ +idLedge::AddPoint +============ +*/ +void idLedge::AddPoint( const idVec3 &v ) { + if ( planes[2].Distance( v ) > 0.0f ) { + start = v; + planes[2].FitThroughPoint( start ); + } + if ( planes[3].Distance( v ) > 0.0f ) { + end = v; + planes[3].FitThroughPoint( end ); + } +} + +/* +============ +idLedge::CreateBevels + + NOTE: this assumes the gravity is vertical +============ +*/ +void idLedge::CreateBevels( const idVec3 &gravityDir ) { + int i, j; + idBounds bounds; + idVec3 size, normal; + + bounds.Clear(); + bounds.AddPoint( start ); + bounds.AddPoint( end ); + size = bounds[1] - bounds[0]; + + // plane through ledge + planes[0].SetNormal( (start - end).Cross( gravityDir ) ); + planes[0].Normalize(); + planes[0].FitThroughPoint( start ); + // axial bevels at start and end point + i = size[1] > size[0]; + normal = vec3_origin; + normal[i] = 1.0f; + j = end[i] > start[i]; + planes[1+j].SetNormal( normal ); + planes[1+!j].SetNormal( -normal ); + planes[1].FitThroughPoint( start ); + planes[2].FitThroughPoint( end ); + numExpandedPlanes = 3; + // if additional bevels are required + if ( idMath::Fabs( size[!i] ) > 0.01f ) { + normal = vec3_origin; + normal[!i] = 1.0f; + j = end[!i] > start[!i]; + planes[3+j].SetNormal( normal ); + planes[3+!j].SetNormal( -normal ); + planes[3].FitThroughPoint( start ); + planes[4].FitThroughPoint( end ); + numExpandedPlanes = 5; + } + // opposite of first + planes[numExpandedPlanes+0] = -planes[0]; + // number of planes used for splitting + numSplitPlanes = numExpandedPlanes + 1; + // top plane + planes[numSplitPlanes+0].SetNormal( (start - end).Cross( planes[0].Normal() ) ); + planes[numSplitPlanes+0].Normalize(); + planes[numSplitPlanes+0].FitThroughPoint( start ); + // bottom plane + planes[numSplitPlanes+1] = -planes[numSplitPlanes+0]; + // total number of planes + numPlanes = numSplitPlanes + 2; +} + +/* +============ +idLedge::Expand +============ +*/ +void idLedge::Expand( const idBounds &bounds, float maxStepHeight ) { + int i, j; + idVec3 v; + + for ( i = 0; i < numExpandedPlanes; i++ ) { + + for ( j = 0; j < 3; j++ ) { + if ( planes[i].Normal()[j] > 0.0f ) { + v[j] = bounds[0][j]; + } + else { + v[j] = bounds[1][j]; + } + } + + planes[i].SetDist( planes[i].Dist() + v * -planes[i].Normal() ); + } + + planes[numSplitPlanes+0].SetDist( planes[numSplitPlanes+0].Dist() + maxStepHeight ); + planes[numSplitPlanes+1].SetDist( planes[numSplitPlanes+1].Dist() + 1.0f ); +} + +/* +============ +idLedge::ChopWinding +============ +*/ +idWinding *idLedge::ChopWinding( const idWinding *winding ) const { + int i; + idWinding *w; + + w = winding->Copy(); + for ( i = 0; i < numPlanes && w; i++ ) { + w = w->Clip( -planes[i], ON_EPSILON, true ); + } + return w; +} + +/* +============ +idLedge::PointBetweenBounds +============ +*/ +bool idLedge::PointBetweenBounds( const idVec3 &v ) const { + return ( planes[2].Distance( v ) < LEDGE_EPSILON ) && ( planes[3].Distance( v ) < LEDGE_EPSILON ); +} + + +//=============================================================== +// +// idAASBuild +// +//=============================================================== + +/* +============ +idAASBuild::LedgeSubdivFlood_r +============ +*/ +void idAASBuild::LedgeSubdivFlood_r( idBrushBSPNode *node, const idLedge *ledge ) { + int s1, i; + idBrushBSPPortal *p1; + idWinding *w; + idList nodeList; + + if ( node->GetFlags() & NODE_VISITED ) { + return; + } + + // if this is not already a ledge area + if ( !( node->GetFlags() & AREA_LEDGE ) ) { + for ( p1 = node->GetPortals(); p1; p1 = p1->Next(s1) ) { + s1 = (p1->GetNode(1) == node); + + if ( !(p1->GetFlags() & FACE_FLOOR) ) { + continue; + } + + // split the area if some part of the floor portal is inside the expanded ledge + w = ledge->ChopWinding( p1->GetWinding() ); + if ( !w ) { + continue; + } + delete w; + + for ( i = 0; i < ledge->numSplitPlanes; i++ ) { + if ( node->PlaneSide( ledge->planes[i], 0.1f ) != SIDE_CROSS ) { + continue; + } + if ( !node->Split( ledge->planes[i], -1 ) ) { + continue; + } + numLedgeSubdivisions++; + DisplayRealTimeString( "\r%6d", numLedgeSubdivisions ); + node->GetChild(0)->SetFlag( NODE_VISITED ); + LedgeSubdivFlood_r( node->GetChild(1), ledge ); + return; + } + + node->SetFlag( AREA_LEDGE ); + break; + } + } + + node->SetFlag( NODE_VISITED ); + + // get all nodes we might need to flood into + for ( p1 = node->GetPortals(); p1; p1 = p1->Next(s1) ) { + s1 = (p1->GetNode(1) == node); + + if ( p1->GetNode( !s1 )->GetContents() & AREACONTENTS_SOLID ) { + continue; + } + + // flood through this portal if the portal is partly inside the expanded ledge + w = ledge->ChopWinding( p1->GetWinding() ); + if ( !w ) { + continue; + } + delete w; + // add to list, cannot flood directly cause portals might be split on the way + nodeList.Append( p1->GetNode( !s1 ) ); + } + + // flood into other nodes + for ( i = 0; i < nodeList.Num(); i++ ) { + LedgeSubdivLeafNodes_r( nodeList[i], ledge ); + } +} + +/* +============ +idAASBuild::LedgeSubdivLeafNodes_r + + The node the ledge was originally part of might be split by other ledges. + Here we recurse down the tree from the original node to find all the new leaf nodes the ledge might be part of. +============ +*/ +void idAASBuild::LedgeSubdivLeafNodes_r( idBrushBSPNode *node, const idLedge *ledge ) { + if ( !node ) { + return; + } + if ( !node->GetChild(0) && !node->GetChild(1) ) { + LedgeSubdivFlood_r( node, ledge ); + return; + } + LedgeSubdivLeafNodes_r( node->GetChild(0), ledge ); + LedgeSubdivLeafNodes_r( node->GetChild(1), ledge ); +} + +/* +============ +idAASBuild::LedgeSubdiv +============ +*/ +void idAASBuild::LedgeSubdiv( idBrushBSPNode *root ) { + int i, j; + idBrush *brush; + idList sideList; + + // create ledge bevels and expand ledges + for ( i = 0; i < ledgeList.Num(); i++ ) { + + ledgeList[i].CreateBevels( aasSettings->gravityDir ); + ledgeList[i].Expand( aasSettings->boundingBoxes[0], aasSettings->maxStepHeight ); + + // if we should write out a ledge map + if ( ledgeMap ) { + sideList.SetNum( 0 ); + for ( j = 0; j < ledgeList[i].numPlanes; j++ ) { + sideList.Append( new idBrushSide( ledgeList[i].planes[j], -1 ) ); + } + + brush = new idBrush(); + brush->FromSides( sideList ); + + ledgeMap->WriteBrush( brush ); + + delete brush; + } + + // flood tree from the ledge node and subdivide areas with the ledge + LedgeSubdivLeafNodes_r( ledgeList[i].node, &ledgeList[i] ); + + // remove the node visited flags + ledgeList[i].node->RemoveFlagRecurseFlood( NODE_VISITED ); + } +} + +/* +============ +idAASBuild::IsLedgeSide_r +============ +*/ +bool idAASBuild::IsLedgeSide_r( idBrushBSPNode *node, idFixedWinding *w, const idPlane &plane, const idVec3 &normal, const idVec3 &origin, const float radius ) { + int res, i; + idFixedWinding back; + float dist; + + if ( !node ) { + return false; + } + + while ( node->GetChild(0) && node->GetChild(1) ) { + dist = node->GetPlane().Distance( origin ); + if ( dist > radius ) { + res = SIDE_FRONT; + } + else if ( dist < -radius ) { + res = SIDE_BACK; + } + else { + res = w->Split( &back, node->GetPlane(), LEDGE_EPSILON ); + } + if ( res == SIDE_FRONT ) { + node = node->GetChild(0); + } + else if ( res == SIDE_BACK ) { + node = node->GetChild(1); + } + else if ( res == SIDE_ON ) { + // continue with the side the winding faces + if ( node->GetPlane().Normal() * normal > 0.0f ) { + node = node->GetChild(0); + } + else { + node = node->GetChild(1); + } + } + else { + if ( IsLedgeSide_r( node->GetChild(1), &back, plane, normal, origin, radius ) ) { + return true; + } + node = node->GetChild(0); + } + } + + if ( node->GetContents() & AREACONTENTS_SOLID ) { + return false; + } + + for ( i = 0; i < w->GetNumPoints(); i++ ) { + if ( plane.Distance( (*w)[i].ToVec3() ) > 0.0f ) { + return true; + } + } + + return false; +} + +/* +============ +idAASBuild::AddLedge +============ +*/ +void idAASBuild::AddLedge( const idVec3 &v1, const idVec3 &v2, idBrushBSPNode *node ) { + int i, j, merged; + + // first try to merge the ledge with existing ledges + merged = -1; + for ( i = 0; i < ledgeList.Num(); i++ ) { + + for ( j = 0; j < 2; j++ ) { + if ( idMath::Fabs( ledgeList[i].planes[j].Distance( v1 ) ) > LEDGE_EPSILON ) { + break; + } + if ( idMath::Fabs( ledgeList[i].planes[j].Distance( v2 ) ) > LEDGE_EPSILON ) { + break; + } + } + if ( j < 2 ) { + continue; + } + + if ( !ledgeList[i].PointBetweenBounds( v1 ) && + !ledgeList[i].PointBetweenBounds( v2 ) ) { + continue; + } + + if ( merged == -1 ) { + ledgeList[i].AddPoint( v1 ); + ledgeList[i].AddPoint( v2 ); + merged = i; + } + else { + ledgeList[merged].AddPoint( ledgeList[i].start ); + ledgeList[merged].AddPoint( ledgeList[i].end ); + ledgeList.RemoveIndex(i); + break; + } + } + + // if the ledge could not be merged + if ( merged == -1 ) { + ledgeList.Append( idLedge( v1, v2, aasSettings->gravityDir, node ) ); + } +} + +/* +============ +idAASBuild::FindLeafNodeLedges +============ +*/ +void idAASBuild::FindLeafNodeLedges( idBrushBSPNode *root, idBrushBSPNode *node ) { + int s1, i; + idBrushBSPPortal *p1; + idWinding *w; + idVec3 v1, v2, normal, origin; + idFixedWinding winding; + idBounds bounds; + idPlane plane; + float radius; + + for ( p1 = node->GetPortals(); p1; p1 = p1->Next(s1) ) { + s1 = (p1->GetNode(1) == node); + + if ( !(p1->GetFlags() & FACE_FLOOR) ) { + continue; + } + + if ( s1 ) { + plane = p1->GetPlane(); + w = p1->GetWinding()->Reverse(); + } + else { + plane = -p1->GetPlane(); + w = p1->GetWinding(); + } + + for ( i = 0; i < w->GetNumPoints(); i++ ) { + + v1 = (*w)[i].ToVec3(); + v2 = (*w)[(i+1)%w->GetNumPoints()].ToVec3(); + normal = (v2 - v1).Cross( aasSettings->gravityDir ); + if ( normal.Normalize() < 0.5f ) { + continue; + } + + winding.Clear(); + winding += v1 + normal * LEDGE_EPSILON * 0.5f; + winding += v2 + normal * LEDGE_EPSILON * 0.5f; + winding += winding[1].ToVec3() + ( aasSettings->maxStepHeight + 1.0f ) * aasSettings->gravityDir; + winding += winding[0].ToVec3() + ( aasSettings->maxStepHeight + 1.0f ) * aasSettings->gravityDir; + + winding.GetBounds( bounds ); + origin = (bounds[1] - bounds[0]) * 0.5f; + radius = origin.Length() + LEDGE_EPSILON; + origin = bounds[0] + origin; + + plane.FitThroughPoint( v1 + aasSettings->maxStepHeight * aasSettings->gravityDir ); + + if ( !IsLedgeSide_r( root, &winding, plane, normal, origin, radius ) ) { + continue; + } + + AddLedge( v1, v2, node ); + } + + if ( w != p1->GetWinding() ) { + delete w; + } + } +} + +/* +============ +idAASBuild::FindLedges_r +============ +*/ +void idAASBuild::FindLedges_r( idBrushBSPNode *root, idBrushBSPNode *node ) { + if ( !node ) { + return; + } + + if ( node->GetContents() & AREACONTENTS_SOLID ) { + return; + } + + if ( !node->GetChild(0) && !node->GetChild(1) ) { + if ( node->GetFlags() & NODE_VISITED ) { + return; + } + FindLeafNodeLedges( root, node ); + node->SetFlag( NODE_VISITED ); + return; + } + + FindLedges_r( root, node->GetChild(0) ); + FindLedges_r( root, node->GetChild(1) ); +} + +/* +============ +idAASBuild::WriteLedgeMap +============ +*/ +void idAASBuild::WriteLedgeMap( const idStr &fileName, const idStr &ext ) { + ledgeMap = new idBrushMap( fileName, ext ); + ledgeMap->SetTexture( "textures/base_trim/bluetex4q_ed" ); +} + +/* +============ +idAASBuild::LedgeSubdivision + + NOTE: this assumes the bounding box is higher than the maximum step height + only ledges with vertical sides are considered +============ +*/ +void idAASBuild::LedgeSubdivision( idBrushBSP &bsp ) { + numLedgeSubdivisions = 0; + ledgeList.Clear(); + + common->Printf( "[Ledge Subdivision]\n" ); + + bsp.GetRootNode()->RemoveFlagRecurse( NODE_VISITED ); + FindLedges_r( bsp.GetRootNode(), bsp.GetRootNode() ); + bsp.GetRootNode()->RemoveFlagRecurse( NODE_VISITED ); + + common->Printf( "\r%6d ledges\n", ledgeList.Num() ); + + LedgeSubdiv( bsp.GetRootNode() ); + + common->Printf( "\r%6d subdivisions\n", numLedgeSubdivisions ); +} diff --git a/src/aas/AASBuild_merge.cpp b/src/aas/AASBuild_merge.cpp new file mode 100644 index 0000000..a4002b1 --- /dev/null +++ b/src/aas/AASBuild_merge.cpp @@ -0,0 +1,163 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../idlib/precompiled.h" +#pragma hdrstop + +#include "AASBuild_local.h" + +/* +============ +idAASBuild::AllGapsLeadToOtherNode +============ +*/ +bool idAASBuild::AllGapsLeadToOtherNode( idBrushBSPNode *nodeWithGaps, idBrushBSPNode *otherNode ) { + int s; + idBrushBSPPortal *p; + + for ( p = nodeWithGaps->GetPortals(); p; p = p->Next(s) ) { + s = (p->GetNode(1) == nodeWithGaps); + + if ( !PortalIsGap( p, s ) ) { + continue; + } + + if ( p->GetNode(!s) != otherNode ) { + return false; + } + } + return true; +} + +/* +============ +idAASBuild::MergeWithAdjacentLeafNodes +============ +*/ +bool idAASBuild::MergeWithAdjacentLeafNodes( idBrushBSP &bsp, idBrushBSPNode *node ) { + int s, numMerges = 0, otherNodeFlags; + idBrushBSPPortal *p; + + do { + for ( p = node->GetPortals(); p; p = p->Next(s) ) { + s = (p->GetNode(1) == node); + + // both leaf nodes must have the same contents + if ( node->GetContents() != p->GetNode(!s)->GetContents() ) { + continue; + } + + // cannot merge leaf nodes if one is near a ledge and the other is not + if ( (node->GetFlags() & AREA_LEDGE) != (p->GetNode(!s)->GetFlags() & AREA_LEDGE) ) { + continue; + } + + // cannot merge leaf nodes if one has a floor portal and the other a gap portal + if ( node->GetFlags() & AREA_FLOOR ) { + if ( p->GetNode(!s)->GetFlags() & AREA_GAP ) { + if ( !AllGapsLeadToOtherNode( p->GetNode(!s), node ) ) { + continue; + } + } + } + else if ( node->GetFlags() & AREA_GAP ) { + if ( p->GetNode(!s)->GetFlags() & AREA_FLOOR ) { + if ( !AllGapsLeadToOtherNode( node, p->GetNode(!s) ) ) { + continue; + } + } + } + + otherNodeFlags = p->GetNode(!s)->GetFlags(); + + // try to merge the leaf nodes + if ( bsp.TryMergeLeafNodes( p, s ) ) { + node->SetFlag( otherNodeFlags ); + if ( node->GetFlags() & AREA_FLOOR ) { + node->RemoveFlag( AREA_GAP ); + } + numMerges++; + DisplayRealTimeString( "\r%6d", ++numMergedLeafNodes ); + break; + } + } + } while( p ); + + if ( numMerges ) { + return true; + } + return false; +} + +/* +============ +idAASBuild::MergeLeafNodes_r +============ +*/ +void idAASBuild::MergeLeafNodes_r( idBrushBSP &bsp, idBrushBSPNode *node ) { + + if ( !node ) { + return; + } + + if ( node->GetContents() & AREACONTENTS_SOLID ) { + return; + } + + if ( node->GetFlags() & NODE_DONE ) { + return; + } + + if ( !node->GetChild(0) && !node->GetChild(1) ) { + MergeWithAdjacentLeafNodes( bsp, node ); + node->SetFlag( NODE_DONE ); + return; + } + + MergeLeafNodes_r( bsp, node->GetChild(0) ); + MergeLeafNodes_r( bsp, node->GetChild(1) ); + + return; +} + +/* +============ +idAASBuild::MergeLeafNodes +============ +*/ +void idAASBuild::MergeLeafNodes( idBrushBSP &bsp ) { + numMergedLeafNodes = 0; + + common->Printf( "[Merge Leaf Nodes]\n" ); + + MergeLeafNodes_r( bsp, bsp.GetRootNode() ); + bsp.GetRootNode()->RemoveFlagRecurse( NODE_DONE ); + bsp.PruneMergedTree_r( bsp.GetRootNode() ); + + common->Printf( "\r%6d leaf nodes merged\n", numMergedLeafNodes ); +} diff --git a/src/aas/AASCluster.cpp b/src/aas/AASCluster.cpp new file mode 100644 index 0000000..9aefc6e --- /dev/null +++ b/src/aas/AASCluster.cpp @@ -0,0 +1,556 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../idlib/precompiled.h" +#pragma hdrstop + +#include "AASFile.h" +#include "AASFile_local.h" +#include "AASCluster.h" + + +/* +================ +idAASCluster::UpdatePortal +================ +*/ +bool idAASCluster::UpdatePortal( int areaNum, int clusterNum ) { + int portalNum; + aasPortal_t *portal; + + // find the portal for this area + for ( portalNum = 1; portalNum < file->portals.Num(); portalNum++ ) { + if ( file->portals[portalNum].areaNum == areaNum ) { + break; + } + } + + if ( portalNum >= file->portals.Num() ) { + common->Error( "no portal for area %d", areaNum ); + return true; + } + + portal = &file->portals[portalNum]; + + // if the portal is already fully updated + if ( portal->clusters[0] == clusterNum ) { + return true; + } + if ( portal->clusters[1] == clusterNum ) { + return true; + } + // if the portal has no front cluster yet + if ( !portal->clusters[0] ) { + portal->clusters[0] = clusterNum; + } + // if the portal has no back cluster yet + else if ( !portal->clusters[1] ) + { + portal->clusters[1] = clusterNum; + } + else + { + // remove the cluster portal flag contents + file->areas[areaNum].contents &= ~AREACONTENTS_CLUSTERPORTAL; + return false; + } + + // set the area cluster number to the negative portal number + file->areas[areaNum].cluster = -portalNum; + + // add the portal to the cluster using the portal index + file->AppendPortalIndex( portalNum, clusterNum ); + return true; +} + +/* +================ +idAASCluster::FloodClusterAreas_r +================ +*/ +bool idAASCluster::FloodClusterAreas_r( int areaNum, int clusterNum ) { + aasArea_t *area; + aasFace_t *face; + int faceNum, i; + idReachability *reach; + + area = &file->areas[areaNum]; + + // if the area is already part of a cluster + if ( area->cluster > 0 ) { + if ( area->cluster == clusterNum ) { + return true; + } + // there's a reachability going from one cluster to another only in one direction + common->Error( "cluster %d touched cluster %d at area %d\r\n", clusterNum, file->areas[areaNum].cluster, areaNum ); + return false; + } + + // if this area is a cluster portal + if ( area->contents & AREACONTENTS_CLUSTERPORTAL ) { + return UpdatePortal( areaNum, clusterNum ); + } + + // set the area cluster number + area->cluster = clusterNum; + + if ( !noFaceFlood ) { + // use area faces to flood into adjacent areas + for ( i = 0; i < area->numFaces; i++ ) { + faceNum = abs(file->faceIndex[area->firstFace + i]); + face = &file->faces[faceNum]; + if ( face->areas[0] == areaNum ) { + if ( face->areas[1] ) { + if ( !FloodClusterAreas_r( face->areas[1], clusterNum ) ) { + return false; + } + } + } + else { + if ( face->areas[0] ) { + if ( !FloodClusterAreas_r( face->areas[0], clusterNum ) ) { + return false; + } + } + } + } + } + + // use the reachabilities to flood into other areas + for ( reach = file->areas[areaNum].reach; reach; reach = reach->next ) { + if ( !FloodClusterAreas_r( reach->toAreaNum, clusterNum) ) { + return false; + } + } + + // use the reversed reachabilities to flood into other areas + for ( reach = file->areas[areaNum].rev_reach; reach; reach = reach->rev_next ) { + if ( !FloodClusterAreas_r( reach->fromAreaNum, clusterNum) ) { + return false; + } + } + + return true; +} + +/* +================ +idAASCluster::RemoveAreaClusterNumbers +================ +*/ +void idAASCluster::RemoveAreaClusterNumbers( void ) { + int i; + + for ( i = 1; i < file->areas.Num(); i++ ) { + file->areas[i].cluster = 0; + } +} + +/* +================ +idAASCluster::NumberClusterAreas +================ +*/ +void idAASCluster::NumberClusterAreas( int clusterNum ) { + int i, portalNum; + aasCluster_t *cluster; + aasPortal_t *portal; + + cluster = &file->clusters[clusterNum]; + cluster->numAreas = 0; + cluster->numReachableAreas = 0; + + // number all areas in this cluster WITH reachabilities + for ( i = 1; i < file->areas.Num(); i++ ) { + + if ( file->areas[i].cluster != clusterNum ) { + continue; + } + + if ( !(file->areas[i].flags & (AREA_REACHABLE_WALK|AREA_REACHABLE_FLY)) ) { + continue; + } + + file->areas[i].clusterAreaNum = cluster->numAreas++; + cluster->numReachableAreas++; + } + + // number all portals in this cluster WITH reachabilities + for ( i = 0; i < cluster->numPortals; i++ ) { + portalNum = file->portalIndex[cluster->firstPortal + i]; + portal = &file->portals[portalNum]; + + if ( !(file->areas[portal->areaNum].flags & (AREA_REACHABLE_WALK|AREA_REACHABLE_FLY)) ) { + continue; + } + + if ( portal->clusters[0] == clusterNum ) { + portal->clusterAreaNum[0] = cluster->numAreas++; + } + else { + portal->clusterAreaNum[1] = cluster->numAreas++; + } + cluster->numReachableAreas++; + } + + // number all areas in this cluster WITHOUT reachabilities + for ( i = 1; i < file->areas.Num(); i++ ) { + + if ( file->areas[i].cluster != clusterNum ) { + continue; + } + + if ( file->areas[i].flags & (AREA_REACHABLE_WALK|AREA_REACHABLE_FLY) ) { + continue; + } + + file->areas[i].clusterAreaNum = cluster->numAreas++; + } + + // number all portals in this cluster WITHOUT reachabilities + for ( i = 0; i < cluster->numPortals; i++ ) { + portalNum = file->portalIndex[cluster->firstPortal + i]; + portal = &file->portals[portalNum]; + + if ( file->areas[portal->areaNum].flags & (AREA_REACHABLE_WALK|AREA_REACHABLE_FLY) ) { + continue; + } + + if ( portal->clusters[0] == clusterNum ) { + portal->clusterAreaNum[0] = cluster->numAreas++; + } + else { + portal->clusterAreaNum[1] = cluster->numAreas++; + } + } +} + +/* +================ +idAASCluster::FindClusters +================ +*/ +bool idAASCluster::FindClusters( void ) { + int i, clusterNum; + aasCluster_t cluster; + + RemoveAreaClusterNumbers(); + + for ( i = 1; i < file->areas.Num(); i++ ) { + // if the area is already part of a cluster + if ( file->areas[i].cluster ) { + continue; + } + + // if not flooding through faces only use areas that have reachabilities + if ( noFaceFlood ) { + if ( !(file->areas[i].flags & (AREA_REACHABLE_WALK|AREA_REACHABLE_FLY)) ) { + continue; + } + } + + // if the area is a cluster portal + if ( file->areas[i].contents & AREACONTENTS_CLUSTERPORTAL ) { + continue; + } + + cluster.numAreas = 0; + cluster.numReachableAreas = 0; + cluster.firstPortal = file->portalIndex.Num(); + cluster.numPortals = 0; + clusterNum = file->clusters.Num(); + file->clusters.Append( cluster ); + + // flood the areas in this cluster + if ( !FloodClusterAreas_r( i, clusterNum ) ) { + return false; + } + + // number the cluster areas + NumberClusterAreas( clusterNum ); + } + return true; +} + +/* +================ +idAASCluster::CreatePortals +================ +*/ +void idAASCluster::CreatePortals( void ) { + int i; + aasPortal_t portal; + + for ( i = 1; i < file->areas.Num(); i++ ) { + // if the area is a cluster portal + if ( file->areas[i].contents & AREACONTENTS_CLUSTERPORTAL ) { + portal.areaNum = i; + portal.clusters[0] = portal.clusters[1] = 0; + portal.maxAreaTravelTime = 0; + file->portals.Append( portal ); + } + } +} + +/* +================ +idAASCluster::TestPortals +================ +*/ +bool idAASCluster::TestPortals( void ) { + int i; + aasPortal_t *portal, *portal2; + aasArea_t *area, *area2; + idReachability *reach; + bool ok; + + ok = true; + for ( i = 1; i < file->portals.Num(); i++ ) { + portal = &file->portals[i]; + area = &file->areas[portal->areaNum]; + + // if this portal was already removed + if ( !( area->contents & AREACONTENTS_CLUSTERPORTAL) ) { + continue; + } + + // may not removed this portal if it has a reachability to a removed portal + for ( reach = area->reach; reach; reach = reach->next ) { + area2 = &file->areas[ reach->toAreaNum ]; + if ( area2->contents & AREACONTENTS_CLUSTERPORTAL ) { + continue; + } + if ( area2->cluster < 0 ) { + break; + } + } + if ( reach ) { + continue; + } + + // may not removed this portal if it has a reversed reachability to a removed portal + for ( reach = area->rev_reach; reach; reach = reach->rev_next ) { + area2 = &file->areas[ reach->toAreaNum ]; + if ( area2->contents & AREACONTENTS_CLUSTERPORTAL ) { + continue; + } + if ( area2->cluster < 0 ) { + break; + } + } + if ( reach ) { + continue; + } + + // portal should have two clusters set + if ( !portal->clusters[0] ) { + area->contents &= ~AREACONTENTS_CLUSTERPORTAL; + ok = false; + continue; + } + if ( !portal->clusters[1] ) { + area->contents &= ~AREACONTENTS_CLUSTERPORTAL; + ok = false; + continue; + } + + // this portal may not have reachabilities to a portal that doesn't seperate the same clusters + for ( reach = area->reach; reach; reach = reach->next ) { + area2 = &file->areas[ reach->toAreaNum ]; + + if ( !(area2->contents & AREACONTENTS_CLUSTERPORTAL) ) { + continue; + } + + if ( area2->cluster > 0 ) { + area2->contents &= ~AREACONTENTS_CLUSTERPORTAL; + ok = false; + continue; + } + + portal2 = &file->portals[ -file->areas[ reach->toAreaNum ].cluster ]; + + if ( ( portal2->clusters[0] != portal->clusters[0] && portal2->clusters[0] != portal->clusters[1] ) || + ( portal2->clusters[1] != portal->clusters[0] && portal2->clusters[1] != portal->clusters[1] ) ) { + area2->contents &= ~AREACONTENTS_CLUSTERPORTAL; + ok = false; + continue; + } + } + } + + return ok; +} + +/* +================ +idAASCluster::RemoveInvalidPortals +================ +*/ +void idAASCluster::RemoveInvalidPortals( void ) { + int i, j, k, face1Num, face2Num, otherAreaNum, numOpenAreas, numInvalidPortals; + aasFace_t *face1, *face2; + + numInvalidPortals = 0; + for ( i = 0; i < file->areas.Num(); i++ ) { + if ( !( file->areas[i].contents & AREACONTENTS_CLUSTERPORTAL ) ) { + continue; + } + + numOpenAreas = 0; + for ( j = 0; j < file->areas[i].numFaces; j++ ) { + face1Num = file->faceIndex[ file->areas[i].firstFace + j ]; + face1 = &file->faces[ abs(face1Num) ]; + otherAreaNum = face1->areas[ face1Num < 0 ]; + + if ( !otherAreaNum ) { + continue; + } + + for ( k = 0; k < j; k++ ) { + face2Num = file->faceIndex[ file->areas[i].firstFace + k ]; + face2 = &file->faces[ abs(face2Num) ]; + if ( otherAreaNum == face2->areas[ face2Num < 0 ] ) { + break; + } + } + if ( k < j ) { + continue; + } + + if ( !( file->areas[otherAreaNum].contents & AREACONTENTS_CLUSTERPORTAL ) ) { + numOpenAreas++; + } + } + + if ( numOpenAreas <= 1 ) { + file->areas[i].contents &= AREACONTENTS_CLUSTERPORTAL; + numInvalidPortals++; + } + } + + common->Printf( "\r%6d invalid portals removed\n", numInvalidPortals ); +} + +/* +================ +idAASCluster::Build +================ +*/ +bool idAASCluster::Build( idAASCompilerFile *file ) { + + common->Printf( "[Clustering]\n" ); + + this->file = file; + this->noFaceFlood = true; + + RemoveInvalidPortals(); + + while( 1 ) { + + // delete all existing clusters + file->DeleteClusters(); + + // create the portals from the portal areas + CreatePortals(); + + common->Printf( "\r%6d", file->portals.Num() ); + + // find the clusters + if ( !FindClusters() ) { + continue; + } + + // test the portals + if ( !TestPortals() ) { + continue; + } + + break; + } + + common->Printf( "\r%6d portals\n", file->portals.Num() ); + common->Printf( "%6d clusters\n", file->clusters.Num() ); + + for ( int i = 0; i < file->clusters.Num(); i++ ) { + common->Printf( "%6d reachable areas in cluster %d\n", file->clusters[i].numReachableAreas, i ); + } + + file->ReportRoutingEfficiency(); + + return true; +} + +/* +================ +idAASCluster::BuildSingleCluster +================ +*/ +bool idAASCluster::BuildSingleCluster( idAASCompilerFile *file ) { + int i, numAreas; + aasCluster_t cluster; + + common->Printf( "[Clustering]\n" ); + + this->file = file; + + // delete all existing clusters + file->DeleteClusters(); + + cluster.firstPortal = 0; + cluster.numPortals = 0; + cluster.numAreas = file->areas.Num(); + cluster.numReachableAreas = 0; + // give all reachable areas in the cluster a number + for ( i = 0; i < file->areas.Num(); i++ ) { + file->areas[i].cluster = file->clusters.Num(); + if ( file->areas[i].flags & (AREA_REACHABLE_WALK|AREA_REACHABLE_FLY) ) { + file->areas[i].clusterAreaNum = cluster.numReachableAreas++; + } + } + // give the remaining areas a number within the cluster + numAreas = cluster.numReachableAreas; + for ( i = 0; i < file->areas.Num(); i++ ) { + if ( file->areas[i].flags & (AREA_REACHABLE_WALK|AREA_REACHABLE_FLY) ) { + continue; + } + file->areas[i].clusterAreaNum = numAreas++; + } + file->clusters.Append( cluster ); + + common->Printf( "%6d portals\n", file->portals.Num() ); + common->Printf( "%6d clusters\n", file->clusters.Num() ); + + for ( i = 0; i < file->clusters.Num(); i++ ) { + common->Printf( "%6d reachable areas in cluster %d\n", file->clusters[i].numReachableAreas, i ); + } + + file->ReportRoutingEfficiency(); + + return true; +} diff --git a/src/aas/AASCompilerFile.h b/src/aas/AASCompilerFile.h new file mode 100644 index 0000000..d6cc775 --- /dev/null +++ b/src/aas/AASCompilerFile.h @@ -0,0 +1,147 @@ +/* +=========================================================================== + +Quake 4 Reconstructed GPL Source Code +Copyright (C) 2026 Justin Marshall(IceColdDuke). + +=========================================================================== +*/ + +#ifndef __AASCOMPILERFILE_H__ +#define __AASCOMPILERFILE_H__ + +#include "AASFile.h" + +/* +=============================================================================== + + Compiler-facing adapter for the Quake 4 idAASFile interface. + + The Doom 3 compiler operated directly on idAASFileLocal's idList members. + Quake 4 deliberately exposes the same operations through idAASFile virtuals + so the compiler in Toolsx86.dll does not depend on the engine's private AAS + object layout. These small list views preserve the original compiler code's + shape while routing every access through that public interface. + +=============================================================================== +*/ + +template< class type > +class idAASCompilerList { +public: + typedef int ( idAASFile::*numFunction_t )( void ) const; + typedef type &( idAASFile::*getFunction_t )( int ); + typedef int ( idAASFile::*appendFunction_t )( type & ); + typedef void ( idAASFile::*setNumFunction_t )( int ); + + idAASCompilerList( idAASFile *file, numFunction_t numFunction, + getFunction_t getFunction, appendFunction_t appendFunction = NULL, + setNumFunction_t setNumFunction = NULL ) : + file( file ), numFunction( numFunction ), getFunction( getFunction ), + appendFunction( appendFunction ), setNumFunction( setNumFunction ) { + } + + int Num( void ) const { + return ( file->*numFunction )(); + } + + type &operator[]( int index ) { + return ( file->*getFunction )( index ); + } + + int Append( type &value ) { + assert( appendFunction != NULL ); + return ( file->*appendFunction )( value ); + } + + void SetNum( int count ) { + assert( setNumFunction != NULL ); + ( file->*setNumFunction )( count ); + } + +private: + idAASFile * file; + numFunction_t numFunction; + getFunction_t getFunction; + appendFunction_t appendFunction; + setNumFunction_t setNumFunction; +}; + +class idAASCompilerPlaneList { +public: + explicit idAASCompilerPlaneList( idAASFile *file ) : file( file ) {} + + idPlane &operator[]( int index ) { + return file->GetPlane( index ); + } + + int FindPlane( const idPlane &plane, float normalEpsilon, float distanceEpsilon ) { + return file->FindPlane( plane, normalEpsilon, distanceEpsilon ); + } + +private: + idAASFile *file; +}; + +class idAASCompilerFile { +public: + explicit idAASCompilerFile( idAASFile *file ) : + file( file ), + planeList( file ), + vertices( file, &idAASFile::GetNumVertices, &idAASFile::GetVertex, &idAASFile::AppendVertex ), + edges( file, &idAASFile::GetNumEdges, &idAASFile::GetEdge, &idAASFile::AppendEdge ), + edgeIndex( file, &idAASFile::GetNumEdgeIndexes, &idAASFile::GetEdgeIndex, &idAASFile::AppendEdgeIndex ), + faces( file, &idAASFile::GetNumFaces, &idAASFile::GetFace, &idAASFile::AppendFace ), + faceIndex( file, &idAASFile::GetNumFaceIndexes, &idAASFile::GetFaceIndex, &idAASFile::AppendFaceIndex ), + areas( file, &idAASFile::GetNumAreas, &idAASFile::GetArea, &idAASFile::AppendArea ), + nodes( file, &idAASFile::GetNumNodes, &idAASFile::GetNode, &idAASFile::AppendNode, &idAASFile::SetNumNodes ), + portals( file, &idAASFile::GetNumPortals, &idAASFile::GetPortal, &idAASFile::AppendPortal ), + portalIndex( file, &idAASFile::GetNumPortalIndexes, &idAASFile::GetPortalIndex ), + clusters( file, &idAASFile::GetNumClusters, &idAASFile::GetCluster, &idAASFile::AppendCluster ), + settings( file->GetSettings() ) { + assert( file != NULL ); + } + + ~idAASCompilerFile( void ) { + delete file; + } + + void Clear( void ) { file->Clear(); } + void DeleteClusters( void ) { file->DeleteClusters(); } + void DeleteReachabilities( void ) { file->DeleteReachabilities(); } + idVec3 EdgeCenter( int edgeNum ) const { return file->EdgeCenter( edgeNum ); } + idVec3 FaceCenter( int faceNum ) const { return file->FaceCenter( faceNum ); } + void FinishAreas( void ) { file->FinishAreas(); } + idAASSettings &GetSettings( void ) { return file->GetSettings(); } + void LinkReversedReachability( void ) { file->LinkReversedReachability(); } + bool Load( const idStr &name, unsigned int crc ) { return file->Load( name, crc ); } + void Optimize( void ) { file->Optimize(); } + void ReportRoutingEfficiency( void ) const { file->ReportRoutingEfficiency(); } + void SetSizes( sizeEstimate_t size ) { file->SetSizes( size ); } + bool Trace( aasTrace_t &trace, const idVec3 &start, const idVec3 &end ) const { return file->Trace( trace, start, end ); } + bool Write( const idStr &name, unsigned int crc ) { return file->Write( name, crc ); } + + int AppendPortalIndex( aasIndex_t &portalNum, int clusterNum ) { + return file->AppendPortalIndex( portalNum, clusterNum ); + } + + idAASFile *GetFile( void ) { return file; } + + idAASCompilerPlaneList planeList; + idAASCompilerList< aasVertex_t > vertices; + idAASCompilerList< aasEdge_t > edges; + idAASCompilerList< aasIndex_t > edgeIndex; + idAASCompilerList< aasFace_t > faces; + idAASCompilerList< aasIndex_t > faceIndex; + idAASCompilerList< aasArea_t > areas; + idAASCompilerList< aasNode_t > nodes; + idAASCompilerList< aasPortal_t > portals; + idAASCompilerList< aasIndex_t > portalIndex; + idAASCompilerList< aasCluster_t > clusters; + idAASSettings & settings; + +private: + idAASFile *file; +}; + +#endif /* !__AASCOMPILERFILE_H__ */ diff --git a/src/aas/AASReach.cpp b/src/aas/AASReach.cpp new file mode 100644 index 0000000..b0005cd --- /dev/null +++ b/src/aas/AASReach.cpp @@ -0,0 +1,945 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../idlib/precompiled.h" +#pragma hdrstop + +#include "AASFile.h" +#include "AASFile_local.h" +#include "AASReach.h" + +#define INSIDEUNITS 2.0f +#define INSIDEUNITS_WALKEND 0.5f +#define INSIDEUNITS_WALKSTART 0.1f +#define INSIDEUNITS_SWIMEND 0.5f +#define INSIDEUNITS_FLYEND 0.5f +#define INSIDEUNITS_WATERJUMP 15.0f + + +/* +================ +idAASReach::ReachabilityExists +================ +*/ +bool idAASReach::ReachabilityExists( int fromAreaNum, int toAreaNum ) { + aasArea_t *area; + idReachability *reach; + + area = &file->areas[fromAreaNum]; + for ( reach = area->reach; reach; reach = reach->next ) { + if ( reach->toAreaNum == toAreaNum ) { + return true; + } + } + return false; +} + +/* +================ +idAASReach::CanSwimInArea +================ +*/ +ID_INLINE bool idAASReach::CanSwimInArea( int areaNum ) { + return ( file->areas[areaNum].contents & AREACONTENTS_WATER ) != 0; +} + +/* +================ +idAASReach::AreaHasFloor +================ +*/ +ID_INLINE bool idAASReach::AreaHasFloor( int areaNum ) { + return ( file->areas[areaNum].flags & AREA_FLOOR ) != 0; +} + +/* +================ +idAASReach::AreaIsClusterPortal +================ +*/ +ID_INLINE bool idAASReach::AreaIsClusterPortal( int areaNum ) { + return ( file->areas[areaNum].contents & AREACONTENTS_CLUSTERPORTAL ) != 0; +} + +/* +================ +idAASReach::AddReachabilityToArea +================ +*/ +void idAASReach::AddReachabilityToArea( idReachability *reach, int areaNum ) { + aasArea_t *area; + + area = &file->areas[areaNum]; + reach->next = area->reach; + area->reach = reach; + numReachabilities++; +} + +/* +================ +idAASReach::Reachability_Fly +================ +*/ +void idAASReach::Reachability_Fly( int areaNum ) { + int i, faceNum, otherAreaNum; + aasArea_t *area; + aasFace_t *face; + idReachability_Fly *reach; + + area = &file->areas[areaNum]; + + for ( i = 0; i < area->numFaces; i++ ) { + faceNum = file->faceIndex[area->firstFace + i]; + face = &file->faces[abs(faceNum)]; + + otherAreaNum = face->areas[INTSIGNBITNOTSET(faceNum)]; + + if ( otherAreaNum == 0 ) { + continue; + } + + if ( ReachabilityExists( areaNum, otherAreaNum ) ) { + continue; + } + + // create reachability going through this face + reach = new idReachability_Fly(); + reach->travelType = TFL_FLY; + reach->toAreaNum = otherAreaNum; + reach->fromAreaNum = areaNum; + reach->edgeNum = 0; + reach->travelTime = 1; + reach->start = file->FaceCenter( abs(faceNum) ); + if ( faceNum < 0 ) { + reach->end = reach->start + file->planeList[face->planeNum].Normal() * INSIDEUNITS_FLYEND; + } else { + reach->end = reach->start - file->planeList[face->planeNum].Normal() * INSIDEUNITS_FLYEND; + } + AddReachabilityToArea( reach, areaNum ); + } +} + +/* +================ +idAASReach::Reachability_Swim +================ +*/ +void idAASReach::Reachability_Swim( int areaNum ) { + int i, faceNum, otherAreaNum; + aasArea_t *area; + aasFace_t *face; + idReachability_Swim *reach; + + if ( !CanSwimInArea( areaNum ) ) { + return; + } + + area = &file->areas[areaNum]; + + for ( i = 0; i < area->numFaces; i++ ) { + faceNum = file->faceIndex[area->firstFace + i]; + face = &file->faces[abs(faceNum)]; + + otherAreaNum = face->areas[INTSIGNBITNOTSET(faceNum)]; + + if ( otherAreaNum == 0 ) { + continue; + } + + if ( !CanSwimInArea( otherAreaNum ) ) { + continue; + } + + if ( ReachabilityExists( areaNum, otherAreaNum ) ) { + continue; + } + + // create reachability going through this face + reach = new idReachability_Swim(); + reach->travelType = TFL_SWIM; + reach->toAreaNum = otherAreaNum; + reach->fromAreaNum = areaNum; + reach->edgeNum = 0; + reach->travelTime = 1; + reach->start = file->FaceCenter( abs(faceNum) ); + if ( faceNum < 0 ) { + reach->end = reach->start + file->planeList[face->planeNum].Normal() * INSIDEUNITS_SWIMEND; + } else { + reach->end = reach->start - file->planeList[face->planeNum].Normal() * INSIDEUNITS_SWIMEND; + } + AddReachabilityToArea( reach, areaNum ); + } +} + +/* +================ +idAASReach::Reachability_EqualFloorHeight +================ +*/ +void idAASReach::Reachability_EqualFloorHeight( int areaNum ) { + int i, k, l, m, n, faceNum, face1Num, face2Num, otherAreaNum, edge1Num, edge2Num; + aasArea_t *area, *otherArea; + aasFace_t *face, *face1, *face2; + idReachability_Walk *reach; + + if ( !AreaHasFloor( areaNum ) ) { + return; + } + + area = &file->areas[areaNum]; + + for ( i = 0; i < area->numFaces; i++ ) { + faceNum = file->faceIndex[area->firstFace + i]; + face = &file->faces[abs(faceNum)]; + + otherAreaNum = face->areas[INTSIGNBITNOTSET(faceNum)]; + if ( !AreaHasFloor( otherAreaNum ) ) { + continue; + } + + otherArea = &file->areas[otherAreaNum]; + + for ( k = 0; k < area->numFaces; k++ ) { + face1Num = file->faceIndex[area->firstFace + k]; + face1 = &file->faces[abs(face1Num)]; + + if ( !( face1->flags & FACE_FLOOR ) ) { + continue; + } + for ( l = 0; l < otherArea->numFaces; l++ ) { + face2Num = file->faceIndex[otherArea->firstFace + l]; + face2 = &file->faces[abs(face2Num)]; + + if ( !( face2->flags & FACE_FLOOR ) ) { + continue; + } + + for ( m = 0; m < face1->numEdges; m++ ) { + edge1Num = abs(file->edgeIndex[face1->firstEdge + m]); + for ( n = 0; n < face2->numEdges; n++ ) { + edge2Num = abs(file->edgeIndex[face2->firstEdge + n]); + if ( edge1Num == edge2Num ) { + break; + } + } + if ( n < face2->numEdges ) { + break; + } + } + if ( m < face1->numEdges ) { + break; + } + } + if ( l < otherArea->numFaces ) { + break; + } + } + if ( k < area->numFaces ) { + // create reachability + reach = new idReachability_Walk(); + reach->travelType = TFL_WALK; + reach->toAreaNum = otherAreaNum; + reach->fromAreaNum = areaNum; + reach->edgeNum = abs( edge1Num ); + reach->travelTime = 1; + reach->start = file->EdgeCenter( edge1Num ); + if ( faceNum < 0 ) { + reach->end = reach->start + file->planeList[face->planeNum].Normal() * INSIDEUNITS_WALKEND; + } + else { + reach->end = reach->start - file->planeList[face->planeNum].Normal() * INSIDEUNITS_WALKEND; + } + AddReachabilityToArea( reach, areaNum ); + } + } +} + +/* +================ +idAASReach::Reachability_Step_Barrier_WaterJump_WalkOffLedge +================ +*/ +bool idAASReach::Reachability_Step_Barrier_WaterJump_WalkOffLedge( int area1num, int area2num ) { + int i, j, k, l, edge1Num, edge2Num, areas[10]; + int floor_bestArea1FloorEdgeNum, floor_bestArea2FloorEdgeNum, floor_foundReach; + int water_bestArea1FloorEdgeNum, water_bestArea2FloorEdgeNum, water_foundReach; + int side1, faceSide1, floorFace1Num; + float dist, dist1, dist2, diff, invGravityDot, orthogonalDot; + float x1, x2, x3, x4, y1, y2, y3, y4, tmp, y; + float length, floor_bestLength, water_bestLength, floor_bestDist, water_bestDist; + idVec3 v1, v2, v3, v4, tmpv, p1area1, p1area2, p2area1, p2area2; + idVec3 normal, orthogonal, edgeVec, start, end; + idVec3 floor_bestStart, floor_bestEnd, floor_bestNormal; + idVec3 water_bestStart, water_bestEnd, water_bestNormal; + idVec3 testPoint; + idPlane *plane; + aasArea_t *area1, *area2; + aasFace_t *floorFace1, *floorFace2, *floor_bestFace1, *water_bestFace1; + aasEdge_t *edge1, *edge2; + idReachability_Walk *walkReach; + idReachability_BarrierJump *barrierJumpReach; + idReachability_WaterJump *waterJumpReach; + idReachability_WalkOffLedge *walkOffLedgeReach; + aasTrace_t trace; + + // must be able to walk or swim in the first area + if ( !AreaHasFloor( area1num ) && !CanSwimInArea( area1num ) ) { + return false; + } + + if ( !AreaHasFloor( area2num ) && !CanSwimInArea( area2num ) ) { + return false; + } + + area1 = &file->areas[area1num]; + area2 = &file->areas[area2num]; + + // if the areas are not near anough in the x-y direction + for ( i = 0; i < 2; i++ ) { + if ( area1->bounds[0][i] > area2->bounds[1][i] + 2.0f ) { + return false; + } + if ( area1->bounds[1][i] < area2->bounds[0][i] - 2.0f ) { + return false; + } + } + + floor_foundReach = false; + floor_bestDist = 99999; + floor_bestLength = 0; + floor_bestArea2FloorEdgeNum = 0; + + water_foundReach = false; + water_bestDist = 99999; + water_bestLength = 0; + water_bestArea2FloorEdgeNum = 0; + + for ( i = 0; i < area1->numFaces; i++ ) { + floorFace1Num = file->faceIndex[area1->firstFace + i]; + faceSide1 = floorFace1Num < 0; + floorFace1 = &file->faces[abs(floorFace1Num)]; + + // if this isn't a floor face + if ( !(floorFace1->flags & FACE_FLOOR) ) { + + // if we can swim in the first area + if ( CanSwimInArea( area1num ) ) { + + // face plane must be more or less horizontal + plane = &file->planeList[ floorFace1->planeNum ^ (!faceSide1) ]; + if ( plane->Normal() * file->settings.invGravityDir < file->settings.minFloorCos ) { + continue; + } + } + else { + // if we can't swim in the area it must be a ground face + continue; + } + } + + for ( k = 0; k < floorFace1->numEdges; k++ ) { + edge1Num = file->edgeIndex[floorFace1->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 ( !(floorFace1->flags & FACE_FLOOR) ) { + side1 = (side1 == faceSide1); + } + edge1Num = abs(edge1Num); + edge1 = &file->edges[edge1Num]; + // vertices of the edge + v1 = file->vertices[edge1->vertexNum[!side1]]; + v2 = file->vertices[edge1->vertexNum[side1]]; + // get a vertical plane through the edge + // NOTE: normal is pointing into area 2 because the face edges are stored counter clockwise + edgeVec = v2 - v1; + normal = edgeVec.Cross( file->settings.invGravityDir ); + normal.Normalize(); + dist = normal * v1; + + // check the faces from the second area + for ( j = 0; j < area2->numFaces; j++ ) { + floorFace2 = &file->faces[abs(file->faceIndex[area2->firstFace + j])]; + // must be a ground face + if ( !(floorFace2->flags & FACE_FLOOR) ) { + continue; + } + // check the edges of this ground face + for ( l = 0; l < floorFace2->numEdges; l++ ) { + edge2Num = abs(file->edgeIndex[floorFace2->firstEdge + l]); + edge2 = &file->edges[edge2Num]; + // vertices of the edge + v3 = file->vertices[edge2->vertexNum[0]]; + v4 = file->vertices[edge2->vertexNum[1]]; + // check the distance between the two points and the vertical plane through the edge of area1 + diff = normal * v3 - dist; + if ( diff < -0.2f || diff > 0.2f ) { + continue; + } + diff = normal * v4 - dist; + if ( diff < -0.2f || diff > 0.2f ) { + 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 + orthogonal = file->settings.invGravityDir.Cross( normal ); + invGravityDot = file->settings.invGravityDir * file->settings.invGravityDir; + orthogonalDot = orthogonal * orthogonal; + // projection into the step plane + // NOTE: since gravity is vertical this is just the z coordinate + y1 = v1[2];//(v1 * file->settings.invGravity) / invGravityDot; + y2 = v2[2];//(v2 * file->settings.invGravity) / invGravityDot; + y3 = v3[2];//(v3 * file->settings.invGravity) / invGravityDot; + y4 = v4[2];//(v4 * file->settings.invGravity) / invGravityDot; + + x1 = (v1 * orthogonal) / orthogonalDot; + x2 = (v2 * orthogonal) / orthogonalDot; + x3 = (v3 * orthogonal) / orthogonalDot; + x4 = (v4 * orthogonal) / orthogonalDot; + + if ( x1 > x2 ) { + tmp = x1; x1 = x2; x2 = tmp; + tmp = y1; y1 = y2; y2 = tmp; + tmpv = v1; v1 = v2; v2 = tmpv; + } + if ( x3 > x4 ) { + tmp = x3; x3 = x4; x4 = tmp; + tmp = y3; y3 = y4; y4 = tmp; + tmpv = v3; v3 = v4; v4 = tmpv; + } + // if the two projected edge lines have no overlap + if ( x2 <= x3 || x4 <= x1 ) { + continue; + } + // if the two lines fully overlap + if ( (x1 - 0.5f < x3 && x4 < x2 + 0.5f) && (x3 - 0.5f < x1 && x2 < x4 + 0.5f) ) { + dist1 = y3 - y1; + dist2 = y4 - y2; + p1area1 = v1; + p2area1 = v2; + p1area2 = v3; + p2area2 = v4; + } + else { + // if the points are equal + if ( x1 > x3 - 0.1f && x1 < x3 + 0.1f ) { + dist1 = y3 - y1; + p1area1 = v1; + p1area2 = v3; + } + else if ( x1 < x3 ) { + y = y1 + (x3 - x1) * (y2 - y1) / (x2 - x1); + dist1 = y3 - y; + p1area1 = v3; + p1area1[2] = y; + p1area2 = v3; + } + else { + y = y3 + (x1 - x3) * (y4 - y3) / (x4 - x3); + dist1 = y - y1; + p1area1 = v1; + p1area2 = v1; + p1area2[2] = y; + } + // if the points are equal + if ( x2 > x4 - 0.1f && x2 < x4 + 0.1f ) { + dist2 = y4 - y2; + p2area1 = v2; + p2area2 = v4; + } + else if ( x2 < x4 ) { + y = y3 + (x2 - x3) * (y4 - y3) / (x4 - x3); + dist2 = y - y2; + p2area1 = v2; + p2area2 = v2; + p2area2[2] = y; + } + else { + y = y1 + (x4 - x1) * (y2 - y1) / (x2 - x1); + dist2 = y4 - y; + p2area1 = v4; + p2area1[2] = y; + p2area2 = v4; + } + } + + // if both distances are pretty much equal then we take the middle of the points + if ( dist1 > dist2 - 1.0f && dist1 < dist2 + 1.0f ) { + dist = dist1; + start = ( p1area1 + p2area1 ) * 0.5f; + end = ( p1area2 + p2area2 ) * 0.5f; + } + else if (dist1 < dist2) { + dist = dist1; + start = p1area1; + end = p1area2; + } + else { + dist = dist2; + start = p2area1; + end = p2area2; + } + + // get the length of the overlapping part of the edges of the two areas + length = (p2area2 - p1area2).Length(); + + if ( floorFace1->flags & FACE_FLOOR ) { + // if the vertical distance is smaller + if ( dist < floor_bestDist || + // or the vertical distance is pretty much the same + // but the overlapping part of the edges is longer + (dist < floor_bestDist + 1.0f && length > floor_bestLength) ) { + floor_bestDist = dist; + floor_bestLength = length; + floor_foundReach = true; + floor_bestArea1FloorEdgeNum = edge1Num; + floor_bestArea2FloorEdgeNum = edge2Num; + floor_bestFace1 = floorFace1; + floor_bestStart = start; + floor_bestNormal = normal; + floor_bestEnd = end; + } + } + 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.0f && length > water_bestLength) ) { + water_bestDist = dist; + water_bestLength = length; + water_foundReach = true; + water_bestArea1FloorEdgeNum = edge1Num; + water_bestArea2FloorEdgeNum = edge2Num; + water_bestFace1 = floorFace1; + water_bestStart = start; // best start point in area1 + water_bestNormal = normal; // normal is pointing into area2 + water_bestEnd = end; // best point towards area2 + } + } + } + } + } + } + // + // NOTE: swim reachabilities should already be filtered out + // + // Steps + // + // --------- + // | step height -> TFL_WALK + // --------| + // + // --------- + // ~~~~~~~~| step height and low water -> TFL_WALK + // --------| + // + // ~~~~~~~~~~~~~~~~~~ + // --------- + // | step height and low water up to the step -> TFL_WALK + // --------| + // + // check for a step reachability + if ( floor_foundReach ) { + // if area2 is higher but lower than the maximum step height + // NOTE: floor_bestDist >= 0 also catches equal floor reachabilities + if ( floor_bestDist >= 0 && floor_bestDist < file->settings.maxStepHeight ) { + // create walk reachability from area1 to area2 + walkReach = new idReachability_Walk(); + walkReach->travelType = TFL_WALK; + walkReach->toAreaNum = area2num; + walkReach->fromAreaNum = area1num; + walkReach->start = floor_bestStart + INSIDEUNITS_WALKSTART * floor_bestNormal; + walkReach->end = floor_bestEnd + INSIDEUNITS_WALKEND * floor_bestNormal; + walkReach->edgeNum = abs( floor_bestArea1FloorEdgeNum ); + walkReach->travelTime = 0; + if ( area2->flags & AREA_CROUCH ) { + walkReach->travelTime += file->settings.tt_startCrouching; + } + AddReachabilityToArea( walkReach, area1num ); + return true; + } + } + // + // Water Jumps + // + // --------- + // | + // ~~~~~~~~| + // | + // | higher than step height and water up to waterjump height -> TFL_WATERJUMP + // --------| + // + // ~~~~~~~~~~~~~~~~~~ + // --------- + // | + // | + // | + // | higher than step height and low water up to the step -> TFL_WATERJUMP + // --------| + // + // check for a waterjump reachability + if ( water_foundReach ) { + // get a test point a little bit towards area1 + testPoint = water_bestEnd - INSIDEUNITS * water_bestNormal; + // go down the maximum waterjump height + testPoint[2] -= file->settings.maxWaterJumpHeight; + // if there IS water the sv_maxwaterjump height below the bestend point + if ( area1->flags & AREA_LIQUID ) { + // don't create rediculous water jump reachabilities from areas very far below the water surface + if ( water_bestDist < file->settings.maxWaterJumpHeight + 24 ) { + // water jumping from or towards a crouch only areas is not possible + if ( !(area1->flags & AREA_CROUCH) && !(area2->flags & AREA_CROUCH) ) { + // create water jump reachability from area1 to area2 + waterJumpReach = new idReachability_WaterJump(); + waterJumpReach->travelType = TFL_WATERJUMP; + waterJumpReach->toAreaNum = area2num; + waterJumpReach->fromAreaNum = area1num; + waterJumpReach->start = water_bestStart; + waterJumpReach->end = water_bestEnd + INSIDEUNITS_WATERJUMP * water_bestNormal; + waterJumpReach->edgeNum = abs( floor_bestArea1FloorEdgeNum ); + waterJumpReach->travelTime = file->settings.tt_waterJump; + AddReachabilityToArea( waterJumpReach, area1num ); + return true; + } + } + } + } + // + // Barrier Jumps + // + // --------- + // | + // | + // | + // | higher than max step height lower than max barrier height -> TFL_BARRIERJUMP + // --------| + // + // --------- + // | + // | + // | + // ~~~~~~~~| higher than max step height lower than max barrier height + // --------| and a thin layer of water in the area to jump from -> TFL_BARRIERJUMP + // + // check for a barrier jump reachability + if ( floor_foundReach ) { + //if area2 is higher but lower than the maximum barrier jump height + if ( floor_bestDist > 0 && floor_bestDist < file->settings.maxBarrierHeight ) { + //if no water in area1 or a very thin layer of water on the ground + if ( !water_foundReach || (floor_bestDist - water_bestDist < 16) ) { + // cannot perform a barrier jump towards or from a crouch area + if ( !(area1->flags & AREA_CROUCH) && !(area2->flags & AREA_CROUCH) ) { + // create barrier jump reachability from area1 to area2 + barrierJumpReach = new idReachability_BarrierJump(); + barrierJumpReach->travelType = TFL_BARRIERJUMP; + barrierJumpReach->toAreaNum = area2num; + barrierJumpReach->fromAreaNum = area1num; + barrierJumpReach->start = floor_bestStart + INSIDEUNITS_WALKSTART * floor_bestNormal; + barrierJumpReach->end = floor_bestEnd + INSIDEUNITS_WALKEND * floor_bestNormal; + barrierJumpReach->edgeNum = abs( floor_bestArea1FloorEdgeNum ); + barrierJumpReach->travelTime = file->settings.tt_barrierJump; + AddReachabilityToArea( barrierJumpReach, area1num ); + return true; + } + } + } + } + // + // Walk and Walk Off Ledge + // + // --------| + // | can walk or step back -> TFL_WALK + // --------- + // + // --------| + // | + // | + // | + // | cannot walk/step back -> TFL_WALKOFFLEDGE + // --------- + // + // --------| + // | + // |~~~~~~~~ + // | + // | cannot step back but can waterjump back -> TFL_WALKOFFLEDGE + // --------- FIXME: create TFL_WALK reach?? + // + // check for a walk or walk off ledge reachability + if ( floor_foundReach ) { + if ( floor_bestDist < 0 ) { + if ( floor_bestDist > -file->settings.maxStepHeight ) { + // create walk reachability from area1 to area2 + walkReach = new idReachability_Walk(); + walkReach->travelType = TFL_WALK; + walkReach->toAreaNum = area2num; + walkReach->fromAreaNum = area1num; + walkReach->start = floor_bestStart + INSIDEUNITS_WALKSTART * floor_bestNormal; + walkReach->end = floor_bestEnd + INSIDEUNITS_WALKEND * floor_bestNormal; + walkReach->edgeNum = abs( floor_bestArea1FloorEdgeNum ); + walkReach->travelTime = 1; + AddReachabilityToArea( walkReach, area1num ); + return true; + } + // if no maximum fall height set or less than the max + if ( !file->settings.maxFallHeight || idMath::Fabs(floor_bestDist) < file->settings.maxFallHeight ) { + // trace a bounding box vertically to check for solids + floor_bestEnd += INSIDEUNITS * floor_bestNormal; + start = floor_bestEnd; + start[2] = floor_bestStart[2]; + end = floor_bestEnd; + end[2] += 4; + trace.areas = areas; + trace.maxAreas = sizeof(areas) / sizeof(int); + file->Trace( trace, start, end ); + // if the trace didn't start in solid and nothing was hit + if ( trace.lastAreaNum && trace.fraction >= 1.0f ) { + // the trace end point must be in the goal area + if ( trace.lastAreaNum == area2num ) { + // don't create reachability if going through a cluster portal + for (i = 0; i < trace.numAreas; i++) { + if ( AreaIsClusterPortal( trace.areas[i] ) ) { + break; + } + } + if ( i >= trace.numAreas ) { + // create a walk off ledge reachability from area1 to area2 + walkOffLedgeReach = new idReachability_WalkOffLedge(); + walkOffLedgeReach->travelType = TFL_WALKOFFLEDGE; + walkOffLedgeReach->toAreaNum = area2num; + walkOffLedgeReach->fromAreaNum = area1num; + walkOffLedgeReach->start = floor_bestStart; + walkOffLedgeReach->end = floor_bestEnd; + walkOffLedgeReach->edgeNum = abs( floor_bestArea1FloorEdgeNum ); + walkOffLedgeReach->travelTime = file->settings.tt_startWalkOffLedge + idMath::Fabs(floor_bestDist) * 50 / file->settings.gravityValue; + AddReachabilityToArea( walkOffLedgeReach, area1num ); + return true; + } + } + } + } + } + } + return false; +} + +/* +================ +idAASReach::Reachability_WalkOffLedge +================ +*/ +void idAASReach::Reachability_WalkOffLedge( int areaNum ) { + int i, j, faceNum, edgeNum, side, reachAreaNum, p, areas[10]; + aasArea_t *area; + aasFace_t *face; + aasEdge_t *edge; + idPlane *plane; + idVec3 v1, v2, mid, dir, testEnd; + idReachability_WalkOffLedge *reach; + aasTrace_t trace; + + if ( !AreaHasFloor( areaNum ) || CanSwimInArea( areaNum ) ) { + return; + } + + area = &file->areas[areaNum]; + + for ( i = 0; i < area->numFaces; i++ ) { + faceNum = file->faceIndex[area->firstFace + i]; + face = &file->faces[abs(faceNum)]; + + // face must be a floor face + if ( !(face->flags & FACE_FLOOR) ) { + continue; + } + + for ( j = 0; j < face->numEdges; j++ ) { + + edgeNum = file->edgeIndex[face->firstEdge + j]; + edge = &file->edges[abs(edgeNum)]; + + //if ( !(edge->flags & EDGE_LEDGE) ) { + // continue; + //} + + side = edgeNum < 0; + + v1 = file->vertices[edge->vertexNum[side]]; + v2 = file->vertices[edge->vertexNum[!side]]; + + plane = &file->planeList[face->planeNum ^ INTSIGNBITSET(faceNum) ]; + + // get the direction into the other area + dir = plane->Normal().Cross( v2 - v1 ); + dir.Normalize(); + + mid = ( v1 + v2 ) * 0.5f; + testEnd = mid + INSIDEUNITS_WALKEND * dir; + testEnd[2] -= file->settings.maxFallHeight + 1.0f; + trace.areas = areas; + trace.maxAreas = sizeof(areas) / sizeof(int); + file->Trace( trace, mid, testEnd ); + + reachAreaNum = trace.lastAreaNum; + if ( !reachAreaNum || reachAreaNum == areaNum ) { + continue; + } + if ( idMath::Fabs( mid[2] - trace.endpos[2] ) > file->settings.maxFallHeight ) { + continue; + } + if ( !AreaHasFloor( reachAreaNum ) && !CanSwimInArea( reachAreaNum ) ) { + continue; + } + if ( ReachabilityExists( areaNum, reachAreaNum) ) { + continue; + } + // if not going through a cluster portal + for ( p = 0; p < trace.numAreas; p++ ) { + if ( AreaIsClusterPortal( trace.areas[p] ) ) { + break; + } + } + if ( p < trace.numAreas ) { + continue; + } + + reach = new idReachability_WalkOffLedge(); + reach->travelType = TFL_WALKOFFLEDGE; + reach->toAreaNum = reachAreaNum; + reach->fromAreaNum = areaNum; + reach->start = mid; + reach->end = trace.endpos; + reach->edgeNum = abs( edgeNum ); + reach->travelTime = file->settings.tt_startWalkOffLedge + idMath::Fabs(mid[2] - trace.endpos[2]) * 50 / file->settings.gravityValue; + AddReachabilityToArea( reach, areaNum ); + } + } +} + +/* +================ +idAASReach::FlagReachableAreas +================ +*/ +void idAASReach::FlagReachableAreas( idAASCompilerFile *file ) { + int i, numReachableAreas; + + numReachableAreas = 0; + for ( i = 1; i < file->areas.Num(); i++ ) { + + if ( ( file->areas[i].flags & ( AREA_FLOOR | AREA_LADDER ) ) || + ( file->areas[i].contents & AREACONTENTS_WATER ) ) { + file->areas[i].flags |= AREA_REACHABLE_WALK; + } + if ( file->GetSettings().allowFlyReachabilities ) { + file->areas[i].flags |= AREA_REACHABLE_FLY; + } + numReachableAreas++; + } + + common->Printf( "%6d reachable areas\n", numReachableAreas ); +} + +/* +================ +idAASReach::Build +================ +*/ +bool idAASReach::Build( const idMapFile *mapFile, idAASCompilerFile *file ) { + int i, j, lastPercent, percent; + + this->mapFile = mapFile; + this->file = file; + numReachabilities = 0; + + common->Printf( "[Reachability]\n" ); + + // delete all existing reachabilities + file->DeleteReachabilities(); + + FlagReachableAreas( file ); + + for ( i = 1; i < file->areas.Num(); i++ ) { + if ( !( file->areas[i].flags & AREA_REACHABLE_WALK ) ) { + continue; + } + if ( file->GetSettings().allowSwimReachabilities ) { + Reachability_Swim( i ); + } + Reachability_EqualFloorHeight( i ); + } + + lastPercent = -1; + for ( i = 1; i < file->areas.Num(); i++ ) { + + if ( !( file->areas[i].flags & AREA_REACHABLE_WALK ) ) { + continue; + } + + for ( j = 0; j < file->areas.Num(); j++ ) { + if ( i == j ) { + continue; + } + + if ( !( file->areas[j].flags & AREA_REACHABLE_WALK ) ) { + continue; + } + + if ( ReachabilityExists( i, j ) ) { + continue; + } + if ( Reachability_Step_Barrier_WaterJump_WalkOffLedge( i, j ) ) { + continue; + } + } + + //Reachability_WalkOffLedge( i ); + + percent = 100 * i / file->areas.Num(); + if ( percent > lastPercent ) { + common->Printf( "\r%6d%%", percent ); + lastPercent = percent; + } + } + + if ( file->GetSettings().allowFlyReachabilities ) { + for ( i = 1; i < file->areas.Num(); i++ ) { + Reachability_Fly( i ); + } + } + + file->LinkReversedReachability(); + + common->Printf( "\r%6d reachabilities\n", numReachabilities ); + + return true; +} diff --git a/src/aas/AASSettingsTools.cpp b/src/aas/AASSettingsTools.cpp new file mode 100644 index 0000000..6713f03 --- /dev/null +++ b/src/aas/AASSettingsTools.cpp @@ -0,0 +1,303 @@ +/* +=========================================================================== + +Quake 4 Reconstructed GPL Source Code +Copyright (C) 2026 Justin Marshall(IceColdDuke). + +=========================================================================== +*/ + +/* +=============================================================================== + + Tools-DLL copy of idAASSettings. + + The retail executable and Toolsx86.dll each contain the settings methods + they use. Keep this implementation in the tools target only: it combines + the Quake 4 PDB/decompiler behavior with the tool-only FromDict and + ValidEntity routines evidenced by the retail Toolsx86.dll strings. + +=============================================================================== +*/ + +#include "../idlib/precompiled.h" +#pragma hdrstop + +#include "AASFile.h" + +#if defined( _M_IX86 ) +static_assert( sizeof( idAASSettings ) == 240, "Quake 4 idAASSettings ABI drift" ); +#endif + +idAASSettings::idAASSettings( void ) { + numBoundingBoxes = 1; + boundingBoxes[0] = idBounds( idVec3( -16, -16, 0 ), idVec3( 16, 16, 72 ) ); + usePatches = false; + writeBrushMap = false; + playerFlood = false; + noOptimize = false; + allowSwimReachabilities = false; + allowFlyReachabilities = false; + generateTacticalFeatures = false; + iAASOnly = 0; + fileExtension = "aas48"; + gravity.Set( 0, 0, -1066 ); + gravityDir = gravity; + gravityValue = gravityDir.Normalize(); + invGravityDir = -gravityDir; + maxStepHeight = 14.0f; + maxBarrierHeight = 32.0f; + maxWaterJumpHeight = 20.0f; + maxFallHeight = 64.0f; + minFloorCos = 0.7f; + tt_barrierJump = 100; + tt_startCrouching = 100; + tt_waterJump = 100; + tt_startWalkOffLedge = 100; + debugColor.Set( 1, 0, 0, 1 ); + debugDraw = true; +} + +bool idAASSettings::ParseBool( Lexer &src, bool &value ) { + if ( !src.ExpectTokenString( "=" ) ) { + return false; + } + value = src.ParseBool(); + return true; +} + +bool idAASSettings::ParseInt( Lexer &src, int &value ) { + if ( !src.ExpectTokenString( "=" ) ) { + return false; + } + value = src.ParseInt(); + return true; +} + +bool idAASSettings::ParseFloat( Lexer &src, float &value ) { + if ( !src.ExpectTokenString( "=" ) ) { + return false; + } + value = src.ParseFloat(); + return true; +} + +bool idAASSettings::ParseVector( Lexer &src, idVec3 &value ) { + return src.ExpectTokenString( "=" ) && src.Parse1DMatrix( 3, value.ToFloatPtr() ); +} + +bool idAASSettings::ParseBBoxes( Lexer &src ) { + idToken token; + idBounds bounds; + + numBoundingBoxes = 0; + if ( !src.ExpectTokenString( "{" ) ) { + return false; + } + while ( src.ReadToken( &token ) ) { + if ( token == "}" ) { + return numBoundingBoxes > 0; + } + if ( numBoundingBoxes >= MAX_AAS_BOUNDING_BOXES ) { + src.Error( "more than %d AAS bounding boxes", MAX_AAS_BOUNDING_BOXES ); + return false; + } + src.UnreadToken( &token ); + if ( !src.Parse1DMatrix( 3, bounds[0].ToFloatPtr() ) || + !src.ExpectTokenString( "-" ) || + !src.Parse1DMatrix( 3, bounds[1].ToFloatPtr() ) ) { + return false; + } + boundingBoxes[numBoundingBoxes++] = bounds; + } + return false; +} + +bool idAASSettings::FromParser( Lexer &src ) { + idToken token; + + if ( !src.ExpectTokenString( "{" ) ) { + return false; + } + while ( src.ReadToken( &token ) ) { + if ( token == "}" ) { + break; + } + if ( token == "bboxes" ) { if ( !ParseBBoxes( src ) ) return false; } + else if ( token == "usePatches" ) { if ( !ParseBool( src, usePatches ) ) return false; } + else if ( token == "writeBrushMap" ) { if ( !ParseBool( src, writeBrushMap ) ) return false; } + else if ( token == "playerFlood" ) { if ( !ParseBool( src, playerFlood ) ) return false; } + else if ( token == "allowSwimReachabilities" ) { if ( !ParseBool( src, allowSwimReachabilities ) ) return false; } + else if ( token == "allowFlyReachabilities" ) { if ( !ParseBool( src, allowFlyReachabilities ) ) return false; } + else if ( token == "generateTacticalFeatures" ) { if ( !ParseBool( src, generateTacticalFeatures ) ) return false; } + else if ( token == "fileExtension" ) { + if ( !src.ExpectTokenString( "=" ) || !src.ExpectTokenType( TT_STRING, 0, &token ) ) return false; + fileExtension = token; + } + else if ( token == "gravity" ) { + if ( !ParseVector( src, gravity ) ) return false; + gravityDir = gravity; + gravityValue = gravityDir.Normalize(); + invGravityDir = -gravityDir; + } + else if ( token == "maxStepHeight" ) { if ( !ParseFloat( src, maxStepHeight ) ) return false; } + else if ( token == "maxBarrierHeight" ) { if ( !ParseFloat( src, maxBarrierHeight ) ) return false; } + else if ( token == "maxWaterJumpHeight" ) { if ( !ParseFloat( src, maxWaterJumpHeight ) ) return false; } + else if ( token == "maxFallHeight" ) { if ( !ParseFloat( src, maxFallHeight ) ) return false; } + else if ( token == "minFloorCos" ) { if ( !ParseFloat( src, minFloorCos ) ) return false; } + else if ( token == "tt_barrierJump" ) { if ( !ParseInt( src, tt_barrierJump ) ) return false; } + else if ( token == "tt_startCrouching" ) { if ( !ParseInt( src, tt_startCrouching ) ) return false; } + else if ( token == "tt_waterJump" ) { if ( !ParseInt( src, tt_waterJump ) ) return false; } + else if ( token == "tt_startWalkOffLedge" ) { if ( !ParseInt( src, tt_startWalkOffLedge ) ) return false; } + else { + src.Error( "invalid token '%s'", token.c_str() ); + return false; + } + } + + if ( numBoundingBoxes <= 0 ) { + src.Error( "no valid bounding box" ); + return false; + } + + const idDict *aasDef = gameEdit ? gameEdit->FindEntityDefDict( fileExtension, false ) : NULL; + if ( aasDef ) { + idVec3 color; + if ( aasDef->GetVector( "debugColor", "1.0 0.0 0.0", color ) ) { + debugColor.Set( color.x, color.y, color.z, 1.0f ); + } + aasDef->GetBool( "debugDraw", "1", debugDraw ); + aasDef->GetBool( "generateTacticalFeatures", "0", generateTacticalFeatures ); + } + return true; +} + +bool idAASSettings::FromFile( const idStr &fileName ) { + Lexer src( LEXFL_ALLOWPATHNAMES | LEXFL_NOSTRINGESCAPECHARS | LEXFL_NOSTRINGCONCAT ); + common->Printf( "loading %s\n", fileName.c_str() ); + if ( !src.LoadFile( fileName.c_str() ) ) { + common->Warning( "couldn't load %s", fileName.c_str() ); + return false; + } + if ( !src.ExpectTokenString( "settings" ) ) { + common->Warning( "%s is not a settings file", fileName.c_str() ); + return false; + } + return FromParser( src ); +} + +bool idAASSettings::FromDict( const char *name, const idDict *dict ) { + idBounds bounds; + idVec3 color; + + if ( !dict->GetVector( "mins", "0 0 0", bounds[0] ) ) common->Error( "Missing 'mins' in entityDef '%s'", name ); + if ( !dict->GetVector( "maxs", "0 0 0", bounds[1] ) ) common->Error( "Missing 'maxs' in entityDef '%s'", name ); + numBoundingBoxes = 1; + boundingBoxes[0] = bounds; + + if ( !dict->GetBool( "usePatches", "0", usePatches ) ) common->Error( "Missing 'usePatches' in entityDef '%s'", name ); + if ( !dict->GetBool( "writeBrushMap", "0", writeBrushMap ) ) common->Error( "Missing 'writeBrushMap' in entityDef '%s'", name ); + if ( !dict->GetBool( "playerFlood", "0", playerFlood ) ) common->Error( "Missing 'playerFlood' in entityDef '%s'", name ); + if ( !dict->GetBool( "allowSwimReachabilities", "0", allowSwimReachabilities ) ) common->Error( "Missing 'allowSwimReachabilities' in entityDef '%s'", name ); + if ( !dict->GetBool( "allowFlyReachabilities", "0", allowFlyReachabilities ) ) common->Error( "Missing 'allowFlyReachabilities' in entityDef '%s'", name ); + if ( !dict->GetString( "fileExtension", "", fileExtension ) ) common->Error( "Missing 'fileExtension' in entityDef '%s'", name ); + if ( !dict->GetVector( "gravity", "0 0 -1066", gravity ) ) common->Error( "Missing 'gravity' in entityDef '%s'", name ); + gravityDir = gravity; + gravityValue = gravityDir.Normalize(); + invGravityDir = -gravityDir; + if ( !dict->GetFloat( "maxStepHeight", "0", maxStepHeight ) ) common->Error( "Missing 'maxStepHeight' in entityDef '%s'", name ); + if ( !dict->GetFloat( "maxBarrierHeight", "0", maxBarrierHeight ) ) common->Error( "Missing 'maxBarrierHeight' in entityDef '%s'", name ); + if ( !dict->GetFloat( "maxWaterJumpHeight", "0", maxWaterJumpHeight ) ) common->Error( "Missing 'maxWaterJumpHeight' in entityDef '%s'", name ); + if ( !dict->GetFloat( "maxFallHeight", "0", maxFallHeight ) ) common->Error( "Missing 'maxFallHeight' in entityDef '%s'", name ); + if ( !dict->GetFloat( "minFloorCos", "0", minFloorCos ) ) common->Error( "Missing 'minFloorCos' in entityDef '%s'", name ); + if ( !dict->GetInt( "tt_barrierJump", "0", tt_barrierJump ) ) common->Error( "Missing 'tt_barrierJump' in entityDef '%s'", name ); + if ( !dict->GetInt( "tt_startCrouching", "0", tt_startCrouching ) ) common->Error( "Missing 'tt_startCrouching' in entityDef '%s'", name ); + if ( !dict->GetInt( "tt_waterJump", "0", tt_waterJump ) ) common->Error( "Missing 'tt_waterJump' in entityDef '%s'", name ); + if ( !dict->GetInt( "tt_startWalkOffLedge", "0", tt_startWalkOffLedge ) ) common->Error( "Missing 'tt_startWalkOffLedge' in entityDef '%s'", name ); + if ( dict->GetVector( "debugColor", "1.0 0.0 0.0", color ) ) debugColor.Set( color.x, color.y, color.z, 1.0f ); + dict->GetBool( "debugDraw", "1", debugDraw ); + dict->GetBool( "generateTacticalFeatures", "0", generateTacticalFeatures ); + return true; +} + +bool idAASSettings::WriteToFile( idFile *file ) const { + file->WriteFloatString( "{\n" ); + file->WriteFloatString( "\tbboxes\n\t{\n" ); + for ( int i = 0; i < numBoundingBoxes; i++ ) { + file->WriteFloatString( "\t\t(%f %f %f)-(%f %f %f)\n", + boundingBoxes[i][0].x, boundingBoxes[i][0].y, boundingBoxes[i][0].z, + boundingBoxes[i][1].x, boundingBoxes[i][1].y, boundingBoxes[i][1].z ); + } + file->WriteFloatString( "\t}\n" ); + file->WriteFloatString( "\tusePatches = %d\n", usePatches ); + file->WriteFloatString( "\twriteBrushMap = %d\n", writeBrushMap ); + file->WriteFloatString( "\tplayerFlood = %d\n", playerFlood ); + file->WriteFloatString( "\tallowSwimReachabilities = %d\n", allowSwimReachabilities ); + file->WriteFloatString( "\tallowFlyReachabilities = %d\n", allowFlyReachabilities ); + file->WriteFloatString( "\tgenerateTacticalFeatures = %d\n", generateTacticalFeatures ); + file->WriteFloatString( "\tfileExtension = \"%s\"\n", fileExtension.c_str() ); + file->WriteFloatString( "\tgravity = (%f %f %f)\n", gravity.x, gravity.y, gravity.z ); + file->WriteFloatString( "\tmaxStepHeight = %f\n", maxStepHeight ); + file->WriteFloatString( "\tmaxBarrierHeight = %f\n", maxBarrierHeight ); + file->WriteFloatString( "\tmaxWaterJumpHeight = %f\n", maxWaterJumpHeight ); + file->WriteFloatString( "\tmaxFallHeight = %f\n", maxFallHeight ); + file->WriteFloatString( "\tminFloorCos = %f\n", minFloorCos ); + file->WriteFloatString( "\ttt_barrierJump = %d\n", tt_barrierJump ); + file->WriteFloatString( "\ttt_startCrouching = %d\n", tt_startCrouching ); + file->WriteFloatString( "\ttt_waterJump = %d\n", tt_waterJump ); + file->WriteFloatString( "\ttt_startWalkOffLedge = %d\n", tt_startWalkOffLedge ); + file->WriteFloatString( "}\n" ); + return true; +} + +bool idAASSettings::ValidForBounds( const idBounds &bounds ) const { + for ( int i = 0; i < 3; i++ ) { + if ( bounds[0][i] < boundingBoxes[0][0][i] || bounds[1][i] > boundingBoxes[0][1][i] ) { + return false; + } + } + return true; +} + +bool idAASSettings::ValidEntity( const char *classname, bool *needFlyReachabilities ) const { + if ( needFlyReachabilities ) { + *needFlyReachabilities = false; + } + if ( playerFlood && ( !idStr::Cmp( classname, "info_player_start" ) || + !idStr::Cmp( classname, "info_player_deathmatch" ) || + !idStr::Cmp( classname, "func_teleporter" ) ) ) { + return true; + } + + const idDict *dict = gameEdit ? gameEdit->FindEntityDefDict( classname, false ) : NULL; + idStr useAAS; + if ( !dict || !dict->GetString( "use_aas", NULL, useAAS ) || fileExtension.Icmp( useAAS ) ) { + return false; + } + + idBounds bounds; + idVec3 size; + bounds.Zero(); + if ( dict->GetVector( "mins", NULL, bounds[0] ) ) { + dict->GetVector( "maxs", NULL, bounds[1] ); + } else if ( dict->GetVector( "size", NULL, size ) ) { + bounds[0].Set( size.x * -0.5f, size.y * -0.5f, 0.0f ); + bounds[1].Set( size.x * 0.5f, size.y * 0.5f, size.z ); + } + if ( !ValidForBounds( bounds ) ) { + common->Error( "%s cannot use %s\n", classname, fileExtension.c_str() ); + } + if ( needFlyReachabilities ) { + dict->GetBool( "flying", "0", *needFlyReachabilities ); + } + return true; +} + +float idAASSettings::Radius( float scale ) const { + const idBounds &bounds = boundingBoxes[0]; + float radius = idMath::Fabs( bounds[0].x ); + if ( idMath::Fabs( bounds[1].x ) > radius ) radius = idMath::Fabs( bounds[1].x ); + if ( idMath::Fabs( bounds[0].y ) > radius ) radius = idMath::Fabs( bounds[0].y ); + if ( idMath::Fabs( bounds[1].y ) > radius ) radius = idMath::Fabs( bounds[1].y ); + return radius * scale; +} diff --git a/src/aas/AASTactical.cpp b/src/aas/AASTactical.cpp new file mode 100644 index 0000000..d4b7d8a --- /dev/null +++ b/src/aas/AASTactical.cpp @@ -0,0 +1,412 @@ +/* +=========================================================================== + +Quake 4 Reconstructed GPL Source Code +Copyright (C) 2026 Justin Marshall (IceColdDuke). + +Quake 4 tactical AAS generator reconstructed from the public Quake 4 AAS +format, the retail Toolsx86.dll behavior, and the Doom 3 AAS compiler. + +=========================================================================== +*/ + +#include "../idlib/precompiled.h" +#pragma hdrstop + +#include "AASFile.h" + +namespace { + +const int TACTICAL_CONTENTS = CONTENTS_SOLID | CONTENTS_MONSTERCLIP | CONTENTS_AAS_SOLID; +const float TACTICAL_WALL_EPSILON = 0.5f; +const float TACTICAL_FEATURE_INSET = 1.0f; +const float TACTICAL_LEAN_DISTANCE = 16.0f; + +struct tacticalCandidate_t { + idVec3 origin; + idVec3 normal; + int height; + int flags; + idList areas; +}; + +class idAASTacticalBuilder { +public: + idAASTacticalBuilder( idAASFile *aasFile, idCollisionModel *worldModel ) : + file( aasFile ), world( worldModel ), settings( aasFile->GetSettings() ) { + } + + int Build( const idMapFile &mapFile ) { + file->ClearTactical(); + for ( int i = 0; i < file->GetNumAreas(); i++ ) { + aasArea_t &area = file->GetArea( i ); + area.numFeatures = 0; + area.firstFeature = 0; + } + + AddManualFeatures( mapFile ); + AddAutomaticCoverFeatures(); + CommitFeatures(); + return candidates.Num(); + } + +private: + bool Trace( const idVec3 &start, const idVec3 &end ) const { + trace_t trace; + collisionModelManager->Translation( &trace, start, end, NULL, mat3_identity, + TACTICAL_CONTENTS, world, vec3_origin, mat3_identity ); + return trace.fraction < 1.0f; + } + + bool TraceWall( const idVec3 &origin, const idVec3 &normal, float height ) const { + idVec3 start = origin; + start.z += height; + const float distance = settings.Radius() + 12.0f; + return Trace( start, start + normal * distance ); + } + + int AreaForPoint( const idVec3 &point ) const { + idVec3 raised = point; + raised.z += 1.0f; + int areaNum = file->PointAreaNum( raised ); + if ( areaNum > 0 ) { + return areaNum; + } + const idBounds searchBounds( idVec3( -64.0f, -64.0f, -64.0f ), idVec3( 64.0f, 64.0f, 64.0f ) ); + return file->PointReachableAreaNum( point, searchBounds, AREA_REACHABLE_WALK, 0 ); + } + + bool ValidPackedOrigin( const idVec3 &origin ) const { + for ( int i = 0; i < 3; i++ ) { + if ( origin[i] < -32768.0f || origin[i] > 32767.0f ) { + common->Warning( "[AAST] feature at (%g %g %g) is outside the Quake 4 packed coordinate range", + origin.x, origin.y, origin.z ); + return false; + } + } + return true; + } + + int AddCandidate( const idVec3 &inOrigin, const idVec3 &inNormal, int height, int flags, int areaNum ) { + if ( flags == 0 || !ValidPackedOrigin( inOrigin ) ) { + return -1; + } + + idVec3 normal = inNormal; + normal.z = 0.0f; + if ( normal.Normalize() == 0.0f ) { + normal.Set( 1.0f, 0.0f, 0.0f ); + } + height = idMath::ClampInt( 0, 255, height ); + + for ( int i = 0; i < candidates.Num(); i++ ) { + tacticalCandidate_t &candidate = candidates[i]; + if ( ( candidate.origin - inOrigin ).LengthSqr() > Square( 3.0f ) || candidate.normal * normal < 0.98f ) { + continue; + } + candidate.flags |= flags; + candidate.height = Max( candidate.height, height ); + if ( areaNum > 0 ) { + candidate.areas.AddUnique( areaNum ); + } + return i; + } + + tacticalCandidate_t candidate; + candidate.origin = inOrigin; + candidate.normal = normal; + candidate.height = height; + candidate.flags = flags; + if ( areaNum > 0 ) { + candidate.areas.Append( areaNum ); + } + return candidates.Append( candidate ); + } + + void AddManualFeatures( const idMapFile &mapFile ) { + for ( int i = 0; i < mapFile.GetNumEntities(); i++ ) { + const idDict &dict = mapFile.GetEntity( i )->epairs; + if ( idStr::Icmp( dict.GetString( "classname" ), "info_tactical_feature" ) != 0 ) { + continue; + } + + const idVec3 origin = dict.GetVector( "origin", "0 0 0" ); + const float angle = dict.GetFloat( "angle", "0" ); + const idVec3 normal = idAngles( 0.0f, angle, 0.0f ).ToForward(); + int height = dict.GetInt( "height", "72" ); + int flags = dict.GetInt( "flags", "0" ); + + if ( dict.GetBool( "vantage", "0" ) ) { + flags |= FEATURE_VANTAGE; + } + if ( dict.GetBool( "pinch", "0" ) ) { + flags |= FEATURE_PINCH; + } + if ( dict.GetBool( "cover", "0" ) ) { + flags |= FEATURE_COVER; + } + if ( dict.GetBool( "lean_left", "0" ) ) { + flags |= FEATURE_COVER | FEATURE_LOOK_LEFT; + } + if ( dict.GetBool( "corner_left", "0" ) ) { + flags |= FEATURE_COVER | FEATURE_LOOK_LEFT | FEATURE_CORNER_LEFT; + } + if ( dict.GetBool( "lean_right", "0" ) ) { + flags |= FEATURE_COVER | FEATURE_LOOK_RIGHT; + } + if ( dict.GetBool( "corner_right", "0" ) ) { + flags |= FEATURE_COVER | FEATURE_LOOK_RIGHT | FEATURE_CORNER_RIGHT; + } + if ( height >= 20 && height < 64 ) { + flags |= FEATURE_COVER | FEATURE_LOOK_OVER; + } + + const int areaNum = AreaForPoint( origin ); + if ( areaNum == 0 ) { + common->Warning( "[AAST] info_tactical_feature at (%g %g %g) is not in an AAS area", + origin.x, origin.y, origin.z ); + continue; + } + AddCandidate( origin, normal, height, flags, areaNum ); + } + } + + bool FindSolidWallPlane( int areaNum, const idVec3 &start, const idVec3 &end, idPlane &wallPlane ) const { + const aasArea_t &area = file->GetArea( areaNum ); + for ( int i = 0; i < area.numFaces; i++ ) { + const int signedFaceNum = file->GetFaceIndex( area.firstFace + i ); + const aasFace_t &face = file->GetFace( abs( signedFaceNum ) ); + if ( !( face.flags & FACE_SOLID ) ) { + continue; + } + const int orientedPlaneNum = face.planeNum ^ ( signedFaceNum < 0 ? 1 : 0 ); + const idPlane &plane = file->GetPlane( orientedPlaneNum ); + if ( idMath::Fabs( plane.Normal().z ) > 0.25f ) { + continue; + } + if ( idMath::Fabs( plane.Distance( start ) ) <= TACTICAL_WALL_EPSILON && + idMath::Fabs( plane.Distance( end ) ) <= TACTICAL_WALL_EPSILON ) { + wallPlane = plane; + return true; + } + } + return false; + } + + void TestCoverPoint( int areaNum, const idVec3 &point, const idPlane &wallPlane ) { + idVec3 wallNormal = -wallPlane.Normal(); + wallNormal.z = 0.0f; + if ( wallNormal.Normalize() == 0.0f ) { + return; + } + + idVec3 origin = point + wallPlane.Normal() * TACTICAL_FEATURE_INSET; + int coverHeight = 0; + for ( int height = 20; height <= 72; height += 4 ) { + if ( TraceWall( origin, wallNormal, static_cast( height ) ) ) { + coverHeight = height; + } + } + if ( coverHeight < 20 ) { + return; + } + + int flags = FEATURE_COVER; + if ( coverHeight < 68 ) { + flags |= FEATURE_LOOK_OVER; + AddCandidate( origin, wallNormal, coverHeight, flags, areaNum ); + return; + } + + coverHeight = 72; + const idVec3 up( 0.0f, 0.0f, 1.0f ); + const idVec3 right = wallNormal.Cross( up ); + const float lookHeight = static_cast( coverHeight ) - TACTICAL_LEAN_DISTANCE; + idVec3 lookOrigin = origin; + lookOrigin.z += lookHeight; + + const idVec3 leftLook = lookOrigin - right * TACTICAL_LEAN_DISTANCE; + if ( !Trace( lookOrigin, leftLook ) && !TraceWall( origin - right * TACTICAL_LEAN_DISTANCE, wallNormal, lookHeight ) ) { + flags |= FEATURE_LOOK_LEFT | FEATURE_CORNER_LEFT; + } + + const idVec3 rightLook = lookOrigin + right * TACTICAL_LEAN_DISTANCE; + if ( !Trace( lookOrigin, rightLook ) && !TraceWall( origin + right * TACTICAL_LEAN_DISTANCE, wallNormal, lookHeight ) ) { + flags |= FEATURE_LOOK_RIGHT | FEATURE_CORNER_RIGHT; + } + + if ( flags & ( FEATURE_LOOK_LEFT | FEATURE_LOOK_RIGHT ) ) { + AddCandidate( origin, wallNormal, coverHeight, flags, areaNum ); + } + } + + void AddAutomaticCoverFeatures() { + for ( int areaNum = 1; areaNum < file->GetNumAreas(); areaNum++ ) { + const aasArea_t &area = file->GetArea( areaNum ); + if ( !( area.flags & AREA_FLOOR ) || ( area.flags & AREA_LIQUID ) ) { + continue; + } + + for ( int faceOffset = 0; faceOffset < area.numFaces; faceOffset++ ) { + const int signedFaceNum = file->GetFaceIndex( area.firstFace + faceOffset ); + const aasFace_t &floorFace = file->GetFace( abs( signedFaceNum ) ); + if ( !( floorFace.flags & FACE_FLOOR ) ) { + continue; + } + + for ( int edgeOffset = 0; edgeOffset < floorFace.numEdges; edgeOffset++ ) { + const int signedEdgeNum = file->GetEdgeIndex( floorFace.firstEdge + edgeOffset ); + const aasEdge_t &edge = file->GetEdge( abs( signedEdgeNum ) ); + const idVec3 start = file->GetVertex( edge.vertexNum[0] ); + const idVec3 end = file->GetVertex( edge.vertexNum[1] ); + + idPlane wallPlane; + if ( !FindSolidWallPlane( areaNum, start, end, wallPlane ) ) { + continue; + } + + idVec3 edgeDirection = end - start; + edgeDirection.z = 0.0f; + const float edgeLength = edgeDirection.Normalize(); + if ( edgeLength < 4.0f ) { + continue; + } + + const float inset = Min( 8.0f, edgeLength * 0.25f ); + TestCoverPoint( areaNum, start + edgeDirection * inset, wallPlane ); + TestCoverPoint( areaNum, end - edgeDirection * inset, wallPlane ); + + const int segments = Max( 1, idMath::Ftoi( edgeLength / 96.0f ) ); + for ( int sample = 1; sample < segments; sample++ ) { + TestCoverPoint( areaNum, start + edgeDirection * ( edgeLength * sample / segments ), wallPlane ); + } + } + } + } + } + + static unsigned char PackNormal( float value ) { + const int packed = idMath::Ftoi( ( value + 1.0f ) * 127.0f + 0.5f ); + return static_cast( idMath::ClampInt( 0, 254, packed ) ); + } + + void CommitFeatures() { + idList featureNumbers; + featureNumbers.SetNum( candidates.Num() ); + for ( int i = 0; i < candidates.Num(); i++ ) { + const tacticalCandidate_t &candidate = candidates[i]; + aasFeature_t feature; + memset( &feature, 0, sizeof( feature ) ); + feature.x = static_cast( idMath::Ftoi( candidate.origin.x ) ); + feature.y = static_cast( idMath::Ftoi( candidate.origin.y ) ); + feature.z = static_cast( idMath::Ftoi( candidate.origin.z ) ); + feature.flags = static_cast( candidate.flags ); + feature.normalx = PackNormal( candidate.normal.x ); + feature.normaly = PackNormal( candidate.normal.y ); + feature.height = static_cast( candidate.height ); + featureNumbers[i] = file->AppendFeature( feature ); + } + + for ( int areaNum = 0; areaNum < file->GetNumAreas(); areaNum++ ) { + aasArea_t &area = file->GetArea( areaNum ); + area.firstFeature = static_cast( file->GetNumFeatureIndexes() ); + for ( int candidateNum = 0; candidateNum < candidates.Num(); candidateNum++ ) { + if ( candidates[candidateNum].areas.FindIndex( areaNum ) < 0 ) { + continue; + } + aasIndex_t featureNum = featureNumbers[candidateNum]; + file->AppendFeatureIndex( featureNum ); + area.numFeatures++; + } + } + } + + idAASFile *file; + idCollisionModel *world; + idAASSettings &settings; + idList candidates; +}; + +static idStr TacticalMapName( const char *argument ) { + idStr mapName = argument; + mapName.BackSlashesToSlashes(); + mapName.StripFileExtension(); + if ( mapName.Icmpn( "maps/", 5 ) != 0 ) { + mapName = "maps/" + mapName; + } + return mapName; +} + +} + +void RunAASTactical_f( const idCmdArgs &args ) { + if ( args.Argc() != 2 ) { + common->Printf( "runAASTactical \n" ); + return; + } + + common->ClearWarnings( "generating tactical AAS features" ); + common->SetRefreshOnPrint( true ); + common->Printf( "[AAST] Generating Tactical Features...\n" ); + + const idStr mapName = TacticalMapName( args.Argv( 1 ) ); + idStr mapFileName = mapName; + mapFileName.SetFileExtension( "map" ); + idMapFile mapFile; + if ( !mapFile.Parse( mapFileName ) ) { + common->Warning( "[AAST] couldn't load map file '%s'", mapFileName.c_str() ); + common->SetRefreshOnPrint( false ); + common->PrintWarnings(); + return; + } + + cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" ); + collisionModelManager->LoadMap( &mapFile, false ); + idCollisionModel *world = collisionModelManager->LoadModel( mapFile.GetName(), WORLD_MODEL_NAME ); + if ( world == NULL ) { + common->Warning( "[AAST] couldn't load collision world for '%s'", mapName.c_str() ); + collisionModelManager->FreeMap( mapFile.GetName() ); + common->SetRefreshOnPrint( false ); + common->PrintWarnings(); + return; + } + + int totalFeatures = 0; + const idDict *types = gameEdit->FindEntityDefDict( "aas_types", false ); + if ( types == NULL ) { + common->Warning( "[AAST] unable to find entityDef 'aas_types'" ); + } else { + const idKeyValue *type = types->MatchPrefix( "type" ); + while ( type != NULL ) { + const idDict *settingsDict = gameEdit->FindEntityDefDict( type->GetValue(), false ); + if ( settingsDict != NULL ) { + idAASSettings settings; + settings.FromDict( type->GetValue(), settingsDict ); + if ( settings.generateTacticalFeatures ) { + idStr aasName = mapName; + aasName.SetFileExtension( settings.fileExtension ); + idAASFile *file = AASFile->CreateNew(); + if ( file->Load( aasName, mapFile.GetGeometryCRC() ) ) { + file->SetSettings( settings ); + idAASTacticalBuilder builder( file, world ); + const int count = builder.Build( mapFile ); + if ( file->Write( aasName, mapFile.GetGeometryCRC() ) ) { + common->Printf( "[AAST] %d Features Generated for %s\n", count, settings.fileExtension.c_str() ); + totalFeatures += count; + } + } else { + common->Warning( "[AAST] couldn't load '%s'; run runAAS first", aasName.c_str() ); + } + delete file; + } + } + type = types->MatchPrefix( "type", type ); + } + } + + collisionModelManager->FreeMap( mapFile.GetName() ); + common->Printf( "[AAST] %d Features Generated\n", totalFeatures ); + common->SetRefreshOnPrint( false ); + common->PrintWarnings(); +} + diff --git a/src/aas/Brush.cpp b/src/aas/Brush.cpp new file mode 100644 index 0000000..57b8fbc --- /dev/null +++ b/src/aas/Brush.cpp @@ -0,0 +1,1582 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../idlib/precompiled.h" +#pragma hdrstop + +#include "Brush.h" + +#define BRUSH_EPSILON 0.1f +#define BRUSH_PLANE_NORMAL_EPSILON 0.00001f +#define BRUSH_PLANE_DIST_EPSILON 0.01f + +#define OUTPUT_UPDATE_TIME 500 // update every 500 msec + +//#define OUTPUT_CHOP_STATS + +/* +============ +DisplayRealTimeString +============ +*/ +void DisplayRealTimeString( char *string, ... ) { + va_list argPtr; + char buf[MAX_STRING_CHARS]; + static int lastUpdateTime; + int time; + + time = Sys_Milliseconds(); + if ( time > lastUpdateTime + OUTPUT_UPDATE_TIME ) { + va_start( argPtr, string ); + vsprintf( buf, string, argPtr ); + va_end( argPtr ); + common->Printf( buf ); + lastUpdateTime = time; + } +} + + +//=============================================================== +// +// idBrushSide +// +//=============================================================== + +/* +============ +idBrushSide::idBrushSide +============ +*/ +idBrushSide::idBrushSide( void ) { + flags = 0; + planeNum = -1; + winding = NULL; +} + +/* +============ +idBrushSide::idBrushSide +============ +*/ +idBrushSide::idBrushSide( const idPlane &plane, int planeNum ) { + this->flags = 0; + this->plane = plane; + this->planeNum = planeNum; + this->winding = NULL; +} + +/* +============ +idBrushSide::~idBrushSide +============ +*/ +idBrushSide::~idBrushSide( void ) { + if ( winding ) { + delete winding; + } +} + +/* +============ +idBrushSide::Copy +============ +*/ +idBrushSide *idBrushSide::Copy( void ) const { + idBrushSide *side; + + side = new idBrushSide( plane, planeNum ); + side->flags = flags; + if ( winding ) { + side->winding = winding->Copy(); + } + else { + side->winding = NULL; + } + return side; +} + +/* +============ +idBrushSide::Split +============ +*/ +int idBrushSide::Split( const idPlane &splitPlane, idBrushSide **front, idBrushSide **back ) const { + idWinding *frontWinding, *backWinding; + + assert( winding ); + + *front = *back = NULL; + + winding->Split( splitPlane, 0.0f, &frontWinding, &backWinding ); + + if ( frontWinding ) { + (*front) = new idBrushSide( plane, planeNum ); + (*front)->winding = frontWinding; + (*front)->flags = flags; + } + + if ( backWinding ) { + (*back) = new idBrushSide( plane, planeNum ); + (*back)->winding = backWinding; + (*back)->flags = flags; + } + + if ( frontWinding && backWinding ) { + return PLANESIDE_CROSS; + } + else if ( frontWinding ) { + return PLANESIDE_FRONT; + } + else { + return PLANESIDE_BACK; + } +} + + +//=============================================================== +// +// idBrushSide +// +//=============================================================== + +/* +============ +idBrush::idBrush +============ +*/ +idBrush::idBrush( void ) { + contents = flags = 0; + bounds.Clear(); + sides.Clear(); + windingsValid = false; +} + + +/* +============ +idBrush::~idBrush +============ +*/ +idBrush::~idBrush( void ) { + for ( int i = 0; i < sides.Num(); i++ ) { + delete sides[i]; + } +} + +/* +============ +idBrush::RemoveSidesWithoutWinding +============ +*/ +bool idBrush::RemoveSidesWithoutWinding( void ) { + int i; + + for ( i = 0; i < sides.Num(); i++ ) { + + if ( sides[i]->winding ) { + continue; + } + + sides.RemoveIndex( i ); + i--; + } + + return ( sides.Num() >= 4 ); +} + +/* +============ +idBrush::CreateWindings +============ +*/ +bool idBrush::CreateWindings( void ) { + int i, j; + idBrushSide *side; + + bounds.Clear(); + for ( i = 0; i < sides.Num(); i++ ) { + side = sides[i]; + + if ( side->winding ) { + delete side->winding; + } + + side->winding = new idWinding( side->plane.Normal(), side->plane.Dist() ); + + for ( j = 0; j < sides.Num() && side->winding; j++ ) { + if ( i == j ) { + continue; + } + // keep the winding if on the clip plane + side->winding = side->winding->Clip( -sides[j]->plane, BRUSH_EPSILON, true ); + } + + if ( side->winding ) { + for ( j = 0; j < side->winding->GetNumPoints(); j++ ) { + bounds.AddPoint( (*side->winding)[j].ToVec3() ); + } + } + } + + if ( bounds[0][0] > bounds[1][0] ) { + return false; + } + for ( i = 0; i < 3; i++ ) { + if ( bounds[0][i] < MIN_WORLD_COORD || bounds[1][i] > MAX_WORLD_COORD ) { + return false; + } + } + + windingsValid = true; + + return true; +} + +/* +============ +idBrush::BoundBrush +============ +*/ +void idBrush::BoundBrush( const idBrush *original ) { + int i, j; + idBrushSide *side; + idWinding *w; + + assert( windingsValid ); + + bounds.Clear(); + for ( i = 0; i < sides.Num(); i++ ) { + side = sides[i]; + + w = side->winding; + + if ( !w ) { + continue; + } + + for ( j = 0; j < w->GetNumPoints(); j++ ) { + bounds.AddPoint( (*w)[j].ToVec3() ); + } + } + + if ( bounds[0][0] > bounds[1][0] ) { + if ( original ) { + idBrushMap *bm = new idBrushMap( "error_brush", "_original" ); + bm->WriteBrush( original ); + delete bm; + } + common->Error( "idBrush::BoundBrush: brush %d on entity %d without windings", primitiveNum, entityNum ); + } + + for ( i = 0; i < 3; i++ ) { + if ( bounds[0][i] < MIN_WORLD_COORD || bounds[1][i] > MAX_WORLD_COORD ) { + if ( original ) { + idBrushMap *bm = new idBrushMap( "error_brush", "_original" ); + bm->WriteBrush( original ); + delete bm; + } + common->Error( "idBrush::BoundBrush: brush %d on entity %d is unbounded", primitiveNum, entityNum ); + } + } +} + +/* +============ +idBrush::FromSides +============ +*/ +bool idBrush::FromSides( idList &sideList ) { + int i; + + for ( i = 0; i < sideList.Num(); i++ ) { + sides.Append( sideList[i] ); + } + + sideList.Clear(); + + return CreateWindings(); +} + +/* +============ +idBrush::FromWinding +============ +*/ +bool idBrush::FromWinding( const idWinding &w, const idPlane &windingPlane ) { + int i, j, bestAxis; + idPlane plane; + idVec3 normal, axialNormal; + + sides.Append( new idBrushSide( windingPlane, -1 ) ); + sides.Append( new idBrushSide( -windingPlane, -1 ) ); + + bestAxis = 0; + for ( i = 1; i < 3; i++ ) { + if ( idMath::Fabs( windingPlane.Normal()[i] ) > idMath::Fabs( windingPlane.Normal()[bestAxis] ) ) { + bestAxis = i; + } + } + axialNormal = vec3_origin; + if ( windingPlane.Normal()[bestAxis] > 0.0f ) { + axialNormal[bestAxis] = 1.0f; + } + else { + axialNormal[bestAxis] = -1.0f; + } + + for ( i = 0; i < w.GetNumPoints(); i++ ) { + j = (i+1) % w.GetNumPoints(); + normal = ( w[j].ToVec3() - w[i].ToVec3() ).Cross( axialNormal ); + if ( normal.Normalize() < 0.5f ) { + continue; + } + plane.SetNormal( normal ); + plane.FitThroughPoint( w[j].ToVec3() ); + sides.Append( new idBrushSide( plane, -1 ) ); + } + + if ( sides.Num() < 4 ) { + for ( i = 0; i < sides.Num(); i++ ) { + delete sides[i]; + } + sides.Clear(); + return false; + } + + sides[0]->winding = w.Copy(); + windingsValid = true; + BoundBrush(); + + return true; +} + +/* +============ +idBrush::FromBounds +============ +*/ +bool idBrush::FromBounds( const idBounds &bounds ) { + int axis, dir; + idVec3 normal; + idPlane plane; + + for ( axis = 0; axis < 3; axis++ ) { + for ( dir = -1; dir <= 1; dir += 2 ) { + normal = vec3_origin; + normal[axis] = dir; + plane.SetNormal( normal ); + plane.SetDist( dir * bounds[(dir == 1)][axis] ); + sides.Append( new idBrushSide( plane, -1 ) ); + } + } + + return CreateWindings(); +} + +/* +============ +idBrush::Transform +============ +*/ +void idBrush::Transform( const idVec3 &origin, const idMat3 &axis ) { + int i; + bool transformed = false; + + if ( axis.IsRotated() ) { + for ( i = 0; i < sides.Num(); i++ ) { + sides[i]->plane.RotateSelf( vec3_origin, axis ); + } + transformed = true; + } + if ( origin != vec3_origin ) { + for ( i = 0; i < sides.Num(); i++ ) { + sides[i]->plane.TranslateSelf( origin ); + } + transformed = true; + } + if ( transformed ) { + CreateWindings(); + } +} + +/* +============ +idBrush::GetVolume +============ +*/ +float idBrush::GetVolume( void ) const { + int i; + idWinding *w; + idVec3 corner; + float d, area, volume; + + // grab the first valid point as a corner + w = NULL; + for ( i = 0; i < sides.Num(); i++ ) { + w = sides[i]->winding; + if ( w ) { + break; + } + } + if ( !w ) { + return 0.0f; + } + corner = (*w)[0].ToVec3(); + + // create tetrahedrons to all other sides + volume = 0.0f; + for ( ; i < sides.Num(); i++) { + w = sides[i]->winding; + if ( !w ) { + continue; + } + d = -( corner * sides[i]->plane.Normal() - sides[i]->plane.Dist() ); + area = w->GetArea(); + volume += d * area; + } + + return ( volume * ( 1.0f / 3.0f ) ); +} + +/* +============ +idBrush::Subtract +============ +*/ +bool idBrush::Subtract( const idBrush *b, idBrushList &list ) const { + int i; + idBrush *front, *back; + const idBrush *in; + + list.Clear(); + in = this; + for ( i = 0; i < b->sides.Num() && in; i++ ) { + + in->Split( b->sides[i]->plane, b->sides[i]->planeNum, &front, &back ); + + if ( in != this ) { + delete in; + } + if ( front ) { + list.AddToTail( front ); + } + in = back; + } + // if didn't really intersect + if ( !in ) { + list.Free(); + return false; + } + + delete in; + return true; +} + +/* +============ +idBrush::TryMerge +============ +*/ +bool idBrush::TryMerge( const idBrush *brush, const idPlaneSet &planeList ) { + int i, j, k, l, m, seperatingPlane; + const idBrush *brushes[2]; + const idWinding *w; + const idPlane *plane; + + // brush bounds should overlap + for ( i = 0; i < 3; i++ ) { + if ( bounds[0][i] > brush->bounds[1][i] + 0.1f ) { + return false; + } + if ( bounds[1][i] < brush->bounds[0][i] - 0.1f ) { + return false; + } + } + + // the brushes should share an opposite plane + seperatingPlane = -1; + for ( i = 0; i < GetNumSides(); i++ ) { + for ( j = 0; j < brush->GetNumSides(); j++ ) { + if ( GetSide(i)->GetPlaneNum() == (brush->GetSide(j)->GetPlaneNum() ^ 1) ) { + // may only have one seperating plane + if ( seperatingPlane != -1 ) { + return false; + } + seperatingPlane = GetSide(i)->GetPlaneNum(); + break; + } + } + } + if ( seperatingPlane == -1 ) { + return false; + } + + brushes[0] = this; + brushes[1] = brush; + + for ( i = 0; i < 2; i++ ) { + + j = !i; + + for ( k = 0; k < brushes[i]->GetNumSides(); k++ ) { + + // if the brush side plane is the seprating plane + if ( !( ( brushes[i]->GetSide(k)->GetPlaneNum() ^ seperatingPlane ) >> 1 ) ) { + continue; + } + + plane = &brushes[i]->GetSide(k)->GetPlane(); + + // all the non seperating brush sides of the other brush should be at the back or on the plane + for ( l = 0; l < brushes[j]->GetNumSides(); l++ ) { + + w = brushes[j]->GetSide(l)->GetWinding(); + if ( !w ) { + continue; + } + + if ( !( ( brushes[j]->GetSide(l)->GetPlaneNum() ^ seperatingPlane ) >> 1 ) ) { + continue; + } + + for ( m = 0; m < w->GetNumPoints(); m++ ) { + if ( plane->Distance( (*w)[m].ToVec3() ) > 0.1f ) { + return false; + } + } + } + } + } + + // add any sides from the other brush to this brush + for ( i = 0; i < brush->GetNumSides(); i++ ) { + for ( j = 0; j < GetNumSides(); j++ ) { + if ( !( ( brush->GetSide(i)->GetPlaneNum() ^ GetSide(j)->GetPlaneNum() ) >> 1 ) ) { + break; + } + } + if ( j < GetNumSides() ) { + sides[j]->flags &= brush->GetSide(i)->GetFlags(); + continue; + } + sides.Append( brush->GetSide(i)->Copy() ); + } + + // remove any side from this brush that is the opposite of a side of the other brush + for ( i = 0; i < GetNumSides(); i++ ) { + for ( j = 0; j < brush->GetNumSides(); j++ ) { + if ( GetSide(i)->GetPlaneNum() == ( brush->GetSide(j)->GetPlaneNum() ^ 1 ) ) { + break; + } + } + if ( j < brush->GetNumSides() ) { + delete sides[i]; + sides.RemoveIndex(i); + i--; + continue; + } + } + + contents |= brush->contents; + + CreateWindings(); + BoundBrush(); + + return true; +} + +/* +============ +idBrush::Split +============ +*/ +int idBrush::Split( const idPlane &plane, int planeNum, idBrush **front, idBrush **back ) const { + int res, i, j; + idBrushSide *side, *frontSide, *backSide; + float dist, maxBack, maxFront, *maxBackWinding, *maxFrontWinding; + idWinding *w, *mid; + + assert( windingsValid ); + + if ( front ) { + *front = NULL; + } + if ( back ) { + *back = NULL; + } + + res = bounds.PlaneSide( plane, -BRUSH_EPSILON ); + if ( res == PLANESIDE_FRONT ) { + if ( front ) { + *front = Copy(); + } + return res; + } + if ( res == PLANESIDE_BACK ) { + if ( back ) { + *back = Copy(); + } + return res; + } + + maxBackWinding = (float *) _alloca16( sides.Num() * sizeof(float) ); + maxFrontWinding = (float *) _alloca16( sides.Num() * sizeof(float) ); + + maxFront = maxBack = 0.0f; + for ( i = 0; i < sides.Num(); i++ ) { + side = sides[i]; + + w = side->winding; + + if ( !w ) { + continue; + } + + maxBackWinding[i] = 10.0f; + maxFrontWinding[i] = -10.0f; + + for ( j = 0; j < w->GetNumPoints(); j++ ) { + + dist = plane.Distance( (*w)[j].ToVec3() ); + if ( dist > maxFrontWinding[i] ) { + maxFrontWinding[i] = dist; + } + if ( dist < maxBackWinding[i] ) { + maxBackWinding[i] = dist; + } + } + + if ( maxFrontWinding[i] > maxFront ) { + maxFront = maxFrontWinding[i]; + } + if ( maxBackWinding[i] < maxBack ) { + maxBack = maxBackWinding[i]; + } + } + + if ( maxFront < BRUSH_EPSILON ) { + if ( back ) { + *back = Copy(); + } + return PLANESIDE_BACK; + } + + if ( maxBack > -BRUSH_EPSILON ) { + if ( front ) { + *front = Copy(); + } + return PLANESIDE_FRONT; + } + + mid = new idWinding( plane.Normal(), plane.Dist() ); + + for ( i = 0; i < sides.Num() && mid; i++ ) { + mid = mid->Clip( -sides[i]->plane, BRUSH_EPSILON, false ); + } + + if ( mid ) { + if ( mid->IsTiny() ) { + delete mid; + mid = NULL; + } + else if ( mid->IsHuge() ) { + // if the winding is huge then the brush is unbounded + common->Warning( "brush %d on entity %d is unbounded" + "( %1.2f %1.2f %1.2f )-( %1.2f %1.2f %1.2f )-( %1.2f %1.2f %1.2f )", primitiveNum, entityNum, + bounds[0][0], bounds[0][1], bounds[0][2], bounds[1][0], bounds[1][1], bounds[1][2], + bounds[1][0]-bounds[0][0], bounds[1][1]-bounds[0][1], bounds[1][2]-bounds[0][2] ); + delete mid; + mid = NULL; + } + } + + if ( !mid ) { + if ( maxFront > - maxBack ) { + if ( front ) { + *front = Copy(); + } + return PLANESIDE_FRONT; + } + else { + if ( back ) { + *back = Copy(); + } + return PLANESIDE_BACK; + } + } + + if ( !front && !back ) { + delete mid; + return PLANESIDE_CROSS; + } + + *front = new idBrush(); + (*front)->SetContents( contents ); + (*front)->SetEntityNum( entityNum ); + (*front)->SetPrimitiveNum( primitiveNum ); + *back = new idBrush(); + (*back)->SetContents( contents ); + (*back)->SetEntityNum( entityNum ); + (*back)->SetPrimitiveNum( primitiveNum ); + + for ( i = 0; i < sides.Num(); i++ ) { + side = sides[i]; + + if ( !side->winding ) { + continue; + } + + // if completely at the front + if ( maxBackWinding[i] >= BRUSH_EPSILON ) { + (*front)->sides.Append( side->Copy() ); + } + // if completely at the back + else if ( maxFrontWinding[i] <= -BRUSH_EPSILON ) { + (*back)->sides.Append( side->Copy() ); + } + else { + // split the side + side->Split( plane, &frontSide, &backSide ); + if ( frontSide ) { + (*front)->sides.Append( frontSide ); + } + else if ( maxFrontWinding[i] > -BRUSH_EPSILON ) { + // favor an overconstrained brush + side = side->Copy(); + side->winding = side->winding->Clip( idPlane( plane.Normal(), (plane.Dist() - (BRUSH_EPSILON+0.02f)) ), 0.01f, true ); + assert( side->winding ); + (*front)->sides.Append( side ); + } + if ( backSide ) { + (*back)->sides.Append( backSide ); + } + else if ( maxBackWinding[i] < BRUSH_EPSILON ) { + // favor an overconstrained brush + side = side->Copy(); + side->winding = side->winding->Clip( idPlane( -plane.Normal(), -(plane.Dist() + (BRUSH_EPSILON+0.02f)) ), 0.01f, true ); + assert( side->winding ); + (*back)->sides.Append( side ); + } + } + } + + side = new idBrushSide( -plane, planeNum^1 ); + side->winding = mid->Reverse(); + side->flags |= SFL_SPLIT; + (*front)->sides.Append( side ); + (*front)->windingsValid = true; + (*front)->BoundBrush( this ); + + side = new idBrushSide( plane, planeNum ); + side->winding = mid; + side->flags |= SFL_SPLIT; + (*back)->sides.Append( side ); + (*back)->windingsValid = true; + (*back)->BoundBrush( this ); + + return PLANESIDE_CROSS; +} + +/* +============ +idBrush::AddBevelsForAxialBox +============ +*/ +#define BRUSH_BEVEL_EPSILON 0.1f + +void idBrush::AddBevelsForAxialBox( void ) { + int axis, dir, i, j, k, l, order; + idBrushSide *side, *newSide; + idPlane plane; + idVec3 normal, vec; + idWinding *w, *w2; + float d, minBack; + + assert( windingsValid ); + + // add the axial planes + order = 0; + for ( axis = 0; axis < 3; axis++ ) { + + for ( dir = -1; dir <= 1; dir += 2, order++ ) { + + // see if the plane is already present + for ( i = 0; i < sides.Num(); i++ ) { + if ( dir > 0 ) { + if ( sides[i]->plane.Normal()[axis] >= 0.9999f ) { + break; + } + } + else { + if ( sides[i]->plane.Normal()[axis] <= -0.9999f ) { + break; + } + } + } + + if ( i >= sides.Num() ) { + normal = vec3_origin; + normal[axis] = dir; + plane.SetNormal( normal ); + plane.SetDist( dir * bounds[(dir == 1)][axis] ); + newSide = new idBrushSide( plane, -1 ); + newSide->SetFlag( SFL_BEVEL ); + sides.Append( newSide ); + } + } + } + + // if the brush is pure axial we're done + if ( sides.Num() == 6 ) { + return; + } + + // test the non-axial plane edges + for ( i = 0; i < sides.Num(); i++ ) { + side = sides[i]; + w = side->winding; + if ( !w ) { + continue; + } + + for ( j = 0; j < w->GetNumPoints(); j++) { + k = (j+1) % w->GetNumPoints(); + vec = (*w)[j].ToVec3() - (*w)[k].ToVec3(); + if ( vec.Normalize() < 0.5f ) { + continue; + } + for ( k = 0; k < 3; k++ ) { + if ( vec[k] == 1.0f || vec[k] == -1.0f || (vec[k] == 0.0f && vec[(k+1)%3] == 0.0f) ) { + 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 + normal = vec3_origin; + normal[axis] = dir; + normal = vec.Cross( normal ); + if ( normal.Normalize() < 0.5f ) { + continue; + } + plane.SetNormal( normal ); + plane.FitThroughPoint( (*w)[j].ToVec3() ); + + // if all the points on all the sides are + // behind this plane, it is a proper edge bevel + for ( k = 0; k < sides.Num(); k++ ) { + + // if this plane has allready been used, skip it + if ( plane.Compare( sides[k]->plane, 0.001f, 0.1f ) ) { + break; + } + + w2 = sides[k]->winding; + if ( !w2 ) { + continue; + } + minBack = 0.0f; + for ( l = 0; l < w2->GetNumPoints(); l++ ) { + d = plane.Distance( (*w2)[l].ToVec3() ); + if ( d > BRUSH_BEVEL_EPSILON ) { + break; // point at the front + } + if ( d < minBack ) { + minBack = d; + } + } + // if some point was at the front + if ( l < w2->GetNumPoints() ) { + break; + } + // if no points at the back then the winding is on the bevel plane + if ( minBack > -BRUSH_BEVEL_EPSILON ) { + break; + } + } + + if ( k < sides.Num() ) { + continue; // wasn't part of the outer hull + } + + // add this plane + newSide = new idBrushSide( plane, -1 ); + newSide->SetFlag( SFL_BEVEL ); + sides.Append( newSide ); + } + } + } + } +} + +/* +============ +idBrush::ExpandForAxialBox +============ +*/ +void idBrush::ExpandForAxialBox( const idBounds &bounds ) { + int i, j; + idBrushSide *side; + idVec3 v; + + AddBevelsForAxialBox(); + + for ( i = 0; i < sides.Num(); i++ ) { + side = sides[i]; + + for ( j = 0; j < 3; j++ ) { + if ( side->plane.Normal()[j] > 0.0f ) { + v[j] = bounds[0][j]; + } + else { + v[j] = bounds[1][j]; + } + } + + side->plane.SetDist( side->plane.Dist() + v * -side->plane.Normal() ); + } + + if ( !CreateWindings() ) { + common->Error( "idBrush::ExpandForAxialBox: brush %d on entity %d imploded", primitiveNum, entityNum ); + } + + /* + // after expansion at least all non bevel sides should have a winding + for ( i = 0; i < sides.Num(); i++ ) { + side = sides[i]; + if ( !side->winding ) { + if ( !( side->flags & SFL_BEVEL ) ) { + int shit = 1; + } + } + } + */ +} + +/* +============ +idBrush::Copy +============ +*/ +idBrush *idBrush::Copy( void ) const { + int i; + idBrush *b; + + b = new idBrush(); + b->entityNum = entityNum; + b->primitiveNum = primitiveNum; + b->contents = contents; + b->windingsValid = windingsValid; + b->bounds = bounds; + for ( i = 0; i < sides.Num(); i++ ) { + b->sides.Append( sides[i]->Copy() ); + } + return b; +} + + +//=============================================================== +// +// idBrushList +// +//=============================================================== + +/* +============ +idBrushList::idBrushList +============ +*/ +idBrushList::idBrushList( void ) { + numBrushes = numBrushSides = 0; + head = tail = NULL; +} + +/* +============ +idBrushList::~idBrushList +============ +*/ +idBrushList::~idBrushList( void ) { +} + +/* +============ +idBrushList::GetBounds +============ +*/ +idBounds idBrushList::GetBounds( void ) const { + idBounds bounds; + idBrush *b; + + bounds.Clear(); + for ( b = Head(); b; b = b->Next() ) { + bounds += b->GetBounds(); + } + return bounds; +} + +/* +============ +idBrushList::AddToTail +============ +*/ +void idBrushList::AddToTail( idBrush *brush ) { + brush->next = NULL; + if ( tail ) { + tail->next = brush; + } + tail = brush; + if ( !head ) { + head = brush; + } + numBrushes++; + numBrushSides += brush->sides.Num(); +} + +/* +============ +idBrushList::AddToTail +============ +*/ +void idBrushList::AddToTail( idBrushList &list ) { + idBrush *brush, *next; + + for ( brush = list.head; brush; brush = next ) { + next = brush->next; + brush->next = NULL; + if ( tail ) { + tail->next = brush; + } + tail = brush; + if ( !head ) { + head = brush; + } + numBrushes++; + numBrushSides += brush->sides.Num(); + } + list.head = list.tail = NULL; + list.numBrushes = 0; +} + +/* +============ +idBrushList::AddToFront +============ +*/ +void idBrushList::AddToFront( idBrush *brush ) { + brush->next = head; + head = brush; + if ( !tail ) { + tail = brush; + } + numBrushes++; + numBrushSides += brush->sides.Num(); +} + +/* +============ +idBrushList::AddToFront +============ +*/ +void idBrushList::AddToFront( idBrushList &list ) { + idBrush *brush, *next; + + for ( brush = list.head; brush; brush = next ) { + next = brush->next; + brush->next = head; + head = brush; + if ( !tail ) { + tail = brush; + } + numBrushes++; + numBrushSides += brush->sides.Num(); + } + list.head = list.tail = NULL; + list.numBrushes = 0; +} + +/* +============ +idBrushList::Remove +============ +*/ +void idBrushList::Remove( idBrush *brush ) { + idBrush *b, *last; + + last = NULL; + for ( b = head; b; b = b->next ) { + if ( b == brush ) { + if ( last ) { + last->next = b->next; + } + else { + head = b->next; + } + if ( b == tail ) { + tail = last; + } + numBrushes--; + numBrushSides -= brush->sides.Num(); + return; + } + last = b; + } +} + +/* +============ +idBrushList::Delete +============ +*/ +void idBrushList::Delete( idBrush *brush ) { + idBrush *b, *last; + + last = NULL; + for ( b = head; b; b = b->next ) { + if ( b == brush ) { + if ( last ) { + last->next = b->next; + } + else { + head = b->next; + } + if ( b == tail ) { + tail = last; + } + numBrushes--; + numBrushSides -= b->sides.Num(); + delete b; + return; + } + last = b; + } +} + +/* +============ +idBrushList::Copy +============ +*/ +idBrushList *idBrushList::Copy( void ) const { + idBrush *brush; + idBrushList *list; + + list = new idBrushList; + + for ( brush = head; brush; brush = brush->next ) { + list->AddToTail( brush->Copy() ); + } + return list; +} + +/* +============ +idBrushList::Free +============ +*/ +void idBrushList::Free( void ) { + idBrush *brush, *next; + + for ( brush = head; brush; brush = next ) { + next = brush->next; + delete brush; + } + head = tail = NULL; + numBrushes = numBrushSides = 0; +} + +/* +============ +idBrushList::Split +============ +*/ +void idBrushList::Split( const idPlane &plane, int planeNum, idBrushList &frontList, idBrushList &backList, bool useBrushSavedPlaneSide ) { + idBrush *b, *front, *back; + + frontList.Clear(); + backList.Clear(); + + if ( !useBrushSavedPlaneSide ) { + for ( b = head; b; b = b->next ) { + b->Split( plane, planeNum, &front, &back ); + if ( front ) { + frontList.AddToTail( front ); + } + if ( back ) { + backList.AddToTail( back ); + } + } + return; + } + + for ( b = head; b; b = b->next ) { + if ( b->savedPlaneSide & BRUSH_PLANESIDE_BOTH ) { + b->Split( plane, planeNum, &front, &back ); + if ( front ) { + frontList.AddToTail( front ); + } + if ( back ) { + backList.AddToTail( back ); + } + } + else if ( b->savedPlaneSide & BRUSH_PLANESIDE_FRONT ) { + frontList.AddToTail( b->Copy() ); + } + else { + backList.AddToTail( b->Copy() ); + } + } +} + +/* +============ +idBrushList::Chop +============ +*/ +void idBrushList::Chop( bool (*ChopAllowed)( idBrush *b1, idBrush *b2 ) ) { + idBrush *b1, *b2, *next; + idBrushList sub1, sub2, keep; + int i, j, c1, c2; + idPlaneSet planeList; + +#ifdef OUTPUT_CHOP_STATS + common->Printf( "[Brush CSG]\n"); + common->Printf( "%6d original brushes\n", this->Num() ); +#endif + + CreatePlaneList( planeList ); + + for ( b1 = this->Head(); b1; b1 = this->Head() ) { + + for ( b2 = b1->next; b2; b2 = next ) { + + next = b2->next; + + for ( i = 0; i < 3; i++ ) { + if ( b1->bounds[0][i] >= b2->bounds[1][i] ) { + break; + } + if ( b1->bounds[1][i] <= b2->bounds[0][i] ) { + break; + } + } + if ( i < 3 ) { + continue; + } + + for ( i = 0; i < b1->GetNumSides(); i++ ) { + for ( j = 0; j < b2->GetNumSides(); j++ ) { + if ( b1->GetSide(i)->GetPlaneNum() == ( b2->GetSide(j)->GetPlaneNum() ^ 1 ) ) { + // opposite planes, so not touching + break; + } + } + if ( j < b2->GetNumSides() ) { + break; + } + } + if ( i < b1->GetNumSides() ) { + continue; + } + + sub1.Clear(); + sub2.Clear(); + + c1 = 999999; + c2 = 999999; + + // if b2 may chop up b1 + if ( !ChopAllowed || ChopAllowed( b2, b1 ) ) { + if ( !b1->Subtract( b2, sub1 ) ) { + // didn't really intersect + continue; + } + if ( sub1.IsEmpty() ) { + // b1 is swallowed by b2 + this->Delete( b1 ); + break; + } + c1 = sub1.Num(); + } + + // if b1 may chop up b2 + if ( !ChopAllowed || ChopAllowed( b1, b2 ) ) { + if ( !b2->Subtract( b1, sub2 ) ) { + // didn't really intersect + continue; + } + if ( sub2.IsEmpty() ) { + // b2 is swallowed by b1 + sub1.Free(); + this->Delete( b2 ); + continue; + } + c2 = sub2.Num(); + } + + if ( sub1.IsEmpty() && sub2.IsEmpty() ) { + continue; + } + + // don't allow too much fragmentation + if ( c1 > 2 && c2 > 2 ) { + sub1.Free(); + sub2.Free(); + continue; + } + + if ( c1 < c2 ) { + sub2.Free(); + this->AddToTail( sub1 ); + this->Delete( b1 ); + break; + } + else { + sub1.Free(); + this->AddToTail( sub2 ); + this->Delete( b2 ); + continue; + } + } + + if ( !b2 ) { + // b1 is no longer intersecting anything, so keep it + this->Remove( b1 ); + keep.AddToTail( b1 ); +#ifdef OUTPUT_CHOP_STATS + DisplayRealTimeString( "\r%6d", keep.numBrushes ); +#endif + } + } + + *this = keep; + +#ifdef OUTPUT_CHOP_STATS + common->Printf( "\r%6d output brushes\n", Num() ); +#endif +} + + +/* +============ +idBrushList::Merge +============ +*/ +void idBrushList::Merge( bool (*MergeAllowed)( idBrush *b1, idBrush *b2 ) ) { + idPlaneSet planeList; + idBrush *b1, *b2, *nextb2; + int numMerges; + + common->Printf( "[Brush Merge]\n"); + common->Printf( "%6d original brushes\n", Num() ); + + CreatePlaneList( planeList ); + + numMerges = 0; + for ( b1 = Head(); b1; b1 = b1->next ) { + + for ( b2 = Head(); b2; b2 = nextb2 ) { + nextb2 = b2->Next(); + + if ( b2 == b1 ) { + continue; + } + + if ( MergeAllowed && !MergeAllowed( b1, b2 ) ) { + continue; + } + + if ( b1->TryMerge( b2, planeList ) ) { + Delete( b2 ); + DisplayRealTimeString( "\r%6d", ++numMerges ); + nextb2 = Head(); + } + } + } + + common->Printf( "\r%6d brushes merged\n", numMerges ); +} + +/* +============ +idBrushList::SetFlagOnFacingBrushSides +============ +*/ +void idBrushList::SetFlagOnFacingBrushSides( const idPlane &plane, int flag ) { + int i; + idBrush *b; + const idWinding *w; + + for ( b = head; b; b = b->next ) { + if ( idMath::Fabs( b->GetBounds().PlaneDistance( plane ) ) > 0.1f ) { + continue; + } + for ( i = 0; i < b->GetNumSides(); i++ ) { + w = b->GetSide(i)->GetWinding(); + if ( !w ) { + if ( b->GetSide(i)->GetPlane().Compare( plane, BRUSH_PLANE_NORMAL_EPSILON, BRUSH_PLANE_DIST_EPSILON ) ) { + b->GetSide(i)->SetFlag( flag ); + } + continue; + } + if ( w->PlaneSide( plane ) == SIDE_ON ) { + b->GetSide(i)->SetFlag( flag ); + } + } + } +} + +/* +============ +idBrushList::CreatePlaneList +============ +*/ +void idBrushList::CreatePlaneList( idPlaneSet &planeList ) const { + int i; + idBrush *b; + idBrushSide *side; + + planeList.Resize( 512, 128 ); + for ( b = Head(); b; b = b->Next() ) { + for ( i = 0; i < b->GetNumSides(); i++ ) { + side = b->GetSide( i ); + side->SetPlaneNum( planeList.FindPlane( side->GetPlane(), BRUSH_PLANE_NORMAL_EPSILON, BRUSH_PLANE_DIST_EPSILON ) ); + } + } +} + +/* +============ +idBrushList::CreatePlaneList +============ +*/ +void idBrushList::WriteBrushMap( const idStr &fileName, const idStr &ext ) const { + idBrushMap *map; + + map = new idBrushMap( fileName, ext ); + map->WriteBrushList( *this ); + delete map; +} + + +//=============================================================== +// +// idBrushMap +// +//=============================================================== + +/* +============ +idBrushMap::idBrushMap +============ +*/ +idBrushMap::idBrushMap( const idStr &fileName, const idStr &ext ) { + idStr qpath; + + qpath = fileName; + qpath.StripFileExtension(); + qpath += ext; + qpath.SetFileExtension( "map" ); + + common->Printf( "writing %s...\n", qpath.c_str() ); + + fp = fileSystem->OpenFileWrite( qpath, "fs_devpath" ); + if ( !fp ) { + common->Error( "Couldn't open %s\n", qpath.c_str() ); + return; + } + + texture = "textures/washroom/btile01"; + + fp->WriteFloatString( "Version %1.2f\n", (float) CURRENT_MAP_VERSION ); + fp->WriteFloatString( "{\n" ); + fp->WriteFloatString( "\"classname\" \"worldspawn\"\n" ); + + brushCount = 0; +} + +/* +============ +idBrushMap::~idBrushMap +============ +*/ +idBrushMap::~idBrushMap( void ) { + if ( !fp ) { + return; + } + fp->WriteFloatString( "}\n" ); + fileSystem->CloseFile( fp ); +} + +/* +============ +idBrushMap::WriteBrush +============ +*/ +void idBrushMap::WriteBrush( const idBrush *brush ) { + int i; + idBrushSide *side; + + if ( !fp ) { + return; + } + + fp->WriteFloatString( "// primitive %d\n{\nbrushDef3\n{\n", brushCount++ ); + + for ( i = 0; i < brush->GetNumSides(); i++ ) { + side = brush->GetSide( i ); + fp->WriteFloatString( " ( %f %f %f %f ) ", side->GetPlane()[0], side->GetPlane()[1], side->GetPlane()[2], -side->GetPlane().Dist() ); + fp->WriteFloatString( "( ( 0.031250 0 0 ) ( 0 0.031250 0 ) ) %s 0 0 0\n", texture.c_str() ); + + } + fp->WriteFloatString( "}\n}\n" ); +} + +/* +============ +idBrushMap::WriteBrushList +============ +*/ +void idBrushMap::WriteBrushList( const idBrushList &brushList ) { + idBrush *b; + + if ( !fp ) { + return; + } + + for ( b = brushList.Head(); b; b = b->Next() ) { + WriteBrush( b ); + } +} diff --git a/src/aas/BrushBSP.cpp b/src/aas/BrushBSP.cpp new file mode 100644 index 0000000..cbc4fee --- /dev/null +++ b/src/aas/BrushBSP.cpp @@ -0,0 +1,2151 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../idlib/precompiled.h" +#pragma hdrstop + +#include "Brush.h" +#include "BrushBSP.h" + + +#define BSP_GRID_SIZE 512.0f +#define SPLITTER_EPSILON 0.1f +#define VERTEX_MELT_EPSILON 0.1f +#define VERTEX_MELT_HASH_SIZE 32 + +#define PORTAL_PLANE_NORMAL_EPSILON 0.00001f +#define PORTAL_PLANE_DIST_EPSILON 0.01f + +//#define OUPUT_BSP_STATS_PER_GRID_CELL + + +//=============================================================== +// +// idBrushBSPPortal +// +//=============================================================== + +/* +============ +idBrushBSPPortal::idBrushBSPPortal +============ +*/ +idBrushBSPPortal::idBrushBSPPortal( void ) { + planeNum = -1; + winding = NULL; + nodes[0] = nodes[1] = NULL; + next[0] = next[1] = NULL; + faceNum = 0; + flags = 0; +} + +/* +============ +idBrushBSPPortal::~idBrushBSPPortal +============ +*/ +idBrushBSPPortal::~idBrushBSPPortal( void ) { + if ( winding ) { + delete winding; + } +} + +/* +============ +idBrushBSPPortal::AddToNodes +============ +*/ +void idBrushBSPPortal::AddToNodes( idBrushBSPNode *front, idBrushBSPNode *back ) { + if ( nodes[0] || nodes[1] ) { + common->Error( "AddToNode: allready included" ); + } + + assert( front && back ); + + nodes[0] = front; + next[0] = front->portals; + front->portals = this; + + nodes[1] = back; + next[1] = back->portals; + back->portals = this; +} + +/* +============ +idBrushBSPPortal::RemoveFromNode +============ +*/ +void idBrushBSPPortal::RemoveFromNode( idBrushBSPNode *l ) { + idBrushBSPPortal **pp, *t; + + // remove reference to the current portal + pp = &l->portals; + while (1) + { + t = *pp; + if ( !t ) { + common->Error( "idBrushBSPPortal::RemoveFromNode: portal not in node" ); + } + + if ( t == this ) { + break; + } + + if ( t->nodes[0] == l ) { + pp = &t->next[0]; + } + else if ( t->nodes[1] == l ) { + pp = &t->next[1]; + } + else { + common->Error( "idBrushBSPPortal::RemoveFromNode: portal not bounding node" ); + } + } + + if ( nodes[0] == l ) { + *pp = next[0]; + nodes[0] = NULL; + } + else if ( nodes[1] == l ) { + *pp = next[1]; + nodes[1] = NULL; + } + else { + common->Error( "idBrushBSPPortal::RemoveFromNode: mislinked portal" ); + } +} + +/* +============ +idBrushBSPPortal::Flip +============ +*/ +void idBrushBSPPortal::Flip( void ) { + idBrushBSPNode *frontNode, *backNode; + + frontNode = nodes[0]; + backNode = nodes[1]; + + if ( frontNode ) { + RemoveFromNode( frontNode ); + } + if ( backNode ) { + RemoveFromNode( backNode ); + } + AddToNodes( frontNode, backNode ); + + plane = -plane; + planeNum ^= 1; + winding->ReverseSelf(); +} + +/* +============ +idBrushBSPPortal::Split +============ +*/ +int idBrushBSPPortal::Split( const idPlane &splitPlane, idBrushBSPPortal **front, idBrushBSPPortal **back ) { + idWinding *frontWinding, *backWinding; + + (*front) = (*back) = NULL; + winding->Split( splitPlane, 0.1f, &frontWinding, &backWinding ); + if ( frontWinding ) { + (*front) = new idBrushBSPPortal(); + (*front)->plane = plane; + (*front)->planeNum = planeNum; + (*front)->flags = flags; + (*front)->winding = frontWinding; + } + if ( backWinding ) { + (*back) = new idBrushBSPPortal(); + (*back)->plane = plane; + (*back)->planeNum = planeNum; + (*back)->flags = flags; + (*back)->winding = backWinding; + } + + if ( frontWinding && backWinding ) { + return PLANESIDE_CROSS; + } + else if ( frontWinding ) { + return PLANESIDE_FRONT; + } + else { + return PLANESIDE_BACK; + } +} + + +//=============================================================== +// +// idBrushBSPNode +// +//=============================================================== + +/* +============ +idBrushBSPNode::idBrushBSPNode +============ +*/ +idBrushBSPNode::idBrushBSPNode( void ) { + brushList.Clear(); + contents = 0; + flags = 0; + volume = NULL; + portals = NULL; + children[0] = children[1] = NULL; + areaNum = 0; + occupied = 0; +} + +/* +============ +idBrushBSPNode::~idBrushBSPNode +============ +*/ +idBrushBSPNode::~idBrushBSPNode( void ) { + idBrushBSPPortal *p; + + // delete brushes + brushList.Free(); + + // delete volume brush + if ( volume ) { + delete volume; + } + + // delete portals + for ( p = portals; p; p = portals ) { + p->RemoveFromNode( this ); + if ( !p->nodes[0] && !p->nodes[1] ) { + delete p; + } + } +} + +/* +============ +idBrushBSPNode::SetContentsFromBrushes +============ +*/ +void idBrushBSPNode::SetContentsFromBrushes( void ) { + idBrush *brush; + + contents = 0; + for ( brush = brushList.Head(); brush; brush = brush->Next() ) { + contents |= brush->GetContents(); + } +} + +/* +============ +idBrushBSPNode::GetPortalBounds +============ +*/ +idBounds idBrushBSPNode::GetPortalBounds( void ) { + int s, i; + idBrushBSPPortal *p; + idBounds bounds; + + bounds.Clear(); + for ( p = portals; p; p = p->next[s] ) { + s = (p->nodes[1] == this); + + for ( i = 0; i < p->winding->GetNumPoints(); i++ ) { + bounds.AddPoint( (*p->winding)[i].ToVec3() ); + } + } + return bounds; +} + +/* +============ +idBrushBSPNode::TestLeafNode +============ +*/ +bool idBrushBSPNode::TestLeafNode( void ) { + int s, n; + float d; + idBrushBSPPortal *p; + idVec3 center; + idPlane plane; + + n = 0; + center = vec3_origin; + for ( p = portals; p; p = p->next[s] ) { + s = (p->nodes[1] == this); + center += p->winding->GetCenter(); + n++; + } + + center /= n; + + for ( p = portals; p; p = p->next[s] ) { + s = (p->nodes[1] == this); + if ( s ) { + plane = -p->GetPlane(); + } + else { + plane = p->GetPlane(); + } + d = plane.Distance( center ); + if ( d < 0.0f ) { + return false; + } + } + return true; +} + +/* +============ +idBrushBSPNode::Split +============ +*/ +bool idBrushBSPNode::Split( const idPlane &splitPlane, int splitPlaneNum ) { + int s, i; + idWinding *mid; + idBrushBSPPortal *p, *midPortal, *newPortals[2]; + idBrushBSPNode *newNodes[2]; + + mid = new idWinding( splitPlane.Normal(), splitPlane.Dist() ); + + for ( p = portals; p && mid; p = p->next[s] ) { + s = (p->nodes[1] == this); + if ( s ) { + mid = mid->Clip( -p->plane, 0.1f, false ); + } + else { + mid = mid->Clip( p->plane, 0.1f, false ); + } + } + + if ( !mid ) { + return false; + } + + // allocate two new nodes + for ( i = 0; i < 2; i++ ) { + newNodes[i] = new idBrushBSPNode(); + newNodes[i]->flags = flags; + newNodes[i]->contents = contents; + newNodes[i]->parent = this; + } + + // split all portals of the node + for ( p = portals; p; p = portals ) { + s = (p->nodes[1] == this); + p->Split( splitPlane, &newPortals[0], &newPortals[1] ); + for ( i = 0; i < 2; i++ ) { + if ( newPortals[i] ) { + if ( s ) { + newPortals[i]->AddToNodes( p->nodes[0], newNodes[i] ); + } + else { + newPortals[i]->AddToNodes( newNodes[i], p->nodes[1] ); + } + } + } + p->RemoveFromNode( p->nodes[0] ); + p->RemoveFromNode( p->nodes[1] ); + delete p; + } + + // add seperating portal + midPortal = new idBrushBSPPortal(); + midPortal->plane = splitPlane; + midPortal->planeNum = splitPlaneNum; + midPortal->winding = mid; + midPortal->AddToNodes( newNodes[0], newNodes[1] ); + + // set new child nodes + children[0] = newNodes[0]; + children[1] = newNodes[1]; + plane = splitPlane; + + return true; +} + +/* +============ +idBrushBSPNode::PlaneSide +============ +*/ +int idBrushBSPNode::PlaneSide( const idPlane &plane, float epsilon ) const { + int s, side; + idBrushBSPPortal *p; + bool front, back; + + front = back = false; + for ( p = portals; p; p = p->next[s] ) { + s = (p->nodes[1] == this); + + side = p->winding->PlaneSide( plane, epsilon ); + if ( side == SIDE_CROSS || side == SIDE_ON) { + return side; + } + if ( side == SIDE_FRONT ) { + if ( back ) { + return SIDE_CROSS; + } + front = true; + } + if ( side == SIDE_BACK ) { + if ( front ) { + return SIDE_CROSS; + } + back = true; + } + } + + if ( front ) { + return SIDE_FRONT; + } + return SIDE_BACK; +} + +/* +============ +idBrushBSPNode::RemoveFlagFlood +============ +*/ +void idBrushBSPNode::RemoveFlagFlood( int flag ) { + int s; + idBrushBSPPortal *p; + + RemoveFlag( flag ); + + for ( p = GetPortals(); p; p = p->Next(s) ) { + s = (p->GetNode(1) == this); + + if ( !(p->GetNode( !s )->GetFlags() & flag ) ) { + continue; + } + + p->GetNode( !s )->RemoveFlagFlood( flag ); + } +} + +/* +============ +idBrushBSPNode::RemoveFlagRecurse +============ +*/ +void idBrushBSPNode::RemoveFlagRecurse( int flag ) { + RemoveFlag( flag ); + if ( children[0] ) { + children[0]->RemoveFlagRecurse( flag ); + } + if ( children[1] ) { + children[1]->RemoveFlagRecurse( flag ); + } +} + +/* +============ +idBrushBSPNode::RemoveFlagRecurseFlood +============ +*/ +void idBrushBSPNode::RemoveFlagRecurseFlood( int flag ) { + RemoveFlag( flag ); + if ( !children[0] && !children[1] ) { + RemoveFlagFlood( flag ); + } + else { + if ( children[0] ) { + children[0]->RemoveFlagRecurseFlood( flag ); + } + if ( children[1] ) { + children[1]->RemoveFlagRecurseFlood( flag ); + } + } +} + + +//=============================================================== +// +// idBrushBSP +// +//=============================================================== + +/* +============ +idBrushBSP::idBrushBSP +============ +*/ +idBrushBSP::idBrushBSP( void ) { + root = outside = NULL; + numSplits = numPrunedSplits = 0; + brushMapContents = 0; + brushMap = NULL; +} + +/* +============ +idBrushBSP::~idBrushBSP +============ +*/ +idBrushBSP::~idBrushBSP( void ) { + + RemoveMultipleLeafNodeReferences_r( root ); + Free_r( root ); + + if ( outside ) { + delete outside; + } +} + +/* +============ +idBrushBSP::RemoveMultipleLeafNodeReferences_r +============ +*/ +void idBrushBSP::RemoveMultipleLeafNodeReferences_r( idBrushBSPNode *node ) { + if ( !node ) { + return; + } + + if ( node->children[0] ) { + if ( node->children[0]->parent != node ) { + node->children[0] = NULL; + } + else { + RemoveMultipleLeafNodeReferences_r( node->children[0] ); + } + } + if ( node->children[1] ) { + if ( node->children[1]->parent != node ) { + node->children[1] = NULL; + } + else { + RemoveMultipleLeafNodeReferences_r( node->children[1] ); + } + } +} + +/* +============ +idBrushBSP::Free_r +============ +*/ +void idBrushBSP::Free_r( idBrushBSPNode *node ) { + if ( !node ) { + return; + } + + Free_r( node->children[0] ); + Free_r( node->children[1] ); + + delete node; +} + +/* +============ +idBrushBSP::IsValidSplitter +============ +*/ +ID_INLINE bool idBrushBSP::IsValidSplitter( const idBrushSide *side ) { + return !( side->GetFlags() & ( SFL_SPLIT | SFL_USED_SPLITTER ) ); +} + +/* +============ +idBrushBSP::BrushSplitterStats +============ +*/ +typedef struct splitterStats_s { + int numFront; // number of brushes at the front of the splitter + int numBack; // number of brushes at the back of the splitter + int numSplits; // number of brush sides split by the splitter + int numFacing; // number of brushes facing this splitter + int epsilonBrushes; // number of tiny brushes this splitter would create +} splitterStats_t; + +int idBrushBSP::BrushSplitterStats( const idBrush *brush, int planeNum, const idPlaneSet &planeList, bool *testedPlanes, struct splitterStats_s &stats ) { + int i, j, num, s, lastNumSplits; + const idPlane *plane; + const idWinding *w; + float d, d_front, d_back, brush_front, brush_back; + + plane = &planeList[planeNum]; + + // get the plane side for the brush bounds + s = brush->GetBounds().PlaneSide( *plane, SPLITTER_EPSILON ); + if ( s == PLANESIDE_FRONT ) { + stats.numFront++; + return BRUSH_PLANESIDE_FRONT; + } + if ( s == PLANESIDE_BACK ) { + stats.numBack++; + return BRUSH_PLANESIDE_BACK; + } + + // if the brush actually uses the planenum, we can tell the side for sure + for ( i = 0; i < brush->GetNumSides(); i++ ) { + num = brush->GetSide( i )->GetPlaneNum(); + + if ( !(( num ^ planeNum ) >> 1) ) { + if ( num == planeNum ) { + stats.numBack++; + stats.numFacing++; + return ( BRUSH_PLANESIDE_BACK | BRUSH_PLANESIDE_FACING ); + } + if ( num == ( planeNum ^ 1 ) ) { + stats.numFront++; + stats.numFacing++; + return ( BRUSH_PLANESIDE_FRONT | BRUSH_PLANESIDE_FACING ); + } + } + } + + lastNumSplits = stats.numSplits; + brush_front = brush_back = 0.0f; + for ( i = 0; i < brush->GetNumSides(); i++ ) { + + if ( !IsValidSplitter( brush->GetSide( i ) ) ) { + continue; + } + + j = brush->GetSide( i )->GetPlaneNum(); + if ( testedPlanes[j] || testedPlanes[j^1] ) { + continue; + } + + w = brush->GetSide(i)->GetWinding(); + if ( !w ) { + continue; + } + d_front = d_back = 0.0f; + for ( j = 0; j < w->GetNumPoints(); j++ ) { + d = plane->Distance( (*w)[j].ToVec3() ); + if ( d > d_front ) { + d_front = d; + } + else if ( d < d_back ) { + d_back = d; + } + } + if ( d_front > SPLITTER_EPSILON && d_back < -SPLITTER_EPSILON ) { + stats.numSplits++; + } + if ( d_front > brush_front ) { + brush_front = d_front; + } + else if ( d_back < brush_back ) { + brush_back = d_back; + } + } + + // if brush sides are split and the brush only pokes one unit through the plane + if ( stats.numSplits > lastNumSplits && (brush_front < 1.0f || brush_back > -1.0f) ) { + stats.epsilonBrushes++; + } + + return BRUSH_PLANESIDE_BOTH; +} + +/* +============ +idBrushBSP::FindSplitter +============ +*/ +int idBrushBSP::FindSplitter( idBrushBSPNode *node, const idPlaneSet &planeList, bool *testedPlanes, struct splitterStats_s &bestStats ) { + int i, planeNum, bestSplitter, value, bestValue, f, numBrushSides; + idBrush *brush, *b; + splitterStats_t stats; + + memset( testedPlanes, 0, planeList.Num() * sizeof( bool ) ); + + bestSplitter = -1; + bestValue = -99999999; + for ( brush = node->brushList.Head(); brush; brush = brush->Next() ) { + + if ( brush->GetFlags() & BFL_NO_VALID_SPLITTERS ) { + continue; + } + + for ( i = 0; i < brush->GetNumSides(); i++ ) { + + if ( !IsValidSplitter( brush->GetSide(i) ) ) { + continue; + } + + planeNum = brush->GetSide(i)->GetPlaneNum(); + + if ( testedPlanes[planeNum] || testedPlanes[planeNum^1] ) { + continue; + } + + testedPlanes[planeNum] = testedPlanes[planeNum^1] = true; + + if ( node->volume->Split( planeList[planeNum], planeNum, NULL, NULL ) != PLANESIDE_CROSS ) { + continue; + } + + memset( &stats, 0, sizeof( stats ) ); + + f = 15 + 5 * (brush->GetSide(i)->GetPlane().Type() < PLANETYPE_TRUEAXIAL); + numBrushSides = node->brushList.NumSides(); + + for ( b = node->brushList.Head(); b; b = b->Next() ) { + + // if the brush has no valid splitters left + if ( b->GetFlags() & BFL_NO_VALID_SPLITTERS ) { + b->SetPlaneSide( BRUSH_PLANESIDE_BOTH ); + } + else { + b->SetPlaneSide( BrushSplitterStats( b, planeNum, planeList, testedPlanes, stats ) ); + } + + numBrushSides -= b->GetNumSides(); + // best value we can get using this plane as a splitter + value = f * (stats.numFacing + numBrushSides) - 10 * stats.numSplits - stats.epsilonBrushes * 1000; + // if the best value for this plane can't get any better than the best value we have + if ( value < bestValue ) { + break; + } + } + + if ( b ) { + continue; + } + + value = f * stats.numFacing - 10 * stats.numSplits - abs(stats.numFront - stats.numBack) - stats.epsilonBrushes * 1000; + + if ( value > bestValue ) { + bestValue = value; + bestSplitter = planeNum; + bestStats = stats; + + for ( b = node->brushList.Head(); b; b = b->Next() ) { + b->SavePlaneSide(); + } + } + } + } + + return bestSplitter; +} + +/* +============ +idBrushBSP::SetSplitterUsed +============ +*/ +void idBrushBSP::SetSplitterUsed( idBrushBSPNode *node, int planeNum ) { + int i, numValidBrushSplitters; + idBrush *brush; + + for ( brush = node->brushList.Head(); brush; brush = brush->Next() ) { + if ( !( brush->GetSavedPlaneSide() & BRUSH_PLANESIDE_FACING ) ) { + continue; + } + numValidBrushSplitters = 0; + for ( i = 0; i < brush->GetNumSides(); i++ ) { + + if ( !(( brush->GetSide(i)->GetPlaneNum() ^ planeNum ) >> 1) ) { + brush->GetSide(i)->SetFlag( SFL_USED_SPLITTER ); + } + else if ( IsValidSplitter( brush->GetSide(i) ) ) { + numValidBrushSplitters++; + } + } + if ( numValidBrushSplitters == 0 ) { + brush->SetFlag( BFL_NO_VALID_SPLITTERS ); + } + } +} + +/* +============ +idBrushBSP::BuildBrushBSP_r +============ +*/ +idBrushBSPNode *idBrushBSP::BuildBrushBSP_r( idBrushBSPNode *node, const idPlaneSet &planeList, bool *testedPlanes, int skipContents ) { + int planeNum; + splitterStats_t bestStats; + + planeNum = FindSplitter( node, planeList, testedPlanes, bestStats ); + + // if no split plane found this is a leaf node + if ( planeNum == -1 ) { + + node->SetContentsFromBrushes(); + + if ( brushMap && ( node->contents & brushMapContents ) ) { + brushMap->WriteBrush( node->volume ); + } + + // free node memory + node->brushList.Free(); + delete node->volume; + node->volume = NULL; + + node->children[0] = node->children[1] = NULL; + return node; + } + + numSplits++; + numGridCellSplits++; + + // mark all brush sides on the split plane as used + SetSplitterUsed( node, planeNum ); + + // set node split plane + node->plane = planeList[planeNum]; + + // allocate children + node->children[0] = new idBrushBSPNode(); + node->children[1] = new idBrushBSPNode(); + + // split node volume and brush list for children + node->volume->Split( node->plane, -1, &node->children[0]->volume, &node->children[1]->volume ); + node->brushList.Split( node->plane, -1, node->children[0]->brushList, node->children[1]->brushList, true ); + node->children[0]->parent = node->children[1]->parent = node; + + // free node memory + node->brushList.Free(); + delete node->volume; + node->volume = NULL; + + // process children + node->children[0] = BuildBrushBSP_r( node->children[0], planeList, testedPlanes, skipContents ); + node->children[1] = BuildBrushBSP_r( node->children[1], planeList, testedPlanes, skipContents ); + + // if both children contain the skip contents + if ( node->children[0]->contents & node->children[1]->contents & skipContents ) { + node->contents = node->children[0]->contents | node->children[1]->contents; + delete node->children[0]; + delete node->children[1]; + node->children[0] = node->children[1] = NULL; + numSplits--; + numGridCellSplits--; + } + + return node; +} + +/* +============ +idBrushBSP::ProcessGridCell +============ +*/ +idBrushBSPNode *idBrushBSP::ProcessGridCell( idBrushBSPNode *node, int skipContents ) { + idPlaneSet planeList; + bool *testedPlanes; + +#ifdef OUPUT_BSP_STATS_PER_GRID_CELL + common->Printf( "[Grid Cell %d]\n", ++numGridCells ); + common->Printf( "%6d brushes\n", node->brushList.Num() ); +#endif + + numGridCellSplits = 0; + + // chop away all brush overlap + node->brushList.Chop( BrushChopAllowed ); + + // merge brushes if possible + //node->brushList.Merge( BrushMergeAllowed ); + + // create a list with planes for this grid cell + node->brushList.CreatePlaneList( planeList ); + +#ifdef OUPUT_BSP_STATS_PER_GRID_CELL + common->Printf( "[Grid Cell BSP]\n" ); +#endif + + testedPlanes = new bool[planeList.Num()]; + + BuildBrushBSP_r( node, planeList, testedPlanes, skipContents ); + + delete testedPlanes; + +#ifdef OUPUT_BSP_STATS_PER_GRID_CELL + common->Printf( "\r%6d splits\n", numGridCellSplits ); +#endif + + return node; +} + +/* +============ +idBrushBSP::BuildGrid_r +============ +*/ +void idBrushBSP::BuildGrid_r( idList &gridCells, idBrushBSPNode *node ) { + int axis; + float dist; + idBounds bounds; + idVec3 normal, halfSize; + + if ( !node->brushList.Num() ) { + delete node->volume; + node->volume = NULL; + node->children[0] = node->children[1] = NULL; + return; + } + + bounds = node->volume->GetBounds(); + halfSize = (bounds[1] - bounds[0]) * 0.5f; + for ( axis = 0; axis < 3; axis++ ) { + if ( halfSize[axis] > BSP_GRID_SIZE ) { + dist = BSP_GRID_SIZE * ( floor( (bounds[0][axis] + halfSize[axis]) / BSP_GRID_SIZE ) + 1 ); + } + else { + dist = BSP_GRID_SIZE * ( floor( bounds[0][axis] / BSP_GRID_SIZE ) + 1 ); + } + if ( dist > bounds[0][axis] + 1.0f && dist < bounds[1][axis] - 1.0f ) { + break; + } + } + if ( axis >= 3 ) { + gridCells.Append( node ); + return; + } + + numSplits++; + + normal = vec3_origin; + normal[axis] = 1.0f; + node->plane.SetNormal( normal ); + node->plane.SetDist( (int) dist ); + + // allocate children + node->children[0] = new idBrushBSPNode(); + node->children[1] = new idBrushBSPNode(); + + // split volume and brush list for children + node->volume->Split( node->plane, -1, &node->children[0]->volume, &node->children[1]->volume ); + node->brushList.Split( node->plane, -1, node->children[0]->brushList, node->children[1]->brushList ); + node->children[0]->brushList.SetFlagOnFacingBrushSides( node->plane, SFL_USED_SPLITTER ); + node->children[1]->brushList.SetFlagOnFacingBrushSides( node->plane, SFL_USED_SPLITTER ); + node->children[0]->parent = node->children[1]->parent = node; + + // free node memory + node->brushList.Free(); + delete node->volume; + node->volume = NULL; + + // process children + BuildGrid_r( gridCells, node->children[0] ); + BuildGrid_r( gridCells, node->children[1] ); +} + +/* +============ +idBrushBSP::Build +============ +*/ +void idBrushBSP::Build( idBrushList brushList, int skipContents, + bool (*ChopAllowed)( idBrush *b1, idBrush *b2 ), + bool (*MergeAllowed)( idBrush *b1, idBrush *b2 ) ) { + + int i; + idList gridCells; + + common->Printf( "[Brush BSP]\n" ); + common->Printf( "%6d brushes\n", brushList.Num() ); + + BrushChopAllowed = ChopAllowed; + BrushMergeAllowed = MergeAllowed; + + numGridCells = 0; + treeBounds = brushList.GetBounds(); + root = new idBrushBSPNode(); + root->brushList = brushList; + root->volume = new idBrush(); + root->volume->FromBounds( treeBounds ); + root->parent = NULL; + + BuildGrid_r( gridCells, root ); + + common->Printf( "\r%6d grid cells\n", gridCells.Num() ); + +#ifdef OUPUT_BSP_STATS_PER_GRID_CELL + for ( i = 0; i < gridCells.Num(); i++ ) { + ProcessGridCell( gridCells[i], skipContents ); + } +#else + common->Printf( "\r%6d %%", 0 ); + for ( i = 0; i < gridCells.Num(); i++ ) { + DisplayRealTimeString( "\r%6d", i * 100 / gridCells.Num() ); + ProcessGridCell( gridCells[i], skipContents ); + } + common->Printf( "\r%6d %%\n", 100 ); +#endif + + common->Printf( "\r%6d splits\n", numSplits ); + + if ( brushMap ) { + delete brushMap; + } +} + +/* +============ +idBrushBSP::WriteBrushMap +============ +*/ +void idBrushBSP::WriteBrushMap( const idStr &fileName, const idStr &ext, int contents ) { + brushMap = new idBrushMap( fileName, ext ); + brushMapContents = contents; +} + +/* +============ +idBrushBSP::PruneTree_r +============ +*/ +void idBrushBSP::PruneTree_r( idBrushBSPNode *node, int contents ) { + int i, s; + idBrushBSPNode *nodes[2]; + idBrushBSPPortal *p, *nextp; + + if ( !node->children[0] || !node->children[1] ) { + return; + } + + PruneTree_r( node->children[0], contents ); + PruneTree_r( node->children[1], contents ); + + if ( ( node->children[0]->contents & node->children[1]->contents & contents ) ) { + + node->contents = node->children[0]->contents | node->children[1]->contents; + // move all child portals to parent + for ( i = 0; i < 2; i++ ) { + for ( p = node->children[i]->portals; p; p = nextp ) { + s = ( p->nodes[1] == node->children[i] ); + nextp = p->next[s]; + nodes[s] = node; + nodes[!s] = p->nodes[!s]; + p->RemoveFromNode( p->nodes[0] ); + p->RemoveFromNode( p->nodes[1] ); + if ( nodes[!s] == node->children[!i] ) { + delete p; // portal seperates both children + } + else { + p->AddToNodes( nodes[0], nodes[1] ); + } + } + } + + delete node->children[0]; + delete node->children[1]; + node->children[0] = NULL; + node->children[1] = NULL; + + numPrunedSplits++; + } +} + +/* +============ +idBrushBSP::PruneTree +============ +*/ +void idBrushBSP::PruneTree( int contents ) { + numPrunedSplits = 0; + common->Printf( "[Prune BSP]\n" ); + PruneTree_r( root, contents ); + common->Printf( "%6d splits pruned\n", numPrunedSplits ); +} + +/* +============ +idBrushBSP::BaseWindingForNode +============ +*/ +#define BASE_WINDING_EPSILON 0.001f + +idWinding *idBrushBSP::BaseWindingForNode( idBrushBSPNode *node ) { + idWinding *w; + idBrushBSPNode *n; + + w = new idWinding( node->plane.Normal(), node->plane.Dist() ); + + // clip by all the parents + for ( n = node->parent; n && w; n = n->parent ) { + + if ( n->children[0] == node ) { + // take front + w = w->Clip( n->plane, BASE_WINDING_EPSILON ); + } + else { + // take back + w = w->Clip( -n->plane, BASE_WINDING_EPSILON ); + } + node = n; + } + + return w; +} + +/* +============ +idBrushBSP::MakeNodePortal + + create the new portal by taking the full plane winding for the cutting + plane and clipping it by all of parents of this node +============ +*/ +void idBrushBSP::MakeNodePortal( idBrushBSPNode *node ) { + idBrushBSPPortal *newPortal, *p; + idWinding *w; + int side; + + w = BaseWindingForNode( node ); + + // clip the portal by all the other portals in the node + for ( p = node->portals; p && w; p = p->next[side] ) { + if ( p->nodes[0] == node ) { + side = 0; + w = w->Clip( p->plane, 0.1f ); + } + else if ( p->nodes[1] == node ) { + side = 1; + w = w->Clip( -p->plane, 0.1f ); + } + else { + common->Error( "MakeNodePortal: mislinked portal" ); + } + } + + if ( !w ) { + return; + } + + if ( w->IsTiny() ) { + delete w; + return; + } + + newPortal = new idBrushBSPPortal(); + newPortal->plane = node->plane; + newPortal->winding = w; + newPortal->AddToNodes( node->children[0], node->children[1] ); +} + +/* +============ +idBrushBSP::SplitNodePortals + + Move or split the portals that bound the node so that the node's children have portals instead of node. +============ +*/ +#define SPLIT_WINDING_EPSILON 0.001f + +void idBrushBSP::SplitNodePortals( idBrushBSPNode *node ) { + int side; + idBrushBSPPortal *p, *nextPortal, *newPortal; + idBrushBSPNode *f, *b, *otherNode; + idPlane *plane; + idWinding *frontWinding, *backWinding; + + plane = &node->plane; + f = node->children[0]; + b = node->children[1]; + + for ( p = node->portals; p; p = nextPortal ) { + if (p->nodes[0] == node) { + side = 0; + } + else if (p->nodes[1] == node) { + side = 1; + } + else { + common->Error( "idBrushBSP::SplitNodePortals: mislinked portal" ); + } + nextPortal = p->next[side]; + + otherNode = p->nodes[!side]; + p->RemoveFromNode( p->nodes[0] ); + p->RemoveFromNode( p->nodes[1] ); + + // cut the portal into two portals, one on each side of the cut plane + p->winding->Split( *plane, SPLIT_WINDING_EPSILON, &frontWinding, &backWinding ); + + if ( frontWinding && frontWinding->IsTiny() ) { + delete frontWinding; + frontWinding = NULL; + //tinyportals++; + } + + if ( backWinding && backWinding->IsTiny() ) { + delete backWinding; + backWinding = NULL; + //tinyportals++; + } + + if ( !frontWinding && !backWinding ) { + // tiny windings on both sides + continue; + } + + if ( !frontWinding ) { + delete backWinding; + if ( side == 0 ) { + p->AddToNodes( b, otherNode ); + } + else { + p->AddToNodes( otherNode, b ); + } + continue; + } + if ( !backWinding ) { + delete frontWinding; + if ( side == 0 ) { + p->AddToNodes( f, otherNode ); + } + else { + p->AddToNodes( otherNode, f ); + } + continue; + } + + // the winding is split + newPortal = new idBrushBSPPortal(); + *newPortal = *p; + newPortal->winding = backWinding; + delete p->winding; + p->winding = frontWinding; + + if ( side == 0 ) { + p->AddToNodes( f, otherNode ); + newPortal->AddToNodes( b, otherNode ); + } + else { + p->AddToNodes( otherNode, f ); + newPortal->AddToNodes( otherNode, b ); + } + } + + node->portals = NULL; +} + +/* +============ +idBrushBSP::MakeTreePortals_r +============ +*/ +void idBrushBSP::MakeTreePortals_r( idBrushBSPNode *node ) { + int i; + idBounds bounds; + + numPortals++; + DisplayRealTimeString( "\r%6d", numPortals ); + + bounds = node->GetPortalBounds(); + + if ( bounds[0][0] >= bounds[1][0] ) { + //common->Warning( "node without volume" ); + } + + for ( i = 0; i < 3; i++ ) { + if ( bounds[0][i] < MIN_WORLD_COORD || bounds[1][i] > MAX_WORLD_COORD ) { + common->Warning( "node with unbounded volume" ); + break; + } + } + + if ( !node->children[0] || !node->children[1] ) { + return; + } + + MakeNodePortal( node ); + SplitNodePortals( node ); + + MakeTreePortals_r( node->children[0] ); + MakeTreePortals_r( node->children[1] ); +} + +/* +============ +idBrushBSP::MakeOutsidePortals +============ +*/ +void idBrushBSP::MakeOutsidePortals( void ) { + int i, j, n; + idBounds bounds; + idBrushBSPPortal *p, *portals[6]; + idVec3 normal; + idPlane planes[6]; + + // pad with some space so there will never be null volume leaves + bounds = treeBounds.Expand( 32 ); + + for ( i = 0; i < 3; i++ ) { + if ( bounds[0][i] > bounds[1][i] ) { + common->Error( "empty BSP tree" ); + } + } + + outside = new idBrushBSPNode(); + outside->parent = NULL; + outside->children[0] = outside->children[1] = NULL; + outside->brushList.Clear(); + outside->portals = NULL; + outside->contents = 0; + + for ( i = 0; i < 3; i++ ) { + for ( j = 0; j < 2; j++ ) { + + p = new idBrushBSPPortal(); + normal = vec3_origin; + normal[i] = j ? -1 : 1; + p->plane.SetNormal( normal ); + p->plane.SetDist( j ? -bounds[j][i] : bounds[j][i] ); + p->winding = new idWinding( p->plane.Normal(), p->plane.Dist() ); + p->AddToNodes( root, outside ); + + n = j * 3 + i; + portals[n] = p; + } + } + + // clip the base windings with all the other planes + for ( i = 0; i < 6; i++ ) { + for ( j = 0; j < 6; j++ ) { + if (j == i) { + continue; + } + portals[i]->winding = portals[i]->winding->Clip( portals[j]->plane, ON_EPSILON ); + } + } +} + +/* +============ +idBrushBSP::Portalize +============ +*/ +void idBrushBSP::Portalize( void ) { + common->Printf( "[Portalize BSP]\n" ); + common->Printf( "%6d nodes\n", (numSplits - numPrunedSplits) * 2 + 1 ); + numPortals = 0; + MakeOutsidePortals(); + MakeTreePortals_r( root ); + common->Printf( "\r%6d nodes portalized\n", numPortals ); +} + +/* +============= +LeakFile + +Finds the shortest possible chain of portals that +leads from the outside leaf to a specific occupied leaf. +============= +*/ +void idBrushBSP::LeakFile( const idStr &fileName ) { + int count, next, s; + idVec3 mid; + idFile *lineFile; + idBrushBSPNode *node, *nextNode; + idBrushBSPPortal *p, *nextPortal; + idStr qpath, name; + + if ( !outside->occupied ) { + return; + } + + qpath = fileName; + qpath.SetFileExtension( "lin" ); + + common->Printf( "writing %s...\n", qpath.c_str() ); + + lineFile = fileSystem->OpenFileWrite( qpath, "fs_devpath" ); + if ( !lineFile ) { + common->Error( "Couldn't open %s\n", qpath.c_str() ); + return; + } + + count = 0; + node = outside; + while( node->occupied > 1 ) { + + // find the best portal exit + next = node->occupied; + for (p = node->portals; p; p = p->next[!s] ) { + s = (p->nodes[0] == node); + if ( p->nodes[s]->occupied && p->nodes[s]->occupied < next ) { + nextPortal = p; + nextNode = p->nodes[s]; + next = nextNode->occupied; + } + } + node = nextNode; + mid = nextPortal->winding->GetCenter(); + lineFile->Printf( "%f %f %f\n", mid[0], mid[1], mid[2] ); + count++; + } + + // add the origin of the entity from which the leak was found + lineFile->Printf( "%f %f %f\n", leakOrigin[0], leakOrigin[1], leakOrigin[2] ); + + fileSystem->CloseFile( lineFile ); +} + +/* +============ +idBrushBSP::FloodThroughPortals_r +============ +*/ +void idBrushBSP::FloodThroughPortals_r( idBrushBSPNode *node, int contents, int depth ) { + idBrushBSPPortal *p; + int s; + + if ( node->occupied ) { + common->Error( "FloodThroughPortals_r: node already occupied\n" ); + } + if ( !node ) { + common->Error( "FloodThroughPortals_r: NULL node\n" ); + } + + node->occupied = depth; + + for ( p = node->portals; p; p = p->next[s] ) { + s = (p->nodes[1] == node); + + // if the node at the other side of the portal is removed + if ( !p->nodes[!s] ) { + continue; + } + + // if the node at the other side of the portal is occupied already + if ( p->nodes[!s]->occupied ) { + continue; + } + + // can't flood through the portal if it has the seperating contents at the other side + if ( p->nodes[!s]->contents & contents ) { + continue; + } + + // flood recursively through the current portal + FloodThroughPortals_r( p->nodes[!s], contents, depth+1 ); + } +} + +/* +============ +idBrushBSP::FloodFromOrigin +============ +*/ +bool idBrushBSP::FloodFromOrigin( const idVec3 &origin, int contents ) { + idBrushBSPNode *node; + + //find the leaf to start in + node = root; + while( node->children[0] && node->children[1] ) { + + if ( node->plane.Side( origin ) == PLANESIDE_BACK ) { + node = node->children[1]; + } + else { + node = node->children[0]; + } + } + + if ( !node ) { + return false; + } + + // if inside the inside/outside seperating contents + if ( node->contents & contents ) { + return false; + } + + // if the node is already occupied + if ( node->occupied ) { + return false; + } + + FloodThroughPortals_r( node, contents, 1 ); + + return true; +} + +/* +============ +idBrushBSP::FloodFromEntities + + Marks all nodes that can be reached by entites. +============ +*/ +bool idBrushBSP::FloodFromEntities( const idMapFile *mapFile, int contents, const idStrList &classNames ) { + int i, j; + bool inside; + idVec3 origin; + idMapEntity *mapEnt; + idStr classname; + + inside = false; + outside->occupied = 0; + + // skip the first entity which is assumed to be the worldspawn + for ( i = 1; i < mapFile->GetNumEntities(); i++ ) { + + mapEnt = mapFile->GetEntity( i ); + + if ( !mapEnt->epairs.GetVector( "origin", "", origin ) ) { + continue; + } + + if ( !mapEnt->epairs.GetString( "classname", "", classname ) ) { + continue; + } + + for ( j = 0; j < classNames.Num(); j++ ) { + if ( classname.Icmp( classNames[j] ) == 0 ) { + break; + } + } + + if ( j >= classNames.Num() ) { + continue; + } + + origin[2] += 1; + + // nudge around a little + if ( FloodFromOrigin( origin, contents ) ) { + inside = true; + } + + if ( outside->occupied ) { + leakOrigin = origin; + break; + } + } + + if ( !inside ) { + common->Warning( "no entities inside" ); + } + else if ( outside->occupied ) { + common->Warning( "reached outside from entity %d (%s)", i, classname.c_str() ); + } + + return ( inside && !outside->occupied ); +} + +/* +============ +idBrushBSP::RemoveOutside_r +============ +*/ +void idBrushBSP::RemoveOutside_r( idBrushBSPNode *node, int contents ) { + + if ( !node ) { + return; + } + + if ( node->children[0] || node->children[1] ) { + RemoveOutside_r( node->children[0], contents ); + RemoveOutside_r( node->children[1], contents ); + return; + } + + if ( !node->occupied ) { + if ( !( node->contents & contents ) ) { + outsideLeafNodes++; + node->contents |= contents; + } + else { + solidLeafNodes++; + } + } + else { + insideLeafNodes++; + } +} + +/* +============ +idBrushBSP::RemoveOutside +============ +*/ +bool idBrushBSP::RemoveOutside( const idMapFile *mapFile, int contents, const idStrList &classNames ) { + common->Printf( "[Remove Outside]\n" ); + + solidLeafNodes = outsideLeafNodes = insideLeafNodes = 0; + + if ( !FloodFromEntities( mapFile, contents, classNames ) ) { + return false; + } + + RemoveOutside_r( root, contents ); + + common->Printf( "%6d solid leaf nodes\n", solidLeafNodes ); + common->Printf( "%6d outside leaf nodes\n", outsideLeafNodes ); + common->Printf( "%6d inside leaf nodes\n", insideLeafNodes ); + + //PruneTree( contents ); + + return true; +} + +/* +============ +idBrushBSP::SetPortalPlanes_r +============ +*/ +void idBrushBSP::SetPortalPlanes_r( idBrushBSPNode *node, idPlaneSet &planeList ) { + int s; + idBrushBSPPortal *p; + + if ( !node ) { + return; + } + + for ( p = node->portals; p; p = p->next[s] ) { + s = (p->nodes[1] == node); + if ( p->planeNum == -1 ) { + p->planeNum = planeList.FindPlane( p->plane, PORTAL_PLANE_NORMAL_EPSILON, PORTAL_PLANE_DIST_EPSILON ); + } + } + SetPortalPlanes_r( node->children[0], planeList ); + SetPortalPlanes_r( node->children[1], planeList ); +} + +/* +============ +idBrushBSP::SetPortalPlanes + + give all portals a plane number +============ +*/ +void idBrushBSP::SetPortalPlanes( void ) { + SetPortalPlanes_r( root, portalPlanes ); +} + +/* +============ +idBrushBSP::MergeLeafNodePortals +============ +*/ +void idBrushBSP::MergeLeafNodePortals( idBrushBSPNode *node, int skipContents ) { + int s1, s2; + bool foundPortal; + idBrushBSPPortal *p1, *p2, *nextp1, *nextp2; + idWinding *newWinding, *reverse; + + // pass 1: merge all portals that seperate the same leaf nodes + for ( p1 = node->GetPortals(); p1; p1 = nextp1 ) { + s1 = (p1->GetNode(1) == node); + nextp1 = p1->Next(s1); + + for ( p2 = nextp1; p2; p2 = nextp2 ) { + s2 = (p2->GetNode(1) == node); + nextp2 = p2->Next(s2); + + // if both portals seperate the same leaf nodes + if ( p1->nodes[!s1] == p2->nodes[!s2] ) { + + // add the winding of p2 to the winding of p1 + p1->winding->AddToConvexHull( p2->winding, p1->plane.Normal() ); + + // delete p2 + p2->RemoveFromNode( p2->nodes[0] ); + p2->RemoveFromNode( p2->nodes[1] ); + delete p2; + + numMergedPortals++; + + nextp1 = node->GetPortals(); + break; + } + } + } + + // pass 2: merge all portals in the same plane if they all have the skip contents at the other side + for ( p1 = node->GetPortals(); p1; p1 = nextp1 ) { + s1 = (p1->GetNode(1) == node); + nextp1 = p1->Next(s1); + + if ( !(p1->nodes[!s1]->contents & skipContents) ) { + continue; + } + + // test if all portals in this plane have the skip contents at the other side + foundPortal = false; + for ( p2 = node->GetPortals(); p2; p2 = nextp2 ) { + s2 = (p2->GetNode(1) == node); + nextp2 = p2->Next(s2); + + if ( p2 == p1 || (p2->planeNum & ~1) != (p1->planeNum & ~1) ) { + continue; + } + foundPortal = true; + if ( !(p2->nodes[!s2]->contents & skipContents) ) { + break; + } + } + + // if all portals in this plane have the skip contents at the other side + if ( !p2 && foundPortal ) { + for ( p2 = node->GetPortals(); p2; p2 = nextp2 ) { + s2 = (p2->GetNode(1) == node); + nextp2 = p2->Next(s2); + + if ( p2 == p1 || (p2->planeNum & ~1) != (p1->planeNum & ~1) ) { + continue; + } + + // add the winding of p2 to the winding of p1 + p1->winding->AddToConvexHull( p2->winding, p1->plane.Normal() ); + + // delete p2 + p2->RemoveFromNode( p2->nodes[0] ); + p2->RemoveFromNode( p2->nodes[1] ); + delete p2; + + numMergedPortals++; + } + nextp1 = node->GetPortals(); + } + } + + // pass 3: try to merge portals in the same plane that have the skip contents at the other side + for ( p1 = node->GetPortals(); p1; p1 = nextp1 ) { + s1 = (p1->GetNode(1) == node); + nextp1 = p1->Next(s1); + + if ( !(p1->nodes[!s1]->contents & skipContents) ) { + continue; + } + + for ( p2 = nextp1; p2; p2 = nextp2 ) { + s2 = (p2->GetNode(1) == node); + nextp2 = p2->Next(s2); + + if ( !(p2->nodes[!s2]->contents & skipContents) ) { + continue; + } + + if ( (p2->planeNum & ~1) != (p1->planeNum & ~1) ) { + continue; + } + + // try to merge the two portal windings + if ( p2->planeNum == p1->planeNum ) { + newWinding = p1->winding->TryMerge( *p2->winding, p1->plane.Normal() ); + } + else { + reverse = p2->winding->Reverse(); + newWinding = p1->winding->TryMerge( *reverse, p1->plane.Normal() ); + delete reverse; + } + + // if successfully merged + if ( newWinding ) { + + // replace the winding of the first portal + delete p1->winding; + p1->winding = newWinding; + + // delete p2 + p2->RemoveFromNode( p2->nodes[0] ); + p2->RemoveFromNode( p2->nodes[1] ); + delete p2; + + numMergedPortals++; + + nextp1 = node->GetPortals(); + break; + } + } + } +} + +/* +============ +idBrushBSP::MergePortals_r +============ +*/ +void idBrushBSP::MergePortals_r( idBrushBSPNode *node, int skipContents ) { + + if ( !node ) { + return; + } + + if ( node->contents & skipContents ) { + return; + } + + if ( !node->children[0] && !node->children[1] ) { + MergeLeafNodePortals( node, skipContents ); + return; + } + + MergePortals_r( node->children[0], skipContents ); + MergePortals_r( node->children[1], skipContents ); +} + +/* +============ +idBrushBSP::MergePortals +============ +*/ +void idBrushBSP::MergePortals( int skipContents ) { + numMergedPortals = 0; + common->Printf( "[Merge Portals]\n" ); + SetPortalPlanes(); + MergePortals_r( root, skipContents ); + common->Printf( "%6d portals merged\n", numMergedPortals ); +} + +/* +============ +idBrushBSP::PruneMergedTree_r +============ +*/ +void idBrushBSP::PruneMergedTree_r( idBrushBSPNode *node ) { + int i; + idBrushBSPNode *leafNode; + + if ( !node ) { + return; + } + + PruneMergedTree_r( node->children[0] ); + PruneMergedTree_r( node->children[1] ); + + for ( i = 0; i < 2; i++ ) { + if ( node->children[i] ) { + leafNode = node->children[i]->children[0]; + if ( leafNode && leafNode == node->children[i]->children[1] ) { + if ( leafNode->parent == node->children[i] ) { + leafNode->parent = node; + } + delete node->children[i]; + node->children[i] = leafNode; + } + } + } +} + +/* +============ +idBrushBSP::UpdateTreeAfterMerge_r +============ +*/ +void idBrushBSP::UpdateTreeAfterMerge_r( idBrushBSPNode *node, const idBounds &bounds, idBrushBSPNode *oldNode, idBrushBSPNode *newNode ) { + + if ( !node ) { + return; + } + + if ( !node->children[0] && !node->children[1] ) { + return; + } + + if ( node->children[0] == oldNode ) { + node->children[0] = newNode; + } + if ( node->children[1] == oldNode ) { + node->children[1] = newNode; + } + + switch( bounds.PlaneSide( node->plane, 2.0f ) ) { + case PLANESIDE_FRONT: + UpdateTreeAfterMerge_r( node->children[0], bounds, oldNode, newNode ); + break; + case PLANESIDE_BACK: + UpdateTreeAfterMerge_r( node->children[1], bounds, oldNode, newNode ); + break; + default: + UpdateTreeAfterMerge_r( node->children[0], bounds, oldNode, newNode ); + UpdateTreeAfterMerge_r( node->children[1], bounds, oldNode, newNode ); + break; + } +} + +/* +============ +idBrushBSP::TryMergeLeafNodes + + NOTE: multiple brances of the BSP tree might point to the same leaf node after merging +============ +*/ +bool idBrushBSP::TryMergeLeafNodes( idBrushBSPPortal *portal, int side ) { + int i, j, k, s1, s2, s; + idBrushBSPNode *nodes[2], *node1, *node2; + idBrushBSPPortal *p1, *p2, *p, *nextp; + idPlane plane; + idWinding *w; + idBounds bounds, b; + + nodes[0] = node1 = portal->nodes[side]; + nodes[1] = node2 = portal->nodes[!side]; + + // check if the merged node would still be convex + for ( i = 0; i < 2; i++ ) { + + j = !i; + + for ( p1 = nodes[i]->portals; p1; p1 = p1->next[s1] ) { + s1 = (p1->nodes[1] == nodes[i]); + + if ( p1->nodes[!s1] == nodes[j] ) { + continue; + } + + if ( s1 ) { + plane = -p1->plane; + } + else { + plane = p1->plane; + } + + // all the non seperating portals of the other node should be at the front or on the plane + for ( p2 = nodes[j]->portals; p2; p2 = p2->next[s2] ) { + s2 = (p2->nodes[1] == nodes[j]); + + if ( p2->nodes[!s2] == nodes[i] ) { + continue; + } + + w = p2->winding; + for ( k = 0; k < w->GetNumPoints(); k++ ) { + if ( plane.Distance( (*w)[k].ToVec3() ) < -0.1f ) { + return false; + } + } + } + } + } + + // remove all portals that seperate the two nodes + for ( p = node1->portals; p; p = nextp ) { + s = (p->nodes[1] == node1); + nextp = p->next[s]; + + if ( p->nodes[!s] == node2 ) { + p->RemoveFromNode( p->nodes[0] ); + p->RemoveFromNode( p->nodes[1] ); + delete p; + } + } + + // move all portals of node2 to node1 + for ( p = node2->portals; p; p = node2->portals ) { + s = (p->nodes[1] == node2); + + nodes[s] = node1; + nodes[!s] = p->nodes[!s]; + p->RemoveFromNode( p->nodes[0] ); + p->RemoveFromNode( p->nodes[1] ); + p->AddToNodes( nodes[0], nodes[1] ); + } + + // get bounds for the new node + bounds.Clear(); + for ( p = node1->portals; p; p = p->next[s] ) { + s = (p->nodes[1] == node1); + p->GetWinding()->GetBounds( b ); + bounds += b; + } + + // replace every reference to node2 by a reference to node1 + UpdateTreeAfterMerge_r( root, bounds, node2, node1 ); + + delete node2; + + return true; +} + +/* +============ +idBrushBSP::MeltFloor_r + + flood through portals touching the bounds to find all vertices that might be inside the bounds +============ +*/ +void idBrushBSP::MeltFlood_r( idBrushBSPNode *node, int skipContents, idBounds &bounds, idVectorSet &vertexList ) { + int s1, i; + idBrushBSPPortal *p1; + idBounds b; + const idWinding *w; + + node->SetFlag( NODE_VISITED ); + + for ( p1 = node->GetPortals(); p1; p1 = p1->Next(s1) ) { + s1 = (p1->GetNode(1) == node); + + if ( p1->GetNode( !s1 )->GetFlags() & NODE_VISITED ) { + continue; + } + + w = p1->GetWinding(); + + for ( i = 0; i < w->GetNumPoints(); i++ ) { + if ( bounds.ContainsPoint( (*w)[i].ToVec3() ) ) { + vertexList.FindVector( (*w)[i].ToVec3(), VERTEX_MELT_EPSILON ); + } + } + } + + for ( p1 = node->GetPortals(); p1; p1 = p1->Next(s1) ) { + s1 = (p1->GetNode(1) == node); + + if ( p1->GetNode( !s1 )->GetFlags() & NODE_VISITED ) { + continue; + } + + if ( p1->GetNode( !s1 )->GetContents() & skipContents ) { + continue; + } + + w = p1->GetWinding(); + w->GetBounds( b ); + + if ( !bounds.IntersectsBounds( b ) ) { + continue; + } + + MeltFlood_r( p1->GetNode( !s1 ), skipContents, bounds, vertexList ); + } +} + +/* +============ +idBrushBSP::MeltLeafNodePortals +============ +*/ +void idBrushBSP::MeltLeafNodePortals( idBrushBSPNode *node, int skipContents, idVectorSet &vertexList ) { + int s1, i; + idBrushBSPPortal *p1; + idBounds bounds; + + if ( node->GetFlags() & NODE_DONE ) { + return; + } + + node->SetFlag( NODE_DONE ); + + // melt things together + for ( p1 = node->GetPortals(); p1; p1 = p1->Next(s1) ) { + s1 = (p1->GetNode(1) == node); + + if ( p1->GetNode( !s1 )->GetFlags() & NODE_DONE ) { + continue; + } + + p1->winding->GetBounds( bounds ); + bounds.ExpandSelf( 2 * VERTEX_MELT_HASH_SIZE * VERTEX_MELT_EPSILON ); + vertexList.Init( bounds[0], bounds[1], VERTEX_MELT_HASH_SIZE, 128 ); + + // get all vertices to be considered + MeltFlood_r( node, skipContents, bounds, vertexList ); + node->RemoveFlagFlood( NODE_VISITED ); + + for ( i = 0; i < vertexList.Num(); i++ ) { + if ( p1->winding->InsertPointIfOnEdge( vertexList[i], p1->plane, 0.1f ) ) { + numInsertedPoints++; + } + } + } + DisplayRealTimeString( "\r%6d", numInsertedPoints ); +} + +/* +============ +idBrushBSP::MeltPortals_r +============ +*/ +void idBrushBSP::MeltPortals_r( idBrushBSPNode *node, int skipContents, idVectorSet &vertexList ) { + if ( !node ) { + return; + } + + if ( node->contents & skipContents ) { + return; + } + + if ( !node->children[0] && !node->children[1] ) { + MeltLeafNodePortals( node, skipContents, vertexList ); + return; + } + + MeltPortals_r( node->children[0], skipContents, vertexList ); + MeltPortals_r( node->children[1], skipContents, vertexList ); +} + +/* +============ +idBrushBSP::RemoveLeafNodeColinearPoints +============ +*/ +void idBrushBSP::RemoveLeafNodeColinearPoints( idBrushBSPNode *node ) { + int s1; + idBrushBSPPortal *p1; + + // remove colinear points + for ( p1 = node->GetPortals(); p1; p1 = p1->Next(s1) ) { + s1 = (p1->GetNode(1) == node); + p1->winding->RemoveColinearPoints( p1->plane.Normal(), 0.1f ); + } +} + +/* +============ +idBrushBSP::RemoveColinearPoints_r +============ +*/ +void idBrushBSP::RemoveColinearPoints_r( idBrushBSPNode *node, int skipContents ) { + if ( !node ) { + return; + } + + if ( node->contents & skipContents ) { + return; + } + + if ( !node->children[0] && !node->children[1] ) { + RemoveLeafNodeColinearPoints( node ); + return; + } + + RemoveColinearPoints_r( node->children[0], skipContents ); + RemoveColinearPoints_r( node->children[1], skipContents ); +} + +/* +============ +idBrushBSP::MeltPortals +============ +*/ +void idBrushBSP::MeltPortals( int skipContents ) { + idVectorSet vertexList; + + numInsertedPoints = 0; + common->Printf( "[Melt Portals]\n" ); + RemoveColinearPoints_r( root, skipContents ); + MeltPortals_r( root, skipContents, vertexList ); + root->RemoveFlagRecurse( NODE_DONE ); + common->Printf( "\r%6d points inserted\n", numInsertedPoints ); +} diff --git a/src/engine/CMakeLists.txt b/src/engine/CMakeLists.txt deleted file mode 100644 index 004e3b7..0000000 --- a/src/engine/CMakeLists.txt +++ /dev/null @@ -1,469 +0,0 @@ -function(q4_add_engine_object target) - add_library(${target} OBJECT ${ARGN}) - target_include_directories(${target} - PRIVATE - ${PROJECT_SOURCE_DIR} - ${PROJECT_SOURCE_DIR}/renderer/jpeg-6 - ${PROJECT_SOURCE_DIR}/sound/OggVorbis/ogg/include - ${PROJECT_SOURCE_DIR}/sound/OggVorbis/vorbis/include - "C:/Program Files (x86)/Microsoft DirectX SDK (June 2010)/Include" - ) - target_compile_definitions(${target} - PRIVATE - WIN32 - _WINDOWS - _LOAD_DLL - _USE_32BIT_TIME_T - Q4_NO_PUNKBUSTER - Q4_RECON_SEED - Q4_DISABLE_TOOLS - Q4_DISABLE_GL_LOGGING - Q4_DISABLE_TASKKEY_HOOK - $<$:_DEBUG> - $<$:NDEBUG;_FINAL> - ) - set_target_properties(${target} PROPERTIES FOLDER "Engine/Seed") -endfunction() - -file(GLOB_RECURSE Q4_AAS_SOURCES CONFIGURE_DEPENDS - ${PROJECT_SOURCE_DIR}/aas/*.cpp -) -set(Q4_AAS_FILE_SOURCE ${PROJECT_SOURCE_DIR}/aas/AASFile.cpp) -set(Q4_AAS_COMPILER_SOURCES - ${PROJECT_SOURCE_DIR}/aas/AASSettingsTools.cpp - ${PROJECT_SOURCE_DIR}/aas/AASTactical.cpp - ${PROJECT_SOURCE_DIR}/aas/AASBuild.cpp - ${PROJECT_SOURCE_DIR}/aas/AASBuild_file.cpp - ${PROJECT_SOURCE_DIR}/aas/AASBuild_gravity.cpp - ${PROJECT_SOURCE_DIR}/aas/AASBuild_ledge.cpp - ${PROJECT_SOURCE_DIR}/aas/AASBuild_merge.cpp - ${PROJECT_SOURCE_DIR}/aas/AASCluster.cpp - ${PROJECT_SOURCE_DIR}/aas/AASReach.cpp - ${PROJECT_SOURCE_DIR}/aas/Brush.cpp - ${PROJECT_SOURCE_DIR}/aas/BrushBSP.cpp -) -list(REMOVE_ITEM Q4_AAS_SOURCES ${Q4_AAS_COMPILER_SOURCES}) - -file(GLOB_RECURSE Q4_CM_SOURCES CONFIGURE_DEPENDS - ${PROJECT_SOURCE_DIR}/cm/*.cpp -) -set(Q4_CM_MODEL_SOURCE ${PROJECT_SOURCE_DIR}/cm/CollisionModel.cpp) -set(Q4_CM_LOAD_SOURCE ${PROJECT_SOURCE_DIR}/cm/collisionmodel_load.cpp) -set(Q4_CM_CONTACTS_SOURCE ${PROJECT_SOURCE_DIR}/cm/collisionmodel_contacts.cpp) -set(Q4_CM_CONTENTS_SOURCE ${PROJECT_SOURCE_DIR}/cm/collisionmodel_contents.cpp) -set(Q4_CM_TRACE_SOURCE ${PROJECT_SOURCE_DIR}/cm/collisionmodel_trace.cpp) -set(Q4_CM_TRANSLATE_SOURCE ${PROJECT_SOURCE_DIR}/cm/collisionmodel_translate.cpp) -list(REMOVE_ITEM Q4_CM_SOURCES ${Q4_CM_MODEL_SOURCE}) - -file(GLOB Q4_FRAMEWORK_TOP_SOURCES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/framework/*.cpp) -set(Q4_FRAMEWORK_PLAYER_MODEL_SOURCE ${PROJECT_SOURCE_DIR}/framework/DeclPlayerModel.cpp) -set(Q4_FRAMEWORK_ENGINE_TOP_SOURCES ${Q4_FRAMEWORK_TOP_SOURCES}) -# DeclPlayerModel is owned by gamex86.dll in the retail PDB. Keep it out of -# q4xp.exe so the reconstructed compiland and its allocator live in the same -# module that registers DECL_PLAYER_MODEL. -list(REMOVE_ITEM Q4_FRAMEWORK_ENGINE_TOP_SOURCES ${Q4_FRAMEWORK_PLAYER_MODEL_SOURCE}) -set(Q4_FRAMEWORK_SESSION_SOURCES - ${PROJECT_SOURCE_DIR}/framework/common.cpp - ${PROJECT_SOURCE_DIR}/framework/session.cpp - ${PROJECT_SOURCE_DIR}/framework/session_menu.cpp -) -set(Q4_FRAMEWORK_EDITFIELD_SOURCE ${PROJECT_SOURCE_DIR}/framework/editfield.cpp) -set(Q4_FRAMEWORK_CONSOLE_SOURCE ${PROJECT_SOURCE_DIR}/framework/console.cpp) -set(Q4_FRAMEWORK_USERCMD_SOURCE ${PROJECT_SOURCE_DIR}/framework/usercmdgen.cpp) -set(Q4_FRAMEWORK_CONSOLE_SOURCES - ${Q4_FRAMEWORK_EDITFIELD_SOURCE} - ${Q4_FRAMEWORK_CONSOLE_SOURCE} - ${Q4_FRAMEWORK_USERCMD_SOURCE} -) -file(GLOB Q4_FRAMEWORK_ASYNC_SOURCES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/framework/async/*.cpp) -set(Q4_FRAMEWORK_MSGCHANNEL_SOURCE ${PROJECT_SOURCE_DIR}/framework/async/msgchannel.cpp) -list(REMOVE_ITEM Q4_FRAMEWORK_ASYNC_SOURCES ${Q4_FRAMEWORK_MSGCHANNEL_SOURCE}) -set(Q4_FRAMEWORK_CORE_SOURCES ${Q4_FRAMEWORK_ENGINE_TOP_SOURCES}) -list(REMOVE_ITEM Q4_FRAMEWORK_CORE_SOURCES - ${Q4_FRAMEWORK_SESSION_SOURCES} - ${Q4_FRAMEWORK_CONSOLE_SOURCES} -) - -file(GLOB_RECURSE Q4_UI_SOURCES CONFIGURE_DEPENDS - ${PROJECT_SOURCE_DIR}/ui/*.cpp -) - -# Retail quake4.exe renderer compilation units. Keep this explicit: the PDB -# records one object for each entry and the list doubles as a boundary audit. -set(Q4_RENDERER_SOURCES - ${PROJECT_SOURCE_DIR}/renderer/Cinematic.cpp - ${PROJECT_SOURCE_DIR}/renderer/draw_arb.cpp - ${PROJECT_SOURCE_DIR}/renderer/draw_arb2.cpp - ${PROJECT_SOURCE_DIR}/renderer/draw_common.cpp - ${PROJECT_SOURCE_DIR}/renderer/draw_nv10.cpp - ${PROJECT_SOURCE_DIR}/renderer/draw_nv20.cpp - ${PROJECT_SOURCE_DIR}/renderer/draw_r200.cpp - ${PROJECT_SOURCE_DIR}/renderer/GuiModel.cpp - ${PROJECT_SOURCE_DIR}/renderer/Image_files.cpp - ${PROJECT_SOURCE_DIR}/renderer/Image_init.cpp - ${PROJECT_SOURCE_DIR}/renderer/Image_load.cpp - ${PROJECT_SOURCE_DIR}/renderer/Image_process.cpp - ${PROJECT_SOURCE_DIR}/renderer/Image_program.cpp - ${PROJECT_SOURCE_DIR}/renderer/Image_stats.cpp - ${PROJECT_SOURCE_DIR}/renderer/Interaction.cpp - ${PROJECT_SOURCE_DIR}/renderer/Material.cpp - ${PROJECT_SOURCE_DIR}/renderer/MegaTexture.cpp - ${PROJECT_SOURCE_DIR}/renderer/Model.cpp - ${PROJECT_SOURCE_DIR}/renderer/ModelDecal.cpp - ${PROJECT_SOURCE_DIR}/renderer/ModelManager.cpp - ${PROJECT_SOURCE_DIR}/renderer/ModelOverlay.cpp - ${PROJECT_SOURCE_DIR}/renderer/Model_ase.cpp - ${PROJECT_SOURCE_DIR}/renderer/Model_beam.cpp - ${PROJECT_SOURCE_DIR}/renderer/Model_liquid.cpp - ${PROJECT_SOURCE_DIR}/renderer/Model_lwo.cpp - ${PROJECT_SOURCE_DIR}/renderer/Model_ma.cpp - ${PROJECT_SOURCE_DIR}/renderer/Model_md3.cpp - ${PROJECT_SOURCE_DIR}/renderer/Model_md5.cpp - ${PROJECT_SOURCE_DIR}/renderer/Model_sprite.cpp - ${PROJECT_SOURCE_DIR}/renderer/RenderEntity.cpp - ${PROJECT_SOURCE_DIR}/renderer/RenderSystem.cpp - ${PROJECT_SOURCE_DIR}/renderer/RenderSystem_init.cpp - ${PROJECT_SOURCE_DIR}/renderer/RenderWorld.cpp - ${PROJECT_SOURCE_DIR}/renderer/RenderWorld_demo.cpp - ${PROJECT_SOURCE_DIR}/renderer/RenderWorld_load.cpp - ${PROJECT_SOURCE_DIR}/renderer/RenderWorld_portals.cpp - ${PROJECT_SOURCE_DIR}/renderer/Shaders.cpp - ${PROJECT_SOURCE_DIR}/renderer/rvGLSLShader.cpp - ${PROJECT_SOURCE_DIR}/renderer/rvIndexBuffer.cpp - ${PROJECT_SOURCE_DIR}/renderer/rvMesh.cpp - ${PROJECT_SOURCE_DIR}/renderer/rvPrimBatch.cpp - ${PROJECT_SOURCE_DIR}/renderer/rvRenderModelMD5R.cpp - ${PROJECT_SOURCE_DIR}/renderer/rvSpecial.cpp - ${PROJECT_SOURCE_DIR}/renderer/rvTexRenderTarget.cpp - ${PROJECT_SOURCE_DIR}/renderer/rvVertexBuffer.cpp - ${PROJECT_SOURCE_DIR}/renderer/rvVertexBufferCopy.cpp - ${PROJECT_SOURCE_DIR}/renderer/rvVertexFormat.cpp - ${PROJECT_SOURCE_DIR}/renderer/tr_backend.cpp - ${PROJECT_SOURCE_DIR}/renderer/tr_deform.cpp - ${PROJECT_SOURCE_DIR}/renderer/tr_font.cpp - ${PROJECT_SOURCE_DIR}/renderer/tr_guisurf.cpp - ${PROJECT_SOURCE_DIR}/renderer/tr_light.cpp - ${PROJECT_SOURCE_DIR}/renderer/tr_lightrun.cpp - ${PROJECT_SOURCE_DIR}/renderer/tr_main.cpp - ${PROJECT_SOURCE_DIR}/renderer/tr_orderIndexes.cpp - ${PROJECT_SOURCE_DIR}/renderer/tr_polytope.cpp - ${PROJECT_SOURCE_DIR}/renderer/tr_render.cpp - ${PROJECT_SOURCE_DIR}/renderer/tr_rendertools.cpp - ${PROJECT_SOURCE_DIR}/renderer/tr_shadowbounds.cpp - ${PROJECT_SOURCE_DIR}/renderer/tr_stencilshadow.cpp - ${PROJECT_SOURCE_DIR}/renderer/tr_subview.cpp - ${PROJECT_SOURCE_DIR}/renderer/tr_trace.cpp - ${PROJECT_SOURCE_DIR}/renderer/tr_trisurf.cpp - ${PROJECT_SOURCE_DIR}/renderer/tr_turboshadow.cpp - ${PROJECT_SOURCE_DIR}/renderer/VertexCache.cpp -) - -# Retail BSE object boundaries recovered from quake4.pdb. -set(Q4_BSE_SOURCES - ${PROJECT_SOURCE_DIR}/bse/BSE_Bounds.cpp - ${PROJECT_SOURCE_DIR}/bse/BSE_Effect.cpp - ${PROJECT_SOURCE_DIR}/bse/BSE_EffectTemplate.cpp - ${PROJECT_SOURCE_DIR}/bse/BSE_Electricity.cpp - ${PROJECT_SOURCE_DIR}/bse/BSE_Envelopes.cpp - ${PROJECT_SOURCE_DIR}/bse/BSE_Light.cpp - ${PROJECT_SOURCE_DIR}/bse/BSE_Manager.cpp - ${PROJECT_SOURCE_DIR}/bse/BSE_ParseParticle2.cpp - ${PROJECT_SOURCE_DIR}/bse/BSE_Particle.cpp - ${PROJECT_SOURCE_DIR}/bse/BSE_Render.cpp - ${PROJECT_SOURCE_DIR}/bse/BSE_Segment.cpp - ${PROJECT_SOURCE_DIR}/bse/BSE_SegmentTemplate.cpp - ${PROJECT_SOURCE_DIR}/bse/BSE_SpawnDomains.cpp -) -file(GLOB Q4_JPEG_SOURCES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/renderer/jpeg-6/*.c) - -file(GLOB_RECURSE Q4_SOUND_SOURCES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/sound/*.cpp) -file(GLOB_RECURSE Q4_OGGVORBIS_SOURCES CONFIGURE_DEPENDS - ${PROJECT_SOURCE_DIR}/sound/OggVorbis/*.c -) - -file(GLOB_RECURSE Q4_SYS_SOURCES CONFIGURE_DEPENDS - ${PROJECT_SOURCE_DIR}/sys/*.cpp -) - -q4_add_engine_object(q4_engine_aas_file ${Q4_AAS_FILE_SOURCE}) -q4_add_engine_object(q4_engine_aas ${Q4_AAS_SOURCES}) -target_compile_definitions(q4_engine_aas PRIVATE Q4_AAS_LEGACY_SEED) -q4_add_engine_object(q4_engine_cm_model ${Q4_CM_MODEL_SOURCE}) -target_compile_definitions(q4_engine_cm_model PRIVATE Q4_CM_MODEL_ONLY) -q4_add_engine_object(q4_engine_cm_load ${Q4_CM_LOAD_SOURCE}) -q4_add_engine_object(q4_engine_cm_contacts ${Q4_CM_CONTACTS_SOURCE}) -q4_add_engine_object(q4_engine_cm_contents ${Q4_CM_CONTENTS_SOURCE}) -q4_add_engine_object(q4_engine_cm_trace ${Q4_CM_TRACE_SOURCE}) -q4_add_engine_object(q4_engine_cm_translate ${Q4_CM_TRANSLATE_SOURCE}) -q4_add_engine_object(q4_engine_cm_seed ${Q4_CM_SOURCES}) -target_compile_definitions(q4_engine_cm_seed PRIVATE Q4_CM_LEGACY_SEED) -set(Q4_FRAMEWORK_CORE_TARGETS) -foreach(source IN LISTS Q4_FRAMEWORK_CORE_SOURCES) - get_filename_component(stem ${source} NAME_WE) - set(target q4_engine_framework_core_${stem}) - q4_add_engine_object(${target} ${source}) - target_compile_definitions(${target} PRIVATE Q4_RECON_ENGINE_PRIVATE Q4_RECON_SDK_MSGQUEUE) - list(APPEND Q4_FRAMEWORK_CORE_TARGETS ${target}) -endforeach() -add_custom_target(q4_engine_framework_core DEPENDS ${Q4_FRAMEWORK_CORE_TARGETS}) -set_target_properties(q4_engine_framework_core PROPERTIES FOLDER "Engine/Seed") -q4_add_engine_object(q4_engine_framework_editfield ${Q4_FRAMEWORK_EDITFIELD_SOURCE}) -q4_add_engine_object(q4_engine_framework_console_impl ${Q4_FRAMEWORK_CONSOLE_SOURCE}) -q4_add_engine_object(q4_engine_framework_usercmd ${Q4_FRAMEWORK_USERCMD_SOURCE}) -add_custom_target(q4_engine_framework_console - DEPENDS - q4_engine_framework_editfield - q4_engine_framework_console_impl - q4_engine_framework_usercmd -) -set_target_properties(q4_engine_framework_console PROPERTIES FOLDER "Engine/Seed") -set(Q4_FRAMEWORK_ASYNC_TARGETS) -foreach(source IN LISTS Q4_FRAMEWORK_ASYNC_SOURCES) - get_filename_component(stem ${source} NAME_WE) - set(target q4_engine_framework_async_${stem}) - q4_add_engine_object(${target} ${source}) - target_compile_definitions(${target} PRIVATE Q4_RECON_ENGINE_PRIVATE Q4_RECON_SDK_MSGQUEUE) - list(APPEND Q4_FRAMEWORK_ASYNC_TARGETS ${target}) -endforeach() -add_custom_target(q4_engine_framework_async DEPENDS ${Q4_FRAMEWORK_ASYNC_TARGETS}) -set_target_properties(q4_engine_framework_async PROPERTIES FOLDER "Engine/Seed") -q4_add_engine_object(q4_engine_framework_msgchannel ${Q4_FRAMEWORK_MSGCHANNEL_SOURCE}) -target_compile_definitions(q4_engine_framework_msgchannel PRIVATE Q4_RECON_SDK_MSGQUEUE) -q4_add_engine_object(q4_engine_framework_session ${Q4_FRAMEWORK_SESSION_SOURCES}) -foreach(target q4_engine_framework_editfield q4_engine_framework_console_impl q4_engine_framework_usercmd q4_engine_framework_session) - target_compile_definitions(${target} PRIVATE Q4_RECON_ENGINE_PRIVATE Q4_RECON_SDK_MSGQUEUE) -endforeach() -set(Q4_UI_TARGETS) -foreach(source IN LISTS Q4_UI_SOURCES) - get_filename_component(stem ${source} NAME_WE) - set(target q4_engine_ui_${stem}) - q4_add_engine_object(${target} ${source}) - target_compile_definitions(${target} PRIVATE Q4_RECON_ENGINE_PRIVATE Q4_RECON_SDK_MSGQUEUE) - list(APPEND Q4_UI_TARGETS ${target}) -endforeach() -add_custom_target(q4_engine_ui DEPENDS ${Q4_UI_TARGETS}) -set_target_properties(q4_engine_ui PROPERTIES FOLDER "Engine/Seed") - -set(Q4_RENDERER_TARGETS) -foreach(source IN LISTS Q4_RENDERER_SOURCES) - get_filename_component(stem ${source} NAME_WE) - set(target q4_engine_renderer_${stem}) - q4_add_engine_object(${target} ${source}) - target_compile_definitions(${target} PRIVATE Q4_RECON_ENGINE_PRIVATE Q4_RECON_SDK_MSGQUEUE) - list(APPEND Q4_RENDERER_TARGETS ${target}) -endforeach() -add_custom_target(q4_engine_renderer DEPENDS ${Q4_RENDERER_TARGETS}) -set_target_properties(q4_engine_renderer PROPERTIES FOLDER "Engine/Seed") -set(Q4_BSE_TARGETS) -foreach(source IN LISTS Q4_BSE_SOURCES) - get_filename_component(stem ${source} NAME_WE) - string(TOLOWER ${stem} stem_lower) - set(target q4_engine_bse_${stem_lower}) - q4_add_engine_object(${target} ${source}) - target_compile_definitions(${target} PRIVATE Q4_RECON_ENGINE_PRIVATE Q4_RECON_SDK_MSGQUEUE) - list(APPEND Q4_BSE_TARGETS ${target}) -endforeach() -add_custom_target(q4_engine_bse DEPENDS ${Q4_BSE_TARGETS}) -set_target_properties(q4_engine_bse PROPERTIES FOLDER "Engine/Seed") -q4_add_engine_object(q4_bse_abi ${PROJECT_SOURCE_DIR}/tests/abi/BSE_ABI.cpp) -target_compile_definitions(q4_bse_abi PRIVATE Q4_RECON_ENGINE_PRIVATE Q4_RECON_SDK_MSGQUEUE) -set_target_properties(q4_bse_abi PROPERTIES FOLDER "Reconstruction/ABI") -q4_add_engine_object(q4_renderer_raven_abi ${PROJECT_SOURCE_DIR}/tests/abi/RendererRaven_ABI.cpp) -target_compile_definitions(q4_renderer_raven_abi PRIVATE Q4_RECON_ENGINE_PRIVATE Q4_RECON_SDK_MSGQUEUE) -set_target_properties(q4_renderer_raven_abi PROPERTIES FOLDER "Reconstruction/ABI") -q4_add_engine_object(q4_thirdparty_jpeg ${Q4_JPEG_SOURCES}) -target_include_directories(q4_thirdparty_jpeg PRIVATE ${PROJECT_SOURCE_DIR}/renderer/jpeg-6) -set_target_properties(q4_thirdparty_jpeg PROPERTIES FOLDER "ThirdParty") -set(Q4_SOUND_TARGETS) -foreach(source IN LISTS Q4_SOUND_SOURCES) - get_filename_component(stem ${source} NAME_WE) - set(target q4_engine_sound_${stem}) - q4_add_engine_object(${target} ${source}) - target_include_directories(${target} PRIVATE ${PROJECT_SOURCE_DIR}/sound ${PROJECT_SOURCE_DIR}/sys/win32) - target_compile_definitions(${target} PRIVATE Q4_RECON_ENGINE_PRIVATE Q4_RECON_SDK_MSGQUEUE Q4_RECONSTRUCTED_SOUND) - list(APPEND Q4_SOUND_TARGETS ${target}) -endforeach() -add_custom_target(q4_engine_sound DEPENDS ${Q4_SOUND_TARGETS}) -set_target_properties(q4_engine_sound PROPERTIES FOLDER "Engine/Seed") -q4_add_engine_object(q4_thirdparty_oggvorbis ${Q4_OGGVORBIS_SOURCES}) -target_include_directories(q4_thirdparty_oggvorbis PRIVATE - ${PROJECT_SOURCE_DIR}/sound/OggVorbis - ${PROJECT_SOURCE_DIR}/sound/OggVorbis/ogg - ${PROJECT_SOURCE_DIR}/sound/OggVorbis/vorbis - ${PROJECT_SOURCE_DIR}/sound/OggVorbis/vorbissrc -) -set_target_properties(q4_thirdparty_oggvorbis PROPERTIES FOLDER "ThirdParty") -set(Q4_SYS_TARGETS) -foreach(source IN LISTS Q4_SYS_SOURCES) - get_filename_component(stem ${source} NAME_WE) - set(target q4_engine_sys_${stem}) - q4_add_engine_object(${target} ${source}) - target_compile_definitions(${target} PRIVATE Q4_RECON_ENGINE_PRIVATE Q4_RECON_SDK_MSGQUEUE) - list(APPEND Q4_SYS_TARGETS ${target}) -endforeach() -add_custom_target(q4_engine_sys DEPENDS ${Q4_SYS_TARGETS}) -set_target_properties(q4_engine_sys PROPERTIES FOLDER "Engine/Seed") - -add_custom_target(q4_engine_seed - DEPENDS - q4_engine_aas - q4_engine_aas_file - q4_engine_cm_model - q4_engine_cm_load - q4_engine_cm_contacts - q4_engine_cm_contents - q4_engine_cm_trace - q4_engine_cm_translate - q4_engine_cm_seed - q4_engine_framework_core - q4_engine_framework_console - q4_engine_framework_async - q4_engine_framework_msgchannel - q4_engine_framework_session - q4_engine_ui - q4_engine_renderer - q4_engine_bse - q4_thirdparty_jpeg - q4_engine_sound - q4_thirdparty_oggvorbis - q4_engine_sys -) -set_target_properties(q4_engine_seed PROPERTIES FOLDER "Engine") - -add_custom_target(q4_reconstruction_foundation - DEPENDS - q4_idlib - q4_engine_aas - q4_engine_aas_file - q4_engine_cm_model - q4_engine_cm_load - q4_engine_cm_contacts - q4_engine_cm_contents - q4_engine_cm_trace - q4_engine_cm_translate - q4_engine_cm_seed - q4_engine_framework_console - q4_engine_framework_core_cmdsystem - q4_engine_framework_core_compressor - q4_engine_framework_core_cvarsystem - q4_engine_framework_core_declaf - q4_engine_framework_core_declentitydef - q4_engine_framework_core_declmanager - q4_engine_framework_core_declpda - q4_engine_framework_core_declskin - q4_engine_framework_core_decltable - q4_engine_framework_core_demofile - q4_engine_framework_core_eventloop - q4_engine_framework_core_file - q4_engine_framework_core_filesystem - q4_engine_framework_core_keyinput - q4_engine_framework_core_unzip - q4_engine_framework_async_asyncnetwork - q4_engine_framework_async_networksystem - q4_engine_framework_msgchannel - q4_engine_ui - q4_thirdparty_jpeg - q4_thirdparty_oggvorbis -) -set_target_properties(q4_reconstruction_foundation PROPERTIES FOLDER "Reconstruction") - -# Full reconstructed Win32 engine integration target. The per-compiland -# object targets above remain the source-ownership/ABI audit surface; this -# target compiles the same recovered sources into a runnable executable. -# The editor and compilers live in the external q4_tools target and cross the -# reconstructed retail Tools API rather than being compiled into the engine. -# CURL remains disabled at this reconstruction frontier. -set(Q4_OPENAL_LOADER_SOURCES - ${PROJECT_SOURCE_DIR}/openal/idal.cpp -) - -add_executable(q4xp WIN32 - ${PROJECT_SOURCE_DIR}/sys/win32/rc/Quake4.rc - ${Q4_AAS_SOURCES} - ${Q4_CM_MODEL_SOURCE} - ${Q4_CM_SOURCES} - ${Q4_FRAMEWORK_ENGINE_TOP_SOURCES} - ${Q4_FRAMEWORK_ASYNC_SOURCES} - ${Q4_FRAMEWORK_MSGCHANNEL_SOURCE} - ${Q4_UI_SOURCES} - ${Q4_RENDERER_SOURCES} - ${Q4_BSE_SOURCES} - ${Q4_JPEG_SOURCES} - ${Q4_SOUND_SOURCES} - ${Q4_OGGVORBIS_SOURCES} - ${Q4_OPENAL_LOADER_SOURCES} - ${Q4_SYS_SOURCES} -) - -target_include_directories(q4xp PRIVATE - ${PROJECT_SOURCE_DIR} - ${PROJECT_SOURCE_DIR}/renderer/jpeg-6 - ${PROJECT_SOURCE_DIR}/sound - ${PROJECT_SOURCE_DIR}/sys/win32 - ${PROJECT_SOURCE_DIR}/sound/OggVorbis - ${PROJECT_SOURCE_DIR}/sound/OggVorbis/ogg - ${PROJECT_SOURCE_DIR}/sound/OggVorbis/ogg/include - ${PROJECT_SOURCE_DIR}/sound/OggVorbis/vorbis - ${PROJECT_SOURCE_DIR}/sound/OggVorbis/vorbis/include - ${PROJECT_SOURCE_DIR}/sound/OggVorbis/vorbissrc - "C:/Program Files (x86)/Microsoft DirectX SDK (June 2010)/Include" -) - -target_compile_definitions(q4xp PRIVATE - WIN32 - _WINDOWS - _LOAD_DLL - __DOOM_DLL__ - _USE_32BIT_TIME_T - Q4_NO_PUNKBUSTER - Q4_RECON_SEED - Q4_RECON_ENGINE_PRIVATE - Q4_RECON_SDK_MSGQUEUE - Q4_DISABLE_TASKKEY_HOOK - Q4_DISABLE_TOOLS - Q4_DISABLE_GL_LOGGING - Q4_RECONSTRUCTED_SOUND - ID_ENABLE_CURL=0 - $<$:_DEBUG> - $<$:NDEBUG;_FINAL> -) - -target_link_directories(q4xp PRIVATE - "C:/Program Files (x86)/Microsoft DirectX SDK (June 2010)/Lib/x86" - ${PROJECT_SOURCE_DIR}/openal/lib -) - -target_link_libraries(q4xp PRIVATE - q4_idlib - opengl32 - winmm - ws2_32 - iphlpapi - dbghelp - comctl32 - dinput8 - dsound - dxguid - version -) - -if(MSVC) - # Keep an address map and symbols beside the reconstructed executable so - # retail-startup crashes can be resolved without enabling the tools DLL. - target_compile_options(q4xp PRIVATE /Zi) - target_link_options(q4xp PRIVATE - /DEBUG:FULL - /PDB:E:/projects/Quake4Alpha/q4xp.pdb - /MAP:E:/projects/Quake4Alpha/q4xp.map - /STACK:16777216,4096 - ) -endif() - -set_target_properties(q4xp PROPERTIES - OUTPUT_NAME q4xp - RUNTIME_OUTPUT_DIRECTORY "E:/projects/Quake4Alpha" - RUNTIME_OUTPUT_DIRECTORY_DEBUG "E:/projects/Quake4Alpha" - RUNTIME_OUTPUT_DIRECTORY_RELEASE "E:/projects/Quake4Alpha" - PDB_OUTPUT_DIRECTORY "E:/projects/Quake4Alpha" - FOLDER "Engine" -) diff --git a/src/game.vcproj b/src/game.vcproj new file mode 100644 index 0000000..677ec6b --- /dev/null +++ b/src/game.vcproj @@ -0,0 +1,1115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/game/CMakeLists.txt b/src/game/CMakeLists.txt deleted file mode 100644 index 9079a51..0000000 --- a/src/game/CMakeLists.txt +++ /dev/null @@ -1,56 +0,0 @@ -file(GLOB_RECURSE Q4_GAME_SOURCES CONFIGURE_DEPENDS - ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp -) - -# Callbacks.cpp is generated-style source text included by gamesys/Class.cpp; -# it is not a standalone translation unit in the Quake 4 SDK project. -list(REMOVE_ITEM Q4_GAME_SOURCES - ${CMAKE_CURRENT_SOURCE_DIR}/gamesys/Callbacks.cpp -) - -add_library(gamex86 SHARED - ${Q4_GAME_SOURCES} - ${PROJECT_SOURCE_DIR}/framework/DeclPlayerModel.cpp -) - -target_include_directories(gamex86 PRIVATE - ${PROJECT_SOURCE_DIR} - ${PROJECT_SOURCE_DIR}/game - "C:/Program Files (x86)/Microsoft DirectX SDK (June 2010)/Include" -) - -target_compile_definitions(gamex86 PRIVATE - WIN32 - _WINDOWS - _LOAD_DLL - _USE_32BIT_TIME_T - GAME_DLL - Q4SDK - Q4_RECON_RETAIL_UI_MANAGER_ABI - Q4_NO_PUNKBUSTER - $<$:_DEBUG> - $<$:NDEBUG;_FINAL> -) - -target_link_libraries(gamex86 PRIVATE q4_game_idlib winmm) - -if(MSVC) - target_compile_options(gamex86 PRIVATE /Zi) - target_link_options(gamex86 PRIVATE - /DEF:${CMAKE_CURRENT_SOURCE_DIR}/game.def - /DEBUG:FULL - /PDB:E:/projects/Quake4Alpha/q4base/gamex86.pdb - ) -endif() - -set_target_properties(gamex86 PROPERTIES - OUTPUT_NAME gamex86 - PREFIX "" - RUNTIME_OUTPUT_DIRECTORY "E:/projects/Quake4Alpha/q4base" - RUNTIME_OUTPUT_DIRECTORY_DEBUG "E:/projects/Quake4Alpha/q4base" - RUNTIME_OUTPUT_DIRECTORY_RELEASE "E:/projects/Quake4Alpha/q4base" - LIBRARY_OUTPUT_DIRECTORY "E:/projects/Quake4Alpha/q4base" - ARCHIVE_OUTPUT_DIRECTORY "E:/projects/Quake4Alpha/q4base" - PDB_OUTPUT_DIRECTORY "E:/projects/Quake4Alpha/q4base" - FOLDER "Game" -) diff --git a/src/idlib.vcproj b/src/idlib.vcproj new file mode 100644 index 0000000..aa1f584 --- /dev/null +++ b/src/idlib.vcproj @@ -0,0 +1,713 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/idlib/CMakeLists.txt b/src/idlib/CMakeLists.txt deleted file mode 100644 index 41e4345..0000000 --- a/src/idlib/CMakeLists.txt +++ /dev/null @@ -1,117 +0,0 @@ -# This is the exact first-party idlib compiland set recorded in quake4.pdb. -# Additional SDK files remain in the tree for the game DLLs but are not linked -# into the reconstructed executable until evidence assigns them to the target. -set(Q4_IDLIB_SOURCES - BitMsg.cpp - CmdArgs.cpp - Dict.cpp - Heap.cpp - LangDict.cpp - Lexer.cpp - LexerFactory.cpp - Lib.cpp - mapfile.cpp - Parser.cpp - Str.cpp - Timer.cpp - Token.cpp - bv/Bounds.cpp - bv/Box.cpp - bv/Frustum.cpp - bv/Sphere.cpp - containers/HashIndex.cpp - geometry/JointTransform.cpp - geometry/Surface.cpp - geometry/Surface_Patch.cpp - geometry/TraceModel.cpp - geometry/Winding.cpp - math/Angles.cpp - math/Math.cpp - math/Matrix.cpp - math/Plane.cpp - math/Polynomial.cpp - math/Quat.cpp - math/Radians.cpp - math/Rotation.cpp - math/Simd.cpp - math/Simd_3DNow.cpp - math/Simd_generic.cpp - math/Simd_MMX.cpp - math/Simd_SSE.cpp - math/Simd_SSE2.cpp - math/Simd_SSE3.cpp - math/Vector.cpp - hashing/CRC32.cpp - hashing/MD4.cpp - hashing/MD5.cpp -) - -add_library(q4_idlib STATIC ${Q4_IDLIB_SOURCES}) - -target_include_directories(q4_idlib - PUBLIC - ${PROJECT_SOURCE_DIR} - ${CMAKE_CURRENT_SOURCE_DIR} - PRIVATE - "C:/Program Files (x86)/Microsoft DirectX SDK (June 2010)/Include" -) - -target_compile_definitions(q4_idlib - PRIVATE - WIN32 - _WINDOWS - _LOAD_DLL - _USE_32BIT_TIME_T - Q4_NO_PUNKBUSTER - Q4_RECON_SEED - Q4_RECON_ENGINE_PRIVATE - Q4_RECON_SDK_MSGQUEUE - $<$:_DEBUG> - $<$:NDEBUG;_FINAL> -) - -target_precompile_headers(q4_idlib PRIVATE precompiled.h) - -set_target_properties(q4_idlib PROPERTIES - OUTPUT_NAME idlib - FOLDER "Engine" -) - -# The Quake 4 SDK builds idlib a second time with Q4SDK for gamex86.dll. -# Several public classes (notably the SIMD hierarchy) are macro-sensitive, so -# linking the engine-private idlib into the game DLL gives the DLL a different -# vtable layout than the one seen by its game translation units. -set(Q4_GAME_IDLIB_SOURCES - ${Q4_IDLIB_SOURCES} - geometry/Winding2D.cpp - math/Lcp.cpp - math/Ode.cpp -) - -add_library(q4_game_idlib STATIC ${Q4_GAME_IDLIB_SOURCES}) - -target_include_directories(q4_game_idlib - PUBLIC - ${PROJECT_SOURCE_DIR} - ${CMAKE_CURRENT_SOURCE_DIR} - PRIVATE - "C:/Program Files (x86)/Microsoft DirectX SDK (June 2010)/Include" -) - -target_compile_definitions(q4_game_idlib - PRIVATE - WIN32 - _WINDOWS - _USE_32BIT_TIME_T - Q4SDK - Q4_NO_PUNKBUSTER - $<$:_DEBUG> - $<$:NDEBUG;_FINAL> -) - -target_precompile_headers(q4_game_idlib PRIVATE precompiled.h) - -set_target_properties(q4_game_idlib PROPERTIES - OUTPUT_NAME game_idlib - FOLDER "Game" -) diff --git a/src/mpgame.vcproj b/src/mpgame.vcproj new file mode 100644 index 0000000..42d0beb --- /dev/null +++ b/src/mpgame.vcproj @@ -0,0 +1,1112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/q4sdk.sln b/src/q4sdk.sln new file mode 100644 index 0000000..2f4df2a --- /dev/null +++ b/src/q4sdk.sln @@ -0,0 +1,46 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Game", "game.vcproj", "{49BEC5C6-B964-417A-851E-808886B57430}" + ProjectSection(ProjectDependencies) = postProject + {49BEC5C6-B964-417A-851E-808886B57400} = {49BEC5C6-B964-417A-851E-808886B57400} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "idLib", "idlib.vcproj", "{49BEC5C6-B964-417A-851E-808886B57400}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MPGame", "mpgame.vcproj", "{F2EF9123-B7C3-4F2F-A351-747B595BB534}" + ProjectSection(ProjectDependencies) = postProject + {49BEC5C6-B964-417A-851E-808886B57400} = {49BEC5C6-B964-417A-851E-808886B57400} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + DebugInline = DebugInline + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {49BEC5C6-B964-417A-851E-808886B57430}.Debug.ActiveCfg = Debug|Win32 + {49BEC5C6-B964-417A-851E-808886B57430}.Debug.Build.0 = Debug|Win32 + {49BEC5C6-B964-417A-851E-808886B57430}.DebugInline.ActiveCfg = DebugInline|Win32 + {49BEC5C6-B964-417A-851E-808886B57430}.DebugInline.Build.0 = DebugInline|Win32 + {49BEC5C6-B964-417A-851E-808886B57430}.Release.ActiveCfg = Release|Win32 + {49BEC5C6-B964-417A-851E-808886B57430}.Release.Build.0 = Release|Win32 + {49BEC5C6-B964-417A-851E-808886B57400}.Debug.ActiveCfg = Debug|Win32 + {49BEC5C6-B964-417A-851E-808886B57400}.Debug.Build.0 = Debug|Win32 + {49BEC5C6-B964-417A-851E-808886B57400}.DebugInline.ActiveCfg = DebugInline|Win32 + {49BEC5C6-B964-417A-851E-808886B57400}.DebugInline.Build.0 = DebugInline|Win32 + {49BEC5C6-B964-417A-851E-808886B57400}.Release.ActiveCfg = Release|Win32 + {49BEC5C6-B964-417A-851E-808886B57400}.Release.Build.0 = Release|Win32 + {F2EF9123-B7C3-4F2F-A351-747B595BB534}.Debug.ActiveCfg = Debug|Win32 + {F2EF9123-B7C3-4F2F-A351-747B595BB534}.Debug.Build.0 = Debug|Win32 + {F2EF9123-B7C3-4F2F-A351-747B595BB534}.DebugInline.ActiveCfg = DebugInline|Win32 + {F2EF9123-B7C3-4F2F-A351-747B595BB534}.DebugInline.Build.0 = DebugInline|Win32 + {F2EF9123-B7C3-4F2F-A351-747B595BB534}.Release.ActiveCfg = Release|Win32 + {F2EF9123-B7C3-4F2F-A351-747B595BB534}.Release.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/src/sys/win32/rc/Common.rc b/src/sys/win32/rc/Common.rc new file mode 100644 index 0000000..235d7f9 --- /dev/null +++ b/src/sys/win32/rc/Common.rc @@ -0,0 +1,170 @@ +// Microsoft Visual C++ generated resource script. +// +#include "common_resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.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 +BEGIN + "common_resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_TOOLS_OPEN DIALOGEX 0, 0, 308, 217 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | + WS_CAPTION | WS_SYSMENU +CAPTION "Open" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "File &name:",IDC_STATIC,13,181,34,8 + EDITTEXT IDC_TOOLS_FILENAME,51,179,187,13,ES_AUTOHSCROLL + LTEXT "Look In:",IDC_STATIC,7,9,27,8 + CONTROL "",IDC_TOOLS_LOOKIN,"ComboBoxEx32",CBS_DROPDOWNLIST | + CBS_SORT | WS_VSCROLL | WS_TABSTOP,35,7,195,118 + PUSHBUTTON "",IDC_TOOLS_BACK,241,7,14,13,BS_BITMAP + CONTROL "",IDC_TOOLS_FILELIST,"SysListView32",LVS_LIST | + LVS_SINGLESEL | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7, + 24,294,149 + DEFPUSHBUTTON "&Open",IDOK,251,179,50,14 + PUSHBUTTON "Cancel",IDCANCEL,251,197,50,14 +END + +IDD_RENDERBUMPFLAT DIALOGEX 0, 0, 186, 73 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "Render Bump Flat" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + EDITTEXT IDC_RBF_WIDTH,36,6,40,14,ES_AUTOHSCROLL | ES_NUMBER + EDITTEXT IDC_RBF_HEIGHT,36,30,40,14,ES_AUTOHSCROLL | ES_NUMBER + DEFPUSHBUTTON "OK",IDOK,129,7,50,14 + PUSHBUTTON "Cancel",IDCANCEL,129,24,50,14 + LTEXT "Width:",IDC_STATIC,6,6,22,8 + LTEXT "Height:",IDC_STATIC,6,30,24,8 + LTEXT "File:",IDC_STATIC,6,54,14,8 + LTEXT "",IDC_RBF_FILENAME,24,54,156,8 +END + +IDD_NEWNAME DIALOG 0, 0, 186, 46 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "New" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_TOOLS_EDITNAME,31,10,106,12,ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,143,7,36,14 + PUSHBUTTON "Cancel",IDCANCEL,143,24,36,14 + LTEXT "Name:",IDC_STATIC,7,12,22,8 +END + +IDD_DIALOG_GOTOLINE DIALOGEX 0, 0, 166, 54 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "Go To Line" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,55,33,50,14 + PUSHBUTTON "Cancel",IDCANCEL,108,33,50,14 + LTEXT "&Line number (0 - 0):",IDC_GOTOLINE_STATIC,7,4,152,8 + EDITTEXT IDC_GOTOLINE_EDIT,7,15,152,14,ES_AUTOHSCROLL | ES_NUMBER +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_TOOLS_OPEN BITMAP "res\\dbg_open.bmp" +IDB_TOOLS_BACK BITMAP "res\\dbg_back.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_RENDERBUMPFLAT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 66 + END + + IDD_NEWNAME, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 39 + END + + IDD_DIALOG_GOTOLINE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 159 + TOPMARGIN, 3 + BOTTOMMARGIN, 47 + END +END +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/src/sys/win32/rc/PropTree.rc b/src/sys/win32/rc/PropTree.rc new file mode 100644 index 0000000..ff68313 --- /dev/null +++ b/src/sys/win32/rc/PropTree.rc @@ -0,0 +1,133 @@ +// Microsoft Visual C++ generated resource script. +// +#include "proptree_resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.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 +BEGIN + "proptree_resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "#define _AFX_NO_SPLITTER_RESOURCES\r\n" + "#define _AFX_NO_OLE_RESOURCES\r\n" + "#define _AFX_NO_TRACKER_RESOURCES\r\n" + "#define _AFX_NO_PROPERTY_RESOURCES\r\n" + "\r\n" + "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n" + "#ifdef _WIN32\r\n" + "LANGUAGE 9, 1\r\n" + "#pragma code_page(1252)\r\n" + "#endif //_WIN32\r\n" + "#include ""res\\PropTree.rc2"" // non-Microsoft Visual C++ edited resources\r\n" + "#include ""afxres.rc"" // Standard components\r\n" + "#endif\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Cursor +// + +IDC_FPOINT CURSOR "res\\fpoint.cur" +IDC_SPLITTER CURSOR "res\\spliter.cur" + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_ME_EDIT_MENU MENU +BEGIN + POPUP "EditMenu" + BEGIN + MENUITEM "Insert File", ID_EDITMENU_INSERTFILE + MENUITEM SEPARATOR + MENUITEM "Undo", 57643 + MENUITEM SEPARATOR + MENUITEM "Cut", 57635 + MENUITEM "Copy", 57634 + MENUITEM "Paste", 57637 + MENUITEM "Delete", ID_EDIT_DELETE + MENUITEM SEPARATOR + MENUITEM "Select All", ID_EDIT_SELECTALL + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_TRUE "True" + IDS_FALSE "False" + IDS_NOITEMSEL "No Item Selected" + IDS_SELFORINFO "Select an item to see its description" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#define _AFX_NO_SPLITTER_RESOURCES +#define _AFX_NO_OLE_RESOURCES +#define _AFX_NO_TRACKER_RESOURCES +#define _AFX_NO_PROPERTY_RESOURCES + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE 9, 1 +#pragma code_page(1252) +#endif //_WIN32 +#include "res\PropTree.rc2" // non-Microsoft Visual C++ edited resources +// MFC standard resources are already linked by the DLL target. Including +// afxres.rc here duplicates its string-table blocks when this resource script +// is combined with Radiant.rc. +#endif + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/src/sys/win32/rc/Quake4.rc b/src/sys/win32/rc/Quake4.rc new file mode 100644 index 0000000..c770b44 --- /dev/null +++ b/src/sys/win32/rc/Quake4.rc @@ -0,0 +1,6 @@ +#include "doom_resource.h" + +// Visual resources recovered from the matching retail quake4.exe. Keep the +// original numeric identifiers because the Win32 code loads them directly. +IDI_ICON1 ICON "retail\\quake4\\icons\\1024_1033.ico" +IDB_BITMAP_LOGO BITMAP "retail\\quake4\\bitmaps\\4001_1033.bmp" diff --git a/src/sys/win32/rc/Radiant.rc b/src/sys/win32/rc/Radiant.rc new file mode 100644 index 0000000..ff6aaf6 --- /dev/null +++ b/src/sys/win32/rc/Radiant.rc @@ -0,0 +1,2824 @@ +// Microsoft Visual C++ generated resource script. +// +#include "Radiant_resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.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 +BEGIN + "Radiant_resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDR_MAINFRAME ICON "retail\\Toolsx86\\icons\\7052_1033.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDR_SHADERFRAME BITMAP "res\\Toolbar.bmp" +IDR_TOOLBAR1 BITMAP "res\\toolbar1.bmp" +IDB_VIEWDEFAULT BITMAP "res\\bitmap2.bmp" +IDB_VIEWQE4 BITMAP "res\\viewdefa.bmp" +IDB_VIEW4WAY BITMAP "res\\viewoppo.bmp" +IDB_VIEWDEFAULT_Z BITMAP "res\\bmp00001.bmp" +IDR_TOOLBAR_SCALELOCK BITMAP "res\\toolbar2.bmp" +IDR_TOOLBAR_ADVANCED BITMAP "res\\bmp00002.bmp" +IDB_IENDCAP BITMAP "res\\iendcap.bmp" +IDB_ENDCAP BITMAP "res\\endcap.bmp" +IDB_BEVEL BITMAP "res\\bevel.bmp" +IDB_IBEVEL BITMAP "res\\ibevel.bmp" +IDB_BITMAP_GROUPS BITMAP "res\\bmp00003.bmp" +IDB_BITMAP_MATERIAL BITMAP "res\\bmp00004.bmp" +IDB_BITMAP_HSB BITMAP "res\\cchsb.bmp" +IDB_BITMAP_RGB BITMAP "res\\ccrgb.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// Toolbar +// + +IDR_SHADERFRAME TOOLBAR 16, 15 +BEGIN + BUTTON ID_FILE_OPEN + BUTTON ID_FILE_SAVE + SEPARATOR + BUTTON ID_EDIT_CUT + BUTTON ID_EDIT_COPY + BUTTON ID_EDIT_PASTE +END + +IDR_TOOLBAR1 TOOLBAR 16, 15 +BEGIN + BUTTON ID_FILE_OPEN + BUTTON ID_FILE_SAVE + SEPARATOR + BUTTON ID_BRUSH_FLIPX + BUTTON ID_BRUSH_ROTATEX + BUTTON ID_BRUSH_FLIPY + BUTTON ID_BRUSH_ROTATEY + BUTTON ID_BRUSH_FLIPZ + BUTTON ID_BRUSH_ROTATEZ + SEPARATOR + BUTTON ID_POPUP_SELECTION + SEPARATOR + BUTTON ID_SELECTION_CSGSUBTRACT + BUTTON ID_SELECTION_MAKEHOLLOW + SEPARATOR + BUTTON ID_VIEW_CHANGE + SEPARATOR + BUTTON ID_TEXTURES_POPUP + SEPARATOR + BUTTON ID_VIEW_CAMERATOGGLE + BUTTON ID_VIEW_CAMERAUPDATE + BUTTON ID_VIEW_CUBICCLIPPING + SEPARATOR + BUTTON ID_VIEW_ENTITY + SEPARATOR + BUTTON ID_VIEW_CLIPPER + SEPARATOR + BUTTON ID_SELECT_MOUSEROTATE + SEPARATOR + BUTTON ID_SELECT_COMPLETE_ENTITY + BUTTON ID_SCALELOCKX + BUTTON ID_SCALELOCKY + BUTTON ID_SCALELOCKZ +END + +IDR_TOOLBAR_SCALELOCK TOOLBAR 16, 15 +BEGIN + BUTTON ID_SCALELOCKX + BUTTON ID_SCALELOCKY + BUTTON ID_SCALELOCKZ +END + +IDR_TOOLBAR_ADVANCED TOOLBAR 16, 17 +BEGIN + BUTTON ID_FILE_OPEN + BUTTON ID_FILE_SAVE + SEPARATOR + BUTTON ID_BRUSH_FLIPX + BUTTON ID_BRUSH_ROTATEX + BUTTON ID_BRUSH_FLIPY + BUTTON ID_BRUSH_ROTATEY + BUTTON ID_BRUSH_FLIPZ + BUTTON ID_BRUSH_ROTATEZ + SEPARATOR + BUTTON ID_SELECTION_SELECTCOMPLETETALL + BUTTON ID_SELECTION_SELECTTOUCHING + BUTTON ID_SELECTION_SELECTPARTIALTALL + BUTTON ID_SELECTION_SELECTINSIDE + SEPARATOR + BUTTON ID_SELECTION_CSGSUBTRACT + BUTTON ID_SELECTION_CSGMERGE + BUTTON ID_SELECTION_MAKEHOLLOW + BUTTON ID_VIEW_CLIPPER + SEPARATOR + BUTTON ID_VIEW_CHANGE + BUTTON ID_TEXTURES_POPUP + BUTTON ID_VIEW_CUBICCLIPPING + SEPARATOR + BUTTON ID_SELECT_MOUSEROTATE + SEPARATOR + BUTTON ID_SELECT_MOUSESCALE + SEPARATOR + BUTTON ID_SCALELOCKX + BUTTON ID_SCALELOCKY + BUTTON ID_SCALELOCKZ + SEPARATOR + BUTTON ID_SELECTION_MOVEONLY + SEPARATOR + BUTTON ID_SELECT_BRUSHESONLY + BUTTON ID_SELECT_NOMODELS + BUTTON ID_SELECT_BYBOUNDINGBRUSH + BUTTON ID_PATCH_SHOWBOUNDINGBOX + SEPARATOR + BUTTON ID_SHOW_ENTITIES + SEPARATOR + BUTTON ID_PATCH_WIREFRAME + BUTTON ID_PATCH_BEND + BUTTON ID_PATCH_INSDEL + BUTTON ID_CURVE_CAP + BUTTON ID_PATCH_WELD + BUTTON ID_PATCH_DRILLDOWN + SEPARATOR + BUTTON ID_SHOW_LIGHTVOLUMES + BUTTON ID_SHOW_LIGHTTEXTURES + SEPARATOR + BUTTON ID_SPLINES_POPUP + BUTTON ID_SPLINES_EDITPOINTS + BUTTON ID_SPLINES_ADDPOINTS + BUTTON ID_SPLINES_INSERTPOINTS + BUTTON ID_SPLINES_DELETEPOINTS + SEPARATOR + BUTTON ID_SOUND_SHOWSOUNDVOLUMES + BUTTON ID_SOUND_SHOWSELECTEDSOUNDVOLUMES + SEPARATOR + BUTTON ID_SHOW_DOOM +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_SHADERFRAME MENU +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&New\tCtrl+N", ID_FILE_NEW + MENUITEM "&Open...\tCtrl+O", ID_FILE_OPEN + MENUITEM "&Save\tCtrl+S", ID_FILE_SAVE + MENUITEM "Save &As...", ID_FILE_SAVE_AS + MENUITEM SEPARATOR + MENUITEM "Close", ID_FILE_CLOSE + END + POPUP "&Edit" + BEGIN + MENUITEM "&Undo\tCtrl+Z", ID_EDIT_UNDO + MENUITEM SEPARATOR + MENUITEM "Cu&t\tCtrl+X", ID_EDIT_CUT + MENUITEM "&Copy\tCtrl+C", ID_EDIT_COPY + MENUITEM "&Paste\tCtrl+V", ID_EDIT_PASTE + MENUITEM "Delete\tDel", ID_EDIT_CLEAR + MENUITEM SEPARATOR + MENUITEM "&Find...", ID_EDIT_FIND + MENUITEM "Find &Next\tF3", ID_EDIT_REPEAT + MENUITEM "&Replace...", ID_EDIT_REPLACE + MENUITEM SEPARATOR + MENUITEM "Select &All", ID_EDIT_SELECT_ALL + END +END + +IDR_MAINFRAME MENU +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&New\tCtrl+N", ID_FILE_NEW + MENUITEM "&Open...\tCtrl+O", ID_FILE_OPEN + MENUITEM SEPARATOR + MENUITEM "P&rint Setup...", ID_FILE_PRINT_SETUP + MENUITEM SEPARATOR + MENUITEM "Recent File", ID_FILE_MRU_FILE1, GRAYED + MENUITEM SEPARATOR + MENUITEM "E&xit", ID_APP_EXIT + END + POPUP "&View" + BEGIN + MENUITEM "&Toolbar", ID_VIEW_TOOLBAR + MENUITEM "&Status Bar", ID_VIEW_STATUS_BAR + END + POPUP "&Help" + BEGIN + MENUITEM "&About Radiant...", ID_APP_ABOUT + END +END + +IDR_RADIANTYPE MENU +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&New\tCtrl+N", ID_FILE_NEW + MENUITEM "&Open...\tCtrl+O", ID_FILE_OPEN + MENUITEM "&Close", ID_FILE_CLOSE + MENUITEM "&Save\tCtrl+S", ID_FILE_SAVE + MENUITEM "Save &As...", ID_FILE_SAVE_AS + MENUITEM SEPARATOR + MENUITEM "&Print...\tCtrl+P", ID_FILE_PRINT + MENUITEM "Print Pre&view", ID_FILE_PRINT_PREVIEW + MENUITEM "P&rint Setup...", ID_FILE_PRINT_SETUP + MENUITEM SEPARATOR + MENUITEM "Recent File", ID_FILE_MRU_FILE1, GRAYED + MENUITEM SEPARATOR + MENUITEM "E&xit", ID_APP_EXIT + END + POPUP "&Edit" + BEGIN + MENUITEM "&Undo\tCtrl+Z", ID_EDIT_UNDO + MENUITEM SEPARATOR + MENUITEM "Cu&t\tCtrl+X", ID_EDIT_CUT + MENUITEM "&Copy\tCtrl+C", ID_EDIT_COPY + MENUITEM "&Paste\tCtrl+V", ID_EDIT_PASTE + END + POPUP "&View" + BEGIN + MENUITEM "&Toolbar", ID_VIEW_TOOLBAR + MENUITEM "&Status Bar", ID_VIEW_STATUS_BAR + END + POPUP "&Window" + BEGIN + MENUITEM "&New Window", ID_WINDOW_NEW + MENUITEM "&Cascade", ID_WINDOW_CASCADE + MENUITEM "&Tile", ID_WINDOW_TILE_HORZ + MENUITEM "&Arrange Icons", ID_WINDOW_ARRANGE + END + POPUP "&Help" + BEGIN + MENUITEM "&About Radiant...", ID_APP_ABOUT + END +END + +IDR_MENU_QUAKE3 MENU +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&New map", ID_FILE_NEW + MENUITEM SEPARATOR + MENUITEM "&Open...", ID_FILE_OPEN + MENUITEM "&Load...", ID_FILE_IMPORTMAP + MENUITEM "&Save", ID_FILE_SAVE + MENUITEM "Save &as...", ID_FILE_SAVEAS + MENUITEM "Save s&elected...", ID_FILE_EXPORTMAP + MENUITEM SEPARATOR + MENUITEM SEPARATOR + MENUITEM "Save re&gion...", ID_FILE_SAVEREGION + MENUITEM SEPARATOR + MENUITEM "New p&roject...", ID_FILE_NEWPROJECT, GRAYED + MENUITEM "Load &project...", ID_FILE_LOADPROJECT + MENUITEM "Pro&ject settings...", ID_FILE_PROJECTSETTINGS + MENUITEM SEPARATOR + MENUITEM "&Pointfile...", ID_FILE_POINTFILE + MENUITEM SEPARATOR + MENUITEM "E&xit", ID_FILE_EXIT + END + POPUP "&Edit" + BEGIN + MENUITEM "&Undo\tCtrl-Z", ID_EDIT_UNDO + MENUITEM "&Redo\tShift-Ctrl-Z", ID_EDIT_REDO + MENUITEM SEPARATOR + MENUITEM "&Copy\tCtrl-C", ID_EDIT_COPYBRUSH + MENUITEM "&Paste\tCtrl-V", ID_EDIT_PASTEBRUSH + MENUITEM "&Delete\tBackspace", ID_SELECTION_DELETE + MENUITEM SEPARATOR + MENUITEM "Map Info...", ID_EDIT_MAPINFO + MENUITEM "Entity Info...", ID_EDIT_ENTITYINFO + MENUITEM SEPARATOR + MENUITEM "Brush Scripts...", ID_BRUSH_SCRIPTS + MENUITEM SEPARATOR + MENUITEM "Load Pre&fab...", ID_EDIT_LOADPREFAB + MENUITEM "Save Selection as Prefab...", ID_EDIT_SAVEPREFAB + MENUITEM SEPARATOR + MENUITEM "Preferences...", ID_PREFS + END + POPUP "&View" + BEGIN + POPUP "Toggle" + BEGIN + MENUITEM "Camera View", ID_TOGGLECAMERA + MENUITEM "Console View", ID_TOGGLECONSOLE + MENUITEM "Entity View", ID_VIEW_ENTITY + MENUITEM "Groups View", ID_VIEW_GROUPS + MENUITEM "XY (Top)", ID_TOGGLEVIEW + MENUITEM "YZ (Side)", ID_TOGGLEVIEW_YZ + MENUITEM "XZ (Front)", ID_TOGGLEVIEW_XZ + MENUITEM "Z View", ID_TOGGLEZ + END + MENUITEM SEPARATOR + MENUITEM "&Center\tEnd", ID_VIEW_CENTER + MENUITEM "&Up Floor\tPage Up", ID_VIEW_UPFLOOR + MENUITEM "&Down Floor\tPage Down", ID_VIEW_DOWNFLOOR + MENUITEM SEPARATOR + MENUITEM "&Next (XY, YZ, XY)\tCtrl-TAB", ID_VIEW_NEXTVIEW + POPUP "Layout" + BEGIN + MENUITEM "XY (Top)", ID_VIEW_XY + MENUITEM "YZ", ID_VIEW_SIDE + MENUITEM "XZ", ID_VIEW_FRONT + END + POPUP "Zoom" + BEGIN + MENUITEM "&XY 100%", ID_VIEW_100 + MENUITEM "XY Zoom &In\tDelete", ID_VIEW_ZOOMIN + MENUITEM "XY Zoom &Out\tInsert", ID_VIEW_ZOOMOUT + MENUITEM SEPARATOR + MENUITEM "&Z 100%", ID_VIEW_Z100 + MENUITEM "Z Zoo&m In\tctrl-Delete", ID_VIEW_ZZOOMIN + MENUITEM "Z Zoom O&ut\tctrl-Insert", ID_VIEW_ZZOOMOUT + MENUITEM SEPARATOR + MENUITEM "Cubic Clip Zoom In\tctrl-]", ID_VIEW_CUBEIN + MENUITEM "Cubic Clip Zoom Out\tctrl-[", ID_VIEW_CUBEOUT + END + MENUITEM SEPARATOR + POPUP "Show" + BEGIN + MENUITEM "Show &Names", ID_VIEW_SHOWNAMES + , CHECKED + MENUITEM "Show Blocks", ID_VIEW_SHOWBLOCKS + , CHECKED + MENUITEM "Show C&oordinates", ID_VIEW_SHOWCOORDINATES + , CHECKED + MENUITEM "Show &Entities", ID_VIEW_SHOWENT, CHECKED + MENUITEM "Show &Path", ID_VIEW_SHOWPATH + , CHECKED + MENUITEM "Show &Lights", ID_VIEW_SHOWLIGHTS + , CHECKED + MENUITEM "Show Dynamics", ID_VIEW_SHOWWATER + , CHECKED + MENUITEM "Show Clip &Brush", ID_VIEW_SHOWCLIP + , CHECKED + MENUITEM "Show &Hint Brush", ID_VIEW_SHOWHINT + , CHECKED + MENUITEM "Show Wor&ld", ID_VIEW_SHOWWORLD + , CHECKED + MENUITEM "Show Detail\tctrl-D", ID_VIEW_SHOWDETAIL + , CHECKED + MENUITEM "Show Curves", ID_VIEW_SHOWCURVES + , CHECKED + MENUITEM "Show Caulk", ID_VIEW_SHOWCAULK + , CHECKED + MENUITEM "Show Angles", ID_VIEW_SHOWANGLES + , CHECKED + MENUITEM "Show Vis Portals", ID_VIEW_SHOW_SHOWVISPORTALS + , CHECKED + MENUITEM "Show No Draw", ID_VIEW_SHOW_NODRAW + , CHECKED + MENUITEM "Show Combat Nodes", ID_VIEW_SHOWCOMBATNODES + , CHECKED + MENUITEM "Show Triggers", ID_VIEW_SHOWTRIGGERS + , CHECKED + END + POPUP "Hide/Show" + BEGIN + MENUITEM "Hide Selected", ID_VIEW_HIDESHOW_HIDESELECTED + + MENUITEM "Hide Not Selected", ID_VIEW_HIDESHOW_HIDENOTSELECTED + + MENUITEM "Show Hidden", ID_VIEW_HIDESHOW_SHOWHIDDEN + + END + MENUITEM "Cycle Precision Cursor", ID_PRECISION_CURSOR_CYCLE + MENUITEM SEPARATOR + POPUP "Entities as" + BEGIN + MENUITEM "Bounding box", ID_VIEW_ENTITIESAS_BOUNDINGBOX + + MENUITEM "Wireframe", ID_VIEW_ENTITIESAS_WIREFRAME + + MENUITEM "Selected Wireframe", ID_VIEW_ENTITIESAS_SELECTEDWIREFRAME + + MENUITEM "Selected Skinned", ID_VIEW_ENTITIESAS_SELECTEDSKINNED + + MENUITEM "Skinned", ID_VIEW_ENTITIESAS_SKINNED + + MENUITEM "Skinned and Boxed", ID_VIEW_ENTITIESAS_SKINNEDANDBOXED + + END + MENUITEM SEPARATOR + MENUITEM "Cubic Clipping", ID_VIEW_CUBICCLIPPING + , CHECKED + MENUITEM SEPARATOR + MENUITEM "Render Mode", ID_VIEW_RENDERMODE + MENUITEM "Realtime Rebuild", ID_VIEW_REALTIMEREBUILD + MENUITEM "Render Light Outlines", ID_VIEW_RENDERENTITYOUTLINES + MENUITEM "Material Animation", ID_VIEW_MATERIALANIMATION + MENUITEM "Rebuild Render Data", ID_VIEW_REBUILDRENDERDATA + MENUITEM "Render Selection", ID_VIEW_RENDERSELECTION + MENUITEM "Render Sound", ID_VIEW_RENDERSOUND + END + POPUP "&Selection" + BEGIN + POPUP "Drag" + BEGIN + MENUITEM "Drag &Edges", ID_SELECTION_DRAGEDGES + MENUITEM "Drag &Vertices", ID_SELECTION_DRAGVERTECIES + + END + MENUITEM SEPARATOR + MENUITEM "&Clone", ID_SELECTION_CLONE + MENUITEM "Deselect\tEsc", ID_SELECTION_DESELECT + MENUITEM SEPARATOR + POPUP "Flip" + BEGIN + MENUITEM "Flip &X", ID_BRUSH_FLIPX + MENUITEM "Flip &Y", ID_BRUSH_FLIPY + MENUITEM "Flip &Z", ID_BRUSH_FLIPZ + END + MENUITEM SEPARATOR + POPUP "Rotate" + BEGIN + MENUITEM "Rotate X", ID_BRUSH_ROTATEX + MENUITEM "Rotate Y", ID_BRUSH_ROTATEY + MENUITEM "Rotate Z", ID_BRUSH_ROTATEZ + MENUITEM "Arbitrary rotation...", ID_SELECTION_ARBITRARYROTATION + + END + MENUITEM SEPARATOR + MENUITEM "Scale...", ID_SELECT_SCALE + POPUP "CSG" + BEGIN + MENUITEM "&Hollow", ID_SELECTION_MAKEHOLLOW + MENUITEM "&Subtract", ID_SELECTION_CSGSUBTRACT + MENUITEM "&Merge", ID_SELECTION_CSGMERGE + END + MENUITEM SEPARATOR + POPUP "Select" + BEGIN + MENUITEM "Select Complete &Tall", ID_SELECTION_SELECTCOMPLETETALL + + MENUITEM "Select T&ouching", ID_SELECTION_SELECTTOUCHING + + MENUITEM "Select &Partial Tall", ID_SELECTION_SELECTPARTIALTALL + + MENUITEM "Select &Inside", ID_SELECTION_SELECTINSIDE + + MENUITEM "All Targets", ID_SELECT_ALLTARGETS + MENUITEM "Complete Entity", 32866 + END + MENUITEM SEPARATOR + POPUP "Clipper" + BEGIN + MENUITEM "Toggle Clipper", ID_VIEW_CLIPPER + MENUITEM SEPARATOR + MENUITEM "Clip selection", ID_CLIP_SELECTED + MENUITEM "Split selectedion", ID_SPLIT_SELECTED + MENUITEM "Flip Clip orientation", ID_FLIP_CLIP + END + MENUITEM SEPARATOR + MENUITEM "Connect entities", ID_SELECTION_CONNECT + MENUITEM "Ungroup entity", ID_SELECTION_UNGROUPENTITY + MENUITEM SEPARATOR + MENUITEM "Make detail", ID_SELECTION_MAKE_DETAIL + MENUITEM "Make structural", ID_SELECTION_MAKE_STRUCTURAL + MENUITEM SEPARATOR + MENUITEM "Combine", ID_SELECTION_COMBINE + MENUITEM SEPARATOR + POPUP "Export" + BEGIN + MENUITEM "To OBJ", ID_SELECTION_EXPORT_TOOBJ + + MENUITEM "To CM", ID_SELECTION_EXPORT_TOCM + END + END + POPUP "&Bsp" + BEGIN + MENUITEM SEPARATOR + END + POPUP "&Grid" + BEGIN + MENUITEM "Grid 0.125 \t&Shift + 1", ID_GRID_POINT125 + MENUITEM "Grid 0.25 \t&Shift + 2", ID_GRID_POINT25 + MENUITEM "Grid 0.5 \t&Shift + 3", ID_GRID_POINT5 + MENUITEM "Grid1\t&1", ID_GRID_1 + MENUITEM "Grid2\t&2", ID_GRID_2 + MENUITEM "Grid4\t&3", ID_GRID_4 + MENUITEM "Grid8\t&4", ID_GRID_8, CHECKED + MENUITEM "Grid16\t&5", ID_GRID_16 + MENUITEM "Grid32\t&6", ID_GRID_32 + MENUITEM "Grid64\t&7", ID_GRID_64 + MENUITEM SEPARATOR + MENUITEM "Snap to grid", ID_VIEW_SHOWTRIGGERS + , CHECKED + END + POPUP "Ma&terials" + BEGIN + MENUITEM "Show In &Use\tU", ID_TEXTURES_SHOWINUSE + MENUITEM "Show &All\tCtrl-A", ID_TEXTURES_SHOWALL + MENUITEM "&Hide All", ID_TEXTURES_HIDEALL + MENUITEM SEPARATOR + MENUITEM "&Surface Inspector\tS", ID_TEXTURES_INSPECTOR + MENUITEM SEPARATOR + MENUITEM "Find / Replace...", ID_TEXTURE_REPLACEALL + MENUITEM SEPARATOR + POPUP "Texture Lock" + BEGIN + MENUITEM "Moves", ID_TOGGLE_LOCK, CHECKED + MENUITEM "Rotations", ID_TOGGLE_ROTATELOCK + , CHECKED + END + MENUITEM SEPARATOR + MENUITEM "Reload", ID_TEXTURES_RELOADSHADERS + MENUITEM SEPARATOR + POPUP "Texture Window Scale" + BEGIN + MENUITEM "200%", ID_TEXTURES_TEXTUREWINDOWSCALE_200 + + MENUITEM "100%", ID_TEXTURES_TEXTUREWINDOWSCALE_100 + + MENUITEM "50%", ID_TEXTURES_TEXTUREWINDOWSCALE_50 + + MENUITEM "25%", ID_TEXTURES_TEXTUREWINDOWSCALE_25 + + MENUITEM "10%", ID_TEXTURES_TEXTUREWINDOWSCALE_10 + + END + MENUITEM SEPARATOR + MENUITEM "Generate Material List", ID_MATERIALS_GENERATEMATERIALSLIST + + END + POPUP "&Misc" + BEGIN + MENUITEM "&Benchmark", ID_MISC_BENCHMARK + POPUP "&Colors" + BEGIN + POPUP "Themes" + BEGIN + MENUITEM "QE4 Original", ID_COLOR_SETORIGINAL + MENUITEM "Q3Radiant Original", ID_COLOR_SETQER + MENUITEM "Black and Green", ID_COLOR_SETBLACK + MENUITEM "Max/Maya/Lightwave", ID_THEMES_MAX + MENUITEM "Super Mal", ID_COLOR_SUPERMAL + END + MENUITEM SEPARATOR + MENUITEM "&Texture Background...", ID_TEXTUREBK + MENUITEM "Grid Background...", ID_COLORS_XYBK + MENUITEM "Grid Major...", ID_COLORS_MAJOR + MENUITEM "Grid Minor...", ID_COLORS_MINOR + MENUITEM "Grid Text...", ID_COLORS_GRIDTEXT + MENUITEM "Grid Block...", ID_COLORS_GRIDBLOCK + MENUITEM "Default Brush...", ID_COLORS_BRUSH + MENUITEM "Selected Brush...", ID_COLORS_SELECTEDBRUSH + MENUITEM "Clipper...", ID_COLORS_CLIPPER + MENUITEM "Active View name...", ID_COLORS_VIEWNAME + END + MENUITEM "&Gamma...", ID_MISC_GAMMA + MENUITEM "Find brush...", ID_MISC_FINDBRUSH + MENUITEM "Next leak spot\tctrl-l", ID_MISC_NEXTLEAKSPOT + MENUITEM "Previous leak spot\tctrl-p", ID_MISC_PREVIOUSLEAKSPOT + MENUITEM "&Print XY View", ID_MISC_PRINTXY + MENUITEM "&Select Entity Color...\tK", ID_MISC_SELECTENTITYCOLOR + MENUITEM "Find or Replace Entity", ID_MISC_FINDORREPLACEENTITY + MENUITEM "SetViewPos", ID_MISC_SETVIEWPOS + END + POPUP "&Region" + BEGIN + MENUITEM "&Off", ID_REGION_OFF + MENUITEM "&Set XY", ID_REGION_SETXY + MENUITEM "Set &Tall Brush", ID_REGION_SETTALLBRUSH + MENUITEM "Set &Brush", ID_REGION_SETBRUSH + MENUITEM "Set Se&lected Brushes", ID_REGION_SETSELECTION + END + POPUP "&Brush" + BEGIN + MENUITEM "3 sided\tctrl-3", ID_BRUSH_3SIDED + MENUITEM "4 sided\tctrl-4", ID_BRUSH_4SIDED + MENUITEM "5 sided\tctrl-5", ID_BRUSH_5SIDED + MENUITEM "6 sided\tctrl-6", ID_BRUSH_6SIDED + MENUITEM "7 sided\tctrl-7", ID_BRUSH_7SIDED + MENUITEM "8 sided\tctrl-8", ID_BRUSH_8SIDED + MENUITEM "9 sided\tctrl-9", ID_BRUSH_9SIDED + MENUITEM SEPARATOR + MENUITEM "Arbitrary sided...", ID_BRUSH_ARBITRARYSIDED + MENUITEM SEPARATOR + POPUP "Primitives" + BEGIN + MENUITEM "Cone...", ID_BRUSH_MAKECONE + MENUITEM "Sphere...", ID_BRUSH_PRIMITIVES_SPHERE + + END + END + POPUP "&Patch" + BEGIN + MENUITEM "Cylinder", ID_CURVE_PATCHTUBE + POPUP "More Cylinders" + BEGIN + MENUITEM "Dense Cylinder", ID_CURVE_PATCHDENSETUBE + MENUITEM "Very Dense Cylinder", ID_CURVE_PATCHVERYDENSETUBE + + MENUITEM "Square Cylinder", ID_CURVE_PATCHSQUARE + END + MENUITEM SEPARATOR + MENUITEM "End cap", ID_CURVE_PATCHENDCAP + MENUITEM "Bevel", ID_CURVE_PATCHBEVEL + POPUP "More End caps, Bevels" + BEGIN + MENUITEM "Square Endcap", ID_CURVE_MOREENDCAPSBEVELS_SQUAREBEVEL + + MENUITEM "Square Bevel", ID_CURVE_MOREENDCAPSBEVELS_SQUAREENDCAP + + END + MENUITEM SEPARATOR + MENUITEM "Cone", ID_CURVE_PATCHCONE + MENUITEM "Sphere", ID_CURVE_PRIMITIVES_SPHERE + , GRAYED + MENUITEM SEPARATOR + MENUITEM "Simple Patch Mesh...", ID_CURVE_SIMPLEPATCHMESH + MENUITEM SEPARATOR + POPUP "Insert" + BEGIN + MENUITEM "Insert (2) Columns", ID_CURVE_INSERT_INSERTCOLUMN + + MENUITEM "Add (2) Columns", ID_CURVE_INSERT_ADDCOLUMN + + MENUITEM SEPARATOR + MENUITEM "Insert (2) Rows", ID_CURVE_INSERT_INSERTROW + + MENUITEM "Add (2) Rows", ID_CURVE_INSERT_ADDROW + END + POPUP "Delete" + BEGIN + MENUITEM "First (2) Columns", ID_CURVE_DELETE_FIRSTCOLUMN + + MENUITEM "Last (2) Columns", ID_CURVE_DELETE_LASTCOLUMN + + MENUITEM SEPARATOR + MENUITEM "First (2) Rows", ID_CURVE_DELETE_FIRSTROW + MENUITEM "Last (2) Rows", ID_CURVE_DELETE_LASTROW + END + MENUITEM SEPARATOR + POPUP "Matrix" + BEGIN + MENUITEM "Invert", ID_CURVE_NEGATIVE + POPUP "Re-disperse" + BEGIN + MENUITEM "Cols", ID_CURVE_REDISPERSE_COLS + + MENUITEM "Rows", ID_CURVE_REDISPERSE_ROWS + + END + MENUITEM "Transpose", ID_CURVE_MATRIX_TRANSPOSE + + END + MENUITEM SEPARATOR + POPUP "Cap" + BEGIN + MENUITEM "Normal", ID_CURVE_CAP + MENUITEM "Inverted Bevel", ID_CURVE_CAP_INVERTEDBEVEL + + MENUITEM "Inverted Endcap", ID_CURVE_CAP_INVERTEDENDCAP + + MENUITEM SEPARATOR + MENUITEM "Cycle Cap Texture", ID_CURVE_CYCLECAP + END + MENUITEM SEPARATOR + POPUP "Overlay" + BEGIN + MENUITEM "Set", ID_CURVE_OVERLAY_SET + MENUITEM "Clear", ID_CURVE_OVERLAY_CLEAR + END + MENUITEM SEPARATOR + MENUITEM "Thicken...", ID_CURVE_THICKEN + MENUITEM "Combine", ID_PATCH_COMBINE + MENUITEM SEPARATOR + MENUITEM "Nurb Editor", ID_PATCH_NURBEDITOR + END + POPUP "P&lugins" + BEGIN + MENUITEM "Refresh", ID_PLUGINS_REFRESH + MENUITEM SEPARATOR + END + POPUP "&Help" + BEGIN + MENUITEM "Help\tF1", ID_HELP + MENUITEM SEPARATOR + MENUITEM "Command list...", ID_HELP_COMMANDLIST + MENUITEM SEPARATOR + MENUITEM "&About...", ID_HELP_ABOUT + END +END + +IDR_POPUP_TEXTURE MENU +BEGIN + POPUP "Popup" + BEGIN + MENUITEM "&Wireframe", ID_TEXTURES_WIREFRAME + MENUITEM "&Flat shade", ID_TEXTURES_FLATSHADE + MENUITEM "&Nearest", ID_VIEW_NEAREST + MENUITEM "Nearest &Mipmap", ID_VIEW_NEARESTMIPMAP + MENUITEM "&Linear", ID_VIEW_LINEAR + MENUITEM "&Bilinear", ID_VIEW_BILINEAR + MENUITEM "B&ilinear Mipmap", ID_VIEW_BILINEARMIPMAP + MENUITEM "T&rilinear", ID_VIEW_TRILINEAR + END +END + +IDR_POPUP_SELECTION MENU +BEGIN + POPUP "Popup" + BEGIN + MENUITEM "Select Complete &Tall", ID_SELECTION_SELECTCOMPLETETALL + + MENUITEM "Select T&ouching", ID_SELECTION_SELECTTOUCHING + MENUITEM "Select &Partial Tall", ID_SELECTION_SELECTPARTIALTALL + + MENUITEM "Select &Inside", ID_SELECTION_SELECTINSIDE + END +END + +IDR_POPUP_VIEW MENU +BEGIN + POPUP "Popup" + BEGIN + MENUITEM "XY (Top)", ID_VIEW_XY + MENUITEM "XZ", ID_VIEW_SIDE + MENUITEM "YZ", ID_VIEW_FRONT + END +END + +IDR_MENU_DROP MENU +BEGIN + POPUP "Select" + BEGIN + MENUITEM "Select Complete &Tall", ID_SELECTION_SELECTCOMPLETETALL + + MENUITEM "Select T&ouching", ID_SELECTION_SELECTTOUCHING + MENUITEM "Select &Partial Tall", ID_SELECTION_SELECTPARTIALTALL + + MENUITEM "Select &Inside", ID_SELECTION_SELECTINSIDE + END + POPUP "Force Visibility" + BEGIN + MENUITEM "On", 27395 + MENUITEM "Off", 27396 + END + MENUITEM "Ungroup entity", ID_SELECTION_UNGROUPENTITY + MENUITEM "New Model...", ID_DROP_NEWMODEL + MENUITEM SEPARATOR +END + +IDR_MENU_EV MENU +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&Open...", 4 + MENUITEM "&Save", 1236 + MENUITEM "E&xit", 1 + END +END + +IDR_POPUP_ENTITY MENU +BEGIN + POPUP "Popup" + BEGIN + MENUITEM "Wireframe", ID_VIEW_ENTITIESAS_WIREFRAME + MENUITEM "Skinned", ID_VIEW_ENTITIESAS_SKINNED + END +END + +IDR_POPUP_GROUP MENU +BEGIN + POPUP "Popup" + BEGIN + MENUITEM "XY (Top)", ID_VIEW_XY + MENUITEM "XZ", ID_VIEW_SIDE + MENUITEM "YZ", ID_VIEW_FRONT + END +END + +IDR_POPUP_SPLINE MENU +BEGIN + POPUP "Popup" + BEGIN + POPUP "New Camera" + BEGIN + MENUITEM "Fixed", ID_POPUP_NEWCAMERA_FIXED + MENUITEM "Interpolated", ID_POPUP_NEWCAMERA_INTERPOLATED + + MENUITEM "Spline", ID_POPUP_NEWCAMERA_SPLINE + + END + MENUITEM "Camera Inspector...", ID_MATERIAL_INFO + MENUITEM SEPARATOR + MENUITEM "Test Camera", ID_SPLINE_TEST + END +END + +IDR_POPUP_MATERIAL MENU +BEGIN + POPUP "Popup" + BEGIN + MENUITEM "Edit...", ID_MATERIAL_EDIT + MENUITEM "Info...", ID_MATERIAL_INFO + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDR_ACCELERATOR1 ACCELERATORS +BEGIN + "3", ID_BRUSH_3SIDED, VIRTKEY, CONTROL, NOINVERT + "4", ID_BRUSH_4SIDED, VIRTKEY, CONTROL, NOINVERT + "5", ID_BRUSH_5SIDED, VIRTKEY, CONTROL, NOINVERT + "6", ID_BRUSH_6SIDED, VIRTKEY, CONTROL, NOINVERT + "7", ID_BRUSH_7SIDED, VIRTKEY, CONTROL, NOINVERT + "8", ID_BRUSH_8SIDED, VIRTKEY, CONTROL, NOINVERT + "9", ID_BRUSH_9SIDED, VIRTKEY, CONTROL, NOINVERT + "D", ID_VIEW_SHOWDETAIL, VIRTKEY, CONTROL, NOINVERT + "K", ID_SELECTION_CONNECT, VIRTKEY, CONTROL, NOINVERT + "L", ID_MISC_NEXTLEAKSPOT, VIRTKEY, CONTROL, NOINVERT + "M", ID_SELECTION_MAKE_DETAIL, VIRTKEY, CONTROL, NOINVERT + "O", ID_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT + "P", ID_MISC_PREVIOUSLEAKSPOT, VIRTKEY, CONTROL, NOINVERT + "S", ID_FILE_SAVE, VIRTKEY, CONTROL, NOINVERT + VK_DELETE, ID_VIEW_ZZOOMIN, VIRTKEY, CONTROL, NOINVERT + VK_INSERT, ID_VIEW_ZZOOMOUT, VIRTKEY, CONTROL, NOINVERT + "X", ID_FILE_EXIT, VIRTKEY, CONTROL, NOINVERT +END + +IDR_MAINFRAME ACCELERATORS +BEGIN + "7", ID_BRUSH_7SIDED, VIRTKEY, CONTROL, NOINVERT + "8", ID_BRUSH_8SIDED, VIRTKEY, CONTROL, NOINVERT + "9", ID_BRUSH_9SIDED, VIRTKEY, CONTROL, NOINVERT + "D", ID_VIEW_SHOWDETAIL, VIRTKEY, CONTROL, NOINVERT + "K", ID_SELECTION_CONNECT, VIRTKEY, CONTROL, NOINVERT + "L", ID_MISC_NEXTLEAKSPOT, VIRTKEY, CONTROL, NOINVERT + "M", ID_SELECTION_MAKE_DETAIL, VIRTKEY, CONTROL, NOINVERT + "O", ID_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT + "P", ID_MISC_PREVIOUSLEAKSPOT, VIRTKEY, CONTROL, NOINVERT + "S", ID_FILE_SAVE, VIRTKEY, CONTROL, NOINVERT + VK_DELETE, ID_VIEW_ZZOOMIN, VIRTKEY, CONTROL, NOINVERT + VK_DOWN, ID_SELECTION_SELECT_NUDGEDOWN, VIRTKEY, ALT, NOINVERT + VK_INSERT, ID_VIEW_ZZOOMOUT, VIRTKEY, CONTROL, NOINVERT + "X", ID_FILE_EXIT, VIRTKEY, CONTROL, NOINVERT +END + +IDR_ACCEL_SURFACE ACCELERATORS +BEGIN + VK_ESCAPE, ID_BYEBYE, VIRTKEY, NOINVERT +END + +IDR_MINIACCEL ACCELERATORS +BEGIN + VK_DOWN, ID_SELECTION_SELECT_NUDGEDOWN, VIRTKEY, ALT, NOINVERT + VK_LEFT, ID_SELECTION_SELECT_NUDGELEFT, VIRTKEY, ALT, NOINVERT + VK_RIGHT, ID_SELECTION_SELECT_NUDGERIGHT, VIRTKEY, ALT, NOINVERT + VK_UP, ID_SELECTION_SELECT_NUDGEUP, VIRTKEY, ALT, NOINVERT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_FINDTEXTURE DIALOG 0, 0, 129, 53 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Find Texture" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,10,30,50,14 + PUSHBUTTON "Cancel",IDCANCEL,70,30,50,14 + EDITTEXT IDC_EDIT1,10,10,110,14,ES_AUTOHSCROLL +END + +IDD_ENTITY DIALOGEX 0, 0, 234, 354 +STYLE DS_SETFONT | DS_3DLOOK | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | + WS_CLIPSIBLINGS | WS_CAPTION | WS_THICKFRAME +EXSTYLE WS_EX_OVERLAPPEDWINDOW | WS_EX_TOOLWINDOW +CAPTION "Entity" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + LISTBOX IDC_E_LIST,5,5,180,94,LBS_SORT | LBS_NOINTEGRALHEIGHT | + LBS_WANTKEYBOARDINPUT | WS_VSCROLL | WS_TABSTOP, + WS_EX_CLIENTEDGE + EDITTEXT IDC_E_COMMENT,5,102,180,45,ES_MULTILINE | ES_READONLY | + WS_VSCROLL,WS_EX_CLIENTEDGE + PUSHBUTTON "135",IDC_E_135,5,276,15,15 + PUSHBUTTON "180",IDC_E_180,5,292,15,15 + PUSHBUTTON "225",IDC_E_225,5,306,15,15 + PUSHBUTTON "270",IDC_E_270,21,306,15,15 + PUSHBUTTON "90",IDC_E_90,21,276,15,15 + PUSHBUTTON "45",IDC_E_45,35,276,15,15 + PUSHBUTTON "360",IDC_E_0,35,292,15,15 + PUSHBUTTON "315",IDC_E_315,35,306,15,15 + PUSHBUTTON "Up",IDC_E_UP,60,281,15,15 + PUSHBUTTON "Dn",IDC_E_DOWN,60,297,15,15 + LISTBOX IDC_E_PROPS,5,151,180,91,LBS_SORT | LBS_USETABSTOPS | + LBS_NOINTEGRALHEIGHT | LBS_WANTKEYBOARDINPUT | + WS_VSCROLL | WS_TABSTOP,WS_EX_CLIENTEDGE + PUSHBUTTON "Del Key/Pair",IDC_E_DELPROP,105,281,45,15 + EDITTEXT IDC_E_STATUS,83,298,95,30,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL | WS_HSCROLL + LTEXT "Key",IDC_STATIC_KEY,5,246,25,10 + LTEXT "Value",IDC_STATIC_VALUE,5,262,25,10 + EDITTEXT IDC_E_KEY_FIELD,40,245,145,12,ES_AUTOHSCROLL + EDITTEXT IDC_E_VALUE_FIELD,40,261,145,12,ES_AUTOHSCROLL + PUSHBUTTON "Hide",IDC_BTN_HIDE,190,328,7,6 + PUSHBUTTON "Sound...",IDC_BTN_ASSIGNSOUND,198,247,36,11 + PUSHBUTTON "Model...",IDC_BTN_ASSIGNMODEL,198,264,36,11 + CONTROL "Tab1",IDC_TAB_MODE,"SysTabControl32",TCS_BOTTOM | + WS_BORDER,3,338,223,14 +END + +IDD_GAMMA DIALOGEX 0, 0, 135, 94 +STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU +CAPTION "Gamma" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + EDITTEXT IDC_G_EDIT,5,8,46,13,ES_AUTOHSCROLL,WS_EX_CLIENTEDGE + DEFPUSHBUTTON "OK",IDOK,91,5,39,14 + PUSHBUTTON "Cancel",IDCANCEL,91,23,39,14 + LTEXT "0.0 is brightest\n1.0 is darkest\n\nYou must restart for the settings to take effect", + IDC_STATIC,7,25,61,55 +END + +IDD_FINDBRUSH DIALOGEX 0, 0, 127, 76 +STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU +CAPTION "Find brush" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,5,55,50,14 + PUSHBUTTON "Cancel",IDCANCEL,65,55,50,14 + EDITTEXT IDC_FIND_ENTITY,80,15,46,13,ES_AUTOHSCROLL, + WS_EX_CLIENTEDGE + EDITTEXT IDC_FIND_BRUSH,80,30,46,13,ES_AUTOHSCROLL, + WS_EX_CLIENTEDGE + LTEXT "Entity number",IDC_STATIC,10,15,60,8 + LTEXT "Brush number",IDC_STATIC,10,30,65,8 +END + +IDD_ROTATE DIALOGEX 0, 0, 128, 91 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Arbitrary rotation" +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + EDITTEXT IDC_ROTX,23,3,29,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SPIN1,"msctls_updown32",UDS_WRAP | + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,52,3,11,14 + EDITTEXT IDC_ROTY,23,21,29,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SPIN2,"msctls_updown32",UDS_WRAP | + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,53,21,11,14 + EDITTEXT IDC_ROTZ,23,39,29,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SPIN3,"msctls_updown32",UDS_WRAP | + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,53,39,11,14 + DEFPUSHBUTTON "OK",IDOK,82,3,39,14 + PUSHBUTTON "Cancel",IDCANCEL,82,19,39,14 + PUSHBUTTON "Apply",IDC_APPLY,82,39,39,14 + RTEXT "X",IDC_STATIC,8,6,8,8 + RTEXT "Y",IDC_STATIC,8,24,8,8 + RTEXT "Z",IDC_STATIC,8,42,8,8 + CONTROL "Flat Center Rotation",IDC_CHK_FLAT,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,11,60,79,10 +END + +IDD_SIDES DIALOGEX 0, 0, 130, 47 +STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU +CAPTION "Arbitrrary sides" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + EDITTEXT IDC_SIDES,27,6,40,14,ES_AUTOHSCROLL,WS_EX_CLIENTEDGE + DEFPUSHBUTTON "OK",IDOK,85,4,41,14 + PUSHBUTTON "Cancel",IDCANCEL,85,22,41,14 + LTEXT "Sides:",IDC_STATIC,6,8,20,8 +END + +IDD_ABOUT DIALOG 0, 0, 275, 223 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "About Quake 4 Radiant" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,236,7,35,14 + CONTROL 127,IDC_STATIC,"Static",SS_BITMAP | SS_CENTERIMAGE | + SS_REALSIZEIMAGE | SS_SUNKEN | WS_BORDER,7,7,87,80 + GROUPBOX "OpenGL Properties",IDC_STATIC,5,93,265,56 + LTEXT "Vendor:\t\tWHOEVER",IDC_ABOUT_GLVENDOR,10,101,253,10 + LTEXT "Version:\t\t1.1",IDC_ABOUT_GLVERSION,10,111,249,10 + LTEXT "Renderer:\tWHATEVER",IDC_ABOUT_GLRENDERER,10,121,253,24 + GROUPBOX "OpenGL Extensions",IDC_STATIC,5,152,265,65 + CONTROL "DOOM Radiant 1.0 build 199\nCopyright ©1999, 2003 Id Software, Inc.\n\n", + IDC_STATIC,"Static",SS_LEFTNOWORDWRAP | WS_GROUP,99,7, + 132,78 + EDITTEXT IDC_ABOUT_GLEXTENSIONS,10,162,256,53,ES_MULTILINE | + ES_AUTOVSCROLL | ES_READONLY | WS_DISABLED | NOT + WS_BORDER +END + +IDD_SURFACE DIALOGEX 400, 100, 449, 249 +STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_CAPTION | + WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +CAPTION "Surface inspector" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + EDITTEXT IDC_HSHIFT,121,22,45,12,ES_AUTOHSCROLL + CONTROL "Spin2",IDC_SPIN_HSHIFT,"msctls_updown32",UDS_ALIGNRIGHT | + UDS_AUTOBUDDY | UDS_ARROWKEYS,155,23,11,14 + EDITTEXT IDC_VSHIFT,121,38,45,12,ES_AUTOHSCROLL + CONTROL "Spin2",IDC_SPIN_VSHIFT,"msctls_updown32",UDS_ALIGNRIGHT | + UDS_AUTOBUDDY | UDS_ARROWKEYS,155,39,11,14 + EDITTEXT IDC_ROTATE,121,58,45,12,ES_AUTOHSCROLL + CONTROL "Spin2",IDC_SPIN_ROTATE,"msctls_updown32",UDS_ALIGNRIGHT | + UDS_AUTOBUDDY | UDS_ARROWKEYS,155,61,11,14 + EDITTEXT IDC_HSCALE,121,87,43,12,ES_AUTOHSCROLL | ES_WANTRETURN + EDITTEXT IDC_VSCALE,121,103,44,12,ES_AUTOHSCROLL | ES_WANTRETURN + EDITTEXT IDC_EDIT_WIDTH,53,141,30,12,ES_AUTOHSCROLL | + ES_WANTRETURN + CONTROL "Spin1",IDC_SPIN_WIDTH,"msctls_updown32",UDS_WRAP | + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,77,139,11,18 + EDITTEXT IDC_EDIT_HEIGHT,91,141,30,12,ES_AUTOHSCROLL | + ES_WANTRETURN + CONTROL "Spin1",IDC_SPIN_HEIGHT,"msctls_updown32", + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,113,138,11,18 + EDITTEXT IDC_TEXTURE,33,6,133,12,ES_AUTOHSCROLL + PUSHBUTTON "CAP",IDC_BTN_PATCHDETAILS,11,158,34,12 + PUSHBUTTON "Ok",IDOK,412,233,34,12 + PUSHBUTTON "Cancel",IDCANCEL,377,233,34,12 + RTEXT "Shift vertically",IDC_STATIC,51,40,65,8 + RTEXT "Scale horizontally",IDC_STATIC,51,89,65,8 + RTEXT "Scale vertically",IDC_STATIC,51,105,65,8 + RTEXT "Rotate",IDC_STATIC,95,60,22,8 + RTEXT "Material",IDC_STATIC,3,8,28,8 + RTEXT "Shift horizontally",IDC_STATIC,51,24,65,8 + GROUPBOX "Texturing",IDC_STATIC,3,125,168,51 + PUSHBUTTON "Natural",IDC_BTN_PATCHNATURAL,50,158,34,12 + PUSHBUTTON "Fit",IDC_BTN_FACEFIT,11,141,34,12 + LTEXT "Width",IDC_STATIC,54,132,20,8 + LTEXT "Height",IDC_STATIC,91,132,22,8 + EDITTEXT IDC_EDIT_HORZ,138,195,21,12,ES_AUTOHSCROLL + GROUPBOX "Texture Tool Goes here",IDC_STATIC,179,7,262,207 + CONTROL "Subdivide Patch",IDC_CHECK_SUBDIVIDE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,3,181,66,11 + CONTROL "Slider1",IDC_SLIDER_HORZ,"msctls_trackbar32", + TBS_AUTOTICKS | WS_TABSTOP,36,193,99,17 + LTEXT "Horizontal",IDC_STATIC,3,196,34,10 + EDITTEXT IDC_EDIT_VERT,137,217,21,12,ES_AUTOHSCROLL + CONTROL "Slider1",IDC_SLIDER_VERT,"msctls_trackbar32", + TBS_AUTOTICKS | WS_TABSTOP,35,215,99,17 + LTEXT "Vertical",IDC_STATIC,3,218,34,10 + PUSHBUTTON "Flip X",IDC_BTN_FLIPX,89,158,34,12 + PUSHBUTTON "Flip Y",IDC_BTN_FLIPY,128,158,34,12 + CONTROL "Absolute",IDC_CHECK_ABSOLUTE,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,8,86,41,10 + GROUPBOX "",IDC_STATIC,2,76,168,44 +END + +IDD_DLG_PREFS DIALOGEX 0, 0, 386, 263 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Quake 4 Radiant Preferences" +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + CONTROL "",IDC_RADIO_VIEWTYPE,"Button",BS_AUTORADIOBUTTON | + WS_DISABLED | WS_GROUP,18,35,10,10 + CONTROL "",IDC_RADIO_VIEWTYPE2,"Button",BS_AUTORADIOBUTTON,45,35, + 11,10 + CONTROL "",IDC_RADIO_VIEWTYPE3,"Button",BS_AUTORADIOBUTTON | + WS_DISABLED,73,35,11,10 + CONTROL "",IDC_RADIO_VIEWTYPE4,"Button",BS_AUTORADIOBUTTON | + WS_DISABLED,99,35,11,10 + CONTROL "OpenGL display lists",IDC_CHECK_DISPLAYLISTS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,15,47,79,10 + CONTROL "Solid selection boxes",IDC_CHECK_NOSTIPPLE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,15,60,82,10 + CONTROL "Slider1",IDC_SLIDER_CAMSPEED,"msctls_trackbar32", + WS_TABSTOP,128,23,92,11 + CONTROL "Update XY views during\nmouse drags", + IDC_CHECK_CAMXYUPDATE,"Button",BS_AUTOCHECKBOX | + BS_MULTILINE | WS_TABSTOP,130,48,91,18 + CONTROL "QE4 update model",IDC_CHECK_QE4PAINTING,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,130,68,75,10 + CONTROL "Slider1",IDC_SLIDER_TEXTUREQUALITY,"msctls_trackbar32", + TBS_AUTOTICKS | WS_TABSTOP,237,34,127,11 + CONTROL "Texture toolbar",IDC_CHECK_TEXTURETOOLBAR,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,236,60,63,10 + CONTROL "Texture scrollbar",IDC_CHECK_TEXTURESCROLLBAR,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,236,71,68,10 + CONTROL "Texture subset",IDC_CHECK_TEXTUREWINDOW,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,308,60,63,10 + CONTROL "Right click to drop entities",IDC_CHECK_RIGHTCLICK, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,103,95,10 + CONTROL "Face selection",IDC_CHECK_FACE,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,13,115,62,10 + EDITTEXT IDC_EDIT_ROTATION,58,127,24,12,ES_AUTOHSCROLL + CONTROL "ALT + multi-drag",IDC_CHECK_ALTDRAG,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,114,103,68,10 + CONTROL "Snap T to Grid",IDC_CHECK_SNAPT,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,114,115,62,10 + CONTROL "Mouse chaser",IDC_CHECK_MOUSECHASE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,114,126,68,10 + CONTROL "Patch Toolbar",IDC_CHECK_WIDETOOLBAR,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,193,103,61,10 + CONTROL "Light drawing",IDC_CHECK_LIGHTDRAW,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,193,115,58,10 + CONTROL "Paint sizing info",IDC_CHECK_SIZEPAINT,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,193,126,65,10 + CONTROL "Hi Color Textures",IDC_CHECK_HICOLOR,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,281,103,70,10 + LTEXT "Startup Shaders:",IDC_STATIC,281,116,54,8 + COMBOBOX IDC_COMBO_SHADERS,281,126,82,54,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + CONTROL "Don't clamp plane points",IDC_CHECK_NOCLAMP,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,13,157,93,10 + CONTROL "Snapshots",IDC_CHECK_SNAPSHOTS,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,13,168,49,10 + CONTROL "Use +setgame for run",IDC_CHECK_SETGAME,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,13,181,83,10 + CONTROL "Run game after QBSP3...",IDC_CHECK_RUNQUAKE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,118,157,96,10 + CONTROL "Load last project on open",IDC_CHECK_LOADLAST,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,118,169,96,10 + CONTROL "Load last map on open",IDC_CHECK_LOADLASTMAP,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,118,181,88,10 + CONTROL "Auto save every ",IDC_CHECK_AUTOSAVE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,118,193,66,10 + EDITTEXT IDC_EDIT_AUTOSAVE,185,193,27,12,ES_AUTOHSCROLL | + ES_NUMBER + CONTROL "Spin1",IDC_SPIN_AUTOSAVE,"msctls_updown32",UDS_WRAP | + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,209,191,9,14 + LTEXT "Status point size:",IDC_STATIC,256,157,54,8 + EDITTEXT IDC_EDIT_STATUSPOINTSIZE,313,155,29,12,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SPIN_POINTSIZE,"msctls_updown32",UDS_WRAP | + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,342,154,9,14 + LTEXT "Undo Levels:",IDC_STATIC,256,170,43,8 + EDITTEXT IDC_EDIT_UNDOLEVELS,313,168,29,12,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SPIN_UNDO,"msctls_updown32",UDS_WRAP | + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,342,168,9,14 + DEFPUSHBUTTON "OK",IDOK,341,236,38,14 + PUSHBUTTON "Cancel",IDCANCEL,299,236,38,14 + LTEXT "minutes",IDC_STATIC,213,194,25,8 + GROUPBOX "Views / Rendering",IDC_STATIC,7,4,372,86 + GROUPBOX "Tool settings / Stuff that wouldn't fit anywhere else", + IDC_STATIC,7,145,372,83 + CONTROL 147,IDB_VIEWDEFAULT,"Static",SS_BITMAP,13,14,21,19 + CONTROL 148,IDB_VIEWDEFAULT2,"Static",SS_BITMAP,40,14,21,19 + CONTROL 149,IDB_VIEWDEFAULT3,"Static",SS_BITMAP,67,14,21,19 + GROUPBOX "New functionality:",IDC_STATIC,7,92,372,52 + CONTROL 150,IDB_VIEWDEFAULT_Z,"Static",SS_BITMAP,93,14,21,19 + LTEXT "slow",IDC_STATIC,131,35,15,8 + LTEXT "fast",IDC_STATIC,204,35,12,8 + GROUPBOX "Camera ",IDC_STATIC,126,13,100,72 + LTEXT "Rotation inc:",IDC_STATIC,15,129,41,8 + GROUPBOX "Texturing",IDC_STATIC,231,13,141,72 + LTEXT "Quality",IDC_STATIC,237,23,22,8 + LTEXT "Low",IDC_STATIC,239,47,14,8 + LTEXT "High",IDC_STATIC,347,48,16,8 + LTEXT "Map Path:",IDC_STATIC,13,215,34,8 + EDITTEXT IDC_EDIT_MAPS,49,212,90,12,ES_AUTOHSCROLL + CONTROL "Use new map format",IDC_CHECK_NEWMAPFORMAT,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,13,194,81,10 +END + +IDD_DLG_MAPINFO DIALOG 0, 0, 181, 183 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Map Info" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "Close",IDOK,138,7,36,14 + LTEXT "Total Brushes",IDC_STATIC,7,14,50,8 + EDITTEXT IDC_EDIT_TOTALBRUSHES,66,12,40,12,ES_RIGHT | + ES_AUTOHSCROLL | ES_NUMBER + LTEXT "Total Entities",IDC_STATIC,7,30,50,8 + EDITTEXT IDC_EDIT_TOTALENTITIES,66,28,40,12,ES_RIGHT | + ES_AUTOHSCROLL | ES_NUMBER + LTEXT "Net brush count\n(non entity)",IDC_STATIC,7,49,57,17 + EDITTEXT IDC_EDIT_NET,66,47,40,12,ES_RIGHT | ES_AUTOHSCROLL | + ES_NUMBER + LISTBOX IDC_LIST_ENTITIES,7,81,167,95,LBS_SORT | LBS_USETABSTOPS | + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + LTEXT "Entity breakdown",IDC_STATIC,7,71,56,8 +END + +IDD_DLG_ENTITYLIST DIALOGEX 0, 0, 492, 244 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Entities" +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + DEFPUSHBUTTON "Close",IDOK,446,223,39,14 + DEFPUSHBUTTON "Select",IDC_SELECT,401,223,39,14 + CONTROL "List2",IDC_LIST_ENTITY,"SysListView32",LVS_REPORT | + LVS_SINGLESEL | LVS_EDITLABELS | LVS_NOSORTHEADER | + WS_BORDER | WS_TABSTOP,178,7,307,208 + LISTBOX IDC_LIST_ENTITIES,7,7,165,230,LBS_SORT | + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP +END + +IDD_DLG_SCRIPTS DIALOG 0, 0, 212, 215 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Available Scripts" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "New...",IDC_NEW,166,74,39,14,WS_DISABLED + PUSHBUTTON "Close",IDOK,168,194,37,14 + LISTBOX IDC_LIST_SCRIPTS,7,56,146,152,LBS_SORT | + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + DEFPUSHBUTTON "Edit...",IDC_EDIT,166,90,39,14,WS_DISABLED + DEFPUSHBUTTON "Run",IDC_RUN,166,56,39,14 + LTEXT "WARNING: BrushScripting is in a highly experimental state and is far from complete. If you attempt to use them it is VERY LIKELY that Q3Radiant will crash. Save your work before attempting to make use of any scripting features.", + IDC_STATIC,7,7,198,38 +END + +IDD_DLG_NEWPROJECT DIALOG 0, 0, 246, 74 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "New project" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_EDIT_NAME,57,28,116,12,ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,189,7,50,14 + PUSHBUTTON "Cancel",IDCANCEL,189,24,50,14 + LTEXT "This will create a new directory beneath your Quake2 path based on the project name you give.", + IDC_STATIC,7,7,165,19 + LTEXT "Project name:",IDC_STATIC,7,30,44,8 + CONTROL "Include game dll files",IDC_CHECK1,"Button", + BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,7,49,81,10 +END + +IDD_DLG_COMMANDLIST DIALOG 0, 0, 283, 223 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Mapped Commands" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "Close",IDOK,241,202,35,14 + LISTBOX IDC_LIST_COMMANDS,7,7,221,209,LBS_SORT | LBS_USETABSTOPS | + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP +END + +IDD_DIALOG_SCALE DIALOG 0, 0, 122, 74 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Scale" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,79,7,36,14 + PUSHBUTTON "Cancel",IDCANCEL,79,23,36,14 + LTEXT "X:",IDC_STATIC,7,15,8,8 + LTEXT "Y:",IDC_STATIC,7,32,8,8 + LTEXT "Z:",IDC_STATIC,7,49,8,8 + EDITTEXT IDC_EDIT_X,22,13,32,12,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_Y,22,30,32,12,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_Z,22,47,32,12,ES_AUTOHSCROLL +END + +IDD_DIALOG_FINDREPLACE DIALOGEX 0, 0, 222, 98 +STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_CAPTION | + WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +CAPTION "Find replace texture(s)" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + EDITTEXT IDC_EDIT_FIND,27,16,132,12,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_REPLACE,45,31,114,12,ES_AUTOHSCROLL + CONTROL "Replace within selected brushes only", + IDC_CHECK_SELECTED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 7,51,133,10 + CONTROL "Force replacement (ignore current texture name)", + IDC_CHECK_FORCE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7, + 63,167,10 + DEFPUSHBUTTON "OK",IDOK,175,7,40,14 + PUSHBUTTON "Close",IDCANCEL,175,77,40,14 + LTEXT "Find:",IDC_STATIC,7,18,16,8 + LTEXT "Replace:",IDC_STATIC,7,33,30,8 + DEFPUSHBUTTON "Apply",IDC_BTN_APPLY,175,25,40,14 + CONTROL "Live updates from Texture/Camera Windows", + IDC_CHECK_LIVE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7, + 79,157,10 +END + +IDD_DIALOG_STAIRS DIALOG 0, 0, 196, 143 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Stairs" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,147,7,42,14 + PUSHBUTTON "Cancel",IDCANCEL,136,24,42,14 + LTEXT "This creates a set of stairs using the selected brush as a template. You may optionally specify a rotation angle to be applied to each new step", + IDC_STATIC,7,7,120,37 + LTEXT "After pressing OK, left click to select a ""height"" point for the stairs. Enough stairs will be created to consume the hieght (Z) between the selected brush and the point you specify", + IDC_STATIC,7,46,116,53 + LTEXT "Rotation per step:",IDC_STATIC,7,104,57,8 + EDITTEXT IDC_EDIT_ROTATION,69,102,40,12,ES_AUTOHSCROLL +END + +IDD_DIALOG_INPUT DIALOG 0, 0, 171, 173 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "BrushScript Input" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,127,7,37,14 + PUSHBUTTON "Cancel",IDCANCEL,127,24,37,14 + LTEXT "Field1:",IDC_STATIC_FIELD1,7,13,96,8 + EDITTEXT IDC_EDIT_FIELD1,7,23,47,14,ES_AUTOHSCROLL + LTEXT "Field1:",IDC_STATIC_FIELD2,7,41,105,8 + EDITTEXT IDC_EDIT_FIELD2,7,51,47,14,ES_AUTOHSCROLL + LTEXT "Field1:",IDC_STATIC_FIELD3,7,71,115,8 + EDITTEXT IDC_EDIT_FIELD3,7,81,47,14,ES_AUTOHSCROLL + LTEXT "Field1:",IDC_STATIC_FIELD4,7,103,129,8 + EDITTEXT IDC_EDIT_FIELD4,7,113,47,14,ES_AUTOHSCROLL + LTEXT "Field1:",IDC_STATIC_FIELD5,7,133,135,8 + EDITTEXT IDC_EDIT_FIELD5,7,142,47,14,ES_AUTOHSCROLL +END + +IDD_DLG_INFORMATION DIALOGEX 0, 0, 186, 95 +STYLE DS_SETFONT | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Information" +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + EDITTEXT IDC_EDIT1,7,7,172,81,ES_MULTILINE | WS_DISABLED | + WS_VSCROLL +END + +IDD_PROJECT DIALOGEX 250, 100, 341, 335 +STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU +CAPTION "Project Settings" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + EDITTEXT IDC_PRJBASEPATH,75,20,205,12,ES_AUTOHSCROLL + EDITTEXT IDC_PRJMAPSPATH,75,40,205,12,ES_AUTOHSCROLL + EDITTEXT IDC_PRJRSHCMD,75,60,205,12,ES_AUTOHSCROLL + EDITTEXT IDC_PRJREMOTEBASE,75,80,205,12,ES_AUTOHSCROLL + EDITTEXT IDC_PRJENTITYPATH,75,100,205,12,ES_AUTOHSCROLL + EDITTEXT IDC_PRJTEXPATH,75,120,205,12,ES_AUTOHSCROLL + LISTBOX IDC_CMD_LIST,10,157,270,90,LBS_SORT | + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP, + WS_EX_CLIENTEDGE + PUSHBUTTON "Add...",IDC_ADDCMD,290,150,45,13 + PUSHBUTTON "Change...",IDC_EDITCMD,290,167,45,13 + PUSHBUTTON "Remove",IDC_REMCMD,290,184,45,13 + CONTROL "Use brush primitives in MAP files",IDC_CHECK_BPRIMIT, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,270,136,9 + DEFPUSHBUTTON "OK",IDOK,290,10,45,13 + PUSHBUTTON "Cancel",IDCANCEL,290,26,45,13 + GROUPBOX "Project settings",IDC_STATIC,5,7,280,133 + LTEXT "basepath",IDC_STATIC,42,22,30,8 + LTEXT "rshcmd",IDC_STATIC,48,62,24,8 + LTEXT "remotebasepath",IDC_STATIC,20,82,52,8 + LTEXT "entitypath",IDC_STATIC,40,102,32,8 + LTEXT "texturepath",IDC_STATIC,36,122,36,8 + GROUPBOX "Menu commands",IDC_STATIC,5,146,280,105 + LTEXT "mapspath",IDC_STATIC,40,42,32,8 + GROUPBOX "Misc settings",IDC_STATIC,5,258,280,73 +END + +IDD_ADDCMD DIALOG 300, 200, 312, 65 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Add command" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,116,44,37,13 + PUSHBUTTON "Cancel",IDCANCEL,159,44,37,13 + EDITTEXT IDC_CMDMENUTEXT,44,8,254,12,ES_AUTOHSCROLL + EDITTEXT IDC_CMDCOMMAND,44,26,254,12,ES_AUTOHSCROLL + LTEXT "Menu text",IDC_STATIC,5,10,32,8 + LTEXT "Command",IDC_STATIC,5,28,32,8 +END + +IDD_TEXTUREBAR DIALOG 0, 0, 313, 19 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_HSHIFT,33,4,32,12,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SPIN_HSHIFT,"msctls_updown32",UDS_ALIGNRIGHT | + UDS_AUTOBUDDY | UDS_ARROWKEYS,56,3,10,14 + EDITTEXT IDC_VSHIFT,76,4,32,12,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SPIN_VSHIFT,"msctls_updown32",UDS_ALIGNRIGHT | + UDS_AUTOBUDDY | UDS_ARROWKEYS,97,3,11,14 + EDITTEXT IDC_HSCALE,143,4,32,12,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SPIN_HSCALE,"msctls_updown32",UDS_ALIGNRIGHT | + UDS_AUTOBUDDY | UDS_ARROWKEYS,164,3,11,14 + EDITTEXT IDC_VSCALE,188,4,32,12,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SPIN_VSCALE,"msctls_updown32",UDS_ALIGNRIGHT | + UDS_AUTOBUDDY | UDS_ARROWKEYS,210,3,11,14 + EDITTEXT IDC_ROTATE,252,4,32,12,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SPIN_ROTATE,"msctls_updown32",UDS_ALIGNRIGHT | + UDS_AUTOBUDDY | UDS_ARROWKEYS,272,3,11,14 + LTEXT "Shift H",IDC_STATIC,7,6,22,8 + LTEXT "V",IDC_STATIC,68,6,8,8 + LTEXT "Scale H",IDC_STATIC,112,6,26,8 + LTEXT "V",IDC_STATIC,180,6,8,8 + LTEXT "Rotate",IDC_STATIC,226,6,22,8 + DEFPUSHBUTTON "Button1",IDC_BTN_APPLYTEXTURESTUFF,278,2,11,8,NOT + WS_VISIBLE | NOT WS_TABSTOP + EDITTEXT IDC_EDIT_ROTATEAMT,287,4,20,12,ES_AUTOHSCROLL +END + +IDD_DIALOG_TEXTURELIST DIALOGEX 0, 0, 314, 318 +STYLE DS_SETFONT | DS_CONTROL | WS_CHILD +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + DEFPUSHBUTTON "&Load",IDC_LOAD,7,7,35,12,WS_DISABLED + CONTROL "Tree1",IDC_TREE_TEXTURES,"SysTreeView32",TVS_HASBUTTONS | + TVS_HASLINES | TVS_LINESATROOT | TVS_DISABLEDRAGDROP | + TVS_SHOWSELALWAYS | TVS_NOTOOLTIPS | WS_BORDER | + WS_TABSTOP,7,24,300,137 + DEFPUSHBUTTON "&Reload",IDC_REFRESH,45,7,35,12 + CONTROL "",IDC_PREVIEW,"Static",SS_BLACKFRAME | SS_NOTIFY | + SS_SUNKEN,7,166,300,145 + CONTROL "Hide root entries",IDC_CHECK_HIDEROOT,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,163,7,67,10 +END + +IDD_DIALOG_NEWPATCH DIALOG 0, 0, 129, 58 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Patch density" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,90,7,32,14 + PUSHBUTTON "Cancel",IDCANCEL,90,24,32,14 + LTEXT "Width:",IDC_STATIC,9,9,22,8 + LTEXT "Height:",IDC_STATIC,7,27,24,8 + COMBOBOX IDC_COMBO_WIDTH,38,7,33,51,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_COMBO_HEIGHT,38,25,33,51,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP +END + +IDD_DIALOG_TEXTURELAYOUT DIALOG 0, 0, 186, 95 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Patch texture layout" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,141,7,38,14 + PUSHBUTTON "Cancel",IDCANCEL,141,24,38,14 + LTEXT "Texture will be fit across the patch based on the x and y values given. Values of 1x1 will ""fit"" the texture. 2x2 will repeat it twice, etc.", + IDC_STATIC,7,7,122,36 + LTEXT "Texture x:",IDC_STATIC,7,48,32,8 + EDITTEXT IDC_EDIT_X,42,46,30,12,ES_AUTOHSCROLL + LTEXT "Texture y:",IDC_STATIC,7,64,32,8 + EDITTEXT IDC_EDIT_Y,42,62,30,12,ES_AUTOHSCROLL +END + +IDD_DIALOG_CAP DIALOG 0, 0, 171, 84 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Cap" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,123,7,41,14 + PUSHBUTTON "Cancel",IDCANCEL,123,25,41,14 + CONTROL "Bevel",IDC_RADIO_CAP,"Button",BS_AUTORADIOBUTTON | + WS_GROUP,31,10,50,10 + CONTROL "Endcap",IDC_RADIO_CAP2,"Button",BS_AUTORADIOBUTTON,31, + 28,50,10 + CONTROL "Inverted Bevel",IDC_RADIO_CAP3,"Button", + BS_AUTORADIOBUTTON,31,47,68,10 + CONTROL "Inverted Endcap",IDC_RADIO_CAP4,"Button", + BS_AUTORADIOBUTTON,31,65,75,10 + CONTROL 177,IDC_STATIC,"Static",SS_BITMAP | SS_REALSIZEIMAGE | + SS_SUNKEN,7,7,18,15 + CONTROL 178,IDC_STATIC,"Static",SS_BITMAP | SS_REALSIZEIMAGE | + SS_SUNKEN,7,44,18,15 + CONTROL 176,IDC_STATIC,"Static",SS_BITMAP | SS_REALSIZEIMAGE | + SS_SUNKEN,7,26,18,15 + CONTROL 175,IDC_STATIC,"Static",SS_BITMAP | SS_REALSIZEIMAGE | + SS_SUNKEN,7,63,17,14 +END + +IDD_DIALOG_THICKEN DIALOG 0, 0, 204, 63 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Thicken Patch" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,158,7,39,14 + PUSHBUTTON "Cancel",IDCANCEL,158,24,39,14 + LTEXT "This produces a func_grouped set of patches that contains the original patch along with the 'thick' patch and an optional set of seam patches. ", + IDC_STATIC,7,7,145,33 + EDITTEXT IDC_EDIT_AMOUNT,38,42,26,12,ES_AUTOHSCROLL + LTEXT "Amount:",IDC_STATIC,7,44,27,8 + CONTROL "Seams",IDC_CHECK_SEAMS,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,74,43,37,10 +END + +IDD_DIALOG_PATCH DIALOG 0, 0, 272, 178 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Patch Properties" +FONT 8, "MS Sans Serif" +BEGIN + COMBOBOX IDC_COMBO_ROW,15,29,34,79,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_COMBO_COL,54,29,34,84,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + EDITTEXT IDC_EDIT_X,34,49,54,12,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_Y,33,64,55,12,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_Z,33,81,55,12,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_S,33,98,55,12,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_T,33,115,55,12,ES_AUTOHSCROLL + COMBOBOX IDC_COMBO_TYPE,33,134,55,79,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + EDITTEXT IDC_EDIT_NAME,106,29,144,12,ES_AUTOHSCROLL + EDITTEXT IDC_HSHIFT,107,48,45,12,ES_AUTOHSCROLL + CONTROL "Spin2",IDC_SPIN_HSHIFT,"msctls_updown32",UDS_ALIGNRIGHT | + UDS_AUTOBUDDY | UDS_ARROWKEYS,141,48,11,14 + EDITTEXT IDC_VSHIFT,107,64,45,12,ES_AUTOHSCROLL + CONTROL "Spin2",IDC_SPIN_VSHIFT,"msctls_updown32",UDS_ALIGNRIGHT | + UDS_AUTOBUDDY | UDS_ARROWKEYS,141,64,11,14 + EDITTEXT IDC_HSCALE,107,81,45,12,ES_AUTOHSCROLL + CONTROL "Spin2",IDC_SPIN_HSCALE,"msctls_updown32",UDS_ALIGNRIGHT | + UDS_AUTOBUDDY | UDS_ARROWKEYS,141,81,11,14 + EDITTEXT IDC_VSCALE,107,98,45,12,ES_AUTOHSCROLL + CONTROL "Spin2",IDC_SPIN_VSCALE,"msctls_updown32",UDS_ALIGNRIGHT | + UDS_AUTOBUDDY | UDS_ARROWKEYS,141,98,11,14 + EDITTEXT IDC_ROTATE,107,115,45,12,ES_AUTOHSCROLL + CONTROL "Spin2",IDC_SPIN_ROTATE,"msctls_updown32",UDS_ALIGNRIGHT | + UDS_AUTOBUDDY | UDS_ARROWKEYS,141,114,11,14 + PUSHBUTTON "CAP",IDC_BTN_PATCHDETAILS,109,134,32,13 + PUSHBUTTON "Set...",IDC_BTN_PATCHRESET,148,134,32,13 + PUSHBUTTON "Natural",IDC_BTN_PATCHNATURAL,185,134,32,13 + PUSHBUTTON "Fit",IDC_BTN_PATCHFIT,225,134,32,13 + PUSHBUTTON "Apply",IDC_APPLY,184,159,38,12 + DEFPUSHBUTTON "Done",IDOK,227,159,38,12 + LTEXT "Row:",IDC_STATIC,15,17,18,8 + LTEXT "Column:",IDC_STATIC,54,17,26,8 + LTEXT "X:",IDC_STATIC,21,51,8,8 + LTEXT "Y:",IDC_STATIC,21,66,8,8 + LTEXT "Z:",IDC_STATIC,21,83,8,8 + LTEXT "S:",IDC_STATIC,21,100,8,8 + LTEXT "T:",IDC_STATIC,21,117,8,8 + LTEXT "Name:",IDC_STATIC,106,17,22,8 + LTEXT "Vertical shift",IDC_STATIC,157,66,65,8 + LTEXT "Horizontal stretch",IDC_STATIC,157,83,65,8 + LTEXT "Vertical stretch",IDC_STATIC,157,100,65,8 + LTEXT "Rotate",IDC_STATIC,157,117,30,8 + LTEXT "Horizontal shift",IDC_STATIC,157,51,65,8 + GROUPBOX "Texturing",IDC_STATIC,99,7,166,148 + GROUPBOX "Details",IDC_STATIC,7,7,87,148 + LTEXT "Type:",IDC_STATIC,13,136,19,8 +END + +IDD_PLAYWAVE DIALOG 0, 0, 33, 15 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + PUSHBUTTON "Play",IDC_BTN_PLAY,0,0,33,15 +END + +IDD_TEXLIST DIALOG 0, 0, 251, 273 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Load multiple texture paths" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "Load",IDOK,194,7,50,14 + PUSHBUTTON "Cancel",IDCANCEL,194,24,50,14 + LISTBOX IDC_LIST_TEXTURES,7,7,179,259,LBS_SORT | + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP +END + +IDD_DLG_GROUP DIALOG 0, 0, 224, 241 +STYLE DS_SETFONT | DS_CONTROL | WS_CHILD | WS_BORDER +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Tree1",IDC_TREE_GROUP,"SysTreeView32",TVS_HASBUTTONS | + TVS_HASLINES | TVS_LINESATROOT | TVS_EDITLABELS | + TVS_SHOWSELALWAYS | TVS_CHECKBOXES | TVS_TRACKSELECT | + WS_BORDER | WS_TABSTOP,7,7,210,209 + PUSHBUTTON "Add...",IDC_BTN_ADD,7,221,33,12 + PUSHBUTTON "Delete",IDC_BTN_DEL,83,221,33,12 + PUSHBUTTON "Edit...",IDC_BTN_EDIT,44,221,33,12 + PUSHBUTTON "Hide All",IDC_BTN_HIDEALL,135,221,33,12 + PUSHBUTTON "Show All",IDC_BTN_SHOWALL,174,221,33,12 +END + +IDD_DIALOG_LIGHT DIALOGEX 0, 0, 311, 287 +STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +CAPTION "Light Inspector" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + CONTROL "Equilateral Radius",IDC_CHECK_EQUALRADIUS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,13,22,73,10 + EDITTEXT IDC_EDIT_RADIUSX,39,41,19,12,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_RADIUSY,63,41,19,12,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_RADIUSZ,86,41,19,12,ES_AUTOHSCROLL + CONTROL "0.0",IDC_RADIO_FALLOFF,"Button",BS_AUTORADIOBUTTON | + WS_GROUP,11,67,26,10 + CONTROL "0.5",IDC_RADIO_FALLOFF2,"Button",BS_AUTORADIOBUTTON,38, + 67,26,10 + CONTROL "1.0",IDC_RADIO_FALLOFF3,"Button",BS_AUTORADIOBUTTON,66, + 67,26,10 + EDITTEXT IDC_EDIT_TARGETX,37,152,19,12,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_TARGETY,61,152,19,12,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_TARGETZ,84,152,19,12,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_RIGHTX,37,167,19,12,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_RIGHTY,61,167,19,12,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_RIGHTZ,84,167,19,12,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_UPX,37,181,19,12,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_UPY,61,181,19,12,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_UPZ,84,181,19,12,ES_AUTOHSCROLL + CONTROL "Explicit start/end points",IDC_CHECK_EXPLICITFALLOFF, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,197,89,10 + EDITTEXT IDC_EDIT_STARTX,37,209,19,12,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_STARTY,61,209,19,12,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_STARTZ,84,209,19,12,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_ENDX,37,223,19,12,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_ENDY,61,223,19,12,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_ENDZ,83,223,19,12,ES_AUTOHSCROLL + PUSHBUTTON "",IDC_BTN_COLOR,163,38,22,10,BS_BITMAP + CONTROL "Cast Shadows",IDC_CHECK_SHADOWS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,135,17,59,10 + CONTROL "Cast Diffuse",IDC_CHECK_DIFFUSE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,198,18,51,8 + CONTROL "Cast Specular",IDC_CHECK_SPECULAR,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,252,18,58,8 + DEFPUSHBUTTON "OK",IDOK,7,270,40,14 + PUSHBUTTON "Apply",IDC_APPLY,102,270,40,14 + PUSHBUTTON "Cancel",IDCANCEL,54,270,40,14 + GROUPBOX "",IDC_STATIC,7,13,123,106 + GROUPBOX "",IDC_STATIC,7,134,123,133 + LTEXT "Fall-off",IDC_STATIC,9,57,22,8 + LTEXT "Color",IDC_STATIC,141,39,17,8 + LTEXT "Texture",IDC_STATIC,135,68,25,8 + LTEXT "Radius",IDC_STATIC,15,43,23,8 + LTEXT "X",IDC_STATIC,46,33,8,8 + LTEXT "Y",IDC_STATIC,70,33,8,8 + LTEXT "Z",IDC_STATIC,93,33,8,8 + RTEXT "Target",IDC_STATIC,11,154,22,8 + LTEXT "X",IDC_STATIC,44,144,8,8 + LTEXT "Y",IDC_STATIC,68,144,8,8 + LTEXT "Z",IDC_STATIC,91,144,8,8 + RTEXT "Right",IDC_STATIC,15,169,18,8 + RTEXT "Up",IDC_STATIC,23,182,10,8 + RTEXT "Start",IDC_STATIC,17,211,16,8 + RTEXT "End",IDC_STATIC,19,225,14,8 + COMBOBOX IDC_COMBO_TEXTURE,163,67,141,116,CBS_DROPDOWN | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + CONTROL "Point Light",IDC_CHECK_POINT,"Button", + BS_AUTORADIOBUTTON | WS_GROUP,7,7,48,10 + CONTROL "Projected Light",IDC_CHECK_PROJECTED,"Button", + BS_AUTORADIOBUTTON,7,123,63,12 + CONTROL "Center",IDC_CHECK_CENTER,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,11,105,37,10 + EDITTEXT IDC_EDIT_CENTERX,49,104,19,12,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_CENTERY,73,104,19,12,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_CENTERZ,96,104,19,12,ES_AUTOHSCROLL + LTEXT "X",IDC_STATIC,56,96,8,8 + LTEXT "Y",IDC_STATIC,80,96,8,8 + LTEXT "Z",IDC_STATIC,103,96,8,8 + CONTROL "Parallel",IDC_CHECK_PARALLEL,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,11,90,39,10 + CONTROL "Slider1",IDC_SLIDER_BRIGHTNESS,"msctls_trackbar32", + WS_TABSTOP,228,36,76,15 + LTEXT "Brightness",IDC_STATIC,195,39,34,8 + CONTROL "",IDC_LIGHTPREVIEW,"Static",SS_BLACKFRAME | SS_NOTIFY | + SS_SUNKEN,137,91,173,175 + PUSHBUTTON "Apply Different",IDC_APPLY_DIFFERENT,150,270,60,14 +END + +IDD_DLG_CAMERA DIALOGEX 0, 0, 277, 295 +STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "Camera Inspector" +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + DEFPUSHBUTTON "Close",IDOK,220,274,50,14 + LTEXT "Name",IDC_STATIC,7,14,24,8 + EDITTEXT IDC_EDIT_CAM_NAME,32,12,154,12,ES_AUTOHSCROLL + LTEXT "Edit >",IDC_STATIC,11,70,20,8 + COMBOBOX IDC_COMBO_SPLINES,33,68,154,81,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add Target...",IDC_BTN_ADDTARGET,34,54,53,11 + SCROLLBAR IDC_SCROLLBAR_SEGMENT,14,168,165,10 + LTEXT "Length ( seconds )",IDC_STATIC,14,140,60,8 + EDITTEXT IDC_EDIT_LENGTH,75,138,30,12,ES_AUTOHSCROLL + LTEXT "Current Time:",IDC_STATIC,14,156,43,8 + LISTBOX IDC_LIST_EVENTS,14,207,152,65,LBS_USETABSTOPS | + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + GROUPBOX "Time",IDC_STATIC,7,129,200,159 + EDITTEXT IDC_EDIT_SEGMENT,61,154,40,12,ES_RIGHT | ES_AUTOHSCROLL | + ES_READONLY + LTEXT "Events",IDC_STATIC,14,195,23,8 + PUSHBUTTON "Add...",IDC_BTN_ADDEVENT,171,207,32,11 + PUSHBUTTON "Del",IDC_BTN_DELEVENT,171,221,32,11 + DEFPUSHBUTTON "Apply",IDC_APPLY,220,91,50,14 + DEFPUSHBUTTON "Load...",ID_FILE_OPEN,220,26,50,14 + DEFPUSHBUTTON "Save...",ID_FILE_SAVE,220,42,50,14 + DEFPUSHBUTTON "New",ID_FILE_NEW,220,7,50,14 + DEFPUSHBUTTON "Preview",IDC_TESTCAMERA,220,109,50,14 + CONTROL "Track Camera",IDC_CHECK_TRACKCAMERA,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,14,180,61,10 + LTEXT "of",IDC_STATIC,109,155,11,9 + EDITTEXT IDC_EDIT_TOTALSEGMENTS,122,154,35,12,ES_RIGHT | + ES_AUTOHSCROLL | ES_READONLY + GROUPBOX "Path and Target editing",IDC_STATIC,7,43,201,83 + CONTROL "Edit Points",IDC_RADIO_EDITPOINTS,"Button", + BS_AUTORADIOBUTTON | WS_GROUP,13,86,52,9 + CONTROL "Add Points",IDC_RADIO_EDITPOINTS2,"Button", + BS_AUTORADIOBUTTON,13,97,52,9 + PUSHBUTTON "Delete Selected",IDC_BTN_DELETEPOINTS,13,109,61,11 + LTEXT "Type:",IDC_STATIC,7,30,20,9 + EDITTEXT IDC_EDIT_TYPE,32,28,48,12,ES_AUTOHSCROLL | ES_READONLY + PUSHBUTTON "Select All",IDC_BTN_SELECTALL,78,109,61,11 +END + +IDD_DLG_CAMERAEVENT DIALOG 0, 0, 166, 188 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Camera Event" +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Wait",IDC_RADIO_EVENT,"Button",BS_AUTORADIOBUTTON | + WS_GROUP,15,18,31,10 + CONTROL "Target wait",IDC_RADIO9,"Button",BS_AUTORADIOBUTTON,15, + 28,51,10 + CONTROL "Speed",IDC_RADIO10,"Button",BS_AUTORADIOBUTTON,15,38,37, + 10 + CONTROL "Change target",IDC_RADIO5,"Button",BS_AUTORADIOBUTTON, + 15,48,61,10 + CONTROL "Snap target",IDC_RADIO6,"Button",BS_AUTORADIOBUTTON,15, + 58,53,10 + CONTROL "FOV",IDC_RADIO7,"Button",BS_AUTORADIOBUTTON,15,68,30,10 + CONTROL "Cmd",IDC_RADIO8,"Button",BS_AUTORADIOBUTTON,15,78,30,10 + CONTROL "Trigger",IDC_RADIO11,"Button",BS_AUTORADIOBUTTON,15,88, + 38,10 + CONTROL "Stop",IDC_RADIO12,"Button",BS_AUTORADIOBUTTON,15,98,31, + 10 + CONTROL "Switch Cameras",IDC_RADIO13,"Button",BS_AUTORADIOBUTTON, + 15,108,67,10 + CONTROL "Fade Out",IDC_RADIO14,"Button",BS_AUTORADIOBUTTON,15, + 118,45,10 + CONTROL "Fade In",IDC_RADIO15,"Button",BS_AUTORADIOBUTTON,15,128, + 40,10 + CONTROL "Feather",IDC_RADIO16,"Button",BS_AUTORADIOBUTTON,15,139, + 40,10 + EDITTEXT IDC_EDIT_PARAM,7,169,124,12,ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,124,7,35,14 + PUSHBUTTON "Cancel",IDCANCEL,123,24,35,14 + GROUPBOX "Type",IDC_STATIC,7,7,81,147 + LTEXT "Paremeter:",IDC_STATIC,7,159,35,8 +END + +IDD_DLG_CAMERATARGET DIALOG 0, 0, 194, 93 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Camera Target" +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Fixed",IDC_RADIO_FIXED,"Button",BS_AUTORADIOBUTTON | + WS_GROUP,15,38,37,10 + CONTROL "Interpolated",IDC_RADIO2,"Button",BS_AUTORADIOBUTTON,15, + 49,60,11 + CONTROL "Spline",IDC_RADIO3,"Button",BS_AUTORADIOBUTTON,15,61,60, + 11 + DEFPUSHBUTTON "OK",IDOK,146,7,41,11 + PUSHBUTTON "Cancel",IDCANCEL,146,21,41,11 + LTEXT "Name:",IDC_STATIC,7,7,23,10 + EDITTEXT IDC_EDIT_NAME,32,7,105,12,ES_AUTOHSCROLL + GROUPBOX "Type",IDC_STATIC,7,26,130,53 +END + +IDD_DLG_WAIT DIALOGEX 0, 0, 186, 90 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "Please wait..." +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + EDITTEXT IDC_WAITSTR,7,7,172,38,ES_CENTER | ES_MULTILINE | + WS_DISABLED | NOT WS_BORDER + PUSHBUTTON "Cancel",IDCANCEL,68,51,50,14 +END + +IDD_DIALOG_ENTITY DIALOGEX 0, 0, 262, 292 +STYLE DS_SETFONT | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + COMBOBOX IDC_COMBO_CLASS,7,17,212,134,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Gui...",IDC_BUTTON_GUI,213,265,42,14 + PUSHBUTTON "Sound...",IDC_BUTTON_SOUND,213,250,42,14 + PUSHBUTTON "Model...",IDC_BUTTON_MODEL,213,234,42,14 + PUSHBUTTON "Dn",IDC_E_DOWN,58,261,15,15 + PUSHBUTTON "Up",IDC_E_UP,58,245,15,15 + PUSHBUTTON "315",IDC_E_315,37,270,15,15 + PUSHBUTTON "270",IDC_E_270,23,270,15,15 + PUSHBUTTON "225",IDC_E_225,7,270,15,15 + PUSHBUTTON "360",IDC_E_0,37,256,15,15 + PUSHBUTTON "180",IDC_E_180,7,256,15,15 + PUSHBUTTON "45",IDC_E_45,37,240,15,15 + PUSHBUTTON "90",IDC_E_90,23,240,15,15 + PUSHBUTTON "135",IDC_E_135,7,240,15,15 + PUSHBUTTON "X",IDC_BUTTON_BROWSE,244,217,11,11 + EDITTEXT IDC_EDIT_VAL,23,218,217,12,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_KEY,23,203,217,12,ES_AUTOHSCROLL + LISTBOX IDC_LIST_KEYVAL,7,111,248,85,LBS_OWNERDRAWVARIABLE | + LBS_HASSTRINGS | LBS_USETABSTOPS | LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Create",IDC_BUTTON_CREATE,221,18,34,11 + LTEXT "Entity Class",IDC_STATIC_TITLE,7,7,37,8 + LTEXT "Key",IDC_STATIC_KEY,7,205,13,8 + LTEXT "Val",IDC_STATIC_VAL,7,220,11,8 + LISTBOX IDC_LIST_VARS,7,35,248,73,LBS_OWNERDRAWVARIABLE | + LBS_HASSTRINGS | LBS_USETABSTOPS | LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Particle...",IDC_BUTTON_PARTICLE,168,265,42,14 + COMBOBOX IDC_ENTITY_ANIMATIONS,74,234,79,101,CBS_DROPDOWNLIST | + CBS_SORT | WS_VSCROLL | WS_TABSTOP + CONTROL "",IDC_ANIMATION_SLIDER,"msctls_trackbar32",TBS_BOTH | + TBS_NOTICKS | WS_TABSTOP,75,249,100,15 + PUSHBUTTON "Play",IDC_ENTITY_PLAY_ANIM,157,234,19,14 + DEFPUSHBUTTON "Stop",IDC_ENTITY_STOP_ANIM,180,234,20,14 + LTEXT "Static",IDC_ENTITY_CURRENT_ANIM,189,253,19,8 + PUSHBUTTON "Skin...",IDC_BUTTON_SKIN,119,265,42,14 + PUSHBUTTON "Curve...",IDC_BUTTON_CURVE,73,264,42,14 +END + +IDD_DIALOG_COLORS DIALOG 0, 0, 383, 206 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Color Picker" +FONT 8, "Tahoma" +BEGIN + GROUPBOX "RGB",IDC_STATIC_RGB_RECT,7,3,138,157 + GROUPBOX "HSBO",IDC_STATIC_HSB_RECT,150,3,180,157 + EDITTEXT IDC_EDIT_RED,13,181,32,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SPIN_RED,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,37,179,11, + 14 + EDITTEXT IDC_EDIT_GREEN,51,182,33,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SPIN_GREEN,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,77,180,11, + 14 + EDITTEXT IDC_EDIT_BLUE,91,182,33,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SPIN_BLUE,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,117,180, + 11,14 + EDITTEXT IDC_EDIT_HUE,156,182,33,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SPIN_HUE,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,181,180, + 11,14 + EDITTEXT IDC_EDIT_SAT,195,182,33,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SPIN_SAT,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,221,180, + 11,14 + EDITTEXT IDC_EDIT_VAL,235,182,31,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SPIN_VAL,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,259,180, + 11,14 + PUSHBUTTON "Old Color...",IDC_BTN_OLDCOLOR,332,188,47,14 + DEFPUSHBUTTON "OK",IDOK,339,7,38,14 + PUSHBUTTON "Cancel",IDCANCEL,339,24,38,14 + LTEXT "Saturation",IDC_STATIC,196,173,33,8 + GROUPBOX "RGB Values",IDC_STATIC,7,163,137,39 + LTEXT "Red",IDC_STATIC,15,172,14,8 + LTEXT "Green",IDC_STATIC,51,173,20,8 + LTEXT "Blue",IDC_STATIC,91,173,15,8 + GROUPBOX "HSBO Values",IDC_STATIC,152,163,169,39 + LTEXT "Hue",IDC_STATIC,158,174,14,8 + LTEXT "Brightness",IDC_STATIC,236,173,34,8 + PUSHBUTTON "1",IDC_BUTTON_COLOR1,335,70,14,13 + PUSHBUTTON "2",IDC_BUTTON_COLOR2,359,70,14,13 + EDITTEXT IDC_EDIT_OVERBRIGHT,277,182,31,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SPIN_OVERBRIGHT,"msctls_updown32", + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,301,180,11,14 + LTEXT "Overbright",IDC_STATIC,278,173,36,8 + LTEXT "Static",IDC_STATIC_NEWCOLOR,343,156,22,18,WS_BORDER + LTEXT "Presets, click to assign",IDC_STATIC,333,44,44,18 +END + +IDD_DIALOG_INSPECTORS DIALOGEX 0, 0, 330, 327 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_THICKFRAME +CAPTION "Inspectors" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_TAB_INSPECTOR,"SysTabControl32",TCS_BOTTOM | + TCS_HOTTRACK | TCS_MULTILINE | TCS_TOOLTIPS | + TCS_FOCUSNEVER,7,307,316,13 +END + +IDD_DIALOG_TEXTURE DIALOGEX 0, 0, 274, 335 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_POPUP | WS_SYSMENU +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "Custom1",IDC_CUSTOM1,"",WS_TABSTOP,7,7,260,321 +END + +IDD_DIALOG_GETSTRING DIALOGEX 0, 0, 207, 114 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "Input needed..." +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,32,93,50,14 + PUSHBUTTON "Cancel",IDCANCEL,121,93,50,14 + LTEXT "Static",IDC_PROMPT,7,7,193,47 + EDITTEXT IDC_EDIT1,11,66,185,15,ES_AUTOHSCROLL +END + +IDD_ENTFINDREPLACE DIALOGEX 0, 0, 425, 129 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "Entity key Find / Replace (F3 = find next)" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + PUSHBUTTON "Cancel",IDCANCEL,164,108,87,14 + PUSHBUTTON "Find",IDC_FIND,33,108,121,14 + PUSHBUTTON "Replace",IDC_REPLACE,259,108,121,14 + GROUPBOX "FIND",IDC_STATIC,7,10,177,84,BS_CENTER + EDITTEXT IDC_EDIT_FIND_KEY,39,22,121,14,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_FIND_VALUE,39,45,121,14,ES_AUTOHSCROLL + RTEXT "Key",IDC_STATIC,23,25,13,8 + RTEXT "Value",IDC_STATIC,7,46,29,8 + GROUPBOX "FIND",IDC_STATIC,226,10,177,84,BS_CENTER + EDITTEXT IDC_EDIT_REPLACE_KEY,258,23,121,14,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_REPLACE_VALUE,258,45,121,14,ES_AUTOHSCROLL + RTEXT "Key",IDC_STATIC,242,26,13,8 + RTEXT "Value",IDC_STATIC,234,47,21,8 + PUSHBUTTON "->",IDC_KEYCOPY,190,22,31,14 + PUSHBUTTON "->",IDC_VALUECOPY,190,46,31,14 + CONTROL "Whole-string match only", + IDC_CHECK_FIND_WHOLESTRINGMATCHONLY,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,41,64,93,10 + CONTROL "Select all matching ents at once", + IDC_CHECK_SELECTALLMATCHING,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,41,77,117,10 +END + +IDD_DIALOG_PREVIEW DIALOGEX 0, 0, 328, 322 +STYLE DS_SYSMODAL | DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | + WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_TOPMOST +CAPTION "Dialog" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,285,301,36,14 + PUSHBUTTON "Cancel",IDCANCEL,246,301,36,14 + CONTROL "",IDC_TREE_MEDIA,"SysTreeView32",TVS_HASBUTTONS | + TVS_HASLINES | TVS_LINESATROOT | TVS_DISABLEDRAGDROP | + TVS_SHOWSELALWAYS | TVS_NOTOOLTIPS | WS_BORDER | + WS_TABSTOP,7,7,314,165 + CONTROL "",IDC_PREVIEW,"Static",SS_BLACKFRAME | SS_NOTIFY | + SS_SUNKEN,7,175,173,140 + EDITTEXT IDC_EDIT_INFO,185,175,136,118,ES_MULTILINE | + ES_AUTOVSCROLL | ES_READONLY + PUSHBUTTON "Reload",IDC_BUTTON_RELOAD,185,301,36,14 + PUSHBUTTON "Play",IDC_BUTTON_PLAY,7,175,36,14 +END + +IDD_DIALOG_CONSOLE DIALOGEX 0, 0, 295, 292 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + EDITTEXT IDC_EDIT_CONSOLE,7,7,281,261,ES_MULTILINE | WS_VSCROLL | + NOT WS_TABSTOP + EDITTEXT IDC_EDIT_INPUT,7,273,281,12,ES_AUTOHSCROLL | + ES_WANTRETURN +END + +IDD_DIALOG_COMMENTS DIALOGEX 0, 0, 225, 115 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "Commented Item" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,177,7,41,14 + PUSHBUTTON "Cancel",IDCANCEL,177,25,41,14 + LTEXT "Name:",IDC_STATIC,7,9,22,8 + EDITTEXT IDC_EDIT_NAME,33,7,135,12,ES_AUTOHSCROLL + LTEXT "Path:",IDC_STATIC,7,23,18,8 + EDITTEXT IDC_EDIT_PATH,33,21,135,12,ES_AUTOHSCROLL + LTEXT "Comments:",IDC_STATIC,7,38,37,8 + EDITTEXT IDC_EDIT_COMMENTS,48,37,122,71,ES_MULTILINE | + ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL +END + +IDD_DIALOG_EDITVIEW DIALOGEX 0, 0, 546, 367 +STYLE DS_SETFONT | DS_NOIDLEMSG | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | + WS_SYSMENU | WS_THICKFRAME +EXSTYLE WS_EX_WINDOWEDGE +CAPTION "Media Editor" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + PUSHBUTTON "Close",IDOK,503,7,36,14 + EDITTEXT IDC_EDIT_INFO,7,25,532,313,ES_MULTILINE | ES_AUTOVSCROLL | + ES_NOHIDESEL | ES_WANTRETURN | WS_VSCROLL | WS_HSCROLL + PUSHBUTTON "Save",IDC_BUTTON_SAVE,46,7,36,14 + PUSHBUTTON "Open...",IDC_BUTTON_OPEN,7,7,36,14 + LTEXT "Current Line:",IDC_STATIC_LINE,105,348,43,8 + DEFPUSHBUTTON "Goto",IDC_BUTTON_GOTO,7,346,36,14 + EDITTEXT IDC_EDIT_GOTO,45,347,40,12,ES_AUTOHSCROLL + EDITTEXT IDC_EDIT_LINE,152,348,40,12,ES_AUTOHSCROLL | ES_READONLY | + NOT WS_BORDER +END + +IDD_DIALOG_EDITPREVIEW DIALOGEX 0, 0, 328, 322 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CAPTION | WS_THICKFRAME +EXSTYLE WS_EX_OVERLAPPEDWINDOW +CAPTION "Media Preview" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_PREVIEW,"Static",SS_BLACKFRAME | SS_NOTIFY | + SS_SUNKEN,7,7,314,308 +END + +IDD_DIALOG_NEWCURVE DIALOGEX 0, 0, 186, 90 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "Edit Curve Type" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,129,7,50,14 + PUSHBUTTON "Cancel",IDCANCEL,129,24,50,14 + COMBOBOX IDC_COMBO_CURVES,8,7,114,200,CBS_DROPDOWN | CBS_SORT | + WS_VSCROLL | WS_TABSTOP +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "id Software" + VALUE "FileDescription", "Quake 4 Radiant" + VALUE "FileVersion", "1, 0, 0, 1" + VALUE "InternalName", "Toolsx86" + VALUE "LegalCopyright", "Copyright (C) 2004 id Software" + VALUE "OriginalFilename", "Toolsx86.dll" + VALUE "ProductName", "Quake 4" + VALUE "ProductVersion", "1, 0, 0, 1" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_GAMMA, DIALOG + BEGIN + RIGHTMARGIN, 127 + BOTTOMMARGIN, 76 + END + + IDD_ROTATE, DIALOG + BEGIN + BOTTOMMARGIN, 65 + END + + IDD_SIDES, DIALOG + BEGIN + RIGHTMARGIN, 126 + BOTTOMMARGIN, 45 + END + + IDD_SURFACE, DIALOG + BEGIN + LEFTMARGIN, 2 + RIGHTMARGIN, 446 + BOTTOMMARGIN, 234 + END + + IDD_DLG_PREFS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 379 + TOPMARGIN, 4 + BOTTOMMARGIN, 256 + END + + IDD_DLG_MAPINFO, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 174 + TOPMARGIN, 7 + BOTTOMMARGIN, 176 + END + + IDD_DLG_ENTITYLIST, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 485 + TOPMARGIN, 7 + BOTTOMMARGIN, 237 + END + + IDD_DLG_SCRIPTS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 205 + TOPMARGIN, 7 + BOTTOMMARGIN, 208 + END + + IDD_DLG_NEWPROJECT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 239 + TOPMARGIN, 7 + BOTTOMMARGIN, 67 + END + + IDD_DLG_COMMANDLIST, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 276 + TOPMARGIN, 7 + BOTTOMMARGIN, 216 + END + + IDD_DIALOG_SCALE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 115 + TOPMARGIN, 7 + BOTTOMMARGIN, 67 + END + + IDD_DIALOG_FINDREPLACE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 215 + TOPMARGIN, 7 + BOTTOMMARGIN, 80 + END + + IDD_DIALOG_STAIRS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 189 + TOPMARGIN, 7 + BOTTOMMARGIN, 136 + END + + IDD_DIALOG_INPUT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 164 + TOPMARGIN, 7 + BOTTOMMARGIN, 166 + END + + IDD_DLG_INFORMATION, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 88 + END + + IDD_ADDCMD, DIALOG + BEGIN + BOTTOMMARGIN, 60 + END + + IDD_DIALOG_TEXTURELIST, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 307 + TOPMARGIN, 7 + BOTTOMMARGIN, 311 + END + + IDD_DIALOG_NEWPATCH, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 122 + TOPMARGIN, 7 + BOTTOMMARGIN, 51 + END + + IDD_DIALOG_TEXTURELAYOUT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 88 + END + + IDD_DIALOG_CAP, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 164 + TOPMARGIN, 7 + BOTTOMMARGIN, 77 + END + + IDD_DIALOG_THICKEN, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 197 + TOPMARGIN, 7 + BOTTOMMARGIN, 56 + END + + IDD_DIALOG_PATCH, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 265 + TOPMARGIN, 7 + BOTTOMMARGIN, 171 + END + + IDD_TEXLIST, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 244 + TOPMARGIN, 7 + BOTTOMMARGIN, 266 + END + + IDD_DLG_GROUP, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 217 + TOPMARGIN, 7 + BOTTOMMARGIN, 233 + END + + IDD_DIALOG_LIGHT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 304 + TOPMARGIN, 7 + BOTTOMMARGIN, 284 + END + + IDD_DLG_CAMERA, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 270 + TOPMARGIN, 7 + BOTTOMMARGIN, 288 + END + + IDD_DLG_CAMERAEVENT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 159 + TOPMARGIN, 7 + BOTTOMMARGIN, 181 + END + + IDD_DLG_CAMERATARGET, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 187 + TOPMARGIN, 7 + BOTTOMMARGIN, 86 + END + + IDD_DLG_WAIT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 83 + END + + IDD_DIALOG_ENTITY, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 255 + TOPMARGIN, 7 + BOTTOMMARGIN, 285 + END + + IDD_DIALOG_INSPECTORS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 323 + TOPMARGIN, 7 + BOTTOMMARGIN, 320 + END + + IDD_DIALOG_TEXTURE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 267 + TOPMARGIN, 7 + BOTTOMMARGIN, 328 + END + + IDD_DIALOG_PREVIEW, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 321 + TOPMARGIN, 7 + BOTTOMMARGIN, 315 + END + + IDD_DIALOG_CONSOLE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 288 + TOPMARGIN, 7 + BOTTOMMARGIN, 285 + END + + IDD_DIALOG_COMMENTS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 218 + TOPMARGIN, 7 + BOTTOMMARGIN, 108 + END + + IDD_DIALOG_EDITVIEW, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 539 + TOPMARGIN, 7 + BOTTOMMARGIN, 360 + END + + IDD_DIALOG_EDITPREVIEW, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 321 + TOPMARGIN, 7 + BOTTOMMARGIN, 315 + END + + IDD_DIALOG_NEWCURVE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 83 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog Info +// + +IDD_DIALOG_NEWCURVE DLGINIT +BEGIN + IDC_COMBO_CURVES, 0x403, 17, 0 +0x6143, 0x6d74, 0x6c75, 0x526c, 0x6d6f, 0x7053, 0x696c, 0x656e, "\000" + IDC_COMBO_CURVES, 0x403, 6, 0 +0x754e, 0x6272, 0x0073, + 0 +END + +IDD_DIALOG_NEWPATCH DLGINIT +BEGIN + IDC_COMBO_WIDTH, 0x403, 2, 0 +0x0033, + IDC_COMBO_WIDTH, 0x403, 2, 0 +0x0035, + IDC_COMBO_WIDTH, 0x403, 2, 0 +0x0037, + IDC_COMBO_WIDTH, 0x403, 2, 0 +0x0039, + IDC_COMBO_WIDTH, 0x403, 3, 0 +0x3131, "\000" + IDC_COMBO_WIDTH, 0x403, 3, 0 +0x3331, "\000" + IDC_COMBO_WIDTH, 0x403, 3, 0 +0x3531, "\000" + IDC_COMBO_HEIGHT, 0x403, 2, 0 +0x0033, + IDC_COMBO_HEIGHT, 0x403, 2, 0 +0x0035, + IDC_COMBO_HEIGHT, 0x403, 2, 0 +0x0037, + IDC_COMBO_HEIGHT, 0x403, 2, 0 +0x0039, + IDC_COMBO_HEIGHT, 0x403, 3, 0 +0x3131, "\000" + IDC_COMBO_HEIGHT, 0x403, 3, 0 +0x3331, "\000" + IDC_COMBO_HEIGHT, 0x403, 3, 0 +0x3531, "\000" + 0 +END + +IDD_DLG_PREFS DLGINIT +BEGIN + IDC_COMBO_SHADERS, 0x403, 5, 0 +0x6f4e, 0x656e, "\000" + IDC_COMBO_SHADERS, 0x403, 7, 0 +0x6f43, 0x6d6d, 0x6e6f, "\000" + IDC_COMBO_SHADERS, 0x403, 4, 0 +0x6c41, 0x006c, + 0 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDR_MAINFRAME "Quake 4 Radiant" + IDR_RADIANTYPE "\nRadian\nRadian\n\n\nRadiant.Document\nRadian Document" + IDR_SHADERTYPE "\nUntitled\nSHADER Document\nShader Files (*.shader)\n.shader\nShaderFileType\nSHADER File Type\nSHADER\nShader Files\n" +END + +STRINGTABLE +BEGIN + AFX_IDS_APP_TITLE "Quake 4 Radiant" + AFX_IDS_IDLEMESSAGE "Ready" +END + +STRINGTABLE +BEGIN + ID_INDICATOR_EXT "EXT" + ID_INDICATOR_CAPS "CAP" + ID_INDICATOR_NUM "NUM" + ID_INDICATOR_SCRL "SCRL" + ID_INDICATOR_OVR "OVR" + ID_INDICATOR_REC "REC" +END + +STRINGTABLE +BEGIN + ID_FILE_NEW "Create a new document\nNew" + ID_FILE_OPEN "Open an existing map\nOpen" + ID_FILE_CLOSE "Close the active document\nClose" + ID_FILE_SAVE "Save the active map\nSave" + ID_FILE_SAVE_AS "Save the active document with a new name\nSave As" + ID_FILE_PAGE_SETUP "Change the printing options\nPage Setup" + ID_FILE_PRINT_SETUP "Change the printer and printing options\nPrint Setup" + ID_FILE_PRINT "Print the active document\nPrint" + ID_FILE_PRINT_PREVIEW "Display full pages\nPrint Preview" +END + +STRINGTABLE +BEGIN + ID_APP_ABOUT "Display program information, version number and copyright\nAbout" + ID_APP_EXIT "Quit the application; prompts to save documents\nExit" +END + +STRINGTABLE +BEGIN + ID_FILE_MRU_FILE1 "Open this document" + ID_FILE_MRU_FILE2 "Open this document" + ID_FILE_MRU_FILE3 "Open this document" + ID_FILE_MRU_FILE4 "Open this document" + ID_FILE_MRU_FILE5 "Open this document" + ID_FILE_MRU_FILE6 "Open this document" + ID_FILE_MRU_FILE7 "Open this document" + ID_FILE_MRU_FILE8 "Open this document" + ID_FILE_MRU_FILE9 "Open this document" + ID_FILE_MRU_FILE10 "Open this document" + ID_FILE_MRU_FILE11 "Open this document" + ID_FILE_MRU_FILE12 "Open this document" + ID_FILE_MRU_FILE13 "Open this document" + ID_FILE_MRU_FILE14 "Open this document" + ID_FILE_MRU_FILE15 "Open this document" + ID_FILE_MRU_FILE16 "Open this document" +END + +STRINGTABLE +BEGIN + ID_NEXT_PANE "Switch to the next window pane\nNext Pane" + ID_PREV_PANE "Switch back to the previous window pane\nPrevious Pane" +END + +STRINGTABLE +BEGIN + ID_WINDOW_NEW "Open another window for the active document\nNew Window" + ID_WINDOW_ARRANGE "Arrange icons at the bottom of the window\nArrange Icons" + ID_WINDOW_CASCADE "Arrange windows so they overlap\nCascade Windows" + ID_WINDOW_TILE_HORZ "Arrange windows as non-overlapping tiles\nTile Windows" + ID_WINDOW_TILE_VERT "Arrange windows as non-overlapping tiles\nTile Windows" + ID_WINDOW_SPLIT "Split the active window into panes\nSplit" +END + +STRINGTABLE +BEGIN + ID_EDIT_CLEAR "Erase the selection\nErase" + ID_EDIT_CLEAR_ALL "Erase everything\nErase All" + ID_EDIT_COPY "Copy the selection and put it on the Clipboard\nCopy" + ID_EDIT_CUT "Cut the selection and put it on the Clipboard\nCut" + ID_EDIT_FIND "Find the specified text\nFind" + ID_EDIT_PASTE "Insert Clipboard contents\nPaste" + ID_EDIT_REPEAT "Repeat the last action\nRepeat" + ID_EDIT_REPLACE "Replace specific text with different text\nReplace" + ID_EDIT_SELECT_ALL "Select the entire document\nSelect All" + ID_EDIT_UNDO "Undo the last action\nUndo" + ID_EDIT_REDO "Redo the previously undone action\nRedo" +END + +STRINGTABLE +BEGIN + ID_VIEW_TOOLBAR "Show or hide the toolbar\nToggle ToolBar" + ID_VIEW_STATUS_BAR "Show or hide the status bar\nToggle StatusBar" +END + +STRINGTABLE +BEGIN + AFX_IDS_SCSIZE "Change the window size" + AFX_IDS_SCMOVE "Change the window position" + AFX_IDS_SCMINIMIZE "Reduce the window to an icon" + AFX_IDS_SCMAXIMIZE "Enlarge the window to full size" + AFX_IDS_SCNEXTWINDOW "Switch to the next document window" + AFX_IDS_SCPREVWINDOW "Switch to the previous document window" + AFX_IDS_SCCLOSE "Close the active window and prompts to save the documents" +END + +STRINGTABLE +BEGIN + AFX_IDS_SCRESTORE "Restore the window to normal size" + AFX_IDS_SCTASKLIST "Activate Task List" + AFX_IDS_MDICHILD "Activate this window" +END + +STRINGTABLE +BEGIN + AFX_IDS_PREVIEW_CLOSE "Close print preview mode\nCancel Preview" +END + +STRINGTABLE +BEGIN + ID_VIEW_XY "View original layout (Top)" + ID_VIEW_SIDE "Side view" + ID_VIEW_FRONT "Front view" + ID_VIEW_CAMERATOGGLE "Toggle full time camera preview\nCamera preview" + ID_TEXTURES_POPUP "Texture view mode\nTexture view mode" + ID_POPUP_SELECTION "Selection\nSelection" + ID_VIEW_CHANGE "Change views\nChange views" + ID_VIEW_CAMERAUPDATE "Update Camera\nUpdate Camera" + ID_VIEW_CLIPPER "Set clipper mode\nClipper" + ID_PREFS "Preferences\nPreferences" + ID_EDIT_MAPINFO "Entity Information\nEntity list" + ID_BRUSH_SCRIPTS "Define and run BrushScripts\nBrushScripts" +END + +STRINGTABLE +BEGIN + ID_HELP_COMMANDLIST "Provides a list of the currently bound commands" +END + +STRINGTABLE +BEGIN + ID_SELECTION_SELECTINSIDE "Select Inside\nSelect Inside" +END + +STRINGTABLE +BEGIN + ID_VIEW_ENTITY "Toggle entity inspector\nEntity inspector" +END + +STRINGTABLE +BEGIN + ID_SELECT_BRUSHESONLY "Brushes are your friend, only select them\nOnly select brushes." + ID_SELECT_BYBOUNDINGBRUSH + "Select primitives by bounding brush\nSelect primitives by bounding brush" + ID_VIEW_RENDERMODE "Camera view shows render data\nCamera view shows render data" + ID_VIEW_REBUILDRENDERDATA + "Rebuild render data now\nRebuild render data now" + ID_VIEW_REALTIMEREBUILD "Rebuild render data in realtime\nRebuild render data in realtime" + ID_VIEW_RENDERENTITYOUTLINES + "Render light entities in render mode\nRender light entities in render mode" + ID_VIEW_MATERIALANIMATION + "Render material (shader) animation\nRender material (shader) animation" +END + +STRINGTABLE +BEGIN + ID_SELECT_NOMODELS "Don't select models\nDon't select models" + ID_VIEW_RENDERSOUND "Render sound\nRender sound" + ID_SOUND_POPUP "Show sound volumes when selected\nShow sound volumes when selected" + ID_SOUND_SHOWSOUNDVOLUMES + "Always show sound volumes\nAlways show sound volumes" + ID_SPLINES_EDITPOINTS "Edit Curve Points\nEdit Curve Points" + ID_SPLINES_ADDPOINTS "Add Curve Points\nAdd Curve Points" + ID_SPLINES_INSERTPOINTS "Insert Curve Point\nInsert Curve Point" + ID_SPLINES_DELETEPOINTS "Delete Curve Point\nDelete Curve Point" +END + +STRINGTABLE +BEGIN + ID_BRUSH_FLIPX "Flip selected brushes along x-axis\nx-axis Flip" + ID_BRUSH_FLIPY "Flip selected brushes along y-axis\ny-axis Flip" + ID_BRUSH_FLIPZ "Flip selected brushes along z-axis\nz-axis Flip" + ID_BRUSH_ROTATEX "Rotate selected brushes along x-axis\nx-axis Rotate" + ID_BRUSH_ROTATEY "Rotate selected brushes along y-axis\ny-axis Rotate" + ID_BRUSH_ROTATEZ "Rotate selected brushes along z-axis\nz-axis Rotate" +END + +STRINGTABLE +BEGIN + ID_SELECTION_MAKEHOLLOW "Hollow Selection\nHollow" + ID_SELECTION_SELECTPARTIALTALL "Select Partial Tall\nSelect Partial Tall" +END + +STRINGTABLE +BEGIN + ID_SELECTION_SELECTCOMPLETETALL "Complete Tall\nComplete Tall" + ID_SELECTION_CSGSUBTRACT "CSG Subtract\nCSG Subtract" + ID_SELECTION_SELECTTOUCHING "Select Touching\nSelect Touching" +END + +STRINGTABLE +BEGIN + ID_SELECT_MOUSEROTATE "Free rotation\nFree Rotation" + ID_TEXTURE_REPLACESELECTED + "Find and replace texture names in selected brushes/faces" + ID_TEXTURE_REPLACEALL "Find and replace texture names in all brushes/faces" + ID_SELECT_COMPLETE_ENTITY + "Select the entire entity from the currently selected brushes." + ID_SCALELOCKX "Scale X\nScale X" + ID_SCALELOCKY "Scale Y\nScale Y" + ID_SCALELOCKZ "Scale Z\nScale Z" + ID_VIEW_CUBICCLIPPING "Cubic clip the camera view\nCubic clip the camera view" + ID_FILE_PROJECTSETTINGS "View and edit project attributes" + ID_VIEW_CUBEOUT "Zoom cubic clip out" + ID_VIEW_CUBEIN "Zoom cubic clip in" +END + +STRINGTABLE +BEGIN + ID_FILE_SAVEREGION "Save defined region" + ID_FILE_LOADREGION "Load saved region" + ID_TOOLBAR_MAIN "Standard toolbar" + ID_TOOLBAR_TEXTURE "Texture control toolbar" + ID_TEXTURES_LOAD "Load from a specific directory" + ID_CURVE_CYLINDER "Create a cylinder" +END + +STRINGTABLE +BEGIN + ID_FILE_IMPORTMAP "Load map, leaving current map intact" + ID_FILE_EXPORTMAP "Save selection into map file" + ID_EDIT_LOADPREFAB "Load .map from prefab path" + ID_VIEW_SHOWCURVES "Show Curved brushes" + ID_TEXTURES_LOADLIST "Loads from material list" + ID_DONTSELECTCURVE "Select all primitives with bounding brushes\nSelect all primitives with bounding brushes" +END + +STRINGTABLE +BEGIN + ID_CONVERTCURVES "Turns selected brush into a new test curve" + ID_PATCH_SHOWBOUNDINGBOX + "Show primitive bounding box\nShow primitive bounding box" + ID_CURVE_SIMPLEPATCHMESH "Builds a flat patch mesh, you specify the size" + ID_PATCH_WIREFRAME "Show patches as wireframes\nShow patches as wireframes" + ID_PATCH_WELD "Welds equal patch points during moves\nWelds equal patch points during moves" + ID_CURVE_PATCHTUBE "Create a cylinder" + ID_CURVE_PATCHENDCAP "Create an endcap" + ID_CURVE_PATCHBEVEL "Create a bevel" + ID_PATCH_DRILLDOWN "Selects drill down rows and columns\nSelects drill down rows and columns" + ID_CURVE_LOADPATCHFILE "Load corresponding .patch file\nLoad corresponding .patch file" + ID_CURVE_INSERTROW "Inserts a row in the selected patch(s)\nInserts a row in the selected patch(s)" + ID_CURVE_INSERTCOLUMN "Inserts a column in the selected patch(s)\nInserts a column in the selected patch(s)" +END + +STRINGTABLE +BEGIN + ID_CURVE_DELETEROW "Deletes a row in the current patch(s)\nDeletes a row in the current patch(s)" + ID_CURVE_DELETECOLUMN "Deletes a column in the current Patch(s)\nDeletes a column in the current Patch(s)" + ID_PATCH_INSDEL "Redisperse patch points\nRedisperse patch points" + ID_CURVE_INSERT_ADDCOLUMN + "Add a column to the end of the patch\nAdd a column to the end of the patch" + ID_CURVE_INSERT_INSERTCOLUMN + "Insert a column at the beginning of the patch\nInsert a column at the beginning of the patch" + ID_CURVE_INSERT_ADDROW "Add a row at the end of the patch\nAdd a row at the end of the patch" + ID_CURVE_INSERT_INSERTROW + "Insert a row at the beginning of the patch\nInsert a row at the beginning of the patch" + ID_CURVE_DELETE_FIRSTCOLUMN + "Delete the first (2) columns\nDelete the first (2) columns" + ID_CURVE_DELETE_LASTCOLUMN + "Delete the last (2) columns\nDelete the last (2) columns" + ID_CURVE_DELETE_FIRSTROW + "Delete the first (2) rows\nDelete the first (2) rows" + ID_CURVE_DELETE_LASTROW "Delete the last (2) rows\nDelete the last (2) rows" + ID_CURVE_NEGATIVE "Toggle negative flag\nToggle negative flag" + ID_PATCH_BEND "Patch Bend mode\nPatch Bend mode" + ID_CURVE_PATCHDENSETUBE "Create a dense tube" + ID_CURVE_PATCHVERYDENSETUBE "Create a very dense cylinder" + ID_CURVE_CAP "Put caps on the current patch\nPut caps on the current patch" +END + +STRINGTABLE +BEGIN + ID_CURVE_REDISPERSE_ROWS + "Re-disperse rows across the dimensions of the patch" + ID_CURVE_REDISPERSE_COLS + "Re-disperse columns across the dimensions of the patch" + ID_CURVE_PATCHSQUARE "Create a very square cylinder" + ID_TEXTURES_FLUSH "Flush texture palette" +END + +STRINGTABLE +BEGIN + ID_TEXTURES_RELOADSHADERS + "Reload Shaders and apply visual changes to current map" + ID_SHOW_ENTITIES "Show models as\nShow Models as" +END + +STRINGTABLE +BEGIN + ID_VIEW_OPENGLLIGHTING "Toggle OpenGL Lighting" + ID_SELECTION_CSGMERGE "CSG Merge\nCSG Merge" +END + +STRINGTABLE +BEGIN + ID_SHOW_LIGHTVOLUMES "Show Light Volumes\nShow Light Volumes" + ID_SHOW_DOOM "Enable DOOM\n" + ID_SELECTION_MOVEONLY "Move selection only ( No size )\nMove selection only ( No size )" +END + +STRINGTABLE +BEGIN + ID_SPLINES_POPUP "Spline options\nSpline options" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/src/sys/win32/rc/res/BEVEL.BMP b/src/sys/win32/rc/res/BEVEL.BMP new file mode 100644 index 0000000..5c2dcd6 Binary files /dev/null and b/src/sys/win32/rc/res/BEVEL.BMP differ diff --git a/src/sys/win32/rc/res/BITMAP2.BMP b/src/sys/win32/rc/res/BITMAP2.BMP new file mode 100644 index 0000000..7982fa2 Binary files /dev/null and b/src/sys/win32/rc/res/BITMAP2.BMP differ diff --git a/src/sys/win32/rc/res/BMP00001.BMP b/src/sys/win32/rc/res/BMP00001.BMP new file mode 100644 index 0000000..bc656df Binary files /dev/null and b/src/sys/win32/rc/res/BMP00001.BMP differ diff --git a/src/sys/win32/rc/res/BMP0002.BMP b/src/sys/win32/rc/res/BMP0002.BMP new file mode 100644 index 0000000..50ab191 Binary files /dev/null and b/src/sys/win32/rc/res/BMP0002.BMP differ diff --git a/src/sys/win32/rc/res/DEFTEX.WAL b/src/sys/win32/rc/res/DEFTEX.WAL new file mode 100644 index 0000000..498b56a Binary files /dev/null and b/src/sys/win32/rc/res/DEFTEX.WAL differ diff --git a/src/sys/win32/rc/res/ENDCAP.BMP b/src/sys/win32/rc/res/ENDCAP.BMP new file mode 100644 index 0000000..cb25691 Binary files /dev/null and b/src/sys/win32/rc/res/ENDCAP.BMP differ diff --git a/src/sys/win32/rc/res/GetString.htm b/src/sys/win32/rc/res/GetString.htm new file mode 100644 index 0000000..fd2b1b1 --- /dev/null +++ b/src/sys/win32/rc/res/GetString.htm @@ -0,0 +1,19 @@ + + + + + + + + + + +
+
+ +
+TODO: Place controls here. +
+ + + \ No newline at end of file diff --git a/src/sys/win32/rc/res/IBEVEL.BMP b/src/sys/win32/rc/res/IBEVEL.BMP new file mode 100644 index 0000000..4624f5d Binary files /dev/null and b/src/sys/win32/rc/res/IBEVEL.BMP differ diff --git a/src/sys/win32/rc/res/IENDCAP.BMP b/src/sys/win32/rc/res/IENDCAP.BMP new file mode 100644 index 0000000..892bbde Binary files /dev/null and b/src/sys/win32/rc/res/IENDCAP.BMP differ diff --git a/src/sys/win32/rc/res/MEFileToolbar.bmp b/src/sys/win32/rc/res/MEFileToolbar.bmp new file mode 100644 index 0000000..42d18e7 Binary files /dev/null and b/src/sys/win32/rc/res/MEFileToolbar.bmp differ diff --git a/src/sys/win32/rc/res/MEtoolbar.bmp b/src/sys/win32/rc/res/MEtoolbar.bmp new file mode 100644 index 0000000..a49be78 Binary files /dev/null and b/src/sys/win32/rc/res/MEtoolbar.bmp differ diff --git a/src/sys/win32/rc/res/MaterialEditor.ico b/src/sys/win32/rc/res/MaterialEditor.ico new file mode 100644 index 0000000..be1cee6 Binary files /dev/null and b/src/sys/win32/rc/res/MaterialEditor.ico differ diff --git a/src/sys/win32/rc/res/PropTree.rc2 b/src/sys/win32/rc/res/PropTree.rc2 new file mode 100644 index 0000000..83f74ca --- /dev/null +++ b/src/sys/win32/rc/res/PropTree.rc2 @@ -0,0 +1,13 @@ +// +// PROPTREE.RC2 - resources Microsoft Visual C++ does not edit directly +// + +#ifdef APSTUDIO_INVOKED + #error this file is not editable by Microsoft Visual C++ +#endif //APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// Add manually edited resources here... + +///////////////////////////////////////////////////////////////////////////// diff --git a/src/sys/win32/rc/res/Q.BMP b/src/sys/win32/rc/res/Q.BMP new file mode 100644 index 0000000..4894e0b Binary files /dev/null and b/src/sys/win32/rc/res/Q.BMP differ diff --git a/src/sys/win32/rc/res/RADIANT3.GIF b/src/sys/win32/rc/res/RADIANT3.GIF new file mode 100644 index 0000000..adbe1f5 Binary files /dev/null and b/src/sys/win32/rc/res/RADIANT3.GIF differ diff --git a/src/sys/win32/rc/res/Radiant.ico b/src/sys/win32/rc/res/Radiant.ico new file mode 100644 index 0000000..f644e9a Binary files /dev/null and b/src/sys/win32/rc/res/Radiant.ico differ diff --git a/src/sys/win32/rc/res/RadiantDoc.ico b/src/sys/win32/rc/res/RadiantDoc.ico new file mode 100644 index 0000000..3288c6f Binary files /dev/null and b/src/sys/win32/rc/res/RadiantDoc.ico differ diff --git a/src/sys/win32/rc/res/TOOLBAR1.BMP b/src/sys/win32/rc/res/TOOLBAR1.BMP new file mode 100644 index 0000000..55e05ec Binary files /dev/null and b/src/sys/win32/rc/res/TOOLBAR1.BMP differ diff --git a/src/sys/win32/rc/res/TOOLBAR2.BMP b/src/sys/win32/rc/res/TOOLBAR2.BMP new file mode 100644 index 0000000..dfa5dd1 Binary files /dev/null and b/src/sys/win32/rc/res/TOOLBAR2.BMP differ diff --git a/src/sys/win32/rc/res/Toolbar.bmp b/src/sys/win32/rc/res/Toolbar.bmp new file mode 100644 index 0000000..1956dd5 Binary files /dev/null and b/src/sys/win32/rc/res/Toolbar.bmp differ diff --git a/src/sys/win32/rc/res/VIEWDEFA.BMP b/src/sys/win32/rc/res/VIEWDEFA.BMP new file mode 100644 index 0000000..30233a8 Binary files /dev/null and b/src/sys/win32/rc/res/VIEWDEFA.BMP differ diff --git a/src/sys/win32/rc/res/VIEWOPPO.BMP b/src/sys/win32/rc/res/VIEWOPPO.BMP new file mode 100644 index 0000000..afa4270 Binary files /dev/null and b/src/sys/win32/rc/res/VIEWOPPO.BMP differ diff --git a/src/sys/win32/rc/res/bmp00002.bmp b/src/sys/win32/rc/res/bmp00002.bmp new file mode 100644 index 0000000..6d77432 Binary files /dev/null and b/src/sys/win32/rc/res/bmp00002.bmp differ diff --git a/src/sys/win32/rc/res/bmp00003.bmp b/src/sys/win32/rc/res/bmp00003.bmp new file mode 100644 index 0000000..c7270fd Binary files /dev/null and b/src/sys/win32/rc/res/bmp00003.bmp differ diff --git a/src/sys/win32/rc/res/bmp00004.bmp b/src/sys/win32/rc/res/bmp00004.bmp new file mode 100644 index 0000000..7aca914 Binary files /dev/null and b/src/sys/win32/rc/res/bmp00004.bmp differ diff --git a/src/sys/win32/rc/res/bmp00005.bmp b/src/sys/win32/rc/res/bmp00005.bmp new file mode 100644 index 0000000..6e8e840 Binary files /dev/null and b/src/sys/win32/rc/res/bmp00005.bmp differ diff --git a/src/sys/win32/rc/res/cchsb.bmp b/src/sys/win32/rc/res/cchsb.bmp new file mode 100644 index 0000000..4b8c94a Binary files /dev/null and b/src/sys/win32/rc/res/cchsb.bmp differ diff --git a/src/sys/win32/rc/res/ccrgb.bmp b/src/sys/win32/rc/res/ccrgb.bmp new file mode 100644 index 0000000..5d25db1 Binary files /dev/null and b/src/sys/win32/rc/res/ccrgb.bmp differ diff --git a/src/sys/win32/rc/res/dbg_back.bmp b/src/sys/win32/rc/res/dbg_back.bmp new file mode 100644 index 0000000..b8e1470 Binary files /dev/null and b/src/sys/win32/rc/res/dbg_back.bmp differ diff --git a/src/sys/win32/rc/res/dbg_breakpoint.ico b/src/sys/win32/rc/res/dbg_breakpoint.ico new file mode 100644 index 0000000..db5d0ed Binary files /dev/null and b/src/sys/win32/rc/res/dbg_breakpoint.ico differ diff --git a/src/sys/win32/rc/res/dbg_current.ico b/src/sys/win32/rc/res/dbg_current.ico new file mode 100644 index 0000000..9392c06 Binary files /dev/null and b/src/sys/win32/rc/res/dbg_current.ico differ diff --git a/src/sys/win32/rc/res/dbg_currentline.ico b/src/sys/win32/rc/res/dbg_currentline.ico new file mode 100644 index 0000000..6d3435e Binary files /dev/null and b/src/sys/win32/rc/res/dbg_currentline.ico differ diff --git a/src/sys/win32/rc/res/dbg_empty.ico b/src/sys/win32/rc/res/dbg_empty.ico new file mode 100644 index 0000000..2bd4717 Binary files /dev/null and b/src/sys/win32/rc/res/dbg_empty.ico differ diff --git a/src/sys/win32/rc/res/dbg_open.bmp b/src/sys/win32/rc/res/dbg_open.bmp new file mode 100644 index 0000000..bb8613c Binary files /dev/null and b/src/sys/win32/rc/res/dbg_open.bmp differ diff --git a/src/sys/win32/rc/res/dbg_toolbar.bmp b/src/sys/win32/rc/res/dbg_toolbar.bmp new file mode 100644 index 0000000..12a4afa Binary files /dev/null and b/src/sys/win32/rc/res/dbg_toolbar.bmp differ diff --git a/src/sys/win32/rc/res/doom.ico b/src/sys/win32/rc/res/doom.ico new file mode 100644 index 0000000..2a6b79a Binary files /dev/null and b/src/sys/win32/rc/res/doom.ico differ diff --git a/src/sys/win32/rc/res/fpoint.cur b/src/sys/win32/rc/res/fpoint.cur new file mode 100644 index 0000000..ca13f10 Binary files /dev/null and b/src/sys/win32/rc/res/fpoint.cur differ diff --git a/src/sys/win32/rc/res/guied.ico b/src/sys/win32/rc/res/guied.ico new file mode 100644 index 0000000..79127de Binary files /dev/null and b/src/sys/win32/rc/res/guied.ico differ diff --git a/src/sys/win32/rc/res/guied_collapse.ico b/src/sys/win32/rc/res/guied_collapse.ico new file mode 100644 index 0000000..86b0ac8 Binary files /dev/null and b/src/sys/win32/rc/res/guied_collapse.ico differ diff --git a/src/sys/win32/rc/res/guied_expand.ico b/src/sys/win32/rc/res/guied_expand.ico new file mode 100644 index 0000000..c02206c Binary files /dev/null and b/src/sys/win32/rc/res/guied_expand.ico differ diff --git a/src/sys/win32/rc/res/guied_hand.cur b/src/sys/win32/rc/res/guied_hand.cur new file mode 100644 index 0000000..72ef9db Binary files /dev/null and b/src/sys/win32/rc/res/guied_hand.cur differ diff --git a/src/sys/win32/rc/res/guied_nav_visible.ico b/src/sys/win32/rc/res/guied_nav_visible.ico new file mode 100644 index 0000000..f3e58b4 Binary files /dev/null and b/src/sys/win32/rc/res/guied_nav_visible.ico differ diff --git a/src/sys/win32/rc/res/guied_nav_visibledisabled.ico b/src/sys/win32/rc/res/guied_nav_visibledisabled.ico new file mode 100644 index 0000000..290eaed Binary files /dev/null and b/src/sys/win32/rc/res/guied_nav_visibledisabled.ico differ diff --git a/src/sys/win32/rc/res/guied_scripts.ico b/src/sys/win32/rc/res/guied_scripts.ico new file mode 100644 index 0000000..6eec8c8 Binary files /dev/null and b/src/sys/win32/rc/res/guied_scripts.ico differ diff --git a/src/sys/win32/rc/res/guied_scripts_white.ico b/src/sys/win32/rc/res/guied_scripts_white.ico new file mode 100644 index 0000000..91f09f2 Binary files /dev/null and b/src/sys/win32/rc/res/guied_scripts_white.ico differ diff --git a/src/sys/win32/rc/res/guied_viewer_toolbar.bmp b/src/sys/win32/rc/res/guied_viewer_toolbar.bmp new file mode 100644 index 0000000..01385af Binary files /dev/null and b/src/sys/win32/rc/res/guied_viewer_toolbar.bmp differ diff --git a/src/sys/win32/rc/res/icon2.ico b/src/sys/win32/rc/res/icon2.ico new file mode 100644 index 0000000..b59992f Binary files /dev/null and b/src/sys/win32/rc/res/icon2.ico differ diff --git a/src/sys/win32/rc/res/logo_sm3dfx.bmp b/src/sys/win32/rc/res/logo_sm3dfx.bmp new file mode 100644 index 0000000..9a1e106 Binary files /dev/null and b/src/sys/win32/rc/res/logo_sm3dfx.bmp differ diff --git a/src/sys/win32/rc/res/matedtree.bmp b/src/sys/win32/rc/res/matedtree.bmp new file mode 100644 index 0000000..0ee91b3 Binary files /dev/null and b/src/sys/win32/rc/res/matedtree.bmp differ diff --git a/src/sys/win32/rc/res/me_disabled_icon.ico b/src/sys/win32/rc/res/me_disabled_icon.ico new file mode 100644 index 0000000..290eaed Binary files /dev/null and b/src/sys/win32/rc/res/me_disabled_icon.ico differ diff --git a/src/sys/win32/rc/res/me_enabled.ico b/src/sys/win32/rc/res/me_enabled.ico new file mode 100644 index 0000000..8e47a28 Binary files /dev/null and b/src/sys/win32/rc/res/me_enabled.ico differ diff --git a/src/sys/win32/rc/res/me_off_icon.ico b/src/sys/win32/rc/res/me_off_icon.ico new file mode 100644 index 0000000..9077bbc Binary files /dev/null and b/src/sys/win32/rc/res/me_off_icon.ico differ diff --git a/src/sys/win32/rc/res/me_on_icon.ico b/src/sys/win32/rc/res/me_on_icon.ico new file mode 100644 index 0000000..f3e58b4 Binary files /dev/null and b/src/sys/win32/rc/res/me_on_icon.ico differ diff --git a/src/sys/win32/rc/res/qe3.ico b/src/sys/win32/rc/res/qe3.ico new file mode 100644 index 0000000..d83f677 Binary files /dev/null and b/src/sys/win32/rc/res/qe3.ico differ diff --git a/src/sys/win32/rc/res/shaderbar.bmp b/src/sys/win32/rc/res/shaderbar.bmp new file mode 100644 index 0000000..913de51 Binary files /dev/null and b/src/sys/win32/rc/res/shaderbar.bmp differ diff --git a/src/sys/win32/rc/res/shaderdoc.ico b/src/sys/win32/rc/res/shaderdoc.ico new file mode 100644 index 0000000..3288c6f Binary files /dev/null and b/src/sys/win32/rc/res/shaderdoc.ico differ diff --git a/src/sys/win32/rc/res/shaderframe.ico b/src/sys/win32/rc/res/shaderframe.ico new file mode 100644 index 0000000..f644e9a Binary files /dev/null and b/src/sys/win32/rc/res/shaderframe.ico differ diff --git a/src/sys/win32/rc/res/spliter.cur b/src/sys/win32/rc/res/spliter.cur new file mode 100644 index 0000000..1a4fff6 Binary files /dev/null and b/src/sys/win32/rc/res/spliter.cur differ diff --git a/src/sys/win32/rc/retail/quake4/bitmaps/4001_1033.bmp b/src/sys/win32/rc/retail/quake4/bitmaps/4001_1033.bmp new file mode 100644 index 0000000..dfe1ac5 Binary files /dev/null and b/src/sys/win32/rc/retail/quake4/bitmaps/4001_1033.bmp differ diff --git a/src/sys/win32/rc/retail/quake4/icons/1024_1033.ico b/src/sys/win32/rc/retail/quake4/icons/1024_1033.ico new file mode 100644 index 0000000..749124d Binary files /dev/null and b/src/sys/win32/rc/retail/quake4/icons/1024_1033.ico differ diff --git a/src/sys/win32/rc/retail/quake4/manifest.json b/src/sys/win32/rc/retail/quake4/manifest.json new file mode 100644 index 0000000..3451fef --- /dev/null +++ b/src/sys/win32/rc/retail/quake4/manifest.json @@ -0,0 +1,27 @@ +{ + "source": "E:\\projects\\Quake4Alpha\\quake4.exe", + "source_size": 4919296, + "source_sha256": "3b713ad6114ae9a8427bc23c7317c736d86c67419eb8a1e275c9591aaf7be6ea", + "resource_directory_rva": "0x013b5000", + "resource_directory_size": 613584, + "emitted": [ + { + "resource_type": "2", + "resource_name": "4001", + "language": "1033", + "rva": "0x013ba9f0", + "path": "bitmaps/4001_1033.bmp", + "size": 589880, + "sha256": "bef78ce90873fdad6eaa8030056a5cc9d9b6e4a4973c44e893109bd183745ce1" + }, + { + "resource_type": "14", + "resource_name": "1024", + "language": "1033", + "rva": "0x013ba990", + "path": "icons/1024_1033.ico", + "size": 22486, + "sha256": "0d6c0d7b3293db959c4268743b6fabc05b7b242438641acd005a15f079c96a37" + } + ] +} diff --git a/src/sys/win32/rc/retail/toolsx86/all-icons-contact-sheet.png b/src/sys/win32/rc/retail/toolsx86/all-icons-contact-sheet.png new file mode 100644 index 0000000..d4b1b1e Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/all-icons-contact-sheet.png differ diff --git a/src/sys/win32/rc/retail/toolsx86/bitmaps/1003_1033.bmp b/src/sys/win32/rc/retail/toolsx86/bitmaps/1003_1033.bmp new file mode 100644 index 0000000..bb8613c Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/bitmaps/1003_1033.bmp differ diff --git a/src/sys/win32/rc/retail/toolsx86/bitmaps/1004_1033.bmp b/src/sys/win32/rc/retail/toolsx86/bitmaps/1004_1033.bmp new file mode 100644 index 0000000..b8e1470 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/bitmaps/1004_1033.bmp differ diff --git a/src/sys/win32/rc/retail/toolsx86/bitmaps/15469_1033.bmp b/src/sys/win32/rc/retail/toolsx86/bitmaps/15469_1033.bmp new file mode 100644 index 0000000..d14ba48 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/bitmaps/15469_1033.bmp differ diff --git a/src/sys/win32/rc/retail/toolsx86/bitmaps/20122_1033.bmp b/src/sys/win32/rc/retail/toolsx86/bitmaps/20122_1033.bmp new file mode 100644 index 0000000..12a4afa Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/bitmaps/20122_1033.bmp differ diff --git a/src/sys/win32/rc/retail/toolsx86/bitmaps/2043_1033.bmp b/src/sys/win32/rc/retail/toolsx86/bitmaps/2043_1033.bmp new file mode 100644 index 0000000..6af8abd Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/bitmaps/2043_1033.bmp differ diff --git a/src/sys/win32/rc/retail/toolsx86/bitmaps/30994_1033.bmp b/src/sys/win32/rc/retail/toolsx86/bitmaps/30994_1033.bmp new file mode 100644 index 0000000..b5861c4 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/bitmaps/30994_1033.bmp differ diff --git a/src/sys/win32/rc/retail/toolsx86/bitmaps/30996_1033.bmp b/src/sys/win32/rc/retail/toolsx86/bitmaps/30996_1033.bmp new file mode 100644 index 0000000..3fe90db Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/bitmaps/30996_1033.bmp differ diff --git a/src/sys/win32/rc/retail/toolsx86/bitmaps/5100_1033.bmp b/src/sys/win32/rc/retail/toolsx86/bitmaps/5100_1033.bmp new file mode 100644 index 0000000..01385af Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/bitmaps/5100_1033.bmp differ diff --git a/src/sys/win32/rc/retail/toolsx86/bitmaps/5134_1033.bmp b/src/sys/win32/rc/retail/toolsx86/bitmaps/5134_1033.bmp new file mode 100644 index 0000000..645bbe5 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/bitmaps/5134_1033.bmp differ diff --git a/src/sys/win32/rc/retail/toolsx86/bitmaps/6008_1033.bmp b/src/sys/win32/rc/retail/toolsx86/bitmaps/6008_1033.bmp new file mode 100644 index 0000000..b69ca09 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/bitmaps/6008_1033.bmp differ diff --git a/src/sys/win32/rc/retail/toolsx86/bitmaps/7057_1033.bmp b/src/sys/win32/rc/retail/toolsx86/bitmaps/7057_1033.bmp new file mode 100644 index 0000000..dfa5dd1 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/bitmaps/7057_1033.bmp differ diff --git a/src/sys/win32/rc/retail/toolsx86/bitmaps/7058_1033.bmp b/src/sys/win32/rc/retail/toolsx86/bitmaps/7058_1033.bmp new file mode 100644 index 0000000..6d77432 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/bitmaps/7058_1033.bmp differ diff --git a/src/sys/win32/rc/retail/toolsx86/bitmaps/7069_1033.bmp b/src/sys/win32/rc/retail/toolsx86/bitmaps/7069_1033.bmp new file mode 100644 index 0000000..892bbde Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/bitmaps/7069_1033.bmp differ diff --git a/src/sys/win32/rc/retail/toolsx86/bitmaps/7070_1033.bmp b/src/sys/win32/rc/retail/toolsx86/bitmaps/7070_1033.bmp new file mode 100644 index 0000000..cb25691 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/bitmaps/7070_1033.bmp differ diff --git a/src/sys/win32/rc/retail/toolsx86/bitmaps/7071_1033.bmp b/src/sys/win32/rc/retail/toolsx86/bitmaps/7071_1033.bmp new file mode 100644 index 0000000..5c2dcd6 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/bitmaps/7071_1033.bmp differ diff --git a/src/sys/win32/rc/retail/toolsx86/bitmaps/7072_1033.bmp b/src/sys/win32/rc/retail/toolsx86/bitmaps/7072_1033.bmp new file mode 100644 index 0000000..4624f5d Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/bitmaps/7072_1033.bmp differ diff --git a/src/sys/win32/rc/retail/toolsx86/bitmaps/7079_1033.bmp b/src/sys/win32/rc/retail/toolsx86/bitmaps/7079_1033.bmp new file mode 100644 index 0000000..c7270fd Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/bitmaps/7079_1033.bmp differ diff --git a/src/sys/win32/rc/retail/toolsx86/bitmaps/7080_1033.bmp b/src/sys/win32/rc/retail/toolsx86/bitmaps/7080_1033.bmp new file mode 100644 index 0000000..7aca914 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/bitmaps/7080_1033.bmp differ diff --git a/src/sys/win32/rc/retail/toolsx86/bitmaps/7081_1033.bmp b/src/sys/win32/rc/retail/toolsx86/bitmaps/7081_1033.bmp new file mode 100644 index 0000000..4b8c94a Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/bitmaps/7081_1033.bmp differ diff --git a/src/sys/win32/rc/retail/toolsx86/bitmaps/7082_1033.bmp b/src/sys/win32/rc/retail/toolsx86/bitmaps/7082_1033.bmp new file mode 100644 index 0000000..5d25db1 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/bitmaps/7082_1033.bmp differ diff --git a/src/sys/win32/rc/retail/toolsx86/bitmaps/7093_1033.bmp b/src/sys/win32/rc/retail/toolsx86/bitmaps/7093_1033.bmp new file mode 100644 index 0000000..a5eb88a Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/bitmaps/7093_1033.bmp differ diff --git a/src/sys/win32/rc/retail/toolsx86/bitmaps/IDB_EV_TOOLBAR_1033.bmp b/src/sys/win32/rc/retail/toolsx86/bitmaps/IDB_EV_TOOLBAR_1033.bmp new file mode 100644 index 0000000..b69ca09 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/bitmaps/IDB_EV_TOOLBAR_1033.bmp differ diff --git a/src/sys/win32/rc/retail/toolsx86/cursors/30977_1033.cur b/src/sys/win32/rc/retail/toolsx86/cursors/30977_1033.cur new file mode 100644 index 0000000..181428e Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/cursors/30977_1033.cur differ diff --git a/src/sys/win32/rc/retail/toolsx86/cursors/30978_1033.cur b/src/sys/win32/rc/retail/toolsx86/cursors/30978_1033.cur new file mode 100644 index 0000000..620b5f0 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/cursors/30978_1033.cur differ diff --git a/src/sys/win32/rc/retail/toolsx86/cursors/30998_1033.cur b/src/sys/win32/rc/retail/toolsx86/cursors/30998_1033.cur new file mode 100644 index 0000000..3f7f46f Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/cursors/30998_1033.cur differ diff --git a/src/sys/win32/rc/retail/toolsx86/cursors/30999_1033.cur b/src/sys/win32/rc/retail/toolsx86/cursors/30999_1033.cur new file mode 100644 index 0000000..535b8d0 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/cursors/30999_1033.cur differ diff --git a/src/sys/win32/rc/retail/toolsx86/cursors/31000_1033.cur b/src/sys/win32/rc/retail/toolsx86/cursors/31000_1033.cur new file mode 100644 index 0000000..5e1abe2 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/cursors/31000_1033.cur differ diff --git a/src/sys/win32/rc/retail/toolsx86/cursors/31001_1033.cur b/src/sys/win32/rc/retail/toolsx86/cursors/31001_1033.cur new file mode 100644 index 0000000..17c168f Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/cursors/31001_1033.cur differ diff --git a/src/sys/win32/rc/retail/toolsx86/cursors/31002_1033.cur b/src/sys/win32/rc/retail/toolsx86/cursors/31002_1033.cur new file mode 100644 index 0000000..37b92e4 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/cursors/31002_1033.cur differ diff --git a/src/sys/win32/rc/retail/toolsx86/cursors/31003_1033.cur b/src/sys/win32/rc/retail/toolsx86/cursors/31003_1033.cur new file mode 100644 index 0000000..acb351a Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/cursors/31003_1033.cur differ diff --git a/src/sys/win32/rc/retail/toolsx86/cursors/31004_1033.cur b/src/sys/win32/rc/retail/toolsx86/cursors/31004_1033.cur new file mode 100644 index 0000000..7e103e0 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/cursors/31004_1033.cur differ diff --git a/src/sys/win32/rc/retail/toolsx86/cursors/31005_1033.cur b/src/sys/win32/rc/retail/toolsx86/cursors/31005_1033.cur new file mode 100644 index 0000000..08332e5 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/cursors/31005_1033.cur differ diff --git a/src/sys/win32/rc/retail/toolsx86/cursors/31006_1033.cur b/src/sys/win32/rc/retail/toolsx86/cursors/31006_1033.cur new file mode 100644 index 0000000..2694f24 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/cursors/31006_1033.cur differ diff --git a/src/sys/win32/rc/retail/toolsx86/cursors/31007_1033.cur b/src/sys/win32/rc/retail/toolsx86/cursors/31007_1033.cur new file mode 100644 index 0000000..c3a2834 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/cursors/31007_1033.cur differ diff --git a/src/sys/win32/rc/retail/toolsx86/cursors/31008_1033.cur b/src/sys/win32/rc/retail/toolsx86/cursors/31008_1033.cur new file mode 100644 index 0000000..a75ab5f Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/cursors/31008_1033.cur differ diff --git a/src/sys/win32/rc/retail/toolsx86/cursors/31009_1033.cur b/src/sys/win32/rc/retail/toolsx86/cursors/31009_1033.cur new file mode 100644 index 0000000..154619f Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/cursors/31009_1033.cur differ diff --git a/src/sys/win32/rc/retail/toolsx86/cursors/31010_1033.cur b/src/sys/win32/rc/retail/toolsx86/cursors/31010_1033.cur new file mode 100644 index 0000000..d515354 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/cursors/31010_1033.cur differ diff --git a/src/sys/win32/rc/retail/toolsx86/cursors/31011_1033.cur b/src/sys/win32/rc/retail/toolsx86/cursors/31011_1033.cur new file mode 100644 index 0000000..3388d0b Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/cursors/31011_1033.cur differ diff --git a/src/sys/win32/rc/retail/toolsx86/cursors/5105_1033.cur b/src/sys/win32/rc/retail/toolsx86/cursors/5105_1033.cur new file mode 100644 index 0000000..7fe0886 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/cursors/5105_1033.cur differ diff --git a/src/sys/win32/rc/retail/toolsx86/icon-contact-sheet.png b/src/sys/win32/rc/retail/toolsx86/icon-contact-sheet.png new file mode 100644 index 0000000..e563e3a Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/icon-contact-sheet.png differ diff --git a/src/sys/win32/rc/retail/toolsx86/icons/112_1033.ico b/src/sys/win32/rc/retail/toolsx86/icons/112_1033.ico new file mode 100644 index 0000000..c7bd4a8 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/icons/112_1033.ico differ diff --git a/src/sys/win32/rc/retail/toolsx86/icons/1211_1033.ico b/src/sys/win32/rc/retail/toolsx86/icons/1211_1033.ico new file mode 100644 index 0000000..972bf83 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/icons/1211_1033.ico differ diff --git a/src/sys/win32/rc/retail/toolsx86/icons/15665_1033.ico b/src/sys/win32/rc/retail/toolsx86/icons/15665_1033.ico new file mode 100644 index 0000000..b9e280f Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/icons/15665_1033.ico differ diff --git a/src/sys/win32/rc/retail/toolsx86/icons/17012_1033.ico b/src/sys/win32/rc/retail/toolsx86/icons/17012_1033.ico new file mode 100644 index 0000000..1dd94f7 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/icons/17012_1033.ico differ diff --git a/src/sys/win32/rc/retail/toolsx86/icons/2000_1033.ico b/src/sys/win32/rc/retail/toolsx86/icons/2000_1033.ico new file mode 100644 index 0000000..fe2b27a Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/icons/2000_1033.ico differ diff --git a/src/sys/win32/rc/retail/toolsx86/icons/2038_1033.ico b/src/sys/win32/rc/retail/toolsx86/icons/2038_1033.ico new file mode 100644 index 0000000..400d97f Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/icons/2038_1033.ico differ diff --git a/src/sys/win32/rc/retail/toolsx86/icons/2049_1033.ico b/src/sys/win32/rc/retail/toolsx86/icons/2049_1033.ico new file mode 100644 index 0000000..8b7ab2e Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/icons/2049_1033.ico differ diff --git a/src/sys/win32/rc/retail/toolsx86/icons/2050_1033.ico b/src/sys/win32/rc/retail/toolsx86/icons/2050_1033.ico new file mode 100644 index 0000000..fec1cec Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/icons/2050_1033.ico differ diff --git a/src/sys/win32/rc/retail/toolsx86/icons/2051_1033.ico b/src/sys/win32/rc/retail/toolsx86/icons/2051_1033.ico new file mode 100644 index 0000000..daed2a5 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/icons/2051_1033.ico differ diff --git a/src/sys/win32/rc/retail/toolsx86/icons/2052_1033.ico b/src/sys/win32/rc/retail/toolsx86/icons/2052_1033.ico new file mode 100644 index 0000000..b3882d6 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/icons/2052_1033.ico differ diff --git a/src/sys/win32/rc/retail/toolsx86/icons/2053_1033.ico b/src/sys/win32/rc/retail/toolsx86/icons/2053_1033.ico new file mode 100644 index 0000000..d74a593 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/icons/2053_1033.ico differ diff --git a/src/sys/win32/rc/retail/toolsx86/icons/2054_1033.ico b/src/sys/win32/rc/retail/toolsx86/icons/2054_1033.ico new file mode 100644 index 0000000..c7c2988 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/icons/2054_1033.ico differ diff --git a/src/sys/win32/rc/retail/toolsx86/icons/2070_1033.ico b/src/sys/win32/rc/retail/toolsx86/icons/2070_1033.ico new file mode 100644 index 0000000..9be0969 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/icons/2070_1033.ico differ diff --git a/src/sys/win32/rc/retail/toolsx86/icons/2072_1033.ico b/src/sys/win32/rc/retail/toolsx86/icons/2072_1033.ico new file mode 100644 index 0000000..d119202 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/icons/2072_1033.ico differ diff --git a/src/sys/win32/rc/retail/toolsx86/icons/2073_1033.ico b/src/sys/win32/rc/retail/toolsx86/icons/2073_1033.ico new file mode 100644 index 0000000..1be484a Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/icons/2073_1033.ico differ diff --git a/src/sys/win32/rc/retail/toolsx86/icons/2075_1033.ico b/src/sys/win32/rc/retail/toolsx86/icons/2075_1033.ico new file mode 100644 index 0000000..0eec4e6 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/icons/2075_1033.ico differ diff --git a/src/sys/win32/rc/retail/toolsx86/icons/2076_1033.ico b/src/sys/win32/rc/retail/toolsx86/icons/2076_1033.ico new file mode 100644 index 0000000..f147c86 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/icons/2076_1033.ico differ diff --git a/src/sys/win32/rc/retail/toolsx86/icons/2077_1033.ico b/src/sys/win32/rc/retail/toolsx86/icons/2077_1033.ico new file mode 100644 index 0000000..c46f57b Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/icons/2077_1033.ico differ diff --git a/src/sys/win32/rc/retail/toolsx86/icons/2078_1033.ico b/src/sys/win32/rc/retail/toolsx86/icons/2078_1033.ico new file mode 100644 index 0000000..7cc8058 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/icons/2078_1033.ico differ diff --git a/src/sys/win32/rc/retail/toolsx86/icons/2079_1033.ico b/src/sys/win32/rc/retail/toolsx86/icons/2079_1033.ico new file mode 100644 index 0000000..52720e2 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/icons/2079_1033.ico differ diff --git a/src/sys/win32/rc/retail/toolsx86/icons/21113_1033.ico b/src/sys/win32/rc/retail/toolsx86/icons/21113_1033.ico new file mode 100644 index 0000000..757a93e Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/icons/21113_1033.ico differ diff --git a/src/sys/win32/rc/retail/toolsx86/icons/21114_1033.ico b/src/sys/win32/rc/retail/toolsx86/icons/21114_1033.ico new file mode 100644 index 0000000..6c0aaed Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/icons/21114_1033.ico differ diff --git a/src/sys/win32/rc/retail/toolsx86/icons/21115_1033.ico b/src/sys/win32/rc/retail/toolsx86/icons/21115_1033.ico new file mode 100644 index 0000000..46931f9 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/icons/21115_1033.ico differ diff --git a/src/sys/win32/rc/retail/toolsx86/icons/5092_1033.ico b/src/sys/win32/rc/retail/toolsx86/icons/5092_1033.ico new file mode 100644 index 0000000..4ad1a05 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/icons/5092_1033.ico differ diff --git a/src/sys/win32/rc/retail/toolsx86/icons/5098_1033.ico b/src/sys/win32/rc/retail/toolsx86/icons/5098_1033.ico new file mode 100644 index 0000000..3512651 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/icons/5098_1033.ico differ diff --git a/src/sys/win32/rc/retail/toolsx86/icons/5099_1033.ico b/src/sys/win32/rc/retail/toolsx86/icons/5099_1033.ico new file mode 100644 index 0000000..0012519 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/icons/5099_1033.ico differ diff --git a/src/sys/win32/rc/retail/toolsx86/icons/5101_1033.ico b/src/sys/win32/rc/retail/toolsx86/icons/5101_1033.ico new file mode 100644 index 0000000..d217bb7 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/icons/5101_1033.ico differ diff --git a/src/sys/win32/rc/retail/toolsx86/icons/5102_1033.ico b/src/sys/win32/rc/retail/toolsx86/icons/5102_1033.ico new file mode 100644 index 0000000..5b2560c Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/icons/5102_1033.ico differ diff --git a/src/sys/win32/rc/retail/toolsx86/icons/5103_1033.ico b/src/sys/win32/rc/retail/toolsx86/icons/5103_1033.ico new file mode 100644 index 0000000..88593a3 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/icons/5103_1033.ico differ diff --git a/src/sys/win32/rc/retail/toolsx86/icons/5127_1033.ico b/src/sys/win32/rc/retail/toolsx86/icons/5127_1033.ico new file mode 100644 index 0000000..1885209 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/icons/5127_1033.ico differ diff --git a/src/sys/win32/rc/retail/toolsx86/icons/5153_1033.ico b/src/sys/win32/rc/retail/toolsx86/icons/5153_1033.ico new file mode 100644 index 0000000..0aef0b8 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/icons/5153_1033.ico differ diff --git a/src/sys/win32/rc/retail/toolsx86/icons/6003_1033.ico b/src/sys/win32/rc/retail/toolsx86/icons/6003_1033.ico new file mode 100644 index 0000000..a3c99e6 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/icons/6003_1033.ico differ diff --git a/src/sys/win32/rc/retail/toolsx86/icons/7052_1033.ico b/src/sys/win32/rc/retail/toolsx86/icons/7052_1033.ico new file mode 100644 index 0000000..12b129d Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/icons/7052_1033.ico differ diff --git a/src/sys/win32/rc/retail/toolsx86/icons/8002_1033.ico b/src/sys/win32/rc/retail/toolsx86/icons/8002_1033.ico new file mode 100644 index 0000000..4615161 Binary files /dev/null and b/src/sys/win32/rc/retail/toolsx86/icons/8002_1033.ico differ diff --git a/src/sys/win32/rc/retail/toolsx86/manifest.json b/src/sys/win32/rc/retail/toolsx86/manifest.json new file mode 100644 index 0000000..7e32cb0 --- /dev/null +++ b/src/sys/win32/rc/retail/toolsx86/manifest.json @@ -0,0 +1,666 @@ +{ + "source": "E:\\projects\\Quake4Alpha\\Toolsx86.dll", + "source_size": 3416064, + "source_sha256": "66bd959f17f765c5885362882f51ca9222eade8642e5c0127fcdede87f7978b9", + "resource_directory_rva": "0x016ac000", + "resource_directory_size": 663008, + "emitted": [ + { + "resource_type": "2", + "resource_name": "IDB_EV_TOOLBAR", + "language": "1033", + "rva": "0x016bcd60", + "path": "bitmaps/IDB_EV_TOOLBAR_1033.bmp", + "size": 478, + "sha256": "71abd04261f637cefd1f388893549a8f981e3ea05450db1eb0f4bfc9fd1ba271" + }, + { + "resource_type": "2", + "resource_name": "1003", + "language": "1033", + "rva": "0x016b0c98", + "path": "bitmaps/1003_1033.bmp", + "size": 502, + "sha256": "ec7e8c8da7149ecc368bfec9a4e7336180d593b1211002f56798e564cb1cdb17" + }, + { + "resource_type": "2", + "resource_name": "1004", + "language": "1033", + "rva": "0x016b0e80", + "path": "bitmaps/1004_1033.bmp", + "size": 310, + "sha256": "8436f943492ced0572ccd69ebbd6c335f74e2006ad85a60cee92fd49e71d2462" + }, + { + "resource_type": "2", + "resource_name": "2043", + "language": "1033", + "rva": "0x016dd758", + "path": "bitmaps/2043_1033.bmp", + "size": 21560, + "sha256": "c22d1e83370dba12a6988c5280ae26f653536439985fb4b567220f5c4b4b01e8" + }, + { + "resource_type": "2", + "resource_name": "5100", + "language": "1033", + "rva": "0x016d6ca0", + "path": "bitmaps/5100_1033.bmp", + "size": 478, + "sha256": "d3d46db4893ed4e24fc100e171a19ecb6fecb6fd21bdd031d37d98c37b36d4b4" + }, + { + "resource_type": "2", + "resource_name": "5134", + "language": "1033", + "rva": "0x016cdff0", + "path": "bitmaps/5134_1033.bmp", + "size": 1078, + "sha256": "e7bcfa33d670025f4e00215dc52dfbb9d5fd4ffdb0c0e4a1e55a42287319384d" + }, + { + "resource_type": "2", + "resource_name": "6008", + "language": "1033", + "rva": "0x016dca70", + "path": "bitmaps/6008_1033.bmp", + "size": 478, + "sha256": "71abd04261f637cefd1f388893549a8f981e3ea05450db1eb0f4bfc9fd1ba271" + }, + { + "resource_type": "2", + "resource_name": "7057", + "language": "1033", + "rva": "0x016f6b38", + "path": "bitmaps/7057_1033.bmp", + "size": 478, + "sha256": "64d0ed0f770360c2aed088be95baf007ec7ec8ab87d5804362eeb83622d26a50" + }, + { + "resource_type": "2", + "resource_name": "7058", + "language": "1033", + "rva": "0x016f6d08", + "path": "bitmaps/7058_1033.bmp", + "size": 6374, + "sha256": "edef8e72ee6408932bbe9ae5a7841ff0b507c6be3a5ad75026e2bbec6ad310ed" + }, + { + "resource_type": "2", + "resource_name": "7069", + "language": "1033", + "rva": "0x016f85e0", + "path": "bitmaps/7069_1033.bmp", + "size": 486, + "sha256": "cce0a72891b0bcf3918e8f9319b97d9adb9ea69a1878ee035969d9909d213029" + }, + { + "resource_type": "2", + "resource_name": "7070", + "language": "1033", + "rva": "0x016f87b8", + "path": "bitmaps/7070_1033.bmp", + "size": 486, + "sha256": "96760adf9d7d05421da3578d0cd68f95069f034a72c4ccc235655631dba0d4e3" + }, + { + "resource_type": "2", + "resource_name": "7071", + "language": "1033", + "rva": "0x016f8990", + "path": "bitmaps/7071_1033.bmp", + "size": 486, + "sha256": "8abd4fa018b64e014362e95171bccc8e2b446782ce7d4f2c76821151b08182d9" + }, + { + "resource_type": "2", + "resource_name": "7072", + "language": "1033", + "rva": "0x016f8b68", + "path": "bitmaps/7072_1033.bmp", + "size": 486, + "sha256": "d2137819936c0664053b5f399d34a257fa00ca024ad61b8bc8d804d01cca39db" + }, + { + "resource_type": "2", + "resource_name": "7079", + "language": "1033", + "rva": "0x016f8d40", + "path": "bitmaps/7079_1033.bmp", + "size": 1014, + "sha256": "44effbbad46fa3bcf5fe71279a7d4f839a9b3ab4628f9430f899d0a342686ada" + }, + { + "resource_type": "2", + "resource_name": "7080", + "language": "1033", + "rva": "0x016f9128", + "path": "bitmaps/7080_1033.bmp", + "size": 502, + "sha256": "60b55a8d92ed811b5416c331657584b27bf8a7dae03037862fd082f8a68cd3f2" + }, + { + "resource_type": "2", + "resource_name": "7081", + "language": "1033", + "rva": "0x016f9310", + "path": "bitmaps/7081_1033.bmp", + "size": 122262, + "sha256": "80664ad6ef38c0a0100586014d0d707ab3ce7c7e76003a1fbbf2d94633dee1d5" + }, + { + "resource_type": "2", + "resource_name": "7082", + "language": "1033", + "rva": "0x01717098", + "path": "bitmaps/7082_1033.bmp", + "size": 108734, + "sha256": "47fd73c187f5bd62c68b3fccb362dbacd05a7cc14b85e3f940bb0ea8430bf1db" + }, + { + "resource_type": "2", + "resource_name": "7093", + "language": "1033", + "rva": "0x01731948", + "path": "bitmaps/7093_1033.bmp", + "size": 47936, + "sha256": "d9cf7fb05389e402f6bc88b08951f0c84b930948198647eba5d3d6ef5dc13baa" + }, + { + "resource_type": "2", + "resource_name": "15469", + "language": "1033", + "rva": "0x016f0a20", + "path": "bitmaps/15469_1033.bmp", + "size": 1014, + "sha256": "ec8747eed1d221ff6e749dd97c2dbc4ece71a2286c5de67c6e5fadf4bbd9c47b" + }, + { + "resource_type": "2", + "resource_name": "20122", + "language": "1033", + "rva": "0x016b6a30", + "path": "bitmaps/20122_1033.bmp", + "size": 1198, + "sha256": "e0f7251d34c26bcba49d2c0d9e12267b1894fe4ab7bba38d71fe9e16a1c1bbfd" + }, + { + "resource_type": "2", + "resource_name": "30994", + "language": "1033", + "rva": "0x01748518", + "path": "bitmaps/30994_1033.bmp", + "size": 198, + "sha256": "baf0520b920913155bcbede3d0ff5fae3a3506566de13a501f9aca07c15f9745" + }, + { + "resource_type": "2", + "resource_name": "30996", + "language": "1033", + "rva": "0x017485d0", + "path": "bitmaps/30996_1033.bmp", + "size": 338, + "sha256": "d4f6f3f9bf38396dfe3e590650308c81395f9873ae257ae639cb3b63b150bb10" + }, + { + "resource_type": "12", + "resource_name": "5105", + "language": "1033", + "rva": "0x016d7160", + "path": "cursors/5105_1033.cur", + "size": 766, + "sha256": "3e29c884e3902845184fd94f300b8c870a1a596f5b049df9606cad12a885840c" + }, + { + "resource_type": "12", + "resource_name": "30977", + "language": "1033", + "rva": "0x017471a8", + "path": "cursors/30977_1033.cur", + "size": 518, + "sha256": "edb061d4b764a7794ed2362638dd0ecadd0a0f0d22aeb863ab6dc7fb07103c8a" + }, + { + "resource_type": "12", + "resource_name": "30978", + "language": "1033", + "rva": "0x01748908", + "path": "cursors/30978_1033.cur", + "size": 518, + "sha256": "ee638137dcf3a007a1ca2a78579d4839a8abf8fd402079819f9415016e11c0ad" + }, + { + "resource_type": "12", + "resource_name": "30998", + "language": "1033", + "rva": "0x01747998", + "path": "cursors/30998_1033.cur", + "size": 326, + "sha256": "a6dcfde3bfa144f871ee3182c0a6ce47da955987733582090ce752520c157495" + }, + { + "resource_type": "12", + "resource_name": "30999", + "language": "1033", + "rva": "0x01747308", + "path": "cursors/30999_1033.cur", + "size": 326, + "sha256": "a3050de87cbdd46eae2e1a5979465af6c350b95bf0cba63b70c32061e8ac8f55" + }, + { + "resource_type": "12", + "resource_name": "31000", + "language": "1033", + "rva": "0x01747848", + "path": "cursors/31000_1033.cur", + "size": 326, + "sha256": "093f192bb1ba7018438a67eb3a91e1c85f35f63e86960c163b241f3a2c78f36a" + }, + { + "resource_type": "12", + "resource_name": "31001", + "language": "1033", + "rva": "0x017476f8", + "path": "cursors/31001_1033.cur", + "size": 326, + "sha256": "bee246dddb6dea971665aebe5fdd1046fbe4a0a1ffd3d0f8d4dc585ee1bea979" + }, + { + "resource_type": "12", + "resource_name": "31002", + "language": "1033", + "rva": "0x01748028", + "path": "cursors/31002_1033.cur", + "size": 326, + "sha256": "ff844d80b064ba31e66716d4e95b12dc92abd48425171d44cda6c6797fc740f8" + }, + { + "resource_type": "12", + "resource_name": "31003", + "language": "1033", + "rva": "0x017475a8", + "path": "cursors/31003_1033.cur", + "size": 326, + "sha256": "c17e1eda5b2e2b0c03eb9efb737ba7106a98e08d1b40e5bed6dedce8ba000644" + }, + { + "resource_type": "12", + "resource_name": "31004", + "language": "1033", + "rva": "0x01747c38", + "path": "cursors/31004_1033.cur", + "size": 326, + "sha256": "050f66327debbff2dca3adf91f522767e8c4e014f6b491ee5c1802f5b635f131" + }, + { + "resource_type": "12", + "resource_name": "31005", + "language": "1033", + "rva": "0x01747458", + "path": "cursors/31005_1033.cur", + "size": 326, + "sha256": "48c702e32c0f1c00f842d1e9ae9a7884f91a1a3185ae7b242cdf1a0944bb6e33" + }, + { + "resource_type": "12", + "resource_name": "31006", + "language": "1033", + "rva": "0x01747ae8", + "path": "cursors/31006_1033.cur", + "size": 326, + "sha256": "2124ef1c42fda2e5387075d9e3f62d35f198f14cf62434caccd335f9aaf00e04" + }, + { + "resource_type": "12", + "resource_name": "31007", + "language": "1033", + "rva": "0x01747d88", + "path": "cursors/31007_1033.cur", + "size": 326, + "sha256": "89ddad138a48d2b9b531e89c73b87318c48f980af1a4a4d30a686c36961363fd" + }, + { + "resource_type": "12", + "resource_name": "31008", + "language": "1033", + "rva": "0x01747ed8", + "path": "cursors/31008_1033.cur", + "size": 326, + "sha256": "5903cc06c7235227d0438cef15de65edc3304a3fb366642f3e0b6e343f5b7cca" + }, + { + "resource_type": "12", + "resource_name": "31009", + "language": "1033", + "rva": "0x01748178", + "path": "cursors/31009_1033.cur", + "size": 326, + "sha256": "a27ee60d0f9e1cdbf0512c25d845a14ca3679fb238f68bb8d52008a9db61debb" + }, + { + "resource_type": "12", + "resource_name": "31010", + "language": "1033", + "rva": "0x017482c8", + "path": "cursors/31010_1033.cur", + "size": 326, + "sha256": "dd0278ea7446014fb8aafb9114c77c909db35505adc17c54a4d7f7c7a2871036" + }, + { + "resource_type": "12", + "resource_name": "31011", + "language": "1033", + "rva": "0x01748418", + "path": "cursors/31011_1033.cur", + "size": 326, + "sha256": "15223ceead884bbd92b75e44ae8ae84295ccccbee8762a15725eda04e76b54fd" + }, + { + "resource_type": "14", + "resource_name": "112", + "language": "1033", + "rva": "0x016b5e98", + "path": "icons/112_1033.ico", + "size": 2998, + "sha256": "e5b931b591372708cafb9ed41e87e4d3622615ef4481f9c7525b46a314eb6e41" + }, + { + "resource_type": "14", + "resource_name": "1211", + "language": "1033", + "rva": "0x016b10d0", + "path": "icons/1211_1033.ico", + "size": 318, + "sha256": "560a8185e8913f224913da0a4f2fa4f32f5ccdf5a16435292a4b9a29d2c8e2ee" + }, + { + "resource_type": "14", + "resource_name": "2000", + "language": "1033", + "rva": "0x016d0dd8", + "path": "icons/2000_1033.ico", + "size": 318, + "sha256": "ec4d19a9fa78546362de48ed291d3256cce08916dc8a2b4e23934d1dff92ca29" + }, + { + "resource_type": "14", + "resource_name": "2038", + "language": "1033", + "rva": "0x016e8808", + "path": "icons/2038_1033.ico", + "size": 22486, + "sha256": "c7bdff04dc71113a507cd42ad1a80fa84ca551690df2af138754ba36afac9131" + }, + { + "resource_type": "14", + "resource_name": "2049", + "language": "1033", + "rva": "0x016e8b50", + "path": "icons/2049_1033.ico", + "size": 766, + "sha256": "85b364875570f7bbefc73f0ce0b2e8f62316c92d6056d3fb6f98222b7134d83a" + }, + { + "resource_type": "14", + "resource_name": "2050", + "language": "1033", + "rva": "0x016e8e50", + "path": "icons/2050_1033.ico", + "size": 766, + "sha256": "b62e9e96d3e4b7c45536e5959276d51851558a78349cb072d9b7ef8fcd2d5025" + }, + { + "resource_type": "14", + "resource_name": "2051", + "language": "1033", + "rva": "0x016e9150", + "path": "icons/2051_1033.ico", + "size": 766, + "sha256": "76b468c7fd8590b08ed35031f8d13689c93fd0e03c43ad907fc1ce95c50b6b88" + }, + { + "resource_type": "14", + "resource_name": "2052", + "language": "1033", + "rva": "0x016e9450", + "path": "icons/2052_1033.ico", + "size": 766, + "sha256": "1fac5908cbd3ce57dec144835c58496c55faa70af9e6c232ca25bf44d3949a5e" + }, + { + "resource_type": "14", + "resource_name": "2053", + "language": "1033", + "rva": "0x016e9750", + "path": "icons/2053_1033.ico", + "size": 766, + "sha256": "58826068a090746b03323ed1a070095f771548c5ee6390ae37ec250f19275395" + }, + { + "resource_type": "14", + "resource_name": "2054", + "language": "1033", + "rva": "0x016e9a50", + "path": "icons/2054_1033.ico", + "size": 766, + "sha256": "54f2c5e7cd2d982de6283237333ff9070ddc8de5f6b8730d46f8af5b0f2552dd" + }, + { + "resource_type": "14", + "resource_name": "2070", + "language": "1033", + "rva": "0x016e9b90", + "path": "icons/2070_1033.ico", + "size": 318, + "sha256": "4a53a513e14ff5337f0f45266a418f971275fcb91993bc5fc195340f2c24ae12" + }, + { + "resource_type": "14", + "resource_name": "2072", + "language": "1033", + "rva": "0x016e9cd0", + "path": "icons/2072_1033.ico", + "size": 318, + "sha256": "cc6420a9899c9179baace360eab00de7d81146078bbe3c66abbd256cda7adbb9" + }, + { + "resource_type": "14", + "resource_name": "2073", + "language": "1033", + "rva": "0x016e9e10", + "path": "icons/2073_1033.ico", + "size": 318, + "sha256": "77e3518c6b0cf49ab9376de374cc781ef65c005e509575512f1b188c21e51faf" + }, + { + "resource_type": "14", + "resource_name": "2075", + "language": "1033", + "rva": "0x016e9f50", + "path": "icons/2075_1033.ico", + "size": 318, + "sha256": "3b73fe2dfdeb46cf3bd5548fda2058bd3b65b117554d7f633dbd08f34d786c47" + }, + { + "resource_type": "14", + "resource_name": "2076", + "language": "1033", + "rva": "0x016ea090", + "path": "icons/2076_1033.ico", + "size": 318, + "sha256": "c7856dbf912e96d2b5c5cd2365e25d060acb855ad17b1db36630013cd5a31637" + }, + { + "resource_type": "14", + "resource_name": "2077", + "language": "1033", + "rva": "0x016ea1d0", + "path": "icons/2077_1033.ico", + "size": 318, + "sha256": "508d4f348b19eea357bb4c630dede9f28995c571d4824528450f806dcace1d97" + }, + { + "resource_type": "14", + "resource_name": "2078", + "language": "1033", + "rva": "0x016ea310", + "path": "icons/2078_1033.ico", + "size": 318, + "sha256": "ca9e7d252c4a9ae859366017b8f94723ee505dcf0b104aa0094f7982653a6c4a" + }, + { + "resource_type": "14", + "resource_name": "2079", + "language": "1033", + "rva": "0x016ea450", + "path": "icons/2079_1033.ico", + "size": 318, + "sha256": "3237be1e6d36236e7ea0782455068efa60d306ff4f2db73ff412fa3fcbcf8f06" + }, + { + "resource_type": "14", + "resource_name": "5092", + "language": "1033", + "rva": "0x016d0f18", + "path": "icons/5092_1033.ico", + "size": 318, + "sha256": "b8eb7dd57522998b06fffadc91b7b85923f68731f4c5968d80c3afa393bc9dc7" + }, + { + "resource_type": "14", + "resource_name": "5098", + "language": "1033", + "rva": "0x016d66a0", + "path": "icons/5098_1033.ico", + "size": 22486, + "sha256": "ed0e24cbe857cb8a869743291c16b72deeefe19ce3fcd0c31a2af4b83bf25456" + }, + { + "resource_type": "14", + "resource_name": "5099", + "language": "1033", + "rva": "0x016d6828", + "path": "icons/5099_1033.ico", + "size": 318, + "sha256": "3b037bcd4b963fa4ea59d37ce465ca341641642f6bbb7c6ca43d4ef66e8d95b8" + }, + { + "resource_type": "14", + "resource_name": "5101", + "language": "1033", + "rva": "0x016d6968", + "path": "icons/5101_1033.ico", + "size": 318, + "sha256": "9fafebd6bffbe5ebb0e2bb3a996a8115f28de2e72a46dee6694f3f2e452c428d" + }, + { + "resource_type": "14", + "resource_name": "5102", + "language": "1033", + "rva": "0x016d6aa8", + "path": "icons/5102_1033.ico", + "size": 318, + "sha256": "12481f915db5ceb1b2c933f22c48638361eec168bf2955263c3fea73cf345d7b" + }, + { + "resource_type": "14", + "resource_name": "5103", + "language": "1033", + "rva": "0x016d6be8", + "path": "icons/5103_1033.ico", + "size": 318, + "sha256": "83635a7b458959108c1209860d41c6bcf754eabd8ac8488e55cb5cc7fd8038ee" + }, + { + "resource_type": "14", + "resource_name": "5127", + "language": "1033", + "rva": "0x016c8808", + "path": "icons/5127_1033.ico", + "size": 766, + "sha256": "1025522605a2c47165030064efaa3f6caceaf6e3c57c692ef25e96812c6297d1" + }, + { + "resource_type": "14", + "resource_name": "5153", + "language": "1033", + "rva": "0x016cdf90", + "path": "icons/5153_1033.ico", + "size": 22486, + "sha256": "d4510ab76b51f3667c516534c7f48d54d0becda8623b1f7921808b2665710d43" + }, + { + "resource_type": "14", + "resource_name": "6003", + "language": "1033", + "rva": "0x016dca10", + "path": "icons/6003_1033.ico", + "size": 22486, + "sha256": "f8543cc46636cecd210b1f6dd047fab342c3cda473b9a10dddb8327b24b96a8e" + }, + { + "resource_type": "14", + "resource_name": "7052", + "language": "1033", + "rva": "0x016f6b10", + "path": "icons/7052_1033.ico", + "size": 1078, + "sha256": "e09441af754fe41ec511947171f8f73a92beb2518455c8eddd7cc1b73b05380f" + }, + { + "resource_type": "14", + "resource_name": "8002", + "language": "1033", + "rva": "0x016bcd00", + "path": "icons/8002_1033.ico", + "size": 22486, + "sha256": "f5296213be0c0252f37da381aa324b27f628633541bd05163001bb83c5d1e754" + }, + { + "resource_type": "14", + "resource_name": "15665", + "language": "1033", + "rva": "0x016f05d8", + "path": "icons/15665_1033.ico", + "size": 22486, + "sha256": "e2fff88875226a55463ecbe95e36cfeceeb9149f988cf8f727a472502d0bf07b" + }, + { + "resource_type": "14", + "resource_name": "17012", + "language": "1033", + "rva": "0x016f65a8", + "path": "icons/17012_1033.ico", + "size": 22486, + "sha256": "0823c8c5bf88ae2f952af35b96397631fd03b1e46c0e686d5a27438b2575cf64" + }, + { + "resource_type": "14", + "resource_name": "21113", + "language": "1033", + "rva": "0x016b62c8", + "path": "icons/21113_1033.ico", + "size": 318, + "sha256": "017868ed73d2451d663edf44c4507bfb4f8e17974f46090a3e7599e7851e1faf" + }, + { + "resource_type": "14", + "resource_name": "21114", + "language": "1033", + "rva": "0x016b6408", + "path": "icons/21114_1033.ico", + "size": 318, + "sha256": "fd811fc5ef5014801f9f74d62e35ba6da013229afe05d350427e4feaa8881d5f" + }, + { + "resource_type": "14", + "resource_name": "21115", + "language": "1033", + "rva": "0x016b6548", + "path": "icons/21115_1033.ico", + "size": 318, + "sha256": "12277df21909a49b7033da9bec357e74168e8f8b5b90d77af13b7610accd38fd" + } + ] +} diff --git a/src/tools/ToolRenderAdapters.cpp b/src/tools/ToolRenderAdapters.cpp new file mode 100644 index 0000000..d797ceb --- /dev/null +++ b/src/tools/ToolRenderAdapters.cpp @@ -0,0 +1,56 @@ +/* +=========================================================================== + +Quake 4 Reconstructed GPL Source Code +Copyright (C) 2026 Justin Marshall(IceColdDuke). + +Small tool-DLL-side adapters for renderer operations that are intentionally +not exported as concrete engine symbols. Object ownership remains with the +engine interfaces passed through toolsImport_t. + +=========================================================================== +*/ + +#include "../idlib/precompiled.h" +#pragma hdrstop + +#include "../renderer/Image.h" + +idImage *idMaterial::GetEditorImage( void ) const { + if ( editorImage != NULL ) { + return editorImage; + } + + if ( stages != NULL ) { + for ( int i = 0; i < numStages; ++i ) { + if ( stages[i].lighting == SL_DIFFUSE && stages[i].texture.image != NULL ) { + editorImage = stages[i].texture.image; + break; + } + } + if ( editorImage == NULL && numStages > 0 ) { + editorImage = stages[0].texture.image; + } + } + + if ( editorImage == NULL && globalImages != NULL ) { + editorImage = globalImages->defaultImage; + } + return editorImage; +} + +void idImageManager::BindNull( void ) { + qglDisable( GL_TEXTURE_2D ); + qglDisable( GL_TEXTURE_3D ); + qglDisable( GL_TEXTURE_CUBE_MAP_EXT ); +} + +void GL_State( int stateBits ) { + (void)stateBits; + qglColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE ); + qglDepthMask( GL_TRUE ); + qglDepthFunc( GL_LEQUAL ); + qglDisable( GL_BLEND ); + qglDisable( GL_ALPHA_TEST ); + qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); +} diff --git a/src/tools/Tools.h b/src/tools/Tools.h new file mode 100644 index 0000000..10cf9eb --- /dev/null +++ b/src/tools/Tools.h @@ -0,0 +1,275 @@ +/* +=========================================================================== + +Quake 4 Reconstructed GPL Source Code +Copyright (C) 2026 Justin Marshall(IceColdDuke). + +Retail tools API reconstructed from the matching Quake 4 executable and PDB. + +=========================================================================== +*/ + +#ifndef __TOOLS_H__ +#define __TOOLS_H__ + +#include + +#if defined( _WIN32 ) +#include +#else +typedef void *HDC; +struct tagMSG; +#endif + +#define TOOLS_API_VERSION 2 + +class idAASFile; +class idCmdArgs; +class idCmdSystem; +class idCommon; +class idCVarSystem; +class idDeclManager; +class idDict; +class idFileSystem; +class idGameEdit; +class idImageManager; +class idInterpreter; +class idPlane; +class idProgram; +class idRenderModelManager; +class idRenderSystem; +class idSession; +class idSoundSystem; +class idSys; +class idUserInterfaceManager; +class idVec3; +class idVec4; +class idWindow; +class rvDeclAFEdit; +class rvDeclEffectEdit; +class rvDeclLipSyncEdit; +class rvDeclPlaybackEdit; +class rvISourceControl; +class rvMaterialEdit; +class rvSoundShaderEdit; +class rvVarEdit; +class rvWindowEdit; +struct srfTriangles_s; + +struct indexRef_s { + int frontCapStart; + int rearCapStart; + int silStart; + int end; +}; + +struct SOOData_s { + int numShadowIndexes; + int firstShadowIndex; + int numShadowVerts; + int firstShadowVert; + int indexFrustumNumber; + idVec4 *shadowVerts; + int *shadowIndexes; + indexRef_s *indexRef; +}; + +typedef void *(*toolsAlloc_t)( size_t size ); +typedef void (*toolsFree_t)( void *pointer ); +typedef size_t (*toolsMSize_t)( void *pointer ); + +// The virtual order is recovered from rvToolsStub in quake4.pdb. The FX +// entries remain in place for ABI compatibility even though this port uses +// BSE and intentionally does not import the Doom particle/FX editor. +class rvTools { +public: + virtual void * GetInstance( void ) const = 0; + virtual void * GetParentWindow( void ) const = 0; + virtual void * GetDC( void ) const = 0; + virtual bool IsParentWindowVisible( void ) = 0; + virtual void InitTool( int tool, const idDict *dict ) = 0; + virtual void ShutdownTool( int tool ) = 0; + virtual int IsToolActive( int tool ) = 0; + virtual void Frame( void ) = 0; + virtual void Shutdown( void ) = 0; + virtual void HandleToolPrint( const char *text ) = 0; + virtual void HandleMapChange( void ) = 0; + virtual void StartLevelLoad( void ) = 0; + virtual void EndLevelLoad( void ) = 0; + virtual rvISourceControl *GetSourceControl( void ) = 0; + virtual bool MakeGameCurrent( void ) = 0; + virtual void SetDefaultState( void ) = 0; + virtual void Set2D( int width, int height ) = 0; + virtual void DoRBFDialog( const char *fileName ) = 0; + virtual void PlaybackEditorRefresh( void ) = 0; + virtual void ModViewShutdown( const char *reason ) = 0; + virtual void ModViewRun( void ) = 0; + virtual int ModViewGetJointStatus( int joint ) = 0; + virtual bool ModViewIsSurfaceSelected( const char *surface ) = 0; + virtual bool ModViewIsSurfaceHidden( const char *surface ) = 0; + virtual const idVec4 * ModViewGetBgrndColor( void ) = 0; + + virtual bool FXEditorIsActive( void ) = 0; + virtual void FXEditorRefreshEffects( void ) = 0; + virtual void FXEditorPlayEffect( bool loop ) = 0; + virtual void FXEditorStopEffect( void ) = 0; + virtual void FXEditorRefreshMaterials( void ) = 0; + + virtual void GEAllocateWindowWrapper( idWindow *window ) = 0; + virtual void GEDeallocateWindowWrapper( idWindow *window ) = 0; + virtual void GEFinish( idWindow *window ) = 0; + virtual void GEAddScript( idWindow *window, const char *name, const char *script ) = 0; + virtual void GEVariableDictSet( idWindow *window, const char *name, const char *value ) = 0; + virtual void GESetStateKey( idWindow *window, const char *name, const char *value, bool update ) = 0; + virtual void GEMessage( const char *format, ... ) = 0; + virtual void DebuggerPrint( const char *text ) = 0; + virtual void DebuggerCheckBreakpoint( idInterpreter *interpreter, idProgram *program, int instructionPointer ) = 0; + virtual void DmapCleanupOptimizedShadowTris( int numTris, srfTriangles_s *triangles ) = 0; + virtual bool DmapSuperOptimizeOccluders( SOOData_s *data, idVec4 *planes, int *indexes, int numIndexes, idPlane plane, idVec3 origin ) = 0; + virtual void DmapOutputString( const char *text ) = 0; + virtual void SetListenerArea( int area ) = 0; + virtual int GetListenerArea( void ) const = 0; + virtual void PhonemeGen( const idCmdArgs &args ) = 0; + virtual void ShakesGen( const idCmdArgs &args ) = 0; + virtual void RoQFileEncode( const idCmdArgs &args ) = 0; + virtual void Renderbump( const idCmdArgs &args ) = 0; + virtual void RenderbumpFlat( const idCmdArgs &args ) = 0; + virtual void Dmap( const idCmdArgs &args ) = 0; + virtual void RunAAS( const idCmdArgs &args ) = 0; + virtual void RunAASDir( const idCmdArgs &args ) = 0; + virtual void RunReach( const idCmdArgs &args ) = 0; + virtual void RunAASTactical( const idCmdArgs &args ) = 0; + virtual void LocaliseGuis( const idCmdArgs &args ) = 0; + virtual void LocaliseLipsyncs( const idCmdArgs &args ) = 0; + virtual void LocaliseMaps( const idCmdArgs &args ) = 0; + virtual void LocaliseValidateLipsyncs( const idCmdArgs &args ) = 0; + virtual void LocaliseValidateStrings( const idCmdArgs &args ) = 0; + virtual bool HandleMessage( tagMSG *message ) = 0; + virtual int SetupPixelFormat( HDC dc ) = 0; + virtual bool MakeCurrent( HDC dc ) = 0; + virtual ~rvTools() {} +}; + +class rvToolsStub : public rvTools { +public: + virtual void * GetInstance( void ) const; + virtual void * GetParentWindow( void ) const; + virtual void * GetDC( void ) const; + virtual bool IsParentWindowVisible( void ); + virtual void InitTool( int tool, const idDict *dict ); + virtual void ShutdownTool( int tool ); + virtual int IsToolActive( int tool ); + virtual void Frame( void ); + virtual void Shutdown( void ); + virtual void HandleToolPrint( const char *text ); + virtual void HandleMapChange( void ); + virtual void StartLevelLoad( void ); + virtual void EndLevelLoad( void ); + virtual rvISourceControl *GetSourceControl( void ); + virtual bool MakeGameCurrent( void ); + virtual void SetDefaultState( void ); + virtual void Set2D( int width, int height ); + virtual void DoRBFDialog( const char *fileName ); + virtual void PlaybackEditorRefresh( void ); + virtual void ModViewShutdown( const char *reason ); + virtual void ModViewRun( void ); + virtual int ModViewGetJointStatus( int joint ); + virtual bool ModViewIsSurfaceSelected( const char *surface ); + virtual bool ModViewIsSurfaceHidden( const char *surface ); + virtual const idVec4 * ModViewGetBgrndColor( void ); + virtual bool FXEditorIsActive( void ); + virtual void FXEditorRefreshEffects( void ); + virtual void FXEditorPlayEffect( bool loop ); + virtual void FXEditorStopEffect( void ); + virtual void FXEditorRefreshMaterials( void ); + virtual void GEAllocateWindowWrapper( idWindow *window ); + virtual void GEDeallocateWindowWrapper( idWindow *window ); + virtual void GEFinish( idWindow *window ); + virtual void GEAddScript( idWindow *window, const char *name, const char *script ); + virtual void GEVariableDictSet( idWindow *window, const char *name, const char *value ); + virtual void GESetStateKey( idWindow *window, const char *name, const char *value, bool update ); + virtual void GEMessage( const char *format, ... ); + virtual void DebuggerPrint( const char *text ); + virtual void DebuggerCheckBreakpoint( idInterpreter *interpreter, idProgram *program, int instructionPointer ); + virtual void DmapCleanupOptimizedShadowTris( int numTris, srfTriangles_s *triangles ); + virtual bool DmapSuperOptimizeOccluders( SOOData_s *data, idVec4 *planes, int *indexes, int numIndexes, idPlane plane, idVec3 origin ); + virtual void DmapOutputString( const char *text ); + virtual void SetListenerArea( int area ); + virtual int GetListenerArea( void ) const; + virtual void PhonemeGen( const idCmdArgs &args ); + virtual void ShakesGen( const idCmdArgs &args ); + virtual void RoQFileEncode( const idCmdArgs &args ); + virtual void Renderbump( const idCmdArgs &args ); + virtual void RenderbumpFlat( const idCmdArgs &args ); + virtual void Dmap( const idCmdArgs &args ); + virtual void RunAAS( const idCmdArgs &args ); + virtual void RunAASDir( const idCmdArgs &args ); + virtual void RunReach( const idCmdArgs &args ); + virtual void RunAASTactical( const idCmdArgs &args ); + virtual void LocaliseGuis( const idCmdArgs &args ); + virtual void LocaliseLipsyncs( const idCmdArgs &args ); + virtual void LocaliseMaps( const idCmdArgs &args ); + virtual void LocaliseValidateLipsyncs( const idCmdArgs &args ); + virtual void LocaliseValidateStrings( const idCmdArgs &args ); + virtual bool HandleMessage( tagMSG *message ); + virtual int SetupPixelFormat( HDC dc ); + virtual bool MakeCurrent( HDC dc ); + virtual ~rvToolsStub() {} +}; + +struct toolsImport_t { + int version; + int instance; + void * ownerWnd; + void * ownerDC; + void * hGLRC; + void * pfd; + idSys * sys; + idCommon * common; + idCmdSystem * cmdSystem; + idCVarSystem * cvarSystem; + idFileSystem * fileSystem; + idRenderSystem * renderSystem; + idSoundSystem * soundSystem; + idRenderModelManager * renderModelManager; + idUserInterfaceManager *uiManager; + idDeclManager * declManager; + idAASFile * AASFile; + class idCollisionModelManager *collisionModelManager; + idGameEdit * gameEdit; + rvMaterialEdit * materialEdit; + rvSoundShaderEdit * soundShaderEdit; + rvDeclAFEdit * declAFEdit; + rvDeclPlaybackEdit * declPlaybackEdit; + rvDeclEffectEdit * declEffectEdit; + rvDeclLipSyncEdit * declLipSyncEdit; + rvWindowEdit * windowEdit; + rvVarEdit * varEdit; + idSession * session; + idImageManager * globalImages; + class idConsole * console; +}; + +struct toolsExport_t { + int version; + rvTools * tools; +}; + +typedef toolsExport_t *(*GetToolsAPI_t)( + toolsImport_t *imports, + toolsAlloc_t allocator, + toolsFree_t deallocator, + toolsMSize_t msize +); + +#if defined( _WIN32 ) && !defined( _WIN64 ) +static_assert( sizeof( toolsImport_t ) == 120, "retail toolsImport_t ABI drift" ); +static_assert( sizeof( toolsExport_t ) == 8, "retail toolsExport_t ABI drift" ); +static_assert( sizeof( SOOData_s ) == 32, "retail SOOData_s ABI drift" ); +static_assert( sizeof( indexRef_s ) == 16, "retail indexRef_s ABI drift" ); +#endif + +extern rvToolsStub toolsStub; +extern rvTools *tools; + +#endif /* !__TOOLS_H__ */ diff --git a/src/tools/ToolsStub.cpp b/src/tools/ToolsStub.cpp new file mode 100644 index 0000000..d1456ab --- /dev/null +++ b/src/tools/ToolsStub.cpp @@ -0,0 +1,280 @@ +/* +=========================================================================== + +Quake 4 Reconstructed GPL Source Code +Copyright (C) 2026 Justin Marshall(IceColdDuke). + +Bootstrap implementation of the retail Toolsx86 API. Compiler and editor +front-ends replace these placeholders incrementally as their Doom baselines +are reconciled with Quake 4. + +=========================================================================== +*/ + +#include "../idlib/precompiled.h" +#pragma hdrstop + +#include "Tools.h" +#include "ToolsStub.inl" +#include "compilers/dmap/dmap.h" +#include "../sys/win32/win_local.h" + +static toolsImport_t toolsImports; +static toolsAlloc_t toolsAllocator; +static toolsFree_t toolsDeallocator; +static toolsMSize_t toolsAllocationSize; + +// Tool translation units use the same global interface names as the engine. +// In the DLL they are aliases for the pointers supplied through toolsImport_t. +idSys *sys = NULL; +idCommon *common = NULL; +idCmdSystem *cmdSystem = NULL; +idCVarSystem *cvarSystem = NULL; +idFileSystem *fileSystem = NULL; +idRenderSystem *renderSystem = NULL; +idSoundSystem *soundSystem = NULL; +idRenderModelManager *renderModelManager = NULL; +idUserInterfaceManager *uiManager = NULL; +idDeclManager *declManager = NULL; +idAASFile *AASFile = NULL; +idCollisionModelManager *collisionModelManager = NULL; +idGameEdit *gameEdit = NULL; +rvMaterialEdit *materialEdit = NULL; +rvSoundShaderEdit *soundShaderEdit = NULL; +rvDeclAFEdit *declAFEdit = NULL; +rvDeclPlaybackEdit *declPlaybackEdit = NULL; +rvDeclEffectEdit *declEffectEdit = NULL; +rvDeclLipSyncEdit *declLipSyncEdit = NULL; +rvWindowEdit *windowEdit = NULL; +rvVarEdit *varEdit = NULL; +idSession *session = NULL; +idImageManager *globalImages = NULL; +idConsole *console = NULL; +idCVar *idCVar::staticVars = NULL; + +// The retail tools module owns a mirror of the engine's Win32 state. Editor +// code only consumes the parent window and shared OpenGL context fields; those +// are supplied explicitly by toolsImport_t instead of reaching into quake4.exe. +Win32Vars_t win32; + +// Doom compiler sources call these engine globals directly. The tools DLL +// owns its editor mask and routes timing through the imported Quake 4 idSys. +int com_editors = 0; +bool com_editorActive = false; +HWND com_hwndMsg = NULL; +bool com_outputMsg = false; +int Sys_Milliseconds( void ) { + return sys ? sys->Milliseconds() : 0; +} +void Sys_GrabMouseCursor( bool grabIt ) { + if ( sys != NULL ) { + sys->GrabMouseCursor( grabIt ); + } +} +void Com_WriteConfigToFile( const char *filename ) { + if ( common != NULL ) { + common->WriteConfigToFile( filename ); + } +} +void Sys_Error( const char *error, ... ) { + char buffer[4096]; + va_list args; + va_start( args, error ); + _vsnprintf_s( buffer, sizeof( buffer ), _TRUNCATE, error, args ); + va_end( args ); + if ( common != NULL ) { + common->Error( "%s", buffer ); + } +} + +void RunAAS_f( const idCmdArgs &args ); +void RunAASDir_f( const idCmdArgs &args ); +void RunReach_f( const idCmdArgs &args ); +void RunAASTactical_f( const idCmdArgs &args ); +void Dmap_f( const idCmdArgs &args ); +void RadiantInit( void ); +void RadiantRun( void ); +void RadiantShutdown( void ); +void RadiantPrint( const char *text ); +void LightEditorInit( const idDict *spawnArgs ); +void LightEditorRun( void ); +void LightEditorShutdown( void ); +int WINAPI QEW_SetupPixelFormat( HDC hDC, bool zbuffer ); +optimizedShadow_t SuperOptimizeOccluders( idVec4 *verts, glIndex_t *indexes, int numIndexes, + idPlane projectionPlane, idVec3 projectionOrigin ); +void CleanupOptimizedShadowTris( srfTriangles_t *tri ); + +class rvToolsLocal : public rvToolsStub { +public: + rvToolsLocal() : activeTools( 0 ), listenerArea( -1 ) {} + + virtual void *GetInstance( void ) const { return reinterpret_cast( toolsImports.instance ); } + virtual void *GetParentWindow( void ) const { return toolsImports.ownerWnd; } + virtual void *GetDC( void ) const { return toolsImports.ownerDC; } + virtual bool IsParentWindowVisible( void ) { + return toolsImports.ownerWnd != NULL && ::IsWindowVisible( static_cast( toolsImports.ownerWnd ) ) != FALSE; + } + virtual void InitTool( int tool, const idDict *dict ) { + if ( tool & EDITOR_RADIANT ) { + RadiantInit(); + activeTools |= EDITOR_RADIANT; + } + if ( tool & EDITOR_LIGHT ) { + LightEditorInit( dict ); + activeTools |= EDITOR_LIGHT; + } + const int supported = EDITOR_RADIANT | EDITOR_LIGHT; + if ( tool & ~supported ) { + Unavailable( "requested editor front-end" ); + } + } + virtual void ShutdownTool( int tool ) { + const int closing = tool == -1 ? activeTools : tool; + if ( closing & EDITOR_LIGHT ) { + LightEditorShutdown(); + } + if ( closing & EDITOR_RADIANT ) { + RadiantShutdown(); + } + activeTools = tool == -1 ? 0 : ( activeTools & ~tool ); + } + virtual int IsToolActive( int tool ) { + return tool == -1 ? activeTools : ( activeTools & tool ); + } + virtual void Frame( void ) { + if ( activeTools & EDITOR_RADIANT ) { + RadiantRun(); + } else if ( activeTools & EDITOR_LIGHT ) { + LightEditorRun(); + } + } + virtual void Shutdown( void ) { ShutdownTool( -1 ); } + virtual void HandleToolPrint( const char *text ) { RadiantPrint( text ); } + virtual int SetupPixelFormat( HDC dc ) { return QEW_SetupPixelFormat( dc, true ); } + virtual bool MakeCurrent( HDC dc ) { return qwglMakeCurrent( dc, win32.hGLRC ) != FALSE; } + virtual void SetListenerArea( int area ) { listenerArea = area; } + virtual int GetListenerArea( void ) const { return listenerArea; } + virtual void DmapCleanupOptimizedShadowTris( int numTris, srfTriangles_s *triangles ) { + (void)numTris; + CleanupOptimizedShadowTris( triangles ); + } + virtual bool DmapSuperOptimizeOccluders( SOOData_s *data, idVec4 *verts, int *indexes, + int numIndexes, idPlane plane, idVec3 origin ) { + optimizedShadow_t optimized = SuperOptimizeOccluders( verts, indexes, numIndexes, plane, origin ); + if ( !optimized.verts || !optimized.indexes ) { + return true; + } + + const int firstVertex = data->firstShadowVert; + const int firstIndex = data->firstShadowIndex; + if ( firstVertex + optimized.numVerts > 0x18000 || firstIndex + optimized.totalIndexes > 0x18000 ) { + Mem_Free( optimized.verts ); + Mem_Free( optimized.indexes ); + return true; + } + + for ( int i = 0; i < optimized.numVerts; i++ ) { + data->shadowVerts[firstVertex + i].Set( optimized.verts[i].x, optimized.verts[i].y, optimized.verts[i].z, 1.0f ); + } + for ( int i = 0; i < optimized.totalIndexes; i++ ) { + data->shadowIndexes[firstIndex + i] = firstVertex + optimized.indexes[i]; + } + + indexRef_s &ref = data->indexRef[data->indexFrustumNumber++]; + ref.frontCapStart = firstIndex; + ref.rearCapStart = ref.frontCapStart + optimized.numFrontCapIndexes; + ref.silStart = ref.rearCapStart + optimized.numRearCapIndexes; + ref.end = ref.silStart + optimized.numSilPlaneIndexes; + data->numShadowVerts = firstVertex + optimized.numVerts; + data->numShadowIndexes = firstIndex + optimized.totalIndexes; + + Mem_Free( optimized.verts ); + Mem_Free( optimized.indexes ); + return false; + } + + virtual void PhonemeGen( const idCmdArgs &args ) { Unavailable( "phoneme generator" ); } + virtual void ShakesGen( const idCmdArgs &args ) { Unavailable( "shake generator" ); } + virtual void RoQFileEncode( const idCmdArgs &args ) { Unavailable( "RoQ encoder" ); } + virtual void Renderbump( const idCmdArgs &args ) { Unavailable( "renderbump" ); } + virtual void RenderbumpFlat( const idCmdArgs &args ) { Unavailable( "renderbumpFlat" ); } + virtual void Dmap( const idCmdArgs &args ) { Dmap_f( args ); } + virtual void RunAAS( const idCmdArgs &args ) { RunAAS_f( args ); } + virtual void RunAASDir( const idCmdArgs &args ) { RunAASDir_f( args ); } + virtual void RunReach( const idCmdArgs &args ) { RunReach_f( args ); } + virtual void RunAASTactical( const idCmdArgs &args ) { RunAASTactical_f( args ); } + +private: + void Unavailable( const char *name ) { + if ( toolsImports.common != NULL ) { + toolsImports.common->Printf( "Toolsx86 bootstrap: %s is not connected yet.\n", name ); + } + } + + int activeTools; + int listenerArea; +}; + +static rvToolsLocal toolsLocal; +static toolsExport_t toolsExport; + +extern "C" __declspec( dllexport ) toolsExport_t * __cdecl GetToolsAPI( + toolsImport_t *imports, + toolsAlloc_t allocator, + toolsFree_t deallocator, + toolsMSize_t msize ) { + + memset( &toolsImports, 0, sizeof( toolsImports ) ); + toolsAllocator = allocator; + toolsDeallocator = deallocator; + toolsAllocationSize = msize; + + toolsExport.version = 0; + toolsExport.tools = NULL; + if ( imports == NULL || imports->version != TOOLS_API_VERSION ) { + return &toolsExport; + } + + toolsImports = *imports; + memset( &win32, 0, sizeof( win32 ) ); + win32.hWnd = static_cast( imports->ownerWnd ); + win32.hInstance = reinterpret_cast( imports->instance ); + win32.hDC = static_cast( imports->ownerDC ); + win32.hGLRC = static_cast( imports->hGLRC ); + if ( imports->pfd != NULL ) { + win32.pfd = *static_cast( imports->pfd ); + } + sys = imports->sys; + common = imports->common; + cmdSystem = imports->cmdSystem; + cvarSystem = imports->cvarSystem; + idCVar::RegisterStaticVars(); + fileSystem = imports->fileSystem; + renderSystem = imports->renderSystem; + soundSystem = imports->soundSystem; + renderModelManager = imports->renderModelManager; + uiManager = imports->uiManager; + declManager = imports->declManager; + AASFile = imports->AASFile; + collisionModelManager = imports->collisionModelManager; + gameEdit = imports->gameEdit; + materialEdit = imports->materialEdit; + soundShaderEdit = imports->soundShaderEdit; + declAFEdit = imports->declAFEdit; + declPlaybackEdit = imports->declPlaybackEdit; + declEffectEdit = imports->declEffectEdit; + declLipSyncEdit = imports->declLipSyncEdit; + windowEdit = imports->windowEdit; + varEdit = imports->varEdit; + session = imports->session; + globalImages = imports->globalImages; + console = imports->console; + idLib::sys = sys; + idLib::common = common; + idLib::cvarSystem = cvarSystem; + idLib::fileSystem = fileSystem; + Memory::InitAllocator( allocator, deallocator, msize ); + toolsExport.version = TOOLS_API_VERSION; + toolsExport.tools = &toolsLocal; + return &toolsExport; +} diff --git a/src/tools/ToolsStub.inl b/src/tools/ToolsStub.inl new file mode 100644 index 0000000..3bfc93d --- /dev/null +++ b/src/tools/ToolsStub.inl @@ -0,0 +1,64 @@ +/* Shared no-op rvTools implementation used before the tools DLL is loaded. */ + +inline void *rvToolsStub::GetInstance( void ) const { return NULL; } +inline void *rvToolsStub::GetParentWindow( void ) const { return NULL; } +inline void *rvToolsStub::GetDC( void ) const { return NULL; } +inline bool rvToolsStub::IsParentWindowVisible( void ) { return false; } +inline void rvToolsStub::InitTool( int tool, const idDict *dict ) {} +inline void rvToolsStub::ShutdownTool( int tool ) {} +inline int rvToolsStub::IsToolActive( int tool ) { return 0; } +inline void rvToolsStub::Frame( void ) {} +inline void rvToolsStub::Shutdown( void ) {} +inline void rvToolsStub::HandleToolPrint( const char *text ) {} +inline void rvToolsStub::HandleMapChange( void ) {} +inline void rvToolsStub::StartLevelLoad( void ) {} +inline void rvToolsStub::EndLevelLoad( void ) {} +inline rvISourceControl *rvToolsStub::GetSourceControl( void ) { return NULL; } +inline bool rvToolsStub::MakeGameCurrent( void ) { return false; } +inline void rvToolsStub::SetDefaultState( void ) {} +inline void rvToolsStub::Set2D( int width, int height ) {} +inline void rvToolsStub::DoRBFDialog( const char *fileName ) {} +inline void rvToolsStub::PlaybackEditorRefresh( void ) {} +inline void rvToolsStub::ModViewShutdown( const char *reason ) {} +inline void rvToolsStub::ModViewRun( void ) {} +inline int rvToolsStub::ModViewGetJointStatus( int joint ) { return 0; } +inline bool rvToolsStub::ModViewIsSurfaceSelected( const char *surface ) { return false; } +inline bool rvToolsStub::ModViewIsSurfaceHidden( const char *surface ) { return false; } +inline const idVec4 *rvToolsStub::ModViewGetBgrndColor( void ) { return NULL; } +inline bool rvToolsStub::FXEditorIsActive( void ) { return false; } +inline void rvToolsStub::FXEditorRefreshEffects( void ) {} +inline void rvToolsStub::FXEditorPlayEffect( bool loop ) {} +inline void rvToolsStub::FXEditorStopEffect( void ) {} +inline void rvToolsStub::FXEditorRefreshMaterials( void ) {} +inline void rvToolsStub::GEAllocateWindowWrapper( idWindow *window ) {} +inline void rvToolsStub::GEDeallocateWindowWrapper( idWindow *window ) {} +inline void rvToolsStub::GEFinish( idWindow *window ) {} +inline void rvToolsStub::GEAddScript( idWindow *window, const char *name, const char *script ) {} +inline void rvToolsStub::GEVariableDictSet( idWindow *window, const char *name, const char *value ) {} +inline void rvToolsStub::GESetStateKey( idWindow *window, const char *name, const char *value, bool update ) {} +inline void rvToolsStub::GEMessage( const char *format, ... ) {} +inline void rvToolsStub::DebuggerPrint( const char *text ) {} +inline void rvToolsStub::DebuggerCheckBreakpoint( idInterpreter *interpreter, idProgram *program, int instructionPointer ) {} +inline void rvToolsStub::DmapCleanupOptimizedShadowTris( int numTris, srfTriangles_s *triangles ) {} +inline bool rvToolsStub::DmapSuperOptimizeOccluders( SOOData_s *data, idVec4 *planes, int *indexes, int numIndexes, idPlane plane, idVec3 origin ) { return false; } +inline void rvToolsStub::DmapOutputString( const char *text ) {} +inline void rvToolsStub::SetListenerArea( int area ) {} +inline int rvToolsStub::GetListenerArea( void ) const { return -1; } +inline void rvToolsStub::PhonemeGen( const idCmdArgs &args ) {} +inline void rvToolsStub::ShakesGen( const idCmdArgs &args ) {} +inline void rvToolsStub::RoQFileEncode( const idCmdArgs &args ) {} +inline void rvToolsStub::Renderbump( const idCmdArgs &args ) {} +inline void rvToolsStub::RenderbumpFlat( const idCmdArgs &args ) {} +inline void rvToolsStub::Dmap( const idCmdArgs &args ) {} +inline void rvToolsStub::RunAAS( const idCmdArgs &args ) {} +inline void rvToolsStub::RunAASDir( const idCmdArgs &args ) {} +inline void rvToolsStub::RunReach( const idCmdArgs &args ) {} +inline void rvToolsStub::RunAASTactical( const idCmdArgs &args ) {} +inline void rvToolsStub::LocaliseGuis( const idCmdArgs &args ) {} +inline void rvToolsStub::LocaliseLipsyncs( const idCmdArgs &args ) {} +inline void rvToolsStub::LocaliseMaps( const idCmdArgs &args ) {} +inline void rvToolsStub::LocaliseValidateLipsyncs( const idCmdArgs &args ) {} +inline void rvToolsStub::LocaliseValidateStrings( const idCmdArgs &args ) {} +inline bool rvToolsStub::HandleMessage( tagMSG *message ) { return false; } +inline int rvToolsStub::SetupPixelFormat( HDC dc ) { return 0; } +inline bool rvToolsStub::MakeCurrent( HDC dc ) { return false; } diff --git a/src/tools/Toolsx86.def b/src/tools/Toolsx86.def new file mode 100644 index 0000000..a945a65 --- /dev/null +++ b/src/tools/Toolsx86.def @@ -0,0 +1,3 @@ +LIBRARY Toolsx86 +EXPORTS + GetToolsAPI diff --git a/src/tools/comafx/CDIB.cpp b/src/tools/comafx/CDIB.cpp new file mode 100644 index 0000000..7380ccb --- /dev/null +++ b/src/tools/comafx/CDIB.cpp @@ -0,0 +1,1022 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#ifdef ID_DEBUG_MEMORY +#undef new +#endif + +#include "math.h" +#include "CDIB.h" + +// Original ColorPicker/DIB source by Rajiv Ramachandran +// included with Permission from the author + +#define BIG_DISTANCE 10000000L + +#define DIST(r1,g1,b1,r2,g2,b2) \ + (long) (3L*(long)((r1)-(r2))*(long)((r1)-(r2)) + \ + 4L*(long)((g1)-(g2))*(long)((g1)-(g2)) + \ + 2L*(long)((b1)-(b2))*(long)((b1)-(b2))) + + +static unsigned char masktable[] = { 0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01 }; + + + +CDIB::CDIB(HANDLE hDib,int nBits) +{ + m_pVoid = NULL; + m_pLinePtr = NULL; + m_bUseGamma=FALSE; + width=height=0; + if(hDib) + { + CreateFromHandle(hDib,nBits); + } +} + +CDIB::~CDIB() +{ + DestroyDIB(); +} + +void CDIB::DestroyDIB() +{ + if(m_pVoid) free(m_pVoid); + m_pVoid = NULL; + if(m_pLinePtr) free(m_pLinePtr); + m_pLinePtr = NULL; +} + + +BOOL CDIB::Create(int width,int height,int bits) +{ + /* + Free existing image + */ + DestroyDIB(); +// ASSERT(bits == 24 || bits == 8); + +BITMAPINFOHEADER bmInfo; + + memset(&bmInfo,0,sizeof(BITMAPINFOHEADER)); + bmInfo.biSize = sizeof(BITMAPINFOHEADER); + bmInfo.biWidth = width; + bmInfo.biHeight = height; + bmInfo.biPlanes = 1; + bmInfo.biBitCount = bits; + bmInfo.biCompression = BI_RGB; + return Create(bmInfo); +} + +BOOL CDIB::Create(BITMAPINFOHEADER& bmInfo) +{ + bytes = (bmInfo.biBitCount*bmInfo.biWidth)>>3; + height = bmInfo.biHeight; + width = bmInfo.biWidth; +// bmInfo.biHeight *= -1; + while(bytes%4) bytes++; + + int size; + size = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*GetPaletteSize(bmInfo) + bytes*height; + m_pVoid = (void *)malloc(size); + if(!m_pVoid) return FALSE; + + m_pInfo = (PBITMAPINFO )m_pVoid; + memcpy((void *)&m_pInfo->bmiHeader,(void *)&bmInfo,sizeof(BITMAPINFOHEADER)); + m_pRGB = (RGBQUAD *)((unsigned char *)m_pVoid + sizeof(BITMAPINFOHEADER)) ; + m_pBits = (unsigned char *)(m_pVoid) + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*GetPaletteSize(); + +int i; +BYTE **ptr; + m_pLinePtr = (BYTE **)malloc(sizeof(BYTE *)*height); + if(!m_pLinePtr) return FALSE; + for(i=0,ptr=m_pLinePtr; i < height; i++,ptr++) + { + //*ptr = (int)(m_pBits)+(i*bytes); + //*ptr = (int)GetLinePtr(i); + *ptr = m_pBits + (height-i-1)*bytes; + } + m_nFlags = 0; + return TRUE; +} + +void CDIB::SetPalette(unsigned char *palette) +{ +int i,size; +RGBQUAD *rgb; + if(!palette) return; + size = GetPaletteSize(); + for(i=0,rgb = m_pRGB; i < size; i++,rgb++,palette+=3) + { + if(m_bUseGamma) + { + rgb->rgbRed = Gamma[palette[0]]; + rgb->rgbGreen = Gamma[palette[1]]; + rgb->rgbBlue = Gamma[palette[2]]; + } + else + { + rgb->rgbRed = palette[0]; + rgb->rgbGreen = palette[1]; + rgb->rgbBlue = palette[2]; + } + rgb->rgbReserved = (BYTE)0; + } +} + +void CDIB::SetPalette(RGBQUAD *pRGB) +{ +int size; + if(!pRGB) return; + size = GetPaletteSize(); + memcpy(m_pRGB,pRGB,size*sizeof(RGBQUAD)); +} + + +int CDIB::GetPaletteSize() +{ + return GetPaletteSize(m_pInfo->bmiHeader); +} + + +int CDIB::GetPaletteSize(BITMAPINFOHEADER& bmInfo) +{ + switch(bmInfo.biBitCount) + { + case 1: + return 2; + case 4: + return 16; + case 8: + return 256; + default: + return 0; + } +} + + +void CDIB::SetPixel(int x,int y,COLORREF color) +{ +unsigned char *ptr; + ASSERT(x >= 0 && y >=0); + ASSERT(x < width && y < height); + +// ptr = m_pBits + (y*bytes) + x * 3; + ptr = (unsigned char *)m_pLinePtr[y]; + ptr += x*3; + *ptr++ = (unsigned char)GetBValue(color); + *ptr++ = (unsigned char)GetGValue(color); + *ptr++ = (unsigned char)GetRValue(color); +} + +void CDIB::SetPixel8(int x,int y,unsigned char color) +{ +unsigned char *ptr,*aptr; + ASSERT(x >= 0 && y >=0); + ASSERT(x < width && y < height); + +// ptr = m_pBits + (y*bytes) + x ; +// ptr = (unsigned char *)m_pLinePtr[y] ; + ptr = GetLinePtr(y); + aptr = ptr; + ptr += x; + *ptr = color; +} + + +COLORREF CDIB::GetPixel(int x,int y) +{ +unsigned char *ptr; +COLORREF color; + ASSERT(x >= 0 && y >=0); + ASSERT(x < width && y < height); + +// ptr = m_pBits + (y*bytes) + x * 3; + ptr = GetLinePtr(y); + ptr += (x*3); + color = RGB(*(ptr+2),*(ptr+1),*ptr); + return color; +} + +CBitmap *CDIB::GetTempBitmap(CDC& dc) +{ +HBITMAP hBitmap; +CBitmap *temp; + ASSERT(m_pVoid != NULL); + hBitmap = CreateDIBitmap(dc.m_hDC, + (PBITMAPINFOHEADER)m_pInfo, + CBM_INIT, + (const void *)m_pBits, + m_pInfo, + DIB_RGB_COLORS); + + if(hBitmap == NULL) return NULL; + temp = CBitmap::FromHandle(hBitmap); + return temp; +} + +CBitmap *CDIB::GetBitmap(CDC& dc) +{ +HBITMAP hBitmap; +CBitmap *temp; + ASSERT(m_pVoid != NULL); + hBitmap = CreateDIBitmap(dc.m_hDC, + (PBITMAPINFOHEADER)m_pInfo, + CBM_INIT, + (const void *)m_pBits, + m_pInfo, + DIB_RGB_COLORS); + + if(hBitmap == NULL) return NULL; + temp = CBitmap::FromHandle(hBitmap); + if(temp) + { + BITMAP bmp; + LPVOID lpVoid; + temp->GetBitmap(&bmp); + lpVoid = malloc(bmp.bmWidthBytes*bmp.bmHeight); + if(!lpVoid) return NULL; + temp->GetBitmapBits(bmp.bmWidthBytes*bmp.bmHeight,lpVoid); + CBitmap *newBmp = new CBitmap; + newBmp->CreateBitmapIndirect(&bmp); + newBmp->SetBitmapBits(bmp.bmWidthBytes*bmp.bmHeight,lpVoid); + free(lpVoid); + return newBmp; + } + else return NULL; + +} + +void CDIB::CopyLine(int source,int dest) +{ +unsigned char *src,*dst; + ASSERT(source <= height && source >= 0); + ASSERT(dest <= height && dest >= 0); + if(source == dest) return; + src = GetLinePtr(source); + dst = GetLinePtr(dest); + memcpy(dst,src,bytes); +} + +void CDIB::InitDIB(COLORREF color) +{ +int i,j; +unsigned char *ptr; + + if(m_pInfo->bmiHeader.biBitCount == 24) + { + unsigned char col[3]; + col[0]=GetBValue(color); + col[1]=GetGValue(color); + col[2]=GetRValue(color); + for(i=0,ptr = m_pBits; i < height; i++) + { + ptr = m_pBits + i*bytes; + for(j=0; j < width ; j++,ptr+=3) + { + memcpy(ptr,col,3); + } + } + } + else + { + for(i=0,ptr = m_pBits; i < height; i++,ptr+=bytes) + { + memset(ptr,(BYTE)color,bytes); + } + } +} + + +void CDIB::BitBlt(HDC hDest,int nXDest,int nYDest,int nWidth,int nHeight,int xSrc,int ySrc) +{ + SetDIBitsToDevice(hDest,nXDest,nYDest,nWidth,nHeight,xSrc,Height()-ySrc-nHeight,0,Height(),m_pBits,m_pInfo,DIB_RGB_COLORS); +} + +void CDIB::StretchBlt(HDC hDest,int nXDest,int nYDest,int nDWidth,int nDHeight,int xSrc,int ySrc,int nSWidth,int nSHeight) +{ + int err; + err = StretchDIBits(hDest,nXDest,nYDest,nDWidth,nDHeight,xSrc,ySrc,nSWidth,nSHeight,m_pBits,(CONST BITMAPINFO * )&m_pInfo->bmiHeader,DIB_RGB_COLORS,SRCCOPY); +} + +void CDIB::ExpandBlt(int nXDest,int nYDest,int xRatio,int yRatio,CDIB& dibSrc,int xSrc,int ySrc,int nSWidth,int nSHeight) +{ + SetPalette(dibSrc.m_pRGB); + + nSWidth = xSrc+nSWidth > dibSrc.width ? dibSrc.width-xSrc : nSWidth; + nSHeight = ySrc+nSHeight > dibSrc.height? dibSrc.height-ySrc : nSHeight; + + Expand(nXDest,nYDest,xRatio,yRatio,dibSrc,xSrc,ySrc,nSWidth,nSHeight); +} + +void CDIB::Expand(int nXDest,int nYDest,int xRatio,int yRatio,CDIB& dibSrc,int xSrc,int ySrc,int nSWidth,int nSHeight) +{ +int xNum,yNum,xErr,yErr; +int nDWidth,nDHeight; + + nDWidth = nSWidth*xRatio; + nDHeight = nSHeight*yRatio; + + nDWidth = nXDest+nDWidth > width ? width-nXDest : nDWidth ; + nDHeight = nYDest+nDHeight > height ? height-nYDest : nDHeight; + + xNum = nDWidth/xRatio; + yNum = nDHeight/yRatio; + xErr = nDWidth%xRatio; + yErr = nDHeight%yRatio; + +unsigned char *buffer,*srcPtr,*destPtr,*ptr; +int i,j,k; + + buffer = (unsigned char *)malloc(nDWidth+20); + if(!buffer) return; + + for(i=0; i < yNum; i++,ySrc++) + { + srcPtr = dibSrc.GetLinePtr(ySrc) + xSrc; + ptr = buffer; + for(j=0; j < xNum; j++,ptr+=xRatio) + { + memset(ptr,*(srcPtr+j),xRatio); + k=*(srcPtr+j); + } + memset(ptr,(unsigned char)k,xErr); + for(j=0; j < yRatio ; j++,nYDest++) + { + destPtr = GetLinePtr(nYDest) + nXDest; + memcpy(destPtr,buffer,nDWidth); + } + } + for(j=0; j < yErr; j++,nYDest++) + { + destPtr = GetLinePtr(nYDest) + nXDest; + memcpy(destPtr,buffer,nDWidth); + } + free(buffer); +} + +void CDIB::StretchBlt(int nXDest,int nYDest,int nDWidth,int nDHeight,CDIB& dibSrc,int xSrc,int ySrc,int nSWidth,int nSHeight) +{ + SetPalette(dibSrc.m_pRGB); + nDWidth = nXDest+nDWidth > width ? width-nXDest : nDWidth ; + nDHeight = nYDest+nDHeight > height ? height-nYDest : nDHeight; + + nSWidth = xSrc+nSWidth > dibSrc.width ? dibSrc.width-xSrc : nSWidth; + nSHeight = ySrc+nSHeight > dibSrc.height? dibSrc.height-ySrc : nSHeight; + +int xDiv,yDiv; +int xMod,yMod; + + xDiv = nDWidth/nSWidth; + xMod = nDWidth%nSWidth; + + yDiv = nDHeight/nSHeight; + yMod = nDHeight%nSHeight; + + if(!xMod && !yMod && xDiv > 0 && yDiv > 0) + { + ExpandBlt(nXDest,nYDest,xDiv,yDiv,dibSrc,xSrc,ySrc,nSWidth,nSHeight); + return; + } + +unsigned char *tempPtr,*srcPix,*destPix,*q; + tempPtr = (unsigned char *)malloc(nDWidth+20); +int i,j,k,l,x,y,m; +int xErr,yErr; + for(i=yErr=m=0; i < nSHeight; i++) + { + srcPix = dibSrc.GetLinePtr(i+ySrc) + xSrc; + q = tempPtr; + for(j=l=xErr=0; j < nSWidth; j++,srcPix++) + { + k = xDiv; + xErr += xMod; + if(xErr >= nSWidth) + { + k++; + xErr%=nSWidth; + } + x=0; + while(l < nDWidth && x < k) + { + *q++ = *srcPix; + l++; + x++; + } + } + while(l < nDWidth) + { + *q++=*srcPix; + l++; + } + k= yDiv; + yErr += yMod; + if(yErr >= nSHeight) + { + k++; + yErr%=nSHeight; + } + y=0; + while(m < nDHeight && y < k) + { + destPix = GetLinePtr(m+nYDest) + nXDest; + memcpy(destPix,tempPtr,nDWidth); + m++; + y++; + } + } + while(m < nDHeight ) + { + destPix = GetLinePtr(m+nYDest) + nXDest; + memcpy(destPix,tempPtr,nDWidth); + m++; + } + free(tempPtr); +} + +void CDIB::BitBlt(int nXDest,int nYDest,int nWidth,int nHeight,CDIB& dibSrc,int nSrcX,int nSrcY,BYTE *colors) +{ + SetPalette(dibSrc.m_pRGB); + if(nXDest < 0) + { + nSrcX -= nXDest; + nWidth += nXDest; + nXDest=0; + } + if(nYDest < 0) + { + nSrcY -= nYDest; + nHeight += nYDest; + nYDest=0; + } + if(nSrcX < 0) + { + nXDest -= nSrcX; + nWidth += nSrcX; + nSrcX=0; + } + if(nSrcY < 0) + { + nYDest -= nSrcY; + nHeight += nSrcY; + nSrcY=0; + } + nWidth = nXDest+nWidth > width ? width-nXDest : nWidth ; + nHeight = nYDest+nHeight > height ? height-nYDest : nHeight; + + nWidth = nSrcX+nWidth > dibSrc.width ? dibSrc.width-nSrcX : nWidth; + nHeight = nSrcY+nHeight > dibSrc.height? dibSrc.height-nSrcY : nHeight; + + nWidth = __max(0,nWidth); + nHeight = __max(0,nHeight); +int i,k,l,j; +unsigned char *srcPtr,*destPtr; + if(!colors) + { + for(i=0,k=nSrcY,l=nYDest; i < nHeight; i++,k++,l++) + { + if(k < 0 || l < 0) + { + continue; + } + else + { + srcPtr = dibSrc.GetLinePtr(k); + destPtr = GetLinePtr(l); + memcpy(destPtr+nXDest,srcPtr+nSrcX,nWidth); + } + } + } + else + { + for(i=0,k=nSrcY,l=nYDest; i < nHeight; i++,k++,l++) + { + if(k < 0 || l < 0) + { + continue; + } + else + { + srcPtr = dibSrc.GetLinePtr(k)+nXDest; + destPtr = GetLinePtr(l)+nSrcX; + for(j=0; j < nWidth; j++,srcPtr++,destPtr++) + { + if(colors[*srcPtr]) *destPtr=*srcPtr; + } + } + } + } +} + +unsigned char *CDIB::GetLinePtr(int line) +{ +/*unsigned char *ptr; + ptr = m_pBits + (height-line-1)*bytes; + return ptr;*/ + return m_pLinePtr[line]; +} + +BOOL CDIB::CopyDIB(CDIB& dib) +{ + if(Create(dib.m_pInfo->bmiHeader)) + { + SetPalette(dib.m_pRGB); + memcpy(m_pBits,dib.m_pBits,height*bytes); + return TRUE; + } + return FALSE; +} + +void CDIB::ReplaceColor(unsigned char oldColor,unsigned char newColor) +{ +int i,j; +unsigned char *ptr; + for(i=0; i < height; i++) + { + ptr = GetLinePtr(i); + for(j=0; j < width; j++) + { + if(ptr[j] == oldColor) ptr[j] = newColor; + } + } +} + + +CDIB& CDIB::operator=(CDIB& dib) +{ + CopyDIB(dib); + return *this; +} + +HANDLE CDIB::GetDIBits(int nStartX,int nStartY,int nCx,int nCy) +{ + if(nStartX == -1) + { + nStartX = nStartY=0; + nCx = width; + nCy = height; + CDIB dib; + dib.Create(nCx,nCy,8); + dib.BitBlt(0,0,nCx,nCy,*this,0,0); + dib.SetPalette(m_pRGB); + return dib.DIBHandle(); + } + return DIBHandle(); +} + +DWORD CDIB::GetDIBSize() +{ + return sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*GetPaletteSize() + bytes*height; +} + +HANDLE CDIB::DIBHandle() +{ +int nSize; +HANDLE hMem; + nSize = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*GetPaletteSize() + bytes*height; + hMem = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,nSize); + if(hMem == NULL) return NULL; +UCHAR *lpVoid,*pBits; +LPBITMAPINFOHEADER pHead; +RGBQUAD *pRgb; + lpVoid = (UCHAR *)GlobalLock(hMem); + pHead = (LPBITMAPINFOHEADER )lpVoid; + memcpy(pHead,&m_pInfo->bmiHeader,sizeof(BITMAPINFOHEADER)); + pRgb = (RGBQUAD *)(lpVoid + sizeof(BITMAPINFOHEADER) ); + memcpy(pRgb,m_pRGB,sizeof(RGBQUAD)*GetPaletteSize()); + pBits = lpVoid + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*GetPaletteSize(); + memcpy(pBits,m_pBits,height*bytes); + GlobalUnlock(lpVoid); + return hMem; +} + +BOOL CDIB::CreateFromHandle(HANDLE hMem,int bits) +{ + DestroyDIB(); +UCHAR *lpVoid,*pBits; +LPBITMAPINFOHEADER pHead; +RGBQUAD *pRgb; + lpVoid = (UCHAR *)GlobalLock(hMem); + pHead = (LPBITMAPINFOHEADER )lpVoid; + width = pHead->biWidth; + height = pHead->biHeight; + m_nBits = pHead->biBitCount; + if(pHead->biCompression != BI_RGB) + { + GlobalUnlock(lpVoid); + return FALSE; + } + if(pHead->biBitCount >= 15) + { + if(pHead->biBitCount != 24) + { + GlobalUnlock(lpVoid); + return FALSE; + } + } + if(!Create(*pHead)) + { + GlobalUnlock(lpVoid); + return FALSE; + } + pRgb = (RGBQUAD *)(lpVoid + sizeof(BITMAPINFOHEADER) ); + memcpy(m_pRGB,pRgb,sizeof(RGBQUAD)*GetPaletteSize()); + pBits = lpVoid + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*GetPaletteSize(); + memcpy(m_pBits,pBits,height*bytes); + GlobalUnlock(lpVoid); + return TRUE; +} + +void CDIB::UseGamma(float fg,BOOL bUse) +{ + m_bUseGamma = bUse; + m_fOldGamma = m_fGamma; + m_fGamma = fg; + CreateGammaCurve(); +} + + +void CDIB::CreateGammaCurve() +{ +int i; + for(i=0;i<256;++i) + { + Gamma[i]=(int)(255 * powf((double)i/255,m_fGamma) + (double)0.5); + } +} + + + +void CDIB::GetPixel(UINT x,UINT y,int& pixel) +{ + ASSERT(x < (UINT)Width()); + ASSERT(y < (UINT)Height()); + if(x >= (UINT)Width()) return; + if(y >= (UINT)Height()) return; + pixel=(GetLinePtr(y))[x]; +} + +BOOL CDIB::Make8Bit(CDIB& dib) +{ +int nBits; + ASSERT(Width() == dib.Width()); + ASSERT(Height() == dib.Height()); + nBits = dib.GetBitCount(); + switch(nBits) + { + case 1: + return SwitchFromOne(dib); + break; + case 4: + return SwitchFromFour(dib); + break; + case 8: + return SwitchPalette(dib); + break; + case 24: + return SwitchFrom24(dib); + break; + default: + return FALSE; + } + return FALSE; +} + +/* +BOOL CDIB::SwitchFrom24(CDIB& dib) +{ +int i,j,w,h; +unsigned char *sPtr,*dPtr; + w = Width(); + h = Height(); + memset(CachePtr,0,sizeof(CachePtr)); + for(i=0; i < h; i++) + { + dPtr = GetLinePtr(i); + sPtr = dib.GetLinePtr(i); + for(j=0 ; j < w; j++,dPtr++,sPtr+=3) + { + *dPtr = ClosestColor((RGBQUAD *)sPtr); + } + } + return TRUE; +} +*/ + + +BOOL CDIB::SwitchFromOne(CDIB& dib) +{ +int i,j,w,h; +unsigned char *sPtr,*dPtr; +unsigned char cols[2]; + w = Width(); + h = Height(); + memset(CachePtr,0,sizeof(CachePtr)); + cols[0]=ClosestColor(dib.m_pRGB); + cols[1]=ClosestColor(dib.m_pRGB+1); + for(i=0; i < h; i++) + { + dPtr = GetLinePtr(i); + sPtr = dib.GetLinePtr(i); + for(j=0 ; j < w; j++,dPtr++) + { + if(!(sPtr[j>>3] & masktable[j&7])) *dPtr = cols[0]; + else *dPtr = cols[1]; + } + } + return TRUE; +} + +BOOL CDIB::SwitchFromFour(CDIB& dib) +{ +int i,n,j,w,h; +unsigned char *sPtr,*dPtr; +unsigned char cols[16]; + w = Width(); + h = Height(); + memset(CachePtr,0,sizeof(CachePtr)); + for(i=0; i < 16; i++) + { + cols[i]=ClosestColor(dib.m_pRGB+i); + } + for(i=0; i < h; i++) + { + dPtr = GetLinePtr(i); + sPtr = dib.GetLinePtr(i); + for(j=0 ; j < w; j++,dPtr++) + { + if(!(j&1)) n = (*sPtr & 0xf0)>>4; + else + { + n = *sPtr & 0x0f; + sPtr++; + } + *dPtr = cols[n]; + } + } + return TRUE; +} + +BOOL CDIB::SwitchPalette(CDIB& dib) +{ +int i,j,w,h; +unsigned char *sPtr,*dPtr; +unsigned char cols[256]; + w = Width(); + h = Height(); + memset(CachePtr,0,sizeof(CachePtr)); + for(i=0; i < 256; i++) + { + cols[i]=ClosestColor(dib.m_pRGB+i); + } + for(i=0; i < h; i++) + { + dPtr = GetLinePtr(i); + sPtr = dib.GetLinePtr(i); + for(j=0 ; j < w; j++,sPtr++,dPtr++) + { + *dPtr = cols[*sPtr]; + } + } + return TRUE; +} + + +int CDIB::ClosestColor(RGBQUAD *pRgb) +{ +unsigned int dist=BIG_DISTANCE,i,d,c; +RGBQUAD *pQuad=m_pRGB; +unsigned int pSize=GetPaletteSize(); + for(i=0; i < pSize;i++) + { + if(CachePtr[i]) + { + if(!memcmp((void *)&CacheQuad[i],(void *)pRgb,3)) + { + return i; + } + } + } + for(i=0; i < pSize; i++,pQuad++) + { + d = Distance(*pRgb,*pQuad); + if(!d) + { + CacheQuad[i]=*pRgb; + CachePtr[i]=1; + return i; + } + if(dist > d) + { + c = i; + dist = d; + } + } + CacheQuad[c]=*pRgb; + CachePtr[c]=1; + return c; +} + +unsigned int CDIB::Distance(RGBQUAD& rgb1,RGBQUAD& rgb2) +{ +unsigned int d; + d = 3*(unsigned)((rgb1.rgbRed)-(rgb2.rgbRed))*(unsigned)((rgb1.rgbRed)-(rgb2.rgbRed)); + d += 4*(unsigned)((rgb1.rgbGreen)-(rgb2.rgbGreen))*(unsigned)((rgb1.rgbGreen)-(rgb2.rgbGreen)) ; + d += 2*(unsigned)((rgb1.rgbBlue)-(rgb2.rgbBlue))*(unsigned)((rgb1.rgbBlue)-(rgb2.rgbBlue)); + return d; +} + +BOOL CDIB::OpenDIB(CString& csFileName) +{ +CFile file; + if(!file.Open(csFileName,CFile::modeRead | CFile::typeBinary)) + { + return FALSE; + } + file.Close(); + if(OpenBMP(csFileName)) return TRUE; + return FALSE; +} + + + +BOOL CDIB::SaveDIB(CString& csFileName,BitmapType type) +{ +CFile file; + if(!file.Open(csFileName,CFile::modeCreate | CFile::typeBinary)) + { + return FALSE; + } + file.Close(); + switch(type) + { + case BMP: + return SaveBMP(csFileName); + default: + return FALSE; + } + return FALSE; +} + +BOOL CDIB::SaveBMP(CString& csFileName) +{ +BITMAPFILEHEADER bFile; +CFile file; + if(!file.Open(csFileName,CFile::modeWrite | CFile::typeBinary)) + { + return FALSE; + } + ::ZeroMemory(&bFile,sizeof(bFile)); + memcpy((void *)&bFile.bfType,"BM",2); + bFile.bfSize = GetDIBSize() + sizeof(bFile); + bFile.bfOffBits = sizeof(BITMAPINFOHEADER) + GetPaletteSize()*sizeof(RGBQUAD) + sizeof(BITMAPFILEHEADER); + file.Write(&bFile,sizeof(bFile)); + file.Write(m_pVoid,GetDIBSize()); + file.Close(); + return TRUE; + +} + +BOOL CDIB::OpenBMP(CString& csFileName) +{ +BITMAPFILEHEADER bFile; +BITMAPINFOHEADER head; +CFile file; + if(!file.Open(csFileName,CFile::modeRead | CFile::typeBinary)) + { + return FALSE; + } + file.Read(&bFile,sizeof(bFile)); + if(memcmp((void *)&bFile.bfType,"BM",2)) + { + file.Close(); + return FALSE; + } + file.Read(&head,sizeof(head)); + if(!Create(head)) + { + file.Close(); + return FALSE; + } + file.Read(m_pRGB,sizeof(RGBQUAD)*GetPaletteSize()); + file.Seek(bFile.bfOffBits,CFile::begin); + file.Read(m_pBits,height*bytes); + file.Close(); + return TRUE; + +} + + +int CDIB::CountColors() +{ + ASSERT(GetBitCount()==8); +BYTE colors[256],*ptr; +int nNum=0,i,j,w,d; + w = Width(); + d = Height(); + memset(colors,0,256); + for(i=0; i < d; i++) + { + ptr = GetLinePtr(i); + for(j=0; j < w; j++,ptr++) + { + if(!colors[*ptr]) + { + colors[*ptr]=1; + nNum++; + } + } + } + return nNum; +} + +int CDIB::EnumColors(BYTE *array) +{ + ASSERT(GetBitCount()==8); +BYTE *ptr; +int nNum=0,i,j,w,d; + w = Width(); + d = Height(); + memset(array,0,256); + for(i=0; i < d; i++) + { + ptr = GetLinePtr(i); + for(j=0; j < w; j++,ptr++) + { + if(!array[*ptr]) + { + array[*ptr]=1; + nNum++; + } + } + } + return nNum; +} + +COLORREF CDIB::PaletteColor(int nIndex) +{ + ASSERT(nIndex < 256); +RGBQUAD *pRGB= m_pRGB+nIndex; + return RGB(pRGB->rgbRed,pRGB->rgbGreen,pRGB->rgbBlue); +} + +BOOL CDIB::SwitchFrom24(CDIB& dib) +{ +int i,j,w,h,c; +unsigned char *sPtr,*dPtr; +BYTE *index_ptr=NULL; +RGBQUAD rgb; + w = Width(); + h = Height(); + index_ptr = (BYTE *)malloc(0x7FFF+1); + if(!index_ptr) return FALSE; + memset(CachePtr,0,sizeof(CachePtr)); + for(i=0; i <= 0x7FFF; i++) + { + rgb.rgbRed = (((i & 0x7C00)>>10) << 3) | 0x07; + rgb.rgbGreen = (((i & 0x3e0)>>5) << 3) | 0x07; + rgb.rgbBlue = ((i & 0x1F)<<3) | 0x07; + index_ptr[i] = ClosestColor(&rgb); + } + for(i=0; i < h; i++) + { + dPtr = GetLinePtr(i); + sPtr = dib.GetLinePtr(i); + for(j=0 ; j < w; j++,dPtr++,sPtr+=3) + { + c = (*sPtr >> 3) | ((*(sPtr+1) >> 3) << 5) | ((*(sPtr+2) >> 3) << 10); + *dPtr = index_ptr[c]; + } + } + free(index_ptr); + return TRUE; +} diff --git a/src/tools/comafx/CDIB.h b/src/tools/comafx/CDIB.h new file mode 100644 index 0000000..a3fbafc --- /dev/null +++ b/src/tools/comafx/CDIB.h @@ -0,0 +1,121 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef __CDIB__ +#define __CDIB__ + +// Original ColorPicker/DIB source by Rajiv Ramachandran +// included with permission from the author + +class CDIB { +public: + enum BitmapType { + BMP, + GIF, + TIFF + }; + CDIB( HANDLE hDib = NULL,int nBits = 8 ); + virtual ~CDIB(); + + CDIB & operator=( CDIB& dib ); + BOOL IsValid() { return ( m_pVoid && Width() && Height() ); } + void UseGamma( float fg, BOOL bUse = TRUE ); + BOOL CreateFromHandle( HANDLE hDib, int nBits ); + BOOL Create( int width, int height, int bits = 24 ); + BOOL Create( BITMAPINFOHEADER& bmInfo ); + BOOL CopyDIB( CDIB& dib ); + BOOL OpenDIB( CString &fileName ); + BOOL SaveDIB( CString &fileName, BitmapType type ); + void ReplaceColor(unsigned char oldColor,unsigned char newColor); + HANDLE GetDIBits(int nStartX=-1,int nStartY=-1,int nCx=-1,int nCy=-1); + CBitmap * GetBitmap(CDC& dc); + CBitmap * GetTempBitmap(CDC& dc); + DWORD GetDIBSize(); + int GetPaletteSize(BITMAPINFOHEADER& bmInfo); + int GetPaletteSize(); + int CountColors(); + int EnumColors(BYTE *colors); + void InitDIB(COLORREF color); + void CopyLine(int source,int dest); + void DestroyDIB(); + void SetPalette(unsigned char *palette); + void SetPalette(RGBQUAD *pRGB); + COLORREF PaletteColor(int index); + void SetPixel(int x,int y,COLORREF color); + void SetPixel8(int x,int y,unsigned char color); + COLORREF GetPixel(int x,int y); + void GetPixel(UINT x,UINT y,int& pixel); + void BitBlt(HDC hDest,int nXDest,int nYDest,int nWidth,int nHeight,int xSrc,int ySrc); + void BitBlt(int nXDest,int nYDest,int nWidth,int nHeight,CDIB& dibSrc,int nSrcX,int nSrcY,BYTE *colors=NULL); + void StretchBlt(HDC hDest,int nXDest,int nYDest,int nDWidth,int nDHeight,int xSrc,int ySrc,int nSWidth,int nSHeight); + void StretchBlt(int nXDest,int nYDest,int nDWidth,int nDHeight,CDIB& dibSrc,int xSrc,int ySrc,int nSWidth,int nSHeight); + void ExpandBlt(int nXDest,int nYDest,int xRatio,int yRatio,CDIB& dibSrc,int xSrc,int ySrc,int nSWidth,int nSHeight); + void SetFlags(int flag) { m_nFlags = flag; } + int Height() { return height ; } + int Width() { return width ; } + unsigned char *GetLinePtr(int line); + inline int GetBitCount() { return m_pInfo->bmiHeader.biBitCount; } + BOOL Make8Bit( CDIB &dib ); + BOOL SwitchFromOne( CDIB &dib ); + BOOL SwitchFromFour( CDIB &dib ); + BOOL SwitchFrom24( CDIB &dib ); + BOOL SwitchPalette( CDIB &dib ); + int ClosestColor(RGBQUAD *pRgb ); + LPBITMAPINFO GetBitmapInfo() { return m_pInfo; } + static unsigned int Distance( RGBQUAD& rgb1, RGBQUAD& rgb2 ); + +protected: + HANDLE DIBHandle(); + BOOL OpenBMP( CString &csFileName ); + BOOL OpenGIF( CString &csFileName ); + BOOL OpenTIFF( CString &csFileName ); + BOOL SaveBMP( CString &csFileName ); + BOOL SaveGIF( CString &csFileName ); + BOOL SaveTIFF( CString &csFileName ); + void CreateGammaCurve(); + void Expand( int nXDest, int nYDest, int xRatio, int yRatio, CDIB &dibSrc, int xSrc, int ySrc, int nSWidth, int nSHeight ); + + unsigned char * m_pBits; + PBITMAPINFO m_pInfo; + RGBQUAD * m_pRGB; + void * m_pVoid; + BYTE ** m_pLinePtr; + int height; + int bytes; + int width; + int m_nBits; + int m_nFlags; + BOOL m_bUseGamma; + float m_fGamma; + float m_fOldGamma; + unsigned char Gamma[256]; + RGBQUAD CacheQuad[256]; + char CachePtr[256]; +}; + +#endif /* !__CDIB__ */ diff --git a/src/tools/comafx/CPathTreeCtrl.cpp b/src/tools/comafx/CPathTreeCtrl.cpp new file mode 100644 index 0000000..0999329 --- /dev/null +++ b/src/tools/comafx/CPathTreeCtrl.cpp @@ -0,0 +1,305 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "CPathTreeCtrl.h" + + +/* +================ +CPathTreeCtrl::CPathTreeCtrl +================ +*/ +CPathTreeCtrl::CPathTreeCtrl() { +} + +/* +================ +CPathTreeCtrl::~CPathTreeCtrl +================ +*/ +CPathTreeCtrl::~CPathTreeCtrl() { +} + +/* +================ +CPathTreeCtrl::PreSubclassWindow +================ +*/ +void CPathTreeCtrl::PreSubclassWindow() { + CTreeCtrl::PreSubclassWindow(); + EnableToolTips( TRUE ); +} + +/* +================ +CPathTreeCtrl::FindItem + +Find the given path in the tree. +================ +*/ +HTREEITEM CPathTreeCtrl::FindItem( const idStr &pathName ) { + int lastSlash; + idStr path, tmpPath, itemName; + HTREEITEM item, parentItem; + + parentItem = NULL; + item = GetRootItem(); + + lastSlash = pathName.Last( '/' ); + + while( item && lastSlash > path.Length() ) { + itemName = GetItemText( item ); + tmpPath = path + itemName; + if ( pathName.Icmpn( tmpPath, tmpPath.Length() ) == 0 ) { + parentItem = item; + item = GetChildItem( item ); + path = tmpPath + "/"; + } else { + item = GetNextSiblingItem( item ); + } + } + + for ( item = GetChildItem( parentItem ); item; item = GetNextSiblingItem( item ) ) { + itemName = GetItemText( item ); + if ( pathName.Icmp( path + itemName ) == 0 ) { + return item; + } + } + + return NULL; +} + +/* +================ +CPathTreeCtrl::InsertPathIntoTree + +Inserts a new item going from the root down the tree only creating paths where necessary. +This is slow and should only be used to insert single items. +================ +*/ +HTREEITEM CPathTreeCtrl::InsertPathIntoTree( const idStr &pathName, const int id ) { + int lastSlash; + idStr path, tmpPath, itemName; + HTREEITEM item, parentItem; + + parentItem = NULL; + item = GetRootItem(); + + lastSlash = pathName.Last( '/' ); + + while( item && lastSlash > path.Length() ) { + itemName = GetItemText( item ); + tmpPath = path + itemName; + if ( pathName.Icmpn( tmpPath, tmpPath.Length() ) == 0 ) { + parentItem = item; + item = GetChildItem( item ); + path = tmpPath + "/"; + } else { + item = GetNextSiblingItem( item ); + } + } + + while( lastSlash > path.Length() ) { + pathName.Mid( path.Length(), pathName.Length(), tmpPath ); + tmpPath.Left( tmpPath.Find( '/' ), itemName ); + parentItem = InsertItem( itemName, parentItem ); + path += itemName + "/"; + } + + pathName.Mid( path.Length(), pathName.Length(), itemName ); + item = InsertItem( itemName, parentItem, TVI_SORT ); + SetItemData( item, id ); + + return item; +} + +/* +================ +CPathTreeCtrl::AddPathToTree + +Adds a new item to the tree. +Assumes new paths after the current stack path do not yet exist. +================ +*/ +HTREEITEM CPathTreeCtrl::AddPathToTree( const idStr &pathName, const int id, idPathTreeStack &stack ) { + int lastSlash; + idStr itemName, tmpPath; + HTREEITEM item; + + lastSlash = pathName.Last( '/' ); + + while( stack.Num() > 1 ) { + if ( pathName.Icmpn( stack.TopName(), stack.TopNameLength() ) == 0 ) { + break; + } + stack.Pop(); + } + + while( lastSlash > stack.TopNameLength() ) { + pathName.Mid( stack.TopNameLength(), pathName.Length(), tmpPath ); + tmpPath.Left( tmpPath.Find( '/' ), itemName ); + item = InsertItem( itemName, stack.TopItem() ); + stack.Push( item, itemName ); + } + + pathName.Mid( stack.TopNameLength(), pathName.Length(), itemName ); + item = InsertItem( itemName, stack.TopItem() ); + SetItemData( item, id ); + + return item; +} + +/* +================ +CPathTreeCtrl::SearchTree + +Search the three using the search string. +Adds the matched tree items to the result tree. +Returns the number of items added to the result tree. +================ +*/ +int CPathTreeCtrl::SearchTree( treeItemCompare_t compare, void *data, CPathTreeCtrl &result ) { + idPathTreeStack stack, searchStack; + HTREEITEM item, child; + idStr name; + int id, numItems; + + numItems = 0; + result.DeleteAllItems(); + stack.PushRoot( NULL ); + + item = GetRootItem(); + searchStack.PushRoot( item ); + id = 0; + + while( searchStack.Num() > 0 ) { + + for ( child = GetChildItem( item ); child; child = GetChildItem( child ) ) { + searchStack.Push( item, GetItemText( item ) ); + item = child; + } + + name = searchStack.TopName(); + name += GetItemText( item ); + id = GetItemData( item ); + + if ( compare( data, item, name ) ) { + result.AddPathToTree( name, id, stack ); + numItems++; + } + + for ( item = GetNextSiblingItem( item ); item == NULL; ) { + item = GetNextSiblingItem( searchStack.TopItem() ); + searchStack.Pop(); + if ( searchStack.Num() <= 0 ) { + return numItems; + } + } + } + + return numItems; +} + +BEGIN_MESSAGE_MAP(CPathTreeCtrl,CTreeCtrl) + //{{AFX_MSG_MAP(CPathTreeCtrl) + ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText) + ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText) + ON_WM_MOUSEMOVE() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +/* +================ +CPathTreeCtrl::OnToolHitTest +================ +*/ +int CPathTreeCtrl::OnToolHitTest( CPoint point, TOOLINFO * pTI ) const { + RECT rect; + + UINT nFlags; + HTREEITEM hitem = HitTest( point, &nFlags ); + if( nFlags & TVHT_ONITEM ) { + GetItemRect( hitem, &rect, TRUE ); + pTI->hwnd = m_hWnd; + pTI->uId = (UINT)hitem; + pTI->lpszText = LPSTR_TEXTCALLBACK; + pTI->rect = rect; + return pTI->uId; + } + return -1; +} + +/* +================ +CPathTreeCtrl::OnToolTipText +================ +*/ +BOOL CPathTreeCtrl::OnToolTipText( UINT id, NMHDR * pNMHDR, LRESULT * pResult ) { + // need to handle both ANSI and UNICODE versions of the message + TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR; + TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR; + + UINT nID = pNMHDR->idFrom; + + *pResult = 0; + + // Do not process the message from built in tooltip + if( nID == (UINT)m_hWnd && + (( pNMHDR->code == TTN_NEEDTEXTA && pTTTA->uFlags & TTF_IDISHWND ) || + ( pNMHDR->code == TTN_NEEDTEXTW && pTTTW->uFlags & TTF_IDISHWND ) ) ) { + return FALSE; + } + + CString toolTip = "?"; + + // Get the mouse position + const MSG* pMessage; + CPoint pt; + pMessage = GetCurrentMessage(); + ASSERT ( pMessage ); + pt = pMessage->pt; + ScreenToClient( &pt ); + + // get the tree item + UINT nFlags; + HTREEITEM hitem = HitTest( pt, &nFlags ); + + if( nFlags & TVHT_ONITEM ) { + // relay message to parent + pTTTA->hdr.hwndFrom = GetSafeHwnd(); + pTTTA->hdr.idFrom = (UINT) hitem; + if ( GetParent()->SendMessage( WM_NOTIFY, ( TTN_NEEDTEXT << 16 ) | GetDlgCtrlID(), (LPARAM)pTTTA ) == FALSE ) { + return FALSE; + } + } + + return TRUE; // message was handled +} diff --git a/src/tools/comafx/CPathTreeCtrl.h b/src/tools/comafx/CPathTreeCtrl.h new file mode 100644 index 0000000..15c5a70 --- /dev/null +++ b/src/tools/comafx/CPathTreeCtrl.h @@ -0,0 +1,93 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef __CPATHTREECTR_H__ +#define __CPATHTREECTR_H__ + +/* +=============================================================================== + + Tree Control for path names. + +=============================================================================== +*/ + +class idPathTreeStack { +public: + idPathTreeStack( void ) { size = 0; } + + void PushRoot( HTREEITEM root ); + void Push( HTREEITEM item, const char *name ); + void Pop( void ) { size--; } + HTREEITEM TopItem( void ) const { return stackItem[size-1]; } + const char * TopName( void ) const { return stackName[size-1]; } + int TopNameLength( void ) const { return stackName[size-1].Length(); } + int Num( void ) const { return size; } + +private: + int size; + HTREEITEM stackItem[128]; + idStr stackName[128]; +}; + +ID_INLINE void idPathTreeStack::PushRoot( HTREEITEM root ) { + assert( size == 0 ); + stackItem[size] = root; + stackName[size] = ""; + size++; +} + +ID_INLINE void idPathTreeStack::Push( HTREEITEM item, const char *name ) { + assert( size < 127 ); + stackItem[size] = item; + stackName[size] = stackName[size-1] + name + "/"; + size++; +} + +typedef bool (*treeItemCompare_t)( void *data, HTREEITEM item, const char *name ); + + +class CPathTreeCtrl : public CTreeCtrl { +public: + CPathTreeCtrl(); + ~CPathTreeCtrl(); + + HTREEITEM FindItem( const idStr &pathName ); + HTREEITEM InsertPathIntoTree( const idStr &pathName, const int id ); + HTREEITEM AddPathToTree( const idStr &pathName, const int id, idPathTreeStack &stack ); + int SearchTree( treeItemCompare_t compare, void *data, CPathTreeCtrl &result ); + +protected: + virtual void PreSubclassWindow(); + virtual int OnToolHitTest( CPoint point, TOOLINFO * pTI ) const; + afx_msg BOOL OnToolTipText( UINT id, NMHDR * pNMHDR, LRESULT * pResult ); + + DECLARE_MESSAGE_MAP() +}; + +#endif /* !__CPATHTREECTR_H__ */ diff --git a/src/tools/comafx/CSyntaxRichEditCtrl.cpp b/src/tools/comafx/CSyntaxRichEditCtrl.cpp new file mode 100644 index 0000000..1332492 --- /dev/null +++ b/src/tools/comafx/CSyntaxRichEditCtrl.cpp @@ -0,0 +1,1910 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "CSyntaxRichEditCtrl.h" + +#ifdef ID_DEBUG_MEMORY +#undef new +#undef DEBUG_NEW +#define DEBUG_NEW new +#endif + +// NOTE: known bug, if you directly jump to a not yet highligted page with the first line starting +// inside a multi-line comment then the multi-line comment is not picked up and highlighted + +const int AUTOCOMPLETE_WIDTH = 200; +const int AUTOCOMPLETE_HEIGHT = 180; +const int AUTOCOMPLETE_OFFSET = 16; + +const int FUNCPARMTOOLTIP_WIDTH = 16; +const int FUNCPARMTOOLTIP_HEIGHT = 20; +const int FUNCPARMTOOLTIP_OFFSET = 16; + +const COLORREF DEFAULT_BACK_COLOR = SRE_COLOR_WHITE; +const COLORREF INVALID_BACK_COLOR = SRE_COLOR_WHITE - 2; +const COLORREF MULTILINE_COMMENT_BACK_COLOR = SRE_COLOR_WHITE - 1; + +#define IDC_LISTBOX_AUTOCOMPLETE 700 +#define IDC_EDITBOX_FUNCPARMS 701 + +static keyWord_t defaultKeyWords[] = { + { NULL, SRE_COLOR_BLACK, "" } +}; + +BEGIN_MESSAGE_MAP(CSyntaxRichEditCtrl, CRichEditCtrl) + ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipNotify) + ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipNotify) + ON_WM_GETDLGCODE() + ON_WM_KEYDOWN() + ON_WM_CHAR() + ON_WM_LBUTTONDOWN() + ON_WM_MOUSEWHEEL() + ON_WM_MOUSEMOVE() + ON_WM_VSCROLL() + ON_WM_SIZE() + ON_NOTIFY_REFLECT(EN_PROTECTED, OnProtected) + ON_CONTROL_REFLECT(EN_CHANGE, OnChange) + ON_LBN_SELCANCEL(IDC_LISTBOX_AUTOCOMPLETE, OnAutoCompleteListBoxChange) + ON_LBN_SELCHANGE(IDC_LISTBOX_AUTOCOMPLETE, OnAutoCompleteListBoxChange) + ON_LBN_DBLCLK(IDC_LISTBOX_AUTOCOMPLETE, OnAutoCompleteListBoxDblClk) +END_MESSAGE_MAP() + + +/* +================ +CSyntaxRichEditCtrl::CSyntaxRichEditCtrl +================ +*/ +CSyntaxRichEditCtrl::CSyntaxRichEditCtrl( void ) { + m_TextDoc = NULL; + keyWords = defaultKeyWords; + keyWordColors = NULL; + keyWordLengths = NULL; + caseSensitive = false; + allowPathNames = true; + keyWordAutoCompletion = true; + updateRange.cpMin = 0; + updateRange.cpMax = 0; + updateSyntaxHighlighting = true; + stringColorIndex = 0; + stringColorLine = -1; + autoCompleteStart = -1; + funcParmToolTipStart = -1; + bracedSection[0] = -1; + bracedSection[1] = -1; + GetObjectMembers = NULL; + GetFunctionParms = NULL; + GetToolTip = NULL; + mousePoint.x = 0; + mousePoint.y = 0; + keyWordToolTip = NULL; + m_pchTip = NULL; + m_pwchTip = NULL; +} + +/* +================ +CSyntaxRichEditCtrl::~CSyntaxRichEditCtrl +================ +*/ +CSyntaxRichEditCtrl::~CSyntaxRichEditCtrl( void ) { + FreeKeyWordsFromFile(); + delete m_pchTip; + delete m_pwchTip; + m_DefaultFont->Release(); +} + +/* +================ +CSyntaxRichEditCtrl::InitFont +================ +*/ +void CSyntaxRichEditCtrl::InitFont( void ) { + LOGFONT lf; + CFont font; + PARAFORMAT pf; + int logx, tabSize; + + // set the font + memset( &lf, 0, sizeof( lf ) ); + lf.lfHeight = FONT_HEIGHT * 10; + lf.lfWidth = FONT_WIDTH * 10; + lf.lfCharSet = ANSI_CHARSET; + lf.lfPitchAndFamily = FIXED_PITCH | FF_MODERN; + strcpy( lf.lfFaceName, FONT_NAME ); + font.CreatePointFontIndirect( &lf ); + + SetFont( &font ); + + // get the tab size in twips + logx = ::GetDeviceCaps( GetDC()->GetSafeHdc(), LOGPIXELSX ); + tabSize = TAB_SIZE * FONT_WIDTH * 1440 / logx; + + // set the tabs + memset( &pf, 0, sizeof( PARAFORMAT ) ); + pf.cbSize = sizeof( PARAFORMAT ); + pf.dwMask = PFM_TABSTOPS; + for ( pf.cTabCount = 0; pf.cTabCount < MAX_TAB_STOPS; pf.cTabCount++ ) { + pf.rgxTabs[pf.cTabCount] = pf.cTabCount * tabSize; + } + + SetParaFormat( pf ); + + memset( &defaultCharFormat, 0, sizeof( defaultCharFormat ) ); + defaultCharFormat.dwMask = CFM_CHARSET | CFM_FACE | CFM_SIZE | CFM_BOLD | CFM_COLOR | CFM_PROTECTED | CFM_BACKCOLOR; + defaultCharFormat.yHeight = FONT_HEIGHT * 20; + defaultCharFormat.bCharSet = ANSI_CHARSET; + defaultCharFormat.bPitchAndFamily = FIXED_PITCH | FF_MODERN; + defaultCharFormat.crTextColor = SRE_COLOR_BLACK; + defaultCharFormat.crBackColor = DEFAULT_BACK_COLOR; + defaultCharFormat.dwEffects = CFE_PROTECTED; + strcpy( defaultCharFormat.szFaceName, FONT_NAME ); + defaultCharFormat.cbSize = sizeof( defaultCharFormat ); + + SetDefaultCharFormat( defaultCharFormat ); + + defaultColor = SRE_COLOR_BLACK; + singleLineCommentColor = SRE_COLOR_DARK_GREEN; + multiLineCommentColor = SRE_COLOR_DARK_GREEN; + stringColor[0] = stringColor[1] = SRE_COLOR_DARK_CYAN; + literalColor = SRE_COLOR_GREY; + braceHighlightColor = SRE_COLOR_RED; + + // get the default tom::ITextFont + tom::ITextRange *irange; + tom::ITextFont *ifont; + + m_TextDoc->Range( 0, 0, &irange ); + irange->get_Font( &ifont ); + + ifont->get_Duplicate( &m_DefaultFont ); + + ifont->Release(); + irange->Release(); +} + +/* +================ +CSyntaxRichEditCtrl::SetCharType +================ +*/ +void CSyntaxRichEditCtrl::SetCharType( int first, int last, int type ) { + for ( int i = first; i <= last; i++ ) { + charType[i] = type; + } +} + +/* +================ +CSyntaxRichEditCtrl::InitSyntaxHighlighting +================ +*/ +void CSyntaxRichEditCtrl::InitSyntaxHighlighting( void ) { + SetCharType( 0x00, 0xFF, CT_PUNCTUATION ); + SetCharType( '\0', ' ', CT_WHITESPACE ); + SetCharType( '/', '/', CT_COMMENT ); + SetCharType( '\"', '\"', CT_STRING ); + SetCharType( '\'', '\'', CT_LITERAL ); + SetCharType( 'a', 'z', CT_NAME ); + SetCharType( 'A', 'Z', CT_NAME ); + SetCharType( '_', '_', CT_NAME ); + SetCharType( '#', '#', CT_NAME ); + SetCharType( '0', '9', CT_NUMBER ); +} + +/* +================ +CSyntaxRichEditCtrl::Init +================ +*/ +void CSyntaxRichEditCtrl::Init( void ) { + + // get the Rich Edit ITextDocument to use the wonky TOM interface + IRichEditOle *ire = GetIRichEditOle(); + IUnknown *iu = (IUnknown *)ire; + if ( iu == NULL || iu->QueryInterface( tom::IID_ITextDocument, (void**) &m_TextDoc ) != S_OK ) { + m_TextDoc = NULL; + } + + InitFont(); + + InitSyntaxHighlighting(); + + SetEventMask( GetEventMask() | ENM_CHANGE | ENM_KEYEVENTS | ENM_MOUSEEVENTS | ENM_PROTECTED ); // ENM_SCROLLEVENTS + + EnableToolTips( TRUE ); + + // create auto complete list box + CRect rect( 0, 0, AUTOCOMPLETE_WIDTH, AUTOCOMPLETE_HEIGHT ); + autoCompleteListBox.Create( WS_DLGFRAME | WS_VISIBLE | WS_VSCROLL | LBS_SORT | LBS_NOTIFY, rect, this, IDC_LISTBOX_AUTOCOMPLETE ); + autoCompleteListBox.SetFont( GetParent()->GetFont() ); + autoCompleteListBox.ShowWindow( FALSE ); + + // create function parameter tool tip + funcParmToolTip.Create( WS_VISIBLE | WS_BORDER, rect, this, IDC_EDITBOX_FUNCPARMS ); + funcParmToolTip.SetFont( GetParent()->GetFont() ); + funcParmToolTip.ShowWindow( FALSE ); +} + +/* +================ +CSyntaxRichEditCtrl::FindKeyWord +================ +*/ +ID_INLINE int CSyntaxRichEditCtrl::FindKeyWord( const char *keyWord, int length ) const { + int i, hash; + + if ( caseSensitive ) { + hash = idStr::Hash( keyWord, length ); + } else { + hash = idStr::IHash( keyWord, length ); + } + for ( i = keyWordHash.First( hash ); i != -1; i = keyWordHash.Next( i ) ) { + if ( length != keyWordLengths[i] ) { + continue; + } + if ( caseSensitive ) { + if ( idStr::Cmpn( keyWords[i].keyWord, keyWord, length ) != 0 ) { + continue; + } + } else { + if ( idStr::Icmpn( keyWords[i].keyWord, keyWord, length ) != 0 ) { + continue; + } + } + return i; + } + return -1; +} + +/* +================ +CSyntaxRichEditCtrl::SetKeyWords +================ +*/ +void CSyntaxRichEditCtrl::SetKeyWords( const keyWord_t kws[] ) { + int i, numKeyWords, hash; + + keyWords = kws; + + for ( numKeyWords = 0; keyWords[numKeyWords].keyWord; numKeyWords++ ) { + } + + delete keyWordColors; + keyWordColors = new COLORREF[numKeyWords]; + + for ( i = 0; i < numKeyWords; i++ ) { + keyWordColors[i] = keyWords[i].color; + } + + delete keyWordLengths; + keyWordLengths = new int[numKeyWords]; + + for ( i = 0; i < numKeyWords; i++ ) { + keyWordLengths[i] = idStr::Length( keyWords[i].keyWord ); + } + + keyWordHash.Clear( 1024, 1024 ); + for ( i = 0; i < numKeyWords; i++ ) { + if ( caseSensitive ) { + hash = idStr::Hash( keyWords[i].keyWord, keyWordLengths[i] ); + } else { + hash = idStr::IHash( keyWords[i].keyWord, keyWordLengths[i] ); + } + keyWordHash.Add( hash, i ); + } +} + +/* +================ +CSyntaxRichEditCtrl::LoadKeyWordsFromFile +================ +*/ +bool CSyntaxRichEditCtrl::LoadKeyWordsFromFile( const char *fileName ) { + idParser src; + idToken token, name, description; + byte red, green, blue; + keyWord_t keyword; + + if ( !src.LoadFile( fileName ) ) { + return false; + } + + FreeKeyWordsFromFile(); + + while( src.ReadToken( &token ) ) { + if ( token.Icmp( "keywords" ) == 0 ) { + src.ExpectTokenString( "{" ); + while( src.ReadToken( &token ) ) { + if ( token == "}" ) { + break; + } + if ( token == "{" ) { + + // parse name + src.ExpectTokenType( TT_STRING, 0, &name ); + src.ExpectTokenString( "," ); + + // parse color + src.ExpectTokenString( "(" ); + src.ExpectTokenType( TT_NUMBER, TT_INTEGER, &token ); + red = token.GetIntValue(); + src.ExpectTokenString( "," ); + src.ExpectTokenType( TT_NUMBER, TT_INTEGER, &token ); + green = token.GetIntValue(); + src.ExpectTokenString( "," ); + src.ExpectTokenType( TT_NUMBER, TT_INTEGER, &token ); + blue = token.GetIntValue(); + src.ExpectTokenString( ")" ); + src.ExpectTokenString( "," ); + + // parse description + src.ExpectTokenType( TT_STRING, 0, &description ); + src.ExpectTokenString( "}" ); + + keyword.keyWord = Mem_CopyString( name ); + keyword.color = RGB( red, green, blue ); + keyword.description = Mem_CopyString( description ); + + keyWordsFromFile.Append( keyword ); + } + } + } else { + src.SkipBracedSection(); + } + } + + keyword.keyWord = NULL; + keyword.color = RGB( 255, 255, 255 ); + keyword.description = NULL; + keyWordsFromFile.Append( keyword ); + + SetKeyWords( keyWordsFromFile.Ptr() ); + + return true; +} + +/* +================ +CSyntaxRichEditCtrl::FreeKeyWordsFromFile +================ +*/ +void CSyntaxRichEditCtrl::FreeKeyWordsFromFile( void ) { + for ( int i = 0; i < keyWordsFromFile.Num(); i++ ) { + Mem_Free( const_cast( keyWordsFromFile[i].keyWord ) ); + Mem_Free( const_cast( keyWordsFromFile[i].description ) ); + } + keyWordsFromFile.Clear(); +} + +/* +================ +CSyntaxRichEditCtrl::SetDefaultColor +================ +*/ +void CSyntaxRichEditCtrl::SetDefaultColor( const COLORREF color ) { + defaultColor = color; +} + +/* +================ +CSyntaxRichEditCtrl::SetCommentColor +================ +*/ +void CSyntaxRichEditCtrl::SetCommentColor( const COLORREF color ) { + singleLineCommentColor = color; + multiLineCommentColor = color; +} + +/* +================ +CSyntaxRichEditCtrl::SetStringColor +================ +*/ +void CSyntaxRichEditCtrl::SetStringColor( const COLORREF color, const COLORREF altColor ) { + stringColor[0] = color; + if ( altColor == -1 ) { + stringColor[1] = color; + } else { + stringColor[1] = altColor; + } +} + +/* +================ +CSyntaxRichEditCtrl::SetLiteralColor +================ +*/ +void CSyntaxRichEditCtrl::SetLiteralColor( const COLORREF color ) { + literalColor = color; +} + +/* +================ +CSyntaxRichEditCtrl::SetObjectMemberCallback +================ +*/ +void CSyntaxRichEditCtrl::SetObjectMemberCallback( objectMemberCallback_t callback ) { + GetObjectMembers = callback; +} + +/* +================ +CSyntaxRichEditCtrl::SetFunctionParmCallback +================ +*/ +void CSyntaxRichEditCtrl::SetFunctionParmCallback( toolTipCallback_t callback ) { + GetFunctionParms = callback; +} + +/* +================ +CSyntaxRichEditCtrl::SetToolTipCallback +================ +*/ +void CSyntaxRichEditCtrl::SetToolTipCallback( toolTipCallback_t callback ) { + GetToolTip = callback; +} + +/* +================ +CSyntaxRichEditCtrl::SetCaseSensitive +================ +*/ +void CSyntaxRichEditCtrl::SetCaseSensitive( bool caseSensitive ) { + this->caseSensitive = caseSensitive; +} + +/* +================ +CSyntaxRichEditCtrl::AllowPathNames +================ +*/ +void CSyntaxRichEditCtrl::AllowPathNames( bool allow ) { + allowPathNames = allow; +} + +/* +================ +CSyntaxRichEditCtrl::EnableKeyWordAutoCompletion +================ +*/ +void CSyntaxRichEditCtrl::EnableKeyWordAutoCompletion( bool enable ) { + keyWordAutoCompletion = enable; +} + +/* +================ +CSyntaxRichEditCtrl::GetVisibleRange +================ +*/ +CHARRANGE CSyntaxRichEditCtrl::GetVisibleRange( void ) const { + RECT rectArea; + int firstLine, lastLine; + CHARRANGE range; + + firstLine = GetFirstVisibleLine(); + GetClientRect( &rectArea ); + lastLine = firstLine + ( rectArea.bottom / ( defaultCharFormat.yHeight / 20 ) ); + + if ( lastLine >= GetLineCount() ) { + lastLine = GetLineCount() - 1; + } + range.cpMin = LineIndex( firstLine ); + if ( range.cpMin < 0 ) { + range.cpMin = 0; + } + range.cpMax = LineIndex( lastLine ); + if ( range.cpMax == -1 ) { + range.cpMax = range.cpMin + LineLength( range.cpMin ); + } else { + range.cpMax += LineLength( range.cpMax ); + } + if ( range.cpMax >= GetTextLength() ) { + range.cpMax = GetTextLength() - 1; + } + return range; +} + +/* +================ +CSyntaxRichEditCtrl::SetDefaultFont +================ +*/ +void CSyntaxRichEditCtrl::SetDefaultFont( int startCharIndex, int endCharIndex ) { + tom::ITextRange *range; + + updateSyntaxHighlighting = false; + + m_TextDoc->Range( startCharIndex, endCharIndex, &range ); + + m_TextDoc->Undo( tom::tomSuspend, NULL ); + range->put_Font( m_DefaultFont ); + m_TextDoc->Undo( tom::tomResume, NULL ); + + range->Release(); + + updateSyntaxHighlighting = true; +} + +/* +================ +CSyntaxRichEditCtrl::SetColor +================ +*/ +void CSyntaxRichEditCtrl::SetColor( int startCharIndex, int endCharIndex, COLORREF foreColor, COLORREF backColor, bool bold ) { + tom::ITextRange *range; + tom::ITextFont *font; + long prop; + + updateSyntaxHighlighting = false; + + m_TextDoc->Range( startCharIndex, endCharIndex, &range ); + range->get_Font( &font ); + + m_TextDoc->Undo( tom::tomSuspend, &prop ); + font->put_ForeColor( foreColor ); + m_TextDoc->Undo( tom::tomResume, &prop ); + + m_TextDoc->Undo( tom::tomSuspend, &prop ); + font->put_BackColor( backColor ); + m_TextDoc->Undo( tom::tomResume, &prop ); + + m_TextDoc->Undo( tom::tomSuspend, &prop ); + font->put_Bold( bold ? tom::tomTrue : tom::tomFalse ); + m_TextDoc->Undo( tom::tomResume, &prop ); + + font->Release(); + range->Release(); + + updateSyntaxHighlighting = true; +} + +/* +================ +CSyntaxRichEditCtrl::GetForeColor +================ +*/ +COLORREF CSyntaxRichEditCtrl::GetForeColor( int charIndex ) const { + tom::ITextRange *range; + tom::ITextFont *font; + long foreColor; + + m_TextDoc->Range( charIndex, charIndex, &range ); + range->get_Font( &font ); + + font->get_BackColor( &foreColor ); + + font->Release(); + range->Release(); + + return foreColor; +} + +/* +================ +CSyntaxRichEditCtrl::GetBackColor +================ +*/ +COLORREF CSyntaxRichEditCtrl::GetBackColor( int charIndex ) const { + tom::ITextRange *range; + tom::ITextFont *font; + long backColor; + + m_TextDoc->Range( charIndex, charIndex, &range ); + range->get_Font( &font ); + + font->get_BackColor( &backColor ); + + font->Release(); + range->Release(); + + return backColor; +} + +/* +================ +CSyntaxRichEditCtrl::HighlightSyntax + + Update the syntax highlighting for the given character range. +================ +*/ +void CSyntaxRichEditCtrl::HighlightSyntax( int startCharIndex, int endCharIndex ) { + int c, t, line, charIndex, textLength, syntaxStart, keyWordLength, keyWordIndex; + const char *keyWord; + CHARRANGE visRange; + CString text; + + // get text length + GetTextRange( 0, GetTextLength(), text ); + textLength = text.GetLength(); + + // make sure the indexes are within bounds + if ( startCharIndex < 0 ) { + startCharIndex = 0; + } + if ( endCharIndex < 0 ) { + endCharIndex = textLength - 1; + } else if ( endCharIndex >= textLength ) { + endCharIndex = textLength - 1; + } + + // move the start index to the beginning of the line + for ( ; startCharIndex > 0; startCharIndex-- ) { + if ( idStr::CharIsNewLine( text[startCharIndex-1] ) ) { + break; + } + } + + // move the end index to the end of the line + for ( ; endCharIndex < textLength - 1; endCharIndex++ ) { + if ( idStr::CharIsNewLine( text[endCharIndex+1] ) ) { + break; + } + } + + // get the visible char range + visRange = GetVisibleRange(); + + // never update beyond the visible range + if ( startCharIndex < visRange.cpMin ) { + SetColor( startCharIndex, visRange.cpMin - 1, SRE_COLOR_BLACK, INVALID_BACK_COLOR, false ); + startCharIndex = visRange.cpMin; + } + if ( visRange.cpMax < endCharIndex ) { + SetColor( visRange.cpMax, endCharIndex, SRE_COLOR_BLACK, INVALID_BACK_COLOR, false ); + endCharIndex = visRange.cpMax; + if ( endCharIndex >= textLength ) { + endCharIndex = textLength - 1; + } + } + + // test if the start index is inside a multi-line comment + if ( startCharIndex > 0 ) { + // multi-line comments have a slightly different background color + if ( GetBackColor( startCharIndex-1 ) == MULTILINE_COMMENT_BACK_COLOR ) { + for( ; startCharIndex > 0; startCharIndex-- ) { + if ( text[startCharIndex] == '/' && text[startCharIndex+1] == '*' ) { + break; + } + } + } + } + + // test if the end index is inside a multi-line comment + if ( endCharIndex < textLength - 1 ) { + // multi-line comments have a slightly different background color + if ( GetBackColor( endCharIndex+1 ) == MULTILINE_COMMENT_BACK_COLOR ) { + for( endCharIndex++; endCharIndex < textLength - 1; endCharIndex++ ) { + if ( text[endCharIndex-1] == '*' && text[endCharIndex] == '/' ) { + break; + } + } + } + } + + line = 0; + stringColorLine = -1; + stringColorIndex = 0; + + // set the default color + SetDefaultFont( startCharIndex, endCharIndex + 1 ); + + // syntax based colors + for( charIndex = startCharIndex; charIndex <= endCharIndex; charIndex++ ) { + + t = charType[text[charIndex]]; + switch( t ) { + case CT_WHITESPACE: { + if ( idStr::CharIsNewLine( text[charIndex] ) ) { + line++; + } + break; + } + case CT_COMMENT: { + c = text[charIndex+1]; + if ( c == '/' ) { + // single line comment + syntaxStart = charIndex; + for ( charIndex += 2; charIndex < textLength; charIndex++ ) { + if ( idStr::CharIsNewLine( text[charIndex] ) ) { + break; + } + } + SetColor( syntaxStart, charIndex + 1, singleLineCommentColor, DEFAULT_BACK_COLOR, false ); + } else if ( c == '*' ) { + // multi-line comment + syntaxStart = charIndex; + for ( charIndex += 2; charIndex < textLength; charIndex++ ) { + if ( text[charIndex] == '*' && text[charIndex+1] == '/' ) { + break; + } + } + charIndex++; + SetColor( syntaxStart, charIndex + 1, multiLineCommentColor, MULTILINE_COMMENT_BACK_COLOR, false ); + } + break; + } + case CT_STRING: { + if ( line != stringColorLine ) { + stringColorLine = line; + stringColorIndex = 0; + } + syntaxStart = charIndex; + for ( charIndex++; charIndex < textLength; charIndex++ ) { + c = text[charIndex]; + if ( charType[c] == CT_STRING && text[charIndex-1] != '\\' ) { + break; + } + if ( idStr::CharIsNewLine( c ) ) { + line++; + break; + } + } + SetColor( syntaxStart, charIndex + 1, stringColor[stringColorIndex], DEFAULT_BACK_COLOR, false ); + stringColorIndex ^= 1; + break; + } + case CT_LITERAL: { + syntaxStart = charIndex; + for ( charIndex++; charIndex < textLength; charIndex++ ) { + c = text[charIndex]; + if ( charType[c] == CT_LITERAL && text[charIndex-1] != '\\' ) { + break; + } + if ( idStr::CharIsNewLine( c ) ) { + line++; + break; + } + } + SetColor( syntaxStart, charIndex + 1, literalColor, DEFAULT_BACK_COLOR, false ); + break; + } + case CT_NUMBER: { + break; + } + case CT_NAME: { + syntaxStart = charIndex; + keyWord = ((const char *)text) + charIndex; + for ( charIndex++; charIndex < textLength; charIndex++ ) { + c = text[charIndex]; + t = charType[c]; + if ( t != CT_NAME && t != CT_NUMBER ) { + // allow path names + if ( !allowPathNames || ( c != '/' && c != '\\' && c != '.' ) ) { + break; + } + } + } + keyWordLength = charIndex - syntaxStart; + keyWordIndex = FindKeyWord( keyWord, keyWordLength ); + if ( keyWordIndex != -1 ) { + SetColor( syntaxStart, syntaxStart + keyWordLength, keyWordColors[keyWordIndex], DEFAULT_BACK_COLOR, false ); + } + break; + } + case CT_PUNCTUATION: { + break; + } + } + } + + // show braced section + BracedSectionShow(); +} + +/* +================ +CSyntaxRichEditCtrl::UpdateVisibleRange + + Updates the visible character range if it is not yet properly highlighted. +================ +*/ +void CSyntaxRichEditCtrl::UpdateVisibleRange( void ) { + CHARRANGE visRange; + tom::ITextRange *range; + tom::ITextFont *font; + long backColor; + bool update = false; + + if ( !updateSyntaxHighlighting ) { + return; + } + + visRange = GetVisibleRange(); + + m_TextDoc->Range( visRange.cpMin, visRange.cpMax, &range ); + range->get_End( &visRange.cpMax ); + + range->get_Font( &font ); + + range->SetRange( visRange.cpMin, visRange.cpMin ); + while( 1 ) { + range->get_Start( &visRange.cpMin ); + if ( visRange.cpMin >= visRange.cpMax ) { + break; + } + font->get_BackColor( &backColor ); + if ( backColor == INVALID_BACK_COLOR ) { + update = true; + break; + } + if ( range->Move( tom::tomCharFormat, 1, NULL ) != S_OK ) { + break; + } + } + + font->Release(); + range->Release(); + + if ( update ) { + HighlightSyntax( visRange.cpMin, visRange.cpMax - 1 ); + } +} + +/* +================ +CSyntaxRichEditCtrl::GetCursorPos +================ +*/ +void CSyntaxRichEditCtrl::GetCursorPos( int &line, int &column, int &character ) const { + long start, end; + char buffer[MAX_STRING_CHARS]; + + GetSel( start, end ); + line = LineFromChar( start ); + start -= LineIndex( line ); + GetLine( line, buffer, sizeof( buffer ) ); + for ( column = 1, character = 0; character < start; character++ ) { + if ( idStr::CharIsTab( buffer[character] ) ) { + column += TAB_SIZE; + column -= column % TAB_SIZE; + } else { + column++; + } + } + character++; +} + +/* +================ +CSyntaxRichEditCtrl::GetText +================ +*/ +void CSyntaxRichEditCtrl::GetText( idStr &text ) const { + GetText( text, 0, GetTextLength() ); +} + +/* +================ +CSyntaxRichEditCtrl::GetText +================ +*/ +void CSyntaxRichEditCtrl::GetText( idStr &text, int startCharIndex, int endCharIndex ) const { + tom::ITextRange *range; + BSTR bstr; + m_TextDoc->Range( startCharIndex, endCharIndex, &range ); + range->get_Text( &bstr ); + const int convertedLength = WideCharToMultiByte( CP_ACP, 0, bstr, -1, NULL, 0, NULL, NULL ); + if ( convertedLength > 0 ) { + char *converted = new char[convertedLength]; + WideCharToMultiByte( CP_ACP, 0, bstr, -1, converted, convertedLength, NULL, NULL ); + text = converted; + delete[] converted; + } else { + text.Clear(); + } + SysFreeString( bstr ); + range->Release(); + text.StripTrailingOnce( "\r" ); // remove last carriage return which is always added to a tom::ITextRange +} + +/* +================ +CSyntaxRichEditCtrl::SetText +================ +*/ +void CSyntaxRichEditCtrl::SetText( const char *text ) { + SetSel( 0, -1 ); + ReplaceSel( text, FALSE ); + SetSel( 0, 0 ); +} + +/* +================ +CSyntaxRichEditCtrl::FindNext +================ +*/ +bool CSyntaxRichEditCtrl::FindNext( const char *find, bool matchCase, bool matchWholeWords, bool searchForward ) { + long selStart, selEnd, flags, search, length, start; + tom::ITextRange *range; + + if ( find[0] == '\0' ) { + return false; + } + + GetSel( selStart, selEnd ); + + flags = 0; + flags |= matchCase ? tom::tomMatchCase : 0; + flags |= matchWholeWords ? tom::tomMatchWord : 0; + + if ( searchForward ) { + m_TextDoc->Range( selEnd, GetTextLength(), &range ); + search = GetTextLength() - selEnd; + } else { + m_TextDoc->Range( 0, selStart, &range ); + search = -selStart; + } + + if ( range->FindShit( A2BSTR(find), search, flags, &length ) == S_OK ) { + + m_TextDoc->Freeze( NULL ); + + range->get_Start( &start ); + range->Release(); + + SetSel( start, start + length ); + + int line = Max( (int) LineFromChar( start ) - 5, 0 ); + LineScroll( line - GetFirstVisibleLine(), 0 ); + + UpdateVisibleRange(); + + m_TextDoc->Unfreeze( NULL ); + return true; + } else { + range->Release(); + return false; + } +} + +/* +================ +CSyntaxRichEditCtrl::ReplaceAll +================ +*/ +int CSyntaxRichEditCtrl::ReplaceAll( const char *find, const char *replace, bool matchCase, bool matchWholeWords ) { + long selStart, selEnd, flags, search, length, start; + int numReplaced; + tom::ITextRange *range; + CComBSTR bstr( find ); + + if ( find[0] == '\0' ) { + return 0; + } + + m_TextDoc->Freeze( NULL ); + + GetSel( selStart, selEnd ); + + flags = 0; + flags |= matchCase ? tom::tomMatchCase : 0; + flags |= matchWholeWords ? tom::tomMatchWord : 0; + + m_TextDoc->Range( 0, GetTextLength(), &range ); + search = GetTextLength(); + + numReplaced = 0; + while( range->FindShit( bstr, search, flags, &length ) == S_OK ) { + range->get_Start( &start ); + ReplaceText( start, start + length, replace ); + numReplaced++; + } + + range->Release(); + + m_TextDoc->Unfreeze( NULL ); + + return numReplaced; +} + +/* +================ +CSyntaxRichEditCtrl::ReplaceText +================ +*/ +void CSyntaxRichEditCtrl::ReplaceText( int startCharIndex, int endCharIndex, const char *replace ) { + tom::ITextRange *range; + CComBSTR bstr( replace ); + + m_TextDoc->Range( startCharIndex, endCharIndex, &range ); + range->put_Text( bstr ); + range->Release(); +} + +/* +================ +CSyntaxRichEditCtrl::AutoCompleteInsertText +================ +*/ +void CSyntaxRichEditCtrl::AutoCompleteInsertText( void ) { + long selStart, selEnd; + int index; + + index = autoCompleteListBox.GetCurSel(); + if ( index >= 0 ) { + CString text; + autoCompleteListBox.GetText( index, text ); + GetSel( selStart, selEnd ); + selStart = autoCompleteStart; + SetSel( selStart, selEnd ); + ReplaceSel( text, TRUE ); + } +} + +/* +================ +CSyntaxRichEditCtrl::AutoCompleteUpdate +================ +*/ +void CSyntaxRichEditCtrl::AutoCompleteUpdate( void ) { + long selStart, selEnd; + int index; + idStr text; + + GetSel( selStart, selEnd ); + GetText( text, autoCompleteStart, selStart ); + index = autoCompleteListBox.FindString( -1, text ); + if ( index >= 0 && index < autoCompleteListBox.GetCount() ) { + autoCompleteListBox.SetCurSel( index ); + } +} + +/* +================ +CSyntaxRichEditCtrl::AutoCompleteShow +================ +*/ +void CSyntaxRichEditCtrl::AutoCompleteShow( int charIndex ) { + CPoint point; + CRect rect; + + autoCompleteStart = charIndex; + point = PosFromChar( charIndex ); + GetClientRect( rect ); + if ( point.y < rect.bottom - AUTOCOMPLETE_OFFSET - AUTOCOMPLETE_HEIGHT ) { + rect.top = point.y + AUTOCOMPLETE_OFFSET; + rect.bottom = point.y + AUTOCOMPLETE_OFFSET + AUTOCOMPLETE_HEIGHT; + } else { + rect.top = point.y - AUTOCOMPLETE_HEIGHT; + rect.bottom = point.y; + } + rect.left = point.x; + rect.right = point.x + AUTOCOMPLETE_WIDTH; + autoCompleteListBox.MoveWindow( &rect ); + autoCompleteListBox.ShowWindow( TRUE ); + AutoCompleteUpdate(); +} + +/* +================ +CSyntaxRichEditCtrl::AutoCompleteHide +================ +*/ +void CSyntaxRichEditCtrl::AutoCompleteHide( void ) { + autoCompleteStart = -1; + autoCompleteListBox.ShowWindow( FALSE ); +} + +/* +================ +CSyntaxRichEditCtrl::ToolTipShow +================ +*/ +void CSyntaxRichEditCtrl::ToolTipShow( int charIndex, const char *string ) { + CPoint point, p1, p2; + CRect rect; + + funcParmToolTipStart = charIndex; + funcParmToolTip.SetWindowText( string ); + p1 = funcParmToolTip.PosFromChar( 0 ); + p2 = funcParmToolTip.PosFromChar( strlen( string ) - 1 ); + point = PosFromChar( charIndex ); + GetClientRect( rect ); + if ( point.y < rect.bottom - FUNCPARMTOOLTIP_OFFSET - FUNCPARMTOOLTIP_HEIGHT ) { + rect.top = point.y + FUNCPARMTOOLTIP_OFFSET; + rect.bottom = point.y + FUNCPARMTOOLTIP_OFFSET + FUNCPARMTOOLTIP_HEIGHT; + } else { + rect.top = point.y - FUNCPARMTOOLTIP_HEIGHT; + rect.bottom = point.y; + } + rect.left = point.x; + rect.right = point.x + FUNCPARMTOOLTIP_WIDTH + p2.x - p1.x; + funcParmToolTip.MoveWindow( &rect ); + funcParmToolTip.ShowWindow( TRUE ); +} + +/* +================ +CSyntaxRichEditCtrl::ToolTipHide +================ +*/ +void CSyntaxRichEditCtrl::ToolTipHide( void ) { + funcParmToolTipStart = -1; + funcParmToolTip.ShowWindow( FALSE ); +} + +/* +================ +CSyntaxRichEditCtrl::BracedSectionStart +================ +*/ +bool CSyntaxRichEditCtrl::BracedSectionStart( char braceStartChar, char braceEndChar ) { + long selStart, selEnd; + int brace, i; + idStr text; + + GetSel( selStart, selEnd ); + GetText( text, 0, GetTextLength() ); + + for ( brace = 1, i = selStart; i < text.Length(); i++ ) { + if ( text[i] == braceStartChar ) { + brace++; + } else if ( text[i] == braceEndChar ) { + brace--; + if ( brace == 0 ) { + break; + } + } + } + if ( brace == 0 ) { + bracedSection[0] = selStart - 1; + bracedSection[1] = i; + BracedSectionShow(); + } + + return ( brace == 0 ); +} + +/* +================ +CSyntaxRichEditCtrl::BracedSectionEnd +================ +*/ +bool CSyntaxRichEditCtrl::BracedSectionEnd( char braceStartChar, char braceEndChar ) { + long selStart, selEnd; + int brace, i; + idStr text; + + GetSel( selStart, selEnd ); + GetText( text, 0, GetTextLength() ); + + for ( brace = 1, i = Min( selStart-2, (long)text.Length()-1 ); i >= 0; i-- ) { + if ( text[i] == braceStartChar ) { + brace--; + if ( brace == 0 ) { + break; + } + } else if ( text[i] == braceEndChar ) { + brace++; + } + } + + if ( brace == 0 ) { + bracedSection[0] = i; + bracedSection[1] = selStart - 1; + BracedSectionAdjustEndTabs(); + BracedSectionShow(); + } + + return ( brace == 0 ); +} + +/* +================ +CSyntaxRichEditCtrl::BracedSectionAdjustEndTabs +================ +*/ +void CSyntaxRichEditCtrl::BracedSectionAdjustEndTabs( void ) { + int line, lineIndex, length, column, numTabs, i; + char buffer[1024]; + idStr text; + + line = LineFromChar( bracedSection[0] ); + length = GetLine( line, buffer, sizeof( buffer ) ); + for ( numTabs = 0; numTabs < length; numTabs++ ) { + if ( !idStr::CharIsTab( buffer[numTabs] ) ) { + break; + } + text.Append( '\t' ); + } + + line = LineFromChar( bracedSection[1] ); + lineIndex = LineIndex( line ); + length = GetLine( line, buffer, sizeof( buffer ) ); + column = bracedSection[1] - lineIndex; + for ( i = 0; i < column; i++ ) { + if ( charType[buffer[i]] != CT_WHITESPACE ) { + return; + } + } + + ReplaceText( lineIndex, lineIndex + column, text ); + + bracedSection[1] += numTabs - column; + SetSel( bracedSection[1]+1, bracedSection[1]+1 ); +} + +/* +================ +CSyntaxRichEditCtrl::BracedSectionShow +================ +*/ +void CSyntaxRichEditCtrl::BracedSectionShow( void ) { + for ( int i = 0; i < 2; i++ ) { + if ( bracedSection[i] >= 0 ) { + SetColor( bracedSection[i], bracedSection[i] + 1, braceHighlightColor, DEFAULT_BACK_COLOR, true ); + } + } +} + +/* +================ +CSyntaxRichEditCtrl::BracedSectionHide +================ +*/ +void CSyntaxRichEditCtrl::BracedSectionHide( void ) { + for ( int i = 0; i < 2; i++ ) { + if ( bracedSection[i] >= 0 ) { + SetColor( bracedSection[i], bracedSection[i] + 1, defaultColor, DEFAULT_BACK_COLOR, false ); + bracedSection[i] = -1; + } + } +} + +/* +================ +CSyntaxRichEditCtrl::GetNameBeforeCurrentSelection +================ +*/ +bool CSyntaxRichEditCtrl::GetNameBeforeCurrentSelection( CString &name, int &charIndex ) const { + long selStart, selEnd; + int line, column, length; + char buffer[1024]; + + GetSel( selStart, selEnd ); + charIndex = selStart; + line = LineFromChar( selStart ); + length = GetLine( line, buffer, sizeof( buffer ) ); + column = selStart - LineIndex( line ) - 1; + do { + buffer[column--] = '\0'; + } while( charType[buffer[column]] == CT_WHITESPACE ); + for ( length = 0; length < column; length++ ) { + if ( charType[buffer[column-length-1]] != CT_NAME ) { + break; + } + } + if ( length > 0 ) { + name = buffer + column - length; + return true; + } + return false; +} + +/* +================ +CSyntaxRichEditCtrl::GetNameForMousePosition +================ +*/ +bool CSyntaxRichEditCtrl::GetNameForMousePosition( idStr &name ) const { + int charIndex, startCharIndex, endCharIndex, type; + idStr text; + + charIndex = CharFromPos( mousePoint ); + + for ( startCharIndex = charIndex; startCharIndex > 0; startCharIndex-- ) { + GetText( text, startCharIndex - 1, startCharIndex ); + type = charType[text[0]]; + if ( type != CT_NAME && type != CT_NUMBER ) { + break; + } + } + + for ( endCharIndex = charIndex; endCharIndex < GetTextLength(); endCharIndex++ ) { + GetText( text, endCharIndex, endCharIndex + 1 ); + type = charType[text[0]]; + if ( type != CT_NAME && type != CT_NUMBER ) { + break; + } + } + + GetText( name, startCharIndex, endCharIndex ); + + return ( endCharIndex > startCharIndex ); +} + +/* +================ +CSyntaxRichEditCtrl::GoToLine +================ +*/ +void CSyntaxRichEditCtrl::GoToLine( int line ) { + + int index = LineIndex( line ); + + m_TextDoc->Freeze( NULL ); + + SetSel( index, index ); + + m_TextDoc->Unfreeze( NULL ); + + UpdateVisibleRange(); + + RedrawWindow(); +} + +/* +================ +CSyntaxRichEditCtrl::OnToolHitTest +================ +*/ +int CSyntaxRichEditCtrl::OnToolHitTest( CPoint point, TOOLINFO* pTI ) const { + CRichEditCtrl::OnToolHitTest( point, pTI ); + + pTI->hwnd = GetSafeHwnd(); + pTI->uId = (UINT_PTR)GetSafeHwnd(); + pTI->uFlags |= TTF_IDISHWND; + pTI->lpszText = LPSTR_TEXTCALLBACK; + pTI->rect = CRect( point, point ); + pTI->rect.right += 100; + pTI->rect.bottom += 20; + return pTI->uId; +} + +/* +================ +CSyntaxRichEditCtrl::OnToolTipNotify +================ +*/ +BOOL CSyntaxRichEditCtrl::OnToolTipNotify( UINT id, NMHDR *pNMHDR, LRESULT *pResult ) { + TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR; + TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR; + + *pResult = 0; + + idStr name; + + if ( GetNameForMousePosition( name ) ) { + CString toolTip; + + if ( GetToolTip == NULL || !GetToolTip( name, toolTip ) ) { + + int keyWordIndex = FindKeyWord( name, name.Length() ); + + if ( keyWordIndex != -1 && keyWords[keyWordIndex].description[0] != '\0' ) { + toolTip = keyWords[keyWordIndex].description; + } else { + toolTip = name.c_str(); + } + } + + AFX_MODULE_THREAD_STATE *state = AfxGetModuleThreadState(); + + // set max tool tip width to enable multi-line tool tips using "\r\n" for line breaks + state->m_pToolTip->SetMaxTipWidth( 500 ); + + // set the number of milliseconds after which the tool tip automatically disappears + state->m_pToolTip->SetDelayTime( TTDT_AUTOPOP, 5000 + toolTip.GetLength() * 50 ); + +#ifndef _UNICODE + if( pNMHDR->code == TTN_NEEDTEXTA ) { + delete m_pchTip; + m_pchTip = new TCHAR[toolTip.GetLength() + 2]; + lstrcpyn( m_pchTip, toolTip, toolTip.GetLength() + 1 ); + pTTTW->lpszText = (WCHAR*)m_pchTip; + } else { + delete m_pwchTip; + m_pwchTip = new WCHAR[toolTip.GetLength() + 2]; + _mbstowcsz( m_pwchTip, toolTip, toolTip.GetLength() + 1 ); + pTTTW->lpszText = (WCHAR*)m_pwchTip; + } +#else + if( pNMHDR->code == TTN_NEEDTEXTA ) { + delete m_pchTip; + m_pchTip = new TCHAR[toolTip.GetLength() + 2]; + _wcstombsz( m_pchTip, toolTip, toolTip.GetLength() + 1 ); + pTTTA->lpszText = (LPTSTR)m_pchTip; + } else { + delete m_pwchTip; + m_pwchTip = new WCHAR[toolTip.GetLength() + 2]; + lstrcpyn( m_pwchTip, toolTip, toolTip.GetLength() + 1 ); + pTTTA->lpszText = (LPTSTR) m_pwchTip; + } +#endif + + return TRUE; + } + return FALSE; +} + +/* +================ +CSyntaxRichEditCtrl::OnGetDlgCode +================ +*/ +UINT CSyntaxRichEditCtrl::OnGetDlgCode() { + // get all keys, including tabs + return DLGC_WANTALLKEYS | DLGC_WANTARROWS | DLGC_WANTCHARS | DLGC_WANTMESSAGE | DLGC_WANTTAB; +} + +/* +================ +CSyntaxRichEditCtrl::OnKeyDown +================ +*/ +void CSyntaxRichEditCtrl::OnKeyDown( UINT nKey, UINT nRepCnt, UINT nFlags ) { + + if ( m_TextDoc == NULL ) { + return; + } + + if ( autoCompleteStart >= 0 ) { + int sel; + + switch( nKey ) { + case VK_UP: { // up arrow + sel = Max( 0, autoCompleteListBox.GetCurSel() - 1 ); + autoCompleteListBox.SetCurSel( sel ); + return; + } + case VK_DOWN: { // down arrow + sel = Min( autoCompleteListBox.GetCount() - 1, autoCompleteListBox.GetCurSel() + 1 ); + autoCompleteListBox.SetCurSel( sel ); + return; + } + case VK_PRIOR: { // page up key + sel = Max( 0, autoCompleteListBox.GetCurSel() - 10 ); + autoCompleteListBox.SetCurSel( sel ); + return; + } + case VK_NEXT: { // page down key + sel = Min( autoCompleteListBox.GetCount() - 1, autoCompleteListBox.GetCurSel() + 10 ); + autoCompleteListBox.SetCurSel( sel ); + return; + } + case VK_HOME: { // home key + autoCompleteListBox.SetCurSel( 0 ); + return; + } + case VK_END: { + autoCompleteListBox.SetCurSel( autoCompleteListBox.GetCount() - 1 ); + return; + } + case VK_RETURN: // enter key + case VK_TAB: { // tab key + AutoCompleteInsertText(); + AutoCompleteHide(); + return; + } + case VK_LEFT: // left arrow + case VK_RIGHT: // right arrow + case VK_INSERT: // insert key + case VK_DELETE: { // delete key + return; + } + } + } + + BracedSectionHide(); + + switch( nKey ) { + case VK_TAB: { // multi-line tabs + long selStart, selEnd; + + GetSel( selStart, selEnd ); + + // if multiple lines are selected add tabs to, or remove tabs from all of them + if ( selEnd > selStart ) { + CString text; + + text = GetSelText(); + + if ( GetAsyncKeyState( VK_SHIFT ) & 0x8000 ) { + if ( idStr::CharIsTab( text[0] ) ) { + text.Delete( 0, 1 ); + } + for ( int i = 0; i < text.GetLength() - 2; i++ ) { + if ( idStr::CharIsNewLine( text[i] ) ) { + do { + i++; + } while( idStr::CharIsNewLine( text[i] ) ); + if ( idStr::CharIsTab( text[i] ) ) { + text.Delete( i, 1 ); + } + } + } + } else { + text.Insert( 0, '\t' ); + for ( int i = 0; i < text.GetLength() - 1; i++ ) { + if ( idStr::CharIsNewLine( text[i] ) ) { + do { + i++; + } while( idStr::CharIsNewLine( text[i] ) ); + text.Insert( i, '\t' ); + } + } + } + + ReplaceSel( text, TRUE ); + SetSel( selStart, selStart + text.GetLength() ); + } else { + ReplaceSel( "\t", TRUE ); + } + return; + } + case VK_RETURN: { // auto-indentation + long selStart, selEnd; + int line, length, numTabs, i; + char buffer[1024]; + idStr text; + + GetSel( selStart, selEnd ); + line = LineFromChar( selStart ); + length = GetLine( line, buffer, sizeof( buffer ) ); + for ( numTabs = 0; numTabs < length; numTabs++ ) { + if ( !idStr::CharIsTab( buffer[numTabs] ) ) { + break; + } + } + bool first = true; + for ( i = numTabs; i < length; i++ ) { + if ( buffer[i] == '{' ) { + numTabs++; + first = false; + } else if ( buffer[i] == '}' && !first ) { + numTabs--; + } + } + text = "\r\n"; + for ( i = 0; i < numTabs; i++ ) { + text.Append( '\t' ); + } + ReplaceSel( text, TRUE ); + return; + } + } + + m_TextDoc->Freeze( NULL ); + + CRichEditCtrl::OnKeyDown( nKey, nRepCnt, nFlags ); + + UpdateVisibleRange(); + + m_TextDoc->Unfreeze( NULL ); +} + +/* +================ +CSyntaxRichEditCtrl::OnChar +================ +*/ +void CSyntaxRichEditCtrl::OnChar( UINT nChar, UINT nRepCnt, UINT nFlags ) { + + if ( nChar == VK_TAB ) { + return; // tab is handle in OnKeyDown + } + + CRichEditCtrl::OnChar( nChar, nRepCnt, nFlags ); + + // if the auto-complete list box is up + if ( autoCompleteStart >= 0 ) { + long selStart, selEnd; + + if ( charType[nChar] == CT_NAME ) { + AutoCompleteUpdate(); + return; + } else if ( nChar == VK_BACK ) { + GetSel( selStart, selEnd ); + if ( selStart > autoCompleteStart ) { + AutoCompleteUpdate(); + } else { + AutoCompleteHide(); + } + return; + } else { + AutoCompleteHide(); + } + } + + // if the function parameter tool tip is up + if ( funcParmToolTipStart >= 0 ) { + long selStart, selEnd; + + if ( nChar == ')' || nChar == VK_ESCAPE ) { + ToolTipHide(); + } else if ( nChar == VK_BACK ) { + GetSel( selStart, selEnd ); + if ( selStart < funcParmToolTipStart ) { + ToolTipHide(); + } + } + } + + // show keyword auto-completion + if ( keyWordAutoCompletion && charType[nChar] == CT_NAME && funcParmToolTipStart < 0 ) { + long selStart, selEnd; + int line, column, length, i; + char buffer[1024]; + + GetSel( selStart, selEnd ); + line = LineFromChar( selStart ); + length = GetLine( line, buffer, sizeof( buffer ) ); + column = selStart - LineIndex( line ); + if ( column <= 1 || charType[buffer[column-2]] == CT_WHITESPACE ) { + if ( column >= length-1 || charType[buffer[column]] == CT_WHITESPACE ) { + + autoCompleteListBox.ResetContent(); + for ( i = 0; keyWords[i].keyWord; i++ ) { + autoCompleteListBox.AddString( keyWords[i].keyWord ); + } + AutoCompleteShow( selStart - 1 ); + } + } + return; + } + + // highlight braced sections + if ( nChar == '{' ) { + BracedSectionStart( '{', '}' ); + } else if ( nChar == '}' ) { + BracedSectionEnd( '{', '}' ); + } else if ( nChar == '(' ) { + BracedSectionStart( '(', ')' ); + } else if ( nChar == ')' ) { + BracedSectionEnd( '(', ')' ); + } else if ( nChar == '[' ) { + BracedSectionStart( '[', ']' ); + } else if ( nChar == ']' ) { + BracedSectionEnd( '[', ']' ); + } else if ( nChar == '<' ) { + BracedSectionStart( '<', '>' ); + } else if ( nChar == '>' ) { + BracedSectionEnd( '<', '>' ); + } + + // show object member auto-completion + if ( nChar == '.' && GetObjectMembers && funcParmToolTipStart < 0 ) { + int charIndex; + CString name; + + if ( GetNameBeforeCurrentSelection( name, charIndex ) ) { + autoCompleteListBox.ResetContent(); + if ( GetObjectMembers( name, autoCompleteListBox ) ) { + AutoCompleteShow( charIndex ); + } + } + return; + } + + // show function parameter tool tip + if ( nChar == '(' && GetFunctionParms ) { + int charIndex; + CString name; + + if ( GetNameBeforeCurrentSelection( name, charIndex ) ) { + CString parmString; + if ( GetFunctionParms( name, parmString ) ) { + ToolTipShow( charIndex, parmString ); + } + } + return; + } +} + +/* +================ +CSyntaxRichEditCtrl::OnLButtonDown +================ +*/ +void CSyntaxRichEditCtrl::OnLButtonDown( UINT nFlags, CPoint point ) { + + if ( autoCompleteStart >= 0 ) { + AutoCompleteHide(); + } + + BracedSectionHide(); + + CRichEditCtrl::OnLButtonDown( nFlags, point ); +} + +/* +================ +CSyntaxRichEditCtrl::OnMouseWheel +================ +*/ +BOOL CSyntaxRichEditCtrl::OnMouseWheel( UINT nFlags, short zDelta, CPoint pt ) { + if ( autoCompleteStart >= 0 ) { + int sel; + + if ( zDelta > 0 ) { + sel = Max( 0, autoCompleteListBox.GetCurSel() - ( zDelta / WHEEL_DELTA ) ); + } else { + sel = Min( autoCompleteListBox.GetCount() - 1, autoCompleteListBox.GetCurSel() - ( zDelta / WHEEL_DELTA ) ); + } + autoCompleteListBox.SetCurSel( sel ); + return TRUE; + } + + m_TextDoc->Freeze( NULL ); + + LineScroll( -3 * ( (int) zDelta ) / WHEEL_DELTA, 0 ); + + UpdateVisibleRange(); + + m_TextDoc->Unfreeze( NULL ); + + return TRUE; +} + +/* +================ +CSyntaxRichEditCtrl::OnMouseMove +================ +*/ +void CSyntaxRichEditCtrl::OnMouseMove( UINT nFlags, CPoint point ) { + CRichEditCtrl::OnMouseMove( nFlags, point ); + + if ( point != mousePoint ) { + mousePoint = point; + + // remove tool tip and activate the tool tip control, otherwise + // tool tips stop working until the mouse moves over another window first + AFX_MODULE_THREAD_STATE *state = AfxGetModuleThreadState(); + state->m_pToolTip->Pop(); + state->m_pToolTip->Activate( TRUE ); + } +} + +/* +================ +CSyntaxRichEditCtrl::OnSize +================ +*/ +void CSyntaxRichEditCtrl::OnSize( UINT nType, int cx, int cy ) { + m_TextDoc->Freeze( NULL ); + + CRichEditCtrl::OnSize( nType, cx, cy ); + + m_TextDoc->Unfreeze( NULL ); + + UpdateVisibleRange(); +} + +/* +================ +CSyntaxRichEditCtrl::OnVScroll +================ +*/ +void CSyntaxRichEditCtrl::OnVScroll( UINT nSBCode, UINT nPos, CScrollBar* pScrollBar ) { + m_TextDoc->Freeze( NULL ); + + CRichEditCtrl::OnVScroll( nSBCode, nPos, pScrollBar ); + + SetFocus(); + + UpdateVisibleRange(); + + m_TextDoc->Unfreeze( NULL ); +} + +/* +================ +CSyntaxRichEditCtrl::OnProtected +================ +*/ +void CSyntaxRichEditCtrl::OnProtected( NMHDR *pNMHDR, LRESULT *pResult ) { + ENPROTECTED* pEP = (ENPROTECTED*)pNMHDR; + + *pResult = 0; + + updateRange = pEP->chrg; + + switch( pEP->msg ) { + case WM_MOUSEMOVE: { + break; + } + case WM_SETTEXT: { + updateRange.cpMin = pEP->chrg.cpMin; + updateRange.cpMax = pEP->chrg.cpMin + strlen( (LPCTSTR) pEP->lParam ); + break; + } + case WM_CUT: { + break; + } + case WM_COPY: { + break; + } + case WM_PASTE: { + break; + } + case WM_CLEAR: { + break; + } + case WM_UNDO: { + break; + } + default: { + break; + } + } +} + +/* +================ +CSyntaxRichEditCtrl::OnChange +================ +*/ +void CSyntaxRichEditCtrl::OnChange() { + long selStart, selEnd; + + if ( !updateSyntaxHighlighting ) { + return; + } + + GetSel( selStart, selEnd ); + selStart = Min( selStart, updateRange.cpMin ); + selEnd = Max( selEnd, updateRange.cpMax ); + + HighlightSyntax( selStart, selEnd ); + + // send EN_CHANGE notification to parent window + NMHDR pNMHDR; + pNMHDR.hwndFrom = GetSafeHwnd(); + pNMHDR.idFrom = GetDlgCtrlID(); + pNMHDR.code = EN_CHANGE; + GetParent()->SendMessage( WM_NOTIFY, ( EN_CHANGE << 16 ) | GetDlgCtrlID(), (LPARAM)&pNMHDR ); +} + +/* +================ +CSyntaxRichEditCtrl::OnAutoCompleteListBoxChange +================ +*/ +void CSyntaxRichEditCtrl::OnAutoCompleteListBoxChange() { + // steal focus back from the auto-complete list box + SetFocus(); +} + +/* +================ +CSyntaxRichEditCtrl::OnAutoCompleteListBoxDblClk +================ +*/ +void CSyntaxRichEditCtrl::OnAutoCompleteListBoxDblClk() { + // steal focus back from the auto-complete list box + SetFocus(); + + // insert current auto-complete selection + AutoCompleteInsertText(); + AutoCompleteHide(); +} diff --git a/src/tools/comafx/CSyntaxRichEditCtrl.h b/src/tools/comafx/CSyntaxRichEditCtrl.h new file mode 100644 index 0000000..a5538a7 --- /dev/null +++ b/src/tools/comafx/CSyntaxRichEditCtrl.h @@ -0,0 +1,236 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef __CSYNTAXRICHEDITCTR_H__ +#define __CSYNTAXRICHEDITCTR_H__ + +/* +=============================================================================== + + Rich Edit Control with: + + - syntax highlighting + - braced section highlighting + - braced section auto-indentation + - multi-line tabs + - keyword auto-completion + - object member auto-completion + - keyword tool tip + - function parameter tool tip + +=============================================================================== +*/ + +// use #import on Vista to generate .tlh header to copy from intermediate compile directory to local directory for subsequent builds +// rename: avoids warning C4278: 'FindText': identifier in type library 'riched20.dll' is already a macro; use the 'rename' qualifier +// no_auto_exclude: avoids warnings +// no_namespace: no longer using this option, which avoids variable redifinition compile errors on Vista +//#define GENERATE_TLH +#ifdef GENERATE_TLH +# import "riched20.dll" raw_interfaces_only, raw_native_types, named_guids, no_auto_exclude, no_implementation, rename( "FindText", "FindShit" ) +#else +# include "riched20.tlh" +#endif + +static const char * FONT_NAME = "Courier"; +static const int FONT_HEIGHT = 10; +static const int FONT_WIDTH = 8; +static const int TAB_SIZE = 4; + +static const COLORREF SRE_COLOR_BLACK = RGB( 0, 0, 0 ); +static const COLORREF SRE_COLOR_WHITE = RGB( 255, 255, 255 ); +static const COLORREF SRE_COLOR_RED = RGB( 255, 0, 0 ); +static const COLORREF SRE_COLOR_GREEN = RGB( 0, 255, 0 ); +static const COLORREF SRE_COLOR_BLUE = RGB( 0, 0, 255 ); +static const COLORREF SRE_COLOR_YELLOW = RGB( 255, 255, 0 ); +static const COLORREF SRE_COLOR_MAGENTA = RGB( 255, 0, 255 ); +static const COLORREF SRE_COLOR_CYAN = RGB( 0, 255, 255 ); +static const COLORREF SRE_COLOR_ORANGE = RGB( 255, 128, 0 ); +static const COLORREF SRE_COLOR_PURPLE = RGB( 150, 0, 150 ); +static const COLORREF SRE_COLOR_PINK = RGB( 186, 102, 123 ); +static const COLORREF SRE_COLOR_GREY = RGB( 85, 85, 85 ); +static const COLORREF SRE_COLOR_BROWN = RGB( 100, 90, 20 ); +static const COLORREF SRE_COLOR_LIGHT_GREY = RGB( 170, 170, 170 ); +static const COLORREF SRE_COLOR_LIGHT_BROWN = RGB( 170, 150, 20 ); +static const COLORREF SRE_COLOR_DARK_GREEN = RGB( 0, 128, 0 ); +static const COLORREF SRE_COLOR_DARK_CYAN = RGB( 0, 150, 150 ); +static const COLORREF SRE_COLOR_DARK_YELLOW = RGB( 220, 200, 20 ); + +typedef struct { + const char * keyWord; + COLORREF color; + const char * description; +} keyWord_t; + +typedef bool (*objectMemberCallback_t)( const char *objectName, CListBox &listBox ); +typedef bool (*toolTipCallback_t)( const char *name, CString &string ); + + +class CSyntaxRichEditCtrl : public CRichEditCtrl { +public: + CSyntaxRichEditCtrl( void ); + ~CSyntaxRichEditCtrl( void ); + + void Init( void ); + + void SetCaseSensitive( bool caseSensitive ); + void AllowPathNames( bool allow ); + void EnableKeyWordAutoCompletion( bool enable ); + void SetKeyWords( const keyWord_t kws[] ); + bool LoadKeyWordsFromFile( const char *fileName ); + void SetObjectMemberCallback( objectMemberCallback_t callback ); + void SetFunctionParmCallback( toolTipCallback_t callback ); + void SetToolTipCallback( toolTipCallback_t callback ); + + void SetDefaultColor( const COLORREF color ); + void SetCommentColor( const COLORREF color ); + void SetStringColor( const COLORREF color, const COLORREF altColor = -1 ); + void SetLiteralColor( const COLORREF color ); + + COLORREF GetForeColor( int charIndex ) const; + COLORREF GetBackColor( int charIndex ) const; + + void GetCursorPos( int &line, int &column, int &character ) const; + CHARRANGE GetVisibleRange( void ) const; + + void GetText( idStr &text ) const; + void GetText( idStr &text, int startCharIndex, int endCharIndex ) const; + void SetText( const char *text ); + + void GoToLine( int line ); + bool FindNext( const char *find, bool matchCase, bool matchWholeWords, bool searchForward ); + int ReplaceAll( const char *find, const char *replace, bool matchCase, bool matchWholeWords ); + void ReplaceText( int startCharIndex, int endCharIndex, const char *replace ); + +protected: + virtual int OnToolHitTest( CPoint point, TOOLINFO* pTI ) const; + afx_msg BOOL OnToolTipNotify( UINT id, NMHDR *pNMHDR, LRESULT *pResult ); + afx_msg UINT OnGetDlgCode(); + afx_msg void OnChar( UINT nChar, UINT nRepCnt, UINT nFlags ); + afx_msg void OnKeyDown( UINT nKey, UINT nRepCnt, UINT nFlags ); + afx_msg void OnLButtonDown( UINT nFlags, CPoint point ); + afx_msg BOOL OnMouseWheel( UINT nFlags, short zDelta, CPoint pt ); + afx_msg void OnMouseMove( UINT nFlags, CPoint point ); + afx_msg void OnVScroll( UINT nSBCode, UINT nPos, CScrollBar* pScrollBar ); + afx_msg void OnSize( UINT nType, int cx, int cy ); + afx_msg void OnProtected( NMHDR *pNMHDR, LRESULT *pResult ); + afx_msg void OnChange(); + afx_msg void OnAutoCompleteListBoxChange(); + afx_msg void OnAutoCompleteListBoxDblClk(); + + DECLARE_MESSAGE_MAP() + + // settings + CHARFORMAT2 defaultCharFormat; + COLORREF defaultColor; + COLORREF singleLineCommentColor; + COLORREF multiLineCommentColor; + COLORREF stringColor[2]; + COLORREF literalColor; + COLORREF braceHighlightColor; + + typedef enum { + CT_WHITESPACE, + CT_COMMENT, + CT_STRING, + CT_LITERAL, + CT_NUMBER, + CT_NAME, + CT_PUNCTUATION + } charType_t; + + int charType[256]; + + idList keyWordsFromFile; + const keyWord_t * keyWords; + int * keyWordLengths; + COLORREF * keyWordColors; + idHashIndex keyWordHash; + + bool caseSensitive; + bool allowPathNames; + bool keyWordAutoCompletion; + + objectMemberCallback_t GetObjectMembers; + toolTipCallback_t GetFunctionParms; + toolTipCallback_t GetToolTip; + + // run-time variables + tom::ITextDocument * m_TextDoc; + tom::ITextFont * m_DefaultFont; + + CHARRANGE updateRange; + bool updateSyntaxHighlighting; + int stringColorIndex; + int stringColorLine; + + int autoCompleteStart; + CListBox autoCompleteListBox; + + int funcParmToolTipStart; + CEdit funcParmToolTip; + + int bracedSection[2]; + + CPoint mousePoint; + CToolTipCtrl * keyWordToolTip; + TCHAR * m_pchTip; + WCHAR * m_pwchTip; + +protected: + void InitFont( void ); + void InitSyntaxHighlighting( void ); + void SetCharType( int first, int last, int type ); + void SetDefaultFont( int startCharIndex, int endCharIndex ); + void SetColor( int startCharIndex, int endCharIndex, COLORREF foreColor, COLORREF backColor, bool bold ); + + void FreeKeyWordsFromFile( void ); + int FindKeyWord( const char *keyWord, int length ) const; + + void HighlightSyntax( int startCharIndex, int endCharIndex ); + void UpdateVisibleRange( void ); + + bool GetNameBeforeCurrentSelection( CString &name, int &charIndex ) const; + bool GetNameForMousePosition( idStr &name ) const; + + void AutoCompleteInsertText( void ); + void AutoCompleteUpdate( void ); + void AutoCompleteShow( int charIndex ); + void AutoCompleteHide( void ); + + void ToolTipShow( int charIndex, const char *string ); + void ToolTipHide( void ); + + bool BracedSectionStart( char braceStartChar, char braceEndChar ); + bool BracedSectionEnd( char braceStartChar, char braceEndChar ); + void BracedSectionAdjustEndTabs( void ); + void BracedSectionShow( void ); + void BracedSectionHide( void ); +}; + +#endif /* !__CSYNTAXRICHEDITCTR_H__ */ diff --git a/src/tools/comafx/DialogColorPicker.cpp b/src/tools/comafx/DialogColorPicker.cpp new file mode 100644 index 0000000..4d4b7ce --- /dev/null +++ b/src/tools/comafx/DialogColorPicker.cpp @@ -0,0 +1,1314 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "../../sys/win32/rc/Radiant_resource.h" +#include "DialogColorPicker.h" + +#ifdef ID_DEBUG_MEMORY +#undef new +#undef DEBUG_NEW +#define DEBUG_NEW new +#endif + +// Old color picker + +class CMyColorDialog : public CColorDialog +{ + DECLARE_DYNCREATE(CMyColorDialog); + // Construction +public: + CMyColorDialog( COLORREF clrInit = 0, DWORD dwFlags = 0, CWnd *pParentWnd = NULL ); + virtual int DoModal(); + +protected: + enum { NCUSTCOLORS = 16 }; + static COLORREF c_CustColors[NCUSTCOLORS]; + static COLORREF c_LastCustColors[NCUSTCOLORS]; + static bool c_NeedToInitCustColors; + static void InitCustColors(); + static void SaveCustColors(); + + // Dialog Data + //{{AFX_DATA(CMyColorDialog) + //}}AFX_DATA + +protected: + // ClassWizard generate virtual function overrides + //{{AFX_VIRTUAL(CMyColorDialog) + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + + // Generated message map functions + //{{AFX_MSG(CMyColorDialog) + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +IMPLEMENT_DYNCREATE( CMyColorDialog, CColorDialog ) + +bool CMyColorDialog::c_NeedToInitCustColors = true; +COLORREF CMyColorDialog::c_CustColors[]; +COLORREF CMyColorDialog::c_LastCustColors[]; + +#define SECTION _T("Custom Colors") + +void CMyColorDialog::InitCustColors() { + for ( int i = 0; i < NCUSTCOLORS; i++) { + CString entry; + entry.Format( "tool_color%d", i); + idCVar *cvar = cvarSystem->Find( entry ); + if ( cvar ) { + c_LastCustColors[i] = c_CustColors[i] = cvar->GetInteger(); + } else { + c_LastCustColors[i] = c_CustColors[i] = RGB( 255, 255, 255 ); + } + } + c_NeedToInitCustColors= false; +} + +void CMyColorDialog::SaveCustColors() { + for (int i = 0; i < NCUSTCOLORS; i++) { + if ( c_LastCustColors[i] != c_CustColors[i] ) { + CString entry; + entry.Format( "tool_color%d", i ); + if ( c_CustColors[i] == RGB( 255, 255, 255 ) ) { + cvarSystem->SetCVarString( entry, "" ); + } else { + cvarSystem->SetCVarString( entry, va( "%d", c_CustColors[i] ), CVAR_TOOL ); + } + c_LastCustColors[i] = c_CustColors[i]; + } + } +} + +CMyColorDialog::CMyColorDialog( COLORREF clrInit, DWORD dwFlags, + CWnd* pParentWnd) : CColorDialog(clrInit,dwFlags,pParentWnd) +{ + //{{AFX_DATA_INIT(CMyColorDialog) + //}}AFX_DATA_INIT + if (c_NeedToInitCustColors) { + InitCustColors(); + } + m_cc.lpCustColors = c_CustColors; +} + +int CMyColorDialog::DoModal() { + int code = CColorDialog::DoModal(); + SaveCustColors(); + return code; +} + +void CMyColorDialog::DoDataExchange(CDataExchange* pDX) { + // overridden (calls this base class) + CColorDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CMyColorDialog) + //}}AFX_DATA_MAP +} + +BEGIN_MESSAGE_MAP(CMyColorDialog, CColorDialog) +//{{AFX_MSG_MAP(CMyColorDialog) +//}}AFX_MSG_MAP +END_MESSAGE_MAP() + +COLORREF DoOldColor(COLORREF cr) { + CMyColorDialog dlg(cr, CC_FULLOPEN | CC_RGBINIT | CC_ANYCOLOR); + if (dlg.DoModal() == IDOK) { + return dlg.GetColor(); + } + return cr; +} + +// New color picker + +// Original ColorPicker/DIB source by Rajiv Ramachandran +// included with Permission from the author + +#define RADUIS 100 + +#define IN_NOTHING 0 +#define IN_CIRCLE 1 +#define IN_BRIGHT 2 +#define IN_OVERBRIGHT 3 + +int Distance(CPoint pt1,CPoint pt2); + + +double Slope( CPoint pt1,CPoint pt2 ) { + double x,y; + + y = pt2.y - pt1.y; + x = pt2.x - pt1.x; + if( x ) { + return y/x; + } else { + return BAD_SLOPE; + } +} + +CPoint Intersection(LineDesc l1,LineDesc l2) +{ + CPoint pt; + double x,y; + + if(l1.slope == l2.slope) + { + // Parallel lines, no intersection + return CPoint(0,0); + } + else + if(l1.slope == BAD_SLOPE ) + { + // First Line is vertical, eqn is x=0 + // Put x = 0 in second line eqn to get y; + x = l1.x; + y = l2.slope * x + l2.c; + } + else + if(l2.slope == BAD_SLOPE) + { + // second line is vertical Equation of line is x=0; + // Put x = 0 in first line eqn to get y; + x = l2.x; + y = l1.slope * l2.x + l1.c; + } + else + { + y = ((l1.c * l2.slope) - (l2.c * l1.slope))/(l2.slope - l1.slope); + x = (y - l1.c)/l1.slope; + } + + return CPoint((int)x,(int)y); +} + +double FindC(LineDesc& l) +{ + double c; + + if(l.slope == BAD_SLOPE) + { + c = l.y; + } + else + { + c = l.y - l.slope * l.x; + } + return c; +} + +CPoint PointOnLine(CPoint pt1,CPoint pt2,int len,int maxlen ) +{ + double x,y,m,a,c,C,A; + double a2,c2,m2,B; + CPoint opt = pt1; + CPoint pt; + + pt1.y *= -1; + pt2.y *= -1; + + a = (double)len; + + if(pt2.x != pt1.x) + { + m = (double)(pt2.y - pt1.y)/(pt2.x - pt1.x); + m2 = m*m; + a2 = a*a; + c = (double)pt1.y - m * (double)pt1.x; + c2 = c*c; + + + A = 1.0; + + x = pt1.x; + + B = 2.0 * pt1.x; + + x *= x; + C = x - a2/(m2 + 1); + + x = (B + idMath::Sqrt(B*B - (4.0*A*C)))/(2.0*A); + y = m*x + c; + pt = CPoint((int)x,(int)y); + if(Distance(pt,pt1) > maxlen || Distance(pt,pt2) > maxlen) + { + x = (B - idMath::Sqrt(B*B - (4.0*A*C)))/(2.0 * A); + y = m*x + c; + pt = CPoint((int)x,(int)y); + } + } + else + { + a2 = a*a; + y = idMath::Sqrt(a2); + x = 0; + pt = CPoint((int)x,(int)y); + pt += pt1; + if(Distance(pt,pt1) > maxlen || Distance(pt,pt2) > maxlen) + { + y = -1.0 *y; + pt = CPoint((int)x,(int)y); + pt+=pt1; + } + } + pt.y *= -1; + return pt; +} + + +int Distance(CPoint pt1,CPoint pt2) +{ + double a; + int x,y; + + y = (pt1.y - pt2.y); + y *= y; + + x = (pt1.x - pt2.x); + x *= x; + + a = (double)x + (double)y ; + a = idMath::Sqrt(a); + return (int)a; +} + +double AngleFromPoint(CPoint pt,CPoint center) +{ + double x,y; + + y = -1 * (pt.y - center.y); + x = pt.x - center.x; + if(x == 0 && y == 0) + { + return 0.0; + } + else + { + return atan2(y,x); + } +} + +CPoint PtFromAngle(double angle,double sat,CPoint center) +{ + angle = DEG2RAD(angle); + sat = TOSCALE(sat); + + double x,y; + + x = sat * cos(angle); + y = sat * sin(angle); + + CPoint pt; + + pt = CPoint((int)x,(int)y); + pt.y *= -1; + pt += center; + return pt; +} + +RGBType HSVType::toRGB() +{ + RGBType rgb; + + if(!h && !s) + { + rgb.r = rgb.g = rgb.b = v; + } + + double min,max,delta,hue; + + max = v; + delta = (max * s)/255.0; + min = max - delta; + + hue = h; + if(h > 300 || h <= 60) + { + rgb.r = (int)max; + if(h > 300) + { + rgb.g = (int)min; + hue = (hue - 360.0)/60.0; + rgb.b = (int)((hue * delta - min) * -1); + } + else + { + rgb.b = (int)min; + hue = hue / 60.0; + rgb.g = (int)(hue * delta + min); + } + } + else if(h > 60 && h < 180) + { + rgb.g = (int)max; + if(h < 120) + { + rgb.b = (int)min; + hue = (hue/60.0 - 2.0 ) * delta; + rgb.r = (int)(min - hue); + } + else + { + rgb.r = (int)min; + hue = (hue/60 - 2.0) * delta; + rgb.b = (int)(min + hue); + } + } + else + { + rgb.b = (int)max; + if(h < 240) + { + rgb.r = (int)min; + hue = (hue/60.0 - 4.0 ) * delta; + rgb.g = (int)(min - hue); + } + else + { + rgb.g = (int)min; + hue = (hue/60 - 4.0) * delta; + rgb.r = (int)(min + hue); + } + } + return rgb; +} + + +HSVType RGBType::toHSV() +{ + HSVType hsv; + + double min,max,delta,temp; + + min = __min(r,__min(g,b)); + max = __max(r,__max(g,b)); + delta = max - min; + + hsv.v = (int)max; + if(!delta) + { + hsv.h = hsv.s = 0; + } + else + { + temp = delta/max; + hsv.s = (int)(temp*255); + + if(r == (int)max) + { + temp = (double)(g-b)/delta; + } + else + if(g == (int)max) + { + temp = 2.0 + ((double)(b-r)/delta); + } + else + { + temp = 4.0 + ((double)(r-g)/delta); + } + temp *= 60; + if(temp < 0) + { + temp+=360; + } + if(temp == 360) + { + temp = 0; + } + hsv.h = (int)temp; + } + return hsv; + +} +///////////////////////////////////////////////////////////////////////////// +// CDialogColorPicker dialog + + +CDialogColorPicker::CDialogColorPicker( COLORREF c, CWnd* pParent /*=NULL*/) + : CDialog(CDialogColorPicker::IDD, pParent) +{ + //{{AFX_DATA_INIT(CDialogColorPicker) + m_overBright = 0.0f; + //}}AFX_DATA_INIT + + Vertex = CPoint(102,108); + Top = CPoint(102,9); + Left = CPoint(23,147); + Right = CPoint(181,147); + + color.r = GetRValue(c); + color.g = GetGValue(c); + color.b = GetBValue(c); + + m_OldColor = color; + hsvColor = color.toHSV(); + m_bInMouse = FALSE; + m_bInitOver = FALSE; + m_bInDrawAll = FALSE; + overBright = 1.0f; + UpdateParent = NULL; +} + +CDialogColorPicker::~CDialogColorPicker() +{ + if(m_RgbBitmap.GetSafeHandle()) + { + m_RgbBitmap.DeleteObject(); + } + if(m_HsbBitmap.GetSafeHandle()) + { + m_HsbBitmap.DeleteObject(); + } +} + + +BEGIN_MESSAGE_MAP(CDialogColorPicker, CDialog) + //{{AFX_MSG_MAP(CDialogColorPicker) + ON_WM_LBUTTONDOWN() + ON_WM_LBUTTONUP() + ON_WM_MOUSEMOVE() + ON_WM_SYSCOLORCHANGE() + ON_WM_PAINT() + ON_EN_CHANGE(IDC_EDIT_BLUE, OnChangeEditBlue) + ON_EN_CHANGE(IDC_EDIT_GREEN, OnChangeEditGreen) + ON_EN_CHANGE(IDC_EDIT_HUE, OnChangeEditHue) + ON_EN_CHANGE(IDC_EDIT_RED, OnChangeEditRed) + ON_EN_CHANGE(IDC_EDIT_SAT, OnChangeEditSat) + ON_EN_CHANGE(IDC_EDIT_VAL, OnChangeEditVal) + ON_EN_CHANGE(IDC_EDIT_OVERBRIGHT, OnChangeEditOverbright) + ON_BN_CLICKED(IDC_BTN_OLDCOLOR, OnBtnColor) + ON_WM_TIMER() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CDialogColorPicker message handlers + +void CDialogColorPicker::OnLButtonDown(UINT nFlags, CPoint point) { + if(hsbRect.PtInRect(point)) { + m_bInMouse = FALSE; + if(InCircle(point)) { + m_nMouseIn = IN_CIRCLE; + } else if (InBright(point)) { + m_nMouseIn = IN_BRIGHT; + } else if (InOverBright(point)) { + m_nMouseIn = IN_OVERBRIGHT; + } else { + m_nMouseIn = IN_NOTHING; + } + + if(m_nMouseIn) { + SetCapture(); + TrackPoint(point); + } + } + else if (rgbRect.PtInRect(point)) { + m_nMouseIn = IN_NOTHING; + if(rects[RED].PtInRect(point)) { + SetCapture(); + m_bInMouse = TRUE; + nIndex = RED; + } else if (rects[GREEN].PtInRect(point)) { + SetCapture(); + m_bInMouse = TRUE; + nIndex = GREEN; + } else if (rects[BLUE].PtInRect(point)) { + SetCapture(); + m_bInMouse = TRUE; + nIndex = BLUE; + } + } + + CDialog::OnLButtonDown(nFlags, point); +} + +void CDialogColorPicker::OnLButtonUp(UINT nFlags, CPoint point) +{ + if(GetCapture() == this) + { + ReleaseCapture(); + m_bInMouse = FALSE; + } + CDialog::OnLButtonUp(nFlags, point); +} + +void CDialogColorPicker::OnMouseMove(UINT nFlags, CPoint point) +{ + if(GetCapture() == this && m_nMouseIn) + { + TrackPoint(point); + } + else if(GetCapture() == this && m_bInMouse) + { + double val; + BOOL bChange = FALSE; + + if(nIndex == RED) + { + if(point.y > Vertex.y) + { + point.y = Vertex.y; + } + point.x = Vertex.x; + val = Distance(point,Vertex); + if(val > RedLen) + { + val = RedLen; + } + CClientDC dc(this); + DrawLines(&dc); + val = (val/RedLen)*255; + color.r = (int)val; + CPoint pt; + pt = PointOnLine(Vertex,Top,(color.r*RedLen)/255,RedLen); + rects[RED] = CRect(pt.x - RECT_WIDTH ,pt.y-RECT_WIDTH ,pt.x+RECT_WIDTH ,pt.y+RECT_WIDTH ); + CalcCuboid(); + DrawLines(&dc); + bChange = TRUE; + } + else if(nIndex == GREEN) + { + if(point.x > Vertex.x) + { + point.x = Vertex.x; + } + point.y = rects[GREEN].top + RECT_WIDTH; + val = Distance(point,Vertex); + if(val > GreenLen) + { + val = GreenLen; + } + CClientDC dc(this); + DrawLines(&dc); + val = (val/GreenLen)*255; + color.g = (int)val; + CPoint pt; + pt = PointOnLine(Vertex,Left,(color.g*GreenLen)/255,GreenLen); + rects[GREEN] = CRect(pt.x - RECT_WIDTH ,pt.y-RECT_WIDTH ,pt.x+RECT_WIDTH ,pt.y+RECT_WIDTH ); + CalcCuboid(); + DrawLines(&dc); + bChange = TRUE; + } + else if(nIndex == BLUE) + { + if(point.x < Vertex.x) + { + point.x = Vertex.x; + } + point.y = rects[BLUE].top + RECT_WIDTH; + val = Distance(point,Vertex); + if(val > BlueLen) + { + val = BlueLen; + } + CClientDC dc(this); + DrawLines(&dc); + val = (val/BlueLen)*255; + color.b = (int)val; + CPoint pt; + pt = PointOnLine(Vertex,Right,(color.b*GreenLen)/255,BlueLen); + rects[BLUE] = CRect(pt.x - RECT_WIDTH ,pt.y-RECT_WIDTH ,pt.x+RECT_WIDTH ,pt.y+RECT_WIDTH ); + CalcCuboid(); + DrawLines(&dc); + bChange = TRUE; + } + if(bChange) + { + hsvColor = color.toHSV(); + SetEditVals(); + CClientDC dc(this); + DrawMarkers(&dc); + CalcRects(); + SetDIBPalette(); + + InvalidateRect(&brightRect,FALSE); + DrawHSB(&dc); + } + } + CDialog::OnMouseMove(nFlags, point); +} + +void CDialogColorPicker::OnPaint() +{ + CPaintDC dc(this); // device context for painting + + DrawHSB(&dc); + DrawRGB(&dc); +} + +BOOL CDialogColorPicker::OnInitDialog() +{ + CDialog::OnInitDialog(); + + GetDlgItem(IDC_STATIC_RGB_RECT)->GetWindowRect(&rgbRect); + GetDlgItem(IDC_STATIC_HSB_RECT)->GetWindowRect(&hsbRect); + ScreenToClient(&rgbRect); + ScreenToClient(&hsbRect); + + GetDlgItem(IDC_STATIC_NEWCOLOR)->GetWindowRect(&NewColorRect); + ScreenToClient(&NewColorRect); + + + CWindowDC dc(NULL); + CSize bmSize; + + // Set Up HSB + + memDC.CreateCompatibleDC(&dc); + + LoadMappedBitmap(m_HsbBitmap,IDB_BITMAP_HSB,bmSize); + hsbWidth = bmSize.cx; + hsbHeight = bmSize.cy; + + hsbRect.InflateRect(-5,-5); + hsbRect.top += 20; + hsbRect.left += 10; + + m_Centre = CPoint(RADIUS,RADIUS); + m_Centre += CPoint(hsbRect.left,hsbRect.top); + + brightRect = CRect(hsbRect.left+hsbWidth+20,hsbRect.top,hsbRect.left+hsbWidth+20+20,hsbRect.top + hsbHeight); + overBrightRect = brightRect; + overBrightRect.OffsetRect(brightRect.Width() + 5, 0); + + CreateBrightDIB(); + CalcRects(); + SetDIBPalette(); + + + // Set Up RGB + + LoadMappedBitmap(m_RgbBitmap,IDB_BITMAP_RGB,bmSize); + rgbWidth = bmSize.cx; + rgbHeight = bmSize.cy; + + rgbRect.InflateRect(-5,-5); + rgbRect.top+=10; + rgbRect.left-=3; + + CPoint pt = CPoint(rgbRect.left,rgbRect.top); + + Top += pt; + Left += pt; + Right += pt; + Vertex += pt; + // TODO: Add your specialized code here and/or call the base class + + RedLen = Distance(Vertex,Top); + GreenLen = Distance(Vertex,Left); + BlueLen = Distance(Vertex,Right); + + CalcSlopes(); + CalcCuboid(); + + SetSpinVals(); + SetEditVals(); + + m_bInitOver = TRUE; + + SetTimer(0, 50, NULL); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void CDialogColorPicker::DrawMarkers(CDC *pDC) +{ + if(m_CurrentRect.Width()) + { + CPen *oldPen; + CBrush *oldBrush; + int oldMode; + CRect cr = m_CurrentRect; + + oldPen = (CPen *)pDC->SelectStockObject(WHITE_PEN); + oldBrush = (CBrush *)pDC->SelectStockObject(NULL_BRUSH); + + oldMode = pDC->SetROP2(R2_XORPEN); + + pDC->Rectangle(&cr); + CPen pen; + pen.CreatePen(PS_SOLID,2,RGB(255,255,255)); + pDC->SelectObject(&pen); + pDC->Rectangle(&brightMark); + + pDC->SelectObject(oldPen); + pDC->SelectObject(oldBrush); + pDC->SetROP2(oldMode); + pen.DeleteObject(); + } +} + +BOOL CDialogColorPicker::InCircle(CPoint pt) +{ + return Distance(pt,m_Centre) <= RADIUS; +} + +BOOL CDialogColorPicker::InBright(CPoint pt) +{ + return brightRect.PtInRect(pt); +} + +BOOL CDialogColorPicker::InOverBright(CPoint pt) +{ + return overBrightRect.PtInRect(pt); +} + +void CDialogColorPicker::TrackPoint(CPoint pt) +{ + if(m_nMouseIn == IN_CIRCLE) + { + CClientDC dc(this); + + DrawMarkers(&dc); + + hsvColor.h = (int)RAD2DEG(AngleFromPoint(pt,m_Centre)); + if(hsvColor.h < 0) + { + hsvColor.h += 360; + } + hsvColor.s = (int)SCALETOMAX(Distance(pt,m_Centre)); + if(hsvColor.s > 255) hsvColor.s = 255; + + SetDIBPalette(); + CalcRects(); + + + InvalidateRect(&brightRect,FALSE); + + DrawMarkers(&dc); + + color = hsvColor.toRGB(); + SetEditVals(); + DrawLines(&dc); + CalcCuboid(); + DrawRGB(&dc); + + } + else if(m_nMouseIn == IN_BRIGHT) + { + double d; + d = brightRect.bottom - pt.y; + d *= 255; + d /= brightRect.Height(); + if(d < 0 ) d = 0; + if(d > 255) d = 255; + CClientDC dc(this); + DrawMarkers(&dc); + hsvColor.v = (int)d; + CalcRects(); + DrawMarkers(&dc); + + color = hsvColor.toRGB(); + SetEditVals(); + DrawLines(&dc); + CalcCuboid(); + DrawRGB(&dc); + } +} + +void CDialogColorPicker::CreateBrightDIB() +{ + CDIB& d = m_BrightDIB; + + d.Create(brightRect.Width(),brightRect.Height(),8); + for(int i=0; i < d.Height(); i++) + { + memset(d.GetLinePtr(i),i,d.Width()); + } +} + +void CDialogColorPicker::SetDIBPalette() +{ + BYTE palette[768],*p; + HSVType h = hsvColor; + double d; + + d = 255.0/brightRect.Height(); + p = palette; + for(int i=brightRect.Height()-1; i >= 0 ;i--,p+=3) + { + h.v = (int)((double)i * d); + RGBType rgb = h.toRGB(); + p[0] = rgb.r; + p[1] = rgb.g; + p[2] = rgb.b; + } + m_BrightDIB.SetPalette(palette); +} + +void CDialogColorPicker::CalcRects() +{ + CPoint pt; + + pt = PtFromAngle(hsvColor.h,hsvColor.s,m_Centre); + m_CurrentRect = CRect(pt.x - RECT_WIDTH,pt.y - RECT_WIDTH,pt.x+RECT_WIDTH,pt.y + RECT_WIDTH); + + int y; + + y = (int)(((double)hsvColor.v/255)*brightRect.Height()); + y = brightRect.bottom - y; + brightMark = CRect(brightRect.left - 2, y - 4, brightRect.right+2,y+4); +} + + +void CDialogColorPicker::DrawHSB(CDC *pDC) +{ + if(m_HsbBitmap.GetSafeHandle()) + { + CBitmap *pOldBitmap ; + pOldBitmap = (CBitmap *)memDC.SelectObject(&m_HsbBitmap); + pDC->BitBlt(hsbRect.left,hsbRect.top,hsbWidth,hsbHeight,&memDC,0,0,SRCCOPY); + m_BrightDIB.BitBlt(pDC->m_hDC,brightRect.left,brightRect.top,brightRect.Width(),brightRect.Height(),0,0); + DrawMarkers(pDC); + memDC.SelectObject(pOldBitmap); + } +} + +void CDialogColorPicker::DrawRGB(CDC *pDC) +{ + if(m_RgbBitmap.GetSafeHandle()) + { + CBitmap *pOldBitmap ; + pOldBitmap = (CBitmap *)memDC.SelectObject(&m_RgbBitmap); + pDC->BitBlt(rgbRect.left,rgbRect.top,rgbWidth,rgbHeight,&memDC,0,0,SRCCOPY); + DrawLines(pDC); + memDC.SelectObject(pOldBitmap); + } +} + +void CDialogColorPicker::DrawLines(CDC *pDC) +{ + CPoint pt[3]; + + pt[0] = PointOnLine(Vertex,Top,(color.r*RedLen)/255,RedLen); + pt[1] = PointOnLine(Vertex,Left,(color.g*GreenLen)/255,GreenLen); + pt[2] = PointOnLine(Vertex,Right,(color.b*BlueLen)/255,BlueLen); + + COLORREF col = RGB(255,255,255); + CRect cr; + + for(int i = 0; i < 3; i++ ) { + cr = CRect(pt[i].x - RECT_WIDTH ,pt[i].y-RECT_WIDTH ,pt[i].x+RECT_WIDTH ,pt[i].y+RECT_WIDTH ); + rects[i] = cr; + DrawXorRect(pDC,cr); + } + + CPen *oldPen; + int oldMode; + + oldPen = (CPen *)pDC->SelectStockObject(WHITE_PEN); + oldMode = pDC->SetROP2(R2_XORPEN); + + /* + Draw the following lines : + + 1 -2 + 2 -3 + 3 - 4 + 4- 5 + 5 -2 + 5 - 6 + 6-7 + 7-4 + */ + pDC->MoveTo(m_Cuboid[1]); + pDC->LineTo(m_Cuboid[2]); + pDC->LineTo(m_Cuboid[3]); + pDC->LineTo(m_Cuboid[4]); + pDC->LineTo(m_Cuboid[5]); + pDC->LineTo(m_Cuboid[2]); + + pDC->MoveTo(m_Cuboid[5]); + pDC->LineTo(m_Cuboid[6]); + pDC->LineTo(m_Cuboid[7]); + pDC->LineTo(m_Cuboid[4]); + + pDC->MoveTo(m_Cuboid[1]); + pDC->LineTo(m_Cuboid[6]); + + pDC->SelectObject(oldPen); + pDC->SetROP2(oldMode); + + DrawFilledColor(pDC,NewColorRect,color.color()); +} + +void CDialogColorPicker::DrawXorRect(CDC *pDC,CRect& cr) +{ + CPen pen,*oldPen; + CBrush *oldBrush; + int oldMode; + + pen.CreatePen(PS_SOLID,1,RGB(255,255,255)); + oldPen = (CPen *)pDC->SelectObject(&pen); + oldBrush = (CBrush *)pDC->SelectStockObject(NULL_BRUSH); + oldMode =pDC->SetROP2(R2_XORPEN); + pDC->Rectangle(&cr); + pDC->SetROP2(oldMode); + pDC->SelectObject(oldPen); + pDC->SelectObject(oldBrush); + pen.DeleteObject(); + +} + +void CDialogColorPicker::CalcSlopes() +{ + lines[RED].slope = Slope(Top,Vertex); + lines[GREEN].slope = Slope(Left,Vertex); + lines[BLUE].slope = Slope(Right,Vertex); + + int i; + + for( i = 0; i < 3; i++ ) { + lines[i].x = Vertex.x; + lines[i].y = Vertex.y; + lines[i].c = FindC(lines[i]); + } +} + +/* + + Cuboid points + 0 = vertex + 1 = Red Axis + 2 = Red Green Intersection + 3 = Green Axis + 4 = Blue Green Intersection + 5 = Blue Green Red Intersection + 6 = Red Blue Intersection + 7 = Blue Axis + + Draw the following lines : + + 1 -2 + 2 -3 + 3 - 4 + 4- 5 + 5 -2 + 5 - 6 + 6-7 + 7-4 +*/ + +void CDialogColorPicker::CalcCuboid() +{ + double rLen,gLen,bLen; + + rLen = (double)(color.r*RedLen)/255; + gLen = (double)(color.g*GreenLen)/255; + bLen = (double)(color.b*BlueLen)/255; + + LineDesc l[12]; + + m_Cuboid[0] = Vertex; + m_Cuboid[1] = PointOnLine(Vertex,Top,(int)rLen,RedLen); + m_Cuboid[3] = PointOnLine(Vertex,Left,(int)gLen,GreenLen); + m_Cuboid[7] = PointOnLine(Vertex,Right,(int)bLen,BlueLen); + + l[0] = lines[RED]; + l[1] = lines[GREEN]; + l[2] = lines[BLUE]; + + l[3].slope = lines[GREEN].slope; + l[3].x = m_Cuboid[1].x; + l[3].y = m_Cuboid[1].y; + l[3].c = FindC(l[3]); + + l[4].slope = lines[RED].slope; + l[4].x = m_Cuboid[3].x; + l[4].y = m_Cuboid[3].y; + l[4].c = FindC(l[4]); + + l[5].slope = lines[BLUE].slope; + l[5].x = m_Cuboid[3].x; + l[5].y = m_Cuboid[3].y; + l[5].c = FindC(l[5]); + + l[6].slope = lines[GREEN].slope; + l[6].x = m_Cuboid[7].x; + l[6].y = m_Cuboid[7].y; + l[6].c = FindC(l[6]); + + l[10].slope = lines[BLUE].slope; + l[10].x = m_Cuboid[1].x; + l[10].y = m_Cuboid[1].y; + l[10].c = FindC(l[10]); + + l[11].slope = lines[RED].slope; + l[11].x = m_Cuboid[7].x; + l[11].y = m_Cuboid[7].y; + l[11].c = FindC(l[11]); + + m_Cuboid[2] = Intersection(l[3],l[4]); + m_Cuboid[4] = Intersection(l[5],l[6]); + m_Cuboid[6] = Intersection(l[10],l[11]); + + l[7].slope = lines[RED].slope; + l[7].x = m_Cuboid[4].x; + l[7].y = m_Cuboid[4].y; + l[7].c = FindC(l[7]); + + l[8].slope = lines[BLUE].slope; + l[8].x = m_Cuboid[2].x; + l[8].y = m_Cuboid[2].y; + l[8].c = FindC(l[8]); + + m_Cuboid[5] = Intersection(l[7],l[8]); + +} + +void CDialogColorPicker::SetSpinVals() +{ + ((CSpinButtonCtrl *)GetDlgItem(IDC_SPIN_RED))->SetRange(0,255); + ((CSpinButtonCtrl *)GetDlgItem(IDC_SPIN_GREEN))->SetRange(0,255); + ((CSpinButtonCtrl *)GetDlgItem(IDC_SPIN_BLUE))->SetRange(0,255); + + ((CSpinButtonCtrl *)GetDlgItem(IDC_SPIN_HUE))->SetRange(0,360); + ((CSpinButtonCtrl *)GetDlgItem(IDC_SPIN_SAT))->SetRange(0,255); + ((CSpinButtonCtrl *)GetDlgItem(IDC_SPIN_VAL))->SetRange(0,255); + + ((CSpinButtonCtrl *)GetDlgItem(IDC_SPIN_OVERBRIGHT))->SetRange(0,1023); + +} + +void CDialogColorPicker::SetEditVals() +{ + ((CSpinButtonCtrl *)GetDlgItem(IDC_SPIN_RED))->SetPos(color.r); + ((CSpinButtonCtrl *)GetDlgItem(IDC_SPIN_GREEN))->SetPos(color.g); + ((CSpinButtonCtrl *)GetDlgItem(IDC_SPIN_BLUE))->SetPos(color.b); + + ((CSpinButtonCtrl *)GetDlgItem(IDC_SPIN_HUE))->SetPos(hsvColor.h); + ((CSpinButtonCtrl *)GetDlgItem(IDC_SPIN_SAT))->SetPos(hsvColor.s); + ((CSpinButtonCtrl *)GetDlgItem(IDC_SPIN_VAL))->SetPos(hsvColor.v); + +} + +void CDialogColorPicker::OnChangeEditBlue() +{ + int b; + + b = GetDlgItemInt(IDC_EDIT_BLUE); + if( b != color.b && m_bInitOver) + { + color.b = b; + if(color.b < 0) color.b = 0; + if(color.b > 255) color.b = 255; + hsvColor = color.toHSV(); + DrawAll(); + } +} + +void CDialogColorPicker::OnChangeEditGreen() +{ + int g; + + g = GetDlgItemInt(IDC_EDIT_GREEN); + if(g != color.g && m_bInitOver) + { + color.g = g; + if(color.g < 0) color.g = 0; + if(color.g > 255) color.g = 255; + hsvColor = color.toHSV(); + DrawAll(); + } +} + +void CDialogColorPicker::OnChangeEditRed() +{ + int r; + + r = GetDlgItemInt(IDC_EDIT_RED); + if(r != color.r && m_bInitOver) + { + color.r = r; + if(color.r < 0) color.r = 0; + if(color.r > 255) color.r = 255; + hsvColor = color.toHSV(); + DrawAll(); + } +} + +void CDialogColorPicker::OnChangeEditHue() +{ + int h; + + h = GetDlgItemInt(IDC_EDIT_HUE); + if(h != hsvColor.h && m_bInitOver) + { + hsvColor.h = h; + if(hsvColor.h < 0) hsvColor.h = 0; + if(hsvColor.h > 359) hsvColor.h = 359; + color = hsvColor.toRGB(); + DrawAll(); + } +} + +void CDialogColorPicker::OnChangeEditSat() +{ + int s; + + s = GetDlgItemInt(IDC_EDIT_SAT); + if(s != hsvColor.s && m_bInitOver) + { + hsvColor.s = s; + if(hsvColor.s < 0) hsvColor.s = 0; + if(hsvColor.s > 255) hsvColor.s = 255; + color = hsvColor.toRGB(); + DrawAll(); + } +} + +void CDialogColorPicker::OnChangeEditVal() +{ + int v; + + v = GetDlgItemInt(IDC_EDIT_VAL); + if(v != hsvColor.v && m_bInitOver) + { + hsvColor.v = v; + if(hsvColor.v < 0) hsvColor.v = 0; + if(hsvColor.v > 255) hsvColor.v = 255; + color = hsvColor.toRGB(); + DrawAll(); + } +} + +void CDialogColorPicker::OnChangeEditOverbright() { + CString str; + GetDlgItemText(IDC_EDIT_OVERBRIGHT, str); + if(m_bInitOver) { + overBright = atof(str); + } +} + +void CDialogColorPicker::DrawAll() +{ + if(m_bInitOver && !m_bInDrawAll) + { + CClientDC dc(this); + + DrawMarkers(&dc); + DrawLines(&dc); + m_bInDrawAll = TRUE; + CalcCuboid(); + CalcRects(); + SetDIBPalette(); + DrawRGB(&dc); + DrawHSB(&dc); + SetEditVals(); + m_bInDrawAll = FALSE; + } +} + +void CDialogColorPicker::DrawFilledColor(CDC *pDC,CRect cr,COLORREF c) +{ + pDC->FillSolidRect(&cr,c); + pDC->Draw3dRect(&cr,RGB(0,0,0),RGB(0,0,0)); + cr.InflateRect(-1,-1); + pDC->Draw3dRect(&cr,RGB(192,192,192),RGB(128,128,128)); +} + +void CDialogColorPicker::LoadMappedBitmap(CBitmap& bitmap,UINT nIdResource,CSize& size) +{ + CBitmap *pOldBitmap; + + if(bitmap.GetSafeHandle()) bitmap.DeleteObject(); + + if(bitmap.LoadBitmap(nIdResource)) + { + + int width,height; + BITMAP bmInfo; + + ::GetObject(bitmap.m_hObject,sizeof(bmInfo),&bmInfo); + width = bmInfo.bmWidth; + height = bmInfo.bmHeight; + + COLORREF colorWindow = ::GetSysColor(COLOR_3DFACE); + COLORREF sourceColor = RGB(192,192,192); + + pOldBitmap = (CBitmap *)memDC.SelectObject(&bitmap); + + int i,j; + + for(i=0; i < height; i++) + { + for(j=0; j < width; j++) + { + if(memDC.GetPixel(j,i) == sourceColor) + { + memDC.SetPixel(j,i,colorWindow); + } + } + } + + memDC.SelectObject(&pOldBitmap); + size = CSize(width,height); + } +} + +void CDialogColorPicker::OnSysColorChange() +{ + CSize size; + LoadMappedBitmap(m_HsbBitmap,IDB_BITMAP_HSB,size); + LoadMappedBitmap(m_RgbBitmap,IDB_BITMAP_RGB,size); +} + +void CDialogColorPicker::OnTimer(UINT nIDEvent) { + if ( UpdateParent ) { + UpdateParent( color.r, color.g, color.b, 1.0f ); + } +} + +void CDialogColorPicker::OnBtnColor() { + COLORREF cr = DoOldColor(GetColor()); + color.r = GetRValue(cr); + color.g = GetGValue(cr); + color.b = GetBValue(cr); + hsvColor = color.toHSV(); + DrawAll(); +} + +bool DoNewColor( int* i1, int* i2, int* i3, float *overBright, void (*Update)( float, float, float, float ) ) { + COLORREF cr = (*i1) + ((*i2) <<8) + ((*i3) <<16); + CDialogColorPicker dlg( cr ); + //CMyColorDialog dlg(cr, CC_FULLOPEN | CC_RGBINIT | CC_ANYCOLOR); + + dlg.UpdateParent = Update; + + if ( dlg.DoModal() == IDOK ) { + *i1 = (dlg.GetColor() & 255); + *i2 = ((dlg.GetColor() >> 8) & 255); + *i3 = ((dlg.GetColor() >> 16) & 255); + *overBright = dlg.GetOverBright(); + return true; + } + return false; +} diff --git a/src/tools/comafx/DialogColorPicker.h b/src/tools/comafx/DialogColorPicker.h new file mode 100644 index 0000000..563b737 --- /dev/null +++ b/src/tools/comafx/DialogColorPicker.h @@ -0,0 +1,193 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef __DIALOGCOLORPICKER__ +#define __DIALOGCOLORPICKER__ + +// Original ColorPicker/DIB source by Rajiv Ramachandran +// included with permission from the author + +#include "CDib.h" + +#define RADIUS 100 +#define PI 3.14159265358 + +#define RECT_WIDTH 5 + +#define TOSCALE(x) (((x)*RADIUS)/255.0) +#define SCALETOMAX(x) (((x)*255.0)/RADIUS) + + +#define RED 0 +#define GREEN 1 +#define BLUE 2 + +#define BAD_SLOPE 1000000.0 + + +struct HSVType; + +struct RGBType { + COLORREF color() { return RGB( r, g, b ); } + HSVType toHSV(); + int r, g, b; +}; + +struct HSVType { + RGBType toRGB(); + int h, s, v; +}; + +struct LineDesc { + double x, y; + double slope; + double c; +}; + + +class CDialogColorPicker : public CDialog +{ +// Construction +public: + CDialogColorPicker(COLORREF c,CWnd* pParent = NULL); // standard constructor + ~CDialogColorPicker(); + + COLORREF GetColor() { return color.color();}; + float GetOverBright() { return overBright; }; + + + // Dialog Data + //{{AFX_DATA(CDialogColorPicker) + enum { IDD = IDD_DIALOG_COLORS }; + float m_overBright; + //}}AFX_DATA + + void (*UpdateParent)( float r, float g, float b, float a ); + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CDialogColorPicker) + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CDialogColorPicker) + afx_msg void OnLButtonDown(UINT nFlags, CPoint point); + afx_msg void OnLButtonUp(UINT nFlags, CPoint point); + afx_msg void OnMouseMove(UINT nFlags, CPoint point); + afx_msg void OnSysColorChange(); + afx_msg void OnPaint(); + virtual BOOL OnInitDialog(); + afx_msg void OnChangeEditBlue(); + afx_msg void OnChangeEditGreen(); + afx_msg void OnChangeEditHue(); + afx_msg void OnChangeEditRed(); + afx_msg void OnChangeEditSat(); + afx_msg void OnChangeEditVal(); + afx_msg void OnChangeEditOverbright(); + afx_msg void OnTimer(UINT nIDEvent); + afx_msg void OnBtnColor(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + + void DrawFilledColor(CDC *pDC,CRect cr,COLORREF c); + void DrawLines(CDC *pDC); + void DrawXorRect(CDC *pDC,CRect& cr); + void CalcSlopes(); + void CalcCuboid(); + + void CreateBrightDIB(); + void SetDIBPalette(); + void DrawMarkers(CDC *pDC); + void TrackPoint(CPoint pt); + void CalcRects(); + + BOOL InCircle(CPoint pt); + BOOL InBright(CPoint pt); + BOOL InOverBright(CPoint pt); + + + void SetSpinVals(); + void SetEditVals(); + void DrawAll(); + + void DrawRGB(CDC *pDC); + void DrawHSB(CDC *pDC); + + void LoadMappedBitmap(CBitmap& bitmap,UINT nIdResource,CSize& size); + + CBitmap m_RgbBitmap,m_HsbBitmap; + + CDC memDC; + CPoint m_Centre; + CDIB m_BrightDIB; + + int rgbWidth; + int rgbHeight; + int hsbWidth; + int hsbHeight; + + int m_nMouseIn; + CRect m_CurrentRect,brightMark; + CRect brightRect; + CRect overBrightRect; + + HSVType hsvColor; + + RGBType color; + RGBType m_OldColor; + CPoint Vertex; + CPoint Top; + CPoint Left; + CPoint Right; + CRect rects[3]; + CPoint m_Cuboid[8]; + BOOL m_bInMouse; + int nIndex; + int RedLen; + int GreenLen; + int BlueLen; + LineDesc lines[3]; + + + CRect rgbRect; + CRect hsbRect; + CRect OldColorRect; + CRect NewColorRect; + + BOOL m_bInitOver; + BOOL m_bInDrawAll; + + float overBright; +}; + +bool DoNewColor( int* i1, int* i2, int* i3, float *overBright, void (*Update)( float, float, float, float ) = NULL ); + +#endif /* !__DIALOGCOLORPICKER__ */ diff --git a/src/tools/comafx/DialogGoToLine.cpp b/src/tools/comafx/DialogGoToLine.cpp new file mode 100644 index 0000000..01f4899 --- /dev/null +++ b/src/tools/comafx/DialogGoToLine.cpp @@ -0,0 +1,134 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "../../sys/win32/rc/Common_resource.h" + +#include "DialogGoToLine.h" + +#ifdef ID_DEBUG_MEMORY +#undef new +#undef DEBUG_NEW +#define DEBUG_NEW new +#endif + + +IMPLEMENT_DYNAMIC(DialogGoToLine, CDialog) + +/* +================ +DialogGoToLine::DialogGoToLine +================ +*/ +DialogGoToLine::DialogGoToLine( CWnd* pParent /*=NULL*/ ) + : CDialog(DialogGoToLine::IDD, pParent) + , firstLine(0) + , lastLine(0) + , line(0) +{ +} + +/* +================ +DialogGoToLine::~DialogGoToLine +================ +*/ +DialogGoToLine::~DialogGoToLine() { +} + +/* +================ +DialogGoToLine::DoDataExchange +================ +*/ +void DialogGoToLine::DoDataExchange(CDataExchange* pDX) { + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(DialogGoToLine) + DDX_Control( pDX, IDC_GOTOLINE_EDIT, numberEdit); + //}}AFX_DATA_MAP +} + +/* +================ +DialogGoToLine::SetRange +================ +*/ +void DialogGoToLine::SetRange( int firstLine, int lastLine ) { + this->firstLine = firstLine; + this->lastLine = lastLine; +} + +/* +================ +DialogGoToLine::GetLine +================ +*/ +int DialogGoToLine::GetLine( void ) const { + return line; +} + +/* +================ +DialogGoToLine::OnInitDialog +================ +*/ +BOOL DialogGoToLine::OnInitDialog() { + + CDialog::OnInitDialog(); + + GetDlgItem( IDC_GOTOLINE_STATIC )->SetWindowText( va( "&Line number (%d - %d):", firstLine, lastLine ) ); + + numberEdit.SetWindowText( va( "%d", firstLine ) ); + numberEdit.SetSel( 0, -1 ); + numberEdit.SetFocus(); + + return FALSE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + + +BEGIN_MESSAGE_MAP(DialogGoToLine, CDialog) + ON_BN_CLICKED(IDOK, OnBnClickedOk) +END_MESSAGE_MAP() + + +// DialogGoToLine message handlers + +/* +================ +DialogGoToLine::OnBnClickedOk +================ +*/ +void DialogGoToLine::OnBnClickedOk() { + CString text; + numberEdit.GetWindowText( text ); + line = idMath::ClampInt( firstLine, lastLine, atoi( text ) ); + OnOK(); +} diff --git a/src/tools/comafx/DialogGoToLine.h b/src/tools/comafx/DialogGoToLine.h new file mode 100644 index 0000000..b418c8c --- /dev/null +++ b/src/tools/comafx/DialogGoToLine.h @@ -0,0 +1,63 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef __DIALOGGOTOLINE_H__ +#define __DIALOGGOTOLINE_H__ + +// DialogGoToLine dialog + +class DialogGoToLine : public CDialog { + + DECLARE_DYNAMIC(DialogGoToLine) + +public: + + DialogGoToLine( CWnd* pParent = NULL ); // standard constructor + virtual ~DialogGoToLine(); + + enum { IDD = IDD_DIALOG_GOTOLINE }; + + void SetRange( int firstLine, int lastLine ); + int GetLine( void ) const; + +protected: + virtual BOOL OnInitDialog(); + virtual void DoDataExchange( CDataExchange* pDX ); // DDX/DDV support + afx_msg void OnBnClickedOk(); + + DECLARE_MESSAGE_MAP() + +private: + + CEdit numberEdit; + int firstLine; + int lastLine; + int line; +}; + +#endif /* !__DIALOGGOTOLINE_H__ */ diff --git a/src/tools/comafx/DialogName.cpp b/src/tools/comafx/DialogName.cpp new file mode 100644 index 0000000..d349318 --- /dev/null +++ b/src/tools/comafx/DialogName.cpp @@ -0,0 +1,78 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "../../sys/win32/rc/common_resource.h" +#include "DialogName.h" + +///////////////////////////////////////////////////////////////////////////// +// DialogName dialog + + +DialogName::DialogName(const char *pName, CWnd* pParent /*=NULL*/) + : CDialog(DialogName::IDD, pParent) +{ + //{{AFX_DATA_INIT(DialogName) + m_strName = _T(""); + //}}AFX_DATA_INIT + m_strCaption = pName; +} + + +void DialogName::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(DialogName) + DDX_Text(pDX, IDC_TOOLS_EDITNAME, m_strName); + //}}AFX_DATA_MAP +} + +BOOL DialogName::OnInitDialog() +{ + CDialog::OnInitDialog(); + + SetWindowText(m_strCaption); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +BEGIN_MESSAGE_MAP(DialogName, CDialog) + //{{AFX_MSG_MAP(DialogName) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// DialogName message handlers + +void DialogName::OnOK() +{ + CDialog::OnOK(); +} diff --git a/src/tools/comafx/DialogName.h b/src/tools/comafx/DialogName.h new file mode 100644 index 0000000..0b2a85b --- /dev/null +++ b/src/tools/comafx/DialogName.h @@ -0,0 +1,76 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef __DIALOGNAME_H__ +#define __DIALOGNAME_H__ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// NameDlg.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// DialogName dialog + +class DialogName : public CDialog +{ + CString m_strCaption; +// Construction +public: + DialogName(const char *pName, CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(DialogName) + enum { IDD = IDD_NEWNAME }; + CString m_strName; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(DialogName) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(DialogName) + virtual BOOL OnInitDialog(); + virtual void OnOK(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif /* !__DIALOGNAME_H__ */ diff --git a/src/tools/comafx/StdAfx.cpp b/src/tools/comafx/StdAfx.cpp new file mode 100644 index 0000000..85e9cdb --- /dev/null +++ b/src/tools/comafx/StdAfx.cpp @@ -0,0 +1,382 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "../../sys/win32/win_local.h" + +// source file that includes just the standard includes +// Radiant.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +/* +=============================================================================== + + Afx initialization. + +=============================================================================== +*/ + +bool afxInitialized = false; + +/* +================ +InitAfx +================ +*/ +void InitAfx( void ) { + if ( !afxInitialized ) { + AfxWinInit( win32.hInstance, NULL, "", SW_SHOW ); + AfxInitRichEdit(); + afxInitialized = true; + } +} + + +/* +=============================================================================== + + Tool Tips. + +=============================================================================== +*/ + +/* +================ +DefaultOnToolHitTest +================ +*/ +int DefaultOnToolHitTest( const toolTip_t *toolTips, const CDialog *dialog, CPoint point, TOOLINFO* pTI ) { + CWnd *wnd; + RECT clientRect, rect; + + dialog->GetWindowRect( &clientRect ); + point.x += clientRect.left; + point.y += clientRect.top; + for ( int i = 0; toolTips[i].tip; i++ ) { + wnd = dialog->GetDlgItem( toolTips[i].id ); + if ( !( wnd->GetStyle() & WS_VISIBLE ) ) { + continue; + } + wnd->GetWindowRect( &rect ); + if ( point.x >= rect.left && point.x <= rect.right && point.y >= rect.top && point.y <= rect.bottom ) { + pTI->hwnd = dialog->GetSafeHwnd(); + pTI->uFlags |= TTF_IDISHWND; + pTI->uFlags &= ~TTF_CENTERTIP; + pTI->uId = (UINT_PTR) wnd->GetSafeHwnd(); + return pTI->uId; + } + } + return -1; +} + +/* +================ +DefaultOnToolTipNotify +================ +*/ +BOOL DefaultOnToolTipNotify( const toolTip_t *toolTips, UINT id, NMHDR *pNMHDR, LRESULT *pResult ) { + // need to handle both ANSI and UNICODE versions of the message + TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR; + TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR; + + *pResult = 0; + + UINT nID = pNMHDR->idFrom; + if ( pTTTA->uFlags & TTF_IDISHWND ) { + // idFrom is actually the HWND of the tool + nID = ::GetDlgCtrlID((HWND)nID); + } + + int i; + for ( i = 0; toolTips[i].tip; i++ ) { + if ( toolTips[i].id == nID ) { + break; + } + } + + if ( !toolTips[i].tip ) { + return FALSE; + } + + if ( pNMHDR->code == TTN_NEEDTEXTA ) { + lstrcpyn( pTTTA->szText, toolTips[i].tip, sizeof(pTTTA->szText) ); + } else { + _mbstowcsz( pTTTW->szText, toolTips[i].tip, sizeof(pTTTW->szText) ); + } + return TRUE; +} + + +/* +=============================================================================== + + Common control tools. + +=============================================================================== +*/ + +/* +================ +EditControlEnterHit + + returns true if [Enter] was hit in the edit box + all 'return' characters in the text are removed and a single line is maintained + the edit control must be multi-line with auto-vscroll +================ +*/ +bool EditControlEnterHit( CEdit *edit ) { + CString strIn, strOut; + if ( edit->GetLineCount() > 1 ) { + edit->GetWindowText( strIn ); + for ( int i = 0; i < strIn.GetLength(); i++ ) { + if ( strIn[i] >= ' ' ) { + strOut.AppendChar( strIn[i] ); + } + } + edit->SetWindowText( strOut ); + edit->SetSel( 0, strOut.GetLength() ); + return true; + } + return false; +} + +/* +================ +EditVerifyFloat +================ +*/ +float EditVerifyFloat( CEdit *edit, bool allowNegative ) { + + CString strIn, strOut; + bool dot = false; + int start, end; + + edit->GetSel( start, end ); + edit->GetWindowText( strIn ); + for ( int i = 0; i < strIn.GetLength(); i++ ) { + // first character may be a minus sign + if ( allowNegative && strOut.GetLength() == 0 && strIn[i] == '-' ) { + strOut.AppendChar( '-' ); + } + // the string may contain one dot + else if ( !dot && strIn[i] == '.' ) { + strOut.AppendChar( strIn[i] ); + dot = true; + } + else if ( strIn[i] >= '0' && strIn[i] <= '9' ) { + strOut.AppendChar( strIn[i] ); + } + } + edit->SetWindowText( strOut ); + edit->SetSel( start, end ); + + return atof(strOut.GetBuffer(0)); + +} + +/* +================ +SpinFloatString +================ +*/ +void SpinFloatString( CString &str, bool up ) { + int i, dotIndex = -1, digitIndex = -1; + + for ( i = 0; str[i]; i++ ) { + if ( str[i] == '.' ) { + if ( dotIndex == -1 ) { + dotIndex = i; + } + } + else if ( str[i] != '0' ) { + if ( digitIndex == -1 ) { + digitIndex = i; + } + } + } + if ( digitIndex == -1 ) { + str.SetString( "1" ); + return; + } + + if ( dotIndex != -1 ) { + str.Delete( dotIndex, 1 ); + if ( digitIndex > dotIndex ) { + digitIndex--; + } + } + else { + dotIndex = i; + } + + if ( up ) { + if ( str[digitIndex] == '9' ) { + str.SetAt( digitIndex, '0' ); + if ( digitIndex == 0 ) { + str.Insert( 0, '1' ); + dotIndex++; + } + else { + str.SetAt( digitIndex-1, '1' ); + } + } + else { + str.SetAt( digitIndex, str[digitIndex] + 1 ); + } + } + else { + if ( str[digitIndex] == '1' ) { + if ( str[digitIndex+1] == '\0' ) { + str.SetAt( digitIndex, '0' ); + str.AppendChar( '9' ); + } + else if ( str[digitIndex+1] == '0' ) { + str.SetAt( digitIndex, '0' ); + str.SetAt( digitIndex+1, '9' ); + } + else { + str.SetAt( digitIndex+1, str[digitIndex+1] - 1 ); + } + } + else { + str.SetAt( digitIndex, str[digitIndex] - 1 ); + } + } + if ( dotIndex < str.GetLength() ) { + str.Insert( dotIndex, '.' ); + // remove trailing zeros + for ( i = str.GetLength()-1; i >= 0; i-- ) { + if ( str[i] != '0' && str[i] != '.' ) { + break; + } + } + if ( i < str.GetLength() - 1 ) { + str.Delete( i+1, str.GetLength() - i ); + } + } + for ( i = 0; str[i]; i++ ) { + if ( str[i] == '.' ) { + if ( i > 1 ) { + str.Delete( 0, i-1 ); + } + break; + } + if ( str[i] != '0' ) { + if ( i > 0 ) { + str.Delete( 0, i ); + } + break; + } + } +} + +/* +================ +EditSpinFloat +================ +*/ +float EditSpinFloat( CEdit *edit, bool up ) { + CString str; + + edit->GetWindowText( str ); + SpinFloatString( str, up ); + edit->SetWindowText( str ); + return atof( str ); +} + +/* +================ +SetSafeComboBoxSelection +================ +*/ +int SetSafeComboBoxSelection( CComboBox *combo, const char *string, int skip ) { + int index; + + index = combo->FindString( -1, string ); + if ( index == -1 ) { + index = 0; + } + if ( combo->GetCount() != 0 ) { + if ( index == skip ) { + index = ( skip + 1 ) % combo->GetCount(); + } + combo->SetCurSel( index ); + } + + return index; +} + +/* +================ +GetComboBoxSelection +================ +*/ +int GetSafeComboBoxSelection( CComboBox *combo, CString &string, int skip ) { + int index; + + index = combo->GetCurSel(); + if ( index == CB_ERR ) { + index = 0; + } + if ( combo->GetCount() != 0 ) { + if ( index == skip ) { + index = ( skip + 1 ) % combo->GetCount(); + } + combo->GetLBText( index, string ); + } + else { + string = ""; + } + + return index; +} + +/* +================ +UnsetSafeComboBoxSelection +================ +*/ +int UnsetSafeComboBoxSelection( CComboBox *combo, CString &string ) { + int skip, index; + + skip = combo->FindString( -1, string ); + index = combo->GetCurSel(); + if ( index == CB_ERR ) { + index = 0; + } + if ( combo->GetCount() != 0 ) { + if ( index == skip ) { + index = ( skip + 1 ) % combo->GetCount(); + } + combo->SetCurSel( index ); + } + + return index; +} diff --git a/src/tools/comafx/StdAfx.h b/src/tools/comafx/StdAfx.h new file mode 100644 index 0000000..936b3c7 --- /dev/null +++ b/src/tools/comafx/StdAfx.h @@ -0,0 +1,76 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef __AFX_STDAFX_H__ +#define __AFX_STDAFX_H__ + +// Match the Win32 API level used by the original Quake 4 tools. This must +// precede MFC so the current Windows SDK remains compatible with the legacy +// DirectX SDK headers used by the engine. +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0501 +#endif +#ifndef WINVER +#define WINVER 0x0501 +#endif + +// include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently + +//#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers + +#include // MFC core and standard components +#include // MFC extensions +#include // MFC OLE automation classes +#ifndef _AFX_NO_AFXCMN_SUPPORT +#include // MFC support for Windows Common Controls +#endif // _AFX_NO_AFXCMN_SUPPORT + +void InitAfx( void ); + +// tool tips +typedef struct toolTip_s { + int id; + char *tip; +} toolTip_t; + +int DefaultOnToolHitTest( const toolTip_t *toolTips, const CDialog *dialog, CPoint point, TOOLINFO* pTI ); +BOOL DefaultOnToolTipNotify( const toolTip_t *toolTips, UINT id, NMHDR *pNMHDR, LRESULT *pResult ); + +// edit control +bool EditControlEnterHit( CEdit *edit ); +float EditVerifyFloat( CEdit *edit, bool allowNegative = true ); +float EditSpinFloat( CEdit *edit, bool up ); + +// combo box +int SetSafeComboBoxSelection( CComboBox *combo, const char *string, int skip ); +int GetSafeComboBoxSelection( CComboBox *combo, CString &string, int skip ); +int UnsetSafeComboBoxSelection( CComboBox *combo, CString &string ); + +#endif /* !__AFX_STDAFX_H__ */ diff --git a/src/tools/comafx/VectorCtl.cpp b/src/tools/comafx/VectorCtl.cpp new file mode 100644 index 0000000..d67ba26 --- /dev/null +++ b/src/tools/comafx/VectorCtl.cpp @@ -0,0 +1,424 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#include "../../idlib/precompiled.h" +#pragma hdrstop + + +#include "VectorCtl.h" +#include + +BEGIN_MESSAGE_MAP(CVectorCtl, CButton) + //{{AFX_MSG_MAP(idGLWidget) + ON_WM_LBUTTONDOWN() + ON_WM_LBUTTONUP() + ON_WM_MOUSEMOVE() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +CVectorCtl::CVectorCtl () : + m_bBmpCreated (FALSE), + m_bImageChange (TRUE), + m_bBackgroundBitmapUsed (FALSE), + m_clrDiffuse (DEFAULT_DIFFUSE), + m_clrAmbient (DEFAULT_AMBIENT), + m_clrLight (DEFAULT_LIGHT), + m_clrBackgroundStart (DEFAULT_START_BACKGROUND_COLOR), + m_clrBackgroundEnd (DEFAULT_END_BACKGROUND_COLOR), + m_dSpecularExponent (DEFAULT_SPEC_EXP), + m_bHasFocus (FALSE), + m_bSelected (FALSE), + m_bFrontVector (FALSE), + m_dSensitivity (20.0), + m_procVectorChanging (NULL), + m_procVectorChanged (NULL) +{ + double DefaultVec[3] = DEFAULT_VEC; + for (int i=0; i<3; i++) { + m_dVec[i] = DefaultVec[i]; + pCtl[i] = NULL; + } + + rotationQuat.Set( 0.0f, 0.0f, 0.0f, 1.0f ); + lastPress.Zero(); + radius = 0.6f; +} + + +CVectorCtl::~CVectorCtl () +{ + if (m_bBmpCreated) + m_dcMem.SelectObject (m_pOldBitmap); + ClearBackgroundBitmap (); +} + +// Owner-drawn control service function: +void CVectorCtl::DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct ) +{ + CDC *pDC = CDC::FromHandle (lpDrawItemStruct->hDC); // Get CDC to draw + + if (!m_bSelected && lpDrawItemStruct->itemState & ODS_SELECTED) { + // Just got re-selected (user starts a new mouse dragging session) + } else if (m_bSelected && // Last state was selected + !(lpDrawItemStruct->itemState & ODS_SELECTED) && // New state is NOT selected + (lpDrawItemStruct->itemState & ODS_FOCUS) && // New state is still in focus + m_procVectorChanged) // User asked for a callback + // User has left the track-ball and asked for a callback. + m_procVectorChanged ( rotationQuat ); + + m_bHasFocus = lpDrawItemStruct->itemState & ODS_FOCUS; // Update focus status + m_bSelected = lpDrawItemStruct->itemState & ODS_SELECTED; // Update selection status + + if (!m_bBmpCreated) // 1st time + InitBitmap (lpDrawItemStruct, pDC); + if (m_bImageChange) { // Image has changes - recalc it! + if (m_procVectorChanging) // User has specified a callback + m_procVectorChanging ( rotationQuat ); // Call it! + BuildImage (lpDrawItemStruct); + m_bImageChange = FALSE; + } + pDC->BitBlt (0,0,m_iWidth, m_iHeight, &m_dcMem, 0, 0, SRCCOPY); // Update screen +} + +// Mouse was dragged +void CVectorCtl::OnMouseDrag (int ixMove, int iyMove) +{ + RotateByXandY (double(-iyMove) / m_dSensitivity, + double(ixMove) / m_dSensitivity); +} + +// Recalc ball image +void CVectorCtl::BuildImage (LPDRAWITEMSTRUCT lpDrawItemStruct) +{ + int xf, yf; + + for (int x=0; x EPS) { + Norm = sqrt (Norm); + m_dVec[0] /= Norm; + m_dVec[1] /= Norm; + m_dVec[2] /= Norm; + return TRUE; + } else { // Reset to defualt vector + double DefaultVec[3] = DEFAULT_VEC; + for (int i=0; i<3; i++) + m_dVec[i] = DefaultVec[i]; + return FALSE; + } +} + +// Calculate lightning effect for specific pixel on ball's surface +COLORREF CVectorCtl::CalcLight (double dx, double dy, double dz) +{ + double NL = dx * m_dVec[0] + dy * m_dVec[1] + dz * m_dVec[2], + RV = 2.0 * NL, + rx = m_dVec[0] - (dx * RV), + ry = m_dVec[1] - (dy * RV), + rz = m_dVec[2] - (dz * RV); + + if (NL < 0.0) // Diffuse coefficient + NL = 0.0; + + RV = max (0.0, -rz); + RV = double(pow (RV, m_dSpecularExponent)); + + int r = int ( double(GetRValue(m_clrDiffuse)) * NL + // Diffuse + double(GetRValue(m_clrLight)) * RV + // Specular + double(GetRValue(m_clrAmbient))), // Ambient + + g = int ( double(GetGValue(m_clrDiffuse)) * NL + // Diffuse + double(GetGValue(m_clrLight)) * RV + // Specular + double(GetGValue(m_clrAmbient))), // Ambient + + b = int ( double(GetBValue(m_clrDiffuse)) * NL + // Diffuse + double(GetBValue(m_clrLight)) * RV + // Specular + double(GetBValue(m_clrAmbient))); // Ambient + + r = min (255, r); // Cutoff highlight + g = min (255, g); + b = min (255, b); + return RGB(BYTE(r),BYTE(g),BYTE(b)); +} + + +// Start memory buffer bitmap and measure it +void CVectorCtl::InitBitmap (LPDRAWITEMSTRUCT lpDrawItemStruct, CDC *pDC) +{ + m_iWidth = lpDrawItemStruct->rcItem.right - lpDrawItemStruct->rcItem.left; + m_iHeight = lpDrawItemStruct->rcItem.bottom - lpDrawItemStruct->rcItem.top; + m_bmpBuffer.CreateCompatibleBitmap (pDC, m_iWidth, m_iHeight); + m_bBmpCreated = TRUE; + m_dcMem.CreateCompatibleDC (pDC); + m_pOldBitmap = m_dcMem.SelectObject (&m_bmpBuffer); + SetRadius (max (min (m_iWidth, m_iHeight) - 2, 0) / 2); + SetCenter (m_iWidth / 2, m_iHeight / 2); + CreateBackground (); +} + +// Set new specular intensity +BOOL CVectorCtl::SetSpecularExponent (double dExp) +{ + if (dExp < 1.0 || dExp > 200.0) + return FALSE; + m_dSpecularExponent = dExp; + Redraw (); + return TRUE; +} + +// Rotate our vector around the X and Y axis +void CVectorCtl::RotateByXandY (double XRot, double YRot) +{ // Angles are in radians + + if (XRot == 0.0 && YRot == 0.0) { + return; + } + + double cx = cos(XRot), + sx = sin(XRot), + cy = cos(YRot), + sy = sin(YRot), + dx = m_dVec[0] * cy + m_dVec[1] * sx * sy + m_dVec[2] * cx * sy, + dy = m_dVec[1] * cx - m_dVec[2] * sx, + dz = -m_dVec[0] * sy + m_dVec[1] * sx * cy + m_dVec[2] * cx * cy; + + if (!m_bFrontVector || dz >= 0.0) { // Vector is bounds free + m_dVec[0] = dx; + m_dVec[1] = dy; + m_dVec[2] = dz; + } else { // Otherwise, do not allow Z to be negative (light shines from behind) + m_dVec[2] = 0.0; + m_dVec[0] = dx; + m_dVec[1] = dy; + Normalize (); + } + Redraw (); +} + + +void CVectorCtl::UpdateAxisControls () +{ + CString cs; + for (int i=0; i<3; i++) + if (pCtl[i]) { + cs.Format ("%+1.5f",m_dVec[i]); + pCtl[i]->SetWindowText (cs); + } +} + +void CVectorCtl::SetAxisControl (int nXCtl, int nYCtl, int nZCtl) +{ + pCtl[0] = GetParent()->GetDlgItem(nXCtl); + pCtl[1] = GetParent()->GetDlgItem(nYCtl); + pCtl[2] = GetParent()->GetDlgItem(nZCtl); +} + +void CVectorCtl::SetRadius (UINT uRadius) +{ + m_iRadius = uRadius; + m_iSqrRadius = m_iRadius * m_iRadius; + CreateBackground (); + Redraw (TRUE); +} + + +void CVectorCtl::SetCenter (UINT uHorizPos, UINT uVertPos) +{ + m_iXCenter = uHorizPos; + m_iYCenter = uVertPos; + CreateBackground (); + Redraw (TRUE); +} + + +void CVectorCtl::SetAxis (double d, int nAxis) +{ + if (fabs(d)>=1.0) { + m_dVec[nAxis]=d > 1.0 ? 1.0 : -1.0; + m_dVec[(nAxis+1) %3]=m_dVec[(nAxis+2) %3]=0.0; + Redraw (); + return; + } + m_dVec[nAxis] = d; + Normalize (); + Redraw (); +} + +void CVectorCtl::SetVector (double dx, double dy, double dz) +{ + m_dVec[0] = dx; + m_dVec[1] = dy; + m_dVec[2] = dz; + Normalize (); + Redraw (); +} + +void CVectorCtl::SetBackgroundColor (COLORREF clrStart, COLORREF clrEnd) +{ + ClearBackgroundBitmap (); + m_clrBackgroundStart = clrStart; + m_clrBackgroundEnd = clrEnd; + CreateBackground (); +} + + +BOOL CVectorCtl::SetBackgroundImage (UINT uBackgroundBitmapID) +{ + if (m_bBackgroundBitmapUsed) { + ClearBackgroundBitmap (); + CreateBackground (); + } + if (!m_bmpBack.LoadBitmap (uBackgroundBitmapID)) + return FALSE; + m_bBackgroundBitmapUsed = TRUE; + CreateBackground (); + return TRUE; +} + +void CVectorCtl::CreateBackground () +{ + if (!m_bBmpCreated) + return; // No image yet + if (!m_bBackgroundBitmapUsed) { // No background used - fill with gradient color + double r = GetRValue (m_clrBackgroundStart), + g = GetGValue (m_clrBackgroundStart), + b = GetBValue (m_clrBackgroundStart), + rd = double (GetRValue (m_clrBackgroundEnd) - r) / double (m_iHeight), + gd = double (GetGValue (m_clrBackgroundEnd) - g) / double (m_iHeight), + bd = double (GetBValue (m_clrBackgroundEnd) - b) / double (m_iHeight); + for (int j=0; j. + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +/***************************************************************************** +* * +* Vector control * +* ---------------- * +* * +* A 3D vector MFC control derived from CButton. * +* Features: * +* - Real-time light rendering on a 3D ball. * +* - Variable ball radius and position. * +* - Supports bitmap background (tiled). * +* - Supports vertical gradient color background (from color to color). * +* - Variable ball color (diffuse), light color and ambient color. * +* - Variable specular intensity. * +* - Supports attached controls (for automatic update). * +* - Variable mouse sensitivity. * +* - Supports front clipping (vector will not have negative Z values). * +* - Supports callback functions for the following events: * +* 1. The trackball has moved (vector is changing). * +* 2. The user dropped the trackball (released left mouse button) * +* i.e., the vector was changed. * +* * +* * +*****************************************************************************/ + +#ifndef _VECTOR_CTL_H +#define _VECTOR_CTL_H + +// Callback pointer prototype: +typedef void (*VectorCtlCallbackProc)( idQuat rotation ); + +// The callback should look like: +// void CALLBACK MyCallBack (double dVecX, double dVecY, double dVecZ); +// or +// static void CALLBACK MyClass::MyCallBack (double dVecX, double dVecY, double dVecZ); + + +class CVectorCtl : public CButton +{ + +#define EPS 1.0e-6 // Epsilon + +#define DEFAULT_VEC {0.00, 0.00, 1.00} // Default start vector +#define DEFAULT_DIFFUSE RGB( 30, 0, 200) // Default diffuse color +#define DEFAULT_AMBIENT RGB( 20, 20, 20) // Default ambient color +#define DEFAULT_LIGHT RGB(200, 200, 200) // Default light color +#define DEFAULT_START_BACKGROUND_COLOR RGB( 0, 0, 0) // Default gradient background start color +#define DEFAULT_END_BACKGROUND_COLOR RGB(140, 0, 120) // Default gradient background end color +#define DEFAULT_SPEC_EXP 25.0 // Default specular intensity +#define VAL_NOT_IN_USE -50000 // Internal use + + +public: + CVectorCtl (); + + virtual ~CVectorCtl (); + + // Owner-drawn control support function + virtual void DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct ); + + // Sets / Gets diffuse (ball) color. + void SetDiffuseColor (COLORREF clr) { m_clrDiffuse = clr; Redraw (); } + COLORREF GetDiffuseColor () { return m_clrDiffuse; } + + // Sets / Gets ambient (background) color. + void SetAmbientColor (COLORREF clr) { m_clrAmbient = clr; Redraw (); } + COLORREF GetAmbientColor () { return m_clrAmbient; } + + // Sets / Gets light color. + void SetLightColor (COLORREF clr) { m_clrLight = clr; Redraw (); } + COLORREF GetLightColor () { return m_clrLight; } + + // Sets background gradient color (from start to finish vertically) + void SetBackgroundColor (COLORREF clrStart, COLORREF clrEnd); + + // Sets a background bitmap (resource ID) + BOOL SetBackgroundImage (UINT uBackgroundBitmapID); + + // Sets / Gets specular intensity + BOOL SetSpecularExponent (double dExp); + double GetSpecularExponent () { return m_dSpecularExponent; } + + // Enables auto-update of axis controls. + // Place the control's ID and the SetWindowText function will be called + // for each vector component to display the value in the control. + void SetAxisControl (int nXCtl, int nYCtl, int nZCtl); + + // Sets / Gets ball radius (in pixels) + void SetRadius (UINT uRadius); + UINT GetRadius () { return UINT(m_iRadius); } + + // Sets / Gets ball position (in pixels) + void SetCenter (UINT uHorizPos, UINT uVertPos); + UINT GetHorizCenter () { return UINT(m_iXCenter); } + UINT GetVertCenter () { return UINT(m_iYCenter); } + + // Sets / Gets vector components + void SetX (double dx) { SetAxis (dx, 0); } + double GetX() { return m_dVec[0]; } + void SetY (double dy) { SetAxis (dy, 1); } + double GetY() { return m_dVec[1]; } + void SetZ (double dz) { SetAxis (dz, 2); } + double GetZ() { return m_dVec[2]; } + void SetVector (double dx, double dy, double dz); + void SetidAxis( const idMat3 &mat ) { + rotationMatrix = mat; + rotationQuat = mat.ToQuat(); + m_dVec = mat[2]; + } + + // Sets / Gets mouse sensitivity + BOOL SetSensitivity (UINT uSens); + UINT GetSensitivity () { return UINT(m_dSensitivity); } + + // Bounds / Unbounds vector to front (positive Z) only + void ClipToFront (BOOL bEnable) { m_bFrontVector = bEnable; } + + // Set user-defined callback function to call whenever the vector has changed. + // Set to NULL to disable callback. + void SetVectorChangingCallback (VectorCtlCallbackProc proc) + { m_procVectorChanging = proc; } + + // Set user-defined callback function to call whenever the vector has finished + // changing (user dropped track-ball). + // Set to NULL to disable callback. + void SetVectorChangedCallback (VectorCtlCallbackProc proc) + { m_procVectorChanged = proc; } + +private: + afx_msg void OnLButtonDown(UINT nFlags, CPoint point); + afx_msg void OnLButtonUp(UINT nFlags, CPoint point); + afx_msg void OnMouseMove(UINT nFlags, CPoint point); + + // Mouse is being dragged + void OnMouseDrag (int , int); + // Create and measure off-screen buffer + void InitBitmap (LPDRAWITEMSTRUCT lpDrawItemStruct, CDC *pDC); + // Build image to BitBlt + void BuildImage (LPDRAWITEMSTRUCT lpDrawItemStruct); + // Free resources of background (non-ball) bitmap + void ClearBackgroundBitmap (); + // Normalize vector + BOOL Normalize (); + // Calculate lightning effect for a pixel on the ball + COLORREF CalcLight (double dx, double dy, double dz); + // Rotate our vector by X and Y angles + void RotateByXandY (double XRot, double YRot); + // Create background image resource + void CreateBackground (); + // Force redraw of entire image + void Redraw (BOOL bErase = FALSE); + // Update user-defined vector components controls + void UpdateAxisControls (); + // Sets a specific vector component to a specific value + void SetAxis (double d, int nAxis); + + CBitmap m_bmpBuffer, // Buffer bitmap for BitBlt + m_bmpBack; // Background image bitmap + CDC m_dcMem; // Memory DC + BOOL m_bBmpCreated, // Was the bitmap created ? + m_bBackgroundBitmapUsed,// Are we using a background bitmap ? + m_bImageChange, // Has the image changed ? + m_bFrontVector, // Is the vector constrained to be facing front (positive Z) ? + m_bHasFocus, // Does the control have the focus ? + m_bSelected; // Is the control selected ? + int m_iWidth, // Region width + m_iHeight, // Region height + m_iRadius, // Ball radius + m_iSqrRadius, // Ball radius to the power of two + m_iXCenter, // X center point + m_iYCenter; // Y center point + CBitmap *m_pOldBitmap; // Previously selected bitmap + COLORREF m_clrDiffuse, // Ball diffusion color (self color) + m_clrAmbient, // Ambient (background) color + m_clrLight, // Color of light + m_clrBackgroundStart, // Background color gradient start + m_clrBackgroundEnd; // Background color gradient end + CWnd *pCtl[3]; // Pointers to axis display controls + double m_dSpecularExponent, // Specularity effect intensity + m_dSensitivity; // The bigger the number the less sensitive the mouse gets + // Valid ranges are 1..MAX_UINT + idVec3 m_dVec; // Vector components + idMat3 rotationMatrix; // + idQuat rotationQuat; + idQuat previousQuat; + idVec3 lastPress; + float radius; + + + VectorCtlCallbackProc m_procVectorChanging, + m_procVectorChanged; + +protected: + DECLARE_MESSAGE_MAP() + + +}; + +#endif diff --git a/src/tools/comafx/riched20.tlh b/src/tools/comafx/riched20.tlh new file mode 100644 index 0000000..53a18b1 --- /dev/null +++ b/src/tools/comafx/riched20.tlh @@ -0,0 +1,865 @@ +// Created by Microsoft (R) C/C++ Compiler Version 14.00.50727.762 (d29fdb1b). +// +// c:\alienbrainwork\rage\build\win32\debug\intermediate\tools\riched20.tlh +// +// C++ source equivalent of Win32 type library riched20.dll +// compiler-generated file created 06/14/07 at 10:58:45 - DO NOT EDIT! + +#pragma once +#pragma pack(push, 8) + +#include + +namespace tom { + +// +// Forward references and typedefs +// + +struct __declspec(uuid("8cc497c9-a1df-11ce-8098-00aa0047be5d")) +/* LIBID */ __tom; +enum __MIDL___MIDL_itf_tom_0000_0001; +struct __declspec(uuid("8cc497c0-a1df-11ce-8098-00aa0047be5d")) +/* dual interface */ ITextDocument; +struct __declspec(uuid("8cc497c1-a1df-11ce-8098-00aa0047be5d")) +/* dual interface */ ITextSelection; +struct __declspec(uuid("8cc497c2-a1df-11ce-8098-00aa0047be5d")) +/* dual interface */ ITextRange; +struct __declspec(uuid("8cc497c3-a1df-11ce-8098-00aa0047be5d")) +/* dual interface */ ITextFont; +struct __declspec(uuid("8cc497c4-a1df-11ce-8098-00aa0047be5d")) +/* dual interface */ ITextPara; +struct __declspec(uuid("8cc497c5-a1df-11ce-8098-00aa0047be5d")) +/* dual interface */ ITextStoryRanges; +struct __declspec(uuid("01c25500-4268-11d1-883a-3c8b00c10000")) +/* dual interface */ ITextDocument2; +struct __declspec(uuid("a3787420-4267-11d1-883a-3c8b00c10000")) +/* interface */ ITextMsgFilter; +struct _RemotableHandle; +union __MIDL_IWinTypes_0009; +typedef enum __MIDL___MIDL_itf_tom_0000_0001 tomConstants; +typedef struct _RemotableHandle * wireHWND; +typedef unsigned long UINT_PTR; +typedef long LONG_PTR; + +// +// Smart pointer typedef declarations +// + +_COM_SMARTPTR_TYPEDEF(ITextFont, __uuidof(ITextFont)); +_COM_SMARTPTR_TYPEDEF(ITextPara, __uuidof(ITextPara)); +_COM_SMARTPTR_TYPEDEF(ITextRange, __uuidof(ITextRange)); +_COM_SMARTPTR_TYPEDEF(ITextSelection, __uuidof(ITextSelection)); +_COM_SMARTPTR_TYPEDEF(ITextStoryRanges, __uuidof(ITextStoryRanges)); +_COM_SMARTPTR_TYPEDEF(ITextDocument, __uuidof(ITextDocument)); +_COM_SMARTPTR_TYPEDEF(ITextDocument2, __uuidof(ITextDocument2)); +_COM_SMARTPTR_TYPEDEF(ITextMsgFilter, __uuidof(ITextMsgFilter)); + +// +// Type library items +// + +enum __MIDL___MIDL_itf_tom_0000_0001 +{ + tomFalse = 0, + tomTrue = -1, + tomUndefined = -9999999, + tomToggle = -9999998, + tomAutoColor = -9999997, + tomDefault = -9999996, + tomSuspend = -9999995, + tomResume = -9999994, + tomApplyNow = 0, + tomApplyLater = 1, + tomTrackParms = 2, + tomCacheParms = 3, + tomBackward = -1073741823, + tomForward = 1073741823, + tomMove = 0, + tomExtend = 1, + tomNoSelection = 0, + tomSelectionIP = 1, + tomSelectionNormal = 2, + tomSelectionFrame = 3, + tomSelectionColumn = 4, + tomSelectionRow = 5, + tomSelectionBlock = 6, + tomSelectionInlineShape = 7, + tomSelectionShape = 8, + tomSelStartActive = 1, + tomSelAtEOL = 2, + tomSelOvertype = 4, + tomSelActive = 8, + tomSelReplace = 16, + tomEnd = 0, + tomStart = 32, + tomCollapseEnd = 0, + tomCollapseStart = 1, + tomClientCoord = 256, + tomNone = 0, + tomSingle = 1, + tomWords = 2, + tomDouble = 3, + tomDotted = 4, + tomDash = 5, + tomDashDot = 6, + tomDashDotDot = 7, + tomWave = 8, + tomThick = 9, + tomHair = 10, + tomLineSpaceSingle = 0, + tomLineSpace1pt5 = 1, + tomLineSpaceDouble = 2, + tomLineSpaceAtLeast = 3, + tomLineSpaceExactly = 4, + tomLineSpaceMultiple = 5, + tomAlignLeft = 0, + tomAlignCenter = 1, + tomAlignRight = 2, + tomAlignJustify = 3, + tomAlignDecimal = 3, + tomAlignBar = 4, + tomAlignInterWord = 3, + tomAlignInterLetter = 4, + tomAlignScaled = 5, + tomAlignGlyphs = 6, + tomAlignSnapGrid = 7, + tomSpaces = 0, + tomDots = 1, + tomDashes = 2, + tomLines = 3, + tomThickLines = 4, + tomEquals = 5, + tomTabBack = -3, + tomTabNext = -2, + tomTabHere = -1, + tomListNone = 0, + tomListBullet = 1, + tomListNumberAsArabic = 2, + tomListNumberAsLCLetter = 3, + tomListNumberAsUCLetter = 4, + tomListNumberAsLCRoman = 5, + tomListNumberAsUCRoman = 6, + tomListNumberAsSequence = 7, + tomListParentheses = 65536, + tomListPeriod = 131072, + tomListPlain = 196608, + tomCharacter = 1, + tomWord = 2, + tomSentence = 3, + tomParagraph = 4, + tomLine = 5, + tomStory = 6, + tomScreen = 7, + tomSection = 8, + tomColumn = 9, + tomRow = 10, + tomWindow = 11, + tomCell = 12, + tomCharFormat = 13, + tomParaFormat = 14, + tomTable = 15, + tomObject = 16, + tomMatchWord = 2, + tomMatchCase = 4, + tomMatchPattern = 8, + tomUnknownStory = 0, + tomMainTextStory = 1, + tomFootnotesStory = 2, + tomEndnotesStory = 3, + tomCommentsStory = 4, + tomTextFrameStory = 5, + tomEvenPagesHeaderStory = 6, + tomPrimaryHeaderStory = 7, + tomEvenPagesFooterStory = 8, + tomPrimaryFooterStory = 9, + tomFirstPageHeaderStory = 10, + tomFirstPageFooterStory = 11, + tomNoAnimation = 0, + tomLasVegasLights = 1, + tomBlinkingBackground = 2, + tomSparkleText = 3, + tomMarchingBlackAnts = 4, + tomMarchingRedAnts = 5, + tomShimmer = 6, + tomWipeDown = 7, + tomWipeRight = 8, + tomAnimationMax = 8, + tomLowerCase = 0, + tomUpperCase = 1, + tomTitleCase = 2, + tomSentenceCase = 4, + tomToggleCase = 5, + tomReadOnly = 256, + tomShareDenyRead = 512, + tomShareDenyWrite = 1024, + tomPasteFile = 4096, + tomCreateNew = 16, + tomCreateAlways = 32, + tomOpenExisting = 48, + tomOpenAlways = 64, + tomTruncateExisting = 80, + tomRTF = 1, + tomText = 2, + tomHTML = 3, + tomWordDocument = 4, + tomBold = -2147483647, + tomItalic = -2147483646, + tomUnderline = -2147483644, + tomStrikeout = -2147483640, + tomProtected = -2147483632, + tomLink = -2147483616, + tomSmallCaps = -2147483584, + tomAllCaps = -2147483520, + tomHidden = -2147483392, + tomOutline = -2147483136, + tomShadow = -2147482624, + tomEmboss = -2147481600, + tomImprint = -2147479552, + tomDisabled = -2147475456, + tomRevised = -2147467264, + tomNormalCaret = 0, + tomKoreanBlockCaret = 1, + tomIncludeInset = 1, + tomIgnoreCurrentFont = 0, + tomMatchFontCharset = 1, + tomMatchFontSignature = 2, + tomCharset = 0x80000000, + tomRE10Mode = 1, + tomNoIME = 524288, + tomSelfIME = 262144 +}; + +struct __declspec(uuid("8cc497c3-a1df-11ce-8098-00aa0047be5d")) +ITextFont : IDispatch +{ + // + // Raw methods provided by interface + // + + virtual HRESULT __stdcall get_Duplicate ( + /*[out,retval]*/ struct ITextFont * * ppFont ) = 0; + virtual HRESULT __stdcall put_Duplicate ( + /*[in]*/ struct ITextFont * ppFont ) = 0; + virtual HRESULT __stdcall CanChange ( + /*[out,retval]*/ long * pB ) = 0; + virtual HRESULT __stdcall IsEqual ( + /*[in]*/ struct ITextFont * pFont, + /*[out,retval]*/ long * pB ) = 0; + virtual HRESULT __stdcall Reset ( + /*[in]*/ long Value ) = 0; + virtual HRESULT __stdcall get_Style ( + /*[out,retval]*/ long * pValue ) = 0; + virtual HRESULT __stdcall put_Style ( + /*[in]*/ long pValue ) = 0; + virtual HRESULT __stdcall get_AllCaps ( + /*[out,retval]*/ long * pValue ) = 0; + virtual HRESULT __stdcall put_AllCaps ( + /*[in]*/ long pValue ) = 0; + virtual HRESULT __stdcall get_Animation ( + /*[out,retval]*/ long * pValue ) = 0; + virtual HRESULT __stdcall put_Animation ( + /*[in]*/ long pValue ) = 0; + virtual HRESULT __stdcall get_BackColor ( + /*[out,retval]*/ long * pValue ) = 0; + virtual HRESULT __stdcall put_BackColor ( + /*[in]*/ long pValue ) = 0; + virtual HRESULT __stdcall get_Bold ( + /*[out,retval]*/ long * pValue ) = 0; + virtual HRESULT __stdcall put_Bold ( + /*[in]*/ long pValue ) = 0; + virtual HRESULT __stdcall get_Emboss ( + /*[out,retval]*/ long * pValue ) = 0; + virtual HRESULT __stdcall put_Emboss ( + /*[in]*/ long pValue ) = 0; + virtual HRESULT __stdcall get_ForeColor ( + /*[out,retval]*/ long * pValue ) = 0; + virtual HRESULT __stdcall put_ForeColor ( + /*[in]*/ long pValue ) = 0; + virtual HRESULT __stdcall get_Hidden ( + /*[out,retval]*/ long * pValue ) = 0; + virtual HRESULT __stdcall put_Hidden ( + /*[in]*/ long pValue ) = 0; + virtual HRESULT __stdcall get_Engrave ( + /*[out,retval]*/ long * pValue ) = 0; + virtual HRESULT __stdcall put_Engrave ( + /*[in]*/ long pValue ) = 0; + virtual HRESULT __stdcall get_Italic ( + /*[out,retval]*/ long * pValue ) = 0; + virtual HRESULT __stdcall put_Italic ( + /*[in]*/ long pValue ) = 0; + virtual HRESULT __stdcall get_Kerning ( + /*[out,retval]*/ float * pValue ) = 0; + virtual HRESULT __stdcall put_Kerning ( + /*[in]*/ float pValue ) = 0; + virtual HRESULT __stdcall get_LanguageID ( + /*[out,retval]*/ long * pValue ) = 0; + virtual HRESULT __stdcall put_LanguageID ( + /*[in]*/ long pValue ) = 0; + virtual HRESULT __stdcall get_Name ( + /*[out,retval]*/ BSTR * pbstr ) = 0; + virtual HRESULT __stdcall put_Name ( + /*[in]*/ BSTR pbstr ) = 0; + virtual HRESULT __stdcall get_Outline ( + /*[out,retval]*/ long * pValue ) = 0; + virtual HRESULT __stdcall put_Outline ( + /*[in]*/ long pValue ) = 0; + virtual HRESULT __stdcall get_Position ( + /*[out,retval]*/ float * pValue ) = 0; + virtual HRESULT __stdcall put_Position ( + /*[in]*/ float pValue ) = 0; + virtual HRESULT __stdcall get_Protected ( + /*[out,retval]*/ long * pValue ) = 0; + virtual HRESULT __stdcall put_Protected ( + /*[in]*/ long pValue ) = 0; + virtual HRESULT __stdcall get_Shadow ( + /*[out,retval]*/ long * pValue ) = 0; + virtual HRESULT __stdcall put_Shadow ( + /*[in]*/ long pValue ) = 0; + virtual HRESULT __stdcall get_Size ( + /*[out,retval]*/ float * pValue ) = 0; + virtual HRESULT __stdcall put_Size ( + /*[in]*/ float pValue ) = 0; + virtual HRESULT __stdcall get_SmallCaps ( + /*[out,retval]*/ long * pValue ) = 0; + virtual HRESULT __stdcall put_SmallCaps ( + /*[in]*/ long pValue ) = 0; + virtual HRESULT __stdcall get_Spacing ( + /*[out,retval]*/ float * pValue ) = 0; + virtual HRESULT __stdcall put_Spacing ( + /*[in]*/ float pValue ) = 0; + virtual HRESULT __stdcall get_StrikeThrough ( + /*[out,retval]*/ long * pValue ) = 0; + virtual HRESULT __stdcall put_StrikeThrough ( + /*[in]*/ long pValue ) = 0; + virtual HRESULT __stdcall get_Subscript ( + /*[out,retval]*/ long * pValue ) = 0; + virtual HRESULT __stdcall put_Subscript ( + /*[in]*/ long pValue ) = 0; + virtual HRESULT __stdcall get_Superscript ( + /*[out,retval]*/ long * pValue ) = 0; + virtual HRESULT __stdcall put_Superscript ( + /*[in]*/ long pValue ) = 0; + virtual HRESULT __stdcall get_Underline ( + /*[out,retval]*/ long * pValue ) = 0; + virtual HRESULT __stdcall put_Underline ( + /*[in]*/ long pValue ) = 0; + virtual HRESULT __stdcall get_Weight ( + /*[out,retval]*/ long * pValue ) = 0; + virtual HRESULT __stdcall put_Weight ( + /*[in]*/ long pValue ) = 0; +}; + +struct __declspec(uuid("8cc497c4-a1df-11ce-8098-00aa0047be5d")) +ITextPara : IDispatch +{ + // + // Raw methods provided by interface + // + + virtual HRESULT __stdcall get_Duplicate ( + /*[out,retval]*/ struct ITextPara * * ppPara ) = 0; + virtual HRESULT __stdcall put_Duplicate ( + /*[in]*/ struct ITextPara * ppPara ) = 0; + virtual HRESULT __stdcall CanChange ( + /*[out,retval]*/ long * pB ) = 0; + virtual HRESULT __stdcall IsEqual ( + /*[in]*/ struct ITextPara * pPara, + /*[out,retval]*/ long * pB ) = 0; + virtual HRESULT __stdcall Reset ( + /*[in]*/ long Value ) = 0; + virtual HRESULT __stdcall get_Style ( + /*[out,retval]*/ long * pValue ) = 0; + virtual HRESULT __stdcall put_Style ( + /*[in]*/ long pValue ) = 0; + virtual HRESULT __stdcall get_Alignment ( + /*[out,retval]*/ long * pValue ) = 0; + virtual HRESULT __stdcall put_Alignment ( + /*[in]*/ long pValue ) = 0; + virtual HRESULT __stdcall get_Hyphenation ( + /*[out,retval]*/ long * pValue ) = 0; + virtual HRESULT __stdcall put_Hyphenation ( + /*[in]*/ long pValue ) = 0; + virtual HRESULT __stdcall get_FirstLineIndent ( + /*[out,retval]*/ float * pValue ) = 0; + virtual HRESULT __stdcall get_KeepTogether ( + /*[out,retval]*/ long * pValue ) = 0; + virtual HRESULT __stdcall put_KeepTogether ( + /*[in]*/ long pValue ) = 0; + virtual HRESULT __stdcall get_KeepWithNext ( + /*[out,retval]*/ long * pValue ) = 0; + virtual HRESULT __stdcall put_KeepWithNext ( + /*[in]*/ long pValue ) = 0; + virtual HRESULT __stdcall get_LeftIndent ( + /*[out,retval]*/ float * pValue ) = 0; + virtual HRESULT __stdcall get_LineSpacing ( + /*[out,retval]*/ float * pValue ) = 0; + virtual HRESULT __stdcall get_LineSpacingRule ( + /*[out,retval]*/ long * pValue ) = 0; + virtual HRESULT __stdcall get_ListAlignment ( + /*[out,retval]*/ long * pValue ) = 0; + virtual HRESULT __stdcall put_ListAlignment ( + /*[in]*/ long pValue ) = 0; + virtual HRESULT __stdcall get_ListLevelIndex ( + /*[out,retval]*/ long * pValue ) = 0; + virtual HRESULT __stdcall put_ListLevelIndex ( + /*[in]*/ long pValue ) = 0; + virtual HRESULT __stdcall get_ListStart ( + /*[out,retval]*/ long * pValue ) = 0; + virtual HRESULT __stdcall put_ListStart ( + /*[in]*/ long pValue ) = 0; + virtual HRESULT __stdcall get_ListTab ( + /*[out,retval]*/ float * pValue ) = 0; + virtual HRESULT __stdcall put_ListTab ( + /*[in]*/ float pValue ) = 0; + virtual HRESULT __stdcall get_ListType ( + /*[out,retval]*/ long * pValue ) = 0; + virtual HRESULT __stdcall put_ListType ( + /*[in]*/ long pValue ) = 0; + virtual HRESULT __stdcall get_NoLineNumber ( + /*[out,retval]*/ long * pValue ) = 0; + virtual HRESULT __stdcall put_NoLineNumber ( + /*[in]*/ long pValue ) = 0; + virtual HRESULT __stdcall get_PageBreakBefore ( + /*[out,retval]*/ long * pValue ) = 0; + virtual HRESULT __stdcall put_PageBreakBefore ( + /*[in]*/ long pValue ) = 0; + virtual HRESULT __stdcall get_RightIndent ( + /*[out,retval]*/ float * pValue ) = 0; + virtual HRESULT __stdcall put_RightIndent ( + /*[in]*/ float pValue ) = 0; + virtual HRESULT __stdcall SetIndents ( + /*[in]*/ float StartIndent, + /*[in]*/ float LeftIndent, + /*[in]*/ float RightIndent ) = 0; + virtual HRESULT __stdcall SetLineSpacing ( + /*[in]*/ long LineSpacingRule, + /*[in]*/ float LineSpacing ) = 0; + virtual HRESULT __stdcall get_SpaceAfter ( + /*[out,retval]*/ float * pValue ) = 0; + virtual HRESULT __stdcall put_SpaceAfter ( + /*[in]*/ float pValue ) = 0; + virtual HRESULT __stdcall get_SpaceBefore ( + /*[out,retval]*/ float * pValue ) = 0; + virtual HRESULT __stdcall put_SpaceBefore ( + /*[in]*/ float pValue ) = 0; + virtual HRESULT __stdcall get_WidowControl ( + /*[out,retval]*/ long * pValue ) = 0; + virtual HRESULT __stdcall put_WidowControl ( + /*[in]*/ long pValue ) = 0; + virtual HRESULT __stdcall get_TabCount ( + /*[out,retval]*/ long * pCount ) = 0; + virtual HRESULT __stdcall AddTab ( + /*[in]*/ float tbPos, + /*[in]*/ long tbAlign, + /*[in]*/ long tbLeader ) = 0; + virtual HRESULT __stdcall ClearAllTabs ( ) = 0; + virtual HRESULT __stdcall DeleteTab ( + /*[in]*/ float tbPos ) = 0; + virtual HRESULT __stdcall GetTab ( + /*[in]*/ long iTab, + /*[out]*/ float * ptbPos, + /*[out]*/ long * ptbAlign, + /*[out]*/ long * ptbLeader ) = 0; +}; + +struct __declspec(uuid("8cc497c2-a1df-11ce-8098-00aa0047be5d")) +ITextRange : IDispatch +{ + // + // Raw methods provided by interface + // + + virtual HRESULT __stdcall get_Text ( + /*[out,retval]*/ BSTR * pbstr ) = 0; + virtual HRESULT __stdcall put_Text ( + /*[in]*/ BSTR pbstr ) = 0; + virtual HRESULT __stdcall get_Char ( + /*[out,retval]*/ long * pch ) = 0; + virtual HRESULT __stdcall put_Char ( + /*[in]*/ long pch ) = 0; + virtual HRESULT __stdcall get_Duplicate ( + /*[out,retval]*/ struct ITextRange * * ppRange ) = 0; + virtual HRESULT __stdcall get_FormattedText ( + /*[out,retval]*/ struct ITextRange * * ppRange ) = 0; + virtual HRESULT __stdcall put_FormattedText ( + /*[in]*/ struct ITextRange * ppRange ) = 0; + virtual HRESULT __stdcall get_Start ( + /*[out,retval]*/ long * pcpFirst ) = 0; + virtual HRESULT __stdcall put_Start ( + /*[in]*/ long pcpFirst ) = 0; + virtual HRESULT __stdcall get_End ( + /*[out,retval]*/ long * pcpLim ) = 0; + virtual HRESULT __stdcall put_End ( + /*[in]*/ long pcpLim ) = 0; + virtual HRESULT __stdcall get_Font ( + /*[out,retval]*/ struct ITextFont * * pFont ) = 0; + virtual HRESULT __stdcall put_Font ( + /*[in]*/ struct ITextFont * pFont ) = 0; + virtual HRESULT __stdcall get_Para ( + /*[out,retval]*/ struct ITextPara * * pPara ) = 0; + virtual HRESULT __stdcall put_Para ( + /*[in]*/ struct ITextPara * pPara ) = 0; + virtual HRESULT __stdcall get_StoryLength ( + /*[out,retval]*/ long * pcch ) = 0; + virtual HRESULT __stdcall get_StoryType ( + /*[out,retval]*/ long * pValue ) = 0; + virtual HRESULT __stdcall Collapse ( + /*[in]*/ long bStart ) = 0; + virtual HRESULT __stdcall Expand ( + /*[in]*/ long Unit, + /*[out,retval]*/ long * pDelta ) = 0; + virtual HRESULT __stdcall GetIndex ( + /*[in]*/ long Unit, + /*[out,retval]*/ long * pIndex ) = 0; + virtual HRESULT __stdcall SetIndex ( + /*[in]*/ long Unit, + /*[in]*/ long Index, + /*[in]*/ long Extend ) = 0; + virtual HRESULT __stdcall SetRange ( + /*[in]*/ long cpActive, + /*[in]*/ long cpOther ) = 0; + virtual HRESULT __stdcall InRange ( + /*[in]*/ struct ITextRange * pRange, + /*[out,retval]*/ long * pB ) = 0; + virtual HRESULT __stdcall InStory ( + /*[in]*/ struct ITextRange * pRange, + /*[out,retval]*/ long * pB ) = 0; + virtual HRESULT __stdcall IsEqual ( + /*[in]*/ struct ITextRange * pRange, + /*[out,retval]*/ long * pB ) = 0; + virtual HRESULT __stdcall Select ( ) = 0; + virtual HRESULT __stdcall StartOf ( + /*[in]*/ long Unit, + /*[in]*/ long Extend, + /*[out,retval]*/ long * pDelta ) = 0; + virtual HRESULT __stdcall EndOf ( + /*[in]*/ long Unit, + /*[in]*/ long Extend, + /*[out,retval]*/ long * pDelta ) = 0; + virtual HRESULT __stdcall Move ( + /*[in]*/ long Unit, + /*[in]*/ long Count, + /*[out,retval]*/ long * pDelta ) = 0; + virtual HRESULT __stdcall MoveStart ( + /*[in]*/ long Unit, + /*[in]*/ long Count, + /*[out,retval]*/ long * pDelta ) = 0; + virtual HRESULT __stdcall MoveEnd ( + /*[in]*/ long Unit, + /*[in]*/ long Count, + /*[out,retval]*/ long * pDelta ) = 0; + virtual HRESULT __stdcall MoveWhile ( + /*[in]*/ VARIANT * Cset, + /*[in]*/ long Count, + /*[out,retval]*/ long * pDelta ) = 0; + virtual HRESULT __stdcall MoveStartWhile ( + /*[in]*/ VARIANT * Cset, + /*[in]*/ long Count, + /*[out,retval]*/ long * pDelta ) = 0; + virtual HRESULT __stdcall MoveEndWhile ( + /*[in]*/ VARIANT * Cset, + /*[in]*/ long Count, + /*[out,retval]*/ long * pDelta ) = 0; + virtual HRESULT __stdcall MoveUntil ( + /*[in]*/ VARIANT * Cset, + /*[in]*/ long Count, + /*[out,retval]*/ long * pDelta ) = 0; + virtual HRESULT __stdcall MoveStartUntil ( + /*[in]*/ VARIANT * Cset, + /*[in]*/ long Count, + /*[out,retval]*/ long * pDelta ) = 0; + virtual HRESULT __stdcall MoveEndUntil ( + /*[in]*/ VARIANT * Cset, + /*[in]*/ long Count, + /*[out,retval]*/ long * pDelta ) = 0; + virtual HRESULT __stdcall FindShit ( + /*[in]*/ BSTR bstr, + /*[in]*/ long cch, + /*[in]*/ long Flags, + /*[out,retval]*/ long * pLength ) = 0; + virtual HRESULT __stdcall FindTextStart ( + /*[in]*/ BSTR bstr, + /*[in]*/ long cch, + /*[in]*/ long Flags, + /*[out,retval]*/ long * pLength ) = 0; + virtual HRESULT __stdcall FindTextEnd ( + /*[in]*/ BSTR bstr, + /*[in]*/ long cch, + /*[in]*/ long Flags, + /*[out,retval]*/ long * pLength ) = 0; + virtual HRESULT __stdcall Delete ( + /*[in]*/ long Unit, + /*[in]*/ long Count, + /*[out,retval]*/ long * pDelta ) = 0; + virtual HRESULT __stdcall Cut ( + /*[out]*/ VARIANT * pVar ) = 0; + virtual HRESULT __stdcall Copy ( + /*[out]*/ VARIANT * pVar ) = 0; + virtual HRESULT __stdcall Paste ( + /*[in]*/ VARIANT * pVar, + /*[in]*/ long Format ) = 0; + virtual HRESULT __stdcall CanPaste ( + /*[in]*/ VARIANT * pVar, + /*[in]*/ long Format, + /*[out,retval]*/ long * pB ) = 0; + virtual HRESULT __stdcall CanEdit ( + /*[out,retval]*/ long * pbCanEdit ) = 0; + virtual HRESULT __stdcall ChangeCase ( + /*[in]*/ long Type ) = 0; + virtual HRESULT __stdcall GetPoint ( + /*[in]*/ long Type, + /*[out]*/ long * px, + /*[out]*/ long * py ) = 0; + virtual HRESULT __stdcall SetPoint ( + /*[in]*/ long x, + /*[in]*/ long y, + /*[in]*/ long Type, + /*[in]*/ long Extend ) = 0; + virtual HRESULT __stdcall ScrollIntoView ( + /*[in]*/ long Value ) = 0; + virtual HRESULT __stdcall GetEmbeddedObject ( + /*[out,retval]*/ IUnknown * * ppv ) = 0; +}; + +struct __declspec(uuid("8cc497c1-a1df-11ce-8098-00aa0047be5d")) +ITextSelection : ITextRange +{ + // + // Raw methods provided by interface + // + + virtual HRESULT __stdcall get_Flags ( + /*[out,retval]*/ long * pFlags ) = 0; + virtual HRESULT __stdcall put_Flags ( + /*[in]*/ long pFlags ) = 0; + virtual HRESULT __stdcall get_Type ( + /*[out,retval]*/ long * pType ) = 0; + virtual HRESULT __stdcall MoveLeft ( + /*[in]*/ long Unit, + /*[in]*/ long Count, + /*[in]*/ long Extend, + /*[out,retval]*/ long * pDelta ) = 0; + virtual HRESULT __stdcall MoveRight ( + /*[in]*/ long Unit, + /*[in]*/ long Count, + /*[in]*/ long Extend, + /*[out,retval]*/ long * pDelta ) = 0; + virtual HRESULT __stdcall MoveUp ( + /*[in]*/ long Unit, + /*[in]*/ long Count, + /*[in]*/ long Extend, + /*[out,retval]*/ long * pDelta ) = 0; + virtual HRESULT __stdcall MoveDown ( + /*[in]*/ long Unit, + /*[in]*/ long Count, + /*[in]*/ long Extend, + /*[out,retval]*/ long * pDelta ) = 0; + virtual HRESULT __stdcall HomeKey ( + /*[in]*/ long Unit, + /*[in]*/ long Extend, + /*[out,retval]*/ long * pDelta ) = 0; + virtual HRESULT __stdcall EndKey ( + /*[in]*/ long Unit, + /*[in]*/ long Extend, + /*[out,retval]*/ long * pDelta ) = 0; + virtual HRESULT __stdcall TypeText ( + /*[in]*/ BSTR bstr ) = 0; +}; + +struct __declspec(uuid("8cc497c5-a1df-11ce-8098-00aa0047be5d")) +ITextStoryRanges : IDispatch +{ + // + // Raw methods provided by interface + // + + virtual HRESULT __stdcall _NewEnum ( + /*[out,retval]*/ IUnknown * * ppunkEnum ) = 0; + virtual HRESULT __stdcall Item ( + /*[in]*/ long Index, + /*[out,retval]*/ struct ITextRange * * ppRange ) = 0; + virtual HRESULT __stdcall get_Count ( + /*[out,retval]*/ long * pCount ) = 0; +}; + +struct __declspec(uuid("8cc497c0-a1df-11ce-8098-00aa0047be5d")) +ITextDocument : IDispatch +{ + // + // Raw methods provided by interface + // + + virtual HRESULT __stdcall get_Name ( + /*[out,retval]*/ BSTR * pName ) = 0; + virtual HRESULT __stdcall get_Selection ( + /*[out,retval]*/ struct ITextSelection * * ppSel ) = 0; + virtual HRESULT __stdcall get_StoryCount ( + /*[out,retval]*/ long * pCount ) = 0; + virtual HRESULT __stdcall get_StoryRanges ( + /*[out,retval]*/ struct ITextStoryRanges * * ppStories ) = 0; + virtual HRESULT __stdcall get_Saved ( + /*[out,retval]*/ long * pValue ) = 0; + virtual HRESULT __stdcall put_Saved ( + /*[in]*/ long pValue ) = 0; + virtual HRESULT __stdcall get_DefaultTabStop ( + /*[out,retval]*/ float * pValue ) = 0; + virtual HRESULT __stdcall put_DefaultTabStop ( + /*[in]*/ float pValue ) = 0; + virtual HRESULT __stdcall New ( ) = 0; + virtual HRESULT __stdcall Open ( + /*[in]*/ VARIANT * pVar, + /*[in]*/ long Flags, + /*[in]*/ long CodePage ) = 0; + virtual HRESULT __stdcall Save ( + /*[in]*/ VARIANT * pVar, + /*[in]*/ long Flags, + /*[in]*/ long CodePage ) = 0; + virtual HRESULT __stdcall Freeze ( + /*[out,retval]*/ long * pCount ) = 0; + virtual HRESULT __stdcall Unfreeze ( + /*[out,retval]*/ long * pCount ) = 0; + virtual HRESULT __stdcall BeginEditCollection ( ) = 0; + virtual HRESULT __stdcall EndEditCollection ( ) = 0; + virtual HRESULT __stdcall Undo ( + /*[in]*/ long Count, + /*[out,retval]*/ long * prop ) = 0; + virtual HRESULT __stdcall Redo ( + /*[in]*/ long Count, + /*[out,retval]*/ long * prop ) = 0; + virtual HRESULT __stdcall Range ( + /*[in]*/ long cp1, + /*[in]*/ long cp2, + /*[out,retval]*/ struct ITextRange * * ppRange ) = 0; + virtual HRESULT __stdcall RangeFromPoint ( + /*[in]*/ long x, + /*[in]*/ long y, + /*[out,retval]*/ struct ITextRange * * ppRange ) = 0; +}; + +struct __declspec(uuid("01c25500-4268-11d1-883a-3c8b00c10000")) +ITextDocument2 : ITextDocument +{ + // + // Raw methods provided by interface + // + + virtual HRESULT __stdcall AttachMsgFilter ( + /*[in]*/ IUnknown * pFilter ) = 0; + virtual HRESULT __stdcall SetEffectColor ( + /*[in]*/ long Index, + /*[in]*/ unsigned long cr ) = 0; + virtual HRESULT __stdcall GetEffectColor ( + /*[in]*/ long Index, + /*[out]*/ unsigned long * pcr ) = 0; + virtual HRESULT __stdcall get_CaretType ( + /*[out,retval]*/ long * pCaretType ) = 0; + virtual HRESULT __stdcall put_CaretType ( + /*[in]*/ long pCaretType ) = 0; + virtual HRESULT __stdcall GetImmContext ( + /*[out,retval]*/ long * pContext ) = 0; + virtual HRESULT __stdcall ReleaseImmContext ( + /*[in]*/ long Context ) = 0; + virtual HRESULT __stdcall GetPreferredFont ( + /*[in]*/ long cp, + /*[in]*/ long CodePage, + /*[in]*/ long Option, + /*[in]*/ long curCodepage, + /*[in]*/ long curFontSize, + /*[out]*/ BSTR * pbstr, + /*[out]*/ long * pPitchAndFamily, + /*[out]*/ long * pNewFontSize ) = 0; + virtual HRESULT __stdcall get_NotificationMode ( + /*[out,retval]*/ long * pMode ) = 0; + virtual HRESULT __stdcall put_NotificationMode ( + /*[in]*/ long pMode ) = 0; + virtual HRESULT __stdcall GetClientRect ( + /*[in]*/ long Type, + /*[out]*/ long * pLeft, + /*[out]*/ long * pTop, + /*[out]*/ long * pRight, + /*[out]*/ long * pBottom ) = 0; + virtual HRESULT __stdcall get_SelectionEx ( + /*[out,retval]*/ struct ITextSelection * * ppSel ) = 0; + virtual HRESULT __stdcall GetWindow ( + /*[out]*/ long * phWnd ) = 0; + virtual HRESULT __stdcall GetFEFlags ( + /*[out]*/ long * pFlags ) = 0; + virtual HRESULT __stdcall UpdateWindow ( ) = 0; + virtual HRESULT __stdcall CheckTextLimit ( + long cch, + long * pcch ) = 0; + virtual HRESULT __stdcall IMEInProgress ( + long Mode ) = 0; + virtual HRESULT __stdcall SysBeep ( ) = 0; + virtual HRESULT __stdcall Update ( + /*[in]*/ long Mode ) = 0; + virtual HRESULT __stdcall Notify ( + /*[in]*/ long Notify ) = 0; +}; + +#pragma pack(push, 4) + +union __MIDL_IWinTypes_0009 +{ + long hInproc; + long hRemote; +}; + +#pragma pack(pop) + +#pragma pack(push, 4) + +struct _RemotableHandle +{ + long fContext; + union __MIDL_IWinTypes_0009 u; +}; + +#pragma pack(pop) + +struct __declspec(uuid("a3787420-4267-11d1-883a-3c8b00c10000")) +ITextMsgFilter : IUnknown +{ + // + // Raw methods provided by interface + // + + virtual HRESULT __stdcall AttachDocument ( + /*[in]*/ wireHWND hwnd, + /*[in]*/ struct ITextDocument2 * pTextDoc ) = 0; + virtual HRESULT __stdcall HandleMessage ( + /*[in,out]*/ unsigned int * pmsg, + /*[in,out]*/ UINT_PTR * pwparam, + /*[in,out]*/ LONG_PTR * plparam, + /*[out]*/ LONG_PTR * plres ) = 0; + virtual HRESULT __stdcall AttachMsgFilter ( + /*[in]*/ struct ITextMsgFilter * pMsgFilter ) = 0; +}; + +// +// Named GUID constants initializations +// + +extern "C" const GUID __declspec(selectany) LIBID_tom = + {0x8cc497c9,0xa1df,0x11ce,{0x80,0x98,0x00,0xaa,0x00,0x47,0xbe,0x5d}}; +extern "C" const GUID __declspec(selectany) IID_ITextFont = + {0x8cc497c3,0xa1df,0x11ce,{0x80,0x98,0x00,0xaa,0x00,0x47,0xbe,0x5d}}; +extern "C" const GUID __declspec(selectany) IID_ITextPara = + {0x8cc497c4,0xa1df,0x11ce,{0x80,0x98,0x00,0xaa,0x00,0x47,0xbe,0x5d}}; +extern "C" const GUID __declspec(selectany) IID_ITextRange = + {0x8cc497c2,0xa1df,0x11ce,{0x80,0x98,0x00,0xaa,0x00,0x47,0xbe,0x5d}}; +extern "C" const GUID __declspec(selectany) IID_ITextSelection = + {0x8cc497c1,0xa1df,0x11ce,{0x80,0x98,0x00,0xaa,0x00,0x47,0xbe,0x5d}}; +extern "C" const GUID __declspec(selectany) IID_ITextStoryRanges = + {0x8cc497c5,0xa1df,0x11ce,{0x80,0x98,0x00,0xaa,0x00,0x47,0xbe,0x5d}}; +extern "C" const GUID __declspec(selectany) IID_ITextDocument = + {0x8cc497c0,0xa1df,0x11ce,{0x80,0x98,0x00,0xaa,0x00,0x47,0xbe,0x5d}}; +extern "C" const GUID __declspec(selectany) IID_ITextDocument2 = + {0x01c25500,0x4268,0x11d1,{0x88,0x3a,0x3c,0x8b,0x00,0xc1,0x00,0x00}}; +extern "C" const GUID __declspec(selectany) IID_ITextMsgFilter = + {0xa3787420,0x4267,0x11d1,{0x88,0x3a,0x3c,0x8b,0x00,0xc1,0x00,0x00}}; + +} // namespace tom + +#pragma pack(pop) diff --git a/src/tools/common/AlphaPopup.cpp b/src/tools/common/AlphaPopup.cpp new file mode 100644 index 0000000..e355b79 --- /dev/null +++ b/src/tools/common/AlphaPopup.cpp @@ -0,0 +1,342 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "../../sys/win32/win_local.h" +#include "ColorButton.h" +#include "MaskEdit.h" +#include "../../sys/win32/rc/guied_resource.h" + +static HHOOK gAlphaHook = NULL; +static HWND gAlphaDlg = NULL; + +/* +================ +AlphaSlider_DrawArrow + +Draws the arrow under alpha slider +================ +*/ +static void AlphaSlider_DrawArrow ( HDC hDC, RECT* pRect, COLORREF color ) +{ + POINT ptsArrow[3]; + + ptsArrow[0].x = pRect->left; + ptsArrow[0].y = pRect->bottom; + ptsArrow[1].x = (pRect->left + pRect->right)/2; + ptsArrow[1].y = pRect->top; + ptsArrow[2].x = pRect->right; + ptsArrow[2].y = pRect->bottom; + + HBRUSH arrowBrush = CreateSolidBrush ( color ); + HPEN arrowPen = CreatePen ( PS_SOLID, 1, color ); + + HGDIOBJ oldBrush = SelectObject ( hDC, arrowBrush ); + HGDIOBJ oldPen = SelectObject ( hDC, arrowPen ); + + SetPolyFillMode(hDC, WINDING); + Polygon(hDC, ptsArrow, 3); + + SelectObject ( hDC, oldBrush ); + SelectObject ( hDC, oldPen ); + + DeleteObject ( arrowBrush ); + DeleteObject ( arrowPen ); +} + +/* +================ +AlphaSlider_WndProc + +Window procedure for the alpha slider control +================ +*/ +LRESULT CALLBACK AlphaSlider_WndProc ( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + switch ( msg ) + { + case WM_LBUTTONDOWN: + { + RECT rClient; + float v; + + GetClientRect ( hwnd, &rClient ); + v = (float)((short)LOWORD(lParam)-5) / (float)(rClient.right - rClient.left - 10); + if ( v < 0 ) v = 0; + if ( v > 1.0f ) v = 1.0f; + SetWindowLong ( hwnd, GWL_USERDATA, MAKELONG(0x8000,(unsigned short)(255.0f * v)) ); + InvalidateRect ( hwnd, NULL, FALSE ); + + SetCapture ( hwnd ); + + break; + } + + case WM_MOUSEMOVE: + if ( LOWORD(GetWindowLong ( hwnd, GWL_USERDATA ) ) & 0x8000 ) + { + RECT rClient; + float v; + + GetClientRect ( hwnd, &rClient ); + v = (float)((short)LOWORD(lParam)-5) / (float)(rClient.right - rClient.left - 10); + if ( v < 0 ) v = 0; + if ( v > 1.0f ) v = 1.0f; + SetWindowLong ( hwnd, GWL_USERDATA, MAKELONG(0x8000,(unsigned short)(255.0f * v)) ); + InvalidateRect ( hwnd, NULL, FALSE ); + } + break; + + case WM_LBUTTONUP: + if ( LOWORD(GetWindowLong ( hwnd, GWL_USERDATA ) ) & 0x8000 ) + { + RECT rClient; + float v; + + GetClientRect ( hwnd, &rClient ); + v = (float)((short)LOWORD(lParam)-5) / (float)(rClient.right - rClient.left - 10); + if ( v < 0 ) v = 0; + if ( v > 1.0f ) v = 1.0f; + SetWindowLong ( hwnd, GWL_USERDATA, MAKELONG(0x8000,(unsigned short)(255.0f * v)) ); + InvalidateRect ( hwnd, NULL, FALSE ); + ReleaseCapture ( ); + SendMessage ( GetParent ( hwnd ), WM_COMMAND, MAKELONG(GetWindowLong (hwnd,GWL_ID),0), 0 ); + } + break; + + case WM_PAINT: + { + PAINTSTRUCT ps; + HDC hDC = BeginPaint ( hwnd, &ps ); + + RECT rDraw; + RECT rClient; + GetClientRect ( hwnd, &rClient ); + + // Setup the gradient rect + CopyRect ( &rDraw, &rClient ); + rDraw.left += 5; + rDraw.right -= 5; + rDraw.bottom -= 6; + + // Draw the gradient + int parts = 20; + RECT rColor; + float step = (float)(rDraw.right-rDraw.left) / (float)parts; + CopyRect ( &rColor, &rDraw ); + for ( int i = 0; i < parts; i ++ ) + { + float color = ((float)i / (float)parts) * 255.0f; + + rColor.left = rDraw.left + i * step; + rColor.right = rColor.left + step + 1; + + HBRUSH brush = CreateSolidBrush ( RGB((int)color,(int)color,(int)color) ); + FillRect ( hDC, &rColor, brush ); + DeleteObject ( brush ); + } + + // Draw a frame around the gradient + FrameRect (hDC, &rDraw, (HBRUSH)GetStockObject ( BLACK_BRUSH ) ); + + // Make sure the area below the graident is filled in + rClient.top = rDraw.bottom; + FillRect ( hDC, &rClient, GetSysColorBrush ( COLOR_3DFACE ) ); + + // Draw the thumb + RECT rThumb; + short s = HIWORD(GetWindowLong ( hwnd, GWL_USERDATA )); + float thumb = (float)(short)s; + thumb /= 255.0f; + thumb *= (float)(rDraw.right-rDraw.left); + rThumb.left = rDraw.left - 5 + thumb; + rThumb.right = rThumb.left + 10; + rThumb.top = rDraw.bottom + 1; + rThumb.bottom = rThumb.top + 5; + AlphaSlider_DrawArrow ( hDC, &rThumb, RGB(0,0,0) ); + + EndPaint ( hwnd, &ps ); + return 0; + } + } + + return DefWindowProc ( hwnd, msg, wParam, lParam ); +} + +/* +================ +AlphaSelectDlg_GetMsgProc + +Ensures normal dialog functions work in the alpha select dialog +================ +*/ +LRESULT FAR PASCAL AlphaSelectDlg_GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam) +{ + LPMSG lpMsg = (LPMSG) lParam; + + if ( nCode >= 0 && PM_REMOVE == wParam ) + { + // Don't translate non-input events. + if ( (lpMsg->message >= WM_KEYFIRST && lpMsg->message <= WM_KEYLAST) ) + { + if ( IsDialogMessage( gAlphaDlg, lpMsg) ) + { + // The value returned from this hookproc is ignored, + // and it cannot be used to tell Windows the message has been handled. + // To avoid further processing, convert the message to WM_NULL + // before returning. + lpMsg->message = WM_NULL; + lpMsg->lParam = 0; + lpMsg->wParam = 0; + } + } + } + + return CallNextHookEx(gAlphaHook, nCode, wParam, lParam); +} + +/* +================ +AlphaSelectDlg_WndProc + +Window procedure for the alpha select dialog +================ +*/ +INT_PTR CALLBACK AlphaSelectDlg_WndProc ( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + switch ( msg ) + { + case WM_INITDIALOG: + { + int color; + + gAlphaDlg = hwnd; + gAlphaHook = SetWindowsHookEx( WH_GETMESSAGE, AlphaSelectDlg_GetMsgProc, NULL, GetCurrentThreadId() ); + color = GetRValue(ColorButton_GetColor ((HWND)lParam)); + + // The lParam for the alpha select dialog is the window handle of the button pressed + SetWindowLong ( hwnd, GWL_USERDATA, lParam ); + + // Subclass the alpha + SetWindowLong ( GetDlgItem ( hwnd, IDC_GUIED_ALPHASLIDER ), GWL_USERDATA, MAKELONG(0,color) ); + + // Numbers only on the edit box and start it with the current alpha value. + NumberEdit_Attach ( GetDlgItem ( hwnd, IDC_GUIED_ALPHA ) ); + SetWindowText ( GetDlgItem ( hwnd, IDC_GUIED_ALPHA ), va("%.3f", ((float)color / 255.0f) ) ); + break; + } + + case WM_DESTROY: + UnhookWindowsHookEx( gAlphaHook ); + ReleaseCapture ( ); + gAlphaDlg = NULL; + break; + + case WM_ACTIVATE: + if ( !LOWORD(wParam) ) + { + EndDialog ( hwnd, 0 ); + } + break; + + case WM_COMMAND: + switch ( LOWORD(wParam) ) + { + case IDC_GUIED_ALPHA: + { + char temp[64]; + float value; + + // Get the current text in the window and convert it to a float + GetDlgItemText ( hwnd, IDC_GUIED_ALPHA, temp, 64 ); + value = atof ( temp ); + + if ( value < 0.0f ) + { + value = 0.0f; + } + else if ( value > 1.0f ) + { + value = 1.0f; + } + + // Set the current alpha value in the slider + SetWindowLong ( GetDlgItem ( hwnd, IDC_GUIED_ALPHASLIDER ), GWL_USERDATA, MAKELONG(0,(255.0f * value)) ); + break; + } + + case IDC_GUIED_ALPHASLIDER: + case IDOK: + { + int color = (short)HIWORD(GetWindowLong ( GetDlgItem ( hwnd, IDC_GUIED_ALPHASLIDER ), GWL_USERDATA )); + ColorButton_SetColor ( (HWND)GetWindowLong ( hwnd, GWL_USERDATA ), RGB(color,color,color) ); + EndDialog ( hwnd, 0 ); + break; + } + + case IDCANCEL: + EndDialog ( hwnd, 0 ); + break; + } + break; + } + + return FALSE; +} + +/* +================ +AlphaButton_OpenPopup + +Opens the popup window under the alpha button +================ +*/ +void AlphaButton_OpenPopup ( HWND button ) +{ + RECT rWindow; + WNDCLASSEX wndClass; + HWND dlg; + + // Make sure the alpha slider window class is registered + memset ( &wndClass, 0, sizeof(wndClass) ); + wndClass.cbSize = sizeof(WNDCLASSEX); + wndClass.lpszClassName = "GUIED_ALPHASLIDER"; + wndClass.lpfnWndProc = AlphaSlider_WndProc; + wndClass.hInstance = win32.hInstance; + RegisterClassEx ( &wndClass ); + + GetWindowRect ( button, &rWindow ); + dlg = CreateDialogParam ( win32.hInstance, MAKEINTRESOURCE(IDD_GUIED_ALPHA), GetParent(button), AlphaSelectDlg_WndProc, (LPARAM)button ); + + SetWindowPos ( dlg, NULL, rWindow.left, rWindow.bottom + 1, 0, 0, SWP_NOSIZE|SWP_NOZORDER ); + ShowWindow ( dlg, SW_SHOW ); + UpdateWindow ( dlg ); + SetFocus ( dlg ); +} diff --git a/src/tools/common/ColorButton.cpp b/src/tools/common/ColorButton.cpp new file mode 100644 index 0000000..2f35479 --- /dev/null +++ b/src/tools/common/ColorButton.cpp @@ -0,0 +1,206 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "ColorButton.h" + +static const int ARROW_SIZE_CX = 4 ; +static const int ARROW_SIZE_CY = 2 ; + +/* +================ +ColorButton_SetColor + +Sets the current color button color +================ +*/ +void ColorButton_SetColor ( HWND hWnd, COLORREF color ) +{ + if ( NULL == hWnd ) + { + return; + } + SetWindowLong ( hWnd, GWL_USERDATA, color ); + InvalidateRect ( hWnd, NULL, FALSE ); +} + +void ColorButton_SetColor ( HWND hWnd, const char* color ) +{ + float red; + float green; + float blue; + float alpha; + + if ( NULL == hWnd ) + { + return; + } + + sscanf ( color, "%f,%f,%f,%f", &red, &green, &blue, &alpha ); + + ColorButton_SetColor ( hWnd, RGB(red*255.0f, green*255.0f, blue*255.0f) ); +} + +void AlphaButton_SetColor ( HWND hWnd, const char* color ) +{ + float red; + float green; + float blue; + float alpha; + + if ( NULL == hWnd ) + { + return; + } + + sscanf ( color, "%f,%f,%f,%f", &red, &green, &blue, &alpha ); + + ColorButton_SetColor ( hWnd, RGB(alpha*255.0f, alpha*255.0f, alpha*255.0f) ); +} + +/* +================ +ColorButton_GetColor + +Retrieves the current color button color +================ +*/ +COLORREF ColorButton_GetColor ( HWND hWnd ) +{ + return (COLORREF) GetWindowLong ( hWnd, GWL_USERDATA ); +} + +/* +================ +ColorButton_DrawArrow + +Draws the arrow on the color button +================ +*/ +static void ColorButton_DrawArrow ( HDC hDC, RECT* pRect, COLORREF color ) +{ + POINT ptsArrow[3]; + + ptsArrow[0].x = pRect->left; + ptsArrow[0].y = pRect->top; + ptsArrow[1].x = pRect->right; + ptsArrow[1].y = pRect->top; + ptsArrow[2].x = (pRect->left + pRect->right)/2; + ptsArrow[2].y = pRect->bottom; + + HBRUSH arrowBrush = CreateSolidBrush ( color ); + HPEN arrowPen = CreatePen ( PS_SOLID, 1, color ); + + HGDIOBJ oldBrush = SelectObject ( hDC, arrowBrush ); + HGDIOBJ oldPen = SelectObject ( hDC, arrowPen ); + + SetPolyFillMode(hDC, WINDING); + Polygon(hDC, ptsArrow, 3); + + SelectObject ( hDC, oldBrush ); + SelectObject ( hDC, oldPen ); + + DeleteObject ( arrowBrush ); + DeleteObject ( arrowPen ); +} + +/* +================ +ColorButton_DrawItem + +Draws the actual color button as as reponse to a WM_DRAWITEM message +================ +*/ +void ColorButton_DrawItem ( HWND hWnd, LPDRAWITEMSTRUCT dis ) +{ + assert ( dis ); + + HDC hDC = dis->hDC; + UINT state = dis->itemState; + RECT rDraw = dis->rcItem; + RECT rArrow; + + // Draw outter edge + UINT uFrameState = DFCS_BUTTONPUSH|DFCS_ADJUSTRECT; + + if (state & ODS_SELECTED) + { + uFrameState |= DFCS_PUSHED; + } + + if (state & ODS_DISABLED) + { + uFrameState |= DFCS_INACTIVE; + } + + DrawFrameControl ( hDC, &rDraw, DFC_BUTTON, uFrameState ); + + // Draw Focus + if (state & ODS_SELECTED) + { + OffsetRect(&rDraw, 1,1); + } + + if (state & ODS_FOCUS) + { + RECT rFocus = {rDraw.left, + rDraw.top, + rDraw.right - 1, + rDraw.bottom}; + + DrawFocusRect ( hDC, &rFocus ); + } + + InflateRect ( &rDraw, -GetSystemMetrics(SM_CXEDGE), -GetSystemMetrics(SM_CYEDGE) ); + + // Draw the arrow + rArrow.left = rDraw.right - ARROW_SIZE_CX - GetSystemMetrics(SM_CXEDGE) /2; + rArrow.right = rArrow.left + ARROW_SIZE_CX; + rArrow.top = (rDraw.bottom + rDraw.top)/2 - ARROW_SIZE_CY / 2; + rArrow.bottom = (rDraw.bottom + rDraw.top)/2 + ARROW_SIZE_CY / 2; + + ColorButton_DrawArrow ( hDC, &rArrow, (state & ODS_DISABLED) ? ::GetSysColor(COLOR_GRAYTEXT) : RGB(0,0,0) ); + + rDraw.right = rArrow.left - GetSystemMetrics(SM_CXEDGE)/2; + + // Draw separator + DrawEdge ( hDC, &rDraw, EDGE_ETCHED, BF_RIGHT); + + rDraw.right -= (GetSystemMetrics(SM_CXEDGE) * 2) + 1 ; + + // Draw Color + if ((state & ODS_DISABLED) == 0) + { + HBRUSH color = CreateSolidBrush ( (COLORREF)GetWindowLong ( hWnd, GWL_USERDATA ) ); + FillRect ( hDC, &rDraw, color ); + FrameRect ( hDC, &rDraw, (HBRUSH)::GetStockObject(BLACK_BRUSH)); + DeleteObject( color ); + } +} diff --git a/src/tools/common/ColorButton.h b/src/tools/common/ColorButton.h new file mode 100644 index 0000000..1f6ca81 --- /dev/null +++ b/src/tools/common/ColorButton.h @@ -0,0 +1,40 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#ifndef COLORBUTTON_H_ +#define COLORBUTTON_H_ + +void ColorButton_DrawItem ( HWND hWnd, LPDRAWITEMSTRUCT dis ); +void ColorButton_SetColor ( HWND hWnd, COLORREF color ); +void ColorButton_SetColor ( HWND hWnd, const char* color ); +COLORREF ColorButton_GetColor ( HWND hWnd ); + +void AlphaButton_SetColor ( HWND hWnd, const char* color ); + +void AlphaButton_OpenPopup ( HWND button ); + +#endif // COLORBUTTON_H_ \ No newline at end of file diff --git a/src/tools/common/DialogHelpers.h b/src/tools/common/DialogHelpers.h new file mode 100644 index 0000000..49e708d --- /dev/null +++ b/src/tools/common/DialogHelpers.h @@ -0,0 +1,128 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef DIALOGHELPERS_H_ +#define DIALOGHELPERS_H_ + +class rvDialogItem +{ +public: + + HWND mWindow; + int mID; + + rvDialogItem ( int id ) { mID = id; } + + void Cache ( HWND parent ) + { + mWindow = GetDlgItem ( parent, mID ); + } + + void Check ( bool checked ) + { + SendMessage ( mWindow, BM_SETCHECK, checked ? BST_CHECKED : BST_UNCHECKED, 0 ); + } + + void Enable ( bool enable ) + { + EnableWindow ( mWindow, enable ); + } + + bool IsChecked ( void ) + { + return SendMessage ( mWindow, BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false; + } + + void SetText ( const char* text ) + { + SetWindowText ( mWindow, text ); + } + + void GetText ( idStr& out ) + { + char text[4096]; + GetWindowText ( mWindow, text, 4095 ); + out = text; + } + + float GetFloat ( void ) + { + idStr text; + GetText ( text ); + return atof( text ); + } + + void SetFloat ( float f ) + { + SetText ( va("%g", f ) ); + } + + operator HWND( void ) const { return mWindow; } +}; + +class rvDialogItemContainer +{ +protected: + + void Cache ( HWND parent, int count ) + { + int i; + unsigned char* ptr; + + ptr = (unsigned char*)this; + for ( i = 0; i < count; i ++, ptr += sizeof(rvDialogItem) ) + { + ((rvDialogItem*)ptr)->Cache ( parent ); + } + } +}; + +#define DIALOGITEM_BEGIN(name) \ +class name : public rvDialogItemContainer \ +{ \ +public: \ + name ( void ) { } \ + name ( HWND hwnd ) { Cache ( hwnd ); } \ + void Cache ( HWND parent ) \ + { \ + rvDialogItemContainer::Cache ( parent, sizeof(*this)/sizeof(rvDialogItem) ); \ + } + + +#define DIALOGITEM(id,name) \ +class c##name : public rvDialogItem \ +{ \ +public: \ + c##name(int localid=id) : rvDialogItem ( localid ) { } \ +} name; + +#define DIALOGITEM_END() \ +}; + + +#endif // DIALOGHELPERS_H_ diff --git a/src/tools/common/MaskEdit.cpp b/src/tools/common/MaskEdit.cpp new file mode 100644 index 0000000..a4ecb31 --- /dev/null +++ b/src/tools/common/MaskEdit.cpp @@ -0,0 +1,97 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#define MASKEDIT_MAXINVALID 1024 +typedef struct +{ + WNDPROC mProc; + char mInvalid[MASKEDIT_MAXINVALID]; +} rvGEMaskEdit; + +/* +================ +MaskEdit_WndProc + +Prevents the invalid characters from being entered +================ +*/ +LRESULT CALLBACK MaskEdit_WndProc ( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + rvGEMaskEdit* edit = (rvGEMaskEdit*)GetWindowLong ( hWnd, GWL_USERDATA ); + WNDPROC wndproc = edit->mProc; + + switch ( msg ) + { + case WM_CHAR: + if ( strchr ( edit->mInvalid, wParam ) ) + { + return 0; + } + + break; + + case WM_DESTROY: + delete edit; + SetWindowLong ( hWnd, GWL_WNDPROC, (LONG)wndproc ); + break; + } + + return CallWindowProc ( wndproc, hWnd, msg, wParam, lParam ); +} + +/* +================ +MaskEdit_Attach + +Attaches the mask edit control to a normal edit control +================ +*/ +void MaskEdit_Attach ( HWND hWnd, const char* invalid ) +{ + rvGEMaskEdit* edit = new rvGEMaskEdit; + edit->mProc = (WNDPROC)GetWindowLong ( hWnd, GWL_WNDPROC ); + strcpy ( edit->mInvalid, invalid ); + SetWindowLong ( hWnd, GWL_USERDATA, (LONG)edit ); + SetWindowLong ( hWnd, GWL_WNDPROC, (LONG)MaskEdit_WndProc ); +} + +/* +================ +NumberEdit_Attach + +Allows editing of floating point numbers +================ +*/ +void NumberEdit_Attach ( HWND hWnd ) +{ + static const char invalid[] = "`~!@#$%^&*()_+|=\\qwertyuiop[]asdfghjkl;'zxcvbnm,/QWERTYUIOP{}ASDFGHJKL:ZXCVBNM<>"; + MaskEdit_Attach ( hWnd, invalid ); +} diff --git a/src/tools/common/MaskEdit.h b/src/tools/common/MaskEdit.h new file mode 100644 index 0000000..92facea --- /dev/null +++ b/src/tools/common/MaskEdit.h @@ -0,0 +1,34 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#ifndef MASKEDIT_H_ +#define MASKEDIT_H_ + +void MaskEdit_Attach ( HWND hWnd, const char* invalid ); +void NumberEdit_Attach ( HWND hWnd ); + +#endif // MASKEDIT_H_ \ No newline at end of file diff --git a/src/tools/common/OpenFileDialog.cpp b/src/tools/common/OpenFileDialog.cpp new file mode 100644 index 0000000..dce8fb0 --- /dev/null +++ b/src/tools/common/OpenFileDialog.cpp @@ -0,0 +1,504 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "../../sys/win32/win_local.h" +#include "../../sys/win32/rc/common_resource.h" +#include "OpenFileDialog.h" + +char rvOpenFileDialog::mLookin[ MAX_OSPATH ]; + +/* +================ +rvOpenFileDialog::rvOpenFileDialog + +constructor +================ +*/ +rvOpenFileDialog::rvOpenFileDialog( void ) +{ + mWnd = NULL; + mInstance = NULL; + mBackBitmap = NULL; + mImageList = NULL; + mFlags = 0; +} + +/* +================ +rvOpenFileDialog::~rvOpenFileDialog + +destructor +================ +*/ +rvOpenFileDialog::~rvOpenFileDialog ( void ) +{ + if ( mImageList ) + { + ImageList_Destroy ( mImageList ); + } + + if ( mBackBitmap ) + { + DeleteObject ( mBackBitmap ); + } +} + +/* +================ +rvOpenFileDialog::DoModal + +Opens the dialog and returns true if a filename was found +================ +*/ +bool rvOpenFileDialog::DoModal ( HWND parent ) +{ + mInstance = win32.hInstance; + + INITCOMMONCONTROLSEX ex; + ex.dwICC = ICC_USEREX_CLASSES | ICC_LISTVIEW_CLASSES; + ex.dwSize = sizeof(INITCOMMONCONTROLSEX); + + InitCommonControlsEx ( &ex ); + + return DialogBoxParam ( mInstance, MAKEINTRESOURCE(IDD_TOOLS_OPEN), parent, DlgProc, (LPARAM)this ) ? true : false; +} + +/* +================ +rvOpenFileDialog::UpdateLookIn + +Updates the lookin combo box with the current lookin state +================ +*/ +void rvOpenFileDialog::UpdateLookIn ( void ) +{ + COMBOBOXEXITEM item; + idStr file; + idStr path; + + // Reset the combo box + SendMessage ( mWndLookin, CB_RESETCONTENT, 0, 0 ); + + // Setup the common item structure components + ZeroMemory ( &item, sizeof(item) ); + item.mask = CBEIF_TEXT | CBEIF_INDENT | CBEIF_IMAGE | CBEIF_SELECTEDIMAGE; + + // Add the top left folder + item.pszText = (LPSTR)"base"; + SendMessage ( mWndLookin, CBEM_INSERTITEM, 0, (LPARAM)&item ); + + // Break the lookin path up into its individual components and add them + // to the combo box + path = mLookin; + + while ( path.Length ( ) ) + { + int slash = path.Find ( "/" ); + + // Parse out the next subfolder + if ( slash != -1 ) + { + file = path.Left ( slash ); + path = path.Right ( path.Length ( ) - slash - 1 ); + } + else + { + file = path; + path.Empty ( ); + } + + // Add the sub folder + item.pszText = (LPSTR)file.c_str(); + item.iIndent++; + item.iItem = item.iIndent; + SendMessage ( mWndLookin, CBEM_INSERTITEM, 0, (LPARAM)&item ); + } + + // Set the selection to the last one since thats the deepest folder + SendMessage ( mWndLookin, CB_SETCURSEL, item.iIndent, 0 ); +} + +/* +================ +rvOpenFileDialog::UpdateFileList + +Updates the file list with the files that match the filter in the current +look in directory +================ +*/ +void rvOpenFileDialog::UpdateFileList ( void ) +{ + const char *basepath = mLookin; + idFileList *files; + HWND list = GetDlgItem ( mWnd, IDC_TOOLS_FILELIST ); + int i; + int filter; + + ListView_DeleteAllItems ( list ); + + // Add all the folders first + files = fileSystem->ListFiles ( basepath, "/", true ); + for ( i = 0; i < files->GetNumFiles(); i ++ ) + { + if ( files->GetFile( i )[0] == '.' ) + { + continue; + } + + LVITEM item; + item.mask = LVIF_TEXT; + item.iItem = ListView_GetItemCount ( list ); + item.pszText = (LPSTR)files->GetFile( i ); + item.iSubItem = 0; + ListView_InsertItem ( list, &item ); + } + fileSystem->FreeFileList( files ); + + // Add all the files in the current lookin directory that match the + // current filters. + for ( filter = 0; filter < mFilters.Num(); filter ++ ) + { + files = fileSystem->ListFiles( basepath, mFilters[filter], true ); + for ( i = 0; i < files->GetNumFiles(); i ++ ) + { + if ( files->GetFile( i )[0] == '.' ) + { + continue; + } + + LVITEM item; + item.mask = LVIF_TEXT|LVIF_IMAGE; + item.iImage = 2; + item.iItem = ListView_GetItemCount( list ); + item.pszText = (LPSTR)files->GetFile( i ); + item.iSubItem = 0; + ListView_InsertItem ( list, &item ); + } + fileSystem->FreeFileList( files ); + } +} + +/* +================ +rvOpenFileDialog::HandleCommandOK + +Handles the pressing of the OK button but either opening a selected folder +or closing the dialog with the resulting filename +================ +*/ +void rvOpenFileDialog::HandleCommandOK ( void ) +{ + char temp[256]; + LVITEM item; + + // If nothing is selected then there is nothing to open + int sel = ListView_GetNextItem ( mWndFileList, -1, LVNI_SELECTED ); + if ( sel == -1 ) + { + GetWindowText ( GetDlgItem ( mWnd, IDC_TOOLS_FILENAME ), temp, sizeof(temp)-1 ); + if ( !temp[0] ) + { + return; + } + + item.iImage = 2; + } + else + { + // Get the currently selected item + item.mask = LVIF_IMAGE|LVIF_TEXT; + item.iImage = sel; + item.iSubItem = 0; + item.pszText = temp; + item.cchTextMax = 256; + item.iItem = sel; + ListView_GetItem ( mWndFileList, &item ); + } + + // If the item is a folder then just open that folder + if ( item.iImage == 0 ) + { + if ( strlen( mLookin ) ) + { + idStr::snPrintf( mLookin, sizeof( mLookin ), "%s/%s", mLookin, temp ); + } else { + idStr::Copynz( mLookin, temp, sizeof( mLookin ) ); + } + UpdateLookIn ( ); + UpdateFileList ( ); + } + // If the item is a file then build the filename and end the dialog + else if ( item.iImage == 2 ) + { + mFilename = mLookin; + if ( mFilename.Length ( ) ) + { + mFilename.Append ( "/" ); + } + mFilename.Append ( temp ); + + // Make sure the file exists + if ( mFlags & OFD_MUSTEXIST ) + { + idFile* file; + file = fileSystem->OpenFileRead ( mFilename ); + if ( !file ) + { + MessageBox ( mWnd, va("%s\nFile not found.\nPlease verify the correct file name was given", mFilename.c_str() ), "Open", MB_ICONERROR|MB_OK ); + return; + } + fileSystem->CloseFile ( file ); + } + + EndDialog ( mWnd, 1 ); + } + + return; +} + +/* +================ +rvOpenFileDialog::HandleInitDialog + +Handles the init dialog message +================ +*/ +void rvOpenFileDialog::HandleInitDialog ( void ) +{ + // Cache the more used window handles + mWndFileList = GetDlgItem ( mWnd, IDC_TOOLS_FILELIST ); + mWndLookin = GetDlgItem ( mWnd, IDC_TOOLS_LOOKIN ); + + // Load the custom resources used by the controls + mImageList = ImageList_LoadBitmap ( mInstance, MAKEINTRESOURCE(IDB_TOOLS_OPEN),16,1,RGB(255,255,255) ); + mBackBitmap = (HBITMAP)LoadImage ( mInstance, MAKEINTRESOURCE(IDB_TOOLS_BACK), IMAGE_BITMAP, 16, 16, LR_DEFAULTCOLOR|LR_LOADMAP3DCOLORS ); + + // Attach the image list to the file list and lookin controls + ListView_SetImageList ( mWndFileList, mImageList, LVSIL_SMALL ); + SendMessage( mWndLookin,CBEM_SETIMAGELIST,0,(LPARAM) mImageList ); + + // Back button is a bitmap button + SendMessage( GetDlgItem ( mWnd, IDC_TOOLS_BACK ), BM_SETIMAGE, IMAGE_BITMAP, (LONG) mBackBitmap ); + + // Allow custom titles + SetWindowText ( mWnd, mTitle ); + + // Custom ok button title + if ( mOKTitle.Length ( ) ) + { + SetWindowText ( GetDlgItem ( mWnd, IDOK ), mOKTitle ); + } + + // See if there is a filename in the lookin + idStr temp; + idStr filename = mLookin; + filename.ExtractFileExtension ( temp ); + if ( temp.Length ( ) ) + { + filename.ExtractFileName ( temp ); + SetWindowText ( GetDlgItem ( mWnd, IDC_TOOLS_FILENAME ), temp ); + filename.StripFilename ( ); + idStr::snPrintf( mLookin, sizeof( mLookin ), "%s", filename.c_str() ); + } + + // Update our controls + UpdateLookIn ( ); + UpdateFileList ( ); +} + +/* +================ +rvOpenFileDialog::HandleLookInChange + +Handles a selection change within the lookin control +================ +*/ +void rvOpenFileDialog::HandleLookInChange ( void ) +{ + char temp[256]; + int sel; + int i; + idStr lookin; + + temp[0] = 0; + + sel = SendMessage ( mWndLookin, CB_GETCURSEL, 0, 0 ); + + // If something other than base is selected then walk up the list + // and build the new lookin path + if ( sel >= 1 ) + { + SendMessage ( mWndLookin, CB_GETLBTEXT, 1, (LPARAM)temp ); + idStr::snPrintf( mLookin, sizeof( mLookin ), "%s", temp ); + for ( i = 2; i <= sel; i ++ ) + { + SendMessage ( mWndLookin, CB_GETLBTEXT, i, (LPARAM)temp ); + idStr::snPrintf( mLookin, sizeof( mLookin ), "%s/%s", mLookin, temp ); + } + } + else + { + mLookin[0] = 0; + } + + // Update the controls with the new lookin path + UpdateLookIn ( ); + UpdateFileList ( ); +} + +/* +================ +rvOpenFileDialog::SetFilter + +Set the extensions available in the dialog +================ +*/ +void rvOpenFileDialog::SetFilter ( const char* s ) +{ + idStr filters = s; + idStr filter; + + while ( filters.Length ( ) ) + { + int semi = filters.Find ( ';' ); + if ( semi != -1 ) + { + filter = filters.Left ( semi ); + filters = filters.Right ( filters.Length ( ) - semi ); + } + else + { + filter = filters; + filters.Empty ( ); + } + + mFilters.Append ( filter.c_str() + (filter[0] == '*' ? 1 : 0) ); + } +} + +/* +================ +rvOpenFileDialog::DlgProc + +Dialog Procedure for the open file dialog +================ +*/ +INT_PTR rvOpenFileDialog::DlgProc ( HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam ) +{ + rvOpenFileDialog* dlg = (rvOpenFileDialog*) GetWindowLong ( wnd, GWL_USERDATA ); + + switch ( msg ) + { + case WM_INITDIALOG: + dlg = (rvOpenFileDialog*) lparam; + SetWindowLong ( wnd, GWL_USERDATA, lparam ); + dlg->mWnd = wnd; + dlg->HandleInitDialog ( ); + return TRUE; + + case WM_NOTIFY: + { + NMHDR* nm = (NMHDR*) lparam; + switch ( nm->idFrom ) + { + case IDC_TOOLS_FILELIST: + switch ( nm->code ) + { + case LVN_ITEMCHANGED: + { + NMLISTVIEW* nmlv = (NMLISTVIEW*)nm; + if ( nmlv->uNewState & LVIS_SELECTED ) + { + // Get the currently selected item + LVITEM item; + char temp[256]; + item.mask = LVIF_IMAGE|LVIF_TEXT; + item.iSubItem = 0; + item.pszText = temp; + item.cchTextMax = sizeof(temp)-1; + item.iItem = nmlv->iItem; + ListView_GetItem ( dlg->mWndFileList, &item ); + + if ( item.iImage == 2 ) + { + SetWindowText ( GetDlgItem ( wnd, IDC_TOOLS_FILENAME ), temp ); + } + } + break; + } + + case NM_DBLCLK: + dlg->HandleCommandOK ( ); + break; + } + break; + } + break; + } + + case WM_COMMAND: + switch ( LOWORD ( wparam ) ) + { + case IDOK: + { + dlg->HandleCommandOK ( ); + break; + } + + case IDCANCEL: + EndDialog ( wnd, 0 ); + break; + + case IDC_TOOLS_BACK: + { + int sel = SendMessage ( GetDlgItem ( wnd, IDC_TOOLS_LOOKIN ), CB_GETCURSEL, 0, 0 ); + if ( sel > 0 ) + { + sel--; + SendMessage ( GetDlgItem ( wnd, IDC_TOOLS_LOOKIN ), CB_SETCURSEL, sel, 0 ); + dlg->HandleLookInChange ( ); + } + + break; + } + + case IDC_TOOLS_LOOKIN: + if ( HIWORD ( wparam ) == CBN_SELCHANGE ) + { + dlg->HandleLookInChange ( ); + } + break; + } + break; + } + + return FALSE; +} diff --git a/src/tools/common/OpenFileDialog.h b/src/tools/common/OpenFileDialog.h new file mode 100644 index 0000000..5928e49 --- /dev/null +++ b/src/tools/common/OpenFileDialog.h @@ -0,0 +1,117 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#ifndef OPENFILEDIALOG_H_ +#define OPENFILEDIALOG_H_ + +#define OFD_MUSTEXIST 0x00000001 + +class rvOpenFileDialog +{ +public: + + rvOpenFileDialog ( void ); + ~rvOpenFileDialog ( void ); + + bool DoModal ( HWND parent ); + const char* GetFilename ( void ); + + void SetFilter ( const char* filter ); + void SetTitle ( const char* title ); + void SetOKTitle ( const char* title ); + void SetInitialPath ( const char* path ); + void SetFlags ( int flags ); + + const char* GetInitialPath ( void ); + +protected: + + void UpdateFileList ( void ); + void UpdateLookIn ( void ); + + HWND mWnd; + HWND mWndFileList; + HWND mWndLookin; + + HINSTANCE mInstance; + + HIMAGELIST mImageList; + HBITMAP mBackBitmap; + + static char mLookin[ MAX_OSPATH ]; + idStr mFilename; + idStr mTitle; + idStr mOKTitle; + idStrList mFilters; + + int mFlags; + +private: + + void HandleCommandOK ( void ); + void HandleLookInChange ( void ); + void HandleInitDialog ( void ); + + static INT_PTR CALLBACK DlgProc ( HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam ); +}; + +ID_INLINE const char* rvOpenFileDialog::GetFilename ( void ) +{ + return mFilename.c_str ( ); +} + +ID_INLINE void rvOpenFileDialog::SetTitle ( const char* title ) +{ + mTitle = title; +} + +ID_INLINE void rvOpenFileDialog::SetOKTitle ( const char* title ) +{ + mOKTitle = title; +} + +ID_INLINE void rvOpenFileDialog::SetInitialPath ( const char* path ) +{ + if ( !idStr::Cmpn( mLookin, path, strlen( path ) ) ) + { + return; + } + + idStr::Copynz( mLookin, path, sizeof( mLookin ) ); +} + +ID_INLINE void rvOpenFileDialog::SetFlags ( int flags ) +{ + mFlags = flags; +} + +ID_INLINE const char* rvOpenFileDialog::GetInitialPath ( void ) +{ + return mLookin; +} + +#endif // OPENFILEDIALOG_H_ diff --git a/src/tools/common/PropTree/PropTree.cpp b/src/tools/common/PropTree/PropTree.cpp new file mode 100644 index 0000000..ca9e737 --- /dev/null +++ b/src/tools/common/PropTree/PropTree.cpp @@ -0,0 +1,923 @@ +// PropTree.cpp : implementation file +// +// Copyright (C) 1998-2001 Scott Ramsay +// sramsay@gonavi.com +// http://www.gonavi.com +// +// This material is provided "as is", with absolutely no warranty expressed +// or implied. Any use is at your own risk. +// +// Permission to use or copy this software for any purpose is hereby granted +// without fee, provided the above notices are retained on all copies. +// Permission to modify the code and to distribute modified code is granted, +// provided the above notices are retained, and a notice that the code was +// modified is included with the above copyright notice. +// +// If you use this code, drop me an email. I'd like to know if you find the code +// useful. + +//#include "stdafx.h" +#include "../../../idlib/precompiled.h" +#pragma hdrstop + +#include "PropTree.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +#define PROPTREEITEM_EXPANDCOLUMN 16 // width of the expand column +#define PROPTREEITEM_COLRNG 5 // width of splitter + +//static AFX_EXTENSION_MODULE PropTreeDLL = {NULL, NULL}; +static const CString strOfficeFontName = _T("Tahoma"); +static const CString strDefaultFontName = _T("MS Sans Serif"); + +HINSTANCE ghInst; + +/*extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID) +{ + if (dwReason == DLL_PROCESS_ATTACH) + { + TRACE0("PROPTREE.DLL Initializing!\n"); + + if (!AfxInitExtensionModule(PropTreeDLL, hInstance)) + return 0; + + new CDynLinkLibrary(PropTreeDLL); + + ghInst = hInstance; + } + else if (dwReason == DLL_PROCESS_DETACH) + { + TRACE0("PROPTREE.DLL Terminating!\n"); + AfxTermExtensionModule(PropTreeDLL); + } + + return 1; +}*/ + +void InitPropTree(HINSTANCE hInstance) { + ghInst = hInstance; +} + +static int CALLBACK FontFamilyProcFonts(const LOGFONT FAR* lplf, const TEXTMETRIC FAR*, ULONG, LPARAM) +{ + ASSERT(lplf != NULL); + CString strFont = lplf->lfFaceName; + return strFont.CollateNoCase (strOfficeFontName) == 0 ? 0 : 1; +} + +///////////////////////////////////////////////////////////////////////////// +// CPropTree + +UINT CPropTree::s_nInstanceCount; +CFont* CPropTree::s_pNormalFont; +CFont* CPropTree::s_pBoldFont; +CPropTreeItem* CPropTree::s_pFound; + +CPropTree::CPropTree() : + m_bShowInfo(TRUE), + m_nInfoHeight(50), + m_pVisbleList(NULL), + m_Origin(100,0), + m_nLastUID(1), + m_pFocus(NULL), + m_bDisableInput(FALSE) +{ + m_Root.Expand(); + + // init global resources only once + if (!s_nInstanceCount) + InitGlobalResources(); + s_nInstanceCount++; +} + + +CPropTree::~CPropTree() +{ + DeleteAllItems(); + + s_nInstanceCount--; + + // free global resource when ALL CPropTrees are destroyed + if (!s_nInstanceCount) + FreeGlobalResources(); +} + + +BEGIN_MESSAGE_MAP(CPropTree, CWnd) + //{{AFX_MSG_MAP(CPropTree) + ON_WM_CREATE() + ON_WM_SIZE() + ON_WM_ENABLE() + ON_WM_SYSCOLORCHANGE() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + +///////////////////////////////////////////////////////////////////////////// +// CPropTree message handlers + +const POINT& CPropTree::GetOrigin() +{ + return m_Origin; +} + + +BOOL CPropTree::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID) +{ + CWnd* pWnd = this; + + LPCTSTR pszCreateClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW, ::LoadCursor(NULL, IDC_ARROW)); + + return pWnd->Create(pszCreateClass, _T(""), dwStyle, rect, pParentWnd, nID); +} + + +int CPropTree::OnCreate(LPCREATESTRUCT lpCreateStruct) +{ + if (CWnd::OnCreate(lpCreateStruct) == -1) + return -1; + + DWORD dwStyle; + CRect rc; + + GetClientRect(rc); + + // create CPropTreeList + // + + dwStyle = WS_VISIBLE|WS_CHILD|WS_VSCROLL; + + if (!m_List.Create(dwStyle, rc, this, 100)) + { + TRACE0("Failed to create CPropTreeList\n"); + return -1; + } + + m_List.SetPropOwner(this); + + // create CPropTreeInfo + // + + dwStyle &= ~WS_VSCROLL; + + if (!m_Info.Create(_T(""), dwStyle, rc, this)) + { + TRACE0("Failed to create CPropTreeInfo\n"); + return -1; + } + + m_Info.SetPropOwner(this); + + return 0; +} + + +CWnd* CPropTree::GetCtrlParent() +{ + return &m_List; +} + + +void CPropTree::OnSize(UINT nType, int cx, int cy) +{ + CWnd::OnSize(nType, cx, cy); + ResizeChildWindows(cx, cy); +} + + +void CPropTree::ResizeChildWindows(int cx, int cy) +{ + if (m_bShowInfo) + { + if (IsWindow(m_List.m_hWnd)) + m_List.MoveWindow(0, 0, cx, cy - m_nInfoHeight); + + if (IsWindow(m_Info.m_hWnd)) + m_Info.MoveWindow(0, cy - m_nInfoHeight, cx, m_nInfoHeight); + } + else + { + if (IsWindow(m_List.m_hWnd)) + m_List.MoveWindow(0, 0, cx, cy); + } +} + + +void CPropTree::InitGlobalResources() +{ + NONCLIENTMETRICS info; + info.cbSize = sizeof(info); + + ::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0); + + LOGFONT lf; + memset(&lf, 0, sizeof (LOGFONT)); + + CWindowDC dc(NULL); + lf.lfCharSet = (BYTE)GetTextCharsetInfo(dc.GetSafeHdc(), NULL, 0); + + lf.lfHeight = info.lfMenuFont.lfHeight; + lf.lfWeight = info.lfMenuFont.lfWeight; + lf.lfItalic = info.lfMenuFont.lfItalic; + + // check if we should use system font + _tcscpy(lf.lfFaceName, info.lfMenuFont.lfFaceName); + + BOOL fUseSystemFont = (info.lfMenuFont.lfCharSet > SYMBOL_CHARSET); + if (!fUseSystemFont) + { + // check for "Tahoma" font existance: + if (::EnumFontFamilies(dc.GetSafeHdc(), NULL, FontFamilyProcFonts, 0)==0) + { + // Found! Use MS Office font! + _tcscpy(lf.lfFaceName, strOfficeFontName); + } + else + { + // Not found. Use default font: + _tcscpy(lf.lfFaceName, strDefaultFontName); + } + } + + s_pNormalFont = new CFont; + s_pNormalFont->CreateFontIndirect(&lf); + + lf.lfWeight = FW_BOLD; + s_pBoldFont = new CFont; + s_pBoldFont->CreateFontIndirect(&lf); +} + + +void CPropTree::FreeGlobalResources() +{ + if (s_pNormalFont) + { + delete s_pNormalFont; + s_pNormalFont = NULL; + } + + if (s_pBoldFont) + { + delete s_pBoldFont; + s_pBoldFont = NULL; + } +} + + +CFont* CPropTree::GetNormalFont() +{ + return s_pNormalFont; +} + + +CFont* CPropTree::GetBoldFont() +{ + return s_pBoldFont; +} + + +CPropTreeItem* CPropTree::GetFocusedItem() +{ + return m_pFocus; +} + + +CPropTreeItem* CPropTree::GetRootItem() +{ + return &m_Root; +} + + +void CPropTree::ClearVisibleList() +{ + m_pVisbleList = NULL; +} + + +CPropTreeItem* CPropTree::GetVisibleList() +{ + return m_pVisbleList; +} + + +void CPropTree::AddToVisibleList(CPropTreeItem* pItem) +{ + if (!pItem) + return; + + // check for an empty visible list + if (!m_pVisbleList) + m_pVisbleList = pItem; + else + { + // Add the new item to the end of the list + CPropTreeItem* pNext; + + pNext = m_pVisbleList; + while (pNext->GetNextVisible()) + pNext = pNext->GetNextVisible(); + + pNext->SetNextVisible(pItem); + } + + pItem->SetNextVisible(NULL); +} + + +BOOL CPropTree::EnumItems(CPropTreeItem* pItem, ENUMPROPITEMPROC proc, LPARAM lParam) +{ + if (!pItem || !proc) + return FALSE; + + CPropTreeItem* pNext; + + // don't count the root item in any enumerations + if (pItem!=&m_Root && !proc(this, pItem, lParam)) + return FALSE; + + // recurse thru all child items + pNext = pItem->GetChild(); + + while (pNext) + { + if (!EnumItems(pNext, proc, lParam)) + return FALSE; + + pNext = pNext->GetSibling(); + } + + return TRUE; +} + + +void CPropTree::SetOriginOffset(LONG nOffset) +{ + m_Origin.y = nOffset; +} + + +void CPropTree::UpdatedItems() +{ + if (!IsWindow(m_hWnd)) + return; + + Invalidate(); + + m_List.UpdateResize(); + m_List.Invalidate(); +} + + +void CPropTree::DeleteAllItems() +{ + Delete(NULL); + UpdatedItems(); + m_nLastUID = 1; // reset uid counter +} + + +void CPropTree::DeleteItem(CPropTreeItem* pItem) +{ + Delete(pItem); + UpdatedItems(); +} + + +LONG CPropTree::GetColumn() +{ + return m_Origin.x; +} + + +void CPropTree::SetColumn(LONG nColumn) +{ + CRect rc; + + GetClientRect(rc); + + if (rc.IsRectEmpty()) + nColumn = __max(PROPTREEITEM_EXPANDCOLUMN, nColumn); + else + nColumn = __min(__max(PROPTREEITEM_EXPANDCOLUMN, nColumn), rc.Width() - PROPTREEITEM_EXPANDCOLUMN); + + m_Origin.x = nColumn; + + Invalidate(); +} + + +void CPropTree::Delete(CPropTreeItem* pItem) +{ + if (pItem && pItem!=&m_Root && SendNotify(PTN_DELETEITEM, pItem)) + return; + + // passing in a NULL item is the same as calling DeleteAllItems + if (!pItem) + pItem = &m_Root; + + // Clear the visible list before anything gets deleted + ClearVisibleList(); + + // delete children + + CPropTreeItem* pIter; + CPropTreeItem* pNext; + + pIter = pItem->GetChild(); + while (pIter) + { + pNext = pIter->GetSibling(); + DeleteItem(pIter); + pIter = pNext; + } + + // unlink from tree + if (pItem->GetParent()) + { + if (pItem->GetParent()->GetChild()==pItem) + pItem->GetParent()->SetChild(pItem->GetSibling()); + else + { + pIter = pItem->GetParent()->GetChild(); + while (pIter->GetSibling() && pIter->GetSibling()!=pItem) + pIter = pIter->GetSibling(); + + if (pIter->GetSibling()) + pIter->SetSibling(pItem->GetSibling()); + } + } + + if (pItem!=&m_Root) + { + if (pItem==GetFocusedItem()) + SetFocusedItem(NULL); + delete pItem; + } +} + + +void CPropTree::SetFocusedItem(CPropTreeItem* pItem) +{ + m_pFocus = pItem; + EnsureVisible(m_pFocus); + + if (!IsWindow(m_hWnd)) + return; + + Invalidate(); +} + + +void CPropTree::ShowInfoText(BOOL bShow) +{ + m_bShowInfo = bShow; + + CRect rc; + + GetClientRect(rc); + ResizeChildWindows(rc.Width(), rc.Height()); +} + + +BOOL CPropTree::IsItemVisible(CPropTreeItem* pItem) +{ + if (!pItem) + return FALSE; + + for (CPropTreeItem* pNext = m_pVisbleList; pNext; pNext = pNext->GetNextVisible()) + { + if (pNext==pItem) + return TRUE; + } + + return FALSE; +} + + +void CPropTree::EnsureVisible(CPropTreeItem* pItem) +{ + if (!pItem) + return; + + // item is not scroll visible (expand all parents) + if (!IsItemVisible(pItem)) + { + CPropTreeItem* pParent; + + pParent = pItem->GetParent(); + while (pParent) + { + pParent->Expand(); + pParent = pParent->GetParent(); + } + + UpdatedItems(); + UpdateWindow(); + } + + ASSERT(IsItemVisible(pItem)); + + CRect rc; + + m_List.GetClientRect(rc); + rc.OffsetRect(0, m_Origin.y); + rc.bottom -= pItem->GetHeight(); + + CPoint pt; + + pt = pItem->GetLocation(); + + if (!rc.PtInRect(pt)) + { + LONG oy; + + if (pt.y < rc.top) + oy = pt.y; + else + oy = pt.y - rc.Height() + pItem->GetHeight(); + + m_List.OnVScroll(SB_THUMBTRACK, oy, NULL); + } +} + + +CPropTreeItem* CPropTree::InsertItem(CPropTreeItem* pItem, CPropTreeItem* pParent) +{ + if (!pItem) + return NULL; + + if (!pParent) + pParent = &m_Root; + + if (!pParent->GetChild()) + pParent->SetChild(pItem); + else + { + // add to end of the sibling list + CPropTreeItem* pNext; + + pNext = pParent->GetChild(); + while (pNext->GetSibling()) + pNext = pNext->GetSibling(); + + pNext->SetSibling(pItem); + } + + pItem->SetParent(pParent); + pItem->SetPropOwner(this); + + // auto generate a default ID + pItem->SetCtrlID(m_nLastUID++); + + SendNotify(PTN_INSERTITEM, pItem); + + UpdatedItems(); + + return pItem; +} + + + +LONG CPropTree::HitTest(const POINT& pt) +{ + POINT p = pt; + + CPropTreeItem* pItem; + + // convert screen to tree coordinates + p.y += m_Origin.y; + + if ((pItem = FindItem(pt))!=NULL) + { + if (!pItem->IsRootLevel() && pt.x >= m_Origin.x - PROPTREEITEM_COLRNG && pt.x <= m_Origin.x + PROPTREEITEM_COLRNG) + return HTCOLUMN; + + if (pItem->HitButton(p)) { + return HTBUTTON; + } + + if (pt.x > m_Origin.x + PROPTREEITEM_COLRNG) + return HTATTRIBUTE; + + if (pItem->HitExpand(p)) + return HTEXPAND; + + if (pItem->HitCheckBox(p)) + return HTCHECKBOX; + + return HTLABEL; + } + + return HTCLIENT; +} + + +CPropTreeItem* CPropTree::FindItem(const POINT& pt) +{ + CPropTreeItem* pItem; + + CPoint p = pt; + + // convert screen to tree coordinates + p.y += m_Origin.y; + + // search the visible list for the item + for (pItem = m_pVisbleList; pItem; pItem = pItem->GetNextVisible()) + { + CPoint ipt = pItem->GetLocation(); + if (p.y>=ipt.y && p.yGetHeight()) + return pItem; + } + + return NULL; +} + + +CPropTreeItem* CPropTree::FindItem(UINT nCtrlID) +{ + s_pFound = NULL; + + EnumItems(&m_Root, EnumFindItem, nCtrlID); + + return s_pFound; +} + + +BOOL CALLBACK CPropTree::EnumFindItem(CPropTree*, CPropTreeItem* pItem, LPARAM lParam) +{ + ASSERT(pItem!=NULL); + + if (pItem->GetCtrlID()==(UINT)lParam) + { + s_pFound = pItem; + return FALSE; + } + + return TRUE; +} + + +BOOL CPropTree::IsDisableInput() +{ + return m_bDisableInput; +} + + +void CPropTree::DisableInput(BOOL bDisable) +{ + m_bDisableInput = bDisable; + + CWnd* pWnd; + + if ((pWnd = GetParent())!=NULL) + pWnd->EnableWindow(!bDisable); +} + + +void CPropTree::SelectItems(CPropTreeItem* pItem, BOOL bSelect) +{ + if (!pItem) + pItem = &m_Root; + + EnumItems(pItem, EnumSelectAll, (LPARAM)bSelect); +} + + +CPropTreeItem* CPropTree::FocusFirst() +{ + CPropTreeItem *pold; + + pold = m_pFocus; + + SetFocusedItem(m_pVisbleList); + + if (m_pFocus) + { + SelectItems(NULL, FALSE); + m_pFocus->Select(); + } + + if (pold!=m_pFocus) + SendNotify(PTN_SELCHANGE, m_pFocus); + + return m_pFocus; +} + + +CPropTreeItem* CPropTree::FocusLast() +{ + CPropTreeItem* pNext; + CPropTreeItem* pChange; + + pChange = m_pFocus; + + pNext = m_pVisbleList; + + if (pNext) + { + while (pNext->GetNextVisible()) + pNext = pNext->GetNextVisible(); + + SetFocusedItem(pNext); + + if (m_pFocus) + { + SelectItems(NULL, FALSE); + m_pFocus->Select(); + } + } + + if (pChange!=m_pFocus) + SendNotify(PTN_SELCHANGE, m_pFocus); + + return pNext; +} + + +CPropTreeItem* CPropTree::FocusPrev() +{ + CPropTreeItem* pNext; + CPropTreeItem* pChange; + + pChange = m_pFocus; + + if (m_pFocus==NULL) + { + // get the last visible item + pNext = m_pVisbleList; + while (pNext && pNext->GetNextVisible()) + pNext = pNext->GetNextVisible(); + } + else + { + pNext = m_pVisbleList; + while (pNext && pNext->GetNextVisible()!=m_pFocus) + pNext = pNext->GetNextVisible(); + } + + if (pNext) + SetFocusedItem(pNext); + + if (m_pFocus) + { + SelectItems(NULL, FALSE); + m_pFocus->Select(); + } + + if (pChange!=m_pFocus) + SendNotify(PTN_SELCHANGE, m_pFocus); + + return pNext; +} + + +CPropTreeItem* CPropTree::FocusNext() +{ + CPropTreeItem* pNext; + CPropTreeItem* pChange; + + pChange = m_pFocus; + + if (m_pFocus==NULL) + pNext = m_pVisbleList; + else + if (m_pFocus->GetNextVisible()) + pNext = m_pFocus->GetNextVisible(); + else + pNext = NULL; + + if (pNext) + SetFocusedItem(pNext); + + if (m_pFocus) + { + SelectItems(NULL, FALSE); + m_pFocus->Select(); + } + + if (pChange!=m_pFocus) + SendNotify(PTN_SELCHANGE, m_pFocus); + + return pNext; +} + + +void CPropTree::UpdateMoveAllItems() +{ + EnumItems(&m_Root, EnumMoveAll); +} + + +void CPropTree::RefreshItems(CPropTreeItem* pItem) +{ + if (!pItem) + pItem = &m_Root; + + EnumItems(pItem, EnumRefreshAll); + + UpdatedItems(); +} + + +BOOL CALLBACK CPropTree::EnumSelectAll(CPropTree*, CPropTreeItem* pItem, LPARAM lParam) +{ + if (!pItem) + return FALSE; + + pItem->Select((BOOL)lParam); + + return TRUE; +} + + +BOOL CALLBACK CPropTree::EnumRefreshAll(CPropTree*, CPropTreeItem* pItem, LPARAM) +{ + if (!pItem) + return FALSE; + + pItem->OnRefresh(); + + return TRUE; +} + + +BOOL CALLBACK CPropTree::EnumMoveAll(CPropTree*, CPropTreeItem* pItem, LPARAM) +{ + if (!pItem) + return FALSE; + + pItem->OnMove(); + + return TRUE; +} + + +LRESULT CPropTree::SendNotify(UINT nNotifyCode, CPropTreeItem* pItem) +{ + if (!IsWindow(m_hWnd)) + return 0L; + + if (!(GetStyle() & PTS_NOTIFY)) + return 0L; + + NMPROPTREE nmmp; + LPNMHDR lpnm; + + lpnm = NULL; + + switch (nNotifyCode) + { + case PTN_INSERTITEM: + case PTN_DELETEITEM: + case PTN_DELETEALLITEMS: + case PTN_ITEMCHANGED: + case PTN_ITEMBUTTONCLICK: + case PTN_SELCHANGE: + case PTN_ITEMEXPANDING: + case PTN_COLUMNCLICK: + case PTN_PROPCLICK: + case PTN_CHECKCLICK: + lpnm = (LPNMHDR)&nmmp; + nmmp.pItem = pItem; + break; + } + + if (lpnm) + { + UINT id = (UINT)::GetMenu(m_hWnd); + lpnm->code = nNotifyCode; + lpnm->hwndFrom = m_hWnd; + lpnm->idFrom = id; + + return GetParent()->SendMessage(WM_NOTIFY, (WPARAM)id, (LPARAM)lpnm); + } + + return 0L; +} + + +void CPropTree::OnEnable(BOOL bEnable) +{ + CWnd::OnEnable(bEnable); + Invalidate(); +} + + +void CPropTree::OnSysColorChange() +{ + CWnd::OnSysColorChange(); + + Invalidate(); +} + + +BOOL CPropTree::IsSingleSelection() +{ + // right now only support single selection + return TRUE; +} \ No newline at end of file diff --git a/src/tools/common/PropTree/PropTree.h b/src/tools/common/PropTree/PropTree.h new file mode 100644 index 0000000..64977d7 --- /dev/null +++ b/src/tools/common/PropTree/PropTree.h @@ -0,0 +1,290 @@ +// PropTree.h : header file +// +// Copyright (C) 1998-2001 Scott Ramsay +// sramsay@gonavi.com +// http://www.gonavi.com +// +// This material is provided "as is", with absolutely no warranty expressed +// or implied. Any use is at your own risk. +// +// Permission to use or copy this software for any purpose is hereby granted +// without fee, provided the above notices are retained on all copies. +// Permission to modify the code and to distribute modified code is granted, +// provided the above notices are retained, and a notice that the code was +// modified is included with the above copyright notice. +// +// If you use this code, drop me an email. I'd like to know if you find the code +// useful. + +#if !defined(AFX_PROPT_H__386AA426_6FB7_4B4B_9563_C4CC045BB0C9__INCLUDED_) +#define AFX_PROPT_H__386AA426_6FB7_4B4B_9563_C4CC045BB0C9__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +/*#ifdef _PROPTREE_EXPORT +#define PROPTREE_API __declspec(dllexport) +#else +#define PROPTREE_API __declspec(dllimport) +#endif + +#ifndef _PROPTREE_DLL + #ifdef _UNICODE + #ifdef _DEBUG + #pragma comment(lib, "PropTreeDU") + #pragma message("Automatically linking with PropTreeDU.dll (Debug Unicode)") + #else + #pragma comment(lib, "PropTreeU") + #pragma message("Automatically linking with PropTreeU.dll (Release Unicode)") + #endif + #else + #ifdef _DEBUG + #pragma comment(lib, "PropTreeD") + #pragma message("Automatically linking with PropTreeD.dll (Debug)") + #else + #pragma comment(lib, "PropTree") + #pragma message("Automatically linking with PropTree.dll (Release)") + #endif + #endif // _UNICODE +#endif // _PROPTREE_DLL +*/ + +#define PROPTREE_API + +#include "PropTreeList.h" +#include "PropTreeInfo.h" + +#include "PropTreeItem.h" +#include "PropTreeItemStatic.h" +#include "PropTreeItemEdit.h" +#include "PropTreeItemCombo.h" +#include "PropTreeItemColor.h" +#include "PropTreeItemCheck.h" +#include "PropTreeItemButton.h" +#include "PropTreeItemEditButton.h" +#include "PropTreeItemFileEdit.h" + +class CPropTree; + +typedef BOOL (CALLBACK* ENUMPROPITEMPROC)(CPropTree*, CPropTreeItem*, LPARAM); + +void InitPropTree(HINSTANCE hInstance); + +// CPropTree window styles +#define PTS_NOTIFY 0x00000001 + +// CPropTree HitTest return codes +#define HTPROPFIRST 50 + +#define HTLABEL (HTPROPFIRST + 0) +#define HTCOLUMN (HTPROPFIRST + 1) +#define HTEXPAND (HTPROPFIRST + 2) +#define HTATTRIBUTE (HTPROPFIRST + 3) +#define HTCHECKBOX (HTPROPFIRST + 4) +#define HTBUTTON (HTPROPFIRST + 5) + +// CPropTree WM_NOTIFY notification structure +typedef struct _NMPROPTREE +{ + NMHDR hdr; + CPropTreeItem* pItem; +} NMPROPTREE, *PNMPROPTREE, FAR *LPNMPROPTREE; + +// CPropTree specific Notification Codes +#define PTN_FIRST (0U-1100U) + +#define PTN_INSERTITEM (PTN_FIRST-1) +#define PTN_DELETEITEM (PTN_FIRST-2) +#define PTN_DELETEALLITEMS (PTN_FIRST-3) +#define PTN_ITEMCHANGED (PTN_FIRST-5) +#define PTN_ITEMBUTTONCLICK (PTN_FIRST-6) +#define PTN_SELCHANGE (PTN_FIRST-7) +#define PTN_ITEMEXPANDING (PTN_FIRST-8) +#define PTN_COLUMNCLICK (PTN_FIRST-9) +#define PTN_PROPCLICK (PTN_FIRST-10) +#define PTN_CHECKCLICK (PTN_FIRST-12) + +///////////////////////////////////////////////////////////////////////////// +// CPropTree window + +class PROPTREE_API CPropTree : public CWnd +{ +// Construction +public: + CPropTree(); + virtual ~CPropTree(); + + BOOL Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID); + +// Attributes/Operations +public: + static CFont* GetNormalFont(); + static CFont* GetBoldFont(); + + // Returns the root item of the tree + CPropTreeItem* GetRootItem(); + + // Returns the focused item or NULL for none + CPropTreeItem* GetFocusedItem(); + + // Enumerates an item and all its child items + BOOL EnumItems(CPropTreeItem* pItem, ENUMPROPITEMPROC proc, LPARAM lParam = 0L); + + // Insert a created CPropTreeItem into the control + CPropTreeItem* InsertItem(CPropTreeItem* pItem, CPropTreeItem* pParent = NULL); + + // Delete an item and ALL its children + void DeleteItem(CPropTreeItem* pItem); + + // Delete all items from the tree + void DeleteAllItems(); + + // Return the splitter position + LONG GetColumn(); + + // Set the splitter position + void SetColumn(LONG nColumn); + + // Sets the focused item + void SetFocusedItem(CPropTreeItem* pItem); + + // Show or hide the info text + void ShowInfoText(BOOL bShow = TRUE); + + // Returns TRUE if the item is visible (its parent is expanded) + BOOL IsItemVisible(CPropTreeItem* pItem); + + // Ensures that an item is visible + void EnsureVisible(CPropTreeItem* pItem); + + // do a hit test on the control (returns a HTxxxx code) + LONG HitTest(const POINT& pt); + + // find an item by a location + CPropTreeItem* FindItem(const POINT& pt); + + // find an item by item id + CPropTreeItem* FindItem(UINT nCtrlID); + +protected: + // Actual tree control + CPropTreeList m_List; + + // Descriptive control + CPropTreeInfo m_Info; + + // TRUE to show info control + BOOL m_bShowInfo; + + // Height of the info control + LONG m_nInfoHeight; + + // Root level tree item + CPropTreeItem m_Root; + + // Linked list of visible items + CPropTreeItem* m_pVisbleList; + + // Pointer to the focused item (selected) + CPropTreeItem* m_pFocus; + + // PropTree scroll position. x = splitter position, y = vscroll position + CPoint m_Origin; + + // auto generated last created ID + UINT m_nLastUID; + + // Number of CPropTree controls in the current application + static UINT s_nInstanceCount; + + static CFont* s_pNormalFont; + static CFont* s_pBoldFont; + + BOOL m_bDisableInput; + + // Used for enumeration + static CPropTreeItem* s_pFound; + +public: + // + // functions used by CPropTreeItem (you normally dont need to call these directly) + // + + void AddToVisibleList(CPropTreeItem* pItem); + void ClearVisibleList(); + + void SetOriginOffset(LONG nOffset); + void UpdatedItems(); + void UpdateMoveAllItems(); + void RefreshItems(CPropTreeItem* pItem = NULL); + + // enable or disable tree input + void DisableInput(BOOL bDisable = TRUE); + BOOL IsDisableInput(); + + BOOL IsSingleSelection(); + + CPropTreeItem* GetVisibleList(); + CWnd* GetCtrlParent(); + + const POINT& GetOrigin(); + + void SelectItems(CPropTreeItem* pItem, BOOL bSelect = TRUE); + + // Focus on the first visible item + CPropTreeItem *FocusFirst(); + + // Focus on the last visible item + CPropTreeItem *FocusLast(); + + // Focus on the previous item + CPropTreeItem *FocusPrev(); + + // Focus on the next item + CPropTreeItem *FocusNext(); + + LRESULT SendNotify(UINT nNotifyCode, CPropTreeItem* pItem = NULL); + +protected: + // Resize the child windows to fit the exact dimensions the CPropTree control + void ResizeChildWindows(int cx, int cy); + + // Initialize global resources, brushes, fonts, etc. + void InitGlobalResources(); + + // Free global resources, brushes, fonts, etc. + void FreeGlobalResources(); + + // Recursive version of DeleteItem + void Delete(CPropTreeItem* pItem); + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CPropTree) + //}}AFX_VIRTUAL + +// Implementation +private: + static BOOL CALLBACK EnumFindItem(CPropTree* pProp, CPropTreeItem* pItem, LPARAM lParam); + static BOOL CALLBACK EnumSelectAll(CPropTree*, CPropTreeItem* pItem, LPARAM lParam); + static BOOL CALLBACK EnumMoveAll(CPropTree*, CPropTreeItem* pItem, LPARAM); + static BOOL CALLBACK EnumRefreshAll(CPropTree*, CPropTreeItem* pItem, LPARAM); + + // Generated message map functions +protected: + //{{AFX_MSG(CPropTree) + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnEnable(BOOL bEnable); + afx_msg void OnSysColorChange(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_PROPT_H__386AA426_6FB7_4B4B_9563_C4CC045BB0C9__INCLUDED_) diff --git a/src/tools/common/PropTree/PropTreeInfo.cpp b/src/tools/common/PropTree/PropTreeInfo.cpp new file mode 100644 index 0000000..4603a79 --- /dev/null +++ b/src/tools/common/PropTree/PropTreeInfo.cpp @@ -0,0 +1,110 @@ +// PropTreeInfo.cpp : implementation file +// +// Copyright (C) 1998-2001 Scott Ramsay +// sramsay@gonavi.com +// http://www.gonavi.com +// +// This material is provided "as is", with absolutely no warranty expressed +// or implied. Any use is at your own risk. +// +// Permission to use or copy this software for any purpose is hereby granted +// without fee, provided the above notices are retained on all copies. +// Permission to modify the code and to distribute modified code is granted, +// provided the above notices are retained, and a notice that the code was +// modified is included with the above copyright notice. +// +// If you use this code, drop me an email. I'd like to know if you find the code +// useful. + +//#include "stdafx.h" +#include "../../../idlib/precompiled.h" +#pragma hdrstop + +#include "PropTree.h" +#include "../../../sys/win32/rc/proptree_Resource.h" +#include "PropTreeInfo.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CPropTreeInfo + +CPropTreeInfo::CPropTreeInfo() : + m_pProp(NULL) +{ +} + +CPropTreeInfo::~CPropTreeInfo() +{ +} + + +BEGIN_MESSAGE_MAP(CPropTreeInfo, CStatic) + //{{AFX_MSG_MAP(CPropTreeInfo) + ON_WM_PAINT() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CPropTreeInfo message handlers + +void CPropTreeInfo::SetPropOwner(CPropTree* pProp) +{ + m_pProp = pProp; +} + +void CPropTreeInfo::OnPaint() +{ + CPaintDC dc(this); + CRect rc; + + GetClientRect(rc); + + dc.SelectObject(GetSysColorBrush(COLOR_BTNFACE)); + dc.PatBlt(rc.left, rc.top, rc.Width(), rc.Height(), PATCOPY); + + dc.DrawEdge(&rc, BDR_SUNKENOUTER, BF_RECT); + rc.DeflateRect(4, 4); + + ASSERT(m_pProp!=NULL); + + CPropTreeItem* pItem = m_pProp->GetFocusedItem(); + + if (!m_pProp->IsWindowEnabled()) + dc.SetTextColor(GetSysColor(COLOR_GRAYTEXT)); + else + dc.SetTextColor(GetSysColor(COLOR_BTNTEXT)); + + dc.SetBkMode(TRANSPARENT); + dc.SelectObject(m_pProp->GetBoldFont()); + + CString txt; + + if (!pItem) + txt.LoadString(IDS_NOITEMSEL); + else + txt = pItem->GetLabelText(); + + CRect ir; + ir = rc; + + // draw label + dc.DrawText(txt, &ir, DT_SINGLELINE|DT_CALCRECT); + dc.DrawText(txt, &ir, DT_SINGLELINE); + + ir.top = ir.bottom; + ir.bottom = rc.bottom; + ir.right = rc.right; + + if (pItem) + txt = pItem->GetInfoText(); + else + txt.LoadString(IDS_SELFORINFO); + + dc.SelectObject(m_pProp->GetNormalFont()); + dc.DrawText(txt, &ir, DT_WORDBREAK); +} diff --git a/src/tools/common/PropTree/PropTreeInfo.h b/src/tools/common/PropTree/PropTreeInfo.h new file mode 100644 index 0000000..6c4a902 --- /dev/null +++ b/src/tools/common/PropTree/PropTreeInfo.h @@ -0,0 +1,71 @@ +#if !defined(AFX_PROPTREEINFO_H__22BD9C18_A68C_4BB8_B7FC_C4A7DA0E1EBF__INCLUDED_) +#define AFX_PROPTREEINFO_H__22BD9C18_A68C_4BB8_B7FC_C4A7DA0E1EBF__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// PropTreeInfo.h : header file +// +// +// Copyright (C) 1998-2001 Scott Ramsay +// sramsay@gonavi.com +// http://www.gonavi.com +// +// This material is provided "as is", with absolutely no warranty expressed +// or implied. Any use is at your own risk. +// +// Permission to use or copy this software for any purpose is hereby granted +// without fee, provided the above notices are retained on all copies. +// Permission to modify the code and to distribute modified code is granted, +// provided the above notices are retained, and a notice that the code was +// modified is included with the above copyright notice. +// +// If you use this code, drop me an email. I'd like to know if you find the code +// useful. + +class CPropTree; + +///////////////////////////////////////////////////////////////////////////// +// CPropTreeInfo window + +class PROPTREE_API CPropTreeInfo : public CStatic +{ +// Construction +public: + CPropTreeInfo(); + +// Attributes +public: + // CPropTree class that this class belongs + void SetPropOwner(CPropTree* pProp); + +protected: + CPropTree* m_pProp; + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CPropTreeInfo) + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~CPropTreeInfo(); + + // Generated message map functions +protected: + //{{AFX_MSG(CPropTreeInfo) + afx_msg void OnPaint(); + //}}AFX_MSG + + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_PROPTREEINFO_H__22BD9C18_A68C_4BB8_B7FC_C4A7DA0E1EBF__INCLUDED_) diff --git a/src/tools/common/PropTree/PropTreeItem.cpp b/src/tools/common/PropTree/PropTreeItem.cpp new file mode 100644 index 0000000..17546a5 --- /dev/null +++ b/src/tools/common/PropTree/PropTreeItem.cpp @@ -0,0 +1,590 @@ +// PropTreeItem.cpp +// +// Copyright (C) 1998-2001 Scott Ramsay +// sramsay@gonavi.com +// http://www.gonavi.com +// +// This material is provided "as is", with absolutely no warranty expressed +// or implied. Any use is at your own risk. +// +// Permission to use or copy this software for any purpose is hereby granted +// without fee, provided the above notices are retained on all copies. +// Permission to modify the code and to distribute modified code is granted, +// provided the above notices are retained, and a notice that the code was +// modified is included with the above copyright notice. +// +// If you use this code, drop me an email. I'd like to know if you find the code +// useful. + +//#include "stdafx.h" +#include "../../../idlib/precompiled.h" +#pragma hdrstop + +#include "PropTree.h" + +#include "PropTreeItem.h" + +#define PROPTREEITEM_DEFHEIGHT 21 // default heigt of an item +#define PROPTREEITEM_SPACE 5 // default horz spacing +#define PROPTREEITEM_EXPANDBOX 9 // size of the expand box +#define PROPTREEITEM_CHECKBOX 14 // size of the check box +#define PROPTREEITEM_EXPANDCOLUMN 16 // width of the expand column +#define PNINDENT 16 // child level indent + +#define PROPTREEITEM_EXPANDBOXHALF (PROPTREEITEM_EXPANDBOX/2) + + +///////////////////////////////////////////////////////////////////////////// +// drawing helper functions +// + +// draw a dotted horizontal line +static void _DotHLine(HDC hdc, LONG x, LONG y, LONG w) +{ + for (; w>0; w-=2, x+=2) + SetPixel(hdc, x, y, GetSysColor(COLOR_BTNSHADOW)); +} + + +// draw the plus/minus button +static void _DrawExpand(HDC hdc, LONG x, LONG y, BOOL bExpand, BOOL bFill) +{ + HPEN hPen; + HPEN oPen; + HBRUSH oBrush; + + hPen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNSHADOW)); + oPen = (HPEN)SelectObject(hdc, hPen); + oBrush = (HBRUSH)SelectObject(hdc, GetStockObject(bFill ? WHITE_BRUSH : NULL_BRUSH)); + + Rectangle(hdc, x, y, x + PROPTREEITEM_EXPANDBOX, y + PROPTREEITEM_EXPANDBOX); + SelectObject(hdc, GetStockObject(BLACK_PEN)); + + if (!bExpand) + { + MoveToEx(hdc, x + PROPTREEITEM_EXPANDBOXHALF, y + 2, NULL); + LineTo(hdc, x + PROPTREEITEM_EXPANDBOXHALF, y + PROPTREEITEM_EXPANDBOX - 2); + } + + MoveToEx(hdc, x + 2, y + PROPTREEITEM_EXPANDBOXHALF, NULL); + LineTo(hdc, x + PROPTREEITEM_EXPANDBOX - 2, y + PROPTREEITEM_EXPANDBOXHALF); + + SelectObject(hdc, oPen); + SelectObject(hdc, oBrush); + DeleteObject(hPen); +} + +///////////////////////////////////////////////////////////////////////////// +// CPropTreeItem +// + +CPropTreeItem::CPropTreeItem() : + m_pProp(NULL), + m_sLabel(_T("")), + m_sInfo(_T("")), + m_loc(0,0), + m_rc(0,0,0,0), + m_lParam(0), + m_nCtrlID(0), + m_dwState(0), + m_bActivated(FALSE), + m_bCommitOnce(FALSE), + m_rcExpand(0,0,0,0), + m_rcCheckbox(0,0,0,0), + m_rcButton(0,0,0,0), + m_pParent(NULL), + m_pSibling(NULL), + m_pChild(NULL), + m_pVis(NULL) +{ +} + + +CPropTreeItem::~CPropTreeItem() +{ +} + + +BOOL CPropTreeItem::IsExpanded() +{ + return (m_dwState & TreeItemExpanded) ? TRUE : FALSE; +} + + +BOOL CPropTreeItem::IsSelected() +{ + return (m_dwState & TreeItemSelected) ? TRUE : FALSE; +} + + +BOOL CPropTreeItem::IsChecked() +{ + return (m_dwState & TreeItemChecked) ? TRUE : FALSE; +} + + +BOOL CPropTreeItem::IsReadOnly() +{ + return (m_dwState & TreeItemReadOnly) ? TRUE : FALSE; +} + + +BOOL CPropTreeItem::IsActivated() +{ + return (m_dwState & TreeItemActivated) ? TRUE : FALSE; +} + + +void CPropTreeItem::Select(BOOL bSelect) +{ + if (bSelect) + m_dwState |= TreeItemSelected; + else + m_dwState &= ~TreeItemSelected; +} + + +void CPropTreeItem::Expand(BOOL bExpand) +{ + if (bExpand) + m_dwState |= TreeItemExpanded; + else + m_dwState &= ~TreeItemExpanded; +} + + +void CPropTreeItem::Check(BOOL bCheck) +{ + if (bCheck) + m_dwState |= TreeItemChecked; + else + m_dwState &= ~TreeItemChecked; +} + + +void CPropTreeItem::ReadOnly(BOOL bReadOnly) +{ + if (bReadOnly) + m_dwState |= TreeItemReadOnly; + else + m_dwState &= ~TreeItemReadOnly; +} + + +BOOL CPropTreeItem::IsCheckBox() +{ + return (m_dwState & TreeItemCheckbox) ? TRUE : FALSE; +} + + +void CPropTreeItem::HasCheckBox(BOOL bCheckbox) +{ + if (bCheckbox) + m_dwState |= TreeItemCheckbox; + else + m_dwState &= ~TreeItemCheckbox; +} + + +BOOL CPropTreeItem::HitExpand(const POINT& pt) +{ + return m_rcExpand.PtInRect(pt); +} + + +BOOL CPropTreeItem::HitCheckBox(const POINT& pt) +{ + return m_rcCheckbox.PtInRect(pt); +} + + +BOOL CPropTreeItem::IsRootLevel() +{ + ASSERT(m_pProp!=NULL); + return GetParent() == m_pProp->GetRootItem(); +} + + +LONG CPropTreeItem::GetTotalHeight() +{ + CPropTreeItem* pItem; + LONG nHeight; + + nHeight = GetHeight(); + + if (IsExpanded()) + { + for (pItem = GetChild(); pItem; pItem = pItem->GetSibling()) + nHeight += pItem->GetTotalHeight(); + } + + return nHeight; +} + + +void CPropTreeItem::SetLabelText(LPCTSTR sLabel) +{ + m_sLabel = sLabel; +} + + +LPCTSTR CPropTreeItem::GetLabelText() +{ + return m_sLabel; +} + + +void CPropTreeItem::SetInfoText(LPCTSTR sInfo) +{ + m_sInfo = sInfo; +} + + +LPCTSTR CPropTreeItem::GetInfoText() +{ + return m_sInfo; +} + + +void CPropTreeItem::SetCtrlID(UINT nCtrlID) +{ + m_nCtrlID = nCtrlID; +} + + +UINT CPropTreeItem::GetCtrlID() +{ + return m_nCtrlID; +} + + +LONG CPropTreeItem::GetHeight() +{ + return PROPTREEITEM_DEFHEIGHT; +} + + +LPARAM CPropTreeItem::GetItemValue() +{ + // no items are assocatied with this type + return 0L; +} + + +void CPropTreeItem::SetItemValue(LPARAM) +{ + // no items are assocatied with this type +} + + +void CPropTreeItem::OnMove() +{ + // no attributes, do nothing +} + + +void CPropTreeItem::OnRefresh() +{ + // no attributes, do nothing +} + + +void CPropTreeItem::OnCommit() +{ + // no attributes, do nothing +} + + +void CPropTreeItem::Activate(int activateType, CPoint point) +{ + m_bActivated = TRUE; + m_bCommitOnce = FALSE; + + OnActivate(activateType, point); +} + + +void CPropTreeItem::CommitChanges() +{ + m_bActivated = FALSE; + + if (m_bCommitOnce) + return; + + m_bCommitOnce = TRUE; + + ASSERT(m_pProp!=NULL); + + OnCommit(); + + m_pProp->SendNotify(PTN_ITEMCHANGED, this); + m_pProp->RefreshItems(this); +} + + +void CPropTreeItem::OnActivate(int activateType, CPoint point) +{ + // no attributes, do nothing +} + + +void CPropTreeItem::SetPropOwner(CPropTree* pProp) +{ + m_pProp = pProp; +} + + +const POINT& CPropTreeItem::GetLocation() +{ + return m_loc; +} + + +CPropTreeItem* CPropTreeItem::GetParent() +{ + return m_pParent; +} + + +CPropTreeItem* CPropTreeItem::GetSibling() +{ + return m_pSibling; +} + + +CPropTreeItem* CPropTreeItem::GetChild() +{ + return m_pChild; +} + + +CPropTreeItem* CPropTreeItem::GetNextVisible() +{ + return m_pVis; +} + + +void CPropTreeItem::SetParent(CPropTreeItem* pParent) +{ + m_pParent = pParent; +} + + +void CPropTreeItem::SetSibling(CPropTreeItem* pSibling) +{ + m_pSibling = pSibling; +} + + +void CPropTreeItem::SetChild(CPropTreeItem* pChild) +{ + m_pChild = pChild; +} + + +void CPropTreeItem::SetNextVisible(CPropTreeItem* pVis) +{ + m_pVis = pVis; +} + + +LONG CPropTreeItem::DrawItem(CDC* pDC, const RECT& rc, LONG x, LONG y) +{ + CPoint pt; + LONG nTotal, nCol, ey; + CRect drc, ir; + + ASSERT(m_pProp!=NULL); + + // Add TreeItem the list of visble items + m_pProp->AddToVisibleList(this); + + // store the item's location + m_loc = CPoint(x, y); + + // store the items rectangle position + m_rc.SetRect(m_pProp->GetOrigin().x + PROPTREEITEM_SPACE, m_loc.y, rc.right, m_loc.y + GetHeight()-1); + m_rc.OffsetRect(0, -m_pProp->GetOrigin().y); + + // init temp drawing variables + nTotal = GetHeight(); + ey = (nTotal >> 1) - (PROPTREEITEM_EXPANDBOX >> 1) - 2; + + bool bCheck = false; + + // convert item coordinates to screen coordinates + pt = m_loc; + pt.y -= m_pProp->GetOrigin().y; + nCol = m_pProp->GetOrigin().x; + + if (IsRootLevel()) + drc.SetRect(pt.x + PROPTREEITEM_EXPANDCOLUMN, pt.y, rc.right, pt.y + nTotal); + else + drc.SetRect(pt.x + PROPTREEITEM_EXPANDCOLUMN, pt.y, nCol, pt.y + nTotal); + + // root level items are shaded + if (IsRootLevel()) + { + HGDIOBJ hOld = pDC->SelectObject(GetSysColorBrush(COLOR_BTNFACE)); + pDC->PatBlt(rc.left, drc.top, rc.right - rc.left + 1, drc.Height(), PATCOPY); + pDC->SelectObject(hOld); + } + + // calc/draw expand box position + if (GetChild()) + { + m_rcExpand.left = PROPTREEITEM_EXPANDCOLUMN/2 - PROPTREEITEM_EXPANDBOXHALF; + m_rcExpand.top = m_loc.y + ey; + m_rcExpand.right = m_rcExpand.left + PROPTREEITEM_EXPANDBOX - 1; + m_rcExpand.bottom = m_rcExpand.top + PROPTREEITEM_EXPANDBOX - 1; + + ir = m_rcExpand; + ir.OffsetRect(0, -m_pProp->GetOrigin().y); + _DrawExpand(pDC->m_hDC, ir.left, ir.top, IsExpanded(), !IsRootLevel()); + } + else + m_rcExpand.SetRectEmpty(); + + // calc/draw check box position + if (IsCheckBox()) + { + bCheck = true; + + ir.left = drc.left + PROPTREEITEM_SPACE; + ir.top = m_loc.y + ey; + + ir.right = ir.left + PROPTREEITEM_CHECKBOX; + ir.bottom = ir.top + PROPTREEITEM_CHECKBOX; + + m_rcCheckbox = ir; + } + else + m_rcCheckbox.SetRectEmpty(); + + HRGN hRgn = NULL; + + // create a clipping region for the label + if (!IsRootLevel()) + { + hRgn = CreateRectRgn(drc.left, drc.top, drc.right, drc.bottom); + SelectClipRgn(pDC->m_hDC, hRgn); + } + + // calc label position + ir = drc; + ir.left += PROPTREEITEM_SPACE; + + // offset the label text if item has a check box + if (bCheck) + OffsetRect(&ir, PROPTREEITEM_CHECKBOX + PROPTREEITEM_SPACE * 2, 0); + + // draw label + if (!m_sLabel.IsEmpty()) + { + if (IsRootLevel()) + pDC->SelectObject(CPropTree::GetBoldFont()); + else + pDC->SelectObject(CPropTree::GetNormalFont()); + + pDC->SetTextColor(GetSysColor(COLOR_BTNTEXT)); + pDC->SetBkMode(TRANSPARENT); + pDC->DrawText(m_sLabel, &ir, DT_SINGLELINE|DT_VCENTER|DT_CALCRECT); + + // draw the text highlighted if selected + if (IsSelected()) + { + HGDIOBJ oPen = pDC->SelectObject(GetStockObject(NULL_PEN)); + HGDIOBJ oBrush = pDC->SelectObject(GetSysColorBrush(COLOR_HIGHLIGHT)); + + CRect dr; + dr = drc; + dr.left = PROPTREEITEM_EXPANDCOLUMN; + + pDC->Rectangle(&dr); + + pDC->SelectObject(oPen); + pDC->SelectObject(oBrush); + + pDC->SetTextColor(GetSysColor(COLOR_BTNHIGHLIGHT)); + } + + // check if we need to draw the text as disabled + if (!m_pProp->IsWindowEnabled()) + pDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT)); + + pDC->DrawText(m_sLabel, &ir, DT_SINGLELINE|DT_VCENTER); + } + + // draw check box frame + if (IsCheckBox()) + { + ir = m_rcCheckbox; + ir.OffsetRect(0, -m_pProp->GetOrigin().y); + pDC->DrawFrameControl(&ir, DFC_BUTTON, DFCS_BUTTONCHECK | (IsChecked() ? DFCS_CHECKED : 0)); + } + + // remove clip region + if (hRgn) + { + SelectClipRgn(pDC->m_hDC, NULL); + DeleteObject(hRgn); + } + + // draw horzontal sep + _DotHLine(pDC->m_hDC, PROPTREEITEM_EXPANDCOLUMN, pt.y + nTotal - 1, rc.right - PROPTREEITEM_EXPANDCOLUMN + 1); + + // draw separators + if (!IsRootLevel()) + { + // column sep + CPen pn1(PS_SOLID, 1, GetSysColor(COLOR_BTNSHADOW)); + CPen* pOld; + + pOld = pDC->SelectObject(&pn1); + pDC->MoveTo(nCol, drc.top); + pDC->LineTo(nCol, drc.bottom); + + CPen pn2(PS_SOLID, 1, GetSysColor(COLOR_BTNHIGHLIGHT)); + pDC->SelectObject(&pn2); + pDC->MoveTo(nCol + 1, drc.top); + pDC->LineTo(nCol + 1, drc.bottom); + + pDC->SelectObject(pOld); + } + + // draw attribute + if (!IsRootLevel()) + { + // create clip region + hRgn = CreateRectRgn(m_rc.left, m_rc.top, m_rc.right, m_rc.bottom); + SelectClipRgn(pDC->m_hDC, hRgn); + + DrawAttribute(pDC, m_rc); + + SelectClipRgn(pDC->m_hDC, NULL); + DeleteObject(hRgn); + } + + // draw children + if (GetChild() && IsExpanded()) + { + y += nTotal; + + CPropTreeItem* pNext; + + for (pNext = GetChild(); pNext; pNext = pNext->GetSibling()) + { + LONG nHeight = pNext->DrawItem(pDC, rc, x + (IsRootLevel() ? 0 : PNINDENT), y); + nTotal += nHeight; + y += nHeight; + } + } + + return nTotal; +} + + +void CPropTreeItem::DrawAttribute(CDC*, const RECT&) +{ + // no attributes are assocatied with this type +} diff --git a/src/tools/common/PropTree/PropTreeItem.h b/src/tools/common/PropTree/PropTreeItem.h new file mode 100644 index 0000000..6eafdd2 --- /dev/null +++ b/src/tools/common/PropTree/PropTreeItem.h @@ -0,0 +1,203 @@ +// PropTreeItem.h +// +// Copyright (C) 1998-2001 Scott Ramsay +// sramsay@gonavi.com +// http://www.gonavi.com +// +// This material is provided "as is", with absolutely no warranty expressed +// or implied. Any use is at your own risk. +// +// Permission to use or copy this software for any purpose is hereby granted +// without fee, provided the above notices are retained on all copies. +// Permission to modify the code and to distribute modified code is granted, +// provided the above notices are retained, and a notice that the code was +// modified is included with the above copyright notice. +// +// If you use this code, drop me an email. I'd like to know if you find the code +// useful. + +#ifndef _PROPTREEITEM_H +#define _PROPTREEITEM_H + +class CPropTree; + +class PROPTREE_API CPropTreeItem +{ +// Construction +public: + CPropTreeItem(); + virtual ~CPropTreeItem(); + +// Attributes/Operations +public: + // TreeItem states + BOOL IsExpanded(); + BOOL IsSelected(); + BOOL IsChecked(); + BOOL IsReadOnly(); + BOOL IsActivated(); + + void Select(BOOL bSelect = TRUE); + void Expand(BOOL bExpand = TRUE); + void Check(BOOL bCheck = TRUE); + void ReadOnly(BOOL bReadOnly = TRUE); + + // Returns true if the item has a checkbox + BOOL IsCheckBox(); + + // Pass in true, for the item to have a checkbox + void HasCheckBox(BOOL bCheckbox = TRUE); + + // Returns TRUE if the point is on the expand button + BOOL HitExpand(const POINT& pt); + + // Returns TRUE if the point is on the check box + BOOL HitCheckBox(const POINT& pt); + + // Overrideable - Returns TRUE if the point is on the button + virtual BOOL HitButton(const POINT& pt) { return false;} + + // Returns TRUE if the item is on the root level. Root level items don't have attribute areas + BOOL IsRootLevel(); + + // Returns the total height of the item and all its children + LONG GetTotalHeight(); + + // Set the items label text + void SetLabelText(LPCTSTR sLabel); + + // Return the items label text + LPCTSTR GetLabelText(); + + // Set the items info (description) text + void SetInfoText(LPCTSTR sInfo); + + // Get the items info (description) text + LPCTSTR GetInfoText(); + + // Set the item's ID + void SetCtrlID(UINT nCtrlID); + + // Return the item's ID + UINT GetCtrlID(); + + // Overrideable - draw the item's non attribute area + virtual LONG DrawItem(CDC* pDC, const RECT& rc, LONG x, LONG y); + + // call to mark attribute changes + void CommitChanges(); + + // call to activate item attribute + enum { + ACTIVATE_TYPE_KEYBOARD, + ACTIVATE_TYPE_MOUSE + }; + void Activate(int activateType, CPoint point); + + // + // Overrideables + // + + // The attribute area needs drawing + virtual void DrawAttribute(CDC* pDC, const RECT& rc); + + // Return the height of the item + virtual LONG GetHeight(); + + // Retrieve the item's attribute value + virtual LPARAM GetItemValue(); + + // Set the item's attribute value + virtual void SetItemValue(LPARAM lParam); + + // Called when attribute area has changed size + virtual void OnMove(); + + // Called when the item needs to refresh its data + virtual void OnRefresh(); + + // Called when the item needs to commit its changes + virtual void OnCommit(); + + // Called to activate the item + virtual void OnActivate(int activateType, CPoint point); + + // + // Usually only CPropTree should calls these + // + + void SetPropOwner(CPropTree* pProp); + + // Return the location of the PropItem + const POINT& GetLocation(); + + // TreeItem link pointer access + CPropTreeItem* GetParent(); + CPropTreeItem* GetSibling(); + CPropTreeItem* GetChild(); + CPropTreeItem* GetNextVisible(); + + void SetParent(CPropTreeItem* pParent); + void SetSibling(CPropTreeItem* pSibling); + void SetChild(CPropTreeItem* pChild); + void SetNextVisible(CPropTreeItem* pVis); + +protected: + // CPropTree class that this class belongs + CPropTree* m_pProp; + + // TreeItem label name + CString m_sLabel; + + // Descriptive info text + CString m_sInfo; + + // TreeItem location + CPoint m_loc; + + // TreeItem attribute size + CRect m_rc; + + // user defined LPARAM value + LPARAM m_lParam; + + // ID of control item (should be unique) + UINT m_nCtrlID; + +protected: + enum TreeItemStates + { + TreeItemSelected = 0x00000001, + TreeItemExpanded = 0x00000002, + TreeItemCheckbox = 0x00000004, + TreeItemChecked = 0x00000008, + TreeItemActivated = 0x00000010, + TreeItemReadOnly = 0x00000020, + }; + + // TreeItem state + DWORD m_dwState; + + // TRUE if item is activated + BOOL m_bActivated; + + // TRUE if item has been commited once (activation) + BOOL m_bCommitOnce; + + // Rectangle position of the expand button (if contains one) + CRect m_rcExpand; + + // Rectangle position of the check box (if contains one) + CRect m_rcCheckbox; + + // Rectangle position of the button (if contains one) + CRect m_rcButton; + + // link pointers + CPropTreeItem* m_pParent; + CPropTreeItem* m_pSibling; + CPropTreeItem* m_pChild; + CPropTreeItem* m_pVis; +}; + +#endif // _PROPTREEITEM_H diff --git a/src/tools/common/PropTree/PropTreeItemButton.cpp b/src/tools/common/PropTree/PropTreeItemButton.cpp new file mode 100644 index 0000000..fe095c9 --- /dev/null +++ b/src/tools/common/PropTree/PropTreeItemButton.cpp @@ -0,0 +1,103 @@ +// PropTreeItemButton.cpp : implementation file +// +// Copyright (C) 1998-2001 Scott Ramsay +// sramsay@gonavi.com +// http://www.gonavi.com +// +// This material is provided "as is", with absolutely no warranty expressed +// or implied. Any use is at your own risk. +// +// Permission to use or copy this software for any purpose is hereby granted +// without fee, provided the above notices are retained on all copies. +// Permission to modify the code and to distribute modified code is granted, +// provided the above notices are retained, and a notice that the code was +// modified is included with the above copyright notice. +// +// If you use this code, drop me an email. I'd like to know if you find the code +// useful. + +//#include "stdafx.h" +#include "../../../idlib/precompiled.h" +#pragma hdrstop + +#include "proptree.h" +#include "PropTreeItemButton.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +#define BUTTON_SIZE 17 + +///////////////////////////////////////////////////////////////////////////// +// CPropTreeItemButton + +CPropTreeItemButton::CPropTreeItemButton() { + mouseDown = false; +} + +CPropTreeItemButton::~CPropTreeItemButton() { +} + + +///////////////////////////////////////////////////////////////////////////// +// CPropTreeItemButton message handlers + +LONG CPropTreeItemButton::DrawItem( CDC* pDC, const RECT& rc, LONG x, LONG y ) +{ + CSize textSize; + CRect textRect; + LONG nTotal = 0; + + nTotal = CPropTreeItem::DrawItem( pDC, rc, x, y ); + + textSize = pDC->GetOutputTextExtent( buttonText ); + + buttonRect.left = m_rc.right - ( textSize.cx + 12 + 4); + buttonRect.top = m_rc.top + ((m_rc.bottom - m_rc.top)/2)-BUTTON_SIZE/2; + buttonRect.right = buttonRect.left + textSize.cx + 12; + buttonRect.bottom = buttonRect.top + BUTTON_SIZE; + + UINT buttonStyle; + + if ( (m_dwState & TreeItemChecked) ) { + buttonStyle = DFCS_BUTTONPUSH | DFCS_PUSHED; + } else { + buttonStyle = DFCS_BUTTONPUSH; + } + pDC->DrawFrameControl(&buttonRect, DFC_BUTTON, buttonStyle ); + + textRect = buttonRect; + textRect.left += 4; + textRect.right -= 8; + pDC->DrawText( buttonText, textRect, DT_SINGLELINE|DT_VCENTER ); + + //Adjust hit test rect to acount for window scrolling + hitTestRect = buttonRect; + hitTestRect.OffsetRect(0, m_pProp->GetOrigin().y); + + return nTotal; +} + +void CPropTreeItemButton::DrawAttribute(CDC* pDC, const RECT& rc) { +} + + +LPARAM CPropTreeItemButton::GetItemValue() { + return (LPARAM)0; +} + + +void CPropTreeItemButton::SetItemValue(LPARAM lParam) { +} + + +BOOL CPropTreeItemButton::HitButton( const POINT& pt ) { + return hitTestRect.PtInRect( pt ); +} + +void CPropTreeItemButton::SetButtonText( LPCSTR text ) { + buttonText = text; +} diff --git a/src/tools/common/PropTree/PropTreeItemButton.h b/src/tools/common/PropTree/PropTreeItemButton.h new file mode 100644 index 0000000..84922cb --- /dev/null +++ b/src/tools/common/PropTree/PropTreeItemButton.h @@ -0,0 +1,63 @@ +#pragma once + +// PropTreeItemButton.h : header file +// +// Copyright (C) 1998-2001 Scott Ramsay +// sramsay@gonavi.com +// http://www.gonavi.com +// +// This material is provided "as is", with absolutely no warranty expressed +// or implied. Any use is at your own risk. +// +// Permission to use or copy this software for any purpose is hereby granted +// without fee, provided the above notices are retained on all copies. +// Permission to modify the code and to distribute modified code is granted, +// provided the above notices are retained, and a notice that the code was +// modified is included with the above copyright notice. +// +// If you use this code, drop me an email. I'd like to know if you find the code +// useful. + +#include "PropTreeItem.h" + +///////////////////////////////////////////////////////////////////////////// +// CPropTreeItemButton window + +class PROPTREE_API CPropTreeItemButton : public CPropTreeItem +{ +// Construction +public: + CPropTreeItemButton(); + virtual ~CPropTreeItemButton(); + +// Attributes +public: + // The non-attribute area needs drawing + virtual LONG DrawItem(CDC* pDC, const RECT& rc, LONG x, LONG y); + + // The attribute area needs drawing + virtual void DrawAttribute(CDC* pDC, const RECT& rc); + + // Retrieve the item's attribute value + virtual LPARAM GetItemValue(); + + // Set the item's attribute value + virtual void SetItemValue(LPARAM lParam); + + // Overrideable - Returns TRUE if the point is on the button + virtual BOOL HitButton(const POINT& pt); + + void SetButtonText( LPCSTR text ); + +protected: + CString buttonText; + CRect buttonRect; + CRect hitTestRect; + bool mouseDown; + +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. diff --git a/src/tools/common/PropTree/PropTreeItemCheck.cpp b/src/tools/common/PropTree/PropTreeItemCheck.cpp new file mode 100644 index 0000000..7189e76 --- /dev/null +++ b/src/tools/common/PropTree/PropTreeItemCheck.cpp @@ -0,0 +1,161 @@ +// PropTreeItemCheck.cpp : implementation file +// +// Copyright (C) 1998-2001 Scott Ramsay +// sramsay@gonavi.com +// http://www.gonavi.com +// +// This material is provided "as is", with absolutely no warranty expressed +// or implied. Any use is at your own risk. +// +// Permission to use or copy this software for any purpose is hereby granted +// without fee, provided the above notices are retained on all copies. +// Permission to modify the code and to distribute modified code is granted, +// provided the above notices are retained, and a notice that the code was +// modified is included with the above copyright notice. +// +// If you use this code, drop me an email. I'd like to know if you find the code +// useful. + +//#include "stdafx.h" +#include "../../../idlib/precompiled.h" +#pragma hdrstop + +#include "proptree.h" +#include "PropTreeItemCheck.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +#define CHECK_BOX_SIZE 14 + +///////////////////////////////////////////////////////////////////////////// +// CPropTreeItemCheck + +CPropTreeItemCheck::CPropTreeItemCheck() +{ + checkState = 0; +} + +CPropTreeItemCheck::~CPropTreeItemCheck() +{ +} + + +BEGIN_MESSAGE_MAP(CPropTreeItemCheck, CButton) + //{{AFX_MSG_MAP(CPropTreeItemCheck) + //}}AFX_MSG_MAP + ON_CONTROL_REFLECT(BN_KILLFOCUS, OnBnKillfocus) + ON_CONTROL_REFLECT(BN_CLICKED, OnBnClicked) +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CPropTreeItemCheck message handlers + +void CPropTreeItemCheck::DrawAttribute(CDC* pDC, const RECT& rc) +{ + ASSERT(m_pProp!=NULL); + + // verify the window has been created + if (!IsWindow(m_hWnd)) + { + TRACE0("CPropTreeItemCombo::DrawAttribute() - The window has not been created\n"); + return; + } + + checkRect.left = m_rc.left; + checkRect.top = m_rc.top + ((m_rc.bottom - m_rc.top)/2)-CHECK_BOX_SIZE/2; + checkRect.right = checkRect.left + CHECK_BOX_SIZE; + checkRect.bottom = checkRect.top + CHECK_BOX_SIZE; + + if(!m_bActivated) + pDC->DrawFrameControl(&checkRect, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_FLAT |(checkState ? DFCS_CHECKED : 0)); +} + +void CPropTreeItemCheck::SetCheckState(BOOL state) + { + checkState = state; + + SetCheck(checkState ? BST_CHECKED : BST_UNCHECKED); + } + + +LPARAM CPropTreeItemCheck::GetItemValue() +{ + return (LPARAM)GetCheckState(); +} + + +void CPropTreeItemCheck::SetItemValue(LPARAM lParam) +{ + SetCheckState((BOOL)lParam); +} + + +void CPropTreeItemCheck::OnMove() +{ + if (IsWindow(m_hWnd)) + SetWindowPos(NULL, m_rc.left, m_rc.top, m_rc.Width(), m_rc.Height(), SWP_NOZORDER|SWP_NOACTIVATE); +} + + +void CPropTreeItemCheck::OnRefresh() +{ +} + + +void CPropTreeItemCheck::OnCommit() +{ + ShowWindow(SW_HIDE); +} + + +void CPropTreeItemCheck::OnActivate(int activateType, CPoint point) +{ + if(activateType == CPropTreeItem::ACTIVATE_TYPE_MOUSE) { + //Check where the user clicked + if(point.x < m_rc.left + CHECK_BOX_SIZE) { + SetCheckState(!GetCheckState()); + CommitChanges(); + } else { + SetWindowPos(NULL, m_rc.left, m_rc.top, m_rc.Width(), m_rc.Height(), SWP_NOZORDER|SWP_SHOWWINDOW); + SetFocus(); + } + } else { + SetWindowPos(NULL, m_rc.left, m_rc.top, m_rc.Width(), m_rc.Height(), SWP_NOZORDER|SWP_SHOWWINDOW); + SetFocus(); + } +} + + +bool CPropTreeItemCheck::CreateCheckBox() { + ASSERT(m_pProp!=NULL); + + if (IsWindow(m_hWnd)) + DestroyWindow(); + + DWORD dwStyle = (WS_CHILD|BS_CHECKBOX|BS_NOTIFY|BS_FLAT ); + + if (!Create(NULL, dwStyle, CRect(0,0,0,0), m_pProp->GetCtrlParent(), GetCtrlID())) + { + TRACE0("CPropTreeItemCombo::CreateComboBox() - failed to create combo box\n"); + return FALSE; + } + + return TRUE; +} + +void CPropTreeItemCheck::OnBnKillfocus() +{ + CommitChanges(); +} + +void CPropTreeItemCheck::OnBnClicked() +{ + int state = GetCheck(); + + SetCheckState(GetCheck() == BST_CHECKED ? FALSE : TRUE); + CommitChanges(); +} diff --git a/src/tools/common/PropTree/PropTreeItemCheck.h b/src/tools/common/PropTree/PropTreeItemCheck.h new file mode 100644 index 0000000..b1e7f99 --- /dev/null +++ b/src/tools/common/PropTree/PropTreeItemCheck.h @@ -0,0 +1,95 @@ +#pragma once + +// PropTreeItemCheck.h : header file +// +// Copyright (C) 1998-2001 Scott Ramsay +// sramsay@gonavi.com +// http://www.gonavi.com +// +// This material is provided "as is", with absolutely no warranty expressed +// or implied. Any use is at your own risk. +// +// Permission to use or copy this software for any purpose is hereby granted +// without fee, provided the above notices are retained on all copies. +// Permission to modify the code and to distribute modified code is granted, +// provided the above notices are retained, and a notice that the code was +// modified is included with the above copyright notice. +// +// If you use this code, drop me an email. I'd like to know if you find the code +// useful. + +#include "PropTreeItem.h" + +///////////////////////////////////////////////////////////////////////////// +// CPropTreeItemCheck window + +class PROPTREE_API CPropTreeItemCheck : public CButton, public CPropTreeItem +{ +// Construction +public: + CPropTreeItemCheck(); + virtual ~CPropTreeItemCheck(); + +// Attributes +public: + // The attribute area needs drawing + virtual void DrawAttribute(CDC* pDC, const RECT& rc); + + // Retrieve the item's attribute value + virtual LPARAM GetItemValue(); + + // Set the item's attribute value + virtual void SetItemValue(LPARAM lParam); + + // Called when attribute area has changed size + virtual void OnMove(); + + // Called when the item needs to refresh its data + virtual void OnRefresh(); + + // Called when the item needs to commit its changes + virtual void OnCommit(); + + // Called to activate the item + virtual void OnActivate(int activateType, CPoint point); + + bool HitCheckBoxTest(const POINT& pt); + + bool CreateCheckBox(); + + BOOL GetCheckState() { return checkState; }; + void SetCheckState(BOOL state); + + +protected: + BOOL checkState; + CRect checkRect; + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CPropTreeItemCheck) + //}}AFX_VIRTUAL + +// Implementation +public: + + // Generated message map functions +protected: + //{{AFX_MSG(CPropTreeItemCheck) + //}}AFX_MSG + + DECLARE_MESSAGE_MAP() + +public: + + afx_msg void OnBnKillfocus(); + afx_msg void OnBnClicked(); +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. diff --git a/src/tools/common/PropTree/PropTreeItemColor.cpp b/src/tools/common/PropTree/PropTreeItemColor.cpp new file mode 100644 index 0000000..bf36958 --- /dev/null +++ b/src/tools/common/PropTree/PropTreeItemColor.cpp @@ -0,0 +1,369 @@ +// PropTreeItemColor.cpp : implementation file +// +// Copyright (C) 1998-2001 Scott Ramsay +// sramsay@gonavi.com +// http://www.gonavi.com +// +// This material is provided "as is", with absolutely no warranty expressed +// or implied. Any use is at your own risk. +// +// Permission to use or copy this software for any purpose is hereby granted +// without fee, provided the above notices are retained on all copies. +// Permission to modify the code and to distribute modified code is granted, +// provided the above notices are retained, and a notice that the code was +// modified is included with the above copyright notice. +// +// If you use this code, drop me an email. I'd like to know if you find the code +// useful. + +//#include "stdafx.h" +#include "../../../idlib/precompiled.h" +#pragma hdrstop + +#include "PropTree.h" +#include "../../../sys/win32/rc/proptree_Resource.h" +#include "PropTreeItemColor.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +extern HINSTANCE ghInst; + +typedef struct _ColorTableEntry +{ + COLORREF color; + RECT rcSpot; +} ColorTableEntry; + +static ColorTableEntry _crColors[] = +{ + {RGB(0x00, 0x00, 0x00)}, + {RGB(0xA5, 0x2A, 0x00)}, + {RGB(0x00, 0x40, 0x40)}, + {RGB(0x00, 0x55, 0x00)}, + {RGB(0x00, 0x00, 0x5E)}, + {RGB(0x00, 0x00, 0x8B)}, + {RGB(0x4B, 0x00, 0x82)}, + {RGB(0x28, 0x28, 0x28)}, + + {RGB(0x8B, 0x00, 0x00)}, + {RGB(0xFF, 0x68, 0x20)}, + {RGB(0x8B, 0x8B, 0x00)}, + {RGB(0x00, 0x93, 0x00)}, + {RGB(0x38, 0x8E, 0x8E)}, + {RGB(0x00, 0x00, 0xFF)}, + {RGB(0x7B, 0x7B, 0xC0)}, + {RGB(0x66, 0x66, 0x66)}, + + {RGB(0xFF, 0x00, 0x00)}, + {RGB(0xFF, 0xAD, 0x5B)}, + {RGB(0x32, 0xCD, 0x32)}, + {RGB(0x3C, 0xB3, 0x71)}, + {RGB(0x7F, 0xFF, 0xD4)}, + {RGB(0x7D, 0x9E, 0xC0)}, + {RGB(0x80, 0x00, 0x80)}, + {RGB(0x7F, 0x7F, 0x7F)}, + + {RGB(0xFF, 0xC0, 0xCB)}, + {RGB(0xFF, 0xD7, 0x00)}, + {RGB(0xFF, 0xFF, 0x00)}, + {RGB(0x00, 0xFF, 0x00)}, + {RGB(0x40, 0xE0, 0xD0)}, + {RGB(0xC0, 0xFF, 0xFF)}, + {RGB(0x48, 0x00, 0x48)}, + {RGB(0xC0, 0xC0, 0xC0)}, + + {RGB(0xFF, 0xE4, 0xE1)}, + {RGB(0xD2, 0xB4, 0x8C)}, + {RGB(0xFF, 0xFF, 0xE0)}, + {RGB(0x98, 0xFB, 0x98)}, + {RGB(0xAF, 0xEE, 0xEE)}, + {RGB(0x68, 0x83, 0x8B)}, + {RGB(0xE6, 0xE6, 0xFA)}, + {RGB(0xFF, 0xFF, 0xFF)} +}; + +static void ColorBox(CDC* pDC, CPoint pt, COLORREF clr, BOOL bHover) +{ + CBrush br(clr); + + CBrush* obr = pDC->SelectObject(&br); + + pDC->PatBlt(pt.x, pt.y, 13, 13, PATCOPY); + pDC->SelectObject(obr); + + CRect rc; + rc.SetRect(pt.x - 2, pt.y - 2, pt.x + 15, pt.y + 15); + + pDC->DrawEdge(&rc, (bHover) ? BDR_SUNKENOUTER : BDR_RAISEDINNER, BF_RECT); +} + + + +static LONG FindSpot(CPoint point) +{ + for (LONG i=0; i<40; i++) + { + if (PtInRect(&_crColors[i].rcSpot, point)) + return i; + } + + return -1; +} + + +///////////////////////////////////////////////////////////////////////////// +// CPropTreeItemColor + +COLORREF* CPropTreeItemColor::s_pColors = NULL; + +CPropTreeItemColor::CPropTreeItemColor() : + m_cColor(0), + m_cPrevColor(0), + m_nSpot(-1), + m_bButton(FALSE), + m_bInDialog(FALSE) +{ +} + +CPropTreeItemColor::~CPropTreeItemColor() +{ +} + + +BEGIN_MESSAGE_MAP(CPropTreeItemColor, CWnd) + //{{AFX_MSG_MAP(CPropTreeItemColor) + ON_WM_KILLFOCUS() + ON_WM_PAINT() + ON_WM_CLOSE() + ON_WM_MOUSEMOVE() + ON_WM_SETCURSOR() + ON_WM_LBUTTONDOWN() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + +///////////////////////////////////////////////////////////////////////////// +// CPropTreeItemColor message handlers + +void CPropTreeItemColor::SetDefaultColorsList(COLORREF* pColors) +{ + s_pColors = pColors; +} + + +void CPropTreeItemColor::DrawAttribute(CDC* pDC, const RECT& rc) +{ + ASSERT(m_pProp!=NULL); + + CRect r(rc); + + pDC->SelectObject(IsReadOnly() ? m_pProp->GetNormalFont() : m_pProp->GetBoldFont()); + + if (!m_pProp->IsWindowEnabled()) + pDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT)); + else + pDC->SetTextColor(RGB(0,0,0)); + + r.top += 1; + r.right = r.left + r.Height() - 1; + + CBrush br(m_cColor); + CBrush* pold = pDC->SelectObject(&br); + pDC->PatBlt(r.left, r.top, r.Width(), r.Height(), PATCOPY); + pDC->SelectObject(pold); + + pDC->DrawEdge(&r, EDGE_SUNKEN, BF_RECT); + + CString s; + + r = rc; + r.left += r.Height(); + s.Format(_T("R = %d, G = %d, B = %d"), GetRValue(m_cColor),GetGValue(m_cColor), GetBValue(m_cColor)); + pDC->DrawText(s, r, DT_SINGLELINE|DT_VCENTER); +} + + +LPARAM CPropTreeItemColor::GetItemValue() +{ + return m_cColor; +} + + +void CPropTreeItemColor::SetItemValue(LPARAM lParam) +{ + m_cColor = lParam; +} + + +void CPropTreeItemColor::OnMove() +{ +} + + +void CPropTreeItemColor::OnRefresh() +{ +} + + +void CPropTreeItemColor::OnCommit() +{ + ShowWindow(SW_HIDE); +} + + +void CPropTreeItemColor::OnActivate(int activateType, CPoint point) +{ + CRect r; + + m_cPrevColor = m_cColor; + + r = m_rc; + r.right = r.left + 150; + r.bottom = r.top + 120; + + ASSERT(m_pProp!=NULL); + m_pProp->GetCtrlParent()->ClientToScreen(r); + + if (!IsWindow(m_hWnd)) + { + LPCTSTR pszClassName; + + pszClassName = AfxRegisterWndClass(CS_VREDRAW|CS_HREDRAW, LoadCursor(NULL, IDC_ARROW), (HBRUSH)(COLOR_BTNFACE + 1)); + + DWORD dwStyle = WS_POPUP|WS_DLGFRAME; + + CreateEx(0, pszClassName, _T(""), dwStyle, r, m_pProp->GetCtrlParent(), 0); + m_rcButton.SetRect(40, 94, 110, 114); + } + + SetWindowPos(NULL, r.left, r.top, r.Width() + 1, r.Height(), SWP_NOZORDER|SWP_SHOWWINDOW); + SetFocus(); +} + + +void CPropTreeItemColor::OnKillFocus(CWnd* pNewWnd) +{ + CWnd::OnKillFocus(pNewWnd); + + if (!m_bInDialog) + CommitChanges(); +} + + +void CPropTreeItemColor::OnPaint() +{ + CPaintDC dc(this); + CPoint pt; + + for (LONG i=0; i<40; i++) + { + pt.x = (i & 7) * 18 + 3; + pt.y = (i >> 3) * 18 + 3; + ColorBox(&dc, pt, _crColors[i].color, m_nSpot==i); + SetRect(&_crColors[i].rcSpot, pt.x, pt.y, pt.x + 13, pt.y + 13); + } + + ASSERT(m_pProp!=NULL); + + dc.SelectObject(m_pProp->GetNormalFont()); + + CString s(_T("More Colors")); + + dc.SetBkMode(TRANSPARENT); + dc.SetTextColor(GetSysColor(COLOR_BTNTEXT)); + dc.DrawText(s, &m_rcButton, DT_SINGLELINE|DT_VCENTER|DT_CENTER); + + dc.DrawEdge(&m_rcButton, m_bButton ? BDR_SUNKENOUTER : BDR_RAISEDINNER, BF_RECT); +} + + +void CPropTreeItemColor::OnClose() +{ + CommitChanges(); +} + + +void CPropTreeItemColor::OnMouseMove(UINT, CPoint point) +{ + BOOL bButton; + LONG nSpot; + + nSpot = FindSpot(point); + if (nSpot!=m_nSpot) + { + Invalidate(FALSE); + m_nSpot = nSpot; + } + + bButton = m_rcButton.PtInRect(point); + + if (bButton!=m_bButton) + { + m_bButton = bButton; + Invalidate(FALSE); + } +} + + +BOOL CPropTreeItemColor::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) +{ + if (nHitTest==HTCLIENT) + { + CPoint point; + + GetCursorPos(&point); + ScreenToClient(&point); + + if (FindSpot(point)!=-1 || m_rcButton.PtInRect(point)) + { + SetCursor(LoadCursor(ghInst, MAKEINTRESOURCE(IDC_FPOINT))); + return TRUE; + } + + } + + return CWnd::OnSetCursor(pWnd, nHitTest, message); +} + + +void CPropTreeItemColor::OnLButtonDown(UINT, CPoint point) +{ + if (m_nSpot!=-1) + { + m_cColor = _crColors[m_nSpot].color; + CommitChanges(); + } + else + if (m_rcButton.PtInRect(point)) + { + CHOOSECOLOR cc; + COLORREF clr[16]; + + ZeroMemory(&cc, sizeof(CHOOSECOLOR)); + cc.Flags = CC_FULLOPEN|CC_ANYCOLOR|CC_RGBINIT; + cc.lStructSize = sizeof(CHOOSECOLOR); + cc.hwndOwner = m_hWnd; + cc.rgbResult = m_cColor; + cc.lpCustColors = s_pColors ? s_pColors : clr; + + memset(clr, 0xff, sizeof(COLORREF) * 16); + clr[0] = m_cColor; + + m_bInDialog = TRUE; + + ASSERT(m_pProp!=NULL); + m_pProp->DisableInput(); + + ShowWindow(SW_HIDE); + + if (ChooseColor(&cc)) + m_cColor = cc.rgbResult; + + m_pProp->DisableInput(FALSE); + CommitChanges(); + } +} diff --git a/src/tools/common/PropTree/PropTreeItemColor.h b/src/tools/common/PropTree/PropTreeItemColor.h new file mode 100644 index 0000000..be0b1d5 --- /dev/null +++ b/src/tools/common/PropTree/PropTreeItemColor.h @@ -0,0 +1,98 @@ +#if !defined(AFX_PROPTREEITEMCOLOR_H__50C09AC0_1F02_4150_AA6A_5151345D87A2__INCLUDED_) +#define AFX_PROPTREEITEMCOLOR_H__50C09AC0_1F02_4150_AA6A_5151345D87A2__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// PropTreeItemColor.h : header file +// +// Copyright (C) 1998-2001 Scott Ramsay +// sramsay@gonavi.com +// http://www.gonavi.com +// +// This material is provided "as is", with absolutely no warranty expressed +// or implied. Any use is at your own risk. +// +// Permission to use or copy this software for any purpose is hereby granted +// without fee, provided the above notices are retained on all copies. +// Permission to modify the code and to distribute modified code is granted, +// provided the above notices are retained, and a notice that the code was +// modified is included with the above copyright notice. +// +// If you use this code, drop me an email. I'd like to know if you find the code +// useful. + +#include "PropTreeItem.h" + +///////////////////////////////////////////////////////////////////////////// +// CPropTreeItemColor window + +class PROPTREE_API CPropTreeItemColor : public CWnd, public CPropTreeItem +{ +// Construction +public: + CPropTreeItemColor(); + virtual ~CPropTreeItemColor(); + +// Attributes +public: + // The attribute area needs drawing + virtual void DrawAttribute(CDC* pDC, const RECT& rc); + + // Retrieve the item's attribute value + virtual LPARAM GetItemValue(); + + // Set the item's attribute value + virtual void SetItemValue(LPARAM lParam); + + // Called when attribute area has changed size + virtual void OnMove(); + + // Called when the item needs to refresh its data + virtual void OnRefresh(); + + // Called when the item needs to commit its changes + virtual void OnCommit(); + + // Called to activate the item + virtual void OnActivate(int activateType, CPoint point); + + static void SetDefaultColorsList(COLORREF* pColors); + +protected: + COLORREF m_cColor; + COLORREF m_cPrevColor; + CRect m_rcButton; + LONG m_nSpot; + BOOL m_bButton; + BOOL m_bInDialog; + + static COLORREF* s_pColors; + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CPropTreeItemColor) + //}}AFX_VIRTUAL + +// Implementation +public: + + // Generated message map functions +protected: + //{{AFX_MSG(CPropTreeItemColor) + afx_msg void OnKillFocus(CWnd* pNewWnd); + afx_msg void OnPaint(); + afx_msg void OnClose(); + afx_msg void OnMouseMove(UINT nFlags, CPoint point); + afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message); + afx_msg void OnLButtonDown(UINT nFlags, CPoint point); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_PROPTREEITEMCOLOR_H__50C09AC0_1F02_4150_AA6A_5151345D87A2__INCLUDED_) diff --git a/src/tools/common/PropTree/PropTreeItemCombo.cpp b/src/tools/common/PropTree/PropTreeItemCombo.cpp new file mode 100644 index 0000000..d444447 --- /dev/null +++ b/src/tools/common/PropTree/PropTreeItemCombo.cpp @@ -0,0 +1,233 @@ +// PropTreeItemCombo.cpp : implementation file +// +// Copyright (C) 1998-2001 Scott Ramsay +// sramsay@gonavi.com +// http://www.gonavi.com +// +// This material is provided "as is", with absolutely no warranty expressed +// or implied. Any use is at your own risk. +// +// Permission to use or copy this software for any purpose is hereby granted +// without fee, provided the above notices are retained on all copies. +// Permission to modify the code and to distribute modified code is granted, +// provided the above notices are retained, and a notice that the code was +// modified is included with the above copyright notice. +// +// If you use this code, drop me an email. I'd like to know if you find the code +// useful. + +//#include "stdafx.h" +#include "../../../idlib/precompiled.h" +#pragma hdrstop + +#include "PropTree.h" +#include "../../../sys/win32/rc/proptree_Resource.h" + +#include "PropTreeItemCombo.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +#define DROPDOWN_HEIGHT 100 + +///////////////////////////////////////////////////////////////////////////// +// CPropTreeItemCombo + +CPropTreeItemCombo::CPropTreeItemCombo() : + m_lComboData(0), + m_nDropHeight(DROPDOWN_HEIGHT) +{ +} + +CPropTreeItemCombo::~CPropTreeItemCombo() +{ +} + + +BEGIN_MESSAGE_MAP(CPropTreeItemCombo, CComboBox) + //{{AFX_MSG_MAP(CPropTreeItemCombo) + ON_CONTROL_REFLECT(CBN_SELCHANGE, OnSelchange) + ON_CONTROL_REFLECT(CBN_KILLFOCUS, OnKillfocus) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CPropTreeItemCombo message handlers + +void CPropTreeItemCombo::DrawAttribute(CDC* pDC, const RECT& rc) +{ + ASSERT(m_pProp!=NULL); + + // verify the window has been created + if (!IsWindow(m_hWnd)) + { + TRACE0("CPropTreeItemCombo::DrawAttribute() - The window has not been created\n"); + return; + } + + pDC->SelectObject(IsReadOnly() ? m_pProp->GetNormalFont() : m_pProp->GetBoldFont()); + pDC->SetTextColor(RGB(0,0,0)); + pDC->SetBkMode(TRANSPARENT); + + CRect r = rc; + CString s; + LONG idx; + + if ((idx = GetCurSel())!=CB_ERR) + GetLBText(idx, s); + else + s = _T(""); + + pDC->DrawText(s, r, DT_SINGLELINE|DT_VCENTER); +} + + +LPARAM CPropTreeItemCombo::GetItemValue() +{ + return m_lComboData; +} + + +void CPropTreeItemCombo::SetItemValue(LPARAM lParam) +{ + m_lComboData = lParam; + OnRefresh(); +} + + +void CPropTreeItemCombo::OnMove() +{ + if (IsWindow(m_hWnd) && IsWindowVisible()) + SetWindowPos(NULL, m_rc.left, m_rc.top, m_rc.Width() + 1, m_rc.Height(), SWP_NOZORDER|SWP_SHOWWINDOW); +} + + +void CPropTreeItemCombo::OnRefresh() +{ + LONG idx = FindCBData(m_lComboData); + + if (idx!=CB_ERR) + SetCurSel(idx); +} + + +void CPropTreeItemCombo::OnCommit() +{ + LONG idx; + + // store combo box item data + if ((idx = GetCurSel())==CB_ERR) + m_lComboData = 0; + else + m_lComboData = (LPARAM)GetItemData(idx); + + ShowWindow(SW_HIDE); +} + + +void CPropTreeItemCombo::OnActivate(int activateType, CPoint point) +{ + // activate the combo box + SetWindowPos(NULL, m_rc.left, m_rc.top, m_rc.Width() + 1, m_rc.Height() + m_nDropHeight, SWP_NOZORDER|SWP_SHOWWINDOW); + SetFocus(); + + if (GetCount()) + ShowDropDown(TRUE); +} + + +BOOL CPropTreeItemCombo::CreateComboBox(DWORD dwStyle) +{ + ASSERT(m_pProp!=NULL); + + if (IsWindow(m_hWnd)) + DestroyWindow(); + + // force as not visible child window + dwStyle = (WS_CHILD|WS_VSCROLL|dwStyle) & ~WS_VISIBLE; + + if (!Create(dwStyle, CRect(0,0,0,0), m_pProp->GetCtrlParent(), GetCtrlID())) + { + TRACE0("CPropTreeItemCombo::CreateComboBox() - failed to create combo box\n"); + return FALSE; + } + + SendMessage(WM_SETFONT, (WPARAM)m_pProp->GetNormalFont()->m_hObject); + + return TRUE; +} + + +BOOL CPropTreeItemCombo::CreateComboBoxBool() +{ + ASSERT(m_pProp!=NULL); + + if (IsWindow(m_hWnd)) + DestroyWindow(); + + // force as a non-visible child window + DWORD dwStyle = WS_CHILD|WS_VSCROLL|CBS_SORT|CBS_DROPDOWNLIST; + + if (!Create(dwStyle, CRect(0,0,0,0), m_pProp->GetCtrlParent(), GetCtrlID())) + { + TRACE0("CPropTreeItemCombo::CreateComboBoxBool() - failed to create combo box\n"); + return FALSE; + } + + SendMessage(WM_SETFONT, (WPARAM)m_pProp->GetNormalFont()->m_hObject); + + // file the combo box + LONG idx; + CString s; + + s.LoadString(IDS_TRUE); + idx = AddString(s); + SetItemData(idx, TRUE); + + s.LoadString(IDS_FALSE); + idx = AddString(s); + SetItemData(idx, FALSE); + + return TRUE; +} + + +LONG CPropTreeItemCombo::FindCBData(LPARAM lParam) +{ + LONG idx; + + for (idx = 0; idx < GetCount(); idx++) + { + if (GetItemData(idx)==(DWORD)lParam) + return idx; + } + + return CB_ERR; +} + + +void CPropTreeItemCombo::OnSelchange() +{ + CommitChanges(); +} + + +void CPropTreeItemCombo::OnKillfocus() +{ + CommitChanges(); +} + + +void CPropTreeItemCombo::SetDropDownHeight(LONG nDropHeight) +{ + m_nDropHeight = nDropHeight; +} + + +LONG CPropTreeItemCombo::GetDropDownHeight() +{ + return m_nDropHeight; +} diff --git a/src/tools/common/PropTree/PropTreeItemCombo.h b/src/tools/common/PropTree/PropTreeItemCombo.h new file mode 100644 index 0000000..35f1921 --- /dev/null +++ b/src/tools/common/PropTree/PropTreeItemCombo.h @@ -0,0 +1,103 @@ +#if !defined(AFX_PROPTREEITEMCOMBO_H__9916BC6F_751F_4B15_996F_3C9F6334A259__INCLUDED_) +#define AFX_PROPTREEITEMCOMBO_H__9916BC6F_751F_4B15_996F_3C9F6334A259__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// PropTreeItemCombo.h : header file +// +// Copyright (C) 1998-2001 Scott Ramsay +// sramsay@gonavi.com +// http://www.gonavi.com +// +// This material is provided "as is", with absolutely no warranty expressed +// or implied. Any use is at your own risk. +// +// Permission to use or copy this software for any purpose is hereby granted +// without fee, provided the above notices are retained on all copies. +// Permission to modify the code and to distribute modified code is granted, +// provided the above notices are retained, and a notice that the code was +// modified is included with the above copyright notice. +// +// If you use this code, drop me an email. I'd like to know if you find the code +// useful. + +#include "PropTreeItem.h" + +///////////////////////////////////////////////////////////////////////////// +// CPropTreeItemCombo window + +class PROPTREE_API CPropTreeItemCombo : public CComboBox, public CPropTreeItem +{ +// Construction +public: + CPropTreeItemCombo(); + virtual ~CPropTreeItemCombo(); + +// Attributes +public: + // The attribute area needs drawing + virtual void DrawAttribute(CDC* pDC, const RECT& rc); + + // Retrieve the item's attribute value + virtual LPARAM GetItemValue(); + + // Set the item's attribute value + virtual void SetItemValue(LPARAM lParam); + + // Called when attribute area has changed size + virtual void OnMove(); + + // Called when the item needs to refresh its data + virtual void OnRefresh(); + + // Called when the item needs to commit its changes + virtual void OnCommit(); + + // Called to activate the item + virtual void OnActivate(int activateType, CPoint point); + + // Create your combo box with your specified styles + BOOL CreateComboBox(DWORD dwStyle = WS_CHILD|WS_VSCROLL|CBS_SORT|CBS_DROPDOWNLIST); + + // Create combo box with TRUE/FALSE selections + BOOL CreateComboBoxBool(); + + // Set the height for the dropdown combo box + void SetDropDownHeight(LONG nDropHeight); + + // Get the height of the dropdown combo box + LONG GetDropDownHeight(); + +protected: + LPARAM m_lComboData; + LONG m_nDropHeight; + +// Operations +protected: + LONG FindCBData(LPARAM lParam); + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CPropTreeItemCombo) + //}}AFX_VIRTUAL + +// Implementation +public: + + // Generated message map functions +protected: + //{{AFX_MSG(CPropTreeItemCombo) + afx_msg void OnSelchange(); + afx_msg void OnKillfocus(); + //}}AFX_MSG + + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_PROPTREEITEMCOMBO_H__9916BC6F_751F_4B15_996F_3C9F6334A259__INCLUDED_) diff --git a/src/tools/common/PropTree/PropTreeItemEdit.cpp b/src/tools/common/PropTree/PropTreeItemEdit.cpp new file mode 100644 index 0000000..87384df --- /dev/null +++ b/src/tools/common/PropTree/PropTreeItemEdit.cpp @@ -0,0 +1,212 @@ +// PropTreeItemEdit.cpp : implementation file +// +// Copyright (C) 1998-2001 Scott Ramsay +// sramsay@gonavi.com +// http://www.gonavi.com +// +// This material is provided "as is", with absolutely no warranty expressed +// or implied. Any use is at your own risk. +// +// Permission to use or copy this software for any purpose is hereby granted +// without fee, provided the above notices are retained on all copies. +// Permission to modify the code and to distribute modified code is granted, +// provided the above notices are retained, and a notice that the code was +// modified is included with the above copyright notice. +// +// If you use this code, drop me an email. I'd like to know if you find the code +// useful. + +//#include "stdafx.h" +#include "../../../idlib/precompiled.h" +#pragma hdrstop + +#include "proptree.h" +#include "PropTreeItemEdit.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CPropTreeItemEdit + +CPropTreeItemEdit::CPropTreeItemEdit() : + m_sEdit(_T("")), + m_nFormat(ValueFormatText), + m_bPassword(FALSE), + m_fValue(0.0f) +{ +} + +CPropTreeItemEdit::~CPropTreeItemEdit() +{ +} + + +BEGIN_MESSAGE_MAP(CPropTreeItemEdit, CEdit) + //{{AFX_MSG_MAP(CPropTreeItemEdit) + ON_WM_GETDLGCODE() + ON_WM_KEYDOWN() + ON_CONTROL_REFLECT(EN_KILLFOCUS, OnKillfocus) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CPropTreeItemEdit message handlers + +void CPropTreeItemEdit::DrawAttribute(CDC* pDC, const RECT& rc) +{ + ASSERT(m_pProp!=NULL); + + pDC->SelectObject(IsReadOnly() ? m_pProp->GetNormalFont() : m_pProp->GetBoldFont()); + pDC->SetTextColor(RGB(0,0,0)); + pDC->SetBkMode(TRANSPARENT); + + CRect r = rc; + + TCHAR ch; + + // can't use GetPasswordChar(), because window may not be created yet + ch = (m_bPassword) ? '*' : '\0'; + + if (ch) + { + CString s; + + s = m_sEdit; + for (LONG i=0; iDrawText(s, r, DT_SINGLELINE|DT_VCENTER); + } + else + { + pDC->DrawText(m_sEdit, r, DT_SINGLELINE|DT_VCENTER); + } +} + + + +void CPropTreeItemEdit::SetAsPassword(BOOL bPassword) +{ + m_bPassword = bPassword; +} + + +void CPropTreeItemEdit::SetValueFormat(ValueFormat nFormat) +{ + m_nFormat = nFormat; +} + + +LPARAM CPropTreeItemEdit::GetItemValue() +{ + switch (m_nFormat) + { + case ValueFormatNumber: + return _ttoi(m_sEdit); + + case ValueFormatFloatPointer: + _stscanf(m_sEdit, _T("%f"), &m_fValue); + return (LPARAM)&m_fValue; + } + + return (LPARAM)(LPCTSTR)m_sEdit; +} + + +void CPropTreeItemEdit::SetItemValue(LPARAM lParam) +{ + switch (m_nFormat) + { + case ValueFormatNumber: + m_sEdit.Format(_T("%d"), lParam); + return; + + case ValueFormatFloatPointer: + { + TCHAR tmp[MAX_PATH]; + m_fValue = *(float*)lParam; + _stprintf(tmp, _T("%f"), m_fValue); + m_sEdit = tmp; + } + return; + } + + if (lParam==0L) + { + TRACE0("CPropTreeItemEdit::SetItemValue - Invalid lParam value\n"); + return; + } + + m_sEdit = (LPCTSTR)lParam; +} + + +void CPropTreeItemEdit::OnMove() +{ + if (IsWindow(m_hWnd)) + SetWindowPos(NULL, m_rc.left, m_rc.top, m_rc.Width(), m_rc.Height(), SWP_NOZORDER|SWP_NOACTIVATE); +} + + +void CPropTreeItemEdit::OnRefresh() +{ + if (IsWindow(m_hWnd)) + SetWindowText(m_sEdit); +} + + +void CPropTreeItemEdit::OnCommit() +{ + // hide edit control + ShowWindow(SW_HIDE); + + // store edit text for GetItemValue + GetWindowText(m_sEdit); +} + + +void CPropTreeItemEdit::OnActivate(int activateType, CPoint point) +{ + // Check if the edit control needs creation + if (!IsWindow(m_hWnd)) + { + DWORD dwStyle; + + dwStyle = WS_CHILD|ES_AUTOHSCROLL; + Create(dwStyle, m_rc, m_pProp->GetCtrlParent(), GetCtrlID()); + } + + SendMessage(WM_SETFONT, (WPARAM)m_pProp->GetNormalFont()->m_hObject); + + SetPasswordChar((TCHAR)(m_bPassword ? '*' : 0)); + SetWindowText(m_sEdit); + SetSel(0, -1); + + SetWindowPos(NULL, m_rc.left, m_rc.top, m_rc.Width(), m_rc.Height(), SWP_NOZORDER|SWP_SHOWWINDOW); + SetFocus(); +} + + +UINT CPropTreeItemEdit::OnGetDlgCode() +{ + return CEdit::OnGetDlgCode()|DLGC_WANTALLKEYS; +} + + +void CPropTreeItemEdit::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) +{ + if (nChar==VK_RETURN) + CommitChanges(); + + CEdit::OnKeyDown(nChar, nRepCnt, nFlags); +} + + +void CPropTreeItemEdit::OnKillfocus() +{ + CommitChanges(); +} diff --git a/src/tools/common/PropTree/PropTreeItemEdit.h b/src/tools/common/PropTree/PropTreeItemEdit.h new file mode 100644 index 0000000..610df01 --- /dev/null +++ b/src/tools/common/PropTree/PropTreeItemEdit.h @@ -0,0 +1,108 @@ +#if !defined(AFX_PROPTREEITEMEDIT_H__642536B1_1162_4F99_B09D_9B1BD2CF88B6__INCLUDED_) +#define AFX_PROPTREEITEMEDIT_H__642536B1_1162_4F99_B09D_9B1BD2CF88B6__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// PropTreeItemEdit.h : header file +// +// Copyright (C) 1998-2001 Scott Ramsay +// sramsay@gonavi.com +// http://www.gonavi.com +// +// This material is provided "as is", with absolutely no warranty expressed +// or implied. Any use is at your own risk. +// +// Permission to use or copy this software for any purpose is hereby granted +// without fee, provided the above notices are retained on all copies. +// Permission to modify the code and to distribute modified code is granted, +// provided the above notices are retained, and a notice that the code was +// modified is included with the above copyright notice. +// +// If you use this code, drop me an email. I'd like to know if you find the code +// useful. + +#include "PropTreeItem.h" + +///////////////////////////////////////////////////////////////////////////// +// CPropTreeItemEdit window + +class PROPTREE_API CPropTreeItemEdit : public CEdit, public CPropTreeItem +{ +// Construction +public: + CPropTreeItemEdit(); + virtual ~CPropTreeItemEdit(); + +// Attributes +public: + // The attribute area needs drawing + virtual void DrawAttribute(CDC* pDC, const RECT& rc); + + // Retrieve the item's attribute value + virtual LPARAM GetItemValue(); + + // Set the item's attribute value + virtual void SetItemValue(LPARAM lParam); + + // Called when attribute area has changed size + virtual void OnMove(); + + // Called when the item needs to refresh its data + virtual void OnRefresh(); + + // Called when the item needs to commit its changes + virtual void OnCommit(); + + // Called to activate the item + virtual void OnActivate(int activateType, CPoint point); + + + enum ValueFormat + { + ValueFormatText, + ValueFormatNumber, + ValueFormatFloatPointer + }; + + // Set to specifify format of SetItemValue/GetItemValue + void SetValueFormat(ValueFormat nFormat); + + // Set to TRUE for to use a password edit control + void SetAsPassword(BOOL bPassword); + +protected: + CString m_sEdit; + float m_fValue; + + ValueFormat m_nFormat; + BOOL m_bPassword; + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CPropTreeItemEdit) + //}}AFX_VIRTUAL + +// Implementation +public: + + // Generated message map functions +protected: + //{{AFX_MSG(CPropTreeItemEdit) + afx_msg UINT OnGetDlgCode(); + afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnKillfocus(); + //}}AFX_MSG + + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_PROPTREEITEMEDIT_H__642536B1_1162_4F99_B09D_9B1BD2CF88B6__INCLUDED_) diff --git a/src/tools/common/PropTree/PropTreeItemEditButton.cpp b/src/tools/common/PropTree/PropTreeItemEditButton.cpp new file mode 100644 index 0000000..977fee3 --- /dev/null +++ b/src/tools/common/PropTree/PropTreeItemEditButton.cpp @@ -0,0 +1,259 @@ +// PropTreeItemEdit.cpp : implementation file +// +// Copyright (C) 1998-2001 Scott Ramsay +// sramsay@gonavi.com +// http://www.gonavi.com +// +// This material is provided "as is", with absolutely no warranty expressed +// or implied. Any use is at your own risk. +// +// Permission to use or copy this software for any purpose is hereby granted +// without fee, provided the above notices are retained on all copies. +// Permission to modify the code and to distribute modified code is granted, +// provided the above notices are retained, and a notice that the code was +// modified is included with the above copyright notice. +// +// If you use this code, drop me an email. I'd like to know if you find the code +// useful. + +//#include "stdafx.h" +#include "../../../idlib/precompiled.h" +#pragma hdrstop + +#include "proptree.h" +#include "PropTreeItemEditButton.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +#define BUTTON_SIZE 17 + +///////////////////////////////////////////////////////////////////////////// +// CPropTreeItemEditButton + +CPropTreeItemEditButton::CPropTreeItemEditButton() : +m_sEdit(_T("")), +m_nFormat(ValueFormatText), +m_bPassword(FALSE), +m_fValue(0.0f) +{ + mouseDown = false; +} + +CPropTreeItemEditButton::~CPropTreeItemEditButton() +{ +} + + +BEGIN_MESSAGE_MAP(CPropTreeItemEditButton, CEdit) + //{{AFX_MSG_MAP(CPropTreeItemEditButton) + ON_WM_GETDLGCODE() + ON_WM_KEYDOWN() + ON_CONTROL_REFLECT(EN_KILLFOCUS, OnKillfocus) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CPropTreeItemEditButton message handlers + +LONG CPropTreeItemEditButton::DrawItem( CDC* pDC, const RECT& rc, LONG x, LONG y ) +{ + CSize textSize; + CRect textRect; + LONG nTotal = 0; + + nTotal = CPropTreeItemEdit::DrawItem( pDC, rc, x, y ); + + textSize = pDC->GetOutputTextExtent( buttonText ); + + buttonRect.left = m_rc.right - ( textSize.cx + 12 + 4); + buttonRect.top = m_rc.top + ((m_rc.bottom - m_rc.top)/2)-BUTTON_SIZE/2; + buttonRect.right = buttonRect.left + textSize.cx + 12; + buttonRect.bottom = buttonRect.top + BUTTON_SIZE; + + UINT buttonStyle; + + if ( (m_dwState & TreeItemChecked) ) { + buttonStyle = DFCS_BUTTONPUSH | DFCS_PUSHED; + } else { + buttonStyle = DFCS_BUTTONPUSH; + } + pDC->DrawFrameControl(&buttonRect, DFC_BUTTON, buttonStyle ); + + textRect = buttonRect; + textRect.left += 4; + textRect.right -= 8; + pDC->DrawText( buttonText, textRect, DT_SINGLELINE|DT_VCENTER ); + + //Adjust hit test rect to acount for window scrolling + hitTestRect = buttonRect; + hitTestRect.OffsetRect(0, m_pProp->GetOrigin().y); + + return nTotal; +} + +void CPropTreeItemEditButton::DrawAttribute(CDC* pDC, const RECT& rc) +{ + ASSERT(m_pProp!=NULL); + + pDC->SelectObject(IsReadOnly() ? m_pProp->GetNormalFont() : m_pProp->GetBoldFont()); + pDC->SetTextColor(RGB(0,0,0)); + pDC->SetBkMode(TRANSPARENT); + + CRect r = rc; + r.right = buttonRect.left - 5; + + TCHAR ch; + + // can't use GetPasswordChar(), because window may not be created yet + ch = (m_bPassword) ? '*' : '\0'; + + if (ch) + { + CString s; + + s = m_sEdit; + for (LONG i=0; iDrawText(s, r, DT_SINGLELINE|DT_VCENTER); + } + else + { + pDC->DrawText(m_sEdit, r, DT_SINGLELINE|DT_VCENTER); + } +} + + + +void CPropTreeItemEditButton::SetAsPassword(BOOL bPassword) +{ + m_bPassword = bPassword; +} + + +void CPropTreeItemEditButton::SetValueFormat(ValueFormat nFormat) +{ + m_nFormat = nFormat; +} + + +LPARAM CPropTreeItemEditButton::GetItemValue() +{ + switch (m_nFormat) + { + case ValueFormatNumber: + return _ttoi(m_sEdit); + + case ValueFormatFloatPointer: + _stscanf(m_sEdit, _T("%f"), &m_fValue); + return (LPARAM)&m_fValue; + } + + return (LPARAM)(LPCTSTR)m_sEdit; +} + + +void CPropTreeItemEditButton::SetItemValue(LPARAM lParam) +{ + switch (m_nFormat) + { + case ValueFormatNumber: + m_sEdit.Format(_T("%d"), lParam); + return; + + case ValueFormatFloatPointer: + { + TCHAR tmp[MAX_PATH]; + m_fValue = *(float*)lParam; + _stprintf(tmp, _T("%f"), m_fValue); + m_sEdit = tmp; + } + return; + } + + if (lParam==0L) + { + TRACE0("CPropTreeItemEditButton::SetItemValue - Invalid lParam value\n"); + return; + } + + m_sEdit = (LPCTSTR)lParam; +} + + +void CPropTreeItemEditButton::OnMove() +{ + if (IsWindow(m_hWnd)) + SetWindowPos(NULL, m_rc.left, m_rc.top, m_rc.Width(), m_rc.Height(), SWP_NOZORDER|SWP_NOACTIVATE); +} + + +void CPropTreeItemEditButton::OnRefresh() +{ + if (IsWindow(m_hWnd)) + SetWindowText(m_sEdit); +} + + +void CPropTreeItemEditButton::OnCommit() +{ + // hide edit control + ShowWindow(SW_HIDE); + + // store edit text for GetItemValue + GetWindowText(m_sEdit); +} + + +void CPropTreeItemEditButton::OnActivate(int activateType, CPoint point) +{ + // Check if the edit control needs creation + if (!IsWindow(m_hWnd)) + { + DWORD dwStyle; + + dwStyle = WS_CHILD|ES_AUTOHSCROLL; + Create(dwStyle, m_rc, m_pProp->GetCtrlParent(), GetCtrlID()); + SendMessage(WM_SETFONT, (WPARAM)m_pProp->GetNormalFont()->m_hObject); + } + + SetPasswordChar((TCHAR)(m_bPassword ? '*' : 0)); + SetWindowText(m_sEdit); + SetSel(0, -1); + + SetWindowPos(NULL, m_rc.left, m_rc.top, m_rc.Width() - buttonRect.Width() - 5, m_rc.Height(), SWP_NOZORDER|SWP_SHOWWINDOW); + SetFocus(); +} + + +UINT CPropTreeItemEditButton::OnGetDlgCode() +{ + return CEdit::OnGetDlgCode()|DLGC_WANTALLKEYS; +} + + +void CPropTreeItemEditButton::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) +{ + if (nChar==VK_RETURN) + CommitChanges(); + + CEdit::OnKeyDown(nChar, nRepCnt, nFlags); +} + + +void CPropTreeItemEditButton::OnKillfocus() +{ + CommitChanges(); +} + +BOOL CPropTreeItemEditButton::HitButton( const POINT& pt ) { + return hitTestRect.PtInRect( pt ); +} + +void CPropTreeItemEditButton::SetButtonText( LPCSTR text ) { + buttonText = text; +} diff --git a/src/tools/common/PropTree/PropTreeItemEditButton.h b/src/tools/common/PropTree/PropTreeItemEditButton.h new file mode 100644 index 0000000..1ad2670 --- /dev/null +++ b/src/tools/common/PropTree/PropTreeItemEditButton.h @@ -0,0 +1,125 @@ +#ifndef __PROP_TREE_ITEM_EDIT_BUTTON_H__ +#define __PROP_TREE_ITEM_EDIT_BUTTON_H__ + + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// PropTreeItemEdit.h : header file +// +// Copyright (C) 1998-2001 Scott Ramsay +// sramsay@gonavi.com +// http://www.gonavi.com +// +// This material is provided "as is", with absolutely no warranty expressed +// or implied. Any use is at your own risk. +// +// Permission to use or copy this software for any purpose is hereby granted +// without fee, provided the above notices are retained on all copies. +// Permission to modify the code and to distribute modified code is granted, +// provided the above notices are retained, and a notice that the code was +// modified is included with the above copyright notice. +// +// If you use this code, drop me an email. I'd like to know if you find the code +// useful. + +#include "PropTreeItem.h" +//#include "PropTreeItemEdit.h" + +///////////////////////////////////////////////////////////////////////////// +// CPropTreeItemEditButton window + +class PROPTREE_API CPropTreeItemEditButton : public CPropTreeItemEdit +{ + // Construction +public: + CPropTreeItemEditButton(); + virtual ~CPropTreeItemEditButton(); + + // Attributes +public: + // The non-attribute area needs drawing + virtual LONG DrawItem(CDC* pDC, const RECT& rc, LONG x, LONG y); + + // The attribute area needs drawing + virtual void DrawAttribute(CDC* pDC, const RECT& rc); + + // Retrieve the item's attribute value + virtual LPARAM GetItemValue(); + + // Set the item's attribute value + virtual void SetItemValue(LPARAM lParam); + + // Called when attribute area has changed size + virtual void OnMove(); + + // Called when the item needs to refresh its data + virtual void OnRefresh(); + + // Called when the item needs to commit its changes + virtual void OnCommit(); + + // Called to activate the item + virtual void OnActivate(int activateType, CPoint point); + + + enum ValueFormat + { + ValueFormatText, + ValueFormatNumber, + ValueFormatFloatPointer + }; + + // Set to specifify format of SetItemValue/GetItemValue + void SetValueFormat(ValueFormat nFormat); + + // Set to TRUE for to use a password edit control + void SetAsPassword(BOOL bPassword); + + // Overrideable - Returns TRUE if the point is on the button + virtual BOOL HitButton(const POINT& pt); + + void SetButtonText( LPCSTR text ); + +protected: + CString m_sEdit; + float m_fValue; + + ValueFormat m_nFormat; + BOOL m_bPassword; + + + CString buttonText; + CRect buttonRect; + CRect hitTestRect; + bool mouseDown; + + + // Operations +public: + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CPropTreeItemEditButton) + //}}AFX_VIRTUAL + + // Implementation +public: + + // Generated message map functions +protected: + //{{AFX_MSG(CPropTreeItemEditButton) + afx_msg UINT OnGetDlgCode(); + afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnKillfocus(); + //}}AFX_MSG + + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // __PROP_TREE_ITEM_EDIT_BUTTON_H__ diff --git a/src/tools/common/PropTree/PropTreeItemFileEdit.cpp b/src/tools/common/PropTree/PropTreeItemFileEdit.cpp new file mode 100644 index 0000000..2e95650 --- /dev/null +++ b/src/tools/common/PropTree/PropTreeItemFileEdit.cpp @@ -0,0 +1,130 @@ +// PropTreeItemFileEdit.cpp : implementation file + + +//#include "stdafx.h" +#include "../../../idlib/precompiled.h" +#pragma hdrstop + +#include "proptree.h" +#include "PropTreeItemFileEdit.h" + +#include "../../../sys/win32/rc/proptree_Resource.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CPropTreeItemFileEdit + +CPropTreeItemFileEdit::CPropTreeItemFileEdit() { +} + +CPropTreeItemFileEdit::~CPropTreeItemFileEdit() { +} + + +BEGIN_MESSAGE_MAP(CPropTreeItemFileEdit, CPropTreeItemEdit) + //{{AFX_MSG_MAP(CPropTreeItemFileEdit) + //}}AFX_MSG_MAP + ON_WM_CONTEXTMENU() + ON_WM_CREATE() + + ON_COMMAND(ID_EDITMENU_INSERTFILE, OnInsertFile) + ON_COMMAND(ID_EDIT_UNDO, OnEditUndo) + ON_COMMAND(ID_EDIT_CUT, OnEditCut) + ON_COMMAND(ID_EDIT_COPY, OnEditCopy) + ON_COMMAND(ID_EDIT_PASTE, OnEditPaste) + ON_COMMAND(ID_EDIT_DELETE, OnEditDelete) + ON_COMMAND(ID_EDIT_SELECTALL, OnEditSelectAll) + +END_MESSAGE_MAP() + + +void CPropTreeItemFileEdit::OnContextMenu(CWnd* pWnd, CPoint point) { + + CMenu FloatingMenu; + VERIFY(FloatingMenu.LoadMenu(IDR_ME_EDIT_MENU)); + CMenu* pPopupMenu = FloatingMenu.GetSubMenu (0); + + if(CanUndo()) { + pPopupMenu->EnableMenuItem(ID_EDIT_UNDO, MF_BYCOMMAND | MF_ENABLED); + } else { + pPopupMenu->EnableMenuItem(ID_EDIT_UNDO, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); + } + + DWORD dwSel = GetSel(); + if(HIWORD(dwSel) != LOWORD(dwSel)) { + pPopupMenu->EnableMenuItem(ID_EDIT_CUT, MF_BYCOMMAND | MF_ENABLED); + pPopupMenu->EnableMenuItem(ID_EDIT_COPY, MF_BYCOMMAND | MF_ENABLED); + pPopupMenu->EnableMenuItem(ID_EDIT_DELETE, MF_BYCOMMAND | MF_ENABLED); + } else { + pPopupMenu->EnableMenuItem(ID_EDIT_CUT, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); + pPopupMenu->EnableMenuItem(ID_EDIT_COPY, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); + pPopupMenu->EnableMenuItem(ID_EDIT_DELETE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); + } + + pPopupMenu->TrackPopupMenu (TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this); +} + +int CPropTreeItemFileEdit::OnCreate(LPCREATESTRUCT lpCreateStruct) +{ + if (CPropTreeItemEdit::OnCreate(lpCreateStruct) == -1) + return -1; + + // TODO: Add your specialized creation code here + + return 0; +} + +void CPropTreeItemFileEdit::OnInsertFile() { + CFileDialog dlg(TRUE); + dlg.m_ofn.Flags |= OFN_FILEMUSTEXIST; + + int startSel, endSel; + GetSel(startSel, endSel); + + if( dlg.DoModal()== IDOK) { + + idStr currentText = (char*)GetItemValue(); + idStr newText = currentText.Left(startSel) + currentText.Right(currentText.Length() - endSel); + + idStr filename = fileSystem->OSPathToRelativePath(dlg.m_ofn.lpstrFile); + filename.BackSlashesToSlashes(); + + + newText.Insert(filename, startSel); + + SetItemValue((LPARAM)newText.c_str()); + m_pProp->RefreshItems(this); + + m_pProp->SendNotify(PTN_ITEMCHANGED, this); + + } +} + +void CPropTreeItemFileEdit::OnEditUndo() { + Undo(); +} + +void CPropTreeItemFileEdit::OnEditCut() { + Cut(); +} + +void CPropTreeItemFileEdit::OnEditCopy() { + Copy(); +} + +void CPropTreeItemFileEdit::OnEditPaste() { + Paste(); +} + +void CPropTreeItemFileEdit::OnEditDelete() { + Clear(); +} + +void CPropTreeItemFileEdit::OnEditSelectAll() { + SetSel(0, -1); +} diff --git a/src/tools/common/PropTree/PropTreeItemFileEdit.h b/src/tools/common/PropTree/PropTreeItemFileEdit.h new file mode 100644 index 0000000..8d959d0 --- /dev/null +++ b/src/tools/common/PropTree/PropTreeItemFileEdit.h @@ -0,0 +1,54 @@ +#ifndef __PROP_TREE_ITEM_FILE_EDIT_H__ +#define __PROP_TREE_ITEM_FILE_EDIT_H__ + +#if _MSC_VER > 1000 +#pragma once +#endif + + +//#include "PropTreeItem.h" +//#include "PropTreeItemEdit.h" + +class PROPTREE_API CPropTreeItemFileEdit : public CPropTreeItemEdit +{ + // Construction +public: + CPropTreeItemFileEdit(); + virtual ~CPropTreeItemFileEdit(); + + // Operations +public: + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CPropTreeItemFileEdit) + //}}AFX_VIRTUAL + + // Implementation +public: + + // Generated message map functions +protected: + //{{AFX_MSG(CPropTreeItemFileEdit) + //}}AFX_MSG + + afx_msg void OnInsertFile(); + afx_msg void OnEditUndo(); + afx_msg void OnEditCut(); + afx_msg void OnEditCopy(); + afx_msg void OnEditPaste(); + afx_msg void OnEditDelete(); + afx_msg void OnEditSelectAll(); + + DECLARE_MESSAGE_MAP() +public: + afx_msg void OnContextMenu(CWnd* pWnd, CPoint point); + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} + + +#endif diff --git a/src/tools/common/PropTree/PropTreeItemStatic.cpp b/src/tools/common/PropTree/PropTreeItemStatic.cpp new file mode 100644 index 0000000..c87e7f4 --- /dev/null +++ b/src/tools/common/PropTree/PropTreeItemStatic.cpp @@ -0,0 +1,67 @@ +// PropTreeItemStatic.cpp +// +// Copyright (C) 1998-2001 Scott Ramsay +// sramsay@gonavi.com +// http://www.gonavi.com +// +// This material is provided "as is", with absolutely no warranty expressed +// or implied. Any use is at your own risk. +// +// Permission to use or copy this software for any purpose is hereby granted +// without fee, provided the above notices are retained on all copies. +// Permission to modify the code and to distribute modified code is granted, +// provided the above notices are retained, and a notice that the code was +// modified is included with the above copyright notice. +// +// If you use this code, drop me an email. I'd like to know if you find the code +// useful. + +//#include "stdafx.h" +#include "../../../idlib/precompiled.h" +#pragma hdrstop + +#include "PropTree.h" + +#include "PropTreeItemStatic.h" + + +CPropTreeItemStatic::CPropTreeItemStatic() : + m_sAttribute(_T("")) +{ +} + + +CPropTreeItemStatic::~CPropTreeItemStatic() +{ +} + + +void CPropTreeItemStatic::DrawAttribute(CDC* pDC, const RECT& rc) +{ + ASSERT(m_pProp!=NULL); + + pDC->SelectObject(m_pProp->GetNormalFont()); + pDC->SetTextColor(RGB(0,0,0)); + pDC->SetBkMode(TRANSPARENT); + + CRect r = rc; + pDC->DrawText(m_sAttribute, r, DT_SINGLELINE|DT_VCENTER); +} + + +LPARAM CPropTreeItemStatic::GetItemValue() +{ + return (LPARAM)(LPCTSTR)m_sAttribute; +} + + +void CPropTreeItemStatic::SetItemValue(LPARAM lParam) +{ + if (lParam==0L) + { + TRACE0("CPropTreeItemStatic::SetItemValue() - Invalid lParam value\n"); + return; + } + + m_sAttribute = (LPCTSTR)lParam; +} diff --git a/src/tools/common/PropTree/PropTreeItemStatic.h b/src/tools/common/PropTree/PropTreeItemStatic.h new file mode 100644 index 0000000..e39fa2d --- /dev/null +++ b/src/tools/common/PropTree/PropTreeItemStatic.h @@ -0,0 +1,45 @@ +// PropTreeItemStatic.h +// +// Copyright (C) 1998-2001 Scott Ramsay +// sramsay@gonavi.com +// http://www.gonavi.com +// +// This material is provided "as is", with absolutely no warranty expressed +// or implied. Any use is at your own risk. +// +// Permission to use or copy this software for any purpose is hereby granted +// without fee, provided the above notices are retained on all copies. +// Permission to modify the code and to distribute modified code is granted, +// provided the above notices are retained, and a notice that the code was +// modified is included with the above copyright notice. +// +// If you use this code, drop me an email. I'd like to know if you find the code +// useful. + +#ifndef _PROPTREEITEMSTATIC_H +#define _PROPTREEITEMSTATIC_H + +#include "PropTreeItem.h" + +class PROPTREE_API CPropTreeItemStatic : public CPropTreeItem +{ +public: + CPropTreeItemStatic(); + virtual ~CPropTreeItemStatic(); + +public: + // The attribute area needs drawing + virtual void DrawAttribute(CDC* pDC, const RECT& rc); + + // Retrieve the item's attribute value (in this case the CString) + virtual LPARAM GetItemValue(); + + // Set the item's attribute value + virtual void SetItemValue(LPARAM lParam); + +protected: + CString m_sAttribute; +}; + + +#endif // _PROPTREEITEMSTATIC_H diff --git a/src/tools/common/PropTree/PropTreeList.cpp b/src/tools/common/PropTree/PropTreeList.cpp new file mode 100644 index 0000000..60d0e57 --- /dev/null +++ b/src/tools/common/PropTree/PropTreeList.cpp @@ -0,0 +1,635 @@ +// PropTreeList.cpp : implementation file +// +// Copyright (C) 1998-2001 Scott Ramsay +// sramsay@gonavi.com +// http://www.gonavi.com +// +// This material is provided "as is", with absolutely no warranty expressed +// or implied. Any use is at your own risk. +// +// Permission to use or copy this software for any purpose is hereby granted +// without fee, provided the above notices are retained on all copies. +// Permission to modify the code and to distribute modified code is granted, +// provided the above notices are retained, and a notice that the code was +// modified is included with the above copyright notice. +// +// If you use this code, drop me an email. I'd like to know if you find the code +// useful. + +//#include "stdafx.h" +#include "../../../idlib/precompiled.h" +#pragma hdrstop + +#include "PropTree.h" +#include "../../../sys/win32/rc/proptree_Resource.h" +#include "PropTreeList.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +#define PROPTREEITEM_EXPANDCOLUMN 16 // width of the expand column +#define PROPTREEITEM_COLRNG 5 // width of splitter +#define PROPTREEITEM_DEFHEIGHT 21 // default heigt of an item + +extern HINSTANCE ghInst; + +///////////////////////////////////////////////////////////////////////////// +// CPropTreeList + +CPropTreeList::CPropTreeList() : + m_pProp(NULL), + m_BackBufferSize(0,0), + m_bColDrag(FALSE), + m_nPrevCol(0) +{ +} + +CPropTreeList::~CPropTreeList() +{ +} + + +BEGIN_MESSAGE_MAP(CPropTreeList, CWnd) + //{{AFX_MSG_MAP(CPropTreeList) + ON_WM_SIZE() + ON_WM_PAINT() + ON_WM_SETCURSOR() + ON_WM_LBUTTONDOWN() + ON_WM_LBUTTONUP() + ON_WM_LBUTTONDBLCLK() + ON_WM_MOUSEMOVE() + ON_WM_MOUSEWHEEL() + ON_WM_KEYDOWN() + ON_WM_GETDLGCODE() + ON_WM_VSCROLL() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + +///////////////////////////////////////////////////////////////////////////// +// CPropTreeList message handlers + +void CPropTreeList::SetPropOwner(CPropTree* pProp) +{ + m_pProp = pProp; +} + + +BOOL CPropTreeList::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID) +{ + CWnd* pWnd = this; + + LPCTSTR pszCreateClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS, ::LoadCursor(NULL, IDC_ARROW)); + + return pWnd->Create(pszCreateClass, _T(""), dwStyle, rect, pParentWnd, nID); +} + + +void CPropTreeList::OnSize(UINT nType, int cx, int cy) +{ + CWnd::OnSize(nType, cx, cy); + + RecreateBackBuffer(cx, cy); + + if (m_pProp) + { + UpdateResize(); + Invalidate(); + UpdateWindow(); + + // inform all items that a resize has been made + m_pProp->UpdateMoveAllItems(); + } +} + + +void CPropTreeList::RecreateBackBuffer(int cx, int cy) +{ + if (m_BackBufferSize.cxGetRootItem()->GetTotalHeight(); + si.nPage = nHeight; + + if ((int)si.nPage>si.nMax) + m_pProp->SetOriginOffset(0); + + SetScrollInfo(SB_VERT, &si, TRUE); + + // force set column for clipping + m_pProp->SetColumn(m_pProp->GetColumn()); +} + + +void CPropTreeList::OnPaint() +{ + CPaintDC dc(this); + CDC memdc; + CBitmap* pOldBitmap; + + ASSERT(m_pProp!=NULL); + + m_pProp->ClearVisibleList(); + + memdc.CreateCompatibleDC(&dc); + pOldBitmap = memdc.SelectObject(&m_BackBuffer); + + CRect rc; + GetClientRect(rc); + + // draw control background + memdc.SelectObject(GetSysColorBrush(COLOR_BTNFACE)); + memdc.PatBlt(rc.left, rc.top, rc.Width(), rc.Height(), PATCOPY); + + // draw control inside fill color + rc.DeflateRect(2,2); + memdc.PatBlt(rc.left, rc.top, rc.Width(), rc.Height(), m_pProp->IsWindowEnabled() ? WHITENESS : PATCOPY); + rc.InflateRect(2,2); + + // draw expand column + memdc.SelectObject(GetSysColorBrush(COLOR_BTNFACE)); + memdc.PatBlt(0, 0, PROPTREEITEM_EXPANDCOLUMN, rc.Height(), PATCOPY); + + // draw edge + memdc.DrawEdge(&rc, BDR_SUNKENOUTER, BF_RECT); + + CPropTreeItem* pItem; + LONG nTotal = 0; + + ASSERT(m_pProp->GetRootItem()!=NULL); + + rc.DeflateRect(2,2); + + // create clip region + HRGN hRgn = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom); + SelectClipRgn(memdc.m_hDC, hRgn); + + // draw all items + for (pItem = m_pProp->GetRootItem()->GetChild(); pItem; pItem = pItem->GetSibling()) + { + LONG nHeight = pItem->DrawItem(&memdc, rc, 0, nTotal); + nTotal += nHeight; + } + + // remove clip region + SelectClipRgn(memdc.m_hDC, NULL); + DeleteObject(hRgn); + + // copy back buffer to the display + dc.GetClipBox(&rc); + dc.BitBlt(rc.left, rc.top, rc.Width(), rc.Height(), &memdc, rc.left, rc.top, SRCCOPY); + memdc.DeleteDC(); +} + + +BOOL CPropTreeList::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) +{ + if (nHitTest==HTCLIENT) + { + CPoint pt; + + ASSERT(m_pProp!=NULL); + + GetCursorPos(&pt); + ScreenToClient(&pt); + + switch (m_pProp->HitTest(pt)) + { + case HTCOLUMN: + SetCursor(LoadCursor(ghInst, MAKEINTRESOURCE(IDC_SPLITTER))); + return TRUE; + + case HTCHECKBOX: + case HTBUTTON: + case HTEXPAND: + SetCursor(LoadCursor(ghInst, MAKEINTRESOURCE(IDC_FPOINT))); + return TRUE; + } + } + + return CWnd::OnSetCursor(pWnd, nHitTest, message); +} + + +void CPropTreeList::OnLButtonDown(UINT, CPoint point) +{ + ASSERT(m_pProp!=NULL); + + if (m_pProp->IsDisableInput()) + return; + + m_pProp->SendNotify(NM_CLICK); + + if (!m_pProp->IsWindowEnabled()) + return; + + SetFocus(); + + LONG nHit = m_pProp->HitTest(point); + + CPropTreeItem* pItem; + CRect rc; + CDC* pDC; + + switch (nHit) + { + case HTCOLUMN: + if (m_pProp->SendNotify(PTN_COLUMNCLICK)) + break; + + m_bColDrag = TRUE; + SetCapture(); + + m_nPrevCol = m_pProp->GetOrigin().x; + + // paint drag line + pDC = GetDC(); + GetClientRect(rc); + pDC->PatBlt(m_nPrevCol - PROPTREEITEM_COLRNG/2, 0, PROPTREEITEM_COLRNG, rc.bottom, PATINVERT); + ReleaseDC(pDC); + break; + + case HTCHECKBOX: + if ((pItem = m_pProp->FindItem(point))!=NULL) + { + pItem->Check(!pItem->IsChecked()); + m_pProp->SendNotify(PTN_CHECKCLICK, pItem); + Invalidate(); + } + break; + case HTBUTTON: + if ((pItem = m_pProp->FindItem(point))!=NULL) + { + pItem->Check(); + m_pProp->SendNotify(PTN_ITEMBUTTONCLICK, pItem); + Invalidate(); + } + break; + case HTEXPAND: + if ((pItem = m_pProp->FindItem(point))!=NULL) + { + if (pItem->GetChild() && !m_pProp->SendNotify(PTN_ITEMEXPANDING, pItem)) + { + pItem->Expand(!pItem->IsExpanded()); + + UpdateResize(); + Invalidate(); + UpdateWindow(); + CheckVisibleFocus(); + } + } + break; + + default: + if ((pItem = m_pProp->FindItem(point))!=NULL) + { + CPropTreeItem* pOldFocus = m_pProp->GetFocusedItem(); + + m_pProp->SelectItems(NULL, FALSE); + m_pProp->SetFocusedItem(pItem); + + pItem->Select(); + + Invalidate(); + + if (pItem!=pOldFocus) + m_pProp->SendNotify(PTN_SELCHANGE, pItem); + + if (nHit==HTATTRIBUTE && !pItem->IsRootLevel()) + { + if (!m_pProp->SendNotify(PTN_PROPCLICK, pItem) && !pItem->IsReadOnly()) + pItem->Activate(CPropTreeItem::ACTIVATE_TYPE_MOUSE, point); + } + } + else + { + m_pProp->SelectItems(NULL, FALSE); + m_pProp->SetFocusedItem(NULL); + m_pProp->SendNotify(PTN_SELCHANGE); + Invalidate(); + } + break; + } +} + + +void CPropTreeList::OnLButtonUp(UINT, CPoint point) +{ + if (m_bColDrag) + { + CDC* pDC = GetDC(); + CRect rc; + + GetClientRect(rc); + pDC->PatBlt(m_nPrevCol - PROPTREEITEM_COLRNG/2, 0, PROPTREEITEM_COLRNG, rc.bottom, PATINVERT); + ReleaseDC(pDC); + + m_bColDrag = FALSE; + ReleaseCapture(); + + m_pProp->SetColumn(point.x); + m_pProp->UpdateMoveAllItems(); + Invalidate(); + } else { + LONG nHit = m_pProp->HitTest(point); + CPropTreeItem* pItem; + + switch (nHit) + { + case HTBUTTON: + if ((pItem = m_pProp->FindItem(point))!=NULL) + { + pItem->Check( FALSE ); + Invalidate(); + } + break; + default: + break; + } + } +} + + +void CPropTreeList::OnLButtonDblClk(UINT, CPoint point) +{ + ASSERT(m_pProp!=NULL); + + m_pProp->SendNotify(NM_DBLCLK); + + CPropTreeItem* pItem; + CPropTreeItem* pOldFocus; + + if ((pItem = m_pProp->FindItem(point))!=NULL && pItem->GetChild()) + { + switch (m_pProp->HitTest(point)) + { + case HTCOLUMN: + break; + + case HTCHECKBOX: + pItem->Check(!pItem->IsChecked()); + m_pProp->SendNotify(PTN_CHECKCLICK, pItem); + Invalidate(); + break; + + case HTATTRIBUTE: + if (!pItem->IsRootLevel()) + break; + + // pass thru to default + + default: + pOldFocus = m_pProp->GetFocusedItem(); + m_pProp->SelectItems(NULL, FALSE); + m_pProp->SetFocusedItem(pItem); + pItem->Select(); + + if (pItem!=pOldFocus) + m_pProp->SendNotify(PTN_SELCHANGE, pItem); + + // pass thru to HTEXPAND + + case HTEXPAND: + if (!m_pProp->SendNotify(PTN_ITEMEXPANDING, pItem)) + { + pItem->Expand(!pItem->IsExpanded()); + + UpdateResize(); + Invalidate(); + UpdateWindow(); + CheckVisibleFocus(); + } + break; + } + } +} + + +void CPropTreeList::OnMouseMove(UINT, CPoint point) +{ + if (m_bColDrag) + { + CDC* pDC = GetDC(); + CRect rc; + + GetClientRect(rc); + pDC->PatBlt(m_nPrevCol - PROPTREEITEM_COLRNG/2, 0, PROPTREEITEM_COLRNG, rc.bottom, PATINVERT); + pDC->PatBlt(point.x - PROPTREEITEM_COLRNG/2, 0, PROPTREEITEM_COLRNG, rc.bottom, PATINVERT); + m_nPrevCol = point.x; + ReleaseDC(pDC); + } +} + + +BOOL CPropTreeList::OnMouseWheel(UINT, short zDelta, CPoint) +{ + SCROLLINFO si; + + ZeroMemory(&si, sizeof(SCROLLINFO)); + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_RANGE; + + GetScrollInfo(SB_VERT, &si); + + CRect rc; + GetClientRect(rc); + + if (si.nMax - si.nMin < rc.Height()) + return TRUE; + + SetFocus(); + OnVScroll(zDelta < 0 ? SB_LINEDOWN : SB_LINEUP, 0, NULL); + + return TRUE; +} + + +void CPropTreeList::OnKeyDown(UINT nChar, UINT, UINT) +{ + + CPropTreeItem* pItem; + + ASSERT(m_pProp!=NULL); + + if (m_pProp->IsDisableInput() || !m_pProp->IsWindowEnabled()) + return; + + switch (nChar) + { + case VK_RETURN: + if ((pItem = m_pProp->GetFocusedItem())!=NULL && !pItem->IsRootLevel() && !pItem->IsReadOnly()) + { + pItem->Activate(CPropTreeItem::ACTIVATE_TYPE_KEYBOARD, CPoint(0,0)); + } + break; + + case VK_HOME: + if (m_pProp->FocusFirst()) + Invalidate(); + break; + + case VK_END: + if (m_pProp->FocusLast()) + Invalidate(); + break; + + case VK_LEFT: + if ((pItem = m_pProp->GetFocusedItem())!=NULL) + { + if (!m_pProp->SendNotify(PTN_ITEMEXPANDING, pItem)) + { + if (pItem->GetChild() && pItem->IsExpanded()) + { + pItem->Expand(FALSE); + UpdateResize(); + Invalidate(); + UpdateWindow(); + CheckVisibleFocus(); + break; + } + } + } + else + break; + // pass thru to next case VK_UP + case VK_UP: + if (m_pProp->FocusPrev()) + Invalidate(); + break; + + case VK_RIGHT: + if ((pItem = m_pProp->GetFocusedItem())!=NULL) + { + if (!m_pProp->SendNotify(PTN_ITEMEXPANDING, pItem)) + { + if (pItem->GetChild() && !pItem->IsExpanded()) + { + pItem->Expand(); + UpdateResize(); + Invalidate(); + UpdateWindow(); + CheckVisibleFocus(); + break; + } + } + } + else + break; + // pass thru to next case VK_DOWN + case VK_DOWN: + if (m_pProp->FocusNext()) + Invalidate(); + break; + } +} + + +UINT CPropTreeList::OnGetDlgCode() +{ + return DLGC_WANTARROWS|DLGC_WANTCHARS|DLGC_WANTALLKEYS; +} + + +void CPropTreeList::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar*) +{ + SCROLLINFO si; + CRect rc; + LONG nHeight; + + SetFocus(); + + GetClientRect(rc); + nHeight = rc.Height() + 1; + + ZeroMemory(&si, sizeof(SCROLLINFO)); + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_RANGE; + + GetScrollInfo(SB_VERT, &si); + + LONG ny = m_pProp->GetOrigin().y; + + switch (nSBCode) + { + case SB_LINEDOWN: + ny += PROPTREEITEM_DEFHEIGHT; + break; + + case SB_LINEUP: + ny -= PROPTREEITEM_DEFHEIGHT; + break; + + case SB_PAGEDOWN: + ny += nHeight; + break; + + case SB_PAGEUP: + ny -= nHeight; + break; + + case SB_THUMBTRACK: + ny = nPos; + break; + } + + ny = __min(__max(ny, si.nMin), si.nMax - nHeight); + + m_pProp->SetOriginOffset(ny); + si.fMask = SIF_POS; + si.nPos = ny; + + SetScrollInfo(SB_VERT, &si, TRUE); + Invalidate(); +} + + +void CPropTreeList::CheckVisibleFocus() +{ + ASSERT(m_pProp!=NULL); + + CPropTreeItem* pItem; + + if ((pItem = m_pProp->GetFocusedItem())==NULL) + return; + + if (!m_pProp->IsItemVisible(pItem)) + { + if (m_pProp->IsSingleSelection()) + pItem->Select(FALSE); + + m_pProp->SetFocusedItem(NULL); + m_pProp->SendNotify(PTN_SELCHANGE, NULL); + + Invalidate(); + } +} diff --git a/src/tools/common/PropTree/PropTreeList.h b/src/tools/common/PropTree/PropTreeList.h new file mode 100644 index 0000000..fe25c92 --- /dev/null +++ b/src/tools/common/PropTree/PropTreeList.h @@ -0,0 +1,99 @@ +#if !defined(AFX_PROPTREELIST_H__2E09E831_09F5_44AA_B41D_9C4BF495873C__INCLUDED_) +#define AFX_PROPTREELIST_H__2E09E831_09F5_44AA_B41D_9C4BF495873C__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// PropTreeList.h : header file +// +// Copyright (C) 1998-2001 Scott Ramsay +// sramsay@gonavi.com +// http://www.gonavi.com +// +// This material is provided "as is", with absolutely no warranty expressed +// or implied. Any use is at your own risk. +// +// Permission to use or copy this software for any purpose is hereby granted +// without fee, provided the above notices are retained on all copies. +// Permission to modify the code and to distribute modified code is granted, +// provided the above notices are retained, and a notice that the code was +// modified is included with the above copyright notice. +// +// If you use this code, drop me an email. I'd like to know if you find the code +// useful. + +class CPropTree; + +///////////////////////////////////////////////////////////////////////////// +// CPropTreeList window + +class PROPTREE_API CPropTreeList : public CWnd +{ +// Construction +public: + CPropTreeList(); + virtual ~CPropTreeList(); + + BOOL Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID); + +// Attributes +public: + void SetPropOwner(CPropTree* pProp); + +protected: + // CPropTree class that this class belongs + CPropTree* m_pProp; + + // bitmap back buffer for flicker free drawing + CBitmap m_BackBuffer; + + // current diminsions of the back buffer + CSize m_BackBufferSize; + + // splitter pevious position + LONG m_nPrevCol; + + // TRUE if we are dragging the splitter + BOOL m_bColDrag; + +// Operations +public: + void UpdateResize(); + +protected: + void RecreateBackBuffer(int cx, int cy); + void CheckVisibleFocus(); + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CPropTreeList) + //}}AFX_VIRTUAL + +// Implementation +public: + + // Generated message map functions +protected: + //{{AFX_MSG(CPropTreeList) + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnPaint(); + afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message); + afx_msg void OnLButtonDown(UINT nFlags, CPoint point); + afx_msg void OnLButtonUp(UINT nFlags, CPoint point); + afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point); + afx_msg void OnMouseMove(UINT nFlags, CPoint point); + afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt); + afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg UINT OnGetDlgCode(); + //}}AFX_MSG +public: + afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_PROPTREELIST_H__2E09E831_09F5_44AA_B41D_9C4BF495873C__INCLUDED_) diff --git a/src/tools/common/PropTree/PropTreeView.cpp b/src/tools/common/PropTree/PropTreeView.cpp new file mode 100644 index 0000000..1ccc9a2 --- /dev/null +++ b/src/tools/common/PropTree/PropTreeView.cpp @@ -0,0 +1,103 @@ +// CPropTreeView.cpp : implementation file +// + +//#include "stdafx.h" +#include "../../../idlib/precompiled.h" +#pragma hdrstop + + +#include "PropTreeView.h" + +// CPropTreeView + +IMPLEMENT_DYNCREATE(CPropTreeView, CFormView) + +CPropTreeView::CPropTreeView() +: CFormView((LPCTSTR) NULL) +{ +} + +CPropTreeView::~CPropTreeView() +{ +} + +BEGIN_MESSAGE_MAP(CPropTreeView, CView) + ON_WM_CREATE() + ON_WM_SIZE() + ON_WM_PAINT() +END_MESSAGE_MAP() + + +// CPropTreeView drawing + +void CPropTreeView::OnDraw(CDC* pDC) +{ + CDocument* pDoc = GetDocument(); + // TODO: add draw code here +} + + +// CPropTreeView diagnostics + +#ifdef _DEBUG +void CPropTreeView::AssertValid() const +{ + CView::AssertValid(); +} + +void CPropTreeView::Dump(CDumpContext& dc) const +{ + CView::Dump(dc); +} +#endif //_DEBUG + + +BOOL CPropTreeView::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, + DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, + UINT nID, CCreateContext* pContext) +{ + // create the view window itself + m_pCreateContext = pContext; + if (!CView::Create(lpszClassName, lpszWindowName, + dwStyle, rect, pParentWnd, nID, pContext)) + { + return FALSE; + } + + return TRUE; +} +// CPropTreeView message handlers + +int CPropTreeView::OnCreate(LPCREATESTRUCT lpCreateStruct) +{ + if (CView::OnCreate(lpCreateStruct) == -1) + return -1; + + DWORD dwStyle; + CRect rc; + + // PTS_NOTIFY - CPropTree will send notification messages to the parent window + dwStyle = WS_CHILD|WS_VISIBLE|PTS_NOTIFY; + + // Init the control's size to cover the entire client area + GetClientRect(rc); + + // Create CPropTree control + m_Tree.Create(dwStyle, rc, this, IDC_PROPERTYTREE); + + return 0; +} + +void CPropTreeView::OnSize(UINT nType, int cx, int cy) +{ + CView::OnSize(nType, cx, cy); + + if (::IsWindow(m_Tree.GetSafeHwnd())) + m_Tree.SetWindowPos(NULL, -1, -1, cx, cy, SWP_NOMOVE|SWP_NOZORDER); +} + + +void CPropTreeView::OnPaint() +{ + Default(); +} diff --git a/src/tools/common/PropTree/PropTreeView.h b/src/tools/common/PropTree/PropTreeView.h new file mode 100644 index 0000000..c3d9867 --- /dev/null +++ b/src/tools/common/PropTree/PropTreeView.h @@ -0,0 +1,41 @@ +#pragma once + +#include "PropTree.h" +// CPropTreeView view + +#define IDC_PROPERTYTREE 100 + +class CPropTreeView : public CFormView +{ + DECLARE_DYNCREATE(CPropTreeView) + +protected: + CPropTree m_Tree; + +protected: + CPropTreeView(); // protected constructor used by dynamic creation + virtual ~CPropTreeView(); + +public: + virtual void OnDraw(CDC* pDC); // overridden to draw this view +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + + CPropTree& GetPropertyTreeCtrl() { return m_Tree; }; + +protected: + DECLARE_MESSAGE_MAP() + +public: + virtual BOOL Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, + DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, + CCreateContext* pContext = NULL); + + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnPaint(); +}; + + diff --git a/src/tools/common/PropertyGrid.cpp b/src/tools/common/PropertyGrid.cpp new file mode 100644 index 0000000..3f51c0d --- /dev/null +++ b/src/tools/common/PropertyGrid.cpp @@ -0,0 +1,674 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "../../sys/win32/win_local.h" +#include "PropertyGrid.h" + +class rvPropertyGridItem +{ +public: + + rvPropertyGridItem ( ) + { + } + + idStr mName; + idStr mValue; + rvPropertyGrid::EItemType mType; +}; + +/* +================ +rvPropertyGrid::rvPropertyGrid + +constructor +================ +*/ +rvPropertyGrid::rvPropertyGrid ( void ) +{ + mWindow = NULL; + mEdit = NULL; + mListWndProc = NULL; + mSplitter = 100; + mSelectedItem = -1; + mEditItem = -1; + mState = STATE_NORMAL; +} + +/* +================ +rvPropertyGrid::Create + +Create a new property grid control with the given id and parent +================ +*/ +bool rvPropertyGrid::Create ( HWND parent, int id, int style ) +{ + mStyle = style; + + // Create the List view + mWindow = CreateWindowEx ( 0, "LISTBOX", "", WS_VSCROLL|WS_CHILD|WS_VISIBLE|LBS_OWNERDRAWFIXED|LBS_NOINTEGRALHEIGHT|LBS_NOTIFY, 0, 0, 0, 0, parent, (HMENU)id, win32.hInstance, 0 ); + mListWndProc = (WNDPROC)GetWindowLong ( mWindow, GWL_WNDPROC ); + SetWindowLong ( mWindow, GWL_USERDATA, (LONG)this ); + SetWindowLong ( mWindow, GWL_WNDPROC, (LONG)WndProc ); + + LoadLibrary ( "Riched20.dll" ); + mEdit = CreateWindowEx ( 0, "RichEdit20A", "", WS_CHILD, 0, 0, 0, 0, mWindow, (HMENU) 999, win32.hInstance, NULL ); + SendMessage ( mEdit, EM_SETEVENTMASK, 0, ENM_KEYEVENTS ); + + // Set the font of the list box + HDC dc; + LOGFONT lf; + + dc = GetDC ( mWindow ); + ZeroMemory ( &lf, sizeof(lf) ); + lf.lfHeight = -MulDiv(8, GetDeviceCaps(dc, LOGPIXELSY), 72); + strcpy ( lf.lfFaceName, "MS Shell Dlg" ); + SendMessage ( mWindow, WM_SETFONT, (WPARAM)CreateFontIndirect ( &lf ), 0 ); + SendMessage ( mEdit, WM_SETFONT, (WPARAM)CreateFontIndirect ( &lf ), 0 ); + ReleaseDC ( mWindow, dc ); + + RemoveAllItems ( ); + + return true; +} + +/* +================ +rvPropertyGrid::Move + +Move the window +================ +*/ +void rvPropertyGrid::Move ( int x, int y, int w, int h, BOOL redraw ) +{ + MoveWindow ( mWindow, x, y, w, h, redraw ); +} + +/* +================ +rvPropertyGrid::StartEdit + +Start editing +================ +*/ +void rvPropertyGrid::StartEdit ( int item, bool label ) +{ + rvPropertyGridItem* gitem; + RECT rItem; + + gitem = (rvPropertyGridItem*)SendMessage ( mWindow, LB_GETITEMDATA, item, 0 ); + if ( NULL == gitem ) + { + return; + } + + SendMessage ( mWindow, LB_GETITEMRECT, item, (LPARAM)&rItem ); + if ( label ) + { + rItem.right = rItem.left + mSplitter - 1; + } + else + { + rItem.left = rItem.left + mSplitter + 1; + } + + mState = STATE_EDIT; + mEditItem = item; + mEditLabel = label; + + SetWindowText ( mEdit, label?gitem->mName:gitem->mValue ); + MoveWindow ( mEdit, rItem.left, rItem.top + 2, + rItem.right - rItem.left, + rItem.bottom - rItem.top - 2, TRUE ); + ShowWindow ( mEdit, SW_SHOW ); + + SetFocus ( mEdit ); +} + +/* +================ +rvPropertyGrid::FinishEdit + +Finish editing by copying the data in the edit control to the internal value +================ +*/ +void rvPropertyGrid::FinishEdit ( void ) +{ + char value[1024]; + rvPropertyGridItem* item; + bool update; + + if ( mState != STATE_EDIT ) + { + return; + } + + assert ( mEditItem >= 0 ); + + mState = STATE_FINISHEDIT; + + update = false; + item = (rvPropertyGridItem*)SendMessage ( mWindow, LB_GETITEMDATA, mEditItem, 0 ); + assert ( item ); + + GetWindowText ( mEdit, value, 1023 ); + + if ( !value[0] ) + { + mState = STATE_EDIT; + MessageBeep ( MB_ICONASTERISK ); + return; + } + + if ( !mEditLabel && item->mValue.Cmp ( value ) ) + { + NMPROPGRID nmpg; + nmpg.hdr.code = PGN_ITEMCHANGED; + nmpg.hdr.hwndFrom = mWindow; + nmpg.hdr.idFrom = GetWindowLong ( mWindow, GWL_ID ); + nmpg.mName = item->mName; + nmpg.mValue = value; + + if ( !SendMessage ( GetParent ( mWindow ), WM_NOTIFY, 0, (LONG)&nmpg ) ) + { + mState = STATE_EDIT; + SetFocus ( mEdit ); + return; + } + + // The item may have been destroyed and recreated in the notify call so get it again + item = (rvPropertyGridItem*)SendMessage ( mWindow, LB_GETITEMDATA, mEditItem, 0 ); + if ( item ) + { + item->mValue = value; + update = true; + } + } + else if ( mEditLabel && item->mName.Cmp ( value ) ) + { + int sel; + sel = AddItem ( value, "", PGIT_STRING ); + SetCurSel ( sel ); + StartEdit ( sel, false ); + return; + } + + SetCurSel ( mEditItem ); + + mState = STATE_NORMAL; + mEditItem = -1; + + ShowWindow ( mEdit, SW_HIDE ); + SetFocus ( mWindow ); +} + +/* +================ +rvPropertyGrid::CancelEdit + +Stop editing without saving the data +================ +*/ +void rvPropertyGrid::CancelEdit ( void ) +{ + if ( mState == STATE_EDIT && !mEditLabel ) + { + if ( !*GetItemValue ( mEditItem ) ) + { + RemoveItem ( mEditItem ); + } + } + + mSelectedItem = mEditItem; + mEditItem = -1; + mState = STATE_NORMAL; + ShowWindow ( mEdit, SW_HIDE ); + SetFocus ( mWindow ); + SetCurSel ( mSelectedItem ); +} + +/* +================ +rvPropertyGrid::AddItem + +Add a new item to the property grid +================ +*/ +int rvPropertyGrid::AddItem ( const char* name, const char* value, EItemType type ) +{ + rvPropertyGridItem* item; + int insert; + + // Cant add headers if headers arent enabled + if ( type == PGIT_HEADER && !(mStyle&PGS_HEADERS) ) + { + return -1; + } + + item = new rvPropertyGridItem; + item->mName = name; + item->mValue = value; + item->mType = type; + + insert = SendMessage(mWindow,LB_GETCOUNT,0,0) - ((mStyle&PGS_ALLOWINSERT)?1:0); + + return SendMessage ( mWindow, LB_INSERTSTRING, insert, (LONG)item ); +} + +/* +================ +rvPropertyGrid::RemoveItem + +Remove the item at the given index +================ +*/ +void rvPropertyGrid::RemoveItem ( int index ) +{ + if ( index < 0 || index >= SendMessage ( mWindow, LB_GETCOUNT, 0, 0 ) ) + { + return; + } + + delete (rvPropertyGridItem*)SendMessage ( mWindow, LB_GETITEMDATA, index, 0 ); + + SendMessage ( mWindow, LB_DELETESTRING, index, 0 ); +} + +/* +================ +rvPropertyGrid::RemoveAllItems + +Remove all items from the property grid +================ +*/ +void rvPropertyGrid::RemoveAllItems ( void ) +{ + int i; + + // free the memory for all the items + for ( i = SendMessage ( mWindow, LB_GETCOUNT, 0, 0 ); i > 0; i -- ) + { + delete (rvPropertyGridItem*)SendMessage ( mWindow, LB_GETITEMDATA, i - 1, 0 ); + } + + // remove all items from the listbox itself + SendMessage ( mWindow, LB_RESETCONTENT, 0, 0 ); + + if ( mStyle & PGS_ALLOWINSERT ) + { + // Add the item used to add items + rvPropertyGridItem* item; + item = new rvPropertyGridItem; + item->mName = ""; + item->mValue = ""; + SendMessage ( mWindow, LB_ADDSTRING, 0, (LONG)item ); + } +} + +/* +================ +rvPropertyGrid::GetItemName + +Return name of item at given index +================ +*/ +const char* rvPropertyGrid::GetItemName ( int index ) +{ + rvPropertyGridItem* item; + + item = (rvPropertyGridItem*)SendMessage ( mWindow, LB_GETITEMDATA, index, 0 ); + if ( !item ) + { + return ""; + } + + return item->mName; +} + +/* +================ +rvPropertyGrid::GetItemValue + +Return value of item at given index +================ +*/ +const char* rvPropertyGrid::GetItemValue ( int index ) +{ + rvPropertyGridItem* item; + + item = (rvPropertyGridItem*)SendMessage ( mWindow, LB_GETITEMDATA, index, 0 ); + if ( !item ) + { + return ""; + } + + return item->mValue; +} + +/* +================ +rvPropertyGrid::WndProc + +Window procedure for property grid +================ +*/ +LRESULT CALLBACK rvPropertyGrid::WndProc ( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + rvPropertyGrid* grid = (rvPropertyGrid*) GetWindowLong ( hWnd, GWL_USERDATA ); + + switch ( msg ) + { + case WM_SETFOCUS: +// grid->mEditItem = -1; + break; + + case WM_KEYDOWN: + { + NMKEY nmkey; + nmkey.hdr.code = NM_KEYDOWN; + nmkey.hdr.hwndFrom = grid->mWindow; + nmkey.nVKey = wParam; + nmkey.uFlags = HIWORD(lParam); + nmkey.hdr.idFrom = GetWindowLong ( hWnd, GWL_ID ); + SendMessage ( GetParent ( hWnd ), WM_NOTIFY, nmkey.hdr.idFrom, (LPARAM)&nmkey ); + break; + } + + case WM_CHAR: + { + switch ( wParam ) + { + case VK_RETURN: + if ( grid->mSelectedItem >= 0 ) + { + grid->StartEdit ( grid->mSelectedItem, (*grid->GetItemName ( grid->mSelectedItem ))?false:true); + } + break; + } + break; + } + + case WM_KILLFOCUS: + grid->mSelectedItem = -1; + break; + + case WM_NOTIFY: + { + NMHDR* hdr; + hdr = (NMHDR*)lParam; + if ( hdr->idFrom == 999 ) + { + if ( hdr->code == EN_MSGFILTER ) + { + MSGFILTER* filter; + filter = (MSGFILTER*)lParam; + if ( filter->msg == WM_KEYDOWN ) + { + switch ( filter->wParam ) + { + case VK_RETURN: + case VK_TAB: + grid->FinishEdit ( ); + return 1; + + case VK_ESCAPE: + grid->CancelEdit ( ); + return 1; + } + } + + if ( filter->msg == WM_CHAR || filter->msg == WM_KEYUP ) + { + switch ( filter->wParam ) + { + case VK_RETURN: + case VK_TAB: + case VK_ESCAPE: + return 1; + } + } + } + } + break; + } + + case WM_COMMAND: + if ( lParam == (long)grid->mEdit ) + { + if ( HIWORD(wParam) == EN_KILLFOCUS ) + { + grid->FinishEdit ( ); + return true; + } + } + break; + + case WM_LBUTTONDBLCLK: + grid->mSelectedItem = SendMessage ( hWnd, LB_ITEMFROMPOINT, 0, lParam ); + + // fall through + + case WM_LBUTTONDOWN: + { + int item; + rvPropertyGridItem* gitem; + RECT rItem; + POINT pt; + + if ( grid->mState == rvPropertyGrid::STATE_EDIT ) + { + break; + } + + item = (short)LOWORD(SendMessage ( hWnd, LB_ITEMFROMPOINT, 0, lParam )); + if ( item == -1 ) + { + break; + } + + gitem = (rvPropertyGridItem*)SendMessage ( hWnd, LB_GETITEMDATA, item, 0 ); + pt.x = LOWORD(lParam); + pt.y = HIWORD(lParam); + + SendMessage ( hWnd, LB_GETITEMRECT, item, (LPARAM)&rItem ); + + if ( !gitem->mName.Icmp ( "" ) ) + { + rItem.right = rItem.left + grid->mSplitter - 1; + if ( PtInRect ( &rItem, pt) ) + { + grid->SetCurSel ( item ); + grid->StartEdit ( item, true ); + } + } + else if ( grid->mSelectedItem == item ) + { + rItem.left = rItem.left + grid->mSplitter + 1; + if ( PtInRect ( &rItem, pt) ) + { + grid->StartEdit ( item, false ); + } + } + + if ( grid->mState == rvPropertyGrid::STATE_EDIT ) + { + ClientToScreen ( hWnd, &pt ); + ScreenToClient ( grid->mEdit, &pt ); + SendMessage ( grid->mEdit, WM_LBUTTONDOWN, wParam, MAKELONG(pt.x,pt.y) ); + return 0; + } + + break; + } + + case WM_ERASEBKGND: + { + RECT rClient; + GetClientRect ( hWnd, &rClient ); + FillRect ( (HDC)wParam, &rClient, GetSysColorBrush ( COLOR_3DFACE ) ); + return TRUE; + } + + case WM_SETCURSOR: + { + POINT point; + GetCursorPos ( &point ); + ScreenToClient ( hWnd, &point ); + if ( point.x >= grid->mSplitter - 2 && point.x <= grid->mSplitter + 2 ) + { + SetCursor ( LoadCursor ( NULL, MAKEINTRESOURCE(IDC_SIZEWE))); + return TRUE; + } + break; + } + } + + return CallWindowProc ( grid->mListWndProc, hWnd, msg, wParam, lParam ); +} + +/* +================ +rvPropertyGrid::ReflectMessage + +Handle messages sent to the parent window +================ +*/ +bool rvPropertyGrid::ReflectMessage ( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + switch ( msg ) + { + case WM_COMMAND: + { + if ( (HWND)lParam == mWindow ) + { + switch ( HIWORD(wParam) ) + { + case LBN_SELCHANGE: + mSelectedItem = SendMessage ( mWindow, LB_GETCURSEL, 0, 0 ); + break; + } + } + break; + } + + case WM_DRAWITEM: + HandleDrawItem ( wParam, lParam ); + return true; + + case WM_MEASUREITEM: + { + MEASUREITEMSTRUCT* mis = (MEASUREITEMSTRUCT*) lParam; + mis->itemHeight = 18; + return true; + } + } + + return false; +} + +/* +================ +rvPropertyGrid::HandleDrawItem + +Handle the draw item message +================ +*/ +int rvPropertyGrid::HandleDrawItem ( WPARAM wParam, LPARAM lParam ) +{ + DRAWITEMSTRUCT* dis = (DRAWITEMSTRUCT*) lParam; + rvPropertyGridItem* item = (rvPropertyGridItem*) dis->itemData; + RECT rTemp; + HBRUSH brush; + + if ( !item ) + { + return 0; + } + + rTemp = dis->rcItem; + if ( mStyle & PGS_HEADERS ) + { + brush = GetSysColorBrush ( COLOR_SCROLLBAR ); + rTemp.right = rTemp.left + 10; + FillRect ( dis->hDC, &rTemp, brush ); + rTemp.left = rTemp.right; + rTemp.right = dis->rcItem.right; + } + + if ( item->mType == PGIT_HEADER ) + { + brush = GetSysColorBrush ( COLOR_SCROLLBAR ); + } + else if ( dis->itemState & ODS_SELECTED ) + { + brush = GetSysColorBrush ( COLOR_HIGHLIGHT ); + } + else + { + brush = GetSysColorBrush ( COLOR_WINDOW ); + } + + FillRect ( dis->hDC, &rTemp, brush ); + + HPEN pen = CreatePen ( PS_SOLID, 1, GetSysColor ( COLOR_SCROLLBAR ) ); + HPEN oldpen = (HPEN)SelectObject ( dis->hDC, pen ); + MoveToEx ( dis->hDC, dis->rcItem.left, dis->rcItem.top, NULL ); + LineTo ( dis->hDC, dis->rcItem.right, dis->rcItem.top ); + MoveToEx ( dis->hDC, dis->rcItem.left, dis->rcItem.bottom, NULL ); + LineTo ( dis->hDC, dis->rcItem.right, dis->rcItem.bottom); + + if ( item->mType != PGIT_HEADER ) + { + MoveToEx ( dis->hDC, dis->rcItem.left + mSplitter, dis->rcItem.top, NULL ); + LineTo ( dis->hDC, dis->rcItem.left + mSplitter, dis->rcItem.bottom ); + } + SelectObject ( dis->hDC, oldpen ); + DeleteObject ( pen ); + + int colorIndex = ( (dis->itemState & ODS_SELECTED ) ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT ); + SetTextColor ( dis->hDC, GetSysColor ( colorIndex ) ); + SetBkMode ( dis->hDC, TRANSPARENT ); + SetBkColor ( dis->hDC, GetSysColor ( COLOR_3DFACE ) ); + + RECT rText; + rText = rTemp; + rText.right = rText.left + mSplitter; + rText.left += 2; + + DrawText ( dis->hDC, item->mName, item->mName.Length(), &rText, DT_LEFT|DT_VCENTER|DT_SINGLELINE ); + + rText.left = dis->rcItem.left + mSplitter + 2; + rText.right = dis->rcItem.right; + DrawText ( dis->hDC, item->mValue, item->mValue.Length(), &rText, DT_LEFT|DT_VCENTER|DT_SINGLELINE ); + + return 0; +} \ No newline at end of file diff --git a/src/tools/common/PropertyGrid.h b/src/tools/common/PropertyGrid.h new file mode 100644 index 0000000..11be804 --- /dev/null +++ b/src/tools/common/PropertyGrid.h @@ -0,0 +1,123 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef PROPERTYGRID_H_ +#define PROPERTYGRID_H_ + +#define PGN_ITEMCHANGED 100 + +#define PGS_HEADERS 0x00000001 +#define PGS_ALLOWINSERT 0x00000002 + +typedef struct +{ + NMHDR hdr; + int mItem; + const char* mName; + const char* mValue; + +} NMPROPGRID; + +class rvPropertyGrid +{ +public: + + enum EItemType + { + PGIT_STRING, + PGIT_HEADER, + PGIT_MAX + }; + + rvPropertyGrid ( ); + + bool Create ( HWND parent, int id, int style = 0 ); + + void Move ( int x, int y, int w, int h, BOOL redraw = FALSE ); + + bool ReflectMessage ( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ); + + int AddItem ( const char* name, const char* value, EItemType type = PGIT_STRING ); + + void RemoveItem ( int index ); + void RemoveAllItems ( void ); + + void SetCurSel ( int index ); + int GetCurSel ( void ); + + HWND GetWindow ( void ); + const char* GetItemName ( int index ); + const char* GetItemValue ( int index ); + +protected: + + enum EState + { + STATE_FINISHEDIT, + STATE_EDIT, + STATE_NORMAL, + }; + + void StartEdit ( int item, bool label ); + void FinishEdit ( void ); + void CancelEdit ( void ); + + int HandleDrawItem ( WPARAM wParam, LPARAM lParam ); + + HWND mWindow; + HWND mEdit; + int mEditItem; + bool mEditLabel; + int mSelectedItem; + WNDPROC mListWndProc; + int mSplitter; + int mStyle; + EState mState; + +private: + + static LRESULT CALLBACK WndProc ( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ); +}; + +inline HWND rvPropertyGrid::GetWindow ( void ) +{ + return mWindow; +} + +inline int rvPropertyGrid::GetCurSel ( void ) +{ + return SendMessage ( mWindow, LB_GETCURSEL, 0, 0 ); +} + +inline void rvPropertyGrid::SetCurSel ( int index ) +{ + SendMessage ( mWindow, LB_SETCURSEL, index, 0 ); + mSelectedItem = index; +} + +#endif // PROPERTYGRID_H_ diff --git a/src/tools/common/RegistryOptions.cpp b/src/tools/common/RegistryOptions.cpp new file mode 100644 index 0000000..f754177 --- /dev/null +++ b/src/tools/common/RegistryOptions.cpp @@ -0,0 +1,336 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "RegistryOptions.h" + +/* +================ +rvRegistryOptions::rvRegistryOptions + +Constructor +================ +*/ +rvRegistryOptions::rvRegistryOptions( void ) { +} + +/* +================ +rvRegistryOptions::Init +================ +*/ +void rvRegistryOptions::Init( const char *key ) { + mBaseKey = key; +} + +/* +================ +rvRegistryOptions::Save + +Write the options to the registry +================ +*/ +bool rvRegistryOptions::Save ( void ) +{ + HKEY hKey; + int i; + + // Create the top level key + if ( ERROR_SUCCESS != RegCreateKeyEx ( HKEY_LOCAL_MACHINE, mBaseKey, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hKey, NULL ) ) + { + return false; + } + + // Write out the values + for ( i = 0; i < mValues.GetNumKeyVals(); i ++ ) + { + const idKeyValue* key = mValues.GetKeyVal ( i ); + assert ( key ); + RegSetValueEx ( hKey, key->GetKey().c_str(), 0, REG_SZ, (BYTE*)key->GetValue().c_str(), key->GetValue().Length() ); + } + + // Write Recent Files + for ( i = 0; i < mRecentFiles.Num(); i ++ ) + { + RegSetValueEx ( hKey, va("mru%d",i), 0, REG_SZ, (BYTE*)mRecentFiles[i].c_str(), mRecentFiles[i].Length() ); + } + + return true; +} + +/* +================ +rvRegistryOptions::Load + +Read the options from the registry +================ +*/ +bool rvRegistryOptions::Load ( void ) +{ + HKEY hKey; + char temp[MAX_PATH]; + TCHAR keyname[MAX_PATH]; + DWORD dwType; + DWORD dwSize; + int i; + + mValues.Clear ( ); + mRecentFiles.Clear ( ); + + if ( ERROR_SUCCESS != RegOpenKeyEx ( HKEY_LOCAL_MACHINE, mBaseKey, 0, KEY_READ, &hKey ) ) + { + return false; + } + + // Read in the values and recent files + keyname[0] = 0; + dwSize = MAX_PATH; + for ( i = 0; RegEnumValue ( hKey, i, keyname, &dwSize, NULL, NULL, NULL, NULL ) == ERROR_SUCCESS; i ++ ) + { + temp[0] = '\0'; + dwSize = MAX_PATH; + + if ( ERROR_SUCCESS != RegQueryValueEx ( hKey, keyname, NULL, &dwType, (LPBYTE)temp, &dwSize ) ) + { + continue; + } + + dwSize = MAX_PATH; + + // Skip the mru values + if( !idStr(keyname).IcmpPrefix ( "mru" ) ) + { + continue; + } + + mValues.Set ( keyname, temp ); + } + + // Read Recent Files + for ( i = 0; i < MAX_MRU_SIZE; i ++ ) + { + dwSize = MAX_PATH; + if ( ERROR_SUCCESS != RegQueryValueEx ( hKey, va("mru%d", i ), NULL, &dwType, (LPBYTE)temp, &dwSize ) ) + { + continue; + } + + AddRecentFile ( temp ); + } + + return true; +} + +/* +================ +rvRegistryOptions::SetWindowPlacement + +Set a window placement in the options +================ +*/ +void rvRegistryOptions::SetWindowPlacement ( const char* name, HWND hwnd ) +{ + WINDOWPLACEMENT wp; + + wp.length = sizeof(wp); + ::GetWindowPlacement ( hwnd, &wp ); + + idStr out; + + out = va("%d %d %d %d %d %d %d %d %d %d", + wp.flags, + wp.ptMaxPosition.x, + wp.ptMaxPosition.y, + wp.ptMinPosition.x, + wp.ptMinPosition.y, + wp.rcNormalPosition.left, + wp.rcNormalPosition.top, + wp.rcNormalPosition.right, + wp.rcNormalPosition.bottom, + wp.showCmd ); + + mValues.Set ( name, out ); +} + +/* +================ +rvRegistryOptions::GetWindowPlacement + +Retrieve a window placement from the options +================ +*/ +bool rvRegistryOptions::GetWindowPlacement ( const char* name, HWND hwnd ) +{ + WINDOWPLACEMENT wp; + wp.length = sizeof(wp); + + const idKeyValue* key = mValues.FindKey ( name ); + if ( !key ) + { + return false; + } + + sscanf ( key->GetValue().c_str(), "%d %d %d %d %d %d %d %d %d %d", + &wp.flags, + &wp.ptMaxPosition.x, + &wp.ptMaxPosition.y, + &wp.ptMinPosition.x, + &wp.ptMinPosition.y, + &wp.rcNormalPosition.left, + &wp.rcNormalPosition.top, + &wp.rcNormalPosition.right, + &wp.rcNormalPosition.bottom, + &wp.showCmd ); + + ::SetWindowPlacement ( hwnd, &wp ); + + return true; +} + +/* +================ +rvRegistryOptions::AddRecentFile + +Adds the given filename to the MRU list +================ +*/ +void rvRegistryOptions::AddRecentFile ( const char* filename ) +{ + int i; + + idStr path = filename; + + // Remove duplicates first + for ( i = mRecentFiles.Num() - 1; i >= 0; i -- ) + { + if ( !mRecentFiles[i].Icmp ( filename ) ) + { + mRecentFiles.RemoveIndex ( i ); + break; + } + } + + // Alwasy trip to the max MRU size + while ( mRecentFiles.Num ( ) >= MAX_MRU_SIZE ) + { + mRecentFiles.RemoveIndex ( 0 ); + } + + mRecentFiles.Append ( path ); +} + +/* +================ +rvRegistryOptions::SetColumnWidths + +Set a group of column widths in the options +================ +*/ +void rvRegistryOptions::SetColumnWidths ( const char* name, HWND list ) +{ + LVCOLUMN col; + int index; + idStr widths; + + col.mask = LVCF_WIDTH; + + for ( index = 0; ListView_GetColumn ( list, index, &col ); index ++ ) + { + widths += va("%d ", col.cx ); + } + + mValues.Set ( name, widths ); +} + +/* +================ +rvRegistryOptions::GetColumnWidths + +Retrieve a group of column widths from the options +================ +*/ +void rvRegistryOptions::GetColumnWidths ( const char* name, HWND list ) +{ + idStr widths; + const char* parse; + const char* next; + int index; + + widths = mValues.GetString ( name ); + parse = widths; + index = 0; + + while ( NULL != (next = strchr ( parse, ' ' ) ) ) + { + int width; + + sscanf ( parse, "%d", &width ); + parse = next + 1; + + ListView_SetColumnWidth ( list, index++, width ); + } +} + +/* +================ +rvRegistryOptions::SetBinary + +Set binary data for the given key +================ +*/ +void rvRegistryOptions::SetBinary ( const char* name, const unsigned char* data, int size ) +{ + idStr binary; + for ( size --; size >= 0; size --, data++ ) + { + binary += va("%02x", *data ); + } + + mValues.Set ( name, binary ); +} + +/* +================ +rvRegistryOptions::GetBinary + +Get the binary data for a given key +================ +*/ +void rvRegistryOptions::GetBinary ( const char* name, unsigned char* data, int size ) +{ + const char* parse; + parse = mValues.GetString ( name ); + for ( size --; size >= 0 && *parse && *(parse+1); size --, parse += 2, data ++ ) + { + int value; + sscanf ( parse, "%02x", &value ); + *data = (unsigned char)value; + } +} diff --git a/src/tools/common/RegistryOptions.h b/src/tools/common/RegistryOptions.h new file mode 100644 index 0000000..8886f39 --- /dev/null +++ b/src/tools/common/RegistryOptions.h @@ -0,0 +1,144 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef REGISTRYOPTIONS_H_ +#define REGISTRYOPTIONS_H_ + +class rvRegistryOptions +{ +public: + + static const int MAX_MRU_SIZE = 4; + + rvRegistryOptions(); + + void Init( const char *key ); + + // Write the options to the registery + bool Save ( void ); + + // Read the options from the registry + bool Load ( void ); + + // Window placement routines + void SetWindowPlacement ( const char* name, HWND hwnd ); + bool GetWindowPlacement ( const char* name, HWND hwnd ); + + // List view column sizes + void SetColumnWidths ( const char* name, HWND list ); + void GetColumnWidths ( const char* name, HWND list ); + + // Set routines + void SetFloat ( const char* name, float v ); + void SetLong ( const char* name, long v ); + void SetBool ( const char* name, bool v ); + void SetString ( const char* name, const char* v ); + void SetVec4 ( const char* name, idVec4& v ); + void SetBinary ( const char* name, const unsigned char* data, int size ); + + // Get routines + float GetFloat ( const char* name ); + long GetLong ( const char* name ); + bool GetBool ( const char* name ); + const char* GetString ( const char* name ); + idVec4 GetVec4 ( const char* name ); + void GetBinary ( const char* name, unsigned char* data, int size ); + + // MRU related methods + void AddRecentFile ( const char* filename ); + const char* GetRecentFile ( int index ); + int GetRecentFileCount ( void ); + +private: + + idList mRecentFiles; + idDict mValues; + idStr mBaseKey; +}; + +ID_INLINE void rvRegistryOptions::SetFloat ( const char* name, float v ) +{ + mValues.SetFloat ( name, v ); +} + +ID_INLINE void rvRegistryOptions::SetLong ( const char* name, long v ) +{ + mValues.SetInt ( name, v ); +} + +ID_INLINE void rvRegistryOptions::SetBool ( const char* name, bool v ) +{ + mValues.SetBool ( name, v ); +} + +ID_INLINE void rvRegistryOptions::SetString ( const char* name, const char* v ) +{ + mValues.Set ( name, v ); +} + +ID_INLINE void rvRegistryOptions::SetVec4 ( const char* name, idVec4& v ) +{ + mValues.SetVec4 ( name, v ); +} + +ID_INLINE float rvRegistryOptions::GetFloat ( const char* name ) +{ + return mValues.GetFloat ( name ); +} + +ID_INLINE long rvRegistryOptions::GetLong ( const char* name ) +{ + return mValues.GetInt ( name ); +} + +ID_INLINE bool rvRegistryOptions::GetBool ( const char* name ) +{ + return mValues.GetBool ( name ); +} + +ID_INLINE const char* rvRegistryOptions::GetString ( const char* name ) +{ + return mValues.GetString ( name ); +} + +ID_INLINE idVec4 rvRegistryOptions::GetVec4 ( const char* name ) +{ + return mValues.GetVec4 ( name ); +} + +ID_INLINE int rvRegistryOptions::GetRecentFileCount ( void ) +{ + return mRecentFiles.Num ( ); +} + +ID_INLINE const char* rvRegistryOptions::GetRecentFile ( int index ) +{ + return mRecentFiles[index].c_str ( ); +} + +#endif // REGISTRYOPTIONS_H_ diff --git a/src/tools/common/RenderBumpFlatDialog.cpp b/src/tools/common/RenderBumpFlatDialog.cpp new file mode 100644 index 0000000..c414cee --- /dev/null +++ b/src/tools/common/RenderBumpFlatDialog.cpp @@ -0,0 +1,110 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "../../sys/win32/rc/Common_resource.h" + +idCVar rbfg_DefaultWidth( "rbfg_DefaultWidth", "0", 0, "" ); +idCVar rbfg_DefaultHeight( "rbfg_DefaultHeight", "0", 0, "" ); + +static idStr RBFName; + +static bool CheckPow2(int Num) +{ + while(Num) + { + if ((Num & 1) && (Num != 1)) + { + return false; + } + + Num >>= 1; + } + + return true; +} + +extern void Com_WriteConfigToFile( const char *filename ); + +static BOOL CALLBACK RBFProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_INITDIALOG: + SetDlgItemInt(hwndDlg, IDC_RBF_WIDTH, rbfg_DefaultWidth.GetInteger(), FALSE); + SetDlgItemInt(hwndDlg, IDC_RBF_HEIGHT, rbfg_DefaultHeight.GetInteger(), FALSE); + SetDlgItemText(hwndDlg, IDC_RBF_FILENAME, RBFName); + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDOK: + { + int width, height; + + width = GetDlgItemInt(hwndDlg, IDC_RBF_WIDTH, 0, FALSE); + height = GetDlgItemInt(hwndDlg, IDC_RBF_HEIGHT, 0, FALSE); + + rbfg_DefaultWidth.SetInteger( width ); + rbfg_DefaultHeight.SetInteger( height ); + + Com_WriteConfigToFile( CONFIG_FILE ); + + if (!CheckPow2(width) || !CheckPow2(height)) + { + return TRUE; + } + + DestroyWindow(hwndDlg); + + cmdSystem->BufferCommandText( CMD_EXEC_APPEND, va("renderbumpflat -size %d %d %s\n", width, height, RBFName.c_str() ) ); + return TRUE; + } + + case IDCANCEL: + DestroyWindow(hwndDlg); + return TRUE; + } + } + + return FALSE; +} + +void DoRBFDialog(const char *FileName) +{ + RBFName = FileName; + + Sys_GrabMouseCursor( false ); + + DialogBox(0, MAKEINTRESOURCE(IDD_RENDERBUMPFLAT), 0, (DLGPROC)RBFProc); + + Sys_GrabMouseCursor( true ); +} diff --git a/src/tools/common/RenderBumpFlatDialog.h b/src/tools/common/RenderBumpFlatDialog.h new file mode 100644 index 0000000..0dc0765 --- /dev/null +++ b/src/tools/common/RenderBumpFlatDialog.h @@ -0,0 +1,35 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef __RENDERBUMPFLATDIALOG_H +#define __RENDERBUMPFLATDIALOG_H + +void DoRBFDialog(const char *FileName); + +#endif // __RENDERBUMPFLATDIALOG_H + diff --git a/src/tools/common/RollupPanel.cpp b/src/tools/common/RollupPanel.cpp new file mode 100644 index 0000000..0a9ac58 --- /dev/null +++ b/src/tools/common/RollupPanel.cpp @@ -0,0 +1,1215 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "../../sys/win32/win_local.h" +#include "RollupPanel.h" + +// Based on original code by Johann Nadalutti + +#define RP_PGBUTTONHEIGHT 18 +#define RP_SCROLLBARWIDTH 6 +#define RP_GRPBOXINDENT 6 +#define RP_SCROLLBARCOLOR RGB(150,180,180) +#define RP_ROLLCURSOR MAKEINTRESOURCE(32649) // see IDC_HAND (WINVER >= 0x0500) + +//Popup Menu Ids +#define RP_IDM_EXPANDALL 0x100 +#define RP_IDM_COLLAPSEALL 0x101 +#define RP_IDM_STARTITEMS 0x102 + +idList rvRollupPanel::mDialogs; +HHOOK rvRollupPanel::mDialogHook = NULL; + +#define DEFERPOS + +/* +================ +rvRollupPanel::rvRollupPanel + +constructor +================ +*/ +rvRollupPanel::rvRollupPanel ( void ) +{ + mStartYPos = 0; + mItemHeight = 0; + mWindow = NULL; +} + +/* +================ +rvRollupPanel::~rvRollupPanel + +destructor +================ +*/ +rvRollupPanel::~rvRollupPanel ( void ) +{ + // destroy the items + for ( ; mItems.Num(); ) + { + _RemoveItem ( 0 ); + } +} + +/* +================ +rvRollupPanel::Create + +Create the rollup panel window +================ +*/ +bool rvRollupPanel::Create ( DWORD dwStyle, const RECT& rect, HWND parent, unsigned int id ) +{ + WNDCLASSEX wndClass; + memset ( &wndClass, 0, sizeof(wndClass) ); + wndClass.cbSize = sizeof(WNDCLASSEX); + wndClass.lpszClassName = "ROLLUP_PANEL"; + wndClass.lpfnWndProc = WindowProc; + wndClass.hbrBackground = (HBRUSH)GetSysColorBrush ( COLOR_3DFACE ); + wndClass.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW); + wndClass.lpszMenuName = NULL; + wndClass.hInstance = win32.hInstance; + wndClass.style = CS_VREDRAW | CS_HREDRAW; + RegisterClassEx ( &wndClass ); + + mWindow = CreateWindowEx ( WS_EX_TOOLWINDOW, + "ROLLUP_PANEL", + "", + dwStyle|WS_CLIPSIBLINGS, + rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, + parent, + NULL, + win32.hInstance, + this ); + + if ( !mWindow ) + { + return false; + } + + return true; +} + +/* +================ +rvRollupPanel::InsertItem + +Insert and item into the rollup panel. Return -1 if an error occured +================ +*/ +int rvRollupPanel::InsertItem ( const char* caption, HWND dialog, bool autoDestroy, int index ) +{ + assert ( caption ); + assert ( dialog ); + + // -1 means add to the end + if ( index > 0 && index >= mItems.Num() ) + { + index = -1; + } + + // Get client rect + RECT r; + GetClientRect(mWindow,&r); + + // Create the GroupBox control + HWND groupbox = CreateWindow ( "BUTTON", "", WS_CHILD|BS_GROUPBOX, + r.left, r.top, r.right-r.left, r.bottom-r.top, + mWindow, 0, win32.hInstance, NULL ); + + // Create the expand button + HWND button = CreateWindow ( "BUTTON", caption, WS_CHILD|BS_AUTOCHECKBOX|BS_PUSHLIKE|BS_FLAT, + r.left, r.top, r.right-r.left, r.bottom-r.top, + mWindow, 0, win32.hInstance, NULL ); + + // Change the button's font + SendMessage ( button, WM_SETFONT, (WPARAM) GetStockObject(DEFAULT_GUI_FONT), 0 ); + + // Add item to the item list + RPITEM* item = new RPITEM; + item->mExpanded = false; + item->mEnable = true; + item->mDialog = dialog; + item->mButton = button; + item->mGroupBox = groupbox; + item->mOldDlgProc = (WNDPROC) GetWindowLong ( dialog, DWL_DLGPROC ); + item->mOldButtonProc = (WNDPROC) GetWindowLong ( button, GWL_WNDPROC ); + item->mAutoDestroy = autoDestroy; + strcpy ( item->mCaption, caption ); + + if ( index < 0 ) + { + index = mItems.Append ( item ); + } + else + { + mItems.Insert ( item, index ); + } + + // Store data with the dialog window in its user data + SetWindowLong ( dialog, GWL_USERDATA, (LONG)item ); + + // Attach item to button through user data + SetWindowLong ( button, GWL_USERDATA, (LONG)item ); + SetWindowLong ( button, GWL_ID, index ); + + // Subclass dialog + SetWindowLong ( dialog, DWL_DLGPROC, (LONG)DialogProc ); + + // SubClass button + SetWindowLong ( button, GWL_WNDPROC, (LONG)ButtonProc ); + + // Update + mItemHeight += RP_PGBUTTONHEIGHT+(RP_GRPBOXINDENT/2); + RecallLayout ( ); + + // One hook for all panel dialogs + if ( !mDialogHook ) + { + mDialogHook = SetWindowsHookEx( WH_GETMESSAGE, GetMsgProc, NULL, GetCurrentThreadId() ); + } + + mDialogs.Append ( dialog ); + + return index; +} + +/* +================ +rvRollupPanel::RemoveItem + +Remove the item at the given index from the rollup panel +================ +*/ +void rvRollupPanel::RemoveItem ( int index ) +{ + // safety check + if ( index >= mItems.Num() || index < 0 ) + { + return; + } + + // remove the item + _RemoveItem( index ); + + // update the layout + RecallLayout ( ); +} + +/* +================ +rvRollupPanel::RemoveAllItems + +Remove all items from the control +================ +*/ +void rvRollupPanel::RemoveAllItems() +{ + for ( ; mItems.Num(); ) + { + _RemoveItem ( 0 ); + } + + // update layout + RecallLayout ( ); +} + +/* +================ +rvRollupPanel::_RemoveItem + +called by RemoveItem and RemoveAllItems methods to acutally remove the item +================ +*/ +void rvRollupPanel::_RemoveItem ( int index ) +{ + RPITEM* item = mItems[index]; + + // get the item rect + RECT ir; + GetWindowRect ( item->mDialog, &ir ); + + // update item height + mItemHeight -= RP_PGBUTTONHEIGHT+(RP_GRPBOXINDENT/2); + if ( item->mExpanded ) + { + mItemHeight -= (ir.bottom-ir.top); + } + + // destroy windows + if ( item->mButton ) + { + DestroyWindow ( item->mButton ); + } + if ( item->mGroupBox ) + { + DestroyWindow ( item->mGroupBox ); + } + if ( item->mDialog && item->mAutoDestroy ) + { + DestroyWindow ( item->mDialog ); + mDialogs.Remove ( item->mDialog ); + } + + if ( mDialogs.Num () <= 0 ) + { + UnhookWindowsHookEx( mDialogHook ); + mDialogHook = NULL; + } + + // finish up + mItems.RemoveIndex ( index ); + delete item; +} + +/* +================ +rvRollupPanel::ExpandItem + +expand or collapse the item at the given index +================ +*/ +void rvRollupPanel::ExpandItem( int index, bool expand ) +{ + // safety check + if ( index >= mItems.Num() || index < 0 ) + { + return; + } + + _ExpandItem ( mItems[index], expand ); + + RecallLayout ( ); + + // scroll to this page (automatic page visibility) + if ( expand ) + { + ScrollToItem ( index, false ); + } +} + +/* +================ +rvRollupPanel::ExpandItem + +expand or collapse the item at the given index +================ +*/ +void rvRollupPanel::ExpandAllItems( bool expand ) +{ + int i; + + // expand all items + for ( i=0; i < mItems.Num(); i ++ ) + { + _ExpandItem ( mItems[i], expand ); + } + + RecallLayout(); +} + +/* +================ +rvRollupPanel::ExpandItem + +expand or collapse the item at the given index +================ +*/ +void rvRollupPanel::_ExpandItem ( RPITEM* item, bool expand ) +{ + // check if we need to change state + if ( item->mExpanded == expand || !item->mEnable ) + { + return; + } + + RECT ir; + GetWindowRect ( item->mDialog, &ir ); + + // Expand-collapse + item->mExpanded = expand; + + if ( expand ) + { + mItemHeight += (ir.bottom - ir.top); + } + else + { + mItemHeight -= (ir.bottom - ir.top); + } +} + +/* +================ +rvRollupPanel::EnableItem + +enable/disable the item at the given index +================ +*/ +void rvRollupPanel::EnableItem ( int index, bool enable ) +{ + // safety check + if ( index >= mItems.Num() || index < 0 ) + { + return; + } + + _EnableItem ( mItems[index], enable ); + RecallLayout ( ); +} + +/* +================ +rvRollupPanel::EnableAllItems + +enable/disable all items in the panel +================ +*/ +void rvRollupPanel::EnableAllItems ( bool enable ) +{ + int i; + + for ( i=0; i < mItems.Num(); i++ ) + { + _EnableItem ( mItems[i], enable ); + } + + RecallLayout ( ); +} + +/* +================ +rvRollupPanel::_EnableItem + +Called by EnableItem and EnableAllItems to do the work of enabling/disablgin +the window +================ +*/ +void rvRollupPanel::_EnableItem ( RPITEM* item, bool enable ) +{ + // check if we need to change state + if ( item->mEnable == enable ) + { + return; + } + + RECT ir; + GetWindowRect ( item->mDialog, &ir ); + + item->mEnable = enable; + + if ( item->mExpanded ) + { + mItemHeight -= (ir.bottom-ir.top); + item->mExpanded = false; + } +} + +/* +================ +rvRollupPanel::ScrollToItem + +Scroll a page at the top of the Rollup Panel if top = true or just ensure +item visibility into view if top = false +================ +*/ +void rvRollupPanel::ScrollToItem ( int index, bool top ) +{ + // safety check + if ( index >= mItems.Num() || index < 0 ) + { + return; + } + + RPITEM* item = mItems[index]; + + // get rects + RECT r; + RECT ir; + GetWindowRect ( mWindow, &r ); + GetWindowRect ( item->mDialog, &ir ); + + // check page visibility + if ( top || ((ir.bottom > r.bottom) || (ir.top < r.top))) + { + // compute new mStartYPos + GetWindowRect( item->mButton, &ir ); + mStartYPos -= (ir.top-r.top); + + RecallLayout(); + } +} + +/* +================ +rvRollupPanel::MoveItemAt + +newIndex can be equal to -1 (move at end) +return -1 if an error occurs +================ +*/ +int rvRollupPanel::MoveItemAt ( int index, int newIndex ) +{ + if ( index == newIndex || index >= mItems.Num() || index < 0 ) + { + return -1; + } + + // remove page from its old position + RPITEM* item = mItems[index]; + mItems.RemoveIndex ( index ); + + // insert at its new position + if ( newIndex < 0 ) + { + index = mItems.Append( item ); + } + else + { + mItems.Insert ( item, newIndex ); + index = newIndex; + } + + RecallLayout ( ); + + return index; +} + +/* +================ +rvRollupPanel::RecallLayout + +Update the layout of the control based on current states +================ +*/ +void rvRollupPanel::RecallLayout ( void ) +{ + int bottomPagePos; + RECT r; + int posy; + int i; + + // check StartPosY + GetClientRect ( mWindow, &r ); + bottomPagePos = mStartYPos + mItemHeight; + + if ( bottomPagePos < r.bottom-r.top ) + { + mStartYPos = (r.bottom-r.top) - mItemHeight; + } + if ( mStartYPos > 0 ) + { + mStartYPos = 0; + } + + // update layout +#ifdef DEFERPOS + HDWP hdwp; + hdwp = BeginDeferWindowPos ( mItems.Num() * 3 ); +#endif + posy = mStartYPos; + + for ( i=0; i < mItems.Num(); i++ ) + { + RPITEM* item = mItems[i]; + + // enable / disable button + SendMessage ( item->mButton, BM_SETCHECK, (item->mEnable&item->mExpanded)?BST_CHECKED:BST_UNCHECKED, 0 ); + EnableWindow ( item->mButton, item->mEnable ); + + // Expanded + if ( item->mExpanded && item->mEnable ) + { + RECT ir; + GetWindowRect ( item->mDialog, &ir ); + + // update GroupBox position and size +#ifdef DEFERPOS + DeferWindowPos ( hdwp, +#else + SetWindowPos ( +#endif + item->mGroupBox, 0, 2, posy, + (r.right-r.left)-3-RP_SCROLLBARWIDTH, + (ir.bottom-ir.top)+RP_PGBUTTONHEIGHT+RP_GRPBOXINDENT-4, + SWP_NOZORDER|SWP_SHOWWINDOW); + + //Update Dialog position and size +#ifdef DEFERPOS + DeferWindowPos ( hdwp, +#else + SetWindowPos ( +#endif + item->mDialog, 0, RP_GRPBOXINDENT, posy+RP_PGBUTTONHEIGHT, + (r.right-r.left)-RP_SCROLLBARWIDTH-(RP_GRPBOXINDENT*2), + ir.bottom-ir.top, SWP_NOZORDER|SWP_SHOWWINDOW); + + //Update Button's position and size +#ifdef DEFERPOS + DeferWindowPos ( hdwp, +#else + SetWindowPos ( +#endif + item->mButton, 0, RP_GRPBOXINDENT, posy, + (r.right-r.left)-RP_SCROLLBARWIDTH-(RP_GRPBOXINDENT*2), + RP_PGBUTTONHEIGHT, SWP_NOZORDER|SWP_SHOWWINDOW); + + posy += (ir.bottom-ir.top) + RP_PGBUTTONHEIGHT; + } + // collapsed + else + { + // update GroupBox position and size +#ifdef DEFERPOS + DeferWindowPos ( hdwp, +#else + SetWindowPos ( +#endif + item->mGroupBox, 0, 2, posy, + (r.right-r.left)-3-RP_SCROLLBARWIDTH, 16, SWP_NOZORDER|SWP_SHOWWINDOW); + + // update Dialog position and size +#ifdef DEFERPOS + DeferWindowPos ( hdwp, +#else + SetWindowPos ( +#endif + item->mDialog, 0, RP_GRPBOXINDENT, 0, 0, 0,SWP_NOZORDER|SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE); + + // update Button's position and size +#ifdef DEFERPOS + DeferWindowPos ( hdwp, +#else + SetWindowPos ( +#endif + item->mButton, 0, RP_GRPBOXINDENT, posy, + (r.right-r.left)-RP_SCROLLBARWIDTH-(RP_GRPBOXINDENT*2), + RP_PGBUTTONHEIGHT, SWP_NOZORDER|SWP_SHOWWINDOW); + + posy += RP_PGBUTTONHEIGHT; + } + + posy += (RP_GRPBOXINDENT/2); + + } + +#ifdef DEFERPOS + EndDeferWindowPos ( hdwp ); +#endif + + // update Scroll Bar + RECT br; + SetRect ( &br, r.right-RP_SCROLLBARWIDTH,r.top,r.right,r.bottom); + InvalidateRect( mWindow, &br, FALSE ); + UpdateWindow ( mWindow ); +} + +/* +================ +rvRollupPanel::GetItemIndex + +Return -1 if no matching item was found, otherwise the index of the item +================ +*/ +int rvRollupPanel::GetItemIndex ( HWND wnd ) +{ + int i; + + //Search matching button's hwnd + for ( i=0; i < mItems.Num(); i++ ) + { + if ( wnd == mItems[i]->mButton ) + { + return i; + } + } + + return -1; +} + +int rvRollupPanel::GetItemIndex ( const char* caption ) +{ + int i; + + //Search matching button's hwnd + for ( i=0; i < mItems.Num(); i++ ) + { + if ( !idStr::Icmp ( caption, mItems[i]->mCaption ) ) + { + return i; + } + } + + return -1; +} + +/* +================ +rvRollupPanel::GetItem + +Return NULL if the index is invalid +================ +*/ +RPITEM* rvRollupPanel::GetItem ( int index ) +{ + // safety check + if ( index >= mItems.Num() || index < 0 ) + { + return NULL; + } + + return mItems[index]; +} + +/* +================ +rvRollupPanel::DialogProc + +Dialog procedure for items +================ +*/ +LRESULT CALLBACK rvRollupPanel::DialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + RPITEM* item = (RPITEM*)GetWindowLong ( hWnd, GWL_USERDATA ); + rvRollupPanel* _this = (rvRollupPanel*)GetWindowLong ( GetParent ( hWnd ), GWL_USERDATA ); + + RECT r; + GetClientRect ( _this->mWindow, &r ); + + if ( _this->mItemHeight > r.bottom-r.top ) + { + switch (uMsg) + { + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + { + POINT pos; + GetCursorPos ( &pos ); + _this->mOldMouseYPos = pos.y; + ::SetCapture(hWnd); + return 0; + } + + case WM_LBUTTONUP: + case WM_MBUTTONUP: + { + if ( ::GetCapture() == hWnd ) + { + ::ReleaseCapture(); + return 0; + } + break; + } + + case WM_MOUSEMOVE: + if ( (::GetCapture() == hWnd) && (wParam==MK_LBUTTON || wParam==MK_MBUTTON)) + { + POINT pos; + GetCursorPos(&pos); + _this->mStartYPos += (pos.y-_this->mOldMouseYPos); + _this->RecallLayout(); + _this->mOldMouseYPos = pos.y; + InvalidateRect ( _this->mWindow, NULL, TRUE ); + return 0; + } + + break; + + case WM_SETCURSOR: + if ( (HWND)wParam == hWnd) + { + SetCursor ( LoadCursor (NULL, RP_ROLLCURSOR) ); + return TRUE; + } + break; + } + } + + return ::CallWindowProc ( item->mOldDlgProc, hWnd, uMsg, wParam, lParam ); +} + +/* +================ +rvRollupPanel::DialogProc + +Button procedure for items +================ +*/ +LRESULT CALLBACK rvRollupPanel::ButtonProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if ( uMsg == WM_SETFOCUS ) + { + return FALSE; + } + + RPITEM* item = (RPITEM*)GetWindowLong(hWnd, GWL_USERDATA); + return ::CallWindowProc( item->mOldButtonProc, hWnd, uMsg, wParam, lParam ); +} + +/* +================ +rvRollupPanel::WindowProc + +Window procedure for rollup panel +================ +*/ +LRESULT CALLBACK rvRollupPanel::WindowProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + rvRollupPanel* panel; + panel = (rvRollupPanel*)GetWindowLong (hWnd, GWL_USERDATA); + + switch ( uMsg ) + { + case WM_CREATE: + { + LPCREATESTRUCT cs; + + // Attach the class to the window first + cs = (LPCREATESTRUCT) lParam; + panel = (rvRollupPanel*) cs->lpCreateParams; + SetWindowLong ( hWnd, GWL_USERDATA, (LONG)panel ); + break; + } + + case WM_COMMAND: + panel->HandleCommand ( wParam, lParam ); + break; + + case WM_PAINT: + return panel->HandlePaint ( wParam, lParam ); + + case WM_SIZE: + return panel->HandleSize ( wParam, lParam ); + + case WM_LBUTTONDOWN: + panel->HandleLButtonDown ( wParam, lParam ); + break; + + case WM_LBUTTONUP: + panel->HandleLButtonUp ( wParam, lParam ); + break; + + case WM_MOUSEMOVE: + panel->HandleMouseMove ( wParam, lParam ); + break; + + case WM_MOUSEWHEEL: + panel->HandleMouseWheel ( wParam, lParam ); + break; + + case WM_MOUSEACTIVATE: + panel->HandleMouseActivate ( wParam, lParam ); + break; + + case WM_CONTEXTMENU: + return panel->HandleContextMenu ( wParam, lParam ); + } + + return DefWindowProc ( hWnd, uMsg, wParam, lParam ); +} + +/* +================ +rvRollupPanel::HandleCommand + +Handle the WM_COMMAND message +================ +*/ +int rvRollupPanel::HandleCommand ( WPARAM wParam, LPARAM lParam ) +{ + // popup menu command to expand or collapse pages + if ( LOWORD(wParam) == RP_IDM_EXPANDALL ) + { + ExpandAllItems ( true ); + } + else if ( LOWORD(wParam) == RP_IDM_COLLAPSEALL ) + { + ExpandAllItems ( false ); + } + + // popupMenu command to expand page + else if ( LOWORD(wParam) >= RP_IDM_STARTITEMS && + LOWORD(wParam) < RP_IDM_STARTITEMS + GetItemCount ( ) ) + { + int index = LOWORD(wParam)-RP_IDM_STARTITEMS; + ExpandItem ( index, !IsItemExpanded(index) ); + } + + // button command + else if ( HIWORD(wParam) == BN_CLICKED ) + { + int index = GetItemIndex ((HWND)lParam); + if ( index != -1 ) + { + ExpandItem ( index, !IsItemExpanded ( index ) ); + return 0; + } + } + + return 0; +} + +/* +================ +rvRollupPanel::HandlePaint + +Handle the WM_PAINT message +================ +*/ +int rvRollupPanel::HandlePaint( WPARAM wParam, LPARAM lParam ) +{ + HDC dc; + PAINTSTRUCT ps; + RECT r; + RECT br; + int sbPos; + int sbSize; + int clientHeight; + + dc = BeginPaint ( mWindow, &ps ); + + // scrollbar + GetClientRect ( mWindow, &r ); + SetRect ( &br, r.right-RP_SCROLLBARWIDTH, r.top, r.right, r.bottom ); + DrawEdge ( dc, &br, EDGE_RAISED, BF_RECT ); + + sbPos = 0; + sbSize = 0; + clientHeight = (r.bottom-r.top) - 4; + + if ( mItemHeight > (r.bottom-r.top) ) + { + sbSize = clientHeight - (((mItemHeight-(r.bottom-r.top)) * clientHeight ) / mItemHeight ); + sbPos = -(mStartYPos * clientHeight) / mItemHeight; + } + else + { + sbSize = clientHeight; + } + + br.left +=2; + br.right -=1; + br.top = sbPos+2; + br.bottom = br.top+sbSize; + + HBRUSH brush; + brush = CreateSolidBrush ( RP_SCROLLBARCOLOR ); + FillRect ( dc, &br, brush ); + DeleteObject ( brush ); + + SetRect ( &r, br.left,2,br.right,br.top ); + FillRect ( dc, &r, (HBRUSH)GetStockObject ( BLACK_BRUSH ) ); + + SetRect ( &r, br.left,br.bottom,br.right,2+clientHeight ); + FillRect ( dc, &r, (HBRUSH)GetStockObject ( BLACK_BRUSH ) ); + + return 0; +} + +/* +================ +rvRollupPanel::HandleSize + +Handle the WM_SIZE message +================ +*/ +int rvRollupPanel::HandleSize ( WPARAM wParam, LPARAM lParam ) +{ + DefWindowProc ( mWindow, WM_SIZE, wParam, lParam ); + RecallLayout(); + return 0; +} + +/* +================ +rvRollupPanel::HandleLButtonDown + +Handle the WM_LBUTTONDOWN message +================ +*/ +int rvRollupPanel::HandleLButtonDown ( WPARAM wParam, LPARAM lParam ) +{ + RECT r; + RECT br; + POINT point; + + GetClientRect ( mWindow, &r ); + if ( mItemHeight <= r.bottom - r.top ) + { + return 0; + } + + point.x = LOWORD(lParam); + point.y = HIWORD(lParam); + + SetRect ( &br, r.right - RP_SCROLLBARWIDTH, r.top, r.right, r.bottom ); + + if ( (wParam & MK_LBUTTON) && PtInRect ( &br, point ) ) + { + SetCapture( mWindow ); + + int clientHeight = (r.bottom-r.top) - 4; + + int sbSize = clientHeight - (((mItemHeight - (r.bottom-r.top)) * clientHeight) / mItemHeight ); + int sbPos = -(mStartYPos * clientHeight) / mItemHeight; + + // click inside scrollbar cursor + if ( (point.y < (sbPos + sbSize)) && (point.y > sbPos )) + { + mSBOffset = sbPos - point.y + 1; + } + // click outside scrollbar cursor (2 cases => above or below cursor) + else + { + int distup = point.y - sbPos; + int distdown = (sbPos + sbSize) - point.y; + + if ( distup < distdown ) + { + //above + mSBOffset = 0; + } + else + { + //below + mSBOffset = -sbSize; + } + } + + // calc new m_nStartYPos from mouse pos + int targetPos = point.y + mSBOffset; + mStartYPos =- (targetPos * mItemHeight) / clientHeight; + + // update + RecallLayout(); + } + + return 0; +} + +/* +================ +rvRollupPanel::HandleLButtonUp + +Handle the WM_LBUTTONUP message +================ +*/ +int rvRollupPanel::HandleLButtonUp ( WPARAM wParam, LPARAM lParam ) +{ + if ( GetCapture() == mWindow ) + { + ReleaseCapture(); + } + + return 0; +} + +/* +================ +rvRollupPanel::HandleMouseMove + +Handle the WM_MOUSEMOVE message +================ +*/ +int rvRollupPanel::HandleMouseMove ( WPARAM wParam, LPARAM lParam ) +{ + RECT r; + RECT br; + POINT point; + + GetClientRect ( mWindow, &r ); + if ( mItemHeight <= r.bottom - r.top ) + { + return 0; + } + + point.x = LOWORD(lParam); + point.y = HIWORD(lParam); + + SetRect ( &br, r.right - RP_SCROLLBARWIDTH, r.top, r.right, r.bottom ); + + if ( (wParam & MK_LBUTTON) && (GetCapture() == mWindow )) + { + // calc new m_nStartYPos from mouse pos + int clientHeight = (r.bottom-r.top) - 4; + int targetPos = point.y + mSBOffset; + + mStartYPos =- (targetPos * mItemHeight) / clientHeight; + + RecallLayout ( ); + + InvalidateRect ( mWindow, NULL, FALSE ); +// UpdateWindow ( mWindow ); + } + + return 0; +} + +/* +================ +rvRollupPanel::HandleMouseWheel + +Handle the WM_MOUSEWHEEL message +================ +*/ +int rvRollupPanel::HandleMouseWheel ( WPARAM wParam, LPARAM lParam ) +{ + // calc new m_nStartYPos + mStartYPos += (HIWORD(wParam) / 4); + + RecallLayout(); + + return 0; +} + +/* +================ +rvRollupPanel::HandleMouseActivate + +Handle the WM_MOUSEACTIVATE message +================ +*/ +int rvRollupPanel::HandleMouseActivate ( WPARAM wParam, LPARAM lParam ) +{ + SetFocus ( mWindow ); + return 0; +} + +/* +================ +rvRollupPanel::HandleContextMenu + +Handle the WM_CONTEXTMENU message +================ +*/ +int rvRollupPanel::HandleContextMenu ( WPARAM wParam, LPARAM lParam ) +{ + HMENU menu; + int i; + POINT point; + + menu = CreatePopupMenu ( ); + if ( !menu ) + { + return 0; + } + + point.x = LOWORD(lParam); + point.y = HIWORD(lParam); + + AppendMenu ( menu, MF_STRING, RP_IDM_EXPANDALL, "Expand all" ); + AppendMenu ( menu, MF_STRING, RP_IDM_COLLAPSEALL, "Collapse all" ); + AppendMenu ( menu, MF_SEPARATOR, 0, "" ); + + //Add all pages with checked style for expanded ones + for ( i=0; i < mItems.Num(); i++ ) + { + char itemName[1024]; + GetWindowText ( mItems[i]->mButton, itemName, 1023 ); + AppendMenu ( menu, MF_STRING, RP_IDM_STARTITEMS + i, itemName ); + + if ( mItems[i]->mExpanded ) + { + CheckMenuItem ( menu, RP_IDM_STARTITEMS + i, MF_CHECKED); + } + + TrackPopupMenu ( menu, TPM_LEFTALIGN|TPM_LEFTBUTTON, point.x, point.y, 0, mWindow, NULL ); + } + + return 0; +} + +/* +================ +rvRollupPanel::GetMsgProc + +Ensures normal dialog functions work in the alpha select dialog +================ +*/ +LRESULT FAR PASCAL rvRollupPanel::GetMsgProc ( int nCode, WPARAM wParam, LPARAM lParam ) +{ + LPMSG lpMsg = (LPMSG) lParam; + + if ( nCode >= 0 && PM_REMOVE == wParam ) + { + // Don't translate non-input events. + if ( (lpMsg->message >= WM_KEYFIRST && lpMsg->message <= WM_KEYLAST) ) + { + int i; + for ( i = 0; i < mDialogs.Num(); i ++ ) + { + if ( IsDialogMessage( mDialogs[i], lpMsg) ) + { + // The value returned from this hookproc is ignored, + // and it cannot be used to tell Windows the message has been handled. + // To avoid further processing, convert the message to WM_NULL + // before returning. + lpMsg->message = WM_NULL; + lpMsg->lParam = 0; + lpMsg->wParam = 0; + break; + } + } + } + } + + return CallNextHookEx ( mDialogHook, nCode, wParam, lParam); +} + +/* +================ +rvRollupPanel::AutoSize + +Automatically set the width of the control based on the dialogs it contains +================ +*/ +void rvRollupPanel::AutoSize ( void ) +{ + int i; + int width = 0; + for ( i = 0; i < mItems.Num(); i ++ ) + { + RECT r; + int w; + GetWindowRect ( mItems[i]->mDialog, &r ); + w = (r.right-r.left)+RP_SCROLLBARWIDTH+(RP_GRPBOXINDENT*2); + if ( w > width ) + { + width = w; + } + } + + RECT cr; + GetWindowRect ( mWindow, &cr ); + SetWindowPos ( mWindow, NULL, 0, 0, width, cr.bottom-cr.top, SWP_NOMOVE|SWP_NOZORDER ); +} + diff --git a/src/tools/common/RollupPanel.h b/src/tools/common/RollupPanel.h new file mode 100644 index 0000000..6987d5f --- /dev/null +++ b/src/tools/common/RollupPanel.h @@ -0,0 +1,146 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef ROLLUPPANEL_H_ +#define ROLLUPPANEL_H_ + +#define RPITEM_MAX_NAME 64 + +struct RPITEM +{ + HWND mDialog; + HWND mButton; + HWND mGroupBox; + bool mExpanded; + bool mEnable; + bool mAutoDestroy; + WNDPROC mOldDlgProc; + WNDPROC mOldButtonProc; + char mCaption[RPITEM_MAX_NAME]; +}; + +class rvRollupPanel +{ +public: + + rvRollupPanel ( void ); + virtual ~rvRollupPanel ( void ); + + bool Create ( DWORD dwStyle, const RECT& rect, HWND parent, unsigned int id ); + + int InsertItem ( const char* caption, HWND dialog, bool autoDestroy, int index = -1); + + void RemoveItem ( int index ); + void RemoveAllItems ( void ); + + void ExpandItem ( int index, bool expand = true ); + void ExpandAllItems ( bool expand = true ); + + void EnableItem ( int index, bool enabled = true ); + void EnableAllItems ( bool enable = true ); + + int GetItemCount ( void ); + + RPITEM* GetItem ( int index ); + + int GetItemIndex ( const char* caption ); + int GetItemIndex ( HWND hwnd ); + + void ScrollToItem ( int index, bool top = true ); + int MoveItemAt ( int index, int newIndex ); + bool IsItemExpanded ( int index ); + bool IsItemEnabled ( int index ); + + HWND GetWindow ( void ); + + void AutoSize ( void ); + +protected: + + void RecallLayout ( void ); + void _RemoveItem ( int index ); + void _ExpandItem ( RPITEM* item, bool expand ); + void _EnableItem ( RPITEM* item, bool enable ); + + int HandleCommand ( WPARAM wParam, LPARAM lParam ); + int HandlePaint ( WPARAM wParam, LPARAM lParam ); + int HandleSize ( WPARAM wParam, LPARAM lParam ); + int HandleLButtonDown ( WPARAM wParam, LPARAM lParam ); + int HandleLButtonUp ( WPARAM wParam, LPARAM lParam ); + int HandleMouseMove ( WPARAM wParam, LPARAM lParam ); + int HandleMouseWheel ( WPARAM wParam, LPARAM lParam ); + int HandleMouseActivate ( WPARAM wParam, LPARAM lParam ); + int HandleContextMenu ( WPARAM wParam, LPARAM lParam ); + + // Datas + idList mItems; + int mStartYPos; + int mItemHeight; + int mOldMouseYPos; + int mSBOffset; + HWND mWindow; + + // Window proc + static LRESULT CALLBACK WindowProc ( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); + static LRESULT CALLBACK DialogProc ( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); + static LRESULT CALLBACK ButtonProc ( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); + + static LRESULT FAR PASCAL GetMsgProc ( int nCode, WPARAM wParam, LPARAM lParam ); + static idList mDialogs; + static HHOOK mDialogHook; +}; + +ID_INLINE int rvRollupPanel::GetItemCount ( void ) +{ + return mItems.Num(); +} + +ID_INLINE bool rvRollupPanel::IsItemExpanded ( int index ) +{ + if ( index >= mItems.Num() || index < 0 ) + { + return false; + } + return mItems[index]->mExpanded; +} + +ID_INLINE bool rvRollupPanel::IsItemEnabled( int index ) +{ + if ( index >= mItems.Num() || index < 0 ) + { + return false; + } + return mItems[index]->mEnable; +} + +ID_INLINE HWND rvRollupPanel::GetWindow ( void ) +{ + return mWindow; +} + +#endif // ROLLUPPANEL_H_ diff --git a/src/tools/common/SpinButton.cpp b/src/tools/common/SpinButton.cpp new file mode 100644 index 0000000..e365b90 --- /dev/null +++ b/src/tools/common/SpinButton.cpp @@ -0,0 +1,91 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "SpinButton.h" + +void SpinButton_SetIncrement ( HWND hWnd, float inc ) +{ + SetWindowLong ( hWnd, GWL_USERDATA, (long)(inc * 100.0f) ); +} + +void SpinButton_SetRange ( HWND hWnd, float minRange, float maxRange ) +{ + SendMessage ( hWnd, UDM_SETRANGE32, (LONG)(minRange*100.0f), (LONG)(maxRange*100.0f) ); +} + +void SpinButton_HandleNotify ( NMHDR* hdr ) +{ + // Return if incorrect data in edit box + NM_UPDOWN* udhdr= (NM_UPDOWN*)hdr; + + // Change with 0.1 on each click + char strValue[64]; + float value; + GetWindowText ( (HWND)SendMessage ( hdr->hwndFrom, UDM_GETBUDDY, 0, 0 ), strValue, 63 ); + + float inc = (float)GetWindowLong ( hdr->hwndFrom, GWL_USERDATA ); + if ( inc == 0 ) + { + inc = 100.0f; + SetWindowLong ( hdr->hwndFrom, GWL_USERDATA, 100 ); + } + inc /= 100.0f; + + if ( GetAsyncKeyState ( VK_SHIFT ) & 0x8000 ) + { + inc *= 10.0f; + } + + value = atof(strValue); + value += (udhdr->iDelta)*(inc); + + // Avoid round-off errors + value = floor(value*1e3+0.5)/1e3; + + LONG minRange; + LONG maxRange; + SendMessage ( hdr->hwndFrom, UDM_GETRANGE32, (LONG)&minRange, (LONG)&maxRange ); + if ( minRange != 0 || maxRange != 0 ) + { + float minRangef = (float)(long)minRange / 100.0f; + float maxRangef = (float)maxRange / 100.0f; + if ( value > maxRangef ) + { + value = maxRangef; + } + if ( value < minRangef ) + { + value = minRangef; + } + } + + SetWindowText ( (HWND)SendMessage ( hdr->hwndFrom, UDM_GETBUDDY, 0, 0 ), va("%g",value) ); +} diff --git a/src/tools/common/SpinButton.h b/src/tools/common/SpinButton.h new file mode 100644 index 0000000..f02aa5d --- /dev/null +++ b/src/tools/common/SpinButton.h @@ -0,0 +1,36 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef SPINBUTTON_H_ +#define SPINBUTTON_H_ + +void SpinButton_SetIncrement ( HWND hWnd, float inc ); +void SpinButton_HandleNotify ( NMHDR* hdr ); +void SpinButton_SetRange ( HWND hWnd, float min, float max ); + +#endif // SPINBUTOTN_H_ diff --git a/src/tools/compilers/dmap/dmap.cpp b/src/tools/compilers/dmap/dmap.cpp new file mode 100644 index 0000000..9f09163 --- /dev/null +++ b/src/tools/compilers/dmap/dmap.cpp @@ -0,0 +1,404 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../../idlib/precompiled.h" +#pragma hdrstop + +#include "dmap.h" + +dmapGlobals_t dmapGlobals; + +/* +============ +ProcessModel +============ +*/ +bool ProcessModel( uEntity_t *e, bool floodFill ) { + bspface_t *faces; + + // build a bsp tree using all of the sides + // of all of the structural brushes + faces = MakeStructuralBspFaceList ( e->primitives ); + e->tree = FaceBSP( faces ); + + // create portals at every leaf intersection + // to allow flood filling + MakeTreePortals( e->tree ); + + // classify the leafs as opaque or areaportal + FilterBrushesIntoTree( e ); + + // see if the bsp is completely enclosed + if ( floodFill && !dmapGlobals.noFlood ) { + if ( FloodEntities( e->tree ) ) { + // set the outside leafs to opaque + FillOutside( e ); + } else { + common->Printf ( "**********************\n" ); + common->Warning( "******* leaked *******" ); + common->Printf ( "**********************\n" ); + LeakFile( e->tree ); + // bail out here. If someone really wants to + // process a map that leaks, they should use + // -noFlood + return false; + } + } + + // get minimum convex hulls for each visible side + // this must be done before creating area portals, + // because the visible hull is used as the portal + ClipSidesByTree( e ); + + // determine areas before clipping tris into the + // tree, so tris will never cross area boundaries + FloodAreas( e ); + + // we now have a BSP tree with solid and non-solid leafs marked with areas + // all primitives will now be clipped into this, throwing away + // fragments in the solid areas + PutPrimitivesInAreas( e ); + + // now build shadow volumes for the lights and split + // the optimize lists by the light beam trees + // so there won't be unneeded overdraw in the static + // case + Prelight( e ); + + // optimizing is a superset of fixing tjunctions + if ( !dmapGlobals.noOptimize ) { + OptimizeEntity( e ); + } else if ( !dmapGlobals.noTJunc ) { + FixEntityTjunctions( e ); + } + + // now fix t junctions across areas + FixGlobalTjunctions( e ); + + return true; +} + +/* +============ +ProcessModels +============ +*/ +bool ProcessModels( void ) { + bool oldVerbose; + uEntity_t *entity; + + oldVerbose = dmapGlobals.verbose; + + for ( dmapGlobals.entityNum = 0 ; dmapGlobals.entityNum < dmapGlobals.num_entities ; dmapGlobals.entityNum++ ) { + + entity = &dmapGlobals.uEntities[dmapGlobals.entityNum]; + if ( !entity->primitives ) { + continue; + } + + common->Printf( "############### entity %i ###############\n", dmapGlobals.entityNum ); + + // if we leaked, stop without any more processing + if ( !ProcessModel( entity, (bool)(dmapGlobals.entityNum == 0 ) ) ) { + return false; + } + + // we usually don't want to see output for submodels unless + // something strange is going on + if ( !dmapGlobals.verboseentities ) { + dmapGlobals.verbose = false; + } + } + + dmapGlobals.verbose = oldVerbose; + + return true; +} + +/* +============ +DmapHelp +============ +*/ +void DmapHelp( void ) { + common->Printf( + + "Usage: dmap [options] mapfile\n" + "Options:\n" + "noCurves = don't process curves\n" + "noCM = don't create collision map\n" + "noAAS = don't create AAS files\n" + + ); +} + +/* +============ +ResetDmapGlobals +============ +*/ +void ResetDmapGlobals( void ) { + dmapGlobals.mapFileBase[0] = '\0'; + dmapGlobals.dmapFile = NULL; + dmapGlobals.mapPlanes.Clear(); + dmapGlobals.num_entities = 0; + dmapGlobals.uEntities = NULL; + dmapGlobals.entityNum = 0; + dmapGlobals.mapLights.Clear(); + dmapGlobals.verbose = false; + dmapGlobals.glview = false; + dmapGlobals.noOptimize = false; + dmapGlobals.verboseentities = false; + dmapGlobals.noCurves = false; + dmapGlobals.fullCarve = false; + dmapGlobals.noModelBrushes = false; + dmapGlobals.noTJunc = false; + dmapGlobals.nomerge = false; + dmapGlobals.noFlood = false; + dmapGlobals.noClipSides = false; + dmapGlobals.noLightCarve = false; + dmapGlobals.noShadow = false; + dmapGlobals.shadowOptLevel = SO_NONE; + dmapGlobals.drawBounds.Clear(); + dmapGlobals.drawflag = false; + dmapGlobals.totalShadowTriangles = 0; + dmapGlobals.totalShadowVerts = 0; +} + +/* +============ +Dmap +============ +*/ +void Dmap( const idCmdArgs &args ) { + int i; + int start, end; + char path[1024]; + idStr passedName; + bool leaked = false; + bool noCM = false; + bool noAAS = false; + + ResetDmapGlobals(); + + if ( args.Argc() < 2 ) { + DmapHelp(); + return; + } + + common->Printf("---- dmap ----\n"); + + dmapGlobals.fullCarve = true; + dmapGlobals.shadowOptLevel = SO_MERGE_SURFACES; // create shadows by merging all surfaces, but no super optimization +// dmapGlobals.shadowOptLevel = SO_CLIP_OCCLUDERS; // remove occluders that are completely covered +// dmapGlobals.shadowOptLevel = SO_SIL_OPTIMIZE; +// dmapGlobals.shadowOptLevel = SO_CULL_OCCLUDED; + + dmapGlobals.noLightCarve = true; + + for ( i = 1 ; i < args.Argc() ; i++ ) { + const char *s; + + s = args.Argv(i); + if ( s[0] == '-' ) { + s++; + if ( s[0] == '\0' ) { + continue; + } + } + + if ( !idStr::Icmp( s,"glview" ) ) { + dmapGlobals.glview = true; + } else if ( !idStr::Icmp( s, "v" ) ) { + common->Printf( "verbose = true\n" ); + dmapGlobals.verbose = true; + } else if ( !idStr::Icmp( s, "draw" ) ) { + common->Printf( "drawflag = true\n" ); + dmapGlobals.drawflag = true; + } else if ( !idStr::Icmp( s, "noFlood" ) ) { + common->Printf( "noFlood = true\n" ); + dmapGlobals.noFlood = true; + } else if ( !idStr::Icmp( s, "noLightCarve" ) ) { + common->Printf( "noLightCarve = true\n" ); + dmapGlobals.noLightCarve = true; + } else if ( !idStr::Icmp( s, "lightCarve" ) ) { + common->Printf( "noLightCarve = false\n" ); + dmapGlobals.noLightCarve = false; + } else if ( !idStr::Icmp( s, "noOpt" ) ) { + common->Printf( "noOptimize = true\n" ); + dmapGlobals.noOptimize = true; + } else if ( !idStr::Icmp( s, "verboseentities" ) ) { + common->Printf( "verboseentities = true\n"); + dmapGlobals.verboseentities = true; + } else if ( !idStr::Icmp( s, "noCurves" ) ) { + common->Printf( "noCurves = true\n"); + dmapGlobals.noCurves = true; + } else if ( !idStr::Icmp( s, "noModels" ) ) { + common->Printf( "noModels = true\n" ); + dmapGlobals.noModelBrushes = true; + } else if ( !idStr::Icmp( s, "noClipSides" ) ) { + common->Printf( "noClipSides = true\n" ); + dmapGlobals.noClipSides = true; + } else if ( !idStr::Icmp( s, "noCarve" ) ) { + common->Printf( "noCarve = true\n" ); + dmapGlobals.fullCarve = false; + } else if ( !idStr::Icmp( s, "shadowOpt" ) ) { + dmapGlobals.shadowOptLevel = (shadowOptLevel_t)atoi( args.Argv( i+1 ) ); + common->Printf( "shadowOpt = %i\n",dmapGlobals.shadowOptLevel ); + i += 1; + } else if ( !idStr::Icmp( s, "noTjunc" ) ) { + // triangle optimization won't work properly without tjunction fixing + common->Printf ("noTJunc = true\n" ); + dmapGlobals.noTJunc = true; + dmapGlobals.noOptimize = true; + common->Printf ("forcing noOptimize = true\n" ); + } else if ( !idStr::Icmp( s, "noCM" ) ) { + noCM = true; + common->Printf( "noCM = true\n" ); + } else if ( !idStr::Icmp( s, "noAAS" ) ) { + noAAS = true; + common->Printf( "noAAS = true\n" ); + } else if ( !idStr::Icmp( s, "editorOutput" ) ) { +#ifdef _WIN32 + com_outputMsg = true; +#endif + } else { + break; + } + } + + if ( i >= args.Argc() ) { + common->Error( "usage: dmap [options] mapfile" ); + } + + passedName = args.Argv(i); // may have an extension + passedName.BackSlashesToSlashes(); + if ( passedName.Icmpn( "maps/", 4 ) != 0 ) { + passedName = "maps/" + passedName; + } + + idStr stripped = passedName; + stripped.StripFileExtension(); + idStr::Copynz( dmapGlobals.mapFileBase, stripped, sizeof(dmapGlobals.mapFileBase) ); + + bool region = false; + // if this isn't a regioned map, delete the last saved region map + if ( passedName.Right( 4 ) != ".reg" ) { + sprintf( path, "%s.reg", dmapGlobals.mapFileBase ); + fileSystem->RemoveFile( path ); + } else { + region = true; + } + + + passedName = stripped; + + // delete any old line leak files + sprintf( path, "%s.lin", dmapGlobals.mapFileBase ); + fileSystem->RemoveFile( path ); + + + // + // start from scratch + // + start = Sys_Milliseconds(); + + if ( !LoadDMapFile( passedName ) ) { + return; + } + + if ( ProcessModels() ) { + WriteOutputFile(); + } else { + leaked = true; + } + + FreeDMapFile(); + + common->Printf( "%i total shadow triangles\n", dmapGlobals.totalShadowTriangles ); + common->Printf( "%i total shadow verts\n", dmapGlobals.totalShadowVerts ); + + end = Sys_Milliseconds(); + common->Printf( "-----------------------\n" ); + common->Printf( "%5.0f seconds for dmap\n", ( end - start ) * 0.001f ); + + if ( !leaked ) { + + if ( !noCM ) { + + // make sure the collision model manager is not used by the game + cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" ); + + // create the collision map + start = Sys_Milliseconds(); + + collisionModelManager->LoadMap( dmapGlobals.dmapFile, true ); + collisionModelManager->FreeMap( dmapGlobals.mapFileBase ); + + end = Sys_Milliseconds(); + common->Printf( "-------------------------------------\n" ); + common->Printf( "%5.0f seconds to create collision map\n", ( end - start ) * 0.001f ); + } + + if ( !noAAS && !region ) { + // create AAS files + RunAAS_f( args ); + } + } + + // free the common .map representation + delete dmapGlobals.dmapFile; + + // clear the map plane list + dmapGlobals.mapPlanes.Clear(); + +#ifdef _WIN32 + if ( com_outputMsg && com_hwndMsg != NULL ) { + unsigned int msg = ::RegisterWindowMessage( DMAP_DONE ); + ::PostMessage( com_hwndMsg, msg, 0, 0 ); + } +#endif +} + +/* +============ +Dmap_f +============ +*/ +void Dmap_f( const idCmdArgs &args ) { + + common->ClearWarnings( "running dmap" ); + + // refresh the screen each time we print so it doesn't look + // like it is hung + common->SetRefreshOnPrint( true ); + Dmap( args ); + common->SetRefreshOnPrint( false ); + + common->PrintWarnings(); +} diff --git a/src/tools/compilers/dmap/dmap.h b/src/tools/compilers/dmap/dmap.h new file mode 100644 index 0000000..b2e3210 --- /dev/null +++ b/src/tools/compilers/dmap/dmap.h @@ -0,0 +1,508 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../../renderer/tr_local.h" + +// Quake 4 moved the offline shadow optimizer behind the Tools API. The +// compiler still uses Doom's result shape internally before translating it +// to SOOData_s for the renderer callback. +typedef struct optimizedShadow_s { + idVec3 *verts; + int numVerts; + glIndex_t *indexes; + int numFrontCapIndexes; + int numRearCapIndexes; + int numSilPlaneIndexes; + int totalIndexes; +} optimizedShadow_t; + +extern HWND com_hwndMsg; +extern bool com_outputMsg; +void RunAAS_f( const idCmdArgs &args ); +void Dmap_f( const idCmdArgs &args ); + + +typedef struct primitive_s { + struct primitive_s *next; + + // only one of these will be non-NULL + struct bspbrush_s * brush; + struct mapTri_s * tris; +} primitive_t; + + +typedef struct { + struct optimizeGroup_s *groups; + // we might want to add other fields later +} uArea_t; + +typedef struct { + idMapEntity * mapEntity; // points into mapFile_t data + + idVec3 origin; + primitive_t * primitives; + struct tree_s * tree; + + int numAreas; + uArea_t * areas; +} uEntity_t; + + +// chains of mapTri_t are the general unit of processing +typedef struct mapTri_s { + struct mapTri_s * next; + + const idMaterial * material; + void * mergeGroup; // we want to avoid merging triangles + // from different fixed groups, like guiSurfs and mirrors + int planeNum; // not set universally, just in some areas + + idDrawVert v[3]; + const struct hashVert_s *hashVert[3]; + struct optVertex_s *optVert[3]; +} mapTri_t; + + +typedef struct { + int width, height; + idDrawVert * verts; +} mesh_t; + + +#define MAX_PATCH_SIZE 32 + +#define PLANENUM_LEAF -1 + +typedef struct parseMesh_s { + struct parseMesh_s *next; + mesh_t mesh; + const idMaterial * material; +} parseMesh_t; + +typedef struct bspface_s { + struct bspface_s * next; + int planenum; + bool portal; // all portals will be selected before + // any non-portals + bool checked; // used by SelectSplitPlaneNum() + idWinding * w; +} bspface_t; + +typedef struct { + idVec4 v[2]; // the offset value will always be in the 0.0 to 1.0 range +} textureVectors_t; + +typedef struct side_s { + int planenum; + + const idMaterial * material; + textureVectors_t texVec; + + idWinding * winding; // only clipped to the other sides of the brush + idWinding * visibleHull; // also clipped to the solid parts of the world +} side_t; + + +typedef struct bspbrush_s { + struct bspbrush_s * next; + struct bspbrush_s * original; // chopped up brushes will reference the originals + + int entitynum; // editor numbering for messages + int brushnum; // editor numbering for messages + + const idMaterial * contentShader; // one face's shader will determine the volume attributes + + int contents; + bool opaque; + int outputNumber; // set when the brush is written to the file list + + idBounds bounds; + int numsides; + side_t sides[6]; // variably sized +} uBrush_t; + + +typedef struct drawSurfRef_s { + struct drawSurfRef_s * nextRef; + int outputNumber; +} drawSurfRef_t; + + +typedef struct node_s { + // both leafs and nodes + int planenum; // -1 = leaf node + struct node_s * parent; + idBounds bounds; // valid after portalization + + // nodes only + side_t * side; // the side that created the node + struct node_s * children[2]; + int nodeNumber; // set after pruning + + // leafs only + bool opaque; // view can never be inside + + uBrush_t * brushlist; // fragments of all brushes in this leaf + // needed for FindSideForPortal + + int area; // determined by flood filling up to areaportals + int occupied; // 1 or greater can reach entity + uEntity_t * occupant; // for leak file testing + + struct uPortal_s * portals; // also on nodes during construction +} node_t; + + +typedef struct uPortal_s { + idPlane plane; + node_t *onnode; // NULL = outside box + node_t *nodes[2]; // [0] = front side of plane + struct uPortal_s *next[2]; + idWinding *winding; +} uPortal_t; + +// a tree_t is created by FaceBSP() +typedef struct tree_s { + node_t *headnode; + node_t outside_node; + idBounds bounds; +} tree_t; + +#define MAX_QPATH 256 // max length of a game pathname + +typedef struct { + idRenderLight * def; + renderLight_t parms; + idPlane frustum[6]; + idBounds frustumBounds; + idVec3 globalLightOrigin; + const idMaterial * lightShader; + char name[MAX_QPATH]; // for naming the shadow volume surface and interactions + srfTriangles_t *shadowTris; +} mapLight_t; + +#define MAX_GROUP_LIGHTS 16 + +typedef struct optimizeGroup_s { + struct optimizeGroup_s *nextGroup; + + idBounds bounds; // set in CarveGroupsByLight + + // all of these must match to add a triangle to the triList + bool smoothed; // curves will never merge with brushes + int planeNum; + int areaNum; + const idMaterial * material; + int numGroupLights; + mapLight_t * groupLights[MAX_GROUP_LIGHTS]; // lights effecting this list + void * mergeGroup; // if this differs (guiSurfs, mirrors, etc), the + // groups will not be combined into model surfaces + // after optimization + textureVectors_t texVec; + + bool surfaceEmited; + + mapTri_t * triList; + mapTri_t * regeneratedTris; // after each island optimization + idVec3 axis[2]; // orthogonal to the plane, so optimization can be 2D +} optimizeGroup_t; + +// all primitives from the map are added to optimzeGroups, creating new ones as needed +// each optimizeGroup is then split into the map areas, creating groups in each area +// each optimizeGroup is then divided by each light, creating more groups +// the final list of groups is then tjunction fixed against all groups, then optimized internally +// multiple optimizeGroups will be merged together into .proc surfaces, but no further optimization +// is done on them + + +//============================================================================= + +// dmap.cpp + +typedef enum { + SO_NONE, // 0 + SO_MERGE_SURFACES, // 1 + SO_CULL_OCCLUDED, // 2 + SO_CLIP_OCCLUDERS, // 3 + SO_CLIP_SILS, // 4 + SO_SIL_OPTIMIZE // 5 +} shadowOptLevel_t; + +typedef struct { + // mapFileBase will contain the qpath without any extension: "maps/test_box" + char mapFileBase[1024]; + + idMapFile *dmapFile; + + idPlaneSet mapPlanes; + + int num_entities; + uEntity_t *uEntities; + + int entityNum; + + idList mapLights; + + bool verbose; + + bool glview; + bool noOptimize; + bool verboseentities; + bool noCurves; + bool fullCarve; + bool noModelBrushes; + bool noTJunc; + bool nomerge; + bool noFlood; + bool noClipSides; // don't cut sides by solid leafs, use the entire thing + bool noLightCarve; // extra triangle subdivision by light frustums + shadowOptLevel_t shadowOptLevel; + bool noShadow; // don't create optimized shadow volumes + + idBounds drawBounds; + bool drawflag; + + int totalShadowTriangles; + int totalShadowVerts; +} dmapGlobals_t; + +extern dmapGlobals_t dmapGlobals; + +int FindFloatPlane( const idPlane &plane, bool *fixedDegeneracies = NULL ); + + +//============================================================================= + +// brush.cpp + +#ifndef CLIP_EPSILON +#define CLIP_EPSILON 0.1f +#endif + +#define PSIDE_FRONT 1 +#define PSIDE_BACK 2 +#define PSIDE_BOTH (PSIDE_FRONT|PSIDE_BACK) +#define PSIDE_FACING 4 + +int CountBrushList (uBrush_t *brushes); +uBrush_t *AllocBrush (int numsides); +void FreeBrush (uBrush_t *brushes); +void FreeBrushList (uBrush_t *brushes); +uBrush_t *CopyBrush (uBrush_t *brush); +void DrawBrushList (uBrush_t *brush); +void PrintBrush (uBrush_t *brush); +bool BoundBrush (uBrush_t *brush); +bool CreateBrushWindings (uBrush_t *brush); +uBrush_t *BrushFromBounds( const idBounds &bounds ); +float BrushVolume (uBrush_t *brush); +void WriteBspBrushMap( const char *name, uBrush_t *list ); + +void FilterBrushesIntoTree( uEntity_t *e ); + +void SplitBrush( uBrush_t *brush, int planenum, uBrush_t **front, uBrush_t **back); +node_t *AllocNode( void ); + + +//============================================================================= + +// map.cpp + +bool LoadDMapFile( const char *filename ); +void FreeOptimizeGroupList( optimizeGroup_t *groups ); +void FreeDMapFile( void ); + +//============================================================================= + +// draw.cpp -- draw debug views either directly, or through glserv.exe + +void Draw_ClearWindow( void ); +void DrawWinding( const idWinding *w ); +void DrawAuxWinding( const idWinding *w ); + +void DrawLine( idVec3 v1, idVec3 v2, int color ); + +void GLS_BeginScene( void ); +void GLS_Winding( const idWinding *w, int code ); +void GLS_Triangle( const mapTri_t *tri, int code ); +void GLS_EndScene( void ); + + + +//============================================================================= + +// portals.cpp + +#define MAX_INTER_AREA_PORTALS 1024 + +typedef struct { + int area0, area1; + side_t *side; +} interAreaPortal_t; + +extern interAreaPortal_t interAreaPortals[MAX_INTER_AREA_PORTALS]; +extern int numInterAreaPortals; + +bool FloodEntities( tree_t *tree ); +void FillOutside( uEntity_t *e ); +void FloodAreas( uEntity_t *e ); +void MakeTreePortals( tree_t *tree ); +void FreePortal( uPortal_t *p ); + +//============================================================================= + +// glfile.cpp -- write a debug file to be viewd with glview.exe + +void OutputWinding( idWinding *w, idFile *glview ); +void WriteGLView( tree_t *tree, char *source ); + +//============================================================================= + +// leakfile.cpp + +void LeakFile( tree_t *tree ); + +//============================================================================= + +// facebsp.cpp + +tree_t *AllocTree( void ); + +void FreeTree( tree_t *tree ); + +void FreeTree_r( node_t *node ); +void FreeTreePortals_r( node_t *node ); + + +bspface_t *MakeStructuralBspFaceList( primitive_t *list ); +bspface_t *MakeVisibleBspFaceList( primitive_t *list ); +tree_t *FaceBSP( bspface_t *list ); + +//============================================================================= + +// surface.cpp + +mapTri_t *CullTrisInOpaqueLeafs( mapTri_t *triList, tree_t *tree ); +void ClipSidesByTree( uEntity_t *e ); +void SplitTrisToSurfaces( mapTri_t *triList, tree_t *tree ); +void PutPrimitivesInAreas( uEntity_t *e ); +void Prelight( uEntity_t *e ); + +//============================================================================= + +// tritjunction.cpp + +struct hashVert_s *GetHashVert( idVec3 &v ); +void HashTriangles( optimizeGroup_t *groupList ); +void FreeTJunctionHash( void ); +int CountGroupListTris( const optimizeGroup_t *groupList ); +void FixEntityTjunctions( uEntity_t *e ); +void FixAreaGroupsTjunctions( optimizeGroup_t *groupList ); +void FixGlobalTjunctions( uEntity_t *e ); + +//============================================================================= + +// optimize.cpp -- trianlge mesh reoptimization + +// the shadow volume optimizer call internal optimizer routines, normal triangles +// will just be done by OptimizeEntity() + + +typedef struct optVertex_s { + idDrawVert v; + idVec3 pv; // projected against planar axis, third value is 0 + struct optEdge_s *edges; + struct optVertex_s *islandLink; + bool addedToIsland; + bool emited; // when regenerating triangles +} optVertex_t; + +typedef struct optEdge_s { + optVertex_t *v1, *v2; + struct optEdge_s *islandLink; + bool addedToIsland; + bool created; // not one of the original edges + bool combined; // combined from two or more colinear edges + struct optTri_s *frontTri, *backTri; + struct optEdge_s *v1link, *v2link; +} optEdge_t; + +typedef struct optTri_s { + struct optTri_s *next; + idVec3 midpoint; + optVertex_t *v[3]; + bool filled; +} optTri_t; + +typedef struct { + optimizeGroup_t *group; + optVertex_t *verts; + optEdge_t *edges; + optTri_t *tris; +} optIsland_t; + + +void OptimizeEntity( uEntity_t *e ); +void OptimizeGroupList( optimizeGroup_t *groupList ); + +//============================================================================= + +// tritools.cpp + +mapTri_t *AllocTri( void ); +void FreeTri( mapTri_t *tri ); +int CountTriList( const mapTri_t *list ); +mapTri_t *MergeTriLists( mapTri_t *a, mapTri_t *b ); +mapTri_t *CopyTriList( const mapTri_t *a ); +void FreeTriList( mapTri_t *a ); +mapTri_t *CopyMapTri( const mapTri_t *tri ); +float MapTriArea( const mapTri_t *tri ); +mapTri_t *RemoveBadTris( const mapTri_t *tri ); +void BoundTriList( const mapTri_t *list, idBounds &b ); +void DrawTri( const mapTri_t *tri ); +void FlipTriList( mapTri_t *tris ); +void TriVertsFromOriginal( mapTri_t *tri, const mapTri_t *original ); +void PlaneForTri( const mapTri_t *tri, idPlane &plane ); +idWinding *WindingForTri( const mapTri_t *tri ); +mapTri_t *WindingToTriList( const idWinding *w, const mapTri_t *originalTri ); +void ClipTriList( const mapTri_t *list, const idPlane &plane, float epsilon, mapTri_t **front, mapTri_t **back ); + +//============================================================================= + +// output.cpp + +srfTriangles_t *ShareMapTriVerts( const mapTri_t *tris ); +void WriteOutputFile( void ); + +//============================================================================= + +// shadowopt.cpp + +srfTriangles_t *CreateLightShadow( optimizeGroup_t *shadowerGroups, const mapLight_t *light ); +void FreeBeamTree( struct beamTree_s *beamTree ); + +void CarveTriByBeamTree( const struct beamTree_s *beamTree, const mapTri_t *tri, mapTri_t **lit, mapTri_t **unLit ); diff --git a/src/tools/compilers/dmap/facebsp.cpp b/src/tools/compilers/dmap/facebsp.cpp new file mode 100644 index 0000000..856a7e3 --- /dev/null +++ b/src/tools/compilers/dmap/facebsp.cpp @@ -0,0 +1,499 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../../idlib/precompiled.h" +#pragma hdrstop + +#include "dmap.h" + +int c_faceLeafs; + + +extern int c_nodes; + +void RemovePortalFromNode( uPortal_t *portal, node_t *l ); + +node_t *NodeForPoint( node_t *node, idVec3 origin ) { + float d; + + while( node->planenum != PLANENUM_LEAF ) { + idPlane &plane = dmapGlobals.mapPlanes[node->planenum]; + d = plane.Distance( origin ); + if ( d >= 0 ) { + node = node->children[0]; + } else { + node = node->children[1]; + } + } + + return node; +} + + + +/* +============= +FreeTreePortals_r +============= +*/ +void FreeTreePortals_r (node_t *node) +{ + uPortal_t *p, *nextp; + int s; + + // free children + if (node->planenum != PLANENUM_LEAF) + { + FreeTreePortals_r (node->children[0]); + FreeTreePortals_r (node->children[1]); + } + + // free portals + for (p=node->portals ; p ; p=nextp) + { + s = (p->nodes[1] == node); + nextp = p->next[s]; + + RemovePortalFromNode (p, p->nodes[!s]); + FreePortal (p); + } + node->portals = NULL; +} + +/* +============= +FreeTree_r +============= +*/ +void FreeTree_r (node_t *node) +{ + // free children + if (node->planenum != PLANENUM_LEAF) + { + FreeTree_r (node->children[0]); + FreeTree_r (node->children[1]); + } + + // free brushes + FreeBrushList (node->brushlist); + + // free the node + c_nodes--; + Mem_Free (node); +} + + +/* +============= +FreeTree +============= +*/ +void FreeTree( tree_t *tree ) { + if ( !tree ) { + return; + } + FreeTreePortals_r (tree->headnode); + FreeTree_r (tree->headnode); + Mem_Free (tree); +} + +//=============================================================== + +void PrintTree_r (node_t *node, int depth) +{ + int i; + uBrush_t *bb; + + for (i=0 ; iPrintf(" "); + if (node->planenum == PLANENUM_LEAF) + { + if (!node->brushlist) + common->Printf("NULL\n"); + else + { + for (bb=node->brushlist ; bb ; bb=bb->next) + common->Printf("%i ", bb->original->brushnum); + common->Printf("\n"); + } + return; + } + + idPlane &plane = dmapGlobals.mapPlanes[node->planenum]; + common->Printf( "#%i (%5.2f %5.2f %5.2f %5.2f)\n", node->planenum, + plane[0], plane[1], plane[2], plane[3] ); + PrintTree_r( node->children[0], depth+1 ); + PrintTree_r( node->children[1], depth+1 ); +} + +/* +================ +AllocBspFace +================ +*/ +bspface_t *AllocBspFace( void ) { + bspface_t *f; + + f = (bspface_t *)Mem_Alloc(sizeof(*f)); + memset( f, 0, sizeof(*f) ); + + return f; +} + +/* +================ +FreeBspFace +================ +*/ +void FreeBspFace( bspface_t *f ) { + if ( f->w ) { + delete f->w; + } + Mem_Free( f ); +} + + +/* +================ +SelectSplitPlaneNum +================ +*/ +#define BLOCK_SIZE 1024 +int SelectSplitPlaneNum( node_t *node, bspface_t *list ) { + bspface_t *split; + bspface_t *check; + bspface_t *bestSplit; + int splits, facing, front, back; + int side; + idPlane *mapPlane; + int value, bestValue; + idPlane plane; + int planenum; + bool havePortals; + float dist; + idVec3 halfSize; + + // if it is crossing a 1k block boundary, force a split + // this prevents epsilon problems from extending an + // arbitrary distance across the map + + halfSize = ( node->bounds[1] - node->bounds[0] ) * 0.5f; + for ( int axis = 0; axis < 3; axis++ ) { + if ( halfSize[axis] > BLOCK_SIZE ) { + dist = BLOCK_SIZE * ( floor( ( node->bounds[0][axis] + halfSize[axis] ) / BLOCK_SIZE ) + 1.0f ); + } else { + dist = BLOCK_SIZE * ( floor( node->bounds[0][axis] / BLOCK_SIZE ) + 1.0f ); + } + if ( dist > node->bounds[0][axis] + 1.0f && dist < node->bounds[1][axis] - 1.0f ) { + plane[0] = plane[1] = plane[2] = 0.0f; + plane[axis] = 1.0f; + plane[3] = -dist; + planenum = FindFloatPlane( plane ); + return planenum; + } + } + + // pick one of the face planes + // if we have any portal faces at all, only + // select from them, otherwise select from + // all faces + bestValue = -999999; + bestSplit = list; + + havePortals = false; + for ( split = list ; split ; split = split->next ) { + split->checked = false; + if ( split->portal ) { + havePortals = true; + } + } + + for ( split = list ; split ; split = split->next ) { + if ( split->checked ) { + continue; + } + if ( havePortals != split->portal ) { + continue; + } + mapPlane = &dmapGlobals.mapPlanes[ split->planenum ]; + splits = 0; + facing = 0; + front = 0; + back = 0; + for ( check = list ; check ; check = check->next ) { + if ( check->planenum == split->planenum ) { + facing++; + check->checked = true; // won't need to test this plane again + continue; + } + side = check->w->PlaneSide( *mapPlane ); + if ( side == SIDE_CROSS ) { + splits++; + } else if ( side == SIDE_FRONT ) { + front++; + } else if ( side == SIDE_BACK ) { + back++; + } + } + value = 5*facing - 5*splits; // - abs(front-back); + if ( mapPlane->Type() < PLANETYPE_TRUEAXIAL ) { + value+=5; // axial is better + } + + if ( value > bestValue ) { + bestValue = value; + bestSplit = split; + } + } + + if ( bestValue == -999999 ) { + return -1; + } + + return bestSplit->planenum; +} + +/* +================ +BuildFaceTree_r +================ +*/ +void BuildFaceTree_r( node_t *node, bspface_t *list ) { + bspface_t *split; + bspface_t *next; + int side; + bspface_t *newFace; + bspface_t *childLists[2]; + idWinding *frontWinding, *backWinding; + int i; + int splitPlaneNum; + + splitPlaneNum = SelectSplitPlaneNum( node, list ); + // if we don't have any more faces, this is a node + if ( splitPlaneNum == -1 ) { + node->planenum = PLANENUM_LEAF; + c_faceLeafs++; + return; + } + + // partition the list + node->planenum = splitPlaneNum; + idPlane &plane = dmapGlobals.mapPlanes[ splitPlaneNum ]; + childLists[0] = NULL; + childLists[1] = NULL; + for ( split = list ; split ; split = next ) { + next = split->next; + + if ( split->planenum == node->planenum ) { + FreeBspFace( split ); + continue; + } + + side = split->w->PlaneSide( plane ); + + if ( side == SIDE_CROSS ) { + split->w->Split( plane, CLIP_EPSILON * 2, &frontWinding, &backWinding ); + if ( frontWinding ) { + newFace = AllocBspFace(); + newFace->w = frontWinding; + newFace->next = childLists[0]; + newFace->planenum = split->planenum; + childLists[0] = newFace; + } + if ( backWinding ) { + newFace = AllocBspFace(); + newFace->w = backWinding; + newFace->next = childLists[1]; + newFace->planenum = split->planenum; + childLists[1] = newFace; + } + FreeBspFace( split ); + } else if ( side == SIDE_FRONT ) { + split->next = childLists[0]; + childLists[0] = split; + } else if ( side == SIDE_BACK ) { + split->next = childLists[1]; + childLists[1] = split; + } + } + + + // recursively process children + for ( i = 0 ; i < 2 ; i++ ) { + node->children[i] = AllocNode(); + node->children[i]->parent = node; + node->children[i]->bounds = node->bounds; + } + + // split the bounds if we have a nice axial plane + for ( i = 0 ; i < 3 ; i++ ) { + if ( idMath::Fabs( plane[i] - 1.0 ) < 0.001 ) { + node->children[0]->bounds[0][i] = plane.Dist(); + node->children[1]->bounds[1][i] = plane.Dist(); + break; + } + } + + for ( i = 0 ; i < 2 ; i++ ) { + BuildFaceTree_r ( node->children[i], childLists[i]); + } +} + + +/* +================ +FaceBSP + +List will be freed before returning +================ +*/ +tree_t *FaceBSP( bspface_t *list ) { + tree_t *tree; + bspface_t *face; + int i; + int count; + int start, end; + + start = Sys_Milliseconds(); + + common->Printf( "--- FaceBSP ---\n" ); + + tree = AllocTree (); + + count = 0; + tree->bounds.Clear(); + for ( face = list ; face ; face = face->next ) { + count++; + for ( i = 0 ; i < face->w->GetNumPoints() ; i++ ) { + tree->bounds.AddPoint( (*face->w)[i].ToVec3() ); + } + } + common->Printf( "%5i faces\n", count ); + + tree->headnode = AllocNode(); + tree->headnode->bounds = tree->bounds; + c_faceLeafs = 0; + + BuildFaceTree_r ( tree->headnode, list ); + + common->Printf( "%5i leafs\n", c_faceLeafs ); + + end = Sys_Milliseconds(); + + common->Printf( "%5.1f seconds faceBsp\n", ( end - start ) / 1000.0 ); + + return tree; +} + +//========================================================================== + +/* +================= +MakeStructuralBspFaceList +================= +*/ +bspface_t *MakeStructuralBspFaceList( primitive_t *list ) { + uBrush_t *b; + int i; + side_t *s; + idWinding *w; + bspface_t *f, *flist; + + flist = NULL; + for ( ; list ; list = list->next ) { + b = list->brush; + if ( !b ) { + continue; + } + if ( !b->opaque && !( b->contents & CONTENTS_AREAPORTAL ) ) { + continue; + } + for ( i = 0 ; i < b->numsides ; i++ ) { + s = &b->sides[i]; + w = s->winding; + if ( !w ) { + continue; + } + if ( ( b->contents & CONTENTS_AREAPORTAL ) && ! ( s->material->GetContentFlags() & CONTENTS_AREAPORTAL ) ) { + continue; + } + f = AllocBspFace(); + if ( s->material->GetContentFlags() & CONTENTS_AREAPORTAL ) { + f->portal = true; + } + f->w = w->Copy(); + f->planenum = s->planenum & ~1; + f->next = flist; + flist = f; + } + } + + return flist; +} + +/* +================= +MakeVisibleBspFaceList +================= +*/ +bspface_t *MakeVisibleBspFaceList( primitive_t *list ) { + uBrush_t *b; + int i; + side_t *s; + idWinding *w; + bspface_t *f, *flist; + + flist = NULL; + for ( ; list ; list = list->next ) { + b = list->brush; + if ( !b ) { + continue; + } + if ( !b->opaque && !( b->contents & CONTENTS_AREAPORTAL ) ) { + continue; + } + for ( i = 0 ; i < b->numsides ; i++ ) { + s = &b->sides[i]; + w = s->visibleHull; + if ( !w ) { + continue; + } + f = AllocBspFace(); + if ( s->material->GetContentFlags() & CONTENTS_AREAPORTAL ) { + f->portal = true; + } + f->w = w->Copy(); + f->planenum = s->planenum & ~1; + f->next = flist; + flist = f; + } + } + + return flist; +} + diff --git a/src/tools/compilers/dmap/gldraw.cpp b/src/tools/compilers/dmap/gldraw.cpp new file mode 100644 index 0000000..5c61f46 --- /dev/null +++ b/src/tools/compilers/dmap/gldraw.cpp @@ -0,0 +1,290 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../../idlib/precompiled.h" +#pragma hdrstop + +#include "dmap.h" + +#ifdef WIN32 +#include +#include +#include +//#include + +#define WIN_SIZE 1024 + +void Draw_ClearWindow( void ) { + + if ( !dmapGlobals.drawflag ) { + return; + } + + glDrawBuffer( GL_FRONT ); + + glClearColor( 0.5, 0.5, 0.5, 0 ); + glClear( GL_COLOR_BUFFER_BIT ); + +#if 0 + int w, h, g; + float mx, my; + + w = (dmapGlobals.drawBounds.b[1][0] - dmapGlobals.drawBounds.b[0][0]); + h = (dmapGlobals.drawBounds.b[1][1] - dmapGlobals.drawBounds.b[0][1]); + + mx = dmapGlobals.drawBounds.b[0][0] + w/2; + my = dmapGlobals.drawBounds.b[1][1] + h/2; + + g = w > h ? w : h; + + glLoadIdentity (); + gluPerspective (90, 1, 2, 16384); + gluLookAt (mx, my, draw_maxs[2] + g/2, mx , my, draw_maxs[2], 0, 1, 0); +#else + glMatrixMode( GL_PROJECTION ); + glLoadIdentity (); + glOrtho( dmapGlobals.drawBounds[0][0], dmapGlobals.drawBounds[1][0], + dmapGlobals.drawBounds[0][1], dmapGlobals.drawBounds[1][1], + -1, 1 ); + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); +#endif + glColor3f (0,0,0); +// glPolygonMode (GL_FRONT_AND_BACK, GL_LINE); + glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + glDisable (GL_DEPTH_TEST); +// glEnable (GL_BLEND); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + +#if 0 +//glColor4f (1,0,0,0.5); +// glBegin( GL_LINE_LOOP ); + glBegin( GL_QUADS ); + + glVertex2f( dmapGlobals.drawBounds.b[0][0] + 20, dmapGlobals.drawBounds.b[0][1] + 20 ); + glVertex2f( dmapGlobals.drawBounds.b[1][0] - 20, dmapGlobals.drawBounds.b[0][1] + 20 ); + glVertex2f( dmapGlobals.drawBounds.b[1][0] - 20, dmapGlobals.drawBounds.b[1][1] - 20 ); + glVertex2f( dmapGlobals.drawBounds.b[0][0] + 20, dmapGlobals.drawBounds.b[1][1] - 20 ); + + glEnd (); +#endif + + glFlush (); + +} + +void Draw_SetRed (void) +{ + if (!dmapGlobals.drawflag) + return; + + glColor3f (1,0,0); +} + +void Draw_SetGrey (void) +{ + if (!dmapGlobals.drawflag) + return; + + glColor3f( 0.5f, 0.5f, 0.5f); +} + +void Draw_SetBlack (void) +{ + if (!dmapGlobals.drawflag) + return; + + glColor3f( 0.0f, 0.0f, 0.0f ); +} + +void DrawWinding ( const idWinding *w ) +{ + int i; + + if (!dmapGlobals.drawflag) + return; + + glColor3f( 0.3f, 0.0f, 0.0f ); + glBegin (GL_POLYGON); + for ( i = 0; i < w->GetNumPoints(); i++ ) + glVertex3f( (*w)[i][0], (*w)[i][1], (*w)[i][2] ); + glEnd (); + + glColor3f( 1, 0, 0 ); + glBegin (GL_LINE_LOOP); + for ( i = 0; i < w->GetNumPoints(); i++ ) + glVertex3f( (*w)[i][0], (*w)[i][1], (*w)[i][2] ); + glEnd (); + + glFlush (); +} + +void DrawAuxWinding ( const idWinding *w) +{ + int i; + + if (!dmapGlobals.drawflag) + return; + + glColor3f( 0.0f, 0.3f, 0.0f ); + glBegin (GL_POLYGON); + for ( i = 0; i < w->GetNumPoints(); i++ ) + glVertex3f( (*w)[i][0], (*w)[i][1], (*w)[i][2] ); + glEnd (); + + glColor3f( 0.0f, 1.0f, 0.0f ); + glBegin (GL_LINE_LOOP); + for ( i = 0; i < w->GetNumPoints(); i++ ) + glVertex3f( (*w)[i][0], (*w)[i][1], (*w)[i][2] ); + glEnd (); + + glFlush (); +} + +void DrawLine( idVec3 v1, idVec3 v2, int color ) { + if (!dmapGlobals.drawflag) + return; + + switch( color ) { + case 0: glColor3f( 0, 0, 0 ); break; + case 1: glColor3f( 0, 0, 1 ); break; + case 2: glColor3f( 0, 1, 0 ); break; + case 3: glColor3f( 0, 1, 1 ); break; + case 4: glColor3f( 1, 0, 0 ); break; + case 5: glColor3f( 1, 0, 1 ); break; + case 6: glColor3f( 1, 1, 0 ); break; + case 7: glColor3f( 1, 1, 1 ); break; + } + + + glBegin( GL_LINES ); + + glVertex3fv( v1.ToFloatPtr() ); + glVertex3fv( v2.ToFloatPtr() ); + + glEnd(); + glFlush(); +} + +//============================================================ + +#define GLSERV_PORT 25001 + +bool wins_init; +int draw_socket; + +void GLS_BeginScene (void) +{ + WSADATA winsockdata; + WORD wVersionRequested; + struct sockaddr_in address; + int r; + + if (!wins_init) + { + wins_init = true; + + wVersionRequested = MAKEWORD(1, 1); + + r = WSAStartup (MAKEWORD(1, 1), &winsockdata); + + if (r) + common->Error( "Winsock initialization failed."); + + } + + // connect a socket to the server + + draw_socket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (draw_socket == -1) + common->Error( "draw_socket failed"); + + address.sin_family = AF_INET; + address.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + address.sin_port = GLSERV_PORT; + r = connect (draw_socket, (struct sockaddr *)&address, sizeof(address)); + if (r == -1) + { + closesocket (draw_socket); + draw_socket = 0; + } +} + +void GLS_Winding( const idWinding *w, int code ) +{ + byte buf[1024]; + int i, j; + + if (!draw_socket) + return; + + ((int *)buf)[0] = w->GetNumPoints(); + ((int *)buf)[1] = code; + for ( i = 0; i < w->GetNumPoints(); i++ ) + for (j=0 ; j<3 ; j++) + ((float *)buf)[2+i*3+j] = (*w)[i][j]; + + send (draw_socket, (const char *)buf, w->GetNumPoints() * 12 + 8, 0); +} + +void GLS_Triangle( const mapTri_t *tri, int code ) { + idWinding w; + + w.SetNumPoints( 3 ); + VectorCopy( tri->v[0].xyz, w[0] ); + VectorCopy( tri->v[1].xyz, w[1] ); + VectorCopy( tri->v[2].xyz, w[2] ); + GLS_Winding( &w, code ); +} + +void GLS_EndScene (void) +{ + closesocket (draw_socket); + draw_socket = 0; +} +#else +void Draw_ClearWindow( void ) { +} + +void DrawWinding( const idWinding *w) { +} + +void DrawAuxWinding ( const idWinding *w) { +} + +void GLS_Winding( const idWinding *w, int code ) { +} + +void GLS_BeginScene (void) { +} + +void GLS_EndScene (void) +{ +} + +#endif diff --git a/src/tools/compilers/dmap/glfile.cpp b/src/tools/compilers/dmap/glfile.cpp new file mode 100644 index 0000000..fd98578 --- /dev/null +++ b/src/tools/compilers/dmap/glfile.cpp @@ -0,0 +1,158 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../../idlib/precompiled.h" +#pragma hdrstop + +#include "dmap.h" + +int c_glfaces; + +int PortalVisibleSides( uPortal_t *p ) +{ + int fcon, bcon; + + if (!p->onnode) + return 0; // outside + + fcon = p->nodes[0]->opaque; + bcon = p->nodes[1]->opaque; + + // same contents never create a face + if (fcon == bcon) + return 0; + + if (!fcon) + return 1; + if (!bcon) + return 2; + return 0; +} + +void OutputWinding( idWinding *w, idFile *glview ) +{ + static int level = 128; + float light; + int i; + + glview->WriteFloatString( "%i\n", w->GetNumPoints() ); + level += 28; + light = (level&255)/255.0; + for ( i = 0; i < w->GetNumPoints(); i++ ) { + glview->WriteFloatString( "%6.3f %6.3f %6.3f %6.3f %6.3f %6.3f\n", + (*w)[i][0], + (*w)[i][1], + (*w)[i][2], + light, + light, + light ); + } + glview->WriteFloatString( "\n" ); +} + +/* +============= +OutputPortal +============= +*/ +void OutputPortal( uPortal_t *p, idFile *glview ) { + idWinding *w; + int sides; + + sides = PortalVisibleSides( p ); + if ( !sides ) { + return; + } + + c_glfaces++; + + w = p->winding; + + if ( sides == 2 ) { // back side + w = w->Reverse(); + } + + OutputWinding( w, glview ); + + if ( sides == 2 ) { + delete w; + } +} + +/* +============= +WriteGLView_r +============= +*/ +void WriteGLView_r( node_t *node, idFile *glview ) +{ + uPortal_t *p, *nextp; + + if ( node->planenum != PLANENUM_LEAF ) + { + WriteGLView_r( node->children[0], glview ); + WriteGLView_r( node->children[1], glview ); + return; + } + + // write all the portals + for ( p = node->portals; p; p = nextp ) + { + if ( p->nodes[0] == node ) + { + OutputPortal( p, glview ); + nextp = p->next[0]; + } + else { + nextp = p->next[1]; + } + } +} + +/* +============= +WriteGLView +============= +*/ +void WriteGLView( tree_t *tree, char *source ) +{ + idFile *glview; + + c_glfaces = 0; + common->Printf( "Writing %s\n", source ); + + glview = fileSystem->OpenExplicitFileWrite( source ); + if ( !glview ) { + common->Error( "Couldn't open %s", source ); + } + WriteGLView_r( tree->headnode, glview ); + fileSystem->CloseFile( glview ); + + common->Printf( "%5i c_glfaces\n", c_glfaces ); +} + diff --git a/src/tools/compilers/dmap/leakfile.cpp b/src/tools/compilers/dmap/leakfile.cpp new file mode 100644 index 0000000..5665e4d --- /dev/null +++ b/src/tools/compilers/dmap/leakfile.cpp @@ -0,0 +1,112 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../../idlib/precompiled.h" +#pragma hdrstop + +#include "dmap.h" + +/* +============================================================================== + +LEAF FILE GENERATION + +Save out name.line for qe3 to read +============================================================================== +*/ + + +/* +============= +LeakFile + +Finds the shortest possible chain of portals +that leads from the outside leaf to a specifically +occupied leaf +============= +*/ +void LeakFile (tree_t *tree) +{ + idVec3 mid; + FILE *linefile; + idStr filename; + idStr ospath; + node_t *node; + int count; + + if (!tree->outside_node.occupied) + return; + + common->Printf ("--- LeakFile ---\n"); + + // + // write the points to the file + // + sprintf( filename, "%s.lin", dmapGlobals.mapFileBase ); + ospath = fileSystem->RelativePathToOSPath( filename ); + linefile = fopen( ospath, "w" ); + if ( !linefile ) { + common->Error( "Couldn't open %s\n", filename.c_str() ); + } + + count = 0; + node = &tree->outside_node; + while (node->occupied > 1) + { + int next; + uPortal_t *p, *nextportal; + node_t *nextnode; + int s; + + // find the best portal exit + next = node->occupied; + for (p=node->portals ; p ; p = p->next[!s]) + { + s = (p->nodes[0] == node); + if (p->nodes[s]->occupied + && p->nodes[s]->occupied < next) + { + nextportal = p; + nextnode = p->nodes[s]; + next = nextnode->occupied; + } + } + node = nextnode; + mid = nextportal->winding->GetCenter(); + fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]); + count++; + } + // add the occupant center + node->occupant->mapEntity->epairs.GetVector( "origin", "", mid ); + + fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]); + common->Printf ("%5i point linefile\n", count+1); + + fclose (linefile); +} + diff --git a/src/tools/compilers/dmap/map.cpp b/src/tools/compilers/dmap/map.cpp new file mode 100644 index 0000000..c2b786f --- /dev/null +++ b/src/tools/compilers/dmap/map.cpp @@ -0,0 +1,670 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../../idlib/precompiled.h" +#pragma hdrstop + +#include "dmap.h" + +/* + + After parsing, there will be a list of entities that each has + a list of primitives. + + Primitives are either brushes, triangle soups, or model references. + + Curves are tesselated to triangle soups at load time, but model + references are + Brushes will have + + brushes, each of which has a side definition. + +*/ + +// +// private declarations +// + +#define MAX_BUILD_SIDES 300 + +static int entityPrimitive; // to track editor brush numbers +static int c_numMapPatches; +static int c_areaportals; + +static uEntity_t *uEntity; + +// brushes are parsed into a temporary array of sides, +// which will have duplicates removed before the final brush is allocated +static uBrush_t *buildBrush; + + +#define NORMAL_EPSILON 0.00001f +#define DIST_EPSILON 0.01f + + +/* +=========== +FindFloatPlane +=========== +*/ +int FindFloatPlane( const idPlane &plane, bool *fixedDegeneracies ) { + idPlane p = plane; + bool fixed = p.FixDegeneracies( DIST_EPSILON ); + if ( fixed && fixedDegeneracies ) { + *fixedDegeneracies = true; + } + return dmapGlobals.mapPlanes.FindPlane( p, NORMAL_EPSILON, DIST_EPSILON ); +} + +/* +=========== +SetBrushContents + +The contents on all sides of a brush should be the same +Sets contentsShader, contents, opaque +=========== +*/ +static void SetBrushContents( uBrush_t *b ) { + int contents, c2; + side_t *s; + int i; + bool mixed; + + s = &b->sides[0]; + contents = s->material->GetContentFlags(); + + b->contentShader = s->material; + mixed = false; + + // a brush is only opaque if all sides are opaque + b->opaque = true; + + for ( i=1 ; inumsides ; i++, s++ ) { + s = &b->sides[i]; + + if ( !s->material ) { + continue; + } + + c2 = s->material->GetContentFlags(); + if (c2 != contents) { + mixed = true; + contents |= c2; + } + + if ( s->material->Coverage() != MC_OPAQUE ) { + b->opaque = false; + } + } + + if ( contents & CONTENTS_AREAPORTAL ) { + c_areaportals++; + } + + b->contents = contents; +} + + +//============================================================================ + +/* +=============== +FreeBuildBrush +=============== +*/ +static void FreeBuildBrush( void ) { + int i; + + for ( i = 0 ; i < buildBrush->numsides ; i++ ) { + if ( buildBrush->sides[i].winding ) { + delete buildBrush->sides[i].winding; + } + } + buildBrush->numsides = 0; +} + +/* +=============== +FinishBrush + +Produces a final brush based on the buildBrush->sides array +and links it to the current entity +=============== +*/ +static uBrush_t *FinishBrush( void ) { + uBrush_t *b; + primitive_t *prim; + + // create windings for sides and bounds for brush + if ( !CreateBrushWindings( buildBrush ) ) { + // don't keep this brush + FreeBuildBrush(); + return NULL; + } + + if ( buildBrush->contents & CONTENTS_AREAPORTAL ) { + if (dmapGlobals.num_entities != 1) { + common->Printf("Entity %i, Brush %i: areaportals only allowed in world\n" + , dmapGlobals.num_entities - 1, entityPrimitive); + FreeBuildBrush(); + return NULL; + } + } + + // keep it + b = CopyBrush( buildBrush ); + + FreeBuildBrush(); + + b->entitynum = dmapGlobals.num_entities-1; + b->brushnum = entityPrimitive; + + b->original = b; + + prim = (primitive_t *)Mem_Alloc( sizeof( *prim ) ); + memset( prim, 0, sizeof( *prim ) ); + prim->next = uEntity->primitives; + uEntity->primitives = prim; + + prim->brush = b; + + return b; +} + +/* +================ +AdjustEntityForOrigin +================ +*/ +static void AdjustEntityForOrigin( uEntity_t *ent ) { + primitive_t *prim; + uBrush_t *b; + int i; + side_t *s; + + for ( prim = ent->primitives ; prim ; prim = prim->next ) { + b = prim->brush; + if ( !b ) { + continue; + } + for ( i = 0; i < b->numsides; i++ ) { + idPlane plane; + + s = &b->sides[i]; + + plane = dmapGlobals.mapPlanes[s->planenum]; + plane[3] += plane.Normal() * ent->origin; + + s->planenum = FindFloatPlane( plane ); + + s->texVec.v[0][3] += DotProduct( ent->origin, s->texVec.v[0] ); + s->texVec.v[1][3] += DotProduct( ent->origin, s->texVec.v[1] ); + + // remove any integral shift + s->texVec.v[0][3] -= floor( s->texVec.v[0][3] ); + s->texVec.v[1][3] -= floor( s->texVec.v[1][3] ); + } + CreateBrushWindings(b); + } +} + +/* +================= +RemoveDuplicateBrushPlanes + +Returns false if the brush has a mirrored set of planes, +meaning it encloses no volume. +Also removes planes without any normal +================= +*/ +static bool RemoveDuplicateBrushPlanes( uBrush_t * b ) { + int i, j, k; + side_t *sides; + + sides = b->sides; + + for ( i = 1 ; i < b->numsides ; i++ ) { + + // check for a degenerate plane + if ( sides[i].planenum == -1) { + common->Printf("Entity %i, Brush %i: degenerate plane\n" + , b->entitynum, b->brushnum); + // remove it + for ( k = i + 1 ; k < b->numsides ; k++ ) { + sides[k-1] = sides[k]; + } + b->numsides--; + i--; + continue; + } + + // check for duplication and mirroring + for ( j = 0 ; j < i ; j++ ) { + if ( sides[i].planenum == sides[j].planenum ) { + common->Printf("Entity %i, Brush %i: duplicate plane\n" + , b->entitynum, b->brushnum); + // remove the second duplicate + for ( k = i + 1 ; k < b->numsides ; k++ ) { + sides[k-1] = sides[k]; + } + b->numsides--; + i--; + break; + } + + if ( sides[i].planenum == (sides[j].planenum ^ 1) ) { + // mirror plane, brush is invalid + common->Printf("Entity %i, Brush %i: mirrored plane\n" + , b->entitynum, b->brushnum); + return false; + } + } + } + return true; +} + + +/* +================= +ParseBrush +================= +*/ +static void ParseBrush( const idMapBrush *mapBrush, int primitiveNum ) { + uBrush_t *b; + side_t *s; + const idMapBrushSide *ms; + int i; + bool fixedDegeneracies = false; + + buildBrush->entitynum = dmapGlobals.num_entities-1; + buildBrush->brushnum = entityPrimitive; + buildBrush->numsides = mapBrush->GetNumSides(); + for ( i = 0 ; i < mapBrush->GetNumSides() ; i++ ) { + s = &buildBrush->sides[i]; + ms = mapBrush->GetSide(i); + + memset( s, 0, sizeof( *s ) ); + s->planenum = FindFloatPlane( ms->GetPlane(), &fixedDegeneracies ); + s->material = declManager->FindMaterial( ms->GetMaterial() ); + ms->GetTextureVectors( s->texVec.v ); + // remove any integral shift, which will help with grouping + s->texVec.v[0][3] -= floor( s->texVec.v[0][3] ); + s->texVec.v[1][3] -= floor( s->texVec.v[1][3] ); + } + + // if there are mirrored planes, the entire brush is invalid + if ( !RemoveDuplicateBrushPlanes( buildBrush ) ) { + return; + } + + // get the content for the entire brush + SetBrushContents( buildBrush ); + + b = FinishBrush(); + if ( !b ) { + return; + } + + if ( fixedDegeneracies && dmapGlobals.verboseentities ) { + common->Warning( "brush %d has degenerate plane equations", primitiveNum ); + } +} + +/* +================ +ParseSurface +================ +*/ +static void ParseSurface( const idMapPatch *patch, const idSurface *surface, const idMaterial *material ) { + int i; + mapTri_t *tri; + primitive_t *prim; + + prim = (primitive_t *)Mem_Alloc( sizeof( *prim ) ); + memset( prim, 0, sizeof( *prim ) ); + prim->next = uEntity->primitives; + uEntity->primitives = prim; + + for ( i = 0; i < surface->GetNumIndexes(); i += 3 ) { + tri = AllocTri(); + tri->v[2] = (*surface)[surface->GetIndexes()[i+0]]; + tri->v[1] = (*surface)[surface->GetIndexes()[i+2]]; + tri->v[0] = (*surface)[surface->GetIndexes()[i+1]]; + tri->material = material; + tri->next = prim->tris; + prim->tris = tri; + } + + // set merge groups if needed, to prevent multiple sides from being + // merged into a single surface in the case of gui shaders, mirrors, and autosprites + if ( material->IsDiscrete() ) { + for ( tri = prim->tris ; tri ; tri = tri->next ) { + tri->mergeGroup = (void *)patch; + } + } +} + +/* +================ +ParsePatch +================ +*/ +static void ParsePatch( const idMapPatch *patch, int primitiveNum ) { + const idMaterial *mat; + + if ( dmapGlobals.noCurves ) { + return; + } + + c_numMapPatches++; + + mat = declManager->FindMaterial( patch->GetMaterial() ); + + idSurface_Patch *cp = new idSurface_Patch( *patch ); + + if ( patch->GetExplicitlySubdivided() ) { + cp->SubdivideExplicit( patch->GetHorzSubdivisions(), patch->GetVertSubdivisions(), true ); + } else { + cp->Subdivide( DEFAULT_CURVE_MAX_ERROR, DEFAULT_CURVE_MAX_ERROR, DEFAULT_CURVE_MAX_LENGTH, true ); + } + + ParseSurface( patch, cp, mat ); + + delete cp; +} + +/* +================ +ProcessMapEntity +================ +*/ +static bool ProcessMapEntity( idMapEntity *mapEnt ) { + idMapPrimitive *prim; + + uEntity = &dmapGlobals.uEntities[dmapGlobals.num_entities]; + memset( uEntity, 0, sizeof(*uEntity) ); + uEntity->mapEntity = mapEnt; + dmapGlobals.num_entities++; + + for ( entityPrimitive = 0; entityPrimitive < mapEnt->GetNumPrimitives(); entityPrimitive++ ) { + prim = mapEnt->GetPrimitive(entityPrimitive); + + if ( prim->GetType() == idMapPrimitive::TYPE_BRUSH ) { + ParseBrush( static_cast(prim), entityPrimitive ); + } + else if ( prim->GetType() == idMapPrimitive::TYPE_PATCH ) { + ParsePatch( static_cast(prim), entityPrimitive ); + } + } + + // never put an origin on the world, even if the editor left one there + if ( dmapGlobals.num_entities != 1 ) { + uEntity->mapEntity->epairs.GetVector( "origin", "", uEntity->origin ); + } + + return true; +} + +//=================================================================== + +/* +============== +CreateMapLight + +============== +*/ +static void CreateMapLight( const idMapEntity *mapEnt ) { + mapLight_t *light; + bool dynamic; + + // designers can add the "noPrelight" flag to signal that + // the lights will move around, so we don't want + // to bother chopping up the surfaces under it or creating + // shadow volumes + mapEnt->epairs.GetBool( "noPrelight", "0", dynamic ); + if ( dynamic ) { + return; + } + + light = new mapLight_t; + memset( &light->parms, 0, sizeof( light->parms ) ); + light->name[0] = '\0'; + light->shadowTris = NULL; + light->def = renderModelManager->CreateLightDef(); + + // parse parms exactly as the game do + // use the game's epair parsing code so + // we can use the same renderLight generation + gameEdit->ParseSpawnArgsToRenderLight( &mapEnt->epairs, &light->parms ); + light->def->UpdateRenderLight( &light->parms, true ); + renderSystem->RenderLightFrustum( light->parms, light->frustum ); + light->globalLightOrigin = light->parms.origin + light->parms.axis * light->parms.lightCenter; + if ( light->parms.parallel ) { + idVec3 direction = light->parms.lightCenter; + if ( !direction.Normalize() ) direction.Set( 0, 0, 1 ); + light->globalLightOrigin = light->parms.origin + direction * 100000.0f; + } + light->lightShader = light->parms.shader; + if ( !light->lightShader ) { + light->lightShader = declManager->FindMaterial( light->parms.pointLight ? + "lights/defaultPointLight" : "lights/defaultProjectedLight" ); + } + idWinding *frustumWindings[6] = { NULL, NULL, NULL, NULL, NULL, NULL }; + srfTriangles_t *frustumTris = renderModelManager->PolytopeSurface( 6, light->frustum, frustumWindings ); + if ( frustumTris ) { + light->frustumBounds = frustumTris->bounds; + renderModelManager->FreeStaticTriSurf( frustumTris ); + } else { + light->frustumBounds = idBounds( light->parms.origin ).Expand( light->parms.lightRadius.Length() ); + } + for ( int windingNum = 0; windingNum < 6; windingNum++ ) delete frustumWindings[windingNum]; + + // get the name for naming the shadow surfaces + const char *name; + + mapEnt->epairs.GetString( "name", "", &name ); + + idStr::Copynz( light->name, name, sizeof( light->name ) ); + if ( !light->name[0] ) { + common->Error( "Light at (%f,%f,%f) didn't have a name", + light->parms.origin[0], light->parms.origin[1], light->parms.origin[2] ); + } +#if 0 + // use the renderer code to get the bounding planes for the light + // based on all the parameters + R_RenderLightFrustum( light->parms, light->frustum ); + light->lightShader = light->parms.shader; +#endif + + dmapGlobals.mapLights.Append( light ); + +} + +/* +============== +CreateMapLights + +============== +*/ +static void CreateMapLights( const idMapFile *dmapFile ) { + int i; + const idMapEntity *mapEnt; + const char *value; + + for ( i = 0 ; i < dmapFile->GetNumEntities() ; i++ ) { + mapEnt = dmapFile->GetEntity(i); + mapEnt->epairs.GetString( "classname", "", &value); + if ( !idStr::Icmp( value, "light" ) ) { + CreateMapLight( mapEnt ); + } + + } + +} + +/* +================ +LoadDMapFile +================ +*/ +bool LoadDMapFile( const char *filename ) { + primitive_t *prim; + idBounds mapBounds; + int brushes, triSurfs; + int i; + int size; + + common->Printf( "--- LoadDMapFile ---\n" ); + common->Printf( "loading %s\n", filename ); + + // load and parse the map file into canonical form + dmapGlobals.dmapFile = new idMapFile(); + if ( !dmapGlobals.dmapFile->Parse(filename) ) { + delete dmapGlobals.dmapFile; + dmapGlobals.dmapFile = NULL; + common->Warning( "Couldn't load map file: '%s'", filename ); + return false; + } + + dmapGlobals.mapPlanes.Clear(); + dmapGlobals.mapPlanes.SetGranularity( 1024 ); + + // process the canonical form into utility form + dmapGlobals.num_entities = 0; + c_numMapPatches = 0; + c_areaportals = 0; + + size = dmapGlobals.dmapFile->GetNumEntities() * sizeof( dmapGlobals.uEntities[0] ); + dmapGlobals.uEntities = (uEntity_t *)Mem_Alloc( size ); + memset( dmapGlobals.uEntities, 0, size ); + + // allocate a very large temporary brush for building + // the brushes as they are loaded + buildBrush = AllocBrush( MAX_BUILD_SIDES ); + + for ( i = 0 ; i < dmapGlobals.dmapFile->GetNumEntities() ; i++ ) { + ProcessMapEntity( dmapGlobals.dmapFile->GetEntity(i) ); + } + + CreateMapLights( dmapGlobals.dmapFile ); + + brushes = 0; + triSurfs = 0; + + mapBounds.Clear(); + for ( prim = dmapGlobals.uEntities[0].primitives ; prim ; prim = prim->next ) { + if ( prim->brush ) { + brushes++; + mapBounds.AddBounds( prim->brush->bounds ); + } else if ( prim->tris ) { + triSurfs++; + } + } + + common->Printf( "%5i total world brushes\n", brushes ); + common->Printf( "%5i total world triSurfs\n", triSurfs ); + common->Printf( "%5i patches\n", c_numMapPatches ); + common->Printf( "%5i entities\n", dmapGlobals.num_entities ); + common->Printf( "%5i planes\n", dmapGlobals.mapPlanes.Num() ); + common->Printf( "%5i areaportals\n", c_areaportals ); + common->Printf( "size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n", mapBounds[0][0], mapBounds[0][1],mapBounds[0][2], + mapBounds[1][0], mapBounds[1][1], mapBounds[1][2] ); + + return true; +} + +/* +================ +FreeOptimizeGroupList +================ +*/ +void FreeOptimizeGroupList( optimizeGroup_t *groups ) { + optimizeGroup_t *next; + + for ( ; groups ; groups = next ) { + next = groups->nextGroup; + FreeTriList( groups->triList ); + Mem_Free( groups ); + } +} + +/* +================ +FreeDMapFile +================ +*/ +void FreeDMapFile( void ) { + int i, j; + + FreeBrush( buildBrush ); + buildBrush = NULL; + + // free the entities and brushes + for ( i = 0 ; i < dmapGlobals.num_entities ; i++ ) { + uEntity_t *ent; + primitive_t *prim, *nextPrim; + + ent = &dmapGlobals.uEntities[i]; + + FreeTree( ent->tree ); + + // free primitives + for ( prim = ent->primitives ; prim ; prim = nextPrim ) { + nextPrim = prim->next; + if ( prim->brush ) { + FreeBrush( prim->brush ); + } + if ( prim->tris ) { + FreeTriList( prim->tris ); + } + Mem_Free( prim ); + } + + // free area surfaces + if ( ent->areas ) { + for ( j = 0 ; j < ent->numAreas ; j++ ) { + uArea_t *area; + + area = &ent->areas[j]; + FreeOptimizeGroupList( area->groups ); + + } + Mem_Free( ent->areas ); + } + } + + Mem_Free( dmapGlobals.uEntities ); + + dmapGlobals.num_entities = 0; + + // free the map lights + for ( i = 0; i < dmapGlobals.mapLights.Num(); i++ ) { + renderModelManager->FreeLightDef( dmapGlobals.mapLights[i]->def ); + } + dmapGlobals.mapLights.DeleteContents( true ); +} diff --git a/src/tools/compilers/dmap/optimize.cpp b/src/tools/compilers/dmap/optimize.cpp new file mode 100644 index 0000000..d8a1f1b --- /dev/null +++ b/src/tools/compilers/dmap/optimize.cpp @@ -0,0 +1,1999 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../../idlib/precompiled.h" +#pragma hdrstop + +//#pragma optimize( "", off ) + +#include "dmap.h" +#ifdef WIN32 +#include +#include +#define qglBegin glBegin +#define qglBlendFunc glBlendFunc +#define qglColor3f glColor3f +#define qglDisable glDisable +#define qglEnable glEnable +#define qglEnd glEnd +#define qglFlush glFlush +#define qglPointSize glPointSize +#define qglVertex3fv glVertex3fv +#endif + +/* + + New vertexes will be created where edges cross. + + optimization requires an accurate t junction fixer. + + + +*/ + +idBounds optBounds; + +#define MAX_OPT_VERTEXES 0x10000 +int numOptVerts; +optVertex_t optVerts[MAX_OPT_VERTEXES]; + +#define MAX_OPT_EDGES 0x40000 +static int numOptEdges; +static optEdge_t optEdges[MAX_OPT_EDGES]; + +static bool IsTriangleValid( const optVertex_t *v1, const optVertex_t *v2, const optVertex_t *v3 ); +static bool IsTriangleDegenerate( const optVertex_t *v1, const optVertex_t *v2, const optVertex_t *v3 ); + +static idRandom orandom; + +/* +============== +ValidateEdgeCounts +============== +*/ +static void ValidateEdgeCounts( optIsland_t *island ) { + optVertex_t *vert; + optEdge_t *e; + int c; + + for ( vert = island->verts ; vert ; vert = vert->islandLink ) { + c = 0; + for ( e = vert->edges ; e ; ) { + c++; + if ( e->v1 == vert ) { + e = e->v1link; + } else if ( e->v2 == vert ) { + e = e->v2link; + } else { + common->Error( "ValidateEdgeCounts: mislinked" ); + } + } + if ( c != 2 && c != 0 ) { + // this can still happen at diamond intersections +// common->Printf( "ValidateEdgeCounts: %i edges\n", c ); + } + } +} + + +/* +==================== +AllocEdge +==================== +*/ +static optEdge_t *AllocEdge( void ) { + optEdge_t *e; + + if ( numOptEdges == MAX_OPT_EDGES ) { + common->Error( "MAX_OPT_EDGES" ); + } + e = &optEdges[ numOptEdges ]; + numOptEdges++; + memset( e, 0, sizeof( *e ) ); + + return e; +} + +/* +==================== +RemoveEdgeFromVert +==================== +*/ +static void RemoveEdgeFromVert( optEdge_t *e1, optVertex_t *vert ) { + optEdge_t **prev; + optEdge_t *e; + + if ( !vert ) { + return; + } + prev = &vert->edges; + while ( *prev ) { + e = *prev; + if ( e == e1 ) { + if ( e1->v1 == vert ) { + *prev = e1->v1link; + } else if ( e1->v2 == vert ) { + *prev = e1->v2link; + } else { + common->Error( "RemoveEdgeFromVert: vert not found" ); + } + return; + } + + if ( e->v1 == vert ) { + prev = &e->v1link; + } else if ( e->v2 == vert ) { + prev = &e->v2link; + } else { + common->Error( "RemoveEdgeFromVert: vert not found" ); + } + } +} + +/* +==================== +UnlinkEdge +==================== +*/ +static void UnlinkEdge( optEdge_t *e, optIsland_t *island ) { + optEdge_t **prev; + + RemoveEdgeFromVert( e, e->v1 ); + RemoveEdgeFromVert( e, e->v2 ); + + for ( prev = &island->edges ; *prev ; prev = &(*prev)->islandLink ) { + if ( *prev == e ) { + *prev = e->islandLink; + return; + } + } + + common->Error( "RemoveEdgeFromIsland: couldn't free edge" ); +} + + +/* +==================== +LinkEdge +==================== +*/ +static void LinkEdge( optEdge_t *e ) { + e->v1link = e->v1->edges; + e->v1->edges = e; + + e->v2link = e->v2->edges; + e->v2->edges = e; +} + +#ifdef __linux__ + +optVertex_t *FindOptVertex( idDrawVert *v, optimizeGroup_t *opt ); + +#else + +/* +================ +FindOptVertex +================ +*/ +static optVertex_t *FindOptVertex( idDrawVert *v, optimizeGroup_t *opt ) { + int i; + float x, y; + optVertex_t *vert; + + // deal with everything strictly as 2D + x = v->xyz * opt->axis[0]; + y = v->xyz * opt->axis[1]; + + // should we match based on the t-junction fixing hash verts? + for ( i = 0 ; i < numOptVerts ; i++ ) { + if ( optVerts[i].pv[0] == x && optVerts[i].pv[1] == y ) { + return &optVerts[i]; + } + } + + if ( numOptVerts >= MAX_OPT_VERTEXES ) { + common->Error( "MAX_OPT_VERTEXES" ); + return NULL; + } + + numOptVerts++; + + vert = &optVerts[i]; + memset( vert, 0, sizeof( *vert ) ); + vert->v = *v; + vert->pv[0] = x; + vert->pv[1] = y; + vert->pv[2] = 0; + + optBounds.AddPoint( vert->pv ); + + return vert; +} + +#endif + +/* +================ +DrawAllEdges +================ +*/ +static void DrawAllEdges( void ) { + int i; + + if ( !dmapGlobals.drawflag ) { + return; + } + + Draw_ClearWindow(); + + qglBegin( GL_LINES ); + for ( i = 0 ; i < numOptEdges ; i++ ) { + if ( optEdges[i].v1 == NULL ) { + continue; + } + qglColor3f( 1, 0, 0 ); + qglVertex3fv( optEdges[i].v1->pv.ToFloatPtr() ); + qglColor3f( 0, 0, 0 ); + qglVertex3fv( optEdges[i].v2->pv.ToFloatPtr() ); + } + qglEnd(); + qglFlush(); + +// GLimp_SwapBuffers(); +} + +/* +================ +DrawVerts +================ +*/ +static void DrawVerts( optIsland_t *island ) { + optVertex_t *vert; + + if ( !dmapGlobals.drawflag ) { + return; + } + + qglEnable( GL_BLEND ); + qglBlendFunc( GL_ONE, GL_ONE ); + qglColor3f( 0.3f, 0.3f, 0.3f ); + qglPointSize( 3 ); + qglBegin( GL_POINTS ); + for ( vert = island->verts ; vert ; vert = vert->islandLink ) { + qglVertex3fv( vert->pv.ToFloatPtr() ); + } + qglEnd(); + qglDisable( GL_BLEND ); + qglFlush(); +} + +/* +================ +DrawEdges +================ +*/ +static void DrawEdges( optIsland_t *island ) { + optEdge_t *edge; + + if ( !dmapGlobals.drawflag ) { + return; + } + + Draw_ClearWindow(); + + qglBegin( GL_LINES ); + for ( edge = island->edges ; edge ; edge = edge->islandLink ) { + if ( edge->v1 == NULL ) { + continue; + } + qglColor3f( 1, 0, 0 ); + qglVertex3fv( edge->v1->pv.ToFloatPtr() ); + qglColor3f( 0, 0, 0 ); + qglVertex3fv( edge->v2->pv.ToFloatPtr() ); + } + qglEnd(); + qglFlush(); + +// GLimp_SwapBuffers(); +} + +//================================================================= + +/* +================= +VertexBetween +================= +*/ +static bool VertexBetween( const optVertex_t *p1, const optVertex_t *v1, const optVertex_t *v2 ) { + idVec3 d1, d2; + float d; + + d1 = p1->pv - v1->pv; + d2 = p1->pv - v2->pv; + d = d1 * d2; + if ( d < 0 ) { + return true; + } + return false; +} + + +/* +==================== +EdgeIntersection + +Creates a new optVertex_t where the line segments cross. +This should only be called if PointsStraddleLine returned true + +Will return NULL if the lines are colinear +==================== +*/ +static optVertex_t *EdgeIntersection( const optVertex_t *p1, const optVertex_t *p2, + const optVertex_t *l1, const optVertex_t *l2, optimizeGroup_t *opt ) { + float f; + idDrawVert *v; + idVec3 dir1, dir2, cross1, cross2; + + dir1 = p1->pv - l1->pv; + dir2 = p1->pv - l2->pv; + cross1 = dir1.Cross( dir2 ); + + dir1 = p2->pv - l1->pv; + dir2 = p2->pv - l2->pv; + cross2 = dir1.Cross( dir2 ); + + if ( cross1[2] - cross2[2] == 0 ) { + return NULL; + } + + f = cross1[2] / ( cross1[2] - cross2[2] ); + + // FIXME: how are we freeing this, since it doesn't belong to a tri? + v = (idDrawVert *)Mem_Alloc( sizeof( *v ) ); + memset( v, 0, sizeof( *v ) ); + + v->xyz = p1->v.xyz * ( 1.0 - f ) + p2->v.xyz * f; + v->normal = p1->v.normal * ( 1.0 - f ) + p2->v.normal * f; + v->normal.Normalize(); + v->st[0] = p1->v.st[0] * ( 1.0 - f ) + p2->v.st[0] * f; + v->st[1] = p1->v.st[1] * ( 1.0 - f ) + p2->v.st[1] * f; + + return FindOptVertex( v, opt ); +} + + +/* +==================== +PointsStraddleLine + +Colinear is considdered crossing. +==================== +*/ +static bool PointsStraddleLine( optVertex_t *p1, optVertex_t *p2, optVertex_t *l1, optVertex_t *l2 ) { + bool t1, t2; + + t1 = IsTriangleDegenerate( l1, l2, p1 ); + t2 = IsTriangleDegenerate( l1, l2, p2 ); + if ( t1 && t2 ) { + // colinear case + float s1, s2, s3, s4; + bool positive, negative; + + s1 = ( p1->pv - l1->pv ) * ( l2->pv - l1->pv ); + s2 = ( p2->pv - l1->pv ) * ( l2->pv - l1->pv ); + s3 = ( p1->pv - l2->pv ) * ( l2->pv - l1->pv ); + s4 = ( p2->pv - l2->pv ) * ( l2->pv - l1->pv ); + + if ( s1 > 0 || s2 > 0 || s3 > 0 || s4 > 0 ) { + positive = true; + } else { + positive = false; + } + if ( s1 < 0 || s2 < 0 || s3 < 0 || s4 < 0 ) { + negative = true; + } else { + negative = false; + } + + if ( positive && negative ) { + return true; + } + return false; + } else if ( p1 != l1 && p1 != l2 && p2 != l1 && p2 != l2 ) { + // no shared verts + t1 = IsTriangleValid( l1, l2, p1 ); + t2 = IsTriangleValid( l1, l2, p2 ); + if ( t1 && t2 ) { + return false; + } + + t1 = IsTriangleValid( l1, p1, l2 ); + t2 = IsTriangleValid( l1, p2, l2 ); + if ( t1 && t2 ) { + return false; + } + + return true; + } else { + // a shared vert, not colinear, so not crossing + return false; + } +} + + +/* +==================== +EdgesCross +==================== +*/ +static bool EdgesCross( optVertex_t *a1, optVertex_t *a2, optVertex_t *b1, optVertex_t *b2 ) { + // if both verts match, consider it to be crossed + if ( a1 == b1 && a2 == b2 ) { + return true; + } + if ( a1 == b2 && a2 == b1 ) { + return true; + } + // if only one vert matches, it might still be colinear, which + // would be considered crossing + + // if both lines' verts are on opposite sides of the other + // line, it is crossed + if ( !PointsStraddleLine( a1, a2, b1, b2 ) ) { + return false; + } + if ( !PointsStraddleLine( b1, b2, a1, a2 ) ) { + return false; + } + + return true; +} + +/* +==================== +TryAddNewEdge + +==================== +*/ +static bool TryAddNewEdge( optVertex_t *v1, optVertex_t *v2, optIsland_t *island ) { + optEdge_t *e; + + // if the new edge crosses any other edges, don't add it + for ( e = island->edges ; e ; e = e->islandLink ) { + if ( EdgesCross( e->v1, e->v2, v1, v2 ) ) { + return false; + } + } + + if ( dmapGlobals.drawflag ) { + qglBegin( GL_LINES ); + qglColor3f( 0, ( 128 + orandom.RandomInt( 127 ) )/ 255.0, 0 ); + qglVertex3fv( v1->pv.ToFloatPtr() ); + qglVertex3fv( v2->pv.ToFloatPtr() ); + qglEnd(); + qglFlush(); + } + // add it + e = AllocEdge(); + + e->islandLink = island->edges; + island->edges = e; + e->v1 = v1; + e->v2 = v2; + + e->created = true; + + // link the edge to its verts + LinkEdge( e ); + + return true; +} + +typedef struct { + optVertex_t *v1, *v2; + float length; +} edgeLength_t; + + +static int LengthSort( const void *a, const void *b ) { + const edgeLength_t *ea, *eb; + + ea = (const edgeLength_t *)a; + eb = (const edgeLength_t *)b; + if ( ea->length < eb->length ) { + return -1; + } + if ( ea->length > eb->length ) { + return 1; + } + return 0; +} + +/* +================== +AddInteriorEdges + +Add all possible edges between the verts +================== +*/ +static void AddInteriorEdges( optIsland_t *island ) { + int c_addedEdges; + optVertex_t *vert, *vert2; + int c_verts; + edgeLength_t *lengths; + int numLengths; + int i; + + DrawVerts( island ); + + // count the verts + c_verts = 0; + for ( vert = island->verts ; vert ; vert = vert->islandLink ) { + if ( !vert->edges ) { + continue; + } + c_verts++; + } + + // allocate space for all the lengths + lengths = (edgeLength_t *)Mem_Alloc( sizeof( *lengths ) * c_verts * c_verts / 2 ); + numLengths = 0; + for ( vert = island->verts ; vert ; vert = vert->islandLink ) { + if ( !vert->edges ) { + continue; + } + for ( vert2 = vert->islandLink ; vert2 ; vert2 = vert2->islandLink ) { + idVec3 dir; + + if ( !vert2->edges ) { + continue; + } + lengths[numLengths].v1 = vert; + lengths[numLengths].v2 = vert2; + dir = ( vert->pv - vert2->pv ) ; + lengths[numLengths].length = dir.Length(); + numLengths++; + } + } + + + // sort by length, shortest first + qsort( lengths, numLengths, sizeof( lengths[0] ), LengthSort ); + + // try to create them in that order + c_addedEdges = 0; + for ( i = 0 ; i < numLengths ; i++ ) { + if ( TryAddNewEdge( lengths[i].v1, lengths[i].v2, island ) ) { + c_addedEdges++; + } + } + + if ( dmapGlobals.verbose ) { + common->Printf( "%6i tested segments\n", numLengths ); + common->Printf( "%6i added interior edges\n", c_addedEdges ); + } + + Mem_Free( lengths ); +} + + + +//================================================================== + +/* +==================== +RemoveIfColinear + +==================== +*/ +#define COLINEAR_EPSILON 0.1 +static void RemoveIfColinear( optVertex_t *ov, optIsland_t *island ) { + optEdge_t *e, *e1, *e2; + optVertex_t *v1, *v2, *v3; + idVec3 dir1, dir2; + float len, dist; + idVec3 point; + idVec3 offset; + float off; + + v2 = ov; + + // we must find exactly two edges before testing for colinear + e1 = NULL; + e2 = NULL; + for ( e = ov->edges ; e ; ) { + if ( !e1 ) { + e1 = e; + } else if ( !e2 ) { + e2 = e; + } else { + return; // can't remove a vertex with three edges + } + if ( e->v1 == v2 ) { + e = e->v1link; + } else if ( e->v2 == v2 ) { + e = e->v2link; + } else { + common->Error( "RemoveIfColinear: mislinked edge" ); + } + } + + // can't remove if no edges + if ( !e1 ) { + return; + } + + if ( !e2 ) { + // this may still happen legally when a tiny triangle is + // the only thing in a group + common->Printf( "WARNING: vertex with only one edge\n" ); + return; + } + + if ( e1->v1 == v2 ) { + v1 = e1->v2; + } else if ( e1->v2 == v2 ) { + v1 = e1->v1; + } else { + common->Error( "RemoveIfColinear: mislinked edge" ); + } + if ( e2->v1 == v2 ) { + v3 = e2->v2; + } else if ( e2->v2 == v2 ) { + v3 = e2->v1; + } else { + common->Error( "RemoveIfColinear: mislinked edge" ); + } + + if ( v1 == v3 ) { + common->Error( "RemoveIfColinear: mislinked edge" ); + } + + // they must point in opposite directions + dist = ( v3->pv - v2->pv ) * ( v1->pv - v2->pv ); + if ( dist >= 0 ) { + return; + } + + // see if they are colinear + VectorSubtract( v3->v.xyz, v1->v.xyz, dir1 ); + len = dir1.Normalize(); + VectorSubtract( v2->v.xyz, v1->v.xyz, dir2 ); + dist = DotProduct( dir2, dir1 ); + VectorMA( v1->v.xyz, dist, dir1, point ); + VectorSubtract( point, v2->v.xyz, offset ); + off = offset.Length(); + + if ( off > COLINEAR_EPSILON ) { + return; + } + + if ( dmapGlobals.drawflag ) { + qglBegin( GL_LINES ); + qglColor3f( 1, 1, 0 ); + qglVertex3fv( v1->pv.ToFloatPtr() ); + qglVertex3fv( v2->pv.ToFloatPtr() ); + qglEnd(); + qglFlush(); + qglBegin( GL_LINES ); + qglColor3f( 0, 1, 1 ); + qglVertex3fv( v2->pv.ToFloatPtr() ); + qglVertex3fv( v3->pv.ToFloatPtr() ); + qglEnd(); + qglFlush(); + } + + // replace the two edges with a single edge + UnlinkEdge( e1, island ); + UnlinkEdge( e2, island ); + + // v2 should have no edges now + if ( v2->edges ) { + common->Error( "RemoveIfColinear: didn't remove properly" ); + } + + + // if there is an existing edge that already + // has these exact verts, we have just collapsed a + // sliver triangle out of existance, and all the edges + // can be removed + for ( e = island->edges ; e ; e = e->islandLink ) { + if ( ( e->v1 == v1 && e->v2 == v3 ) + || ( e->v1 == v3 && e->v2 == v1 ) ) { + UnlinkEdge( e, island ); + RemoveIfColinear( v1, island ); + RemoveIfColinear( v3, island ); + return; + } + } + + // if we can't add the combined edge, link + // the originals back in + if ( !TryAddNewEdge( v1, v3, island ) ) { + e1->islandLink = island->edges; + island->edges = e1; + LinkEdge( e1 ); + + e2->islandLink = island->edges; + island->edges = e2; + LinkEdge( e2 ); + return; + } + + // recursively try to combine both verts now, + // because things may have changed since the last combine test + RemoveIfColinear( v1, island ); + RemoveIfColinear( v3, island ); +} + +/* +==================== +CombineColinearEdges +==================== +*/ +static void CombineColinearEdges( optIsland_t *island ) { + int c_edges; + optVertex_t *ov; + optEdge_t *e; + + c_edges = 0; + for ( e = island->edges ; e ; e = e->islandLink ) { + c_edges++; + } + if ( dmapGlobals.verbose ) { + common->Printf( "%6i original exterior edges\n", c_edges ); + } + + for ( ov = island->verts ; ov ; ov = ov->islandLink ) { + RemoveIfColinear( ov, island ); + } + + c_edges = 0; + for ( e = island->edges ; e ; e = e->islandLink ) { + c_edges++; + } + if ( dmapGlobals.verbose ) { + common->Printf( "%6i optimized exterior edges\n", c_edges ); + } +} + + +//================================================================== + +/* +=================== +FreeOptTriangles + +=================== +*/ +static void FreeOptTriangles( optIsland_t *island ) { + optTri_t *opt, *next; + + for ( opt = island->tris ; opt ; opt = next ) { + next = opt->next; + Mem_Free( opt ); + } + + island->tris = NULL; +} + + +/* +================= +IsTriangleValid + +empty area will be considered invalid. +Due to some truly aweful epsilon issues, a triangle can switch between +valid and invalid depending on which order you look at the verts, so +consider it invalid if any one of the possibilities is invalid. +================= +*/ +static bool IsTriangleValid( const optVertex_t *v1, const optVertex_t *v2, const optVertex_t *v3 ) { + idVec3 d1, d2, normal; + + d1 = v2->pv - v1->pv; + d2 = v3->pv - v1->pv; + normal = d1.Cross( d2 ); + if ( normal[2] <= 0 ) { + return false; + } + + d1 = v3->pv - v2->pv; + d2 = v1->pv - v2->pv; + normal = d1.Cross( d2 ); + if ( normal[2] <= 0 ) { + return false; + } + + d1 = v1->pv - v3->pv; + d2 = v2->pv - v3->pv; + normal = d1.Cross( d2 ); + if ( normal[2] <= 0 ) { + return false; + } + + return true; +} + + +/* +================= +IsTriangleDegenerate + +Returns false if it is either front or back facing +================= +*/ +static bool IsTriangleDegenerate( const optVertex_t *v1, const optVertex_t *v2, const optVertex_t *v3 ) { +#if 1 + idVec3 d1, d2, normal; + + d1 = v2->pv - v1->pv; + d2 = v3->pv - v1->pv; + normal = d1.Cross( d2 ); + if ( normal[2] == 0 ) { + return true; + } + return false; +#else + return (bool)!IsTriangleValid( v1, v2, v3 ); +#endif +} + + +/* +================== +PointInTri + +Tests if a 2D point is inside an original triangle +================== +*/ +static bool PointInTri( const idVec3 &p, const mapTri_t *tri, optIsland_t *island ) { + idVec3 d1, d2, normal; + + // the normal[2] == 0 case is not uncommon when a square is triangulated in + // the opposite manner to the original + + d1 = tri->optVert[0]->pv - p; + d2 = tri->optVert[1]->pv - p; + normal = d1.Cross( d2 ); + if ( normal[2] < 0 ) { + return false; + } + + d1 = tri->optVert[1]->pv - p; + d2 = tri->optVert[2]->pv - p; + normal = d1.Cross( d2 ); + if ( normal[2] < 0 ) { + return false; + } + + d1 = tri->optVert[2]->pv - p; + d2 = tri->optVert[0]->pv - p; + normal = d1.Cross( d2 ); + if ( normal[2] < 0 ) { + return false; + } + + return true; +} + + +/* +==================== +LinkTriToEdge + +==================== +*/ +static void LinkTriToEdge( optTri_t *optTri, optEdge_t *edge ) { + if ( ( edge->v1 == optTri->v[0] && edge->v2 == optTri->v[1] ) + || ( edge->v1 == optTri->v[1] && edge->v2 == optTri->v[2] ) + || ( edge->v1 == optTri->v[2] && edge->v2 == optTri->v[0] ) ) { + if ( edge->backTri ) { + common->Printf( "Warning: LinkTriToEdge: already in use\n" ); + return; + } + edge->backTri = optTri; + return; + } + if ( ( edge->v1 == optTri->v[1] && edge->v2 == optTri->v[0] ) + || ( edge->v1 == optTri->v[2] && edge->v2 == optTri->v[1] ) + || ( edge->v1 == optTri->v[0] && edge->v2 == optTri->v[2] ) ) { + if ( edge->frontTri ) { + common->Printf( "Warning: LinkTriToEdge: already in use\n" ); + return; + } + edge->frontTri = optTri; + return; + } + common->Error( "LinkTriToEdge: edge not found on tri" ); +} + +/* +=============== +CreateOptTri +=============== +*/ +static void CreateOptTri( optVertex_t *first, optEdge_t *e1, optEdge_t *e2, optIsland_t *island ) { + optEdge_t *opposite; + optVertex_t *second, *third; + optTri_t *optTri; + mapTri_t *tri; + + if ( e1->v1 == first ) { + second = e1->v2; + } else if ( e1->v2 == first ) { + second = e1->v1; + } else { + common->Error( "CreateOptTri: mislinked edge" ); + } + + if ( e2->v1 == first ) { + third = e2->v2; + } else if ( e2->v2 == first ) { + third = e2->v1; + } else { + common->Error( "CreateOptTri: mislinked edge" ); + } + + if ( !IsTriangleValid( first, second, third ) ) { + common->Error( "CreateOptTri: invalid" ); + } + +//DrawEdges( island ); + + // identify the third edge + if ( dmapGlobals.drawflag ) { + qglColor3f(1,1,0); + qglBegin( GL_LINES ); + qglVertex3fv( e1->v1->pv.ToFloatPtr() ); + qglVertex3fv( e1->v2->pv.ToFloatPtr() ); + qglEnd(); + qglFlush(); + qglColor3f(0,1,1); + qglBegin( GL_LINES ); + qglVertex3fv( e2->v1->pv.ToFloatPtr() ); + qglVertex3fv( e2->v2->pv.ToFloatPtr() ); + qglEnd(); + qglFlush(); + } + + for ( opposite = second->edges ; opposite ; ) { + if ( opposite != e1 && ( opposite->v1 == third || opposite->v2 == third ) ) { + break; + } + if ( opposite->v1 == second ) { + opposite = opposite->v1link; + } else if ( opposite->v2 == second ) { + opposite = opposite->v2link; + } else { + common->Error( "BuildOptTriangles: mislinked edge" ); + } + } + + if ( !opposite ) { + common->Printf( "Warning: BuildOptTriangles: couldn't locate opposite\n" ); + return; + } + + if ( dmapGlobals.drawflag ) { + qglColor3f(1,0,1); + qglBegin( GL_LINES ); + qglVertex3fv( opposite->v1->pv.ToFloatPtr() ); + qglVertex3fv( opposite->v2->pv.ToFloatPtr() ); + qglEnd(); + qglFlush(); + } + + // create new triangle + optTri = (optTri_t *)Mem_Alloc( sizeof( *optTri ) ); + optTri->v[0] = first; + optTri->v[1] = second; + optTri->v[2] = third; + optTri->midpoint = ( optTri->v[0]->pv + optTri->v[1]->pv + optTri->v[2]->pv ) * ( 1.0f / 3.0f ); + optTri->next = island->tris; + island->tris = optTri; + + if ( dmapGlobals.drawflag ) { + qglColor3f( 1, 1, 1 ); + qglPointSize( 4 ); + qglBegin( GL_POINTS ); + qglVertex3fv( optTri->midpoint.ToFloatPtr() ); + qglEnd(); + qglFlush(); + } + + // find the midpoint, and scan through all the original triangles to + // see if it is inside any of them + for ( tri = island->group->triList ; tri ; tri = tri->next ) { + if ( PointInTri( optTri->midpoint, tri, island ) ) { + break; + } + } + if ( tri ) { + optTri->filled = true; + } else { + optTri->filled = false; + } + if ( dmapGlobals.drawflag ) { + if ( optTri->filled ) { + qglColor3f( ( 128 + orandom.RandomInt( 127 ) )/ 255.0, 0, 0 ); + } else { + qglColor3f( 0, ( 128 + orandom.RandomInt( 127 ) ) / 255.0, 0 ); + } + qglBegin( GL_TRIANGLES ); + qglVertex3fv( optTri->v[0]->pv.ToFloatPtr() ); + qglVertex3fv( optTri->v[1]->pv.ToFloatPtr() ); + qglVertex3fv( optTri->v[2]->pv.ToFloatPtr() ); + qglEnd(); + qglColor3f( 1, 1, 1 ); + qglBegin( GL_LINE_LOOP ); + qglVertex3fv( optTri->v[0]->pv.ToFloatPtr() ); + qglVertex3fv( optTri->v[1]->pv.ToFloatPtr() ); + qglVertex3fv( optTri->v[2]->pv.ToFloatPtr() ); + qglEnd(); + qglFlush(); + } + + // link the triangle to it's edges + LinkTriToEdge( optTri, e1 ); + LinkTriToEdge( optTri, e2 ); + LinkTriToEdge( optTri, opposite ); +} + +// debugging tool +static void ReportNearbyVertexes( const optVertex_t *v, const optIsland_t *island ) { + const optVertex_t *ov; + float d; + idVec3 vec; + + common->Printf( "verts near 0x%p (%f, %f)\n", v, v->pv[0], v->pv[1] ); + for ( ov = island->verts ; ov ; ov = ov->islandLink ) { + if ( ov == v ) { + continue; + } + + vec = ov->pv - v->pv; + + d = vec.Length(); + if ( d < 1 ) { + common->Printf( "0x%p = (%f, %f)\n", ov, ov->pv[0], ov->pv[1] ); + } + } +} + +/* +==================== +BuildOptTriangles + +Generate a new list of triangles from the optEdeges +==================== +*/ +static void BuildOptTriangles( optIsland_t *island ) { + optVertex_t *ov, *second, *third, *middle; + optEdge_t *e1, *e1Next, *e2, *e2Next, *check, *checkNext; + + // free them + FreeOptTriangles( island ); + + // clear the vertex emitted flags + for ( ov = island->verts ; ov ; ov = ov->islandLink ) { + ov->emited = false; + } + + // clear the edge triangle links + for ( check = island->edges ; check ; check = check->islandLink ) { + check->frontTri = check->backTri = NULL; + } + + // check all possible triangle made up out of the + // edges coming off the vertex + for ( ov = island->verts ; ov ; ov = ov->islandLink ) { + if ( !ov->edges ) { + continue; + } + +#if 0 +if ( dmapGlobals.drawflag && ov == (optVertex_t *)0x1845a60 ) { +for ( e1 = ov->edges ; e1 ; e1 = e1Next ) { + qglBegin( GL_LINES ); + qglColor3f( 0,1,0 ); + qglVertex3fv( e1->v1->pv.ToFloatPtr() ); + qglVertex3fv( e1->v2->pv.ToFloatPtr() ); + qglEnd(); + qglFlush(); + if ( e1->v1 == ov ) { + e1Next = e1->v1link; + } else if ( e1->v2 == ov ) { + e1Next = e1->v2link; + } +} +} +#endif + for ( e1 = ov->edges ; e1 ; e1 = e1Next ) { + if ( e1->v1 == ov ) { + second = e1->v2; + e1Next = e1->v1link; + } else if ( e1->v2 == ov ) { + second = e1->v1; + e1Next = e1->v2link; + } else { + common->Error( "BuildOptTriangles: mislinked edge" ); + } + + // if the vertex has already been used, it can't be used again + if ( second->emited ) { + continue; + } + + for ( e2 = ov->edges ; e2 ; e2 = e2Next ) { + if ( e2->v1 == ov ) { + third = e2->v2; + e2Next = e2->v1link; + } else if ( e2->v2 == ov ) { + third = e2->v1; + e2Next = e2->v2link; + } else { + common->Error( "BuildOptTriangles: mislinked edge" ); + } + if ( e2 == e1 ) { + continue; + } + + // if the vertex has already been used, it can't be used again + if ( third->emited ) { + continue; + } + + // if the triangle is backwards or degenerate, don't use it + if ( !IsTriangleValid( ov, second, third ) ) { + continue; + } + + // see if any other edge bisects these two, which means + // this triangle shouldn't be used + for ( check = ov->edges ; check ; check = checkNext ) { + if ( check->v1 == ov ) { + middle = check->v2; + checkNext = check->v1link; + } else if ( check->v2 == ov ) { + middle = check->v1; + checkNext = check->v2link; + } else { + common->Error( "BuildOptTriangles: mislinked edge" ); + } + + if ( check == e1 || check == e2 ) { + continue; + } + + if ( IsTriangleValid( ov, second, middle ) + && IsTriangleValid( ov, middle, third ) ) { + break; // should use the subdivided ones + } + } + + if ( check ) { + continue; // don't use it + } + + // the triangle is valid + CreateOptTri( ov, e1, e2, island ); + } + } + + // later vertexes will not emit triangles that use an + // edge that this vert has already used + ov->emited = true; + } +} + + + +/* +==================== +RegenerateTriangles + +Add new triangles to the group's regeneratedTris +==================== +*/ +static void RegenerateTriangles( optIsland_t *island ) { + optTri_t *optTri; + mapTri_t *tri; + int c_out; + + c_out = 0; + + for ( optTri = island->tris ; optTri ; optTri = optTri->next ) { + if ( !optTri->filled ) { + continue; + } + + // create a new mapTri_t + tri = AllocTri(); + + tri->material = island->group->material; + tri->mergeGroup = island->group->mergeGroup; + + tri->v[0] = optTri->v[0]->v; + tri->v[1] = optTri->v[1]->v; + tri->v[2] = optTri->v[2]->v; + + idPlane plane; + PlaneForTri( tri, plane ); + if ( plane.Normal() * dmapGlobals.mapPlanes[ island->group->planeNum ].Normal() <= 0 ) { + // this can happen reasonably when a triangle is nearly degenerate in + // optimization planar space, and winds up being degenerate in 3D space + common->Printf( "WARNING: backwards triangle generated!\n" ); + // discard it + FreeTri( tri ); + continue; + } + + c_out++; + tri->next = island->group->regeneratedTris; + island->group->regeneratedTris = tri; + } + + FreeOptTriangles( island ); + + if ( dmapGlobals.verbose ) { + common->Printf( "%6i tris out\n", c_out ); + } +} + +//=========================================================================== + +/* +==================== +RemoveInteriorEdges + +Edges that have triangles of the same type (filled / empty) +on both sides will be removed +==================== +*/ +static void RemoveInteriorEdges( optIsland_t *island ) { + int c_interiorEdges; + int c_exteriorEdges; + optEdge_t *e, *next; + bool front, back; + + c_exteriorEdges = 0; + c_interiorEdges = 0; + for ( e = island->edges ; e ; e = next ) { + // we might remove the edge, so get the next link now + next = e->islandLink; + + if ( !e->frontTri ) { + front = false; + } else { + front = e->frontTri->filled; + } + if ( !e->backTri ) { + back = false; + } else { + back = e->backTri->filled; + } + + if ( front == back ) { + // free the edge + UnlinkEdge( e, island ); + c_interiorEdges++; + continue; + } + + c_exteriorEdges++; + } + + if ( dmapGlobals.verbose ) { + common->Printf( "%6i original interior edges\n", c_interiorEdges ); + common->Printf( "%6i original exterior edges\n", c_exteriorEdges ); + } +} + +//================================================================================== + +typedef struct { + optVertex_t *v1, *v2; +} originalEdges_t; + +/* +================= +AddEdgeIfNotAlready +================= +*/ +void AddEdgeIfNotAlready( optVertex_t *v1, optVertex_t *v2 ) { + optEdge_t *e; + + // make sure that there isn't an identical edge already added + for ( e = v1->edges ; e ; ) { + if ( ( e->v1 == v1 && e->v2 == v2 ) || ( e->v1 == v2 && e->v2 == v1 ) ) { + return; // already added + } + if ( e->v1 == v1 ) { + e = e->v1link; + } else if ( e->v2 == v1 ) { + e = e->v2link; + } else { + common->Error( "SplitEdgeByList: bad edge link" ); + } + } + + // this edge is a keeper + e = AllocEdge(); + e->v1 = v1; + e->v2 = v2; + + e->islandLink = NULL; + + // link the edge to its verts + LinkEdge( e ); +} + + + +/* +================= +DrawOriginalEdges +================= +*/ +static void DrawOriginalEdges( int numOriginalEdges, originalEdges_t *originalEdges ) { + int i; + + if ( !dmapGlobals.drawflag ) { + return; + } + Draw_ClearWindow(); + + qglBegin( GL_LINES ); + for ( i = 0 ; i < numOriginalEdges ; i++ ) { + qglColor3f( 1, 0, 0 ); + qglVertex3fv( originalEdges[i].v1->pv.ToFloatPtr() ); + qglColor3f( 0, 0, 0 ); + qglVertex3fv( originalEdges[i].v2->pv.ToFloatPtr() ); + } + qglEnd(); + qglFlush(); +} + + +typedef struct edgeCrossing_s { + struct edgeCrossing_s *next; + optVertex_t *ov; +} edgeCrossing_t; + +static originalEdges_t *originalEdges; +static int numOriginalEdges; + +/* +================= +AddOriginalTriangle +================= +*/ +static void AddOriginalTriangle( optVertex_t *v[3] ) { + optVertex_t *v1, *v2; + + // if this triangle is backwards (possible with epsilon issues) + // ignore it completely + if ( !IsTriangleValid( v[0], v[1], v[2] ) ) { + common->Printf( "WARNING: backwards triangle in input!\n" ); + return; + } + + for ( int i = 0 ; i < 3 ; i++ ) { + v1 = v[i]; + v2 = v[(i+1)%3]; + + if ( v1 == v2 ) { + // this probably shouldn't happen, because the + // tri would be degenerate + continue; + } + int j; + // see if there is an existing one + for ( j = 0 ; j < numOriginalEdges ; j++ ) { + if ( originalEdges[j].v1 == v1 && originalEdges[j].v2 == v2 ) { + break; + } + if ( originalEdges[j].v2 == v1 && originalEdges[j].v1 == v2 ) { + break; + } + } + + if ( j == numOriginalEdges ) { + // add it + originalEdges[j].v1 = v1; + originalEdges[j].v2 = v2; + numOriginalEdges++; + } + } +} + +/* +================= +AddOriginalEdges +================= +*/ +static void AddOriginalEdges( optimizeGroup_t *opt ) { + mapTri_t *tri; + optVertex_t *v[3]; + int numTris; + + if ( dmapGlobals.verbose ) { + common->Printf( "----\n" ); + common->Printf( "%6i original tris\n", CountTriList( opt->triList ) ); + } + + optBounds.Clear(); + + // allocate space for max possible edges + numTris = CountTriList( opt->triList ); + originalEdges = (originalEdges_t *)Mem_Alloc( numTris * 3 * sizeof( *originalEdges ) ); + numOriginalEdges = 0; + + // add all unique triangle edges + numOptVerts = 0; + numOptEdges = 0; + for ( tri = opt->triList ; tri ; tri = tri->next ) { + v[0] = tri->optVert[0] = FindOptVertex( &tri->v[0], opt ); + v[1] = tri->optVert[1] = FindOptVertex( &tri->v[1], opt ); + v[2] = tri->optVert[2] = FindOptVertex( &tri->v[2], opt ); + + AddOriginalTriangle( v ); + } +} + +/* +===================== +SplitOriginalEdgesAtCrossings +===================== +*/ +void SplitOriginalEdgesAtCrossings( optimizeGroup_t *opt ) { + int i, j, k, l; + int numOriginalVerts; + edgeCrossing_t **crossings; + + numOriginalVerts = numOptVerts; + // now split any crossing edges and create optEdges + // linked to the vertexes + + // debug drawing bounds + dmapGlobals.drawBounds = optBounds; + + dmapGlobals.drawBounds[0][0] -= 2; + dmapGlobals.drawBounds[0][1] -= 2; + dmapGlobals.drawBounds[1][0] += 2; + dmapGlobals.drawBounds[1][1] += 2; + + // generate crossing points between all the original edges + crossings = (edgeCrossing_t **)Mem_ClearedAlloc( numOriginalEdges * sizeof( *crossings ) ); + + for ( i = 0 ; i < numOriginalEdges ; i++ ) { + if ( dmapGlobals.drawflag ) { + DrawOriginalEdges( numOriginalEdges, originalEdges ); + qglBegin( GL_LINES ); + qglColor3f( 0, 1, 0 ); + qglVertex3fv( originalEdges[i].v1->pv.ToFloatPtr() ); + qglColor3f( 0, 0, 1 ); + qglVertex3fv( originalEdges[i].v2->pv.ToFloatPtr() ); + qglEnd(); + qglFlush(); + } + for ( j = i+1 ; j < numOriginalEdges ; j++ ) { + optVertex_t *v1, *v2, *v3, *v4; + optVertex_t *newVert; + edgeCrossing_t *cross; + + v1 = originalEdges[i].v1; + v2 = originalEdges[i].v2; + v3 = originalEdges[j].v1; + v4 = originalEdges[j].v2; + + if ( !EdgesCross( v1, v2, v3, v4 ) ) { + continue; + } + + // this is the only point in optimization where + // completely new points are created, and it only + // happens if there is overlapping coplanar + // geometry in the source triangles + newVert = EdgeIntersection( v1, v2, v3, v4, opt ); + + if ( !newVert ) { +//common->Printf( "lines %i (%i to %i) and %i (%i to %i) are colinear\n", i, v1 - optVerts, v2 - optVerts, +// j, v3 - optVerts, v4 - optVerts ); // !@# + // colinear, so add both verts of each edge to opposite + if ( VertexBetween( v3, v1, v2 ) ) { + cross = (edgeCrossing_t *)Mem_ClearedAlloc( sizeof( *cross ) ); + cross->ov = v3; + cross->next = crossings[i]; + crossings[i] = cross; + } + + if ( VertexBetween( v4, v1, v2 ) ) { + cross = (edgeCrossing_t *)Mem_ClearedAlloc( sizeof( *cross ) ); + cross->ov = v4; + cross->next = crossings[i]; + crossings[i] = cross; + } + + if ( VertexBetween( v1, v3, v4 ) ) { + cross = (edgeCrossing_t *)Mem_ClearedAlloc( sizeof( *cross ) ); + cross->ov = v1; + cross->next = crossings[j]; + crossings[j] = cross; + } + + if ( VertexBetween( v2, v3, v4 ) ) { + cross = (edgeCrossing_t *)Mem_ClearedAlloc( sizeof( *cross ) ); + cross->ov = v2; + cross->next = crossings[j]; + crossings[j] = cross; + } + + continue; + } +#if 0 +if ( newVert && newVert != v1 && newVert != v2 && newVert != v3 && newVert != v4 ) { +common->Printf( "lines %i (%i to %i) and %i (%i to %i) cross at new point %i\n", i, v1 - optVerts, v2 - optVerts, + j, v3 - optVerts, v4 - optVerts, newVert - optVerts ); +} else if ( newVert ) { +common->Printf( "lines %i (%i to %i) and %i (%i to %i) intersect at old point %i\n", i, v1 - optVerts, v2 - optVerts, + j, v3 - optVerts, v4 - optVerts, newVert - optVerts ); +} +#endif + if ( newVert != v1 && newVert != v2 ) { + cross = (edgeCrossing_t *)Mem_ClearedAlloc( sizeof( *cross ) ); + cross->ov = newVert; + cross->next = crossings[i]; + crossings[i] = cross; + } + + if ( newVert != v3 && newVert != v4 ) { + cross = (edgeCrossing_t *)Mem_ClearedAlloc( sizeof( *cross ) ); + cross->ov = newVert; + cross->next = crossings[j]; + crossings[j] = cross; + } + + } + } + + + // now split each edge by its crossing points + // colinear edges will have duplicated edges added, but it won't hurt anything + for ( i = 0 ; i < numOriginalEdges ; i++ ) { + edgeCrossing_t *cross, *nextCross; + int numCross; + optVertex_t **sorted; + + numCross = 0; + for ( cross = crossings[i] ; cross ; cross = cross->next ) { + numCross++; + } + numCross += 2; // account for originals + sorted = (optVertex_t **)Mem_Alloc( numCross * sizeof( *sorted ) ); + sorted[0] = originalEdges[i].v1; + sorted[1] = originalEdges[i].v2; + j = 2; + for ( cross = crossings[i] ; cross ; cross = nextCross ) { + nextCross = cross->next; + sorted[j] = cross->ov; + Mem_Free( cross ); + j++; + } + + // add all possible fragment combinations that aren't divided + // by another point + for ( j = 0 ; j < numCross ; j++ ) { + for ( k = j+1 ; k < numCross ; k++ ) { + for ( l = 0 ; l < numCross ; l++ ) { + if ( sorted[l] == sorted[j] || sorted[l] == sorted[k] ) { + continue; + } + if ( sorted[j] == sorted[k] ) { + continue; + } + if ( VertexBetween( sorted[l], sorted[j], sorted[k] ) ) { + break; + } + } + if ( l == numCross ) { +//common->Printf( "line %i fragment from point %i to %i\n", i, sorted[j] - optVerts, sorted[k] - optVerts ); + AddEdgeIfNotAlready( sorted[j], sorted[k] ); + } + } + } + + Mem_Free( sorted ); + } + + + Mem_Free( crossings ); + Mem_Free( originalEdges ); + + // check for duplicated edges + for ( i = 0 ; i < numOptEdges ; i++ ) { + for ( j = i+1 ; j < numOptEdges ; j++ ) { + if ( ( optEdges[i].v1 == optEdges[j].v1 && optEdges[i].v2 == optEdges[j].v2 ) + || ( optEdges[i].v1 == optEdges[j].v2 && optEdges[i].v2 == optEdges[j].v1 ) ) { + common->Printf( "duplicated optEdge\n" ); + } + } + } + + if ( dmapGlobals.verbose ) { + common->Printf( "%6i original edges\n", numOriginalEdges ); + common->Printf( "%6i edges after splits\n", numOptEdges ); + common->Printf( "%6i original vertexes\n", numOriginalVerts ); + common->Printf( "%6i vertexes after splits\n", numOptVerts ); + } +} + +//================================================================= + + +/* +=================== +CullUnusedVerts + +Unlink any verts with no edges, so they +won't be used in the retriangulation +=================== +*/ +static void CullUnusedVerts( optIsland_t *island ) { + optVertex_t **prev, *vert; + int c_keep, c_free; + optEdge_t *edge; + + c_keep = 0; + c_free = 0; + + for ( prev = &island->verts ; *prev ; ) { + vert = *prev; + + if ( !vert->edges ) { + // free it + *prev = vert->islandLink; + c_free++; + } else { + edge = vert->edges; + if ( ( edge->v1 == vert && !edge->v1link ) + || ( edge->v2 == vert && !edge->v2link ) ) { + // is is occasionally possible to get a vert + // with only a single edge when colinear optimizations + // crunch down a complex sliver + UnlinkEdge( edge, island ); + // free it + *prev = vert->islandLink; + c_free++; + } else { + prev = &vert->islandLink; + c_keep++; + } + } + } + + if ( dmapGlobals.verbose ) { + common->Printf( "%6i verts kept\n", c_keep ); + common->Printf( "%6i verts freed\n", c_free ); + } +} + + + +/* +==================== +OptimizeIsland + +At this point, all needed vertexes are already in the +list, including any that were added at crossing points. + +Interior and colinear vertexes will be removed, and +a new triangulation will be created. +==================== +*/ +static void OptimizeIsland( optIsland_t *island ) { + // add space-filling fake edges so we have a complete + // triangulation of a convex hull before optimization + AddInteriorEdges( island ); + DrawEdges( island ); + + // determine all the possible triangles, and decide if + // the are filled or empty + BuildOptTriangles( island ); + + // remove interior vertexes that have filled triangles + // between all their edges + RemoveInteriorEdges( island ); + DrawEdges( island ); + + ValidateEdgeCounts( island ); + + // remove vertexes that only have two colinear edges + CombineColinearEdges( island ); + CullUnusedVerts( island ); + DrawEdges( island ); + + // add new internal edges between the remaining exterior edges + // to give us a full triangulation again + AddInteriorEdges( island ); + DrawEdges( island ); + + // determine all the possible triangles, and decide if + // the are filled or empty + BuildOptTriangles( island ); + + // make mapTri_t out of the filled optTri_t + RegenerateTriangles( island ); +} + +/* +================ +AddVertexToIsland_r +================ +*/ +static void AddVertexToIsland_r( optVertex_t *vert, optIsland_t *island ) { + optEdge_t *e; + + // we can't just check islandLink, because the + // last vert will have a NULL + if ( vert->addedToIsland ) { + return; + } + vert->addedToIsland = true; + vert->islandLink = island->verts; + island->verts = vert; + + for ( e = vert->edges ; e ; ) { + if ( !e->addedToIsland ) { + e->addedToIsland = true; + + e->islandLink = island->edges; + island->edges = e; + } + + if ( e->v1 == vert ) { + AddVertexToIsland_r( e->v2, island ); + e = e->v1link; + continue; + } + if ( e->v2 == vert ) { + AddVertexToIsland_r( e->v1, island ); + e = e->v2link; + continue; + } + common->Error( "AddVertexToIsland_r: mislinked vert" ); + } + +} + +/* +==================== +SeparateIslands + +While the algorithm should theoretically handle any collection +of triangles, there are speed and stability benefits to making +it work on as small a list as possible, so separate disconnected +collections of edges and process separately. + +FIXME: we need to separate the source triangles before +doing this, because PointInSourceTris() can give a bad answer if +the source list has triangles not used in the optimization +==================== +*/ +static void SeparateIslands( optimizeGroup_t *opt ) { + int i; + optIsland_t island; + int numIslands; + + DrawAllEdges(); + + numIslands = 0; + for ( i = 0 ; i < numOptVerts ; i++ ) { + if ( optVerts[i].addedToIsland ) { + continue; + } + numIslands++; + memset( &island, 0, sizeof( island ) ); + island.group = opt; + AddVertexToIsland_r( &optVerts[i], &island ); + OptimizeIsland( &island ); + } + if ( dmapGlobals.verbose ) { + common->Printf( "%6i islands\n", numIslands ); + } +} + +static void DontSeparateIslands( optimizeGroup_t *opt ) { + int i; + optIsland_t island; + + DrawAllEdges(); + + memset( &island, 0, sizeof( island ) ); + island.group = opt; + + // link everything together + for ( i = 0 ; i < numOptVerts ; i++ ) { + optVerts[i].islandLink = island.verts; + island.verts = &optVerts[i]; + } + + for ( i = 0 ; i < numOptEdges ; i++ ) { + optEdges[i].islandLink = island.edges; + island.edges = &optEdges[i]; + } + + OptimizeIsland( &island ); +} + + +/* +==================== +PointInSourceTris + +This is a sloppy bounding box check +==================== +*/ +static bool PointInSourceTris( float x, float y, float z, optimizeGroup_t *opt ) { + mapTri_t *tri; + idBounds b; + idVec3 p; + + if ( !opt->material->IsDrawn() ) { + return false; + } + + p[0] = x; + p[1] = y; + p[2] = z; + for ( tri = opt->triList ; tri ; tri = tri->next ) { + b.Clear(); + b.AddPoint( tri->v[0].xyz ); + b.AddPoint( tri->v[1].xyz ); + b.AddPoint( tri->v[2].xyz ); + + if ( b.ContainsPoint( p ) ) { + return true; + } + } + return false; +} + +/* +==================== +OptimizeOptList +==================== +*/ +static void OptimizeOptList( optimizeGroup_t *opt ) { + optimizeGroup_t *oldNext; + + // fix the t junctions among this single list + // so we can match edges + // can we avoid doing this if colinear vertexes break edges? + oldNext = opt->nextGroup; + opt->nextGroup = NULL; + FixAreaGroupsTjunctions( opt ); + opt->nextGroup = oldNext; + + // create the 2D vectors + dmapGlobals.mapPlanes[opt->planeNum].Normal().NormalVectors( opt->axis[0], opt->axis[1] ); + + AddOriginalEdges( opt ); + SplitOriginalEdgesAtCrossings( opt ); + +#if 0 + // seperate any discontinuous areas for individual optimization + // to reduce the scope of the problem + SeparateIslands( opt ); +#else + DontSeparateIslands( opt ); +#endif + + // now free the hash verts + FreeTJunctionHash(); + + // free the original list and use the new one + FreeTriList( opt->triList ); + opt->triList = opt->regeneratedTris; + opt->regeneratedTris = NULL; +} + + +/* +================== +SetGroupTriPlaneNums + +Copies the group planeNum to every triangle in each group +================== +*/ +void SetGroupTriPlaneNums( optimizeGroup_t *groups ) { + mapTri_t *tri; + optimizeGroup_t *group; + + for ( group = groups ; group ; group = group->nextGroup ) { + for ( tri = group->triList ; tri ; tri = tri->next ) { + tri->planeNum = group->planeNum; + } + } +} + + +/* +=================== +OptimizeGroupList + +This will also fix tjunctions + +=================== +*/ +void OptimizeGroupList( optimizeGroup_t *groupList ) { + int c_in, c_edge, c_tjunc2; + optimizeGroup_t *group; + + if ( !groupList ) { + return; + } + + c_in = CountGroupListTris( groupList ); + + // optimize and remove colinear edges, which will + // re-introduce some t junctions + for ( group = groupList ; group ; group = group->nextGroup ) { + OptimizeOptList( group ); + } + c_edge = CountGroupListTris( groupList ); + + // fix t junctions again + FixAreaGroupsTjunctions( groupList ); + FreeTJunctionHash(); + c_tjunc2 = CountGroupListTris( groupList ); + + SetGroupTriPlaneNums( groupList ); + + common->Printf( "----- OptimizeAreaGroups Results -----\n" ); + common->Printf( "%6i tris in\n", c_in ); + common->Printf( "%6i tris after edge removal optimization\n", c_edge ); + common->Printf( "%6i tris after final t junction fixing\n", c_tjunc2 ); +} + + +/* +================== +OptimizeEntity +================== +*/ +void OptimizeEntity( uEntity_t *e ) { + int i; + + common->Printf( "----- OptimizeEntity -----\n" ); + for ( i = 0 ; i < e->numAreas ; i++ ) { + OptimizeGroupList( e->areas[i].groups ); + } +} diff --git a/src/tools/compilers/dmap/optimize_gcc.cpp b/src/tools/compilers/dmap/optimize_gcc.cpp new file mode 100644 index 0000000..8360b03 --- /dev/null +++ b/src/tools/compilers/dmap/optimize_gcc.cpp @@ -0,0 +1,84 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +/* +crazy gcc 3.3.5 optimization bug +happens even at -O1 +if you remove the 'return NULL;' after Error(), it only happens at -O3 / release +see dmap.gcc.zip test map and .proc outputs +*/ + +#include "../../../idlib/precompiled.h" +#pragma hdrstop + +#include "dmap.h" + +extern idBounds optBounds; + +#define MAX_OPT_VERTEXES 0x10000 +extern int numOptVerts; +extern optVertex_t optVerts[MAX_OPT_VERTEXES]; + +/* +================ +FindOptVertex +================ +*/ +optVertex_t *FindOptVertex( idDrawVert *v, optimizeGroup_t *opt ) { + int i; + float x, y; + optVertex_t *vert; + + // deal with everything strictly as 2D + x = v->xyz * opt->axis[0]; + y = v->xyz * opt->axis[1]; + + // should we match based on the t-junction fixing hash verts? + for ( i = 0 ; i < numOptVerts ; i++ ) { + if ( optVerts[i].pv[0] == x && optVerts[i].pv[1] == y ) { + return &optVerts[i]; + } + } + + if ( numOptVerts >= MAX_OPT_VERTEXES ) { + common->Error( "MAX_OPT_VERTEXES" ); + return NULL; + } + + numOptVerts++; + + vert = &optVerts[i]; + memset( vert, 0, sizeof( *vert ) ); + vert->v = *v; + vert->pv[0] = x; + vert->pv[1] = y; + vert->pv[2] = 0; + + optBounds.AddPoint( vert->pv ); + + return vert; +} diff --git a/src/tools/compilers/dmap/output.cpp b/src/tools/compilers/dmap/output.cpp new file mode 100644 index 0000000..ffc9606 --- /dev/null +++ b/src/tools/compilers/dmap/output.cpp @@ -0,0 +1,674 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../../idlib/precompiled.h" +#pragma hdrstop + +#include "dmap.h" + +//================================================================================= + + +#if 0 + +should we try and snap values very close to 0.5, 0.25, 0.125, etc? + + do we write out normals, or just a "smooth shade" flag? +resolved: normals. otherwise adjacent facet shaded surfaces get their + vertexes merged, and they would have to be split apart before drawing + + do we save out "wings" for shadow silhouette info? + + +#endif + +static idFile *procFile; + +#define AREANUM_DIFFERENT -2 +/* +============= +PruneNodes_r + +Any nodes that have all children with the same +area can be combined into a single leaf node + +Returns the area number of all children, or +AREANUM_DIFFERENT if not the same. +============= +*/ +int PruneNodes_r( node_t *node ) { + int a1, a2; + + if ( node->planenum == PLANENUM_LEAF ) { + return node->area; + } + + a1 = PruneNodes_r( node->children[0] ); + a2 = PruneNodes_r( node->children[1] ); + + if ( a1 != a2 || a1 == AREANUM_DIFFERENT ) { + return AREANUM_DIFFERENT; + } + + // free all the nodes below this point + FreeTreePortals_r( node->children[0] ); + FreeTreePortals_r( node->children[1] ); + FreeTree_r( node->children[0] ); + FreeTree_r( node->children[1] ); + + // change this node to a leaf + node->planenum = PLANENUM_LEAF; + node->area = a1; + + return a1; +} + +static void WriteFloat( idFile *f, float v ) +{ + if ( idMath::Fabs(v - idMath::Rint(v)) < 0.001 ) { + f->WriteFloatString( "%i ", (int)idMath::Rint(v) ); + } + else { + f->WriteFloatString( "%f ", v ); + } +} + +void Write1DMatrix( idFile *f, int x, float *m ) { + int i; + + f->WriteFloatString( "( " ); + + for ( i = 0; i < x; i++ ) { + WriteFloat( f, m[i] ); + } + + f->WriteFloatString( ") " ); +} + +static int CountUniqueShaders( optimizeGroup_t *groups ) { + optimizeGroup_t *a, *b; + int count; + + count = 0; + + for ( a = groups ; a ; a = a->nextGroup ) { + if ( !a->triList ) { // ignore groups with no tris + continue; + } + for ( b = groups ; b != a ; b = b->nextGroup ) { + if ( !b->triList ) { + continue; + } + if ( a->material != b->material ) { + continue; + } + if ( a->mergeGroup != b->mergeGroup ) { + continue; + } + break; + } + if ( a == b ) { + count++; + } + } + + return count; +} + + +/* +============== +MatchVert +============== +*/ +#define XYZ_EPSILON 0.01 +#define ST_EPSILON 0.001 +#define COSINE_EPSILON 0.999 + +static bool MatchVert( const idDrawVert *a, const idDrawVert *b ) { + if ( idMath::Fabs( a->xyz[0] - b->xyz[0] ) > XYZ_EPSILON ) { + return false; + } + if ( idMath::Fabs( a->xyz[1] - b->xyz[1] ) > XYZ_EPSILON ) { + return false; + } + if ( idMath::Fabs( a->xyz[2] - b->xyz[2] ) > XYZ_EPSILON ) { + return false; + } + if ( idMath::Fabs( a->st[0] - b->st[0] ) > ST_EPSILON ) { + return false; + } + if ( idMath::Fabs( a->st[1] - b->st[1] ) > ST_EPSILON ) { + return false; + } + + // if the normal is 0 (smoothed normals), consider it a match + if ( a->normal[0] == 0 && a->normal[1] == 0 && a->normal[2] == 0 + && b->normal[0] == 0 && b->normal[1] == 0 && b->normal[2] == 0 ) { + return true; + } + + // otherwise do a dot-product cosine check + if ( DotProduct( a->normal, b->normal ) < COSINE_EPSILON ) { + return false; + } + + return true; +} + +/* +==================== +ShareMapTriVerts + +Converts independent triangles to shared vertex triangles +==================== +*/ +srfTriangles_t *ShareMapTriVerts( const mapTri_t *tris ) { + const mapTri_t *step; + int count; + int i, j; + int numVerts; + int numIndexes; + srfTriangles_t *uTri; + + // unique the vertexes + count = CountTriList( tris ); + + uTri = renderModelManager->AllocStaticTriSurf( count * 3, count * 3 ); + + numVerts = 0; + numIndexes = 0; + + for ( step = tris ; step ; step = step->next ) { + for ( i = 0 ; i < 3 ; i++ ) { + const idDrawVert *dv; + + dv = &step->v[i]; + + // search for a match + for ( j = 0 ; j < numVerts ; j++ ) { + if ( MatchVert( &uTri->verts[j], dv ) ) { + break; + } + } + if ( j == numVerts ) { + numVerts++; + uTri->verts[j].xyz = dv->xyz; + uTri->verts[j].normal = dv->normal; + uTri->verts[j].st[0] = dv->st[0]; + uTri->verts[j].st[1] = dv->st[1]; + } + + uTri->indexes[numIndexes++] = j; + } + } + + uTri->numVerts = numVerts; + uTri->numIndexes = numIndexes; + + return uTri; +} + +/* +================== +CleanupUTriangles +================== +*/ +static void CleanupUTriangles( srfTriangles_t *tri ) { + // perform cleanup operations + + renderModelManager->SimpleCleanupTriangles( tri ); +} + +/* +==================== +WriteUTriangles + +Writes text verts and indexes to procfile +==================== +*/ +static void WriteUTriangles( const srfTriangles_t *uTris ) { + int col; + int i; + + // emit this chain + procFile->WriteFloatString( "/* numVerts = */ %i /* numIndexes = */ %i\n", + uTris->numVerts, uTris->numIndexes ); + + // verts + col = 0; + for ( i = 0 ; i < uTris->numVerts ; i++ ) { + float vec[8]; + const idDrawVert *dv; + + dv = &uTris->verts[i]; + + vec[0] = dv->xyz[0]; + vec[1] = dv->xyz[1]; + vec[2] = dv->xyz[2]; + vec[3] = dv->st[0]; + vec[4] = dv->st[1]; + vec[5] = dv->normal[0]; + vec[6] = dv->normal[1]; + vec[7] = dv->normal[2]; + Write1DMatrix( procFile, 8, vec ); + + if ( ++col == 3 ) { + col = 0; + procFile->WriteFloatString( "\n" ); + } + } + if ( col != 0 ) { + procFile->WriteFloatString( "\n" ); + } + + // indexes + col = 0; + for ( i = 0 ; i < uTris->numIndexes ; i++ ) { + procFile->WriteFloatString( "%i ", uTris->indexes[i] ); + + if ( ++col == 18 ) { + col = 0; + procFile->WriteFloatString( "\n" ); + } + } + if ( col != 0 ) { + procFile->WriteFloatString( "\n" ); + } +} + + +/* +==================== +WriteShadowTriangles + +Writes text verts and indexes to procfile +==================== +*/ +static void WriteShadowTriangles( const srfTriangles_t *tri ) { + int col; + int i; + + // emit this chain + procFile->WriteFloatString( "/* numVerts = */ %i /* noCaps = */ %i /* noFrontCaps = */ %i /* numIndexes = */ %i /* planeBits = */ %i\n", + tri->numVerts, tri->numShadowIndexesNoCaps, tri->numShadowIndexesNoFrontCaps, tri->numIndexes, tri->shadowCapPlaneBits ); + + // verts + col = 0; + for ( i = 0 ; i < tri->numVerts ; i++ ) { + Write1DMatrix( procFile, 3, &tri->shadowVertexes[i].xyz[0] ); + + if ( ++col == 5 ) { + col = 0; + procFile->WriteFloatString( "\n" ); + } + } + if ( col != 0 ) { + procFile->WriteFloatString( "\n" ); + } + + // indexes + col = 0; + for ( i = 0 ; i < tri->numIndexes ; i++ ) { + procFile->WriteFloatString( "%i ", tri->indexes[i] ); + + if ( ++col == 18 ) { + col = 0; + procFile->WriteFloatString( "\n" ); + } + } + if ( col != 0 ) { + procFile->WriteFloatString( "\n" ); + } +} + + +/* +======================= +GroupsAreSurfaceCompatible + +Planes, texcoords, and groupLights can differ, +but the material and mergegroup must match +======================= +*/ +static bool GroupsAreSurfaceCompatible( const optimizeGroup_t *a, const optimizeGroup_t *b ) { + if ( a->material != b->material ) { + return false; + } + if ( a->mergeGroup != b->mergeGroup ) { + return false; + } + return true; +} + +/* +==================== +WriteOutputSurfaces +==================== +*/ +static void WriteOutputSurfaces( int entityNum, int areaNum ) { + mapTri_t *ambient, *copy; + int surfaceNum; + int numSurfaces; + idMapEntity *entity; + uArea_t *area; + optimizeGroup_t *group, *groupStep; + int i; // , j; +// int col; + srfTriangles_t *uTri; +// mapTri_t *tri; +typedef struct interactionTris_s { + struct interactionTris_s *next; + mapTri_t *triList; + mapLight_t *light; +} interactionTris_t; + + interactionTris_t *interactions, *checkInter; //, *nextInter; + + + area = &dmapGlobals.uEntities[entityNum].areas[areaNum]; + entity = dmapGlobals.uEntities[entityNum].mapEntity; + + numSurfaces = CountUniqueShaders( area->groups ); + + + if ( entityNum == 0 ) { + procFile->WriteFloatString( "model { /* name = */ \"_area%i\" /* numSurfaces = */ %i\n\n", + areaNum, numSurfaces ); + } else { + const char *name; + + entity->epairs.GetString( "name", "", &name ); + if ( !name[0] ) { + common->Error( "Entity %i has surfaces, but no name key", entityNum ); + } + procFile->WriteFloatString( "model { /* name = */ \"%s\" /* numSurfaces = */ %i\n\n", + name, numSurfaces ); + } + + surfaceNum = 0; + for ( group = area->groups ; group ; group = group->nextGroup ) { + if ( group->surfaceEmited ) { + continue; + } + + // combine all groups compatible with this one + // usually several optimizeGroup_t can be combined into a single + // surface, even though they couldn't be merged together to save + // vertexes because they had different planes, texture coordinates, or lights. + // Different mergeGroups will stay in separate surfaces. + ambient = NULL; + + // each light that illuminates any of the groups in the surface will + // get its own list of indexes out of the original surface + interactions = NULL; + + for ( groupStep = group ; groupStep ; groupStep = groupStep->nextGroup ) { + if ( groupStep->surfaceEmited ) { + continue; + } + if ( !GroupsAreSurfaceCompatible( group, groupStep ) ) { + continue; + } + + // copy it out to the ambient list + copy = CopyTriList( groupStep->triList ); + ambient = MergeTriLists( ambient, copy ); + groupStep->surfaceEmited = true; + + // duplicate it into an interaction for each groupLight + for ( i = 0 ; i < groupStep->numGroupLights ; i++ ) { + for ( checkInter = interactions ; checkInter ; checkInter = checkInter->next ) { + if ( checkInter->light == groupStep->groupLights[i] ) { + break; + } + } + if ( !checkInter ) { + // create a new interaction + checkInter = (interactionTris_t *)Mem_ClearedAlloc( sizeof( *checkInter ) ); + checkInter->light = groupStep->groupLights[i]; + checkInter->next = interactions; + interactions = checkInter; + } + copy = CopyTriList( groupStep->triList ); + checkInter->triList = MergeTriLists( checkInter->triList, copy ); + } + } + + if ( !ambient ) { + continue; + } + + if ( surfaceNum >= numSurfaces ) { + common->Error( "WriteOutputSurfaces: surfaceNum >= numSurfaces" ); + } + + procFile->WriteFloatString( "/* surface %i */ { ", surfaceNum ); + surfaceNum++; + procFile->WriteFloatString( "\"%s\" ", ambient->material->GetName() ); + + uTri = ShareMapTriVerts( ambient ); + FreeTriList( ambient ); + + CleanupUTriangles( uTri ); + WriteUTriangles( uTri ); + renderModelManager->FreeStaticTriSurf( uTri ); + + procFile->WriteFloatString( "}\n\n" ); + } + + procFile->WriteFloatString( "}\n\n" ); +} + +/* +=============== +WriteNode_r + +=============== +*/ +static void WriteNode_r( node_t *node ) { + int child[2]; + int i; + idPlane *plane; + + if ( node->planenum == PLANENUM_LEAF ) { + // we shouldn't get here unless the entire world + // was a single leaf + procFile->WriteFloatString( "/* node 0 */ ( 0 0 0 0 ) -1 -1\n" ); + return; + } + + for ( i = 0 ; i < 2 ; i++ ) { + if ( node->children[i]->planenum == PLANENUM_LEAF ) { + child[i] = -1 - node->children[i]->area; + } else { + child[i] = node->children[i]->nodeNumber; + } + } + + plane = &dmapGlobals.mapPlanes[node->planenum]; + + procFile->WriteFloatString( "/* node %i */ ", node->nodeNumber ); + Write1DMatrix( procFile, 4, plane->ToFloatPtr() ); + procFile->WriteFloatString( "%i %i\n", child[0], child[1] ); + + if ( child[0] > 0 ) { + WriteNode_r( node->children[0] ); + } + if ( child[1] > 0 ) { + WriteNode_r( node->children[1] ); + } +} + +static int NumberNodes_r( node_t *node, int nextNumber ) { + if ( node->planenum == PLANENUM_LEAF ) { + return nextNumber; + } + node->nodeNumber = nextNumber; + nextNumber++; + nextNumber = NumberNodes_r( node->children[0], nextNumber ); + nextNumber = NumberNodes_r( node->children[1], nextNumber ); + + return nextNumber; +} + +/* +==================== +WriteOutputNodes +==================== +*/ +static void WriteOutputNodes( node_t *node ) { + int numNodes; + + // prune unneeded nodes and count + PruneNodes_r( node ); + numNodes = NumberNodes_r( node, 0 ); + + // output + procFile->WriteFloatString( "nodes { /* numNodes = */ %i\n\n", numNodes ); + procFile->WriteFloatString( "/* node format is: ( planeVector ) positiveChild negativeChild */\n" ); + procFile->WriteFloatString( "/* a child number of 0 is an opaque, solid area */\n" ); + procFile->WriteFloatString( "/* negative child numbers are areas: (-1-child) */\n" ); + + WriteNode_r( node ); + + procFile->WriteFloatString( "}\n\n" ); +} + +/* +==================== +WriteOutputPortals +==================== +*/ +static void WriteOutputPortals( uEntity_t *e ) { + int i, j; + interAreaPortal_t *iap; + idWinding *w; + + procFile->WriteFloatString( "interAreaPortals { /* numAreas = */ %i /* numIAP = */ %i\n\n", + e->numAreas, numInterAreaPortals ); + procFile->WriteFloatString( "/* interAreaPortal format is: numPoints positiveSideArea negativeSideArea ( point) ... */\n" ); + for ( i = 0 ; i < numInterAreaPortals ; i++ ) { + iap = &interAreaPortals[i]; + w = iap->side->winding; + procFile->WriteFloatString("/* iap %i */ %i %i %i ", i, w->GetNumPoints(), iap->area0, iap->area1 ); + for ( j = 0 ; j < w->GetNumPoints() ; j++ ) { + Write1DMatrix( procFile, 3, (*w)[j].ToFloatPtr() ); + } + procFile->WriteFloatString("\n" ); + } + + procFile->WriteFloatString( "}\n\n" ); +} + + +/* +==================== +WriteOutputEntity +==================== +*/ +static void WriteOutputEntity( int entityNum ) { + int i; + uEntity_t *e; + + e = &dmapGlobals.uEntities[entityNum]; + + if ( entityNum != 0 ) { + // entities may have enclosed, empty areas that we don't need to write out + if ( e->numAreas > 1 ) { + e->numAreas = 1; + } + } + + for ( i = 0 ; i < e->numAreas ; i++ ) { + WriteOutputSurfaces( entityNum, i ); + } + + // we will completely skip the portals and nodes if it is a single area + if ( entityNum == 0 && e->numAreas > 1 ) { + // output the area portals + WriteOutputPortals( e ); + + // output the nodes + WriteOutputNodes( e->tree->headnode ); + } +} + + +/* +==================== +WriteOutputFile +==================== +*/ +void WriteOutputFile( void ) { + int i; + uEntity_t *entity; + idStr qpath; + + // write the file + common->Printf( "----- WriteOutputFile -----\n" ); + + sprintf( qpath, "%s." PROC_FILE_EXT, dmapGlobals.mapFileBase ); + + common->Printf( "writing %s\n", qpath.c_str() ); + // _D3XP used fs_cdpath + procFile = fileSystem->OpenFileWrite( qpath, "fs_devpath" ); + if ( !procFile ) { + common->Error( "Error opening %s", qpath.c_str() ); + } + + procFile->WriteFloatString( "%s\n\n", PROC_FILE_ID ); + + // write the entity models and information, writing entities first + for ( i=dmapGlobals.num_entities - 1 ; i >= 0 ; i-- ) { + entity = &dmapGlobals.uEntities[i]; + + if ( !entity->primitives ) { + continue; + } + + WriteOutputEntity( i ); + } + + // write the shadow volumes + for ( i = 0 ; i < dmapGlobals.mapLights.Num() ; i++ ) { + mapLight_t *light = dmapGlobals.mapLights[i]; + if ( !light->shadowTris ) { + continue; + } + + procFile->WriteFloatString( "shadowModel { /* name = */ \"_prelight_%s\"\n\n", light->name ); + WriteShadowTriangles( light->shadowTris ); + procFile->WriteFloatString( "}\n\n" ); + + renderModelManager->FreeStaticTriSurf( light->shadowTris ); + light->shadowTris = NULL; + } + + fileSystem->CloseFile( procFile ); +} diff --git a/src/tools/compilers/dmap/portals.cpp b/src/tools/compilers/dmap/portals.cpp new file mode 100644 index 0000000..6e11089 --- /dev/null +++ b/src/tools/compilers/dmap/portals.cpp @@ -0,0 +1,999 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../../idlib/precompiled.h" +#pragma hdrstop + +#include "dmap.h" + + +interAreaPortal_t interAreaPortals[MAX_INTER_AREA_PORTALS]; +int numInterAreaPortals; + + +int c_active_portals; +int c_peak_portals; + +/* +=========== +AllocPortal +=========== +*/ +uPortal_t *AllocPortal (void) +{ + uPortal_t *p; + + c_active_portals++; + if (c_active_portals > c_peak_portals) + c_peak_portals = c_active_portals; + + p = (uPortal_t *)Mem_Alloc (sizeof(uPortal_t )); + memset (p, 0, sizeof(uPortal_t )); + + return p; +} + + +void FreePortal (uPortal_t *p) +{ + if (p->winding) + delete p->winding; + c_active_portals--; + Mem_Free (p); +} + +//============================================================== + +/* +============= +Portal_Passable + +Returns true if the portal has non-opaque leafs on both sides +============= +*/ +static bool Portal_Passable( uPortal_t *p ) { + if (!p->onnode) { + return false; // to global outsideleaf + } + + if (p->nodes[0]->planenum != PLANENUM_LEAF + || p->nodes[1]->planenum != PLANENUM_LEAF) { + common->Error( "Portal_EntityFlood: not a leaf"); + } + + if ( !p->nodes[0]->opaque && !p->nodes[1]->opaque ) { + return true; + } + + return false; +} + + +//============================================================================= + +int c_tinyportals; + +/* +============= +AddPortalToNodes +============= +*/ +void AddPortalToNodes (uPortal_t *p, node_t *front, node_t *back) { + if (p->nodes[0] || p->nodes[1]) { + common->Error( "AddPortalToNode: allready included"); + } + + p->nodes[0] = front; + p->next[0] = front->portals; + front->portals = p; + + p->nodes[1] = back; + p->next[1] = back->portals; + back->portals = p; +} + + +/* +============= +RemovePortalFromNode +============= +*/ +void RemovePortalFromNode (uPortal_t *portal, node_t *l) +{ + uPortal_t **pp, *t; + +// remove reference to the current portal + pp = &l->portals; + while (1) + { + t = *pp; + if (!t) + common->Error( "RemovePortalFromNode: portal not in leaf"); + + if ( t == portal ) + break; + + if (t->nodes[0] == l) + pp = &t->next[0]; + else if (t->nodes[1] == l) + pp = &t->next[1]; + else + common->Error( "RemovePortalFromNode: portal not bounding leaf"); + } + + if ( portal->nodes[0] == l ) { + *pp = portal->next[0]; + portal->nodes[0] = NULL; + } else if ( portal->nodes[1] == l ) { + *pp = portal->next[1]; + portal->nodes[1] = NULL; + } else { + common->Error( "RemovePortalFromNode: mislinked" ); + } +} + +//============================================================================ + +void PrintPortal (uPortal_t *p) +{ + int i; + idWinding *w; + + w = p->winding; + for ( i = 0; i < w->GetNumPoints(); i++ ) + common->Printf("(%5.0f,%5.0f,%5.0f)\n",(*w)[i][0], (*w)[i][1], (*w)[i][2]); +} + +/* +================ +MakeHeadnodePortals + +The created portals will face the global outside_node +================ +*/ +#define SIDESPACE 8 +static void MakeHeadnodePortals( tree_t *tree ) { + idBounds bounds; + int i, j, n; + uPortal_t *p, *portals[6]; + idPlane bplanes[6], *pl; + node_t *node; + + node = tree->headnode; + + tree->outside_node.planenum = PLANENUM_LEAF; + tree->outside_node.brushlist = NULL; + tree->outside_node.portals = NULL; + tree->outside_node.opaque = false; + + // if no nodes, don't go any farther + if ( node->planenum == PLANENUM_LEAF ) { + return; + } + + // pad with some space so there will never be null volume leafs + for (i=0 ; i<3 ; i++) { + bounds[0][i] = tree->bounds[0][i] - SIDESPACE; + bounds[1][i] = tree->bounds[1][i] + SIDESPACE; + if ( bounds[0][i] >= bounds[1][i] ) { + common->Error( "Backwards tree volume" ); + } + } + + for (i=0 ; i<3 ; i++) { + for (j=0 ; j<2 ; j++) { + n = j*3 + i; + + p = AllocPortal (); + portals[n] = p; + + pl = &bplanes[n]; + memset (pl, 0, sizeof(*pl)); + if (j) { + (*pl)[i] = -1; + (*pl)[3] = bounds[j][i]; + } else { + (*pl)[i] = 1; + (*pl)[3] = -bounds[j][i]; + } + p->plane = *pl; + p->winding = new idWinding( *pl ); + AddPortalToNodes (p, node, &tree->outside_node); + } + } + + // clip the basewindings by all the other planes + for (i=0 ; i<6 ; i++) { + for (j=0 ; j<6 ; j++) { + if (j == i) { + continue; + } + portals[i]->winding = portals[i]->winding->Clip( bplanes[j], ON_EPSILON ); + } + } +} + +//=================================================== + + +/* +================ +BaseWindingForNode +================ +*/ +#define BASE_WINDING_EPSILON 0.001f +#define SPLIT_WINDING_EPSILON 0.001f + +idWinding *BaseWindingForNode (node_t *node) { + idWinding *w; + node_t *n; + + w = new idWinding( dmapGlobals.mapPlanes[node->planenum] ); + + // clip by all the parents + for ( n = node->parent ; n && w ; ) { + idPlane &plane = dmapGlobals.mapPlanes[n->planenum]; + + if ( n->children[0] == node ) { + // take front + w = w->Clip( plane, BASE_WINDING_EPSILON ); + } else { + // take back + idPlane back = -plane; + w = w->Clip( back, BASE_WINDING_EPSILON ); + } + node = n; + n = n->parent; + } + + return w; +} + +//============================================================ + +/* +================== +MakeNodePortal + +create the new portal by taking the full plane winding for the cutting plane +and clipping it by all of parents of this node +================== +*/ +static void MakeNodePortal( node_t *node ) { + uPortal_t *new_portal, *p; + idWinding *w; + idVec3 normal; + int side; + + w = BaseWindingForNode (node); + + // clip the portal by all the other portals in the node + for (p = node->portals ; p && w; p = p->next[side]) + { + idPlane plane; + + if (p->nodes[0] == node) + { + side = 0; + plane = p->plane; + } + else if (p->nodes[1] == node) + { + side = 1; + plane = -p->plane; + } + else { + common->Error( "CutNodePortals_r: mislinked portal"); + side = 0; // quiet a compiler warning + } + + w = w->Clip( plane, CLIP_EPSILON ); + } + + if (!w) + { + return; + } + + if ( w->IsTiny() ) + { + c_tinyportals++; + delete w; + return; + } + + + new_portal = AllocPortal (); + new_portal->plane = dmapGlobals.mapPlanes[node->planenum]; + new_portal->onnode = node; + new_portal->winding = w; + AddPortalToNodes (new_portal, node->children[0], node->children[1]); +} + + +/* +============== +SplitNodePortals + +Move or split the portals that bound node so that the node's +children have portals instead of node. +============== +*/ +static void SplitNodePortals( node_t *node ) { + uPortal_t *p, *next_portal, *new_portal; + node_t *f, *b, *other_node; + int side; + idPlane *plane; + idWinding *frontwinding, *backwinding; + + plane = &dmapGlobals.mapPlanes[node->planenum]; + f = node->children[0]; + b = node->children[1]; + + for ( p = node->portals ; p ; p = next_portal ) { + if (p->nodes[0] == node ) { + side = 0; + } else if ( p->nodes[1] == node ) { + side = 1; + } else { + common->Error( "SplitNodePortals: mislinked portal" ); + side = 0; // quiet a compiler warning + } + next_portal = p->next[side]; + + other_node = p->nodes[!side]; + RemovePortalFromNode (p, p->nodes[0]); + RemovePortalFromNode (p, p->nodes[1]); + + // + // cut the portal into two portals, one on each side of the cut plane + // + p->winding->Split( *plane, SPLIT_WINDING_EPSILON, &frontwinding, &backwinding); + + if ( frontwinding && frontwinding->IsTiny() ) + { + delete frontwinding; + frontwinding = NULL; + c_tinyportals++; + } + + if ( backwinding && backwinding->IsTiny() ) + { + delete backwinding; + backwinding = NULL; + c_tinyportals++; + } + + if ( !frontwinding && !backwinding ) + { // tiny windings on both sides + continue; + } + + if (!frontwinding) + { + delete backwinding; + if (side == 0) + AddPortalToNodes (p, b, other_node); + else + AddPortalToNodes (p, other_node, b); + continue; + } + if (!backwinding) + { + delete frontwinding; + if (side == 0) + AddPortalToNodes (p, f, other_node); + else + AddPortalToNodes (p, other_node, f); + continue; + } + + // the winding is split + new_portal = AllocPortal (); + *new_portal = *p; + new_portal->winding = backwinding; + delete p->winding; + p->winding = frontwinding; + + if (side == 0) + { + AddPortalToNodes (p, f, other_node); + AddPortalToNodes (new_portal, b, other_node); + } + else + { + AddPortalToNodes (p, other_node, f); + AddPortalToNodes (new_portal, other_node, b); + } + } + + node->portals = NULL; +} + + +/* +================ +CalcNodeBounds +================ +*/ +void CalcNodeBounds (node_t *node) +{ + uPortal_t *p; + int s; + int i; + + // calc mins/maxs for both leafs and nodes + node->bounds.Clear(); + for (p = node->portals ; p ; p = p->next[s]) { + s = (p->nodes[1] == node); + for ( i = 0; i < p->winding->GetNumPoints(); i++ ) { + node->bounds.AddPoint( (*p->winding)[i].ToVec3() ); + } + } +} + + +/* +================== +MakeTreePortals_r +================== +*/ +void MakeTreePortals_r (node_t *node) +{ + int i; + + CalcNodeBounds( node ); + + if ( node->bounds[0][0] >= node->bounds[1][0]) { + common->Warning( "node without a volume" ); + } + + for ( i = 0; i < 3; i++ ) { + if ( node->bounds[0][i] < MIN_WORLD_COORD || node->bounds[1][i] > MAX_WORLD_COORD ) { + common->Warning( "node with unbounded volume"); + break; + } + } + if ( node->planenum == PLANENUM_LEAF ) { + return; + } + + MakeNodePortal (node); + SplitNodePortals (node); + + MakeTreePortals_r (node->children[0]); + MakeTreePortals_r (node->children[1]); +} + +/* +================== +MakeTreePortals +================== +*/ +void MakeTreePortals (tree_t *tree) +{ + common->Printf( "----- MakeTreePortals -----\n"); + MakeHeadnodePortals (tree); + MakeTreePortals_r (tree->headnode); +} + +/* +========================================================= + +FLOOD ENTITIES + +========================================================= +*/ + +int c_floodedleafs; + +/* +============= +FloodPortals_r +============= +*/ +void FloodPortals_r (node_t *node, int dist) { + uPortal_t *p; + int s; + + if ( node->occupied ) { + return; + } + + if ( node->opaque ) { + return; + } + + c_floodedleafs++; + node->occupied = dist; + + for (p=node->portals ; p ; p = p->next[s]) { + s = (p->nodes[1] == node); + FloodPortals_r (p->nodes[!s], dist+1); + } +} + +/* +============= +PlaceOccupant +============= +*/ +bool PlaceOccupant( node_t *headnode, idVec3 origin, uEntity_t *occupant ) { + node_t *node; + float d; + idPlane *plane; + + // find the leaf to start in + node = headnode; + while ( node->planenum != PLANENUM_LEAF ) { + plane = &dmapGlobals.mapPlanes[node->planenum]; + d = plane->Distance( origin ); + if ( d >= 0.0f ) { + node = node->children[0]; + } else { + node = node->children[1]; + } + } + + if ( node->opaque ) { + return false; + } + node->occupant = occupant; + + FloodPortals_r (node, 1); + + return true; +} + +/* +============= +FloodEntities + +Marks all nodes that can be reached by entites +============= +*/ +bool FloodEntities( tree_t *tree ) { + int i; + idVec3 origin; + const char *cl; + bool inside; + node_t *headnode; + + headnode = tree->headnode; + common->Printf ("--- FloodEntities ---\n"); + inside = false; + tree->outside_node.occupied = 0; + + c_floodedleafs = 0; + bool errorShown = false; + for (i=1 ; iepairs.GetVector( "origin", "", origin) ) { + continue; + } + + // any entity can have "noFlood" set to skip it + if ( mapEnt->epairs.GetString( "noFlood", "", &cl ) ) { + continue; + } + + mapEnt->epairs.GetString( "classname", "", &cl ); + + if ( !strcmp( cl, "light" ) ) { + const char *v; + + // don't place lights that have a light_start field, because they can still + // be valid if their origin is outside the world + mapEnt->epairs.GetString( "light_start", "", &v); + if ( v[0] ) { + continue; + } + + // don't place fog lights, because they often + // have origins outside the light + mapEnt->epairs.GetString( "texture", "", &v); + if ( v[0] ) { + const idMaterial *mat = declManager->FindMaterial( v ); + if ( mat->IsFogLight() ) { + continue; + } + } + } + + if (PlaceOccupant (headnode, origin, &dmapGlobals.uEntities[i])) { + inside = true; + } + + if (tree->outside_node.occupied && !errorShown) { + errorShown = true; + common->Printf("Leak on entity # %d\n", i); + const char *p; + + mapEnt->epairs.GetString( "classname", "", &p); + common->Printf("Entity classname was: %s\n", p); + mapEnt->epairs.GetString( "name", "", &p); + common->Printf("Entity name was: %s\n", p); + idVec3 origin; + if ( mapEnt->epairs.GetVector( "origin", "", origin)) { + common->Printf("Entity origin is: %f %f %f\n\n\n", origin.x, origin.y, origin.z); + } + } + } + + common->Printf("%5i flooded leafs\n", c_floodedleafs ); + + if (!inside) + { + common->Printf ("no entities in open -- no filling\n"); + } + else if (tree->outside_node.occupied) + { + common->Printf ("entity reached from outside -- no filling\n"); + } + + return (bool)(inside && !tree->outside_node.occupied); +} + +/* +========================================================= + +FLOOD AREAS + +========================================================= +*/ + +static int c_areas; +static int c_areaFloods; + +/* +================= +FindSideForPortal +================= +*/ +static side_t *FindSideForPortal( uPortal_t *p ) { + int i, j, k; + node_t *node; + uBrush_t *b, *orig; + side_t *s, *s2; + + // scan both bordering nodes brush lists for a portal brush + // that shares the plane + for ( i = 0 ; i < 2 ; i++ ) { + node = p->nodes[i]; + for ( b = node->brushlist ; b ; b = b->next ) { + if ( !( b->contents & CONTENTS_AREAPORTAL ) ) { + continue; + } + orig = b->original; + for ( j = 0 ; j < orig->numsides ; j++ ) { + s = orig->sides + j; + if ( !s->visibleHull ) { + continue; + } + if ( !( s->material->GetContentFlags() & CONTENTS_AREAPORTAL ) ) { + continue; + } + if ( ( s->planenum & ~1 ) != ( p->onnode->planenum & ~1 ) ) { + continue; + } + // remove the visible hull from any other portal sides of this portal brush + for ( k = 0; k < orig->numsides; k++ ) { + if ( k == j ) { + continue; + } + s2 = orig->sides + k; + if ( s2->visibleHull == NULL ) { + continue; + } + if ( !( s2->material->GetContentFlags() & CONTENTS_AREAPORTAL ) ) { + continue; + } + common->Warning( "brush has multiple area portal sides at %s", s2->visibleHull->GetCenter().ToString() ); + delete s2->visibleHull; + s2->visibleHull = NULL; + } + return s; + } + } + } + return NULL; +} + +/* +============= +FloodAreas_r +============= +*/ +void FloodAreas_r (node_t *node) +{ + uPortal_t *p; + int s; + + if ( node->area != -1 ) { + return; // allready got it + } + if ( node->opaque ) { + return; + } + + c_areaFloods++; + node->area = c_areas; + + for ( p=node->portals ; p ; p = p->next[s] ) { + node_t *other; + + s = (p->nodes[1] == node); + other = p->nodes[!s]; + + if ( !Portal_Passable(p) ) { + continue; + } + + // can't flood through an area portal + if ( FindSideForPortal( p ) ) { + continue; + } + + FloodAreas_r( other ); + } +} + +/* +============= +FindAreas_r + +Just decend the tree, and for each node that hasn't had an +area set, flood fill out from there +============= +*/ +void FindAreas_r( node_t *node ) { + if ( node->planenum != PLANENUM_LEAF ) { + FindAreas_r (node->children[0]); + FindAreas_r (node->children[1]); + return; + } + + if ( node->opaque ) { + return; + } + + if ( node->area != -1 ) { + return; // allready got it + } + + c_areaFloods = 0; + FloodAreas_r (node); + common->Printf( "area %i has %i leafs\n", c_areas, c_areaFloods ); + c_areas++; +} + +/* +============ +CheckAreas_r +============ +*/ +void CheckAreas_r( node_t *node ) { + if ( node->planenum != PLANENUM_LEAF ) { + CheckAreas_r (node->children[0]); + CheckAreas_r (node->children[1]); + return; + } + if ( !node->opaque && node->area < 0 ) { + common->Error( "CheckAreas_r: area = %i", node->area ); + } +} + +/* +============ +ClearAreas_r + +Set all the areas to -1 before filling +============ +*/ +void ClearAreas_r( node_t *node ) { + if ( node->planenum != PLANENUM_LEAF ) { + ClearAreas_r (node->children[0]); + ClearAreas_r (node->children[1]); + return; + } + node->area = -1; +} + +//============================================================= + + +/* +================= +FindInterAreaPortals_r + +================= +*/ +static void FindInterAreaPortals_r( node_t *node ) { + uPortal_t *p; + int s; + int i; + idWinding *w; + interAreaPortal_t *iap; + side_t *side; + + if ( node->planenum != PLANENUM_LEAF ) { + FindInterAreaPortals_r( node->children[0] ); + FindInterAreaPortals_r( node->children[1] ); + return; + } + + if ( node->opaque ) { + return; + } + + for ( p=node->portals ; p ; p = p->next[s] ) { + node_t *other; + + s = (p->nodes[1] == node); + other = p->nodes[!s]; + + if ( other->opaque ) { + continue; + } + + // only report areas going from lower number to higher number + // so we don't report the portal twice + if ( other->area <= node->area ) { + continue; + } + + side = FindSideForPortal( p ); +// w = p->winding; + if ( !side ) { + common->Warning( "FindSideForPortal failed at %s", p->winding->GetCenter().ToString() ); + continue; + } + w = side->visibleHull; + if ( !w ) { + continue; + } + + // see if we have created this portal before + for ( i = 0 ; i < numInterAreaPortals ; i++ ) { + iap = &interAreaPortals[i]; + + if ( side == iap->side && + ( ( p->nodes[0]->area == iap->area0 && p->nodes[1]->area == iap->area1 ) + || ( p->nodes[1]->area == iap->area0 && p->nodes[0]->area == iap->area1 ) ) ) { + break; + } + } + + if ( i != numInterAreaPortals ) { + continue; // already emited + } + + iap = &interAreaPortals[numInterAreaPortals]; + numInterAreaPortals++; + if ( side->planenum == p->onnode->planenum ) { + iap->area0 = p->nodes[0]->area; + iap->area1 = p->nodes[1]->area; + } else { + iap->area0 = p->nodes[1]->area; + iap->area1 = p->nodes[0]->area; + } + iap->side = side; + + } +} + + + + + +/* +============= +FloodAreas + +Mark each leaf with an area, bounded by CONTENTS_AREAPORTAL +Sets e->areas.numAreas +============= +*/ +void FloodAreas( uEntity_t *e ) { + common->Printf ("--- FloodAreas ---\n"); + + // set all areas to -1 + ClearAreas_r( e->tree->headnode ); + + // flood fill from non-opaque areas + c_areas = 0; + FindAreas_r( e->tree->headnode ); + + common->Printf ("%5i areas\n", c_areas); + e->numAreas = c_areas; + + // make sure we got all of them + CheckAreas_r( e->tree->headnode ); + + // identify all portals between areas if this is the world + if ( e == &dmapGlobals.uEntities[0] ) { + numInterAreaPortals = 0; + FindInterAreaPortals_r( e->tree->headnode ); + } +} + +/* +====================================================== + +FILL OUTSIDE + +====================================================== +*/ + +static int c_outside; +static int c_inside; +static int c_solid; + +void FillOutside_r (node_t *node) +{ + if (node->planenum != PLANENUM_LEAF) + { + FillOutside_r (node->children[0]); + FillOutside_r (node->children[1]); + return; + } + + // anything not reachable by an entity + // can be filled away + if (!node->occupied) { + if ( !node->opaque ) { + c_outside++; + node->opaque = true; + } else { + c_solid++; + } + } else { + c_inside++; + } + +} + +/* +============= +FillOutside + +Fill (set node->opaque = true) all nodes that can't be reached by entities +============= +*/ +void FillOutside( uEntity_t *e ) { + c_outside = 0; + c_inside = 0; + c_solid = 0; + common->Printf ("--- FillOutside ---\n"); + FillOutside_r( e->tree->headnode ); + common->Printf ("%5i solid leafs\n", c_solid); + common->Printf ("%5i leafs filled\n", c_outside); + common->Printf ("%5i inside leafs\n", c_inside); +} diff --git a/src/tools/compilers/dmap/shadowopt3.cpp b/src/tools/compilers/dmap/shadowopt3.cpp new file mode 100644 index 0000000..62cefc4 --- /dev/null +++ b/src/tools/compilers/dmap/shadowopt3.cpp @@ -0,0 +1,1263 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../../idlib/precompiled.h" +#pragma hdrstop + +#include "dmap.h" +#include "../../../renderer/tr_local.h" + +/* + + given a set of faces that are clipped to the required frustum + + make 2D projection for each vertex + + for each edge + add edge, generating new points at each edge intersection + + ?add all additional edges to make a full triangulation + + make full triangulation + + for each triangle + find midpoint + find original triangle with midpoint closest to view + annotate triangle with that data + project all vertexes to that plane + output the triangle as a front cap + + snap all vertexes + make a back plane projection for all vertexes + + for each edge + if one side doesn't have a triangle + make a sil edge to back plane projection + continue + if triangles on both sides have two verts in common + continue + make a sil edge from one triangle to the other + + + + + classify triangles on common planes, so they can be optimized + + what about interpenetrating triangles??? + + a perfect shadow volume will have every edge exactly matched with + an opposite, and no two triangles covering the same area on either + the back projection or a silhouette edge. + + Optimizing the triangles on the projected plane can give a significant + improvement, but the quadratic time nature of the optimization process + probably makes it untenable. + + There exists some small room for further triangle count optimizations of the volumes + by collapsing internal surface geometry in some cases, or allowing original triangles + to extend outside the exactly light frustum without being clipped, but it probably + isn't worth it. + + Triangle count optimizations at the expense of a slight fill rate cost + may be apropriate in some cases. + + + Perform the complete clipping on all triangles + for each vertex + project onto the apropriate plane and mark plane bit as in use +for each triangle + if points project onto different planes, clip +*/ + + +typedef struct { + idVec3 v[3]; + idVec3 edge[3]; // positive side is inside the triangle + glIndex_t index[3]; + idPlane plane; // positive side is forward for the triangle, which is away from the light + int planeNum; // from original triangle, not calculated from the clipped verts +} shadowTri_t; + +static const int MAX_SHADOW_TRIS = 32768; + +static shadowTri_t outputTris[MAX_SHADOW_TRIS]; +static int numOutputTris; + +typedef struct shadowOptEdge_s { + glIndex_t index[2]; + struct shadowOptEdge_s *nextEdge; +} shadowOptEdge_t; + +static const int MAX_SIL_EDGES = MAX_SHADOW_TRIS*3; +static shadowOptEdge_t silEdges[MAX_SIL_EDGES]; +static int numSilEdges; + +typedef struct silQuad_s { + int nearV[2]; + int farV[2]; // will always be a projection of near[] + struct silQuad_s *nextQuad; +} silQuad_t; + +static const int MAX_SIL_QUADS = MAX_SHADOW_TRIS*3; +static silQuad_t silQuads[MAX_SIL_QUADS]; +static int numSilQuads; + + +typedef struct { + idVec3 normal; // all sil planes go through the projection origin + shadowOptEdge_t *edges; + silQuad_t *fragmentedQuads; +} silPlane_t; + +static float EDGE_PLANE_EPSILON = 0.1f; +static float UNIQUE_EPSILON = 0.1f; + +static int numSilPlanes; +static silPlane_t *silPlanes; + +// the uniqued verts are still in projection centered space, not global space +static int numUniqued; +static int numUniquedBeforeProjection; +static int maxUniqued; +static idVec3 *uniqued; + +static optimizedShadow_t ret; +static int maxRetIndexes; + +static int FindUniqueVert( idVec3 &v ); + +//===================================================================================== + +/* +================= +CreateEdgesForTri +================= +*/ +static void CreateEdgesForTri( shadowTri_t *tri ) { + for ( int j = 0 ; j < 3 ; j++ ) { + idVec3 &v1 = tri->v[j]; + idVec3 &v2 = tri->v[(j+1)%3]; + + tri->edge[j].Cross( v2, v1 ); + tri->edge[j].Normalize(); + } +} + + +static const float EDGE_EPSILON = 0.1f; + +static bool TriOutsideTri( const shadowTri_t *a, const shadowTri_t *b ) { +#if 0 + if ( a->v[0] * b->edge[0] <= EDGE_EPSILON + && a->v[1] * b->edge[0] <= EDGE_EPSILON + && a->v[2] * b->edge[0] <= EDGE_EPSILON ) { + return true; + } + if ( a->v[0] * b->edge[1] <= EDGE_EPSILON + && a->v[1] * b->edge[1] <= EDGE_EPSILON + && a->v[2] * b->edge[1] <= EDGE_EPSILON ) { + return true; + } + if ( a->v[0] * b->edge[2] <= EDGE_EPSILON + && a->v[1] * b->edge[2] <= EDGE_EPSILON + && a->v[2] * b->edge[2] <= EDGE_EPSILON ) { + return true; + } +#else + for ( int i = 0 ; i < 3 ; i++ ) { + int j; + for ( j = 0 ; j < 3 ; j++ ) { + float d = a->v[j] * b->edge[i]; + if ( d > EDGE_EPSILON ) { + break; + } + } + if ( j == 3 ) { + return true; + } + } +#endif + return false; +} + +static bool TriBehindTri( const shadowTri_t *a, const shadowTri_t *b ) { + float d; + + d = b->plane.Distance( a->v[0] ); + if ( d > 0 ) { + return true; + } + d = b->plane.Distance( a->v[1] ); + if ( d > 0 ) { + return true; + } + d = b->plane.Distance( a->v[2] ); + if ( d > 0 ) { + return true; + } + + return false; +} + +/* +=================== +ClipTriangle_r +=================== +*/ +static int c_removedFragments; +static void ClipTriangle_r( const shadowTri_t *tri, int startTri, int skipTri, int numTris, const shadowTri_t *tris ) { + // create edge planes for this triangle + + // compare against all the other triangles + for ( int i = startTri ; i < numTris ; i++ ) { + if ( i == skipTri ) { + continue; + } + const shadowTri_t *other = &tris[i]; + + if ( TriOutsideTri( tri, other ) ) { + continue; + } + if ( TriOutsideTri( other, tri ) ) { + continue; + } + // they overlap to some degree + + // if other is behind tri, it doesn't clip it + if ( !TriBehindTri( tri, other ) ) { + continue; + } + + // clip it + idWinding *w = new idWinding( tri->v, 3 ); + + for ( int j = 0 ; j < 4 && w ; j++ ) { + idWinding *front, *back; + + // keep any portion in front of other's plane + if ( j == 0 ) { + w->Split( other->plane, ON_EPSILON, &front, &back ); + } else { + w->Split( idPlane( other->edge[j-1], 0.0f ), ON_EPSILON, &front, &back ); + } + if ( back ) { + // recursively clip these triangles to all subsequent triangles + for ( int k = 2 ; k < back->GetNumPoints() ; k++ ) { + shadowTri_t fragment = *tri; + + fragment.v[0] = (*back)[0].ToVec3(); + fragment.v[1] = (*back)[k-1].ToVec3(); + fragment.v[2] = (*back)[k].ToVec3(); + CreateEdgesForTri( &fragment ); + ClipTriangle_r( &fragment, i + 1, skipTri, numTris, tris ); + } + delete back; + } + + delete w; + w = front; + } + if ( w ) { + delete w; + } + + c_removedFragments++; + // any fragments will have been added recursively + return; + } + + // this fragment is frontmost, so add it to the output list + if ( numOutputTris == MAX_SHADOW_TRIS ) { + common->Error( "numOutputTris == MAX_SHADOW_TRIS" ); + } + + outputTris[numOutputTris] = *tri; + numOutputTris++; +} + + +/* +==================== +ClipOccluders + +Generates outputTris by clipping all the triangles against each other, +retaining only those closest to the projectionOrigin +==================== +*/ +static void ClipOccluders( idVec4 *verts, glIndex_t *indexes, int numIndexes, + idVec3 projectionOrigin ) { + int numTris = numIndexes / 3; + int i; + shadowTri_t *tris = (shadowTri_t *)_alloca( numTris * sizeof( *tris ) ); + shadowTri_t *tri; + + common->Printf( "ClipOccluders: %i triangles\n", numTris ); + + for ( i = 0 ; i < numTris ; i++ ) { + tri = &tris[i]; + + // the indexes are in reversed order from tr_stencilshadow + tri->v[0] = verts[indexes[i*3+2]].ToVec3() - projectionOrigin; + tri->v[1] = verts[indexes[i*3+1]].ToVec3() - projectionOrigin; + tri->v[2] = verts[indexes[i*3+0]].ToVec3() - projectionOrigin; + + idVec3 d1 = tri->v[1] - tri->v[0]; + idVec3 d2 = tri->v[2] - tri->v[0]; + + tri->plane.ToVec4().ToVec3().Cross( d2, d1 ); + tri->plane.ToVec4().ToVec3().Normalize(); + tri->plane[3] = - ( tri->v[0] * tri->plane.ToVec4().ToVec3() ); + + // get the plane number before any clipping + // we should avoid polluting the regular dmap planes with these + // that are offset from the light origin... + tri->planeNum = FindFloatPlane( tri->plane ); + + CreateEdgesForTri( tri ); + } + + // clear our output buffer + numOutputTris = 0; + + // for each triangle, clip against all other triangles + int numRemoved = 0; + int numComplete = 0; + int numFragmented = 0; + + for ( i = 0 ; i < numTris ; i++ ) { + int oldOutput = numOutputTris; + c_removedFragments = 0; + ClipTriangle_r( &tris[i], 0, i, numTris, tris ); + if ( numOutputTris == oldOutput ) { + numRemoved++; // completely unused + } else if ( c_removedFragments == 0 ) { + // the entire triangle is visible + numComplete++; + shadowTri_t *out = &outputTris[oldOutput]; + *out = tris[i]; + numOutputTris = oldOutput+1; + } else { + numFragmented++; + // we made at least one fragment + + // if we are at the low optimization level, just use a single + // triangle if it produced any fragments + if ( dmapGlobals.shadowOptLevel == SO_CULL_OCCLUDED ) { + shadowTri_t *out = &outputTris[oldOutput]; + *out = tris[i]; + numOutputTris = oldOutput+1; + } + } + } + common->Printf( "%i triangles completely invisible\n", numRemoved ); + common->Printf( "%i triangles completely visible\n", numComplete ); + common->Printf( "%i triangles fragmented\n", numFragmented ); + common->Printf( "%i shadowing fragments before optimization\n", numOutputTris ); +} + +//===================================================================================== + +/* +================ +OptimizeOutputTris +================ +*/ +static void OptimizeOutputTris( void ) { + int i; + + // optimize the clipped surfaces + optimizeGroup_t *optGroups = NULL; + optimizeGroup_t *checkGroup; + + for ( i = 0 ; i < numOutputTris ; i++ ) { + shadowTri_t *tri = &outputTris[i]; + + int planeNum = tri->planeNum; + + // add it to an optimize group + for ( checkGroup = optGroups ; checkGroup ; checkGroup = checkGroup->nextGroup ) { + if ( checkGroup->planeNum == planeNum ) { + break; + } + } + if ( !checkGroup ) { + // create a new optGroup + checkGroup = (optimizeGroup_t *)Mem_ClearedAlloc( sizeof( *checkGroup ) ); + checkGroup->planeNum = planeNum; + checkGroup->nextGroup = optGroups; + optGroups = checkGroup; + } + + // create a mapTri for the optGroup + mapTri_t *mtri = (mapTri_t *)Mem_ClearedAlloc( sizeof( *mtri ) ); + mtri->v[0].xyz = tri->v[0]; + mtri->v[1].xyz = tri->v[1]; + mtri->v[2].xyz = tri->v[2]; + mtri->next = checkGroup->triList; + checkGroup->triList = mtri; + } + + OptimizeGroupList( optGroups ); + + numOutputTris = 0; + for ( checkGroup = optGroups ; checkGroup ; checkGroup = checkGroup->nextGroup ) { + for ( mapTri_t *mtri = checkGroup->triList ; mtri ; mtri = mtri->next ) { + shadowTri_t *tri = &outputTris[numOutputTris]; + numOutputTris++; + tri->v[0] = mtri->v[0].xyz; + tri->v[1] = mtri->v[1].xyz; + tri->v[2] = mtri->v[2].xyz; + } + } + FreeOptimizeGroupList( optGroups ); +} + +//================================================================================== + +static int EdgeSort( const void *a, const void *b ) { + if ( *(unsigned *)a < *(unsigned *)b ) { + return -1; + } + if ( *(unsigned *)a > *(unsigned *)b ) { + return 1; + } + return 0; +} + +/* +===================== +GenerateSilEdges + +Output tris must be tjunction fixed and vertex uniqued +A edge that is not exactly matched is a silhouette edge +We could skip this and rely completely on the matched quad removal +for all sil edges, but this will avoid the bulk of the checks. +===================== +*/ +static void GenerateSilEdges( void ) { + int i, j; + + unsigned *edges = (unsigned *)_alloca( (numOutputTris*3+1)*sizeof(*edges) ); + int numEdges = 0; + + numSilEdges = 0; + + for ( i = 0 ; i < numOutputTris ; i++ ) { + int a = outputTris[i].index[0]; + int b = outputTris[i].index[1]; + int c = outputTris[i].index[2]; + if ( a == b || a == c || b == c ) { + continue; // degenerate + } + + for ( j = 0 ; j < 3 ; j++ ) { + int v1, v2; + + v1 = outputTris[i].index[j]; + v2 = outputTris[i].index[(j+1)%3]; + if ( v1 == v2 ) { + continue; // degenerate + } + if ( v1 > v2 ) { + edges[numEdges] = ( v1 << 16 ) | ( v2 << 1 ); + } else { + edges[numEdges] = ( v2 << 16 ) | ( v1 << 1 ) | 1; + } + numEdges++; + } + } + + qsort( edges, numEdges, sizeof( edges[0] ), EdgeSort ); + edges[numEdges] = -1; // force the last to make an edge if no matched to previous + + for ( i = 0 ; i < numEdges ; i++ ) { + if ( ( edges[i] ^ edges[i+1] ) == 1 ) { + // skip the next one, because we matched and + // removed both + i++; + continue; + } + // this is an unmatched edge, so we need to generate a sil plane + int v1, v2; + if ( edges[i] & 1 ) { + v2 = edges[i] >> 16; + v1 = ( edges[i] >> 1 ) & 0x7fff; + } else { + v1 = edges[i] >> 16; + v2 = ( edges[i] >> 1 ) & 0x7fff; + } + + if ( numSilEdges == MAX_SIL_EDGES ) { + common->Error( "numSilEdges == MAX_SIL_EDGES" ); + } + silEdges[numSilEdges].index[0] = v1; + silEdges[numSilEdges].index[1] = v2; + numSilEdges++; + } +} + +//================================================================================== + +/* +===================== +GenerateSilPlanes + +Groups the silEdges into common planes +===================== +*/ +void GenerateSilPlanes( void ) { + numSilPlanes = 0; + silPlanes = (silPlane_t *)Mem_Alloc( sizeof( *silPlanes ) * numSilEdges ); + + // identify the silPlanes + numSilPlanes = 0; + for ( int i = 0 ; i < numSilEdges ; i++ ) { + if ( silEdges[i].index[0] == silEdges[i].index[1] ) { + continue; // degenerate + } + + idVec3 &v1 = uniqued[silEdges[i].index[0]]; + idVec3 &v2 = uniqued[silEdges[i].index[1]]; + + // search for an existing plane + int j; + for ( j = 0 ; j < numSilPlanes ; j++ ) { + float d = v1 * silPlanes[j].normal; + float d2 = v2 * silPlanes[j].normal; + + if ( fabs( d ) < EDGE_PLANE_EPSILON + && fabs( d2 ) < EDGE_PLANE_EPSILON ) { + silEdges[i].nextEdge = silPlanes[j].edges; + silPlanes[j].edges = &silEdges[i]; + break; + } + } + + if ( j == numSilPlanes ) { + // create a new silPlane + silPlanes[j].normal.Cross( v2, v1 ); + silPlanes[j].normal.Normalize(); + silEdges[i].nextEdge = NULL; + silPlanes[j].edges = &silEdges[i]; + silPlanes[j].fragmentedQuads = NULL; + numSilPlanes++; + } + } +} + +//================================================================================== + +/* +============= +SaveQuad +============= +*/ +static void SaveQuad( silPlane_t *silPlane, silQuad_t &quad ) { + // this fragment is a final fragment + if ( numSilQuads == MAX_SIL_QUADS ) { + common->Error( "numSilQuads == MAX_SIL_QUADS" ); + } + silQuads[numSilQuads] = quad; + silQuads[numSilQuads].nextQuad = silPlane->fragmentedQuads; + silPlane->fragmentedQuads = &silQuads[numSilQuads]; + numSilQuads++; +} + + +/* +=================== +FragmentSilQuad + +Clip quads, or reconstruct? +Generate them T-junction free, or require another pass of fix-tjunc? +Call optimizer on a per-sil-plane basis? + will this ever introduce tjunctions with the front faces? + removal of planes can allow the rear projection to be farther optimized + +For quad clipping + PlaneThroughEdge + +quad clipping introduces new vertexes + +Cannot just fragment edges, must emit full indexes + +what is the bounds on max indexes? + the worst case is that all edges but one carve an existing edge in the middle, + giving twice the input number of indexes (I think) + +can we avoid knowing about projected positions and still optimize? + +Fragment all edges first +Introduces T-junctions +create additional silEdges, linked to silPlanes + +In theory, we should never have more than one edge clipping a given +fragment, but it is more robust if we check them all +=================== +*/ +static void FragmentSilQuad( silQuad_t quad, silPlane_t *silPlane, + shadowOptEdge_t *startEdge, shadowOptEdge_t *skipEdge ) { + if ( quad.nearV[0] == quad.nearV[1] ) { + return; + } + + for ( shadowOptEdge_t *check = startEdge ; check ; check = check->nextEdge ) { + if ( check == skipEdge ) { + // don't clip against self + continue; + } + + if ( check->index[0] == check->index[1] ) { + continue; + } + + // make planes through both points of check + for ( int i = 0 ; i < 2 ; i++ ) { + idVec3 plane; + + plane.Cross( uniqued[check->index[i]], silPlane->normal ); + plane.Normalize(); + + if ( plane.Length() < 0.9 ) { + continue; + } + + // if the other point on check isn't on the negative side of the plane, + // flip the plane + if ( uniqued[check->index[!i]] * plane > 0 ) { + plane = -plane; + } + + float d1 = uniqued[quad.nearV[0]] * plane; + float d2 = uniqued[quad.nearV[1]] * plane; + + float d3 = uniqued[quad.farV[0]] * plane; + float d4 = uniqued[quad.farV[1]] * plane; + + // it is better to conservatively NOT split the quad, which, at worst, + // will leave some extra overdraw + + // if the plane divides the incoming edge, split it and recurse + // with the outside fraction before continuing with the inside fraction + if ( ( d1 > EDGE_PLANE_EPSILON && d3 > EDGE_PLANE_EPSILON && d2 < -EDGE_PLANE_EPSILON && d4 < -EDGE_PLANE_EPSILON ) + || ( d2 > EDGE_PLANE_EPSILON && d4 > EDGE_PLANE_EPSILON && d1 < -EDGE_PLANE_EPSILON && d3 < -EDGE_PLANE_EPSILON ) ) { + float f = d1 / ( d1 - d2 ); + float f2 = d3 / ( d3 - d4 ); +f = f2; + if ( f <= 0.0001 || f >= 0.9999 ) { + common->Error( "Bad silQuad fraction" ); + } + + // finding uniques may be causing problems here + idVec3 nearMid = (1-f) * uniqued[quad.nearV[0]] + f * uniqued[quad.nearV[1]]; + int nearMidIndex = FindUniqueVert( nearMid ); + idVec3 farMid = (1-f) * uniqued[quad.farV[0]] + f * uniqued[quad.farV[1]]; + int farMidIndex = FindUniqueVert( farMid ); + + silQuad_t clipped = quad; + + if ( d1 > EDGE_PLANE_EPSILON ) { + clipped.nearV[1] = nearMidIndex; + clipped.farV[1] = farMidIndex; + FragmentSilQuad( clipped, silPlane, check->nextEdge, skipEdge ); + quad.nearV[0] = nearMidIndex; + quad.farV[0] = farMidIndex; + } else { + clipped.nearV[0] = nearMidIndex; + clipped.farV[0] = farMidIndex; + FragmentSilQuad( clipped, silPlane, check->nextEdge, skipEdge ); + quad.nearV[1] = nearMidIndex; + quad.farV[1] = farMidIndex; + } + } + } + + // make a plane through the line of check + idPlane separate; + + idVec3 dir = uniqued[check->index[1]] - uniqued[check->index[0]]; + separate.Normal().Cross( dir, silPlane->normal ); + separate.Normal().Normalize(); + separate.ToVec4()[3] = -(uniqued[check->index[1]] * separate.Normal()); + + // this may miss a needed separation when the quad would be + // clipped into a triangle and a quad + float d1 = separate.Distance( uniqued[quad.nearV[0]] ); + float d2 = separate.Distance( uniqued[quad.farV[0]] ); + + if ( ( d1 < EDGE_PLANE_EPSILON && d2 < EDGE_PLANE_EPSILON ) + || ( d1 > -EDGE_PLANE_EPSILON && d2 > -EDGE_PLANE_EPSILON ) ) { + continue; + } + + // split the quad at this plane + float f = d1 / ( d1 - d2 ); + idVec3 mid0 = (1-f) * uniqued[quad.nearV[0]] + f * uniqued[quad.farV[0]]; + int mid0Index = FindUniqueVert( mid0 ); + + d1 = separate.Distance( uniqued[quad.nearV[1]] ); + d2 = separate.Distance( uniqued[quad.farV[1]] ); + f = d1 / ( d1 - d2 ); + if ( f < 0 || f > 1 ) { + continue; + } + + idVec3 mid1 = (1-f) * uniqued[quad.nearV[1]] + f * uniqued[quad.farV[1]]; + int mid1Index = FindUniqueVert( mid1 ); + + silQuad_t clipped = quad; + + clipped.nearV[0] = mid0Index; + clipped.nearV[1] = mid1Index; + FragmentSilQuad( clipped, silPlane, check->nextEdge, skipEdge ); + quad.farV[0] = mid0Index; + quad.farV[1] = mid1Index; + } + + SaveQuad( silPlane, quad ); +} + + +/* +=============== +FragmentSilQuads +=============== +*/ +static void FragmentSilQuads( void ) { + // group the edges into common planes + GenerateSilPlanes(); + + numSilQuads = 0; + + // fragment overlapping edges + for ( int i = 0 ; i < numSilPlanes ; i++ ) { + silPlane_t *sil = &silPlanes[i]; + + for ( shadowOptEdge_t *e1 = sil->edges ; e1 ; e1 = e1->nextEdge ) { + silQuad_t quad; + + quad.nearV[0] = e1->index[0]; + quad.nearV[1] = e1->index[1]; + if ( e1->index[0] == e1->index[1] ) { + common->Error( "FragmentSilQuads: degenerate edge" ); + } + quad.farV[0] = e1->index[0] + numUniquedBeforeProjection; + quad.farV[1] = e1->index[1] + numUniquedBeforeProjection; + FragmentSilQuad( quad, sil, sil->edges, e1 ); + } + } +} + +//======================================================================= + +/* +===================== +EmitFragmentedSilQuads + +===================== +*/ +static void EmitFragmentedSilQuads( void ) { + int i, j, k; + mapTri_t *mtri; + + for ( i = 0 ; i < numSilPlanes ; i++ ) { + silPlane_t *sil = &silPlanes[i]; + + // prepare for optimizing the sil quads on each side of the sil plane + optimizeGroup_t groups[2]; + memset( &groups, 0, sizeof( groups ) ); + idPlane planes[2]; + planes[0].Normal() = sil->normal; + planes[0][3] = 0; + planes[1] = -planes[0]; + groups[0].planeNum = FindFloatPlane( planes[0] ); + groups[1].planeNum = FindFloatPlane( planes[1] ); + + // emit the quads that aren't matched + for ( silQuad_t *f1 = sil->fragmentedQuads ; f1 ; f1 = f1->nextQuad ) { + silQuad_t *f2; + for ( f2 = sil->fragmentedQuads ; f2 ; f2 = f2->nextQuad ) { + if ( f2 == f1 ) { + continue; + } + // in theory, this is sufficient, but we might + // have some cases of tripple+ matching, or unclipped rear projections + if ( f1->nearV[0] == f2->nearV[1] && f1->nearV[1] == f2->nearV[0] ) { + break; + } + } + // if we went through all the quads without finding a match, emit the quad + if ( !f2 ) { + optimizeGroup_t *gr; + idVec3 v1, v2, normal; + + mtri = (mapTri_t *)Mem_ClearedAlloc( sizeof( *mtri ) ); + mtri->v[0].xyz = uniqued[f1->nearV[0]]; + mtri->v[1].xyz = uniqued[f1->nearV[1]]; + mtri->v[2].xyz = uniqued[f1->farV[1]]; + + v1 = mtri->v[1].xyz - mtri->v[0].xyz; + v2 = mtri->v[2].xyz - mtri->v[0].xyz; + normal.Cross( v2, v1 ); + + if ( normal * planes[0].Normal() > 0 ) { + gr = &groups[0]; + } else { + gr = &groups[1]; + } + + mtri->next = gr->triList; + gr->triList = mtri; + + mtri = (mapTri_t *)Mem_ClearedAlloc( sizeof( *mtri ) ); + mtri->v[0].xyz = uniqued[f1->farV[0]]; + mtri->v[1].xyz = uniqued[f1->nearV[0]]; + mtri->v[2].xyz = uniqued[f1->farV[1]]; + + mtri->next = gr->triList; + gr->triList = mtri; + +#if 0 + // emit a sil quad all the way to the projection plane + int index = ret.totalIndexes; + if ( index + 6 > maxRetIndexes ) { + common->Error( "maxRetIndexes exceeded" ); + } + ret.indexes[index+0] = f1->nearV[0]; + ret.indexes[index+1] = f1->nearV[1]; + ret.indexes[index+2] = f1->farV[1]; + ret.indexes[index+3] = f1->farV[0]; + ret.indexes[index+4] = f1->nearV[0]; + ret.indexes[index+5] = f1->farV[1]; + ret.totalIndexes += 6; +#endif + } + } + + + // optimize + for ( j = 0 ; j < 2 ; j++ ) { + if ( !groups[j].triList ) { + continue; + } + if ( dmapGlobals.shadowOptLevel == SO_SIL_OPTIMIZE ) { + OptimizeGroupList( &groups[j] ); + } + // add as indexes + for ( mtri = groups[j].triList ; mtri ; mtri = mtri->next ) { + for ( k = 0 ; k < 3 ; k++ ) { + if ( ret.totalIndexes == maxRetIndexes ) { + common->Error( "maxRetIndexes exceeded" ); + } + ret.indexes[ret.totalIndexes] = FindUniqueVert( mtri->v[k].xyz ); + ret.totalIndexes++; + } + } + FreeTriList( groups[j].triList ); + } + } + + // we don't need the silPlane grouping anymore + Mem_Free( silPlanes ); +} + +/* +================= +EmitUnoptimizedSilEdges +================= +*/ +static void EmitUnoptimizedSilEdges( void ) { + int i; + + for ( i = 0 ; i < numSilEdges ; i++ ) { + int v1 = silEdges[i].index[0]; + int v2 = silEdges[i].index[1]; + int index = ret.totalIndexes; + ret.indexes[index+0] = v1; + ret.indexes[index+1] = v2; + ret.indexes[index+2] = v2+numUniquedBeforeProjection; + ret.indexes[index+3] = v1+numUniquedBeforeProjection; + ret.indexes[index+4] = v1; + ret.indexes[index+5] = v2+numUniquedBeforeProjection; + ret.totalIndexes += 6; + } +} + +//================================================================================== + +/* +================ +FindUniqueVert +================ +*/ +static int FindUniqueVert( idVec3 &v ) { + int k; + + for ( k = 0 ; k < numUniqued ; k++ ) { + idVec3 &check = uniqued[k]; + if ( fabs( v[0] - check[0] ) < UNIQUE_EPSILON + && fabs( v[1] - check[1] ) < UNIQUE_EPSILON + && fabs( v[2] - check[2] ) < UNIQUE_EPSILON ) { + return k; + } + } + if ( numUniqued == maxUniqued ) { + common->Error( "FindUniqueVert: numUniqued == maxUniqued" ); + } + uniqued[numUniqued] = v; + numUniqued++; + + return k; +} + +/* +=================== +UniqueVerts + +Snaps all triangle verts together, setting tri->index[] +and generating numUniqued and uniqued. +These are still in projection-centered space, not global space +=================== +*/ +static void UniqueVerts( void ) { + int i, j; + + // we may add to uniqued later when splitting sil edges, so leave + // some extra room + maxUniqued = 100000; // numOutputTris * 10 + 1000; + uniqued = (idVec3 *)Mem_Alloc( sizeof( *uniqued ) * maxUniqued ); + numUniqued = 0; + + for ( i = 0 ; i < numOutputTris ; i++ ) { + for ( j = 0 ; j < 3 ; j++ ) { + outputTris[i].index[j] = FindUniqueVert( outputTris[i].v[j] ); + } + } +} + +/* +====================== +ProjectUniqued +====================== +*/ +static void ProjectUniqued( idVec3 projectionOrigin, idPlane projectionPlane ) { + // calculate the projection + idVec4 mat[4]; + + renderSystem->LightProjectionMatrix( projectionOrigin, projectionPlane, mat ); + + if ( numUniqued * 2 > maxUniqued ) { + common->Error( "ProjectUniqued: numUniqued * 2 > maxUniqued" ); + } + + // this is goofy going back and forth between the spaces, + // but I don't want to change R_LightProjectionMatrix righ tnow... + for ( int i = 0 ; i < numUniqued ; i++ ) { + // put the vert back in global space, instead of light centered space + idVec3 in = uniqued[i] + projectionOrigin; + + // project to far plane + float w, oow; + idVec3 out; + + w = in * mat[3].ToVec3() + mat[3][3]; + + oow = 1.0 / w; + out.x = ( in * mat[0].ToVec3() + mat[0][3] ) * oow; + out.y = ( in * mat[1].ToVec3() + mat[1][3] ) * oow; + out.z = ( in * mat[2].ToVec3() + mat[2][3] ) * oow; + + uniqued[numUniqued+i] = out - projectionOrigin; + } + numUniqued *= 2; +} + +/* +==================== +SuperOptimizeOccluders + +This is the callback from the renderer shadow generation routine, after +verts have been culled against individual frustums of point lights + +==================== +*/ +optimizedShadow_t SuperOptimizeOccluders( idVec4 *verts, glIndex_t *indexes, int numIndexes, + idPlane projectionPlane, idVec3 projectionOrigin ) +{ + memset( &ret, 0, sizeof( ret ) ); + + // generate outputTris, removing fragments that are occluded by closer fragments + ClipOccluders( verts, indexes, numIndexes, projectionOrigin ); + + if ( dmapGlobals.shadowOptLevel >= SO_CULL_OCCLUDED ) { + OptimizeOutputTris(); + } + + // match up common verts + UniqueVerts(); + + // now that we have uniqued the vertexes, we can find unmatched + // edges, which are silhouette planes + GenerateSilEdges(); + + // generate the projected verts + numUniquedBeforeProjection = numUniqued; + ProjectUniqued( projectionOrigin, projectionPlane ); + + // fragment the sil edges where the overlap, + // possibly generating some additional unique verts + if ( dmapGlobals.shadowOptLevel >= SO_CLIP_SILS ) { + FragmentSilQuads(); + } + + // indexes for face and projection caps + ret.numFrontCapIndexes = numOutputTris * 3; + ret.numRearCapIndexes = numOutputTris * 3; + if ( dmapGlobals.shadowOptLevel >= SO_CLIP_SILS ) { + ret.numSilPlaneIndexes = numSilQuads * 12; // this is the worst case with clipping + } else { + ret.numSilPlaneIndexes = numSilEdges * 6; // this is the worst case with clipping + } + + ret.totalIndexes = 0; + + maxRetIndexes = ret.numFrontCapIndexes + ret.numRearCapIndexes + ret.numSilPlaneIndexes; + + ret.indexes = (glIndex_t *)Mem_Alloc( maxRetIndexes * sizeof( ret.indexes[0] ) ); + for ( int i = 0 ; i < numOutputTris ; i++ ) { + // flip the indexes so the surface triangle faces outside the shadow volume + ret.indexes[i*3+0] = outputTris[i].index[2]; + ret.indexes[i*3+1] = outputTris[i].index[1]; + ret.indexes[i*3+2] = outputTris[i].index[0]; + + ret.indexes[(numOutputTris+i)*3+0] = numUniquedBeforeProjection + outputTris[i].index[0]; + ret.indexes[(numOutputTris+i)*3+1] = numUniquedBeforeProjection + outputTris[i].index[1]; + ret.indexes[(numOutputTris+i)*3+2] = numUniquedBeforeProjection + outputTris[i].index[2]; + } + // emit the sil planes + ret.totalIndexes = ret.numFrontCapIndexes + ret.numRearCapIndexes; + + if ( dmapGlobals.shadowOptLevel >= SO_CLIP_SILS ) { + // re-optimize the sil planes, cutting + EmitFragmentedSilQuads(); + } else { + // indexes for silhouette edges + EmitUnoptimizedSilEdges(); + } + + // we have all the verts now + // create twice the uniqued verts + ret.numVerts = numUniqued; + ret.verts = (idVec3 *)Mem_Alloc( ret.numVerts * sizeof( ret.verts[0] ) ); + for ( int i = 0 ; i < numUniqued ; i++ ) { + // put the vert back in global space, instead of light centered space + ret.verts[i] = uniqued[i] + projectionOrigin; + } + + // set the final index count + ret.numSilPlaneIndexes = ret.totalIndexes - (ret.numFrontCapIndexes + ret.numRearCapIndexes); + + // free out local data + Mem_Free( uniqued ); + + return ret; +} + +/* +================= +RemoveDegenerateTriangles +================= +*/ +static void RemoveDegenerateTriangles( srfTriangles_t *tri ) { + int c_removed; + int i; + int a, b, c; + + // check for completely degenerate triangles + c_removed = 0; + for ( i = 0 ; i < tri->numIndexes ; i+=3 ) { + a = tri->indexes[i]; + b = tri->indexes[i+1]; + c = tri->indexes[i+2]; + if ( a == b || a == c || b == c ) { + c_removed++; + memmove( tri->indexes + i, tri->indexes + i + 3, ( tri->numIndexes - i - 3 ) * sizeof( tri->indexes[0] ) ); + tri->numIndexes -= 3; + if ( i < tri->numShadowIndexesNoCaps ) { + tri->numShadowIndexesNoCaps -= 3; + } + if ( i < tri->numShadowIndexesNoFrontCaps ) { + tri->numShadowIndexesNoFrontCaps -= 3; + } + i -= 3; + } + } + + // this doesn't free the memory used by the unused verts + + if ( c_removed ) { + common->Printf( "removed %i degenerate triangles from shadow\n", c_removed ); + } +} + +/* +==================== +CleanupOptimizedShadowTris + +Uniques all verts across the frustums +removes matched sil quads at frustum seams +removes degenerate tris +==================== +*/ +void CleanupOptimizedShadowTris( srfTriangles_t *tri ) { + int i; + + // unique all the verts + maxUniqued = tri->numVerts; + uniqued = (idVec3 *)_alloca( sizeof( *uniqued ) * maxUniqued ); + numUniqued = 0; + + glIndex_t *remap = (glIndex_t *)_alloca( sizeof( *remap ) * tri->numVerts ); + + for ( i = 0 ; i < tri->numIndexes ; i++ ) { + if ( tri->indexes[i] > tri->numVerts || tri->indexes[i] < 0 ) { + common->Error( "CleanupOptimizedShadowTris: index out of range" ); + } + } + + for ( i = 0 ; i < tri->numVerts ; i++ ) { + remap[i] = FindUniqueVert( tri->shadowVertexes[i].xyz.ToVec3() ); + } + tri->numVerts = numUniqued; + for ( i = 0 ; i < tri->numVerts ; i++ ) { + tri->shadowVertexes[i].xyz.ToVec3() = uniqued[i]; + tri->shadowVertexes[i].xyz[3] = 1; + } + + for ( i = 0 ; i < tri->numIndexes ; i++ ) { + tri->indexes[i] = remap[tri->indexes[i]]; + } + + // remove matched quads + int numSilIndexes = tri->numShadowIndexesNoCaps; + for ( int i = 0 ; i < numSilIndexes ; i+=6 ) { + int j; + for ( j = i+6 ; j < numSilIndexes ; j+=6 ) { + // if there is a reversed quad match, we can throw both of them out + // this is not a robust check, it relies on the exact ordering of + // quad indexes + if ( tri->indexes[i+0] == tri->indexes[j+1] + && tri->indexes[i+1] == tri->indexes[j+0] + && tri->indexes[i+2] == tri->indexes[j+3] + && tri->indexes[i+3] == tri->indexes[j+5] + && tri->indexes[i+4] == tri->indexes[j+1] + && tri->indexes[i+5] == tri->indexes[j+3] ) { + break; + } + } + if ( j == numSilIndexes ) { + continue; + } + int k; + // remove first quad + for ( k = i+6 ; k < j ; k++ ) { + tri->indexes[k-6] = tri->indexes[k]; + } + // remove second quad + for ( k = j+6 ; k < tri->numIndexes ; k++ ) { + tri->indexes[k-12] = tri->indexes[k]; + } + numSilIndexes -= 12; + i -= 6; + } + + int removed = tri->numShadowIndexesNoCaps - numSilIndexes; + + tri->numIndexes -= removed; + tri->numShadowIndexesNoCaps -= removed; + tri->numShadowIndexesNoFrontCaps -= removed; + + // remove degenerates after we have removed quads, so the double + // triangle pairing isn't disturbed + RemoveDegenerateTriangles( tri ); +} + +/* +======================== +CreateLightShadow + +This is called from dmap in util/surface.cpp +shadowerGroups should be exactly clipped to the light frustum before calling. +shadowerGroups is optimized by this function, but the contents can be freed, because the returned +lightShadow_t list is a further culling and optimization of the data. +======================== +*/ +srfTriangles_t *CreateLightShadow( optimizeGroup_t *shadowerGroups, const mapLight_t *light ) {; + + common->Printf( "----- CreateLightShadow %p -----\n", light ); + + // optimize all the groups + OptimizeGroupList( shadowerGroups ); + + // combine all the triangles into one list + mapTri_t *combined; + + combined = NULL; + for ( optimizeGroup_t *group = shadowerGroups ; group ; group = group->nextGroup ) { + combined = MergeTriLists( combined, CopyTriList( group->triList ) ); + } + + if ( !combined ) { + return NULL; + } + + // find uniqued vertexes + srfTriangles_t *occluders = ShareMapTriVerts( combined ); + + FreeTriList( combined ); + + // find silhouette information for the triSurf + renderModelManager->CleanupTriangles( occluders, false, true, false, false ); + + // call the normal shadow creation, but with the superOptimize flag set, which will + // call back to SuperOptimizeOccluders after clipping the triangles to each frustum + srfTriangles_t *shadowTris; + if ( dmapGlobals.shadowOptLevel == SO_MERGE_SURFACES ) { + shadowTris = renderModelManager->CreateShadowVolume( occluders, light->def, SG_STATIC ); + } else { + shadowTris = renderModelManager->CreateShadowVolume( occluders, light->def, SG_OFFLINE ); + } + renderModelManager->FreeStaticTriSurf( occluders ); + + if ( shadowTris ) { + dmapGlobals.totalShadowTriangles += shadowTris->numIndexes / 3; + dmapGlobals.totalShadowVerts += shadowTris->numVerts / 3; + } + + return shadowTris; +} diff --git a/src/tools/compilers/dmap/tritjunction.cpp b/src/tools/compilers/dmap/tritjunction.cpp new file mode 100644 index 0000000..e5b709a --- /dev/null +++ b/src/tools/compilers/dmap/tritjunction.cpp @@ -0,0 +1,663 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../../idlib/precompiled.h" +#pragma hdrstop + +#include "dmap.h" + +/* + + T junction fixing never creates more xyz points, but + new vertexes will be created when different surfaces + cause a fix + + The vertex cleaning accomplishes two goals: removing extranious low order + bits to avoid numbers like 1.000001233, and grouping nearby vertexes + together. Straight truncation accomplishes the first foal, but two vertexes + only a tiny epsilon apart could still be spread to different snap points. + To avoid this, we allow the merge test to group points together that + snapped to neighboring integer coordinates. + + Snaping verts can drag some triangles backwards or collapse them to points, + which will cause them to be removed. + + + When snapping to ints, a point can move a maximum of sqrt(3)/2 distance + Two points that were an epsilon apart can then become sqrt(3) apart + + A case that causes recursive overflow with point to triangle fixing: + + A + C D + B + + Triangle ABC tests against point D and splits into triangles ADC and DBC + Triangle DBC then tests against point A again and splits into ABC and ADB + infinite recursive loop + + + For a given source triangle + init the no-check list to hold the three triangle hashVerts + + recursiveFixTriAgainstHash + + recursiveFixTriAgainstHashVert_r + if hashVert is on the no-check list + exit + if the hashVert should split the triangle + add to the no-check list + recursiveFixTriAgainstHash(a) + recursiveFixTriAgainstHash(b) + +*/ + +#define SNAP_FRACTIONS 32 +//#define SNAP_FRACTIONS 8 +//#define SNAP_FRACTIONS 1 + +#define VERTEX_EPSILON ( 1.0 / SNAP_FRACTIONS ) + +#define COLINEAR_EPSILON ( 1.8 * VERTEX_EPSILON ) + +#define HASH_BINS 16 + +typedef struct hashVert_s { + struct hashVert_s *next; + idVec3 v; + int iv[3]; +} hashVert_t; + +static idBounds hashBounds; +static idVec3 hashScale; +static hashVert_t *hashVerts[HASH_BINS][HASH_BINS][HASH_BINS]; +static int numHashVerts, numTotalVerts; +static int hashIntMins[3], hashIntScale[3]; + +/* +=============== +GetHashVert + +Also modifies the original vert to the snapped value +=============== +*/ +struct hashVert_s *GetHashVert( idVec3 &v ) { + int iv[3]; + int block[3]; + int i; + hashVert_t *hv; + + numTotalVerts++; + + // snap the vert to integral values + for ( i = 0 ; i < 3 ; i++ ) { + iv[i] = floor( ( v[i] + 0.5/SNAP_FRACTIONS ) * SNAP_FRACTIONS ); + block[i] = ( iv[i] - hashIntMins[i] ) / hashIntScale[i]; + if ( block[i] < 0 ) { + block[i] = 0; + } else if ( block[i] >= HASH_BINS ) { + block[i] = HASH_BINS - 1; + } + } + + // see if a vertex near enough already exists + // this could still fail to find a near neighbor right at the hash block boundary + for ( hv = hashVerts[block[0]][block[1]][block[2]] ; hv ; hv = hv->next ) { +#if 0 + if ( hv->iv[0] == iv[0] && hv->iv[1] == iv[1] && hv->iv[2] == iv[2] ) { + VectorCopy( hv->v, v ); + return hv; + } +#else + for ( i = 0 ; i < 3 ; i++ ) { + int d; + d = hv->iv[i] - iv[i]; + if ( d < -1 || d > 1 ) { + break; + } + } + if ( i == 3 ) { + VectorCopy( hv->v, v ); + return hv; + } +#endif + } + + // create a new one + hv = (hashVert_t *)Mem_Alloc( sizeof( *hv ) ); + + hv->next = hashVerts[block[0]][block[1]][block[2]]; + hashVerts[block[0]][block[1]][block[2]] = hv; + + hv->iv[0] = iv[0]; + hv->iv[1] = iv[1]; + hv->iv[2] = iv[2]; + + hv->v[0] = (float)iv[0] / SNAP_FRACTIONS; + hv->v[1] = (float)iv[1] / SNAP_FRACTIONS; + hv->v[2] = (float)iv[2] / SNAP_FRACTIONS; + + VectorCopy( hv->v, v ); + + numHashVerts++; + + return hv; +} + + +/* +================== +HashBlocksForTri + +Returns an inclusive bounding box of hash +bins that should hold the triangle +================== +*/ +static void HashBlocksForTri( const mapTri_t *tri, int blocks[2][3] ) { + idBounds bounds; + int i; + + bounds.Clear(); + bounds.AddPoint( tri->v[0].xyz ); + bounds.AddPoint( tri->v[1].xyz ); + bounds.AddPoint( tri->v[2].xyz ); + + // add a 1.0 slop margin on each side + for ( i = 0 ; i < 3 ; i++ ) { + blocks[0][i] = ( bounds[0][i] - 1.0 - hashBounds[0][i] ) / hashScale[i]; + if ( blocks[0][i] < 0 ) { + blocks[0][i] = 0; + } else if ( blocks[0][i] >= HASH_BINS ) { + blocks[0][i] = HASH_BINS - 1; + } + + blocks[1][i] = ( bounds[1][i] + 1.0 - hashBounds[0][i] ) / hashScale[i]; + if ( blocks[1][i] < 0 ) { + blocks[1][i] = 0; + } else if ( blocks[1][i] >= HASH_BINS ) { + blocks[1][i] = HASH_BINS - 1; + } + } +} + + +/* +================= +HashTriangles + +Removes triangles that are degenerated or flipped backwards +================= +*/ +void HashTriangles( optimizeGroup_t *groupList ) { + mapTri_t *a; + int vert; + int i; + optimizeGroup_t *group; + + // clear the hash tables + memset( hashVerts, 0, sizeof( hashVerts ) ); + + numHashVerts = 0; + numTotalVerts = 0; + + // bound all the triangles to determine the bucket size + hashBounds.Clear(); + for ( group = groupList ; group ; group = group->nextGroup ) { + for ( a = group->triList ; a ; a = a->next ) { + hashBounds.AddPoint( a->v[0].xyz ); + hashBounds.AddPoint( a->v[1].xyz ); + hashBounds.AddPoint( a->v[2].xyz ); + } + } + + // spread the bounds so it will never have a zero size + for ( i = 0 ; i < 3 ; i++ ) { + hashBounds[0][i] = floor( hashBounds[0][i] - 1 ); + hashBounds[1][i] = ceil( hashBounds[1][i] + 1 ); + hashIntMins[i] = hashBounds[0][i] * SNAP_FRACTIONS; + + hashScale[i] = ( hashBounds[1][i] - hashBounds[0][i] ) / HASH_BINS; + hashIntScale[i] = hashScale[i] * SNAP_FRACTIONS; + if ( hashIntScale[i] < 1 ) { + hashIntScale[i] = 1; + } + } + + // add all the points to the hash buckets + for ( group = groupList ; group ; group = group->nextGroup ) { + // don't create tjunctions against discrete surfaces (blood decals, etc) + if ( group->material != NULL && group->material->IsDiscrete() ) { + continue; + } + for ( a = group->triList ; a ; a = a->next ) { + for ( vert = 0 ; vert < 3 ; vert++ ) { + a->hashVert[vert] = GetHashVert( a->v[vert].xyz ); + } + } + } +} + +/* +================= +FreeTJunctionHash + +The optimizer may add some more crossing verts +after t junction processing +================= +*/ +void FreeTJunctionHash( void ) { + int i, j, k; + hashVert_t *hv, *next; + + for ( i = 0 ; i < HASH_BINS ; i++ ) { + for ( j = 0 ; j < HASH_BINS ; j++ ) { + for ( k = 0 ; k < HASH_BINS ; k++ ) { + for ( hv = hashVerts[i][j][k] ; hv ; hv = next ) { + next = hv->next; + Mem_Free( hv ); + } + } + } + } + memset( hashVerts, 0, sizeof( hashVerts ) ); +} + + +/* +================== +FixTriangleAgainstHashVert + +Returns a list of two new mapTri if the hashVert is +on an edge of the given mapTri, otherwise returns NULL. +================== +*/ +static mapTri_t *FixTriangleAgainstHashVert( const mapTri_t *a, const hashVert_t *hv ) { + int i; + const idDrawVert *v1, *v2, *v3; + idDrawVert split; + idVec3 dir; + float len; + float frac; + mapTri_t *new1, *new2; + idVec3 temp; + float d, off; + const idVec3 *v; + idPlane plane1, plane2; + + v = &hv->v; + + // if the triangle already has this hashVert as a vert, + // it can't be split by it + if ( a->hashVert[0] == hv || a->hashVert[1] == hv || a->hashVert[2] == hv ) { + return NULL; + } + + // we probably should find the edge that the vertex is closest to. + // it is possible to be < 1 unit away from multiple + // edges, but we only want to split by one of them + for ( i = 0 ; i < 3 ; i++ ) { + v1 = &a->v[i]; + v2 = &a->v[(i+1)%3]; + v3 = &a->v[(i+2)%3]; + VectorSubtract( v2->xyz, v1->xyz, dir ); + len = dir.Normalize(); + + // if it is close to one of the edge vertexes, skip it + VectorSubtract( *v, v1->xyz, temp ); + d = DotProduct( temp, dir ); + if ( d <= 0 || d >= len ) { + continue; + } + + // make sure it is on the line + VectorMA( v1->xyz, d, dir, temp ); + VectorSubtract( temp, *v, temp ); + off = temp.Length(); + if ( off <= -COLINEAR_EPSILON || off >= COLINEAR_EPSILON ) { + continue; + } + + // take the x/y/z from the splitter, + // but interpolate everything else from the original tri + VectorCopy( *v, split.xyz ); + frac = d / len; + split.st[0] = v1->st[0] + frac * ( v2->st[0] - v1->st[0] ); + split.st[1] = v1->st[1] + frac * ( v2->st[1] - v1->st[1] ); + split.normal[0] = v1->normal[0] + frac * ( v2->normal[0] - v1->normal[0] ); + split.normal[1] = v1->normal[1] + frac * ( v2->normal[1] - v1->normal[1] ); + split.normal[2] = v1->normal[2] + frac * ( v2->normal[2] - v1->normal[2] ); + split.normal.Normalize(); + + // split the tri + new1 = CopyMapTri( a ); + new1->v[(i+1)%3] = split; + new1->hashVert[(i+1)%3] = hv; + new1->next = NULL; + + new2 = CopyMapTri( a ); + new2->v[i] = split; + new2->hashVert[i] = hv; + new2->next = new1; + + plane1.FromPoints( new1->hashVert[0]->v, new1->hashVert[1]->v, new1->hashVert[2]->v ); + plane2.FromPoints( new2->hashVert[0]->v, new2->hashVert[1]->v, new2->hashVert[2]->v ); + + d = DotProduct( plane1, plane2 ); + + // if the two split triangle's normals don't face the same way, + // it should not be split + if ( d <= 0 ) { + FreeTriList( new2 ); + continue; + } + + return new2; + } + + + return NULL; +} + + + +/* +================== +FixTriangleAgainstHash + +Potentially splits a triangle into a list of triangles based on tjunctions +================== +*/ +static mapTri_t *FixTriangleAgainstHash( const mapTri_t *tri ) { + mapTri_t *fixed; + mapTri_t *a; + mapTri_t *test, *next; + int blocks[2][3]; + int i, j, k; + hashVert_t *hv; + + // if this triangle is degenerate after point snapping, + // do nothing (this shouldn't happen, because they should + // be removed as they are hashed) + if ( tri->hashVert[0] == tri->hashVert[1] + || tri->hashVert[0] == tri->hashVert[2] + || tri->hashVert[1] == tri->hashVert[2] ) { + return NULL; + } + + fixed = CopyMapTri( tri ); + fixed->next = NULL; + + HashBlocksForTri( tri, blocks ); + for ( i = blocks[0][0] ; i <= blocks[1][0] ; i++ ) { + for ( j = blocks[0][1] ; j <= blocks[1][1] ; j++ ) { + for ( k = blocks[0][2] ; k <= blocks[1][2] ; k++ ) { + for ( hv = hashVerts[i][j][k] ; hv ; hv = hv->next ) { + // fix all triangles in the list against this point + test = fixed; + fixed = NULL; + for ( ; test ; test = next ) { + next = test->next; + a = FixTriangleAgainstHashVert( test, hv ); + if ( a ) { + // cut into two triangles + a->next->next = fixed; + fixed = a; + FreeTri( test ); + } else { + test->next = fixed; + fixed = test; + } + } + } + } + } + } + + return fixed; +} + + +/* +================== +CountGroupListTris +================== +*/ +int CountGroupListTris( const optimizeGroup_t *groupList ) { + int c; + + c = 0; + for ( ; groupList ; groupList = groupList->nextGroup ) { + c += CountTriList( groupList->triList ); + } + + return c; +} + +/* +================== +FixAreaGroupsTjunctions +================== +*/ +void FixAreaGroupsTjunctions( optimizeGroup_t *groupList ) { + const mapTri_t *tri; + mapTri_t *newList; + mapTri_t *fixed; + int startCount, endCount; + optimizeGroup_t *group; + + if ( dmapGlobals.noTJunc ) { + return; + } + + if ( !groupList ) { + return; + } + + startCount = CountGroupListTris( groupList ); + + if ( dmapGlobals.verbose ) { + common->Printf( "----- FixAreaGroupsTjunctions -----\n" ); + common->Printf( "%6i triangles in\n", startCount ); + } + + HashTriangles( groupList ); + + for ( group = groupList ; group ; group = group->nextGroup ) { + // don't touch discrete surfaces + if ( group->material != NULL && group->material->IsDiscrete() ) { + continue; + } + + newList = NULL; + for ( tri = group->triList ; tri ; tri = tri->next ) { + fixed = FixTriangleAgainstHash( tri ); + newList = MergeTriLists( newList, fixed ); + } + FreeTriList( group->triList ); + group->triList = newList; + } + + endCount = CountGroupListTris( groupList ); + if ( dmapGlobals.verbose ) { + common->Printf( "%6i triangles out\n", endCount ); + } +} + + +/* +================== +FixEntityTjunctions +================== +*/ +void FixEntityTjunctions( uEntity_t *e ) { + int i; + + for ( i = 0 ; i < e->numAreas ; i++ ) { + FixAreaGroupsTjunctions( e->areas[i].groups ); + FreeTJunctionHash(); + } +} + +/* +================== +FixGlobalTjunctions +================== +*/ +void FixGlobalTjunctions( uEntity_t *e ) { + mapTri_t *a; + int vert; + int i; + optimizeGroup_t *group; + int areaNum; + + common->Printf( "----- FixGlobalTjunctions -----\n" ); + + // clear the hash tables + memset( hashVerts, 0, sizeof( hashVerts ) ); + + numHashVerts = 0; + numTotalVerts = 0; + + // bound all the triangles to determine the bucket size + hashBounds.Clear(); + for ( areaNum = 0 ; areaNum < e->numAreas ; areaNum++ ) { + for ( group = e->areas[areaNum].groups ; group ; group = group->nextGroup ) { + for ( a = group->triList ; a ; a = a->next ) { + hashBounds.AddPoint( a->v[0].xyz ); + hashBounds.AddPoint( a->v[1].xyz ); + hashBounds.AddPoint( a->v[2].xyz ); + } + } + } + + // spread the bounds so it will never have a zero size + for ( i = 0 ; i < 3 ; i++ ) { + hashBounds[0][i] = floor( hashBounds[0][i] - 1 ); + hashBounds[1][i] = ceil( hashBounds[1][i] + 1 ); + hashIntMins[i] = hashBounds[0][i] * SNAP_FRACTIONS; + + hashScale[i] = ( hashBounds[1][i] - hashBounds[0][i] ) / HASH_BINS; + hashIntScale[i] = hashScale[i] * SNAP_FRACTIONS; + if ( hashIntScale[i] < 1 ) { + hashIntScale[i] = 1; + } + } + + // add all the points to the hash buckets + for ( areaNum = 0 ; areaNum < e->numAreas ; areaNum++ ) { + for ( group = e->areas[areaNum].groups ; group ; group = group->nextGroup ) { + // don't touch discrete surfaces + if ( group->material != NULL && group->material->IsDiscrete() ) { + continue; + } + + for ( a = group->triList ; a ; a = a->next ) { + for ( vert = 0 ; vert < 3 ; vert++ ) { + a->hashVert[vert] = GetHashVert( a->v[vert].xyz ); + } + } + } + } + + // add all the func_static model vertexes to the hash buckets + // optionally inline some of the func_static models + if ( dmapGlobals.entityNum == 0 ) { + for ( int eNum = 1 ; eNum < dmapGlobals.num_entities ; eNum++ ) { + uEntity_t *entity = &dmapGlobals.uEntities[eNum]; + const char *className = entity->mapEntity->epairs.GetString( "classname" ); + if ( idStr::Icmp( className, "func_static" ) ) { + continue; + } + const char *modelName = entity->mapEntity->epairs.GetString( "model" ); + if ( !modelName ) { + continue; + } + if ( !strstr( modelName, ".lwo" ) && !strstr( modelName, ".ase" ) && !strstr( modelName, ".ma" ) ) { + continue; + } + + idRenderModel *model = renderModelManager->FindModel( modelName ); + +// common->Printf( "adding T junction verts for %s.\n", entity->mapEntity->epairs.GetString( "name" ) ); + + idMat3 axis; + // get the rotation matrix in either full form, or single angle form + if ( !entity->mapEntity->epairs.GetMatrix( "rotation", "1 0 0 0 1 0 0 0 1", axis ) ) { + float angle = entity->mapEntity->epairs.GetFloat( "angle" ); + if ( angle != 0.0f ) { + axis = idAngles( 0.0f, angle, 0.0f ).ToMat3(); + } else { + axis.Identity(); + } + } + + idVec3 origin = entity->mapEntity->epairs.GetVector( "origin" ); + + for ( i = 0 ; i < model->NumSurfaces() ; i++ ) { + const modelSurface_t *surface = model->Surface( i ); + const srfTriangles_t *tri = surface->geometry; + + mapTri_t mapTri; + memset( &mapTri, 0, sizeof( mapTri ) ); + mapTri.material = surface->shader; + // don't let discretes (autosprites, etc) merge together + if ( mapTri.material->IsDiscrete() ) { + mapTri.mergeGroup = (void *)surface; + } + for ( int j = 0 ; j < tri->numVerts ; j += 3 ) { + idVec3 v = tri->verts[j].xyz * axis + origin; + GetHashVert( v ); + } + } + } + } + + + + // now fix each area + for ( areaNum = 0 ; areaNum < e->numAreas ; areaNum++ ) { + for ( group = e->areas[areaNum].groups ; group ; group = group->nextGroup ) { + // don't touch discrete surfaces + if ( group->material != NULL && group->material->IsDiscrete() ) { + continue; + } + + mapTri_t *newList = NULL; + for ( mapTri_t *tri = group->triList ; tri ; tri = tri->next ) { + mapTri_t *fixed = FixTriangleAgainstHash( tri ); + newList = MergeTriLists( newList, fixed ); + } + FreeTriList( group->triList ); + group->triList = newList; + } + } + + + // done + FreeTJunctionHash(); +} diff --git a/src/tools/compilers/dmap/tritools.cpp b/src/tools/compilers/dmap/tritools.cpp new file mode 100644 index 0000000..1746998 --- /dev/null +++ b/src/tools/compilers/dmap/tritools.cpp @@ -0,0 +1,387 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../../idlib/precompiled.h" +#pragma hdrstop + +#include "dmap.h" + +/* + + All triangle list functions should behave reasonably with NULL lists. + +*/ + +/* +=============== +AllocTri +=============== +*/ +mapTri_t *AllocTri( void ) { + mapTri_t *tri; + + tri = (mapTri_t *)Mem_Alloc( sizeof( *tri ) ); + memset( tri, 0, sizeof( *tri ) ); + return tri; +} + +/* +=============== +FreeTri +=============== +*/ +void FreeTri( mapTri_t *tri ) { + Mem_Free( tri ); +} + + +/* +=============== +MergeTriLists + +This does not copy any tris, it just relinks them +=============== +*/ +mapTri_t *MergeTriLists( mapTri_t *a, mapTri_t *b ) { + mapTri_t **prev; + + prev = &a; + while ( *prev ) { + prev = &(*prev)->next; + } + + *prev = b; + + return a; +} + + +/* +=============== +FreeTriList +=============== +*/ +void FreeTriList( mapTri_t *a ) { + mapTri_t *next; + + for ( ; a ; a = next ) { + next = a->next; + Mem_Free( a ); + } +} + +/* +=============== +CopyTriList +=============== +*/ +mapTri_t *CopyTriList( const mapTri_t *a ) { + mapTri_t *testList; + const mapTri_t *tri; + + testList = NULL; + for ( tri = a ; tri ; tri = tri->next ) { + mapTri_t *copy; + + copy = CopyMapTri( tri ); + copy ->next = testList; + testList = copy; + } + + return testList; +} + + +/* +============= +CountTriList +============= +*/ +int CountTriList( const mapTri_t *tri ) { + int c; + + c = 0; + while ( tri ) { + c++; + tri = tri->next; + } + + return c; +} + + +/* +=============== +CopyMapTri +=============== +*/ +mapTri_t *CopyMapTri( const mapTri_t *tri ) { + mapTri_t *t; + + t = (mapTri_t *)Mem_Alloc( sizeof( *t ) ); + *t = *tri; + + return t; +} + +/* +=============== +MapTriArea +=============== +*/ +float MapTriArea( const mapTri_t *tri ) { + return idWinding::TriangleArea( tri->v[0].xyz, tri->v[1].xyz, tri->v[2].xyz ); +} + +/* +=============== +RemoveBadTris + +Return a new list with any zero or negative area triangles removed +=============== +*/ +mapTri_t *RemoveBadTris( const mapTri_t *list ) { + mapTri_t *newList; + mapTri_t *copy; + const mapTri_t *tri; + + newList = NULL; + + for ( tri = list ; tri ; tri = tri->next ) { + if ( MapTriArea( tri ) > 0 ) { + copy = CopyMapTri( tri ); + copy->next = newList; + newList = copy; + } + } + + return newList; +} + +/* +================ +BoundTriList +================ +*/ +void BoundTriList( const mapTri_t *list, idBounds &b ) { + b.Clear(); + for ( ; list ; list = list->next ) { + b.AddPoint( list->v[0].xyz ); + b.AddPoint( list->v[1].xyz ); + b.AddPoint( list->v[2].xyz ); + } +} + +/* +================ +DrawTri +================ +*/ +void DrawTri( const mapTri_t *tri ) { + idWinding w; + + w.SetNumPoints( 3 ); + VectorCopy( tri->v[0].xyz, w[0] ); + VectorCopy( tri->v[1].xyz, w[1] ); + VectorCopy( tri->v[2].xyz, w[2] ); + DrawWinding( &w ); +} + + +/* +================ +FlipTriList + +Swaps the vertex order +================ +*/ +void FlipTriList( mapTri_t *tris ) { + mapTri_t *tri; + + for ( tri = tris ; tri ; tri = tri->next ) { + idDrawVert v; + const struct hashVert_s *hv; + struct optVertex_s *ov; + + v = tri->v[0]; + tri->v[0] = tri->v[2]; + tri->v[2] = v; + + hv = tri->hashVert[0]; + tri->hashVert[0] = tri->hashVert[2]; + tri->hashVert[2] = hv; + + ov = tri->optVert[0]; + tri->optVert[0] = tri->optVert[2]; + tri->optVert[2] = ov; + } +} + +/* +================ +WindingForTri +================ +*/ +idWinding *WindingForTri( const mapTri_t *tri ) { + idWinding *w; + + w = new idWinding( 3 ); + w->SetNumPoints( 3 ); + VectorCopy( tri->v[0].xyz, (*w)[0] ); + VectorCopy( tri->v[1].xyz, (*w)[1] ); + VectorCopy( tri->v[2].xyz, (*w)[2] ); + + return w; +} + +/* +================ +TriVertsFromOriginal + +Regenerate the texcoords and colors on a fragmented tri from the plane equations +================ +*/ +void TriVertsFromOriginal( mapTri_t *tri, const mapTri_t *original ) { + int i, j; + float denom; + + denom = idWinding::TriangleArea( original->v[0].xyz, original->v[1].xyz, original->v[2].xyz ); + if ( denom == 0 ) { + return; // original was degenerate, so it doesn't matter + } + + for ( i = 0 ; i < 3 ; i++ ) { + float a, b, c; + + // find the barycentric coordinates + a = idWinding::TriangleArea( tri->v[i].xyz, original->v[1].xyz, original->v[2].xyz ) / denom; + b = idWinding::TriangleArea( tri->v[i].xyz, original->v[2].xyz, original->v[0].xyz ) / denom; + c = idWinding::TriangleArea( tri->v[i].xyz, original->v[0].xyz, original->v[1].xyz ) / denom; + + // regenerate the interpolated values + tri->v[i].st[0] = a * original->v[0].st[0] + + b * original->v[1].st[0] + c * original->v[2].st[0]; + tri->v[i].st[1] = a * original->v[0].st[1] + + b * original->v[1].st[1] + c * original->v[2].st[1]; + + for ( j = 0 ; j < 3 ; j++ ) { + tri->v[i].normal[j] = a * original->v[0].normal[j] + + b * original->v[1].normal[j] + c * original->v[2].normal[j]; + } + tri->v[i].normal.Normalize(); + } +} + +/* +================ +WindingToTriList + +Generates a new list of triangles with proper texcoords from a winding +created by clipping the originalTri + +OriginalTri can be NULL if you don't care about texCoords +================ +*/ +mapTri_t *WindingToTriList( const idWinding *w, const mapTri_t *originalTri ) { + mapTri_t *tri; + mapTri_t *triList; + int i, j; + const idVec3 *vec; + + if ( !w ) { + return NULL; + } + + triList = NULL; + for ( i = 2 ; i < w->GetNumPoints() ; i++ ) { + tri = AllocTri(); + if ( !originalTri ) { + memset( tri, 0, sizeof( *tri ) ); + } else { + *tri = *originalTri; + } + tri->next = triList; + triList = tri; + + for ( j = 0 ; j < 3 ; j++ ) { + if ( j == 0 ) { + vec = &((*w)[0]).ToVec3(); + } else if ( j == 1 ) { + vec = &((*w)[i-1]).ToVec3(); + } else { + vec = &((*w)[i]).ToVec3(); + } + + VectorCopy( *vec, tri->v[j].xyz ); + } + if ( originalTri ) { + TriVertsFromOriginal( tri, originalTri ); + } + } + + return triList; +} + + +/* +================== +ClipTriList +================== +*/ +void ClipTriList( const mapTri_t *list, const idPlane &plane, float epsilon, + mapTri_t **front, mapTri_t **back ) { + const mapTri_t *tri; + mapTri_t *newList; + idWinding *w, *frontW, *backW; + + *front = NULL; + *back = NULL; + + for ( tri = list ; tri ; tri = tri->next ) { + w = WindingForTri( tri ); + w->Split( plane, epsilon, &frontW, &backW ); + + newList = WindingToTriList( frontW, tri ); + *front = MergeTriLists( *front, newList ); + + newList = WindingToTriList( backW, tri ); + *back = MergeTriLists( *back, newList ); + + delete w; + } + +} + +/* +================== +PlaneForTri +================== +*/ +void PlaneForTri( const mapTri_t *tri, idPlane &plane ) { + plane.FromPoints( tri->v[0].xyz, tri->v[1].xyz, tri->v[2].xyz ); +} diff --git a/src/tools/compilers/dmap/ubrush.cpp b/src/tools/compilers/dmap/ubrush.cpp new file mode 100644 index 0000000..ecb175b --- /dev/null +++ b/src/tools/compilers/dmap/ubrush.cpp @@ -0,0 +1,709 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../../idlib/precompiled.h" +#pragma hdrstop + +#include "dmap.h" + +int c_active_brushes; + +int c_nodes; + +// if a brush just barely pokes onto the other side, +// let it slide by without chopping +#define PLANESIDE_EPSILON 0.001 +//0.1 + + + + +/* +================ +CountBrushList +================ +*/ +int CountBrushList (uBrush_t *brushes) +{ + int c; + + c = 0; + for ( ; brushes ; brushes = brushes->next) + c++; + return c; +} + + +int BrushSizeForSides( int numsides ) { + int c; + + // allocate a structure with a variable number of sides at the end +// c = (int)&(((uBrush_t *)0)->sides[numsides]); // bounds checker complains about this + c = sizeof( uBrush_t ) + sizeof( side_t ) * (numsides - 6); + + return c; +} + +/* +================ +AllocBrush +================ +*/ +uBrush_t *AllocBrush (int numsides) +{ + uBrush_t *bb; + int c; + + c = BrushSizeForSides( numsides ); + + bb = (uBrush_t *)Mem_Alloc(c); + memset (bb, 0, c); + c_active_brushes++; + return bb; +} + +/* +================ +FreeBrush +================ +*/ +void FreeBrush (uBrush_t *brushes) +{ + int i; + + for ( i = 0 ; i < brushes->numsides ; i++ ) { + if ( brushes->sides[i].winding ) { + delete brushes->sides[i].winding; + } + if ( brushes->sides[i].visibleHull ) { + delete brushes->sides[i].visibleHull; + } + } + Mem_Free (brushes); + c_active_brushes--; +} + + +/* +================ +FreeBrushList +================ +*/ +void FreeBrushList (uBrush_t *brushes) +{ + uBrush_t *next; + + for ( ; brushes ; brushes = next) + { + next = brushes->next; + + FreeBrush (brushes); + } +} + +/* +================== +CopyBrush + +Duplicates the brush, the sides, and the windings +================== +*/ +uBrush_t *CopyBrush (uBrush_t *brush) +{ + uBrush_t *newbrush; + int size; + int i; + + size = BrushSizeForSides( brush->numsides ); + + newbrush = AllocBrush (brush->numsides); + memcpy (newbrush, brush, size); + + for (i=0 ; inumsides ; i++) + { + if (brush->sides[i].winding) + newbrush->sides[i].winding = brush->sides[i].winding->Copy(); + } + + return newbrush; +} + + +/* +================ +DrawBrushList +================ +*/ +void DrawBrushList (uBrush_t *brush) +{ + int i; + side_t *s; + + GLS_BeginScene (); + for ( ; brush ; brush=brush->next) + { + for (i=0 ; inumsides ; i++) + { + s = &brush->sides[i]; + if (!s->winding) + continue; + GLS_Winding (s->winding, 0); + } + } + GLS_EndScene (); +} + + +/* +============= +PrintBrush +============= +*/ +void PrintBrush (uBrush_t *brush) +{ + int i; + + common->Printf( "brush: %p\n", brush ); + for ( i=0;inumsides ; i++ ) { + brush->sides[i].winding->Print(); + common->Printf ("\n"); + } +} + +/* +================== +BoundBrush + +Sets the mins/maxs based on the windings +returns false if the brush doesn't enclose a valid volume +================== +*/ +bool BoundBrush (uBrush_t *brush) { + int i, j; + idWinding *w; + + brush->bounds.Clear(); + for ( i = 0; i < brush->numsides; i++ ) { + w = brush->sides[i].winding; + if (!w) + continue; + for ( j = 0; j < w->GetNumPoints(); j++ ) + brush->bounds.AddPoint( (*w)[j].ToVec3() ); + } + + for ( i = 0; i < 3; i++ ) { + if (brush->bounds[0][i] < MIN_WORLD_COORD || brush->bounds[1][i] > MAX_WORLD_COORD + || brush->bounds[0][i] >= brush->bounds[1][i] ) { + return false; + } + } + + return true; +} + +/* +================== +CreateBrushWindings + +makes basewindigs for sides and mins / maxs for the brush +returns false if the brush doesn't enclose a valid volume +================== +*/ +bool CreateBrushWindings (uBrush_t *brush) { + int i, j; + idWinding *w; + idPlane *plane; + side_t *side; + + for ( i = 0; i < brush->numsides; i++ ) { + side = &brush->sides[i]; + plane = &dmapGlobals.mapPlanes[side->planenum]; + w = new idWinding( *plane ); + for ( j = 0; j < brush->numsides && w; j++ ) { + if ( i == j ) { + continue; + } + if ( brush->sides[j].planenum == ( brush->sides[i].planenum ^ 1 ) ) { + continue; // back side clipaway + } + plane = &dmapGlobals.mapPlanes[brush->sides[j].planenum^1]; + w = w->Clip( *plane, 0 );//CLIP_EPSILON); + } + if ( side->winding ) { + delete side->winding; + } + side->winding = w; + } + + return BoundBrush( brush ); +} + +/* +================== +BrushFromBounds + +Creates a new axial brush +================== +*/ +uBrush_t *BrushFromBounds( const idBounds &bounds ) { + uBrush_t *b; + int i; + idPlane plane; + + b = AllocBrush (6); + b->numsides = 6; + for (i=0 ; i<3 ; i++) { + plane[0] = plane[1] = plane[2] = 0; + plane[i] = 1; + plane[3] = -bounds[1][i]; + b->sides[i].planenum = FindFloatPlane( plane ); + + plane[i] = -1; + plane[3] = bounds[0][i]; + b->sides[3+i].planenum = FindFloatPlane( plane ); + } + + CreateBrushWindings (b); + + return b; +} + +/* +================== +BrushVolume + +================== +*/ +float BrushVolume (uBrush_t *brush) { + int i; + idWinding *w; + idVec3 corner; + float d, area, volume; + idPlane *plane; + + if (!brush) + return 0; + + // grab the first valid point as the corner + + w = NULL; + for ( i = 0; i < brush->numsides; i++ ) { + w = brush->sides[i].winding; + if (w) + break; + } + if (!w) { + return 0; + } + VectorCopy ( (*w)[0], corner); + + // make tetrahedrons to all other faces + + volume = 0; + for ( ; i < brush->numsides; i++ ) + { + w = brush->sides[i].winding; + if (!w) + continue; + plane = &dmapGlobals.mapPlanes[brush->sides[i].planenum]; + d = -plane->Distance( corner ); + area = w->GetArea(); + volume += d * area; + } + + volume /= 3; + return volume; +} + + +/* +================== +WriteBspBrushMap + +FIXME: use new brush format +================== +*/ +void WriteBspBrushMap( const char *name, uBrush_t *list ) { + idFile * f; + side_t * s; + int i; + idWinding * w; + + common->Printf ("writing %s\n", name); + f = fileSystem->OpenFileWrite( name ); + + if ( !f ) { + common->Error( "Can't write %s\b", name); + } + + f->Printf( "{\n\"classname\" \"worldspawn\"\n" ); + + for ( ; list ; list=list->next ) + { + f->Printf( "{\n" ); + for (i=0,s=list->sides ; inumsides ; i++,s++) + { + w = new idWinding( dmapGlobals.mapPlanes[s->planenum] ); + + f->Printf ("( %i %i %i ) ", (int)(*w)[0][0], (int)(*w)[0][1], (int)(*w)[0][2]); + f->Printf ("( %i %i %i ) ", (int)(*w)[1][0], (int)(*w)[1][1], (int)(*w)[1][2]); + f->Printf ("( %i %i %i ) ", (int)(*w)[2][0], (int)(*w)[2][1], (int)(*w)[2][2]); + + f->Printf ("notexture 0 0 0 1 1\n" ); + delete w; + } + f->Printf ("}\n"); + } + f->Printf ("}\n"); + + fileSystem->CloseFile(f); + +} + + +//===================================================================================== + +/* +==================== +FilterBrushIntoTree_r + +==================== +*/ +int FilterBrushIntoTree_r( uBrush_t *b, node_t *node ) { + uBrush_t *front, *back; + int c; + + if ( !b ) { + return 0; + } + + // add it to the leaf list + if ( node->planenum == PLANENUM_LEAF ) { + b->next = node->brushlist; + node->brushlist = b; + + // classify the leaf by the structural brush + if ( b->opaque ) { + node->opaque = true; + } + + return 1; + } + + // split it by the node plane + SplitBrush ( b, node->planenum, &front, &back ); + FreeBrush( b ); + + c = 0; + c += FilterBrushIntoTree_r( front, node->children[0] ); + c += FilterBrushIntoTree_r( back, node->children[1] ); + + return c; +} + + +/* +===================== +FilterBrushesIntoTree + +Mark the leafs as opaque and areaportals and put brush +fragments in each leaf so portal surfaces can be matched +to materials +===================== +*/ +void FilterBrushesIntoTree( uEntity_t *e ) { + primitive_t *prim; + uBrush_t *b, *newb; + int r; + int c_unique, c_clusters; + + common->Printf( "----- FilterBrushesIntoTree -----\n"); + + c_unique = 0; + c_clusters = 0; + for ( prim = e->primitives ; prim ; prim = prim->next ) { + b = prim->brush; + if ( !b ) { + continue; + } + c_unique++; + newb = CopyBrush( b ); + r = FilterBrushIntoTree_r( newb, e->tree->headnode ); + c_clusters += r; + } + + common->Printf( "%5i total brushes\n", c_unique ); + common->Printf( "%5i cluster references\n", c_clusters ); +} + + + +/* +================ +AllocTree +================ +*/ +tree_t *AllocTree (void) +{ + tree_t *tree; + + tree = (tree_t *)Mem_Alloc(sizeof(*tree)); + memset (tree, 0, sizeof(*tree)); + tree->bounds.Clear(); + + return tree; +} + +/* +================ +AllocNode +================ +*/ +node_t *AllocNode (void) +{ + node_t *node; + + node = (node_t *)Mem_Alloc(sizeof(*node)); + memset (node, 0, sizeof(*node)); + + return node; +} + +//============================================================ + +/* +================== +BrushMostlyOnSide + +================== +*/ +int BrushMostlyOnSide (uBrush_t *brush, idPlane &plane) { + int i, j; + idWinding *w; + float d, max; + int side; + + max = 0; + side = PSIDE_FRONT; + for ( i = 0; i < brush->numsides; i++ ) { + w = brush->sides[i].winding; + if (!w) + continue; + for ( j = 0; j < w->GetNumPoints(); j++ ) + { + d = plane.Distance( (*w)[j].ToVec3() ); + if (d > max) + { + max = d; + side = PSIDE_FRONT; + } + if (-d > max) + { + max = -d; + side = PSIDE_BACK; + } + } + } + return side; +} + +/* +================ +SplitBrush + +Generates two new brushes, leaving the original +unchanged +================ +*/ +void SplitBrush (uBrush_t *brush, int planenum, uBrush_t **front, uBrush_t **back) { + uBrush_t *b[2]; + int i, j; + idWinding *w, *cw[2], *midwinding; + side_t *s, *cs; + float d, d_front, d_back; + + *front = *back = NULL; + idPlane &plane = dmapGlobals.mapPlanes[planenum]; + + // check all points + d_front = d_back = 0; + for ( i = 0; i < brush->numsides; i++ ) + { + w = brush->sides[i].winding; + if (!w) { + continue; + } + for ( j = 0; j < w->GetNumPoints(); j++ ) { + d = plane.Distance( (*w)[j].ToVec3() ); + if (d > 0 && d > d_front) + d_front = d; + if (d < 0 && d < d_back) + d_back = d; + } + } + if (d_front < 0.1) // PLANESIDE_EPSILON) + { // only on back + *back = CopyBrush( brush ); + return; + } + if (d_back > -0.1) // PLANESIDE_EPSILON) + { // only on front + *front = CopyBrush( brush ); + return; + } + + // create a new winding from the split plane + + w = new idWinding( plane ); + for ( i = 0; i < brush->numsides && w; i++ ) { + idPlane &plane2 = dmapGlobals.mapPlanes[brush->sides[i].planenum ^ 1]; + w = w->Clip( plane2, 0 ); // PLANESIDE_EPSILON); + } + + if ( !w || w->IsTiny() ) { + // the brush isn't really split + int side; + + side = BrushMostlyOnSide( brush, plane ); + if (side == PSIDE_FRONT) + *front = CopyBrush (brush); + if (side == PSIDE_BACK) + *back = CopyBrush (brush); + return; + } + + if ( w->IsHuge() ) { + common->Printf ("WARNING: huge winding\n"); + } + + midwinding = w; + + // split it for real + + for ( i = 0; i < 2; i++ ) { + b[i] = AllocBrush (brush->numsides+1); + memcpy( b[i], brush, sizeof( uBrush_t ) - sizeof( brush->sides ) ); + b[i]->numsides = 0; + b[i]->next = NULL; + b[i]->original = brush->original; + } + + // split all the current windings + + for ( i = 0; i < brush->numsides; i++ ) { + s = &brush->sides[i]; + w = s->winding; + if (!w) + continue; + w->Split( plane, 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1] ); + for ( j = 0; j < 2; j++ ) { + if ( !cw[j] ) { + continue; + } +/* + if ( cw[j]->IsTiny() ) + { + delete cw[j]; + continue; + } +*/ + cs = &b[j]->sides[b[j]->numsides]; + b[j]->numsides++; + *cs = *s; + cs->winding = cw[j]; + } + } + + + // see if we have valid polygons on both sides + + for (i=0 ; i<2 ; i++) + { + if ( !BoundBrush (b[i]) ) { + break; + } + + if ( b[i]->numsides < 3 ) + { + FreeBrush (b[i]); + b[i] = NULL; + } + } + + if ( !(b[0] && b[1]) ) + { + if (!b[0] && !b[1]) + common->Printf ("split removed brush\n"); + else + common->Printf ("split not on both sides\n"); + if (b[0]) + { + FreeBrush (b[0]); + *front = CopyBrush (brush); + } + if (b[1]) + { + FreeBrush (b[1]); + *back = CopyBrush (brush); + } + return; + } + + // add the midwinding to both sides + for (i=0 ; i<2 ; i++) + { + cs = &b[i]->sides[b[i]->numsides]; + b[i]->numsides++; + + cs->planenum = planenum^i^1; + cs->material = NULL; + if (i==0) + cs->winding = midwinding->Copy(); + else + cs->winding = midwinding; + } + +{ + float v1; + int i; + + for (i=0 ; i<2 ; i++) + { + v1 = BrushVolume (b[i]); + if (v1 < 1.0) + { + FreeBrush (b[i]); + b[i] = NULL; +// common->Printf ("tiny volume after clip\n"); + } + } +} + + *front = b[0]; + *back = b[1]; +} diff --git a/src/tools/compilers/dmap/usurface.cpp b/src/tools/compilers/dmap/usurface.cpp new file mode 100644 index 0000000..fd8d326 --- /dev/null +++ b/src/tools/compilers/dmap/usurface.cpp @@ -0,0 +1,1045 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../../idlib/precompiled.h" +#pragma hdrstop + +#include "dmap.h" + + +#define TEXTURE_OFFSET_EQUAL_EPSILON 0.005 +#define TEXTURE_VECTOR_EQUAL_EPSILON 0.001 + +/* +=============== +AddTriListToArea + +The triList is appended to the apropriate optimzeGroup_t, +creating a new one if needed. +The entire list is assumed to come from the same planar primitive +=============== +*/ +static void AddTriListToArea( uEntity_t *e, mapTri_t *triList, int planeNum, int areaNum, textureVectors_t *texVec ) { + uArea_t *area; + optimizeGroup_t *group; + int i, j; + + if ( !triList ) { + return; + } + + area = &e->areas[areaNum]; + for ( group = area->groups ; group ; group = group->nextGroup ) { + if ( group->material == triList->material + && group->planeNum == planeNum + && group->mergeGroup == triList->mergeGroup ) { + // check the texture vectors + for ( i = 0 ; i < 2 ; i++ ) { + for ( j = 0 ; j < 3 ; j++ ) { + if ( idMath::Fabs( texVec->v[i][j] - group->texVec.v[i][j] ) > TEXTURE_VECTOR_EQUAL_EPSILON ) { + break; + } + } + if ( j != 3 ) { + break; + } + if ( idMath::Fabs( texVec->v[i][3] - group->texVec.v[i][3] ) > TEXTURE_OFFSET_EQUAL_EPSILON ) { + break; + } + } + if ( i == 2 ) { + break; // exact match + } else { + // different texture offsets + i = 1; // just for debugger breakpoint + } + } + } + + if ( !group ) { + group = (optimizeGroup_t *)Mem_Alloc( sizeof( *group ) ); + memset( group, 0, sizeof( *group ) ); + group->planeNum = planeNum; + group->mergeGroup = triList->mergeGroup; + group->material = triList->material; + group->nextGroup = area->groups; + group->texVec = *texVec; + area->groups = group; + } + + group->triList = MergeTriLists( group->triList, triList ); +} + +/* +=================== +TexVecForTri +=================== +*/ +static void TexVecForTri( textureVectors_t *texVec, mapTri_t *tri ) { + float area, inva; + idVec3 temp; + idVec5 d0, d1; + idDrawVert *a, *b, *c; + + a = &tri->v[0]; + b = &tri->v[1]; + c = &tri->v[2]; + + d0[0] = b->xyz[0] - a->xyz[0]; + d0[1] = b->xyz[1] - a->xyz[1]; + d0[2] = b->xyz[2] - a->xyz[2]; + d0[3] = b->st[0] - a->st[0]; + d0[4] = b->st[1] - a->st[1]; + + d1[0] = c->xyz[0] - a->xyz[0]; + d1[1] = c->xyz[1] - a->xyz[1]; + d1[2] = c->xyz[2] - a->xyz[2]; + d1[3] = c->st[0] - a->st[0]; + d1[4] = c->st[1] - a->st[1]; + + area = d0[3] * d1[4] - d0[4] * d1[3]; + inva = 1.0 / area; + + temp[0] = (d0[0] * d1[4] - d0[4] * d1[0]) * inva; + temp[1] = (d0[1] * d1[4] - d0[4] * d1[1]) * inva; + temp[2] = (d0[2] * d1[4] - d0[4] * d1[2]) * inva; + temp.Normalize(); + texVec->v[0].ToVec3() = temp; + texVec->v[0][3] = tri->v[0].xyz * texVec->v[0].ToVec3() - tri->v[0].st[0]; + + temp[0] = (d0[3] * d1[0] - d0[0] * d1[3]) * inva; + temp[1] = (d0[3] * d1[1] - d0[1] * d1[3]) * inva; + temp[2] = (d0[3] * d1[2] - d0[2] * d1[3]) * inva; + temp.Normalize(); + texVec->v[1].ToVec3() = temp; + texVec->v[1][3] = tri->v[0].xyz * texVec->v[0].ToVec3() - tri->v[0].st[1]; +} + + +/* +================= +TriListForSide +================= +*/ +//#define SNAP_FLOAT_TO_INT 8 +#define SNAP_FLOAT_TO_INT 256 +#define SNAP_INT_TO_FLOAT (1.0/SNAP_FLOAT_TO_INT) + +mapTri_t *TriListForSide( const side_t *s, const idWinding *w ) { + int i, j; + idDrawVert *dv; + mapTri_t *tri, *triList; + const idVec3 *vec; + const idMaterial *si; + + si = s->material; + + // skip any generated faces + if ( !si ) { + return NULL; + } + + // don't create faces for non-visible sides + if ( !si->SurfaceCastsShadow() && !si->IsDrawn() ) { + return NULL; + } + + if ( 1 ) { + // triangle fan using only the outer verts + // this gives the minimum triangle count, + // but may have some very distended triangles + triList = NULL; + for ( i = 2 ; i < w->GetNumPoints() ; i++ ) { + tri = AllocTri(); + tri->material = si; + tri->next = triList; + triList = tri; + + for ( j = 0 ; j < 3 ; j++ ) { + if ( j == 0 ) { + vec = &((*w)[0]).ToVec3(); + } else if ( j == 1 ) { + vec = &((*w)[i-1]).ToVec3(); + } else { + vec = &((*w)[i]).ToVec3(); + } + + dv = tri->v + j; +#if 0 + // round the xyz to a given precision + for ( k = 0 ; k < 3 ; k++ ) { + dv->xyz[k] = SNAP_INT_TO_FLOAT * floor( vec[k] * SNAP_FLOAT_TO_INT + 0.5 ); + } +#else + VectorCopy( *vec, dv->xyz ); +#endif + + // calculate texture s/t from brush primitive texture matrix + dv->st[0] = DotProduct( dv->xyz, s->texVec.v[0] ) + s->texVec.v[0][3]; + dv->st[1] = DotProduct( dv->xyz, s->texVec.v[1] ) + s->texVec.v[1][3]; + + // copy normal + dv->normal = dmapGlobals.mapPlanes[s->planenum].Normal(); + if ( dv->normal.Length() < 0.9 || dv->normal.Length() > 1.1 ) { + common->Error( "Bad normal in TriListForSide" ); + } + } + } + } else { + // triangle fan from central point, more verts and tris, but less distended + // I use this when debugging some tjunction problems + triList = NULL; + for ( i = 0 ; i < w->GetNumPoints() ; i++ ) { + idVec3 midPoint; + + tri = AllocTri(); + tri->material = si; + tri->next = triList; + triList = tri; + + for ( j = 0 ; j < 3 ; j++ ) { + if ( j == 0 ) { + vec = &midPoint; + midPoint = w->GetCenter(); + } else if ( j == 1 ) { + vec = &((*w)[i]).ToVec3(); + } else { + vec = &((*w)[(i+1)%w->GetNumPoints()]).ToVec3(); + } + + dv = tri->v + j; + + VectorCopy( *vec, dv->xyz ); + + // calculate texture s/t from brush primitive texture matrix + dv->st[0] = DotProduct( dv->xyz, s->texVec.v[0] ) + s->texVec.v[0][3]; + dv->st[1] = DotProduct( dv->xyz, s->texVec.v[1] ) + s->texVec.v[1][3]; + + // copy normal + dv->normal = dmapGlobals.mapPlanes[s->planenum].Normal(); + if ( dv->normal.Length() < 0.9f || dv->normal.Length() > 1.1f ) { + common->Error( "Bad normal in TriListForSide" ); + } + } + } + } + + // set merge groups if needed, to prevent multiple sides from being + // merged into a single surface in the case of gui shaders, mirrors, and autosprites + if ( s->material->IsDiscrete() ) { + for ( tri = triList ; tri ; tri = tri->next ) { + tri->mergeGroup = (void *)s; + } + } + + return triList; +} + +//================================================================================= + +/* +==================== +ClipSideByTree_r + +Adds non-opaque leaf fragments to the convex hull +==================== +*/ +static void ClipSideByTree_r( idWinding *w, side_t *side, node_t *node ) { + idWinding *front, *back; + + if ( !w ) { + return; + } + + if ( node->planenum != PLANENUM_LEAF ) { + if ( side->planenum == node->planenum ) { + ClipSideByTree_r( w, side, node->children[0] ); + return; + } + if ( side->planenum == ( node->planenum ^ 1) ) { + ClipSideByTree_r( w, side, node->children[1] ); + return; + } + + w->Split( dmapGlobals.mapPlanes[ node->planenum ], ON_EPSILON, &front, &back ); + delete w; + + ClipSideByTree_r( front, side, node->children[0] ); + ClipSideByTree_r( back, side, node->children[1] ); + + return; + } + + // if opaque leaf, don't add + if ( !node->opaque ) { + if ( !side->visibleHull ) { + side->visibleHull = w->Copy(); + } + else { + side->visibleHull->AddToConvexHull( w, dmapGlobals.mapPlanes[ side->planenum ].Normal() ); + } + } + + delete w; + return; +} + + +/* +===================== +ClipSidesByTree + +Creates side->visibleHull for all visible sides + +The visible hull for a side will consist of the convex hull of +all points in non-opaque clusters, which allows overlaps +to be trimmed off automatically. +===================== +*/ +void ClipSidesByTree( uEntity_t *e ) { + uBrush_t *b; + int i; + idWinding *w; + side_t *side; + primitive_t *prim; + + common->Printf( "----- ClipSidesByTree -----\n"); + + for ( prim = e->primitives ; prim ; prim = prim->next ) { + b = prim->brush; + if ( !b ) { + // FIXME: other primitives! + continue; + } + for ( i = 0 ; i < b->numsides ; i++ ) { + side = &b->sides[i]; + if ( !side->winding) { + continue; + } + w = side->winding->Copy(); + side->visibleHull = NULL; + ClipSideByTree_r( w, side, e->tree->headnode ); + // for debugging, we can choose to use the entire original side + // but we skip this if the side was completely clipped away + if ( side->visibleHull && dmapGlobals.noClipSides ) { + delete side->visibleHull; + side->visibleHull = side->winding->Copy(); + } + } + } +} + + + +//================================================================================= + +/* +==================== +ClipTriIntoTree_r + +This is used for adding curve triangles +The winding will be freed before it returns +==================== +*/ +void ClipTriIntoTree_r( idWinding *w, mapTri_t *originalTri, uEntity_t *e, node_t *node ) { + idWinding *front, *back; + + if ( !w ) { + return; + } + + if ( node->planenum != PLANENUM_LEAF ) { + w->Split( dmapGlobals.mapPlanes[ node->planenum ], ON_EPSILON, &front, &back ); + delete w; + + ClipTriIntoTree_r( front, originalTri, e, node->children[0] ); + ClipTriIntoTree_r( back, originalTri, e, node->children[1] ); + + return; + } + + // if opaque leaf, don't add + if ( !node->opaque && node->area >= 0 ) { + mapTri_t *list; + int planeNum; + idPlane plane; + textureVectors_t texVec; + + list = WindingToTriList( w, originalTri ); + + PlaneForTri( originalTri, plane ); + planeNum = FindFloatPlane( plane ); + + TexVecForTri( &texVec, originalTri ); + + AddTriListToArea( e, list, planeNum, node->area, &texVec ); + } + + delete w; + return; +} + + + +//============================================================= + +/* +==================== +CheckWindingInAreas_r + +Returns the area number that the winding is in, or +-2 if it crosses multiple areas. + +==================== +*/ +static int CheckWindingInAreas_r( const idWinding *w, node_t *node ) { + idWinding *front, *back; + + if ( !w ) { + return -1; + } + + if ( node->planenum != PLANENUM_LEAF ) { + int a1, a2; +#if 0 + if ( side->planenum == node->planenum ) { + return CheckWindingInAreas_r( w, node->children[0] ); + } + if ( side->planenum == ( node->planenum ^ 1) ) { + return CheckWindingInAreas_r( w, node->children[1] ); + } +#endif + w->Split( dmapGlobals.mapPlanes[ node->planenum ], ON_EPSILON, &front, &back ); + + a1 = CheckWindingInAreas_r( front, node->children[0] ); + delete front; + a2 = CheckWindingInAreas_r( back, node->children[1] ); + delete back; + + if ( a1 == -2 || a2 == -2 ) { + return -2; // different + } + if ( a1 == -1 ) { + return a2; // one solid + } + if ( a2 == -1 ) { + return a1; // one solid + } + + if ( a1 != a2 ) { + return -2; // cross areas + } + return a1; + } + + return node->area; +} + + + +/* +==================== +PutWindingIntoAreas_r + +Clips a winding down into the bsp tree, then converts +the fragments to triangles and adds them to the area lists +==================== +*/ +static void PutWindingIntoAreas_r( uEntity_t *e, const idWinding *w, side_t *side, node_t *node ) { + idWinding *front, *back; + int area; + + if ( !w ) { + return; + } + + if ( node->planenum != PLANENUM_LEAF ) { + if ( side->planenum == node->planenum ) { + PutWindingIntoAreas_r( e, w, side, node->children[0] ); + return; + } + if ( side->planenum == ( node->planenum ^ 1) ) { + PutWindingIntoAreas_r( e, w, side, node->children[1] ); + return; + } + + // see if we need to split it + // adding the "noFragment" flag to big surfaces like sky boxes + // will avoid potentially dicing them up into tons of triangles + // that take forever to optimize back together + if ( !dmapGlobals.fullCarve || side->material->NoFragment() ) { + area = CheckWindingInAreas_r( w, node ); + if ( area >= 0 ) { + mapTri_t *tri; + + // put in single area + tri = TriListForSide( side, w ); + AddTriListToArea( e, tri, side->planenum, area, &side->texVec ); + return; + } + } + + w->Split( dmapGlobals.mapPlanes[ node->planenum ], ON_EPSILON, &front, &back ); + + PutWindingIntoAreas_r( e, front, side, node->children[0] ); + if ( front ) { + delete front; + } + + PutWindingIntoAreas_r( e, back, side, node->children[1] ); + if ( back ) { + delete back; + } + + return; + } + + // if opaque leaf, don't add + if ( node->area >= 0 && !node->opaque ) { + mapTri_t *tri; + + tri = TriListForSide( side, w ); + AddTriListToArea( e, tri, side->planenum, node->area, &side->texVec ); + } +} + +/* +================== +AddMapTriToAreas + +Used for curves and inlined models +================== +*/ +void AddMapTriToAreas( mapTri_t *tri, uEntity_t *e ) { + int area; + idWinding *w; + + // skip degenerate triangles from pinched curves + if ( MapTriArea( tri ) <= 0 ) { + return; + } + + if ( dmapGlobals.fullCarve ) { + // always fragment into areas + w = WindingForTri( tri ); + ClipTriIntoTree_r( w, tri, e, e->tree->headnode ); + return; + } + + w = WindingForTri( tri ); + area = CheckWindingInAreas_r( w, e->tree->headnode ); + delete w; + if ( area == -1 ) { + return; + } + if ( area >= 0 ) { + mapTri_t *newTri; + idPlane plane; + int planeNum; + textureVectors_t texVec; + + // put in single area + newTri = CopyMapTri( tri ); + newTri->next = NULL; + + PlaneForTri( tri, plane ); + planeNum = FindFloatPlane( plane ); + + TexVecForTri( &texVec, newTri ); + + AddTriListToArea( e, newTri, planeNum, area, &texVec ); + } else { + // fragment into areas + w = WindingForTri( tri ); + ClipTriIntoTree_r( w, tri, e, e->tree->headnode ); + } +} + +/* +===================== +PutPrimitivesInAreas + +===================== +*/ +void PutPrimitivesInAreas( uEntity_t *e ) { + uBrush_t *b; + int i; + side_t *side; + primitive_t *prim; + mapTri_t *tri; + + common->Printf( "----- PutPrimitivesInAreas -----\n"); + + // allocate space for surface chains for each area + e->areas = (uArea_t *)Mem_Alloc( e->numAreas * sizeof( e->areas[0] ) ); + memset( e->areas, 0, e->numAreas * sizeof( e->areas[0] ) ); + + // for each primitive, clip it to the non-solid leafs + // and divide it into different areas + for ( prim = e->primitives ; prim ; prim = prim->next ) { + b = prim->brush; + + if ( !b ) { + // add curve triangles + for ( tri = prim->tris ; tri ; tri = tri->next ) { + AddMapTriToAreas( tri, e ); + } + continue; + } + + // clip in brush sides + for ( i = 0 ; i < b->numsides ; i++ ) { + side = &b->sides[i]; + if ( !side->visibleHull ) { + continue; + } + PutWindingIntoAreas_r( e, side->visibleHull, side, e->tree->headnode ); + } + } + + + // optionally inline some of the func_static models + if ( dmapGlobals.entityNum == 0 ) { + bool inlineAll = dmapGlobals.uEntities[0].mapEntity->epairs.GetBool( "inlineAllStatics" ); + + for ( int eNum = 1 ; eNum < dmapGlobals.num_entities ; eNum++ ) { + uEntity_t *entity = &dmapGlobals.uEntities[eNum]; + const char *className = entity->mapEntity->epairs.GetString( "classname" ); + if ( idStr::Icmp( className, "func_static" ) ) { + continue; + } + if ( !entity->mapEntity->epairs.GetBool( "inline" ) && !inlineAll ) { + continue; + } + const char *modelName = entity->mapEntity->epairs.GetString( "model" ); + if ( !modelName ) { + continue; + } + idRenderModel *model = renderModelManager->FindModel( modelName ); + + common->Printf( "inlining %s.\n", entity->mapEntity->epairs.GetString( "name" ) ); + + idMat3 axis; + // get the rotation matrix in either full form, or single angle form + if ( !entity->mapEntity->epairs.GetMatrix( "rotation", "1 0 0 0 1 0 0 0 1", axis ) ) { + float angle = entity->mapEntity->epairs.GetFloat( "angle" ); + if ( angle != 0.0f ) { + axis = idAngles( 0.0f, angle, 0.0f ).ToMat3(); + } else { + axis.Identity(); + } + } + + idVec3 origin = entity->mapEntity->epairs.GetVector( "origin" ); + + for ( i = 0 ; i < model->NumSurfaces() ; i++ ) { + const modelSurface_t *surface = model->Surface( i ); + const srfTriangles_t *tri = surface->geometry; + + mapTri_t mapTri; + memset( &mapTri, 0, sizeof( mapTri ) ); + mapTri.material = surface->shader; + // don't let discretes (autosprites, etc) merge together + if ( mapTri.material->IsDiscrete() ) { + mapTri.mergeGroup = (void *)surface; + } + for ( int j = 0 ; j < tri->numIndexes ; j += 3 ) { + for ( int k = 0 ; k < 3 ; k++ ) { + idVec3 v = tri->verts[tri->indexes[j+k]].xyz; + + mapTri.v[k].xyz = v * axis + origin; + + mapTri.v[k].normal = tri->verts[tri->indexes[j+k]].normal * axis; + mapTri.v[k].st = tri->verts[tri->indexes[j+k]].st; + } + AddMapTriToAreas( &mapTri, e ); + } + } + } + } +} + +//============================================================================ + +/* +================= +ClipTriByLight + +Carves a triangle by the frustom planes of a light, producing +a (possibly empty) list of triangles on the inside and outside. + +The original triangle is not modified. + +If no clipping is required, the result will be a copy of the original. + +If clipping was required, the outside fragments will be planar clips, which +will benefit from re-optimization. +================= +*/ +static void ClipTriByLight( const mapLight_t *light, const mapTri_t *tri, + mapTri_t **in, mapTri_t **out ) { + idWinding *inside, *oldInside; + idWinding *outside[6]; + bool hasOutside; + int i; + + *in = NULL; + *out = NULL; + + // clip this winding to the light + inside = WindingForTri( tri ); + hasOutside = false; + for ( i = 0 ; i < 6 ; i++ ) { + oldInside = inside; + if ( oldInside ) { + oldInside->Split( light->frustum[i], 0, &outside[i], &inside ); + delete oldInside; + } + else { + outside[i] = NULL; + } + if ( outside[i] ) { + hasOutside = true; + } + } + + if ( !inside ) { + // the entire winding is outside this light + + // free the clipped fragments + for ( i = 0 ; i < 6 ; i++ ) { + if ( outside[i] ) { + delete outside[i]; + } + } + + *out = CopyMapTri( tri ); + (*out)->next = NULL; + + return; + } + + if ( !hasOutside ) { + // the entire winding is inside this light + + // free the inside copy + delete inside; + + *in = CopyMapTri( tri ); + (*in)->next = NULL; + + return; + } + + // the winding is split + *in = WindingToTriList( inside, tri ); + delete inside; + + // combine all the outside fragments + for ( i = 0 ; i < 6 ; i++ ) { + if ( outside[i] ) { + mapTri_t *list; + + list = WindingToTriList( outside[i], tri ); + delete outside[i]; + *out = MergeTriLists( *out, list ); + } + } +} + +/* +================= +BoundOptimizeGroup +================= +*/ +static void BoundOptimizeGroup( optimizeGroup_t *group ) { + group->bounds.Clear(); + for ( mapTri_t *tri = group->triList ; tri ; tri = tri->next ) { + group->bounds.AddPoint( tri->v[0].xyz ); + group->bounds.AddPoint( tri->v[1].xyz ); + group->bounds.AddPoint( tri->v[2].xyz ); + } +} + +/* +==================== +BuildLightShadows + +Build the beam tree and shadow volume surface for a light +==================== +*/ +static void BuildLightShadows( uEntity_t *e, mapLight_t *light ) { + int i; + optimizeGroup_t *group; + mapTri_t *tri; + mapTri_t *shadowers; + optimizeGroup_t *shadowerGroups; + idVec3 lightOrigin; + bool hasPerforatedSurface = false; + + // + // build a group list of all the triangles that will contribute to + // the optimized shadow volume, leaving the original triangles alone + // + + + // shadowers will contain all the triangles that will contribute to the + // shadow volume + shadowerGroups = NULL; + lightOrigin = light->globalLightOrigin; + + // if the light is no-shadows, don't add any surfaces + // to the beam tree at all + if ( !light->parms.noShadows + && light->lightShader->LightCastsShadows() ) { + for ( i = 0 ; i < e->numAreas ; i++ ) { + for ( group = e->areas[i].groups ; group ; group = group->nextGroup ) { + // if the surface doesn't cast shadows, skip it + if ( !group->material->SurfaceCastsShadow() ) { + continue; + } + + // if the group doesn't face away from the light, it + // won't contribute to the shadow volume + if ( dmapGlobals.mapPlanes[ group->planeNum ].Distance( lightOrigin ) > 0 ) { + continue; + } + + // if the group bounds doesn't intersect the light bounds, + // skip it + if ( !group->bounds.IntersectsBounds( light->frustumBounds ) ) { + continue; + } + + // build up a list of the triangle fragments inside the + // light frustum + shadowers = NULL; + for ( tri = group->triList ; tri ; tri = tri->next ) { + mapTri_t *in, *out; + + // clip it to the light frustum + ClipTriByLight( light, tri, &in, &out ); + FreeTriList( out ); + shadowers = MergeTriLists( shadowers, in ); + } + + // if we didn't get any out of this group, we don't + // need to create a new group in the shadower list + if ( !shadowers ) { + continue; + } + + // find a group in shadowerGroups to add these to + // we will ignore everything but planenum, and we + // can merge across areas + optimizeGroup_t *check; + + for ( check = shadowerGroups ; check ; check = check->nextGroup ) { + if ( check->planeNum == group->planeNum ) { + break; + } + } + if ( !check ) { + check = (optimizeGroup_t *)Mem_Alloc( sizeof( *check ) ); + *check = *group; + check->triList = NULL; + check->nextGroup = shadowerGroups; + shadowerGroups = check; + } + + // if any surface is a shadow-casting perforated or translucent surface, we + // can't use the face removal optimizations because we can see through + // some of the faces + if ( group->material->Coverage() != MC_OPAQUE ) { + hasPerforatedSurface = true; + } + + check->triList = MergeTriLists( check->triList, shadowers ); + } + } + } + + // take the shadower group list and create a beam tree and shadow volume + light->shadowTris = CreateLightShadow( shadowerGroups, light ); + + if ( light->shadowTris && hasPerforatedSurface ) { + // can't ever remove front faces, because we can see through some of them + light->shadowTris->numShadowIndexesNoCaps = light->shadowTris->numShadowIndexesNoFrontCaps = + light->shadowTris->numIndexes; + } + + // we don't need the original shadower triangles for anything else + FreeOptimizeGroupList( shadowerGroups ); +} + + +/* +==================== +CarveGroupsByLight + +Divide each group into an inside group and an outside group, based +on which fragments are illuminated by the light's beam tree +==================== +*/ +static void CarveGroupsByLight( uEntity_t *e, mapLight_t *light ) { + int i; + optimizeGroup_t *group, *newGroup, *carvedGroups, *nextGroup; + mapTri_t *tri, *inside, *outside; + uArea_t *area; + + for ( i = 0 ; i < e->numAreas ; i++ ) { + area = &e->areas[i]; + carvedGroups = NULL; + + // we will be either freeing or reassigning the groups as we go + for ( group = area->groups ; group ; group = nextGroup ) { + nextGroup = group->nextGroup; + // if the surface doesn't get lit, don't carve it up + if ( ( light->lightShader->IsFogLight() && !group->material->ReceivesFog() ) + || ( !light->lightShader->IsFogLight() && !group->material->ReceivesLighting() ) + || !group->bounds.IntersectsBounds( light->frustumBounds ) ) { + + group->nextGroup = carvedGroups; + carvedGroups = group; + continue; + } + + if ( group->numGroupLights == MAX_GROUP_LIGHTS ) { + common->Error( "MAX_GROUP_LIGHTS around %f %f %f", + group->triList->v[0].xyz[0], group->triList->v[0].xyz[1], group->triList->v[0].xyz[2] ); + } + + // if the group doesn't face the light, + // it won't get carved at all + if ( !light->lightShader->LightEffectsBackSides() && + !group->material->ReceivesLightingOnBackSides() && + dmapGlobals.mapPlanes[ group->planeNum ].Distance( light->parms.origin ) <= 0 ) { + + group->nextGroup = carvedGroups; + carvedGroups = group; + continue; + } + + // split into lists for hit-by-light, and not-hit-by-light + inside = NULL; + outside = NULL; + + for ( tri = group->triList ; tri ; tri = tri->next ) { + mapTri_t *in, *out; + + ClipTriByLight( light, tri, &in, &out ); + inside = MergeTriLists( inside, in ); + outside = MergeTriLists( outside, out ); + } + + if ( inside ) { + newGroup = (optimizeGroup_t *)Mem_Alloc( sizeof( *newGroup ) ); + *newGroup = *group; + newGroup->groupLights[newGroup->numGroupLights] = light; + newGroup->numGroupLights++; + newGroup->triList = inside; + newGroup->nextGroup = carvedGroups; + carvedGroups = newGroup; + } + + if ( outside ) { + newGroup = (optimizeGroup_t *)Mem_Alloc( sizeof( *newGroup ) ); + *newGroup = *group; + newGroup->triList = outside; + newGroup->nextGroup = carvedGroups; + carvedGroups = newGroup; + } + + // free the original + group->nextGroup = NULL; + FreeOptimizeGroupList( group ); + } + + // replace this area's group list with the new one + area->groups = carvedGroups; + } +} + +/* +===================== +Prelight + +Break optimize groups up into additional groups at light boundaries, so +optimization won't cross light bounds +===================== +*/ +void Prelight( uEntity_t *e ) { + int i; + int start, end; + mapLight_t *light; + + // don't prelight anything but the world entity + if ( dmapGlobals.entityNum != 0 ) { + return; + } + + if ( dmapGlobals.shadowOptLevel > 0 ) { + common->Printf( "----- BuildLightShadows -----\n" ); + start = Sys_Milliseconds(); + + // calc bounds for all the groups to speed things up + for ( i = 0 ; i < e->numAreas ; i++ ) { + uArea_t *area = &e->areas[i]; + + for ( optimizeGroup_t *group = area->groups ; group ; group = group->nextGroup ) { + BoundOptimizeGroup( group ); + } + } + + for ( i = 0 ; i < dmapGlobals.mapLights.Num() ; i++ ) { + light = dmapGlobals.mapLights[i]; + BuildLightShadows( e, light ); + } + + end = Sys_Milliseconds(); + common->Printf( "%5.1f seconds for BuildLightShadows\n", ( end - start ) / 1000.0 ); + } + + + if ( !dmapGlobals.noLightCarve ) { + common->Printf( "----- CarveGroupsByLight -----\n" ); + start = Sys_Milliseconds(); + // now subdivide the optimize groups into additional groups for + // each light that illuminates them + for ( i = 0 ; i < dmapGlobals.mapLights.Num() ; i++ ) { + light = dmapGlobals.mapLights[i]; + CarveGroupsByLight( e, light ); + } + + end = Sys_Milliseconds(); + common->Printf( "%5.1f seconds for CarveGroupsByLight\n", ( end - start ) / 1000.0 ); + } + +} + + diff --git a/src/tools/radiant/CSG.CPP b/src/tools/radiant/CSG.CPP new file mode 100644 index 0000000..cc12679 --- /dev/null +++ b/src/tools/radiant/CSG.CPP @@ -0,0 +1,687 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" + +const float PLANE_EPSILON = 0.0001f; + +/* +============= +CSG_MakeHollow +============= +*/ +void CSG_MakeHollow (void) +{ + brush_t *b, *front, *back, *next; + face_t *f; + face_t split; + idVec3 move; + int i; + + for (b = selected_brushes.next ; b != &selected_brushes ; b=next) + { + next = b->next; + + if (b->owner->eclass->fixedsize || b->pPatch || b->hiddenBrush || b->modelHandle > 0) + continue; + + for ( f = b->brush_faces; f; f = f->next ) { + split = *f; + VectorScale (f->plane, g_qeglobals.d_gridsize, move); + for (i=0 ; i<3 ; i++) + VectorSubtract (split.planepts[i], move, split.planepts[i]); + + Brush_SplitBrushByFace (b, &split, &front, &back); + if (back) + Brush_Free (back); + if (front) + Brush_AddToList (front, &selected_brushes); + } + Brush_Free (b); + } + Sys_UpdateWindows (W_ALL); +} + +/* +============= +Brush_Merge + + Returns a new brush that is created by merging brush1 and brush2. + May return NULL if brush1 and brush2 do not create a convex brush when merged. + The input brushes brush1 and brush2 stay intact. + + if onlyshape is true then the merge is allowed based on the shape only + otherwise the texture/shader references of faces in the same plane have to + be the same as well. +============= +*/ +brush_t *Brush_Merge(brush_t *brush1, brush_t *brush2, int onlyshape) +{ + int i, shared; + brush_t *newbrush; + face_t *face1, *face2, *newface, *f; + + // check for bounding box overlapp + for (i = 0; i < 3; i++) + { + if (brush1->mins[i] > brush2->maxs[i] + ON_EPSILON + || brush1->maxs[i] < brush2->mins[i] - ON_EPSILON) + { + // never merge if the brushes overlap + return NULL; + } + } + // + shared = 0; + // check if the new brush would be convex... flipped planes make a brush non-convex + for (face1 = brush1->brush_faces; face1; face1 = face1->next) + { + // don't check the faces of brush 1 and 2 touching each other + for (face2 = brush2->brush_faces; face2; face2 = face2->next) + { + if ( face1->plane.Compare( -face2->plane, PLANE_EPSILON ) ) + { + shared++; + // there may only be ONE shared side + if (shared > 1) + return NULL; + break; + } + } + // if this face plane is shared + if (face2) continue; + // + for (face2 = brush2->brush_faces; face2; face2 = face2->next) + { + // don't check the faces of brush 1 and 2 touching each other + for ( f = brush1->brush_faces; f; f = f->next ) { + if ( face2->plane.Compare( -f->plane, PLANE_EPSILON ) ) { + break; + } + } + if ( f ) { + continue; + } + + if ( face1->plane.Compare( face2->plane, PLANE_EPSILON ) ) + { + //if the texture/shader references should be the same but are not + if (!onlyshape && stricmp(face1->texdef.name, face2->texdef.name) != 0) + return NULL; + continue; + } + // + if ( face1->face_winding->PlanesConcave( *face2->face_winding, + face1->plane.Normal(), face2->plane.Normal(), -face1->plane[3], -face2->plane[3])) + { + return NULL; + } //end if + } //end for + } //end for + // + newbrush = Brush_Alloc(); + // + for (face1 = brush1->brush_faces; face1; face1 = face1->next) + { + // don't add the faces of brush 1 and 2 touching each other + for (face2 = brush2->brush_faces; face2; face2 = face2->next) + { + if ( face1->plane.Compare( -face2->plane, PLANE_EPSILON ) ) { + break; + } + } + if ( face2 ) + continue; + // don't add faces with the same plane twice + for (f = newbrush->brush_faces; f; f = f->next) + { + if ( face1->plane.Compare( f->plane, PLANE_EPSILON ) ) + break; + if ( face1->plane.Compare( -f->plane, PLANE_EPSILON ) ) + break; + } + if ( f ) { + continue; + } + + newface = Face_Alloc(); + newface->texdef = face1->texdef; + VectorCopy(face1->planepts[0], newface->planepts[0]); + VectorCopy(face1->planepts[1], newface->planepts[1]); + VectorCopy(face1->planepts[2], newface->planepts[2]); + newface->plane = face1->plane; + newface->next = newbrush->brush_faces; + newbrush->brush_faces = newface; + } + + for (face2 = brush2->brush_faces; face2; face2 = face2->next) { + // don't add the faces of brush 1 and 2 touching each other + for (face1 = brush1->brush_faces; face1; face1 = face1->next) + { + if ( face2->plane.Compare( -face1->plane, PLANE_EPSILON ) ) { + break; + } + } + if (face1) + continue; + // don't add faces with the same plane twice + for (f = newbrush->brush_faces; f; f = f->next) + { + if ( face2->plane.Compare( f->plane, PLANE_EPSILON ) ) + break; + if ( face2->plane.Compare( -f->plane, PLANE_EPSILON ) ) + break; + } + if ( f ) { + continue; + } + // + newface = Face_Alloc(); + newface->texdef = face2->texdef; + VectorCopy(face2->planepts[0], newface->planepts[0]); + VectorCopy(face2->planepts[1], newface->planepts[1]); + VectorCopy(face2->planepts[2], newface->planepts[2]); + newface->plane = face2->plane; + newface->next = newbrush->brush_faces; + newbrush->brush_faces = newface; + } + // link the new brush to an entity + Entity_LinkBrush (brush1->owner, newbrush); + // build windings for the faces + Brush_BuildWindings( newbrush, false); + return newbrush; +} + +/* +============= +Brush_MergeListPairs + + Returns a list with merged brushes. + Tries to merge brushes pair wise. + The input list is destroyed. + Input and output should be a single linked list using .next +============= +*/ +brush_t *Brush_MergeListPairs(brush_t *brushlist, int onlyshape) +{ + int nummerges, merged; + brush_t *b1, *b2, *tail, *newbrush, *newbrushlist; + brush_t *lastb2; + + if (!brushlist) return NULL; + + nummerges = 0; + do + { + for (tail = brushlist; tail; tail = tail->next) + { + if (!tail->next) break; + } + merged = 0; + newbrushlist = NULL; + for (b1 = brushlist; b1; b1 = brushlist) + { + lastb2 = b1; + for (b2 = b1->next; b2; b2 = b2->next) + { + newbrush = Brush_Merge(b1, b2, onlyshape); + if (newbrush) + { + tail->next = newbrush; + lastb2->next = b2->next; + brushlist = brushlist->next; + b1->next = b1->prev = NULL; + b2->next = b2->prev = NULL; + Brush_Free(b1); + Brush_Free(b2); + for (tail = brushlist; tail; tail = tail->next) + { + if (!tail->next) break; + } //end for + merged++; + nummerges++; + break; + } + lastb2 = b2; + } + //if b1 can't be merged with any of the other brushes + if (!b2) + { + brushlist = brushlist->next; + //keep b1 + b1->next = newbrushlist; + newbrushlist = b1; + } + } + brushlist = newbrushlist; + } while(merged); + return newbrushlist; +} + +/* +============= +Brush_MergeList + + Tries to merge all brushes in the list into one new brush. + The input brush list stays intact. + Returns NULL if no merged brush can be created. + To create a new brush the brushes in the list may not overlap and + the outer faces of the brushes together should make a new convex brush. + + if onlyshape is true then the merge is allowed based on the shape only + otherwise the texture/shader references of faces in the same plane have to + be the same as well. +============= +*/ +brush_t *Brush_MergeList(brush_t *brushlist, int onlyshape) +{ + brush_t *brush1, *brush2, *brush3, *newbrush; + face_t *face1, *face2, *face3, *newface, *f; + + if (!brushlist) return NULL; + for (brush1 = brushlist; brush1; brush1 = brush1->next) + { + // check if the new brush would be convex... flipped planes make a brush concave + for (face1 = brush1->brush_faces; face1; face1 = face1->next) + { + // don't check face1 if it touches another brush + for (brush2 = brushlist; brush2; brush2 = brush2->next) + { + if (brush2 == brush1) continue; + for (face2 = brush2->brush_faces; face2; face2 = face2->next) + { + if ( face1->plane.Compare( -face2->plane, PLANE_EPSILON ) ) { + break; + } + } + if (face2) + break; + } + // if face1 touches another brush + if (brush2) + continue; + // + for (brush2 = brush1->next; brush2; brush2 = brush2->next) + { + // don't check the faces of brush 2 touching another brush + for (face2 = brush2->brush_faces; face2; face2 = face2->next) + { + for (brush3 = brushlist; brush3; brush3 = brush3->next) + { + if (brush3 == brush2) continue; + for (face3 = brush3->brush_faces; face3; face3 = face3->next) + { + if ( face2->plane.Compare( -face3->plane, PLANE_EPSILON ) ) + break; + } + if (face3) + break; + } + // if face2 touches another brush + if (brush3) + continue; + // + if ( face1->plane.Compare( face2->plane, PLANE_EPSILON ) ) + { + //if the texture/shader references should be the same but are not + if (!onlyshape && stricmp(face1->texdef.name, face2->texdef.name) != 0) + return NULL; + continue; + } + // + if ( face1->face_winding->PlanesConcave( *face2->face_winding, + face1->plane.Normal(), face2->plane.Normal(), -face1->plane[3], -face2->plane[3])) + { + return NULL; + } + } + } + } + } + // + newbrush = Brush_Alloc(); + // + for (brush1 = brushlist; brush1; brush1 = brush1->next) + { + for (face1 = brush1->brush_faces; face1; face1 = face1->next) + { + // don't add face1 to the new brush if it touches another brush + for (brush2 = brushlist; brush2; brush2 = brush2->next) + { + if (brush2 == brush1) continue; + for (face2 = brush2->brush_faces; face2; face2 = face2->next) + { + if ( face1->plane.Compare( -face2->plane, PLANE_EPSILON ) ) { + break; + } + } + if (face2) + break; + } + if (brush2) + continue; + // don't add faces with the same plane twice + for (f = newbrush->brush_faces; f; f = f->next) + { + if ( face1->plane.Compare( f->plane, PLANE_EPSILON ) ) + break; + if ( face1->plane.Compare( -f->plane, PLANE_EPSILON ) ) + break; + } + if (f) + continue; + // + newface = Face_Alloc(); + newface->texdef = face1->texdef; + VectorCopy(face1->planepts[0], newface->planepts[0]); + VectorCopy(face1->planepts[1], newface->planepts[1]); + VectorCopy(face1->planepts[2], newface->planepts[2]); + newface->plane = face1->plane; + newface->next = newbrush->brush_faces; + newbrush->brush_faces = newface; + } + } + // link the new brush to an entity + Entity_LinkBrush (brushlist->owner, newbrush); + // build windings for the faces + Brush_BuildWindings( newbrush, false); + return newbrush; +} + +/* +============= +Brush_Subtract + + Returns a list of brushes that remain after B is subtracted from A. + May by empty if A is contained inside B. + The originals are undisturbed. +============= +*/ +brush_t *Brush_Subtract(brush_t *a, brush_t *b) +{ + // a - b = out (list) + brush_t *front, *back; + brush_t *in, *out, *next; + face_t *f; + + in = a; + out = NULL; + for (f = b->brush_faces; f && in; f = f->next) + { + Brush_SplitBrushByFace(in, f, &front, &back); + if (in != a) Brush_Free(in); + if (front) + { // add to list + front->next = out; + out = front; + } + in = back; + } + //NOTE: in != a just in case brush b has no faces + if (in && in != a) + { + Brush_Free(in); + } + else + { //didn't really intersect + for (b = out; b; b = next) + { + next = b->next; + b->next = b->prev = NULL; + Brush_Free(b); + } + return a; + } + return out; +} + +/* +============= +CSG_Subtract +============= +*/ +void CSG_Subtract (void) +{ + brush_t *b, *s, *fragments, *nextfragment, *frag, *next, *snext; + brush_t fragmentlist; + int i, numfragments, numbrushes; + + Sys_Status ("Subtracting...\n"); + + if (selected_brushes.next == &selected_brushes) + { + Sys_Status("No brushes selected.\n"); + return; + } + + fragmentlist.next = &fragmentlist; + fragmentlist.prev = &fragmentlist; + + numfragments = 0; + numbrushes = 0; + for (b = selected_brushes.next ; b != &selected_brushes ; b=next) + { + next = b->next; + + if (b->owner->eclass->fixedsize || b->modelHandle > 0) + continue; // can't use texture from a fixed entity, so don't subtract + + // chop all fragments further up + for (s = fragmentlist.next; s != &fragmentlist; s = snext) + { + snext = s->next; + + for (i=0 ; i<3 ; i++) + if (b->mins[i] >= s->maxs[i] - ON_EPSILON + || b->maxs[i] <= s->mins[i] + ON_EPSILON) + break; + if (i != 3) + continue; // definately don't touch + fragments = Brush_Subtract(s, b); + // if the brushes did not really intersect + if (fragments == s) + continue; + // try to merge fragments + fragments = Brush_MergeListPairs(fragments, true); + // add the fragments to the list + for (frag = fragments; frag; frag = nextfragment) + { + nextfragment = frag->next; + frag->next = NULL; + frag->owner = s->owner; + Brush_AddToList(frag, &fragmentlist); + } + // free the original brush + Brush_Free(s); + } + + // chop any active brushes up + for (s = active_brushes.next; s != &active_brushes; s = snext) + { + snext = s->next; + + if (s->owner->eclass->fixedsize || s->pPatch || s->hiddenBrush || s->modelHandle > 0) + continue; + + //face_t *pFace = s->brush_faces; + if ( s->brush_faces->d_texture && ( s->brush_faces->d_texture->GetContentFlags()& CONTENTS_NOCSG ) ) + { + continue; + } + + for (i=0 ; i<3 ; i++) + if (b->mins[i] >= s->maxs[i] - ON_EPSILON + || b->maxs[i] <= s->mins[i] + ON_EPSILON) + break; + if (i != 3) + continue; // definately don't touch + + fragments = Brush_Subtract(s, b); + // if the brushes did not really intersect + if (fragments == s) + continue; + // + Undo_AddBrush(s); + // one extra brush chopped up + numbrushes++; + // try to merge fragments + fragments = Brush_MergeListPairs(fragments, true); + // add the fragments to the list + for (frag = fragments; frag; frag = nextfragment) + { + nextfragment = frag->next; + frag->next = NULL; + frag->owner = s->owner; + Brush_AddToList(frag, &fragmentlist); + } + // free the original brush + Brush_Free(s); + } + } + + // move all fragments to the active brush list + for (frag = fragmentlist.next; frag != &fragmentlist; frag = nextfragment) + { + nextfragment = frag->next; + numfragments++; + Brush_RemoveFromList(frag); + Brush_AddToList(frag, &active_brushes); + Undo_EndBrush(frag); + } + + if (numfragments == 0) + { + common->Printf("Selected brush%s did not intersect with any other brushes.\n", + (selected_brushes.next->next == &selected_brushes) ? "":"es"); + return; + } + Sys_Status("done."); + common->Printf(" (created %d fragment%s out of %d brush%s)\n", numfragments, (numfragments == 1)?"":"s", + numbrushes, (numbrushes == 1)?"":"es"); + Sys_UpdateWindows(W_ALL); +} + +/* +============= +CSG_Merge +============= +*/ +void CSG_Merge(void) +{ + brush_t *b, *next, *newlist, *newbrush; + struct entity_s *owner; + + Sys_Status("Merging...\n"); + + if (selected_brushes.next == &selected_brushes) + { + Sys_Status("No brushes selected.\n"); + return; + } + + if (selected_brushes.next->next == &selected_brushes) + { + Sys_Status("At least two brushes have to be selected.\n"); + return; + } + + owner = selected_brushes.next->owner; + + for (b = selected_brushes.next; b != &selected_brushes; b = next) + { + next = b->next; + + if (b->owner->eclass->fixedsize || b->modelHandle > 0) + { + // can't use texture from a fixed entity, so don't subtract + Sys_Status("Cannot add fixed size entities.\n"); + return; + } + + if (b->pPatch) + { + Sys_Status("Cannot add patches.\n"); + return; + } + + if ( b->brush_faces->d_texture && ( b->brush_faces->d_texture->GetContentFlags() & CONTENTS_NOCSG ) ) + { + Sys_Status("Cannot add brushes using shaders that don't allows CSG operations.\n"); + return; + } + + if (b->owner != owner) + { + Sys_Status("Cannot add brushes from different entities.\n"); + return; + } + + } + + newlist = NULL; + for (b = selected_brushes.next; b != &selected_brushes; b = next) + { + next = b->next; + + Brush_RemoveFromList(b); + b->next = newlist; + b->prev = NULL; + newlist = b; + } + + newbrush = Brush_MergeList(newlist, true); + // if the new brush would not be convex + if (!newbrush) + { + // add the brushes back into the selection + for (b = newlist; b; b = next) + { + next = b->next; + b->next = NULL; + b->prev = NULL; + Brush_AddToList(b, &selected_brushes); + } + Sys_Status("Cannot add a set of brushes with a concave hull.\n"); + return; + } + // free the original brushes + for (b = newlist; b; b = next) + { + next = b->next; + b->next = NULL; + b->prev = NULL; + Brush_Free(b); + } + Brush_AddToList(newbrush, &selected_brushes); + + Sys_Status ("done.\n"); + Sys_UpdateWindows (W_ALL); +} diff --git a/src/tools/radiant/CamWnd.cpp b/src/tools/radiant/CamWnd.cpp new file mode 100644 index 0000000..acf4349 --- /dev/null +++ b/src/tools/radiant/CamWnd.cpp @@ -0,0 +1,2165 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" +#include "XYWnd.h" +#include "CamWnd.h" +#include "splines.h" +#include + +#include "../../renderer/tr_local.h" +#include "../../renderer/model_local.h" // for idRenderModelMD5 + +#ifdef _DEBUG + #define new DEBUG_NEW + #undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +extern void DrawPathLines(); + +int g_axialAnchor = -1; +int g_axialDest = -1; +bool g_bAxialMode = false; + +void ValidateAxialPoints() { + int faceCount = g_ptrSelectedFaces.GetSize(); + if (faceCount > 0) { + face_t *selFace = reinterpret_cast < face_t * > (g_ptrSelectedFaces.GetAt(0)); + if (g_axialAnchor >= selFace->face_winding->GetNumPoints()) { + g_axialAnchor = 0; + } + if (g_axialDest >= selFace->face_winding->GetNumPoints()) { + g_axialDest = 0; + } + } else { + g_axialDest = 0; + g_axialAnchor = 0; + } +} + +// CCamWnd +IMPLEMENT_DYNCREATE(CCamWnd, CWnd); + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +CCamWnd::CCamWnd() { + m_pXYFriend = NULL; + memset(&m_Camera, 0, sizeof(camera_t)); + m_pSide_select = NULL; + m_bClipMode = false; + worldDirty = true; + worldModel = NULL; + renderMode = false; + rebuildMode = false; + entityMode = false; + animationMode = false; + selectMode = false; + soundMode = false; + saveValid = false; + Cam_Init(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +CCamWnd::~CCamWnd() { +} + +BEGIN_MESSAGE_MAP(CCamWnd, CWnd) +//{{AFX_MSG_MAP(CCamWnd) + ON_WM_KEYDOWN() + ON_WM_PAINT() + ON_WM_DESTROY() + ON_WM_CLOSE() + ON_WM_MOUSEMOVE() + ON_WM_LBUTTONDOWN() + ON_WM_LBUTTONUP() + ON_WM_MBUTTONDOWN() + ON_WM_MBUTTONUP() + ON_WM_RBUTTONDOWN() + ON_WM_RBUTTONUP() + ON_WM_CREATE() + ON_WM_SIZE() + ON_WM_KEYUP() + ON_WM_NCCALCSIZE() + ON_WM_KILLFOCUS() + ON_WM_SETFOCUS() + ON_WM_TIMER() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() +/* + ======================================================================================================================= + ======================================================================================================================= + */ +LONG WINAPI CamWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + RECT rect; + + GetClientRect(hWnd, &rect); + + switch (uMsg) + { + case WM_KILLFOCUS: + case WM_SETFOCUS: + SendMessage(hWnd, WM_NCACTIVATE, uMsg == WM_SETFOCUS, 0); + return 0; + + case WM_NCCALCSIZE: // don't let windows copy pixels + DefWindowProc(hWnd, uMsg, wParam, lParam); + return WVR_REDRAW; + } + + return DefWindowProc(hWnd, uMsg, wParam, lParam); +} + +// +// ======================================================================================================================= +// CCamWnd message handlers +// ======================================================================================================================= +// +BOOL CCamWnd::PreCreateWindow(CREATESTRUCT &cs) { + WNDCLASS wc; + HINSTANCE hInstance = AfxGetInstanceHandle(); + if (::GetClassInfo(hInstance, CAMERA_WINDOW_CLASS, &wc) == FALSE) { + // Register a new class + memset(&wc, 0, sizeof(wc)); + + // wc.style = CS_NOCLOSE | CS_OWNDC; + wc.style = CS_NOCLOSE; + wc.hInstance = hInstance; + wc.lpszClassName = CAMERA_WINDOW_CLASS; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.lpfnWndProc = CamWndProc; + if (AfxRegisterClass(&wc) == FALSE) { + common->Warning("Radiant: failed to register %s (error %lu)", CAMERA_WINDOW_CLASS, GetLastError()); + return FALSE; + } + } + + cs.lpszClass = CAMERA_WINDOW_CLASS; + cs.lpszName = "CAM"; + if (cs.style != QE3_CHILDSTYLE) { + cs.style = QE3_SPLITTER_STYLE; + } + + BOOL bResult = CWnd::PreCreateWindow(cs); + + // + // See if the class already exists and if not then we need to register our new + // window class. + // + return bResult; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CCamWnd::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { + g_pParentWnd->HandleKey(nChar, nRepCnt, nFlags); +} + +brush_t *g_pSplitList = NULL; + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CCamWnd::OnPaint() { + CPaintDC dc(this); // device context for painting + bool bPaint = true; + + if (!qwglMakeCurrent(dc.m_hDC, win32.hGLRC)) { + common->Printf("ERROR: wglMakeCurrent failed..\n "); + common->Printf("Please restart " EDITOR_WINDOWTEXT " if the camera view is not working\n"); + } + else { + QE_CheckOpenGLForErrors(); + g_pSplitList = NULL; + if (g_bClipMode) { + if (g_Clip1.Set() && g_Clip2.Set()) { + g_pSplitList = ((g_pParentWnd->ActiveXY()->GetViewType() == XZ) ? !g_bSwitch : g_bSwitch) ? &g_brBackSplits : &g_brFrontSplits; + } + } + + Cam_Draw(); + QE_CheckOpenGLForErrors(); + qwglSwapBuffers(dc.m_hDC); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CCamWnd::SetXYFriend(CXYWnd *pWnd) { + m_pXYFriend = pWnd; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CCamWnd::OnDestroy() { + CWnd::OnDestroy(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CCamWnd::OnClose() { + CWnd::OnClose(); +} + +extern void Select_RotateTexture(float amt, bool absolute); + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CCamWnd::OnMouseMove(UINT nFlags, CPoint point) { + CRect r; + GetClientRect(r); + if (GetCapture() == this && (GetAsyncKeyState(VK_MENU) & 0x8000) && !((GetAsyncKeyState(VK_SHIFT) & 0x8000) || (GetAsyncKeyState(VK_CONTROL) & 0x8000))) { + if (GetAsyncKeyState(VK_CONTROL) & 0x8000) { + Select_RotateTexture((float)point.y - m_ptLastCursor.y); + } + else if (GetAsyncKeyState(VK_SHIFT) & 0x8000) { + Select_ScaleTexture((float)point.x - m_ptLastCursor.x, (float)m_ptLastCursor.y - point.y); + } + else { + Select_ShiftTexture((float)point.x - m_ptLastCursor.x, (float)m_ptLastCursor.y - point.y); + } + } + else { + Cam_MouseMoved(point.x, r.bottom - 1 - point.y, nFlags); + } + + m_ptLastCursor = point; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CCamWnd::OnLButtonDown(UINT nFlags, CPoint point) { + m_ptLastCursor = point; + OriginalMouseDown(nFlags, point); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CCamWnd::OnLButtonUp(UINT nFlags, CPoint point) { + OriginalMouseUp(nFlags, point); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CCamWnd::OnMButtonDown(UINT nFlags, CPoint point) { + OriginalMouseDown(nFlags, point); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CCamWnd::OnMButtonUp(UINT nFlags, CPoint point) { + OriginalMouseUp(nFlags, point); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CCamWnd::OnRButtonDown(UINT nFlags, CPoint point) { + OriginalMouseDown(nFlags, point); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CCamWnd::OnRButtonUp(UINT nFlags, CPoint point) { + OriginalMouseUp(nFlags, point); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +int CCamWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) { + if (CWnd::OnCreate(lpCreateStruct) == -1) { + return -1; + } + + CDC *pDC = GetDC(); + HDC hDC = pDC->GetSafeHdc(); + + QEW_SetupPixelFormat(hDC, true); + + HFONT hfont = CreateFont( + 12, // logical height of font + 0, // logical average character width + 0, // angle of escapement + 0, // base-line orientation angle + 0, // font weight + 0, // italic attribute flag + 0, // underline attribute flag + 0, // strikeout attribute flag + 0, // character set identifier + 0, // output precision + 0, // clipping precision + 0, // output quality + FIXED_PITCH | FF_MODERN, // pitch and family + "Lucida Console" // pointer to typeface name string + ); + + if (!hfont) { + Error("couldn't create font"); + } + + HFONT hOldFont = (HFONT)SelectObject(hDC, hfont); + + wglMakeCurrent (hDC, win32.hGLRC); + + if ((g_qeglobals.d_font_list = qglGenLists(256)) == 0) { + common->Warning( "couldn't create font dlists" ); + } + + // create the bitmap display lists we're making images of glyphs 0 thru 255 + if ( !qwglUseFontBitmaps(hDC, 0, 255, g_qeglobals.d_font_list) ) { + common->Warning( "wglUseFontBitmaps failed (%d). Trying again.", GetLastError() ); + + // FIXME: This is really wacky, sometimes the first call fails, but calling it again makes it work + // This probably indicates there's something wrong somewhere else in the code, but I'm not sure what + if ( !qwglUseFontBitmaps(hDC, 0, 255, g_qeglobals.d_font_list) ) { + common->Warning( "wglUseFontBitmaps failed again (%d). Trying outlines.", GetLastError() ); + + if (!qwglUseFontOutlines(hDC, 0, 255, g_qeglobals.d_font_list, 0.0f, 0.1f, WGL_FONT_LINES, NULL)) { + common->Warning( "wglUseFontOutlines also failed (%d), no coordinate text will be visible.", GetLastError() ); + } + } + } + + SelectObject(hDC, hOldFont); + ReleaseDC(pDC); + + // indicate start of glyph display lists + qglListBase(g_qeglobals.d_font_list); + + // report OpenGL information + common->Printf("GL_VENDOR: %s\n", qglGetString(GL_VENDOR)); + common->Printf("GL_RENDERER: %s\n", qglGetString(GL_RENDERER)); + common->Printf("GL_VERSION: %s\n", qglGetString(GL_VERSION)); + common->Printf("GL_EXTENSIONS: %s\n", qglGetString(GL_EXTENSIONS)); + + return 0; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CCamWnd::OriginalMouseUp(UINT nFlags, CPoint point) { + CRect r; + GetClientRect(r); + Cam_MouseUp(point.x, r.bottom - 1 - point.y, nFlags); + if (!(nFlags & (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON))) { + ReleaseCapture(); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CCamWnd::OriginalMouseDown(UINT nFlags, CPoint point) { + // if (GetTopWindow()->GetSafeHwnd() != GetSafeHwnd()) BringWindowToTop(); + CRect r; + GetClientRect(r); + SetFocus(); + SetCapture(); + + // if (!(GetAsyncKeyState(VK_MENU) & 0x8000)) + Cam_MouseDown(point.x, r.bottom - 1 - point.y, nFlags); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CCamWnd::Cam_Init() { + // m_Camera.draw_mode = cd_texture; + m_Camera.origin[0] = 0.0f; + m_Camera.origin[1] = 20.0f; + m_Camera.origin[2] = 72.0f; + m_Camera.color[0] = 0.3f; + m_Camera.color[1] = 0.3f; + m_Camera.color[2] = 0.3f; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CCamWnd::Cam_BuildMatrix() { + float xa, ya; + float matrix[4][4]; + int i; + + xa = ((renderMode) ? -m_Camera.angles[PITCH] : m_Camera.angles[PITCH]) * idMath::M_DEG2RAD; + ya = m_Camera.angles[YAW] * idMath::M_DEG2RAD; + + // the movement matrix is kept 2d + m_Camera.forward[0] = cos(ya); + m_Camera.forward[1] = sin(ya); + m_Camera.right[0] = m_Camera.forward[1]; + m_Camera.right[1] = -m_Camera.forward[0]; + + qglGetFloatv(GL_PROJECTION_MATRIX, &matrix[0][0]); + + for (i = 0; i < 3; i++) { + m_Camera.vright[i] = matrix[i][0]; + m_Camera.vup[i] = matrix[i][1]; + m_Camera.vpn[i] = matrix[i][2]; + } + + m_Camera.vright.Normalize(); + m_Camera.vup.Normalize(); + m_Camera.vpn.Normalize(); + InitCull(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ + +void CCamWnd::Cam_ChangeFloor(bool up) { + brush_t *b; + float d, bestd, current; + idVec3 start, dir; + + start[0] = m_Camera.origin[0]; + start[1] = m_Camera.origin[1]; + start[2] = HUGE_DISTANCE; + dir[0] = dir[1] = 0; + dir[2] = -1; + + current = HUGE_DISTANCE - (m_Camera.origin[2] - 72); + if (up) { + bestd = 0; + } + else { + bestd = HUGE_DISTANCE*2; + } + + for (b = active_brushes.next; b != &active_brushes; b = b->next) { + if (!Brush_Ray(start, dir, b, &d)) { + continue; + } + + if (up && d < current && d > bestd) { + bestd = d; + } + + if (!up && d > current && d < bestd) { + bestd = d; + } + } + + if (bestd == 0 || bestd == HUGE_DISTANCE*2) { + return; + } + + m_Camera.origin[2] += current - bestd; + Sys_UpdateWindows(W_CAMERA | W_Z_OVERLAY); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CCamWnd::Cam_PositionDrag() { + int x, y; + Sys_GetCursorPos(&x, &y); + if (x != m_ptCursor.x || y != m_ptCursor.y) { + x -= m_ptCursor.x; + VectorMA(m_Camera.origin, x, m_Camera.vright, m_Camera.origin); + y -= m_ptCursor.y; + m_Camera.origin[2] -= y; + SetCursorPos(m_ptCursor.x, m_ptCursor.y); + Sys_UpdateWindows(W_CAMERA | W_XY_OVERLAY); + } +} + +void CCamWnd::Cam_MouseLook() { + CPoint current; + + GetCursorPos(¤t); + if (current.x != m_ptCursor.x || current.y != m_ptCursor.y) { + + current.x -= m_ptCursor.x; + current.y -= m_ptCursor.y; + + m_Camera.angles[PITCH] -= (float)((float)current.y * 0.25f); + m_Camera.angles[YAW] -= (float)((float)current.x * 0.25f); + + SetCursorPos(m_ptCursor.x, m_ptCursor.y); + + Cam_BuildMatrix(); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CCamWnd::Cam_MouseControl(float dtime) { + int xl, xh; + int yl, yh; + float xf, yf; + if (g_PrefsDlg.m_nMouseButtons == 2) { + if (m_nCambuttonstate != (MK_RBUTTON | MK_SHIFT)) { + return; + } + } + else { + if (m_nCambuttonstate != MK_RBUTTON) { + return; + } + } + + xf = (float)(m_ptButton.x - m_Camera.width / 2) / (m_Camera.width / 2); + yf = (float)(m_ptButton.y - m_Camera.height / 2) / (m_Camera.height / 2); + + xl = m_Camera.width / 3; + xh = xl * 2; + yl = m_Camera.height / 3; + yh = yl * 2; + + // common->Printf("xf-%f yf-%f xl-%i xh-i% yl-i% yh-i%\n",xf,yf,xl,xh,yl,yh); +#if 0 + + // strafe + if (buttony < yl && (buttonx < xl || buttonx > xh)) { + VectorMA(camera.origin, xf * dtime * g_nMoveSpeed, camera.right, camera.origin); + } + else +#endif + { + xf *= 1.0f - idMath::Fabs(yf); + if ( xf < 0.0f ) { + xf += 0.1f; + if ( xf > 0.0f ) { + xf = 0.0f; + } + } + else { + xf -= 0.1f; + if ( xf < 0.0f ) { + xf = 0.0f; + } + } + + VectorMA(m_Camera.origin, yf * dtime * g_PrefsDlg.m_nMoveSpeed, m_Camera.forward, m_Camera.origin); + m_Camera.angles[YAW] += xf * -dtime * g_PrefsDlg.m_nAngleSpeed; + } + + Cam_BuildMatrix(); + int nUpdate = (g_PrefsDlg.m_bCamXYUpdate) ? (W_CAMERA | W_XY) : (W_CAMERA); + Sys_UpdateWindows(nUpdate); + g_pParentWnd->PostMessage(WM_TIMER, 0, 0); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CCamWnd::Cam_MouseDown(int x, int y, int buttons) { + idVec3 dir; + float f, r, u; + int i; + + // calc ray direction + u = (float)(y - m_Camera.height / 2) / (m_Camera.width / 2); + r = (float)(x - m_Camera.width / 2) / (m_Camera.width / 2); + f = 1; + + for (i = 0; i < 3; i++) { + dir[i] = m_Camera.vpn[i] * f + m_Camera.vright[i] * r + m_Camera.vup[i] * u; + } + + dir.Normalize(); + + GetCursorPos(&m_ptCursor); + + m_nCambuttonstate = buttons; + m_ptButton.x = x; + m_ptButton.y = y; + + // + // LBUTTON = manipulate selection shift-LBUTTON = select middle button = grab + // texture ctrl-middle button = set entire brush to texture ctrl-shift-middle + // button = set single face to texture + // + int nMouseButton = g_PrefsDlg.m_nMouseButtons == 2 ? MK_RBUTTON : MK_MBUTTON; + if + ( + (buttons == MK_LBUTTON) || + (buttons == (MK_LBUTTON | MK_SHIFT)) || + (buttons == (MK_LBUTTON | MK_CONTROL)) || + (buttons == (MK_LBUTTON | MK_CONTROL | MK_SHIFT)) || + (buttons == nMouseButton) || + (buttons == (nMouseButton | MK_SHIFT)) || + (buttons == (nMouseButton | MK_CONTROL)) || + (buttons == (nMouseButton | MK_SHIFT | MK_CONTROL)) + ) { + if (g_PrefsDlg.m_nMouseButtons == 2 && (buttons == (MK_RBUTTON | MK_SHIFT))) { + Cam_MouseControl( 0.1f ); + } + else { + // something global needs to track which window is responsible for stuff + Patch_SetView(W_CAMERA); + Drag_Begin(x, y, buttons, m_Camera.vright, m_Camera.vup, m_Camera.origin, dir); + } + + return; + } + + if ( buttons == MK_RBUTTON ) { + Cam_MouseControl( 0.1f ); + return; + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CCamWnd::Cam_MouseUp(int x, int y, int buttons) { + m_nCambuttonstate = 0; + Drag_MouseUp(buttons); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CCamWnd::Cam_MouseMoved(int x, int y, int buttons) { + m_nCambuttonstate = buttons; + if (!buttons) { + return; + } + + m_ptButton.x = x; + m_ptButton.y = y; + + if (buttons == (MK_RBUTTON | MK_CONTROL)) { + Cam_PositionDrag(); + Sys_UpdateWindows(W_XY | W_CAMERA | W_Z); + return; + } + else if ( buttons == (MK_RBUTTON | MK_CONTROL | MK_SHIFT) ) { + Cam_MouseLook(); + Sys_UpdateWindows(W_XY | W_CAMERA | W_Z); + return; + } + + GetCursorPos(&m_ptCursor); + + if (buttons & (MK_LBUTTON | MK_MBUTTON)) { + Drag_MouseMoved(x, y, buttons); + Sys_UpdateWindows(W_XY | W_CAMERA | W_Z); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CCamWnd::InitCull() { + int i; + + VectorSubtract(m_Camera.vpn, m_Camera.vright, m_vCull1); + VectorAdd(m_Camera.vpn, m_Camera.vright, m_vCull2); + + for (i = 0; i < 3; i++) { + if (m_vCull1[i] > 0) { + m_nCullv1[i] = 3 + i; + } + else { + m_nCullv1[i] = i; + } + + if (m_vCull2[i] > 0) { + m_nCullv2[i] = 3 + i; + } + else { + m_nCullv2[i] = i; + } + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +bool CCamWnd::CullBrush(brush_t *b, bool cubicOnly) { + int i; + idVec3 point; + float d; + + if ( b->forceVisibile ) { + return false; + } + + if (g_PrefsDlg.m_bCubicClipping) { + + float distance = g_PrefsDlg.m_nCubicScale * 64; + + idVec3 mid; + for (int i = 0; i < 3; i++) { + mid[i] = (b->mins[i] + ((b->maxs[i] - b->mins[i]) / 2)); + } + + point = mid - m_Camera.origin; + if (point.Length() > distance) { + return true; + } + + } + + if (cubicOnly) { + return false; + } + + for (i = 0; i < 3; i++) { + point[i] = b->mins[m_nCullv1[i]] - m_Camera.origin[i]; + } + + d = DotProduct(point, m_vCull1); + if (d < -1) { + return true; + } + + for (i = 0; i < 3; i++) { + point[i] = b->mins[m_nCullv2[i]] - m_Camera.origin[i]; + } + + d = DotProduct(point, m_vCull2); + if (d < -1) { + return true; + } + + return false; +} + +#if 0 + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CCamWnd::DrawLightRadius(brush_t *pBrush) { + // if lighting + int nRadius = Brush_LightRadius(pBrush); + if (nRadius > 0) { + Brush_SetLightColor(pBrush); + qglEnable(GL_BLEND); + qglPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + qglDisable(GL_BLEND); + qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + } +} +#endif + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void setGLMode(int mode) { + switch (mode) + { + case cd_wire: + qglPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + globalImages->BindNull(); + qglDisable(GL_BLEND); + qglDisable(GL_DEPTH_TEST); + qglColor3f( 1.0f, 1.0f, 1.0f ); + break; + + case cd_solid: + qglCullFace(GL_FRONT); + qglEnable(GL_CULL_FACE); + qglShadeModel(GL_FLAT); + qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + globalImages->BindNull(); + qglDisable(GL_BLEND); + qglEnable(GL_DEPTH_TEST); + qglDepthFunc(GL_LEQUAL); + break; + + case cd_texture: + qglCullFace(GL_FRONT); + qglEnable(GL_CULL_FACE); + qglShadeModel(GL_FLAT); + qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + qglDisable(GL_BLEND); + qglEnable(GL_DEPTH_TEST); + qglDepthFunc(GL_LEQUAL); + break; + + case cd_blend: + qglCullFace(GL_FRONT); + qglEnable(GL_CULL_FACE); + qglShadeModel(GL_FLAT); + qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + qglDisable(GL_DEPTH_TEST); + qglEnable(GL_BLEND); + qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + break; + } +} + + +extern void glLabeledPoint(idVec4 &color, idVec3 &point, float size, const char *label); +void DrawAxial(face_t *selFace) { + if (g_bAxialMode) { + idVec3 points[4]; + + for (int j = 0; j < selFace->face_winding->GetNumPoints(); j++) { + glLabeledPoint(idVec4(1, 1, 1, 1), (*selFace->face_winding)[j].ToVec3(), 3, va("%i", j)); + } + + ValidateAxialPoints(); + points[0] = (*selFace->face_winding)[g_axialAnchor].ToVec3(); + VectorMA (points[0], 1, selFace->plane, points[0]); + VectorMA (points[0], 4, selFace->plane, points[1]); + points[3] = (*selFace->face_winding)[g_axialDest].ToVec3(); + VectorMA (points[3], 1, selFace->plane, points[3]); + VectorMA (points[3], 4, selFace->plane, points[2]); + glLabeledPoint(idVec4(1, 0, 0, 1), points[1], 3, "Anchor"); + glLabeledPoint(idVec4(1, 1, 0, 1), points[2], 3, "Dest"); + qglBegin (GL_LINE_STRIP); + qglVertex3fv( points[0].ToFloatPtr() ); + qglVertex3fv( points[1].ToFloatPtr() ); + qglVertex3fv( points[2].ToFloatPtr() ); + qglVertex3fv( points[3].ToFloatPtr() ); + qglEnd(); + } +} + + +/* + ======================================================================================================================= + Cam_Draw + ======================================================================================================================= + */ +void CCamWnd::SetProjectionMatrix() { + float xfov = 90; + float yfov = 2 * atan((float)m_Camera.height / m_Camera.width) * idMath::M_RAD2DEG; +#if 0 + float screenaspect = (float)m_Camera.width / m_Camera.height; + qglLoadIdentity(); + gluPerspective(yfov, screenaspect, 2, 8192); +#else + float xmin, xmax, ymin, ymax; + float width, height; + float zNear; + float projectionMatrix[16]; + + // + // set up projection matrix + // + zNear = cvarSystem->GetCVarFloat( "r_znear" ); + + ymax = zNear * tan( yfov * idMath::PI / 360.0f ); + ymin = -ymax; + + xmax = zNear * tan( xfov * idMath::PI / 360.0f ); + xmin = -xmax; + + width = xmax - xmin; + height = ymax - ymin; + + projectionMatrix[0] = 2 * zNear / width; + projectionMatrix[4] = 0; + projectionMatrix[8] = ( xmax + xmin ) / width; // normally 0 + projectionMatrix[12] = 0; + + projectionMatrix[1] = 0; + projectionMatrix[5] = 2 * zNear / height; + projectionMatrix[9] = ( ymax + ymin ) / height; // normally 0 + projectionMatrix[13] = 0; + + // this is the far-plane-at-infinity formulation + projectionMatrix[2] = 0; + projectionMatrix[6] = 0; + projectionMatrix[10] = -1; + projectionMatrix[14] = -2 * zNear; + + projectionMatrix[3] = 0; + projectionMatrix[7] = 0; + projectionMatrix[11] = -1; + projectionMatrix[15] = 0; + + qglLoadMatrixf( projectionMatrix ); +#endif +} + +void CCamWnd::Cam_Draw() { + brush_t *brush; + face_t *face; + + // float yfov; + int i; + + if (!active_brushes.next) { + return; // not valid yet + } + + // set the sound origin for both simple draw and rendered mode + // the editor uses opposite pitch convention + idMat3 axis = idAngles( -m_Camera.angles.pitch, m_Camera.angles.yaw, m_Camera.angles.roll ).ToMat3(); + soundSystem->PlaceListener( m_Camera.origin, axis, 0, Sys_Milliseconds(), "Undefined" ); + + if (renderMode) { + Cam_Render(); + } + + qglViewport(0, 0, m_Camera.width, m_Camera.height); + qglScissor(0, 0, m_Camera.width, m_Camera.height); + qglClearColor(g_qeglobals.d_savedinfo.colors[COLOR_CAMERABACK][0], g_qeglobals.d_savedinfo.colors[COLOR_CAMERABACK][1], g_qeglobals.d_savedinfo.colors[COLOR_CAMERABACK][2], 0); + + if (!renderMode) { + qglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } + + qglDisable(GL_LIGHTING); + qglMatrixMode(GL_PROJECTION); + + SetProjectionMatrix(); + + qglRotatef(-90, 1, 0, 0); // put Z going up + qglRotatef(90, 0, 0, 1); // put Z going up + qglRotatef(m_Camera.angles[0], 0, 1, 0); + qglRotatef(-m_Camera.angles[1], 0, 0, 1); + qglTranslatef(-m_Camera.origin[0], -m_Camera.origin[1], -m_Camera.origin[2]); + + Cam_BuildMatrix(); + + for (brush = active_brushes.next; brush != &active_brushes; brush = brush->next) { + + if ( CullBrush(brush, false) ) { + continue; + } + + if ( FilterBrush(brush) ) { + continue; + } + + if (renderMode) { + if (!(entityMode && brush->owner->eclass->fixedsize)) { + continue; + } + } + + setGLMode(m_Camera.draw_mode); + Brush_Draw(brush); + } + + + //qglDepthMask ( 1 ); // Ok, write now + qglMatrixMode(GL_PROJECTION); + + qglTranslatef(g_qeglobals.d_select_translate[0],g_qeglobals.d_select_translate[1],g_qeglobals.d_select_translate[2]); + + brush_t *pList = (g_bClipMode && g_pSplitList) ? g_pSplitList : &selected_brushes; + + if (!renderMode) { + // draw normally + for (brush = pList->next; brush != pList; brush = brush->next) { + if (brush->pPatch) { + continue; + } + setGLMode(m_Camera.draw_mode); + Brush_Draw(brush, true); + } + } + + // blend on top + + setGLMode(m_Camera.draw_mode); + qglDisable(GL_LIGHTING); + qglColor4f( g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][0],g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][1],g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][2], 0.25f ); + qglEnable(GL_BLEND); + qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + globalImages->BindNull(); + for (brush = pList->next; brush != pList; brush = brush->next) { + if (brush->pPatch || brush->modelHandle > 0) { + Brush_Draw(brush, true); + + // DHM - Nerve:: patch display lists/models mess with the state + qglEnable(GL_BLEND); + qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + qglColor4f( g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][0],g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][1],g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][2], 0.25f ); + globalImages->BindNull(); + continue; + } + + if ( brush->owner->eclass->entityModel ) { + continue; + } + + for (face = brush->brush_faces; face; face = face->next) { + Face_Draw(face); + } + } + + int nCount = g_ptrSelectedFaces.GetSize(); + + if (!renderMode) { + for (int i = 0; i < nCount; i++) { + face_t *selFace = reinterpret_cast < face_t * > (g_ptrSelectedFaces.GetAt(i)); + Face_Draw(selFace); + DrawAxial(selFace); + } + } + + // non-zbuffered outline + qglDisable(GL_BLEND); + qglDisable(GL_DEPTH_TEST); + qglPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + + if (renderMode) { + qglColor3f(1, 0, 0); + for (int i = 0; i < nCount; i++) { + face_t *selFace = reinterpret_cast < face_t * > (g_ptrSelectedFaces.GetAt(i)); + Face_Draw(selFace); + } + } + + qglColor3f(1, 1, 1); + for (brush = pList->next; brush != pList; brush = brush->next) { + if (brush->pPatch || brush->modelHandle > 0) { + continue; + } + + for (face = brush->brush_faces; face; face = face->next) { + Face_Draw(face); + } + } + // edge / vertex flags + if (g_qeglobals.d_select_mode == sel_vertex) { + qglPointSize(4); + qglColor3f(0, 1, 0); + qglBegin(GL_POINTS); + for (i = 0; i < g_qeglobals.d_numpoints; i++) { + qglVertex3fv( g_qeglobals.d_points[i].ToFloatPtr() ); + } + + qglEnd(); + qglPointSize(1); + } + else if (g_qeglobals.d_select_mode == sel_edge) { + float *v1, *v2; + + qglPointSize(4); + qglColor3f(0, 0, 1); + qglBegin(GL_POINTS); + for (i = 0; i < g_qeglobals.d_numedges; i++) { + v1 = g_qeglobals.d_points[g_qeglobals.d_edges[i].p1].ToFloatPtr(); + v2 = g_qeglobals.d_points[g_qeglobals.d_edges[i].p2].ToFloatPtr(); + qglVertex3f( (v1[0] + v2[0]) * 0.5f, (v1[1] + v2[1]) * 0.5f, (v1[2] + v2[2]) * 0.5f ); + } + + qglEnd(); + qglPointSize(1); + } + + g_splineList->draw (static_cast(g_qeglobals.d_select_mode == sel_addpoint || g_qeglobals.d_select_mode == sel_editpoint)); + + if ( g_qeglobals.selectObject && (g_qeglobals.d_select_mode == sel_addpoint || g_qeglobals.d_select_mode == sel_editpoint) ) { + g_qeglobals.selectObject->drawSelection(); + } + + // draw pointfile + qglEnable(GL_DEPTH_TEST); + + DrawPathLines(); + + if (g_qeglobals.d_pointfile_display_list) { + Pointfile_Draw(); + } + + // + // bind back to the default texture so that we don't have problems elsewhere + // using/modifying texture maps between contexts + // + globalImages->BindNull(); + + qglFinish(); + QE_CheckOpenGLForErrors(); + + if (!renderMode) { + // clean up any deffered tri's + renderSystem->ToggleSmpFrame(); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CCamWnd::OnSize(UINT nType, int cx, int cy) { + CWnd::OnSize(nType, cx, cy); + + CRect rect; + GetClientRect(rect); + m_Camera.width = rect.right; + m_Camera.height = rect.bottom; + InvalidateRect(NULL, false); +} + + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CCamWnd::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) { + g_pParentWnd->HandleKey(nChar, nRepCnt, nFlags, false); +} + +// +// ======================================================================================================================= +// Timo brush primitive texture shifting, using camera view to select translations:: +// ======================================================================================================================= +// +void CCamWnd::ShiftTexture_BrushPrimit(face_t *f, int x, int y) { +/* + idVec3 texS, texT; + idVec3 viewX, viewY; + int XS, XT, YS, YT; + int outS, outT; +#ifdef _DEBUG + if (!g_qeglobals.m_bBrushPrimitMode) { + common->Printf("Warning : unexpected call to CCamWnd::ShiftTexture_BrushPrimit with brush primitive mode disbaled\n"); + return; + } +#endif + // compute face axis base + //ComputeAxisBase(f->plane.Normal(), texS, texT); + + // compute camera view vectors + VectorCopy(m_Camera.vup, viewY); + VectorCopy(m_Camera.vright, viewX); + + // compute best vectors + //ComputeBest2DVector(viewX, texS, texT, XS, XT); + //ComputeBest2DVector(viewY, texS, texT, YS, YT); + + // check this is not a degenerate case + if ((XS == YS) && (XT == YT)) + { +#ifdef _DEBUG + common->Printf("Warning : degenerate best vectors axis base in CCamWnd::ShiftTexture_BrushPrimit\n"); +#endif + // forget it + Select_ShiftTexture_BrushPrimit(f, x, y, false); + return; + } + + // compute best fitted translation in face axis base + outS = XS * x + YS * y; + outT = XT * x + YT * y; + + // call actual texture shifting code + Select_ShiftTexture_BrushPrimit(f, outS, outT, false); +*/ +} + + +bool IsBModel(brush_t *b) { + const char *v = ValueForKey( b->owner, "model" ); + if (v && *v) { + const char *n = ValueForKey( b->owner, "name"); + return (stricmp( n, v ) == 0); + } + return false; +} + +/* +================ +BuildEntityRenderState + +Creates or updates modelDef and lightDef for an entity +================ +*/ +int Brush_ToTris(brush_t *brush, idTriList *tris, idMatList *mats, bool models, bool bmodel); + +void CCamWnd::BuildEntityRenderState( entity_t *ent, bool update) { + const char *v; + idDict spawnArgs; + const char *name = NULL; + + Entity_UpdateSoundEmitter( ent ); + + // delete the existing def if we aren't creating a brand new world + if ( !update ) { + if ( ent->lightDef >= 0 ) { + g_qeglobals.rw->FreeLightDef( ent->lightDef ); + ent->lightDef = -1; + } + + if ( ent->modelDef >= 0 ) { + g_qeglobals.rw->FreeEntityDef( ent->modelDef ); + ent->modelDef = -1; + } + } + + // if an entity doesn't have any brushes at all, don't do anything + if ( ent->brushes.onext == &ent->brushes ) { + return; + } + + // if the brush isn't displayed (filtered or culled), don't do anything + if (FilterBrush(ent->brushes.onext)) { + return; + } + + spawnArgs = ent->epairs; + if (ent->eclass->defArgs.FindKey("model")) { + spawnArgs.Set("model", ent->eclass->defArgs.GetString("model")); + } + + // any entity can have a model + name = ValueForKey( ent, "name" ); + v = spawnArgs.GetString("model"); + if ( v && *v ) { + renderEntity_t refent; + + refent.referenceSoundHandle = ent->soundEmitter ? ent->soundEmitter->Handle() : -1; + + if ( !stricmp( name, v ) ) { + // build the model from brushes + idTriList tris(1024); + idMatList mats(1024); + + for (brush_t *b = ent->brushes.onext; b != &ent->brushes; b = b->onext) { + Brush_ToTris( b, &tris, &mats, false, true); + } + + if ( ent->modelDef >= 0 ) { + g_qeglobals.rw->FreeEntityDef( ent->modelDef ); + ent->modelDef = -1; + } + + idRenderModel *bmodel = renderModelManager->FindModel( name ); + + if ( bmodel ) { + renderModelManager->RemoveModel( bmodel ); + renderModelManager->FreeModel( bmodel ); + } + + bmodel = renderModelManager->AllocModel(); + + bmodel->InitEmpty( name ); + + // add the surfaces to the renderModel + modelSurface_t surf; + for ( int i = 0 ; i < tris.Num() ; i++ ) { + surf.geometry = tris[i]; + surf.shader = mats[i]; + bmodel->AddSurface( surf ); + } + + bmodel->FinishSurfaces(); + + renderModelManager->AddModel( bmodel ); + + // FIXME: brush entities + gameEdit->ParseSpawnArgsToRenderEntity( &spawnArgs, &refent ); + + ent->modelDef = g_qeglobals.rw->AddEntityDef( &refent ); + + } else { + // use the game's epair parsing code so + // we can use the same renderEntity generation + gameEdit->ParseSpawnArgsToRenderEntity( &spawnArgs, &refent ); + idRenderModelMD5 *md5 = dynamic_cast( refent.hModel ); + if (md5) { + idStr str; + spawnArgs.GetString("anim", "idle", str); + refent.numJoints = md5->NumJoints(); + if ( update && refent.joints ) { + Mem_Free16( refent.joints ); + } + refent.joints = ( idJointMat * )Mem_Alloc16( refent.numJoints * sizeof( *refent.joints ) ); + const idMD5Anim *anim = gameEdit->ANIM_GetAnimFromEntityDef(spawnArgs.GetString("classname"), str); + int frame = spawnArgs.GetInt("frame") + 1; + if ( frame < 1 ) { + frame = 1; + } + const idVec3 &offset = gameEdit->ANIM_GetModelOffsetFromEntityDef( spawnArgs.GetString("classname") ); + gameEdit->ANIM_CreateAnimFrame( md5, anim, refent.numJoints, refent.joints, ( frame * 1000 ) / 24, offset, false ); + } + if (ent->modelDef >= 0) { + g_qeglobals.rw->FreeEntityDef( ent->modelDef ); + } + ent->modelDef = g_qeglobals.rw->AddEntityDef( &refent ); + } + } + + // check for lightdefs + if (!(ent->eclass->nShowFlags & ECLASS_LIGHT)) { + return; + } + + if ( spawnArgs.GetBool( "start_off" ) ) { + return; + } + // use the game's epair parsing code so + // we can use the same renderLight generation + + renderLight_t lightParms; + + gameEdit->ParseSpawnArgsToRenderLight( &spawnArgs, &lightParms ); + lightParms.referenceSoundHandle = ent->soundEmitter ? ent->soundEmitter->Handle() : -1; + + if (update && ent->lightDef >= 0) { + g_qeglobals.rw->UpdateLightDef( ent->lightDef, &lightParms ); + } else { + if (ent->lightDef >= 0) { + g_qeglobals.rw->FreeLightDef(ent->lightDef); + } + ent->lightDef = g_qeglobals.rw->AddLightDef( &lightParms ); + } + +} + +void Tris_ToOBJ(const char *outFile, idTriList *tris, idMatList *mats) { + idFile *f = fileSystem->OpenExplicitFileWrite( outFile ); + if ( f ) { + char out[1024]; + strcpy(out, outFile); + StripExtension(out); + + idList matNames; + int i, j, k; + int indexBase = 1; + idStr lastMaterial(""); + int matCount = 0; + //idStr basePath = cvarSystem->GetCVarString( "fs_savepath" ); + f->Printf( "mtllib %s.mtl\n", out ); + for (i = 0; i < tris->Num(); i++) { + srfTriangles_t *tri = (*tris)[i]; + for (j = 0; j < tri->numVerts; j++) { + f->Printf( "v %f %f %f\n", tri->verts[j].xyz.x, tri->verts[j].xyz.z, -tri->verts[j].xyz.y ); + } + for (j = 0; j < tri->numVerts; j++) { + f->Printf( "vt %f %f\n", tri->verts[j].st.x, 1.0f - tri->verts[j].st.y ); + } + for (j = 0; j < tri->numVerts; j++) { + f->Printf( "vn %f %f %f\n", tri->verts[j].normal.x, tri->verts[j].normal.y, tri->verts[j].normal.z ); + } + + if (stricmp( (*mats)[i]->GetName(), lastMaterial)) { + lastMaterial = (*mats)[i]->GetName(); + + bool found = false; + for (k = 0; k < matNames.Num(); k++) { + if ( idStr::Icmp(matNames[k]->c_str(), lastMaterial.c_str()) == 0 ) { + found = true; + // f->Printf( "usemtl m%i\n", k ); + f->Printf( "usemtl %s\n", lastMaterial.c_str() ); + break; + } + } + + if (!found) { + // f->Printf( "usemtl m%i\n", matCount++ ); + f->Printf( "usemtl %s\n", lastMaterial.c_str() ); + matNames.Append(new idStr(lastMaterial)); + } + } + + for (int j = 0; j < tri->numIndexes; j += 3) { + int i1, i2, i3; + i1 = tri->indexes[j+2] + indexBase; + i2 = tri->indexes[j+1] + indexBase; + i3 = tri->indexes[j] + indexBase; + f->Printf( "f %i/%i/%i %i/%i/%i %i/%i/%i\n", i1,i1,i1, i2,i2,i2, i3,i3,i3 ); + } + + indexBase += tri->numVerts; + + } + fileSystem->CloseFile( f ); + + strcat(out, ".mtl"); + f = fileSystem->OpenExplicitFileWrite( out ); + if (f) { + for (k = 0; k < matNames.Num(); k++) { + // This presumes the diffuse tga name matches the material name + f->Printf( "newmtl %s\n\tNs 0\n\td 1\n\tillum 2\n\tKd 0 0 0 \n\tKs 0.22 0.22 0.22 \n\tKa 0 0 0 \n\tmap_Kd %s/base/%s.tga\n\n\n", matNames[k]->c_str(), "z:/d3xp", matNames[k]->c_str() ); + } + fileSystem->CloseFile( f ); + } + + } +} + +int Brush_TransformModel(brush_t *brush, idTriList *tris, idMatList *mats) { + int ret = 0; + if (brush->modelHandle > 0 ) { + idRenderModel *model = brush->modelHandle; + if (model) { + float a = FloatForKey(brush->owner, "angle"); + float s, c; + //FIXME: support full rotation matrix + bool matrix = false; + if (a) { + s = sin( DEG2RAD(a) ); + c = cos( DEG2RAD(a) ); + } + idMat3 mat; + if (GetMatrixForKey(brush->owner, "rotation", mat)) { + matrix = true; + } + + + for (int i = 0; i < model->NumSurfaces() ; i++) { + const modelSurface_t *surf = model->Surface( i ); + srfTriangles_t *tri = surf->geometry; + srfTriangles_t *tri2 = renderModelManager->CopyStaticTriSurf(tri); + for (int j = 0; j < tri2->numVerts; j++) { + idVec3 v; + if (matrix) { + v = tri2->verts[j].xyz * brush->owner->rotation + brush->owner->origin; + } else { + v = tri2->verts[j].xyz; + VectorAdd(v, brush->owner->origin, v); + float x = v[0]; + float y = v[1]; + if (a) { + float x2 = (((x - brush->owner->origin[0]) * c) - ((y - brush->owner->origin[1]) * s)) + brush->owner->origin[0]; + float y2 = (((x - brush->owner->origin[0]) * s) + ((y - brush->owner->origin[1]) * c)) + brush->owner->origin[1]; + x = x2; + y = y2; + } + v[0] = x; + v[1] = y; + } + tri2->verts[j].xyz = v; + } + tris->Append(tri2); + mats->Append( surf->shader ); + } + return model->NumSurfaces(); + } + } + return ret; +} + + +#define MAX_TRI_SURFACES 16384 +int Brush_ToTris(brush_t *brush, idTriList *tris, idMatList *mats, bool models, bool bmodel) { + int i, j; + srfTriangles_t *tri; + // + // patches + // + if (brush->modelHandle > 0 ) { + if (!models) { + return 0; + } else { + return Brush_TransformModel(brush, tris, mats); + } + } + + int numSurfaces = 0; + + if ( brush->owner->eclass->fixedsize && !brush->entityModel) { + return NULL; + } + + if ( brush->pPatch ) { + patchMesh_t *pm; + int width, height; + + pm = brush->pPatch; + + // build a patch mesh + idSurface_Patch *cp = new idSurface_Patch( pm->width * 6, pm->height * 6 ); + cp->SetSize( pm->width, pm->height ); + for ( i = 0; i < pm->width; i++ ) { + for ( j = 0; j < pm->height; j++ ) { + (*cp)[j*cp->GetWidth()+i].xyz = pm->ctrl(i, j).xyz; + (*cp)[j*cp->GetWidth()+i].st = pm->ctrl(i, j).st; + } + } + + // subdivide it + if ( pm->explicitSubdivisions ) { + cp->SubdivideExplicit( pm->horzSubdivisions, pm->vertSubdivisions, true ); + } else { + cp->Subdivide( DEFAULT_CURVE_MAX_ERROR, DEFAULT_CURVE_MAX_ERROR, DEFAULT_CURVE_MAX_LENGTH, true ); + } + width = cp->GetWidth(); + height = cp->GetHeight(); + + // convert to srfTriangles + tri = renderModelManager->AllocStaticTriSurf( width * height, 6 * ( width - 1 ) * ( height - 1 ) ); + tri->numVerts = width * height; + tri->numIndexes = 6 * ( width - 1 ) * ( height - 1 ); + for ( i = 0 ; i < tri->numVerts ; i++ ) { + tri->verts[i] = (*cp)[i]; + if (bmodel) { + tri->verts[i].xyz -= brush->owner->origin; + } + } + + tri->numIndexes = 0; + for ( i = 1 ; i < width ; i++ ) { + for ( j = 1 ; j < height ; j++ ) { + tri->indexes[tri->numIndexes++] = ( j - 1 ) * width + i; + tri->indexes[tri->numIndexes++] = ( j - 1 ) * width + i - 1; + tri->indexes[tri->numIndexes++] = j * width + i - 1; + + tri->indexes[tri->numIndexes++] = j * width + i; + tri->indexes[tri->numIndexes++] = ( j - 1 ) * width + i; + tri->indexes[tri->numIndexes++] = j * width + i - 1; + } + } + + delete cp; + + tris->Append(tri); + mats->Append(pm->d_texture); + //surfaces[numSurfaces] = tri; + //materials[numSurfaces] = pm->d_texture; + return 1; + } + + // + // normal brush + // + for ( face_t *face = brush->brush_faces ; face; face = face->next ) { + idWinding *w; + + w = face->face_winding; + if (!w) { + continue; // freed or degenerate face + } + + tri = renderModelManager->AllocStaticTriSurf( w->GetNumPoints(), ( w->GetNumPoints() - 2 ) * 3 ); + tri->numVerts = w->GetNumPoints(); + tri->numIndexes = ( w->GetNumPoints() - 2 ) * 3; + + for ( i = 0 ; i < tri->numVerts ; i++ ) { + + tri->verts[i].Clear(); + + tri->verts[i].xyz[0] = (*w)[i][0]; + tri->verts[i].xyz[1] = (*w)[i][1]; + tri->verts[i].xyz[2] = (*w)[i][2]; + + if ( bmodel ) { + tri->verts[i].xyz -= brush->owner->origin; + } + + tri->verts[i].st[0] = (*w)[i][3]; + tri->verts[i].st[1] = (*w)[i][4]; + + tri->verts[i].normal = face->plane.Normal(); + } + + tri->numIndexes = 0; + for ( i = 2 ; i < w->GetNumPoints() ; i++ ) { + tri->indexes[tri->numIndexes++] = 0; + tri->indexes[tri->numIndexes++] = i-1; + tri->indexes[tri->numIndexes++] = i; + } + + tris->Append(tri); + mats->Append(face->d_texture); + numSurfaces++; + } + + return numSurfaces; +} + +void Select_ToOBJ() { + int i; + CFileDialog dlgFile(FALSE, "obj", NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, "Wavefront object files (*.obj)|*.obj||", g_pParentWnd); + if (dlgFile.DoModal() == IDOK) { + idTriList tris(1024); + idMatList mats(1024); + + for (brush_t *b = selected_brushes.next; b != &selected_brushes; b = b->next) { + + if ( b->hiddenBrush ) { + continue; + } + + if (FilterBrush(b)) { + continue; + } + + Brush_ToTris(b, &tris, &mats, true, false); + } + + Tris_ToOBJ(dlgFile.GetPathName().GetBuffer(0), &tris, &mats); + + for( i = 0; i < tris.Num(); i++ ) { + renderModelManager->FreeStaticTriSurf( tris[i] ); + } + tris.Clear(); + } +} + +void Select_ToCM() { + CFileDialog dlgFile( FALSE, "lwo, ase", NULL, 0, "(*.lwo)|*.lwo|(*.ase)|*.ase|(*.ma)|*.ma||", g_pParentWnd ); + + if ( dlgFile.DoModal() == IDOK ) { + idMapEntity *mapEnt; + idMapPrimitive *p; + idStr name; + + name = fileSystem->OSPathToRelativePath( dlgFile.GetPathName() ); + name.BackSlashesToSlashes(); + + mapEnt = new idMapEntity(); + mapEnt->epairs.Set( "name", name.c_str() ); + + for ( brush_t *b = selected_brushes.next; b != &selected_brushes; b = b->next ) { + + if ( b->hiddenBrush ) { + continue; + } + + if ( FilterBrush( b ) ) { + continue; + } + + p = BrushToMapPrimitive( b, b->owner->origin ); + if ( p ) { + mapEnt->AddPrimitive( p ); + } + } + + collisionModelManager->WriteCollisionModelForMapEntity( mapEnt, name.c_str() ); + + delete mapEnt; + } +} + + +/* +================= +BuildRendererState + +Builds models, lightdefs, and modeldefs for the current editor data +so it can be rendered by the game renderSystem +================= +*/ +void CCamWnd::BuildRendererState() { + renderEntity_t worldEntity; + entity_t *ent; + brush_t *brush; + + FreeRendererState(); + + // the renderWorld holds all the references and defs + g_qeglobals.rw->InitFromMap( NULL ); + + // create the raw model for all the brushes + int numBrushes = 0; + int numSurfaces = 0; + + // the renderModel for the world holds all the geometry that isn't in an entity + worldModel = renderModelManager->AllocModel(); + worldModel->InitEmpty( "EditorWorldModel" ); + + for ( brush_t *brushList = &active_brushes ; brushList ; + brushList = (brushList == &active_brushes) ? &selected_brushes : NULL ) { + + for (brush = brushList->next; brush != brushList; brush = brush->next) { + + if ( brush->hiddenBrush ) { + continue; + } + + if (FilterBrush(brush)) { + continue; + } + + if (CullBrush(brush, true)) { + continue; + } + + idTriList tris(1024); + idMatList mats(1024); + + if (!IsBModel(brush)) { + numSurfaces += Brush_ToTris( brush, &tris, &mats, false, false ); + } + + // add the surfaces to the renderModel + modelSurface_t surf; + for ( int i = 0 ; i < tris.Num() ; i++ ) { + surf.geometry = tris[i]; + surf.shader = mats[i]; + worldModel->AddSurface( surf ); + } + + numBrushes++; + } + } + + // bound and clean the triangles + worldModel->FinishSurfaces(); + + // the worldEntity just has the handle for the worldModel + memset( &worldEntity, 0, sizeof( worldEntity ) ); + worldEntity.hModel = worldModel; + worldEntity.axis = mat3_default; + worldEntity.shaderParms[0] = 1; + worldEntity.shaderParms[1] = 1; + worldEntity.shaderParms[2] = 1; + worldEntity.shaderParms[3] = 1; + + worldModelDef = g_qeglobals.rw->AddEntityDef( &worldEntity ); + + // create the light and model entities exactly the way the game code would + for ( ent = entities.next ; ent != &entities ; ent = ent->next ) { + if ( ent->brushes.onext == &ent->brushes ) { + continue; + } + + if (CullBrush(ent->brushes.onext, true)) { + continue; + } + + if (Map_IsBrushFiltered(ent->brushes.onext)) { + continue; + } + + BuildEntityRenderState( ent, false ); + } + + //common->Printf("Render data used %d brushes\n", numBrushes); + worldDirty = false; + UpdateCaption(); +} + +/* +=============================== +CCamWnd::UpdateRenderEntities + + Creates a new entity state list + returns true if a repaint is needed +=============================== +*/ +bool CCamWnd::UpdateRenderEntities() { + + if (rebuildMode) { + return false; + } + + bool ret = false; + for ( entity_t *ent = entities.next ; ent != &entities ; ent = ent->next ) { + BuildEntityRenderState( ent, (ent->lightDef != -1 || ent->modelDef != -1 || ent->soundEmitter ) ? true : false ); + if (ret == false && ent->modelDef || ent->lightDef) { + ret = true; + } + } + return ret; +} + +/* +============================ +CCamWnd::FreeRendererState + + Frees the render state data +============================ +*/ +void CCamWnd::FreeRendererState() { + + for ( entity_t *ent = entities.next ; ent != &entities ; ent = ent->next ) { + if (ent->lightDef >= 0) { + g_qeglobals.rw->FreeLightDef( ent->lightDef ); + ent->lightDef = -1; + } + + if (ent->modelDef >= 0) { + renderEntity_t *refent = const_cast(g_qeglobals.rw->GetRenderEntity( ent->modelDef )); + if ( refent ) { + if ( refent->callbackData ) { + Mem_Free( refent->callbackData ); + refent->callbackData = NULL; + } + if ( refent->joints ) { + Mem_Free16(refent->joints); + refent->joints = NULL; + } + } + g_qeglobals.rw->FreeEntityDef( ent->modelDef ); + ent->modelDef = -1; + } + } + + if ( worldModel ) { + renderModelManager->FreeModel( worldModel ); + worldModel = NULL; + } + +} + + +/* +======================== +CCamWnd::UpdateCaption + + updates the caption based on rendermode and whether the render mode needs updated +======================== +*/ +void CCamWnd::UpdateCaption() { + + idStr strCaption; + + if (worldDirty) { + strCaption = "*"; + } + // FIXME: + strCaption += (renderMode) ? "RENDER" : "CAM"; + if (renderMode) { + strCaption += (rebuildMode) ? " (Realtime)" : ""; + strCaption += (entityMode) ? " +lights" : ""; + strCaption += (selectMode) ? " +selected" : ""; + strCaption += (animationMode) ? " +anim" : ""; + } + strCaption += (soundMode) ? " +snd" : ""; + SetWindowText(strCaption); +} + +/* +=========================== +CCamWnd::ToggleRenderMode + + Toggles the render mode +=========================== +*/ +void CCamWnd::ToggleRenderMode() { + renderMode ^= 1; + UpdateCaption(); +} + +/* +=========================== +CCamWnd::ToggleRebuildMode + + Toggles the rebuild mode +=========================== +*/ +void CCamWnd::ToggleRebuildMode() { + rebuildMode ^= 1; + UpdateCaption(); +} + +/* +=========================== +CCamWnd::ToggleEntityMode + + Toggles the entity mode +=========================== +*/ +void CCamWnd::ToggleEntityMode() { + entityMode ^= 1; + UpdateCaption(); +} + + +/* +=========================== +CCamWnd::ToggleRenderMode + + Toggles the render mode +=========================== +*/ +void CCamWnd::ToggleAnimationMode() { + animationMode ^= 1; + if (animationMode) { + SetTimer(0, 10, NULL); + } else { + KillTimer(0); + } + UpdateCaption(); +} + +/* +=========================== +CCamWnd::ToggleSoundMode + + Toggles the sound mode +=========================== +*/ +void CCamWnd::ToggleSoundMode() { + soundMode ^= 1; + + UpdateCaption(); + + for ( entity_t *ent = entities.next ; ent != &entities ; ent = ent->next ) { + Entity_UpdateSoundEmitter( ent ); + } +} + +/* +=========================== +CCamWnd::ToggleRenderMode + + Toggles the render mode +=========================== +*/ +void CCamWnd::ToggleSelectMode() { + selectMode ^= 1; + UpdateCaption(); +} + +/* +========================= +CCamWnd::MarkWorldDirty + + marks the render world as dirty +========================= +*/ +void CCamWnd::MarkWorldDirty() { + worldDirty = true; + UpdateCaption(); +} + + +/* +========================= +CCamWnd::DrawEntityData + + Draws entity data ( experimental ) +========================= +*/ +extern void glBox(idVec4 &color, idVec3 &point, float size); + +void CCamWnd::DrawEntityData() { + + qglMatrixMode( GL_MODELVIEW ); + qglLoadIdentity(); + qglMatrixMode( GL_PROJECTION ); + qglLoadIdentity(); + + SetProjectionMatrix(); + + qglRotatef(-90, 1, 0, 0); // put Z going up + qglRotatef(90, 0, 0, 1); // put Z going up + qglRotatef(m_Camera.angles[0], 0, 1, 0); + qglRotatef(-m_Camera.angles[1], 0, 0, 1); + qglTranslatef(-m_Camera.origin[0], -m_Camera.origin[1], -m_Camera.origin[2]); + + Cam_BuildMatrix(); + + if (!(entityMode || selectMode)) { + return; + } + + qglDisable(GL_BLEND); + qglDisable(GL_DEPTH_TEST); + qglPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + globalImages->BindNull(); + idVec3 color(0, 1, 0); + qglColor3fv( color.ToFloatPtr() ); + + brush_t *brushList = &active_brushes; + int pass = 0; + while (brushList) { + for (brush_t *brush = brushList->next; brush != brushList; brush = brush->next) { + + if (CullBrush(brush, true)) { + continue; + } + + if (FilterBrush(brush)) { + continue; + } + + if ((pass == 1 && selectMode) || (entityMode && pass == 0 && brush->owner->lightDef >= 0)) { + Brush_DrawXY(brush, 0, true, true); + } + + } + brushList = (brushList == &active_brushes) ? &selected_brushes : NULL; + color.x = 1; + color.y = 0; + pass++; + qglColor3fv( color.ToFloatPtr() ); + } + +} + + +/* + ======================================================================================================================= + Cam_Render + + This used the renderSystem to draw a fully lit view of the world + ======================================================================================================================= + */ +void CCamWnd::Cam_Render() { + + renderView_t refdef; + CPaintDC dc(this); // device context for painting + + + if (!active_brushes.next) { + return; // not valid yet + } + + if (!qwglMakeCurrent(dc.m_hDC, win32.hGLRC)) { + common->Printf("ERROR: wglMakeCurrent failed..\n "); + common->Printf("Please restart " EDITOR_WINDOWTEXT " if the camera view is not working\n"); + return; + } + + // save the editor state + //qglPushAttrib( GL_ALL_ATTRIB_BITS ); + qglClearColor( 0.1f, 0.1f, 0.1f, 0.0f ); + qglScissor( 0, 0, m_Camera.width, m_Camera.height ); + qglClear( GL_COLOR_BUFFER_BIT ); + + // qwglSwapBuffers(dc.m_hDC); + + // create the model, using explicit normals + if ( rebuildMode && worldDirty ) { + BuildRendererState(); + } + + // render it + renderSystem->BeginFrame( m_Camera.width, m_Camera.height ); + + memset( &refdef, 0, sizeof( refdef ) ); + refdef.vieworg = m_Camera.origin; + + // the editor uses opposite pitch convention + refdef.viewaxis = idAngles( -m_Camera.angles.pitch, m_Camera.angles.yaw, m_Camera.angles.roll ).ToMat3(); + + refdef.width = SCREEN_WIDTH; + refdef.height = SCREEN_HEIGHT; + refdef.fov_x = 90; + refdef.fov_y = 2 * atan((float)m_Camera.height / m_Camera.width) * idMath::M_RAD2DEG; + + // only set in animation mode to give a consistent look + if (animationMode) { + refdef.time = Sys_Milliseconds(); + } + + g_qeglobals.rw->RenderScene( &refdef ); + + int frontEnd, backEnd; + + renderSystem->EndFrame( &frontEnd, &backEnd ); +//common->Printf( "front:%i back:%i\n", frontEnd, backEnd ); + + //qglPopAttrib(); + //DrawEntityData(); + + //qwglSwapBuffers(dc.m_hDC); + // get back to the editor state + qglMatrixMode( GL_MODELVIEW ); + qglLoadIdentity(); + Cam_BuildMatrix(); +} + + +void CCamWnd::OnTimer(UINT nIDEvent) +{ + if (animationMode || nIDEvent == 1) { + Sys_UpdateWindows(W_CAMERA); + } + if (nIDEvent == 1) { + KillTimer(1); + } + + if (!animationMode ) { + KillTimer(0); + } +} + + +void CCamWnd::UpdateCameraView() { + if (QE_SingleBrush(true, true)) { + brush_t *b = selected_brushes.next; + if (b->owner->eclass->nShowFlags & ECLASS_CAMERAVIEW) { + // find the entity that targets this + const char *name = ValueForKey(b->owner, "name"); + entity_t *ent = FindEntity("target", name); + if (ent) { + if (!saveValid) { + saveOrg = m_Camera.origin; + saveAng = m_Camera.angles; + saveValid = true; + } + idVec3 v = b->owner->origin - ent->origin; + v.Normalize(); + idAngles ang = v.ToMat3().ToAngles(); + ang.pitch = -ang.pitch; + ang.roll = 0.0f; + SetView( ent->origin, ang ); + Cam_BuildMatrix(); + Sys_UpdateWindows( W_CAMERA ); + return; + } + } + } + if (saveValid) { + SetView(saveOrg, saveAng); + Cam_BuildMatrix(); + Sys_UpdateWindows(W_CAMERA); + saveValid = false; + } +} diff --git a/src/tools/radiant/CamWnd.h b/src/tools/radiant/CamWnd.h new file mode 100644 index 0000000..3fa590f --- /dev/null +++ b/src/tools/radiant/CamWnd.h @@ -0,0 +1,205 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#if !defined(AFX_CAMWND_H__44B4BA03_781B_11D1_B53C_00AA00A410FC__INCLUDED_) +#define AFX_CAMWND_H__44B4BA03_781B_11D1_B53C_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + +typedef enum +{ + cd_wire, + cd_solid, + cd_texture, + cd_light, + cd_blend +} camera_draw_mode; + +typedef struct +{ + int width, height; + + idVec3 origin; + idAngles angles; + + camera_draw_mode draw_mode; + + idVec3 color; // background + + idVec3 forward, right, up; // move matrix + idVec3 vup, vpn, vright; // view matrix +} camera_t; + + +///////////////////////////////////////////////////////////////////////////// +// CCamWnd window +class CXYWnd; + +class CCamWnd : public CWnd +{ + DECLARE_DYNCREATE(CCamWnd); +// Construction +public: + CCamWnd(); + +// Attributes +public: + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CCamWnd) + protected: + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + //}}AFX_VIRTUAL + +// Implementation +public: + void ShiftTexture_BrushPrimit(face_t *f, int x, int y); + CXYWnd* m_pXYFriend; + void SetXYFriend(CXYWnd* pWnd); + virtual ~CCamWnd(); + camera_t& Camera(){return m_Camera;}; + void Cam_MouseControl(float dtime); + void Cam_ChangeFloor(bool up); + void BuildRendererState(); + void ToggleRenderMode(); + void ToggleRebuildMode(); + void ToggleEntityMode(); + void ToggleSelectMode(); + void ToggleAnimationMode(); + void ToggleSoundMode(); + void SetProjectionMatrix(); + void UpdateCameraView(); + + void BuildEntityRenderState( entity_t *ent, bool update ); + bool GetRenderMode() { + return renderMode; + } + bool GetRebuildMode() { + return rebuildMode; + } + bool GetEntityMode() { + return entityMode; + } + bool GetAnimationMode() { + return animationMode; + } + bool GetSelectMode() { + return selectMode; + } + bool GetSoundMode() { + return soundMode; + } + + + bool UpdateRenderEntities(); + void MarkWorldDirty(); + + void SetView( const idVec3 &origin, const idAngles &angles ) { + m_Camera.origin = origin; + m_Camera.angles = angles; + } + +protected: + void Cam_Init(); + void Cam_BuildMatrix(); + void Cam_PositionDrag(); + void Cam_MouseLook(); + void Cam_MouseDown(int x, int y, int buttons); + void Cam_MouseUp (int x, int y, int buttons); + void Cam_MouseMoved (int x, int y, int buttons); + void InitCull(); + bool CullBrush (brush_t *b, bool cubicOnly); + void Cam_Draw(); + void Cam_Render(); + + // game renderer interaction + qhandle_t worldModelDef; + idRenderModel *worldModel; // createRawModel of the brush and patch geometry + bool worldDirty; + bool renderMode; + bool rebuildMode; + bool entityMode; + bool selectMode; + bool animationMode; + bool soundMode; + void FreeRendererState(); + void UpdateCaption(); + bool BuildBrushRenderData(brush_t *brush); + void DrawEntityData(); + + + camera_t m_Camera; + int m_nCambuttonstate; + CPoint m_ptButton; + CPoint m_ptCursor; + CPoint m_ptLastCursor; + face_t* m_pSide_select; + idVec3 m_vCull1; + idVec3 m_vCull2; + int m_nCullv1[3]; + int m_nCullv2[3]; + bool m_bClipMode; + idVec3 saveOrg; + idAngles saveAng; + bool saveValid; + + // Generated message map functions +protected: + void OriginalMouseDown(UINT nFlags, CPoint point); + void OriginalMouseUp(UINT nFlags, CPoint point); + //{{AFX_MSG(CCamWnd) + afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnPaint(); + afx_msg void OnDestroy(); + afx_msg void OnClose(); + afx_msg void OnMouseMove(UINT nFlags, CPoint point); + afx_msg void OnLButtonDown(UINT nFlags, CPoint point); + afx_msg void OnLButtonUp(UINT nFlags, CPoint point); + afx_msg void OnMButtonDown(UINT nFlags, CPoint point); + afx_msg void OnMButtonUp(UINT nFlags, CPoint point); + afx_msg void OnRButtonDown(UINT nFlags, CPoint point); + afx_msg void OnRButtonUp(UINT nFlags, CPoint point); + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnTimer(UINT nIDEvent); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_CAMWND_H__44B4BA03_781B_11D1_B53C_00AA00A410FC__INCLUDED_) diff --git a/src/tools/radiant/CameraTargetDlg.cpp b/src/tools/radiant/CameraTargetDlg.cpp new file mode 100644 index 0000000..5ba33b3 --- /dev/null +++ b/src/tools/radiant/CameraTargetDlg.cpp @@ -0,0 +1,78 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "CameraTargetDlg.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CCameraTargetDlg dialog + + +CCameraTargetDlg::CCameraTargetDlg(CWnd* pParent /*=NULL*/) + : CDialog(CCameraTargetDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(CCameraTargetDlg) + m_nType = 0; + m_strName = _T(""); + //}}AFX_DATA_INIT +} + + +void CCameraTargetDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CCameraTargetDlg) + DDX_Radio(pDX, IDC_RADIO_FIXED, m_nType); + DDX_Text(pDX, IDC_EDIT_NAME, m_strName); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CCameraTargetDlg, CDialog) + //{{AFX_MSG_MAP(CCameraTargetDlg) + ON_COMMAND(ID_POPUP_NEWCAMERA_FIXED, OnPopupNewcameraFixed) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CCameraTargetDlg message handlers + +void CCameraTargetDlg::OnPopupNewcameraFixed() +{ + // TODO: Add your command handler code here + +} diff --git a/src/tools/radiant/CameraTargetDlg.h b/src/tools/radiant/CameraTargetDlg.h new file mode 100644 index 0000000..186fdc0 --- /dev/null +++ b/src/tools/radiant/CameraTargetDlg.h @@ -0,0 +1,74 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#if !defined(AFX_CAMERATARGETDLG_H__DE6597C1_1F63_4835_8949_5D2D5F208C6B__INCLUDED_) +#define AFX_CAMERATARGETDLG_H__DE6597C1_1F63_4835_8949_5D2D5F208C6B__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// CameraTargetDlg.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CCameraTargetDlg dialog + +class CCameraTargetDlg : public CDialog +{ +// Construction +public: + CCameraTargetDlg(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CCameraTargetDlg) + enum { IDD = IDD_DLG_CAMERATARGET }; + int m_nType; + CString m_strName; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CCameraTargetDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CCameraTargetDlg) + afx_msg void OnPopupNewcameraFixed(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_CAMERATARGETDLG_H__DE6597C1_1F63_4835_8949_5D2D5F208C6B__INCLUDED_) diff --git a/src/tools/radiant/CapDialog.cpp b/src/tools/radiant/CapDialog.cpp new file mode 100644 index 0000000..b98fc93 --- /dev/null +++ b/src/tools/radiant/CapDialog.cpp @@ -0,0 +1,71 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" +#include "CapDialog.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CCapDialog dialog + + +CCapDialog::CCapDialog(CWnd* pParent /*=NULL*/) + : CDialog(CCapDialog::IDD, pParent) +{ + //{{AFX_DATA_INIT(CCapDialog) + m_nCap = 0; + //}}AFX_DATA_INIT +} + + +void CCapDialog::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CCapDialog) + DDX_Radio(pDX, IDC_RADIO_CAP, m_nCap); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CCapDialog, CDialog) + //{{AFX_MSG_MAP(CCapDialog) + // NOTE: the ClassWizard will add message map macros here + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CCapDialog message handlers diff --git a/src/tools/radiant/CapDialog.h b/src/tools/radiant/CapDialog.h new file mode 100644 index 0000000..c3dff82 --- /dev/null +++ b/src/tools/radiant/CapDialog.h @@ -0,0 +1,75 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#if !defined(AFX_CAPDIALOG_H__10637162_2BD2_11D2_B030_00AA00A410FC__INCLUDED_) +#define AFX_CAPDIALOG_H__10637162_2BD2_11D2_B030_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// CapDialog.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CCapDialog dialog + +class CCapDialog : public CDialog +{ +// Construction +public: + static enum {BEVEL = 0, ENDCAP, IBEVEL, IENDCAP}; + CCapDialog(CWnd* pParent = NULL); // standard constructor + + int getCapType() {return m_nCap;}; +// Dialog Data + //{{AFX_DATA(CCapDialog) + enum { IDD = IDD_DIALOG_CAP }; + int m_nCap; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CCapDialog) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CCapDialog) + // NOTE: the ClassWizard will add member functions here + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_CAPDIALOG_H__10637162_2BD2_11D2_B030_00AA00A410FC__INCLUDED_) diff --git a/src/tools/radiant/CommandsDlg.cpp b/src/tools/radiant/CommandsDlg.cpp new file mode 100644 index 0000000..16ff4b2 --- /dev/null +++ b/src/tools/radiant/CommandsDlg.cpp @@ -0,0 +1,116 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" +#include "CommandsDlg.h" +#include "MainFrm.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CCommandsDlg dialog + + +CCommandsDlg::CCommandsDlg(CWnd* pParent /*=NULL*/) + : CDialog(CCommandsDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(CCommandsDlg) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT +} + + +void CCommandsDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CCommandsDlg) + DDX_Control(pDX, IDC_LIST_COMMANDS, m_lstCommands); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CCommandsDlg, CDialog) + //{{AFX_MSG_MAP(CCommandsDlg) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CCommandsDlg message handlers + +BOOL CCommandsDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + m_lstCommands.SetTabStops(120); + int nCount = g_nCommandCount; + + CFile fileout; + fileout.Open("c:/commandlist.txt", CFile::modeCreate | CFile::modeWrite); + for (int n = 0; n < nCount; n++) + { + CString strLine; + char c = g_Commands[n].m_nKey; + CString strKeys = CString( c ); + for (int k = 0; k < g_nKeyCount; k++) + { + if (g_Keys[k].m_nVKKey == g_Commands[n].m_nKey) + { + strKeys = g_Keys[k].m_strName; + break; + } + } + CString strMod(""); + if (g_Commands[n].m_nModifiers & RAD_SHIFT) + strMod = "Shift"; + if (g_Commands[n].m_nModifiers & RAD_ALT) + strMod += (strMod.GetLength() > 0) ? " + Alt" : "Alt"; + if (g_Commands[n].m_nModifiers & RAD_CONTROL) + strMod += (strMod.GetLength() > 0) ? " + Control" : "Control"; + if (strMod.GetLength() > 0) + { + strMod += " + "; + } + strLine.Format("%s \t%s%s", g_Commands[n].m_strCommand, strMod, strKeys); + m_lstCommands.AddString(strLine); + + strLine.Format("%s \t\t\t%s%s", g_Commands[n].m_strCommand, strMod, strKeys); + + fileout.Write(strLine, strLine.GetLength()); + fileout.Write("\r\n", 2); + } + fileout.Close(); + return TRUE; +} + diff --git a/src/tools/radiant/CommandsDlg.h b/src/tools/radiant/CommandsDlg.h new file mode 100644 index 0000000..14bef4a --- /dev/null +++ b/src/tools/radiant/CommandsDlg.h @@ -0,0 +1,73 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#if !defined(AFX_COMMANDSDLG_H__C80F6E42_8531_11D1_B548_00AA00A410FC__INCLUDED_) +#define AFX_COMMANDSDLG_H__C80F6E42_8531_11D1_B548_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// CommandsDlg.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CCommandsDlg dialog + +class CCommandsDlg : public CDialog +{ +// Construction +public: + CCommandsDlg(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CCommandsDlg) + enum { IDD = IDD_DLG_COMMANDLIST }; + CListBox m_lstCommands; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CCommandsDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CCommandsDlg) + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_COMMANDSDLG_H__C80F6E42_8531_11D1_B548_00AA00A410FC__INCLUDED_) diff --git a/src/tools/radiant/CommentsDlg.cpp b/src/tools/radiant/CommentsDlg.cpp new file mode 100644 index 0000000..11e18cc --- /dev/null +++ b/src/tools/radiant/CommentsDlg.cpp @@ -0,0 +1,65 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" +#include "CommentsDlg.h" + + +// CCommentsDlg dialog + +IMPLEMENT_DYNAMIC(CCommentsDlg, CDialog) +CCommentsDlg::CCommentsDlg(CWnd* pParent /*=NULL*/) + : CDialog(CCommentsDlg::IDD, pParent) + , strName(_T("")) + , strPath(_T("")) + , strComments(_T("")) +{ +} + +CCommentsDlg::~CCommentsDlg() +{ +} + +void CCommentsDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + DDX_Text(pDX, IDC_EDIT_NAME, strName); + DDX_Text(pDX, IDC_EDIT_PATH, strPath); + DDX_Text(pDX, IDC_EDIT_COMMENTS, strComments); +} + + +BEGIN_MESSAGE_MAP(CCommentsDlg, CDialog) +END_MESSAGE_MAP() + + +// CCommentsDlg message handlers diff --git a/src/tools/radiant/CommentsDlg.h b/src/tools/radiant/CommentsDlg.h new file mode 100644 index 0000000..8f60453 --- /dev/null +++ b/src/tools/radiant/CommentsDlg.h @@ -0,0 +1,53 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#pragma once +#include "afxwin.h" + + +// CCommentsDlg dialog + +class CCommentsDlg : public CDialog +{ + DECLARE_DYNAMIC(CCommentsDlg) + +public: + CCommentsDlg(CWnd* pParent = NULL); // standard constructor + virtual ~CCommentsDlg(); + +// Dialog Data + enum { IDD = IDD_DIALOG_COMMENTS }; + +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + + DECLARE_MESSAGE_MAP() +public: + CString strName; + CString strPath; + CString strComments; +}; diff --git a/src/tools/radiant/ConsoleDlg.cpp b/src/tools/radiant/ConsoleDlg.cpp new file mode 100644 index 0000000..4d1efda --- /dev/null +++ b/src/tools/radiant/ConsoleDlg.cpp @@ -0,0 +1,261 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" +#include "ConsoleDlg.h" + + +// CConsoleDlg dialog + +IMPLEMENT_DYNCREATE(CConsoleDlg, CDialog) +CConsoleDlg::CConsoleDlg(CWnd* pParent /*=NULL*/) + : CDialog(CConsoleDlg::IDD) +{ + currentHistoryPosition = -1; + currentCommand = ""; + saveCurrentCommand = true; +} + +CConsoleDlg::~CConsoleDlg() +{ +} + +void CConsoleDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + DDX_Control(pDX, IDC_EDIT_CONSOLE, editConsole); + DDX_Control(pDX, IDC_EDIT_INPUT, editInput); +} + +void CConsoleDlg::AddText( const char *msg ) { + idStr work; + CString work2; + + work = msg; + work.RemoveEscapes(); + work = CEntityDlg::TranslateString( work.c_str() ); + editConsole.GetWindowText( work2 ); + int len = work2.GetLength(); + if ( len + work.Length() > (int)editConsole.GetLimitText() ) { + work2 = work2.Right( editConsole.GetLimitText() * 0.75 ); + len = work2.GetLength(); + editConsole.SetWindowText(work2); + } + editConsole.SetSel( len, len ); + editConsole.ReplaceSel( work ); +} + + +BEGIN_MESSAGE_MAP(CConsoleDlg, CDialog) + ON_WM_SIZE() + ON_WM_SETFOCUS() + ON_WM_ACTIVATE() +END_MESSAGE_MAP() + + +// CConsoleDlg message handlers + +void CConsoleDlg::OnSize(UINT nType, int cx, int cy) +{ + CDialog::OnSize(nType, cx, cy); + + if (editInput.GetSafeHwnd() == NULL) { + return; + } + + CRect rect, crect; + GetWindowRect(rect); + editInput.GetWindowRect(crect); + + editInput.SetWindowPos(NULL, 4, rect.Height() - 4 - crect.Height(), rect.Width() - 8, crect.Height(), SWP_SHOWWINDOW); + editConsole.SetWindowPos(NULL, 4, 4, rect.Width() - 8, rect.Height() - crect.Height() - 8, SWP_SHOWWINDOW); +} + +BOOL CConsoleDlg::PreTranslateMessage(MSG* pMsg) +{ + + if (pMsg->hwnd == editInput.GetSafeHwnd()) { + if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_ESCAPE ) { + Select_Deselect(); + g_pParentWnd->SetFocus (); + return TRUE; + } + + if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN ) { + ExecuteCommand(); + return TRUE; + } + + if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_ESCAPE ) { + if (pMsg->wParam == VK_ESCAPE) { + g_pParentWnd->GetCamera()->SetFocus(); + Select_Deselect(); + } + + return TRUE; + } + + if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_UP ) { + //save off the current in-progress command so we can get back to it + if ( saveCurrentCommand == true ) { + CString str; + editInput.GetWindowText ( str ); + currentCommand = str.GetBuffer ( 0 ); + saveCurrentCommand = false; + } + + if ( consoleHistory.Num () > 0 ) { + editInput.SetWindowText ( consoleHistory[currentHistoryPosition] ); + + int selLocation = consoleHistory[currentHistoryPosition].Length (); + editInput.SetSel ( selLocation , selLocation + 1); +} + + if ( currentHistoryPosition > 0) { + --currentHistoryPosition; + } + + return TRUE; + } + + if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_DOWN ) { + int selLocation = 0; + if ( currentHistoryPosition < consoleHistory.Num () - 1 ) { + ++currentHistoryPosition; + editInput.SetWindowText ( consoleHistory[currentHistoryPosition] ); + selLocation = consoleHistory[currentHistoryPosition].Length (); + } + else { + editInput.SetWindowText ( currentCommand ); + selLocation = currentCommand.Length (); + currentCommand.Clear (); + saveCurrentCommand = true; + } + + editInput.SetSel ( selLocation , selLocation + 1); + + return TRUE; + } + if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_TAB ) { + common->Printf ( "Command History\n----------------\n" ); + for ( int i = 0 ; i < consoleHistory.Num ();i++ ) +{ + common->Printf ( "[cmd %d]: %s\n" , i , consoleHistory[i].c_str() ); + } + common->Printf ( "----------------\n" ); + } + if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_NEXT) { + editConsole.LineScroll ( 10 ); + } + + if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_PRIOR ) { + editConsole.LineScroll ( -10 ); + } + + if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_HOME ) { + editConsole.LineScroll ( -editConsole.GetLineCount() ); + } + + if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_END ) { + editConsole.LineScroll ( editConsole.GetLineCount() ); + } + } + + return CDialog::PreTranslateMessage(pMsg); +} + +void CConsoleDlg::OnSetFocus(CWnd* pOldWnd) { + CDialog::OnSetFocus(pOldWnd); + editInput.SetFocus(); +} + +void CConsoleDlg::SetConsoleText ( const idStr& text ) { + editInput.Clear (); + editInput.SetWindowText ( text.c_str() ); +} + +void CConsoleDlg::ExecuteCommand ( const idStr& cmd ) { + CString str; + if ( cmd.Length() > 0 ) { + str = cmd; + } + else { + editInput.GetWindowText(str); + } + + if ( str != "" ) { + editInput.SetWindowText(""); + common->Printf("%s\n", str.GetBuffer(0)); + + //avoid adding multiple identical commands in a row + int index = consoleHistory.Num (); + + if ( index == 0 || str.GetBuffer(0) != consoleHistory[index-1]) { + //keep the history to 16 commands, removing the oldest command + if ( consoleHistory.Num () > 16 ) { + consoleHistory.RemoveIndex ( 0 ); + } + currentHistoryPosition = consoleHistory.Append ( str.GetBuffer (0) ); + } + else { + currentHistoryPosition = consoleHistory.Num () - 1; + } + + currentCommand.Clear (); + + bool propogateCommand = true; + + //process some of our own special commands + if ( str.CompareNoCase ( "clear" ) == 0) { + editConsole.SetSel ( 0 , -1 ); + editConsole.Clear (); + } + else if ( str.CompareNoCase ( "edit" ) == 0) { + propogateCommand = false; + } + if ( propogateCommand ) { + cmdSystem->BufferCommandText( CMD_EXEC_NOW, str ); + } + + Sys_UpdateWindows(W_ALL); + } +} + +void CConsoleDlg::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized) +{ + CDialog::OnActivate(nState, pWndOther, bMinimized); + + if ( nState == WA_ACTIVE || nState == WA_CLICKACTIVE ) + { + editInput.SetFocus(); + } +} diff --git a/src/tools/radiant/ConsoleDlg.h b/src/tools/radiant/ConsoleDlg.h new file mode 100644 index 0000000..cf6436a --- /dev/null +++ b/src/tools/radiant/ConsoleDlg.h @@ -0,0 +1,66 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#pragma once +#include "afxwin.h" + + +// CConsoleDlg dialog + +class CConsoleDlg : public CDialog +{ + DECLARE_DYNCREATE(CConsoleDlg) + +public: + CConsoleDlg(CWnd* pParent = NULL); // standard constructor + virtual ~CConsoleDlg(); + +// Dialog Data + enum { IDD = IDD_DIALOG_CONSOLE }; + +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + + DECLARE_MESSAGE_MAP() +public: + CEdit editConsole; + CEdit editInput; + void AddText(const char *msg); + void SetConsoleText ( const idStr& text ); + void ExecuteCommand ( const idStr& cmd = "" ); + + idStr consoleStr; + idStrList consoleHistory; + idStr currentCommand; + int currentHistoryPosition; + bool saveCurrentCommand; + + afx_msg void OnSize(UINT nType, int cx, int cy); + virtual BOOL PreTranslateMessage(MSG* pMsg); + afx_msg void OnSetFocus(CWnd* pOldWnd); + afx_msg void OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized); +}; diff --git a/src/tools/radiant/CurveDlg.cpp b/src/tools/radiant/CurveDlg.cpp new file mode 100644 index 0000000..125682e --- /dev/null +++ b/src/tools/radiant/CurveDlg.cpp @@ -0,0 +1,67 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" +#include "CurveDlg.h" + + +// CCurveDlg dialog + +IMPLEMENT_DYNAMIC(CCurveDlg, CDialog) +CCurveDlg::CCurveDlg(CWnd* pParent /*=NULL*/) + : CDialog(CCurveDlg::IDD, pParent) +{ +} + +CCurveDlg::~CCurveDlg() +{ +} + +void CCurveDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + DDX_Control(pDX, IDC_COMBO_CURVES, comboCurve); +} + +void CCurveDlg::OnOK() { + UpdateData(TRUE); + CString str; + comboCurve.GetWindowText( str ); + strCurveType = str; + CDialog::OnOK(); +} + +BEGIN_MESSAGE_MAP(CCurveDlg, CDialog) +END_MESSAGE_MAP() + + +// CCurveDlg message handlers diff --git a/src/tools/radiant/CurveDlg.h b/src/tools/radiant/CurveDlg.h new file mode 100644 index 0000000..9277bf6 --- /dev/null +++ b/src/tools/radiant/CurveDlg.h @@ -0,0 +1,51 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#pragma once + + +// CCurveDlg dialog + +class CCurveDlg : public CDialog +{ + DECLARE_DYNAMIC(CCurveDlg) + +public: + CCurveDlg(CWnd* pParent = NULL); // standard constructor + virtual ~CCurveDlg(); + +// Dialog Data + enum { IDD = IDD_DIALOG_NEWCURVE }; + + idStr strCurveType; +protected: + CComboBox comboCurve; + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual void OnOK(); + + DECLARE_MESSAGE_MAP() +}; diff --git a/src/tools/radiant/DRAG.CPP b/src/tools/radiant/DRAG.CPP new file mode 100644 index 0000000..8decf37 --- /dev/null +++ b/src/tools/radiant/DRAG.CPP @@ -0,0 +1,763 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "splines.h" + +/* drag either multiple brushes, or select plane points from a single brush. */ +bool g_moveOnly = false; +bool drag_ok; +idVec3 drag_xvec; +idVec3 drag_yvec; + +static int buttonstate; +int pressx, pressy; +static idVec3 pressdelta; +static idVec3 vPressStart; +static int buttonx, buttony; + +// int num_move_points; float *move_points[1024]; +int lastx, lasty; + +bool drag_first; + +/* +================ +AxializeVector +================ +*/ +static void AxializeVector( idVec3 &v ) { + idVec3 a; + float o; + int i; + + if (!v[0] && !v[1]) { + return; + } + + if (!v[1] && !v[2]) { + return; + } + + if (!v[0] && !v[2]) { + return; + } + + for (i = 0; i < 3; i++) { + a[i] = idMath::Fabs(v[i]); + } + + if (a[0] > a[1] && a[0] > a[2]) { + i = 0; + } + else if (a[1] > a[0] && a[1] > a[2]) { + i = 1; + } + else { + i = 2; + } + + o = v[i]; + VectorCopy(vec3_origin, v); + if (o < 0) { + v[i] = -1; + } + else { + v[i] = 1; + } +} + +extern bool UpdateActiveDragPoint(const idVec3 &move); +extern void SetActiveDrag(CDragPoint *p); + +/* +================ +Draw_Setup +================ +*/ +static void Drag_Setup( int x, int y, int buttons, + const idVec3 &xaxis, const idVec3 &yaxis, const idVec3 &origin, const idVec3 &dir ) { + qertrace_t t; + face_t *f; + + drag_first = true; + + VectorCopy(vec3_origin, pressdelta); + pressx = x; + pressy = y; + + VectorCopy(xaxis, drag_xvec); + AxializeVector(drag_xvec); + VectorCopy(yaxis, drag_yvec); + AxializeVector(drag_yvec); + + if (g_qeglobals.d_select_mode == sel_addpoint) { + if (g_qeglobals.selectObject) { + g_qeglobals.selectObject->addPoint(origin); + } + else { + g_qeglobals.d_select_mode = sel_brush; + } + + return; + } + + if (g_qeglobals.d_select_mode == sel_editpoint) { + + g_Inspectors->entityDlg.SelectCurvePointByRay( origin, dir, buttons ); + + if ( g_qeglobals.d_num_move_points ) { + drag_ok = true; + } + + Sys_UpdateWindows(W_ALL); + + return; + } + + if (g_qeglobals.d_select_mode == sel_curvepoint) { + SelectCurvePointByRay(origin, dir, buttons); + + if (g_qeglobals.d_num_move_points || g_qeglobals.d_select_mode == sel_area) { + drag_ok = true; + } + + Sys_UpdateWindows(W_ALL); + + Undo_Start("drag curve point"); + Undo_AddBrushList(&selected_brushes); + + return; + } + else { + g_qeglobals.d_num_move_points = 0; + } + + if (selected_brushes.next == &selected_brushes) { + // + // in this case a new brush is created when the dragging takes place in the XYWnd, + // An useless undo is created when the dragging takes place in the CamWnd + // + Undo_Start("create brush"); + + Sys_Status("No selection to drag\n", 0); + return; + } + + if (g_qeglobals.d_select_mode == sel_vertex) { + + if ( radiant_entityMode.GetBool() ) { + return; + } + + SelectVertexByRay(origin, dir); + if (g_qeglobals.d_num_move_points) { + drag_ok = true; + Undo_Start("drag vertex"); + Undo_AddBrushList(&selected_brushes); + return; + } + } + + if (g_qeglobals.d_select_mode == sel_edge) { + + if ( radiant_entityMode.GetBool() ) { + return; + } + + SelectEdgeByRay(origin, dir); + if (g_qeglobals.d_num_move_points) { + drag_ok = true; + Undo_Start("drag edge"); + Undo_AddBrushList(&selected_brushes); + return; + } + } + + // check for direct hit first + t = Test_Ray(origin, dir, true); + SetActiveDrag(t.point); + if (t.point) { + drag_ok = true; + + // point was hit + return; + } + + if (t.selected) { + drag_ok = true; + + Undo_Start("drag selection"); + Undo_AddBrushList(&selected_brushes); + + if (buttons == (MK_LBUTTON | MK_CONTROL)) { + Sys_Status("Shear dragging face\n"); + Brush_SelectFaceForDragging(t.brush, t.face, true); + } + else if (buttons == (MK_LBUTTON | MK_CONTROL | MK_SHIFT)) { + Sys_Status("Sticky dragging brush\n"); + for (f = t.brush->brush_faces; f; f = f->next) { + Brush_SelectFaceForDragging(t.brush, f, false); + } + } + else { + Sys_Status("Dragging entire selection\n"); + } + + return; + } + + if (g_qeglobals.d_select_mode == sel_vertex || g_qeglobals.d_select_mode == sel_edge) { + return; + } + + if ( radiant_entityMode.GetBool() ) { + return; + } + + // check for side hit multiple brushes selected? + if (selected_brushes.next->next != &selected_brushes) { + // yes, special handling + bool bOK = ( g_PrefsDlg.m_bALTEdge ) ? ( ::GetAsyncKeyState( VK_MENU ) != 0 ) : true; + if (bOK) { + for (brush_t * pBrush = selected_brushes.next; pBrush != &selected_brushes; pBrush = pBrush->next) { + if (buttons & MK_CONTROL) { + Brush_SideSelect(pBrush, origin, dir, true); + } + else { + Brush_SideSelect(pBrush, origin, dir, false); + } + } + } + else { + Sys_Status("press ALT to drag multiple edges\n"); + return; + } + } + else { + // single select.. trying to drag fixed entities handle themselves and just move + if (buttons & MK_CONTROL) { + Brush_SideSelect(selected_brushes.next, origin, dir, true); + } + else { + Brush_SideSelect(selected_brushes.next, origin, dir, false); + } + } + + Sys_Status("Side stretch\n"); + drag_ok = true; + + Undo_Start("side stretch"); + Undo_AddBrushList(&selected_brushes); +} + +extern void Face_GetScale_BrushPrimit(face_t *face, float *s, float *t, float *rot); + +/* +================ +Drag_Begin +================ +*/ +void Drag_Begin( int x, int y, int buttons, + const idVec3 &xaxis, const idVec3 &yaxis, const idVec3 &origin, const idVec3 &dir ) { + qertrace_t t; + + drag_ok = false; + VectorCopy(vec3_origin, pressdelta); + VectorCopy(vec3_origin, vPressStart); + + drag_first = true; + + // shift LBUTTON = select entire brush + if (buttons == (MK_LBUTTON | MK_SHIFT) && g_qeglobals.d_select_mode != sel_curvepoint) { + int nFlag = ( ::GetAsyncKeyState( VK_MENU ) != 0 ) ? SF_CYCLE : 0; + if (dir[0] == 0 || dir[1] == 0 || dir[2] == 0) { // extremely low chance of this happening from camera + Select_Ray(origin, dir, nFlag | SF_ENTITIES_FIRST); // hack for XY + } + else { + Select_Ray(origin, dir, nFlag); + } + + return; + } + + // ctrl-shift LBUTTON = select single face + if (buttons == (MK_LBUTTON | MK_CONTROL | MK_SHIFT) && g_qeglobals.d_select_mode != sel_curvepoint) { + if ( radiant_entityMode.GetBool() ) { + return; + } + + // _D3XP disabled + //Select_Deselect( ( ::GetAsyncKeyState( VK_MENU ) == 0 ) ); + Select_Ray(origin, dir, SF_SINGLEFACE); + return; + } + + // LBUTTON + all other modifiers = manipulate selection + if (buttons & MK_LBUTTON) { + Drag_Setup(x, y, buttons, xaxis, yaxis, origin, dir); + return; + } + + if ( radiant_entityMode.GetBool() ) { + return; + } + + int nMouseButton = g_PrefsDlg.m_nMouseButtons == 2 ? MK_RBUTTON : MK_MBUTTON; + + // middle button = grab texture + if (buttons == nMouseButton) { + t = Test_Ray(origin, dir, false); + if (t.face) { + g_qeglobals.d_new_brush_bottom = t.brush->mins; + g_qeglobals.d_new_brush_top = t.brush->maxs; + + // use a local brushprimit_texdef fitted to a default 2x2 texture + brushprimit_texdef_t bp_local; + if (t.brush && t.brush->pPatch) { + texdef_t localtd; + memset(&bp_local.coords, 0, sizeof(bp_local.coords)); + bp_local.coords[0][0] = 1.0f; + bp_local.coords[1][1] = 1.0f; + localtd.SetName(t.brush->pPatch->d_texture->GetName()); + Texture_SetTexture(&localtd, &bp_local, false, true); + Select_CopyPatchTextureCoords ( t.brush->pPatch ); + } else { + Select_ProjectFaceOntoPatch( t.face ); + ConvertTexMatWithQTexture(&t.face->brushprimit_texdef, t.face->d_texture, &bp_local, NULL); + Texture_SetTexture(&t.face->texdef, &bp_local, false, true); + } + UpdateSurfaceDialog(); + UpdatePatchInspector(); + UpdateLightInspector(); + } + else { + Sys_Status("Did not select a texture\n"); + } + + return; + } + + // ctrl-middle button = set entire brush to texture + if (buttons == (nMouseButton | MK_CONTROL)) { + t = Test_Ray(origin, dir, false); + if (t.brush) { + if (t.brush->brush_faces->texdef.name[0] == '(') { + Sys_Status("Can't change an entity texture\n"); + } + else { + Brush_SetTexture + ( + t.brush, + &g_qeglobals.d_texturewin.texdef, + &g_qeglobals.d_texturewin.brushprimit_texdef, + false + ); + Sys_UpdateWindows(W_ALL); + } + } + else { + Sys_Status("Didn't hit a btrush\n"); + } + + return; + } + + // ctrl-shift-middle button = set single face to texture + if (buttons == (nMouseButton | MK_SHIFT | MK_CONTROL)) { + t = Test_Ray(origin, dir, false); + if (t.brush) { + if (t.brush->brush_faces->texdef.name[0] == '(') { + Sys_Status("Can't change an entity texture\n"); + } + else { + SetFaceTexdef + ( + t.brush, + t.face, + &g_qeglobals.d_texturewin.texdef, + &g_qeglobals.d_texturewin.brushprimit_texdef + ); + Brush_Build(t.brush); + Sys_UpdateWindows(W_ALL); + } + } + else { + Sys_Status("Didn't hit a btrush\n"); + } + + return; + } + + if (buttons == (nMouseButton | MK_SHIFT)) { + Sys_Status("Set brush face texture info\n"); + t = Test_Ray(origin, dir, false); + if (t.brush && !t.brush->owner->eclass->fixedsize) { +/* + if (t.brush->brush_faces->texdef.name[0] == '(') { + if (t.brush->owner->eclass->nShowFlags & ECLASS_LIGHT) { + CString strBuff; + idMaterial *pTex = declManager->FindMaterial(g_qeglobals.d_texturewin.texdef.name); + if (pTex) { + idVec3 vColor = pTex->getColor(); + + float fLargest = 0.0f; + for (int i = 0; i < 3; i++) { + if (vColor[i] > fLargest) { + fLargest = vColor[i]; + } + } + + if (fLargest == 0.0f) { + vColor[0] = vColor[1] = vColor[2] = 1.0f; + } + else { + float fScale = 1.0f / fLargest; + for (int i = 0; i < 3; i++) { + vColor[i] *= fScale; + } + } + + strBuff.Format("%f %f %f", pTex->getColor().x, pTex->getColor().y, pTex->getColor().z); + SetKeyValue(t.brush->owner, "_color", strBuff.GetBuffer(0)); + Sys_UpdateWindows(W_ALL); + } + } + else { + Sys_Status("Can't select an entity brush face\n"); + } + } + + else { +*/ + // strcpy(t.face->texdef.name,g_qeglobals.d_texturewin.texdef.name); + t.face->texdef.SetName(g_qeglobals.d_texturewin.texdef.name); + Brush_Build(t.brush); + Sys_UpdateWindows(W_ALL); +// } + } + else { + Sys_Status("Didn't hit a brush\n"); + } + + return; + } +} + + +void Brush_GetBounds(brush_t *b, idVec3 &mins, idVec3 &maxs) { + int i; + + for (i = 0; i < 3; i++) { + mins[i] = 999999; + maxs[i] = -999999; + } + + for (i = 0; i < 3; i++) { + if (b->mins[i] < mins[i]) { + mins[i] = b->mins[i]; + } + + if (b->maxs[i] > maxs[i]) { + maxs[i] = b->maxs[i]; + } + } +} + + +/* +================ +MoveSelection +================ +*/ +static void MoveSelection( const idVec3 &orgMove ) { + int i, success; + brush_t *b; + CString strStatus; + idVec3 vTemp, vTemp2, end, move; + + move = orgMove; + + if (!move[0] && !move[1] && !move[2]) { + return; + } + + move[0] = (g_nScaleHow & SCALE_X) ? 0 : move[0]; + move[1] = (g_nScaleHow & SCALE_Y) ? 0 : move[1]; + move[2] = (g_nScaleHow & SCALE_Z) ? 0 : move[2]; + + if (g_pParentWnd->ActiveXY()->RotateMode() || g_bPatchBendMode) { + float fDeg = -move[2]; + float fAdj = move[2]; + int axis = 0; + if (g_pParentWnd->ActiveXY()->GetViewType() == XY) { + fDeg = -move[1]; + fAdj = move[1]; + axis = 2; + } + else if (g_pParentWnd->ActiveXY()->GetViewType() == XZ) { + fDeg = move[2]; + fAdj = move[2]; + axis = 1; + } + + g_pParentWnd->ActiveXY()->Rotation()[g_qeglobals.rotateAxis] += fAdj; + strStatus.Format + ( + "%s x:: %.1f y:: %.1f z:: %.1f", + (g_bPatchBendMode) ? "Bend angle" : "Rotation", + g_pParentWnd->ActiveXY()->Rotation()[0], + g_pParentWnd->ActiveXY()->Rotation()[1], + g_pParentWnd->ActiveXY()->Rotation()[2] + ); + g_pParentWnd->SetStatusText(2, strStatus); + + if (g_bPatchBendMode) { + Patch_SelectBendNormal(); + Select_RotateAxis(axis, fDeg * 2, false, true); + Patch_SelectBendAxis(); + Select_RotateAxis(axis, fDeg, false, true); + } + else { + Select_RotateAxis(g_qeglobals.rotateAxis, fDeg, false, true); + } + + return; + } + + if (g_pParentWnd->ActiveXY()->ScaleMode()) { + idVec3 v; + v[0] = v[1] = v[2] = 1.0f; + for (int i = 0; i < 3; i++) { + if ( move[i] > 0.0f ) { + v[i] = 1.1f; + } else if ( move[i] < 0.0f ) { + v[i] = 0.9f; + } + } + + Select_Scale(v.x, v.y, v.z); + Sys_UpdateWindows(W_ALL); + return; + } + + idVec3 vDistance; + VectorSubtract(pressdelta, vPressStart, vDistance); + strStatus.Format("Distance x: %.3f y: %.3f z: %.3f", vDistance[0], vDistance[1], vDistance[2]); + g_pParentWnd->SetStatusText(3, strStatus); + + // dragging only a part of the selection + if (UpdateActiveDragPoint(move)) { + UpdateLightInspector(); + return; + } + + // + // this is fairly crappy way to deal with curvepoint and area selection but it + // touches the smallest amount of code this way + // + if (g_qeglobals.d_num_move_points || g_qeglobals.d_num_move_planes || g_qeglobals.d_select_mode == sel_area) { + // area selection + if (g_qeglobals.d_select_mode == sel_area) { + VectorAdd(g_qeglobals.d_vAreaBR, move, g_qeglobals.d_vAreaBR); + return; + } + + // curve point selection + if (g_qeglobals.d_select_mode == sel_curvepoint) { + Patch_UpdateSelected(move); + return; + } + + // vertex selection + if (g_qeglobals.d_select_mode == sel_vertex) { + success = true; + for (b = selected_brushes.next; b != &selected_brushes; b = b->next) { + success &= Brush_MoveVertex(selected_brushes.next, *g_qeglobals.d_move_points[0], move, end, true); + } + + // if (success) + VectorCopy(end, *g_qeglobals.d_move_points[0]); + return; + } + + // all other selection types + for (i = 0; i < g_qeglobals.d_num_move_points; i++) { + VectorAdd(*g_qeglobals.d_move_points[i], move, *g_qeglobals.d_move_points[i]); + } + + if ( g_qeglobals.d_select_mode == sel_editpoint ) { + g_Inspectors->entityDlg.UpdateEntityCurve(); + } + + // + // VectorScale(move, .5, move); for (i=0 ; inext) { + VectorCopy(b->maxs, vTemp); + VectorSubtract(vTemp, b->mins, vTemp); + Brush_Build(b); + for (i = 0; i < 3; i++) { + if + ( + b->mins[i] > b->maxs[i] || + b->maxs[i] - b->mins[i] > MAX_WORLD_SIZE || + b->maxs[i] - b->mins[i] == 0.0f + ) { + break; // dragged backwards or messed up + } + } + + if (i != 3) { + break; + } + + if (b->pPatch) { + VectorCopy(b->maxs, vTemp2); + VectorSubtract(vTemp2, b->mins, vTemp2); + VectorSubtract(vTemp2, vTemp, vTemp2); + + // if (!Patch_DragScale(b->nPatchID, vTemp2, move)) + if (!Patch_DragScale(b->pPatch, vTemp2, move)) { + b = NULL; + break; + } + } + } + + // if any of the brushes were crushed out of existance calcel the entire move + if (b != &selected_brushes) { + Sys_Status("Brush dragged backwards, move canceled\n"); + for (i = 0; i < g_qeglobals.d_num_move_points; i++) { + VectorSubtract(*g_qeglobals.d_move_points[i], move, *g_qeglobals.d_move_points[i]); + } + + for (b = selected_brushes.next; b != &selected_brushes; b = b->next) { + Brush_Build(b); + } + } + } + else { + // + // reset face originals from vertex edit mode this is dirty, but unfortunately + // necessary because Brush_Build can remove windings + // + for (b = selected_brushes.next; b != &selected_brushes; b = b->next) { + Brush_ResetFaceOriginals(b); + } + + Select_Move(move); + } +} + +/* +================ +Drag_MouseMoved +================ +*/ +void Drag_MouseMoved(int x, int y, int buttons) { + idVec3 move, delta; + int i; + + if (!buttons || !drag_ok) { + drag_ok = false; + return; + } + + // clear along one axis + if (buttons & MK_SHIFT) { + drag_first = false; + if (abs(x - pressx) > abs(y - pressy)) { + y = pressy; + } + else { + x = pressx; + } + } + + for (i = 0; i < 3; i++) { + move[i] = drag_xvec[i] * (x - pressx) + drag_yvec[i] * (y - pressy); + if (!g_PrefsDlg.m_bNoClamp) { + move[i] = floor(move[i] / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize; + } + } + + VectorSubtract(move, pressdelta, delta); + VectorCopy(move, pressdelta); + + if (buttons & MK_CONTROL && g_pParentWnd->ActiveXY()->RotateMode()) { + for (i = 0; i < 3; i++) { + if (delta[i] != 0) { + if (delta[i] > 0) { + delta[i] = 15; + } + else { + delta[i] = -15; + } + } + } + } + + MoveSelection(delta); +} + +/* +================ +Drag_MouseUp +================ +*/ +void Drag_MouseUp(int nButtons) { + Sys_Status("drag completed.", 0); + + if (g_qeglobals.d_select_mode == sel_area) { + Patch_SelectAreaPoints(); + g_qeglobals.d_select_mode = sel_curvepoint; + Sys_UpdateWindows(W_ALL); + } + + if (g_qeglobals.d_select_translate[0] || g_qeglobals.d_select_translate[1] || g_qeglobals.d_select_translate[2]) { + Select_Move(g_qeglobals.d_select_translate); + VectorCopy(vec3_origin, g_qeglobals.d_select_translate); + Sys_UpdateWindows(W_CAMERA); + } + + g_pParentWnd->SetStatusText(3, ""); + +/* + if (g_pParentWnd->GetCamera()->UpdateRenderEntities()) { + Sys_UpdateWindows(W_CAMERA); + } +*/ + + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} diff --git a/src/tools/radiant/DialogInfo.cpp b/src/tools/radiant/DialogInfo.cpp new file mode 100644 index 0000000..6d664a9 --- /dev/null +++ b/src/tools/radiant/DialogInfo.cpp @@ -0,0 +1,101 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" +#include "DialogInfo.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CDialogInfo dialog +CDialogInfo g_dlgInfo; + +void ShowInfoDialog(const char* pText) +{ + if (g_dlgInfo.GetSafeHwnd()) + { + g_dlgInfo.m_wndInfo.SetWindowText(pText); + g_dlgInfo.ShowWindow(SW_SHOW); + } + else + { + g_dlgInfo.Create(IDD_DLG_INFORMATION); + g_dlgInfo.m_wndInfo.SetWindowText(pText); + g_dlgInfo.ShowWindow(SW_SHOW); + } + g_pParentWnd->SetFocus(); +} + +void HideInfoDialog() +{ + if (g_dlgInfo.GetSafeHwnd()) + g_dlgInfo.ShowWindow(SW_HIDE); +} + + +CDialogInfo::CDialogInfo(CWnd* pParent /*=NULL*/) + : CDialog(CDialogInfo::IDD, pParent) +{ + //{{AFX_DATA_INIT(CDialogInfo) + //}}AFX_DATA_INIT +} + + +void CDialogInfo::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CDialogInfo) + DDX_Control(pDX, IDC_EDIT1, m_wndInfo); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CDialogInfo, CDialog) + //{{AFX_MSG_MAP(CDialogInfo) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CDialogInfo message handlers + +BOOL CDialogInfo::OnInitDialog() +{ + CDialog::OnInitDialog(); + // TODO: Add extra initialization here + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} diff --git a/src/tools/radiant/DialogInfo.h b/src/tools/radiant/DialogInfo.h new file mode 100644 index 0000000..31f81f6 --- /dev/null +++ b/src/tools/radiant/DialogInfo.h @@ -0,0 +1,75 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#if !defined(AFX_DIALOGINFO_H__81DF2A33_A552_11D1_B58E_00AA00A410FC__INCLUDED_) +#define AFX_DIALOGINFO_H__81DF2A33_A552_11D1_B58E_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// DialogInfo.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CDialogInfo dialog +void HideInfoDialog(); +void ShowInfoDialog(const char* pText); + +class CDialogInfo : public CDialog +{ +// Construction +public: + CDialogInfo(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CDialogInfo) + enum { IDD = IDD_DLG_INFORMATION }; + CEdit m_wndInfo; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CDialogInfo) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CDialogInfo) + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_DIALOGINFO_H__81DF2A33_A552_11D1_B58E_00AA00A410FC__INCLUDED_) diff --git a/src/tools/radiant/DialogTextures.cpp b/src/tools/radiant/DialogTextures.cpp new file mode 100644 index 0000000..0973510 --- /dev/null +++ b/src/tools/radiant/DialogTextures.cpp @@ -0,0 +1,1042 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" +#include "WaitDlg.h" +#include "DialogTextures.h" +#include "DialogInfo.h" +#include "EditViewDlg.h" + +#ifdef _DEBUG + #define new DEBUG_NEW + #undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +HTREEITEM FindTreeItem(CTreeCtrl *tree, HTREEITEM root, const char *text, HTREEITEM forceParent); +extern void Select_SetKeyVal(const char *key, const char *val); + +const char *CDialogTextures::TypeNames[] = { + "None", + "Textures", + "Materials", + "Models", + "Scripts", + "Sounds", + "SoundParent", + "Guis", + "Particles", + "Fx" +}; + +// +// ======================================================================================================================= +// CDialogTextures dialog +// ======================================================================================================================= +// +CDialogTextures::CDialogTextures(CWnd *pParent /* =NULL */ ) : + CDialog(CDialogTextures::IDD, pParent) { + setTexture = true; + ignoreCollapse = false; + mode = TEXTURES; + editMaterial = NULL; + editGui = ""; + //{{AFX_DATA_INIT(CDialogTextures) + //}}AFX_DATA_INIT +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CDialogTextures::DoDataExchange(CDataExchange *pDX) { + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CDialogTextures) + DDX_Control(pDX, IDC_CHECK_HIDEROOT, m_chkHideRoot); + DDX_Control(pDX, IDC_REFRESH, m_btnRefresh); + DDX_Control(pDX, IDC_LOAD, m_btnLoad); + DDX_Control(pDX, IDC_PREVIEW, m_wndPreview); + DDX_Control(pDX, IDC_TREE_TEXTURES, m_treeTextures); + //}}AFX_DATA_MAP +} + +BEGIN_MESSAGE_MAP(CDialogTextures, CDialog) +//{{AFX_MSG_MAP(CDialogTextures) + ON_BN_CLICKED(IDC_LOAD, OnLoad) + ON_BN_CLICKED(IDC_REFRESH, OnRefresh) + ON_NOTIFY(NM_CLICK, IDC_TREE_TEXTURES, OnClickTreeTextures) + ON_NOTIFY(TVN_SELCHANGED, IDC_TREE_TEXTURES, OnSelchangedTreeTextures) + ON_NOTIFY(NM_DBLCLK, IDC_TREE_TEXTURES, OnDblclkTreeTextures) + ON_BN_CLICKED(IDC_PREVIEW, OnPreview) + ON_WM_CREATE() + ON_WM_SIZE() + ON_BN_CLICKED(IDC_CHECK_HIDEROOT, OnCheckHideroot) + ON_COMMAND(ID_MATERIAL_EDIT, OnMaterialEdit) + ON_COMMAND(ID_MATERIAL_INFO, OnMaterialInfo) + //}}AFX_MSG_MAP + ON_WM_SETFOCUS() + ON_NOTIFY(NM_RCLICK, IDC_TREE_TEXTURES, OnNMRclickTreeTextures) +END_MESSAGE_MAP() +// +// ======================================================================================================================= +// CDialogTextures message handlers +// ======================================================================================================================= +// +void CDialogTextures::OnOK() { + //CDialog::OnOK(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +BOOL CDialogTextures::OnInitDialog() { + CDialog::OnInitDialog(); + + m_image.Create(IDB_BITMAP_MATERIAL, 16, 1, RGB(255, 255, 255)); + m_treeTextures.SetImageList(&m_image, TVSIL_NORMAL); + + // m_wndPreview.SubclassDlgItem(IDC_PREVIEW, this); + m_wndPreview.setDrawable(&m_testDrawable); + BuildTree(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +bool CDialogTextures::loadTree( HTREEITEM item, const idStr &name, CWaitDlg *dlg ) { + + if ( item == NULL ) { + return true; + } + + if ( m_treeTextures.ItemHasChildren( item ) ) { + + idStr childName; + HTREEITEM nextItem; + HTREEITEM childItem = m_treeTextures.GetChildItem(item); + + while ( childItem != NULL ) { + + nextItem = m_treeTextures.GetNextItem( childItem, TVGN_NEXT ); + childName = name + "/" + (const char *)m_treeTextures.GetItemText( childItem ); + + if ( m_treeTextures.ItemHasChildren( childItem ) ) { + if ( !loadTree( childItem, childName, dlg ) ) { + return false; + } + } else { + DWORD dw = m_treeTextures.GetItemData( childItem ); + if ( dw == TEXTURES || dw == MATERIALS ) { + if ( dw == TEXTURES ) { + childName = "textures/" + childName; + } + dlg->SetText( childName.c_str() ); + Texture_ForName( childName ); + } + } + if ( dlg->CancelPressed() ) { + return false; + } + + childItem = nextItem; + } + } + + return true; +} + +HTREEITEM CDialogTextures::findItem(const char *name, HTREEITEM item, HTREEITEM *foundItem) { + if (*foundItem || item == NULL) { + return *foundItem; + } + if (m_treeTextures.ItemHasChildren(item)) { + HTREEITEM nextItem; + HTREEITEM childItem = m_treeTextures.GetChildItem(item); + while (childItem != NULL && *foundItem == NULL) { + nextItem = childItem; + if (m_treeTextures.ItemHasChildren(nextItem)) { + findItem(name, nextItem, foundItem); + } else { + DWORD dw = m_treeTextures.GetItemData(nextItem); + if (dw == TEXTURES) { + const char *matName = buildItemName(nextItem, TypeNames[TEXTURES]); + if ( !idStr::Icmpn( name, "textures/", 9 ) && stricmp(name + 9, matName) == 0) { + *foundItem = nextItem; + return *foundItem; + } + } else if (dw == MATERIALS) { + const char *matName = buildItemName(nextItem, TypeNames[MATERIALS]); + if (stricmp(name, matName) == 0) { + *foundItem = nextItem; + return *foundItem; + } + } else if (dw == SOUNDS) { + if (stricmp(name, m_treeTextures.GetItemText(nextItem)) == 0) { + *foundItem = nextItem; + return *foundItem; + } + } + } + childItem = m_treeTextures.GetNextItem(childItem, TVGN_NEXT); + //childItem = nextItem; + } + } + return *foundItem; +} + +void CDialogTextures::CollapseChildren(HTREEITEM parent) { + HTREEITEM nextItem; + HTREEITEM childItem = m_treeTextures.GetChildItem(parent); + while (childItem) { + nextItem = m_treeTextures.GetNextItem(childItem, TVGN_NEXT); + if (m_treeTextures.ItemHasChildren(childItem)) { + CollapseChildren(childItem); + m_treeTextures.Expand(childItem, TVE_COLLAPSE); + } + childItem = nextItem; + } +} + +void CDialogTextures::SelectCurrentItem(bool collapse, const char *name, int id) { + HTREEITEM root = m_treeTextures.GetRootItem(); + idStr qt; + if ((id == TEXTURES) || (id == MATERIALS)) { + HTREEITEM matItem = NULL; + HTREEITEM *matPtr = &matItem; + + // FIXME: This is a hack. How should this really work? + if (id == MATERIALS && !idStr::Icmpn( name, "textures/", 9 ) ) { + // Texture_SetTexture calls SelectCurrentItem with id == MATERIALS + id = TEXTURES; + } + setTexture = false; + if (root) { + if (collapse && !ignoreCollapse) { + CollapseChildren(root); + } + + HTREEITEM *check = NULL; + qt = TypeNames[id]; + qt += "/"; + if (id == TEXTURES && !idStr::Icmpn( name, "textures/", 9 ) ) { + // strip off "textures/" + qt += name + 9; + } else { + qt += name; + } + if (quickTree.Get(qt, &check)) { + matItem = *check; + } + if (matItem == NULL) { + matItem = findItem(name, root, matPtr); + } + if (matItem) { + m_treeTextures.SelectItem(matItem); + } + } + setTexture = true; + } else if (id == SOUNDS) { + if (root) { + if (collapse && !ignoreCollapse) { + CollapseChildren(root); + } + HTREEITEM sel = FindTreeItem(&m_treeTextures, root, name, NULL); + if (sel) { + m_treeTextures.SelectItem(sel); + } + } + } +} + +void CDialogTextures::OnLoad() { + CWaitCursor cursor; + CWaitDlg dlg; + dlg.AllowCancel( true ); + dlg.SetWindowText( "Loading textures..." ); + Texture_HideAll(); + HTREEITEM item = m_treeTextures.GetSelectedItem(); + idStr name = buildItemName( item, TypeNames[TEXTURES] ); + if ( !name.Cmpn( TypeNames[MATERIALS], strlen( TypeNames[MATERIALS] ) ) ) { + name = buildItemName( item, TypeNames[MATERIALS] ); + } + loadTree( item, name, &dlg ); +} + +const char *CDialogTextures::buildItemName(HTREEITEM item, const char *rootName) { + itemName = m_treeTextures.GetItemText(item); + + // have to build the name back up + HTREEITEM parent = m_treeTextures.GetParentItem(item); + while (true) { + idStr strParent = m_treeTextures.GetItemText(parent); + if ( idStr::Icmp(strParent, rootName) == 0 ) { + break; + } + strParent += "/"; + strParent += itemName; + itemName = strParent; + parent = m_treeTextures.GetParentItem(parent); + if (parent == NULL) { + break; + } + } + return itemName; +} +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CDialogTextures::OnRefresh() { + quickTree.Clear(); + + addModels( true ); + + if (mode == TEXTURES) { + idStrList textures(1024); + int count = declManager->GetNumDecls( DECL_MATERIAL ); + int i; + const idMaterial *mat; + + for (i = 0; i < count; i++) { + mat = declManager->MaterialByIndex(i, false); + if ( mat->IsValid() && mat->TestMaterialFlag(MF_EDITOR_VISIBLE) && !idStr::Icmpn( mat->GetName(), "textures/", 9 ) ) { + textures.Append(mat->GetName()); + } + } + + declManager->Reload( false ); + + BuildTree(); + count = textures.Num(); + for (i = 0; i < count; i++) { + mat = declManager->FindMaterial(textures[i].c_str()); + if ( mat ) { + mat->SetMaterialFlag(MF_EDITOR_VISIBLE); + } + } + SelectCurrentItem(false, g_qeglobals.d_texturewin.texdef.name, CDialogTextures::TEXTURES); + } else if (mode == MATERIALS) { + idStrList textures(1024); + int count = declManager->GetNumDecls( DECL_MATERIAL ); + int i; + const idMaterial *mat; + + for (i = 0; i < count; i++) { + mat = declManager->MaterialByIndex(i, false); + if ( mat->IsValid() && mat->TestMaterialFlag(MF_EDITOR_VISIBLE) && idStr::Icmpn( mat->GetName(), "textures/", 9 ) ) { + textures.Append(mat->GetName()); + } + } + + declManager->Reload( false ); + + BuildTree(); + count = textures.Num(); + for (i = 0; i < count; i++) { + mat = declManager->FindMaterial(textures[i].c_str()); + if ( mat ) { + mat->SetMaterialFlag(MF_EDITOR_VISIBLE); + } + } + SelectCurrentItem(false, g_qeglobals.d_texturewin.texdef.name, CDialogTextures::MATERIALS); + } else if (mode == SOUNDS || mode == SOUNDPARENT) { + HTREEITEM root = m_treeTextures.GetRootItem(); + HTREEITEM sib = m_treeTextures.GetNextItem(root, TVGN_ROOT); + while (sib) { + idStr str = m_treeTextures.GetItemText(sib); + if (str.Icmp(TypeNames[SOUNDS]) == 0) { + CWaitCursor cursor; + m_treeTextures.DeleteItem(sib); + + declManager->Reload( false ); + bool rootItems = m_chkHideRoot.GetCheck() == 0; + addSounds(rootItems); + return; + } + sib = m_treeTextures.GetNextSiblingItem(sib); + } + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +HTREEITEM FindTreeItem(CTreeCtrl *tree, HTREEITEM root, const char *text, HTREEITEM forceParent) { + HTREEITEM theItem = NULL; + if (root) { + if ((theItem = tree->GetNextSiblingItem(root)) != NULL) { + theItem = FindTreeItem(tree, theItem, text, NULL); + if (theItem) { + if (forceParent) { + if (tree->GetParentItem(theItem) == forceParent) { + return theItem; + } + } else { + return theItem; + } + } + } + } + + if ((theItem = tree->GetChildItem(root)) != NULL) { + theItem = FindTreeItem(tree, theItem, text, NULL); + if (theItem) { + if (forceParent) { + if (tree->GetParentItem(theItem) == forceParent) { + return theItem; + } + } else { + return theItem; + } + } + } + + if (text && idStr::Icmp(tree->GetItemText(root), text) == 0 ) { + return root; + } + + if (theItem && forceParent) { + if (tree->GetParentItem(theItem) != forceParent) { + theItem = NULL; + } + } + return theItem; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CDialogTextures::BuildTree() { + CWaitCursor cursor; + m_treeTextures.DeleteAllItems(); + bool rootItems = m_chkHideRoot.GetCheck() == 0; + + idTimer timer; + + timer.Start(); + + addMaterials( rootItems ); + // _D3XP removed + //addModels( rootItems ); + addScripts( rootItems ); + addSounds( rootItems ); + addGuis( rootItems ); + addParticles( rootItems ); + + timer.Stop(); + + common->Printf( "CDialogTextures::BuildTree() took %.0f milliseconds\n", timer.Milliseconds() ); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CDialogTextures::OnClickTreeTextures(NMHDR *pNMHDR, LRESULT *pResult) { + *pResult = 0; + + CPoint pt; + GetCursorPos(&pt); + m_treeTextures.ScreenToClient(&pt); + HTREEITEM item = m_treeTextures.HitTest(pt); + + if (item) { + DWORD dw = m_treeTextures.GetItemData(item); + mode = dw; + if (mode == SOUNDS) { + idStr loadName; + if (!m_treeTextures.ItemHasChildren(item)) { + loadName = m_treeTextures.GetItemText(item); + idStr actionName = m_treeTextures.GetItemText(item); + soundSystem->SetMute( false ); + soundSystem->PlayShaderDirectly( SOUNDWORLD_EDITOR, actionName ); + } else { + loadName = m_treeTextures.GetItemText(item); + } + + } else { + soundSystem->StopAllSounds( SOUNDWORLD_EDITOR ); + } + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CDialogTextures::OnSelchangedTreeTextures(NMHDR *pNMHDR, LRESULT *pResult) { + NM_TREEVIEW *pNMTreeView = (NM_TREEVIEW *) pNMHDR; + *pResult = 0; + + editMaterial = NULL; + editGui = ""; + mediaName = ""; + currentFile.Empty(); + m_wndPreview.setDrawable(&m_testDrawable); + HTREEITEM item = m_treeTextures.GetSelectedItem(); + if (item) { + DWORD dw = m_treeTextures.GetItemData(item); + mode = dw; + if ((dw == TEXTURES) || (dw == MATERIALS)) { + idStr matName = m_treeTextures.GetItemText(item); + + // have to build the name back up + HTREEITEM parent = m_treeTextures.GetParentItem(item); + while (true) { + idStr strParent = m_treeTextures.GetItemText(parent); + if ( idStr::Icmp(strParent, TypeNames[dw]) == 0 ) { + break; + } + strParent += "/"; + strParent += matName; + matName = strParent; + parent = m_treeTextures.GetParentItem(parent); + if (parent == NULL) { + break; + } + } + if ( dw == TEXTURES ) { + matName = "textures/" + matName; + } + + const idMaterial *mat = Texture_ForName(matName); + editMaterial = mat; + m_drawMaterial.setMedia(matName); + m_wndPreview.setDrawable(&m_drawMaterial); + m_wndPreview.RedrawWindow(); + + ignoreCollapse = true; + Select_SetDefaultTexture(mat, false, setTexture); + ignoreCollapse = false; + } else if (dw == MODELS) { + idStr strParent; + idStr modelName = m_treeTextures.GetItemText(item); + // have to build the name back up + HTREEITEM parent = m_treeTextures.GetParentItem(item); + while (true) { + strParent = m_treeTextures.GetItemText(parent); + if ( idStr::Icmp(strParent, TypeNames[MODELS]) == 0 ) { + break; + } + strParent += "/"; + strParent += modelName; + modelName = strParent; + parent = m_treeTextures.GetParentItem(parent); + if (parent == NULL) { + break; + } + } + strParent = "models/"; + strParent += modelName; + m_drawModel.setMedia(strParent); + mediaName = strParent; + m_wndPreview.setDrawable(&m_drawModel); + m_drawModel.SetRealTime(0); + m_wndPreview.RedrawWindow(); + } else if (dw == SCRIPTS) { + } else if (dw == SOUNDS) { + } else if (dw == PARTICLES) { + idStr strParent; + idStr modelName = m_treeTextures.GetItemText(item); + // have to build the name back up + HTREEITEM parent = m_treeTextures.GetParentItem(item); + while (true) { + strParent = m_treeTextures.GetItemText(parent); + if ( idStr::Icmp(strParent, TypeNames[PARTICLES]) == 0 ) { + break; + } + strParent += "/"; + strParent += modelName; + modelName = strParent; + parent = m_treeTextures.GetParentItem(parent); + if (parent == NULL) { + break; + } + } + strParent = modelName; + mediaName = strParent; + mediaName += ".prt"; + m_drawModel.setMedia(mediaName); + m_drawModel.SetRealTime(50); + m_wndPreview.setDrawable(&m_drawModel); + m_wndPreview.RedrawWindow(); + } else if (dw == GUIS) { + idStr strParent; + idStr modelName = m_treeTextures.GetItemText(item); + // have to build the name back up + HTREEITEM parent = m_treeTextures.GetParentItem(item); + while (true) { + strParent = m_treeTextures.GetItemText(parent); + if ( idStr::Icmp(strParent, TypeNames[GUIS]) == 0 ) { + break; + } + strParent += "/"; + strParent += modelName; + modelName = strParent; + parent = m_treeTextures.GetParentItem(parent); + if (parent == NULL) { + break; + } + } + strParent = "guis/"; + strParent += modelName; + const idMaterial *mat = declManager->FindMaterial("guisurfs/guipreview"); + materialEdit->SetGui( const_cast( mat ), strParent ); + editGui = strParent; + m_drawMaterial.setMedia("guisurfs/guipreview"); + m_drawMaterial.setScale(4.4f); + m_wndPreview.setDrawable(&m_drawMaterial); + m_wndPreview.RedrawWindow(); + } + } + +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CDialogTextures::addMaterials( bool rootItems ) { + idStrList textures(1024); + idStrList materials(1024); + + textures.SetGranularity( 1024 ); + materials.SetGranularity( 1024 ); + + int count = declManager->GetNumDecls( DECL_MATERIAL ); + if ( count > 0 ) { + for ( int i = 0; i < count; i++ ) { + const idMaterial *mat = declManager->MaterialByIndex( i, false ); + if ( !rootItems ) { + if ( strchr( mat->GetName(), '/' ) == NULL && strchr( mat->GetName(), '\\' ) == NULL ) { + continue; + } + } + // add everything except the textures/ materials + if ( idStr::Icmpn( mat->GetName(), "textures/", 9 ) == 0 ) { + textures.Append( mat->GetName() ); + } else { + materials.Append( mat->GetName() ); + } + } + idStrListSortPaths( textures ); + addStrList( TypeNames[TEXTURES], textures, TEXTURES ); + idStrListSortPaths( materials ); + addStrList( TypeNames[MATERIALS], materials, MATERIALS ); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CDialogTextures::addParticles( bool rootItems ) { + // Quake 4 uses BSE effects rather than Doom's .prt declarations. +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CDialogTextures::addSounds(bool rootItems) { + int i, j; + idStrList list(1024); + idStrList list2(1024); + HTREEITEM base = m_treeTextures.InsertItem(TypeNames[SOUNDS]); + + for(i = 0; i < declManager->GetNumDecls( DECL_SOUND ); i++) { + const idSoundShader *poo = declManager->SoundByIndex(i, false); + list.AddUnique( poo->GetFileName() ); + } + idStrListSortPaths( list ); + + for (i = 0; i < list.Num(); i++) { + HTREEITEM child = m_treeTextures.InsertItem(list[i], base); + m_treeTextures.SetItemData(child, SOUNDPARENT); + m_treeTextures.SetItemImage(child, 0, 1); + list2.Clear(); + for (j = 0; j < declManager->GetNumDecls( DECL_SOUND ); j++) { + const idSoundShader *poo = declManager->SoundByIndex(j, false); + if ( idStr::Icmp( list[i], poo->GetFileName() ) == 0 ) { + list2.Append( poo->GetName() ); + } + } + idStrListSortPaths( list2 ); + for (j = 0; j < list2.Num(); j++) { + HTREEITEM child2 = m_treeTextures.InsertItem( list2[j], child ); + m_treeTextures.SetItemData(child2, SOUNDS); + m_treeTextures.SetItemImage(child2, 2, 2); + } + } + +} + +void CDialogTextures::addStrList( const char *root, const idStrList &list, int id ) { + idStr out, path; + + HTREEITEM base = m_treeTextures.GetRootItem(); + while (base) { + out = m_treeTextures.GetItemText(base); + if (stricmp(root, out) == 0) { + break; + } + base = m_treeTextures.GetNextSiblingItem(base); + } + + if (base == NULL) { + base = m_treeTextures.InsertItem(root); + } + + HTREEITEM item = base; + HTREEITEM add; + + int count = list.Num(); + + idStr last, qt; + for (int i = 0; i < count; i++) { + idStr name = list[i]; + + // now break the name down convert to slashes + name.BackSlashesToSlashes(); + name.Strip(' '); + + int index; + int len = last.Length(); + if (len == 0) { + index = name.Last('/'); + if (index >= 0) { + name.Left(index, last); + } + } + else if (idStr::Icmpn(last, name, len) == 0 && name.Last('/') <= len) { + name.Right(name.Length() - len - 1, out); + add = m_treeTextures.InsertItem(out, item); + qt = root; + qt += "/"; + qt += name; + quickTree.Set(qt, add); + m_treeTextures.SetItemData(add, id); + m_treeTextures.SetItemImage(add, 2, 2); + continue; + } + else { + last.Empty(); + } + + index = 0; + item = base; + path = ""; + while (index >= 0) { + index = name.Find('/'); + if (index >= 0) { + HTREEITEM newItem = NULL; + HTREEITEM *check = NULL; + name.Left( index, out ); + path += out; + qt = root; + qt += "/"; + qt += path; + if (quickTree.Get(qt, &check)) { + newItem = *check; + } + //HTREEITEM newItem = FindTreeItem(&m_treeTextures, item, name.Left(index, out), item); + if (newItem == NULL) { + newItem = m_treeTextures.InsertItem(out, item); + qt = root; + qt += "/"; + qt += path; + quickTree.Set(qt, newItem); + m_treeTextures.SetItemImage(newItem, 0, 1); + } + + assert(newItem); + item = newItem; + name.Right( name.Length() - index - 1, out ); + name = out; + path += "/"; + } + else { + add = m_treeTextures.InsertItem(name, item); + qt = root; + qt += "/"; + qt += path; + qt += name; + quickTree.Set(qt, add); + m_treeTextures.SetItemData(add, id); + m_treeTextures.SetItemImage(add, 2, 2); + path = ""; + } + } + } + +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CDialogTextures::addModels(bool rootItems) { + idFileList *files; + + files = fileSystem->ListFilesTree( "models", ".ase|.lwo|.ma", true ); + + if ( files->GetNumFiles() ) { + addStrList( TypeNames[MODELS], files->GetList(), MODELS ); + } + + fileSystem->FreeFileList( files ); +} + +void CDialogTextures::addGuis( bool rootItems ) { + idFileList *files; + + files = fileSystem->ListFilesTree( "guis", ".gui", true ); + + if ( files->GetNumFiles() ) { + addStrList( TypeNames[GUIS], files->GetList(), GUIS ); + } + + fileSystem->FreeFileList( files ); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CDialogTextures::addScripts(bool rootItems) { +/* + idFileList *files; + + files = fileSystem->ListFilesExt( "def", ".script" ); + + if ( files->GetNumFiles() ) { + addStrList("Scripts", files->GetList(), 3); + } +*/ +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CDialogTextures::OnDblclkTreeTextures(NMHDR *pNMHDR, LRESULT *pResult) { + CPoint pt; + GetCursorPos(&pt); + m_treeTextures.ScreenToClient(&pt); + HTREEITEM item = m_treeTextures.HitTest(pt); + if (item) { + DWORD dw = m_treeTextures.GetItemData(item); + mode = dw; + if (mode == SOUNDS) { + if (!m_treeTextures.ItemHasChildren(item)) { + idStr shaderName = m_treeTextures.GetItemText(item); + Select_SetKeyVal("s_shader", shaderName); + entity_t *ent = selected_brushes.next->owner; + if (ent) { + g_Inspectors->UpdateEntitySel(ent->eclass); + MessageBeep(MB_OK); + } + } + } else if (mode == MODELS || mode == PARTICLES ) { + if (mediaName.Length()) { + g_Inspectors->entityDlg.UpdateKeyVal("model", mediaName); + } + } else if (mode <= MATERIALS) { + OnLoad(); + } + } + + *pResult = 0; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CDialogTextures::OnPreview() { + // TODO: Add your control notification handler code here +} + + +//void CDialogTextures::OnSave() +//{ +/* + CString str; + m_wndEditShader.GetWindowText(str); + if (currentFile.length() && str.GetLength()) { + fileSystem->WriteFile(currentFile, str.GetBuffer(0), str.GetLength()); + } +*/ +//} + +int CDialogTextures::OnCreate(LPCREATESTRUCT lpCreateStruct) +{ + if (CDialog::OnCreate(lpCreateStruct) == -1) + return -1; + + // TODO: Add your specialized creation code here + + return 0; +} + +void CDialogTextures::OnSize(UINT nType, int cx, int cy) +{ + CDialog::OnSize(nType, cx, cy); + + if (m_btnLoad.GetSafeHwnd() == NULL) { + return; + } + + CRect rect, rect2, rect3; + GetClientRect(rect); + m_btnLoad.GetWindowRect(rect2); + + m_btnLoad.SetWindowPos(NULL, rect.left + 4, rect.top + 4, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW); + m_btnRefresh.SetWindowPos(NULL, rect.left + rect2.Width() + 4, rect.top + 4, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW); + + + int right = rect.right - 4 - rect3.Width() - 4; + + + right = rect3.right - 4 - rect3.Width() - 4; + + // The Quake 4 inspector can resize this dialog while its child controls are + // still being attached by MFC. The hide-root option is not used by the Q4 + // material browser, so treat it as optional until it has a real HWND. + if ( ::IsWindow(m_chkHideRoot.GetSafeHwnd()) ) { + m_chkHideRoot.GetWindowRect(rect3); + m_chkHideRoot.SetWindowPos(NULL, right - rect3.Width() * 2, rect.top + 4, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW); + m_chkHideRoot.ShowWindow(SW_HIDE); + } + + int verticalSpace = (rect.Height() - rect2.Height() - 12) / 2; + + m_treeTextures.SetWindowPos(NULL, rect.left + 4, rect.top + 8 + rect2.Height(), (rect.Width() - 8), verticalSpace, SWP_SHOWWINDOW); + m_wndPreview.SetWindowPos(NULL, rect.left + 4, rect.top + 12 + rect2.Height() + verticalSpace, (rect.Width() - 8), verticalSpace, SWP_SHOWWINDOW); + + RedrawWindow(); +} + +BOOL CDialogTextures::PreCreateWindow(CREATESTRUCT& cs) +{ + return CDialog::PreCreateWindow(cs); +} + +void CDialogTextures::OnCheckHideroot() +{ + BuildTree(); +} + +void CDialogTextures::CollapseEditor() { + if (g_qeglobals.d_savedinfo.editorExpanded) { + } +} + + +void CDialogTextures::OnCancel() { +} + + +BOOL CDialogTextures::PreTranslateMessage(MSG* pMsg) +{ + if (pMsg->message == WM_KEYDOWN && (pMsg->wParam == VK_ESCAPE || pMsg->wParam == VK_RETURN)) { + if (pMsg->wParam == VK_ESCAPE) { + g_pParentWnd->GetCamera()->SetFocus(); + Select_Deselect(); + } + return TRUE; + } + + return CDialog::PreTranslateMessage(pMsg); +} + +void CDialogTextures::OnSetFocus(CWnd* pOldWnd) +{ + CDialog::OnSetFocus(pOldWnd); + RedrawWindow(); +} + +void CDialogTextures::OnNMRclickTreeTextures(NMHDR *pNMHDR, LRESULT *pResult) +{ + *pResult = 0; + + CPoint pt; + GetCursorPos(&pt); + m_treeTextures.ScreenToClient(&pt); + HTREEITEM item = m_treeTextures.HitTest(pt); + + if (item) { + DWORD dw = m_treeTextures.GetItemData(item); + mode = dw; + if (mode == TEXTURES || mode == MATERIALS || mode == GUIS) { + m_treeTextures.SelectItem(item); + HandlePopup(this, IDR_POPUP_MATERIAL); + } + } +} + +void CDialogTextures::OnMaterialEdit() { + CEditViewDlg dlg; + if ((mode == TEXTURES || mode == MATERIALS) && editMaterial) { + dlg.SetMode(CEditViewDlg::MATERIALS); + dlg.SetMaterialInfo(editMaterial->GetName(), editMaterial->GetFileName(), editMaterial->GetLineNum()); + dlg.DoModal(); + } else if (mode == GUIS && editGui.Length()) { + dlg.SetMode(CEditViewDlg::GUIS); + dlg.SetGuiInfo(editGui); + dlg.DoModal(); + } +} + +void CDialogTextures::OnMaterialInfo() { +/* + idStr str; + if (editMaterial) { + str = "File: "; + str += editMaterial->getFileName(); + str += "\r\nName: "; + str = editMaterial->getName(); + ShowInfoDialog(str); + } else if (editGui.Length()) { + str = "File: "; + str += editGui; + ShowInfoDialog(str); + } +*/ +} diff --git a/src/tools/radiant/DialogTextures.h b/src/tools/radiant/DialogTextures.h new file mode 100644 index 0000000..ead7aa2 --- /dev/null +++ b/src/tools/radiant/DialogTextures.h @@ -0,0 +1,124 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#ifndef __DIALOGTEXTURES_H +#define __DIALOGTEXTURES_H + +// DialogTextures.h : header file +// + +#include +#include "GLWidget.h" + +///////////////////////////////////////////////////////////////////////////// +// CDialogTextures dialog + +class CDialogTextures : public CDialog +{ +// Construction +public: + enum { NONE, TEXTURES, MATERIALS, MODELS, SCRIPTS, SOUNDS, SOUNDPARENT, GUIS, PARTICLES, FX,NUMIDS }; + static const char *TypeNames[NUMIDS]; + CDialogTextures(CWnd* pParent = NULL); // standard constructor + void OnCancel(); + void CollapseEditor(); + void SelectCurrentItem(bool collapse, const char *name, int id); +// Dialog Data + //{{AFX_DATA(CDialogTextures) + enum { IDD = IDD_DIALOG_TEXTURELIST }; + CButton m_chkHideRoot; + CButton m_btnRefresh; + CButton m_btnLoad; + idGLWidget m_wndPreview; + CTreeCtrl m_treeTextures; + //}}AFX_DATA + + CImageList m_image; + idGLDrawable m_testDrawable; + idGLDrawableMaterial m_drawMaterial; + idGLDrawableModel m_drawModel; + const idMaterial *editMaterial; + idStr editGui; + idStr currentFile; + idStr mediaName; + bool setTexture; + bool ignoreCollapse; + int mode; + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CDialogTextures) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + //}}AFX_VIRTUAL + +// Implementation +protected: + void addStrList(const char *root, const idStrList &list, int id); + void addScripts(bool rootItems); + void addModels(bool rootItems); + void addMaterials(bool rootItems); + void addSounds(bool rootItems); + void addGuis(bool rootItems); + void addParticles(bool rootItems); + void BuildTree(); + void CollapseChildren(HTREEITEM parent); + const char *buildItemName(HTREEITEM item, const char *rootName); + bool loadTree( HTREEITEM item, const idStr &name, CWaitDlg *dlg ); + HTREEITEM findItem(const char *name, HTREEITEM item, HTREEITEM *foundItem); + // Generated message map functions + //{{AFX_MSG(CDialogTextures) + virtual void OnOK(); + virtual BOOL OnInitDialog(); + afx_msg void OnLoad(); + afx_msg void OnRefresh(); + afx_msg void OnClickTreeTextures(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnSelchangedTreeTextures(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnDblclkTreeTextures(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnPreview(); + afx_msg void OnMaterialEdit(); + afx_msg void OnMaterialInfo(); + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnCheckHideroot(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + + idHashTable quickTree; + idStr itemName; + +public: + virtual BOOL PreTranslateMessage(MSG* pMsg); + afx_msg void OnSetFocus(CWnd* pOldWnd); + afx_msg void OnNMRclickTreeTextures(NMHDR *pNMHDR, LRESULT *pResult); +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_DIALOGTEXTURES_H__F3F3F984_E47E_11D1_B61B_00AA00A410FC__INCLUDED_) diff --git a/src/tools/radiant/DialogThick.cpp b/src/tools/radiant/DialogThick.cpp new file mode 100644 index 0000000..c9eedd4 --- /dev/null +++ b/src/tools/radiant/DialogThick.cpp @@ -0,0 +1,73 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" +#include "DialogThick.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CDialogThick dialog + + +CDialogThick::CDialogThick(CWnd* pParent /*=NULL*/) + : CDialog(CDialogThick::IDD, pParent) +{ + //{{AFX_DATA_INIT(CDialogThick) + m_bSeams = TRUE; + m_nAmount = 8; + //}}AFX_DATA_INIT +} + + +void CDialogThick::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CDialogThick) + DDX_Check(pDX, IDC_CHECK_SEAMS, m_bSeams); + DDX_Text(pDX, IDC_EDIT_AMOUNT, m_nAmount); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CDialogThick, CDialog) + //{{AFX_MSG_MAP(CDialogThick) + // NOTE: the ClassWizard will add message map macros here + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CDialogThick message handlers diff --git a/src/tools/radiant/DialogThick.h b/src/tools/radiant/DialogThick.h new file mode 100644 index 0000000..8eb9193 --- /dev/null +++ b/src/tools/radiant/DialogThick.h @@ -0,0 +1,74 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#if !defined(AFX_DIALOGTHICK_H__59F46602_553D_11D2_B082_00AA00A410FC__INCLUDED_) +#define AFX_DIALOGTHICK_H__59F46602_553D_11D2_B082_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// DialogThick.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CDialogThick dialog + +class CDialogThick : public CDialog +{ +// Construction +public: + CDialogThick(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CDialogThick) + enum { IDD = IDD_DIALOG_THICKEN }; + BOOL m_bSeams; + int m_nAmount; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CDialogThick) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CDialogThick) + // NOTE: the ClassWizard will add member functions here + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_DIALOGTHICK_H__59F46602_553D_11D2_B082_00AA00A410FC__INCLUDED_) diff --git a/src/tools/radiant/DlgCamera.cpp b/src/tools/radiant/DlgCamera.cpp new file mode 100644 index 0000000..c62b0ad --- /dev/null +++ b/src/tools/radiant/DlgCamera.cpp @@ -0,0 +1,373 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "../../sys/win32/rc/common_resource.h" +#include "../comafx/DialogName.h" + +#include "qe3.h" +#include "DlgCamera.h" +#include "DlgEvent.h" +#include "splines.h" +#include "CameraTargetDlg.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +CDlgCamera g_dlgCamera; + + +void showCameraInspector() { + if (g_dlgCamera.GetSafeHwnd() == NULL) { + g_dlgCamera.Create(IDD_DLG_CAMERA); + CRect rct; + LONG lSize = sizeof(rct); + if (LoadRegistryInfo("Radiant::CameraInspector", &rct, &lSize)) { + g_dlgCamera.SetWindowPos(NULL, rct.left, rct.top, 0,0, SWP_NOSIZE | SWP_SHOWWINDOW); + } + Sys_UpdateWindows(W_ALL); + } + g_dlgCamera.ShowWindow(SW_SHOW); + g_dlgCamera.setupFromCamera(); +} +///////////////////////////////////////////////////////////////////////////// +// CDlgCamera dialog + + +CDlgCamera::CDlgCamera(CWnd* pParent /*=NULL*/) + : CDialog(CDlgCamera::IDD, pParent) +{ + //{{AFX_DATA_INIT(CDlgCamera) + m_strName = _T(""); + m_fSeconds = 0.0f; + m_trackCamera = TRUE; + m_numSegments = 0; + m_currentSegment = 0; + m_strType = _T(""); + m_editPoints = 0; + //}}AFX_DATA_INIT +} + + +void CDlgCamera::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CDlgCamera) + DDX_Control(pDX, IDC_SCROLLBAR_SEGMENT, m_wndSegments); + DDX_Control(pDX, IDC_LIST_EVENTS, m_wndEvents); + DDX_Control(pDX, IDC_COMBO_SPLINES, m_wndSplines); + DDX_Text(pDX, IDC_EDIT_CAM_NAME, m_strName); + DDX_Text(pDX, IDC_EDIT_LENGTH, m_fSeconds); + DDX_Check(pDX, IDC_CHECK_TRACKCAMERA, m_trackCamera); + DDX_Text(pDX, IDC_EDIT_TOTALSEGMENTS, m_numSegments); + DDX_Text(pDX, IDC_EDIT_SEGMENT, m_currentSegment); + DDX_Text(pDX, IDC_EDIT_TYPE, m_strType); + DDX_Radio(pDX, IDC_RADIO_EDITPOINTS, m_editPoints); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CDlgCamera, CDialog) + //{{AFX_MSG_MAP(CDlgCamera) + ON_BN_CLICKED(IDC_BTN_ADDEVENT, OnBtnAddevent) + ON_BN_CLICKED(IDC_BTN_ADDTARGET, OnBtnAddtarget) + ON_BN_CLICKED(IDC_BTN_DELEVENT, OnBtnDelevent) + ON_CBN_DBLCLK(IDC_COMBO_SPLINES, OnDblclkComboSplines) + ON_CBN_SELCHANGE(IDC_COMBO_SPLINES, OnSelchangeComboSplines) + ON_LBN_SELCHANGE(IDC_LIST_EVENTS, OnSelchangeListEvents) + ON_LBN_DBLCLK(IDC_LIST_EVENTS, OnDblclkListEvents) + ON_WM_DESTROY() + ON_BN_CLICKED(IDC_APPLY, OnApply) + ON_WM_HSCROLL() + ON_BN_CLICKED(ID_FILE_NEW, OnFileNew) + ON_BN_CLICKED(ID_FILE_OPEN, OnFileOpen) + ON_BN_CLICKED(ID_FILE_SAVE, OnFileSave) + ON_BN_CLICKED(IDC_TESTCAMERA, OnTestcamera) + ON_BN_CLICKED(IDC_BTN_DELETEPOINTS, OnBtnDeletepoints) + ON_BN_CLICKED(IDC_BTN_SELECTALL, OnBtnSelectall) + ON_BN_CLICKED(IDC_RADIO_EDITPOINTS, OnRadioEditpoints) + ON_BN_CLICKED(IDC_RADIO_EDITPOINTS2, OnRadioAddPoints) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CDlgCamera message handlers + +void CDlgCamera::OnBtnAddevent() +{ + CDlgEvent dlg; + if (dlg.DoModal() == IDOK) { + long n = m_wndSegments.GetScrollPos() / 4 * 1000; + g_splineList->addEvent(static_cast(dlg.m_event+1), dlg.m_strParm, n); + setupFromCamera(); + } +} + +void CDlgCamera::OnBtnAddtarget() +{ + CCameraTargetDlg dlg; + if (dlg.DoModal() == IDOK) { + g_splineList->addTarget(dlg.m_strName, static_cast(dlg.m_nType)); + setupFromCamera(); + m_wndSplines.SetCurSel(g_splineList->numTargets()); + OnSelchangeComboSplines(); + } +} + +void CDlgCamera::OnBtnDelevent() +{ + // TODO: Add your control notification handler code here + +} + +void CDlgCamera::OnBtnDeltarget() +{ + // TODO: Add your control notification handler code here + +} + +void CDlgCamera::OnDblclkComboSplines() +{ + // TODO: Add your control notification handler code here + +} + +void CDlgCamera::OnSelchangeComboSplines() +{ + UpdateData(TRUE); + g_qeglobals.d_select_mode = (m_editPoints == 0) ? sel_editpoint : sel_addpoint; + g_qeglobals.d_numpoints = 0; + g_qeglobals.d_num_move_points = 0; + int i = m_wndSplines.GetCurSel(); + if (i > 0) { + g_splineList->setActiveTarget(i-1); + g_qeglobals.selectObject = g_splineList->getActiveTarget(i-1); + g_splineList->startEdit(false); + } else { + g_splineList->startEdit(true); + g_qeglobals.selectObject = g_splineList->getPositionObj(); + } + + // * 4.0 to set increments in quarter seconds + m_wndSegments.SetScrollRange(0, g_splineList->getTotalTime() * 4.0); + + Sys_UpdateWindows(W_ALL); +} + +void CDlgCamera::OnSelchangeListEvents() +{ + int sel = m_wndEvents.GetCurSel(); + //g_splineList->setActiveSegment(sel >= 0 ? sel : 0); +} + +void CDlgCamera::OnDblclkListEvents() +{ + // TODO: Add your control notification handler code here + +} + +void CDlgCamera::setupFromCamera() +{ + if (m_wndSplines.GetSafeHwnd()) { + int i; + idStr str; + m_strName = g_splineList->getName(); + m_strType = g_splineList->getPositionObj()->typeStr(); + m_wndSplines.ResetContent(); + m_wndSplines.AddString("Path"); + for (i = 0; i < g_splineList->numTargets(); i++) { + m_wndSplines.AddString(g_splineList->getActiveTarget(i)->getName()); + } + m_wndSplines.SetCurSel(0); + m_fSeconds = g_splineList->getTotalTime(); + m_wndSegments.SetScrollRange(0, g_splineList->getTotalTime() * 4.0); + + m_wndEvents.ResetContent(); + for (i = 0; i < g_splineList->numEvents(); i++) { + str = va("%s\t%s", g_splineList->getEvent(i)->typeStr(), g_splineList->getEvent(i)->getParam()); + m_wndEvents.AddString(str); + } + //m_currentSegment = g_splineList->getActiveSegment(); + //m_numSegments = g_splineList->numSegments(); + } + g_splineList->startEdit(true); + UpdateData(FALSE); +} + +BOOL CDlgCamera::OnInitDialog() +{ + CDialog::OnInitDialog(); + setupFromCamera(); + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void CDlgCamera::OnOK() +{ + g_dlgCamera.ShowWindow(SW_HIDE); + g_qeglobals.d_select_mode = sel_brush; + g_splineList->stopEdit(); + Sys_UpdateWindows(W_ALL); +} + +void CDlgCamera::OnDestroy() +{ + if (GetSafeHwnd()) { + CRect rct; + GetWindowRect(rct); + SaveRegistryInfo("Radiant::CameraInspector", &rct, sizeof(rct)); + } + CDialog::OnDestroy(); + Sys_UpdateWindows(W_ALL); +} + + +void CDlgCamera::OnApply() +{ + UpdateData(TRUE); + g_splineList->setBaseTime(m_fSeconds); + g_splineList->setName(m_strName); + g_splineList->buildCamera(); + m_wndSegments.SetScrollRange(0, g_splineList->getTotalTime() * 4.0); +} + +void CDlgCamera::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) +{ + CDialog::OnHScroll(nSBCode, nPos, pScrollBar); + int max = g_splineList->getTotalTime() * 4; + if (max == 0) { + max = 1; + } + int n = pScrollBar->GetScrollPos(); + switch (nSBCode) { + case SB_LINEUP : { + n--; + } + break; + case SB_LINEDOWN : { + n++; + } + break; + case SB_PAGEUP : { + n -= (float)max * 0.10; + } + break; + case SB_PAGEDOWN : { + n += (float)max * 0.10; + } + break; + case SB_THUMBPOSITION : { + n = nPos; + } + break; + case SB_THUMBTRACK : { + n = nPos; + } + } +// if (n < 0) { +// n = 0; +// } else if (n >= g_splineList->numSegments()) { +// if (g_splineList->numSegments() == 0) { +// g_splineList->buildCamera(); +// } +// n = g_splineList->numSegments() - 1; +// } + pScrollBar->SetScrollPos(n); + if (m_trackCamera) { + float p = (float)n / max; + p *= g_splineList->getTotalTime() * 1000; + g_splineList->startCamera(0); + g_splineList->buildCamera(); + idVec3 dir; + float fov; + g_splineList->getCameraInfo(p, g_pParentWnd->GetCamera()->Camera().origin, dir, &fov); + g_pParentWnd->GetCamera()->Camera().angles[1] = atan2 (dir[1], dir[0])*180/3.14159; + g_pParentWnd->GetCamera()->Camera().angles[0] = asin (dir[2])*180/3.14159; + + } + UpdateData(FALSE); + Sys_UpdateWindows(W_XY | W_CAMERA); +} + +void CDlgCamera::OnFileNew() +{ + g_splineList->clear(); + setupFromCamera(); +} + +void CDlgCamera::OnFileOpen() +{ + DialogName dlg("Open Camera File"); + if (dlg.DoModal() == IDOK) { + g_splineList->clear(); + g_splineList->load(va("cameras/%s.camera", dlg.m_strName)); + } +} + +void CDlgCamera::OnFileSave() +{ + DialogName dlg("Save Camera File"); + if (dlg.DoModal() == IDOK) { + g_splineList->save(va("cameras/%s.camera", dlg.m_strName)); + } +} + +void CDlgCamera::OnTestcamera() +{ + // TODO: Add your control notification handler code here + +} + +void CDlgCamera::OnBtnDeletepoints() +{ + // TODO: Add your control notification handler code here + +} + +void CDlgCamera::OnBtnSelectall() +{ + // TODO: Add your control notification handler code here + +} + +void CDlgCamera::OnRadioEditpoints() +{ + UpdateData(TRUE); + g_qeglobals.d_select_mode = sel_editpoint; +} + +void CDlgCamera::OnRadioAddPoints() +{ + UpdateData(TRUE); + g_qeglobals.d_select_mode = sel_addpoint; +} diff --git a/src/tools/radiant/DlgCamera.h b/src/tools/radiant/DlgCamera.h new file mode 100644 index 0000000..02d54b7 --- /dev/null +++ b/src/tools/radiant/DlgCamera.h @@ -0,0 +1,104 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#if !defined(AFX_DLGCAMERA_H__59C12359_E3EB_4081_9F28_01793D75CF20__INCLUDED_) +#define AFX_DLGCAMERA_H__59C12359_E3EB_4081_9F28_01793D75CF20__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// DlgCamera.h : header file +// + +extern void showCameraInspector(); + +///////////////////////////////////////////////////////////////////////////// +// CDlgCamera dialog + +class CDlgCamera : public CDialog +{ +// Construction +public: + CDlgCamera(CWnd* pParent = NULL); // standard constructor + void setupFromCamera(); + +// Dialog Data + //{{AFX_DATA(CDlgCamera) + enum { IDD = IDD_DLG_CAMERA }; + CScrollBar m_wndSegments; + CListBox m_wndEvents; + CComboBox m_wndSplines; + CString m_strName; + float m_fSeconds; + BOOL m_trackCamera; + int m_numSegments; + int m_currentSegment; + CString m_strType; + int m_editPoints; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CDlgCamera) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + // Generated message map functions + //{{AFX_MSG(CDlgCamera) + afx_msg void OnBtnAddevent(); + afx_msg void OnBtnAddtarget(); + afx_msg void OnBtnDelevent(); + afx_msg void OnBtnDeltarget(); + afx_msg void OnDblclkComboSplines(); + afx_msg void OnSelchangeComboSplines(); + afx_msg void OnSelchangeListEvents(); + afx_msg void OnDblclkListEvents(); + virtual BOOL OnInitDialog(); + virtual void OnOK(); + afx_msg void OnDestroy(); + afx_msg void OnApply(); + afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); + afx_msg void OnFileNew(); + afx_msg void OnFileOpen(); + afx_msg void OnFileSave(); + afx_msg void OnTestcamera(); + afx_msg void OnBtnDeletepoints(); + afx_msg void OnBtnSelectall(); + afx_msg void OnRadioEditpoints(); + afx_msg void OnRadioAddPoints(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_DLGCAMERA_H__59C12359_E3EB_4081_9F28_01793D75CF20__INCLUDED_) diff --git a/src/tools/radiant/DlgEvent.cpp b/src/tools/radiant/DlgEvent.cpp new file mode 100644 index 0000000..c7ab7ef --- /dev/null +++ b/src/tools/radiant/DlgEvent.cpp @@ -0,0 +1,72 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "DlgEvent.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CDlgEvent dialog + + +CDlgEvent::CDlgEvent(CWnd* pParent /*=NULL*/) + : CDialog(CDlgEvent::IDD, pParent) +{ + //{{AFX_DATA_INIT(CDlgEvent) + m_strParm = _T(""); + m_event = 0; + //}}AFX_DATA_INIT +} + + +void CDlgEvent::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CDlgEvent) + DDX_Text(pDX, IDC_EDIT_PARAM, m_strParm); + DDX_Radio(pDX, IDC_RADIO_EVENT, m_event); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CDlgEvent, CDialog) + //{{AFX_MSG_MAP(CDlgEvent) + // NOTE: the ClassWizard will add message map macros here + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CDlgEvent message handlers diff --git a/src/tools/radiant/DlgEvent.h b/src/tools/radiant/DlgEvent.h new file mode 100644 index 0000000..1c10a15 --- /dev/null +++ b/src/tools/radiant/DlgEvent.h @@ -0,0 +1,75 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#if !defined(AFX_DLGEVENT_H__B12EEBE1_FB71_407B_9075_50F63B168567__INCLUDED_) +#define AFX_DLGEVENT_H__B12EEBE1_FB71_407B_9075_50F63B168567__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// DlgEvent.h : header file +// + +#include "splines.h" +///////////////////////////////////////////////////////////////////////////// +// CDlgEvent dialog + +class CDlgEvent : public CDialog +{ +// Construction +public: + CDlgEvent(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CDlgEvent) + enum { IDD = IDD_DLG_CAMERAEVENT }; + CString m_strParm; + int m_event; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CDlgEvent) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CDlgEvent) + // NOTE: the ClassWizard will add member functions here + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_DLGEVENT_H__B12EEBE1_FB71_407B_9075_50F63B168567__INCLUDED_) diff --git a/src/tools/radiant/ECLASS.CPP b/src/tools/radiant/ECLASS.CPP new file mode 100644 index 0000000..9784c87 --- /dev/null +++ b/src/tools/radiant/ECLASS.CPP @@ -0,0 +1,454 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "io.h" +#include "../../renderer/tr_local.h" + +struct evarPrefix_t { + int type; + const char *prefix; +}; + +const evarPrefix_t EvarPrefixes[] = { + { EVAR_STRING, "editor_var " }, + { EVAR_INT, "editor_int " }, + { EVAR_FLOAT, "editor_float " }, + { EVAR_BOOL, "editor_bool " }, + { EVAR_COLOR, "editor_color " }, + { EVAR_MATERIAL,"editor_mat " }, + { EVAR_MODEL, "editor_model " }, + { EVAR_GUI, "editor_gui " }, + { EVAR_SOUND, "editor_snd "} +}; + +const int NumEvarPrefixes = sizeof(EvarPrefixes) / sizeof(evarPrefix_t); + +eclass_t *eclass = NULL; +eclass_t *eclass_bad = NULL; +char eclass_directory[1024]; + +// md3 cache for misc_models +eclass_t *g_md3Cache = NULL; + +/* + +the classname, color triple, and bounding box are parsed out of comments +A ? size means take the exact brush size. + +/*QUAKED (0 0 0) ? +/*QUAKED (0 0 0) (-8 -8 -8) (8 8 8) + +Flag names can follow the size description: + +/*QUAKED func_door (0 .5 .8) ? START_OPEN STONE_SOUND DOOR_DONT_LINK GOLD_KEY SILVER_KEY + +*/ + +void CleanEntityList( eclass_t *&pList ) { + while (pList) { + eclass_t* pTemp = pList->next; + delete pList; + pList = pTemp; + } + pList = NULL; +} + + +void CleanUpEntities() +{ + CleanEntityList(eclass); + CleanEntityList(g_md3Cache); + + if ( eclass_bad ) { + delete eclass_bad; + eclass_bad = NULL; + } +} + +void ExtendBounds(idVec3 v, idVec3 &vMin, idVec3 &vMax) +{ + for (int i = 0 ;i < 3 ;i++) + { + float f = v[i]; + + if (f < vMin[i]) + { + vMin[i] = f; + } + + if (f > vMax[i]) + { + vMax[i] = f; + } + } +} + +bool LoadModel(const char *pLocation, eclass_t *e, idVec3 &vMin, idVec3 &vMax, const char *pSkin) +{ + vMin[0] = vMin[1] = vMin[2] = 999999; + vMax[0] = vMax[1] = vMax[2] = -999999; + + if (strstr(pLocation, ".ase") != NULL) // FIXME: not correct! + { + idBounds b; + e->modelHandle = renderModelManager->FindModel( pLocation ); + b = e->modelHandle->Bounds( NULL ); + VectorCopy(b[0], vMin); + VectorCopy(b[1], vMax); + return true; + } + return false; +} + +eclass_t *EClass_Alloc( void ) { + eclass_t *e; + e = new eclass_t; + if ( e == NULL ) { + return NULL; + } + e->fixedsize = false; + e->unknown = false; + e->mins.Zero(); + e->maxs.Zero(); + e->color.Zero(); + memset( &e->texdef, 0, sizeof( e->texdef ) ); + e->modelHandle = NULL; + e->entityModel = NULL; + e->nFrame = 0; + e->nShowFlags = 0; + e->hPlug = 0; + e->next = NULL; + return e; +} + + +eclass_t *EClass_InitFromDict( const idDict *d, const char *name ) { + eclass_t *e; + const idKeyValue *kv; + + // only include entityDefs with "editor_" values in them + if ( !d->MatchPrefix( "editor_" ) ) { + return NULL; + } + + e = EClass_Alloc(); + if ( !e ) { + return NULL; + } + + e->defArgs = *d; + + idStr str; + idStr text; + idStr varname; + idStr defaultStr; + + e->name = name; + d->GetVector("editor_color", "0 0 1", e->color); + + d->GetString("editor_mins", "", str); + if (str != "?") { + d->GetVector("editor_mins", "0 0 0", e->mins); + d->GetVector("editor_maxs", "0 0 0", e->maxs); + e->fixedsize = true; + } else { + e->fixedsize = false; + } + + + d->GetString("editor_material", "", e->defMaterial); + + //str = d->GetString("model"); + //if (str.Length()) { + // e->entityModel = renderModelManager->FindModel(str); + //} + + str = ""; + + // concatenate all editor usage comments + text = ""; + kv = d->MatchPrefix( "editor_usage" ); + while( kv != NULL ) { + text += kv->GetValue(); + if ( !kv->GetValue().Length() || ( text[ text.Length() - 1 ] != '\n' ) ) { + text += "\n"; + } + kv = d->MatchPrefix( "editor_usage", kv ); + } + + e->desc = text; + + str += "Spawn args:\n"; + for (int i = 0; i < NumEvarPrefixes; i++) { + kv = d->MatchPrefix(EvarPrefixes[i].prefix); + while (kv) { + evar_t ev; + kv->GetKey().Right( kv->GetKey().Length() - strlen(EvarPrefixes[i].prefix), ev.name ); + ev.desc = kv->GetValue(); + ev.type = EvarPrefixes[i].type; + e->vars.Append(ev); + kv = d->MatchPrefix(EvarPrefixes[i].prefix, kv); + } + } + +/* + while( kv != NULL ) { + kv->key.Right( kv->key.Length() - 11, varname ); + str += va( "'%s':\t %s", varname.c_str(), kv->value.c_str() ); + if ( d->GetString( varname, "", defaultStr ) && defaultStr.Length() ) { + str += va( " Default '%s'.", defaultStr.c_str() ); + } + str += "\n"; + kv = d->MatchPrefix( "editor_var ", kv ); + } + + e->comments = Mem_CopyString( str.c_str() ); +*/ + + + // concatenate all variable comments + kv = d->MatchPrefix( "editor_copy" ); + while (kv) { + const char *temp = d->GetString(kv->GetValue()); + if (temp && *temp) { + e->args.Set(kv->GetValue(), d->GetString(kv->GetValue())); + } + kv = d->MatchPrefix("editor_copy", kv); + } + + // setup show flags + e->nShowFlags = 0; + if (d->GetBool("editor_rotatable")) { + e->nShowFlags |= ECLASS_ROTATABLE; + } + + if (d->GetBool("editor_showangle")) { + e->nShowFlags |= ECLASS_ANGLE; + } + + if (d->GetBool("editor_mover")) { + e->nShowFlags |= ECLASS_MOVER; + } + + if (d->GetBool("editor_env") || idStr::Icmpn(e->name, "env_", 4) == 0) { + e->nShowFlags |= (ECLASS_ENV | ECLASS_ROTATABLE); + if (d->GetBool("editor_ragdoll")) { + e->defArgs.Set("model", ""); + } + } + + if (d->GetBool("editor_combatnode")) { + e->nShowFlags |= ECLASS_COMBATNODE; + } + + if (d->GetBool("editor_light")) { + e->nShowFlags |= ECLASS_LIGHT; + } + + if ( idStr::Icmp(e->name, "light") == 0 ) { + e->nShowFlags |= ECLASS_LIGHT; + } else if ( idStr::Icmp(e->name, "path") == 0 ) { + e->nShowFlags |= ECLASS_PATH; + } else if ( idStr::Icmp(e->name, "target_null") == 0 ) { + e->nShowFlags |= ECLASS_CAMERAVIEW; + } else if ( idStr::Icmp(e->name, "worldspawn") == 0 ) { + e->nShowFlags |= ECLASS_WORLDSPAWN; + } else if ( idStr::Icmp(e->name, "speaker") == 0 ) { + e->nShowFlags |= ECLASS_SPEAKER; + } else if ( idStr::Icmp( e->name, "func_emitter" ) == 0 || idStr::Icmp( e->name, "func_splat" ) == 0 ) { + e->nShowFlags |= ECLASS_PARTICLE; + } else if ( idStr::Icmp(e->name, "func_liquid") == 0 ) { + e->nShowFlags |= ECLASS_LIQUID; + } + + return e; +} + +void EClass_InsertSortedList(eclass_t *&pList, eclass_t *e) +{ + eclass_t *s; + + if (!pList) + { + pList = e; + return; + } + + + s = pList; + if (stricmp (e->name, s->name) < 0) + { + e->next = s; + pList = e; + return; + } + + do + { + if (!s->next || stricmp (e->name, s->next->name) < 0) + { + e->next = s->next; + s->next = e; + return; + } + s=s->next; + } while (1); +} + +/* +================= +Eclass_InsertAlphabetized +================= +*/ +void Eclass_InsertAlphabetized (eclass_t *e) +{ +#if 1 + EClass_InsertSortedList(eclass, e); +#else + eclass_t *s; + + if (!eclass) + { + eclass = e; + return; + } + + + s = eclass; + if (stricmp (e->name, s->name) < 0) + { + e->next = s; + eclass = e; + return; + } + + do + { + if (!s->next || stricmp (e->name, s->next->name) < 0) + { + e->next = s->next; + s->next = e; + return; + } + s=s->next; + } while (1); +#endif +} + + +void Eclass_InitForSourceDirectory (const char *path) +{ + int c = declManager->GetNumDecls(DECL_ENTITYDEF); + for (int i = 0; i < c; i++) { + const idDeclEntityDef *def = static_cast( declManager->DeclByIndex( DECL_ENTITYDEF, i ) ); + if ( def ) { + eclass_t *e = EClass_InitFromDict( &def->dict, def->GetName() ); + if ( e ) { + Eclass_InsertAlphabetized (e); + } + } + } + + eclass_bad = EClass_Alloc(); + if ( !eclass_bad ) { + return; + } + eclass_bad->color.x = 0.0f; + eclass_bad->color.y = 0.5f; + eclass_bad->color.z = 0.0f; + eclass_bad->fixedsize = false; + eclass_bad->name = Mem_CopyString( "UKNOWN ENTITY CLASS" ); +} + +eclass_t *Eclass_ForName (const char *name, bool has_brushes) +{ + eclass_t *e; + char buff[1024]; + + if (!name) { + return eclass_bad; + } + + for ( e = eclass; e; e = e->next ) { + if ( !strcmp( name, e->name ) ) { + return e; + } + } + + e = EClass_Alloc(); + if ( !e ) { + return NULL; + } + e->name = Mem_CopyString( name ); + sprintf(buff, "%s not found in def/*.def\n", name); + e->comments = Mem_CopyString( buff ); + e->color.x = 0.0f; + e->color.y = 0.5f; + e->color.z = 0.0f; + e->fixedsize = !has_brushes; + e->mins.x = e->mins.y = e->mins.z = -8.0f; + e->maxs.x = e->maxs.y = e->maxs.z = 8.0f; + Eclass_InsertAlphabetized( e ); + + return e; +} + + +eclass_t* GetCachedModel(entity_t *pEntity, const char *pName, idVec3 &vMin, idVec3 &vMax) +{ + eclass_t *e = NULL; + if (pName == NULL || strlen(pName) == 0) { + return NULL; + } + + for (e = g_md3Cache; e ; e = e->next) { + if (!strcmp (pName, e->name)) { + pEntity->md3Class = e; + VectorCopy(e->mins, vMin); + VectorCopy(e->maxs, vMax); + return e; + } + } + + e = (eclass_t*)Mem_ClearedAlloc(sizeof(*e)); + memset (e, 0, sizeof(*e)); + e->name = Mem_CopyString( pName ); + e->color[0] = e->color[2] = 0.85f; + if (LoadModel(pName, e, vMin, vMax, NULL)) { + EClass_InsertSortedList(g_md3Cache, e); + VectorCopy(vMin, e->mins); + VectorCopy(vMax, e->maxs); + pEntity->md3Class = e; + return e; + } + return NULL; +} diff --git a/src/tools/radiant/EditViewDlg.cpp b/src/tools/radiant/EditViewDlg.cpp new file mode 100644 index 0000000..6c1b23e --- /dev/null +++ b/src/tools/radiant/EditViewDlg.cpp @@ -0,0 +1,303 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" +#include "EditViewDlg.h" + + +// CEditViewDlg dialog + +IMPLEMENT_DYNAMIC(CEditViewDlg, CDialog) +CEditViewDlg::CEditViewDlg(CWnd* pParent /*=NULL*/) + : CDialog(CEditViewDlg::IDD, pParent) +{ + findDlg = NULL; +} + +CEditViewDlg::~CEditViewDlg() { +} + +void CEditViewDlg::DoDataExchange(CDataExchange* pDX) { + CDialog::DoDataExchange(pDX); + DDX_Control(pDX, IDC_EDIT_INFO, editInfo); +} + + +static UINT FindDialogMessage = ::RegisterWindowMessage(FINDMSGSTRING); + +BEGIN_MESSAGE_MAP(CEditViewDlg, CDialog) + ON_WM_SIZE() + ON_BN_CLICKED(IDC_BUTTON_OPEN, OnBnClickedButtonOpen) + ON_BN_CLICKED(IDC_BUTTON_SAVE, OnBnClickedButtonSave) + ON_WM_DESTROY() + ON_WM_TIMER() + ON_BN_CLICKED(IDC_BUTTON_GOTO, OnBnClickedButtonGoto) + ON_REGISTERED_MESSAGE(FindDialogMessage, OnFindDialogMessage) +END_MESSAGE_MAP() + + +// CEditViewDlg message handlers + +void CEditViewDlg::OnSize(UINT nType, int cx, int cy) { + CDialog::OnSize(nType, cx, cy); + if (GetSafeHwnd() == NULL) { + return; + } + CRect rect, crect; + GetClientRect(rect); + CWnd *wnd = GetDlgItem(IDC_BUTTON_OPEN); + if (wnd == NULL || (wnd && wnd->GetSafeHwnd() == NULL)) { + return; + } + wnd->GetWindowRect(crect); + wnd->SetWindowPos(NULL, 4, 4, crect.Width(), crect.Height(), SWP_SHOWWINDOW); + wnd = GetDlgItem(IDC_BUTTON_SAVE); + int left = 8 + crect.Width(); + wnd->SetWindowPos(NULL, left, 4, crect.Width(), crect.Height(), SWP_SHOWWINDOW); + wnd = GetDlgItem(IDOK); + wnd->SetWindowPos(NULL, rect.Width() - crect.Width() - 4, 4, crect.Width(), crect.Height(), SWP_SHOWWINDOW); + editInfo.SetWindowPos(NULL, 4, 8 + crect.Height(), rect.Width() - 8, rect.Height() - crect.Height() * 2 - 16, SWP_SHOWWINDOW); + wnd = GetDlgItem(IDC_BUTTON_GOTO); + wnd->SetWindowPos(NULL, 4, rect.Height() - 4 - crect.Height(), crect.Width(), crect.Height(), SWP_SHOWWINDOW); + wnd = GetDlgItem(IDC_EDIT_GOTO); + wnd->SetWindowPos(NULL, 8 + crect.Width(), rect.Height() - 3 - crect.Height(), crect.Width() + 8, crect.Height() - 3, SWP_SHOWWINDOW); + wnd = GetDlgItem(IDC_STATIC_LINE); + wnd->SetWindowPos(NULL, 30 + crect.Width() * 2, rect.Height() - crect.Height(), crect.Width() * 2, crect.Height(), SWP_SHOWWINDOW); + wnd = GetDlgItem(IDC_EDIT_LINE); + wnd->SetWindowPos(NULL, 40 + crect.Width() * 3, rect.Height() - crect.Height(), crect.Width() + 8, crect.Height(), SWP_SHOWWINDOW); +} + +void CEditViewDlg::ShowFindDlg() { + if (findDlg) { + return; + } + findDlg = new CFindReplaceDialog(); + findDlg->Create(TRUE, findStr, NULL, FR_DOWN, this); + +} + +void CEditViewDlg::OnBnClickedButtonOpen() { + CPreviewDlg *dlg = NULL; + dlg = ((mode == MATERIALS) ? CEntityDlg::ShowMaterialChooser() : CEntityDlg::ShowGuiChooser()); + if (dlg) { + if (mode == MATERIALS) { + const idMaterial *mat = declManager->FindMaterial(dlg->mediaName); + SetMaterialInfo(mat->GetName(), mat->GetFileName(), mat->GetLineNum()); + } else { + SetGuiInfo(dlg->mediaName); + } + } +} + +void CEditViewDlg::OnBnClickedButtonSave() { + if (fileName.Length()) { + CString text; + editInfo.GetWindowText(text); + fileSystem->WriteFile(fileName, text.GetBuffer(0), text.GetLength(), "fs_devpath"); + if (mode == MATERIALS) { + declManager->Reload( false ); + } else { + uiManager->Reload(false); + } + } +} + +void CEditViewDlg::UpdateEditPreview() { + if (GetSafeHwnd() && editInfo.GetSafeHwnd()) { + editInfo.SetWindowText(editText); + editInfo.LineScroll(line); + int cindex = editInfo.LineIndex(line); + int len = editInfo.LineLength(line); + editInfo.SetSel(cindex, cindex); + mediaPreview.SetMode((mode == MATERIALS) ? CMediaPreviewDlg::MATERIALS : CMediaPreviewDlg::GUIS); + mediaPreview.SetMedia((mode == MATERIALS) ? matName : fileName); + SetWindowText(va("Editing %s in file <%s>", (mode == MATERIALS) ? matName.c_str() : fileName.c_str(), fileName.c_str())); + editInfo.SetFocus(); + } +} + +BOOL CEditViewDlg::OnInitDialog() { + CDialog::OnInitDialog(); + + mediaPreview.Create(IDD_DIALOG_EDITPREVIEW, this); + mediaPreview.ShowWindow(SW_SHOW); + + CRect rct; + LONG lSize = sizeof(rct); + if (LoadRegistryInfo("Radiant::EditViewWindow", &rct, &lSize)) { + SetWindowPos(NULL, rct.left, rct.top, rct.Width(), rct.Height(), SWP_SHOWWINDOW); + } + + editInfo.SetTabStops(); + editInfo.SetLimitText(1024 * 1024); + + UpdateEditPreview(); + + SetTimer(1, 250, NULL); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void CEditViewDlg::OnDestroy() { + if (GetSafeHwnd()) { + CRect rct; + GetWindowRect(rct); + SaveRegistryInfo("Radiant::EditViewWindow", &rct, sizeof(rct)); + } + + CDialog::OnDestroy(); +} + +void CEditViewDlg::SetMaterialInfo(const char *name, const char *file, int _line) { + idStr str; + void *buf; + fileName = ""; + matName = ""; + line = 0; + str = fileSystem->OSPathToRelativePath( file ); + int size = fileSystem->ReadFile( str, &buf ); + if (size > 0) { + fileName = str; + matName = name; + line = _line - 1; + if (line < 0) { + line = 0; + } + editText = (char*)buf; + fileSystem->FreeFile(buf); + } + UpdateEditPreview(); +} + +void CEditViewDlg::SetGuiInfo(const char *name) { + fileName = ""; + line = 0; + void *buf; + int size = fileSystem->ReadFile(name, &buf, NULL); + if (size > 0) { + fileName = name; + editText = (char*)buf; + fileSystem->FreeFile(buf); + } + UpdateEditPreview(); +} + +void CEditViewDlg::OnTimer(UINT nIDEvent) { + CDialog::OnTimer(nIDEvent); + CWnd *wnd = GetDlgItem(IDC_EDIT_LINE); + if (wnd) { + int start, end; + editInfo.GetSel(start, end); + wnd->SetWindowText(va("%i",editInfo.LineFromChar(start))); + } + +} + +void CEditViewDlg::OnBnClickedButtonGoto() { + CWnd *wnd = GetDlgItem(IDC_EDIT_GOTO); + if (wnd) { + CString str; + wnd->GetWindowText(str); + if (str.GetLength()) { + int l = atoi(str); + editInfo.SetSel(0, 0); + editInfo.LineScroll(l); + int cindex = editInfo.LineIndex(l); + int len = editInfo.LineLength(l); + editInfo.SetSel(cindex, cindex); + editInfo.RedrawWindow(); + editInfo.SetFocus(); + } + } +} + +BOOL CEditViewDlg::PreTranslateMessage(MSG* pMsg) { + + if (pMsg->message == WM_KEYDOWN && (pMsg->wParam == 's' || pMsg->wParam == 'S') && GetAsyncKeyState(VK_CONTROL) & 0x8000) { + OnBnClickedButtonSave(); + return TRUE; + } + + if (pMsg->message == WM_KEYDOWN && (pMsg->wParam == 'o' || pMsg->wParam == 'O') && GetAsyncKeyState(VK_CONTROL) & 0x8000) { + OnBnClickedButtonOpen(); + return TRUE; + } + + if (pMsg->message == WM_KEYDOWN && (pMsg->wParam == 'f' || pMsg->wParam == 'F') && GetAsyncKeyState(VK_CONTROL) & 0x8000) { + ShowFindDlg(); + return TRUE; + } + + if (pMsg->hwnd == editInfo.GetSafeHwnd() && (pMsg->message == WM_KEYDOWN) && (pMsg->wParam == VK_TAB)) { + // get the char index of the caret position + int nPos = LOWORD(editInfo.CharFromPos(editInfo.GetCaretPos())); + // select zero chars + editInfo.SetSel(nPos, nPos); + // then replace that selection with a TAB + editInfo.ReplaceSel("\t", TRUE); + return TRUE; + } + + return CDialog::PreTranslateMessage(pMsg); +} + +LRESULT CEditViewDlg::OnFindDialogMessage(WPARAM wParam, LPARAM lParam) { + if (findDlg == NULL) { + return 0; + } + + if (findDlg->IsTerminating()) { + findDlg = NULL; + return 0; + } + + // If the FR_FINDNEXT flag is set, + // call the application-defined search routine + // to search for the requested string. + if(findDlg->FindNext()) { + //read data from dialog + findStr = findDlg->GetFindString().GetBuffer(0); + CString str; + editInfo.GetWindowText(str); + editText = str; + int start, end; + editInfo.GetSel(start, end); + start = editText.Find(findStr, false, end); + if (start >= 0) { + editInfo.SetSel(start, start + findStr.Length()); + editInfo.Invalidate(); + editInfo.RedrawWindow(); + } + } + return 0; +} diff --git a/src/tools/radiant/EditViewDlg.h b/src/tools/radiant/EditViewDlg.h new file mode 100644 index 0000000..1a00e41 --- /dev/null +++ b/src/tools/radiant/EditViewDlg.h @@ -0,0 +1,82 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#pragma once + +#include "mediapreviewdlg.h" + +// CEditViewDlg dialog + +class CEditViewDlg : public CDialog +{ + DECLARE_DYNAMIC(CEditViewDlg) + +public: + enum {MATERIALS, GUIS}; + CEditViewDlg(CWnd* pParent = NULL); // standard constructor + virtual ~CEditViewDlg(); + + void SetMode(int _mode) { + mode = _mode; + } + + void SetMaterialInfo(const char *name, const char *file, int line); + void SetGuiInfo(const char *name); + void UpdateEditPreview(); + + void OpenMedia(const char *name); +// Dialog Data + enum { IDD = IDD_DIALOG_EDITVIEW }; + +protected: + CFindReplaceDialog *findDlg; + CMediaPreviewDlg mediaPreview; + int mode; + idStr fileName; + idStr matName; + idStr editText; + idStr findStr; + int line; + CEdit editInfo; + + void ShowFindDlg(); + + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + + DECLARE_MESSAGE_MAP() +public: + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnBnClickedButtonOpen(); + afx_msg void OnBnClickedButtonSave(); + virtual BOOL OnInitDialog(); + afx_msg void OnDestroy(); + afx_msg void OnTimer(UINT nIDEvent); + afx_msg void OnBnClickedButtonGoto(); + virtual BOOL PreTranslateMessage(MSG* pMsg); + afx_msg LRESULT OnFindDialogMessage(WPARAM wParam, LPARAM lParam); + +}; diff --git a/src/tools/radiant/EditorBrush.cpp b/src/tools/radiant/EditorBrush.cpp new file mode 100644 index 0000000..6fdc4be --- /dev/null +++ b/src/tools/radiant/EditorBrush.cpp @@ -0,0 +1,5220 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include + +#include "../../renderer/tr_local.h" +#include "../../renderer/model_local.h" // for idRenderModelMD5 + +void Brush_UpdateLightPoints(brush_t *b, const idVec3 &offset); +void Brush_DrawCurve( brush_t *b, bool bSelected, bool cam ); + +// globals +int g_nBrushId = 0; +bool g_bShowLightVolumes = false; +bool g_bShowLightTextures = false; + +void GLCircle(float x, float y, float z, float r); + +const int POINTS_PER_KNOT = 50; + +/* +================ +DrawRenderModel +================ +*/ +void DrawRenderModel( idRenderModel *model, idVec3 &origin, idMat3 &axis, bool cameraView ) { + for ( int i = 0; i < model->NumSurfaces(); i++ ) { + const modelSurface_t *surf = model->Surface( i ); + const idMaterial *material = surf->shader; + + int nDrawMode = g_pParentWnd->GetCamera()->Camera().draw_mode; + + if ( cameraView && (nDrawMode == cd_texture || nDrawMode == cd_light) ) { + material->GetEditorImage()->Bind(); + } + + qglBegin( GL_TRIANGLES ); + + const srfTriangles_t *tri = surf->geometry; + for ( int j = 0; j < tri->numIndexes; j += 3 ) { + for ( int k = 0; k < 3; k++ ) { + int index = tri->indexes[j + k]; + idVec3 v; + + v = tri->verts[index].xyz * axis + origin; + qglTexCoord2f( tri->verts[index].st.x, tri->verts[index].st.y ); + qglVertex3fv( v.ToFloatPtr() ); + } + } + + qglEnd(); + } +} + +/* +================ +SnapVectorToGrid +================ +*/ +void SnapVectorToGrid(idVec3 &v) { + v.x = floor(v.x / g_qeglobals.d_gridsize + 0.5f) * g_qeglobals.d_gridsize; + v.y = floor(v.y / g_qeglobals.d_gridsize + 0.5f) * g_qeglobals.d_gridsize; + v.z = floor(v.z / g_qeglobals.d_gridsize + 0.5f) * g_qeglobals.d_gridsize; +} + +/* +================ +Brush_Name +================ +*/ +const char *Brush_Name( brush_t *b ) { + static char cBuff[1024]; + + b->numberId = g_nBrushId++; + if (g_qeglobals.m_bBrushPrimitMode) { + sprintf(cBuff, "Brush %i", b->numberId); + Brush_SetEpair(b, "Name", cBuff); + } + + return cBuff; +} + +/* +================ +Brush_Alloc +================ +*/ +brush_t *Brush_Alloc( void ) { + brush_t *b = new brush_t; + b->prev = b->next = NULL; + b->oprev = b->onext = NULL; + b->owner = NULL; + b->mins.Zero(); + b->maxs.Zero(); + + b->lightCenter.Zero(); + b->lightRight.Zero(); + b->lightTarget.Zero(); + b->lightUp.Zero(); + b->lightRadius.Zero(); + b->lightOffset.Zero(); + b->lightColor.Zero(); + b->lightStart.Zero(); + b->lightEnd.Zero(); + b->pointLight = false; + b->startEnd = false; + b->lightTexture = 0; + b->trackLightOrigin = false; + + b->entityModel = false; + b->brush_faces = NULL; + b->hiddenBrush = false; + b->pPatch = NULL; + b->pUndoOwner = NULL; + b->undoId = 0; + b->redoId = 0; + b->ownerId = 0; + b->numberId = 0; + b->itemOwner = 0; + b->bModelFailed = false; + b->modelHandle = NULL; + b->forceVisibile = false; + b->forceWireFrame = false; + return b; +} + +/* +================ +TextureAxisFromPlane +================ +*/ +idVec3 baseaxis[18] = { + idVec3(0, 0, 1), + idVec3(1, 0, 0), + idVec3(0, -1, 0), + + // floor + idVec3(0, 0, -1), + idVec3(1, 0, 0), + idVec3(0, -1, 0), + + // ceiling + idVec3(1, 0, 0), + idVec3(0, 1, 0), + idVec3(0, 0, -1), + + // west wall + idVec3(-1, 0, 0), + idVec3(0, 1, 0), + idVec3(0, 0, -1), + + // east wall + idVec3(0, 1, 0), + idVec3(1, 0, 0), + idVec3(0, 0, -1), + + // south wall + idVec3(0, -1, 0), + idVec3(1, 0, 0), + idVec3(0, 0, -1) // north wall +}; + +void TextureAxisFromPlane( const idPlane &pln, idVec3 &xv, idVec3 &yv) { + int bestaxis; + float dot, best; + int i; + + best = 0; + bestaxis = 0; + + for (i = 0; i < 6; i++) { + dot = DotProduct(pln, baseaxis[i * 3]); + if (dot > best) { + best = dot; + bestaxis = i; + } + } + + VectorCopy(baseaxis[bestaxis * 3 + 1], xv); + VectorCopy(baseaxis[bestaxis * 3 + 2], yv); +} + +/* +================ +ShadeForNormal + + Light different planes differently to improve recognition +================ +*/ +float lightaxis[3] = { 0.6f, 0.8f, 1.0f }; + +float ShadeForNormal(idVec3 normal) { + int i; + float f; + + // axial plane + for (i = 0; i < 3; i++) { + if ( idMath::Fabs(normal[i]) > 0.9f ) { + f = lightaxis[i]; + return f; + } + } + + // between two axial planes + for (i = 0; i < 3; i++) { + if ( idMath::Fabs(normal[i]) < 0.1f ) { + f = (lightaxis[(i + 1) % 3] + lightaxis[(i + 2) % 3]) / 2; + return f; + } + } + + // other + f = (lightaxis[0] + lightaxis[1] + lightaxis[2]) / 3; + return f; +} + +/* +================ +Face_Alloc +================ +*/ +face_t *Face_Alloc(void) { + brushprimit_texdef_t bp; + + face_t *f = (face_t *) Mem_ClearedAlloc(sizeof(*f)); + + bp.coords[0][0] = 0.0f; + bp.coords[1][1] = 0.0f; + f->brushprimit_texdef = bp; + f->dirty = true; + return f; +} + +/* +================ +Face_Free +================ +*/ +void Face_Free(face_t *f) { + assert(f != 0); + + if (f->face_winding) { + delete f->face_winding; + } + + f->texdef.~texdef_t(); + + Mem_Free(f); +} + +/* +================ +Face_Clone +================ +*/ +face_t *Face_Clone(face_t *f) { + face_t *n; + + n = Face_Alloc(); + n->texdef = f->texdef; + n->brushprimit_texdef = f->brushprimit_texdef; + + memcpy(n->planepts, f->planepts, sizeof(n->planepts)); + n->plane = f->plane; + n->originalPlane = f->originalPlane; + n->dirty = f->dirty; + + // all other fields are derived, and will be set by Brush_Build + return n; +} + +/* +================ +Face_FullClone + + Used by Undo. + Makes an exact copy of the face. +================ +*/ +face_t *Face_FullClone(face_t *f) { + face_t *n; + + n = Face_Alloc(); + n->texdef = f->texdef; + n->brushprimit_texdef = f->brushprimit_texdef; + memcpy(n->planepts, f->planepts, sizeof(n->planepts)); + n->plane = f->plane; + n->originalPlane = f->originalPlane; + n->dirty = f->dirty; + if (f->face_winding) { + n->face_winding = f->face_winding->Copy(); + } + else { + n->face_winding = NULL; + } + + n->d_texture = Texture_ForName(n->texdef.name); + return n; +} + +/* +================ +Clamp +================ +*/ +void Clamp(float &f, int nClamp) { + float fFrac = f - static_cast(f); + f = static_cast(f) % nClamp; + f += fFrac; +} + +/* +================ +Face_MoveTexture +================ +*/ +void Face_MoveTexture(face_t *f, idVec3 delta) { + idVec3 vX, vY; + + /* + * #ifdef _DEBUG if (g_PrefsDlg.m_bBrushPrimitMode) common->Printf("Warning : + * Face_MoveTexture not done in brush primitive mode\n"); #endif + */ + if (g_qeglobals.m_bBrushPrimitMode) { + Face_MoveTexture_BrushPrimit(f, delta); + } + else { + TextureAxisFromPlane( f->plane, vX, vY ); + + idVec3 vDP, vShift; + vDP[0] = DotProduct(delta, vX); + vDP[1] = DotProduct(delta, vY); + + double fAngle = DEG2RAD( f->texdef.rotate ); + double c = cos(fAngle); + double s = sin(fAngle); + + vShift[0] = vDP[0] * c - vDP[1] * s; + vShift[1] = vDP[0] * s + vDP[1] * c; + + if (!f->texdef.scale[0]) { + f->texdef.scale[0] = 1; + } + + if (!f->texdef.scale[1]) { + f->texdef.scale[1] = 1; + } + + f->texdef.shift[0] -= vShift[0] / f->texdef.scale[0]; + f->texdef.shift[1] -= vShift[1] / f->texdef.scale[1]; + + // clamp the shifts + Clamp(f->texdef.shift[0], f->d_texture->GetEditorImage()->uploadWidth); + Clamp(f->texdef.shift[1], f->d_texture->GetEditorImage()->uploadHeight); + } +} + +/* +================ +Face_SetColor +================ +*/ +void Face_SetColor(brush_t *b, face_t *f, float fCurveColor) { + float shade; + const idMaterial *q; + + q = f->d_texture; + + // set shading for face + shade = ShadeForNormal( f->plane.Normal() ); + if (g_pParentWnd->GetCamera()->Camera().draw_mode == cd_texture && (b->owner && !b->owner->eclass->fixedsize)) { + // if (b->curveBrush) shade = fCurveColor; + f->d_color[0] = f->d_color[1] = f->d_color[2] = shade; + } + else if ( f && b && b->owner ) { + f->d_color[0] = shade * b->owner->eclass->color.x; + f->d_color[1] = shade * b->owner->eclass->color.y; + f->d_color[2] = shade * b->owner->eclass->color.z; + } +} + +/* +================ +Face_TextureVectors + + NOTE: this is never to get called while in brush primitives mode +================ +*/ +void Face_TextureVectors(face_t *f, float STfromXYZ[2][4]) { + idVec3 pvecs[2]; + int sv, tv; + float ang, sinv, cosv; + float ns, nt; + int i, j; + const idMaterial *q; + texdef_t *td; + +#ifdef _DEBUG + + // + // ++timo when playing with patches, this sometimes get called and the Warning is + // displayed find some way out .. + // + if (g_qeglobals.m_bBrushPrimitMode && !g_qeglobals.bNeedConvert) { + common->Printf("Warning : illegal call of Face_TextureVectors in brush primitive mode\n"); + } +#endif + td = &f->texdef; + q = f->d_texture; + + memset(STfromXYZ, 0, 8 * sizeof (float)); + + if (!td->scale[0]) { + td->scale[0] = (g_PrefsDlg.m_bHiColorTextures) ? 2 : 1; + } + + if (!td->scale[1]) { + td->scale[1] = (g_PrefsDlg.m_bHiColorTextures) ? 2 : 1; + } + + // get natural texture axis + TextureAxisFromPlane( f->plane, pvecs[0], pvecs[1]); + + // rotate axis + if (td->rotate == 0) { + sinv = 0; + cosv = 1; + } + else if (td->rotate == 90) { + sinv = 1; + cosv = 0; + } + else if (td->rotate == 180) { + sinv = 0; + cosv = -1; + } + else if (td->rotate == 270) { + sinv = -1; + cosv = 0; + } + else { + ang = DEG2RAD( td->rotate ); + sinv = sin(ang); + cosv = cos(ang); + } + + if (pvecs[0][0]) { + sv = 0; + } + else if (pvecs[0][1]) { + sv = 1; + } + else { + sv = 2; + } + + if (pvecs[1][0]) { + tv = 0; + } + else if (pvecs[1][1]) { + tv = 1; + } + else { + tv = 2; + } + + for (i = 0; i < 2; i++) { + ns = cosv * pvecs[i][sv] - sinv * pvecs[i][tv]; + nt = sinv * pvecs[i][sv] + cosv * pvecs[i][tv]; + STfromXYZ[i][sv] = ns; + STfromXYZ[i][tv] = nt; + } + + // scale + for (i = 0; i < 2; i++) { + for (j = 0; j < 3; j++) { + STfromXYZ[i][j] = STfromXYZ[i][j] / td->scale[i]; + } + } + + // shift + STfromXYZ[0][3] = td->shift[0]; + STfromXYZ[1][3] = td->shift[1]; + + for (j = 0; j < 4; j++) { + STfromXYZ[0][j] /= q->GetEditorImage()->uploadWidth; + STfromXYZ[1][j] /= q->GetEditorImage()->uploadHeight; + } +} + +/* +================ +Face_MakePlane +================ +*/ +void Face_MakePlane(face_t *f) { + int j; + idVec3 t1, t2, t3; + + idPlane oldPlane = f->plane; + + // convert to a vector / dist plane + for (j = 0; j < 3; j++) { + t1[j] = f->planepts[0][j] - f->planepts[1][j]; + t2[j] = f->planepts[2][j] - f->planepts[1][j]; + t3[j] = f->planepts[1][j]; + } + + f->plane = t1.Cross( t2 ); + //if ( f->plane.Compare( vec3_origin ) ) { + // printf("WARNING: brush plane with no normal\n"); + //} + + f->plane.Normalize(false); + f->plane[3] = - (t3 * f->plane.Normal()); + + if ( !f->dirty && !f->plane.Compare( oldPlane, 0.01f ) ) { + f->dirty = true; + } +} + +/* +================ +EmitTextureCoordinates +================ +*/ +void EmitTextureCoordinates(idVec5 &xyzst, const idMaterial *q, face_t *f, bool force) { + float STfromXYZ[2][4]; + + if (g_qeglobals.m_bBrushPrimitMode && !force) { + EmitBrushPrimitTextureCoordinates(f, f->face_winding); + } + else { + Face_TextureVectors(f, STfromXYZ); + xyzst[3] = DotProduct(xyzst, STfromXYZ[0]) + STfromXYZ[0][3]; + xyzst[4] = DotProduct(xyzst, STfromXYZ[1]) + STfromXYZ[1][3]; + } +} + +/* +================ +Brush_MakeFacePlanes +================ +*/ +void Brush_MakeFacePlanes(brush_t *b) { + face_t *f; + + for (f = b->brush_faces; f; f = f->next) { + Face_MakePlane(f); + } +} + +/* +================ +DrawBrushEntityName +================ +*/ +void DrawBrushEntityName(brush_t *b) { + const char *name; + + // float a, s, c; vec3_t mid; int i; + if (!b->owner) { + return; // during contruction + } + + if (b->owner == world_entity) { + return; + } + + if (b != b->owner->brushes.onext) { + return; // not key brush + } + + if (!(g_qeglobals.d_savedinfo.exclude & EXCLUDE_ANGLES)) { + // draw the angle pointer + float a = FloatForKey(b->owner, "angle"); + if (a) { + float s = sin( DEG2RAD( a ) ); + float c = cos( DEG2RAD( a ) ); + + idVec3 mid = (b->mins + b->maxs) / 2.0f; + + qglBegin(GL_LINE_STRIP); + qglVertex3fv(mid.ToFloatPtr()); + mid[0] += c * 8; + mid[1] += s * 8; + mid[2] += s * 8; + qglVertex3fv(mid.ToFloatPtr()); + mid[0] -= c * 4; + mid[1] -= s * 4; + mid[2] -= s * 4; + mid[0] -= s * 4; + mid[1] += c * 4; + mid[2] += c * 4; + qglVertex3fv(mid.ToFloatPtr()); + mid[0] += c * 4; + mid[1] += s * 4; + mid[2] += s * 4; + mid[0] += s * 4; + mid[1] -= c * 4; + mid[2] -= c * 4; + qglVertex3fv(mid.ToFloatPtr()); + mid[0] -= c * 4; + mid[1] -= s * 4; + mid[2] -= s * 4; + mid[0] += s * 4; + mid[1] -= c * 4; + mid[2] -= c * 4; + qglVertex3fv(mid.ToFloatPtr()); + qglEnd(); + } + } + + int viewType = g_pParentWnd->ActiveXY()->GetViewType(); + float scale = g_pParentWnd->ActiveXY()->Scale(); + + if (g_qeglobals.d_savedinfo.show_names && scale >= 1.0f) { + name = ValueForKey(b->owner, "name"); + int nameLen = strlen(name); + if ( nameLen == 0 ) { + name = ValueForKey(b->owner, "classname"); + nameLen = strlen(name); + } + if ( nameLen > 0 ) { + idVec3 origin = b->owner->origin; + + float halfWidth = ( (nameLen / 2) * (7.0f / scale) ); + float halfHeight = 4.0f / scale; + + switch (viewType) { + case XY: + origin.x -= halfWidth; + origin.y += halfHeight; + break; + case XZ: + origin.x -= halfWidth; + origin.z += halfHeight; + break; + case YZ: + origin.y -= halfWidth; + origin.z += halfHeight; + break; + } + qglRasterPos3fv( origin.ToFloatPtr() ); + qglCallLists(nameLen, GL_UNSIGNED_BYTE, name); + } + } +} + +/* +================ +Brush_MakeFaceWinding + + returns the visible winding +================ +*/ +idWinding *Brush_MakeFaceWinding(brush_t *b, face_t *face, bool keepOnPlaneWinding) { + idWinding *w; + face_t *clip; + idPlane plane; + bool past; + + // get a poly that covers an effectively infinite area + w = new idWinding( face->plane ); + + // chop the poly by all of the other faces + past = false; + for (clip = b->brush_faces; clip && w; clip = clip->next) { + if (clip == face) { + past = true; + continue; + } + + if ( DotProduct(face->plane, clip->plane) > 0.999f && + idMath::Fabs(face->plane[3] - clip->plane[3]) < 0.01f ) { // identical plane, use the later one + if (past) { + delete w; + common->Printf("Unable to create face winding on brush\n"); + return NULL; + } + continue; + } + + // flip the plane, because we want to keep the back side + VectorSubtract(vec3_origin, clip->plane, plane ); + plane[3] = -clip->plane[3]; + + w = w->Clip( plane, ON_EPSILON, keepOnPlaneWinding ); + if ( !w ) { + return w; + } + } + + if ( w->GetNumPoints() < 3) { + delete w; + w = NULL; + } + + if (!w) { + Sys_Status("Unable to create face winding on brush\n"); + } + return w; +} + +/* +================ +Brush_Build + + Builds a brush rendering data and also sets the min/max bounds + TTimo added a bConvert flag to convert between old and new brush texture formats + TTimo brush grouping: update the group treeview if necessary +================ +*/ +void Brush_Build(brush_t *b, bool bSnap, bool bMarkMap, bool bConvert, bool updateLights) { + bool bLocalConvert = false; + +#ifdef _DEBUG + if (!g_qeglobals.m_bBrushPrimitMode && bConvert) { + common->Printf("Warning : conversion from brush primitive to old brush format not implemented\n"); + } +#endif + // + // if bConvert is set and g_qeglobals.bNeedConvert is not, that just means we need + // convert for this brush only + // + if (bConvert && !g_qeglobals.bNeedConvert) { + bLocalConvert = true; + g_qeglobals.bNeedConvert = true; + } + + /* build the windings and generate the bounding box */ + Brush_BuildWindings(b, bSnap, EntityHasModel(b->owner) || b->pPatch, updateLights); + + /* move the points and edges if in select mode */ + if (g_qeglobals.d_select_mode == sel_vertex || g_qeglobals.d_select_mode == sel_edge) { + SetupVertexSelection(); + } + + if (bMarkMap) { + Sys_MarkMapModified(); + g_pParentWnd->GetCamera()->MarkWorldDirty(); + } + + if (bLocalConvert) { + g_qeglobals.bNeedConvert = false; + } +} + +/* +================ +Brush_SplitBrushByFace + + The incoming brush is NOT freed. The incoming face is NOT left referenced. +================ +*/ +void Brush_SplitBrushByFace(brush_t *in, face_t *f, brush_t **front, brush_t **back) { + brush_t *b; + face_t *nf; + idVec3 temp; + + b = Brush_Clone(in); + nf = Face_Clone(f); + + nf->texdef = b->brush_faces->texdef; + nf->brushprimit_texdef = b->brush_faces->brushprimit_texdef; + nf->next = b->brush_faces; + b->brush_faces = nf; + + Brush_Build(b); + Brush_RemoveEmptyFaces(b); + if (!b->brush_faces) { // completely clipped away + Brush_Free(b); + *back = NULL; + } + else { + Entity_LinkBrush(in->owner, b); + *back = b; + } + + b = Brush_Clone(in); + nf = Face_Clone(f); + + // swap the plane winding + VectorCopy(nf->planepts[0], temp); + VectorCopy(nf->planepts[1], nf->planepts[0]); + VectorCopy(temp, nf->planepts[1]); + + nf->texdef = b->brush_faces->texdef; + nf->brushprimit_texdef = b->brush_faces->brushprimit_texdef; + nf->next = b->brush_faces; + b->brush_faces = nf; + + Brush_Build(b); + Brush_RemoveEmptyFaces(b); + if (!b->brush_faces) { // completely clipped away + Brush_Free(b); + *front = NULL; + } + else { + Entity_LinkBrush(in->owner, b); + *front = b; + } +} + +/* +================ +Brush_BestSplitFace + + returns the best face to split the brush with. return NULL if the brush is convex +================ +*/ +face_t *Brush_BestSplitFace(brush_t *b) { + face_t *face, *f, *bestface; + idWinding *front, *back; + int splits, tinywindings, value, bestvalue; + + bestvalue = 999999; + bestface = NULL; + for ( face = b->brush_faces; face; face = face->next ) { + splits = 0; + tinywindings = 0; + for ( f = b->brush_faces; f; f = f->next ) { + if ( f == face ) { + continue; + } + + f->face_winding->Split( face->plane, 0.1f, &front, &back ); + + if ( !front ) { + delete back; + } + else if ( !back ) { + delete front; + } + else { + splits++; + if ( front->IsTiny() ) { + tinywindings++; + } + + if ( back->IsTiny() ) { + tinywindings++; + } + delete front; + delete back; + } + } + + if ( splits ) { + value = splits + 50 * tinywindings; + if ( value < bestvalue ) { + bestvalue = value; + bestface = face; + } + } + } + + return bestface; +} + +/* +================ +Brush_MakeConvexBrushes + + MrE FIXME: this doesn't work because the old Brush_SplitBrushByFace is used + Turns the brush into a minimal number of convex brushes. + If the input brush is convex then it will be returned. Otherwise the input + brush will be freed. + NOTE: the input brush should have windings for the faces. +================ +*/ +brush_t *Brush_MakeConvexBrushes(brush_t *b) { + brush_t *front, *back, *end; + face_t *face; + + b->next = NULL; + face = Brush_BestSplitFace(b); + if (!face) { + return b; + } + + Brush_SplitBrushByFace(b, face, &front, &back); + + // this should never happen + if (!front && !back) { + return b; + } + + Brush_Free(b); + if (!front) { + return Brush_MakeConvexBrushes(back); + } + + b = Brush_MakeConvexBrushes(front); + if (back) { + for (end = b; end->next; end = end->next); + end->next = Brush_MakeConvexBrushes(back); + } + + return b; +} + +/* +================ +Brush_Convex + + returns true if the brush is convex +================ +*/ +int Brush_Convex(brush_t *b) { + face_t *face1, *face2; + + for (face1 = b->brush_faces; face1; face1 = face1->next) { + if (!face1->face_winding) { + continue; + } + + for (face2 = b->brush_faces; face2; face2 = face2->next) { + if (face1 == face2) { + continue; + } + + if (!face2->face_winding) { + continue; + } + + if ( face1->face_winding->PlanesConcave( *face2->face_winding, + face1->plane.Normal(), face2->plane.Normal(), -face1->plane[3], -face2->plane[3] ) ) { + return false; + } + } + } + + return true; +} + +/* +================ +Brush_MoveVertexes + + The input brush must be convex. + The input brush must have face windings. + The output brush will be convex. + Returns true if the WHOLE vertex movement is performed. +================ +*/ +#define MAX_MOVE_FACES 64 +#define TINY_EPSILON 0.0325f + +int Brush_MoveVertex(brush_t *b, const idVec3 &vertex, const idVec3 &delta, idVec3 &end, bool bSnap) { + face_t *f, *face, *newface, *lastface, *nextface; + face_t *movefaces[MAX_MOVE_FACES]; + int movefacepoints[MAX_MOVE_FACES]; + idWinding *w, tmpw(3); + idVec3 start, mid; + idPlane plane; + int i, j, k, nummovefaces, result, done; + float dot, front, back, frac, smallestfrac; + + result = true; + tmpw.SetNumPoints( 3 ); + VectorCopy(vertex, start); + VectorAdd(vertex, delta, end); + + // snap or not? + // + if (bSnap) { + for (i = 0; i < 3; i++) { + end[i] = floor( end[i] / 0.125f + 0.5f ) * 0.125f; + } + } + + VectorCopy(end, mid); + + // if the start and end are the same + if ( start.Compare( end, TINY_EPSILON ) ) { + return false; + } + + // the end point may not be the same as another vertex + for ( face = b->brush_faces; face; face = face->next ) { + w = face->face_winding; + if (!w) { + continue; + } + + for (i = 0; i < w->GetNumPoints(); i++) { + if ( end.Compare( (*w)[i].ToVec3(), TINY_EPSILON ) ) { + VectorCopy(vertex, end); + return false; + } + } + } + + done = false; + while (!done) { + // + // chop off triangles from all brush faces that use the to be moved vertex store + // pointers to these chopped off triangles in movefaces[] + // + nummovefaces = 0; + for (face = b->brush_faces; face; face = face->next) { + w = face->face_winding; + if (!w) { + continue; + } + + for (i = 0; i < w->GetNumPoints(); i++) { + if ( start.Compare( (*w)[i].ToVec3(), TINY_EPSILON ) ) { + if (face->face_winding->GetNumPoints() <= 3) { + movefacepoints[nummovefaces] = i; + movefaces[nummovefaces++] = face; + break; + } + + dot = DotProduct(end, face->plane) + face->plane[3]; + + // if the end point is in front of the face plane + //if ( dot > 0.1f ) { + if ( dot > TINY_EPSILON ) { + // fanout triangle subdivision + for (k = i; k < i + w->GetNumPoints() - 3; k++) { + VectorCopy((*w)[i], tmpw[0]); + VectorCopy((*w)[(k + 1) % w->GetNumPoints()], tmpw[1]); + VectorCopy((*w)[(k + 2) % w->GetNumPoints()], tmpw[2]); + newface = Face_Clone(face); + + // get the original + for (f = face; f->original; f = f->original) {}; + + newface->original = f; + + // store the new winding + if (newface->face_winding) { + delete newface->face_winding; + } + + newface->face_winding = tmpw.Copy(); + + // get the texture + newface->d_texture = Texture_ForName(newface->texdef.name); + + // add the face to the brush + newface->next = b->brush_faces; + b->brush_faces = newface; + + // add this new triangle to the move faces + movefacepoints[nummovefaces] = 0; + movefaces[nummovefaces++] = newface; + } + + // give the original face a new winding + VectorCopy((*w)[(i - 2 + w->GetNumPoints()) % w->GetNumPoints()], tmpw[0]); + VectorCopy((*w)[(i - 1 + w->GetNumPoints()) % w->GetNumPoints()], tmpw[1]); + VectorCopy((*w)[i], tmpw[2]); + delete face->face_winding; + face->face_winding = tmpw.Copy(); + + // add the original face to the move faces + movefacepoints[nummovefaces] = 2; + movefaces[nummovefaces++] = face; + } + else { + // chop a triangle off the face + VectorCopy((*w)[(i - 1 + w->GetNumPoints()) % w->GetNumPoints()], tmpw[0]); + VectorCopy((*w)[i], tmpw[1]); + VectorCopy((*w)[(i + 1) % w->GetNumPoints()], tmpw[2]); + + // remove the point from the face winding + w->RemovePoint( i ); + + // get texture crap right + Face_SetColor(b, face, 1.0); + for (j = 0; j < w->GetNumPoints(); j++) { + EmitTextureCoordinates( (*w)[j], face->d_texture, face ); + } + + // make a triangle face + newface = Face_Clone(face); + + // get the original + for (f = face; f->original; f = f->original) {}; + + newface->original = f; + + // store the new winding + if (newface->face_winding) { + delete newface->face_winding; + } + + newface->face_winding = tmpw.Copy(); + + // get the texture + newface->d_texture = Texture_ForName(newface->texdef.name); + + // add the face to the brush + newface->next = b->brush_faces; + b->brush_faces = newface; + movefacepoints[nummovefaces] = 1; + movefaces[nummovefaces++] = newface; + } + break; + } + } + } + + // + // now movefaces contains pointers to triangle faces that contain the to be moved + // vertex + // + done = true; + VectorCopy(end, mid); + smallestfrac = 1; + for (face = b->brush_faces; face; face = face->next) { + // check if there is a move face that has this face as the original + for (i = 0; i < nummovefaces; i++) { + if (movefaces[i]->original == face) { + break; + } + } + + if (i >= nummovefaces) { + continue; + } + + // check if the original is not a move face itself + for (j = 0; j < nummovefaces; j++) { + if (face == movefaces[j]) { + break; + } + } + + // if the original is not a move face itself + if (j >= nummovefaces) { + memcpy(&plane, &movefaces[i]->original->plane, sizeof(plane)); + } + else { + k = movefacepoints[j]; + w = movefaces[j]->face_winding; + VectorCopy((*w)[(k + 1) % w->GetNumPoints()], tmpw[0]); + VectorCopy((*w)[(k + 2) % w->GetNumPoints()], tmpw[1]); + + k = movefacepoints[i]; + w = movefaces[i]->face_winding; + VectorCopy((*w)[(k + 1) % w->GetNumPoints()], tmpw[2]); + + if ( !plane.FromPoints( tmpw[0].ToVec3(), tmpw[1].ToVec3(), tmpw[2].ToVec3(), false ) ) { + VectorCopy((*w)[(k + 2) % w->GetNumPoints()], tmpw[2]); + if ( !plane.FromPoints( tmpw[0].ToVec3(), tmpw[1].ToVec3(), tmpw[2].ToVec3() ), false ) { + // this should never happen otherwise the face merge did + // a crappy job a previous pass + continue; + } + } + plane[0] = -plane[0]; + plane[1] = -plane[1]; + plane[2] = -plane[2]; + plane[3] = -plane[3]; + } + + // now we've got the plane to check against + front = DotProduct(start, plane) + plane[3]; + back = DotProduct(end, plane) + plane[3]; + + // if the whole move is at one side of the plane + if (front < TINY_EPSILON && back < TINY_EPSILON) { + continue; + } + + if (front > -TINY_EPSILON && back > -TINY_EPSILON) { + continue; + } + + // if there's no movement orthogonal to this plane at all + if ( idMath::Fabs(front - back) < 0.001f ) { + continue; + } + + // ok first only move till the plane is hit + frac = front / (front - back); + if (frac < smallestfrac) { + mid[0] = start[0] + (end[0] - start[0]) * frac; + mid[1] = start[1] + (end[1] - start[1]) * frac; + mid[2] = start[2] + (end[2] - start[2]) * frac; + smallestfrac = frac; + } + + done = false; + } + + // move the vertex + for (i = 0; i < nummovefaces; i++) { + // move vertex to end position + VectorCopy( mid, (*movefaces[i]->face_winding)[movefacepoints[i]] ); + + // create new face plane + for (j = 0; j < 3; j++) { + VectorCopy( (*movefaces[i]->face_winding)[j], movefaces[i]->planepts[j] ); + } + + Face_MakePlane( movefaces[i] ); + if ( movefaces[i]->plane.Normal().Length() < TINY_EPSILON ) { + result = false; + } + } + + // if the brush is no longer convex + if (!result || !Brush_Convex(b)) { + for (i = 0; i < nummovefaces; i++) { + // move the vertex back to the initial position + VectorCopy( start, (*movefaces[i]->face_winding)[movefacepoints[i]] ); + + // create new face plane + for (j = 0; j < 3; j++) { + VectorCopy( (*movefaces[i]->face_winding)[j], movefaces[i]->planepts[j] ); + } + + Face_MakePlane(movefaces[i]); + } + + result = false; + VectorCopy(start, end); + done = true; + } + else { + VectorCopy(mid, start); + } + + // get texture crap right + for (i = 0; i < nummovefaces; i++) { + Face_SetColor( b, movefaces[i], 1.0f ); + for (j = 0; j < movefaces[i]->face_winding->GetNumPoints(); j++) { + EmitTextureCoordinates( (*movefaces[i]->face_winding)[j], movefaces[i]->d_texture, movefaces[i] ); + } + } + + // now try to merge faces with their original faces + lastface = NULL; + for (face = b->brush_faces; face; face = nextface) { + nextface = face->next; + if (!face->original) { + lastface = face; + continue; + } + + if ( !face->plane.Compare( face->original->plane, 0.0001f ) ) { + lastface = face; + continue; + } + + w = face->face_winding->TryMerge( *face->original->face_winding, face->plane.Normal(), true ); + if (!w) { + lastface = face; + continue; + } + + delete face->original->face_winding; + face->original->face_winding = w; + + // get texture crap right + Face_SetColor( b, face->original, 1.0f ); + for (j = 0; j < face->original->face_winding->GetNumPoints(); j++) { + EmitTextureCoordinates( (*face->original->face_winding)[j], face->original->d_texture, face->original); + } + + // remove the face that was merged with the original + if (lastface) { + lastface->next = face->next; + } + else { + b->brush_faces = face->next; + } + + Face_Free(face); + } + } + + return result; +} + +/* +================ +Brush_InsertVertexBetween + + Adds a vertex to the brush windings between the given two points. +================ +*/ +int Brush_InsertVertexBetween(brush_t *b, idVec3 p1, idVec3 p2) { + face_t *face; + idWinding *w, *neww; + idVec3 point; + int i, insert; + + if ( p1.Compare( p2, TINY_EPSILON ) ) { + return false; + } + + VectorAdd( p1, p2, point ); + VectorScale( point, 0.5f, point ); + insert = false; + + // the end point may not be the same as another vertex + for (face = b->brush_faces; face; face = face->next) { + w = face->face_winding; + if (!w) { + continue; + } + + neww = NULL; + for (i = 0; i < w->GetNumPoints(); i++) { + if (! p1.Compare((*w)[i].ToVec3(), TINY_EPSILON)) { + continue; + } + + if ( p2.Compare( (*w)[(i + 1) % w->GetNumPoints()].ToVec3(), TINY_EPSILON ) ) { + neww = new idWinding( *w ); + neww->InsertPoint( point, (i + 1) % w->GetNumPoints() ); + break; + } + else if ( p2.Compare( (*w)[(i - 1 + w->GetNumPoints()) % w->GetNumPoints()].ToVec3(), TINY_EPSILON ) ) { + neww = new idWinding( *w ); + neww->InsertPoint( point, i ); + break; + } + } + + if (neww) { + delete face->face_winding; + face->face_winding = neww; + insert = true; + } + } + + return insert; +} + +/* +================ +Brush_ResetFaceOriginals + + reset points to original faces to NULL +================ +*/ +void Brush_ResetFaceOriginals(brush_t *b) { + face_t *face; + + for (face = b->brush_faces; face; face = face->next) { + face->original = NULL; + } +} + +/* +================ +Brush_Parse + + The brush is NOT linked to any list + FIXME: when using old brush primitives, the test loop for "Brush" and "patchDef2" "patchDef3" + run before each face parsing. It works, but it's a performance hit +================ +*/ +brush_t *Brush_Parse(idVec3 origin) { + brush_t *b; + face_t *f; + int i, j; + idVec3 useOrigin = origin; + + g_qeglobals.d_parsed_brushes++; + b = Brush_Alloc(); + do { + if (!GetToken(true)) { + break; + } + + if (!strcmp(token, "}")) { + break; + } + + // handle "Brush" primitive + if ( idStr::Icmp(token, "brushDef") == 0 || idStr::Icmp(token, "brushDef2") == 0 || idStr::Icmp(token, "brushDef3") == 0 ) { + // Timo parsing new brush format + g_qeglobals.bPrimitBrushes = true; + + // check the map is not mixing the two kinds of brushes + if (g_qeglobals.m_bBrushPrimitMode) { + if (g_qeglobals.bOldBrushes) { + common->Printf("Warning : old brushes and brush primitive in the same file are not allowed ( Brush_Parse )\n"); + } + } + else { + // ++Timo write new brush primitive -> old conversion code for Q3->Q2 conversions ? + common->Printf("Warning : conversion code from brush primitive not done ( Brush_Parse )\n"); + } + + bool newFormat = false; + if ( idStr::Icmp(token, "brushDef2") == 0 ) { + newFormat = true; + + // useOrigin.Zero(); + } + else if ( idStr::Icmp(token, "brushDef3") == 0 ) { + newFormat = true; + } + + + BrushPrimit_Parse(b, newFormat, useOrigin); + + if (newFormat) { + //Brush_BuildWindings(b, true, true, false, false); + } + + if (b == NULL) { + Warning("parsing brush primitive"); + return NULL; + } + else { + continue; + } + } + + if ( idStr::Icmp(token, "patchDef2") == 0 || idStr::Icmp(token, "patchDef3") == 0 ) { + Brush_Free(b); + + // double string compare but will go away soon + b = Patch_Parse( idStr::Icmp(token, "patchDef2") == 0 ); + if (b == NULL) { + Warning("parsing patch/brush"); + return NULL; + } + else { + continue; + } + + // handle inline patch + } + else { + // Timo parsing old brush format + g_qeglobals.bOldBrushes = true; + if (g_qeglobals.m_bBrushPrimitMode) { + // check the map is not mixing the two kinds of brushes + if (g_qeglobals.bPrimitBrushes) { + common->Printf("Warning : old brushes and brush primitive in the same file are not allowed ( Brush_Parse )\n"); + } + + // set the "need" conversion flag + g_qeglobals.bNeedConvert = true; + } + + f = Face_Alloc(); + + // + // add the brush to the end of the chain, so loading and saving a map doesn't + // reverse the order + // + f->next = NULL; + if (!b->brush_faces) { + b->brush_faces = f; + } + else { + face_t *scan; + for (scan = b->brush_faces; scan->next; scan = scan->next) + ; + scan->next = f; + } + + // read the three point plane definition + for (i = 0; i < 3; i++) { + if (i != 0) { + GetToken(true); + } + + if (strcmp(token, "(")) { + Warning("parsing brush"); + return NULL; + } + + for (j = 0; j < 3; j++) { + GetToken(false); + f->planepts[i][j] = atof(token); + } + + GetToken(false); + if (strcmp(token, ")")) { + Warning("parsing brush"); + return NULL; + } + } + } + + // read the texturedef + GetToken(false); + f->texdef.SetName(token); + if (token[0] == '(') { + int i = 32; + } + + GetToken(false); + f->texdef.shift[0] = atoi(token); + GetToken(false); + f->texdef.shift[1] = atoi(token); + GetToken(false); + f->texdef.rotate = atoi(token); + GetToken(false); + f->texdef.scale[0] = atof(token); + GetToken(false); + f->texdef.scale[1] = atof(token); + + // the flags and value field aren't necessarily present + f->d_texture = Texture_ForName(f->texdef.name); + + // + // FIXME: idMaterial f->texdef.flags = f->d_texture->flags; f->texdef.value = + // f->d_texture->value; f->texdef.contents = f->d_texture->contents; + // + if (TokenAvailable()) { + GetToken(false); + GetToken(false); + GetToken(false); + f->texdef.value = atoi(token); + } + } while (1); + + return b; +} + +/* +================ +QERApp_MapPrintf_FILE + + callback for surface properties plugin must fit a PFN_QERAPP_MAPPRINTF ( see isurfaceplugin.h ) + carefully initialize ! +================ +*/ +FILE *g_File; + +void WINAPI QERApp_MapPrintf_FILE(char *text, ...) { + va_list argptr; + char buf[32768]; + + va_start(argptr, text); + vsprintf(buf, text, argptr); + va_end(argptr); + + fprintf(g_File, buf); +} + +/* +================ +Brush_SetEpair + + sets an epair for the given brush +================ +*/ +void Brush_SetEpair(brush_t *b, const char *pKey, const char *pValue) { + if (g_qeglobals.m_bBrushPrimitMode) { + if (b->pPatch) { + Patch_SetEpair(b->pPatch, pKey, pValue); + } + else { + b->epairs.Set(pKey, pValue); + } + } + else { + Sys_Status("Can only set key/values in Brush primitive mode\n"); + } +} + +/* +================ +Brush_GetKeyValue +================ +*/ +const char *Brush_GetKeyValue(brush_t *b, const char *pKey) { + if (g_qeglobals.m_bBrushPrimitMode) { + if (b->pPatch) { + return Patch_GetKeyValue(b->pPatch, pKey); + } + else { + return b->epairs.GetString(pKey); + } + } + else { + Sys_Status("Can only set brush/patch key/values in Brush primitive mode\n"); + } + + return ""; +} + +/* +================ +Brush_Write + + save all brushes as Brush primitive format +================ +*/ +void Brush_Write(brush_t *b, FILE *f, const idVec3 &origin, bool newFormat) { + face_t *fa; + char *pname; + int i; + + if (b->pPatch) { + Patch_Write(b->pPatch, f); + return; + } + + if (g_qeglobals.m_bBrushPrimitMode) { + // save brush primitive format + if (newFormat) { + WriteFileString(f, "{\nbrushDef3\n{\n"); + } + else { + WriteFileString(f, "{\nbrushDef\n{\n"); + } + + // brush epairs + int count = b->epairs.GetNumKeyVals(); + for (int j = 0; j < count; j++) { + WriteFileString(f, "\"%s\" \"%s\"\n", b->epairs.GetKeyVal(j)->GetKey().c_str(), b->epairs.GetKeyVal(j)->GetValue().c_str()); + } + + for (fa = b->brush_faces; fa; fa = fa->next) { + // save planepts + if (newFormat) { + idPlane plane; + + if (fa->dirty) { + fa->planepts[0] -= origin; + fa->planepts[1] -= origin; + fa->planepts[2] -= origin; + plane.FromPoints( fa->planepts[0], fa->planepts[1], fa->planepts[2], false ); + fa->planepts[0] += origin; + fa->planepts[1] += origin; + fa->planepts[2] += origin; + } else { + plane = fa->originalPlane; + } + + WriteFileString(f, " ( "); + for (i = 0; i < 4; i++) { + if (plane[i] == (int)plane[i]) { + WriteFileString(f, "%i ", (int)plane[i]); + } + else { + WriteFileString(f, "%f ", plane[i]); + } + } + + WriteFileString(f, ") "); + } + else { + for (i = 0; i < 3; i++) { + WriteFileString(f, "( "); + for (int j = 0; j < 3; j++) { + if (fa->planepts[i][j] == static_cast(fa->planepts[i][j])) { + WriteFileString(f, "%i ", static_cast(fa->planepts[i][j])); + } + else { + WriteFileString(f, "%f ", fa->planepts[i][j]); + } + } + + WriteFileString(f, ") "); + } + } + + // save texture coordinates + WriteFileString(f, "( ( "); + for (i = 0; i < 3; i++) { + if (fa->brushprimit_texdef.coords[0][i] == static_cast(fa->brushprimit_texdef.coords[0][i])) { + WriteFileString(f, "%i ", static_cast(fa->brushprimit_texdef.coords[0][i])); + } + else { + WriteFileString(f, "%f ", fa->brushprimit_texdef.coords[0][i]); + } + } + + WriteFileString(f, ") ( "); + for (i = 0; i < 3; i++) { + if (fa->brushprimit_texdef.coords[1][i] == static_cast(fa->brushprimit_texdef.coords[1][i])) { + WriteFileString(f, "%i ", static_cast(fa->brushprimit_texdef.coords[1][i])); + } + else { + WriteFileString(f, "%f ", fa->brushprimit_texdef.coords[1][i]); + } + } + + WriteFileString(f, ") ) "); + + char *pName = strlen(fa->texdef.name) > 0 ? fa->texdef.name : "notexture"; + WriteFileString(f, "\"%s\" ", pName); + WriteFileString(f, "%i %i %i\n", 0, 0, 0); + } + + WriteFileString(f, "}\n}\n"); + } + else { + WriteFileString(f, "{\n"); + for (fa = b->brush_faces; fa; fa = fa->next) { + for (i = 0; i < 3; i++) { + WriteFileString(f, "( "); + for (int j = 0; j < 3; j++) { + if (fa->planepts[i][j] == static_cast(fa->planepts[i][j])) { + WriteFileString(f, "%i ", static_cast(fa->planepts[i][j])); + } + else { + WriteFileString(f, "%f ", fa->planepts[i][j]); + } + } + + WriteFileString(f, ") "); + } + + pname = fa->texdef.name; + if (pname[0] == 0) { + pname = "unnamed"; + } + + WriteFileString + ( + f, + "%s %i %i %i ", + pname, + (int)fa->texdef.shift[0], + (int)fa->texdef.shift[1], + (int)fa->texdef.rotate + ); + + if (fa->texdef.scale[0] == (int)fa->texdef.scale[0]) { + WriteFileString(f, "%i ", (int)fa->texdef.scale[0]); + } + else { + WriteFileString(f, "%f ", (float)fa->texdef.scale[0]); + } + + if (fa->texdef.scale[1] == (int)fa->texdef.scale[1]) { + WriteFileString(f, "%i", (int)fa->texdef.scale[1]); + } + else { + WriteFileString(f, "%f", (float)fa->texdef.scale[1]); + } + + WriteFileString(f, " %i %i %i",0, 0, 0); + + WriteFileString(f, "\n"); + } + + WriteFileString(f, "}\n"); + } +} + +/* +================ +QERApp_MapPrintf_MEMFILE + + callback for surface properties plugin must fit a PFN_QERAPP_MAPPRINTF ( see isurfaceplugin.h ) + carefully initialize ! +================ +*/ +CMemFile *g_pMemFile; + +void WINAPI QERApp_MapPrintf_MEMFILE(char *text, ...) { + va_list argptr; + char buf[32768]; + + va_start(argptr, text); + vsprintf(buf, text, argptr); + va_end(argptr); + + MemFile_fprintf(g_pMemFile, buf); +} + +/* +================ +Brush_Write + + save all brushes as Brush primitive format to a CMemFile* +================ +*/ +void Brush_Write(brush_t *b, CMemFile *pMemFile, const idVec3 &origin, bool newFormat) { + face_t *fa; + char *pname; + int i; + + if (b->pPatch) { + Patch_Write(b->pPatch, pMemFile); + return; + } + + if (g_qeglobals.m_bBrushPrimitMode) { + // brush primitive format + if (newFormat) { + MemFile_fprintf(pMemFile, "{\nBrushDef2\n{\n"); + } + else { + MemFile_fprintf(pMemFile, "{\nBrushDef\n{\n"); + } + + // brush epairs + // brush epairs + int count = b->epairs.GetNumKeyVals(); + for (int j = 0; j < count; j++) { + MemFile_fprintf(pMemFile, "\"%s\" \"%s\"\n", b->epairs.GetKeyVal(j)->GetKey().c_str(), b->epairs.GetKeyVal(j)->GetValue().c_str()); + } + + for (fa = b->brush_faces; fa; fa = fa->next) { + if (newFormat) { + // save planepts + idPlane plane; + + if (fa->dirty) { + fa->planepts[0] -= origin; + fa->planepts[1] -= origin; + fa->planepts[2] -= origin; + plane.FromPoints( fa->planepts[0], fa->planepts[1], fa->planepts[2], false ); + fa->planepts[0] += origin; + fa->planepts[1] += origin; + fa->planepts[2] += origin; + } else { + plane = fa->originalPlane; + } + + MemFile_fprintf(pMemFile, " ( "); + for (i = 0; i < 4; i++) { + if (plane[i] == (int)plane[i]) { + MemFile_fprintf(pMemFile, "%i ", (int)plane[i]); + } + else { + MemFile_fprintf(pMemFile, "%f ", plane[i]); + } + } + + MemFile_fprintf(pMemFile, ") "); + } + else { + for (i = 0; i < 3; i++) { + MemFile_fprintf(pMemFile, "( "); + for (int j = 0; j < 3; j++) { + if (fa->planepts[i][j] == static_cast(fa->planepts[i][j])) { + MemFile_fprintf(pMemFile, "%i ", static_cast(fa->planepts[i][j])); + } + else { + MemFile_fprintf(pMemFile, "%f ", fa->planepts[i][j]); + } + } + + MemFile_fprintf(pMemFile, ") "); + } + } + + // save texture coordinates + MemFile_fprintf(pMemFile, "( ( "); + for (i = 0; i < 3; i++) { + if (fa->brushprimit_texdef.coords[0][i] == static_cast(fa->brushprimit_texdef.coords[0][i])) { + MemFile_fprintf(pMemFile, "%i ", static_cast(fa->brushprimit_texdef.coords[0][i])); + } + else { + MemFile_fprintf(pMemFile, "%f ", fa->brushprimit_texdef.coords[0][i]); + } + } + + MemFile_fprintf(pMemFile, ") ( "); + for (i = 0; i < 3; i++) { + if (fa->brushprimit_texdef.coords[1][i] == static_cast(fa->brushprimit_texdef.coords[1][i])) { + MemFile_fprintf(pMemFile, "%i ", static_cast(fa->brushprimit_texdef.coords[1][i])); + } + else { + MemFile_fprintf(pMemFile, "%f ", fa->brushprimit_texdef.coords[1][i]); + } + } + + MemFile_fprintf(pMemFile, ") ) "); + + // save texture attribs + char *pName = strlen(fa->texdef.name) > 0 ? fa->texdef.name : "unnamed"; + MemFile_fprintf(pMemFile, "\"%s\" ", pName); + MemFile_fprintf(pMemFile, "%i %i %i\n", 0, 0, 0); + } + + MemFile_fprintf(pMemFile, "}\n}\n"); + } + else { + // old brushes format also handle surface properties plugin + MemFile_fprintf(pMemFile, "{\n"); + for (fa = b->brush_faces; fa; fa = fa->next) { + for (i = 0; i < 3; i++) { + MemFile_fprintf(pMemFile, "( "); + for (int j = 0; j < 3; j++) { + if (fa->planepts[i][j] == static_cast(fa->planepts[i][j])) { + MemFile_fprintf(pMemFile, "%i ", static_cast(fa->planepts[i][j])); + } + else { + MemFile_fprintf(pMemFile, "%f ", fa->planepts[i][j]); + } + } + + MemFile_fprintf(pMemFile, ") "); + } + + pname = fa->texdef.name; + if (pname[0] == 0) { + pname = "unnamed"; + } + + MemFile_fprintf + ( + pMemFile, + "%s %i %i %i ", + pname, + (int)fa->texdef.shift[0], + (int)fa->texdef.shift[1], + (int)fa->texdef.rotate + ); + + if (fa->texdef.scale[0] == (int)fa->texdef.scale[0]) { + MemFile_fprintf(pMemFile, "%i ", (int)fa->texdef.scale[0]); + } + else { + MemFile_fprintf(pMemFile, "%f ", (float)fa->texdef.scale[0]); + } + + if (fa->texdef.scale[1] == (int)fa->texdef.scale[1]) { + MemFile_fprintf(pMemFile, "%i", (int)fa->texdef.scale[1]); + } + else { + MemFile_fprintf(pMemFile, "%f", (float)fa->texdef.scale[1]); + } + + MemFile_fprintf(pMemFile, " %i %i %i", 0, 0, 0); + + MemFile_fprintf(pMemFile, "\n"); + } + + MemFile_fprintf(pMemFile, "}\n"); + } +} + +/* +================ +Brush_Create + + Create non-textured blocks for entities The brush is NOT linked to any list +================ +*/ +brush_t *Brush_Create(idVec3 mins, idVec3 maxs, texdef_t *texdef) { + int i, j; + idVec3 pts[4][2]; + face_t *f; + brush_t *b; + + // + // brush primitive mode : convert texdef to brushprimit_texdef ? most of the time + // texdef is empty + // + for (i = 0; i < 3; i++) { + if (maxs[i] < mins[i]) { + Error("Brush_InitSolid: backwards"); + } + } + + b = Brush_Alloc(); + + pts[0][0][0] = mins[0]; + pts[0][0][1] = mins[1]; + + pts[1][0][0] = mins[0]; + pts[1][0][1] = maxs[1]; + + pts[2][0][0] = maxs[0]; + pts[2][0][1] = maxs[1]; + + pts[3][0][0] = maxs[0]; + pts[3][0][1] = mins[1]; + + for (i = 0; i < 4; i++) { + pts[i][0][2] = mins[2]; + pts[i][1][0] = pts[i][0][0]; + pts[i][1][1] = pts[i][0][1]; + pts[i][1][2] = maxs[2]; + } + + for (i = 0; i < 4; i++) { + f = Face_Alloc(); + f->texdef = *texdef; + f->next = b->brush_faces; + b->brush_faces = f; + j = (i + 1) % 4; + + VectorCopy(pts[j][1], f->planepts[0]); + VectorCopy(pts[i][1], f->planepts[1]); + VectorCopy(pts[i][0], f->planepts[2]); + } + + f = Face_Alloc(); + f->texdef = *texdef; + f->next = b->brush_faces; + b->brush_faces = f; + + VectorCopy(pts[0][1], f->planepts[0]); + VectorCopy(pts[1][1], f->planepts[1]); + VectorCopy(pts[2][1], f->planepts[2]); + + f = Face_Alloc(); + f->texdef = *texdef; + f->next = b->brush_faces; + b->brush_faces = f; + + VectorCopy(pts[2][0], f->planepts[0]); + VectorCopy(pts[1][0], f->planepts[1]); + VectorCopy(pts[0][0], f->planepts[2]); + + return b; +} + +/* +============= +Brush_Scale +============= +*/ +void Brush_Scale(brush_t* b) { + for ( face_t *f = b->brush_faces; f; f = f->next ) { + for ( int i = 0; i < 3; i++ ) { + VectorScale( f->planepts[i], g_qeglobals.d_gridsize, f->planepts[i] ); + } + } +} + +/* +================ +Brush_CreatePyramid + + Create non-textured pyramid for light entities The brush is NOT linked to any list +================ +*/ +brush_t *Brush_CreatePyramid(idVec3 mins, idVec3 maxs, texdef_t *texdef) { + // ++timo handle new brush primitive ? return here ?? + return Brush_Create(mins, maxs, texdef); + + int i; + for (i = 0; i < 3; i++) { + if (maxs[i] < mins[i]) { + Error("Brush_InitSolid: backwards"); + } + } + + brush_t *b = Brush_Alloc(); + + idVec3 corners[4]; + + float fMid = idMath::Rint(mins[2] + (idMath::Rint((maxs[2] - mins[2]) / 2))); + + corners[0][0] = mins[0]; + corners[0][1] = mins[1]; + corners[0][2] = fMid; + + corners[1][0] = mins[0]; + corners[1][1] = maxs[1]; + corners[1][2] = fMid; + + corners[2][0] = maxs[0]; + corners[2][1] = maxs[1]; + corners[2][2] = fMid; + + corners[3][0] = maxs[0]; + corners[3][1] = mins[1]; + corners[3][2] = fMid; + + idVec3 top, bottom; + + top[0] = idMath::Rint(mins[0] + ((maxs[0] - mins[0]) / 2)); + top[1] = idMath::Rint(mins[1] + ((maxs[1] - mins[1]) / 2)); + top[2] = idMath::Rint(maxs[2]); + + VectorCopy(top, bottom); + bottom[2] = mins[2]; + + // sides + for (i = 0; i < 4; i++) { + face_t *f = Face_Alloc(); + f->texdef = *texdef; + f->next = b->brush_faces; + b->brush_faces = f; + + int j = (i + 1) % 4; + + VectorCopy(top, f->planepts[0]); + VectorCopy(corners[i], f->planepts[1]); + VectorCopy(corners[j], f->planepts[2]); + + f = Face_Alloc(); + f->texdef = *texdef; + f->next = b->brush_faces; + b->brush_faces = f; + + VectorCopy(bottom, f->planepts[2]); + VectorCopy(corners[i], f->planepts[1]); + VectorCopy(corners[j], f->planepts[0]); + } + + return b; +} + +/* +================ +Brush_MakeSided + + Makes the current brush have the given number of 2d sides +================ +*/ +void Brush_MakeSided(int sides) { + int i, axis; + idVec3 mins, maxs; + brush_t *b; + texdef_t *texdef; + face_t *f; + idVec3 mid; + float width; + float sv, cv; + + if (sides < 3) { + Sys_Status("Bad sides number", 0); + return; + } + + if (sides >= MAX_POINTS_ON_WINDING - 4) { + Sys_Status("too many sides.\n"); + return; + } + + if (!QE_SingleBrush()) { + Sys_Status("Must have a single brush selected", 0); + return; + } + + b = selected_brushes.next; + VectorCopy(b->mins, mins); + VectorCopy(b->maxs, maxs); + texdef = &g_qeglobals.d_texturewin.texdef; + + Brush_Free(b); + + if (g_pParentWnd->ActiveXY()) { + switch (g_pParentWnd->ActiveXY()->GetViewType()) + { + case XY: + axis = 2; + break; + case XZ: + axis = 1; + break; + case YZ: + axis = 0; + break; + } + } + else { + axis = 2; + } + + // find center of brush + width = 8; + for (i = 0; i < 3; i++) { + mid[i] = (maxs[i] + mins[i]) * 0.5f; + if (i == axis) { + continue; + } + + if ((maxs[i] - mins[i]) * 0.5f > width) { + width = (maxs[i] - mins[i]) * 0.5f; + } + } + + b = Brush_Alloc(); + + // create top face + f = Face_Alloc(); + f->texdef = *texdef; + f->next = b->brush_faces; + b->brush_faces = f; + + f->planepts[2][(axis + 1) % 3] = mins[(axis + 1) % 3]; + f->planepts[2][(axis + 2) % 3] = mins[(axis + 2) % 3]; + f->planepts[2][axis] = maxs[axis]; + f->planepts[1][(axis + 1) % 3] = maxs[(axis + 1) % 3]; + f->planepts[1][(axis + 2) % 3] = mins[(axis + 2) % 3]; + f->planepts[1][axis] = maxs[axis]; + f->planepts[0][(axis + 1) % 3] = maxs[(axis + 1) % 3]; + f->planepts[0][(axis + 2) % 3] = maxs[(axis + 2) % 3]; + f->planepts[0][axis] = maxs[axis]; + + // create bottom face + f = Face_Alloc(); + f->texdef = *texdef; + f->next = b->brush_faces; + b->brush_faces = f; + + f->planepts[0][(axis + 1) % 3] = mins[(axis + 1) % 3]; + f->planepts[0][(axis + 2) % 3] = mins[(axis + 2) % 3]; + f->planepts[0][axis] = mins[axis]; + f->planepts[1][(axis + 1) % 3] = maxs[(axis + 1) % 3]; + f->planepts[1][(axis + 2) % 3] = mins[(axis + 2) % 3]; + f->planepts[1][axis] = mins[axis]; + f->planepts[2][(axis + 1) % 3] = maxs[(axis + 1) % 3]; + f->planepts[2][(axis + 2) % 3] = maxs[(axis + 2) % 3]; + f->planepts[2][axis] = mins[axis]; + + for (i = 0; i < sides; i++) { + f = Face_Alloc(); + f->texdef = *texdef; + f->next = b->brush_faces; + b->brush_faces = f; + + sv = sin(i * 3.14159265 * 2 / sides); + cv = cos(i * 3.14159265 * 2 / sides); + + f->planepts[0][(axis + 1) % 3] = floor(mid[(axis + 1) % 3] + width * cv + 0.5f); + f->planepts[0][(axis + 2) % 3] = floor(mid[(axis + 2) % 3] + width * sv + 0.5f); + f->planepts[0][axis] = mins[axis]; + + f->planepts[1][(axis + 1) % 3] = f->planepts[0][(axis + 1) % 3]; + f->planepts[1][(axis + 2) % 3] = f->planepts[0][(axis + 2) % 3]; + f->planepts[1][axis] = maxs[axis]; + + f->planepts[2][(axis + 1) % 3] = floor(f->planepts[0][(axis + 1) % 3] - width * sv + 0.5f); + f->planepts[2][(axis + 2) % 3] = floor(f->planepts[0][(axis + 2) % 3] + width * cv + 0.5f); + f->planepts[2][axis] = maxs[axis]; + } + + Brush_AddToList(b, &selected_brushes); + + Entity_LinkBrush(world_entity, b); + + Brush_Build(b); + + Sys_UpdateWindows(W_ALL); +} + +/* +================ +Brush_Free + + Frees the brush with all of its faces and display list. + Unlinks the brush from whichever chain it is in. + Decrements the owner entity's brushcount. + Removes owner entity if this was the last brush unless owner is the world. + Removes from groups + + set bRemoveNode to false to avoid trying to delete the item in group view tree control +================ +*/ +void Brush_Free(brush_t *b, bool bRemoveNode) { + face_t *f, *next; + + // free the patch if it's there + if ( b->pPatch ) { + Patch_Delete(b->pPatch); + } + + // free faces + for ( f = b->brush_faces; f; f = next ) { + next = f->next; + Face_Free(f); + } + + b->epairs.Clear(); + + // unlink from active/selected list + if ( b->next ) { + Brush_RemoveFromList(b); + } + + // unlink from entity list + if ( b->onext ) { + Entity_UnlinkBrush(b); + } + + delete b; +} + +/* +================ +Face_MemorySize + + returns the size in memory of the face +================ +*/ +int Face_MemorySize(face_t *f) { + int size = 0; + + if ( f->face_winding ) { + size += sizeof( idWinding ) + f->face_winding->GetNumPoints() * sizeof( (f->face_winding)[0] ); + } + size += sizeof( face_t ); + return size; +} + +/* +================ +Brush_MemorySize + + returns the size in memory of the brush +================ +*/ +int Brush_MemorySize( brush_t *b ) { + face_t *f; + int size = 0; + if ( b->pPatch ) { + size += Patch_MemorySize( b->pPatch ); + } + + for ( f = b->brush_faces; f; f = f->next ) { + size += Face_MemorySize(f); + } + + size += sizeof( brush_t ) + b->epairs.Size(); + return size; +} + +/* +================ +Brush_Clone + + does not add the brush to any lists +================ +*/ +brush_t *Brush_Clone(brush_t *b) { + brush_t *n = NULL; + face_t *f, *nf; + + if (b->pPatch) { + patchMesh_t *p = Patch_Duplicate(b->pPatch); + Brush_RemoveFromList(p->pSymbiot); + Entity_UnlinkBrush(p->pSymbiot); + n = p->pSymbiot; + } + else { + n = Brush_Alloc(); + n->numberId = g_nBrushId++; + n->owner = b->owner; + n->lightColor = b->lightColor; + n->lightEnd = b->lightEnd; + n->lightOffset = b->lightOffset; + n->lightRadius = b->lightRadius; + n->lightRight = b->lightRight; + n->lightStart = b->lightStart; + n->lightTarget = b->lightTarget; + n->lightCenter = b->lightCenter; + n->lightTexture = b->lightTexture; + n->lightUp = b->lightUp; + n->modelHandle = b->modelHandle; + n->pointLight = b->pointLight; + for (f = b->brush_faces; f; f = f->next) { + nf = Face_Clone(f); + nf->next = n->brush_faces; + n->brush_faces = nf; + } + } + + return n; +} + +/* +================ +Brush_FullClone + + Used by Undo. + Makes an exact copy of the brush. + Does NOT add the new brush to any lists. +================ +*/ +brush_t *Brush_FullClone(brush_t *b) { + brush_t *n = NULL; + face_t *f, *nf, *f2, *nf2; + int j; + + if (b->pPatch) { + patchMesh_t *p = Patch_Duplicate(b->pPatch); + Brush_RemoveFromList(p->pSymbiot); + Entity_UnlinkBrush(p->pSymbiot); + n = p->pSymbiot; + n->owner = b->owner; + Brush_Build(n); + } + else { + n = Brush_Alloc(); + n->numberId = g_nBrushId++; + n->owner = b->owner; + n->lightColor = b->lightColor; + n->lightEnd = b->lightEnd; + n->lightOffset = b->lightOffset; + n->lightRadius = b->lightRadius; + n->lightRight = b->lightRight; + n->lightStart = b->lightStart; + n->lightTarget = b->lightTarget; + n->lightCenter = b->lightCenter; + n->lightTexture = b->lightTexture; + n->lightUp = b->lightUp; + n->modelHandle = b->modelHandle; + n->pointLight = b->pointLight; + VectorCopy(b->mins, n->mins); + VectorCopy(b->maxs, n->maxs); + for (f = b->brush_faces; f; f = f->next) { + if (f->original) { + continue; + } + + nf = Face_FullClone(f); + nf->next = n->brush_faces; + n->brush_faces = nf; + + // copy all faces that have the original set to this face + for (f2 = b->brush_faces; f2; f2 = f2->next) { + if (f2->original == f) { + nf2 = Face_FullClone(f2); + nf2->next = n->brush_faces; + n->brush_faces = nf2; + + // set original + nf2->original = nf; + } + } + } + + for (nf = n->brush_faces; nf; nf = nf->next) { + Face_SetColor( n, nf, 1.0f ); + if (nf->face_winding) { + if (g_qeglobals.m_bBrushPrimitMode) { + EmitBrushPrimitTextureCoordinates(nf, nf->face_winding); + } + else { + for (j = 0; j < nf->face_winding->GetNumPoints(); j++) { + EmitTextureCoordinates( (*nf->face_winding)[j], nf->d_texture, nf ); + } + } + } + } + } + + return n; +} + +extern bool GetMatrixForKey(entity_t *ent, const char *key, idMat3 &mat); +extern bool Patch_Intersect(patchMesh_t *pm, idVec3 origin, idVec3 direction , float &scale); +extern bool RayIntersectsTri + ( + const idVec3 &origin, + const idVec3 &direction, + const idVec3 &vert0, + const idVec3 &vert1, + const idVec3 &vert2, + float &scale + ); + + +/* +================ +RotateVector +================ +*/ +void RotateVector(idVec3 &v, idVec3 origin, float a, float c, float s) { + float x = v[0]; + float y = v[1]; + if (a) { + float x2 = (((x - origin[0]) * c) - ((y - origin[1]) * s)) + origin[0]; + float y2 = (((x - origin[0]) * s) + ((y - origin[1]) * c)) + origin[1]; + x = x2; + y = y2; + } + v[0] = x; + v[1] = y; +} +/* +================ +Brush_ModelIntersect +================ +*/ + +bool Brush_ModelIntersect(brush_t *b, idVec3 origin, idVec3 dir,float &scale) { + idRenderModel *model = b->modelHandle; + idRenderModel *md5; + + if ( !model ) + model = b->owner->eclass->entityModel; + + scale = 0; + if (model) { + if ( model->IsDynamicModel() != DM_STATIC ) { + if ( dynamic_cast( model ) ) { + // take care of animated models + md5 = b->owner->eclass->entityModel; + + const char *classname = ValueForKey( b->owner, "classname" ); + if (stricmp(classname, "func_static") == 0) { + classname = ValueForKey(b->owner, "animclass"); + } + const char *anim = ValueForKey( b->owner, "anim" ); + int frame = IntForKey( b->owner, "frame" ) + 1; + if ( frame < 1 ) { + frame = 1; + } + if ( !anim || !anim[ 0 ] ) { + anim = "idle"; + } + model = gameEdit->ANIM_CreateMeshForAnim( md5, classname, anim, frame, false ); + if ( !model ) { + model = renderModelManager->DefaultModel(); + } + } + } + + bool matrix = false; + idMat3 mat; + float a, s, c; + if (GetMatrixForKey(b->owner, "rotation", mat)) { + matrix = true; + } else { + a = FloatForKey(b->owner, "angle"); + if (a) { + s = sin( DEG2RAD( a ) ); + c = cos( DEG2RAD( a ) ); + } + else { + s = c = 0; + } + } + + for (int i = 0; i < model->NumSurfaces() ; i++) { + const modelSurface_t *surf = model->Surface( i ); + srfTriangles_t *tri = surf->geometry; + for (int j = 0; j < tri->numIndexes; j += 3) { + idVec3 v1, v2, v3; + v1 = tri->verts[tri->indexes[j]].xyz; + v2 = tri->verts[tri->indexes[j + 1]].xyz; + v3 = tri->verts[tri->indexes[j + 2]].xyz; + + if (matrix) { + v1 *= b->owner->rotation; + v1 += b->owner->origin; + v2 *= b->owner->rotation; + v2 += b->owner->origin; + v3 *= b->owner->rotation; + v3 += b->owner->origin; + } else { + v1 += b->owner->origin; + v2 += b->owner->origin; + v3 += b->owner->origin; + RotateVector(v1, b->owner->origin, a, c, s); + RotateVector(v2, b->owner->origin, a, c, s); + RotateVector(v3, b->owner->origin, a, c, s); + } + + if (RayIntersectsTri(origin, dir, v1, v2, v3,scale)) { + return true; + } + } + } + } + + return false; +} + +face_t *Brush_Ray(idVec3 origin, idVec3 dir, brush_t *b, float *dist, bool testPrimitive) { + face_t *f, *firstface = NULL; + idVec3 p1, p2; + float frac, d1, d2; + int i; + float scale = HUGE_DISTANCE * 2; + VectorCopy(origin, p1); + for (i = 0; i < 3; i++) { + p2[i] = p1[i] + dir[i] * HUGE_DISTANCE * 2; + } + + for (f = b->brush_faces; f; f = f->next) { + d1 = DotProduct(p1, f->plane) + f->plane[3]; + d2 = DotProduct(p2, f->plane) + f->plane[3]; + if (d1 >= 0 && d2 >= 0) { + *dist = 0; + return NULL; // ray is on front side of face + } + + if (d1 <= 0 && d2 <= 0) { + continue; + } + + // clip the ray to the plane + frac = d1 / (d1 - d2); + if (d1 > 0) { + firstface = f; + for (i = 0; i < 3; i++) { + p1[i] = p1[i] + frac * (p2[i] - p1[i]); + } + } + else { + for (i = 0; i < 3; i++) { + p2[i] = p1[i] + frac * (p2[i] - p1[i]); + } + } + } + + // find distance p1 is along dir + VectorSubtract(p1, origin, p1); + d1 = DotProduct(p1, dir); + + if (testPrimitive && !g_PrefsDlg.m_selectByBoundingBrush) { + if (b->pPatch) { + if (!Patch_Intersect(b->pPatch, origin, dir, scale)) { + *dist = 0; + return NULL; + } + } + else if ( b->modelHandle != NULL && dynamic_cast< idRenderModelLiquid*> ( b->modelHandle ) == NULL ) { + if (!Brush_ModelIntersect(b, origin, dir, scale)) { + *dist = 0; + return NULL; + } + } + } + + *dist = d1; + return firstface; +} + +/* +================ +Brush_Point +================ +*/ +face_t *Brush_Point(idVec3 origin, brush_t *b) { + face_t *f; + float d1; + + for (f = b->brush_faces; f; f = f->next) { + d1 = DotProduct(origin, f->plane) + f->plane[3]; + if (d1 > 0) { + return NULL; // point is on front side of face + } + } + + return b->brush_faces; +} + +/* +================ +Brush_AddToList +================ +*/ +void Brush_AddToList(brush_t *b, brush_t *list) { + if (b->next || b->prev) { + Error("Brush_AddToList: allready linked"); + } + + if (list == &selected_brushes || list == &active_brushes) { + if (b->pPatch && list == &selected_brushes) { + Patch_Select(b->pPatch); + } + } + + b->list = list; + b->next = list->next; + list->next->prev = b; + list->next = b; + b->prev = list; + +} + +/* +================ +Brush_RemoveFromList +================ +*/ +void Brush_RemoveFromList(brush_t *b) { + if (!b->next || !b->prev) { + Error("Brush_RemoveFromList: not linked"); + } + + if (b->pPatch) { + Patch_Deselect(b->pPatch); + + // Patch_Deselect(b->nPatchID); + } + + b->list = NULL; + b->next->prev = b->prev; + b->prev->next = b->next; + b->next = b->prev = NULL; +} + +/* +================ +SetFaceTexdef + + Doesn't set the curve flags. + NOTE: never trust f->d_texture here, f->texdef and f->d_texture are out of sync when + called by Brush_SetTexture use Texture_ForName() to find the right shader + FIXME: send the right shader ( qtexture_t * ) in the parameters ? + TTimo: surface plugin, added an IPluginTexdef* parameter if not NULL, + get ->Copy() of it into the face ( and remember to hook ) if NULL, ask for a default +================ +*/ +void SetFaceTexdef( brush_t *b, face_t *f, texdef_t *texdef, brushprimit_texdef_t *brushprimit_texdef, bool bFitScale ) { + + if (g_qeglobals.m_bBrushPrimitMode) { + f->texdef = *texdef; + ConvertTexMatWithQTexture(brushprimit_texdef, NULL, &f->brushprimit_texdef, Texture_ForName(f->texdef.name)); + } + else if (bFitScale) { + f->texdef = *texdef; + + // fit the scaling of the texture on the actual plane + idVec3 p1, p2, p3; // absolute coordinates + + // compute absolute coordinates + ComputeAbsolute(f, p1, p2, p3); + + // compute the scale + idVec3 vx, vy; + VectorSubtract(p2, p1, vx); + vx.Normalize(); + VectorSubtract(p3, p1, vy); + vy.Normalize(); + + // assign scale + VectorScale(vx, texdef->scale[0], vx); + VectorScale(vy, texdef->scale[1], vy); + VectorAdd(p1, vx, p2); + VectorAdd(p1, vy, p3); + + // compute back shift scale rot + AbsoluteToLocal(f->plane, f, p1, p2, p3); + } + else { + f->texdef = *texdef; + } + +} + +/* +================ +Brush_SetTexture +================ +*/ +void Brush_SetTexture(brush_t *b, texdef_t *texdef, brushprimit_texdef_t *brushprimit_texdef, bool bFitScale) { + if (b->pPatch) { + Patch_SetTexture(b->pPatch, texdef); + } + else { + for (face_t * f = b->brush_faces; f; f = f->next) { + SetFaceTexdef(b, f, texdef, brushprimit_texdef, bFitScale); + } + + Brush_Build(b); + } +} + +/* +==================== +Brush_SetTextureName +==================== +*/ +void Brush_SetTextureName(brush_t *b, const char *name) { + if (b->pPatch) { + Patch_SetTextureName(b->pPatch, name); + } + else { + for (face_t * f = b->brush_faces; f; f = f->next) { + f->texdef.SetName(name); + } + Brush_Build(b); + } +} + +/* +================ +ClipLineToFace +================ +*/ +bool ClipLineToFace(idVec3 &p1, idVec3 &p2, face_t *f) { + float d1, d2, fr; + int i; + float *v; + + d1 = DotProduct(p1, f->plane) + f->plane[3]; + d2 = DotProduct(p2, f->plane) + f->plane[3]; + + if (d1 >= 0 && d2 >= 0) { + return false; // totally outside + } + + if (d1 <= 0 && d2 <= 0) { + return true; // totally inside + } + + fr = d1 / (d1 - d2); + + if (d1 > 0) { + v = p1.ToFloatPtr(); + } + else { + v = p2.ToFloatPtr(); + } + + for (i = 0; i < 3; i++) { + v[i] = p1[i] + fr * (p2[i] - p1[i]); + } + + return true; +} + +/* +================ +AddPlanept +================ +*/ +int AddPlanept(idVec3 *f) { + int i; + + for (i = 0; i < g_qeglobals.d_num_move_points; i++) { + if (g_qeglobals.d_move_points[i] == f) { + return 0; + } + } + + if (g_qeglobals.d_num_move_points < MAX_MOVE_POINTS) { + g_qeglobals.d_move_points[g_qeglobals.d_num_move_points++] = f; + } else { + Sys_Status("Trying to move too many points\n"); + return 0; + } + + return 1; +} + +/* +================ +AddMovePlane +================ +*/ +void AddMovePlane( idPlane *p ) { + + for (int i = 0; i < g_qeglobals.d_num_move_planes; i++) { + if (g_qeglobals.d_move_planes[i] == p) { + return; + } + } + + if (g_qeglobals.d_num_move_planes < MAX_MOVE_PLANES) { + g_qeglobals.d_move_planes[g_qeglobals.d_num_move_planes++] = p; + } else { + Sys_Status("Trying to move too many planes\n"); + } + +} + +/* +================ +Brush_SelectFaceForDragging + + Adds the faces planepts to move_points, and rotates and adds the planepts of adjacent face if shear is set +================ +*/ +void Brush_SelectFaceForDragging(brush_t *b, face_t *f, bool shear) { + int i; + face_t *f2; + idWinding *w; + float d; + brush_t *b2; + int c; + + if (b->owner->eclass->fixedsize || EntityHasModel(b->owner)) { + return; + } + + c = 0; + for (i = 0; i < 3; i++) { + c += AddPlanept(&f->planepts[i]); + } + + //AddMovePlane(&f->plane); + + if (c == 0) { + return; // allready completely added + } + + // select all points on this plane in all brushes the selection + for (b2 = selected_brushes.next; b2 != &selected_brushes; b2 = b2->next) { + if (b2 == b) { + continue; + } + + for (f2 = b2->brush_faces; f2; f2 = f2->next) { + for (i = 0; i < 3; i++) { + if (idMath::Fabs(DotProduct(f2->planepts[i], f->plane) + f->plane[3]) > ON_EPSILON) { + break; + } + } + + if (i == 3) { // move this face as well + Brush_SelectFaceForDragging(b2, f2, shear); + break; + } + } + } + + // + // if shearing, take all the planes adjacent to selected faces and rotate their + // points so the edge clipped by a selcted face has two of the points + // + if (!shear) { + return; + } + + for (f2 = b->brush_faces; f2; f2 = f2->next) { + if (f2 == f) { + continue; + } + + w = Brush_MakeFaceWinding(b, f2, false); + if (!w) { + continue; + } + + // any points on f will become new control points + for (i = 0; i < w->GetNumPoints(); i++) { + d = DotProduct( (*w)[i], f->plane ) + f->plane[3]; + if (d > -ON_EPSILON && d < ON_EPSILON) { + break; + } + } + + // if none of the points were on the plane, leave it alone + if (i != w->GetNumPoints()) { + if (i == 0) { // see if the first clockwise point was the + /// + /// last point on the winding + d = DotProduct( (*w)[w->GetNumPoints() - 1], f->plane ) + f->plane[3]; + if (d > -ON_EPSILON && d < ON_EPSILON) { + i = w->GetNumPoints() - 1; + } + } + + AddPlanept(&f2->planepts[0]); + //AddMovePlane(&f2->plane); + + VectorCopy((*w)[i], f2->planepts[0]); + if (++i == w->GetNumPoints()) { + i = 0; + } + + // see if the next point is also on the plane + d = DotProduct( (*w)[i], f->plane ) + f->plane[3]; + if (d > -ON_EPSILON && d < ON_EPSILON) { + AddPlanept(&f2->planepts[1]); + } + + VectorCopy( (*w)[i], f2->planepts[1] ); + if (++i == w->GetNumPoints()) { + i = 0; + } + + // the third point is never on the plane + VectorCopy( (*w)[i], f2->planepts[2] ); + } + + delete w; + } +} + +/* +================ +Brush_SideSelect + + The mouse click did not hit the brush, so grab one or more side planes for dragging. +================ +*/ +void Brush_SideSelect(brush_t *b, idVec3 origin, idVec3 dir, bool shear) { + face_t *f, *f2; + idVec3 p1, p2; + + if (g_moveOnly) { + return; + } + + // if (b->pPatch) return; Patch_SideSelect(b->nPatchID, origin, dir); + for (f = b->brush_faces; f; f = f->next) { + VectorCopy(origin, p1); + VectorMA(origin, MAX_WORLD_SIZE, dir, p2); + + for (f2 = b->brush_faces; f2; f2 = f2->next) { + if (f2 == f) { + continue; + } + + ClipLineToFace(p1, p2, f2); + } + + if (f2) { + continue; + } + + if ( p1.Compare( origin ) ) { + continue; + } + + if (ClipLineToFace(p1, p2, f)) { + continue; + } + + Brush_SelectFaceForDragging(b, f, shear); + } +} + +extern void UpdateSelectablePoint(brush_t *b, idVec3 v, int type); +extern void AddSelectablePoint(brush_t *b, idVec3 v, int type, bool priority); +extern void ClearSelectablePoints(brush_t *b); + +/* +================ +Brush_TransformedPoint +================ +*/ +extern void VectorSnapGrid(idVec3 &v); + +idMat3 Brush_RotationMatrix(brush_t *b) { + idMat3 mat; + mat.Identity(); + if (!GetMatrixForKey(b->owner, "light_rotation", mat)) { + GetMatrixForKey(b->owner, "rotation", mat); + } + return mat; +} + +idVec3 Brush_TransformedPoint(brush_t *b, const idVec3 &in) { + idVec3 out = in; + out -= b->owner->origin; + out *= Brush_RotationMatrix(b); + out += b->owner->origin; + return out; +} +/* +================ +Brush_UpdateLightPoints +================ +*/ +void Brush_UpdateLightPoints(brush_t *b, const idVec3 &offset) { + + if (!(b->owner->eclass->nShowFlags & ECLASS_LIGHT)) { + if (b->modelHandle) { + g_bScreenUpdates = false; + g_pParentWnd->GetCamera()->BuildEntityRenderState(b->owner, true); + g_bScreenUpdates = true; + } + return; + } + + if (b->entityModel) { + return; + } + + idVec3 vCenter; + idVec3 *origin = (b->trackLightOrigin) ? &b->owner->lightOrigin : &b->owner->origin; + + if (!GetVectorForKey(b->owner, "_color", b->lightColor)) { + b->lightColor[0] = b->lightColor[1] = b->lightColor[2] = 1; + } + + const char *str = ValueForKey(b->owner, "texture"); + b->lightTexture = -1; + if (str && strlen(str) > 0) { + const idMaterial *q = Texture_LoadLight(str); + if (q) { + b->lightTexture = q->GetEditorImage()->texnum; + } + } + + str = ValueForKey(b->owner, "light_right"); + if (str && *str) { + idVec3 vRight, vUp, vTarget, vTemp; + + if (GetVectorForKey(b->owner, "light_start", b->lightStart)) { + b->startEnd = true; + if (!GetVectorForKey(b->owner, "light_end", b->lightEnd)) { + GetVectorForKey(b->owner, "light_target", b->lightEnd); + } + + + VectorAdd(b->lightEnd, *origin, b->lightEnd); + VectorAdd(b->lightStart, *origin, b->lightStart); + VectorAdd(b->lightStart, offset, b->lightStart); + } + else { + b->startEnd = false; + } + + GetVectorForKey(b->owner, "light_right", vRight); + GetVectorForKey(b->owner, "light_up", vUp); + GetVectorForKey(b->owner, "light_target", vTarget); + if (offset.x || offset.y || offset.z) { + CString str; + VectorAdd(vTarget, offset, vTarget); + SetKeyVec3(b->owner, "light_target", vTarget); + } + + VectorAdd(vTarget, *origin, b->lightTarget); + VectorAdd(b->lightTarget, vRight, b->lightRight); + VectorAdd(b->lightTarget, vUp, b->lightUp); + + UpdateSelectablePoint(b, Brush_TransformedPoint(b, b->lightUp), LIGHT_UP); + UpdateSelectablePoint(b, Brush_TransformedPoint(b, b->lightRight), LIGHT_RIGHT); + UpdateSelectablePoint(b, Brush_TransformedPoint(b, b->lightTarget), LIGHT_TARGET); + UpdateSelectablePoint(b, Brush_TransformedPoint(b, b->lightStart), LIGHT_START); + UpdateSelectablePoint(b, Brush_TransformedPoint(b, b->lightEnd), LIGHT_END); + b->pointLight = false; + } + else { + b->pointLight = true; + + if (GetVectorForKey(b->owner, "light_center", vCenter)) { + + if (offset.x || offset.y || offset.z) { + CString str; + VectorAdd(vCenter, offset, vCenter); + SetKeyVec3(b->owner, "light_center", vCenter); + } + + VectorAdd(vCenter, *origin, b->lightCenter); + UpdateSelectablePoint(b, b->lightCenter, LIGHT_CENTER); + } + + if (!GetVectorForKey(b->owner, "light_radius", b->lightRadius)) { + float f = FloatForKey(b->owner, "light"); + if (f == 0) { + f = 300; + } + + b->lightRadius[0] = b->lightRadius[1] = b->lightRadius[2] = f; + } + else { + } + } + + g_bScreenUpdates = false; + g_pParentWnd->GetCamera()->BuildEntityRenderState(b->owner, true); + g_bScreenUpdates = true; + +} + +/* +================ +Brush_BuildWindings +================ +*/ +void Brush_BuildWindings(brush_t *b, bool bSnap, bool keepOnPlaneWinding, bool updateLights, bool makeFacePlanes) { + idWinding *w; + face_t *face; + float v; + + // clear the mins/maxs bounds + b->mins[0] = b->mins[1] = b->mins[2] = 999999; + b->maxs[0] = b->maxs[1] = b->maxs[2] = -999999; + + if (makeFacePlanes) { + Brush_MakeFacePlanes(b); + } + + face = b->brush_faces; + + float fCurveColor = 1.0f; + + for (; face; face = face->next) { + int i, j; + delete face->face_winding; + w = face->face_winding = Brush_MakeFaceWinding(b, face, keepOnPlaneWinding); + face->d_texture = Texture_ForName(face->texdef.name); + + if (!w) { + continue; + } + + for (i = 0; i < w->GetNumPoints(); i++) { + // add to bounding box + for (j = 0; j < 3; j++) { + v = (*w)[i][j]; + if (v > b->maxs[j]) { + b->maxs[j] = v; + } + + if (v < b->mins[j]) { + b->mins[j] = v; + } + } + } + + // setup s and t vectors, and set color if (!g_PrefsDlg.m_bGLLighting) { + if (makeFacePlanes) { + Face_SetColor(b, face, fCurveColor); + + // } + fCurveColor -= 0.1f; + if ( fCurveColor <= 0.0f ) { + fCurveColor = 1.0f; + } + + // computing ST coordinates for the windings + if (g_qeglobals.m_bBrushPrimitMode) { + if (g_qeglobals.bNeedConvert) { + // + // we have parsed old brushes format and need conversion convert old brush texture + // representation to new format + // + FaceToBrushPrimitFace(face); + #ifdef _DEBUG + // use old texture coordinates code to check against + for (i = 0; i < w->GetNumPoints(); i++) { + EmitTextureCoordinates((*w)[i], face->d_texture, face); + } + #endif + } + + // + // use new texture representation to compute texture coordinates in debug mode we + // will check against old code and warn if there are differences + // + EmitBrushPrimitTextureCoordinates(face, w); + } + else { + for (i = 0; i < w->GetNumPoints(); i++) { + EmitTextureCoordinates((*w)[i], face->d_texture, face); + } + } + } + + } + + if (updateLights) { + idVec3 offset; + offset.Zero(); + Brush_UpdateLightPoints(b, offset); + } +} + +/* +================ +Brush_RemoveEmptyFaces + + Frees any overconstraining faces +================ +*/ +void Brush_RemoveEmptyFaces(brush_t *b) { + face_t *f, *next; + + f = b->brush_faces; + b->brush_faces = NULL; + + for (; f; f = next) { + next = f->next; + if (!f->face_winding) { + Face_Free(f); + } + else { + f->next = b->brush_faces; + b->brush_faces = f; + } + } +} + +/* +================ +Brush_SnapToGrid +================ +*/ +void Brush_SnapToGrid(brush_t *pb) { + int i; + for (face_t * f = pb->brush_faces; f; f = f->next) { + idWinding *w = f->face_winding; + + if (!w) { + continue; // freed face + } + + for (i = 0; i < w->GetNumPoints(); i++) { + SnapVectorToGrid( (*w)[i].ToVec3() ); + } + + for (i = 0; i < 3; i++) { + f->planepts[i].x = (*w)[i].x; + f->planepts[i].y = (*w)[i].y; + f->planepts[i].z = (*w)[i].z; + } + } + idVec3 v; + idStr str; + if (GetVectorForKey(pb->owner, "origin", v)) { + SnapVectorToGrid(pb->owner->origin); + sprintf(str, "%i %i %i", (int)pb->owner->origin.x, (int)pb->owner->origin.y, (int)pb->owner->origin.z); + SetKeyValue(pb->owner, "origin", str); + } + + if (pb->owner->eclass->nShowFlags & ECLASS_LIGHT) { + if (GetVectorForKey(pb->owner, "light_right", v)) { + // projected + SnapVectorToGrid(v); + pb->lightRight = v; + SetKeyVec3(pb->owner, "light_right", v); + GetVectorForKey(pb->owner, "light_up", v); + SnapVectorToGrid(v); + pb->lightUp = v; + SetKeyVec3(pb->owner, "light_up", v); + GetVectorForKey(pb->owner, "light_target", v); + SnapVectorToGrid(v); + pb->lightTarget = v; + SetKeyVec3(pb->owner, "light_target", v); + if (GetVectorForKey(pb->owner, "light_start", v)) { + SnapVectorToGrid(v); + pb->lightStart = v; + SetKeyVec3(pb->owner, "light_start", v); + GetVectorForKey(pb->owner, "light_end", v); + SnapVectorToGrid(v); + pb->lightEnd = v; + SetKeyVec3(pb->owner, "light_end", v); + } + } else { + // point + if (GetVectorForKey(pb->owner, "light_center", v)) { + SnapVectorToGrid(v); + SetKeyVec3(pb->owner, "light_center", v); + } + } + } + + if ( pb->owner->curve ) { + int c = pb->owner->curve->GetNumValues(); + for ( i = 0; i < c; i++ ) { + v = pb->owner->curve->GetValue( i ); + SnapVectorToGrid( v ); + pb->owner->curve->SetValue( i, v ); + } + } + + Brush_Build(pb); +} + +/* +================ +Brush_Rotate +================ +*/ +void Brush_Rotate(brush_t *b, idMat3 matrix, idVec3 origin, bool bBuild) { + for (face_t * f = b->brush_faces; f; f = f->next) { + for (int i = 0; i < 3; i++) { + f->planepts[i] -= origin; + f->planepts[i] *= matrix; + f->planepts[i] += origin; + } + } + + if (bBuild) { + Brush_Build(b, false, false); + } +} + +extern void VectorRotate3Origin( const idVec3 &vIn, const idVec3 &vRotation, const idVec3 &vOrigin, idVec3 &out ); + +/* +================ +Brush_Rotate +================ +*/ +void Brush_Rotate(brush_t *b, idVec3 vAngle, idVec3 vOrigin, bool bBuild) { + for (face_t * f = b->brush_faces; f; f = f->next) { + for (int i = 0; i < 3; i++) { + VectorRotate3Origin(f->planepts[i], vAngle, vOrigin, f->planepts[i]); + } + } + + if (bBuild) { + Brush_Build(b, false, false); + } +} + +/* +================ +Brush_Center +================ +*/ +void Brush_Center(brush_t *b, idVec3 vNewCenter) { + idVec3 vMid; + + // get center of the brush + for (int j = 0; j < 3; j++) { + vMid[j] = b->mins[j] + abs((b->maxs[j] - b->mins[j]) * 0.5f); + } + + // calc distance between centers + VectorSubtract(vNewCenter, vMid, vMid); + Brush_Move(b, vMid, true); +} + +/* +================ +Brush_Resize + + the brush must be a true axial box +================ +*/ +void Brush_Resize( brush_t *b, idVec3 vMin, idVec3 vMax ) { + int i, j; + face_t *f; + + assert( vMin[0] < vMax[0] && vMin[1] < vMax[1] && vMin[2] < vMax[2] ); + + Brush_MakeFacePlanes( b ); + + for( f = b->brush_faces; f; f = f->next ) { + for ( i = 0; i < 3; i++ ) { + if ( f->plane.Normal()[i] >= 0.999f ) { + for ( j = 0; j < 3; j++ ) { + f->planepts[j][i] = vMax[i]; + } + break; + } + if ( f->plane.Normal()[i] <= -0.999f ) { + for ( j = 0; j < 3; j++ ) { + f->planepts[j][i] = vMin[i]; + } + break; + } + } + //assert( i < 3 ); + } + + Brush_Build( b, true ); +} + +/* +================ +HasModel +================ +*/ +eclass_t *HasModel(brush_t *b) { + idVec3 vMin, vMax; + vMin[0] = vMin[1] = vMin[2] = 999999; + vMax[0] = vMax[1] = vMax[2] = -999999; + + if (b->owner->md3Class != NULL) { + return b->owner->md3Class; + } + + if (b->owner->eclass->modelHandle > 0) { + return b->owner->eclass; + } + + eclass_t *e = NULL; + + // FIXME: entity needs to track whether a cache hit failed and not ask again + if (b->owner->eclass->nShowFlags & ECLASS_MISCMODEL) { + const char *pModel = ValueForKey(b->owner, "model"); + if (pModel != NULL && strlen(pModel) > 0) { + e = GetCachedModel(b->owner, pModel, vMin, vMax); + if (e != NULL) { + // + // we need to scale the brush to the proper size based on the model load recreate + // brush just like in load/save + // + VectorAdd(vMin, b->owner->origin, vMin); + VectorAdd(vMax, b->owner->origin, vMax); + Brush_Resize(b, vMin, vMax); + b->bModelFailed = false; + } + else { + b->bModelFailed = true; + } + } + } + + return e; +} + +/* +================ +Entity_GetRotationMatrixAngles +================ +*/ +bool Entity_GetRotationMatrixAngles( entity_t *e, idMat3 &mat, idAngles &angles ) { + int angle; + + /* the angle keyword is a yaw value, except for two special markers */ + if ( GetMatrixForKey( e, "rotation", mat ) ) { + angles = mat.ToAngles(); + return true; + } + else if ( e->epairs.GetInt( "angle", "0", angle ) ) { + if ( angle == -1 ) { // up + angles.Set( 270, 0, 0 ); + } + else if ( angle == -2 ) { // down + angles.Set( 90, 0, 0 ); + } + else { + angles.Set( 0, angle, 0 ); + } + mat = angles.ToMat3(); + return true; + } + else { + mat.Identity(); + angles.Zero(); + return false; + } +} + +/* +================ +FacingVectors +================ +*/ +static void FacingVectors(entity_t *e, idVec3 &forward, idVec3 &right, idVec3 &up) { + idAngles angles; + idMat3 mat; + + Entity_GetRotationMatrixAngles(e, mat, angles); + angles.ToVectors( &forward, &right, &up); +} + +/* +================ +Brush_DrawFacingAngle +================ +*/ +void Brush_DrawFacingAngle( brush_t *b, entity_t *e, bool particle ) { + idVec3 forward, right, up; + idVec3 endpoint, tip1, tip2; + idVec3 start; + float dist; + + VectorAdd(e->brushes.onext->mins, e->brushes.onext->maxs, start); + VectorScale(start, 0.5f, start); + dist = (b->maxs[0] - start[0]) * 2.5f; + + FacingVectors(e, forward, right, up); + VectorMA(start, dist, ( particle ) ? up : forward, endpoint); + + dist = (b->maxs[0] - start[0]) * 0.5f; + VectorMA(endpoint, -dist, ( particle ) ? up : forward, tip1); + VectorMA(tip1, -dist, ( particle ) ? forward : up, tip1); + VectorMA(tip1, 2 * dist, ( particle ) ? forward : up, tip2); + globalImages->BindNull(); + qglColor4f(1, 1, 1, 1); + qglLineWidth(2); + qglBegin(GL_LINES); + qglVertex3fv(start.ToFloatPtr()); + qglVertex3fv(endpoint.ToFloatPtr()); + qglVertex3fv(endpoint.ToFloatPtr()); + qglVertex3fv(tip1.ToFloatPtr()); + qglVertex3fv(endpoint.ToFloatPtr()); + qglVertex3fv(tip2.ToFloatPtr()); + qglEnd(); + qglLineWidth(0.5f); +} + +/* +================ +DrawProjectedLight +================ +*/ +void DrawProjectedLight(brush_t *b, bool bSelected, bool texture) { + int i; + idVec3 v1, v2, cross, vieworg, edge[8][2], v[4]; + idVec3 target, start; + + if (!bSelected && !g_bShowLightVolumes) { + return; + } + + // use the renderer to get the volume outline + idPlane lightProject[4]; + idPlane planes[6]; + srfTriangles_t *tri; + + // use the game's epair parsing code so + // we can use the same renderLight generation + entity_t *ent = b->owner; + idDict spawnArgs; + renderLight_t parms; + + spawnArgs = ent->epairs; + gameEdit->ParseSpawnArgsToRenderLight( &spawnArgs, &parms ); + renderSystem->RenderLightFrustum( parms, planes ); + + tri = renderModelManager->PolytopeSurface(6, planes, NULL); + + qglColor3f(1, 0, 1); + for (i = 0; i < tri->numIndexes; i += 3) { + qglBegin(GL_LINE_LOOP); + glVertex3fv(tri->verts[tri->indexes[i]].xyz.ToFloatPtr()); + glVertex3fv(tri->verts[tri->indexes[i + 1]].xyz.ToFloatPtr()); + glVertex3fv(tri->verts[tri->indexes[i + 2]].xyz.ToFloatPtr()); + qglEnd(); + } + + renderModelManager->FreeStaticTriSurf(tri); + + // draw different selection points for point lights or projected + // lights (FIXME: rotate these based on parms!) + if ( !bSelected ) { + return; + } + + idMat3 mat; + bool transform = GetMatrixForKey(b->owner, "light_rotation", mat); + if (!transform) { + transform = GetMatrixForKey(b->owner, "rotation", mat); + } + idVec3 tv; + idVec3 *origin = (b->trackLightOrigin) ? &b->owner->lightOrigin : &b->owner->origin; + if (b->pointLight) { + if ( b->lightCenter[0] || b->lightCenter[1] || b->lightCenter[2] ) { + qglPointSize(8); + qglColor3f( 1.0f, 0.4f, 0.8f ); + qglBegin(GL_POINTS); + tv = b->lightCenter; + if (transform) { + tv -= *origin; + tv *= mat; + tv += *origin; + } + qglVertex3fv(tv.ToFloatPtr()); + qglEnd(); + qglPointSize(1); + } + return; + } + + // projected light + qglPointSize(8); + qglColor3f( 1.0f, 0.4f, 0.8f ); + qglBegin(GL_POINTS); + tv = b->lightRight; + if (transform) { + tv -= *origin; + tv *= mat; + tv += *origin; + } + qglVertex3fv(tv.ToFloatPtr()); + tv = b->lightTarget; + if (transform) { + tv -= *origin; + tv *= mat; + tv += *origin; + } + qglVertex3fv(tv.ToFloatPtr()); + tv = b->lightUp; + if (transform) { + tv -= *origin; + tv *= mat; + tv += *origin; + } + qglVertex3fv(tv.ToFloatPtr()); + qglEnd(); + + if (b->startEnd) { + qglColor3f( 0.4f, 1.0f, 0.8f ); + qglBegin(GL_POINTS); + qglVertex3fv(b->lightStart.ToFloatPtr()); + qglVertex3fv(b->lightEnd.ToFloatPtr()); + qglEnd(); + } + + qglPointSize(1); +} + +/* +================ +GLCircle +================ +*/ +void GLCircle(float x, float y, float z, float r) +{ + float ix = 0; + float iy = r; + float ig = 3 - 2 * r; + float idgr = -6; + float idgd = 4 * r - 10; + qglPointSize(0.5f); + qglBegin(GL_POINTS); + while (ix <= iy) { + if (ig < 0) { + ig += idgd; + idgd -= 8; + iy--; + } else { + ig += idgr; + idgd -= 4; + } + idgr -= 4; + ix++; + qglVertex3f(x + ix, y + iy, z); + qglVertex3f(x - ix, y + iy, z); + qglVertex3f(x + ix, y - iy, z); + qglVertex3f(x - ix, y - iy, z); + qglVertex3f(x + iy, y + ix, z); + qglVertex3f(x - iy, y + ix, z); + qglVertex3f(x + iy, y - ix, z); + qglVertex3f(x - iy, y - ix, z); + } + qglEnd(); +} + +/* +================ +DrawSpeaker +================ +*/ +void DrawSpeaker(brush_t *b, bool bSelected, bool twoD) { + + if (!(g_qeglobals.d_savedinfo.showSoundAlways || (g_qeglobals.d_savedinfo.showSoundWhenSelected && bSelected))) { + return; + } + + // convert to units ( inches ) + float min = FloatForKey(b->owner, "s_mindistance"); + float max = FloatForKey(b->owner, "s_maxdistance"); + + const char *s = b->owner->epairs.GetString("s_shader"); + if (s && *s) { + const idSoundShader *shader = declManager->FindSound( s, false ); + if ( shader ) { + if ( !min ) { + min = shader->GetMinDistance(); + } + if ( !max ) { + max = shader->GetMaxDistance(); + } + } + } + + if (min == 0 && max == 0) { + return; + } + + + // convert from meters to doom units + min *= METERS_TO_DOOM; + max *= METERS_TO_DOOM; + + if (twoD) { + if (bSelected) { + qglColor4f(g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES].x, g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES].y, g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES].z, .5); + } else { + qglColor4f(b->owner->eclass->color.x, b->owner->eclass->color.y, b->owner->eclass->color.z, .5); + } + qglPolygonMode (GL_FRONT_AND_BACK, GL_LINE); + GLCircle(b->owner->origin.x, b->owner->origin.y, b->owner->origin.z, min); + if (bSelected) { + qglColor4f(g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES].x, g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES].y, g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES].z, 1); + } else { + qglColor4f(b->owner->eclass->color.x, b->owner->eclass->color.y, b->owner->eclass->color.z, 1); + } + GLCircle(b->owner->origin.x, b->owner->origin.y, b->owner->origin.z, max); + } else { + qglPushMatrix(); + qglTranslatef(b->owner->origin.x, b->owner->origin.y, b->owner->origin.z ); + qglColor3f( 0.4f, 0.4f, 0.4f ); + qglPolygonMode (GL_FRONT_AND_BACK, GL_LINE); + GLUquadricObj* qobj = gluNewQuadric(); + gluSphere(qobj, min, 8, 8); + qglColor3f( 0.8f, 0.8f, 0.8f ); + gluSphere(qobj, max, 8, 8); + qglEnable(GL_BLEND); + qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + globalImages->BindNull(); + if (bSelected) { + qglColor4f( g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES].x, g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES].y, g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES].z, 0.35f ); + } else { + qglColor4f( b->owner->eclass->color.x, b->owner->eclass->color.y, b->owner->eclass->color.z, 0.35f ); + } + gluSphere(qobj, min, 8, 8); + if (bSelected) { + qglColor4f( g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES].x, g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES].y, g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES].z, 0.1f ); + } else { + qglColor4f( b->owner->eclass->color.x, b->owner->eclass->color.y, b->owner->eclass->color.z, 0.1f ); + } + gluSphere(qobj, max, 8, 8); + gluDeleteQuadric(qobj); + qglPopMatrix(); + } + + +} + +/* +================ +DrawLight +================ +*/ +void DrawLight(brush_t *b, bool bSelected) { + idVec3 vTriColor; + bool bTriPaint = false; + + vTriColor[0] = vTriColor[2] = 1.0f; + vTriColor[1] = 1.0f; + bTriPaint = true; + + CString strColor = ValueForKey(b->owner, "_color"); + if (strColor.GetLength() > 0) { + float fR, fG, fB; + int n = sscanf(strColor, "%f %f %f", &fR, &fG, &fB); + if (n == 3) { + vTriColor[0] = fR; + vTriColor[1] = fG; + vTriColor[2] = fB; + } + } + + qglColor3f(vTriColor[0], vTriColor[1], vTriColor[2]); + + idVec3 vCorners[4]; + float fMid = b->mins[2] + (b->maxs[2] - b->mins[2]) / 2; + + vCorners[0][0] = b->mins[0]; + vCorners[0][1] = b->mins[1]; + vCorners[0][2] = fMid; + + vCorners[1][0] = b->mins[0]; + vCorners[1][1] = b->maxs[1]; + vCorners[1][2] = fMid; + + vCorners[2][0] = b->maxs[0]; + vCorners[2][1] = b->maxs[1]; + vCorners[2][2] = fMid; + + vCorners[3][0] = b->maxs[0]; + vCorners[3][1] = b->mins[1]; + vCorners[3][2] = fMid; + + idVec3 vTop, vBottom; + + vTop[0] = b->mins[0] + ((b->maxs[0] - b->mins[0]) / 2); + vTop[1] = b->mins[1] + ((b->maxs[1] - b->mins[1]) / 2); + vTop[2] = b->maxs[2]; + + VectorCopy(vTop, vBottom); + vBottom[2] = b->mins[2]; + + idVec3 vSave; + VectorCopy(vTriColor, vSave); + + globalImages->BindNull(); + qglBegin(GL_TRIANGLE_FAN); + qglVertex3fv(vTop.ToFloatPtr()); + int i; + for (i = 0; i <= 3; i++) { + vTriColor[0] *= 0.95f; + vTriColor[1] *= 0.95f; + vTriColor[2] *= 0.95f; + qglColor3f(vTriColor[0], vTriColor[1], vTriColor[2]); + qglVertex3fv(vCorners[i].ToFloatPtr()); + } + + qglVertex3fv(vCorners[0].ToFloatPtr()); + qglEnd(); + + VectorCopy(vSave, vTriColor); + vTriColor[0] *= 0.95f; + vTriColor[1] *= 0.95f; + vTriColor[2] *= 0.95f; + + qglBegin(GL_TRIANGLE_FAN); + qglVertex3fv(vBottom.ToFloatPtr()); + qglVertex3fv(vCorners[0].ToFloatPtr()); + for (i = 3; i >= 0; i--) { + vTriColor[0] *= 0.95f; + vTriColor[1] *= 0.95f; + vTriColor[2] *= 0.95f; + qglColor3f(vTriColor[0], vTriColor[1], vTriColor[2]); + qglVertex3fv(vCorners[i].ToFloatPtr()); + } + + qglEnd(); + + DrawProjectedLight(b, bSelected, true); +} + +/* +================ +Control_Draw +================ +*/ +void Control_Draw(brush_t *b) { + face_t *face; + int i, order; + qtexture_t *prev = 0; + idWinding *w; + + // guarantee the texture will be set first + prev = NULL; + for ( face = b->brush_faces, order = 0; face; face = face->next, order++ ) { + w = face->face_winding; + if (!w) { + continue; // freed face + } + + qglColor4f(1, 1, .5, 1); + qglBegin(GL_POLYGON); + for (i = 0; i < w->GetNumPoints(); i++) { + qglVertex3fv( (*w)[i].ToFloatPtr() ); + } + + qglEnd(); + } +} + +/* +================ +Brush_DrawModel +================ +*/ +void Brush_DrawModel( brush_t *b, bool camera, bool bSelected ) { + idMat3 axis; + idAngles angles; + int nDrawMode = g_pParentWnd->GetCamera()->Camera().draw_mode; + + if ( camera && g_PrefsDlg.m_nEntityShowState != ENTITY_WIREFRAME && nDrawMode != cd_wire ) { + qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); + } + else { + qglPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); + } + + idRenderModel *model = b->modelHandle; + if ( model == NULL ) { + model = b->owner->eclass->entityModel; + } + if ( model ) { + idRenderModel *model2; + + model2 = NULL; + bool fixedBounds = false; + + if ( model->IsDynamicModel() != DM_STATIC ) { + if ( dynamic_cast( model ) ) { + const char *classname = ValueForKey( b->owner, "classname" ); + if (stricmp(classname, "func_static") == 0) { + classname = ValueForKey(b->owner, "animclass"); + } + const char *anim = ValueForKey( b->owner, "anim" ); + int frame = IntForKey( b->owner, "frame" ) + 1; + if ( frame < 1 ) { + frame = 1; + } + if ( !anim || !anim[ 0 ] ) { + anim = "idle"; + } + model2 = gameEdit->ANIM_CreateMeshForAnim( model, classname, anim, frame, false ); + } else if ( dynamic_cast( model ) ) { + fixedBounds = true; + } + + if ( !model2 ) { + idBounds bounds; + if (fixedBounds) { + bounds.Zero(); + bounds.ExpandSelf(12.0f); + } else { + bounds = model->Bounds( NULL ); + } + idVec4 color; + color.w = 1.0f; + if (bSelected) { + color.x = g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES].x; + color.y = g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES].y; + color.z = g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES].z; + } else { + color.x = b->owner->eclass->color.x; + color.y = b->owner->eclass->color.y; + color.z = b->owner->eclass->color.z; + } + idVec3 center = bounds.GetCenter(); + glBox(color, b->owner->origin + center, bounds.GetRadius( center ) ); + model = renderModelManager->DefaultModel(); + } else { + model = model2; + } + } + + Entity_GetRotationMatrixAngles( b->owner, axis, angles ); + + idVec4 colorSave; + qglGetFloatv(GL_CURRENT_COLOR, colorSave.ToFloatPtr()); + + if ( bSelected ) { + qglColor3fv( g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES].ToFloatPtr() ); + } + + DrawRenderModel( model, b->owner->origin, axis, camera ); + + qglColor4fv( colorSave.ToFloatPtr() ); + + if ( bSelected && camera ) + { + //draw selection tints + /* + if ( camera && g_PrefsDlg.m_nEntityShowState != ENTITY_WIREFRAME ) { + qglPolygonMode ( GL_FRONT_AND_BACK , GL_FILL ); + qglColor3fv ( g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES].ToFloatPtr () ); + qglEnable ( GL_BLEND ); + qglBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + DrawRenderModel( model, b->owner->origin, axis, camera ); + } + */ + + //draw white triangle outlines + globalImages->BindNull(); + + qglPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); + qglDisable( GL_BLEND ); + qglDisable( GL_DEPTH_TEST ); + qglColor3f( 1.0f, 1.0f, 1.0f ); + qglPolygonOffset( 1.0f, 3.0f ); + DrawRenderModel( model, b->owner->origin, axis, false ); + qglEnable( GL_DEPTH_TEST ); + } + + if ( model2 ) { + delete model2; + model2 = NULL; + } + } + + if ( bSelected && camera ) { + qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); + } + else if ( camera ) { + globalImages->BindNull(); + } + + if ( g_bPatchShowBounds ) { + for ( face_t *face = b->brush_faces; face; face = face->next ) { + // only draw polygons facing in a direction we care about + idWinding *w = face->face_winding; + if (!w) { + continue; + } + + // + // if (b->alphaBrush && !(face->texdef.flags & SURF_ALPHA)) continue; + // draw the polygon + // + qglBegin(GL_LINE_LOOP); + for (int i = 0; i < w->GetNumPoints(); i++) { + qglVertex3fv( (*w)[i].ToFloatPtr() ); + } + qglEnd(); + } + } +} + +/* +================ +GLTransformedVertex +================ +*/ +void GLTransformedVertex(float x, float y, float z, idMat3 mat, idVec3 origin, idVec3 color, float maxDist) { + idVec3 v(x,y,z); + v -= origin; + v *= mat; + v += origin; + + idVec3 n = v - g_pParentWnd->GetCamera()->Camera().origin; + float max = n.Length() / maxDist; + if (color.x) { + color.x = max; + } else if (color.y) { + color.y = max; + } else { + color.z = max; + } + qglColor3f(color.x, color.y, color.z); + qglVertex3f(v.x, v.y, v.z); + +} + +/* +================ +GLTransformedCircle +================ +*/ +void GLTransformedCircle(int type, idVec3 origin, float r, idMat3 mat, float pointSize, idVec3 color, float maxDist) { + qglPointSize(pointSize); + qglBegin(GL_POINTS); + for (int i = 0; i < 360; i++) { + float cx = origin.x; + float cy = origin.y; + float cz = origin.z; + switch (type) { + case 0: + cx += r * cos((float)i); + cy += r * sin((float)i); + break; + case 1: + cx += r * cos((float)i); + cz += r * sin((float)i); + break; + case 2: + cy += r * sin((float)i); + cz += r * cos((float)i); + break; + default: + break; + } + GLTransformedVertex(cx, cy, cz, mat, origin, color, maxDist); + } + qglEnd(); +} + +/* +================ +Brush_DrawAxis +================ +*/ +void Brush_DrawAxis(brush_t *b) { + if ( g_pParentWnd->ActiveXY()->RotateMode() && b->modelHandle ) { + bool matrix = false; + idMat3 mat; + float a, s, c; + if (GetMatrixForKey(b->owner, "rotation", mat)) { + matrix = true; + } else { + a = FloatForKey(b->owner, "angle"); + if (a) { + s = sin( DEG2RAD( a ) ); + c = cos( DEG2RAD( a ) ); + } + else { + s = c = 0; + } + } + + idBounds bo; + bo.FromTransformedBounds(b->modelHandle->Bounds(), b->owner->origin, b->owner->rotation); + + float dist = (g_pParentWnd->GetCamera()->Camera().origin - bo[0]).Length(); + float dist2 = (g_pParentWnd->GetCamera()->Camera().origin - bo[1]).Length(); + if (dist2 > dist) { + dist = dist2; + } + + float xr, yr, zr; + xr = (b->modelHandle->Bounds()[1].x > b->modelHandle->Bounds()[0].x) ? b->modelHandle->Bounds()[1].x - b->modelHandle->Bounds()[0].x : b->modelHandle->Bounds()[0].x - b->modelHandle->Bounds()[1].x; + yr = (b->modelHandle->Bounds()[1].y > b->modelHandle->Bounds()[0].y) ? b->modelHandle->Bounds()[1].y - b->modelHandle->Bounds()[0].y : b->modelHandle->Bounds()[0].y - b->modelHandle->Bounds()[1].y; + zr = (b->modelHandle->Bounds()[1].z > b->modelHandle->Bounds()[0].z) ? b->modelHandle->Bounds()[1].z - b->modelHandle->Bounds()[0].z : b->modelHandle->Bounds()[0].z - b->modelHandle->Bounds()[1].z; + + globalImages->BindNull(); + + GLTransformedCircle(0, b->owner->origin, xr, mat, 1.25, idVec3(0, 0, 1), dist); + GLTransformedCircle(1, b->owner->origin, yr, mat, 1.25, idVec3(0, 1, 0), dist); + GLTransformedCircle(2, b->owner->origin, zr, mat, 1.25, idVec3(1, 0, 0), dist); + + float wr = xr; + int type = 0; + idVec3 org = b->owner->origin; + if (g_qeglobals.rotateAxis == 0) { + wr = zr; + type = 2; + } else if (g_qeglobals.rotateAxis == 1) { + wr = yr; + type = 1; + } + + if (g_qeglobals.flatRotation) { + if (yr > wr) { + wr = yr; + } + if (zr > wr) { + wr = zr; + } + idVec3 vec = vec3_origin; + vec[g_qeglobals.rotateAxis] = 1.0f; + if (g_qeglobals.flatRotation == 1) { + org = g_pParentWnd->ActiveXY()->RotateOrigin(); + float t = (org - bo.GetCenter()).Length(); + if (t > wr) { + wr = t; + } + } else { + org = bo.GetCenter(); + } + idRotation rot(org, vec, 0); + mat = rot.ToMat3(); + } + GLTransformedCircle(type, org, wr * 1.03f, mat, 1.45f, idVec3(1, 1, 1), dist); + } +} + +/* +================ +Brush_DrawModelInfo +================ +*/ +void Brush_DrawModelInfo(brush_t *b, bool selected) { + if (b->modelHandle > 0) { + GLfloat color[4]; + qglGetFloatv(GL_CURRENT_COLOR, &color[0]); + if (selected) { + qglColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES].ToFloatPtr()); + } + else { + qglColor3fv(b->owner->eclass->color.ToFloatPtr()); + } + + Brush_DrawModel(b, true, selected); + qglColor4fv(color); + + if ( selected ) { + Brush_DrawAxis(b); + } + return; + } +} + +/* +================ +Brush_DrawEmitter +================ +*/ +void Brush_DrawEmitter(brush_t *b, bool bSelected, bool cam) { + if ( !( b->owner->eclass->nShowFlags & ECLASS_PARTICLE ) ) { + return; + } + + if (bSelected) { + qglColor4f(g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES].x, g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES].y, g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES].z, .5); + } else { + qglColor4f(b->owner->eclass->color.x, b->owner->eclass->color.y, b->owner->eclass->color.z, .5); + } + + if ( cam ) { + Brush_DrawFacingAngle( b, b->owner, true ); + } +} + +/* +================ +Brush_DrawEnv +================ +*/ +void Brush_DrawEnv( brush_t *b, bool cameraView, bool bSelected ) { + idVec3 origin, newOrigin; + idMat3 axis, newAxis; + idAngles newAngles; + bool poseIsSet; + + idRenderModel *model = gameEdit->AF_CreateMesh( b->owner->epairs, origin, axis, poseIsSet ); + + if ( !poseIsSet ) { + if ( Entity_GetRotationMatrixAngles( b->owner, newAxis, newAngles ) ) { + axis = newAxis; + } + if ( b->owner->epairs.GetVector( "origin", "0 0 0", newOrigin ) ) { + origin = newOrigin; + } + } + + if ( model ) { + if ( cameraView && g_PrefsDlg.m_nEntityShowState != ENTITY_WIREFRAME ) { + qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); + } + else { + qglPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); + } + + idVec4 colorSave; + qglGetFloatv(GL_CURRENT_COLOR, colorSave.ToFloatPtr()); + + if ( bSelected ) { + qglColor3fv( g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES].ToFloatPtr() ); + } else { + qglColor3f( 1.f, 1.f, 1.f ); + } + DrawRenderModel( model, origin, axis, true ); + globalImages->BindNull(); + delete model; + model = NULL; + + qglColor4fv( colorSave.ToFloatPtr() ); + } +} + +/* +================ +Brush_DrawCombatNode +================ +*/ +void Brush_DrawCombatNode( brush_t *b, bool cameraView, bool bSelected ) { + float min_dist = b->owner->epairs.GetFloat( "min" ); + float max_dist = b->owner->epairs.GetFloat( "max" ); + float fov = b->owner->epairs.GetFloat( "fov", "60" ); + float yaw = b->owner->epairs.GetFloat("angle"); + idVec3 offset = b->owner->epairs.GetVector("offset"); + + idAngles leftang( 0.0f, yaw + fov * 0.5f - 90.0f, 0.0f ); + idVec3 cone_left = leftang.ToForward(); + idAngles rightang( 0.0f, yaw - fov * 0.5f + 90.0f, 0.0f ); + idVec3 cone_right = rightang.ToForward(); + bool disabled = b->owner->epairs.GetBool( "start_off" ); + + idVec4 color; + if ( bSelected ) { + color = colorRed; + } else { + color = colorBlue; + } + + idVec3 leftDir( -cone_left.y, cone_left.x, 0.0f ); + idVec3 rightDir( cone_right.y, -cone_right.x, 0.0f ); + leftDir.NormalizeFast(); + rightDir.NormalizeFast(); + + idMat3 axis = idAngles(0, yaw, 0).ToMat3(); + idVec3 org = b->owner->origin + offset; + idVec3 entorg = b->owner->origin; + float cone_dot = cone_right * axis[ 1 ]; + if ( idMath::Fabs( cone_dot ) > 0.1 ) { + idVec3 pt, pt1, pt2, pt3, pt4; + float cone_dist = max_dist / cone_dot; + pt1 = org + leftDir * min_dist; + pt2 = org + leftDir * cone_dist; + pt3 = org + rightDir * cone_dist; + pt4 = org + rightDir * min_dist; + qglColor4fv(color.ToFloatPtr()); + qglBegin(GL_LINE_STRIP); + qglVertex3fv( pt1.ToFloatPtr()); + qglVertex3fv( pt2.ToFloatPtr()); + qglVertex3fv( pt3.ToFloatPtr()); + qglVertex3fv( pt4.ToFloatPtr()); + qglVertex3fv( pt1.ToFloatPtr()); + qglEnd(); + + qglColor4fv(colorGreen.ToFloatPtr()); + qglBegin(GL_LINE_STRIP); + qglVertex3fv( entorg.ToFloatPtr()); + pt = (pt1 + pt4) * 0.5f; + qglVertex3fv( pt.ToFloatPtr()); + pt = (pt2 + pt3) * 0.5f; + qglVertex3fv( pt.ToFloatPtr()); + idVec3 tip = pt; + idVec3 dir = ((pt1 + pt2) * 0.5f) - tip; + dir.Normalize(); + pt = tip + dir * 15.0f; + qglVertex3fv( pt.ToFloatPtr()); + qglVertex3fv( tip.ToFloatPtr()); + dir = ((pt4 + pt3) * 0.5f) - tip; + dir.Normalize(); + pt = tip + dir * 15.0f; + qglVertex3fv( pt.ToFloatPtr()); + qglEnd(); + } + +} + +/* +================ +Brush_Draw +================ +*/ +void Brush_Draw(brush_t *b, bool bSelected) { + face_t *face; + int i, order; + const idMaterial *prev = NULL; + idWinding *w; + bool model = false; + + // + // (TTimo) NOTE: added by build 173, I check after pPlugEnt so it doesn't + // interfere ? + // + if ( b->hiddenBrush ) { + return; + } + + Brush_DrawCurve( b, bSelected, true ); + + if (b->pPatch) { + Patch_DrawCam(b->pPatch, bSelected); + return; + } + + int nDrawMode = g_pParentWnd->GetCamera()->Camera().draw_mode; + + if (!(g_qeglobals.d_savedinfo.exclude & EXCLUDE_ANGLES) && (b->owner->eclass->nShowFlags & ECLASS_ANGLE)) { + Brush_DrawFacingAngle(b, b->owner, false); + } + + if ( b->owner->eclass->fixedsize ) { + + DrawSpeaker( b, bSelected, false ); + + if ( g_PrefsDlg.m_bNewLightDraw && (b->owner->eclass->nShowFlags & ECLASS_LIGHT) && !(b->modelHandle || b->entityModel) ) { + DrawLight( b, bSelected ); + return; + } + + if ( b->owner->eclass->nShowFlags & ECLASS_ENV ) { + Brush_DrawEnv( b, true, bSelected ); + } + + if ( b->owner->eclass->nShowFlags & ECLASS_COMBATNODE ) { + Brush_DrawCombatNode( b, true, bSelected ); + } + + } + + + if (!(b->owner && (b->owner->eclass->nShowFlags & ECLASS_WORLDSPAWN))) { + qglColor4f( 1.0f, 0.0f, 0.0f, 0.8f ); + qglPointSize(4); + qglBegin(GL_POINTS); + qglVertex3fv(b->owner->origin.ToFloatPtr()); + qglEnd(); + } + + if ( b->owner->eclass->entityModel ) { + qglColor3fv( b->owner->eclass->color.ToFloatPtr() ); + Brush_DrawModel( b, true, bSelected ); + return; + } + + Brush_DrawEmitter( b, bSelected, true ); + + if ( b->modelHandle > 0 && !model ) { + Brush_DrawModelInfo( b, bSelected ); + return; + } + + // guarantee the texture will be set first + prev = NULL; + for (face = b->brush_faces, order = 0; face; face = face->next, order++) { + w = face->face_winding; + if (!w) { + continue; // freed face + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_CAULK) { + if (strstr(face->texdef.name, "caulk")) { + continue; + } + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_VISPORTALS) { + if (strstr(face->texdef.name, "visportal")) { + continue; + } + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_NODRAW) { + if (strstr(face->texdef.name, "nodraw")) { + continue; + } + } + + if ( (nDrawMode == cd_texture || nDrawMode == cd_light) && face->d_texture != prev && !b->forceWireFrame ) { + // set the texture for this face + prev = face->d_texture; + face->d_texture->GetEditorImage()->Bind(); + } + + if (model) { + qglEnable(GL_BLEND); + qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + qglColor4f( face->d_color.x, face->d_color.y, face->d_color.z, 0.1f ); + } else { + qglColor4f( face->d_color.x, face->d_color.y, face->d_color.z, face->d_texture->GetEditorAlpha() ); + } + + qglBegin(GL_POLYGON); + + for (i = 0; i < w->GetNumPoints(); i++) { + if ( !b->forceWireFrame && ( nDrawMode == cd_texture || nDrawMode == cd_light ) ) { + qglTexCoord2fv( &(*w)[i][3] ); + } + + qglVertex3fv( (*w)[i].ToFloatPtr() ); + } + + qglEnd(); + + if (model) { + qglDisable(GL_BLEND); + } + } + + globalImages->BindNull(); +} + +/* +================ +Face_Draw +================ +*/ +void Face_Draw(face_t *f) { + int i; + + if (f->face_winding == NULL) { + return; + } + + qglBegin(GL_POLYGON); + for (i = 0; i < f->face_winding->GetNumPoints(); i++) { + qglVertex3fv( (*f->face_winding)[i].ToFloatPtr() ); + } + + qglEnd(); +} + + +idSurface_SweptSpline *SplineToSweptSpline( idCurve *curve ) { + // expects a vec3 curve and creates a vec4 based swept spline + // must be either nurbs or catmull + idCurve_Spline *newCurve = NULL; + if ( dynamic_cast*>( curve ) ) { + newCurve = new idCurve_NURBS; + } else if ( dynamic_cast*>( curve ) ) { + newCurve = new idCurve_CatmullRomSpline; + } + + if ( curve == NULL || newCurve == NULL ) { + return NULL; + } + + int c = curve->GetNumValues(); + float len = 0.0f; + for ( int i = 0; i < c; i++ ) { + idVec3 v = curve->GetValue( i ); + newCurve->AddValue( curve->GetTime( i ), idVec4( v.x, v.y, v.z, len ) ); + if ( i < c - 1 ) { + len += curve->GetLengthBetweenKnots( i, i + 1 ) * 0.1f; + } + } + + idSurface_SweptSpline *ss = new idSurface_SweptSpline; + ss->SetSpline( newCurve ); + ss->SetSweptCircle( 10.0f ); + ss->Tessellate( newCurve->GetNumValues() * 6, 6 ); + + return ss; +} + +/* +================ +Brush_DrawCurve +================ +*/ +void Brush_DrawCurve( brush_t *b, bool bSelected, bool cam ) { + if ( b == NULL || b->owner->curve == NULL ) { + return; + } + + int maxage = b->owner->curve->GetNumValues(); + int i, time = 0; + qglColor3f( 0.0f, 0.0f, 1.0f ); + for ( i = 0; i < maxage; i++) { + + if ( bSelected && g_qeglobals.d_select_mode == sel_editpoint ) { + idVec3 v = b->owner->curve->GetValue( i ); + if ( cam ) { + glBox( colorBlue, v, 6.0f ); + if ( PointInMoveList( b->owner->curve->GetValueAddress( i ) ) >= 0 ) { + glBox(colorBlue, v, 8.0f ); + } + } else { + qglPointSize( 4.0f ); + qglBegin( GL_POINTS ); + qglVertex3f( v.x, v.y, v.z ); + qglEnd(); + + if ( PointInMoveList( b->owner->curve->GetValueAddress( i ) ) >= 0 ) { + glBox(colorBlue, v, 4.0f ); + } + } + } +/* + if ( cam ) { + idSurface_SweptSpline *ss = SplineToSweptSpline( b->owner->curve ); + if ( ss ) { + idMaterial *mat = declManager->FindMaterial( "_default" ); + mat->GetEditorImage()->Bind(); + qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); + qglBegin( GL_TRIANGLES ); + const int *indexes = ss->GetIndexes(); + const idDrawVert *verts = ss->GetVertices(); + for ( j = 0; j < ss->GetNumIndexes(); j += 3 ) { + for ( k = 0; k < 3; k++ ) { + int index = indexes[ j + 2 - k ]; + float f = ShadeForNormal( verts[index].normal ); + qglColor3f( f, f, f ); + qglTexCoord2fv( verts[index].st.ToFloatPtr() ); + qglVertex3fv( verts[index].xyz.ToFloatPtr() ); + } + } + qglEnd(); + delete ss; + } + } else { +*/ +/* qglPointSize( 1.0f ); + qglBegin( GL_POINTS ); + if ( i + 1 < maxage ) { + int start = b->owner->curve->GetTime( i ); + int end = b->owner->curve->GetTime( i + 1 ); + int inc = (end - start) / POINTS_PER_KNOT; + for ( int j = 0; j < POINTS_PER_KNOT; j++ ) { + idVec3 v = b->owner->curve->GetCurrentValue( start ); + qglVertex3f( v.x, v.y, v.z ); + start += inc; + } + }*/ + // DHM - _D3XP : Makes it easier to see curve + qglBegin( GL_LINE_STRIP ); + if ( i + 1 < maxage ) { + int start = b->owner->curve->GetTime( i ); + int end = b->owner->curve->GetTime( i + 1 ); + int inc = (end - start) / POINTS_PER_KNOT; + for ( int j = 0; j <= POINTS_PER_KNOT; j++ ) { + idVec3 v = b->owner->curve->GetCurrentValue( start ); + qglVertex3f( v.x, v.y, v.z ); + start += inc; + } + } + qglEnd(); +/* + } +*/ + + } + qglPointSize(1); +} + +/* +================ +Brush_DrawXY +================ +*/ +void Brush_DrawXY(brush_t *b, int nViewType, bool bSelected, bool ignoreViewType) { + face_t *face; + int order; + idWinding *w; + int i; + + if ( b->hiddenBrush ) { + return; + } + + idVec4 colorSave; + qglGetFloatv(GL_CURRENT_COLOR, colorSave.ToFloatPtr()); + + if (!(b->owner && (b->owner->eclass->nShowFlags & ECLASS_WORLDSPAWN))) { + qglColor4f( 1.0f, 0.0f, 0.0f, 0.8f ); + qglPointSize(4); + qglBegin(GL_POINTS); + qglVertex3fv(b->owner->origin.ToFloatPtr()); + qglEnd(); + } + + Brush_DrawCurve( b, bSelected, false ); + + qglColor4fv(colorSave.ToFloatPtr()); + + + if (b->pPatch) { + Patch_DrawXY(b->pPatch); + if (!g_bPatchShowBounds) { + return; + } + } + + if (b->owner->eclass->fixedsize) { + + DrawSpeaker(b, bSelected, true); + if (g_PrefsDlg.m_bNewLightDraw && (b->owner->eclass->nShowFlags & ECLASS_LIGHT) && !(b->modelHandle || b->entityModel)) { + idVec3 vCorners[4]; + float fMid = b->mins[2] + (b->maxs[2] - b->mins[2]) / 2; + + vCorners[0][0] = b->mins[0]; + vCorners[0][1] = b->mins[1]; + vCorners[0][2] = fMid; + + vCorners[1][0] = b->mins[0]; + vCorners[1][1] = b->maxs[1]; + vCorners[1][2] = fMid; + + vCorners[2][0] = b->maxs[0]; + vCorners[2][1] = b->maxs[1]; + vCorners[2][2] = fMid; + + vCorners[3][0] = b->maxs[0]; + vCorners[3][1] = b->mins[1]; + vCorners[3][2] = fMid; + + idVec3 vTop, vBottom; + + vTop[0] = b->mins[0] + ((b->maxs[0] - b->mins[0]) / 2); + vTop[1] = b->mins[1] + ((b->maxs[1] - b->mins[1]) / 2); + vTop[2] = b->maxs[2]; + + VectorCopy(vTop, vBottom); + vBottom[2] = b->mins[2]; + + qglPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + qglBegin(GL_TRIANGLE_FAN); + qglVertex3fv(vTop.ToFloatPtr()); + qglVertex3fv(vCorners[0].ToFloatPtr()); + qglVertex3fv(vCorners[1].ToFloatPtr()); + qglVertex3fv(vCorners[2].ToFloatPtr()); + qglVertex3fv(vCorners[3].ToFloatPtr()); + qglVertex3fv(vCorners[0].ToFloatPtr()); + qglEnd(); + qglBegin(GL_TRIANGLE_FAN); + qglVertex3fv(vBottom.ToFloatPtr()); + qglVertex3fv(vCorners[0].ToFloatPtr()); + qglVertex3fv(vCorners[3].ToFloatPtr()); + qglVertex3fv(vCorners[2].ToFloatPtr()); + qglVertex3fv(vCorners[1].ToFloatPtr()); + qglVertex3fv(vCorners[0].ToFloatPtr()); + qglEnd(); + DrawBrushEntityName(b); + DrawProjectedLight(b, bSelected, false); + return; + } else if (b->owner->eclass->nShowFlags & ECLASS_MISCMODEL) { + // if (PaintedModel(b, false)) return; + } else if (b->owner->eclass->nShowFlags & ECLASS_ENV) { + Brush_DrawEnv( b, false, bSelected ); + } else if (b->owner->eclass->nShowFlags & ECLASS_COMBATNODE) { + Brush_DrawCombatNode(b, false, bSelected); + } + + if (b->owner->eclass->entityModel) { + Brush_DrawModel( b, false, bSelected ); + DrawBrushEntityName(b); + qglColor4fv(colorSave.ToFloatPtr()); + return; + } + + } + + qglColor4fv(colorSave.ToFloatPtr()); + + if (b->modelHandle > 0) { + Brush_DrawEmitter( b, bSelected, false ); + Brush_DrawModel(b, false, bSelected); + qglColor4fv(colorSave.ToFloatPtr()); + return; + } + + for (face = b->brush_faces, order = 0; face; face = face->next, order++) { + // only draw polygons facing in a direction we care about + if (!ignoreViewType) { + if (nViewType == XY) { + if (face->plane[2] <= 0) { + continue; + } + } else { + if (nViewType == XZ) { + if (face->plane[1] <= 0) { + continue; + } + } else { + if (face->plane[0] <= 0) { + continue; + } + } + } + } + + w = face->face_winding; + if (!w) { + continue; + } + + // + // if (b->alphaBrush && !(face->texdef.flags & SURF_ALPHA)) continue; + // draw the polygon + // + qglBegin(GL_LINE_LOOP); + for (i = 0; i < w->GetNumPoints(); i++) { + qglVertex3fv( (*w)[i].ToFloatPtr() ); + } + qglEnd(); +/* + for (i = 0; i < 3; i++) { + glLabeledPoint(idVec4(1, 0, 0, 1), face->planepts[i], 3, va("%i", i)); + } +*/ + } + + DrawBrushEntityName(b); +} + +/* +================== +PointValueInPointList +================== +*/ +static int PointValueInPointList( idVec3 v ) { + for ( int i = 0; i < g_qeglobals.d_numpoints; i++ ) { + if ( v == g_qeglobals.d_points[i] ) { + return i; + } + } + return -1; +} + + +extern bool Sys_KeyDown(int key); +/* +================ +Brush_Move +================ +*/ +void Brush_Move(brush_t *b, const idVec3 move, bool bSnap, bool updateOrigin) { + int i; + face_t *f; + char text[128]; + + for (f = b->brush_faces; f; f = f->next) { + idVec3 vTemp; + VectorCopy(move, vTemp); + + if (g_PrefsDlg.m_bTextureLock) { + Face_MoveTexture(f, vTemp); + } + + for (i = 0; i < 3; i++) { + VectorAdd(f->planepts[i], move, f->planepts[i]); + } + } + + bool controlDown = Sys_KeyDown(VK_CONTROL); + Brush_Build(b, bSnap, true, false, !controlDown); + + if (b->pPatch) { + Patch_Move(b->pPatch, move); + } + + if ( b->owner->curve ) { + b->owner->curve->Translate( move ); + Entity_UpdateCurveData( b->owner ); + } + + idVec3 temp; + + // PGM - keep the origin vector up to date on fixed size entities. + if (b->owner->eclass->fixedsize || EntityHasModel(b->owner) || (updateOrigin && GetVectorForKey(b->owner, "origin", temp))) { +// if (!b->entityModel) { + bool adjustOrigin = true; + if(b->trackLightOrigin) { + b->owner->lightOrigin += move; + sprintf(text, "%i %i %i", (int)b->owner->lightOrigin[0], (int)b->owner->lightOrigin[1], (int)b->owner->lightOrigin[2]); + SetKeyValue(b->owner, "light_origin", text); + if (QE_SingleBrush(true, true)) { + adjustOrigin = false; + } + } + + if (adjustOrigin && updateOrigin) { + b->owner->origin += move; + if (g_moveOnly) { + sprintf(text, "%g %g %g", b->owner->origin[0], b->owner->origin[1], b->owner->origin[2]); + } else { + sprintf(text, "%i %i %i", (int)b->owner->origin[0], (int)b->owner->origin[1], (int)b->owner->origin[2]); + } + SetKeyValue(b->owner, "origin", text); + } + + // rebuild the light dragging points now that the origin has changed + idVec3 offset; + offset.Zero(); + if (controlDown) { + offset.x = -move.x; + offset.y = -move.y; + offset.z = -move.z; + Brush_UpdateLightPoints(b, offset); + } else { + offset.Zero(); + Brush_UpdateLightPoints(b, offset); + } + + //} + if (b->owner->eclass->nShowFlags & ECLASS_ENV) { + const idKeyValue *arg = b->owner->epairs.MatchPrefix( "body ", NULL ); + idStr val; + idVec3 org; + idAngles ang; + while ( arg ) { + sscanf( arg->GetValue(), "%f %f %f %f %f %f", &org.x, &org.y, &org.z, &ang.pitch, &ang.yaw, &ang.roll ); + org += move; + val = org.ToString(8); + val += " "; + val += ang.ToString(8); + b->owner->epairs.Set(arg->GetKey(), val); + arg = b->owner->epairs.MatchPrefix( "body ", arg ); + } + } + } +} + +/* +================ +Select_AddProjectedLight +================ +*/ +void Select_AddProjectedLight() { + idVec3 vTemp; + CString str; + + // if (!QE_SingleBrush ()) return; + brush_t *b = selected_brushes.next; + + if (b->owner->eclass->nShowFlags & ECLASS_LIGHT) { + vTemp[0] = vTemp[1] = 0; + vTemp[2] = -256; + str.Format("%f %f %f", vTemp[0], vTemp[1], vTemp[2]); + SetKeyValue(b->owner, "light_target", str); + + vTemp[2] = 0; + vTemp[1] = -128; + str.Format("%f %f %f", vTemp[0], vTemp[1], vTemp[2]); + SetKeyValue(b->owner, "light_up", str); + + vTemp[1] = 0; + vTemp[0] = -128; + str.Format("%f %f %f", vTemp[0], vTemp[1], vTemp[2]); + SetKeyValue(b->owner, "light_right", str); + Brush_Build(b); + } +} + +/* +================ +Brush_Print +================ +*/ +void Brush_Print(brush_t *b) { + int nFace = 0; + for (face_t * f = b->brush_faces; f; f = f->next) { + common->Printf("Face %i\n", nFace++); + common->Printf("%f %f %f\n", f->planepts[0][0], f->planepts[0][1], f->planepts[0][2]); + common->Printf("%f %f %f\n", f->planepts[1][0], f->planepts[1][1], f->planepts[1][2]); + common->Printf("%f %f %f\n", f->planepts[2][0], f->planepts[2][1], f->planepts[2][2]); + } +} + +/* +================ +Brush_MakeSidedCone + + Makes the current brush have the given number of 2d sides and turns it into a cone +================ +*/ +void Brush_MakeSidedCone(int sides) { + int i; + idVec3 mins, maxs; + brush_t *b; + texdef_t *texdef; + face_t *f; + idVec3 mid; + float width; + float sv, cv; + + if (sides < 3) { + Sys_Status("Bad sides number", 0); + return; + } + + if (!QE_SingleBrush()) { + Sys_Status("Must have a single brush selected", 0); + return; + } + + b = selected_brushes.next; + VectorCopy(b->mins, mins); + VectorCopy(b->maxs, maxs); + texdef = &g_qeglobals.d_texturewin.texdef; + + Brush_Free(b); + + // find center of brush + width = 8; + for (i = 0; i < 2; i++) { + mid[i] = (maxs[i] + mins[i]) * 0.5f; + if (maxs[i] - mins[i] > width) { + width = maxs[i] - mins[i]; + } + } + + width *= 0.5f; + + b = Brush_Alloc(); + + // create bottom face + f = Face_Alloc(); + f->texdef = *texdef; + f->next = b->brush_faces; + b->brush_faces = f; + + f->planepts[0][0] = mins[0]; + f->planepts[0][1] = mins[1]; + f->planepts[0][2] = mins[2]; + f->planepts[1][0] = maxs[0]; + f->planepts[1][1] = mins[1]; + f->planepts[1][2] = mins[2]; + f->planepts[2][0] = maxs[0]; + f->planepts[2][1] = maxs[1]; + f->planepts[2][2] = mins[2]; + + for (i = 0; i < sides; i++) { + f = Face_Alloc(); + f->texdef = *texdef; + f->next = b->brush_faces; + b->brush_faces = f; + + sv = sin(i * idMath::TWO_PI / sides); + cv = cos(i * idMath::TWO_PI / sides); + + f->planepts[0][0] = floor( mid[0] + width * cv + 0.5f ); + f->planepts[0][1] = floor( mid[1] + width * sv + 0.5f ); + f->planepts[0][2] = mins[2]; + + f->planepts[1][0] = mid[0]; + f->planepts[1][1] = mid[1]; + f->planepts[1][2] = maxs[2]; + + f->planepts[2][0] = floor( f->planepts[0][0] - width * sv + 0.5f ); + f->planepts[2][1] = floor( f->planepts[0][1] + width * cv + 0.5f ); + f->planepts[2][2] = maxs[2]; + } + + Brush_AddToList(b, &selected_brushes); + + Entity_LinkBrush(world_entity, b); + + Brush_Build(b); + + Sys_UpdateWindows(W_ALL); +} + +/* +================ +Brush_MakeSidedSphere + + Makes the current brushhave the given number of 2d sides and turns it into a sphere +================ +*/ +void Brush_MakeSidedSphere(int sides) { + int i, j; + idVec3 mins, maxs; + brush_t *b; + texdef_t *texdef; + face_t *f; + idVec3 mid; + float radius; + + if (sides < 4) { + Sys_Status("Bad sides number", 0); + return; + } + + if (!QE_SingleBrush()) { + Sys_Status("Must have a single brush selected", 0); + return; + } + + b = selected_brushes.next; + mins = b->mins; + maxs = b->maxs; + texdef = &g_qeglobals.d_texturewin.texdef; + + Brush_Free(b); + + // find center of brush + radius = 8; + for ( i = 0; i < 3; i++ ) { + mid[i] = (maxs[i] + mins[i]) * 0.5f; + if (maxs[i] - mins[i] > radius) { + radius = maxs[i] - mins[i]; + } + } + + radius *= 0.5f; + + b = Brush_Alloc(); + + for (i = 0; i < sides; i++) { + for (j = 0; j < sides - 1; j++) { + f = Face_Alloc(); + f->texdef = *texdef; + f->next = b->brush_faces; + b->brush_faces = f; + + f->planepts[0] = idPolar3(radius, idMath::TWO_PI * i / sides, idMath::PI * ((float)(j) / sides - 0.5f) ).ToVec3() + mid; + f->planepts[1] = idPolar3(radius, idMath::TWO_PI * i / sides, idMath::PI * ((float)(j+1) / sides - 0.5f) ).ToVec3() + mid; + f->planepts[2] = idPolar3(radius, idMath::TWO_PI * (i+1) / sides, idMath::PI * ((float)(j+1) / sides - 0.5f) ).ToVec3() + mid; + } + } + + Brush_AddToList(b, &selected_brushes); + + Entity_LinkBrush(world_entity, b); + + Brush_Build(b); + + Sys_UpdateWindows(W_ALL); +} + +extern void Face_FitTexture_BrushPrimit(face_t *f, idVec3 mins, idVec3 maxs, float nHeight, float nWidth); + +/* +================ +Face_FitTexture +================ +*/ +void Face_FitTexture(face_t *face, float nHeight, float nWidth) { + if (g_qeglobals.m_bBrushPrimitMode) { + idVec3 mins, maxs; + mins[0] = maxs[0] = 0; + Face_FitTexture_BrushPrimit(face, mins, maxs, nHeight, nWidth); + } + else { + /* + * winding_t *w; idBounds bounds; int i; float width, height, temp; float rot_width, + * rot_height; float cosv,sinv,ang; float min_t, min_s, max_t, max_s; float s,t; + * idVec3 vecs[2]; idVec3 coords[4]; texdef_t *td; if (nHeight < 1) { nHeight = 1; + * } if (nWidth < 1) { nWidth = 1; } bounds.Clear(); td = &face->texdef; w = + * face->face_winding; if (!w) { return; } for (i=0 ; inumpoints ; i++) { + * bounds.AddPoint( w->p[i] ); } // // get the current angle // ang = td->rotate / + * 180 * Q_PI; sinv = sin(ang); cosv = cos(ang); // get natural texture axis + * TextureAxisFromPlane(&face->plane, vecs[0], vecs[1]); min_s = DotProduct( + * bounds.b[0], vecs[0] ); min_t = DotProduct( bounds.b[0], vecs[1] ); max_s = + * DotProduct( bounds.b[1], vecs[0] ); max_t = DotProduct( bounds.b[1], vecs[1] ); + * width = max_s - min_s; height = max_t - min_t; coords[0][0] = min_s; + * coords[0][1] = min_t; coords[1][0] = max_s; coords[1][1] = min_t; coords[2][0] + * = min_s; coords[2][1] = max_t; coords[3][0] = max_s; coords[3][1] = max_t; + * min_s = min_t = 999999; max_s = max_t = -999999; for (i=0; i<4; i++) { s = cosv + * * coords[i][0] - sinv * coords[i][1]; t = sinv * coords[i][0] + cosv * + * coords[i][1]; if (i&1) { if (s > max_s) { max_s = s; } } else { if (s < min_s) + * { min_s = s; } if (i<2) { if (t < min_t) { min_t = t; } } else { if (t > max_t) + * { max_t = t; } } } } rot_width = (max_s - min_s); rot_height = (max_t - min_t); + * td->scale[0] = + * -(rot_width/((float)(face->d_texture->GetEditorImage()->uploadWidth*nWidth))); + * td->scale[1] = + * -(rot_height/((float)(face->d_texture->GetEditorImage()->uploadHeight*nHeight))); + * td->shift[0] = min_s/td->scale[0]; temp = (int)(td->shift[0] / + * (face->d_texture->GetEditorImage()->uploadWidth*nWidth)); temp = + * (temp+1)*face->d_texture->GetEditorImage()->uploadWidth*nWidth; td->shift[0] = + * (int)(temp - + * td->shift[0])%(face->d_texture->GetEditorImage()->uploadWidth*nWidth); + * td->shift[1] = min_t/td->scale[1]; temp = (int)(td->shift[1] / + * (face->d_texture->GetEditorImage()->uploadHeight*nHeight)); temp = + * (temp+1)*(face->d_texture->GetEditorImage()->uploadHeight*nHeight); + * td->shift[1] = (int)(temp - + * td->shift[1])%(face->d_texture->GetEditorImage()->uploadHeight*nHeight); + */ + } +} + +/* +================ +Brush_FitTexture +================ +*/ +void Brush_FitTexture(brush_t *b, float nHeight, float nWidth) { + face_t *face; + for (face = b->brush_faces; face; face = face->next) { + Face_FitTexture(face, nHeight, nWidth); + } +} + +void Brush_GetBounds( brush_t *b, idBounds &bo ) { + if ( b == NULL ) { + return; + } + + bo.Clear(); + bo.AddPoint( b->mins ); + bo.AddPoint( b->maxs ); + + if ( b->owner->curve ) { + int c = b->owner->curve->GetNumValues(); + for ( int i = 0; i < c; i++ ) { + bo.AddPoint ( b->owner->curve->GetValue( i ) ); + } + } + +} diff --git a/src/tools/radiant/EditorBrush.h b/src/tools/radiant/EditorBrush.h new file mode 100644 index 0000000..b67506d --- /dev/null +++ b/src/tools/radiant/EditorBrush.h @@ -0,0 +1,79 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +// brush.h + +brush_t * Brush_Alloc(); +void Brush_Free (brush_t *b, bool bRemoveNode = true); +int Brush_MemorySize(brush_t *b); +void Brush_MakeSided (int sides); +void Brush_MakeSidedCone (int sides); +void Brush_Move (brush_t *b, const idVec3 move, bool bSnap = true, bool updateOrigin = true); +int Brush_MoveVertex(brush_t *b, const idVec3 &vertex, const idVec3 &delta, idVec3 &end, bool bSnap); +void Brush_ResetFaceOriginals(brush_t *b); +brush_t * Brush_Parse (const idVec3 origin); +face_t * Brush_Ray (idVec3 origin, idVec3 dir, brush_t *b, float *dist, bool testPrimitive = false); +void Brush_RemoveFromList (brush_t *b); +void Brush_AddToList (brush_t *b, brush_t *list); +void Brush_Build(brush_t *b, bool bSnap = true, bool bMarkMap = true, bool bConvert = false, bool updateLights = true); +void Brush_BuildWindings( brush_t *b, bool bSnap = true, bool keepOnPlaneWinding = false, bool updateLights = true, bool makeFacePlanes = true ); +brush_t * Brush_Clone (brush_t *b); +brush_t * Brush_FullClone(brush_t *b); +brush_t * Brush_Create (idVec3 mins, idVec3 maxs, texdef_t *texdef); +void Brush_Draw( brush_t *b, bool bSelected = false); +void Brush_DrawXY(brush_t *b, int nViewType, bool bSelected = false, bool ignoreViewType = false); +void Brush_SplitBrushByFace (brush_t *in, face_t *f, brush_t **front, brush_t **back); +void Brush_SelectFaceForDragging (brush_t *b, face_t *f, bool shear); +void Brush_SetTexture (brush_t *b, texdef_t *texdef, brushprimit_texdef_t *brushprimit_texdef, bool bFitScale = false); +void Brush_SideSelect (brush_t *b, idVec3 origin, idVec3 dir, bool shear); +void Brush_SnapToGrid(brush_t *pb); +void Brush_Rotate(brush_t *b, idVec3 vAngle, idVec3 vOrigin, bool bBuild = true); +void Brush_MakeSidedSphere(int sides); +void Brush_Write (brush_t *b, FILE *f, const idVec3 &origin, bool newFormat); +void Brush_Write (brush_t *b, CMemFile* pMemFile, const idVec3 &origin, bool NewFormat); +void Brush_RemoveEmptyFaces ( brush_t *b ); +idWinding * Brush_MakeFaceWinding (brush_t *b, face_t *face, bool keepOnPlaneWinding = false); +void Brush_SetTextureName(brush_t *b, const char *name); +void Brush_Print(brush_t* b); +void Brush_FitTexture( brush_t *b, float height, float width ); +void Brush_SetEpair(brush_t *b, const char *pKey, const char *pValue); +const char *Brush_GetKeyValue(brush_t *b, const char *pKey); +const char *Brush_Name(brush_t *b); +void Brush_RebuildBrush(brush_t *b, idVec3 vMins, idVec3 vMaxs, bool patch = true); +void Brush_GetBounds( brush_t *b, idBounds &bo ); + +face_t * Face_Alloc( void ); +void Face_Free( face_t *f ); +face_t * Face_Clone (face_t *f); +void Face_MakePlane (face_t *f); +void Face_Draw( face_t *face ); +void Face_TextureVectors (face_t *f, float STfromXYZ[2][4]); +void Face_FitTexture( face_t * face, float height, float width ); +void SetFaceTexdef (brush_t *b, face_t *f, texdef_t *texdef, brushprimit_texdef_t *brushprimit_texdef, bool bFitScale = false); + +int AddPlanept (idVec3 *f); diff --git a/src/tools/radiant/EditorBrushPrimit.cpp b/src/tools/radiant/EditorBrushPrimit.cpp new file mode 100644 index 0000000..dd64c80 --- /dev/null +++ b/src/tools/radiant/EditorBrushPrimit.cpp @@ -0,0 +1,1239 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" + +#define ZERO_EPSILON 1.0E-6 + +class idVec3D { +public: + double x, y, z; + double & operator[]( const int index ) { + return (&x)[index]; + } + void Zero() { + x = y = z = 0.0; + } +}; + +// +// ======================================================================================================================= +// compute a determinant using Sarrus rule ++timo "inline" this with a macro NOTE:: the three idVec3D are understood as +// columns of the matrix +// ======================================================================================================================= +// +double SarrusDet(idVec3D a, idVec3D b, idVec3D c) { + return (double)a[0] * (double)b[1] * (double)c[2] + (double)b[0] * (double)c[1] * (double)a[2] + (double)c[0] * (double)a[1] * (double)b[2] - (double)c[0] * (double)b[1] * (double)a[2] - (double)a[1] * (double)b[0] * (double)c[2] - (double)a[0] * (double)b[2] * (double)c[1]; +} + +// +// ======================================================================================================================= +// ++timo replace everywhere texX by texS etc. ( > and in q3map !) NOTE:: ComputeAxisBase here and in q3map code must +// always BE THE SAME ! WARNING:: special case behaviour of atan2(y,x) <-> atan(y/x) might not be the same everywhere +// when x == 0 rotation by (0,RotY,RotZ) assigns X to normal +// ======================================================================================================================= +// +void ComputeAxisBase(idVec3 &normal, idVec3D &texS, idVec3D &texT) { + double RotY, RotZ; + + // do some cleaning + if (idMath::Fabs(normal[0]) < 1e-6) { + normal[0] = 0.0f; + } + + if (idMath::Fabs(normal[1]) < 1e-6) { + normal[1] = 0.0f; + } + + if (idMath::Fabs(normal[2]) < 1e-6) { + normal[2] = 0.0f; + } + + RotY = -atan2(normal[2], idMath::Sqrt(normal[1] * normal[1] + normal[0] * normal[0])); + RotZ = atan2(normal[1], normal[0]); + + // rotate (0,1,0) and (0,0,1) to compute texS and texT + texS[0] = -sin(RotZ); + texS[1] = cos(RotZ); + texS[2] = 0; + + // the texT vector is along -Z ( T texture coorinates axis ) + texT[0] = -sin(RotY) * cos(RotZ); + texT[1] = -sin(RotY) * sin(RotZ); + texT[2] = -cos(RotY); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void FaceToBrushPrimitFace(face_t *f) { + idVec3D texX, texY; + idVec3D proj; + + // ST of (0,0) (1,0) (0,1) + idVec5 ST[3]; // [ point index ] [ xyz ST ] + + // + // ++timo not used as long as brushprimit_texdef and texdef are static + // f->brushprimit_texdef.contents=f->texdef.contents; + // f->brushprimit_texdef.flags=f->texdef.flags; + // f->brushprimit_texdef.value=f->texdef.value; + // strcpy(f->brushprimit_texdef.name,f->texdef.name); + // +#ifdef _DEBUG + if (f->plane[0] == 0.0f && f->plane[1] == 0.0f && f->plane[2] == 0.0f) { + common->Printf("Warning : f->plane.normal is (0,0,0) in FaceToBrushPrimitFace\n"); + } + + // check d_texture + if (!f->d_texture) { + common->Printf("Warning : f.d_texture is NULL in FaceToBrushPrimitFace\n"); + return; + } +#endif + // compute axis base + ComputeAxisBase(f->plane.Normal(), texX, texY); + + // compute projection vector + VectorCopy( f->plane, proj ); + VectorScale(proj, -f->plane[3], proj); + + // + // (0,0) in plane axis base is (0,0,0) in world coordinates + projection on the + // affine plane (1,0) in plane axis base is texX in world coordinates + projection + // on the affine plane (0,1) in plane axis base is texY in world coordinates + + // projection on the affine plane use old texture code to compute the ST coords of + // these points + // + VectorCopy(proj, ST[0]); + EmitTextureCoordinates(ST[0], f->d_texture, f); + VectorCopy(texX, ST[1]); + VectorAdd(ST[1], proj, ST[1]); + EmitTextureCoordinates(ST[1], f->d_texture, f); + VectorCopy(texY, ST[2]); + VectorAdd(ST[2], proj, ST[2]); + EmitTextureCoordinates(ST[2], f->d_texture, f); + + // compute texture matrix + f->brushprimit_texdef.coords[0][2] = ST[0][3]; + f->brushprimit_texdef.coords[1][2] = ST[0][4]; + f->brushprimit_texdef.coords[0][0] = ST[1][3] - f->brushprimit_texdef.coords[0][2]; + f->brushprimit_texdef.coords[1][0] = ST[1][4] - f->brushprimit_texdef.coords[1][2]; + f->brushprimit_texdef.coords[0][1] = ST[2][3] - f->brushprimit_texdef.coords[0][2]; + f->brushprimit_texdef.coords[1][1] = ST[2][4] - f->brushprimit_texdef.coords[1][2]; +} + +// +// ======================================================================================================================= +// compute texture coordinates for the winding points +// ======================================================================================================================= +// +void EmitBrushPrimitTextureCoordinates(face_t *f, idWinding *w, patchMesh_t *patch) { + idVec3D texX, texY; + double x, y; + + if (f== NULL || (w == NULL && patch == NULL)) { + return; + } + + // compute axis base + ComputeAxisBase(f->plane.Normal(), texX, texY); + + // + // in case the texcoords matrix is empty, build a default one same behaviour as if + // scale[0]==0 && scale[1]==0 in old code + // + if ( f->brushprimit_texdef.coords[0][0] == 0 && + f->brushprimit_texdef.coords[1][0] == 0 && + f->brushprimit_texdef.coords[0][1] == 0 && + f->brushprimit_texdef.coords[1][1] == 0 ) { + f->brushprimit_texdef.coords[0][0] = 1.0f; + f->brushprimit_texdef.coords[1][1] = 1.0f; + ConvertTexMatWithQTexture(&f->brushprimit_texdef, NULL, &f->brushprimit_texdef, f->d_texture); + } + + int i; + if (w) { + for (i = 0; i < w->GetNumPoints(); i++) { + x = DotProduct((*w)[i], texX); + y = DotProduct((*w)[i], texY); + (*w)[i][3] = f->brushprimit_texdef.coords[0][0] * x + f->brushprimit_texdef.coords[0][1] * y + f->brushprimit_texdef.coords[0][2]; + (*w)[i][4] = f->brushprimit_texdef.coords[1][0] * x + f->brushprimit_texdef.coords[1][1] * y + f->brushprimit_texdef.coords[1][2]; + } + } + + if (patch) { + int j; + for ( i = 0; i < patch->width; i++ ) { + for ( j = 0; j < patch->height; j++ ) { + x = DotProduct(patch->ctrl(i, j).xyz, texX); + y = DotProduct(patch->ctrl(i, j).xyz, texY); + patch->ctrl(i, j).st.x = f->brushprimit_texdef.coords[0][0] * x + f->brushprimit_texdef.coords[0][1] * y + f->brushprimit_texdef.coords[0][2]; + patch->ctrl(i, j).st.y = f->brushprimit_texdef.coords[1][0] * x + f->brushprimit_texdef.coords[1][1] * y + f->brushprimit_texdef.coords[1][2]; + } + } + } +} + +// +// ======================================================================================================================= +// parse a brush in brush primitive format +// ======================================================================================================================= +// +void BrushPrimit_Parse(brush_t *b, bool newFormat, const idVec3 origin) { + face_t *f; + int i, j; + GetToken(true); + if (strcmp(token, "{")) { + Warning("parsing brush primitive"); + return; + } + + do { + if (!GetToken(true)) { + break; + } + + if (!strcmp(token, "}")) { + break; + } + + // reading of b->epairs if any + if (strcmp(token, "(")) { + ParseEpair(&b->epairs); + } + else { // it's a face + f = Face_Alloc(); + f->next = NULL; + if (!b->brush_faces) { + b->brush_faces = f; + } + else { + face_t *scan; + for (scan = b->brush_faces; scan->next; scan = scan->next) + ; + scan->next = f; + } + + if (newFormat) { + // read the three point plane definition + idPlane plane; + for (j = 0; j < 4; j++) { + GetToken(false); + plane[j] = atof(token); + } + + f->plane = plane; + f->originalPlane = plane; + f->dirty = false; + + //idWinding *w = Brush_MakeFaceWinding(b, f, true); + idWinding w; + w.BaseForPlane( plane ); + + for (j = 0; j < 3; j++) { + f->planepts[j].x = w[j].x + origin.x; + f->planepts[j].y = w[j].y + origin.y; + f->planepts[j].z = w[j].z + origin.z; + } + + GetToken(false); + } + else { + for (i = 0; i < 3; i++) { + if (i != 0) { + GetToken(true); + } + + if (strcmp(token, "(")) { + Warning("parsing brush"); + return; + } + + for (j = 0; j < 3; j++) { + GetToken(false); + f->planepts[i][j] = atof(token); + } + + GetToken(false); + if (strcmp(token, ")")) { + Warning("parsing brush"); + return; + } + } + } + + // texture coordinates + GetToken(false); + if (strcmp(token, "(")) { + Warning("parsing brush primitive"); + return; + } + + GetToken(false); + if (strcmp(token, "(")) { + Warning("parsing brush primitive"); + return; + } + + for (j = 0; j < 3; j++) { + GetToken(false); + f->brushprimit_texdef.coords[0][j] = atof(token); + } + + GetToken(false); + if (strcmp(token, ")")) { + Warning("parsing brush primitive"); + return; + } + + GetToken(false); + if (strcmp(token, "(")) { + Warning("parsing brush primitive"); + return; + } + + for (j = 0; j < 3; j++) { + GetToken(false); + f->brushprimit_texdef.coords[1][j] = atof(token); + } + + GetToken(false); + if (strcmp(token, ")")) { + Warning("parsing brush primitive"); + return; + } + + GetToken(false); + if (strcmp(token, ")")) { + Warning("parsing brush primitive"); + return; + } + + // read the texturedef + GetToken(false); + + // strcpy(f->texdef.name, token); + if (g_qeglobals.mapVersion < 2.0) { + f->texdef.SetName(va("textures/%s", token)); + } + else { + f->texdef.SetName(token); + } + + if (TokenAvailable()) { + GetToken(false); + GetToken(false); + GetToken(false); + f->texdef.value = atoi(token); + } + } + } while (1); +} + +// +// ======================================================================================================================= +// compute a fake shift scale rot representation from the texture matrix these shift scale rot values are to be +// understood in the local axis base +// ======================================================================================================================= +// +void TexMatToFakeTexCoords(float texMat[2][3], float shift[2], float *rot, float scale[2]) +{ +#ifdef _DEBUG + + // check this matrix is orthogonal + if (idMath::Fabs(texMat[0][0] * texMat[0][1] + texMat[1][0] * texMat[1][1]) > ZERO_EPSILON) { + common->Printf("Warning : non orthogonal texture matrix in TexMatToFakeTexCoords\n"); + } +#endif + scale[0] = idMath::Sqrt(texMat[0][0] * texMat[0][0] + texMat[1][0] * texMat[1][0]); + scale[1] = idMath::Sqrt(texMat[0][1] * texMat[0][1] + texMat[1][1] * texMat[1][1]); +#ifdef _DEBUG + if (scale[0] < ZERO_EPSILON || scale[1] < ZERO_EPSILON) { + common->Printf("Warning : unexpected scale==0 in TexMatToFakeTexCoords\n"); + } +#endif + // compute rotate value + if (idMath::Fabs(texMat[0][0]) < ZERO_EPSILON) + { +#ifdef _DEBUG + // check brushprimit_texdef[1][0] is not zero + if (idMath::Fabs(texMat[1][0]) < ZERO_EPSILON) { + common->Printf("Warning : unexpected texdef[1][0]==0 in TexMatToFakeTexCoords\n"); + } +#endif + // rotate is +-90 + if (texMat[1][0] > 0) { + *rot = 90.0f; + } + else { + *rot = -90.0f; + } + } + else { + *rot = RAD2DEG(atan2(texMat[1][0], texMat[0][0])); + } + + shift[0] = -texMat[0][2]; + shift[1] = texMat[1][2]; +} + +// +// ======================================================================================================================= +// compute back the texture matrix from fake shift scale rot the matrix returned must be understood as a qtexture_t +// with width=2 height=2 ( the default one ) +// ======================================================================================================================= +// +void FakeTexCoordsToTexMat(float shift[2], float rot, float scale[2], float texMat[2][3]) { + texMat[0][0] = scale[0] * cos(DEG2RAD(rot)); + texMat[1][0] = scale[0] * sin(DEG2RAD(rot)); + texMat[0][1] = -1.0f * scale[1] * sin(DEG2RAD(rot)); + texMat[1][1] = scale[1] * cos(DEG2RAD(rot)); + texMat[0][2] = -shift[0]; + texMat[1][2] = shift[1]; +} + +// +// ======================================================================================================================= +// convert a texture matrix between two qtexture_t if NULL for qtexture_t, basic 2x2 texture is assumed ( straight +// mapping between s/t coordinates and geometric coordinates ) +// ======================================================================================================================= +// +void ConvertTexMatWithQTexture(float texMat1[2][3], const idMaterial *qtex1, float texMat2[2][3], const idMaterial *qtex2, float sScale = 1.0, float tScale = 1.0) { + float s1, s2; + s1 = (qtex1 ? static_cast(qtex1->GetEditorImage()->uploadWidth) : 2.0f) / (qtex2 ? static_cast(qtex2->GetEditorImage()->uploadWidth) : 2.0f); + s2 = (qtex1 ? static_cast(qtex1->GetEditorImage()->uploadHeight) : 2.0f) / (qtex2 ? static_cast(qtex2->GetEditorImage()->uploadHeight) : 2.0f); + s1 *= sScale; + s2 *= tScale; + texMat2[0][0] = s1 * texMat1[0][0]; + texMat2[0][1] = s1 * texMat1[0][1]; + texMat2[0][2] = s1 * texMat1[0][2]; + texMat2[1][0] = s2 * texMat1[1][0]; + texMat2[1][1] = s2 * texMat1[1][1]; + texMat2[1][2] = s2 * texMat1[1][2]; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void ConvertTexMatWithQTexture(brushprimit_texdef_t *texMat1, const idMaterial *qtex1, brushprimit_texdef_t *texMat2, const idMaterial *qtex2, float sScale, float tScale) { + ConvertTexMatWithQTexture(texMat1->coords, qtex1, texMat2->coords, qtex2, sScale, tScale); +} + + +// +// ======================================================================================================================= +// texture locking +// ======================================================================================================================= +// +void Face_MoveTexture_BrushPrimit(face_t *f, idVec3 delta) { + idVec3D texS, texT; + double tx, ty; + idVec3D M[3]; // columns of the matrix .. easier that way + double det; + idVec3D D[2]; + + // compute plane axis base ( doesn't change with translation ) + ComputeAxisBase(f->plane.Normal(), texS, texT); + + // compute translation vector in plane axis base + tx = DotProduct(delta, texS); + ty = DotProduct(delta, texT); + + // fill the data vectors + M[0][0] = tx; + M[0][1] = 1.0f + tx; + M[0][2] = tx; + M[1][0] = ty; + M[1][1] = ty; + M[1][2] = 1.0f + ty; + M[2][0] = 1.0f; + M[2][1] = 1.0f; + M[2][2] = 1.0f; + D[0][0] = f->brushprimit_texdef.coords[0][2]; + D[0][1] = f->brushprimit_texdef.coords[0][0] + f->brushprimit_texdef.coords[0][2]; + D[0][2] = f->brushprimit_texdef.coords[0][1] + f->brushprimit_texdef.coords[0][2]; + D[1][0] = f->brushprimit_texdef.coords[1][2]; + D[1][1] = f->brushprimit_texdef.coords[1][0] + f->brushprimit_texdef.coords[1][2]; + D[1][2] = f->brushprimit_texdef.coords[1][1] + f->brushprimit_texdef.coords[1][2]; + + // solve + det = SarrusDet(M[0], M[1], M[2]); + f->brushprimit_texdef.coords[0][0] = SarrusDet(D[0], M[1], M[2]) / det; + f->brushprimit_texdef.coords[0][1] = SarrusDet(M[0], D[0], M[2]) / det; + f->brushprimit_texdef.coords[0][2] = SarrusDet(M[0], M[1], D[0]) / det; + f->brushprimit_texdef.coords[1][0] = SarrusDet(D[1], M[1], M[2]) / det; + f->brushprimit_texdef.coords[1][1] = SarrusDet(M[0], D[1], M[2]) / det; + f->brushprimit_texdef.coords[1][2] = SarrusDet(M[0], M[1], D[1]) / det; +} + +// +// ======================================================================================================================= +// call Face_MoveTexture_BrushPrimit after idVec3D computation +// ======================================================================================================================= +// +void Select_ShiftTexture_BrushPrimit(face_t *f, float x, float y, bool autoAdjust) { +#if 0 + idVec3D texS, texT; + idVec3D delta; + ComputeAxisBase(f->plane.normal, texS, texT); + VectorScale(texS, x, texS); + VectorScale(texT, y, texT); + VectorCopy(texS, delta); + VectorAdd(delta, texT, delta); + Face_MoveTexture_BrushPrimit(f, delta); +#else + if (autoAdjust) { + x /= f->d_texture->GetEditorImage()->uploadWidth; + y /= f->d_texture->GetEditorImage()->uploadHeight; + } + f->brushprimit_texdef.coords[0][2] += x; + f->brushprimit_texdef.coords[1][2] += y; + EmitBrushPrimitTextureCoordinates(f, f->face_winding); +#endif +} + +// +// ======================================================================================================================= +// best fitted 2D vector is x.X+y.Y +// ======================================================================================================================= +// +void ComputeBest2DVector(idVec3 v, idVec3 X, idVec3 Y, int &x, int &y) { + double sx, sy; + sx = DotProduct(v, X); + sy = DotProduct(v, Y); + if (idMath::Fabs(sy) > idMath::Fabs(sx)) { + x = 0; + if (sy > 0.0) { + y = 1; + } + else { + y = -1; + } + } + else { + y = 0; + if (sx > 0.0) { + x = 1; + } + else { + x = -1; + } + } +} + +// +// ======================================================================================================================= +// in many case we know three points A,B,C in two axis base B1 and B2 and we want the matrix M so that A(B1) = T * +// A(B2) NOTE: 2D homogeneous space stuff NOTE: we don't do any check to see if there's a solution or we have a +// particular case .. need to make sure before calling NOTE: the third coord of the A,B,C point is ignored NOTE: see +// the commented out section to fill M and D ++timo TODO: update the other members to use this when possible +// ======================================================================================================================= +// +void MatrixForPoints(idVec3D M[3], idVec3D D[2], brushprimit_texdef_t *T) { + // + // idVec3D M[3]; // columns of the matrix .. easier that way (the indexing is not + // standard! it's column-line .. later computations are easier that way) + // + double det; + + // idVec3D D[2]; + M[2][0] = 1.0f; + M[2][1] = 1.0f; + M[2][2] = 1.0f; +#if 0 + + // fill the data vectors + M[0][0] = A2[0]; + M[0][1] = B2[0]; + M[0][2] = C2[0]; + M[1][0] = A2[1]; + M[1][1] = B2[1]; + M[1][2] = C2[1]; + M[2][0] = 1.0f; + M[2][1] = 1.0f; + M[2][2] = 1.0f; + D[0][0] = A1[0]; + D[0][1] = B1[0]; + D[0][2] = C1[0]; + D[1][0] = A1[1]; + D[1][1] = B1[1]; + D[1][2] = C1[1]; +#endif + // solve + det = SarrusDet(M[0], M[1], M[2]); + T->coords[0][0] = SarrusDet(D[0], M[1], M[2]) / det; + T->coords[0][1] = SarrusDet(M[0], D[0], M[2]) / det; + T->coords[0][2] = SarrusDet(M[0], M[1], D[0]) / det; + T->coords[1][0] = SarrusDet(D[1], M[1], M[2]) / det; + T->coords[1][1] = SarrusDet(M[0], D[1], M[2]) / det; + T->coords[1][2] = SarrusDet(M[0], M[1], D[1]) / det; +} + +// +// ======================================================================================================================= +// ++timo FIXME quick'n dirty hack, doesn't care about current texture settings (angle) can be improved .. bug #107311 +// mins and maxs are the face bounding box ++timo fixme: we use the face info, mins and maxs are irrelevant +// ======================================================================================================================= +// +void Face_FitTexture_BrushPrimit(face_t *f, idVec3 mins, idVec3 maxs, float height, float width) { + idVec3D BBoxSTMin, BBoxSTMax; + idWinding *w; + int i, j; + double val; + idVec3D M[3], D[2]; + + // idVec3D N[2],Mf[2]; + brushprimit_texdef_t N; + idVec3D Mf[2]; + + + + //memset(f->brushprimit_texdef.coords, 0, sizeof(f->brushprimit_texdef.coords)); + //f->brushprimit_texdef.coords[0][0] = 1.0f; + //f->brushprimit_texdef.coords[1][1] = 1.0f; + //ConvertTexMatWithQTexture(&f->brushprimit_texdef, NULL, &f->brushprimit_texdef, f->d_texture); + // + // we'll be working on a standardized texture size ConvertTexMatWithQTexture( + // &f->brushprimit_texdef, f->d_texture, &f->brushprimit_texdef, NULL ); compute + // the BBox in ST coords + // + EmitBrushPrimitTextureCoordinates(f, f->face_winding); + BBoxSTMin[0] = BBoxSTMin[1] = BBoxSTMin[2] = 999999; + BBoxSTMax[0] = BBoxSTMax[1] = BBoxSTMax[2] = -999999; + + w = f->face_winding; + if (w) { + for (i = 0; i < w->GetNumPoints(); i++) { + // AddPointToBounds in 2D on (S,T) coordinates + for (j = 0; j < 2; j++) { + val = (*w)[i][j + 3]; + if (val < BBoxSTMin[j]) { + BBoxSTMin[j] = val; + } + + if (val > BBoxSTMax[j]) { + BBoxSTMax[j] = val; + } + } + } + } + + // + // we have the three points of the BBox (BBoxSTMin[0].BBoxSTMin[1]) + // (BBoxSTMax[0],BBoxSTMin[1]) (BBoxSTMin[0],BBoxSTMax[1]) in ST space the BP + // matrix we are looking for gives (0,0) (nwidth,0) (0,nHeight) coordinates in + // (Sfit,Tfit) space to these three points we have A(Sfit,Tfit) = (0,0) = Mf * + // A(TexS,TexT) = N * M * A(TexS,TexT) = N * A(S,T) so we solve the system for N + // and then Mf = N * M + // + M[0][0] = BBoxSTMin[0]; + M[0][1] = BBoxSTMax[0]; + M[0][2] = BBoxSTMin[0]; + M[1][0] = BBoxSTMin[1]; + M[1][1] = BBoxSTMin[1]; + M[1][2] = BBoxSTMax[1]; + D[0][0] = 0.0f; + D[0][1] = width; + D[0][2] = 0.0f; + D[1][0] = 0.0f; + D[1][1] = 0.0f; + D[1][2] = height; + MatrixForPoints(M, D, &N); + +#if 0 + + // + // FIT operation gives coordinates of three points of the bounding box in (S',T'), + // our target axis base A(S',T')=(0,0) B(S',T')=(nWidth,0) C(S',T')=(0,nHeight) + // and we have them in (S,T) axis base: A(S,T)=(BBoxSTMin[0],BBoxSTMin[1]) + // B(S,T)=(BBoxSTMax[0],BBoxSTMin[1]) C(S,T)=(BBoxSTMin[0],BBoxSTMax[1]) we + // compute the N transformation so that: A(S',T') = N * A(S,T) + // + N[0][0] = (BBoxSTMax[0] - BBoxSTMin[0]) / width; + N[0][1] = 0.0f; + N[0][2] = BBoxSTMin[0]; + N[1][0] = 0.0f; + N[1][1] = (BBoxSTMax[1] - BBoxSTMin[1]) / height; + N[1][2] = BBoxSTMin[1]; +#endif + // the final matrix is the product (Mf stands for Mfit) + Mf[0][0] = N.coords[0][0] * + f->brushprimit_texdef.coords[0][0] + + N.coords[0][1] * + f->brushprimit_texdef.coords[1][0]; + Mf[0][1] = N.coords[0][0] * + f->brushprimit_texdef.coords[0][1] + + N.coords[0][1] * + f->brushprimit_texdef.coords[1][1]; + Mf[0][2] = N.coords[0][0] * + f->brushprimit_texdef.coords[0][2] + + N.coords[0][1] * + f->brushprimit_texdef.coords[1][2] + + N.coords[0][2]; + Mf[1][0] = N.coords[1][0] * + f->brushprimit_texdef.coords[0][0] + + N.coords[1][1] * + f->brushprimit_texdef.coords[1][0]; + Mf[1][1] = N.coords[1][0] * + f->brushprimit_texdef.coords[0][1] + + N.coords[1][1] * + f->brushprimit_texdef.coords[1][1]; + Mf[1][2] = N.coords[1][0] * + f->brushprimit_texdef.coords[0][2] + + N.coords[1][1] * + f->brushprimit_texdef.coords[1][2] + + N.coords[1][2]; + + // copy back + VectorCopy(Mf[0], f->brushprimit_texdef.coords[0]); + VectorCopy(Mf[1], f->brushprimit_texdef.coords[1]); + + // + // handle the texture size ConvertTexMatWithQTexture( &f->brushprimit_texdef, + // NULL, &f->brushprimit_texdef, f->d_texture ); + // +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Face_ScaleTexture_BrushPrimit(face_t *face, float sS, float sT) { + if (!g_qeglobals.m_bBrushPrimitMode) { + Sys_Status("BP mode required\n"); + return; + } + + brushprimit_texdef_t *pBP = &face->brushprimit_texdef; + BPMatScale(pBP->coords, sS, sT); + + // now emit the coordinates on the winding + EmitBrushPrimitTextureCoordinates(face, face->face_winding); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Face_RotateTexture_BrushPrimit(face_t *face, float amount, idVec3 origin) { + brushprimit_texdef_t *pBP = &face->brushprimit_texdef; + if (amount) { + float x = pBP->coords[0][0]; + float y = pBP->coords[0][1]; + float x1 = pBP->coords[1][0]; + float y1 = pBP->coords[1][1]; + float s = sin( DEG2RAD( amount ) ); + float c = cos( DEG2RAD( amount ) ); + pBP->coords[0][0] = (((x - origin[0]) * c) - ((y - origin[1]) * s)) + origin[0]; + pBP->coords[0][1] = (((x - origin[0]) * s) + ((y - origin[1]) * c)) + origin[1]; + pBP->coords[1][0] = (((x1 - origin[0]) * c) - ((y1 - origin[1]) * s)) + origin[0]; + pBP->coords[1][1] = (((x1 - origin[0]) * s) + ((y1 - origin[1]) * c)) + origin[1]; + EmitBrushPrimitTextureCoordinates(face, face->face_winding); + } +} + +// +// TEXTURE LOCKING (Relevant to the editor only?) +// internally used for texture locking on rotation and flipping the general +// algorithm is the same for both lockings, it's only the geometric transformation +// part that changes so I wanted to keep it in a single function if there are more +// linear transformations that need the locking, going to a C++ or code pointer +// solution would be best (but right now I want to keep brush_primit.cpp striclty +// C) +// +bool txlock_bRotation; + +// rotation locking params +int txl_nAxis; +double txl_fDeg; +idVec3D txl_vOrigin; + +// flip locking params +idVec3D txl_matrix[3]; +idVec3D txl_origin; + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void TextureLockTransformation_BrushPrimit(face_t *f) { + idVec3D Orig, texS, texT; // axis base of initial plane + + // used by transformation algo + idVec3D temp; + int j; + //idVec3D vRotate; // rotation vector + + idVec3D rOrig, rvecS, rvecT; // geometric transformation of (0,0) (1,0) (0,1) { initial plane axis base } + idVec3 rNormal; + idVec3D rtexS, rtexT; // axis base for the transformed plane + idVec3D lOrig, lvecS, lvecT; // [2] are not used ( but usefull for debugging ) + idVec3D M[3]; + double det; + idVec3D D[2]; + + // silence compiler warnings + rOrig.Zero(); + rvecS = rOrig; + rvecT = rOrig; + rNormal.x = rOrig.x; + rNormal.y = rOrig.y; + rNormal.z = rOrig.z; + + // compute plane axis base + ComputeAxisBase(f->plane.Normal(), texS, texT); + Orig.x = vec3_origin.x; + Orig.y = vec3_origin.y; + Orig.z = vec3_origin.z; + + // + // compute coordinates of (0,0) (1,0) (0,1) ( expressed in initial plane axis base + // ) after transformation (0,0) (1,0) (0,1) ( expressed in initial plane axis base + // ) <-> (0,0,0) texS texT ( expressed world axis base ) input: Orig, texS, texT + // (and the global locking params) ouput: rOrig, rvecS, rvecT, rNormal + // + if (txlock_bRotation) { +/* + // rotation vector + vRotate.x = vec3_origin.x; + vRotate.y = vec3_origin.y; + vRotate.z = vec3_origin.z; + vRotate[txl_nAxis] = txl_fDeg; + VectorRotate3Origin(Orig, vRotate, txl_vOrigin, rOrig); + VectorRotate3Origin(texS, vRotate, txl_vOrigin, rvecS); + VectorRotate3Origin(texT, vRotate, txl_vOrigin, rvecT); + + // compute normal of plane after rotation + VectorRotate3(f->plane.Normal(), vRotate, rNormal); +*/ + } + else { + VectorSubtract(Orig, txl_origin, temp); + for (j = 0; j < 3; j++) { + rOrig[j] = DotProduct(temp, txl_matrix[j]) + txl_origin[j]; + } + + VectorSubtract(texS, txl_origin, temp); + for (j = 0; j < 3; j++) { + rvecS[j] = DotProduct(temp, txl_matrix[j]) + txl_origin[j]; + } + + VectorSubtract(texT, txl_origin, temp); + for (j = 0; j < 3; j++) { + rvecT[j] = DotProduct(temp, txl_matrix[j]) + txl_origin[j]; + } + + // + // we also need the axis base of the target plane, apply the transformation matrix + // to the normal too.. + // + for (j = 0; j < 3; j++) { + rNormal[j] = DotProduct(f->plane, txl_matrix[j]); + } + } + + // compute rotated plane axis base + ComputeAxisBase(rNormal, rtexS, rtexT); + + // compute S/T coordinates of the three points in rotated axis base ( in M matrix ) + lOrig[0] = DotProduct(rOrig, rtexS); + lOrig[1] = DotProduct(rOrig, rtexT); + lvecS[0] = DotProduct(rvecS, rtexS); + lvecS[1] = DotProduct(rvecS, rtexT); + lvecT[0] = DotProduct(rvecT, rtexS); + lvecT[1] = DotProduct(rvecT, rtexT); + M[0][0] = lOrig[0]; + M[1][0] = lOrig[1]; + M[2][0] = 1.0f; + M[0][1] = lvecS[0]; + M[1][1] = lvecS[1]; + M[2][1] = 1.0f; + M[0][2] = lvecT[0]; + M[1][2] = lvecT[1]; + M[2][2] = 1.0f; + + // fill data vector + D[0][0] = f->brushprimit_texdef.coords[0][2]; + D[0][1] = f->brushprimit_texdef.coords[0][0] + f->brushprimit_texdef.coords[0][2]; + D[0][2] = f->brushprimit_texdef.coords[0][1] + f->brushprimit_texdef.coords[0][2]; + D[1][0] = f->brushprimit_texdef.coords[1][2]; + D[1][1] = f->brushprimit_texdef.coords[1][0] + f->brushprimit_texdef.coords[1][2]; + D[1][2] = f->brushprimit_texdef.coords[1][1] + f->brushprimit_texdef.coords[1][2]; + + // solve + det = SarrusDet(M[0], M[1], M[2]); + f->brushprimit_texdef.coords[0][0] = SarrusDet(D[0], M[1], M[2]) / det; + f->brushprimit_texdef.coords[0][1] = SarrusDet(M[0], D[0], M[2]) / det; + f->brushprimit_texdef.coords[0][2] = SarrusDet(M[0], M[1], D[0]) / det; + f->brushprimit_texdef.coords[1][0] = SarrusDet(D[1], M[1], M[2]) / det; + f->brushprimit_texdef.coords[1][1] = SarrusDet(M[0], D[1], M[2]) / det; + f->brushprimit_texdef.coords[1][2] = SarrusDet(M[0], M[1], D[1]) / det; +} + +// +// ======================================================================================================================= +// texture locking called before the points on the face are actually rotated +// ======================================================================================================================= +// +void RotateFaceTexture_BrushPrimit(face_t *f, int nAxis, float fDeg, idVec3 vOrigin) { + // this is a placeholder to call the general texture locking algorithm + txlock_bRotation = true; + txl_nAxis = nAxis; + txl_fDeg = fDeg; + VectorCopy(vOrigin, txl_vOrigin); + TextureLockTransformation_BrushPrimit(f); +} + +// +// ======================================================================================================================= +// compute the new brush primit texture matrix for a transformation matrix and a flip order flag (change plane o +// rientation) this matches the select_matrix algo used in select.cpp this needs to be called on the face BEFORE any +// geometric transformation it will compute the texture matrix that will represent the same texture on the face after +// the geometric transformation is done +// ======================================================================================================================= +// +void ApplyMatrix_BrushPrimit(face_t *f, idMat3 matrix, idVec3 origin) { + // this is a placeholder to call the general texture locking algorithm + txlock_bRotation = false; + VectorCopy(matrix[0], txl_matrix[0]); + VectorCopy(matrix[1], txl_matrix[1]); + VectorCopy(matrix[2], txl_matrix[2]); + VectorCopy(origin, txl_origin); + TextureLockTransformation_BrushPrimit(f); +} + +// +// ======================================================================================================================= +// don't do C==A! +// ======================================================================================================================= +// +void BPMatMul(float A[2][3], float B[2][3], float C[2][3]) { + C[0][0] = A[0][0] * B[0][0] + A[0][1] * B[1][0]; + C[1][0] = A[1][0] * B[0][0] + A[1][1] * B[1][0]; + C[0][1] = A[0][0] * B[0][1] + A[0][1] * B[1][1]; + C[1][1] = A[1][0] * B[0][1] + A[1][1] * B[1][1]; + C[0][2] = A[0][0] * B[0][2] + A[0][1] * B[1][2] + A[0][2]; + C[1][2] = A[1][0] * B[0][2] + A[1][1] * B[1][2] + A[1][2]; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void BPMatDump(float A[2][3]) { + common->Printf("%g %g %g\n%g %g %g\n0 0 1\n", A[0][0], A[0][1], A[0][2], A[1][0], A[1][1], A[1][2]); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void BPMatRotate(float A[2][3], float theta) { + float m[2][3]; + float aux[2][3]; + memset(&m, 0, sizeof (float) *6); + m[0][0] = cos( DEG2RAD( theta ) ); + m[0][1] = -sin( DEG2RAD( theta ) ); + m[1][0] = -m[0][1]; + m[1][1] = m[0][0]; + BPMatMul(A, m, aux); + BPMatCopy(aux, A); +} + +void Face_GetScale_BrushPrimit(face_t *face, float *s, float *t, float *rot) { + idVec3D texS, texT; + ComputeAxisBase(face->plane.Normal(), texS, texT); + + if (face == NULL || face->face_winding == NULL) { + return; + } + // find ST coordinates for the center of the face + double Os = 0, Ot = 0; + for (int i = 0; i < face->face_winding->GetNumPoints(); i++) { + Os += DotProduct((*face->face_winding)[i], texS); + Ot += DotProduct((*face->face_winding)[i], texT); + } + + Os /= face->face_winding->GetNumPoints(); + Ot /= face->face_winding->GetNumPoints(); + + brushprimit_texdef_t *pBP = &face->brushprimit_texdef; + + // here we have a special case, M is a translation and it's inverse is easy + float BPO[2][3]; + float aux[2][3]; + float m[2][3]; + memset(&m, 0, sizeof (float) *6); + m[0][0] = 1; + m[1][1] = 1; + m[0][2] = -Os; + m[1][2] = -Ot; + BPMatMul(m, pBP->coords, aux); + m[0][2] = Os; + m[1][2] = Ot; // now M^-1 + BPMatMul(aux, m, BPO); + + // apply a given scale (on S and T) + ConvertTexMatWithQTexture(BPO, face->d_texture, aux, NULL); + + *s = idMath::Sqrt(aux[0][0] * aux[0][0] + aux[1][0] * aux[1][0]); + *t = idMath::Sqrt(aux[0][1] * aux[0][1] + aux[1][1] * aux[1][1]); + + // compute rotate value + if (idMath::Fabs(face->brushprimit_texdef.coords[0][0]) < ZERO_EPSILON) + { + // rotate is +-90 + if (face->brushprimit_texdef.coords[1][0] > 0) { + *rot = 90.0f; + } + else { + *rot = -90.0f; + } + } + else { + *rot = RAD2DEG(atan2(face->brushprimit_texdef.coords[1][0] / (*s) ? (*s) : 1.0f, face->brushprimit_texdef.coords[0][0] / (*t) ? (*t) : 1.0f)); + } + + +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Face_SetExplicitScale_BrushPrimit(face_t *face, float s, float t) { + idVec3D texS, texT; + ComputeAxisBase(face->plane.Normal(), texS, texT); + + // find ST coordinates for the center of the face + double Os = 0, Ot = 0; + + for (int i = 0; i < face->face_winding->GetNumPoints(); i++) { + Os += DotProduct((*face->face_winding)[i], texS); + Ot += DotProduct((*face->face_winding)[i], texT); + } + + Os /= face->face_winding->GetNumPoints(); + Ot /= face->face_winding->GetNumPoints(); + + brushprimit_texdef_t *pBP = &face->brushprimit_texdef; + + // here we have a special case, M is a translation and it's inverse is easy + float BPO[2][3]; + float aux[2][3]; + float m[2][3]; + memset(&m, 0, sizeof (float) *6); + m[0][0] = 1; + m[1][1] = 1; + m[0][2] = -Os; + m[1][2] = -Ot; + BPMatMul(m, pBP->coords, aux); + m[0][2] = Os; + m[1][2] = Ot; // now M^-1 + BPMatMul(aux, m, BPO); + + // apply a given scale (on S and T) + ConvertTexMatWithQTexture(BPO, face->d_texture, aux, NULL); + + // reset the scale (normalize the matrix) + double v1, v2; + v1 = idMath::Sqrt(aux[0][0] * aux[0][0] + aux[1][0] * aux[1][0]); + v2 = idMath::Sqrt(aux[0][1] * aux[0][1] + aux[1][1] * aux[1][1]); + + if (s == 0.0) { + s = v1; + } + if (t == 0.0) { + t = v2; + } + + double sS, sT; + + // put the values for scale on S and T here: + sS = s / v1; + sT = t / v2; + aux[0][0] *= sS; + aux[1][0] *= sS; + aux[0][1] *= sT; + aux[1][1] *= sT; + ConvertTexMatWithQTexture(aux, NULL, BPO, face->d_texture); + BPMatMul(m, BPO, aux); // m is M^-1 + m[0][2] = -Os; + m[1][2] = -Ot; + BPMatMul(aux, m, pBP->coords); + + // now emit the coordinates on the winding + EmitBrushPrimitTextureCoordinates(face, face->face_winding); +} + + +void Face_FlipTexture_BrushPrimit(face_t *f, bool y) { + + float s, t, rot; + Face_GetScale_BrushPrimit(f, &s, &t, &rot); + if (y) { + Face_SetExplicitScale_BrushPrimit(f, 0.0, -t); + } else { + Face_SetExplicitScale_BrushPrimit(f, -s, 0.0); + } +#if 0 + + idVec3D texS, texT; + ComputeAxisBase(f->plane.normal, texS, texT); + double Os = 0, Ot = 0; + for (int i = 0; i < f->face_winding->numpoints; i++) { + Os += DotProduct(f->face_winding->p[i], texS); + Ot += DotProduct(f->face_winding->p[i], texT); + } + + Ot = abs(Ot); + Ot *= t; + Ot /= f->d_texture->GetEditorImage()->uploadHeight; + + Os = abs(Os); + Os *= s; + Os /= f->d_texture->GetEditorImage()->uploadWidth; + + + if (y) { + Face_FitTexture_BrushPrimit(f, texS, texT, -Ot, 1.0); + } else { + Face_FitTexture_BrushPrimit(f, texS, texT, 1.0, -Os); + } + EmitBrushPrimitTextureCoordinates(f, f->face_winding); +#endif +} + +void Brush_FlipTexture_BrushPrimit(brush_t *b, bool y) { + for (face_t *f = b->brush_faces; f; f = f->next) { + Face_FlipTexture_BrushPrimit(f, y); + } +} + +void Face_SetAxialScale_BrushPrimit(face_t *face, bool y) { + + if (!face) { + return; + } + + if (!face->face_winding) { + return; + } + + //float oldS, oldT, oldR; + //Face_GetScale_BrushPrimit(face, &oldS, &oldT, &oldR); + + idVec3D min, max; + min.x = min.y = min.z = 999999.0; + max.x = max.y = max.z = -999999.0; + for (int i = 0; i < face->face_winding->GetNumPoints(); i++) { + for (int j = 0; j < 3; j++) { + if ((*face->face_winding)[i][j] < min[j]) { + min[j] = (*face->face_winding)[i][j]; + } + if ((*face->face_winding)[i][j] > max[j]) { + max[j] = (*face->face_winding)[i][j]; + } + } + } + + idVec3 len; + + if (g_bAxialMode) { + if (g_axialAnchor >= 0 && g_axialAnchor < face->face_winding->GetNumPoints() && + g_axialDest >= 0 && g_axialDest < face->face_winding->GetNumPoints() && + g_axialAnchor != g_axialDest) { + len = (*face->face_winding)[g_axialDest].ToVec3() - (*face->face_winding)[g_axialAnchor].ToVec3(); + } else { + return; + } + } else { + if (y) { + len = (*face->face_winding)[2].ToVec3() - (*face->face_winding)[1].ToVec3(); + } else { + len = (*face->face_winding)[1].ToVec3() - (*face->face_winding)[0].ToVec3(); + } + } + + double dist = len.Length(); + double width = idMath::Fabs(max.x - min.x); + double height = idMath::Fabs(max.z - min.z); + + //len = maxs[2] - mins[2]; + //double yDist = len.Length(); + + + if (dist != 0.0) { + if (dist > face->d_texture->GetEditorImage()->uploadHeight) { + height = 1.0 / (dist / face->d_texture->GetEditorImage()->uploadHeight); + } else { + height /= dist; + } + if (dist > face->d_texture->GetEditorImage()->uploadWidth) { + width = 1.0 / (dist / face->d_texture->GetEditorImage()->uploadWidth); + } else { + width /= dist; + } + } + + if (y) { + Face_SetExplicitScale_BrushPrimit(face, 0.0, height); + //oldT = oldT / height * 10; + //Select_ShiftTexture_BrushPrimit(face, 0, -oldT, true); + } else { + Face_SetExplicitScale_BrushPrimit(face, width, 0.0); + } +/* + common->Printf("Face x: %f y: %f xr: %f yr: %f\n", x, y, xRatio, yRatio); + common->Printf("Texture x: %i y: %i \n",face->d_texture->GetEditorImage()->uploadWidth, face->d_texture->GetEditorImage()->uploadHeight); + + idVec3D texS, texT; + ComputeAxisBase(face->plane.normal, texS, texT); + float Os = 0, Ot = 0; + for (int i = 0; i < face->face_winding->numpoints; i++) { + Os += DotProduct(face->face_winding->p[i], texS); + Ot += DotProduct(face->face_winding->p[i], texT); + } + + common->Printf("Face2 x: %f y: %f \n", Os, Ot); + Os /= face->face_winding->numpoints; + Ot /= face->face_winding->numpoints; + + + //Os /= face->face_winding->numpoints; + //Ot /= face->face_winding->numpoints; + +*/ +} + diff --git a/src/tools/radiant/EditorEntity.cpp b/src/tools/radiant/EditorEntity.cpp new file mode 100644 index 0000000..99c9512 --- /dev/null +++ b/src/tools/radiant/EditorEntity.cpp @@ -0,0 +1,1401 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "../../renderer/tr_local.h" +#include "../../renderer/model_local.h" // for idRenderModelMD5 +int g_entityId = 1; + +#define CURVE_TAG "curve_" + +extern void Brush_Resize(brush_t *b, idVec3 vMin, idVec3 vMax); + +int GetNumKeys(entity_t *ent) +{ +// int iCount = 0; +// for (epair_t* ep=ent->epairs ; ep ; ep=ep->next) +// { +// iCount++; +// } + + int iCount = ent->epairs.GetNumKeyVals(); + return iCount; +} + +const char *GetKeyString(entity_t *ent, int iIndex) +{ +// for (epair_t* ep=ent->epairs ; ep ; ep=ep->next) +// { +// if (!iIndex--) +// return ep->key; +// } +// +// assert(0); +// return NULL; + + if ( iIndex < GetNumKeys(ent) ) + { + return ent->epairs.GetKeyVal(iIndex)->GetKey().c_str(); + } + + assert(0); + return NULL; +} + + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +const char *ValueForKey(entity_t *ent, const char *key) { + return ent->epairs.GetString(key); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void TrackMD3Angles(entity_t *e, const char *key, const char *value) { + if ( idStr::Icmp(key, "angle") != 0 ) { + return; + } + + if ((e->eclass->fixedsize && e->eclass->nShowFlags & ECLASS_MISCMODEL) || EntityHasModel(e)) { + float a = FloatForKey(e, "angle"); + float b = atof(value); + if (a != b) { + idVec3 vAngle; + vAngle[0] = vAngle[1] = 0; + vAngle[2] = -a; + Brush_Rotate(e->brushes.onext, vAngle, e->origin, true); + vAngle[2] = b; + Brush_Rotate(e->brushes.onext, vAngle, e->origin, true); + } + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void SetKeyValue(entity_t *ent, const char *key, const char *value, bool trackAngles) { + if (ent == NULL) { + return; + } + + if (!key || !key[0]) { + return; + } + + if (trackAngles) { + TrackMD3Angles(ent, key, value); + } + + ent->epairs.Set(key, value); + GetVectorForKey(ent, "origin", ent->origin); + + // update sound in case this key was relevent + Entity_UpdateSoundEmitter( ent ); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void SetKeyVec3(entity_t *ent, const char *key, idVec3 v) { + if (ent == NULL) { + return; + } + + if (!key || !key[0]) { + return; + } + + idStr str; + sprintf(str, "%g %g %g", v.x, v.y, v.z); + ent->epairs.Set(key, str); + GetVectorForKey(ent, "origin", ent->origin); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void SetKeyMat3(entity_t *ent, const char *key, idMat3 m) { + if (ent == NULL) { + return; + } + + if (!key || !key[0]) { + return; + } + + idStr str; + + sprintf(str, "%g %g %g %g %g %g %g %g %g",m[0][0],m[0][1],m[0][2],m[1][0],m[1][1],m[1][2],m[2][0],m[2][1],m[2][2]); + + ent->epairs.Set(key, str); + GetVectorForKey(ent, "origin", ent->origin); +} + + + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void DeleteKey(entity_t *ent, const char *key) { + ent->epairs.Delete(key); + if (stricmp(key, "rotation") == 0) { + ent->rotation.Identity(); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +float FloatForKey(entity_t *ent, const char *key) { + const char *k; + + k = ValueForKey(ent, key); + if (k && *k) { + return atof(k); + } + + return 0.0; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +int IntForKey(entity_t *ent, const char *key) { + const char *k; + + k = ValueForKey(ent, key); + return atoi(k); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +bool GetVectorForKey(entity_t *ent, const char *key, idVec3 &vec) { + const char *k; + k = ValueForKey(ent, key); + if (k && strlen(k) > 0) { + sscanf(k, "%f %f %f", &vec[0], &vec[1], &vec[2]); + return true; + } + else { + vec[0] = vec[1] = vec[2] = 0; + } + + return false; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +bool GetVector4ForKey(entity_t *ent, const char *key, idVec4 &vec) { + const char *k; + k = ValueForKey(ent, key); + if (k && strlen(k) > 0) { + sscanf(k, "%f %f %f %f", &vec[0], &vec[1], &vec[2], &vec[3]); + return true; + } + else { + vec[0] = vec[1] = vec[2] = vec[3] = 0; + } + + return false; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +bool GetFloatForKey(entity_t *ent, const char *key, float *f) { + const char *k; + k = ValueForKey(ent, key); + if (k && strlen(k) > 0) { + *f = atof(k); + return true; + } + + *f = 0; + return false; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +bool GetMatrixForKey(entity_t *ent, const char *key, idMat3 &mat) { + const char *k; + k = ValueForKey(ent, key); + if (k && strlen(k) > 0) { + sscanf + ( + k, + "%f %f %f %f %f %f %f %f %f ", + &mat[0][0], + &mat[0][1], + &mat[0][2], + &mat[1][0], + &mat[1][1], + &mat[1][2], + &mat[2][0], + &mat[2][1], + &mat[2][2] + ); + return true; + } + else { + mat.Identity(); + } + + return false; +} + +/* + ======================================================================================================================= + Entity_FreeEpairs Frees the entity epairs. + ======================================================================================================================= + */ +void Entity_FreeEpairs(entity_t *e) { + e->epairs.Clear(); +} + +/* + ======================================================================================================================= + Entity_AddToList + ======================================================================================================================= + */ +void Entity_AddToList(entity_t *e, entity_t *list) { + if (e->next || e->prev) { + Error("Entity_AddToList: allready linked"); + } + + e->next = list->next; + list->next->prev = e; + list->next = e; + e->prev = list; +} + +/* + ======================================================================================================================= + Entity_RemoveFromList + ======================================================================================================================= + */ +void Entity_RemoveFromList(entity_t *e) { + if ( !e->next || !e->prev ) { + Error("Entity_RemoveFromList: not linked"); + } + + e->next->prev = e->prev; + e->prev->next = e->next; + e->next = e->prev = NULL; +} + +/* + ======================================================================================================================= + Entity_Free Frees the entity and any brushes is has. The entity is removed from the global entities list. + ======================================================================================================================= + */ +void Entity_Free( entity_t *e ) { + + while ( e->brushes.onext != &e->brushes ) { + Brush_Free(e->brushes.onext); + } + + if ( e->next ) { + e->next->prev = e->prev; + e->prev->next = e->next; + } + + Entity_FreeEpairs( e ); + + delete e; +} + +/* + ======================================================================================================================= + Entity_MemorySize + ======================================================================================================================= + */ + +int Entity_MemorySize( entity_t *e ) +{ + brush_t *b; + int size; + + size = sizeof( entity_t ) + e->epairs.Size(); + for( b = e->brushes.onext; b != &e->brushes; b = b->onext ) + { + size += Brush_MemorySize( b ); +} + return( size ); +} + +/* + ======================================================================================================================= + ParseEpair + ======================================================================================================================= + */ + +struct EpairFixup { + const char *name; + int type; +}; + + +const EpairFixup FloatFixups[] = { + { "origin", 1 }, + { "rotation", 2 }, + { "_color", 1 }, + { "falloff", 0 }, + { "light", 0 }, + { "light_target", 1 }, + { "light_up", 1 }, + { "light_right", 1 }, + { "light_start", 1 }, + { "light_center", 1 }, + { "light_end", 1 }, + { "light_radius", 1 }, + { "light_origin", 1 } +}; + +const int FixupCount = sizeof(FloatFixups) / sizeof(EpairFixup); + +void FixFloats(idDict *dict) { + int count = dict->GetNumKeyVals(); + for (int i = 0; i < count; i++) { + const idKeyValue *kv = dict->GetKeyVal(i); + for (int j = 0; j < FixupCount; j++) { + if (kv->GetKey().Icmp(FloatFixups[j].name) == 0) { + idStr val; + if (FloatFixups[j].type == 1) { + idVec3 v; + sscanf(kv->GetValue().c_str(), "%f %f %f", &v.x, &v.y, &v.z); + sprintf(val, "%g %g %g", v.x, v.y, v.z); + } else if (FloatFixups[j].type == 2) { + idMat3 mat; + sscanf(kv->GetValue().c_str(), "%f %f %f %f %f %f %f %f %f ",&mat[0][0],&mat[0][1],&mat[0][2],&mat[1][0],&mat[1][1],&mat[1][2],&mat[2][0],&mat[2][1],&mat[2][2]); + sprintf(val, "%g %g %g %g %g %g %g %g %g",mat[0][0],mat[0][1],mat[0][2],mat[1][0],mat[1][1],mat[1][2],mat[2][0],mat[2][1],mat[2][2]); + } else { + float f = atof(kv->GetValue().c_str()); + sprintf(val, "%g", f); + } + dict->Set(kv->GetKey(), val); + break; + } + } + } +} + +void ParseEpair(idDict *dict) { + idStr key = token; + GetToken(false); + idStr val = token; + + if (key.Length() > 0) { + dict->Set(key, val); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +bool EntityHasModel(entity_t *ent) { + if (ent) { + const char *model = ValueForKey(ent, "model"); + const char *name = ValueForKey(ent, "name"); + if (model && *model) { + if ( idStr::Icmp(model, name) ) { + return true; + } + } + } + + return false; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +entity_t *Entity_New() { + entity_t *ent = new entity_t; + + ent->prev = ent->next = NULL; + ent->brushes.prev = ent->brushes.next = NULL; + ent->brushes.oprev = ent->brushes.onext = NULL; + ent->brushes.owner = NULL; + ent->undoId = 0; + ent->redoId = 0; + ent->entityId = g_entityId++; + ent->origin.Zero(); + ent->eclass = NULL; + ent->md3Class = NULL; + ent->lightOrigin.Zero(); + ent->lightRotation.Identity(); + ent->trackLightOrigin = false; + ent->rotation.Identity(); + ent->lightDef = -1; + ent->modelDef = -1; + ent->soundEmitter = NULL; + ent->curve = NULL; + return ent; +} + +void Entity_UpdateCurveData( entity_t *ent ) { + + if ( ent == NULL || ent->curve == NULL ) { + return; + } + + const idKeyValue *kv = ent->epairs.MatchPrefix( CURVE_TAG ); + if ( kv == NULL ) { + if ( ent->curve ) { + delete ent->curve; + ent->curve = NULL; + if ( g_qeglobals.d_select_mode == sel_editpoint ) { + g_qeglobals.d_select_mode = sel_brush; + } + } + return; + } + + int c = ent->curve->GetNumValues(); + idStr str = va( "%i ( ", c ); + idVec3 v; + for ( int i = 0; i < c; i++ ) { + v = ent->curve->GetValue( i ); + str += " "; + str += v.ToString(); + str += " "; + } + str += " )"; + + ent->epairs.Set( kv->GetKey(), str ); + +} + +idCurve *Entity_MakeCurve( entity_t *ent ) { + const idKeyValue *kv = ent->epairs.MatchPrefix( CURVE_TAG ); + if ( kv ) { + idStr str = kv->GetKey().Right( kv->GetKey().Length() - strlen( CURVE_TAG ) ); + if ( str.Icmp( "CatmullRomSpline" ) == 0 ) { + return new idCurve_CatmullRomSpline(); + } else if ( str.Icmp( "Nurbs" ) == 0 ) { + return new idCurve_NURBS(); + } + } + return NULL; +} + +void Entity_SetCurveData( entity_t *ent ) { + + ent->curve = Entity_MakeCurve( ent ); + const idKeyValue *kv = ent->epairs.MatchPrefix( CURVE_TAG ); + if ( kv && ent->curve ) { + idLexer lex; + lex.LoadMemory( kv->GetValue(), kv->GetValue().Length(), "_curve" ); + int numPoints = lex.ParseInt(); + if ( numPoints > 0 ) { + float *fp = new float[numPoints * 3]; + lex.Parse1DMatrix( numPoints * 3, fp ); + int time = 0; + for ( int i = 0; i < numPoints * 3; i += 3 ) { + idVec3 v; + v.x = fp[i]; + v.y = fp[i+1]; + v.z = fp[i+2]; + ent->curve->AddValue( time, v ); + time += 100; + } + delete []fp; + } + } + +} + +entity_t *Entity_PostParse(entity_t *ent, brush_t *pList) { + bool has_brushes; + eclass_t *e; + brush_t *b; + idVec3 mins, maxs, zero; + idBounds bo; + + zero.Zero(); + + Entity_SetCurveData( ent ); + + if (ent->brushes.onext == &ent->brushes) { + has_brushes = false; + } + else { + has_brushes = true; + } + + bool needsOrigin = !GetVectorForKey(ent, "origin", ent->origin); + const char *pModel = ValueForKey(ent, "model"); + + const char *cp = ValueForKey(ent, "classname"); + + if (strlen(cp)) { + e = Eclass_ForName(cp, has_brushes); + } else { + const char *cp2 = ValueForKey(ent, "name"); + if (strlen(cp2)) { + char buff[1024]; + strcpy(buff, cp2); + int len = strlen(buff); + while ((isdigit(buff[len-1]) || buff[len-1] == '_') && len > 0) { + buff[len-1] = '\0'; + len--; + } + e = Eclass_ForName(buff, has_brushes); + SetKeyValue(ent, "classname", buff, false); + } else { + e = Eclass_ForName("", has_brushes); + } + } + + idStr str; + + if (e->defArgs.GetString("model", "", str) && e->entityModel == NULL) { + e->entityModel = gameEdit->ANIM_GetModelFromEntityDef( &e->defArgs ); + } + + ent->eclass = e; + + bool hasModel = EntityHasModel(ent); + + if (hasModel) { + ent->eclass->defArgs.GetString("model", "", str); + if (str.Length()) { + hasModel = false; + ent->epairs.Delete("model"); + } + } + + if (e->nShowFlags & ECLASS_WORLDSPAWN) { + ent->origin.Zero(); + needsOrigin = false; + ent->epairs.Delete( "model" ); + } else if (e->nShowFlags & ECLASS_LIGHT) { + if (GetVectorForKey(ent, "light_origin", ent->lightOrigin)) { + GetMatrixForKey(ent, "light_rotation", ent->lightRotation); + ent->trackLightOrigin = true; + } else if (hasModel) { + SetKeyValue(ent, "light_origin", ValueForKey(ent, "origin")); + ent->lightOrigin = ent->origin; + if (GetMatrixForKey(ent, "rotation", ent->lightRotation)) { + SetKeyValue(ent, "light_rotation", ValueForKey(ent, "rotation")); + } + ent->trackLightOrigin = true; + } + } else if ( e->nShowFlags & ECLASS_ENV ) { + // need to create an origin from the bones here + idVec3 org; + idAngles ang; + bo.Clear(); + bool hasBody = false; + const idKeyValue *arg = ent->epairs.MatchPrefix( "body ", NULL ); + while ( arg ) { + sscanf( arg->GetValue(), "%f %f %f %f %f %f", &org.x, &org.y, &org.z, &ang.pitch, &ang.yaw, &ang.roll ); + bo.AddPoint( org ); + arg = ent->epairs.MatchPrefix( "body ", arg ); + hasBody = true; + } + if (hasBody) { + ent->origin = bo.GetCenter(); + } + } + + if (e->fixedsize || hasModel) { // fixed size entity + if (ent->brushes.onext != &ent->brushes) { + for (b = ent->brushes.onext; b != &ent->brushes; b = b->onext) { + b->entityModel = true; + } + } + + if (hasModel) { + // model entity + idRenderModel *modelHandle = renderModelManager->FindModel( pModel ); + + if ( dynamic_cast( modelHandle ) ) { + bo.Zero(); + bo.ExpandSelf( 12.0f ); + } else { + bo = modelHandle->Bounds( NULL ); + } + + VectorCopy(bo[0], mins); + VectorCopy(bo[1], maxs); + for (int i = 0; i < 3; i++) { + if (mins[i] == maxs[i]) { + mins[i]--; + maxs[i]++; + } + } + VectorAdd(mins, ent->origin, mins); + VectorAdd(maxs, ent->origin, maxs); + b = Brush_Create(mins, maxs, &e->texdef); + b->modelHandle = modelHandle; + + float yaw = 0; + bool convertAngles = GetFloatForKey(ent, "angle", &yaw); + extern void Brush_Rotate(brush_t *b, idMat3 matrix, idVec3 origin, bool bBuild); + extern void Brush_Rotate(brush_t *b, idVec3 rot, idVec3 origin, bool bBuild); + + if (convertAngles) { + idVec3 rot(0, 0, yaw); + Brush_Rotate(b, rot, ent->origin, false); + } + + if (GetMatrixForKey(ent, "rotation", ent->rotation)) { + idBounds bo2; + bo2.FromTransformedBounds(bo, ent->origin, ent->rotation); + b->owner = ent; + Brush_Resize(b, bo2[0], bo2[1]); + } + Entity_LinkBrush(ent, b); + } + + if (!hasModel || (ent->eclass->nShowFlags & ECLASS_LIGHT && hasModel)) { + // create a custom brush + if (ent->trackLightOrigin) { + mins = e->mins + ent->lightOrigin; + maxs = e->maxs + ent->lightOrigin; + } else { + mins = e->mins + ent->origin; + maxs = e->maxs + ent->origin; + } + + b = Brush_Create(mins, maxs, &e->texdef); + GetMatrixForKey(ent, "rotation", ent->rotation); + Entity_LinkBrush(ent, b); + b->trackLightOrigin = ent->trackLightOrigin; + if ( e->texdef.name == NULL ) { + brushprimit_texdef_t bp; + texdef_t td; + td.SetName( ent->eclass->defMaterial ); + Brush_SetTexture( b, &td, &bp, false ); + } + } + } else { // brush entity + if (ent->brushes.next == &ent->brushes) { + printf("Warning: Brush entity with no brushes\n"); + } + + if (!needsOrigin) { + idStr cn = ValueForKey(ent, "classname"); + idStr name = ValueForKey(ent, "name"); + idStr model = ValueForKey(ent, "model"); + if (cn.Icmp("func_static") == 0) { + if (name.Icmp(model) == 0) { + needsOrigin = true; + } + } + } + + if (needsOrigin) { + idVec3 mins, maxs, mid; + int i; + char text[32]; + mins[0] = mins[1] = mins[2] = 999999; + maxs[0] = maxs[1] = maxs[2] = -999999; + + // add in the origin + for (b = ent->brushes.onext; b != &ent->brushes; b = b->onext) { + Brush_Build(b, true, false, false); + for (i = 0; i < 3; i++) { + if (b->mins[i] < mins[i]) { + mins[i] = b->mins[i]; + } + + if (b->maxs[i] > maxs[i]) { + maxs[i] = b->maxs[i]; + } + } + } + + for (i = 0; i < 3; i++) { + ent->origin[i] = (mins[i] + ((maxs[i] - mins[i]) / 2)); + } + + sprintf(text, "%i %i %i", (int)ent->origin[0], (int)ent->origin[1], (int)ent->origin[2]); + SetKeyValue(ent, "origin", text); + } + + if (!(e->nShowFlags & ECLASS_WORLDSPAWN)) { + if (e->defArgs.FindKey("model") == NULL && (pModel == NULL || (pModel && strlen(pModel) == 0))) { + SetKeyValue(ent, "model", ValueForKey(ent, "name")); + } + } + else { + DeleteKey(ent, "origin"); + } + } + + // add all the brushes to the main list + if (pList) { + for (b = ent->brushes.onext; b != &ent->brushes; b = b->onext) { + b->next = pList->next; + pList->next->prev = b; + b->prev = pList; + pList->next = b; + } + } + + FixFloats(&ent->epairs); + + return ent; + +} + +/* + ======================================================================================================================= + Entity_Parse If onlypairs is set, the classname info will not be looked up, and the entity will not be added to the + global list. Used for parsing the project. + ======================================================================================================================= + */ +entity_t *Entity_Parse(bool onlypairs, brush_t *pList) { + entity_t *ent; + + if (!GetToken(true)) { + return NULL; + } + + if (strcmp(token, "{")) { + Error("ParseEntity: { not found"); + } + + ent = Entity_New(); + ent->brushes.onext = ent->brushes.oprev = &ent->brushes; + ent->origin.Zero(); + + int n = 0; + do { + if (!GetToken(true)) { + Warning("ParseEntity: EOF without closing brace"); + return NULL; + } + + if (!strcmp(token, "}")) { + break; + } + + if (!strcmp(token, "{")) { + GetVectorForKey(ent, "origin", ent->origin); + brush_t *b = Brush_Parse(ent->origin); + if (b != NULL) { + b->owner = ent; + + // add to the end of the entity chain + b->onext = &ent->brushes; + b->oprev = ent->brushes.oprev; + ent->brushes.oprev->onext = b; + ent->brushes.oprev = b; + } + else { + break; + } + } + else { + ParseEpair(&ent->epairs); + } + } while (1); + + if (onlypairs) { + return ent; + } + + return Entity_PostParse(ent, pList); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void VectorMidpoint(idVec3 va, idVec3 vb, idVec3 &out) { + for (int i = 0; i < 3; i++) { + out[i] = va[i] + ((vb[i] - va[i]) / 2); + } +} + +/* + ======================================================================================================================= + Entity_Write + ======================================================================================================================= + */ +void Entity_Write(entity_t *e, FILE *f, bool use_region) { + brush_t *b; + idVec3 origin; + char text[128]; + int count; + + // if none of the entities brushes are in the region, don't write the entity at all + if (use_region) { + // in region mode, save the camera position as playerstart + if (!strcmp(ValueForKey(e, "classname"), "info_player_start")) { + fprintf(f, "{\n"); + fprintf(f, "\"classname\" \"info_player_start\"\n"); + fprintf + ( + f, + "\"origin\" \"%i %i %i\"\n", + (int)g_pParentWnd->GetCamera()->Camera().origin[0], + (int)g_pParentWnd->GetCamera()->Camera().origin[1], + (int)g_pParentWnd->GetCamera()->Camera().origin[2] + ); + fprintf(f, "\"angle\" \"%i\"\n", (int)g_pParentWnd->GetCamera()->Camera().angles[YAW]); + fprintf(f, "}\n"); + return; + } + + for (b = e->brushes.onext; b != &e->brushes; b = b->onext) { + if (!Map_IsBrushFiltered(b)) { + break; // got one + } + } + + if (b == &e->brushes) { + return; // nothing visible + } + } + + if (e->eclass->nShowFlags & ECLASS_PLUGINENTITY) { + // NOTE: the whole brush placement / origin stuff is a mess + VectorCopy(e->origin, origin); + sprintf(text, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]); + SetKeyValue(e, "origin", text); + } + + // if fixedsize, calculate a new origin based on the current brush position + else if (e->eclass->fixedsize || EntityHasModel(e)) { + if (!GetVectorForKey(e, "origin", origin)) { + VectorSubtract(e->brushes.onext->mins, e->eclass->mins, origin); + sprintf(text, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]); + SetKeyValue(e, "origin", text); + } + } + + fprintf(f, "{\n"); + + count = e->epairs.GetNumKeyVals(); + for (int j = 0; j < count; j++) { + fprintf(f, "\"%s\" \"%s\"\n", e->epairs.GetKeyVal(j)->GetKey().c_str(), e->epairs.GetKeyVal(j)->GetValue().c_str()); + } + + if (!EntityHasModel(e)) { + count = 0; + for (b = e->brushes.onext; b != &e->brushes; b = b->onext) { + if (e->eclass->fixedsize && !b->entityModel) { + continue; + } + if (!use_region || !Map_IsBrushFiltered(b)) { + fprintf(f, "// brush %i\n", count); + count++; + Brush_Write( b, f, e->origin, ( g_PrefsDlg.m_bNewMapFormat != FALSE ) ); + } + } + } + + fprintf(f, "}\n"); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +bool IsBrushSelected(brush_t *bSel) { + for (brush_t * b = selected_brushes.next; b != NULL && b != &selected_brushes; b = b->next) { + if (b == bSel) { + return true; + } + } + + return false; +} + +// +// ======================================================================================================================= +// Entity_WriteSelected +// ======================================================================================================================= +// +void Entity_WriteSelected(entity_t *e, FILE *f) { + brush_t *b; + idVec3 origin; + char text[128]; + int count; + + for (b = e->brushes.onext; b != &e->brushes; b = b->onext) { + if (IsBrushSelected(b)) { + break; // got one + } + } + + if (b == &e->brushes) { + return; // nothing selected + } + + // if fixedsize, calculate a new origin based on the current brush position + if (e->eclass->fixedsize || EntityHasModel(e)) { + if (!GetVectorForKey(e, "origin", origin)) { + VectorSubtract(e->brushes.onext->mins, e->eclass->mins, origin); + sprintf(text, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]); + SetKeyValue(e, "origin", text); + } + } + + fprintf(f, "{\n"); + + count = e->epairs.GetNumKeyVals(); + for (int j = 0; j < count; j++) { + fprintf(f, "\"%s\" \"%s\"\n", e->epairs.GetKeyVal(j)->GetKey().c_str(), e->epairs.GetKeyVal(j)->GetValue().c_str()); + } + + if (!EntityHasModel(e)) { + count = 0; + for (b = e->brushes.onext; b != &e->brushes; b = b->onext) { + if (e->eclass->fixedsize && !b->entityModel) { + continue; + } + if (IsBrushSelected(b)) { + fprintf(f, "// brush %i\n", count); + count++; + Brush_Write( b, f, e->origin, ( g_PrefsDlg.m_bNewMapFormat != FALSE ) ); + } + } + } + + fprintf(f, "}\n"); +} + +// +// ======================================================================================================================= +// Entity_WriteSelected to a CMemFile +// ======================================================================================================================= +// +void Entity_WriteSelected(entity_t *e, CMemFile *pMemFile) { + brush_t *b; + idVec3 origin; + char text[128]; + int count; + + for (b = e->brushes.onext; b != &e->brushes; b = b->onext) { + if (IsBrushSelected(b)) { + break; // got one + } + } + + if (b == &e->brushes) { + return; // nothing selected + } + + // if fixedsize, calculate a new origin based on the current brush position + if (e->eclass->fixedsize || EntityHasModel(e)) { + if (!GetVectorForKey(e, "origin", origin)) { + VectorSubtract(e->brushes.onext->mins, e->eclass->mins, origin); + sprintf(text, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]); + SetKeyValue(e, "origin", text); + } + } + + MemFile_fprintf(pMemFile, "{\n"); + + count = e->epairs.GetNumKeyVals(); + for (int j = 0; j < count; j++) { + MemFile_fprintf(pMemFile, "\"%s\" \"%s\"\n", e->epairs.GetKeyVal(j)->GetKey().c_str(), e->epairs.GetKeyVal(j)->GetValue().c_str()); + } + + if (!EntityHasModel(e)) { + count = 0; + for (b = e->brushes.onext; b != &e->brushes; b = b->onext) { + if (e->eclass->fixedsize && !b->entityModel) { + continue; + } + if (IsBrushSelected(b)) { + MemFile_fprintf(pMemFile, "// brush %i\n", count); + count++; + Brush_Write( b, pMemFile, e->origin, ( g_PrefsDlg.m_bNewMapFormat != FALSE ) ); + } + } + } + + MemFile_fprintf(pMemFile, "}\n"); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Entity_SetName(entity_t *e, const char *name) { + CString oldName = ValueForKey(e, "name"); + CString oldModel = ValueForKey(e, "model"); + SetKeyValue(e, "name", name); + if (oldName == oldModel) { + SetKeyValue(e, "model", name); + } +} + +extern bool Entity_NameIsUnique(const char *name); + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Entity_Name(entity_t *e, bool force) { + const char *name = ValueForKey(e, "name"); + + if (!force && name && name[0]) { + return; + } + + if (name && name[0] && Entity_NameIsUnique(name)) { + return; + } + + bool setModel = false; + if (name[0]) { + const char *model = ValueForKey(e, "model"); + if (model[0]) { + if ( idStr::Icmp(model, name) == 0 ) { + setModel = true; + } + } + } + + const char *eclass = ValueForKey(e, "classname"); + if (eclass && eclass[0]) { + idStr str = cvarSystem->GetCVarString( "radiant_nameprefix" ); + int id = Map_GetUniqueEntityID(str, eclass); + if (str.Length()) { + SetKeyValue(e, "name", va("%s_%s_%i", str.c_str(), eclass, id)); + } else { + SetKeyValue(e, "name", va("%s_%i", eclass, id)); + } + if (setModel) { + if (str.Length()) { + SetKeyValue(e, "model", va("%s_%s_%i", str.c_str(), eclass, id)); + } else { + SetKeyValue(e, "model", va("%s_%i", eclass, id)); + } + } + } +} + +/* + ======================================================================================================================= + Entity_Create Creates a new entity out of the selected_brushes list. If the entity class is fixed size, the brushes + are only used to find a midpoint. Otherwise, the brushes have their ownership transfered to the new entity. + ======================================================================================================================= + */ +entity_t *Entity_Create(eclass_t *c, bool forceFixed) { + entity_t *e; + brush_t *b; + idVec3 mins, maxs, origin; + char text[32]; + texdef_t td; + brushprimit_texdef_t bp; + + // check to make sure the brushes are ok + for (b = selected_brushes.next; b != &selected_brushes; b = b->next) { + if (b->owner != world_entity) { + Sys_Status("Entity NOT created, brushes not all from world\n"); + Sys_Beep(); + return NULL; + } + } + + idStr str; + if (c->defArgs.GetString("model", "", str) && c->entityModel == NULL) { + c->entityModel = gameEdit->ANIM_GetModelFromEntityDef( &c->defArgs ); + } + + // create it + e = Entity_New(); + e->brushes.onext = e->brushes.oprev = &e->brushes; + e->eclass = c; + e->epairs.Copy(c->args); + SetKeyValue(e, "classname", c->name); + Entity_Name(e, false); + + // add the entity to the entity list + Entity_AddToList(e, &entities); + + if (c->fixedsize) { + // + // just use the selection for positioning b = selected_brushes.next; for (i=0 ; + // i<3 ; i++) { e->origin[i] = b->mins[i] - c->mins[i]; } + // + Select_GetMid(e->origin); + VectorCopy(e->origin, origin); + + // create a custom brush + VectorAdd(c->mins, e->origin, mins); + VectorAdd(c->maxs, e->origin, maxs); + + b = Brush_Create(mins, maxs, &c->texdef); + + Entity_LinkBrush(e, b); + + if (c->defMaterial.Length()) { + td.SetName(c->defMaterial); + Brush_SetTexture(b, &td, &bp, false); + } + + + // delete the current selection + Select_Delete(); + + // select the new brush + b->next = b->prev = &selected_brushes; + selected_brushes.next = selected_brushes.prev = b; + + Brush_Build(b); + } + else { + + Select_GetMid(origin); + + // change the selected brushes over to the new entity + for (b = selected_brushes.next; b != &selected_brushes; b = b->next) { + Entity_UnlinkBrush(b); + Entity_LinkBrush(e, b); + Brush_Build(b); // so the key brush gets a name + if (c->defMaterial.Length()) { + td.SetName(c->defMaterial); + Brush_SetTexture(b, &td, &bp, false); + } + + } + + //for (int i = 0; i < 3; i++) { + // origin[i] = vMin[i] + vMax[i] * 0.5; + //} + + if (!forceFixed) { + SetKeyValue(e, "model", ValueForKey(e, "name")); + } + } + + sprintf(text, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]); + SetKeyValue(e, "origin", text); + VectorCopy(origin, e->origin); + + Sys_UpdateWindows(W_ALL); + return e; +} + +void Brush_MakeDirty(brush_t *b) { + for (face_t *f = b->brush_faces; f; f = f->next) { + f->dirty = true; + } +} +/* + ======================================================================================================================= + Entity_LinkBrush + ======================================================================================================================= + */ +void Entity_LinkBrush(entity_t *e, brush_t *b) { + if (b->oprev || b->onext) { + Error("Entity_LinkBrush: Allready linked"); + } + + Brush_MakeDirty(b); + + b->owner = e; + + b->onext = e->brushes.onext; + b->oprev = &e->brushes; + e->brushes.onext->oprev = b; + e->brushes.onext = b; +} + +/* + ======================================================================================================================= + Entity_UnlinkBrush + ======================================================================================================================= + */ +void Entity_UnlinkBrush(brush_t *b) { + // if (!b->owner || !b->onext || !b->oprev) + if (!b->onext || !b->oprev) { + Error("Entity_UnlinkBrush: Not currently linked"); + } + + b->onext->oprev = b->oprev; + b->oprev->onext = b->onext; + b->onext = b->oprev = NULL; + b->owner = NULL; +} + +/* + ======================================================================================================================= + Entity_Clone + ======================================================================================================================= + */ +entity_t *Entity_Clone(entity_t *e) { + entity_t *n; + + n = Entity_New(); + n->brushes.onext = n->brushes.oprev = &n->brushes; + n->eclass = e->eclass; + n->rotation = e->rotation; + n->origin = e->origin; + + // add the entity to the entity list + Entity_AddToList(n, &entities); + + n->epairs = e->epairs; + + return n; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +int GetUniqueTargetId(int iHint) { + int iMin, iMax, i; + BOOL fFound; + entity_t *pe; + + fFound = FALSE; + pe = entities.next; + iMin = 0; + iMax = 0; + + for (; pe != NULL && pe != &entities; pe = pe->next) { + i = IntForKey(pe, "target"); + if (i) { + iMin = Min(i, iMin); + iMax = Max(i, iMax); + if (i == iHint) { + fFound = TRUE; + } + } + } + + if (fFound) { + return iMax + 1; + } + else { + return iHint; + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +entity_t *FindEntity(const char *pszKey, const char *pszValue) { + entity_t *pe; + + pe = entities.next; + + for (; pe != NULL && pe != &entities; pe = pe->next) { + if (!strcmp(ValueForKey(pe, pszKey), pszValue)) { + return pe; + } + } + + return NULL; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +entity_t *FindEntityInt(const char *pszKey, int iValue) { + entity_t *pe; + + pe = entities.next; + + for (; pe != NULL && pe != &entities; pe = pe->next) { + if (IntForKey(pe, pszKey) == iValue) { + return pe; + } + } + + return NULL; +} + +/* +==================== +Entity_UpdateSoundEmitter + +Deletes the soundEmitter if the entity should not emit a sound due +to it not having one, being filtered away, or the sound mode being turned off. + +Creates or updates the soundEmitter if needed +==================== +*/ +void Entity_UpdateSoundEmitter( entity_t *ent ) { + bool playing = false; + + // if an entity doesn't have any brushes at all, don't do anything + // if the brush isn't displayed (filtered or culled), don't do anything + if ( g_pParentWnd->GetCamera()->GetSoundMode() + && ent->brushes.onext != &ent->brushes && !FilterBrush(ent->brushes.onext) ) { + // check for sounds + const char *v = ValueForKey( ent, "s_shader" ); + if ( v[0] ) { + refSound_t sound; + + gameEdit->ParseSpawnArgsToRefSound( &ent->epairs, &sound ); + if ( !sound.waitfortrigger ) { // waitfortrigger will not start playing immediately + if ( !ent->soundEmitter ) { + const int handle = soundSystem->AllocSoundEmitter( SOUNDWORLD_EDITOR ); + ent->soundEmitter = soundSystem->EmitterForIndex( SOUNDWORLD_EDITOR, handle ); + } + playing = true; + ent->soundEmitter->UpdateEmitter( ent->origin, vec3_origin, 0, &sound.parms ); + // always play on a single channel, so updates always override + ent->soundEmitter->StartSound( sound.shader, SCHANNEL_ONE ); + } + } + } + + // delete the soundEmitter if not used + if ( !playing && ent->soundEmitter ) { + soundSystem->FreeSoundEmitter( SOUNDWORLD_EDITOR, ent->soundEmitter->Handle(), true ); + ent->soundEmitter = NULL; + } + +} diff --git a/src/tools/radiant/EditorEntity.h b/src/tools/radiant/EditorEntity.h new file mode 100644 index 0000000..f7f6107 --- /dev/null +++ b/src/tools/radiant/EditorEntity.h @@ -0,0 +1,96 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +void Eclass_InitForSourceDirectory( const char *path ); +eclass_t * Eclass_ForName( const char *name, bool has_brushes ); +bool Eclass_hasModel(eclass_t *e, idVec3 &vMin, idVec3 &vMax); + +typedef struct entity_s { + struct entity_s *prev, *next; + brush_t brushes; // head/tail of list + int undoId, redoId, entityId; // used for undo/redo + idVec3 origin; + qhandle_t lightDef; + qhandle_t modelDef; + idSoundEmitter *soundEmitter; + eclass_t * eclass; + idDict epairs; + eclass_t * md3Class; + idMat3 rotation; + idVec3 lightOrigin; // for lights that have been combined with models + idMat3 lightRotation; // '' + bool trackLightOrigin; + idCurve *curve; +} entity_t; + +void ParseEpair(idDict *dict); +const char *ValueForKey(entity_t *ent, const char *key); +int GetNumKeys(entity_t *ent); +const char *GetKeyString(entity_t *ent, int iIndex); +void SetKeyValue (entity_t *ent, const char *key, const char *value, bool trackAngles = true); +void DeleteKey (entity_t *ent, const char *key); +float FloatForKey (entity_t *ent, const char *key); +int IntForKey (entity_t *ent, const char *key); +bool GetVectorForKey (entity_t *ent, const char *key, idVec3 &vec); +bool GetVector4ForKey (entity_t *ent, const char *key, idVec4 &vec); +bool GetFloatForKey(entity_t *end, const char *key, float *f); +void SetKeyVec3(entity_t *ent, const char *key, idVec3 v); +void SetKeyMat3(entity_t *ent, const char *key, idMat3 m); +bool GetMatrixForKey(entity_t *ent, const char *key, idMat3 &mat); + +void Entity_UpdateSoundEmitter( entity_t *ent ); +idCurve *Entity_MakeCurve( entity_t *e ); +void Entity_UpdateCurveData( entity_t *e ); +void Entity_SetCurveData( entity_t *e ); +void Entity_Free (entity_t *e); +void Entity_FreeEpairs(entity_t *e); +int Entity_MemorySize(entity_t *e); +entity_t * Entity_Parse (bool onlypairs, brush_t* pList = NULL); +void Entity_Write (entity_t *e, FILE *f, bool use_region); +void Entity_WriteSelected(entity_t *e, FILE *f); +void Entity_WriteSelected(entity_t *e, CMemFile*); +entity_t * Entity_Create (eclass_t *c, bool forceFixed = false); +entity_t * Entity_Clone (entity_t *e); +void Entity_AddToList(entity_t *e, entity_t *list); +void Entity_RemoveFromList(entity_t *e); +bool EntityHasModel(entity_t *ent); + +void Entity_LinkBrush (entity_t *e, brush_t *b); +void Entity_UnlinkBrush (brush_t *b); +entity_t * FindEntity(const char *pszKey, const char *pszValue); +entity_t * FindEntityInt(const char *pszKey, int iValue); +entity_t * Entity_New(); +void Entity_SetName(entity_t *e, const char *name); + +int GetUniqueTargetId(int iHint); +eclass_t * GetCachedModel(entity_t *pEntity, const char *pName, idVec3 &vMin, idVec3 &vMax); + +//Timo : used for parsing epairs in brush primitive +void Entity_Name(entity_t *e, bool force); + +bool IsBrushSelected(brush_t* bSel); diff --git a/src/tools/radiant/EditorMap.cpp b/src/tools/radiant/EditorMap.cpp new file mode 100644 index 0000000..103363e --- /dev/null +++ b/src/tools/radiant/EditorMap.cpp @@ -0,0 +1,1624 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" + +int mapModified; // for quit confirmation (0 = clean, 1 = unsaved, + +// 2 = autosaved, but not regular saved) +char currentmap[1024]; + +brush_t active_brushes; // brushes currently being displayed +brush_t selected_brushes; // highlighted + +face_t *selected_face; +brush_t *selected_face_brush; + +brush_t filtered_brushes; // brushes that have been filtered or regioned + +entity_t entities; // head/tail of doubly linked list + +entity_t *world_entity = NULL; // "classname" "worldspawn" ! + +void AddRegionBrushes(void); +void RemoveRegionBrushes(void); + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void DupLists() { + DWORD dw = GetTickCount(); +} + +/* + * Cross map selection saving this could mess this up if you have only part of a + * complex entity selected... + */ +brush_t between_brushes; +entity_t between_entities; + +bool g_bRestoreBetween = false; + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Map_SaveBetween(void) { + if (g_pParentWnd->ActiveXY()) { + g_bRestoreBetween = true; + g_pParentWnd->ActiveXY()->Copy(); + } + + return; + +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Map_RestoreBetween(void) { + if (g_pParentWnd->ActiveXY() && g_bRestoreBetween) { + g_pParentWnd->ActiveXY()->Paste(); + } + + return; + +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +bool CheckForTinyBrush(brush_t *b, int n, float fSize) { + bool bTiny = false; + for (int i = 0; i < 3; i++) { + if (b->maxs[i] - b->mins[i] < fSize) { + bTiny = true; + } + } + + if (bTiny) { + common->Printf("Possible problem brush (too small) #%i ", n); + } + + return bTiny; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Map_BuildBrushData(void) { + brush_t *b, *next; + + if (active_brushes.next == NULL) { + return; + } + + Sys_BeginWait(); // this could take a while + + int n = 0; + for (b = active_brushes.next; b != NULL && b != &active_brushes; b = next) { + next = b->next; + Brush_Build(b, true, false, false); + if (!b->brush_faces || (g_PrefsDlg.m_bCleanTiny && CheckForTinyBrush(b, n++, g_PrefsDlg.m_fTinySize))) { + Brush_Free(b); + common->Printf("Removed degenerate brush\n"); + } + } + + Sys_EndWait(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +entity_t *Map_FindClass(char *cname) { + entity_t *ent; + + for (ent = entities.next; ent != &entities; ent = ent->next) { + if (!strcmp(cname, ValueForKey(ent, "classname"))) { + return ent; + } + } + + return NULL; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +int Map_GetUniqueEntityID(const char *prefix, const char *eclass) { + entity_t *ent; + int id = 0; + for (ent = entities.next; ent != &entities; ent = ent->next) { + if (!strcmp(eclass, ValueForKey(ent, "classname"))) { + const char *name = ValueForKey(ent, "name"); + if (name && name[0]) { + const char *buf; + if (prefix && *prefix) { + buf = va("%s_%s_", prefix, eclass); + } else { + buf = va("%s_", eclass); + } + int len = strlen(buf); + if ( idStr::Cmpn(name, buf, len) == 0 ) { + int j = atoi(name + len); + if (j > id) { + id = j; + } + } + } + } + } + + return id + 1; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +bool Entity_NameIsUnique(const char *name) { + entity_t *ent; + if (name == NULL) { + return false; + } + + for (ent = entities.next; ent != &entities; ent = ent->next) { + const char *testName = ValueForKey(ent, "name"); + if (testName) { + if ( idStr::Icmp(name, testName) == 0 ) { + return false; + } + } + } + + return true; +} + +/* + ======================================================================================================================= + Map_Free + ======================================================================================================================= + */ +void Map_Free(void) { + g_bRestoreBetween = false; + if (selected_brushes.next && (selected_brushes.next != &selected_brushes)) { + if (g_pParentWnd->MessageBox("Copy selection?", "", MB_YESNO) == IDYES) { + Map_SaveBetween(); + } + } + + // clear all the render and sound system data + g_qeglobals.rw->InitFromMap( NULL ); + soundSystem->StopAllSounds( SOUNDWORLD_EDITOR ); + + Texture_ClearInuse(); + Pointfile_Clear(); + strcpy(currentmap, "unnamed.map"); + Sys_SetTitle(currentmap); + g_qeglobals.d_num_entities = 0; + + if (!active_brushes.next) { // first map + active_brushes.prev = active_brushes.next = &active_brushes; + selected_brushes.prev = selected_brushes.next = &selected_brushes; + filtered_brushes.prev = filtered_brushes.next = &filtered_brushes; + + entities.prev = entities.next = &entities; + } + else { + while (active_brushes.next != &active_brushes) { + Brush_Free(active_brushes.next, false); + } + + while (selected_brushes.next != &selected_brushes) { + Brush_Free(selected_brushes.next, false); + } + + while (filtered_brushes.next != &filtered_brushes) { + Brush_Free(filtered_brushes.next, false); + } + + while (entities.next != &entities) { + Entity_Free(entities.next); + } + } + + if (world_entity) { + Entity_Free(world_entity); + } + + world_entity = NULL; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +entity_t *AngledEntity() { + entity_t *ent = Map_FindClass("info_player_start"); + if (!ent) { + ent = Map_FindClass("info_player_deathmatch"); + } + + if (!ent) { + ent = Map_FindClass("info_player_deathmatch"); + } + + if (!ent) { + ent = Map_FindClass("team_CTF_redplayer"); + } + + if (!ent) { + ent = Map_FindClass("team_CTF_blueplayer"); + } + + if (!ent) { + ent = Map_FindClass("team_CTF_redspawn"); + } + + if (!ent) { + ent = Map_FindClass("team_CTF_bluespawn"); + } + + return ent; +} + + +brush_t *BrushFromMapPatch(idMapPatch *mappatch, idVec3 origin) { + patchMesh_t *pm = MakeNewPatch(mappatch->GetWidth(), mappatch->GetHeight()); + pm->d_texture = Texture_ForName(mappatch->GetMaterial()); + for (int i = 0; i < mappatch->GetWidth(); i++) { + for (int j = 0; j < mappatch->GetHeight(); j++) { + pm->ctrl(i, j).xyz = (*mappatch)[j * mappatch->GetWidth() + i].xyz + origin; + pm->ctrl(i, j).st = (*mappatch)[j * mappatch->GetWidth() + i].st; + } + } + pm->horzSubdivisions = mappatch->GetHorzSubdivisions(); + pm->vertSubdivisions = mappatch->GetVertSubdivisions(); + pm->explicitSubdivisions = mappatch->GetExplicitlySubdivided(); + if (mappatch->epairs.GetNumKeyVals()) { + pm->epairs = new idDict; + *pm->epairs = mappatch->epairs; + } + brush_t *b = AddBrushForPatch(pm, false); + return b; +} + +brush_t *BrushFromMapBrush(idMapBrush *mapbrush, idVec3 origin) { + brush_t *b = NULL; + if (mapbrush) { + b = Brush_Alloc(); + int count = mapbrush->GetNumSides(); + for (int i = 0; i < count; i++) { + idMapBrushSide *side = mapbrush->GetSide(i); + face_t *f = Face_Alloc(); + f->next = NULL; + if (!b->brush_faces) { + b->brush_faces = f; + } + else { + face_t *scan; + for (scan = b->brush_faces; scan->next; scan = scan->next) { + ; + } + scan->next = f; + } + f->plane = side->GetPlane(); + f->originalPlane = f->plane; + f->dirty = false; + + idWinding w; + w.BaseForPlane(f->plane); + + for (int j = 0; j < 3; j++) { + f->planepts[j].x = w[j].x + origin.x; + f->planepts[j].y = w[j].y + origin.y; + f->planepts[j].z = w[j].z + origin.z; + } + + idVec3 mat[2]; + side->GetTextureMatrix(mat[0], mat[1]); + f->brushprimit_texdef.coords[0][0] = mat[0][0]; + f->brushprimit_texdef.coords[0][1] = mat[0][1]; + f->brushprimit_texdef.coords[0][2] = mat[0][2]; + f->brushprimit_texdef.coords[1][0] = mat[1][0]; + f->brushprimit_texdef.coords[1][1] = mat[1][1]; + f->brushprimit_texdef.coords[1][2] = mat[1][2]; + + f->texdef.SetName(side->GetMaterial()); + } + } + return b; +} + +entity_t *EntityFromMapEntity(idMapEntity *mapent, CWaitDlg *dlg) { + entity_t *ent = NULL; + if (mapent) { + ent = Entity_New(); + ent->brushes.onext = ent->brushes.oprev = &ent->brushes; + ent->origin.Zero(); + ent->epairs = mapent->epairs; + GetVectorForKey(ent, "origin", ent->origin); + int count = mapent->GetNumPrimitives(); + long lastUpdate = 0; + idStr status; + for (int i = 0; i < count; i++) { + idMapPrimitive *prim = mapent->GetPrimitive(i); + if (prim) { + // update 20 times a second + if ( (GetTickCount() - lastUpdate) > 50 ) { + lastUpdate = GetTickCount(); + if (prim->GetType() == idMapPrimitive::TYPE_BRUSH) { + sprintf(status, "Reading primitive %i (brush)", i); + } else if (prim->GetType() == idMapPrimitive::TYPE_PATCH) { + sprintf(status, "Reading primitive %i (patch)", i); + } + dlg->SetText(status, true); + } + if ( dlg->CancelPressed() ) { + return ent; + } + + brush_t *b = NULL; + if (prim->GetType() == idMapPrimitive::TYPE_BRUSH) { + idMapBrush *mapbrush = reinterpret_cast(prim); + b = BrushFromMapBrush(mapbrush, ent->origin); + } else if (prim->GetType() == idMapPrimitive::TYPE_PATCH) { + idMapPatch *mappatch = reinterpret_cast(prim); + b = BrushFromMapPatch(mappatch, ent->origin); + } + if (b) { + b->owner = ent; + // add to the end of the entity chain + b->onext = &ent->brushes; + b->oprev = ent->brushes.oprev; + ent->brushes.oprev->onext = b; + ent->brushes.oprev = b; + } + } + } + } + return ent; +} + +extern entity_t *Entity_PostParse(entity_t *ent, brush_t *pList); + /* + ======================================================================================================================= + Map_LoadFile + ======================================================================================================================= + */ +void Map_LoadFile(const char *filename) { + entity_t *ent; + CWaitDlg dlg; + idStr fileStr, status; + idMapFile mapfile; + + Sys_BeginWait(); + Select_Deselect(); + + dlg.AllowCancel( true ); + idStr( filename ).ExtractFileName( fileStr ); + sprintf( status, "Loading %s...", fileStr.c_str() ); + dlg.SetWindowText( status ); + sprintf( status, "Reading file %s...", fileStr.c_str() ); + dlg.SetText( status ); + + // SetInspectorMode(W_CONSOLE); + fileStr = filename; + fileStr.BackSlashesToSlashes(); + + common->Printf( "Map_LoadFile: %s\n", fileStr.c_str() ); + + Map_Free(); + + g_qeglobals.d_parsed_brushes = 0; + strcpy( currentmap, filename ); + + if(mapfile.Parse(filename, true, true)) { + g_qeglobals.bNeedConvert = false; + g_qeglobals.bOldBrushes = false; + g_qeglobals.bPrimitBrushes = false; + g_qeglobals.mapVersion = 1.0; + + long lastUpdate = 0; + int count = mapfile.GetNumEntities(); + for (int i = 0; i < count; i++) { + idMapEntity *mapent = mapfile.GetEntity(i); + if (mapent) { + idStr classname = mapent->epairs.GetString("classname"); + // Update 20 times a second + if ( (GetTickCount() - lastUpdate) > 50 ) { + lastUpdate = GetTickCount(); + sprintf(status, "Loading entity %i (%s)...", i, classname.c_str()); + dlg.SetText(status); + } + if ( dlg.CancelPressed() ) { + Sys_Status("Map load cancelled.\n"); + Map_New(); + return; + } + if (classname == "worldspawn") { + world_entity = EntityFromMapEntity(mapent, &dlg); + Entity_PostParse(world_entity, &active_brushes); + } else { + ent = EntityFromMapEntity(mapent, &dlg); + Entity_PostParse(ent, &active_brushes); + Entity_Name(ent, true); + // add the entity to the end of the entity list + ent->next = &entities; + ent->prev = entities.prev; + entities.prev->next = ent; + entities.prev = ent; + g_qeglobals.d_num_entities++; + } + } + } + } + + if (!world_entity) { + Sys_Status("No worldspawn in map.\n"); + Map_New(); + return; + } + + common->Printf("--- LoadMapFile ---\n"); + common->Printf("%s\n", fileStr.c_str()); + + common->Printf("%5i brushes\n", g_qeglobals.d_parsed_brushes); + common->Printf("%5i entities\n", g_qeglobals.d_num_entities); + + dlg.SetText("Restoring Between"); + Map_RestoreBetween(); + + dlg.SetText("Building Brush Data"); + common->Printf("Map_BuildAllDisplayLists\n"); + Map_BuildBrushData(); + + // + // reset the "need conversion" flag conversion to the good format done in + // Map_BuildBrushData + // + g_qeglobals.bNeedConvert = false; + + // move the view to a start position + ent = AngledEntity(); + + g_pParentWnd->GetCamera()->Camera().angles[PITCH] = 0; + + if (ent) { + GetVectorForKey(ent, "origin", g_pParentWnd->GetCamera()->Camera().origin); + GetVectorForKey(ent, "origin", g_pParentWnd->GetXYWnd()->GetOrigin()); + g_pParentWnd->GetCamera()->Camera().angles[YAW] = FloatForKey(ent, "angle"); + } + else { + g_pParentWnd->GetCamera()->Camera().angles[YAW] = 0; + VectorCopy(vec3_origin, g_pParentWnd->GetCamera()->Camera().origin); + VectorCopy(vec3_origin, g_pParentWnd->GetXYWnd()->GetOrigin()); + } + + Map_RegionOff(); + + mapModified = 0; + + if (GetFileAttributes(filename) & FILE_ATTRIBUTE_READONLY) { + fileStr += " (read only) "; + } + Sys_SetTitle(fileStr); + + Texture_ShowInuse(); + + if (g_pParentWnd->GetCamera()->GetRenderMode()) { + g_pParentWnd->GetCamera()->BuildRendererState(); + } + + Sys_EndWait(); + Sys_UpdateWindows(W_ALL); +} + + +void Map_VerifyCurrentMap(const char *map) { + if ( idStr::Icmp( map, currentmap ) != 0 ) { + Map_LoadFile( map ); + } +} + +idMapPrimitive *BrushToMapPrimitive( const brush_t *b, const idVec3 &origin ) { + if ( b->pPatch ) { + idMapPatch *patch = new idMapPatch( b->pPatch->width * 6, b->pPatch->height * 6 ); + patch->SetSize( b->pPatch->width, b->pPatch->height ); + for ( int i = 0; i < b->pPatch->width; i++ ) { + for ( int j = 0; j < b->pPatch->height; j++ ) { + (*patch)[j*patch->GetWidth()+i].xyz = b->pPatch->ctrl(i, j).xyz - origin; + (*patch)[j*patch->GetWidth()+i].st = b->pPatch->ctrl(i, j).st; + } + } + patch->SetExplicitlySubdivided( b->pPatch->explicitSubdivisions ); + if ( b->pPatch->explicitSubdivisions ) { + patch->SetHorzSubdivisions( b->pPatch->horzSubdivisions ); + patch->SetVertSubdivisions( b->pPatch->vertSubdivisions ); + } + patch->SetMaterial( b->pPatch->d_texture->GetName() ); + if ( b->pPatch->epairs ) { + patch->epairs = *b->pPatch->epairs; + } + return patch; + } + else { + idMapBrush *mapbrush = new idMapBrush; + for ( face_t *f = b->brush_faces; f; f = f->next ) { + idMapBrushSide *side = new idMapBrushSide; + + idPlane plane; + if ( f->dirty ) { + f->planepts[0] -= origin; + f->planepts[1] -= origin; + f->planepts[2] -= origin; + plane.FromPoints( f->planepts[0], f->planepts[1], f->planepts[2], false ); + f->planepts[0] += origin; + f->planepts[1] += origin; + f->planepts[2] += origin; + } else { + plane = f->originalPlane; + } + side->SetPlane( plane ); + side->SetMaterial( f->d_texture->GetName() ); + idVec3 mat[2]; + mat[0][0] = f->brushprimit_texdef.coords[0][0]; + mat[0][1] = f->brushprimit_texdef.coords[0][1]; + mat[0][2] = f->brushprimit_texdef.coords[0][2]; + mat[1][0] = f->brushprimit_texdef.coords[1][0]; + mat[1][1] = f->brushprimit_texdef.coords[1][1]; + mat[1][2] = f->brushprimit_texdef.coords[1][2]; + side->SetTextureMatrix(mat); + mapbrush->AddSide(side); + mapbrush->epairs = b->epairs; + } + return mapbrush; + } +} + +idMapEntity *EntityToMapEntity(entity_t *e, bool use_region, CWaitDlg *dlg) { + idMapEntity *mapent = new idMapEntity; + mapent->epairs = e->epairs; + idStr status; + int count = 0; + long lastUpdate = 0; + if ( !EntityHasModel( e ) ) { + for ( brush_t *b = e->brushes.onext; b != &e->brushes; b = b->onext ) { + count++; + if ( e->eclass->fixedsize && !b->entityModel ) { + continue; + } + if ( !use_region || !Map_IsBrushFiltered( b ) ) { + // Update 20 times a second + if ( GetTickCount() - lastUpdate > 50 ) { + lastUpdate = GetTickCount(); + if ( b->pPatch ) { + sprintf( status, "Adding primitive %i (patch)", count ); + dlg->SetText( status, true ); + } else { + sprintf( status, "Adding primitive %i (brush)", count ); + dlg->SetText( status, true ); + } + } + idMapPrimitive *prim = BrushToMapPrimitive( b, e->origin ); + if ( prim ) { + mapent->AddPrimitive( prim ); + } + } + } + } + return mapent; +} + +/* + ======================================================================================================================= + Map_SaveFile + ======================================================================================================================= + */ +bool Map_SaveFile(const char *filename, bool use_region, bool autosave) { + entity_t *e, *next; + idStr temp; + int count; + brush_t *b; + idStr status; + + int len = strlen(filename); + WIN32_FIND_DATA FileData; + if (FindFirstFile(filename, &FileData) != INVALID_HANDLE_VALUE) { + // the file exists; + if (len > 0 && GetFileAttributes(filename) & FILE_ATTRIBUTE_READONLY) { + g_pParentWnd->MessageBox("File is read only", "Read Only", MB_OK); + return false; + } + } + + if (filename == NULL || len == 0 || (filename && stricmp(filename, "unnamed.map") == 0)) { + CFileDialog dlgSave(FALSE,"map",NULL,OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,"Map Files (*.map)|*.map||",AfxGetMainWnd()); + if (dlgSave.DoModal() == IDOK) { + filename = dlgSave.m_ofn.lpstrFile; + strcpy(currentmap, filename); + } + else { + return false; + } + } + + MEMORYSTATUSEX statex; + statex.dwLength = sizeof (statex); + GlobalMemoryStatusEx (&statex); + if ( statex.dwMemoryLoad > 95 ) { + g_pParentWnd->MessageBox("Physical memory is over 95% utilized. Consider saving and restarting", "Memory"); + } + + CWaitDlg dlg; + Pointfile_Clear(); + + temp = filename; + temp.BackSlashesToSlashes(); + + if ( !use_region ) { + idStr backup; + backup = temp; + backup.StripFileExtension(); + backup.SetFileExtension( ".bak" ); + if ( _unlink(backup) != 0 && errno != 2 ) { // errno 2 means the file doesn't exist, which we don't care about + g_pParentWnd->MessageBox( va("Unable to delete %s: %s", backup.c_str(), strerror(errno) ), "File Error" ); + } + + if ( rename(filename, backup) != 0 ) { + g_pParentWnd->MessageBox( va("Unable to rename %s to %s: %s", filename, backup.c_str(), strerror(errno) ), "File Error" ); + } + } + + common->Printf("Map_SaveFile: %s\n", filename); + + idStr mapFile; + bool localFile = (strstr(filename, ":") != NULL); + if (autosave || localFile) { + mapFile = filename; + } else { + mapFile = fileSystem->OSPathToRelativePath( filename ); + } + + if (use_region) { + AddRegionBrushes(); + } + + idMapFile map; + world_entity->origin.Zero(); + idMapEntity *mapentity = EntityToMapEntity(world_entity, use_region, &dlg); + dlg.SetText("Saving worldspawn..."); + map.AddEntity(mapentity); + + if ( use_region ) { + idStr buf; + sprintf( buf, "{\n\"classname\" \"info_player_start\"\n\"origin\"\t \"%i %i %i\"\n\"angle\"\t \"%i\"\n}\n", + (int)g_pParentWnd->GetCamera()->Camera().origin[0], + (int)g_pParentWnd->GetCamera()->Camera().origin[1], + (int)g_pParentWnd->GetCamera()->Camera().origin[2], + (int)g_pParentWnd->GetCamera()->Camera().angles[YAW] ); + idAutoPtr lexer( LexerFactory::MakeLexer( buf.c_str(), buf.Length(), "regionbuf", + LEXFL_NOSTRINGCONCAT | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES ) ); + idMapEntity *playerstart = idMapEntity::Parse( *lexer ); + map.AddEntity( playerstart ); + } + + count = -1; + for ( e = entities.next; e != &entities; e = next ) { + count++; + next = e->next; + if (e->brushes.onext == &e->brushes) { + Entity_Free(e); // no brushes left, so remove it + } + else { + if (use_region) { + for (b = e->brushes.onext; b != &e->brushes; b = b->onext) { + if (!Map_IsBrushFiltered(b)) { + break; // got one + } + } + + if (b == &e->brushes) { + continue; // nothing visible + } + + } + idVec3 origin; + if (!GetVectorForKey(e, "origin", origin)) { + idStr text; + VectorSubtract(e->brushes.onext->mins, e->eclass->mins, origin); + sprintf(text, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]); + SetKeyValue(e, "origin", text); + } + + if (use_region && !idStr::Icmp(ValueForKey(e, "classname"), "info_player_start")) { + continue; + } + + idStr classname = e->epairs.GetString("classname"); + sprintf(status, "Saving entity %i (%s)...", count, classname.c_str()); + dlg.SetText(status); + + map.AddEntity(EntityToMapEntity(e, use_region, &dlg)); + count++; + } + } + + mapFile.StripFileExtension(); + idStr mapExt = (use_region) ? ".reg" : ".map"; + sprintf(status, "Writing file %s.%s...", mapFile.c_str(), mapExt.c_str()); + dlg.SetText(status); + map.Write(mapFile, mapExt, !(autosave || localFile)); + mapModified = 0; + + if (use_region) { + RemoveRegionBrushes(); + } + + if (!strstr(temp, "autosave")) { + Sys_SetTitle(temp); + } + + Sys_Status("Saved.\n", 0); + + return true; +} + +/* + ======================================================================================================================= + Map_New + ======================================================================================================================= + */ +void Map_New(void) { + common->Printf("Map_New\n"); + Map_Free(); + + Patch_Cleanup(); + g_Inspectors->entityDlg.SetEditEntity ( NULL ); + + world_entity = Entity_New(); + world_entity->brushes.onext = world_entity->brushes.oprev = &world_entity->brushes; + SetKeyValue(world_entity, "classname", "worldspawn"); + world_entity->eclass = Eclass_ForName("worldspawn", true); + + g_pParentWnd->GetCamera()->Camera().angles[YAW] = 0; + g_pParentWnd->GetCamera()->Camera().angles[PITCH] = 0; + VectorCopy(vec3_origin, g_pParentWnd->GetCamera()->Camera().origin); + g_pParentWnd->GetCamera()->Camera().origin[2] = 48; + VectorCopy(vec3_origin, g_pParentWnd->GetXYWnd()->GetOrigin()); + + Map_RestoreBetween(); + + Sys_UpdateWindows(W_ALL); + mapModified = 0; + + g_qeglobals.mapVersion = MAP_VERSION; + +} + + +bool region_active; +idVec3 region_mins(MIN_WORLD_COORD, MIN_WORLD_COORD, MIN_WORLD_COORD); +idVec3 region_maxs(MAX_WORLD_COORD, MAX_WORLD_COORD, MAX_WORLD_COORD); + +brush_t *region_sides[6]; + +/* + ======================================================================================================================= + AddRegionBrushes a regioned map will have temp walls put up at the region boundary + ======================================================================================================================= + */ +void AddRegionBrushes(void) { + idVec3 mins, maxs; + int i; + texdef_t td; + + if (!region_active) { + return; + } + + memset(&td, 0, sizeof(td)); + td = g_qeglobals.d_texturewin.texdef; + + // strcpy (td.name, "REGION"); + td.SetName("textures/REGION"); + +const int REGION_WIDTH = 1024; + + + mins[0] = region_mins[0] - REGION_WIDTH; + maxs[0] = region_mins[0] + 1; + mins[1] = region_mins[1] - REGION_WIDTH; + maxs[1] = region_maxs[1] + REGION_WIDTH; + mins[2] = MIN_WORLD_COORD; + maxs[2] = MAX_WORLD_COORD; + region_sides[0] = Brush_Create(mins, maxs, &td); + + + mins[0] = region_maxs[0] - 1; + maxs[0] = region_maxs[0] + REGION_WIDTH; + region_sides[1] = Brush_Create(mins, maxs, &td); + + mins[0] = region_mins[0] - REGION_WIDTH; + maxs[0] = region_maxs[0] + REGION_WIDTH; + mins[1] = region_mins[1] - REGION_WIDTH; + maxs[1] = region_mins[1] + 1; + region_sides[2] = Brush_Create(mins, maxs, &td); + + mins[1] = region_maxs[1] - 1; + maxs[1] = region_maxs[1] + REGION_WIDTH; + region_sides[3] = Brush_Create(mins, maxs, &td); + + mins = region_mins; + maxs = region_maxs; + maxs[2] = mins[2] + REGION_WIDTH; + region_sides[4] = Brush_Create(mins, maxs, &td); + + mins = region_mins; + maxs = region_maxs; + mins[2] = maxs[2] - REGION_WIDTH; + region_sides[5] = Brush_Create(mins, maxs, &td); + + for (i = 0; i < 6; i++) { + Brush_AddToList(region_sides[i], &selected_brushes); + Entity_LinkBrush(world_entity, region_sides[i]); + Brush_Build(region_sides[i]); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void RemoveRegionBrushes(void) { + int i; + + if (!region_active) { + return; + } + + for (i = 0; i < 6; i++) { + Brush_Free(region_sides[i]); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +bool Map_IsBrushFiltered(brush_t *b) { + int i; + + if (!region_active) { + return false; + } + + for (i = 0; i < 3; i++) { + if (b->mins[i] > region_maxs[i]) { + return true; + } + + if (b->maxs[i] < region_mins[i]) { + return true; + } + } + + return false; +} + +/* + ======================================================================================================================= + Map_RegionOff Other filtering options may still be on + ======================================================================================================================= + */ +void Map_RegionOff(void) { + brush_t *b, *next; + int i; + + region_active = false; + for (i = 0; i < 3; i++) { + region_maxs[i] = MAX_WORLD_COORD; // 4096; + region_mins[i] = MIN_WORLD_COORD; // -4096; + } + + for (b = filtered_brushes.next; b != &filtered_brushes; b = next) { + next = b->next; + if (Map_IsBrushFiltered(b)) { + continue; // still filtered + } + + Brush_RemoveFromList(b); + if (active_brushes.next == NULL || active_brushes.prev == NULL) { + active_brushes.next = &active_brushes; + active_brushes.prev = &active_brushes; + } + + Brush_AddToList(b, &active_brushes); + } + + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Map_ApplyRegion(void) { + brush_t *b, *next; + + region_active = true; + for (b = active_brushes.next; b != &active_brushes; b = next) { + next = b->next; + if (!Map_IsBrushFiltered(b)) { + continue; // still filtered + } + + Brush_RemoveFromList(b); + Brush_AddToList(b, &filtered_brushes); + } + + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + Map_RegionSelectedBrushes + ======================================================================================================================= + */ +void Map_RegionSelectedBrushes(void) { + Map_RegionOff(); + + if (selected_brushes.next == &selected_brushes) { // nothing selected + Sys_Status("Tried to region with no selection...\n"); + return; + } + + region_active = true; + Select_GetBounds(region_mins, region_maxs); + + // move the entire active_brushes list to filtered_brushes + filtered_brushes.next = active_brushes.next; + filtered_brushes.prev = active_brushes.prev; + filtered_brushes.next->prev = &filtered_brushes; + filtered_brushes.prev->next = &filtered_brushes; + + Patch_Deselect(); + // move the entire selected_brushes list to active_brushes + active_brushes.next = selected_brushes.next; + active_brushes.prev = selected_brushes.prev; + active_brushes.next->prev = &active_brushes; + active_brushes.prev->next = &active_brushes; + + // clear selected_brushes + selected_brushes.next = selected_brushes.prev = &selected_brushes; + + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + Map_RegionXY + ======================================================================================================================= + */ +void Map_RegionXY(void) { + Map_RegionOff(); + + region_mins[0] = g_pParentWnd->GetXYWnd()->GetOrigin()[0] - + 0.5 * + g_pParentWnd->GetXYWnd()->Width() / + g_pParentWnd->GetXYWnd()->Scale(); + region_maxs[0] = g_pParentWnd->GetXYWnd()->GetOrigin()[0] + + 0.5 * + g_pParentWnd->GetXYWnd()->Width() / + g_pParentWnd->GetXYWnd()->Scale(); + region_mins[1] = g_pParentWnd->GetXYWnd()->GetOrigin()[1] - + 0.5 * + g_pParentWnd->GetXYWnd()->Height() / + g_pParentWnd->GetXYWnd()->Scale(); + region_maxs[1] = g_pParentWnd->GetXYWnd()->GetOrigin()[1] + + 0.5 * + g_pParentWnd->GetXYWnd()->Height() / + g_pParentWnd->GetXYWnd()->Scale(); + region_mins[2] = MIN_WORLD_COORD; + region_maxs[2] = MAX_WORLD_COORD; + Map_ApplyRegion(); +} + +/* + ======================================================================================================================= + Map_RegionTallBrush + ======================================================================================================================= + */ +void Map_RegionTallBrush(void) { + brush_t *b; + + if (!QE_SingleBrush()) { + return; + } + + b = selected_brushes.next; + + Map_RegionOff(); + + VectorCopy(b->mins, region_mins); + VectorCopy(b->maxs, region_maxs); + region_mins[2] = MIN_WORLD_COORD; + region_maxs[2] = MAX_WORLD_COORD; + + Select_Delete(); + Map_ApplyRegion(); +} + +/* + ======================================================================================================================= + Map_RegionBrush + ======================================================================================================================= + */ +void Map_RegionBrush(void) { + brush_t *b; + + if (!QE_SingleBrush()) { + return; + } + + b = selected_brushes.next; + + Map_RegionOff(); + + VectorCopy(b->mins, region_mins); + VectorCopy(b->maxs, region_maxs); + + Select_Delete(); + Map_ApplyRegion(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void UniqueTargetName(idStr &rStr) { + // make a unique target value + int maxtarg = 0; + for (entity_t * e = entities.next; e != &entities; e = e->next) { + const char *tn = ValueForKey(e, "name"); + if (tn && tn[0]) { + int targetnum = atoi(tn + 1); + if (targetnum > maxtarg) { + maxtarg = targetnum; + } + } + else { + tn = ValueForKey(e, "target"); + if (tn && tn[0]) { + int targetnum = atoi(tn + 1); + if (targetnum > maxtarg) { + maxtarg = targetnum; + } + } + } + } + + sprintf(rStr, "t%i", maxtarg + 1); +} + +// +// ======================================================================================================================= +// Map_ImportFile Timo 09/01/99:: called by CXYWnd::Paste & Map_ImportFile if Map_ImportFile ( prefab ), the buffer +// may contain brushes in old format ( conversion needed ) +// ======================================================================================================================= +// +void Map_ImportBuffer(char *buf, bool renameEntities) { + entity_t *ent; + brush_t *b = NULL; + CPtrArray ptrs; + + Select_Deselect(); + + Undo_Start("import buffer"); + + g_qeglobals.d_parsed_brushes = 0; + if (buf) { + CMapStringToString mapStr; + StartTokenParsing(buf); + g_qeglobals.d_num_entities = 0; + + // + // Timo will be used in Entity_Parse to detect if a conversion between brush + // formats is needed + // + g_qeglobals.bNeedConvert = false; + g_qeglobals.bOldBrushes = false; + g_qeglobals.bPrimitBrushes = false; + g_qeglobals.mapVersion = 1.0; + + if (GetToken(true)) { + if (stricmp(token, "Version") == 0) { + GetToken(false); + g_qeglobals.mapVersion = atof(token); + common->Printf("Map version: %1.2f\n", g_qeglobals.mapVersion); + } else { + UngetToken(); + } + } + + idDict RemappedNames; // since I can't use "map "... sigh. So much for STL... + + while (1) { + // + // use the selected brushes list as it's handy ent = Entity_Parse (false, + // &selected_brushes); + // + ent = Entity_Parse(false, &active_brushes); + if (!ent) { + break; + } + + // end entity for undo + Undo_EndEntity(ent); + + // end brushes for undo + for (b = ent->brushes.onext; b && b != &ent->brushes; b = b->onext) { + Undo_EndBrush(b); + } + + if (!strcmp(ValueForKey(ent, "classname"), "worldspawn")) { + // world brushes need to be added to the current world entity + b = ent->brushes.onext; + while (b && b != &ent->brushes) { + brush_t *bNext = b->onext; + Entity_UnlinkBrush(b); + Entity_LinkBrush(world_entity, b); + ptrs.Add(b); + b = bNext; + } + } + else { + // the following bit remaps conflicting target/targetname key/value pairs + CString str = ValueForKey(ent, "target"); + CString strKey; + CString strTarget(""); + if (str.GetLength() > 0) { + if (FindEntity("target", str.GetBuffer(0))) { + if (!mapStr.Lookup(str, strKey)) { + idStr key; + UniqueTargetName(key); + strKey = key; + mapStr.SetAt(str, strKey); + } + + strTarget = strKey; + SetKeyValue(ent, "target", strTarget.GetBuffer(0)); + } + } + + /* + * str = ValueForKey(ent, "name"); if (str.GetLength() > 0) { if + * (FindEntity("name", str.GetBuffer(0))) { if (!mapStr.Lookup(str, strKey)) { + * UniqueTargetName(strKey); mapStr.SetAt(str, strKey); } Entity_SetName(ent, + * strKey.GetBuffer(0)); } } + */ + CString cstrNameOld = ValueForKey(ent, "name"); + Entity_Name(ent, renameEntities); + CString cstrNameNew = ValueForKey(ent, "name"); + if (cstrNameOld != cstrNameNew) + { + RemappedNames.Set(cstrNameOld, cstrNameNew); + } + // + // if (strTarget.GetLength() > 0) SetKeyValue(ent, "target", + // strTarget.GetBuffer(0)); + // add the entity to the end of the entity list + // + ent->next = &entities; + ent->prev = entities.prev; + entities.prev->next = ent; + entities.prev = ent; + g_qeglobals.d_num_entities++; + + for (b = ent->brushes.onext; b != &ent->brushes; b = b->onext) { + ptrs.Add(b); + } + } + } + + // now iterate through the remapped names, and see if there are any target-connections that need remaking... + // + // (I could probably write this in half the size with STL, but WTF, work with what we have...) + // + int iNumKeyVals = RemappedNames.GetNumKeyVals(); + for (int iKeyVal=0; iKeyVal < iNumKeyVals; iKeyVal++) + { + const idKeyValue *pKeyVal = RemappedNames.GetKeyVal( iKeyVal ); + + LPCSTR psOldName = pKeyVal->GetKey().c_str(); + LPCSTR psNewName = pKeyVal->GetValue().c_str(); + + entity_t *pEntOld = FindEntity("name", psOldName); // original ent we cloned from + entity_t *pEntNew = FindEntity("name", psNewName); // cloned ent + + if (pEntOld && pEntNew) + { + CString cstrTargetNameOld = ValueForKey(pEntOld, "target"); + if (!cstrTargetNameOld.IsEmpty()) + { + // ok, this ent was targeted at another ent, so it's clone needs updating to point to + // the clone of that target, so... + // + entity_t *pEntOldTarget = FindEntity("name", cstrTargetNameOld); + if ( pEntOldTarget ) + { + LPCSTR psNewTargetName = RemappedNames.GetString( cstrTargetNameOld ); + if (psNewTargetName && psNewTargetName[0]) + { + SetKeyValue(pEntNew, "target", psNewTargetName); + } + } + } + } + } + } + + // + // ::ShowWindow(g_qeglobals.d_hwndEntity, FALSE); + // ::LockWindowUpdate(g_qeglobals.d_hwndEntity); + // + g_bScreenUpdates = false; + for (int i = 0; i < ptrs.GetSize(); i++) { + Brush_Build(reinterpret_cast < brush_t * > (ptrs[i]), true, false); + Select_Brush(reinterpret_cast < brush_t * > (ptrs[i]), true, false); + } + + // ::LockWindowUpdate(NULL); + g_bScreenUpdates = true; + + ptrs.RemoveAll(); + + // + // reset the "need conversion" flag conversion to the good format done in + // Map_BuildBrushData + // + g_qeglobals.bNeedConvert = false; + + Sys_UpdateWindows(W_ALL); + + // Sys_MarkMapModified(); + mapModified = 1; + + Undo_End(); +} + +// +// ======================================================================================================================= +// Map_ImportFile +// ======================================================================================================================= +// +void Map_ImportFile(char *fileName) { + char *buf; + idStr temp; + Sys_BeginWait(); + temp = fileName; + temp.BackSlashesToSlashes(); + if (LoadFile( temp, (void **) &buf) != -1) { + Map_ImportBuffer(buf); + Mem_Free( buf ); + Map_BuildBrushData(); + } + + Sys_UpdateWindows(W_ALL); + mapModified = 1; + Sys_EndWait(); +} + +// +// ======================================================================================================================= +// Map_SaveSelected Saves selected world brushes and whole entities with partial/full selections +// ======================================================================================================================= +// +void Map_SaveSelected(char *fileName) { + entity_t *e, *next; + FILE *f; + idStr temp; + int count; + + temp = fileName; + temp.BackSlashesToSlashes(); + f = fopen(temp, "w"); + + if ( !f ) { + common->Printf( "ERROR!!!! Couldn't open %s\n", temp.c_str() ); + return; + } + + // write version + g_qeglobals.mapVersion = MAP_VERSION; + fprintf( f, "Version %1.2f\n", MAP_VERSION ); + + // write world entity second + world_entity->origin.Zero(); + Entity_WriteSelected( world_entity, f ); + + // then write all other ents + count = 1; + for ( e = entities.next; e != &entities; e = next ) { + fprintf( f, "// entity %i\n", count ); + count++; + Entity_WriteSelected( e, f ); + next = e->next; + } + + fclose( f ); +} + +// +// ======================================================================================================================= +// Map_SaveSelected Saves selected world brushes and whole entities with partial/full selections +// ======================================================================================================================= +// +void Map_SaveSelected(CMemFile *pMemFile, CMemFile *pPatchFile) { + entity_t *e, *next; + int count; + CString strTemp; + + // write version + g_qeglobals.mapVersion = MAP_VERSION; + MemFile_fprintf(pMemFile, "Version %1.2f\n", MAP_VERSION); + + // write world entity first + world_entity->origin.Zero(); + Entity_WriteSelected(world_entity, pMemFile); + + // then write all other ents + count = 1; + for (e = entities.next; e != &entities; e = next) { + MemFile_fprintf(pMemFile, "// entity %i\n", count); + count++; + Entity_WriteSelected(e, pMemFile); + next = e->next; + } + + // if (pPatchFile) Patch_WriteFile(pPatchFile); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ + +/* +================ +WriteFileString +================ +*/ +bool WriteFileString( FILE *fp, char *string, ... ) { + long i; + unsigned long u; + double f; + char *str; + idStr buf; + va_list argPtr; + + va_start( argPtr, string ); + + while( *string ) { + switch( *string ) { + case '%': + string++; + while ( (*string >= '0' && *string <= '9') || + *string == '.' || *string == '-' || *string == '+' || *string == '#') { + string++; + } + switch( *string ) { + case 'f': + case 'e': + case 'E': + case 'g': + case 'G': + f = va_arg( argPtr, double ); + sprintf( buf, "%1.10f", f ); + buf.StripTrailing( '0' ); + buf.StripTrailing( '.' ); + fprintf( fp, "%s", buf.c_str() ); + break; + case 'd': + case 'i': + i = va_arg( argPtr, long ); + fprintf( fp, "%d", i ); + break; + case 'u': + u = va_arg( argPtr, unsigned long ); + fprintf( fp, "%u", u ); + break; + case 'o': + u = va_arg( argPtr, unsigned long ); + fprintf( fp, "%o", u ); + break; + case 'x': + u = va_arg( argPtr, unsigned long ); + fprintf( fp, "%x", u ); + break; + case 'X': + u = va_arg( argPtr, unsigned long ); + fprintf( fp, "%X", u ); + break; + case 'c': + i = va_arg( argPtr, long ); + fprintf( fp, "%c", (char) i ); + break; + case 's': + str = va_arg( argPtr, char * ); + fprintf( fp, "%s", str ); + break; + case '%': + fprintf( fp, "%%" ); + break; + default: + common->Error( "WriteFileString: invalid %%%c", *string ); + break; + } + string++; + break; + case '\\': + string++; + switch( *string ) { + case 't': + fprintf( fp, "\t" ); + break; + case 'n': + fprintf( fp, "\n" ); + default: + common->Error( "WriteFileString: unknown escape character \'%c\'", *string ); + break; + } + string++; + break; + default: + fprintf( fp, "%c", *string ); + string++; + break; + } + } + + va_end( argPtr ); + + return true; +} + +/* +================ +MemFile_fprintf +================ +*/ +void MemFile_fprintf( CMemFile *pMemFile, const char *string, ... ) { + char Buffer[4096]; + long i; + unsigned long u; + double f; + char *str; + idStr buf, out; + va_list argPtr; + + char *buff = Buffer; + + va_start( argPtr, string ); + + while( *string ) { + switch( *string ) { + case '%': + string++; + while ( (*string >= '0' && *string <= '9') || + *string == '.' || *string == '-' || *string == '+' || *string == '#') { + string++; + } + switch( *string ) { + case 'f': + case 'e': + case 'E': + case 'g': + case 'G': + f = va_arg( argPtr, double ); + sprintf( buf, "%1.10f", f ); + buf.StripTrailing( '0' ); + buf.StripTrailing( '.' ); + sprintf( buff, "%s", buf.c_str() ); + break; + case 'd': + case 'i': + i = va_arg( argPtr, long ); + sprintf( buff, "%d", i ); + break; + case 'u': + u = va_arg( argPtr, unsigned long ); + sprintf( buff, "%u", u ); + break; + case 'o': + u = va_arg( argPtr, unsigned long ); + sprintf( buff, "%o", u ); + break; + case 'x': + u = va_arg( argPtr, unsigned long ); + sprintf( buff, "%x", u ); + break; + case 'X': + u = va_arg( argPtr, unsigned long ); + sprintf( buff, "%X", u ); + break; + case 'c': + i = va_arg( argPtr, long ); + sprintf( buff, "%c", (char) i ); + break; + case 's': + str = va_arg( argPtr, char * ); + sprintf( buff, "%s", str ); + break; + case '%': + sprintf( buff, "%%" ); + break; + default: + common->Error( "MemFile_fprintf: invalid %%%c", *string ); + break; + } + string++; + break; + case '\\': + string++; + switch( *string ) { + case 't': + sprintf( buff, "\t" ); + break; + case 'n': + sprintf( buff, "\n" ); + default: + common->Error( "MemFile_fprintf: unknown escape character \'%c\'", *string ); + break; + } + string++; + break; + default: + sprintf( buff, "%c", *string ); + string++; + break; + } + + buff = Buffer + strlen(Buffer); + } + + va_end( argPtr ); + + out = Buffer; + pMemFile->Write( out.c_str(), out.Length() ); +} diff --git a/src/tools/radiant/EditorMap.h b/src/tools/radiant/EditorMap.h new file mode 100644 index 0000000..684f4ea --- /dev/null +++ b/src/tools/radiant/EditorMap.h @@ -0,0 +1,67 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +extern char currentmap[1024]; + +// head/tail of doubly linked lists +extern brush_t active_brushes; // brushes currently being displayed +extern brush_t selected_brushes; // highlighted + + +extern CPtrArray& g_ptrSelectedFaces; +extern CPtrArray& g_ptrSelectedFaceBrushes; +//extern face_t *selected_face; +//extern brush_t *selected_face_brush; +extern brush_t filtered_brushes; // brushes that have been filtered or regioned + +extern entity_t entities; +extern entity_t *world_entity; // the world entity is NOT included in + // the entities chain + +extern int modified; // for quit confirmations + +extern idVec3 region_mins, region_maxs; +extern bool region_active; + +void Map_LoadFile (const char *filename); +bool Map_SaveFile (const char *filename, bool use_region, bool autosave = false); +void Map_New (void); +void Map_BuildBrushData(void); + +void Map_RegionOff (void); +void Map_RegionXY (void); +void Map_RegionTallBrush (void); +void Map_RegionBrush (void); +void Map_RegionSelectedBrushes (void); +bool Map_IsBrushFiltered (brush_t *b); + +void Map_SaveSelected(CMemFile* pMemFile, CMemFile* pPatchFile = NULL); +void Map_ImportBuffer (char* buf, bool renameEntities = true); +int Map_GetUniqueEntityID(const char *prefix, const char *eclass); + +idMapPrimitive *BrushToMapPrimitive( const brush_t *b, const idVec3 &origin ); diff --git a/src/tools/radiant/EntKeyFindReplace.cpp b/src/tools/radiant/EntKeyFindReplace.cpp new file mode 100644 index 0000000..e88a25e --- /dev/null +++ b/src/tools/radiant/EntKeyFindReplace.cpp @@ -0,0 +1,199 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +//#include "stdafx.h" +#include "radiant.h" +#include "GetString.h" // for ErrorBox() etc +#include "qe3.h" + +#include "EntKeyFindReplace.h" +//#include "oddbits.h" +/* +#include "stdafx.h" +#include "Radiant.h" +#include "ZWnd.h" +#include "qe3.h" +#include "zclip.h" +*/ + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CEntKeyFindReplace dialog + +CEntKeyFindReplace::CEntKeyFindReplace( CString* p_strFindKey, + CString* p_strFindValue, + CString* p_strReplaceKey, + CString* p_strReplaceValue, + bool* p_bWholeStringMatchOnly, + bool* p_bSelectAllMatchingEnts, + CWnd* pParent /*=NULL*/) + : CDialog(CEntKeyFindReplace::IDD, pParent) +{ + m_pStrFindKey = p_strFindKey; + m_pStrFindValue = p_strFindValue; + m_pStrReplaceKey = p_strReplaceKey; + m_pStrReplaceValue = p_strReplaceValue; + m_pbWholeStringMatchOnly = p_bWholeStringMatchOnly; + m_pbSelectAllMatchingEnts= p_bSelectAllMatchingEnts; + + //{{AFX_DATA_INIT(CEntKeyFindReplace) + m_strFindKey = *m_pStrFindKey; + m_strFindValue = *m_pStrFindValue; + m_strReplaceKey = *m_pStrReplaceKey; + m_strReplaceValue = *m_pStrReplaceValue; + m_bWholeStringMatchOnly = *m_pbWholeStringMatchOnly; + m_bSelectAllMatchingEnts = *m_pbSelectAllMatchingEnts; + //}}AFX_DATA_INIT +} + + +void CEntKeyFindReplace::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CEntKeyFindReplace) + DDX_Text(pDX, IDC_EDIT_FIND_KEY, m_strFindKey); + DDX_Text(pDX, IDC_EDIT_FIND_VALUE, m_strFindValue); + DDX_Text(pDX, IDC_EDIT_REPLACE_KEY, m_strReplaceKey); + DDX_Text(pDX, IDC_EDIT_REPLACE_VALUE, m_strReplaceValue); + DDX_Check(pDX, IDC_CHECK_FIND_WHOLESTRINGMATCHONLY, m_bWholeStringMatchOnly); + DDX_Check(pDX, IDC_CHECK_SELECTALLMATCHING, m_bSelectAllMatchingEnts); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CEntKeyFindReplace, CDialog) + //{{AFX_MSG_MAP(CEntKeyFindReplace) + ON_BN_CLICKED(IDC_REPLACE, OnReplace) + ON_BN_CLICKED(IDC_FIND, OnFind) + ON_BN_CLICKED(IDC_KEYCOPY, OnKeycopy) + ON_BN_CLICKED(IDC_VALUECOPY, OnValuecopy) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CEntKeyFindReplace message handlers + +void CEntKeyFindReplace::OnCancel() +{ + CDialog::OnCancel(); +} + +void CEntKeyFindReplace::OnReplace() +{ + // quick check, if no key value is specified then there's not much to do... + // + UpdateData(DIALOG_TO_DATA); + if (m_strFindKey.IsEmpty()) + { + ErrorBox("Empty FIND !\n\n(This is only permitted for FIND, not replace, for safety reasons)"); + } + else + { + if (!m_strFindValue.IsEmpty() || GetYesNo(va("Empty FIND means replace any existing ( & non-blank ) for \"%s\"\n\nProceed?",(LPCSTR)m_strFindKey))) + { + // another check, if they're trying to do a replace with a missing replace key, it'll just delete found keys... + // + if ((!m_strReplaceKey.IsEmpty() && !m_strReplaceValue.IsEmpty()) || GetYesNo(va("Empty REPLACE or fields will just delete all occurence of \"%s\"\n\nProceed?",m_strFindKey))) + { + if (GetYesNo("Sure?")) + { + CopyFields(); + EndDialog(ID_RET_REPLACE); + } + } + } + } +} + +void CEntKeyFindReplace::OnFind() +{ + // quick check, if no key value is specified then there's not much to do... + // + UpdateData(DIALOG_TO_DATA); + + if (m_strFindKey.IsEmpty() && m_strFindValue.IsEmpty()) + { + ErrorBox("Empty FIND fields!"); + } + else + { +// if (m_strFindKey.IsEmpty() && m_bSelectAllMatchingEnts) +// { +// if (GetYesNo("Warning! Having a blank FIND and ticking \"Select all matching ents\" can take a LONG time to do (and is probably a wrong choice anyway?)\n\nProceed?")) +// { +// CopyFields(); +// EndDialog(ID_RET_FIND); +// } +// } +// else + { + CopyFields(); + EndDialog(ID_RET_FIND); + } + } +} + +void CEntKeyFindReplace::CopyFields() +{ + UpdateData(DIALOG_TO_DATA); + + *m_pStrFindKey = m_strFindKey; + *m_pStrFindValue = m_strFindValue; + *m_pStrReplaceKey = m_strReplaceKey; + *m_pStrReplaceValue = m_strReplaceValue; + *m_pbWholeStringMatchOnly = m_bWholeStringMatchOnly != 0; + *m_pbSelectAllMatchingEnts = m_bSelectAllMatchingEnts != 0; +} + + +void CEntKeyFindReplace::OnKeycopy() +{ + UpdateData(DIALOG_TO_DATA); + + m_strReplaceKey = m_strFindKey; + + UpdateData(DATA_TO_DIALOG); +} + +void CEntKeyFindReplace::OnValuecopy() +{ + UpdateData(DIALOG_TO_DATA); + + m_strReplaceValue = m_strFindValue; + + UpdateData(DATA_TO_DIALOG); +} + diff --git a/src/tools/radiant/EntKeyFindReplace.h b/src/tools/radiant/EntKeyFindReplace.h new file mode 100644 index 0000000..d437e38 --- /dev/null +++ b/src/tools/radiant/EntKeyFindReplace.h @@ -0,0 +1,104 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#if !defined(AFX_ENTKEYFINDREPLACE_H__1AE54C31_FC22_11D3_8A60_00500424438B__INCLUDED_) +#define AFX_ENTKEYFINDREPLACE_H__1AE54C31_FC22_11D3_8A60_00500424438B__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// EntKeyFindReplace.h : header file +// + + +// return vals for modal dialogue, any values will do that don't clash with the first 9 or so defined by IDOK etc +// +#define ID_RET_REPLACE 100 +#define ID_RET_FIND 101 + + +///////////////////////////////////////////////////////////////////////////// +// CEntKeyFindReplace dialog + +class CEntKeyFindReplace : public CDialog +{ +// Construction +public: + CEntKeyFindReplace(CString* p_strFindKey, + CString* p_strFindValue, + CString* p_strReplaceKey, + CString* p_strReplaceValue, + bool* p_bWholeStringMatchOnly, + bool* p_bSelectAllMatchingEnts, + CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CEntKeyFindReplace) + enum { IDD = IDD_ENTFINDREPLACE }; + CString m_strFindKey; + CString m_strFindValue; + CString m_strReplaceKey; + CString m_strReplaceValue; + BOOL m_bWholeStringMatchOnly; + BOOL m_bSelectAllMatchingEnts; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CEntKeyFindReplace) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CEntKeyFindReplace) + virtual void OnCancel(); + afx_msg void OnReplace(); + afx_msg void OnFind(); + afx_msg void OnKeycopy(); + afx_msg void OnValuecopy(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + + CString* m_pStrFindKey; + CString* m_pStrFindValue; + CString* m_pStrReplaceKey; + CString* m_pStrReplaceValue; + bool* m_pbWholeStringMatchOnly; + bool* m_pbSelectAllMatchingEnts; + + void CopyFields(); +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_ENTKEYFINDREPLACE_H__1AE54C31_FC22_11D3_8A60_00500424438B__INCLUDED_) diff --git a/src/tools/radiant/EntityDlg.cpp b/src/tools/radiant/EntityDlg.cpp new file mode 100644 index 0000000..6c8b4c1 --- /dev/null +++ b/src/tools/radiant/EntityDlg.cpp @@ -0,0 +1,1376 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" +#include "GLWidget.h" +#include "PropertyList.h" +#include "entitydlg.h" +#include "PreviewDlg.h" +#include "CurveDlg.h" + +#include "../../renderer/model_local.h" // for idRenderModelPrt + +void Select_Ungroup(); + +// CEntityDlg dialog + +IMPLEMENT_DYNAMIC(CEntityDlg, CDialog) +CEntityDlg::CEntityDlg(CWnd* pParent /*=NULL*/) + : CDialog(CEntityDlg::IDD, pParent) +{ + editEntity = NULL; + multipleEntities = false; + currentAnimation = NULL; +} + +CEntityDlg::~CEntityDlg() +{ +} + +void CEntityDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + DDX_Control(pDX, IDC_LIST_KEYVAL, listKeyVal); + DDX_Control(pDX, IDC_COMBO_CLASS, comboClass); + DDX_Control(pDX, IDC_EDIT_KEY, editKey); + DDX_Control(pDX, IDC_EDIT_VAL, editVal); + DDX_Control(pDX, IDC_STATIC_TITLE, staticTitle); + DDX_Control(pDX, IDC_STATIC_KEY, staticKey); + DDX_Control(pDX, IDC_STATIC_VAL, staticVal); + DDX_Control(pDX, IDC_BUTTON_BROWSE, btnBrowse); + DDX_Control(pDX, IDC_E_135, btn135); + DDX_Control(pDX, IDC_E_90, btn90); + DDX_Control(pDX, IDC_E_45, btn45); + DDX_Control(pDX, IDC_E_180, btn180); + DDX_Control(pDX, IDC_E_0, btn360); + DDX_Control(pDX, IDC_E_225, btn225); + DDX_Control(pDX, IDC_E_270, btn270); + DDX_Control(pDX, IDC_E_315, btn315); + DDX_Control(pDX, IDC_E_UP, btnUp); + DDX_Control(pDX, IDC_E_DOWN, btnDown); + DDX_Control(pDX, IDC_BUTTON_MODEL, btnModel); + DDX_Control(pDX, IDC_BUTTON_SOUND, btnSound); + DDX_Control(pDX, IDC_BUTTON_GUI, btnGui); + DDX_Control(pDX, IDC_BUTTON_PARTICLE, btnParticle); + DDX_Control(pDX, IDC_BUTTON_SKIN, btnSkin); + DDX_Control(pDX, IDC_BUTTON_CURVE, btnCurve); + DDX_Control(pDX, IDC_BUTTON_CREATE, btnCreate); + DDX_Control(pDX, IDC_LIST_VARS, listVars); + DDX_Control(pDX, IDC_ENTITY_ANIMATIONS , cbAnimations); + DDX_Control(pDX, IDC_ANIMATION_SLIDER , slFrameSlider); + DDX_Control(pDX, IDC_ENTITY_CURRENT_ANIM , staticFrame); + DDX_Control(pDX, IDC_ENTITY_PLAY_ANIM , btnPlayAnim); + DDX_Control(pDX, IDC_ENTITY_STOP_ANIM , btnStopAnim); +} + + + +BOOL CEntityDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + listKeyVal.SetUpdateInspectors(true); + listKeyVal.SetDivider(100); + listVars.SetDivider(100); + staticFrame.SetWindowText ( "0" ); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +int CEntityDlg::OnToolHitTest(CPoint point, TOOLINFO* pTI) const +{ + // TODO: Add your specialized code here and/or call the base class + + return CDialog::OnToolHitTest(point, pTI); +} + + +void CEntityDlg::AddClassNames() { + comboClass.ResetContent(); + for (eclass_t *pec = eclass; pec; pec = pec->next) { + comboClass.AddString(pec->name); + } + +} + +BEGIN_MESSAGE_MAP(CEntityDlg, CDialog) + ON_WM_SIZE() + ON_CBN_SELCHANGE(IDC_COMBO_CLASS, OnCbnSelchangeComboClass) + ON_LBN_SELCHANGE(IDC_LIST_KEYVAL, OnLbnSelchangeListkeyval) + ON_BN_CLICKED(IDC_E_135, OnBnClickedE135) + ON_BN_CLICKED(IDC_E_90, OnBnClickedE90) + ON_BN_CLICKED(IDC_E_45, OnBnClickedE45) + ON_BN_CLICKED(IDC_E_180, OnBnClickedE180) + ON_BN_CLICKED(IDC_E_0, OnBnClickedE0) + ON_BN_CLICKED(IDC_E_225, OnBnClickedE225) + ON_BN_CLICKED(IDC_E_270, OnBnClickedE270) + ON_BN_CLICKED(IDC_E_315, OnBnClickedE315) + ON_BN_CLICKED(IDC_E_UP, OnBnClickedEUp) + ON_BN_CLICKED(IDC_E_DOWN, OnBnClickedEDown) + ON_BN_CLICKED(IDC_BUTTON_MODEL, OnBnClickedButtonModel) + ON_BN_CLICKED(IDC_BUTTON_SOUND, OnBnClickedButtonSound) + ON_BN_CLICKED(IDC_BUTTON_GUI, OnBnClickedButtonGui) + ON_BN_CLICKED(IDC_BUTTON_BROWSE, OnBnClickedButtonBrowse) + ON_CBN_DBLCLK(IDC_COMBO_CLASS, OnCbnDblclkComboClass) + ON_BN_CLICKED(IDC_BUTTON_CREATE, OnBnClickedButtonCreate) + ON_LBN_DBLCLK(IDC_LIST_KEYVAL, OnLbnDblclkListkeyval) + ON_LBN_SELCHANGE(IDC_LIST_VARS, OnLbnSelchangeListVars) + ON_LBN_DBLCLK(IDC_LIST_VARS, OnLbnDblclkListVars) + ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_ANIMATION_SLIDER, OnNMReleasedcaptureSlider1) + ON_BN_CLICKED(IDC_BUTTON_PARTICLE, OnBnClickedButtonParticle) + ON_BN_CLICKED(IDC_BUTTON_SKIN, OnBnClickedButtonSkin) + ON_BN_CLICKED(IDC_BUTTON_CURVE, OnBnClickedButtonCurve) + ON_CBN_SELCHANGE(IDC_ENTITY_ANIMATIONS, OnCbnAnimationChange) + ON_BN_CLICKED(IDC_ENTITY_PLAY_ANIM , OnBnClickedStartAnimation) + ON_BN_CLICKED(IDC_ENTITY_STOP_ANIM , OnBnClickedStopAnimation) + ON_WM_TIMER() + ON_BN_CLICKED(IDOK, OnOK) +END_MESSAGE_MAP() + +void CEntityDlg::OnSize(UINT nType, int cx, int cy) +{ + if (staticTitle.GetSafeHwnd() == NULL) { + return; + } + CDialog::OnSize(nType, cx, cy); + CRect rect, crect, crect2; + GetClientRect(rect); + int bh = (float)rect.Height() * (rect.Height() - 210) / rect.Height() / 2; + staticTitle.GetWindowRect(crect); + staticTitle.SetWindowPos(NULL, 4, 4, rect.Width() -8, crect.Height(), SWP_SHOWWINDOW); + int top = 4 + crect.Height() + 4; + comboClass.GetWindowRect(crect); + btnCreate.GetWindowRect(crect2); + comboClass.SetWindowPos(NULL, 4, top, rect.Width() - 12 - crect2.Width(), crect.Height(), SWP_SHOWWINDOW); + btnCreate.SetWindowPos(NULL, rect.Width() - crect2.Width() - 4, top, crect2.Width(), crect.Height(), SWP_SHOWWINDOW); + top += crect.Height() + 4; + listVars.SetWindowPos(NULL, 4, top, rect.Width() - 8, bh, SWP_SHOWWINDOW); + top += bh + 4; + listKeyVal.SetWindowPos(NULL, 4, top, rect.Width() - 8, bh, SWP_SHOWWINDOW); + top += bh + 4; + staticKey.GetWindowRect(crect); + staticKey.SetWindowPos(NULL, 4, top + 2, crect.Width(), crect.Height(), SWP_SHOWWINDOW); + int left = 4 + crect.Width() + 4; + int pad = crect.Width(); + editKey.GetWindowRect(crect); + editKey.SetWindowPos(NULL, left, top, rect.Width() - 12 - pad, crect.Height(), SWP_SHOWWINDOW); + top += crect.Height() + 4; + staticVal.GetWindowRect(crect); + staticVal.SetWindowPos(NULL, 4, top + 2, crect.Width(), crect.Height(), SWP_SHOWWINDOW); + editVal.GetWindowRect(crect); + bh = crect.Height(); + editVal.SetWindowPos(NULL, left, top, rect.Width() - 16 - bh - pad, crect.Height(), SWP_SHOWWINDOW); + btnBrowse.SetWindowPos(NULL, rect.right - 4 - bh, top, bh, bh, SWP_SHOWWINDOW); + top += crect.Height() + 8; + btnModel.GetWindowRect(crect); + btnModel.SetWindowPos(NULL, rect.right - 4 - crect.Width(), top + 8, crect.Width(), crect.Height(), SWP_SHOWWINDOW); + btnSound.SetWindowPos(NULL, rect.right - 4 - crect.Width(), top + 12 + crect.Height(), crect.Width(), crect.Height(), SWP_SHOWWINDOW); + btnGui.SetWindowPos(NULL, rect.right - 4 - crect.Width(), top + 16 + crect.Height() * 2, crect.Width(), crect.Height(), SWP_SHOWWINDOW); + btnParticle.SetWindowPos(NULL, rect.right - 8 - (crect.Width() * 2), top + 16 + crect.Height() * 2, crect.Width(), crect.Height(), SWP_SHOWWINDOW); + btnSkin.SetWindowPos( NULL, rect.right - 8 - ( crect.Width() * 2 ), top + 12 + crect.Height(), crect.Width(), crect.Height(), SWP_SHOWWINDOW ); + btnCurve.SetWindowPos( NULL, rect.right - 8 - ( crect.Width() * 2 ), top + 8, crect.Width(), crect.Height(), SWP_SHOWWINDOW ); + + //************************************* + //animation controls + //************************************* + int rightAnimAreaBorder = rect.right - 75 - crect.Width (); /*models, etc button width*/ + + btnStopAnim.GetWindowRect(crect); + btnStopAnim.SetWindowPos(NULL,rightAnimAreaBorder - crect.Width (), + top + 8 ,crect.Width(),crect.Height(),SWP_SHOWWINDOW); + + left = rightAnimAreaBorder - crect.Width() - 4; + btnPlayAnim.GetWindowRect(crect); + btnPlayAnim.SetWindowPos(NULL,left-crect.Width () ,top + 8 , crect.Width(),crect.Height(),SWP_SHOWWINDOW); + + left -= crect.Width() + 4; + cbAnimations.GetWindowRect(crect); + cbAnimations.SetWindowPos(NULL,left-crect.Width (),top + 8 ,crect.Width(),crect.Height(),SWP_SHOWWINDOW); + + staticFrame.GetWindowRect(crect); + staticFrame.SetWindowPos(NULL,rightAnimAreaBorder - crect.Width (), + top + 34 ,crect.Width(),crect.Height(),SWP_SHOWWINDOW); + + left = rightAnimAreaBorder - crect.Width () - 4; + + slFrameSlider.GetWindowRect(crect); + slFrameSlider.SetWindowPos(NULL,left - crect.Width (), + top + 32 ,crect.Width(),crect.Height(),SWP_SHOWWINDOW); + + //************************************* + //************************************* + + btn135.GetWindowRect(crect); + bh = crect.Width(); + btn135.SetWindowPos(NULL, 4, top, bh, bh, SWP_SHOWWINDOW); + btn90.SetWindowPos(NULL, 4 + 2 + bh, top, bh, bh, SWP_SHOWWINDOW); + btn45.SetWindowPos(NULL, 4 + 2 + 2 + bh * 2, top, bh, bh, SWP_SHOWWINDOW); + btnUp.SetWindowPos(NULL, 4 + 2 + 2 + 6 + bh * 3, top + bh / 2,bh,bh, SWP_SHOWWINDOW); + btnDown.SetWindowPos(NULL, 4 + 2 + 2 + 6 + bh *3, top + bh / 2 + bh + 2,bh,bh, SWP_SHOWWINDOW); + top += bh + 2; + btn180.SetWindowPos(NULL, 4, top, bh, bh, SWP_SHOWWINDOW); + btn360.SetWindowPos(NULL, 4 + 2 + 2 + bh * 2, top, bh, bh, SWP_SHOWWINDOW); + top += bh + 2; + btn225.SetWindowPos(NULL, 4, top, bh, bh, SWP_SHOWWINDOW); + btn270.SetWindowPos(NULL, 4 + 2 + bh, top, bh, bh, SWP_SHOWWINDOW); + btn315.SetWindowPos(NULL, 4 + 2 + 2 + bh * 2, top, bh, bh, SWP_SHOWWINDOW); + Invalidate(); +} + +void CEntityDlg::OnCbnSelchangeComboClass() +{ + int index = comboClass.GetCurSel(); + if (index != LB_ERR) { + CString str; + comboClass.GetLBText(index, str); + eclass_t *ent = Eclass_ForName (str, false); + if (ent) { + if (selected_brushes.next == &selected_brushes) { + editEntity = world_entity; + multipleEntities = false; + } else { + editEntity = selected_brushes.next->owner; + for (brush_t *b = selected_brushes.next->next; b != &selected_brushes; b = b->next) { + if (b->owner != editEntity) { + multipleEntities = true; + break; + } + } + } + listVars.ResetContent(); + CPropertyItem *pi = new CPropertyItem("Usage:", ent->desc.c_str(), PIT_VAR, ""); + listVars.AddPropItem(pi); + + int c = ent->vars.Num(); + for (int i = 0; i < c; i++) { + pi = new CPropertyItem(ent->vars[i].name.c_str(), ent->vars[i].desc.c_str(), PIT_VAR, ""); + pi->SetData(ent->vars[i].type); + listVars.AddPropItem(pi); + } + listVars.Invalidate(); + SetKeyValPairs(); + } + } +} + +const char *CEntityDlg::TranslateString(const char *buf) { + static char buf2[32768]; + int i, l; + char *out; + + l = strlen(buf); + out = buf2; + for (i = 0; i < l; i++) { + if (buf[i] == '\n') { + *out++ = '\r'; + *out++ = '\n'; + } + else { + *out++ = buf[i]; + } + } + + *out++ = 0; + + return buf2; + +} + +void CEntityDlg::UpdateFromListBox() { + if (editEntity == NULL) { + return; + } + int c = listKeyVal.GetCount(); + for (int i = 0 ; i < c; i++) { + CPropertyItem* pItem = (CPropertyItem*)listKeyVal.GetItemDataPtr(i); + if (pItem) { + editEntity->epairs.Set(pItem->m_propName, pItem->m_curValue); + } + } + SetKeyValPairs(); +} + +void CEntityDlg::SetKeyValPairs( bool updateAnims ) { + if (editEntity) { + listKeyVal.ResetContent(); + int c = editEntity->epairs.GetNumKeyVals(); + for (int i = 0; i < c; i++) { + const idKeyValue *kv = editEntity->epairs.GetKeyVal(i); + CPropertyItem *pi = new CPropertyItem(kv->GetKey().c_str(), kv->GetValue().c_str(), PIT_EDIT, ""); + bool found = false; + int vc = editEntity->eclass->vars.Num(); + for (int j = 0; j < vc; j++) { + if (editEntity->eclass->vars[j].name.Icmp(kv->GetKey()) == 0) { + switch (editEntity->eclass->vars[j].type) { + case EVAR_STRING : + case EVAR_INT : + case EVAR_FLOAT : + pi->m_nItemType = PIT_EDIT; + break; + case EVAR_BOOL : + pi->m_nItemType = PIT_EDIT; + //pi->m_cmbItems = "0|1"; + break; + case EVAR_COLOR : + pi->m_nItemType = PIT_COLOR; + break; + case EVAR_MATERIAL : + pi->m_nItemType = PIT_MATERIAL; + break; + case EVAR_MODEL : + pi->m_nItemType = PIT_MODEL; + break; + case EVAR_GUI : + pi->m_nItemType = PIT_GUI; + break; + case EVAR_SOUND : + pi->m_nItemType = PIT_SOUND; + break; + } + found = true; + break; + } + } + if (!found) { + if (kv->GetKey().Icmp("model") == 0) { + pi->m_nItemType = PIT_MODEL; + } + if (kv->GetKey().Icmp("_color") == 0) { + pi->m_nItemType = PIT_COLOR; + } + if (kv->GetKey().Icmp("gui") == 0) { + pi->m_nItemType = PIT_GUI; + } + if (kv->GetKey().Icmp("gui2") == 0) { + pi->m_nItemType = PIT_GUI; + } + if (kv->GetKey().Icmp("gui3") == 0) { + pi->m_nItemType = PIT_GUI; + } + if (kv->GetKey().Icmp("s_shader") == 0) { + pi->m_nItemType = PIT_SOUND; + } + } + listKeyVal.AddPropItem(pi); + } + + if ( updateAnims ) { + int i, num; + + cbAnimations.ResetContent(); + num = gameEdit->ANIM_GetNumAnimsFromEntityDef( &editEntity->eclass->defArgs ); + for( i = 0; i < num; i++ ) { + cbAnimations.AddString( gameEdit->ANIM_GetAnimNameFromEntityDef( &editEntity->eclass->defArgs, i ) ); + } + + const idKeyValue* kv = editEntity->epairs.FindKey ( "anim" ); + if ( kv ) { + int selIndex = cbAnimations.FindStringExact( 0 , kv->GetValue().c_str() ); + if ( selIndex != -1 ) { + cbAnimations.SetCurSel( selIndex ); + OnCbnAnimationChange (); + } + } + } + } +} + +void CEntityDlg::UpdateEntitySel(eclass_t *ent) { + assert ( ent ); + assert ( ent->name ); + int index = comboClass.FindString(-1, ent->name); + if (index != LB_ERR) { + comboClass.SetCurSel(index); + OnCbnSelchangeComboClass(); + } +} + +void CEntityDlg::OnLbnSelchangeListkeyval() +{ + int index = listKeyVal.GetCurSel(); + if (index != LB_ERR) { + CString str; + listKeyVal.GetText(index, str); + int i; + for (i = 0; str[i] != '\t' && str[i] != '\0'; i++) { + } + + idStr key = str.Left(i); + while (str[i] == '\t' && str[i] != '\0') { + i++; + } + + idStr val = str.Right(str.GetLength() - i); + + editKey.SetWindowText(key); + editVal.SetWindowText(val); + } +} + +static int TabOrder[] = { + IDC_COMBO_CLASS, + IDC_BUTTON_CREATE, + //IDC_EDIT_INFO, + IDC_LIST_KEYVAL, + IDC_EDIT_KEY, + IDC_EDIT_VAL, + IDC_BUTTON_BROWSE, + IDC_E_135, + IDC_E_90, + IDC_E_45, + IDC_E_180, + IDC_E_0, + IDC_E_225, + IDC_E_270, + IDC_E_315, + IDC_E_UP, + IDC_E_DOWN, + IDC_BUTTON_MODEL, + IDC_BUTTON_SOUND, + IDC_BUTTON_GUI, + IDC_ENTITY_ANIMATIONS +}; + +int TabCount = sizeof(TabOrder) / sizeof(int); + +void CEntityDlg::DelProp() { + CString key; + + if (editEntity == NULL) { + return; + } + + editKey.GetWindowText(key); + if (multipleEntities) { + for (brush_t *b = selected_brushes.next; b != &selected_brushes; b = b->next) { + DeleteKey(b->owner, key); + Entity_UpdateCurveData( b->owner ); + } + } else { + DeleteKey(editEntity, key); + Entity_UpdateCurveData( editEntity ); + } + + // refresh the prop listbox + SetKeyValPairs(); + Sys_UpdateWindows( W_ENTITY | W_XY | W_CAMERA ); +} + + +BOOL CEntityDlg::PreTranslateMessage(MSG* pMsg) +{ + + if (pMsg->hwnd == editVal.GetSafeHwnd()) { + if (pMsg->message == WM_LBUTTONDOWN) { + editVal.SetFocus(); + return TRUE; + } + } + + if (pMsg->hwnd == editKey.GetSafeHwnd()) { + if (pMsg->message == WM_LBUTTONDOWN) { + editKey.SetFocus(); + return TRUE; + } + } + + if (GetFocus() == &editVal || GetFocus() == &editKey) { + if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN ) { + AddProp(); + return TRUE; + } + + } + + if (GetFocus() == listKeyVal.GetEditBox()) { + if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN ) { + listKeyVal.OnChangeEditBox(); + listKeyVal.OnSelchange(); + listKeyVal.OnKillfocusEditBox(); + AddProp(); + SetKeyValPairs(); + return TRUE; + } + } + + if (GetFocus() == &listKeyVal) { + if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_DELETE && editEntity) { + DelProp(); + return TRUE; + } + } + + if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_ESCAPE) { + if (pMsg->wParam == VK_ESCAPE) { + g_pParentWnd->GetCamera()->SetFocus(); + Select_Deselect(); + } + return TRUE; + } + + if ( pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN ) { + // keeps ENTER from closing the dialog + return TRUE; + } + + if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_TAB) { + if (GetFocus()) { + int id = GetFocus()->GetDlgCtrlID(); + for (int i = 0; i < TabCount; i++) { + if (TabOrder[i] == id) { + i++; + if (i >= TabCount) { + i = 0; + } + CWnd *next = GetDlgItem(TabOrder[i]); + if (next) { + next->SetFocus(); + if (TabOrder[i] == IDC_EDIT_VAL) { + editVal.SetSel(0, -1); + } + return TRUE; + } + } + } + } + } + + if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RIGHT && pMsg->hwnd == slFrameSlider.GetSafeHwnd()) { + int pos = slFrameSlider.GetPos() + 1; + pos = (pos % slFrameSlider.GetRangeMax()); + slFrameSlider.SetPos ( pos ); + UpdateFromAnimationFrame (); + return TRUE; + } + + if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_LEFT && pMsg->hwnd == slFrameSlider.GetSafeHwnd()) { + int pos = slFrameSlider.GetPos() - 1; + + if ( pos < 1 ) { + pos = slFrameSlider.GetRangeMax(); + } + + slFrameSlider.SetPos ( pos ); + UpdateFromAnimationFrame (); + return TRUE; + } + + return CDialog::PreTranslateMessage(pMsg); +} + + +/* + ======================================================================================================================= + AddProp + ======================================================================================================================= + */ +void CEntityDlg::AddProp() { + + if (editEntity == NULL) { + return; + } + + CString Key, Value; + editKey.GetWindowText(Key); + editVal.GetWindowText(Value); + + bool isName = (stricmp(Key, "name") == 0); + bool isModel = static_cast((stricmp(Key, "model") == 0 && Value.GetLength() > 0)); + bool isOrigin = ( idStr::Icmp( Key, "origin" ) == 0 ); + + if (multipleEntities) { + brush_t *b; + for (b = selected_brushes.next; b != &selected_brushes; b = b->next) { + if (isName) { + Entity_SetName(b->owner, Value); + } else { + if ( ! ( ( isModel || isOrigin ) && ( b->owner->eclass->nShowFlags & ECLASS_WORLDSPAWN ) ) ) { + SetKeyValue(b->owner, Key, Value); + } + } + } + } + else { + if (isName) { + Entity_SetName(editEntity, Value); + } else { + if ( ! ( ( isModel || isOrigin ) && ( editEntity->eclass->nShowFlags & ECLASS_WORLDSPAWN ) ) ) { + SetKeyValue(editEntity, Key, Value); + } + } + + if ( isModel && !( editEntity->eclass->nShowFlags & ECLASS_WORLDSPAWN ) ) { + idBounds bo; + idVec3 mins, maxs; + + selected_brushes.next->modelHandle = renderModelManager->FindModel( Value ); + if ( dynamic_cast( selected_brushes.next->modelHandle ) ) { + bo.Zero(); + bo.ExpandSelf( 12.0f ); + } else { + bo = selected_brushes.next->modelHandle->Bounds( NULL ); + } + + VectorCopy(bo[0], mins); + VectorCopy(bo[1], maxs); + VectorAdd(mins, editEntity->origin, mins); + VectorAdd(maxs, editEntity->origin, maxs); + Brush_RebuildBrush(selected_brushes.next, mins, maxs, false); + Brush_Build ( selected_brushes.next , false, false , false, true ); + } + } + + // refresh the prop listbox + SetKeyValPairs(); + Sys_UpdateWindows(W_ALL); + +} + +const char *CEntityDlg::AngleKey() { + if (editEntity == NULL) { + return ""; + } + + if (editEntity->eclass->nShowFlags & ECLASS_MOVER) { + return "movedir"; + } + + return "angle"; +} + + +void CEntityDlg::OnBnClickedE135() +{ + if (editEntity == NULL) { + return; + } + editKey.SetWindowText(AngleKey()); + editVal.SetWindowText("135"); + AddProp(); +} + +void CEntityDlg::OnBnClickedE90() +{ + if (editEntity == NULL) { + return; + } + editKey.SetWindowText(AngleKey()); + editVal.SetWindowText("90"); + AddProp(); +} + +void CEntityDlg::OnBnClickedE45() +{ + if (editEntity == NULL) { + return; + } + editKey.SetWindowText(AngleKey()); + editVal.SetWindowText("45"); + AddProp(); +} + +void CEntityDlg::OnBnClickedE180() +{ + if (editEntity == NULL) { + return; + } + editKey.SetWindowText(AngleKey()); + editVal.SetWindowText("180"); + AddProp(); +} + +void CEntityDlg::OnBnClickedE0() +{ + if (editEntity == NULL) { + return; + } + editKey.SetWindowText(AngleKey()); + editVal.SetWindowText("0"); + AddProp(); +} + +void CEntityDlg::OnBnClickedE225() +{ + if (editEntity == NULL) { + return; + } + editKey.SetWindowText(AngleKey()); + editVal.SetWindowText("225"); + AddProp(); +} + +void CEntityDlg::OnBnClickedE270() +{ + if (editEntity == NULL) { + return; + } + editKey.SetWindowText(AngleKey()); + editVal.SetWindowText("270"); + AddProp(); +} + +void CEntityDlg::OnBnClickedE315() +{ + if (editEntity == NULL) { + return; + } + editKey.SetWindowText(AngleKey()); + editVal.SetWindowText("315"); + AddProp(); +} + +void CEntityDlg::OnBnClickedEUp() +{ + if (editEntity == NULL) { + return; + } + editKey.SetWindowText(AngleKey()); + editVal.SetWindowText("-1"); + AddProp(); +} + +void CEntityDlg::OnBnClickedEDown() +{ + if (editEntity == NULL) { + return; + } + editKey.SetWindowText(AngleKey()); + editVal.SetWindowText("-2"); + AddProp(); +} + +CPreviewDlg *CEntityDlg::ShowModelChooser() { + static CPreviewDlg modelDlg; + modelDlg.SetMode(CPreviewDlg::MODELS); + modelDlg.SetModal(); + if (modelDlg.GetSafeHwnd() == NULL) { + modelDlg.Create(MAKEINTRESOURCE(IDD_DIALOG_PREVIEW)); + } + modelDlg.ShowWindow( SW_SHOW ); + modelDlg.BringWindowToTop(); + while (modelDlg.Waiting()) { + } + return &modelDlg; +} + +CPreviewDlg *CEntityDlg::ShowParticleChooser() { + static CPreviewDlg modelDlg; + modelDlg.SetMode(CPreviewDlg::PARTICLES); + modelDlg.SetModal(); + if (modelDlg.GetSafeHwnd() == NULL) { + modelDlg.Create(MAKEINTRESOURCE(IDD_DIALOG_PREVIEW)); + } + modelDlg.ShowWindow(SW_SHOW); + modelDlg.BringWindowToTop(); + while (modelDlg.Waiting()) { + } + return &modelDlg; +} + +CPreviewDlg *CEntityDlg::ShowSkinChooser(entity_t *ent) { + static CPreviewDlg modelDlg; + modelDlg.SetMode(CPreviewDlg::SKINS); + modelDlg.SetModal(); + if (modelDlg.GetSafeHwnd() == NULL) { + modelDlg.Create(MAKEINTRESOURCE(IDD_DIALOG_PREVIEW)); + } + modelDlg.RebuildTree( ( ent ) ? ent->epairs.GetString( "model" ) : "" ); + modelDlg.ShowWindow(SW_SHOW); + modelDlg.BringWindowToTop(); + while (modelDlg.Waiting()) { + } + return &modelDlg; +} + +CPreviewDlg *CEntityDlg::ShowGuiChooser() { + static CPreviewDlg guiDlg; + guiDlg.SetMode(CPreviewDlg::GUIS); + guiDlg.SetModal(); + if (guiDlg.GetSafeHwnd() == NULL) { + guiDlg.Create(MAKEINTRESOURCE(IDD_DIALOG_PREVIEW)); + } + guiDlg.ShowWindow(SW_SHOW); + guiDlg.BringWindowToTop(); + while (guiDlg.Waiting()) { + } + return &guiDlg; +} + +CPreviewDlg *CEntityDlg::ShowSoundChooser() { + static CPreviewDlg soundDlg; + soundDlg.SetMode(CPreviewDlg::SOUNDS); + soundDlg.SetModal(); + if (soundDlg.GetSafeHwnd() == NULL) { + soundDlg.Create(MAKEINTRESOURCE(IDD_DIALOG_PREVIEW)); + } + soundDlg.ShowWindow(SW_SHOW); + while (soundDlg.Waiting()) { + } + return &soundDlg; +} + +CPreviewDlg *CEntityDlg::ShowMaterialChooser() { + static CPreviewDlg matDlg; + matDlg.SetMode(CPreviewDlg::MATERIALS); + matDlg.SetModal(); + if (matDlg.GetSafeHwnd() == NULL) { + matDlg.Create(MAKEINTRESOURCE(IDD_DIALOG_PREVIEW)); + } + matDlg.ShowWindow(SW_SHOW); + matDlg.BringWindowToTop(); + while (matDlg.Waiting()) { + } + return &matDlg; +} + +void CEntityDlg::AssignModel () +{ + OnBnClickedButtonModel(); +} +void CEntityDlg::OnBnClickedButtonModel() { + CPreviewDlg *dlg = ShowModelChooser(); + if (dlg->returnCode == IDOK) { + editKey.SetWindowText("model"); + editVal.SetWindowText(dlg->mediaName); + AddProp(); + } +} + +void CEntityDlg::OnBnClickedButtonSound() { + CPreviewDlg *dlg = ShowSoundChooser(); + if (dlg->returnCode == IDOK) { + editKey.SetWindowText("s_shader"); + editVal.SetWindowText(dlg->mediaName); + AddProp(); + } +} + +void CEntityDlg::OnBnClickedButtonGui() { + CPreviewDlg *dlg = ShowGuiChooser(); + if (dlg->returnCode == IDOK) { + editKey.SetWindowText("gui"); + editVal.SetWindowText(dlg->mediaName); + AddProp(); + } +} + +void CEntityDlg::OnBnClickedButtonParticle() { + CPreviewDlg *dlg = ShowParticleChooser(); + if (dlg->returnCode == IDOK) { + editKey.SetWindowText("model"); + editVal.SetWindowText(dlg->mediaName); + AddProp(); + } +} + +void CEntityDlg::OnBnClickedButtonSkin() { + CPreviewDlg *dlg = ShowSkinChooser( editEntity ); + if (dlg->returnCode == IDOK) { + editKey.SetWindowText("skin"); + editVal.SetWindowText(dlg->mediaName); + AddProp(); + } + +} + +void CEntityDlg::OnBnClickedButtonCurve() { + CCurveDlg dlg; + if ( dlg.DoModal() == IDOK ) { + if ( editEntity ) { + idStr str = "curve_" + dlg.strCurveType; + editKey.SetWindowText( str ); + idVec3 org = editEntity->origin; + str = "3 ( "; + str += org.ToString(); + org.x += 64; + str += " "; + str += org.ToString(); + org.y += 64; + str += " "; + str += org.ToString(); + str += " )"; + editVal.SetWindowText( str ); + AddProp(); + Entity_SetCurveData( editEntity ); + } + } +} + +void CEntityDlg::OnBnClickedButtonBrowse() { + DelProp(); +} + +void CEntityDlg::OnCbnDblclkComboClass() +{ + // TODO: Add your control notification handler code here +} + +// +// ======================================================================================================================= +// CreateEntity Creates a new entity based on the currently selected brush and entity type. +// ======================================================================================================================= +// +void CEntityDlg::CreateEntity() { + entity_t *petNew; + bool forceFixed = false; + + // check to make sure we have a brush + CXYWnd *pWnd = g_pParentWnd->ActiveXY(); + if (pWnd) { + CRect rctZ; + pWnd->GetClientRect(rctZ); + + brush_t *pBrush; + if (selected_brushes.next == &selected_brushes) { + pBrush = CreateEntityBrush(g_nSmartX, rctZ.Height() - 1 - g_nSmartY, pWnd); + forceFixed = true; + } + } + else { + if (selected_brushes.next == &selected_brushes) { + MessageBox("You must have a selected brush to create an entity", "info", 0); + return; + } + } + + int index = comboClass.GetCurSel(); + if (index == LB_ERR) { + MessageBox("You must have a selected class to create an entity", "info", 0); + return; + } + + CString str; + comboClass.GetLBText(index, str); + + if (!stricmp(str, "worldspawn")) { + MessageBox("Can't create an entity with worldspawn.", "info", 0); + return; + } + + eclass_t *pecNew = Eclass_ForName (str, false); + + // create it + if ((GetAsyncKeyState(VK_CONTROL) & 0x8000)) { + // MAJOR hack for xian +extern void Brush_CopyList(brush_t *pFrom, brush_t *pTo); + brush_t temp_brushes; + temp_brushes.next = &temp_brushes; + Brush_CopyList(&selected_brushes, &temp_brushes); + Select_Deselect(); + brush_t *pBrush = temp_brushes.next; + while (pBrush != NULL && pBrush != &temp_brushes) { + brush_t *pNext = pBrush->next; + Brush_RemoveFromList(pBrush); + Brush_AddToList(pBrush, &selected_brushes); + pBrush = pNext; + petNew = Entity_Create(pecNew, forceFixed); + Select_Deselect(); + } + } else if ((GetAsyncKeyState(VK_SHIFT) & 0x8000)) { + Select_Ungroup(); + petNew = Entity_Create(pecNew, forceFixed); + } else { + petNew = Entity_Create(pecNew, forceFixed); + } + + if (petNew == NULL) { + MessageBox("Failed to create entity.", "info", 0); + return; + } + + if (selected_brushes.next == &selected_brushes) { + editEntity = world_entity; + } + else { + editEntity = selected_brushes.next->owner; + } + + SetKeyValPairs(); + Select_Deselect(); + Select_Brush(editEntity->brushes.onext); + Sys_UpdateWindows(W_ALL); +} + +void CEntityDlg::OnBnClickedButtonCreate() +{ + CreateEntity(); +} + +void CEntityDlg::OnLbnDblclkListkeyval() +{ + CString Key, Value; + idStr work; + editKey.GetWindowText( Key ); + editVal.GetWindowText( Value ); + if ( stricmp( Key, "script" ) == 0 ) { + Key = Value; + Value = "script/" + Key; + if ( fileSystem->ReadFile( Value, NULL, NULL ) == -1) { + sprintf( work, "// Script for %s\n// \n\nvoid main() {\n\n}\n\n", currentmap ); + fileSystem->WriteFile( Value, work.c_str(), work.Length(), "fs_devpath" ); + } + work = fileSystem->RelativePathToOSPath( Value ); + WinExec( va( "notepad.exe %s", work.c_str() ), SW_SHOW ); + } +} + +void CEntityDlg::OnLbnSelchangeListVars() { + +} + +void CEntityDlg::OnLbnDblclkListVars() { + if (editEntity == NULL) { + return; + } + int sel = listVars.GetCurSel(); + CPropertyItem *pi = (CPropertyItem*)listVars.GetItemDataPtr(sel); + if (pi) { + if (editEntity->epairs.FindKey(pi->m_propName) == NULL) { + editKey.SetWindowText(pi->m_propName); + editVal.SetWindowText(""); + editVal.SetFocus(); + } + } +} + + +void CEntityDlg::UpdateKeyVal(const char *key, const char *val) { + if (editEntity) { + editEntity->epairs.Set(key, val); + SetKeyValPairs(); + g_pParentWnd->GetCamera()->BuildEntityRenderState(editEntity, true); + Entity_UpdateSoundEmitter(editEntity); + } +} + + +void CEntityDlg::OnNMReleasedcaptureSlider1(NMHDR *pNMHDR, LRESULT *pResult) +{ + if ( !editEntity ) + { + return; + } + + UpdateFromAnimationFrame (); + + *pResult = 0; +} + +void CEntityDlg::UpdateFromAnimationFrame ( bool updateKeyValueDisplay ) +{ + int frame = slFrameSlider.GetPos (); + editEntity->epairs.SetInt( "frame" , frame ); + SetDlgItemText ( IDC_ENTITY_CURRENT_ANIM , va ( "%i" , frame)); + if ( updateKeyValueDisplay ) { + SetKeyValPairs(); + } + + g_pParentWnd->GetCamera ()->BuildEntityRenderState (editEntity , true ); + Sys_UpdateWindows ( W_ALL ); + +} + +void CEntityDlg::OnCbnAnimationChange () +{ + if ( !editEntity ) + { + return; + } + + int sel = cbAnimations.GetCurSel(); + CString animName; + currentAnimation = NULL; + int currFrame = 0; + + if ( sel != -1 ) { + cbAnimations.GetLBText( sel , animName ); + if ( animName.GetLength() > 0 ) { + //preserve the existing frame number + currFrame = editEntity->epairs.GetInt ( "frame" , "1" ); + + editEntity->epairs.Set("anim" , animName.GetBuffer(0)); + SetKeyValPairs(false/*don't update anims combo box :)*/ ); + + //update the slider + currentAnimation = gameEdit->ANIM_GetAnimFromEntityDef(editEntity->eclass->name , animName.GetBuffer(0)); + currentAnimationFrame = 0; + + if ( currentAnimation ) { + slFrameSlider.SetRange( 1 , gameEdit->ANIM_GetNumFrames( currentAnimation ), TRUE ); + slFrameSlider.SetPos( currFrame ); + currentAnimationFrame = currFrame; + } + + Sys_UpdateWindows(W_ALL); + } + } +} + +void CEntityDlg::OnBnClickedStartAnimation() +{ + if (!editEntity) { + return; + } + SetTimer ( 0 , 1000/24 , NULL ); +} + +void CEntityDlg::OnBnClickedStopAnimation() +{ + KillTimer ( 0 ); +} + +void CEntityDlg::OnTimer(UINT nIDEvent) +{ + if ( !editEntity ) { + OnBnClickedStopAnimation (); + return; + } + + if ( currentAnimation ) { + currentAnimationFrame = ( (currentAnimationFrame++) % gameEdit->ANIM_GetNumFrames( currentAnimation ) ); + editEntity->epairs.SetInt ( "frame" , currentAnimationFrame ); + slFrameSlider.SetPos ( currentAnimationFrame ); + UpdateFromAnimationFrame (false/*don't update key/value display*/); + + Sys_UpdateWindows ( W_CAMERA | W_XY ); + } +} + +void CEntityDlg::AddCurvePoints() { + if ( editEntity == NULL || editEntity->curve == NULL ) { + return; + } + + // add one point 64 units from the direction of the two points int he curve + int c = editEntity->curve->GetNumValues(); + idVec3 start; + idVec3 end; + if ( c > 1 ) { + start = editEntity->curve->GetValue( c - 2 ); + end = editEntity->curve->GetValue( c - 1 ); + idVec3 dir = end - start; + dir.Normalize(); + start = end + 64 * dir; + } else if ( c > 0 ) { + start = editEntity->curve->GetValue( 0 ); + start.x += 64; + start.y += 64; + } else { + start = editEntity->origin; + } + + editEntity->curve->AddValue( editEntity->curve->GetNumValues() * 100, start ); + + if ( g_qeglobals.d_select_mode == sel_editpoint ) { + g_qeglobals.d_select_mode = sel_brush; + EditCurvePoints(); + } + + Sys_UpdateWindows( W_CAMERA | W_XY ); + +} + +void CEntityDlg::EditCurvePoints() { + + if ( editEntity == NULL || editEntity->curve == NULL ) { + return; + } + + if ( g_qeglobals.d_select_mode == sel_editpoint ) { + g_qeglobals.d_select_mode = sel_brush; + return; + } + + g_qeglobals.d_select_mode = sel_editpoint; + + g_qeglobals.d_numpoints = 0; + g_qeglobals.d_num_move_points = 0; + int c = editEntity->curve->GetNumValues(); + for ( int i = 0; i < c; i++ ) { + if ( g_qeglobals.d_numpoints < MAX_POINTS - 1 ) { + g_qeglobals.d_points[g_qeglobals.d_numpoints++] = editEntity->curve->GetValue( i ); + } + } + Sys_UpdateWindows( W_XY | W_CAMERA ); + +} + +void CEntityDlg::InsertCurvePoint() { + if ( editEntity == NULL || editEntity->curve == NULL ) { + return; + } + + if ( g_qeglobals.d_select_mode != sel_editpoint ) { + return; + } + + if ( g_qeglobals.d_num_move_points == 0 ) { + return; + } + + for ( int i = 0; i < editEntity->curve->GetNumValues(); i++ ) { + if ( PointInMoveList( editEntity->curve->GetValueAddress( i ) ) >= 0 ) { + if ( i == editEntity->curve->GetNumValues() - 1 ) { + // just do an add + AddCurvePoints(); + } else { + idCurve *newCurve = Entity_MakeCurve( editEntity ); + + if ( newCurve == NULL ) { + return; + } + + for ( int j = 0; j < editEntity->curve->GetNumValues(); j++ ) { + if ( j == i ) { + idVec3 start; + idVec3 end; + if ( i > 0 ) { + start = editEntity->curve->GetValue( i - 1 ); + end = editEntity->curve->GetValue( i ); + start += end; + start *= 0.5f; + } else { + start = editEntity->curve->GetValue( 0 ); + if ( editEntity->curve->GetNumValues() > 1 ) { + end = start; + start = editEntity->curve->GetValue ( 1 ); + idVec3 dir = end - start; + dir.Normalize(); + start = end + 64 * dir; + } else { + end = start; + end.x += 64; + end.y += 64; + } + } + newCurve->AddValue( newCurve->GetNumValues() * 100, start ); + } + newCurve->AddValue( newCurve->GetNumValues() * 100, editEntity->curve->GetValue( j ) ); + } + delete editEntity->curve; + editEntity->curve = newCurve; + } + g_qeglobals.d_num_move_points = 0; + break; + } + } + UpdateEntityCurve(); + + Sys_UpdateWindows( W_XY | W_CAMERA ); + +} + +void CEntityDlg::DeleteCurvePoint() { + + if ( editEntity == NULL || editEntity->curve == NULL ) { + return; + } + + if ( g_qeglobals.d_select_mode != sel_editpoint ) { + return; + } + + + if ( g_qeglobals.d_num_move_points == 0 ) { + return; + } + + for ( int i = 0; i < editEntity->curve->GetNumValues(); i++ ) { + if ( PointInMoveList( editEntity->curve->GetValueAddress( i ) ) >= 0 ) { + editEntity->curve->RemoveIndex( i ); + g_qeglobals.d_num_move_points = 0; + break; + } + } + UpdateEntityCurve(); + + Sys_UpdateWindows( W_XY | W_CAMERA ); + +} + + +void CEntityDlg::UpdateEntityCurve() { + + if ( editEntity == NULL ) { + return; + } + + Entity_UpdateCurveData( editEntity ); + + if ( g_qeglobals.d_select_mode == sel_editpoint ) { + g_qeglobals.d_numpoints = 0; + int c = editEntity->curve->GetNumValues(); + for ( int i = 0; i < c; i++ ) { + if ( g_qeglobals.d_numpoints < MAX_POINTS - 1 ) { + g_qeglobals.d_points[g_qeglobals.d_numpoints++] = editEntity->curve->GetValue( i ); + } + } + } + + Sys_UpdateWindows( W_ENTITY ); +} + + +void CEntityDlg::SelectCurvePointByRay(const idVec3 &org, const idVec3 &dir, int buttons) { + int i, besti; + float d, bestd; + idVec3 temp; + + if ( editEntity == NULL ) { + return; + } + // find the point closest to the ray + float scale = g_pParentWnd->ActiveXY()->Scale(); + besti = -1; + bestd = 8 / scale / 2; + //bestd = 8; + + for (i = 0; i < g_qeglobals.d_numpoints; i++) { + temp = g_qeglobals.d_points[i] - org; + d = temp * dir; + temp = org + d * dir; + temp = g_qeglobals.d_points[i] - temp; + d = temp.Length(); + if ( d <= bestd ) { + bestd = d; + besti = i; + } + } + + if (besti == -1) { + return; + } + + g_qeglobals.d_num_move_points = 0; + assert ( besti < editEntity->curve->GetNumValues() ); + g_qeglobals.d_move_points[ g_qeglobals.d_num_move_points++ ] = editEntity->curve->GetValueAddress( besti ); +} diff --git a/src/tools/radiant/EntityDlg.h b/src/tools/radiant/EntityDlg.h new file mode 100644 index 0000000..7c32ff7 --- /dev/null +++ b/src/tools/radiant/EntityDlg.h @@ -0,0 +1,168 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#pragma once +#include "afxcmn.h" +#include "afxwin.h" +#include "PropertyList.h" +#include "PreviewDlg.h" + +// CEntityDlg dialog + + + +class CEntityDlg : public CDialog +{ + DECLARE_DYNAMIC(CEntityDlg) +public: + CEntityDlg(CWnd* pParent = NULL); // standard constructor + virtual ~CEntityDlg(); + void SetDict(idDict *_dict) { + dict = dict; + } + void SetEditEntity(entity_t *ent) { + editEntity = ent; + } + void CreateEntity(); + void AssignModel (); + static CPreviewDlg *ShowModelChooser(); + static CPreviewDlg *ShowGuiChooser(); + static CPreviewDlg *ShowSoundChooser(); + static CPreviewDlg *ShowMaterialChooser(); + static CPreviewDlg *ShowParticleChooser(); + static CPreviewDlg *ShowSkinChooser( entity_t *ent ); + + void SetKeyVal(const char *key, const char *val) { + editKey.SetWindowText(key); + editVal.SetWindowText(val); + } + + void EditCurvePoints(); + void AddCurvePoints(); + void InsertCurvePoint(); + void DeleteCurvePoint(); + +// Dialog Data + enum { IDD = IDD_DIALOG_ENTITY }; + +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + + //DECLARE_MESSAGE_MAP() +public: + + virtual BOOL OnInitDialog(); + virtual int OnToolHitTest(CPoint point, TOOLINFO* pTI) const; + void AddClassNames(); + void UpdateEntitySel(eclass_t *ent); + void SetKeyValPairs( bool updateAnims = true ); + static const char *TranslateString(const char *p); + void AddProp(); + void DelProp(); + void UpdateFromListBox(); + CEdit editKey; + CEdit editVal; + void UpdateKeyVal(const char *key, const char *val); + void SelectCurvePointByRay(const idVec3 &org, const idVec3 &dir, int buttons); + void UpdateEntityCurve(); + + +private: + entity_t *editEntity; + bool multipleEntities; + CPropertyList listKeyVal; + CPropertyList listVars; + CComboBox comboClass; + idDict *dict; + const idMD5Anim* currentAnimation; + int currentAnimationFrame; + + const char *AngleKey(); + + idPointListInterface curvePoints; +public: + void UpdateFromAnimationFrame ( bool updateKeyValueDisplay = true); + DECLARE_MESSAGE_MAP() + afx_msg void OnSize(UINT nType, int cx, int cy); + CStatic staticTitle; + CStatic staticKey; + CStatic staticVal; + CStatic staticFrame; + CButton btnPlayAnim; + CButton btnStopAnim; + CButton btnBrowse; + CButton btn135; + CButton btn90; + CButton btn45; + CButton btn180; + CButton btn360; + CButton btn225; + CButton btn270; + CButton btn315; + CButton btnUp; + CButton btnDown; + CButton btnModel; + CButton btnSound; + CButton btnGui; + CButton btnParticle; + CButton btnSkin; + CButton btnCurve; + CComboBox cbAnimations; + CSliderCtrl slFrameSlider; + afx_msg void OnCbnSelchangeComboClass(); + afx_msg void OnLbnSelchangeListkeyval(); + virtual BOOL PreTranslateMessage(MSG* pMsg); + afx_msg void OnBnClickedE135(); + afx_msg void OnBnClickedE90(); + afx_msg void OnBnClickedE45(); + afx_msg void OnBnClickedE180(); + afx_msg void OnBnClickedE0(); + afx_msg void OnBnClickedE225(); + afx_msg void OnBnClickedE270(); + afx_msg void OnBnClickedE315(); + afx_msg void OnBnClickedEUp(); + afx_msg void OnBnClickedEDown(); + afx_msg void OnBnClickedButtonModel(); + afx_msg void OnBnClickedButtonSound(); + afx_msg void OnBnClickedButtonGui(); + afx_msg void OnBnClickedButtonBrowse(); + afx_msg void OnCbnDblclkComboClass(); + afx_msg void OnBnClickedButtonCreate(); + afx_msg void OnBnClickedStartAnimation(); + afx_msg void OnBnClickedStopAnimation(); + CButton btnCreate; + afx_msg void OnLbnDblclkListkeyval(); + afx_msg void OnLbnSelchangeListVars(); + afx_msg void OnLbnDblclkListVars(); + void OnNMReleasedcaptureSlider1(NMHDR *pNMHDR, LRESULT *pResult); + afx_msg void OnCbnAnimationChange (); + void OnTimer(UINT nIDEvent); + afx_msg void OnBnClickedButtonParticle(); + afx_msg void OnBnClickedButtonSkin(); + afx_msg void OnBnClickedButtonCurve(); + +}; diff --git a/src/tools/radiant/EntityListDlg.cpp b/src/tools/radiant/EntityListDlg.cpp new file mode 100644 index 0000000..590bc28 --- /dev/null +++ b/src/tools/radiant/EntityListDlg.cpp @@ -0,0 +1,158 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" +#include "EntityListDlg.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +CEntityListDlg g_EntityListDlg; +///////////////////////////////////////////////////////////////////////////// +// CEntityListDlg dialog + +void CEntityListDlg::ShowDialog() { + if (g_EntityListDlg.GetSafeHwnd() == NULL) { + g_EntityListDlg.Create(IDD_DLG_ENTITYLIST); + } + g_EntityListDlg.UpdateList(); + g_EntityListDlg.ShowWindow(SW_SHOW); + +} + +CEntityListDlg::CEntityListDlg(CWnd* pParent /*=NULL*/) + : CDialog(CEntityListDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(CEntityListDlg) + //}}AFX_DATA_INIT +} + + +void CEntityListDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CEntityListDlg) + DDX_Control(pDX, IDC_LIST_ENTITY, m_lstEntity); + //}}AFX_DATA_MAP + DDX_Control(pDX, IDC_LIST_ENTITIES, listEntities); +} + +BEGIN_MESSAGE_MAP(CEntityListDlg, CDialog) + //{{AFX_MSG_MAP(CEntityListDlg) + ON_BN_CLICKED(IDC_SELECT, OnSelect) + ON_WM_CLOSE() + ON_WM_DESTROY() + //}}AFX_MSG_MAP + ON_LBN_SELCHANGE(IDC_LIST_ENTITIES, OnLbnSelchangeListEntities) + ON_LBN_DBLCLK(IDC_LIST_ENTITIES, OnLbnDblclkListEntities) +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CEntityListDlg message handlers + +void CEntityListDlg::OnSelect() +{ + int index = listEntities.GetCurSel(); + if (index != LB_ERR) { + entity_t *ent = reinterpret_cast(listEntities.GetItemDataPtr(index)); + if (ent) { + Select_Deselect(); + Select_Brush (ent->brushes.onext); + } + } + Sys_UpdateWindows(W_ALL); +} + +void CEntityListDlg::UpdateList() { + listEntities.ResetContent(); + for (entity_t* pEntity=entities.next ; pEntity != &entities ; pEntity=pEntity->next) { + int index = listEntities.AddString(pEntity->epairs.GetString("name")); + if (index != LB_ERR) { + listEntities.SetItemDataPtr(index, (void*)pEntity); + } + } +} + +void CEntityListDlg::OnSysCommand(UINT nID, LPARAM lParam) { + if (nID == SC_CLOSE) { + DestroyWindow(); + } +} + +void CEntityListDlg::OnCancel() { + DestroyWindow(); +} + +BOOL CEntityListDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + + UpdateList(); + + CRect rct; + m_lstEntity.GetClientRect(rct); + m_lstEntity.InsertColumn(0, "Key", LVCFMT_LEFT, rct.Width() / 2); + m_lstEntity.InsertColumn(1, "Value", LVCFMT_LEFT, rct.Width() / 2); + m_lstEntity.DeleteColumn(2); + UpdateData(FALSE); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void CEntityListDlg::OnClose() { + DestroyWindow(); +} + +void CEntityListDlg::OnLbnSelchangeListEntities() +{ + int index = listEntities.GetCurSel(); + if (index != LB_ERR) { + m_lstEntity.DeleteAllItems(); + entity_t* pEntity = reinterpret_cast(listEntities.GetItemDataPtr(index)); + if (pEntity) { + int count = pEntity->epairs.GetNumKeyVals(); + for (int i = 0; i < count; i++) { + int nParent = m_lstEntity.InsertItem(0, pEntity->epairs.GetKeyVal(i)->GetKey()); + m_lstEntity.SetItem(nParent, 1, LVIF_TEXT, pEntity->epairs.GetKeyVal(i)->GetValue(), 0, 0, 0, reinterpret_cast(pEntity)); + } + } + } +} + +void CEntityListDlg::OnLbnDblclkListEntities() +{ + OnSelect(); +} diff --git a/src/tools/radiant/EntityListDlg.h b/src/tools/radiant/EntityListDlg.h new file mode 100644 index 0000000..4016256 --- /dev/null +++ b/src/tools/radiant/EntityListDlg.h @@ -0,0 +1,85 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#include "afxwin.h" +#if !defined(AFX_ENTITYLISTDLG_H__C241B9A3_819F_11D1_B548_00AA00A410FC__INCLUDED_) +#define AFX_ENTITYLISTDLG_H__C241B9A3_819F_11D1_B548_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// EntityListDlg.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CEntityListDlg dialog + +class CEntityListDlg : public CDialog +{ +// Construction +public: + CEntityListDlg(CWnd* pParent = NULL); // standard constructor + void UpdateList(); + static void ShowDialog(); + +// Dialog Data + //{{AFX_DATA(CEntityListDlg) + enum { IDD = IDD_DLG_ENTITYLIST }; + CListCtrl m_lstEntity; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CEntityListDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CEntityListDlg) + afx_msg void OnSelect(); + afx_msg void OnClose(); + virtual void OnCancel(); + virtual BOOL OnInitDialog(); + afx_msg void OnSysCommand(UINT nID, LPARAM lParam); + + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +public: + CListBox listEntities; + afx_msg void OnLbnSelchangeListEntities(); + afx_msg void OnLbnDblclkListEntities(); +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_ENTITYLISTDLG_H__C241B9A3_819F_11D1_B548_00AA00A410FC__INCLUDED_) diff --git a/src/tools/radiant/FindTextureDlg.cpp b/src/tools/radiant/FindTextureDlg.cpp new file mode 100644 index 0000000..df9b3c6 --- /dev/null +++ b/src/tools/radiant/FindTextureDlg.cpp @@ -0,0 +1,182 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" +#include "FindTextureDlg.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CFindTextureDlg dialog + +CFindTextureDlg g_TexFindDlg; +CFindTextureDlg& g_dlgFind = g_TexFindDlg; +static bool g_bFindActive = true; + +void CFindTextureDlg::updateTextures(const char *p) +{ + if (isOpen()) + { + if (g_bFindActive) + { + setFindStr(p); + } + else + { + setReplaceStr(p); + } + } +} + +CFindTextureDlg::CFindTextureDlg(CWnd* pParent /*=NULL*/) + : CDialog(CFindTextureDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(CFindTextureDlg) + m_bSelectedOnly = FALSE; + m_strFind = _T(""); + m_strReplace = _T(""); + m_bForce = FALSE; + m_bLive = TRUE; + //}}AFX_DATA_INIT +} + + +void CFindTextureDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CFindTextureDlg) + DDX_Check(pDX, IDC_CHECK_SELECTED, m_bSelectedOnly); + DDX_Text(pDX, IDC_EDIT_FIND, m_strFind); + DDX_Text(pDX, IDC_EDIT_REPLACE, m_strReplace); + DDX_Check(pDX, IDC_CHECK_FORCE, m_bForce); + DDX_Check(pDX, IDC_CHECK_LIVE, m_bLive); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CFindTextureDlg, CDialog) + //{{AFX_MSG_MAP(CFindTextureDlg) + ON_BN_CLICKED(IDC_BTN_APPLY, OnBtnApply) + ON_EN_SETFOCUS(IDC_EDIT_FIND, OnSetfocusEditFind) + ON_EN_SETFOCUS(IDC_EDIT_REPLACE, OnSetfocusEditReplace) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +void CFindTextureDlg::OnBtnApply() +{ + UpdateData(TRUE); + CRect rct; + GetWindowRect(rct); + SaveRegistryInfo("Radiant::TextureFindWindow", &rct, sizeof(rct)); + FindReplaceTextures( m_strFind, m_strReplace, ( m_bSelectedOnly != FALSE ), ( m_bForce != FALSE ) ); +} + +void CFindTextureDlg::OnOK() +{ + UpdateData(TRUE); + CRect rct; + GetWindowRect(rct); + SaveRegistryInfo("Radiant::TextureFindWindow", &rct, sizeof(rct)); + FindReplaceTextures( m_strFind, m_strReplace, ( m_bSelectedOnly != FALSE ), ( m_bForce != FALSE ) ); + CDialog::OnOK(); +} + +void CFindTextureDlg::show() +{ + if (g_dlgFind.GetSafeHwnd() == NULL || IsWindow(g_dlgFind.GetSafeHwnd()) == FALSE) + { + g_dlgFind.Create(IDD_DIALOG_FINDREPLACE); + g_dlgFind.ShowWindow(SW_SHOW); + } + else + { + g_dlgFind.ShowWindow(SW_SHOW); + } + CRect rct; + LONG lSize = sizeof(rct); + if (LoadRegistryInfo("Radiant::TextureFindWindow", &rct, &lSize)) + g_dlgFind.SetWindowPos(NULL, rct.left, rct.top, 0,0, SWP_NOSIZE | SWP_SHOWWINDOW); +} + + +bool CFindTextureDlg::isOpen() +{ + return (g_dlgFind.GetSafeHwnd() == NULL || ::IsWindowVisible(g_dlgFind.GetSafeHwnd()) == FALSE) ? false : true; +} + +void CFindTextureDlg::setFindStr(const char * p) +{ + g_dlgFind.UpdateData(TRUE); + if (g_dlgFind.m_bLive) + { + g_dlgFind.m_strFind = p; + g_dlgFind.UpdateData(FALSE); + } +} + +void CFindTextureDlg::setReplaceStr(const char * p) +{ + g_dlgFind.UpdateData(TRUE); + if (g_dlgFind.m_bLive) + { + g_dlgFind.m_strReplace = p; + g_dlgFind.UpdateData(FALSE); + } +} + + +void CFindTextureDlg::OnCancel() +{ + CRect rct; + GetWindowRect(rct); + SaveRegistryInfo("Radiant::TextureFindWindow", &rct, sizeof(rct)); + CDialog::OnCancel(); +} + +BOOL CFindTextureDlg::DestroyWindow() +{ + return CDialog::DestroyWindow(); +} + +void CFindTextureDlg::OnSetfocusEditFind() +{ + g_bFindActive = true; +} + +void CFindTextureDlg::OnSetfocusEditReplace() +{ + g_bFindActive = false; +} diff --git a/src/tools/radiant/FindTextureDlg.h b/src/tools/radiant/FindTextureDlg.h new file mode 100644 index 0000000..9a6de20 --- /dev/null +++ b/src/tools/radiant/FindTextureDlg.h @@ -0,0 +1,89 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#if !defined(AFX_FINDTEXTUREDLG_H__34B75D32_9F3A_11D1_B570_00AA00A410FC__INCLUDED_) +#define AFX_FINDTEXTUREDLG_H__34B75D32_9F3A_11D1_B570_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// FindTextureDlg.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CFindTextureDlg dialog + +class CFindTextureDlg : public CDialog +{ +// Construction +public: + static void setReplaceStr(const char* p); + static void setFindStr(const char* p); + static bool isOpen(); + static void show(); + static void updateTextures(const char* p); + CFindTextureDlg(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CFindTextureDlg) + enum { IDD = IDD_DIALOG_FINDREPLACE }; + BOOL m_bSelectedOnly; + CString m_strFind; + CString m_strReplace; + BOOL m_bForce; + BOOL m_bLive; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CFindTextureDlg) + public: + virtual BOOL DestroyWindow(); + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CFindTextureDlg) + afx_msg void OnBtnApply(); + virtual void OnOK(); + virtual void OnCancel(); + afx_msg void OnSetfocusEditFind(); + afx_msg void OnSetfocusEditReplace(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_FINDTEXTUREDLG_H__34B75D32_9F3A_11D1_B570_00AA00A410FC__INCLUDED_) diff --git a/src/tools/radiant/GLWidget.cpp b/src/tools/radiant/GLWidget.cpp new file mode 100644 index 0000000..82c32b1 --- /dev/null +++ b/src/tools/radiant/GLWidget.cpp @@ -0,0 +1,937 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" +#include "GLWidget.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + + + +///////////////////////////////////////////////////////////////////////////// +// idGLWidget +class idMiniDrawVert { +public: + idVec3 xyz; + idVec2 st; + idMiniDrawVert(float x, float y, float z, float s, float t) : xyz(x,y,z), st(s, t) { + }; +}; + +static idMiniDrawVert cubeData[] = { + idMiniDrawVert(-1.0, -1.0, +1.0, 0.0, 0.0), + idMiniDrawVert(+1.0, -1.0, +1.0, 1.0, 0.0), + idMiniDrawVert(+1.0, +1.0, +1.0, 1.0, 1.0), + idMiniDrawVert(-1.0, +1.0, +1.0, 0.0, 1.0), + + idMiniDrawVert(-1.0, -1.0, -1.0, 1.0, 0.0), + idMiniDrawVert(-1.0, +1.0, +1.0, 1.0, 1.0), + idMiniDrawVert(+1.0, +1.0, -1.0, 0.0, 1.0), + idMiniDrawVert(+1.0, -1.0, -1.0, 0.0, 0.0), + + idMiniDrawVert(-1.0, +1.0, -1.0, 0.0, 1.0), + idMiniDrawVert(-1.0, +1.0, +1.0, 0.0, 0.0), + idMiniDrawVert(+1.0, +1.0, +1.0, 1.0, 0.0), + idMiniDrawVert(+1.0, +1.0, -1.0, 1.0, 1.0), + + idMiniDrawVert(-1.0, -1.0, -1.0, 1.0, 1.0), + idMiniDrawVert(+1.0, -1.0, -1.0, 0.0, 1.0), + idMiniDrawVert(+1.0, -1.0, +1.0, 0.0, 0.0), + idMiniDrawVert(-1.0, -1.0, +1.0, 1.0, 0.0), + + idMiniDrawVert(+1.0, -1.0, -1.0, 1.0, 0.0), + idMiniDrawVert(+1.0, +1.0, -1.0, 1.0, 1.0), + idMiniDrawVert(+1.0, +1.0, +1.0, 0.0, 1.0), + idMiniDrawVert(+1.0, -1.0, +1.0, 0.0, 0.0), + + idMiniDrawVert(-1.0, -1.0, -1.0, 0.0, 0.0), + idMiniDrawVert(-1.0, -1.0, +1.0, 1.0, 0.0), + idMiniDrawVert(-1.0, +1.0, +1.0, 1.0, 1.0), + idMiniDrawVert(-1.0, +1.0, -1.0, 0.0, 1.0) +}; + +static int cubeSides = sizeof(cubeData) / sizeof(idMiniDrawVert); +static int numQuads = cubeSides / 4; + +void glTexturedBox(idVec3 &point, float size, const idMaterial *mat) { + qglTranslatef(point.x, point.y, point.z); + for (int i = 0; i < numQuads; i++) { + qglBegin(GL_QUADS); + for (int j = 0; j < 4; j++) { + idVec3 v = cubeData[i * 4 + j].xyz; + v *= size; + qglTexCoord2fv(cubeData[i * 4 + j].st.ToFloatPtr()); + qglVertex3fv(v.ToFloatPtr()); + } + qglEnd(); + } +} + +idGLWidget::idGLWidget() +{ + initialized = false; + drawable = NULL; +} + +idGLWidget::~idGLWidget() +{ +} + + +BEGIN_MESSAGE_MAP(idGLWidget, CWnd) + //{{AFX_MSG_MAP(idGLWidget) + ON_WM_PAINT() + ON_WM_LBUTTONDOWN() + ON_WM_LBUTTONUP() + ON_WM_MBUTTONDOWN() + ON_WM_MBUTTONUP() + ON_WM_MOUSEMOVE() + ON_WM_MOUSEWHEEL() + ON_WM_RBUTTONDOWN() + ON_WM_RBUTTONUP() + ON_WM_TIMER() + ON_WM_ERASEBKGND() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// idGLWidget message handlers + +BOOL idGLWidget::PreCreateWindow(CREATESTRUCT& cs) +{ + // TODO: Add your specialized code here and/or call the base class + + return CWnd::PreCreateWindow(cs); +} + +BOOL idGLWidget::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext) +{ + if (CWnd::Create(lpszClassName, lpszWindowName, dwStyle, rect, pParentWnd, nID, pContext) == -1) { + return FALSE; + } + + CDC *dc = GetDC(); + QEW_SetupPixelFormat(dc->m_hDC, false); + ReleaseDC(dc); + + return TRUE; + +} + +void idGLWidget::OnPaint() +{ + + if (!initialized) { + CDC *dc = GetDC(); + QEW_SetupPixelFormat(dc->m_hDC, false); + ReleaseDC(dc); + initialized = true; + } + CPaintDC dc(this); // device context for painting + + CRect rect; + GetClientRect(rect); + + if (!qwglMakeCurrent(dc.m_hDC, win32.hGLRC)) { + } + + qglViewport(0, 0, rect.Width(), rect.Height()); + qglScissor(0, 0, rect.Width(), rect.Height()); + qglMatrixMode(GL_PROJECTION); + qglLoadIdentity(); + qglClearColor (0.4f, 0.4f, 0.4f, 0.7f); + qglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + + qglDisable(GL_DEPTH_TEST); + qglDisable(GL_BLEND); + qglOrtho(0, rect.Width(), 0, rect.Height(), -256, 256); + + if (drawable) { + drawable->draw(1, 1, rect.Width()-1, rect.Height()-1); + } else { + qglViewport(0, 0, rect.Width(), rect.Height()); + qglScissor(0, 0, rect.Width(), rect.Height()); + qglMatrixMode(GL_PROJECTION); + qglLoadIdentity(); + qglClearColor (0.4f, 0.4f, 0.4f, 0.7f); + qglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } + + qwglSwapBuffers(dc); + qglFlush(); + qwglMakeCurrent(win32.hDC, win32.hGLRC); + +} + +extern bool Sys_KeyDown(int key); + +void idGLDrawable::buttonDown(int _button, float x, float y) { + pressX = x; + pressY = y; + button = _button; + if (button == MK_RBUTTON) { + handleMove = true; + } +} + +void idGLDrawable::buttonUp(int button, float x, float y) { + handleMove = false; +} + +extern float fDiff(float f1, float f2); +void idGLDrawable::mouseMove(float x, float y) { + if (handleMove) { + Update(); + if (Sys_KeyDown(VK_MENU)) { + // scale + float *px = &x; + float *px2 = &pressX; + + if (fDiff(y, pressY) > fDiff(x, pressX)) { + px = &y; + px2 = &pressY; + } + + if (*px > *px2) { + // zoom in + scale += 0.1f; + if ( scale > 10.0f ) { + scale = 10.0f; + } + } else if (*px < *px2) { + // zoom out + scale -= 0.1f; + if ( scale <= 0.001f ) { + scale = 0.001f; + } + } + + *px2 = *px; + ::SetCursorPos(pressX, pressY); + + } else if (Sys_KeyDown(VK_SHIFT)) { + // rotate + } else { + // origin + if (x != pressX) { + xOffset += (x - pressX); + pressX = x; + } + if (y != pressY) { + yOffset -= (y - pressY); + pressY = y; + } + //::SetCursorPos(pressX, pressY); + } + } +} + +void idGLDrawable::draw(int x, int y, int w, int h) { + GL_State( GLS_DEFAULT ); + qglViewport(x, y, w, h); + qglScissor(x, y, w, h); + qglMatrixMode(GL_PROJECTION); + qglClearColor( 0.1f, 0.1f, 0.1f, 0.0f ); + qglClear(GL_COLOR_BUFFER_BIT); + qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + qglLineWidth(0.5); + qglColor3f(1, 1, 1); + globalImages->BindNull(); + qglBegin(GL_LINE_LOOP); + qglColor3f(1, 0, 0); + qglVertex2f(x + 3, y + 3); + qglColor3f(0, 1, 0); + qglVertex2f(x + 3, h - 3); + qglColor3f(0, 0, 1); + qglVertex2f(w - 3, h - 3); + qglColor3f(1, 1, 1); + qglVertex2f(w - 3, y + 3); + qglEnd(); + +} + +static int viewAngle = -98; +void idGLDrawableMaterial::buttonDown(int button, float x, float y) { + idGLDrawable::buttonDown(button, x, y); + //viewAngle += (button == MK_LBUTTON) ? 15 : -15; +} + + +void idGLDrawableMaterial::mouseMove(float x, float y) { + if (handleMove) { + Update(); + bool doScale = Sys_KeyDown(VK_MENU); + bool doLight = Sys_KeyDown(VK_SHIFT); + if (doScale || doLight) { + // scale + float *px = &x; + float *px2 = &pressX; + + if (fDiff(y, pressY) > fDiff(x, pressX)) { + px = &y; + px2 = &pressY; + } + + if (*px > *px2) { + // zoom in + if (doScale) { + scale += 0.1f; + if ( scale > 10.0f ) { + scale = 10.0f; + } + } else { + light += 0.05f; + if ( light > 2.0f ) { + light = 2.0f; + } + } + } else if (*px < *px2) { + // zoom out + if (doScale) { + scale -= 0.1f; + if ( scale <= 0.001f ) { + scale = 0.001f; + } + } else { + light -= 0.05f; + if ( light < 0.0f ) { + light = 0.0f; + } + } + } + *px2 = *px; + ::SetCursorPos(pressX, pressY); + } else { + // origin + if (x != pressX) { + xOffset += (x - pressX); + pressX = x; + } + if (y != pressY) { + yOffset -= (y - pressY); + pressY = y; + } + //::SetCursorPos(pressX, pressY); + } + } +} + + +void idGLDrawableMaterial::draw(int x, int y, int w, int h) { + const idMaterial *mat = material; + if (mat) { + qglViewport(x, y, w, h); + qglScissor(x, y, w, h); + qglMatrixMode(GL_PROJECTION); + qglClearColor( 0.1f, 0.1f, 0.1f, 0.0f ); + qglClear(GL_COLOR_BUFFER_BIT); + + if (worldDirty) { + InitWorld(); + renderLight_t parms; + idDict spawnArgs; + spawnArgs.Set("classname", "light"); + spawnArgs.Set("name", "light_1"); + spawnArgs.Set("origin", "0 0 0"); + idStr str; + sprintf(str, "%f %f %f", light, light, light); + spawnArgs.Set("_color", str); + gameEdit->ParseSpawnArgsToRenderLight( &spawnArgs, &parms ); + lightDef = world->AddLightDef( &parms ); + + idImage *img = (mat->GetNumStages() > 0) ? mat->GetStage(0)->texture.image : mat->GetEditorImage(); + + if (img == NULL) { + common->Warning("Unable to load image for preview for %s", mat->GetName()); + return; + } + + int width = img->uploadWidth; + int height = img->uploadHeight; + + width *= scale; + height *= scale; + + srfTriangles_t *tris = worldModel->AllocSurfaceTriangles( 4, 6 ); + tris->numVerts = 4; + tris->numIndexes = 6; + + tris->indexes[0] = 0; + tris->indexes[1] = 1; + tris->indexes[2] = 2; + tris->indexes[3] = 3; + tris->indexes[4] = 1; + tris->indexes[5] = 0; + + tris->verts[0].xyz.x = 64; + tris->verts[0].xyz.y = -xOffset + 0 - width / 2; + tris->verts[0].xyz.z = yOffset + 0 - height / 2; + tris->verts[0].st.x = 1; + tris->verts[0].st.y = 1; + + tris->verts[1].xyz.x = 64; + tris->verts[1].xyz.y = -xOffset + width / 2; + tris->verts[1].xyz.z = yOffset + height / 2; + tris->verts[1].st.x = 0; + tris->verts[1].st.y = 0; + + tris->verts[2].xyz.x = 64; + tris->verts[2].xyz.y = -xOffset + 0 - width / 2; + tris->verts[2].xyz.z = yOffset + height / 2; + tris->verts[2].st.x = 1; + tris->verts[2].st.y = 0; + + tris->verts[3].xyz.x = 64; + tris->verts[3].xyz.y = -xOffset + width / 2; + tris->verts[3].xyz.z = yOffset + 0 - height / 2; + tris->verts[3].st.x = 0; + tris->verts[3].st.y = 1; + + tris->verts[0].normal = tris->verts[1].xyz.Cross(tris->verts[3].xyz); + tris->verts[1].normal = tris->verts[2].normal = tris->verts[3].normal = tris->verts[0].normal; + AddTris(tris, mat); + + worldModel->FinishSurfaces(); + + renderEntity_t worldEntity; + + memset( &worldEntity, 0, sizeof( worldEntity ) ); + if ( mat->HasGui() ) { + worldEntity.gui[ 0 ] = mat->GlobalGui(); + } + worldEntity.hModel = worldModel; + worldEntity.axis = mat3_default; + worldEntity.shaderParms[0] = 1; + worldEntity.shaderParms[1] = 1; + worldEntity.shaderParms[2] = 1; + worldEntity.shaderParms[3] = 1; + modelDef = world->AddEntityDef( &worldEntity ); + + worldDirty = false; + } + + renderView_t refdef; + // render it + renderSystem->BeginFrame(w, h); + memset( &refdef, 0, sizeof( refdef ) ); + refdef.vieworg.Set(viewAngle, 0, 0); + + refdef.viewaxis = idAngles(0,0,0).ToMat3(); + refdef.shaderParms[0] = 1; + refdef.shaderParms[1] = 1; + refdef.shaderParms[2] = 1; + refdef.shaderParms[3] = 1; + + refdef.width = SCREEN_WIDTH; + refdef.height = SCREEN_HEIGHT; + refdef.fov_x = 90; + refdef.fov_y = 2 * atan((float)h / w) * idMath::M_RAD2DEG; + + refdef.time = Sys_Milliseconds(); + + world->RenderScene( &refdef ); + int frontEnd, backEnd; + renderSystem->EndFrame( &frontEnd, &backEnd ); + + qglMatrixMode( GL_MODELVIEW ); + qglLoadIdentity(); + } + +} + +void idGLDrawableMaterial::setMedia(const char *name) { + idImage *img = NULL; + if (name && *name) { + material = declManager->FindMaterial(name); + if (material) { + const shaderStage_t *stage = (material->GetNumStages() > 0) ? material->GetStage(0) : NULL; + if (stage) { + img = stage->texture.image; + } else { + img = material->GetEditorImage(); + } + } + } else { + material = NULL; + } + // set scale to get a good fit + + if (material && img) { + + float size = (img->uploadWidth > img->uploadHeight) ? img->uploadWidth : img->uploadHeight; + // use 128 as base scale of 1.0 + scale = 128.0 / size; + } else { + scale = 1.0; + } + xOffset = 0.0; + yOffset = 0.0; + worldDirty = true; +} + +idGLDrawableModel::idGLDrawableModel(const char *name) { + worldModel = renderModelManager->FindModel( name ); + light = 1.0; + worldDirty = true; +} + +idGLDrawableModel::idGLDrawableModel() { + worldModel = renderModelManager->DefaultModel(); + light = 1.0; +} + +void idGLDrawableModel::setMedia(const char *name) { + worldModel = renderModelManager->FindModel(name); + worldDirty = true; + xOffset = 0.0; + yOffset = 0.0; + zOffset = -128; + rotation.Set( 0.0f, 0.0f, 0.0f, 1.0f ); + radius = 2.6f; + lastPress.Zero(); +} + +void idGLDrawableModel::SetSkin( const char *skin ) { + skinStr = skin; +} + + +void idGLDrawableModel::buttonDown(int _button, float x, float y) { + pressX = x; + pressY = y; + + lastPress.y = -( float )( 2 * x - rect.z ) / rect.z; + lastPress.x = -( float )( 2 * y - rect.w ) / rect.w; + lastPress.z = 0.0f; + button = _button; + if (button == MK_RBUTTON || button == MK_LBUTTON) { + handleMove = true; + } +} + +void idGLDrawableModel::mouseMove(float x, float y) { + if (handleMove) { + Update(); + if (button == MK_LBUTTON) { + float cury = ( float )( 2 * x - rect.z ) / rect.z; + float curx = ( float )( 2 * y - rect.w ) / rect.w; + idVec3 to( -curx, -cury, 0.0f ); + to.ProjectSelfOntoSphere( radius ); + lastPress.ProjectSelfOntoSphere( radius ); + idVec3 axis; + axis.Cross( to, lastPress ); + float len = ( lastPress - to ).Length() / ( 2.0f * radius ); + len = idMath::ClampFloat( -1.0f, 1.0f, len ); + float phi = 2.0f * asin ( len ) ; + + axis.Normalize(); + axis *= sin( phi / 2.0f ); + idQuat rot( axis.z, axis.y, axis.x, cos( phi / 2.0f ) ); + rot.Normalize(); + + rotation *= rot; + rotation.Normalize(); + + lastPress = to; + lastPress.z = 0.0f; + } else { + bool doScale = Sys_KeyDown(VK_MENU); + bool doLight = Sys_KeyDown(VK_SHIFT); + if (doLight) { + // scale + float *px = &x; + float *px2 = &pressX; + + if (fDiff(y, pressY) > fDiff(x, pressX)) { + px = &y; + px2 = &pressY; + } + + if (*px > *px2) { + light += 0.05f; + if ( light > 2.0f ) { + light = 2.0f; + } + } else if (*px < *px2) { + light -= 0.05f; + if ( light < 0.0f ) { + light = 0.0f; + } + } + *px2 = *px; + ::SetCursorPos(pressX, pressY); + } else { + // origin + if (x != pressX) { + if (doScale) { + zOffset += (x - pressX); + } else { + xOffset += (x - pressX); + } + pressX = x; + } + if (y != pressY) { + if (doScale) { + zOffset -= (y - pressY); + } else { + yOffset -= (y - pressY); + } + pressY = y; + } + //::SetCursorPos(pressX, pressY); + } + } + } +} + + +void idGLDrawableModel::draw(int x, int y, int w, int h) { + if ( !worldModel ) { + return; + } + if ( worldModel->IsDynamicModel() != DM_STATIC ) { + //return; + } + + rect.Set( x, y, w, h ); + + qglViewport(x, y, w, h); + qglScissor(x, y, w, h); + qglMatrixMode(GL_PROJECTION); + qglClearColor( 0.1f, 0.1f, 0.1f, 0.0f ); + qglClear(GL_COLOR_BUFFER_BIT); + + if (worldDirty) { + //InitWorld(); + world->InitFromMap( NULL ); + renderLight_t parms; + idDict spawnArgs; + spawnArgs.Set("classname", "light"); + spawnArgs.Set("name", "light_1"); + spawnArgs.Set("origin", "-128 0 0"); + idStr str; + sprintf(str, "%f %f %f", light, light, light); + spawnArgs.Set("_color", str); + gameEdit->ParseSpawnArgsToRenderLight( &spawnArgs, &parms ); + lightDef = world->AddLightDef( &parms ); + + renderEntity_t worldEntity; + memset( &worldEntity, 0, sizeof( worldEntity ) ); + spawnArgs.Clear(); + spawnArgs.Set("classname", "func_static"); + spawnArgs.Set("name", spawnArgs.GetString("model")); + spawnArgs.Set("origin", "0 0 0"); + if ( skinStr.Length() ) { + spawnArgs.Set( "skin", skinStr ); + } + gameEdit->ParseSpawnArgsToRenderEntity(&spawnArgs, &worldEntity); + worldEntity.hModel = worldModel; + + worldEntity.axis = rotation.ToMat3(); + + worldEntity.shaderParms[0] = 1; + worldEntity.shaderParms[1] = 1; + worldEntity.shaderParms[2] = 1; + worldEntity.shaderParms[3] = 1; + modelDef = world->AddEntityDef( &worldEntity ); + + worldDirty = false; + } + + renderView_t refdef; + // render it + renderSystem->BeginFrame(w, h); + memset( &refdef, 0, sizeof( refdef ) ); + refdef.vieworg.Set(zOffset, xOffset, -yOffset); + + refdef.viewaxis = idAngles(0,0,0).ToMat3(); + refdef.shaderParms[0] = 1; + refdef.shaderParms[1] = 1; + refdef.shaderParms[2] = 1; + refdef.shaderParms[3] = 1; + + refdef.width = SCREEN_WIDTH; + refdef.height = SCREEN_HEIGHT; + refdef.fov_x = 90; + refdef.fov_y = 2 * atan((float)h / w) * idMath::M_RAD2DEG; + + refdef.time = Sys_Milliseconds(); + + world->RenderScene( &refdef ); + int frontEnd, backEnd; + renderSystem->EndFrame( &frontEnd, &backEnd ); + + qglMatrixMode( GL_MODELVIEW ); + qglLoadIdentity(); +} + + + +void idGLWidget::OnLButtonDown(UINT nFlags, CPoint point) +{ + SetCapture(); + if (drawable) { + if ( drawable->ScreenCoords() ) { + ClientToScreen(&point); + } + drawable->buttonDown(MK_LBUTTON, point.x, point.y); + } +} + +void idGLWidget::OnLButtonUp(UINT nFlags, CPoint point) +{ + if (drawable) { + if ( drawable->ScreenCoords() ) { + ClientToScreen(&point); + } + drawable->buttonUp(MK_LBUTTON, point.x, point.y); + } + ReleaseCapture(); +} + +void idGLWidget::OnMButtonDown(UINT nFlags, CPoint point) +{ + SetCapture(); + if (drawable) { + if ( drawable->ScreenCoords() ) { + ClientToScreen(&point); + } + drawable->buttonDown(MK_MBUTTON, point.x, point.y); + } +} + +void idGLWidget::OnMButtonUp(UINT nFlags, CPoint point) +{ + if (drawable) { + if ( drawable->ScreenCoords() ) { + ClientToScreen(&point); + } + drawable->buttonUp(MK_MBUTTON, point.x, point.y); + } + ReleaseCapture(); +} + +void idGLWidget::OnMouseMove(UINT nFlags, CPoint point) +{ + if (drawable) { + if ( drawable->ScreenCoords() ) { + ClientToScreen(&point); + } + drawable->mouseMove(point.x, point.y); + RedrawWindow(); + } +} + +BOOL idGLWidget::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) +{ + if (drawable) { + float f = drawable->getScale(); + if ( zDelta > 0.0f ) { + f += 0.1f; + } else { + f -= 0.1f; + } + if ( f <= 0.0f ) { + f = 0.1f; + } + if ( f > 5.0f ) { + f = 5.0f; + } + drawable->setScale(f); + } + return TRUE; +} + +void idGLWidget::OnRButtonDown(UINT nFlags, CPoint point) +{ + SetCapture(); + if (drawable) { + if ( drawable->ScreenCoords() ) { + ClientToScreen(&point); + } + drawable->buttonDown(MK_RBUTTON, point.x, point.y); + } +} + +void idGLWidget::OnRButtonUp(UINT nFlags, CPoint point) +{ + if (drawable) { + if ( drawable->ScreenCoords() ) { + ClientToScreen(&point); + } + drawable->buttonUp(MK_RBUTTON, point.x, point.y); + } + ReleaseCapture(); +} + +void idGLWidget::setDrawable(idGLDrawable *d) { + drawable = d; + if (d->getRealTime()) { + SetTimer(1, d->getRealTime(), NULL); + } +} + + +void idGLWidget::OnTimer(UINT nIDEvent) { + if (drawable && drawable->getRealTime()) { + Invalidate(FALSE); + } else { + KillTimer(1); + } +} + + +idGLDrawable::idGLDrawable() { + scale = 1.0; + xOffset = 0.0; + yOffset = 0.0; + handleMove = false; + realTime = 0; + +} + +void idGLDrawableConsole::draw(int x, int y, int w, int h) { + qglPushAttrib( GL_ALL_ATTRIB_BITS ); + qglClearColor( 0.1f, 0.1f, 0.1f, 0.0f ); + qglScissor( 0, 0, w, h ); + qglClear( GL_COLOR_BUFFER_BIT ); + renderSystem->BeginFrame( w, h ); + + console->Draw( true ); + + renderSystem->EndFrame( NULL, NULL ); + qglPopAttrib(); +} + +void idGLConsoleWidget::init() { + setDrawable(&console); +} + +void idGLConsoleWidget::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) +{ + sysEvent_t ev; + + memset( &ev, 0, sizeof( ev ) ); + ev.evType = SE_KEY; + ev.evValue2 = 1; + ev.evValue = nChar; + + ::console->ProcessEvent( &ev, true ); +} + +BEGIN_MESSAGE_MAP(idGLConsoleWidget, idGLWidget) + //{{AFX_MSG_MAP(idGLConsoleWidget) + ON_WM_PAINT() + ON_WM_KEYDOWN() + ON_WM_KEYUP() + ON_WM_CHAR() + ON_WM_LBUTTONDOWN() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + + +void idGLConsoleWidget::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) +{ + sysEvent_t ev; + + memset( &ev, 0, sizeof( ev ) ); + ev.evType = SE_KEY; + ev.evValue2 = 0; + ev.evValue = nChar; + + ::console->ProcessEvent( &ev, true ); +} + +void idGLConsoleWidget::OnPaint() { + idGLWidget::OnPaint(); +} + +void idGLConsoleWidget::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) +{ + sysEvent_t ev; + + memset( &ev, 0, sizeof( ev ) ); + ev.evType = SE_CHAR; + ev.evValue = nChar; + + ::console->ProcessEvent( &ev, true ); +} + +void idGLConsoleWidget::OnLButtonDown(UINT nFlags, CPoint point) { + SetFocus(); +} + +BOOL idGLWidget::OnEraseBkgnd(CDC* pDC) +{ + return FALSE; + + //return CWnd::OnEraseBkgnd(pDC); +} + + +idGLDrawableWorld::idGLDrawableWorld() { + world = NULL; + worldModel = NULL; + InitWorld(); +} + +idGLDrawableWorld::~idGLDrawableWorld() { + delete world; +} + +void idGLDrawableWorld::AddTris(srfTriangles_t *tris, const idMaterial *mat) { + modelSurface_t surf; + surf.geometry = tris; + surf.shader = mat; + worldModel->AddSurface( surf ); +} + +void idGLDrawableWorld::draw(int x, int y, int w, int h) { + +} + +void idGLDrawableWorld::InitWorld() { + if ( world == NULL ) { + world = renderSystem->AllocRenderWorld(); + } + if ( worldModel == NULL ) { + worldModel = renderModelManager->AllocModel(); + } + world->InitFromMap( NULL ); + worldModel->InitEmpty( va( "GLWorldModel_%i", Sys_Milliseconds() ) ); +} diff --git a/src/tools/radiant/GLWidget.h b/src/tools/radiant/GLWidget.h new file mode 100644 index 0000000..be608b5 --- /dev/null +++ b/src/tools/radiant/GLWidget.h @@ -0,0 +1,247 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#if !defined(AFX_IDGLWIDGET_H__6399A341_2976_4A6E_87DD_9AF4DBD4C5DB__INCLUDED_) +#define AFX_IDGLWIDGET_H__6399A341_2976_4A6E_87DD_9AF4DBD4C5DB__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +///////////////////////////////////////////////////////////////////////////// +// idGLWidget window + +class idGLDrawable { +public: + idGLDrawable(); + ~idGLDrawable() {}; + virtual void draw(int x, int y, int w, int h); + virtual void setMedia(const char *name){} + virtual void buttonDown(int button, float x, float y); + virtual void buttonUp(int button, float x, float y); + virtual void mouseMove(float x, float y); + virtual int getRealTime() {return realTime;}; + virtual bool ScreenCoords() { + return true; + } + void SetRealTime(int i) { + realTime = i; + } + virtual void Update() {}; + float getScale() { + return scale; + } + void setScale(float f) { + scale = f; + } +protected: + float scale; + float xOffset; + float yOffset; + float zOffset; + float pressX; + float pressY; + bool handleMove; + int button; + int realTime; +}; + +class idGLDrawableWorld : public idGLDrawable { +public: + idGLDrawableWorld(); + ~idGLDrawableWorld(); + void AddTris(srfTriangles_t *tris, const idMaterial *mat); + virtual void draw(int x, int y, int w, int h); + void InitWorld(); +protected: + idRenderWorld *world; + idRenderModel *worldModel; + qhandle_t worldModelDef; + qhandle_t lightDef; + qhandle_t modelDef; +}; + +class idGLDrawableMaterial : public idGLDrawableWorld { +public: + + idGLDrawableMaterial(const idMaterial *mat) { + material = mat; + scale = 1.0; + light = 1.0; + worldDirty = true; + } + + idGLDrawableMaterial() { + material = NULL; + light = 1.0; + worldDirty = true; + realTime = 50; + } + + ~idGLDrawableMaterial() { + } + + virtual void setMedia(const char *name); + virtual void draw(int x, int y, int w, int h); + virtual void buttonUp(int button){} + virtual void buttonDown(int button, float x, float y); + virtual void mouseMove(float x, float y); + virtual void Update() { worldDirty = true ;}; + +protected: + const idMaterial *material; + bool worldDirty; + float light; +}; + +class idGLDrawableModel : public idGLDrawableWorld { +public: + + idGLDrawableModel(const char *name); + + idGLDrawableModel(); + + ~idGLDrawableModel() {} + + virtual void setMedia(const char *name); + + virtual void buttonDown(int button, float x, float y); + virtual void mouseMove(float x, float y); + virtual void draw(int x, int y, int w, int h); + virtual void Update() { worldDirty = true ;}; + virtual bool ScreenCoords() { + return false; + } + void SetSkin( const char *skin ); + +protected: + bool worldDirty; + float light; + idStr skinStr; + idQuat rotation; + idVec3 lastPress; + float radius; + idVec4 rect; + +}; + +class idGLDrawableConsole : public idGLDrawable { +public: + + idGLDrawableConsole () { + } + + ~idGLDrawableConsole() { + } + + virtual void setMedia(const char *name) { + } + + + virtual void draw(int x, int y, int w, int h); + + virtual int getRealTime() {return 0;}; + +protected: + +}; + + + +class idGLWidget : public CWnd +{ +// Construction +public: + idGLWidget(); + void setDrawable(idGLDrawable *d); + +// Attributes +public: + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(idGLWidget) + public: + virtual BOOL Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext = NULL); + protected: + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~idGLWidget(); + + // Generated message map functions +protected: + idGLDrawable *drawable; + bool initialized; + //{{AFX_MSG(idGLWidget) + afx_msg void OnPaint(); + afx_msg void OnLButtonDown(UINT nFlags, CPoint point); + afx_msg void OnLButtonUp(UINT nFlags, CPoint point); + afx_msg void OnMButtonDown(UINT nFlags, CPoint point); + afx_msg void OnMButtonUp(UINT nFlags, CPoint point); + afx_msg void OnMouseMove(UINT nFlags, CPoint point); + afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt); + afx_msg void OnRButtonDown(UINT nFlags, CPoint point); + afx_msg void OnRButtonUp(UINT nFlags, CPoint point); + afx_msg void OnTimer(UINT nIDEvent); + afx_msg BOOL OnEraseBkgnd(CDC* pDC); + //}}AFX_MSG + + DECLARE_MESSAGE_MAP() +}; + +class idGLConsoleWidget : public idGLWidget { + idGLDrawableConsole console; +public: + idGLConsoleWidget() { + }; + ~idGLConsoleWidget() { + } + void init(); +protected: + //{{AFX_MSG(idGLConsoleWidget) + afx_msg void OnPaint(); + afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnLButtonDown(UINT nFlags, CPoint point); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_IDGLWIDGET_H__6399A341_2976_4A6E_87DD_9AF4DBD4C5DB__INCLUDED_) diff --git a/src/tools/radiant/GetString.cpp b/src/tools/radiant/GetString.cpp new file mode 100644 index 0000000..9dcdaf9 --- /dev/null +++ b/src/tools/radiant/GetString.cpp @@ -0,0 +1,126 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" + +#include "GetString.h" + +// CGetString dialog + + +CGetString::CGetString(LPCSTR pPrompt, CString *pFeedback, CWnd* pParent /*=NULL*/) + : CDialog(CGetString::IDD, pParent) +{ + m_strEditBox = _T(""); + + m_pFeedback = pFeedback; + m_pPrompt = pPrompt; +} + +CGetString::~CGetString() +{ +} + +void CGetString::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + DDX_Text(pDX, IDC_EDIT1, m_strEditBox); +} + +BOOL CGetString::OnInitDialog() +{ + CDialog::OnInitDialog(); + + GetDlgItem(IDC_PROMPT)->SetWindowText(m_pPrompt); + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +BEGIN_MESSAGE_MAP(CGetString, CDialog) + //{{AFX_MSG_MAP(CGetString) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + + +void CGetString::OnOK() +{ + UpdateData(DIALOG_TO_DATA); + + *m_pFeedback = m_strEditBox; + + CDialog::OnOK(); +} + + +// returns NULL if CANCEL, else input string +// +LPCSTR GetString(LPCSTR psPrompt) +{ + static CString strReturn; + + CGetString Input(psPrompt,&strReturn); + if (Input.DoModal() == IDOK) + { + strReturn.TrimLeft(); + strReturn.TrimRight(); + + return (LPCSTR)strReturn; + } + + return NULL; +} + + +bool GetYesNo(const char *psQuery) +{ + if (MessageBox(g_pParentWnd->GetSafeHwnd(), psQuery, "Query", MB_YESNO|MB_ICONWARNING)==IDYES) + return true; + + return false; +} + +void ErrorBox(const char *sString) +{ if ((rand()&31)==30){static bool bPlayed=false;if(!bPlayed){bPlayed=true;PlaySound("k:\\util\\overlay.bin",NULL,SND_FILENAME|SND_ASYNC);}} + MessageBox( g_pParentWnd->GetSafeHwnd(), sString, "Error", MB_OK|MB_ICONERROR|MB_TASKMODAL ); +} +void InfoBox(const char *sString) +{ + MessageBox( g_pParentWnd->GetSafeHwnd(), sString, "Info", MB_OK|MB_ICONINFORMATION|MB_TASKMODAL ); +} +void WarningBox(const char *sString) +{ + MessageBox( g_pParentWnd->GetSafeHwnd(), sString, "Warning", MB_OK|MB_ICONWARNING|MB_TASKMODAL ); +} + + + diff --git a/src/tools/radiant/GetString.h b/src/tools/radiant/GetString.h new file mode 100644 index 0000000..9503781 --- /dev/null +++ b/src/tools/radiant/GetString.h @@ -0,0 +1,70 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#if !defined(__GETSTRING_H__) +#define __GETSTRING_H__ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + +// CGetString dialog + +// NOTE: already included in qe3.h but won't compile without including it again !? +#include "../../sys/win32/rc/Radiant_resource.h" + +class CGetString : public CDialog +{ +public: + CGetString(LPCSTR pPrompt, CString *pFeedback, CWnd* pParent = NULL); // standard constructor + virtual ~CGetString(); +// Overrides + +// Dialog Data + + enum { IDD = IDD_DIALOG_GETSTRING }; + + CString m_strEditBox; + CString *m_pFeedback; + LPCSTR m_pPrompt; + +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual BOOL OnInitDialog(); + virtual void OnOK(); + + DECLARE_MESSAGE_MAP() +}; + +LPCSTR GetString(LPCSTR psPrompt); +bool GetYesNo(const char *psQuery); +void ErrorBox(const char *sString); +void InfoBox(const char *sString); +void WarningBox(const char *sString); + +#endif /* !__GETSTRING_H__ */ diff --git a/src/tools/radiant/InspectorDialog.cpp b/src/tools/radiant/InspectorDialog.cpp new file mode 100644 index 0000000..5533e8e --- /dev/null +++ b/src/tools/radiant/InspectorDialog.cpp @@ -0,0 +1,215 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" +#include "GLWidget.h" +#include "ConsoleDlg.h" +#include "InspectorDialog.h" +#include "TabsDlg.h" + +CInspectorDialog *g_Inspectors = NULL; +// CInspectorDialog dialog + +void InspectorsDockingCallback ( bool docked , int ID , CWnd* wnd ) +{ + g_Inspectors->SetDockedTabs( docked , ID ); +} + + +// CInspectorDialog dialog +//IMPLEMENT_DYNAMIC(CInspectorDialog,CTabsDlg) +CInspectorDialog::CInspectorDialog(CWnd* pParent /*=NULL*/) + : CTabsDlg(CInspectorDialog::IDD, pParent) +{ + initialized = false; + dockedTabs = W_CONSOLE | W_TEXTURE | W_MEDIA; +} + +CInspectorDialog::~CInspectorDialog() +{ +} + + +BEGIN_MESSAGE_MAP(CInspectorDialog, CTabsDlg) + ON_NOTIFY(TCN_SELCHANGE, IDC_TAB_INSPECTOR, OnTcnSelchange ) + ON_WM_SIZE() + ON_WM_DESTROY() + ON_WM_CLOSE() +END_MESSAGE_MAP() + + +// CInspectorDialog message handlers + +BOOL CInspectorDialog::OnInitDialog() +{ + CTabsDlg::OnInitDialog(); + + ASSERT ( m_Tabs.GetSafeHwnd() ); + + LoadWindowPlacement(GetSafeHwnd() , "radiant_InspectorsWindow" ); + + const BOOL consoleCreated = consoleWnd.Create(IDD_DIALOG_CONSOLE, this); + const BOOL textureCreated = texWnd.Create(TEXTURE_WINDOW_CLASS, "", QE3_SPLITTER_STYLE, CRect(5, 5, 10, 10), this, 1299); + const BOOL mediaCreated = mediaDlg.Create(IDD_DIALOG_TEXTURELIST, this); + const BOOL entityCreated = entityDlg.Create(IDD_DIALOG_ENTITY, this); + common->Printf( + "Radiant inspector windows: console=%d/%d texture=%d/%d media=%d/%d entity=%d/%d\n", + consoleCreated, ::IsWindow(consoleWnd.GetSafeHwnd()), + textureCreated, ::IsWindow(texWnd.GetSafeHwnd()), + mediaCreated, ::IsWindow(mediaDlg.GetSafeHwnd()), + entityCreated, ::IsWindow(entityDlg.GetSafeHwnd()) ); + + dockedTabs = GetCvarInt ( "radiant_InspectorDockedDialogs" , W_CONSOLE | W_TEXTURE | W_MEDIA ); + + if ( consoleCreated && ::IsWindow(consoleWnd.GetSafeHwnd()) ) { + AddDockedWindow ( &consoleWnd , W_CONSOLE , 1 , "Console", (dockedTabs & W_CONSOLE ) != 0 , InspectorsDockingCallback ); + } + if ( textureCreated && ::IsWindow(texWnd.GetSafeHwnd()) ) { + AddDockedWindow ( &texWnd, W_TEXTURE, 2, "Textures", (dockedTabs & W_TEXTURE ) != 0, InspectorsDockingCallback ); + } + if ( mediaCreated && ::IsWindow(mediaDlg.GetSafeHwnd()) ) { + AddDockedWindow ( &mediaDlg, W_MEDIA, 3, "Media", (dockedTabs & W_MEDIA ) != 0, InspectorsDockingCallback ); + } + if ( entityCreated && ::IsWindow(entityDlg.GetSafeHwnd()) ) { + AddDockedWindow ( &entityDlg, W_ENTITY, 4, "Entity", (dockedTabs & W_ENTITY ) != 0, InspectorsDockingCallback ); + } + + if ( consoleCreated && ::IsWindow(consoleWnd.GetSafeHwnd()) ) { + SetMode(W_CONSOLE); + } + initialized = true; + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void CInspectorDialog::SetMode(int mode, bool updateTabs) { + FocusWindow ( mode ); +} + +void CInspectorDialog::UpdateEntitySel(eclass_t *ent) { + entityDlg.UpdateEntitySel(ent); +} + +void CInspectorDialog::FillClassList() { + entityDlg.AddClassNames(); +} + +void CInspectorDialog::UpdateSelectedEntity() { + entityDlg.SetKeyValPairs(); +} + +bool CInspectorDialog::GetSelectAllCriteria(idStr &key, idStr &val) { + CString k, v; + entityDlg.editKey.GetWindowText(k); + entityDlg.editVal.GetWindowText(v); + key = k; + val = v; + return true; +} + + + +void CInspectorDialog::OnSize(UINT nType, int cx, int cy) +{ + CTabsDlg::OnSize(nType, cx, cy); + + DockedWindowInfo* info = NULL; + POSITION pos; + WORD wID; + + if (!initialized) { + return; + } + + CRect rect; + GetClientRect(rect); + + CRect tabRect; + m_Tabs.GetWindowRect(tabRect); + // retain vert size but size 4 in from edges and 4 up from bottom + tabRect.left = 4; + tabRect.right = rect.Width() - 4; + tabRect.top = rect.Height() - tabRect.Height() - 4; + tabRect.bottom = rect.Height() - 4; + // adjust rect for children size + rect.bottom -= 5 + tabRect.Height(); + + m_Tabs.SetWindowPos(NULL, tabRect.left, tabRect.top, tabRect.Width(), tabRect.Height(), 0); + + for( pos = m_Windows.GetStartPosition(); pos != NULL ; ) + { + m_Windows.GetNextAssoc( pos, wID, (void*&)info ); + + if ( (info->m_State == DockedWindowInfo::DOCKED) ) { + info->m_Window->SetWindowPos(NULL, rect.left, rect.top, rect.Width(), rect.Height(), 0); + } + + } +} + +void CInspectorDialog::OnDestroy() +{ + ::SaveWindowPlacement(GetSafeHwnd() , "radiant_InspectorsWindow" ); + SetCvarInt("radiant_InspectorDockedDialogs" , dockedTabs ); + + CTabsDlg::OnDestroy(); +} + +void CInspectorDialog::OnClose() +{ + CTabsDlg::OnClose(); +} + +BOOL CInspectorDialog::PreTranslateMessage(MSG* pMsg) +{ + // TODO: Add your specialized code here and/or call the base class + if ( pMsg->message == WM_KEYDOWN || pMsg->message == WM_KEYUP) { + g_pParentWnd->PostMessage(pMsg->message, pMsg->wParam, pMsg->lParam); + } + return CTabsDlg::PreTranslateMessage(pMsg); +} + +void CInspectorDialog::SetDockedTabs ( bool docked , int ID ) +{ + if ( docked ) { + dockedTabs |= ID; + } + else { + dockedTabs &= ~ID; + } +} + +void CInspectorDialog::AssignModel () +{ + entityDlg.AssignModel(); +} diff --git a/src/tools/radiant/InspectorDialog.h b/src/tools/radiant/InspectorDialog.h new file mode 100644 index 0000000..9c0ab4c --- /dev/null +++ b/src/tools/radiant/InspectorDialog.h @@ -0,0 +1,77 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#pragma once +#include "afxcmn.h" + +#include "entitydlg.h" +#include "ConsoleDlg.h" +#include "TabsDlg.h" + + +// CInspectorDialog dialog + +class CInspectorDialog : public CTabsDlg +{ + //DECLARE_DYNAMIC(CInspectorDialog)w + +public: + CInspectorDialog(CWnd* pParent = NULL); // standard constructor + virtual ~CInspectorDialog(); + +// Dialog Data + enum { IDD = IDD_DIALOG_INSPECTORS }; + +protected: + bool initialized; + unsigned int dockedTabs; + + DECLARE_MESSAGE_MAP() +public: + virtual BOOL OnInitDialog(); + void AssignModel (); + CTabCtrl tabInspector; + //idGLConsoleWidget consoleWnd; + CConsoleDlg consoleWnd; + CNewTexWnd texWnd; + CDialogTextures mediaDlg; + CEntityDlg entityDlg; + void SetMode(int mode, bool updateTabs = true); + void UpdateEntitySel(eclass_t *ent); + void UpdateSelectedEntity(); + void FillClassList(); + bool GetSelectAllCriteria(idStr &key, idStr &val); + + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnDestroy(); + afx_msg void OnClose(); + virtual BOOL PreTranslateMessage(MSG* pMsg); + + void SetDockedTabs ( bool docked , int ID ); +}; + +extern CInspectorDialog *g_Inspectors; \ No newline at end of file diff --git a/src/tools/radiant/LightDlg.cpp b/src/tools/radiant/LightDlg.cpp new file mode 100644 index 0000000..a806f36 --- /dev/null +++ b/src/tools/radiant/LightDlg.cpp @@ -0,0 +1,967 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" +#include "../../game/game.h" +#include "../comafx/DialogColorPicker.h" +#include "LightDlg.h" + +#ifdef ID_DEBUG_MEMORY +#undef new +#undef DEBUG_NEW +#define DEBUG_NEW new +#endif + + +void CLightInfo::Defaults() { + pointLight = true; + fallOff = 1; + strTexture = ""; + equalRadius = true; + explicitStartEnd = false; + lightRadius.Zero(); + lightTarget.Zero(); + lightRight.Zero(); + lightUp.Zero(); + lightStart.Zero(); + lightEnd.Zero(); + lightCenter.Zero(); + hasCenter = false; + isParallel = false; + castShadows = true; + castSpecular = true; + castDiffuse = true; + rotate = false; + strobe = false; + rotateSpeed = 0; + strobeSpeed = 0; + color[0] = color[1] = color[2] = 255; + fogDensity[0] = fogDensity[1] = fogDensity[2] = 0; + fog = false; + lightRadius[0] = lightRadius[1] = lightRadius[2] = 300; +} + + +void CLightInfo::DefaultPoint() { + idVec3 oldColor = color; + Defaults(); + color = oldColor; + pointLight = true; +} + +void CLightInfo::DefaultProjected() { + idVec3 oldColor = color; + Defaults(); + color = oldColor; + pointLight = false; + lightTarget[2] = -256; + lightUp[1] = -128; + lightRight[0] = -128; +} + +void CLightInfo::FromDict( const idDict *e ) { + + lightRadius.Zero(); + lightTarget.Zero(); + lightRight.Zero(); + lightUp.Zero(); + lightStart.Zero(); + lightEnd.Zero(); + lightCenter.Zero(); + + castShadows = !e->GetBool("noshadows"); + castSpecular = !e->GetBool("nospecular"); + castDiffuse = !e->GetBool("nodiffuse"); + fallOff = e->GetFloat("falloff"); + strTexture = e->GetString("texture"); + + isParallel = e->GetBool("parallel"); + + if (!e->GetVector("_color", "", color)) { + color[0] = color[1] = color[2] = 1; + } + // windows needs 0-255 scale + color[0] *= 255; + color[1] *= 255; + color[2] *= 255; + + if (e->GetVec4("fog", "", fogDensity)) { + fog = true; + } else { + fog = false; + } + + if (e->GetVector("light_right","", lightRight)) { + // projected light + pointLight = false; + e->GetVector("light_target", "", lightTarget); + e->GetVector("light_up", "", lightUp); + if (e->GetVector("light_start", "", lightStart)) { + // explicit start and end points + explicitStartEnd = true; + if (!e->GetVector("light_end", "", lightEnd)) { + // no end, use target + VectorCopy(lightTarget, lightEnd); + } + } else { + explicitStartEnd = false; + // create a start a quarter of the way to the target + lightStart = lightTarget * 0.25; + VectorCopy(lightTarget, lightEnd); + } + } else { + pointLight = true; + if (e->GetVector("light_radius", "", lightRadius)) { + equalRadius = false; + } else { + float radius = e->GetFloat("light"); + if (radius == 0) { + radius = 300; + } + lightRadius[0] = lightRadius[1] = lightRadius[2] = radius; + equalRadius = true; + } + if (e->GetVector("light_center", "", lightCenter)) { + hasCenter = true; + } + } +} + +void CLightInfo::ToDictFromDifferences ( idDict *e, const idDict *differences ) { + for ( int i = 0 ; i < differences->GetNumKeyVals () ; i ++ ) { + const idKeyValue *kv = differences->GetKeyVal( i ); + + if ( kv->GetValue().Length() > 0 ) { + e->Set ( kv->GetKey() ,kv->GetValue() ); + } else { + e->Delete ( kv->GetKey() ); + } + + common->Printf( "Applied difference: %s %s\n" , kv->GetKey().c_str() , kv->GetValue().c_str() ); + } +} + +//write all info to a dict, regardless of light type +void CLightInfo::ToDictWriteAllInfo( idDict *e ) { + e->Set("noshadows", (!castShadows) ? "1" : "0"); + e->Set("nospecular", (!castSpecular) ? "1" : "0"); + e->Set("nodiffuse", (!castDiffuse) ? "1" : "0"); + + e->SetFloat("falloff",fallOff); + + if (strTexture.GetLength() > 0 ) { + e->Set("texture", strTexture); + } + + idVec3 temp = color; + temp /= 255; + e->SetVector("_color", temp); + + if (!equalRadius) { + e->Set("light_radius", va("%g %g %g", lightRadius[0], lightRadius[1], lightRadius[2])); + } else { + e->Set("light_radius", va("%g %g %g", lightRadius[0], lightRadius[0], lightRadius[0])); + } + + e->Set("light_center", va("%g %g %g", lightCenter[0], lightCenter[1], lightCenter[2])); + e->Set("parallel", isParallel?"1":"0"); + + e->Set("light_target", va("%g %g %g", lightTarget[0], lightTarget[1], lightTarget[2])); + e->Set("light_up", va("%g %g %g", lightUp[0], lightUp[1], lightUp[2])); + e->Set("light_right", va("%g %g %g", lightRight[0], lightRight[1], lightRight[2])); + e->Set("light_start", va("%g %g %g", lightStart[0], lightStart[1], lightStart[2])); + e->Set("light_end", va("%g %g %g", lightEnd[0], lightEnd[1], lightEnd[2])); +} + +void CLightInfo::ToDict( idDict *e ) { + + e->Delete("noshadows"); + e->Delete("nospecular"); + e->Delete("nodiffuse"); + e->Delete("falloff"); + e->Delete("parallel"); + e->Delete("texture"); + e->Delete("_color"); + e->Delete("fog"); + e->Delete("light_target"); + e->Delete("light_right"); + e->Delete("light_up"); + e->Delete("light_start"); + e->Delete("light_end"); + e->Delete("light_radius"); + e->Delete("light_center"); + e->Delete("light"); + + e->Set("noshadows", (!castShadows) ? "1" : "0"); + e->Set("nospecular", (!castSpecular) ? "1" : "0"); + e->Set("nodiffuse", (!castDiffuse) ? "1" : "0"); + + e->SetFloat("falloff",fallOff); + + if (strTexture.GetLength() > 0) { + e->Set("texture", strTexture); + } + + idVec3 temp = color; + temp /= 255; + e->SetVector("_color", temp); + + if (fog) { + e->Set("fog", va("%g %g %g %g", fogDensity[0]/255.0, fogDensity[1]/255.0, fogDensity[2]/255.0, fogDensity[3]/255.0)); + } + + if (pointLight) { + if (!equalRadius) { + e->Set("light_radius", va("%g %g %g", lightRadius[0], lightRadius[1], lightRadius[2])); + } else { + e->Set("light_radius", va("%g %g %g", lightRadius[0], lightRadius[0], lightRadius[0])); + } + + if (hasCenter) { + e->Set("light_center", va("%g %g %g", lightCenter[0], lightCenter[1], lightCenter[2])); + } + + if (isParallel) { + e->Set("parallel", "1"); + } + } else { + e->Set("light_target", va("%g %g %g", lightTarget[0], lightTarget[1], lightTarget[2])); + e->Set("light_up", va("%g %g %g", lightUp[0], lightUp[1], lightUp[2])); + e->Set("light_right", va("%g %g %g", lightRight[0], lightRight[1], lightRight[2])); + if (explicitStartEnd) { + e->Set("light_start", va("%g %g %g", lightStart[0], lightStart[1], lightStart[2])); + e->Set("light_end", va("%g %g %g", lightEnd[0], lightEnd[1], lightEnd[2])); + } + } +} + +CLightInfo::CLightInfo() { + Defaults(); +} + + + +///////////////////////////////////////////////////////////////////////////// +// CLightDlg dialog + +CLightDlg *g_LightDialog = NULL; + + +CLightDlg::CLightDlg(CWnd* pParent /*=NULL*/) + : CDialog(CLightDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(CLightDlg) + m_bEqualRadius = FALSE; + m_bExplicitFalloff = FALSE; + m_bPointLight = FALSE; + m_bCheckProjected = FALSE; + m_fFallloff = 0.0f; + m_nFalloff = -1; + m_bRotate = FALSE; + m_bShadows = FALSE; + m_bSpecular = FALSE; + m_bDiffuse = FALSE; + m_fEndX = 0.0f; + m_fEndY = 0.0f; + m_fEndZ = 0.0f; + m_fRadiusX = 0.0f; + m_fRadiusY = 0.0f; + m_fRadiusZ = 0.0f; + m_fRightX = 0.0f; + m_fRightY = 0.0f; + m_fRightZ = 0.0f; + m_fRotate = 0.0f; + m_fStartX = 0.0f; + m_fStartY = 0.0f; + m_fStartZ = 0.0f; + m_fTargetX = 0.0f; + m_fTargetY = 0.0f; + m_fTargetZ = 0.0f; + m_fUpX = 0.0f; + m_fUpY = 0.0f; + m_fUpZ = 0.0f; + m_hasCenter = FALSE; + m_centerX = 0.0f; + m_centerY = 0.0f; + m_centerZ = 0.0f; + m_bIsParallel = FALSE; + //}}AFX_DATA_INIT + m_drawMaterial = new idGLDrawableMaterial(); +} + +CLightDlg::~CLightDlg() { + delete m_drawMaterial; +} + +void CLightDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CLightDlg) + if ( com_editorActive ) { + DDX_Control(pDX, IDC_LIGHTPREVIEW, m_wndPreview); + } + DDX_Control(pDX, IDC_COMBO_TEXTURE, m_wndLights); + DDX_Check(pDX, IDC_CHECK_EQUALRADIUS, m_bEqualRadius); + DDX_Check(pDX, IDC_CHECK_EXPLICITFALLOFF, m_bExplicitFalloff); + DDX_Check(pDX, IDC_CHECK_POINT, m_bPointLight); + DDX_Check(pDX, IDC_CHECK_PROJECTED, m_bCheckProjected); + DDX_Radio(pDX, IDC_RADIO_FALLOFF, m_nFalloff); + DDX_Check(pDX, IDC_CHECK_SHADOWS, m_bShadows); + DDX_Check(pDX, IDC_CHECK_SPECULAR, m_bSpecular); + DDX_Check(pDX, IDC_CHECK_DIFFUSE, m_bDiffuse); + DDX_Check(pDX , IDC_CHECK_PARALLEL , m_bIsParallel ); + DDX_Text(pDX, IDC_EDIT_ENDX, m_fEndX); + DDX_Text(pDX, IDC_EDIT_ENDY, m_fEndY); + DDX_Text(pDX, IDC_EDIT_ENDZ, m_fEndZ); + DDX_Text(pDX, IDC_EDIT_RADIUSX, m_fRadiusX); + DDX_Text(pDX, IDC_EDIT_RADIUSY, m_fRadiusY); + DDX_Text(pDX, IDC_EDIT_RADIUSZ, m_fRadiusZ); + DDX_Text(pDX, IDC_EDIT_RIGHTX, m_fRightX); + DDX_Text(pDX, IDC_EDIT_RIGHTY, m_fRightY); + DDX_Text(pDX, IDC_EDIT_RIGHTZ, m_fRightZ); + DDX_Text(pDX, IDC_EDIT_STARTX, m_fStartX); + DDX_Text(pDX, IDC_EDIT_STARTY, m_fStartY); + DDX_Text(pDX, IDC_EDIT_STARTZ, m_fStartZ); + DDX_Text(pDX, IDC_EDIT_TARGETX, m_fTargetX); + DDX_Text(pDX, IDC_EDIT_TARGETY, m_fTargetY); + DDX_Text(pDX, IDC_EDIT_TARGETZ, m_fTargetZ); + DDX_Text(pDX, IDC_EDIT_UPX, m_fUpX); + DDX_Text(pDX, IDC_EDIT_UPY, m_fUpY); + DDX_Text(pDX, IDC_EDIT_UPZ, m_fUpZ); + DDX_Check(pDX, IDC_CHECK_CENTER, m_hasCenter); + DDX_Text(pDX, IDC_EDIT_CENTERX, m_centerX); + DDX_Text(pDX, IDC_EDIT_CENTERY, m_centerY); + DDX_Text(pDX, IDC_EDIT_CENTERZ, m_centerZ); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CLightDlg, CDialog) + //{{AFX_MSG_MAP(CLightDlg) + ON_BN_CLICKED(IDC_BTN_TEXTURE, OnBtnTexture) + ON_BN_CLICKED(IDC_CHECK_EQUALRADIUS, OnCheckEqualradius) + ON_BN_CLICKED(IDC_CHECK_EXPLICITFALLOFF, OnCheckExplicitfalloff) + ON_BN_CLICKED(IDC_CHECK_POINT, OnCheckPoint) + ON_BN_CLICKED(IDC_CHECK_PROJECTED, OnCheckProjected) + ON_BN_CLICKED(IDC_RADIO_FALLOFF, OnRadioFalloff) + ON_BN_CLICKED(IDC_APPLY, OnApply) + ON_BN_CLICKED(IDC_APPLY_DIFFERENT, OnApplyDifferences) + ON_BN_CLICKED(IDC_BTN_COLOR, OnBtnColor) + ON_WM_CTLCOLOR() + ON_CBN_SELCHANGE(IDC_COMBO_TEXTURE, OnSelchangeComboTexture) + ON_BN_CLICKED(IDC_CHECK_CENTER, OnCheckCenter) + ON_BN_CLICKED(IDC_CHECK_PARALLEL, OnCheckParallel) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CLightDlg message handlers + +void CLightDlg::SetSpecifics() { + if (lightInfo.pointLight) { + GetDlgItem(IDC_EDIT_RADIUSY)->EnableWindow(!lightInfo.equalRadius); + GetDlgItem(IDC_EDIT_RADIUSZ)->EnableWindow(!lightInfo.equalRadius); + GetDlgItem(IDC_EDIT_CENTERX)->EnableWindow(lightInfo.hasCenter); + GetDlgItem(IDC_EDIT_CENTERY)->EnableWindow(lightInfo.hasCenter); + GetDlgItem(IDC_EDIT_CENTERZ)->EnableWindow(lightInfo.hasCenter); + } else { + GetDlgItem(IDC_EDIT_STARTX)->EnableWindow(lightInfo.explicitStartEnd); + GetDlgItem(IDC_EDIT_STARTY)->EnableWindow(lightInfo.explicitStartEnd); + GetDlgItem(IDC_EDIT_STARTZ)->EnableWindow(lightInfo.explicitStartEnd); + GetDlgItem(IDC_EDIT_ENDX)->EnableWindow(lightInfo.explicitStartEnd); + GetDlgItem(IDC_EDIT_ENDY)->EnableWindow(lightInfo.explicitStartEnd); + GetDlgItem(IDC_EDIT_ENDZ)->EnableWindow(lightInfo.explicitStartEnd); + } +} + +void CLightDlg::EnableControls() { + GetDlgItem(IDC_CHECK_EQUALRADIUS)->EnableWindow(lightInfo.pointLight); + GetDlgItem(IDC_EDIT_RADIUSX)->EnableWindow(lightInfo.pointLight); + GetDlgItem(IDC_EDIT_RADIUSY)->EnableWindow(lightInfo.pointLight); + GetDlgItem(IDC_EDIT_RADIUSZ)->EnableWindow(lightInfo.pointLight); + GetDlgItem(IDC_RADIO_FALLOFF)->EnableWindow(lightInfo.pointLight); + GetDlgItem(IDC_RADIO_FALLOFF2)->EnableWindow(lightInfo.pointLight); + GetDlgItem(IDC_RADIO_FALLOFF3)->EnableWindow(lightInfo.pointLight); + GetDlgItem(IDC_EDIT_TARGETX)->EnableWindow(!lightInfo.pointLight); + GetDlgItem(IDC_EDIT_TARGETY)->EnableWindow(!lightInfo.pointLight); + GetDlgItem(IDC_EDIT_TARGETZ)->EnableWindow(!lightInfo.pointLight); + GetDlgItem(IDC_EDIT_RIGHTX)->EnableWindow(!lightInfo.pointLight); + GetDlgItem(IDC_EDIT_RIGHTY)->EnableWindow(!lightInfo.pointLight); + GetDlgItem(IDC_EDIT_RIGHTZ)->EnableWindow(!lightInfo.pointLight); + GetDlgItem(IDC_EDIT_UPX)->EnableWindow(!lightInfo.pointLight); + GetDlgItem(IDC_EDIT_UPY)->EnableWindow(!lightInfo.pointLight); + GetDlgItem(IDC_EDIT_UPZ)->EnableWindow(!lightInfo.pointLight); + GetDlgItem(IDC_EDIT_STARTX)->EnableWindow(!lightInfo.pointLight); + GetDlgItem(IDC_EDIT_STARTY)->EnableWindow(!lightInfo.pointLight); + GetDlgItem(IDC_EDIT_STARTZ)->EnableWindow(!lightInfo.pointLight); + GetDlgItem(IDC_EDIT_ENDX)->EnableWindow(!lightInfo.pointLight); + GetDlgItem(IDC_EDIT_ENDY)->EnableWindow(!lightInfo.pointLight); + GetDlgItem(IDC_EDIT_ENDZ)->EnableWindow(!lightInfo.pointLight); + GetDlgItem(IDC_CHECK_EXPLICITFALLOFF)->EnableWindow(!lightInfo.pointLight); + GetDlgItem(IDC_CHECK_POINT)->EnableWindow(!lightInfo.pointLight); + GetDlgItem(IDC_CHECK_PROJECTED)->EnableWindow(lightInfo.pointLight); + GetDlgItem(IDC_EDIT_CENTERX)->EnableWindow(lightInfo.pointLight); + GetDlgItem(IDC_EDIT_CENTERY)->EnableWindow(lightInfo.pointLight); + GetDlgItem(IDC_EDIT_CENTERZ)->EnableWindow(lightInfo.pointLight); + GetDlgItem(IDC_CHECK_CENTER)->EnableWindow(lightInfo.pointLight); + + reinterpret_cast(GetDlgItem(IDC_CHECK_PROJECTED))->SetCheck(!lightInfo.pointLight); + reinterpret_cast(GetDlgItem(IDC_CHECK_POINT))->SetCheck(lightInfo.pointLight); + + SetSpecifics(); +} + +void CLightDlg::UpdateDialogFromLightInfo( void ) { + m_hasCenter = lightInfo.hasCenter; + m_bEqualRadius = lightInfo.equalRadius; + m_bExplicitFalloff = lightInfo.explicitStartEnd; + m_bPointLight = lightInfo.pointLight; + m_bCheckProjected = !lightInfo.pointLight; + m_fFallloff = lightInfo.fallOff; + if (lightInfo.fallOff < 0.35) { + m_nFalloff = 0; + } else if (lightInfo.fallOff < 0.70) { + m_nFalloff = 1; + } else { + m_nFalloff = 2; + } + //m_bFog = lightInfo.fog; + m_bRotate = lightInfo.rotate; + m_bShadows = lightInfo.castShadows; + m_bSpecular = lightInfo.castSpecular; + //m_bStrobe = lightInfo.strobe; + //m_fStrobe = lightInfo.strobeSpeed; + int sel = m_wndLights.FindStringExact(-1, lightInfo.strTexture); + m_wndLights.SetCurSel(sel); + if (sel >= 0) { + m_drawMaterial->setMedia(lightInfo.strTexture); + } else { + m_drawMaterial->setMedia(lightInfo.strTexture); + } + + m_bDiffuse = lightInfo.castDiffuse; + m_fEndX = lightInfo.lightEnd[0]; + m_fEndY = lightInfo.lightEnd[1]; + m_fEndZ = lightInfo.lightEnd[2]; + m_fRadiusX = lightInfo.lightRadius[0]; + m_fRadiusY = lightInfo.lightRadius[1]; + m_fRadiusZ = lightInfo.lightRadius[2]; + m_fRightX = lightInfo.lightRight[0]; + m_fRightY = lightInfo.lightRight[1]; + m_fRightZ = lightInfo.lightRight[2]; + //m_bRotate = lightInfo.rotate; + //m_fRotate = lightInfo.rotateSpeed; + m_fStartX = lightInfo.lightStart[0]; + m_fStartY = lightInfo.lightStart[1]; + m_fStartZ = lightInfo.lightStart[2]; + m_fTargetX = lightInfo.lightTarget[0]; + m_fTargetY = lightInfo.lightTarget[1]; + m_fTargetZ = lightInfo.lightTarget[2]; + m_fUpX = lightInfo.lightUp[0]; + m_fUpY = lightInfo.lightUp[1]; + m_fUpZ = lightInfo.lightUp[2]; + VectorCopy(lightInfo.color, color); + //m_fFogAlpha = lightInfo.fogDensity[3]; + m_centerX = lightInfo.lightCenter[0]; + m_centerY = lightInfo.lightCenter[1]; + m_centerZ = lightInfo.lightCenter[2]; + + //jhefty - added parallel light updating + m_bIsParallel = lightInfo.isParallel; + + UpdateData(FALSE); +} + +void CLightDlg::UpdateLightInfoFromDialog( void ) { + UpdateData( TRUE ); + + lightInfo.pointLight = ( m_bPointLight != FALSE ); + lightInfo.equalRadius = ( m_bEqualRadius != FALSE ); + lightInfo.explicitStartEnd = ( m_bExplicitFalloff != FALSE ); + + if (lightInfo.pointLight) { + if (m_nFalloff == 0) { + m_fFallloff = 0.0; + } else if (m_nFalloff == 1) { + m_fFallloff = 0.5; + } else { + m_fFallloff = 1.0; + } + } + + lightInfo.fallOff = m_fFallloff; + + //lightInfo.fog = m_bFog; + lightInfo.rotate = ( m_bRotate != FALSE ); + lightInfo.castShadows = ( m_bShadows != FALSE ); + lightInfo.castSpecular = ( m_bSpecular != FALSE ); + + VectorCopy(color, lightInfo.color); + lightInfo.isParallel = (m_bIsParallel == TRUE); + + //lightInfo.fogDensity[3] = m_fFogAlpha; + + //lightInfo.strobe = m_bStrobe; + //lightInfo.strobeSpeed = m_fStrobe; + //lightInfo.rotate = m_bRotate; + //lightInfo.rotateSpeed = m_fRotate; + + int sel = m_wndLights.GetCurSel(); + CString str(""); + if (sel >= 0) { + m_wndLights.GetLBText(sel, str); + } + lightInfo.strTexture = str; + + lightInfo.castDiffuse = ( m_bDiffuse != FALSE ); + lightInfo.lightEnd[0] = m_fEndX; + lightInfo.lightEnd[1] = m_fEndY; + lightInfo.lightEnd[2] = m_fEndZ; + lightInfo.lightRadius[0] = m_fRadiusX; + lightInfo.lightRadius[1] = m_fRadiusY; + lightInfo.lightRadius[2] = m_fRadiusZ; + lightInfo.lightRight[0] = m_fRightX; + lightInfo.lightRight[1] = m_fRightY; + lightInfo.lightRight[2] = m_fRightZ; + lightInfo.lightStart[0] = m_fStartX; + lightInfo.lightStart[1] = m_fStartY; + lightInfo.lightStart[2] = m_fStartZ; + lightInfo.lightTarget[0] = m_fTargetX; + lightInfo.lightTarget[1] = m_fTargetY; + lightInfo.lightTarget[2] = m_fTargetZ; + lightInfo.lightUp[0] = m_fUpX; + lightInfo.lightUp[1] = m_fUpY; + lightInfo.lightUp[2] = m_fUpZ; + + lightInfo.hasCenter = ( m_hasCenter != FALSE ); + lightInfo.lightCenter[0] = m_centerX; + lightInfo.lightCenter[1] = m_centerY; + lightInfo.lightCenter[2] = m_centerZ; +} + +void CLightDlg::SaveLightInfo( const idDict *differences ) { + + if ( com_editorActive ) { + + // used from Radiant + for ( brush_t *b = selected_brushes.next; b && b != &selected_brushes; b = b->next ) { + if ( ( b->owner->eclass->nShowFlags & ECLASS_LIGHT ) && !b->entityModel ) { + if ( differences ) { + lightInfo.ToDictFromDifferences( &b->owner->epairs, differences ); + } else { + lightInfo.ToDict( &b->owner->epairs ); + } + Brush_Build( b ); + } + } + + } else { + + // used in-game + idList list; + + list.SetNum( 128 ); + int count = gameEdit->GetSelectedEntities( list.Ptr(), list.Num() ); + list.SetNum( count ); + + for ( int i = 0; i < count; i++ ) { + if ( differences ) { + gameEdit->EntityChangeSpawnArgs( list[i], differences ); + gameEdit->EntityUpdateChangeableSpawnArgs( list[i], NULL ); + } else { + idDict newArgs; + lightInfo.ToDict( &newArgs ); + gameEdit->EntityChangeSpawnArgs( list[i], &newArgs ); + gameEdit->EntityUpdateChangeableSpawnArgs( list[i], NULL ); + } + gameEdit->EntityUpdateVisuals( list[i] ); + } + } +} + +void CLightDlg::ColorButtons() { + CRect r; + + CClientDC dc(this); + + CButton *pBtn = (CButton *)GetDlgItem(IDC_BTN_COLOR); + pBtn->GetClientRect(&r); + colorBitmap.DeleteObject(); + colorBitmap.CreateCompatibleBitmap(&dc, r.Width(), r.Height()); + CDC MemDC; + MemDC.CreateCompatibleDC(&dc); + CBitmap *pOldBmp = MemDC.SelectObject(&colorBitmap); + { + CBrush br(RGB(color[0], color[1], color[2])); + MemDC.FillRect(r,&br); + } + dc.SelectObject(pOldBmp); + pBtn->SetBitmap(HBITMAP(colorBitmap)); +} + + +void CLightDlg::LoadLightTextures() { + int count = declManager->GetNumDecls( DECL_MATERIAL ); + int i; + const idMaterial *mat; + for (i = 0; i < count; i++) { + mat = declManager->MaterialByIndex(i, false); + idStr str = mat->GetName(); + str.ToLower(); + if (str.Icmpn("lights/", strlen("lights/")) == 0 || str.Icmpn("fogs/", strlen("fogs/")) == 0) { + m_wndLights.AddString(mat->GetName()); + } + } +} + +BOOL CLightDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + + com_editors |= EDITOR_LIGHT; + + UpdateDialog( true ); + + LoadLightTextures(); + + if ( com_editorActive ) { + m_wndPreview.setDrawable(m_drawMaterial); + } + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void CLightDlg::OnDestroy() { + + com_editors &= ~EDITOR_LIGHT; + + return CDialog::OnDestroy(); +} + +void CLightDlg::OnBtnTexture() +{ + // TODO: Add your control notification handler code here + +} + +void CLightDlg::OnCheckEqualradius() +{ + lightInfo.equalRadius = ( reinterpret_cast(GetDlgItem(IDC_CHECK_EQUALRADIUS))->GetCheck() != 0 ); + SetSpecifics(); +} + +void CLightDlg::OnCheckExplicitfalloff() +{ + lightInfo.explicitStartEnd = ( reinterpret_cast(GetDlgItem(IDC_CHECK_EXPLICITFALLOFF))->GetCheck() != 0 ); + SetSpecifics(); +} + +void CLightDlg::OnCheckPoint() +{ + lightInfo.DefaultPoint(); + UpdateDialogFromLightInfo(); + EnableControls(); +} + +void CLightDlg::OnCheckProjected() +{ + lightInfo.DefaultProjected(); + UpdateDialogFromLightInfo(); + EnableControls(); +} + +void CLightDlg::OnRadioFalloff() +{ +} + +void CLightDlg::OnOK() { + UpdateLightInfoFromDialog(); + SaveLightInfo( NULL ); + Sys_UpdateWindows(W_ALL); + CDialog::OnOK(); +} + +entity_t *SingleLightSelected() { + if ( QE_SingleBrush( true, true ) ) { + brush_t *b = selected_brushes.next; + if ( ( b->owner->eclass->nShowFlags & ECLASS_LIGHT ) && !b->entityModel ) { + return b->owner; + } + } + return NULL; +} + +void CLightDlg::UpdateDialog( bool updateChecks ) +{ + CString title; + + lightInfo.Defaults(); + lightInfoOriginal.Defaults (); + + if ( com_editorActive ) { + // used from Radiant + entity_t *e = SingleLightSelected(); + if ( e ) { + lightInfo.FromDict(&e->epairs); + lightInfoOriginal.FromDict(&e->epairs); //our original copy of the values that we compare against for apply differences + title = "Light Editor"; + } else { + //find the last brush belonging to the last entity selected and use that as the source + e = NULL; + for ( brush_t *b = selected_brushes.next ; b != &selected_brushes ; b = b->next ) { + if ( ( b->owner->eclass->nShowFlags & ECLASS_LIGHT ) && !b->entityModel ) { + e = b->owner; + break; + } + } + + if ( e ) { + lightInfo.FromDict( &e->epairs ); + lightInfoOriginal.FromDict(&e->epairs); //our original copy of the values that we compaer against for apply differences + title = "Light Editor - (Multiple lights selected)"; + } else { + title = "Light Editor - (No lights selected)"; + } + } + } else { + // used in-game + idList list; + + list.SetNum( 128 ); + int count = gameEdit->GetSelectedEntities( list.Ptr(), list.Num() ); + list.SetNum( count ); + + if ( count > 0 ) { + lightInfo.FromDict( gameEdit->EntityGetSpawnArgs( list[count-1] ) ); + title = "Light Editor"; + } else { + title = "Light Editor - (No entities selected)"; + } + } + + SetWindowText( title ); + + UpdateDialogFromLightInfo(); + ColorButtons(); + + if ( updateChecks ) { + EnableControls(); + } +} + +void LightEditorInit( const idDict *spawnArgs ) { + if ( renderSystem->IsFullScreen() ) { + common->Printf( "Cannot run the light editor in fullscreen mode.\n" + "Set r_fullscreen to 0 and vid_restart.\n" ); + return; + } + + if ( g_LightDialog == NULL ) { + InitAfx(); + g_LightDialog = new CLightDlg(); + } + + if ( g_LightDialog->GetSafeHwnd() == NULL ) { + g_LightDialog->Create( IDD_DIALOG_LIGHT ); + CRect rct; + LONG lSize = sizeof( rct ); + if ( LoadRegistryInfo( "Radiant::LightWindow", &rct, &lSize ) ) { + g_LightDialog->SetWindowPos(NULL, rct.left, rct.top, 0,0, SWP_NOSIZE); + } + } + + g_LightDialog->ShowWindow( SW_SHOW ); + g_LightDialog->SetFocus(); + g_LightDialog->UpdateDialog( true ); + + if ( spawnArgs ) { + // FIXME: select light based on spawn args + } +} + +void LightEditorRun( void ) { +#if _MSC_VER >= 1300 + MSG *msg = AfxGetCurrentMessage(); // TODO Robert fix me!! +#else + MSG *msg = &m_msgCur; +#endif + + while( ::PeekMessage(msg, NULL, NULL, NULL, PM_NOREMOVE) ) { + // pump message + if ( !AfxGetApp()->PumpMessage() ) { + } + } +} + +void LightEditorShutdown( void ) { + delete g_LightDialog; + g_LightDialog = NULL; +} + +void UpdateLightInspector() { + if ( g_LightDialog && g_LightDialog->GetSafeHwnd() != NULL ) { + g_LightDialog->UpdateDialog(true); //jhefty - update ALL info about the light, including check boxes + } +} + +void CLightDlg::OnApply() { + UpdateLightInfoFromDialog(); + SaveLightInfo( NULL ); + Sys_UpdateWindows( W_ALL ); +} + +void UpdateLightDialog( float r, float g, float b, float a ) { + UpdateRadiantColor( 0.0f, 0.0f, 0.0f, 0.0f ); + g_LightDialog->UpdateColor( r, g, b, a ); +} + +void CLightDlg::UpdateColor( float r, float g, float b, float a ) { + color[0] = a * r; + color[1] = a * g; + color[2] = a * b; + ColorButtons(); + UpdateLightInfoFromDialog(); + SaveLightInfo( NULL ); + Sys_UpdateWindows( W_CAMERA ); +} + +void CLightDlg::OnBtnColor() { + int r, g, b; + float ob; + r = color[0]; + g = color[1]; + b = color[2]; + if ( DoNewColor( &r, &g, &b, &ob, UpdateLightDialog ) ) { + color[0] = ob * r; + color[1] = ob * g; + color[2] = ob * b; + ColorButtons(); + } +} + +void CLightDlg::OnCancel() { + CDialog::OnCancel(); +} + +HBRUSH CLightDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) +{ + HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); + + return hbr; +} + +BOOL CLightDlg::DestroyWindow() +{ + if (GetSafeHwnd()) + { + CRect rct; + GetWindowRect(rct); + SaveRegistryInfo("Radiant::LightWindow", &rct, sizeof(rct)); + } + return CDialog::DestroyWindow(); +} + +void CLightDlg::OnSelchangeComboTexture() +{ + UpdateData(TRUE); + int sel = m_wndLights.GetCurSel(); + CString str; + if (sel >= 0) { + m_wndLights.GetLBText(sel, str); + m_drawMaterial->setMedia(str); + if ( com_editorActive ) { + m_wndPreview.RedrawWindow(); + } + } + Sys_UpdateWindows(W_ALL); +} + +void CLightDlg::OnCheckCenter() +{ + if (reinterpret_cast(GetDlgItem(IDC_CHECK_CENTER))->GetCheck()) { + lightInfo.hasCenter = true; + lightInfo.lightCenter.x = 0; + lightInfo.lightCenter.y = 0; + lightInfo.lightCenter.z = 32; + } else { + lightInfo.hasCenter = false; + lightInfo.lightCenter.Zero(); + } + UpdateDialogFromLightInfo(); + SetSpecifics(); +} + +void CLightDlg::OnCheckParallel() { + if ( reinterpret_cast(GetDlgItem(IDC_CHECK_PARALLEL))->GetCheck() ) { + lightInfo.hasCenter = true; + lightInfo.isParallel = true; + lightInfo.lightCenter.x = 0; + lightInfo.lightCenter.y = 0; + lightInfo.lightCenter.z = 32; + } else { + lightInfo.isParallel = false; + lightInfo.hasCenter = false; + } + + UpdateDialogFromLightInfo(); + SetSpecifics(); +} + +//jhefty - only apply settings that are different +void CLightDlg::OnApplyDifferences () { + idDict differences, modified, original; + + UpdateLightInfoFromDialog(); + + lightInfo.ToDict( &modified ); + lightInfoOriginal.ToDictWriteAllInfo( &original ); + + differences = modified; + + // jhefty - compile a set of modified values to apply + for ( int i = 0; i < modified.GetNumKeyVals (); i ++ ) { + const idKeyValue* valModified = modified.GetKeyVal ( i ); + const idKeyValue* valOriginal = original.FindKey ( valModified->GetKey() ); + + //if it hasn't changed, remove it from the list of values to apply + if ( !valOriginal || ( valModified->GetValue() == valOriginal->GetValue() ) ) { + differences.Delete ( valModified->GetKey() ); + } + } + + SaveLightInfo( &differences ); + + lightInfoOriginal.FromDict( &modified ); + + Sys_UpdateWindows( W_ALL ); +} diff --git a/src/tools/radiant/LightDlg.h b/src/tools/radiant/LightDlg.h new file mode 100644 index 0000000..55ce550 --- /dev/null +++ b/src/tools/radiant/LightDlg.h @@ -0,0 +1,190 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#if !defined(AFX_LIGHTDLG_H__9DF57520_ED11_4BD8_968A_F6A7E34167D2__INCLUDED_) +#define AFX_LIGHTDLG_H__9DF57520_ED11_4BD8_968A_F6A7E34167D2__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "GLWidget.h" + +void LightEditorInit( const idDict *spawnArgs ); + +class CLightInfo { +public: + CLightInfo(); + + bool pointLight; + float fallOff; + CString strTexture; + bool equalRadius; + bool explicitStartEnd; + idVec3 lightStart; + idVec3 lightEnd; + idVec3 lightUp; + idVec3 lightRight; + idVec3 lightTarget; + idVec3 lightCenter; + idVec3 color; + bool fog; + idVec4 fogDensity; + + bool strobe; + float strobeSpeed; + bool rotate; + float rotateSpeed; + + idVec3 lightRadius; + bool castShadows; + bool castSpecular; + bool castDiffuse; + bool hasCenter; + bool isParallel; + + void Defaults(); + void DefaultProjected(); + void DefaultPoint(); + void FromDict( const idDict *e ); + void ToDict( idDict *e ); + void ToDictFromDifferences( idDict *e, const idDict *differences ); + void ToDictWriteAllInfo( idDict *e ); +}; + +///////////////////////////////////////////////////////////////////////////// +// CLightDlg dialog + +class CLightDlg : public CDialog { +public: + CLightDlg(CWnd* pParent = NULL); // standard constructor + ~CLightDlg(); + + void UpdateDialogFromLightInfo( void ); + void UpdateDialog( bool updateChecks ); + void UpdateLightInfoFromDialog( void ); + void UpdateColor( float r, float g, float b, float a ); + void SetSpecifics(); + void EnableControls(); + void LoadLightTextures(); + void ColorButtons(); + void SaveLightInfo( const idDict *differences ); + +// Dialog Data + //{{AFX_DATA(CLightDlg) + enum { IDD = IDD_DIALOG_LIGHT }; + idGLWidget m_wndPreview; + CComboBox m_wndLights; + CSliderCtrl m_wndFalloff; + BOOL m_bEqualRadius; + BOOL m_bExplicitFalloff; + BOOL m_bPointLight; + BOOL m_bCheckProjected; + float m_fFallloff; + int m_nFalloff; + BOOL m_bRotate; + BOOL m_bShadows; + BOOL m_bSpecular; + BOOL m_bDiffuse; + float m_fEndX; + float m_fEndY; + float m_fEndZ; + float m_fRadiusX; + float m_fRadiusY; + float m_fRadiusZ; + float m_fRightX; + float m_fRightY; + float m_fRightZ; + float m_fRotate; + float m_fStartX; + float m_fStartY; + float m_fStartZ; + float m_fTargetX; + float m_fTargetY; + float m_fTargetZ; + float m_fUpX; + float m_fUpY; + float m_fUpZ; + BOOL m_hasCenter; + float m_centerX; + float m_centerY; + float m_centerZ; + BOOL m_bIsParallel; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CLightDlg) + public: + virtual BOOL DestroyWindow(); + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CLightDlg) + virtual BOOL OnInitDialog(); + afx_msg void OnDestroy(); + afx_msg void OnBtnTexture(); + afx_msg void OnCheckEqualradius(); + afx_msg void OnCheckExplicitfalloff(); + afx_msg void OnCheckPoint(); + afx_msg void OnCheckProjected(); + afx_msg void OnRadioFalloff(); + virtual void OnOK(); + afx_msg void OnApply(); + afx_msg void OnBtnColor(); + afx_msg void OnBtnFog(); + afx_msg void OnCheckFog(); + afx_msg void OnCheckRotate(); + afx_msg void OnCheckStrobe(); + virtual void OnCancel(); + afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor); + afx_msg void OnSelchangeComboTexture(); + afx_msg void OnCheckCenter(); + afx_msg void OnCheckParallel(); + afx_msg void OnApplyDifferences(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + +private: + CBitmap colorBitmap; + CBitmap fogBitmap; + CLightInfo lightInfo; + CLightInfo lightInfoOriginal; + idVec3 color; + idGLDrawableMaterial * m_drawMaterial; +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_LIGHTDLG_H__9DF57520_ED11_4BD8_968A_F6A7E34167D2__INCLUDED_) diff --git a/src/tools/radiant/MRU.CPP b/src/tools/radiant/MRU.CPP new file mode 100644 index 0000000..943fb17 --- /dev/null +++ b/src/tools/radiant/MRU.CPP @@ -0,0 +1,679 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include +#include "mru.h" + +//************************************************************* +// File name: mru.c +// +// Description: +// +// Routines for MRU support +// +// Development Team: +// +// Gilles Vollant (100144.2636@compuserve.com) +// +//************************************************************* + + +// CreateMruMenu : MRUMENU constructor +// wNbLruShowInit : nb of item showed in menu +// wNbLruMenuInit : nb of item stored in memory +// wMaxSizeLruItemInit : size max. of filename + + +//************************************************************* +// +// CreateMruMenu() +// +// Purpose: +// +// Allocate and Initialize an MRU and return a pointer on it +// +// +// Parameters: +// +// WORD wNbLruShowInit - Maximum number of item displayed on menu +// WORD wNbLruMenuInit - Maximum number of item stored in memory +// WORD wMaxSizeLruItemInit - Maximum size of an item (ie size of pathname) +// WORD wIdMruInit - ID of the first item in the menu (default:IDMRU) +// +// +// Return: (LPMRUMENU) +// +// Pointer on a MRUMENU structure, used by other function +// +// +// Comments: +// wNbLruShowInit <= wNbLruMenuInit +// +// +// History: Date Author Comment +// 09/24/94 G. Vollant Created +// +//************************************************************* + +LPMRUMENU CreateMruMenu (WORD wNbLruShowInit, + WORD wNbLruMenuInit,WORD wMaxSizeLruItemInit,WORD wIdMruInit) +{ +LPMRUMENU lpMruMenu; + lpMruMenu = (LPMRUMENU)GlobalAllocPtr(GHND,sizeof(MRUMENU)); + + lpMruMenu->wNbItemFill = 0; + lpMruMenu->wNbLruMenu = wNbLruMenuInit; + lpMruMenu->wNbLruShow = wNbLruShowInit; + lpMruMenu->wIdMru = wIdMruInit; + lpMruMenu->wMaxSizeLruItem = wMaxSizeLruItemInit; + lpMruMenu->lpMRU = (LPSTR)GlobalAllocPtr(GHND, + lpMruMenu->wNbLruMenu*(UINT)lpMruMenu->wMaxSizeLruItem); + if (lpMruMenu->lpMRU == NULL) + { + GlobalFreePtr(lpMruMenu); + lpMruMenu = NULL; + } + return lpMruMenu; +} + +//************************************************************* +// +// CreateMruMenuDefault() +// +// Purpose: +// +// Allocate and Initialize an MRU and return a pointer on it +// Use default parameter +// +// +// Parameters: +// +// +// Return: (LPMRUMENU) +// +// Pointer on a MRUMENU structure, used by other function +// +// +// Comments: +// +// +// History: Date Author Comment +// 09/24/94 G. Vollant Created +// +//************************************************************* + +LPMRUMENU CreateMruMenuDefault() +{ + return CreateMruMenu (NBMRUMENUSHOW,NBMRUMENU,MAXSIZEMRUITEM,IDMRU); +} + + +//************************************************************* +// +// DeleteMruMenu() +// +// Purpose: +// Destructor : +// Clean and free a MRUMENU structure +// +// Parameters: +// +// LPMRUMENU lpMruMenu - pointer on MRUMENU, allocated +// by CreateMruMenu() or CreateMruMenuDefault() +// +// +// Return: void +// +// +// Comments: +// +// +// History: Date Author Comment +// 09/24/94 G. Vollant Created +// +//************************************************************* +void DeleteMruMenu(LPMRUMENU lpMruMenu) +{ + GlobalFreePtr(lpMruMenu->lpMRU); + GlobalFreePtr(lpMruMenu); +} + +//************************************************************* +// +// SetNbLruShow() +// +// Purpose: +// Change the maximum number of item displayed on menu +// +// Parameters: +// LPMRUMENU lpMruMenu - pointer on MRUMENU +// WORD wNbLruShowInit - Maximum number of item displayed on menu +// +// +// Return: void +// +// +// Comments: +// +// +// History: Date Author Comment +// 09/24/94 G. Vollant Created +// +//************************************************************* +void SetNbLruShow (LPMRUMENU lpMruMenu,WORD wNbLruShowInit) +{ + lpMruMenu->wNbLruShow = min(wNbLruShowInit,lpMruMenu->wNbLruMenu); +} + +//************************************************************* +// +// SetMenuItem() +// +// Purpose: +// Set the filename of an item +// +// Parameters: +// LPMRUMENU lpMruMenu - pointer on MRUMENU +// WORD wItem - Number of Item to set, zero based +// LPSTR lpItem - String, contain the filename of the item +// +// +// Return: (BOOL) +// TRUE - Function run successfully +// FALSE - Function don't run successfully +// +// +// Comments: +// used when load .INI or reg database +// +// History: Date Author Comment +// 09/24/94 G. Vollant Created +// +//************************************************************* +BOOL SetMenuItem (LPMRUMENU lpMruMenu,WORD wItem,LPSTR lpItem) +{ + if (wItem >= NBMRUMENU) + return FALSE; + _fstrncpy((lpMruMenu->lpMRU) + + ((lpMruMenu->wMaxSizeLruItem) * (UINT)wItem), + lpItem,lpMruMenu->wMaxSizeLruItem-1); + lpMruMenu->wNbItemFill = max(lpMruMenu->wNbItemFill,wItem+1); + return TRUE; +} + +//************************************************************* +// +// GetMenuItem() +// +// Purpose: +// Get the filename of an item +// +// Parameters: +// LPMRUMENU lpMruMenu - pointer on MRUMENU +// WORD wItem - Number of Item to set, zero based +// BOOL fIDMBased - TRUE : wItem is based on ID menu item +// FALSE : wItem is zero-based +// LPSTR lpItem - String where the filename of the item will be +// stored by GetMenuItem() +// UINT uiSize - Size of the lpItem buffer +// +// +// Return: (BOOL) +// TRUE - Function run successfully +// FALSE - Function don't run successfully +// +// +// Comments: +// Used for saving in .INI or reg database, or when user select +// an MRU in File menu +// +// History: Date Author Comment +// 09/24/94 G. Vollant Created +// +//************************************************************* +BOOL GetMenuItem (LPMRUMENU lpMruMenu,WORD wItem, + BOOL fIDMBased,LPSTR lpItem,UINT uiSize) +{ + if (fIDMBased) + wItem -= (lpMruMenu->wIdMru + 1); + if (wItem >= lpMruMenu->wNbItemFill) + return FALSE; + _fstrncpy(lpItem,(lpMruMenu->lpMRU) + + ((lpMruMenu->wMaxSizeLruItem) * (UINT)(wItem)),uiSize); + *(lpItem+uiSize-1) = '\0'; + return TRUE; +} + +//************************************************************* +// +// AddNewItem() +// +// Purpose: +// Add an item at the begin of the list +// +// Parameters: +// LPMRUMENU lpMruMenu - pointer on MRUMENU +// LPSTR lpItem - String contain the filename to add +// +// Return: (BOOL) +// TRUE - Function run successfully +// FALSE - Function don't run successfully +// +// +// Comments: +// Used when used open a file (using File Open common +// dialog, Drag and drop or MRU) +// +// History: Date Author Comment +// 09/24/94 G. Vollant Created +// +//************************************************************* +void AddNewItem (LPMRUMENU lpMruMenu,LPSTR lpItem) +{ +WORD i,j; + for (i=0;iwNbItemFill;i++) + if (lstrcmpi(lpItem,(lpMruMenu->lpMRU) + + ((lpMruMenu->wMaxSizeLruItem) * (UINT)i)) == 0) + { + // Shift the other items + for (j=i;j>0;j--) + lstrcpy((lpMruMenu->lpMRU) + (lpMruMenu->wMaxSizeLruItem * (UINT)j), + (lpMruMenu->lpMRU) + (lpMruMenu->wMaxSizeLruItem * (UINT)(j-1))); + _fstrncpy(lpMruMenu->lpMRU,lpItem,lpMruMenu->wMaxSizeLruItem-1); + return ; + } + lpMruMenu->wNbItemFill = min(lpMruMenu->wNbItemFill+1,lpMruMenu->wNbLruMenu); + for (i=lpMruMenu->wNbItemFill-1;i>0;i--) + lstrcpy(lpMruMenu->lpMRU + (lpMruMenu->wMaxSizeLruItem * (UINT)i), + lpMruMenu->lpMRU + (lpMruMenu->wMaxSizeLruItem * (UINT)(i-1))); + _fstrncpy(lpMruMenu->lpMRU,lpItem,lpMruMenu->wMaxSizeLruItem-1); +} + +//************************************************************* +// +// DelMenuItem() +// +// Purpose: +// Delete an item +// +// Parameters: +// LPMRUMENU lpMruMenu - pointer on MRUMENU +// WORD wItem - Number of Item to set, zero based +// BOOL fIDMBased - TRUE : wItem is based on ID menu item +// FALSE : wItem is zero-based +// +// Return: (BOOL) +// TRUE - Function run successfully +// FALSE - Function don't run successfully +// +// +// Comments: +// Used when used open a file, using MRU, and when an error +// occured (by example, when file was deleted) +// +// History: Date Author Comment +// 09/24/94 G. Vollant Created +// +//************************************************************* +BOOL DelMenuItem(LPMRUMENU lpMruMenu,WORD wItem,BOOL fIDMBased) +{ +WORD i; + if (fIDMBased) + wItem -= (lpMruMenu->wIdMru + 1); + if (lpMruMenu->wNbItemFill <= wItem) + return FALSE; + lpMruMenu->wNbItemFill--; + for (i=wItem;iwNbItemFill;i++) + lstrcpy(lpMruMenu->lpMRU + (lpMruMenu->wMaxSizeLruItem * (UINT)i), + lpMruMenu->lpMRU + (lpMruMenu->wMaxSizeLruItem * (UINT)(i+1))); + return TRUE; +} + +//************************************************************* +// +// PlaceMenuMRUItem() +// +// Purpose: +// Add MRU at the end of a menu +// +// Parameters: +// LPMRUMENU lpMruMenu - pointer on MRUMENU +// HMENU hMenu - Handle of menu where MRU must be added +// UINT uiItem - Item of menu entry where MRU must be added +// +// Return: void +// +// +// Comments: +// Used MRU is modified, for refresh the File menu +// +// History: Date Author Comment +// 09/24/94 G. Vollant Created +// +//************************************************************* +void PlaceMenuMRUItem(LPMRUMENU lpMruMenu,HMENU hMenu,UINT uiItem) +{ +int i; +WORD wNbShow; + if (hMenu == NULL) + return; + // remove old MRU in menu + for (i=0;i<=(int)(lpMruMenu->wNbLruMenu);i++) + RemoveMenu(hMenu,i+lpMruMenu->wIdMru,MF_BYCOMMAND); + + if (lpMruMenu->wNbItemFill == 0) + return; + + // If they are item, insert a separator before the files + InsertMenu(hMenu,uiItem,MF_SEPARATOR,lpMruMenu->wIdMru,NULL); + + wNbShow = min(lpMruMenu->wNbItemFill,lpMruMenu->wNbLruShow); + for (i=(int)wNbShow-1;i>=0;i--) + { + LPSTR lpTxt; + if (lpTxt = (LPSTR)GlobalAllocPtr(GHND,lpMruMenu->wMaxSizeLruItem + 20)) + { + wsprintf(lpTxt,"&%lu %s", + (DWORD)(i+1),lpMruMenu->lpMRU + (lpMruMenu->wMaxSizeLruItem*(UINT)i)); + InsertMenu(hMenu,(((WORD)i)!=(wNbShow-1)) ? (lpMruMenu->wIdMru+i+2) : lpMruMenu->wIdMru, + MF_STRING,lpMruMenu->wIdMru+i+1,lpTxt); + GlobalFreePtr(lpTxt); + } + } + +} + +/////////////////////////////////////////// + + + +//************************************************************* +// +// SaveMruInIni() +// +// Purpose: +// Save MRU in a private .INI +// +// Parameters: +// LPMRUMENU lpMruMenu - pointer on MRUMENU +// LPSTR lpszSection - Points to a null-terminated string containing +// the name of the section +// LPSTR lpszFile - Points to a null-terminated string that names +// the initialization file. +// +// Return: (BOOL) +// TRUE - Function run successfully +// FALSE - Function don't run successfully +// +// +// Comments: +// See WritePrivateProfileString API for more info on lpszSection and lpszFile +// +// History: Date Author Comment +// 09/24/94 G. Vollant Created +// +//************************************************************* +BOOL SaveMruInIni(LPMRUMENU lpMruMenu,LPSTR lpszSection,LPSTR lpszFile) +{ +LPSTR lpTxt; +WORD i; + + lpTxt = (LPSTR)GlobalAllocPtr(GHND,lpMruMenu->wMaxSizeLruItem + 20); + if (lpTxt == NULL) + return FALSE; + + for (i=0;iwNbLruMenu;i++) + { + char szEntry[16]; + wsprintf(szEntry,"File%lu",(DWORD)i+1); + if (!GetMenuItem(lpMruMenu,i,FALSE,lpTxt,lpMruMenu->wMaxSizeLruItem + 10)) + *lpTxt = '\0'; + WritePrivateProfileString(lpszSection,szEntry,lpTxt,lpszFile); + } + GlobalFreePtr(lpTxt); + WritePrivateProfileString(NULL,NULL,NULL,lpszFile); // flush cache + return TRUE; +} + + +//************************************************************* +// +// LoadMruInIni() +// +// Purpose: +// Load MRU from a private .INI +// +// Parameters: +// LPMRUMENU lpMruMenu - pointer on MRUMENU +// LPSTR lpszSection - Points to a null-terminated string containing +// the name of the section +// LPSTR lpszFile - Points to a null-terminated string that names +// the initialization file. +// +// Return: (BOOL) +// TRUE - Function run successfully +// FALSE - Function don't run successfully +// +// +// Comments: +// See GetPrivateProfileString API for more info on lpszSection and lpszFile +// +// History: Date Author Comment +// 09/24/94 G. Vollant Created +// +//************************************************************* +BOOL LoadMruInIni(LPMRUMENU lpMruMenu,LPSTR lpszSection,LPSTR lpszFile) +{ +LPSTR lpTxt; +WORD i; + lpTxt = (LPSTR)GlobalAllocPtr(GHND,lpMruMenu->wMaxSizeLruItem + 20); + if (lpTxt == NULL) + return FALSE; + + for (i=0;iwNbLruMenu;i++) + { + char szEntry[16]; + + wsprintf(szEntry,"File%lu",(DWORD)i+1); + GetPrivateProfileString(lpszSection,szEntry,"",lpTxt, + lpMruMenu->wMaxSizeLruItem + 10,lpszFile); + if (*lpTxt == '\0') + break; + SetMenuItem(lpMruMenu,i,lpTxt); + } + GlobalFreePtr(lpTxt); + return TRUE; +} + +#ifdef WIN32 + +BOOL IsWin395OrHigher(void) +{ + WORD wVer; + + wVer = LOWORD(GetVersion()); + wVer = (((WORD)LOBYTE(wVer)) << 8) | (WORD)HIBYTE(wVer); + + return (wVer >= 0x035F); // 5F = 95 dec +} + + +//************************************************************* +// +// SaveMruInReg() +// +// Purpose: +// Save MRU in the registry +// +// Parameters: +// LPMRUMENU lpMruMenu - pointer on MRUMENU +// LPSTR lpszKey - Points to a null-terminated string +// specifying the name of a key that +// this function opens or creates. +// +// Return: (BOOL) +// TRUE - Function run successfully +// FALSE - Function don't run successfully +// +// +// Comments: +// Win32 function designed for Windows NT and Windows 95 +// See RegCreateKeyEx API for more info on lpszKey +// +// History: Date Author Comment +// 09/24/94 G. Vollant Created +// +//************************************************************* +BOOL SaveMruInReg(LPMRUMENU lpMruMenu,LPSTR lpszKey) +{ +LPSTR lpTxt; +WORD i; +HKEY hCurKey; +DWORD dwDisp; + + lpTxt = (LPSTR)GlobalAllocPtr(GHND,lpMruMenu->wMaxSizeLruItem + 20); + if (lpTxt == NULL) + return FALSE; + + RegCreateKeyEx(HKEY_CURRENT_USER,lpszKey,0,NULL, + REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS,NULL,&hCurKey,&dwDisp); + + for (i=0;iwNbLruMenu;i++) + { + char szEntry[16]; + wsprintf(szEntry,"File%lu",(DWORD)i+1); + if (!GetMenuItem(lpMruMenu,i,FALSE,lpTxt,lpMruMenu->wMaxSizeLruItem + 10)) + *lpTxt = '\0'; + RegSetValueEx(hCurKey,szEntry,0,REG_SZ,(unsigned char*)lpTxt,lstrlen(lpTxt)); + } + RegCloseKey(hCurKey); + GlobalFreePtr(lpTxt); + return TRUE; +} + +//************************************************************* +// +// LoadMruInReg() +// +// Purpose: +// Load MRU from the registry +// +// Parameters: +// LPMRUMENU lpMruMenu - pointer on MRUMENU +// LPSTR lpszKey - Points to a null-terminated string +// specifying the name of a key that +// this function opens or creates. +// +// Return: (BOOL) +// TRUE - Function run successfully +// FALSE - Function don't run successfully +// +// +// Comments: +// Win32 function designed for Windows NT and Windows 95 +// See RegOpenKeyEx API for more info on lpszKey +// +// History: Date Author Comment +// 09/24/94 G. Vollant Created +// +//************************************************************* +BOOL LoadMruInReg(LPMRUMENU lpMruMenu,LPSTR lpszKey) +{ +LPSTR lpTxt; +WORD i; +HKEY hCurKey; +DWORD dwType; + lpTxt = (LPSTR)GlobalAllocPtr(GHND,lpMruMenu->wMaxSizeLruItem + 20); + if (lpTxt == NULL) + return FALSE; + + RegOpenKeyEx(HKEY_CURRENT_USER,lpszKey,0,KEY_READ,&hCurKey); + + + for (i=0;iwNbLruMenu;i++) + { + char szEntry[16]; + DWORD dwSizeBuf; + wsprintf(szEntry,"File%lu",(DWORD)i+1); + *lpTxt = '\0'; + dwSizeBuf = lpMruMenu->wMaxSizeLruItem + 10; + RegQueryValueEx(hCurKey,szEntry,NULL,&dwType,(LPBYTE)lpTxt,&dwSizeBuf); + *(lpTxt+dwSizeBuf)='\0'; + if (*lpTxt == '\0') + break; + SetMenuItem(lpMruMenu,i,lpTxt); + } + RegCloseKey(hCurKey); + GlobalFreePtr(lpTxt); + return TRUE; +} + + +//************************************************************* +// +// GetWin32Kind() +// +// Purpose: +// Get the Win32 platform +// +// Parameters: +// +// Return: (WIN32KIND) +// WINNT - Run under Windows NT +// WIN32S - Run under Windows 3.1x + Win32s +// WIN95ORGREATHER - Run under Windows 95 +// +// +// Comments: +// Win32 function designed for Windows NT and Windows 95 +// See RegOpenKeyEx API for more info on lpszKey +// +// History: Date Author Comment +// 09/24/94 G. Vollant Created +// +//************************************************************* +WIN32KIND GetWin32Kind() +{ +BOOL IsWin395OrHigher(void); + + WORD wVer; + + if ((GetVersion() & 0x80000000) == 0) + return WINNT; + wVer = LOWORD(GetVersion()); + wVer = (((WORD)LOBYTE(wVer)) << 8) | (WORD)HIBYTE(wVer); + + if (wVer >= 0x035F) + return WIN95ORGREATHER; + else + return WIN32S; +} +#endif diff --git a/src/tools/radiant/MRU.H b/src/tools/radiant/MRU.H new file mode 100644 index 0000000..cc5d3f7 --- /dev/null +++ b/src/tools/radiant/MRU.H @@ -0,0 +1,94 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef __MRU_H__ +#define __MRU_H__ + +#define NBMRUMENUSHOW 6 // Default number of MRU showed in the menu File +#define NBMRUMENU 9 // Default number of MRU stored +#define IDMRU 8000 // Default First ID of MRU +#ifdef OFS_MAXPATHNAME +#define MAXSIZEMRUITEM OFS_MAXPATHNAME +#else +#define MAXSIZEMRUITEM 128 // Default max size of an entry +#endif + +typedef struct +{ +WORD wNbItemFill; +WORD wNbLruShow; +WORD wNbLruMenu; +WORD wMaxSizeLruItem; +WORD wIdMru; +LPSTR lpMRU; +} MRUMENU; + +typedef MRUMENU FAR * LPMRUMENU; + +#ifdef __cplusplus +LPMRUMENU CreateMruMenu (WORD wNbLruShowInit=NBMRUMENUSHOW, + WORD wNbLruMenuInit=NBMRUMENU, + WORD wMaxSizeLruItemInit=MAXSIZEMRUITEM, + WORD wIdMruInit=IDMRU); +#else +LPMRUMENU CreateMruMenu (WORD wNbLruShowInit, + WORD wNbLruMenuInit, + WORD wMaxSizeLruItemInit, + WORD wIdMruInit); +#endif + +LPMRUMENU CreateMruMenuDefault(); +void DeleteMruMenu (LPMRUMENU lpMruMenu); + +void SetNbLruShow (LPMRUMENU lpMruMenu,WORD wNbLruShowInit); +BOOL SetMenuItem (LPMRUMENU lpMruMenu,WORD wItem, + LPSTR lpItem); +BOOL GetMenuItem (LPMRUMENU lpMruMenu,WORD wItem, + BOOL fIDMBased,LPSTR lpItem,UINT uiSize); +BOOL DelMenuItem (LPMRUMENU lpMruMenu,WORD wItem,BOOL fIDMBased); +void AddNewItem (LPMRUMENU lpMruMenu,LPSTR lpItem); +void PlaceMenuMRUItem(LPMRUMENU lpMruMenu,HMENU hMenu,UINT uiItem); + +BOOL SaveMruInIni (LPMRUMENU lpMruMenu,LPSTR lpszSection,LPSTR lpszFile); +BOOL LoadMruInIni (LPMRUMENU lpMruMenu,LPSTR lpszSection,LPSTR lpszFile); +#ifdef WIN32 +BOOL SaveMruInReg (LPMRUMENU lpMruMenu,LPSTR lpszKey); +BOOL LoadMruInReg (LPMRUMENU lpMruMenu,LPSTR lpszKey); + +typedef enum +{ +WIN32S, +WINNT, +WIN95ORGREATHER +} WIN32KIND; +WIN32KIND GetWin32Kind(); +#endif + + +////////////////////////////////////////////////////////////// +#endif diff --git a/src/tools/radiant/MainFrm.cpp b/src/tools/radiant/MainFrm.cpp new file mode 100644 index 0000000..96ff7a5 --- /dev/null +++ b/src/tools/radiant/MainFrm.cpp @@ -0,0 +1,6923 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" +#include "ZWnd.h" +#include "CamWnd.h" +#include "MapInfo.h" +#include "MainFrm.h" +#include "RotateDlg.h" +#include "EntityListDlg.h" +#include "NewProjDlg.h" +#include "CommandsDlg.h" +#include "ScaleDialog.h" +#include "FindTextureDlg.h" +#include "SurfaceDlg.h" +#include "shlobj.h" +#include "DialogTextures.h" +#include "PatchDensityDlg.h" +#include "DialogThick.h" +#include "PatchDialog.h" +#include "Undo.h" +#include "NewTexWnd.h" +#include "splines.h" +#include "dlgcamera.h" +#include "mmsystem.h" +#include "LightDlg.h" +#include "GetString.h" +#include "EntKeyFindReplace.h" +#include "InspectorDialog.h" +#include "autocaulk.h" +#include "../compilers/dmap/dmap.h" + +#include "../../sys/win32/rc/common_resource.h" +#include "../comafx/DialogName.h" +#include "../comafx/DialogColorPicker.h" + +#ifdef _DEBUG + #define new DEBUG_NEW + #undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +// globals +CString g_strAppPath; // holds the full path of the executable +CMainFrame *g_pParentWnd = NULL; // used to precast to CMainFrame +CPrefsDlg g_Preferences; // global prefs instance +CPrefsDlg &g_PrefsDlg = g_Preferences; // reference used throughout +int g_nUpdateBits = 0; // window update flags +bool g_bScreenUpdates = true; // whether window painting is active, used in a few places + +// +// to disable updates for speed reasons both of the above should be made members +// of CMainFrame +// bool g_bSnapToGrid = true; // early use, no longer in use, clamping pref will +// be used +// +CString g_strProject; // holds the active project filename + +#define D3XP_ID_FILE_SAVE_COPY ( WM_USER + 28476 ) +#define D3XP_ID_SHOW_MODELS ( WM_USER + 28477 ) + +// +// CMainFrame +// command mapping stuff m_strCommand is the command string m_nKey is the windows +// VK_??? equivelant m_nModifiers are key states as follows bit 0 - shift 1 - alt +// 2 - control 4 - press only +// +#define SPEED_MOVE 32.0f +#define SPEED_TURN 22.5f + +#define MAX_GRID 64.0f +#define MIN_GRID 0.125f + +SCommandInfo g_Commands[] = { + { "Texture_AxialByHeight", 'U', 0, ID_SELECT_AXIALTEXTURE_BYHEIGHT }, + { "Texture_AxialArbitrary", 'U', RAD_SHIFT, ID_SELECT_AXIALTEXTURE_ARBITRARY }, + { "Texture_AxialByWidth", 'U', RAD_CONTROL, ID_SELECT_AXIALTEXTURE_BYWIDTH }, + { "Texture_Decrement", VK_SUBTRACT, RAD_SHIFT, ID_SELECTION_TEXTURE_DEC }, + { "Texture_Increment", VK_ADD, RAD_SHIFT, ID_SELECTION_TEXTURE_INC }, + { "Texture_Fit", '5', RAD_SHIFT, ID_SELECTION_TEXTURE_FIT }, + { "Texture_RotateClock", VK_NEXT, RAD_SHIFT, ID_SELECTION_TEXTURE_ROTATECLOCK }, + { "Texture_RotateCounter", VK_PRIOR, RAD_SHIFT, ID_SELECTION_TEXTURE_ROTATECOUNTER }, + { "Texture_ScaleUp", VK_UP, RAD_CONTROL, ID_SELECTION_TEXTURE_SCALEUP }, + { "Texture_ScaleDown", VK_DOWN, RAD_CONTROL, ID_SELECTION_TEXTURE_SCALEDOWN }, + { "Texture_ShiftLeft", VK_LEFT, RAD_SHIFT, ID_SELECTION_TEXTURE_SHIFTLEFT }, + { "Texture_ShiftRight", VK_RIGHT, RAD_SHIFT, ID_SELECTION_TEXTURE_SHIFTRIGHT }, + { "Texture_ShiftUp", VK_UP, RAD_SHIFT, ID_SELECTION_TEXTURE_SHIFTUP }, + { "Texture_ShiftDown", VK_DOWN, RAD_SHIFT, ID_SELECTION_TEXTURE_SHIFTDOWN }, + { "Texture_ScaleLeft", VK_LEFT, RAD_CONTROL, ID_SELECTION_TEXTURE_SCALELEFT }, + { "Texture_ScaleRight", VK_RIGHT, RAD_CONTROL, ID_SELECTION_TEXTURE_SCALERIGHT }, + { "Texture_InvertX", 'I', RAD_CONTROL|RAD_SHIFT, ID_CURVE_NEGATIVETEXTUREY }, + { "Texture_InvertY", 'I', RAD_SHIFT, ID_CURVE_NEGATIVETEXTUREX }, + { "Texture_ToggleLock", 'T', RAD_SHIFT, ID_TOGGLE_LOCK }, + + { "Texture_ShowAllTextures", 'A', RAD_CONTROL, ID_TEXTURES_SHOWALL }, + + { "Edit_Copy", 'C', RAD_CONTROL, ID_EDIT_COPYBRUSH }, + { "Edit_Paste", 'V', RAD_CONTROL, ID_EDIT_PASTEBRUSH }, + { "Edit_Undo", 'Z', RAD_CONTROL, ID_EDIT_UNDO }, + { "Edit_Redo", 'Y', RAD_CONTROL, ID_EDIT_REDO }, + + { "Camera_Forward", VK_UP, 0, ID_CAMERA_FORWARD }, + { "Camera_Back", VK_DOWN, 0, ID_CAMERA_BACK }, + { "Camera_Left", VK_LEFT, 0, ID_CAMERA_LEFT }, + { "Camera_Right", VK_RIGHT, 0, ID_CAMERA_RIGHT }, + { "Camera_Up", 'D', 0, ID_CAMERA_UP }, + { "Camera_Down", 'C', 0, ID_CAMERA_DOWN }, + { "Camera_AngleUp", 'A', 0, ID_CAMERA_ANGLEUP }, + { "Camera_AngleDown", 'Z', 0, ID_CAMERA_ANGLEDOWN }, + { "Camera_StrafeRight", VK_PERIOD, 0, ID_CAMERA_STRAFERIGHT }, + { "Camera_StrafeLeft", VK_COMMA, 0, ID_CAMERA_STRAFELEFT }, + { "Camera_UpFloor", VK_PRIOR, 0, ID_VIEW_UPFLOOR }, + { "Camera_DownFloor", VK_NEXT, 0, ID_VIEW_DOWNFLOOR }, + { "Camera_CenterView", VK_END, 0, ID_VIEW_CENTER }, + + { "Grid_ZoomOut", VK_INSERT, 0, ID_VIEW_ZOOMOUT }, + { "FileSaveCopy", 'C', RAD_CONTROL|RAD_ALT|RAD_SHIFT, D3XP_ID_FILE_SAVE_COPY }, + { "ShowHideModels", 'M', RAD_CONTROL, D3XP_ID_SHOW_MODELS }, + { "NextView", VK_HOME, 0, ID_VIEW_NEXTVIEW }, + { "Grid_ZoomIn", VK_DELETE, 0, ID_VIEW_ZOOMIN }, + + { "Grid_SetPoint5", '4', RAD_SHIFT, ID_GRID_POINT5 }, + { "Grid_SetPoint25", '3', RAD_SHIFT, ID_GRID_POINT25 }, + { "Grid_SetPoint125", '2', RAD_SHIFT, ID_GRID_POINT125 }, + //{ "Grid_SetPoint0625", '1', RAD_SHIFT, ID_GRID_POINT0625 }, + { "Grid_Set1", '1', 0, ID_GRID_1 }, + { "Grid_Set2", '2', 0, ID_GRID_2 }, + { "Grid_Set4", '3', 0, ID_GRID_4 }, + { "Grid_Set8", '4', 0, ID_GRID_8 }, + { "Grid_Set16", '5', 0, ID_GRID_16 }, + { "Grid_Set32", '6', 0, ID_GRID_32 }, + { "Grid_Set64", '7', 0, ID_GRID_64 }, + { "Grid_Down", 219, 0, ID_GRID_PREV }, + { "Grid_Up", 221, 0, ID_GRID_NEXT }, + + { "Grid_Toggle", '0', 0, ID_GRID_TOGGLE }, + { "Grid_ToggleSizePaint", 'Q', RAD_PRESS, ID_SELECTION_TOGGLESIZEPAINT }, + + { "Grid_PrecisionCursorMode",VK_F11, 0 , ID_PRECISION_CURSOR_CYCLE}, + + { "Grid_NextView", VK_TAB, RAD_CONTROL, ID_VIEW_NEXTVIEW }, + { "Grid_ToggleCrosshairs", 'X', RAD_SHIFT, ID_VIEW_CROSSHAIR }, + + { "Grid_ZZoomOut", VK_INSERT, RAD_CONTROL, ID_VIEW_ZZOOMOUT }, + { "Grid_ZZoomIn", VK_DELETE, RAD_CONTROL, ID_VIEW_ZZOOMIN }, + + { "Brush_Make3Sided", '3', RAD_CONTROL, ID_BRUSH_3SIDED }, + { "Brush_Make4Sided", '4', RAD_CONTROL, ID_BRUSH_4SIDED }, + { "Brush_Make5Sided", '5', RAD_CONTROL, ID_BRUSH_5SIDED }, + { "Brush_Make6Sided", '6', RAD_CONTROL, ID_BRUSH_6SIDED }, + { "Brush_Make7Sided", '7', RAD_CONTROL, ID_BRUSH_7SIDED }, + { "Brush_Make8Sided", '8', RAD_CONTROL, ID_BRUSH_8SIDED }, + { "Brush_Make9Sided", '9', RAD_CONTROL, ID_BRUSH_9SIDED }, + + { "Leak_NextSpot", 'K', RAD_CONTROL|RAD_SHIFT, ID_MISC_NEXTLEAKSPOT }, + { "Leak_PrevSpot", 'L', RAD_CONTROL|RAD_SHIFT, ID_MISC_PREVIOUSLEAKSPOT }, + + { "File_Open", 'O', RAD_CONTROL, ID_FILE_OPEN }, + { "File_Save", 'S', RAD_CONTROL, ID_FILE_SAVE }, + + { "TAB", VK_TAB, 0, ID_PATCH_TAB }, + { "TAB", VK_TAB, RAD_SHIFT, ID_PATCH_TAB }, + + { "Patch_BendMode", 'B', 0, ID_PATCH_BEND }, + { "Patch_FreezeVertices", 'F', 0, ID_CURVE_FREEZE }, + { "Patch_UnFreezeVertices", 'F', RAD_CONTROL, ID_CURVE_UNFREEZE }, + { "Patch_UnFreezeAllVertices",'F', RAD_CONTROL|RAD_SHIFT, ID_CURVE_UNFREEZEALL }, + { "Patch_Thicken", 'T', RAD_CONTROL, ID_CURVE_THICKEN }, + { "Patch_ClearOverlays", 'Y', RAD_SHIFT, ID_CURVE_OVERLAY_CLEAR }, + { "Patch_MakeOverlay", 'Y', 0, ID_CURVE_OVERLAY_SET }, + { "Patch_CycleCapTexturing", 'P', RAD_CONTROL|RAD_SHIFT, ID_CURVE_CYCLECAP }, + { "Patch_CycleCapTexturingAlt",'P', RAD_SHIFT, ID_CURVE_CYCLECAPALT }, + { "Patch_InvertCurve", 'I', RAD_CONTROL, ID_CURVE_NEGATIVE }, + { "Patch_IncPatchColumn", VK_ADD, RAD_CONTROL|RAD_SHIFT, ID_CURVE_INSERTCOLUMN }, + { "Patch_IncPatchRow", VK_ADD, RAD_CONTROL, ID_CURVE_INSERTROW }, + { "Patch_DecPatchColumn", VK_SUBTRACT, RAD_CONTROL|RAD_SHIFT, ID_CURVE_DELETECOLUMN }, + { "Patch_DecPatchRow", VK_SUBTRACT, RAD_CONTROL, ID_CURVE_DELETEROW }, + { "Patch_RedisperseRows", 'E', RAD_CONTROL, ID_CURVE_REDISPERSE_ROWS }, + { "Patch_RedisperseCols", 'E', RAD_CONTROL|RAD_SHIFT, ID_CURVE_REDISPERSE_COLS }, + { "Patch_Naturalize", 'N', RAD_CONTROL, ID_PATCH_NATURALIZE }, + { "Patch_SnapToGrid", 'G', RAD_CONTROL, ID_SELECT_SNAPTOGRID }, + { "Patch_CapCurrentCurve", 'C', RAD_SHIFT, ID_CURVE_CAP }, + + { "Clipper_Toggle", 'X', 0, ID_VIEW_CLIPPER }, + { "Clipper_ClipSelected", VK_RETURN, 0, ID_CLIP_SELECTED }, + { "Clipper_SplitSelected", VK_RETURN, RAD_SHIFT, ID_SPLIT_SELECTED }, + { "Clipper_FlipClip", VK_RETURN, RAD_CONTROL, ID_FLIP_CLIP }, + + { "CameraClip_ZoomOut", 219, RAD_CONTROL, ID_VIEW_CUBEOUT }, + { "CameraClip_ZoomIn", 221, RAD_CONTROL, ID_VIEW_CUBEIN }, + { "CameraClip_Toggle", 220, RAD_CONTROL, ID_VIEW_CUBICCLIPPING }, + + { "ViewTab_EntityInfo", 'N', 0, ID_VIEW_ENTITY }, + { "ViewTab_Console", 'O', 0, ID_VIEW_CONSOLE }, + { "ViewTab_Textures", 'T', 0, ID_VIEW_TEXTURE }, + { "ViewTab_MediaBrowser", 'M', 0, ID_VIEW_MEDIABROWSER }, + + { "Window_SurfaceInspector",'S', 0, ID_TEXTURES_INSPECTOR }, + { "Window_PatchInspector", 'S', RAD_SHIFT, ID_PATCH_INSPECTOR }, + { "Window_EntityList", 'I', 0, ID_EDIT_ENTITYINFO }, + { "Window_Preferences", 'P', 0, ID_PREFS }, + { "Window_ToggleCamera", 'C', RAD_CONTROL|RAD_SHIFT, ID_TOGGLECAMERA }, + { "Window_ToggleView", 'V', RAD_CONTROL|RAD_SHIFT, ID_TOGGLEVIEW }, + { "Window_ToggleZ", 'Z', RAD_CONTROL|RAD_SHIFT, ID_TOGGLEZ }, + { "Window_LightEditor", 'J', 0, ID_PROJECTED_LIGHT }, + { "Window_EntityColor", 'K', 0, ID_MISC_SELECTENTITYCOLOR }, + + { "Selection_DragEdges", 'E', 0, ID_SELECTION_DRAGEDGES }, + { "Selection_DragVertices", 'V', 0, ID_SELECTION_DRAGVERTECIES }, + { "Selection_Clone", VK_SPACE, 0, ID_SELECTION_CLONE }, + { "Selection_Delete", VK_BACK, 0, ID_SELECTION_DELETE }, + { "Selection_UnSelect", VK_ESCAPE, 0, ID_SELECTION_DESELECT }, + { "Selection_Invert", 'I' , 0 , ID_SELECTION_INVERT }, + { "Selection_ToggleMoveOnly",'W', 0, ID_SELECTION_MOVEONLY }, + + { "Selection_MoveDown", VK_SUBTRACT, 0, ID_SELECTION_MOVEDOWN }, + { "Selection_MoveUp", VK_ADD, 0, ID_SELECTION_MOVEUP }, + { "Selection_DumpBrush", 'D', RAD_SHIFT, ID_SELECTION_PRINT }, + { "Selection_NudgeLeft", VK_LEFT, RAD_ALT, ID_SELECTION_SELECT_NUDGELEFT }, + { "Selection_NudgeRight", VK_RIGHT, RAD_ALT, ID_SELECTION_SELECT_NUDGERIGHT }, + { "Selection_NudgeUp", VK_UP, RAD_ALT, ID_SELECTION_SELECT_NUDGEUP }, + { "Selection_NudgeDown", VK_DOWN, RAD_ALT, ID_SELECTION_SELECT_NUDGEDOWN }, + { "Selection_Combine", 'K', RAD_SHIFT, ID_SELECTION_COMBINE }, + { "Selection_Connect", 'K', RAD_CONTROL, ID_SELECTION_CONNECT }, + { "Selection_Ungroup", 'G', RAD_SHIFT, ID_SELECTION_UNGROUPENTITY }, + { "Selection_CSGMerge", 'M', RAD_SHIFT, ID_SELECTION_CSGMERGE }, + + { "Selection_CenterOrigin", 'O', RAD_SHIFT, ID_SELECTION_CENTER_ORIGIN }, + { "Selection_SelectCompleteEntity", 'E' , RAD_CONTROL|RAD_ALT|RAD_SHIFT , ID_SELECT_COMPLETE_ENTITY }, + { "Selection_SelectAllOfType", 'A', RAD_SHIFT, ID_SELECT_ALL }, + + { "Show_ToggleLights", '0' , RAD_ALT , ID_VIEW_SHOWLIGHTS }, + { "Show_TogglePatches", 'P', RAD_CONTROL, ID_VIEW_SHOWCURVES }, + { "Show_ToggleClip", 'L', RAD_CONTROL, ID_VIEW_SHOWCLIP }, + + { "Show_HideSelected", 'H', 0, ID_VIEW_HIDESHOW_HIDESELECTED }, + { "Show_ShowHidden", 'H', RAD_SHIFT, ID_VIEW_HIDESHOW_SHOWHIDDEN }, + { "Show_HideNotSelected", 'H', RAD_CONTROL|RAD_SHIFT, ID_VIEW_HIDESHOW_HIDENOTSELECTED }, + + { "Render_ToggleSound", VK_F9, 0, ID_VIEW_RENDERSOUND }, + { "Render_ToggleSelections", VK_F8, 0, ID_VIEW_RENDERSELECTION }, + { "Render_RebuildData", VK_F7, 0, ID_VIEW_REBUILDRENDERDATA }, + { "Render_ToggleAnimation", VK_F6, 0, ID_VIEW_MATERIALANIMATION}, + { "Render_ToggleEntityOutlines", VK_F5, 0, ID_VIEW_RENDERENTITYOUTLINES }, + { "Render_ToggleRealtimeBuild", VK_F4, 0, ID_VIEW_REALTIMEREBUILD }, + { "Render_Toggle", VK_F3, 0, ID_VIEW_RENDERMODE }, + + { "Find_Textures", 'F', RAD_SHIFT, ID_TEXTURE_REPLACEALL }, + { "Find_Entity", VK_F3, RAD_CONTROL, ID_MISC_FINDORREPLACEENTITY}, + { "Find_NextEntity", VK_F3,RAD_SHIFT, ID_MISC_FINDNEXTENT}, + + { "_ShowDOOM", VK_F2, 0, ID_SHOW_DOOM }, + + { "Rotate_MouseRotate", 'R', 0, ID_SELECT_MOUSEROTATE }, + { "Rotate_ToggleFlatRotation", 'R', RAD_CONTROL, ID_VIEW_CAMERAUPDATE }, + { "Rotate_CycleRotationAxis", 'R', RAD_SHIFT, ID_TOGGLE_ROTATELOCK }, + + { "_AutoCaulk", 'A', RAD_CONTROL|RAD_SHIFT, ID_AUTOCAULK }, // ctrl-shift-a, since SHIFT-A is already taken +}; + +int g_nCommandCount = sizeof(g_Commands) / sizeof(SCommandInfo); + +SKeyInfo g_Keys[] = { + { "Space", VK_SPACE }, + { "Backspace", VK_BACK }, + { "Escape", VK_ESCAPE }, + { "End", VK_END }, + { "Insert", VK_INSERT }, + { "Delete", VK_DELETE }, + { "PageUp", VK_PRIOR }, + { "PageDown", VK_NEXT }, + { "Up", VK_UP }, + { "Down", VK_DOWN }, + { "Left", VK_LEFT }, + { "Right", VK_RIGHT }, + { "F1", VK_F1 }, + { "F2", VK_F2 }, + { "F3", VK_F3 }, + { "F4", VK_F4 }, + { "F5", VK_F5 }, + { "F6", VK_F6 }, + { "F7", VK_F7 }, + { "F8", VK_F8 }, + { "F9", VK_F9 }, + { "F10", VK_F10 }, + { "F11", VK_F11 }, + { "F12", VK_F12 }, + { "Tab", VK_TAB }, + { "Return", VK_RETURN }, + { "Comma", VK_COMMA }, + { "Period", VK_PERIOD }, + { "Plus", VK_ADD }, + { "Multiply", VK_MULTIPLY }, + { "Subtract", VK_SUBTRACT }, + { "NumPad0", VK_NUMPAD0 }, + { "NumPad1", VK_NUMPAD1 }, + { "NumPad2", VK_NUMPAD2 }, + { "NumPad3", VK_NUMPAD3 }, + { "NumPad4", VK_NUMPAD4 }, + { "NumPad5", VK_NUMPAD5 }, + { "NumPad6", VK_NUMPAD6 }, + { "NumPad7", VK_NUMPAD7 }, + { "NumPad8", VK_NUMPAD8 }, + { "NumPad9", VK_NUMPAD9 }, + { "[", 219 }, + { "]", 221 }, + { "\\", 220 } +}; + +int g_nKeyCount = sizeof(g_Keys) / sizeof(SKeyInfo); + +const int CMD_TEXTUREWAD_END = CMD_TEXTUREWAD + 127; +const int CMD_BSPCOMMAND_END = CMD_BSPCOMMAND + 127; +const int IDMRU_END = IDMRU + 9; + +const int g_msgBSPDone = RegisterWindowMessage(DMAP_DONE); +const int g_msgBSPStatus = RegisterWindowMessage(DMAP_MSGID); + +IMPLEMENT_DYNAMIC(CMainFrame, CFrameWnd) +BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) +//{{AFX_MSG_MAP(CMainFrame) + ON_WM_PARENTNOTIFY() + ON_WM_CREATE() + ON_WM_TIMER() + ON_WM_DESTROY() + ON_WM_CLOSE() + ON_WM_KEYDOWN() + ON_WM_SIZE() + ON_COMMAND(ID_VIEW_CAMERATOGGLE, ToggleCamera) + ON_COMMAND(ID_FILE_CLOSE, OnFileClose) + ON_COMMAND(ID_FILE_EXIT, OnFileExit) + ON_COMMAND(ID_FILE_LOADPROJECT, OnFileLoadproject) + ON_COMMAND(ID_FILE_NEW, OnFileNew) + ON_COMMAND(ID_FILE_OPEN, OnFileOpen) + ON_COMMAND(ID_FILE_POINTFILE, OnFilePointfile) + ON_COMMAND(ID_FILE_PRINT, OnFilePrint) + ON_COMMAND(ID_FILE_PRINT_PREVIEW, OnFilePrintPreview) + ON_COMMAND(ID_FILE_SAVE, OnFileSave) + ON_COMMAND(ID_FILE_SAVEAS, OnFileSaveas) + ON_COMMAND(D3XP_ID_FILE_SAVE_COPY, OnFileSaveCopy) + ON_COMMAND(D3XP_ID_SHOW_MODELS, OnViewShowModels ) + ON_COMMAND(ID_VIEW_100, OnView100) + ON_COMMAND(ID_VIEW_CENTER, OnViewCenter) + ON_COMMAND(ID_VIEW_CONSOLE, OnViewConsole) + ON_COMMAND(ID_VIEW_DOWNFLOOR, OnViewDownfloor) + ON_COMMAND(ID_VIEW_ENTITY, OnViewEntity) + ON_COMMAND(ID_VIEW_MEDIABROWSER, OnViewMediaBrowser) + ON_COMMAND(ID_VIEW_FRONT, OnViewFront) + ON_COMMAND(ID_VIEW_SHOWBLOCKS, OnViewShowblocks) + ON_COMMAND(ID_VIEW_SHOWCLIP, OnViewShowclip) + ON_COMMAND(ID_VIEW_SHOWTRIGGERS, OnViewShowTriggers) + ON_COMMAND(ID_VIEW_SHOWCOORDINATES, OnViewShowcoordinates) + ON_COMMAND(ID_VIEW_SHOWENT, OnViewShowent) + ON_COMMAND(ID_VIEW_SHOWLIGHTS, OnViewShowlights) + ON_COMMAND(ID_VIEW_SHOWNAMES, OnViewShownames) + ON_COMMAND(ID_VIEW_SHOWPATH, OnViewShowpath) + ON_COMMAND(ID_VIEW_SHOWCOMBATNODES, OnViewShowCombatNodes) + ON_COMMAND(ID_VIEW_SHOWWATER, OnViewShowwater) + ON_COMMAND(ID_VIEW_SHOWWORLD, OnViewShowworld) + ON_COMMAND(ID_VIEW_TEXTURE, OnViewTexture) + ON_COMMAND(ID_VIEW_UPFLOOR, OnViewUpfloor) + ON_COMMAND(ID_VIEW_XY, OnViewXy) + ON_COMMAND(ID_VIEW_Z100, OnViewZ100) + ON_COMMAND(ID_VIEW_ZOOMIN, OnViewZoomin) + ON_COMMAND(ID_VIEW_ZOOMOUT, OnViewZoomout) + ON_COMMAND(ID_VIEW_ZZOOMIN, OnViewZzoomin) + ON_COMMAND(ID_VIEW_ZZOOMOUT, OnViewZzoomout) + ON_COMMAND(ID_VIEW_SIDE, OnViewSide) + ON_COMMAND(ID_TEXTURES_SHOWINUSE, OnTexturesShowinuse) + ON_COMMAND(ID_TEXTURES_INSPECTOR, OnTexturesInspector) + ON_COMMAND(ID_MISC_FINDBRUSH, OnMiscFindbrush) + ON_COMMAND(ID_MISC_GAMMA, OnMiscGamma) + ON_COMMAND(ID_MISC_NEXTLEAKSPOT, OnMiscNextleakspot) + ON_COMMAND(ID_MISC_PREVIOUSLEAKSPOT, OnMiscPreviousleakspot) + ON_COMMAND(ID_MISC_PRINTXY, OnMiscPrintxy) + ON_COMMAND(ID_MISC_SELECTENTITYCOLOR, OnMiscSelectentitycolor) + ON_COMMAND(ID_MISC_FINDORREPLACEENTITY, OnMiscFindOrReplaceEntity) + ON_COMMAND(ID_MISC_FINDNEXTENT, OnMiscFindNextEntity) + ON_COMMAND(ID_MISC_SETVIEWPOS, OnMiscSetViewPos) + ON_COMMAND(ID_TEXTUREBK, OnTexturebk) + ON_COMMAND(ID_COLORS_MAJOR, OnColorsMajor) + ON_COMMAND(ID_COLORS_MINOR, OnColorsMinor) + ON_COMMAND(ID_COLORS_XYBK, OnColorsXybk) + ON_COMMAND(ID_BRUSH_3SIDED, OnBrush3sided) + ON_COMMAND(ID_BRUSH_4SIDED, OnBrush4sided) + ON_COMMAND(ID_BRUSH_5SIDED, OnBrush5sided) + ON_COMMAND(ID_BRUSH_6SIDED, OnBrush6sided) + ON_COMMAND(ID_BRUSH_7SIDED, OnBrush7sided) + ON_COMMAND(ID_BRUSH_8SIDED, OnBrush8sided) + ON_COMMAND(ID_BRUSH_9SIDED, OnBrush9sided) + ON_COMMAND(ID_BRUSH_ARBITRARYSIDED, OnBrushArbitrarysided) + ON_COMMAND(ID_BRUSH_FLIPX, OnBrushFlipx) + ON_COMMAND(ID_BRUSH_FLIPY, OnBrushFlipy) + ON_COMMAND(ID_BRUSH_FLIPZ, OnBrushFlipz) + ON_COMMAND(ID_BRUSH_ROTATEX, OnBrushRotatex) + ON_COMMAND(ID_BRUSH_ROTATEY, OnBrushRotatey) + ON_COMMAND(ID_BRUSH_ROTATEZ, OnBrushRotatez) + ON_COMMAND(ID_REGION_OFF, OnRegionOff) + ON_COMMAND(ID_REGION_SETBRUSH, OnRegionSetbrush) + ON_COMMAND(ID_REGION_SETSELECTION, OnRegionSetselection) + ON_COMMAND(ID_REGION_SETTALLBRUSH, OnRegionSettallbrush) + ON_COMMAND(ID_REGION_SETXY, OnRegionSetxy) + ON_COMMAND(ID_SELECTION_ARBITRARYROTATION, OnSelectionArbitraryrotation) + ON_COMMAND(ID_SELECTION_CLONE, OnSelectionClone) + ON_COMMAND(ID_SELECTION_CONNECT, OnSelectionConnect) + ON_COMMAND(ID_SELECTION_CSGSUBTRACT, OnSelectionCsgsubtract) + ON_COMMAND(ID_SELECTION_CSGMERGE, OnSelectionCsgmerge) + ON_COMMAND(ID_SELECTION_DELETE, OnSelectionDelete) + ON_COMMAND(ID_SELECTION_DESELECT, OnSelectionDeselect) + ON_COMMAND(ID_SELECTION_DRAGEDGES, OnSelectionDragedges) + ON_COMMAND(ID_SELECTION_DRAGVERTECIES, OnSelectionDragvertecies) + ON_COMMAND(ID_SELECTION_CENTER_ORIGIN, OnSelectionCenterOrigin) + ON_COMMAND(ID_SELECTION_MAKEHOLLOW, OnSelectionMakehollow) + ON_COMMAND(ID_SELECTION_SELECTCOMPLETETALL, OnSelectionSelectcompletetall) + ON_COMMAND(ID_SELECTION_SELECTINSIDE, OnSelectionSelectinside) + ON_COMMAND(ID_SELECTION_SELECTPARTIALTALL, OnSelectionSelectpartialtall) + ON_COMMAND(ID_SELECTION_SELECTTOUCHING, OnSelectionSelecttouching) + ON_COMMAND(ID_SELECTION_UNGROUPENTITY, OnSelectionUngroupentity) + ON_COMMAND(ID_TEXTURES_POPUP, OnTexturesPopup) + ON_COMMAND(ID_SPLINES_POPUP, OnSplinesPopup) + ON_COMMAND(ID_SPLINES_EDITPOINTS, OnSplinesEditPoints) + ON_COMMAND(ID_SPLINES_ADDPOINTS, OnSplinesAddPoints) + ON_COMMAND(ID_SPLINES_INSERTPOINTS, OnSplinesInsertPoint) + ON_COMMAND(ID_SPLINES_DELETEPOINTS, OnSplinesDeletePoint) + ON_COMMAND(ID_POPUP_SELECTION, OnPopupSelection) + ON_COMMAND(ID_VIEW_CHANGE, OnViewChange) + ON_COMMAND(ID_VIEW_CAMERAUPDATE, OnViewCameraupdate) + ON_WM_SIZING() + ON_COMMAND(ID_HELP_ABOUT, OnHelpAbout) + ON_COMMAND(ID_VIEW_CLIPPER, OnViewClipper) + ON_COMMAND(ID_CAMERA_ANGLEDOWN, OnCameraAngledown) + ON_COMMAND(ID_CAMERA_ANGLEUP, OnCameraAngleup) + ON_COMMAND(ID_CAMERA_BACK, OnCameraBack) + ON_COMMAND(ID_CAMERA_DOWN, OnCameraDown) + ON_COMMAND(ID_CAMERA_FORWARD, OnCameraForward) + ON_COMMAND(ID_CAMERA_LEFT, OnCameraLeft) + ON_COMMAND(ID_CAMERA_RIGHT, OnCameraRight) + ON_COMMAND(ID_CAMERA_STRAFELEFT, OnCameraStrafeleft) + ON_COMMAND(ID_CAMERA_STRAFERIGHT, OnCameraStraferight) + ON_COMMAND(ID_CAMERA_UP, OnCameraUp) + ON_COMMAND(ID_GRID_TOGGLE, OnGridToggle) + ON_COMMAND(ID_PREFS, OnPrefs) + ON_COMMAND(ID_TOGGLECAMERA, OnTogglecamera) + ON_COMMAND(ID_TOGGLEVIEW, OnToggleview) + ON_COMMAND(ID_TOGGLEZ, OnTogglez) + ON_COMMAND(ID_TOGGLE_LOCK, OnToggleLock) + ON_COMMAND(ID_EDIT_MAPINFO, OnEditMapinfo) + ON_COMMAND(ID_EDIT_ENTITYINFO, OnEditEntityinfo) + ON_COMMAND(ID_VIEW_NEXTVIEW, OnViewNextview) + ON_COMMAND(ID_HELP_COMMANDLIST, OnHelpCommandlist) + ON_COMMAND(ID_FILE_NEWPROJECT, OnFileNewproject) + ON_COMMAND(ID_FLIP_CLIP, OnFlipClip) + ON_COMMAND(ID_CLIP_SELECTED, OnClipSelected) + ON_COMMAND(ID_SPLIT_SELECTED, OnSplitSelected) + ON_COMMAND(ID_TOGGLEVIEW_XZ, OnToggleviewXz) + ON_COMMAND(ID_TOGGLEVIEW_YZ, OnToggleviewYz) + ON_COMMAND(ID_COLORS_BRUSH, OnColorsBrush) + ON_COMMAND(ID_COLORS_CLIPPER, OnColorsClipper) + ON_COMMAND(ID_COLORS_GRIDTEXT, OnColorsGridtext) + ON_COMMAND(ID_COLORS_SELECTEDBRUSH, OnColorsSelectedbrush) + ON_COMMAND(ID_COLORS_GRIDBLOCK, OnColorsGridblock) + ON_COMMAND(ID_COLORS_VIEWNAME, OnColorsViewname) + ON_COMMAND(ID_COLOR_SETORIGINAL, OnColorSetoriginal) + ON_COMMAND(ID_COLOR_SETQER, OnColorSetqer) + ON_COMMAND(ID_COLOR_SUPERMAL, OnColorSetSuperMal) + ON_COMMAND(ID_THEMES_MAX , OnColorSetMax ) + ON_COMMAND(ID_COLOR_SETBLACK, OnColorSetblack) + ON_COMMAND(ID_SNAPTOGRID, OnSnaptogrid) + ON_COMMAND(ID_SELECT_SCALE, OnSelectScale) + ON_COMMAND(ID_SELECT_MOUSEROTATE, OnSelectMouserotate) + ON_COMMAND(ID_EDIT_COPYBRUSH, OnEditCopybrush) + ON_COMMAND(ID_EDIT_PASTEBRUSH, OnEditPastebrush) + ON_COMMAND(ID_EDIT_UNDO, OnEditUndo) + ON_COMMAND(ID_EDIT_REDO, OnEditRedo) + ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, OnUpdateEditUndo) + ON_UPDATE_COMMAND_UI(ID_EDIT_REDO, OnUpdateEditRedo) + ON_COMMAND(ID_SELECTION_INVERT, OnSelectionInvert) + ON_COMMAND(ID_SELECTION_TEXTURE_DEC, OnSelectionTextureDec) + ON_COMMAND(ID_SELECTION_TEXTURE_FIT, OnSelectionTextureFit) + ON_COMMAND(ID_SELECTION_TEXTURE_INC, OnSelectionTextureInc) + ON_COMMAND(ID_SELECTION_TEXTURE_ROTATECLOCK, OnSelectionTextureRotateclock) + ON_COMMAND(ID_SELECTION_TEXTURE_ROTATECOUNTER, OnSelectionTextureRotatecounter) + ON_COMMAND(ID_SELECTION_TEXTURE_SCALEDOWN, OnSelectionTextureScaledown) + ON_COMMAND(ID_SELECTION_TEXTURE_SCALEUP, OnSelectionTextureScaleup) + ON_COMMAND(ID_SELECTION_TEXTURE_SHIFTDOWN, OnSelectionTextureShiftdown) + ON_COMMAND(ID_SELECTION_TEXTURE_SHIFTLEFT, OnSelectionTextureShiftleft) + ON_COMMAND(ID_SELECTION_TEXTURE_SHIFTRIGHT, OnSelectionTextureShiftright) + ON_COMMAND(ID_SELECTION_TEXTURE_SHIFTUP, OnSelectionTextureShiftup) + ON_COMMAND(ID_GRID_NEXT, OnGridNext) + ON_COMMAND(ID_GRID_PREV, OnGridPrev) + ON_COMMAND(ID_SELECTION_TEXTURE_SCALELEFT, OnSelectionTextureScaleLeft) + ON_COMMAND(ID_SELECTION_TEXTURE_SCALERIGHT, OnSelectionTextureScaleRight) + ON_COMMAND(ID_TEXTURE_REPLACEALL, OnTextureReplaceall) + ON_COMMAND(ID_SCALELOCKX, OnScalelockx) + ON_COMMAND(ID_SCALELOCKY, OnScalelocky) + ON_COMMAND(ID_SCALELOCKZ, OnScalelockz) + ON_COMMAND(ID_SELECT_MOUSESCALE, OnSelectMousescale) + ON_COMMAND(ID_VIEW_CUBICCLIPPING, OnViewCubicclipping) + ON_COMMAND(ID_FILE_IMPORT, OnFileImport) + ON_COMMAND(ID_FILE_PROJECTSETTINGS, OnFileProjectsettings) + ON_UPDATE_COMMAND_UI(ID_FILE_IMPORT, OnUpdateFileImport) + ON_COMMAND(ID_VIEW_CUBEIN, OnViewCubein) + ON_COMMAND(ID_VIEW_CUBEOUT, OnViewCubeout) + ON_COMMAND(ID_FILE_SAVEREGION, OnFileSaveregion) + ON_UPDATE_COMMAND_UI(ID_FILE_SAVEREGION, OnUpdateFileSaveregion) + ON_COMMAND(ID_SELECTION_MOVEDOWN, OnSelectionMovedown) + ON_COMMAND(ID_SELECTION_MOVEUP, OnSelectionMoveup) + ON_COMMAND(ID_TOOLBAR_MAIN, OnToolbarMain) + ON_COMMAND(ID_TOOLBAR_TEXTURE, OnToolbarTexture) + ON_COMMAND(ID_SELECTION_PRINT, OnSelectionPrint) + ON_COMMAND(ID_SELECTION_TOGGLESIZEPAINT, OnSelectionTogglesizepaint) + ON_COMMAND(ID_BRUSH_MAKECONE, OnBrushMakecone) + ON_COMMAND(ID_TEXTURES_LOAD, OnTexturesLoad) + ON_COMMAND(ID_TOGGLE_ROTATELOCK, OnToggleRotatelock) + ON_COMMAND(ID_CURVE_BEVEL, OnCurveBevel) + ON_COMMAND(ID_CURVE_INCREASE_VERT, OnCurveIncreaseVert) + ON_COMMAND(ID_CURVE_DECREASE_VERT, OnCurveDecreaseVert) + ON_COMMAND(ID_CURVE_INCREASE_HORZ, OnCurveIncreaseHorz) + ON_COMMAND(ID_CURVE_DECREASE_HORZ, OnCurveDecreaseHorz) + ON_COMMAND(ID_CURVE_CYLINDER, OnCurveCylinder) + ON_COMMAND(ID_CURVE_EIGHTHSPHERE, OnCurveEighthsphere) + ON_COMMAND(ID_CURVE_ENDCAP, OnCurveEndcap) + ON_COMMAND(ID_CURVE_HEMISPHERE, OnCurveHemisphere) + ON_COMMAND(ID_CURVE_INVERTCURVE, OnCurveInvertcurve) + ON_COMMAND(ID_CURVE_QUARTER, OnCurveQuarter) + ON_COMMAND(ID_CURVE_SPHERE, OnCurveSphere) + ON_COMMAND(ID_FILE_IMPORTMAP, OnFileImportmap) + ON_COMMAND(ID_FILE_EXPORTMAP, OnFileExportmap) + ON_COMMAND(ID_EDIT_LOADPREFAB, OnEditLoadprefab) + ON_COMMAND(ID_VIEW_SHOWCURVES, OnViewShowcurves) + ON_COMMAND(ID_SELECTION_SELECT_NUDGEDOWN, OnSelectionSelectNudgedown) + ON_COMMAND(ID_SELECTION_SELECT_NUDGELEFT, OnSelectionSelectNudgeleft) + ON_COMMAND(ID_SELECTION_SELECT_NUDGERIGHT, OnSelectionSelectNudgeright) + ON_COMMAND(ID_SELECTION_SELECT_NUDGEUP, OnSelectionSelectNudgeup) + ON_WM_SYSKEYDOWN() + ON_COMMAND(ID_TEXTURES_LOADLIST, OnTexturesLoadlist) + ON_COMMAND(ID_DYNAMIC_LIGHTING, OnDynamicLighting) + ON_COMMAND(ID_CURVE_SIMPLEPATCHMESH, OnCurveSimplepatchmesh) + ON_COMMAND(ID_PATCH_SHOWBOUNDINGBOX, OnPatchToggleBox) + ON_COMMAND(ID_PATCH_WIREFRAME, OnPatchWireframe) + ON_COMMAND(ID_CURVE_PATCHCONE, OnCurvePatchcone) + ON_COMMAND(ID_CURVE_PATCHTUBE, OnCurvePatchtube) + ON_COMMAND(ID_PATCH_WELD, OnPatchWeld) + ON_COMMAND(ID_CURVE_PATCHBEVEL, OnCurvePatchbevel) + ON_COMMAND(ID_CURVE_PATCHENDCAP, OnCurvePatchendcap) + ON_COMMAND(ID_CURVE_PATCHINVERTEDBEVEL, OnCurvePatchinvertedbevel) + ON_COMMAND(ID_CURVE_PATCHINVERTEDENDCAP, OnCurvePatchinvertedendcap) + ON_COMMAND(ID_PATCH_DRILLDOWN, OnPatchDrilldown) + ON_COMMAND(ID_CURVE_INSERTCOLUMN, OnCurveInsertcolumn) + ON_COMMAND(ID_CURVE_INSERTROW, OnCurveInsertrow) + ON_COMMAND(ID_CURVE_DELETECOLUMN, OnCurveDeletecolumn) + ON_COMMAND(ID_CURVE_DELETEROW, OnCurveDeleterow) + ON_COMMAND(ID_CURVE_INSERT_ADDCOLUMN, OnCurveInsertAddcolumn) + ON_COMMAND(ID_CURVE_INSERT_ADDROW, OnCurveInsertAddrow) + ON_COMMAND(ID_CURVE_INSERT_INSERTCOLUMN, OnCurveInsertInsertcolumn) + ON_COMMAND(ID_CURVE_INSERT_INSERTROW, OnCurveInsertInsertrow) + ON_COMMAND(ID_CURVE_NEGATIVE, OnCurveNegative) + ON_COMMAND(ID_CURVE_NEGATIVETEXTUREX, OnCurveNegativeTextureX) + ON_COMMAND(ID_CURVE_NEGATIVETEXTUREY, OnCurveNegativeTextureY) + ON_COMMAND(ID_CURVE_DELETE_FIRSTCOLUMN, OnCurveDeleteFirstcolumn) + ON_COMMAND(ID_CURVE_DELETE_FIRSTROW, OnCurveDeleteFirstrow) + ON_COMMAND(ID_CURVE_DELETE_LASTCOLUMN, OnCurveDeleteLastcolumn) + ON_COMMAND(ID_CURVE_DELETE_LASTROW, OnCurveDeleteLastrow) + ON_COMMAND(ID_PATCH_BEND, OnPatchBend) + ON_COMMAND(ID_PATCH_INSDEL, OnPatchInsdel) + ON_COMMAND(ID_PATCH_ENTER, OnPatchEnter) + ON_COMMAND(ID_PATCH_TAB, OnPatchTab) + ON_COMMAND(ID_CURVE_PATCHDENSETUBE, OnCurvePatchdensetube) + ON_COMMAND(ID_CURVE_PATCHVERYDENSETUBE, OnCurvePatchverydensetube) + ON_COMMAND(ID_CURVE_CAP, OnCurveCap) + ON_COMMAND(ID_CURVE_CAP_INVERTEDBEVEL, OnCurveCapInvertedbevel) + ON_COMMAND(ID_CURVE_CAP_INVERTEDENDCAP, OnCurveCapInvertedendcap) + ON_COMMAND(ID_CURVE_REDISPERSE_COLS, OnCurveRedisperseCols) + ON_COMMAND(ID_CURVE_REDISPERSE_ROWS, OnCurveRedisperseRows) + ON_COMMAND(ID_PATCH_NATURALIZE, OnPatchNaturalize) + ON_COMMAND(ID_PATCH_NATURALIZEALT, OnPatchNaturalizeAlt) + ON_COMMAND(ID_SELECT_SNAPTOGRID, OnSnapToGrid) + ON_COMMAND(ID_CURVE_PATCHSQUARE, OnCurvePatchsquare) + ON_COMMAND(ID_TEXTURES_TEXTUREWINDOWSCALE_10, OnTexturesTexturewindowscale10) + ON_COMMAND(ID_TEXTURES_TEXTUREWINDOWSCALE_100, OnTexturesTexturewindowscale100) + ON_COMMAND(ID_TEXTURES_TEXTUREWINDOWSCALE_200, OnTexturesTexturewindowscale200) + ON_COMMAND(ID_TEXTURES_TEXTUREWINDOWSCALE_25, OnTexturesTexturewindowscale25) + ON_COMMAND(ID_TEXTURES_TEXTUREWINDOWSCALE_50, OnTexturesTexturewindowscale50) + ON_COMMAND(ID_TEXTURES_FLUSH, OnTexturesFlush) + ON_COMMAND(ID_CURVE_OVERLAY_CLEAR, OnCurveOverlayClear) + ON_COMMAND(ID_CURVE_OVERLAY_SET, OnCurveOverlaySet) + ON_COMMAND(ID_CURVE_THICKEN, OnCurveThicken) + ON_COMMAND(ID_CURVE_CYCLECAP, OnCurveCyclecap) + ON_COMMAND(ID_CURVE_CYCLECAPALT, OnCurveCyclecapAlt) + ON_COMMAND(ID_CURVE_MATRIX_TRANSPOSE, OnCurveMatrixTranspose) + ON_COMMAND(ID_TEXTURES_RELOADSHADERS, OnTexturesReloadshaders) + ON_COMMAND(ID_SHOW_ENTITIES, OnShowEntities) + ON_COMMAND(ID_VIEW_ENTITIESAS_SKINNED, OnViewEntitiesasSkinned) + ON_COMMAND(ID_VIEW_ENTITIESAS_WIREFRAME, OnViewEntitiesasWireframe) + ON_COMMAND(ID_VIEW_SHOWHINT, OnViewShowhint) + ON_UPDATE_COMMAND_UI(ID_TEXTURES_SHOWINUSE, OnUpdateTexturesShowinuse) + ON_COMMAND(ID_TEXTURES_SHOWALL, OnTexturesShowall) + ON_COMMAND(ID_TEXTURES_HIDEALL, OnTexturesHideall) + ON_COMMAND(ID_PATCH_INSPECTOR, OnPatchInspector) + ON_COMMAND(ID_VIEW_OPENGLLIGHTING, OnViewOpengllighting) + ON_COMMAND(ID_SELECT_ALL, OnSelectAll) + ON_COMMAND(ID_VIEW_SHOWCAULK, OnViewShowcaulk) + ON_COMMAND(ID_CURVE_FREEZE, OnCurveFreeze) + ON_COMMAND(ID_CURVE_UNFREEZE, OnCurveUnFreeze) + ON_COMMAND(ID_CURVE_UNFREEZEALL, OnCurveUnFreezeAll) + ON_COMMAND(ID_SELECT_RESELECT, OnSelectReselect) + ON_COMMAND(ID_VIEW_SHOWANGLES, OnViewShowangles) + ON_COMMAND(ID_EDIT_SAVEPREFAB, OnEditSaveprefab) + ON_COMMAND(ID_CURVE_MOREENDCAPSBEVELS_SQUAREBEVEL, OnCurveMoreendcapsbevelsSquarebevel) + ON_COMMAND(ID_CURVE_MOREENDCAPSBEVELS_SQUAREENDCAP, OnCurveMoreendcapsbevelsSquareendcap) + ON_COMMAND(ID_BRUSH_PRIMITIVES_SPHERE, OnBrushPrimitivesSphere) + ON_COMMAND(ID_VIEW_CROSSHAIR, OnViewCrosshair) + ON_COMMAND(ID_VIEW_HIDESHOW_HIDESELECTED, OnViewHideshowHideselected) + ON_COMMAND(ID_VIEW_HIDESHOW_HIDENOTSELECTED, OnViewHideshowHideNotselected) + ON_COMMAND(ID_VIEW_HIDESHOW_SHOWHIDDEN, OnViewHideshowShowhidden) + ON_COMMAND(ID_TEXTURES_SHADERS_SHOW, OnTexturesShadersShow) + ON_COMMAND(ID_TEXTURES_FLUSH_UNUSED, OnTexturesFlushUnused) + ON_COMMAND(ID_PROJECTED_LIGHT, OnProjectedLight) + ON_COMMAND(ID_SHOW_LIGHTTEXTURES, OnShowLighttextures) + ON_COMMAND(ID_SHOW_LIGHTVOLUMES, OnShowLightvolumes) + ON_WM_ACTIVATE() + ON_COMMAND(ID_SPLINES_MODE, OnSplinesMode) + ON_COMMAND(ID_SPLINES_LOAD, OnSplinesLoad) + ON_COMMAND(ID_SPLINES_SAVE, OnSplinesSave) + //ON_COMMAND(ID_SPLINES_EDIT, OnSplinesEdit) + ON_COMMAND(ID_SPLINE_TEST, OnSplineTest) + ON_COMMAND(ID_POPUP_NEWCAMERA_INTERPOLATED, OnPopupNewcameraInterpolated) + ON_COMMAND(ID_POPUP_NEWCAMERA_SPLINE, OnPopupNewcameraSpline) + ON_COMMAND(ID_POPUP_NEWCAMERA_FIXED, OnPopupNewcameraFixed) + ON_COMMAND(ID_SELECTION_MOVEONLY, OnSelectionMoveonly) + ON_COMMAND(ID_SELECT_BRUSHESONLY, OnSelectBrushesOnly) + ON_COMMAND(ID_SELECT_BYBOUNDINGBRUSH, OnSelectByBoundingBrush) + ON_COMMAND(ID_SELECTION_COMBINE, OnSelectionCombine) + ON_COMMAND(ID_PATCH_COMBINE, OnPatchCombine) + ON_COMMAND(ID_SHOW_DOOM, OnShowDoom) + ON_COMMAND(ID_VIEW_RENDERMODE, OnViewRendermode) + ON_COMMAND(ID_VIEW_REBUILDRENDERDATA, OnViewRebuildrenderdata) + ON_COMMAND(ID_VIEW_REALTIMEREBUILD, OnViewRealtimerebuild) + ON_COMMAND(ID_VIEW_RENDERENTITYOUTLINES, OnViewRenderentityoutlines) + ON_COMMAND(ID_VIEW_MATERIALANIMATION, OnViewMaterialanimation) + ON_COMMAND(ID_SELECT_AXIALTEXTURE_BYWIDTH, OnAxialTextureByWidth) + ON_COMMAND(ID_SELECT_AXIALTEXTURE_BYHEIGHT, OnAxialTextureByHeight) + ON_COMMAND(ID_SELECT_AXIALTEXTURE_ARBITRARY, OnAxialTextureArbitrary) + ON_COMMAND(ID_SELECTION_EXPORT_TOOBJ, OnSelectionExportToobj) + ON_COMMAND(ID_SELECTION_EXPORT_TOCM, OnSelectionExportToCM) + ON_COMMAND(ID_VIEW_RENDERSELECTION, OnViewRenderselection) + ON_COMMAND(ID_SELECT_NOMODELS, OnSelectNomodels) + ON_COMMAND(ID_VIEW_SHOW_SHOWVISPORTALS, OnViewShowShowvisportals) + ON_COMMAND(ID_VIEW_SHOW_NODRAW, OnViewShowNoDraw) + ON_COMMAND(ID_VIEW_RENDERSOUND, OnViewRendersound) + ON_COMMAND(ID_SOUND_SHOWSOUNDVOLUMES, OnSoundShowsoundvolumes) + ON_COMMAND(ID_SOUND_SHOWSELECTEDSOUNDVOLUMES, OnSoundShowselectedsoundvolumes) + ON_COMMAND(ID_PATCH_NURBEDITOR, OnNurbEditor) + ON_COMMAND(ID_SELECT_COMPLETE_ENTITY, OnSelectCompleteEntity) + ON_COMMAND(ID_PRECISION_CURSOR_CYCLE , OnPrecisionCursorCycle) + ON_COMMAND(ID_MATERIALS_GENERATEMATERIALSLIST,OnGenerateMaterialsList) + ON_COMMAND(ID_SELECTION_VIEW_WIREFRAMEON, OnSelectionWireFrameOn) + ON_COMMAND(ID_SELECTION_VIEW_WIREFRAMEOFF, OnSelectionWireFrameOff) + ON_COMMAND(ID_SELECTION_VIEW_VISIBLEON, OnSelectionVisibleOn) + ON_COMMAND(ID_SELECTION_VIEW_VISIBLEOFF, OnSelectionVisibleOff) + //}}AFX_MSG_MAP + ON_COMMAND_RANGE(CMD_TEXTUREWAD, CMD_TEXTUREWAD_END, OnTextureWad) + ON_COMMAND_RANGE(CMD_BSPCOMMAND, CMD_BSPCOMMAND_END, OnBspCommand) + ON_COMMAND_RANGE(IDMRU, IDMRU_END, OnMru) + ON_COMMAND_RANGE(ID_VIEW_NEAREST, ID_TEXTURES_FLATSHADE, OnViewNearest) + ON_COMMAND_RANGE(ID_GRID_POINT0625, ID_GRID_64, OnGrid1) +#if _MSC_VER < 1300 + ON_REGISTERED_MESSAGE(g_msgBSPDone, OnBSPDone) + ON_REGISTERED_MESSAGE(g_msgBSPStatus, OnBSPStatus) + ON_MESSAGE(WM_DISPLAYCHANGE, OnDisplayChange) +#endif + ON_COMMAND(ID_AUTOCAULK, OnAutocaulk) + ON_UPDATE_COMMAND_UI(ID_AUTOCAULK, OnUpdateAutocaulk) + ON_COMMAND(ID_SELECT_ALLTARGETS, OnSelectAlltargets) + END_MESSAGE_MAP() +static UINT indicators[] = { + ID_SEPARATOR, // status line indicator + ID_SEPARATOR, // status line indicator + ID_SEPARATOR, // status line indicator + ID_SEPARATOR, // status line indicator + ID_SEPARATOR, // status line indicator + ID_SEPARATOR, // status line indicator +}; + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnDisplayChange(UINT wParam, long lParam) { + int n = wParam; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnBSPStatus(UINT wParam, long lParam) { + // lparam is an atom contain the text + char buff[1024]; + if (::GlobalGetAtomName(static_cast(lParam), buff, sizeof(buff))) { + common->Printf("%s", buff); + ::GlobalDeleteAtom(static_cast(lParam)); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnBSPDone(UINT wParam, long lParam) { + idStr str = cvarSystem->GetCVarString( "radiant_bspdone" ); + if (str.Length()) { + sndPlaySound(str.c_str(), SND_FILENAME | SND_ASYNC); + } +} + +// +// ======================================================================================================================= +// CMainFrame construction/destruction +// ======================================================================================================================= +// +CMainFrame::CMainFrame() { + m_bDoLoop = false; + g_pParentWnd = this; + m_pXYWnd = NULL; + m_pCamWnd = NULL; + m_pZWnd = NULL; + m_pYZWnd = NULL; + m_pXZWnd = NULL; + m_pActiveXY = NULL; + m_bCamPreview = true; + nurbMode = 0; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +CMainFrame::~CMainFrame() { +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void HandlePopup(CWnd *pWindow, unsigned int uId) { + // Get the current position of the mouse + CPoint ptMouse; + GetCursorPos(&ptMouse); + + // Load up a menu that has the options we are looking for in it + CMenu mnuPopup; + VERIFY(mnuPopup.LoadMenu(uId)); + mnuPopup.GetSubMenu(0)->TrackPopupMenu + ( + TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON, + ptMouse.x, + ptMouse.y, + pWindow + ); + mnuPopup.DestroyMenu(); + + // Set focus back to window + pWindow->SetFocus(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnParentNotify(UINT message, LPARAM lParam) { +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::SetButtonMenuStates() { + CMenu *pMenu = GetMenu(); + if (pMenu) { + // + pMenu->CheckMenuItem(ID_VIEW_SHOWNAMES, MF_BYCOMMAND | MF_CHECKED); + pMenu->CheckMenuItem(ID_VIEW_SHOWCOORDINATES, MF_BYCOMMAND | MF_CHECKED); + pMenu->CheckMenuItem(ID_VIEW_SHOWLIGHTS, MF_BYCOMMAND | MF_CHECKED); + pMenu->CheckMenuItem(ID_VIEW_SHOWCOMBATNODES, MF_BYCOMMAND | MF_CHECKED); + pMenu->CheckMenuItem(ID_VIEW_ENTITY, MF_BYCOMMAND | MF_CHECKED); + pMenu->CheckMenuItem(ID_VIEW_SHOWPATH, MF_BYCOMMAND | MF_CHECKED); + pMenu->CheckMenuItem(ID_VIEW_SHOWWATER, MF_BYCOMMAND | MF_CHECKED); + pMenu->CheckMenuItem(ID_VIEW_SHOWWORLD, MF_BYCOMMAND | MF_CHECKED); + pMenu->CheckMenuItem(ID_VIEW_SHOWCLIP, MF_BYCOMMAND | MF_CHECKED); + pMenu->CheckMenuItem(ID_VIEW_SHOWTRIGGERS, MF_BYCOMMAND | MF_CHECKED); + pMenu->CheckMenuItem(ID_VIEW_SHOWHINT, MF_BYCOMMAND | MF_CHECKED); + pMenu->CheckMenuItem(ID_VIEW_SHOWCAULK, MF_BYCOMMAND | MF_CHECKED); + pMenu->CheckMenuItem(ID_VIEW_SHOW_SHOWVISPORTALS, MF_BYCOMMAND | MF_CHECKED); + pMenu->CheckMenuItem(ID_VIEW_SHOW_NODRAW, MF_BYCOMMAND | MF_CHECKED); + pMenu->CheckMenuItem(ID_VIEW_SHOWANGLES, MF_BYCOMMAND | MF_CHECKED); + + if (!g_qeglobals.d_savedinfo.show_names) { + pMenu->CheckMenuItem(ID_VIEW_SHOWNAMES, MF_BYCOMMAND | MF_UNCHECKED); + } + + if (!g_qeglobals.d_savedinfo.show_coordinates) { + pMenu->CheckMenuItem(ID_VIEW_SHOWCOORDINATES, MF_BYCOMMAND | MF_UNCHECKED); + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_LIGHTS) { + pMenu->CheckMenuItem(ID_VIEW_SHOWLIGHTS, MF_BYCOMMAND | MF_UNCHECKED); + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_COMBATNODES) { + pMenu->CheckMenuItem(ID_VIEW_SHOWCOMBATNODES, MF_BYCOMMAND | MF_UNCHECKED); + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_ENT) { + pMenu->CheckMenuItem(ID_VIEW_ENTITY, MF_BYCOMMAND | MF_UNCHECKED); + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_PATHS) { + pMenu->CheckMenuItem(ID_VIEW_SHOWPATH, MF_BYCOMMAND | MF_UNCHECKED); + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_DYNAMICS) { + pMenu->CheckMenuItem(ID_VIEW_SHOWWATER, MF_BYCOMMAND | MF_UNCHECKED); + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_WORLD) { + pMenu->CheckMenuItem(ID_VIEW_SHOWWORLD, MF_BYCOMMAND | MF_UNCHECKED); + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_CLIP) { + pMenu->CheckMenuItem(ID_VIEW_SHOWCLIP, MF_BYCOMMAND | MF_UNCHECKED); + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_TRIGGERS) { + pMenu->CheckMenuItem(ID_VIEW_SHOWTRIGGERS, MF_BYCOMMAND | MF_UNCHECKED); + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_HINT) { + pMenu->CheckMenuItem(ID_VIEW_SHOWHINT, MF_BYCOMMAND | MF_UNCHECKED); + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_CAULK) { + pMenu->CheckMenuItem(ID_VIEW_SHOWCAULK, MF_BYCOMMAND | MF_UNCHECKED); + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_VISPORTALS) { + pMenu->CheckMenuItem(ID_VIEW_SHOW_SHOWVISPORTALS, MF_BYCOMMAND | MF_UNCHECKED); + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_NODRAW) { + pMenu->CheckMenuItem(ID_VIEW_SHOW_NODRAW, MF_BYCOMMAND | MF_UNCHECKED); + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_ANGLES) { + pMenu->CheckMenuItem(ID_VIEW_SHOWANGLES, MF_BYCOMMAND | MF_UNCHECKED); + } + + pMenu->CheckMenuItem(ID_TOGGLE_LOCK, MF_BYCOMMAND | (g_PrefsDlg.m_bTextureLock) ? MF_CHECKED : MF_UNCHECKED); + pMenu->CheckMenuItem + ( + ID_TOGGLE_ROTATELOCK, + MF_BYCOMMAND | (g_PrefsDlg.m_bRotateLock) ? MF_CHECKED : MF_UNCHECKED + ); + pMenu->CheckMenuItem + ( + ID_VIEW_CUBICCLIPPING, + MF_BYCOMMAND | (g_PrefsDlg.m_bCubicClipping) ? MF_CHECKED : MF_UNCHECKED + ); + pMenu->CheckMenuItem + ( + ID_VIEW_OPENGLLIGHTING, + MF_BYCOMMAND | (g_PrefsDlg.m_bGLLighting) ? MF_CHECKED : MF_UNCHECKED + ); + pMenu->CheckMenuItem(ID_SNAPTOGRID, MF_BYCOMMAND | (!g_PrefsDlg.m_bNoClamp) ? MF_CHECKED : MF_UNCHECKED); + if (m_wndToolBar.GetSafeHwnd()) { + m_wndToolBar.GetToolBarCtrl().CheckButton + ( + ID_VIEW_CUBICCLIPPING, + (g_PrefsDlg.m_bCubicClipping) ? TRUE : FALSE + ); + } + + int n = g_PrefsDlg.m_nTextureScale; + int id; + switch (n) + { + case 10: + id = ID_TEXTURES_TEXTUREWINDOWSCALE_10; + break; + case 25: + id = ID_TEXTURES_TEXTUREWINDOWSCALE_25; + break; + case 50: + id = ID_TEXTURES_TEXTUREWINDOWSCALE_50; + break; + case 200: + id = ID_TEXTURES_TEXTUREWINDOWSCALE_200; + break; + default: + id = ID_TEXTURES_TEXTUREWINDOWSCALE_100; + break; + } + + CheckTextureScale(id); + } + + if (g_qeglobals.d_project_entity) { + // FillTextureMenu(); // redundant but i'll clean it up later.. yeah right.. + FillBSPMenu(); + LoadMruInReg(g_qeglobals.d_lpMruMenu, "Software\\" EDITOR_REGISTRY_KEY "\\MRU" ); + PlaceMenuMRUItem(g_qeglobals.d_lpMruMenu, ::GetSubMenu(::GetMenu(GetSafeHwnd()), 0), ID_FILE_EXIT); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::ShowMenuItemKeyBindings(CMenu *pMenu) { + int i, j; + char key[1024], *ptr; + MENUITEMINFO MenuItemInfo; + + // return; + for (i = 0; i < g_nCommandCount; i++) { + memset(&MenuItemInfo, 0, sizeof(MENUITEMINFO)); + MenuItemInfo.cbSize = sizeof(MENUITEMINFO); + MenuItemInfo.fMask = MIIM_TYPE; + MenuItemInfo.dwTypeData = key; + MenuItemInfo.cch = sizeof(key); + if (!pMenu->GetMenuItemInfo(g_Commands[i].m_nCommand, &MenuItemInfo)) { + continue; + } + + if (MenuItemInfo.fType != MFT_STRING) { + continue; + } + + ptr = strchr(key, '\t'); + if (ptr) { + *ptr = '\0'; + } + + strcat(key, "\t"); + if (g_Commands[i].m_nModifiers) { // are there modifiers present? + if (g_Commands[i].m_nModifiers & RAD_SHIFT) { + strcat(key, "Shift-"); + } + + if (g_Commands[i].m_nModifiers & RAD_ALT) { + strcat(key, "Alt-"); + } + + if (g_Commands[i].m_nModifiers & RAD_CONTROL) { + strcat(key, "Ctrl-"); + } + } + + for (j = 0; j < g_nKeyCount; j++) { + if (g_Commands[i].m_nKey == g_Keys[j].m_nVKKey) { + strcat(key, g_Keys[j].m_strName); + break; + } + } + + if (j >= g_nKeyCount) { + sprintf(&key[strlen(key)], "%c", g_Commands[i].m_nKey); + } + + memset(&MenuItemInfo, 0, sizeof(MENUITEMINFO)); + MenuItemInfo.cbSize = sizeof(MENUITEMINFO); + MenuItemInfo.fMask = MIIM_TYPE; + MenuItemInfo.fType = MFT_STRING; + MenuItemInfo.dwTypeData = key; + MenuItemInfo.cch = strlen(key); + SetMenuItemInfo(pMenu->m_hMenu, g_Commands[i].m_nCommand, FALSE, &MenuItemInfo); + } +} + +/* +============== +MFCCreate +============== +*/ +void MFCCreate( HINSTANCE hInstance ) +{ + HMENU hMenu = NULL; + int i = sizeof(g_qeglobals.d_savedinfo); + long l = i; + + g_qeglobals.d_savedinfo.exclude |= (EXCLUDE_HINT | EXCLUDE_CLIP); + LoadRegistryInfo("radiant_SavedInfo", &g_qeglobals.d_savedinfo, &l); + + int nOldSize = g_qeglobals.d_savedinfo.iSize; + if (g_qeglobals.d_savedinfo.iSize != sizeof(g_qeglobals.d_savedinfo)) { + // fill in new defaults + g_qeglobals.d_savedinfo.iSize = sizeof(g_qeglobals.d_savedinfo); + g_qeglobals.d_savedinfo.fGamma = 1.0; + g_qeglobals.d_savedinfo.iTexMenu = ID_VIEW_BILINEARMIPMAP; + g_qeglobals.d_savedinfo.m_nTextureTweak = 1.0; + + //g_qeglobals.d_savedinfo.exclude = INCLUDE_EASY | INCLUDE_NORMAL | INCLUDE_HARD | INCLUDE_DEATHMATCH; + g_qeglobals.d_savedinfo.show_coordinates = true; + g_qeglobals.d_savedinfo.show_names = false; + + for (i=0 ; i<3 ; i++) { + g_qeglobals.d_savedinfo.colors[COLOR_TEXTUREBACK][i] = 0; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][i] = 1.0; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDMINOR][i] = 0.75; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDMAJOR][i] = 0.5; + g_qeglobals.d_savedinfo.colors[COLOR_CAMERABACK][i] = 0.25; + } + + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBLOCK][0] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBLOCK][1] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBLOCK][2] = 1.0; + + g_qeglobals.d_savedinfo.colors[COLOR_GRIDTEXT][0] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDTEXT][1] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDTEXT][2] = 0.0; + + g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][0] = 1.0; + g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][1] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][2] = 0.0; + + g_qeglobals.d_savedinfo.colors[COLOR_CLIPPER][0] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_CLIPPER][1] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_CLIPPER][2] = 1.0; + + g_qeglobals.d_savedinfo.colors[COLOR_BRUSHES][0] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_BRUSHES][1] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_BRUSHES][2] = 0.0; + + g_qeglobals.d_savedinfo.colors[COLOR_VIEWNAME][0] = 0.5; + g_qeglobals.d_savedinfo.colors[COLOR_VIEWNAME][1] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_VIEWNAME][2] = 0.75; + + + // old size was smaller, reload original prefs + if (nOldSize > 0 && nOldSize < sizeof(g_qeglobals.d_savedinfo)) { + long l = nOldSize; + LoadRegistryInfo("radiant_SavedInfo", &g_qeglobals.d_savedinfo, &l); + } + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { + char *pBuffer = g_strAppPath.GetBufferSetLength(_MAX_PATH + 1); + int nResult = ::GetModuleFileName(NULL, pBuffer, _MAX_PATH); + ASSERT(nResult != 0); + pBuffer[g_strAppPath.ReverseFind('\\') + 1] = '\0'; + g_strAppPath.ReleaseBuffer(); + + com_editors |= EDITOR_RADIANT; + + InitCommonControls(); + g_qeglobals.d_hInstance = AfxGetInstanceHandle(); + MFCCreate(AfxGetInstanceHandle()); + + // g_PrefsDlg.LoadPrefs(); + if (CFrameWnd::OnCreate(lpCreateStruct) == -1) { + return -1; + } + + UINT nID = (g_PrefsDlg.m_bWideToolbar) ? IDR_TOOLBAR_ADVANCED : IDR_TOOLBAR1; + + if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP + | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) || !m_wndToolBar.LoadToolBar(nID)) { + TRACE0("Failed to create toolbar\n"); + return -1; // fail to create + } + + if (!m_wndStatusBar.Create(this) || !m_wndStatusBar.SetIndicators(indicators, sizeof(indicators) / sizeof(UINT))) { + TRACE0("Failed to create status bar\n"); + return -1; // fail to create + } + + m_bCamPreview = true; + + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SCALELOCKX, FALSE); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SCALELOCKY, FALSE); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SCALELOCKZ, FALSE); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SELECT_BYBOUNDINGBRUSH, FALSE); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SELECT_BRUSHESONLY, FALSE); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_PATCH_SHOWBOUNDINGBOX, FALSE); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_PATCH_WELD, TRUE); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_PATCH_DRILLDOWN, TRUE); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SHOW_LIGHTVOLUMES, FALSE); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SHOW_LIGHTTEXTURES, FALSE); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SELECTION_MOVEONLY, FALSE); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SOUND_SHOWSOUNDVOLUMES,g_qeglobals.d_savedinfo.showSoundAlways); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SOUND_SHOWSELECTEDSOUNDVOLUMES,g_qeglobals.d_savedinfo.showSoundWhenSelected); + + m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY); + EnableDocking(CBRS_ALIGN_ANY); + DockControlBar(&m_wndToolBar); + + g_nScaleHow = 0; + + m_wndTextureBar.Create(this, IDD_TEXTUREBAR, CBRS_BOTTOM, 7433); + m_wndTextureBar.EnableDocking(CBRS_ALIGN_ANY); + DockControlBar(&m_wndTextureBar); + + g_qeglobals.d_lpMruMenu = CreateMruMenuDefault(); + + m_bAutoMenuEnable = FALSE; + + LoadCommandMap(); + + CMenu *pMenu = GetMenu(); + ShowMenuItemKeyBindings(pMenu); + + CFont *pFont = new CFont(); + pFont->CreatePointFont(g_PrefsDlg.m_nStatusSize * 10, "Arial"); + m_wndStatusBar.SetFont(pFont); + + + if (g_PrefsDlg.m_bRunBefore == FALSE) { + g_PrefsDlg.m_bRunBefore = TRUE; + g_PrefsDlg.SavePrefs(); + + /* + * if (MessageBox("Would you like QERadiant to build and load a default project? + * If this is the first time you have run QERadiant or you are not familiar with + * editing QE4 project files directly, this is HIGHLY recommended", "Create a + * default project?", MB_YESNO) == IDYES) { OnFileNewproject(); } + */ + } + else + { + // load plugins before the first Map_LoadFile required for model plugins + if (g_PrefsDlg.m_bLoadLastMap && g_PrefsDlg.m_strLastMap.GetLength() > 0) { + Map_LoadFile(g_PrefsDlg.m_strLastMap.GetBuffer(0)); + } + } + + SetGridStatus(); + SetTexValStatus(); + SetButtonMenuStates(); + LoadBarState("RadiantToolBars2"); + + SetActiveXY(m_pXYWnd); + m_pXYWnd->SetFocus(); + + PostMessage(WM_KEYDOWN, 'O', NULL); + + if ( radiant_entityMode.GetBool() ) { + g_qeglobals.d_savedinfo.exclude |= (EXCLUDE_PATHS | EXCLUDE_CLIP | EXCLUDE_CAULK | EXCLUDE_VISPORTALS | EXCLUDE_NODRAW | EXCLUDE_TRIGGERS); + } + + Sys_UpdateWindows ( W_ALL ); + return 0; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ + +void FindReplace(CString& strContents, const char* pTag, const char* pValue) { + if (strcmp(pTag, pValue) == 0) + return; + for (int nPos = strContents.Find(pTag); nPos >= 0; nPos = strContents.Find(pTag)) { + int nRightLen = strContents.GetLength() - strlen(pTag) - nPos; + CString strLeft = strContents.Left(nPos); + CString strRight = strContents.Right(nRightLen); + strLeft += pValue; + strLeft += strRight; + strContents = strLeft; + } +} + +void CMainFrame::LoadCommandMap() { + CString strINI; + char pBuff[1024]; + strINI = g_strAppPath; + strINI += "\\radiant.ini"; + + for (int i = 0; i < g_nCommandCount; i++) { + int nLen = GetPrivateProfileString("Commands", g_Commands[i].m_strCommand, "", pBuff, 1024, strINI); + if (nLen > 0) { + CString strBuff = pBuff; + strBuff.TrimLeft(); + strBuff.TrimRight(); + + int nSpecial = strBuff.Find("+alt"); + g_Commands[i].m_nModifiers = 0; + if (nSpecial >= 0) { + g_Commands[i].m_nModifiers |= RAD_ALT; + FindReplace(strBuff, "+alt", ""); + } + + nSpecial = strBuff.Find("+ctrl"); + if (nSpecial >= 0) { + g_Commands[i].m_nModifiers |= RAD_CONTROL; + FindReplace(strBuff, "+ctrl", ""); + } + + nSpecial = strBuff.Find("+shift"); + if (nSpecial >= 0) { + g_Commands[i].m_nModifiers |= RAD_SHIFT; + FindReplace(strBuff, "+shift", ""); + } + + strBuff.TrimLeft(); + strBuff.TrimRight(); + strBuff.MakeUpper(); + if (nLen == 1) { // most often case.. deal with first + g_Commands[i].m_nKey = __toascii(strBuff.GetAt(0)); + } + else { // special key + for (int j = 0; j < g_nKeyCount; j++) { + if (strBuff.CompareNoCase(g_Keys[j].m_strName) == 0) { + g_Commands[i].m_nKey = g_Keys[j].m_nVKKey; + break; + } + } + } + } + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +BOOL CMainFrame::PreCreateWindow(CREATESTRUCT &cs) { + // TODO: Modify the Window class or styles here by modifying the CREATESTRUCT cs + return CFrameWnd::PreCreateWindow(cs); +} + +// CMainFrame diagnostics +#ifdef _DEBUG + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::AssertValid() const { + CFrameWnd::AssertValid(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::Dump(CDumpContext &dc) const { + CFrameWnd::Dump(dc); +} +#endif // _DEBUG + +// +// ======================================================================================================================= +// CMainFrame message handlers +// ======================================================================================================================= +// +void CMainFrame::CreateQEChildren() { + QE_LoadQuake4Project(); + + QE_Init(); + + common->Printf("Entering message loop\n"); + + m_bDoLoop = true; + SetTimer(QE_TIMER0, 100, NULL); + SetTimer(QE_TIMER1, g_PrefsDlg.m_nAutoSave * 60 * 1000, NULL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +BOOL CMainFrame::OnCommand(WPARAM wParam, LPARAM lParam) { + return CFrameWnd::OnCommand(wParam, lParam); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +LRESULT CMainFrame::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam) { + //RoutineProcessing(); + return CFrameWnd::DefWindowProc(message, wParam, lParam); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::RoutineProcessing() { + if (m_bDoLoop) { + double time = 0.0; + static double oldtime = 0.0; + double delta = 0.0; + + time = Sys_DoubleTime(); + delta = time - oldtime; + oldtime = time; + if (delta > 0.2) { + delta = 0.2; + } + + // run time dependant behavior + if (m_pCamWnd) { + m_pCamWnd->Cam_MouseControl(delta); + } + + if (g_PrefsDlg.m_bQE4Painting && g_nUpdateBits) { + int nBits = g_nUpdateBits; // this is done to keep this routine from being + g_nUpdateBits = 0; // re-entered due to the paint process.. only + UpdateWindows(nBits); // happens in rare cases but causes a stack overflow + } + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +LRESULT CMainFrame::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) { + return CFrameWnd::WindowProc(message, wParam, lParam); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +bool MouseDown() { + if (::GetAsyncKeyState(VK_LBUTTON)) { + return true; + } + + if (::GetAsyncKeyState(VK_RBUTTON)) { + return true; + } + + if (::GetAsyncKeyState(VK_MBUTTON)) { + return true; + } + + return false; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ + +void CMainFrame::OnTimer(UINT nIDEvent) { + static bool autoSavePending = false; + + if ( nIDEvent == QE_TIMER0 && !MouseDown() ) { + QE_CountBrushesAndUpdateStatusBar(); + } + if ( nIDEvent == QE_TIMER1 || autoSavePending ) { + if ( MouseDown() ) { + autoSavePending = true; + return; + } + if ( Sys_Waiting() ) { + autoSavePending = true; + return; + } + QE_CheckAutoSave(); + autoSavePending = false; + } +} + +struct SplitInfo { + int m_nMin; + int m_nCur; +}; + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +bool LoadWindowPlacement(HWND hwnd, const char *pName) { + WINDOWPLACEMENT wp; + wp.length = sizeof(WINDOWPLACEMENT); + + LONG lSize = sizeof(wp); + if (LoadRegistryInfo(pName, &wp, &lSize)) { + ::SetWindowPlacement(hwnd, &wp); + return true; + } + + return false; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void SaveWindowPlacement(HWND hwnd, const char *pName) { + WINDOWPLACEMENT wp; + wp.length = sizeof(WINDOWPLACEMENT); + if (::GetWindowPlacement(hwnd, &wp)) { + SaveRegistryInfo(pName, &wp, sizeof(wp)); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnDestroy() { + KillTimer(QE_TIMER0); + + SaveBarState("RadiantToolBars2"); + + // FIXME original mru stuff needs replaced with mfc stuff + SaveMruInReg(g_qeglobals.d_lpMruMenu, "Software\\" EDITOR_REGISTRY_KEY "\\MRU"); + + DeleteMruMenu(g_qeglobals.d_lpMruMenu); + + SaveRegistryInfo("radiant_SavedInfo", &g_qeglobals.d_savedinfo, sizeof(g_qeglobals.d_savedinfo)); + + SaveWindowPlacement(GetSafeHwnd(), "radiant_MainWindowPlace"); + + SaveWindowPlacement(m_pXYWnd->GetSafeHwnd(), "radiant_xywindow"); + SaveWindowPlacement(m_pXZWnd->GetSafeHwnd(), "radiant_xzwindow"); + SaveWindowPlacement(m_pYZWnd->GetSafeHwnd(), "radiant_yzwindow"); + SaveWindowPlacement(m_pCamWnd->GetSafeHwnd(), "radiant_camerawindow"); + SaveWindowPlacement(m_pZWnd->GetSafeHwnd(), "radiant_zwindow"); + SaveWindowState(g_Inspectors->texWnd.GetSafeHwnd(), "radiant_texwindow"); + + if (m_pXYWnd->GetSafeHwnd()) { + m_pXYWnd->SendMessage(WM_DESTROY, 0, 0); + } + + delete m_pXYWnd; + m_pXYWnd = NULL; + + if (m_pYZWnd->GetSafeHwnd()) { + m_pYZWnd->SendMessage(WM_DESTROY, 0, 0); + } + + delete m_pYZWnd; + m_pYZWnd = NULL; + + if (m_pXZWnd->GetSafeHwnd()) { + m_pXZWnd->SendMessage(WM_DESTROY, 0, 0); + } + + delete m_pXZWnd; + m_pXZWnd = NULL; + + if (m_pZWnd->GetSafeHwnd()) { + m_pZWnd->SendMessage(WM_DESTROY, 0, 0); + } + + delete m_pZWnd; + m_pZWnd = NULL; + + if (m_pCamWnd->GetSafeHwnd()) { + m_pCamWnd->SendMessage(WM_DESTROY, 0, 0); + } + + delete m_pCamWnd; + m_pCamWnd = NULL; + + if ( idStr::Icmp(currentmap, "unnamed.map") != 0 ) { + g_PrefsDlg.m_strLastMap = currentmap; + g_PrefsDlg.SavePrefs(); + } + + CleanUpEntities(); + + while (active_brushes.next != &active_brushes) { + Brush_Free(active_brushes.next, false); + } + + while (selected_brushes.next != &selected_brushes) { + Brush_Free(selected_brushes.next, false); + } + + while (filtered_brushes.next != &filtered_brushes) { + Brush_Free(filtered_brushes.next, false); + } + + while (entities.next != &entities) { + Entity_Free(entities.next); + } + + + g_qeglobals.d_project_entity->epairs.Clear(); + + entity_t *pEntity = g_qeglobals.d_project_entity->next; + while (pEntity != NULL && pEntity != g_qeglobals.d_project_entity) { + entity_t *pNextEntity = pEntity->next; + Entity_Free(pEntity); + pEntity = pNextEntity; + } + + Texture_Cleanup(); + + if (world_entity) { + Entity_Free(world_entity); + } + + // + // FIXME: idMaterial + // if (notexture) { // Timo // Surface properties plugin #ifdef _DEBUG if ( + // !notexture->pData ) common->Printf("WARNING: found a qtexture_t* with no + // IPluginQTexture\n"); #endif if ( notexture->pData ) + // GETPLUGINTEXDEF(notexture)->DecRef(); Mem_Free(notexture); } + // if (current_texture) free(current_texture); + // + + // FIXME: idMaterial FreeShaders(); + CFrameWnd::OnDestroy(); + +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnClose() { + if (ConfirmModified()) { + g_Inspectors->SaveWindowPlacement (); + ShowWindow( SW_HIDE ); + common->ActivateTool( false ); + ::ShowWindow( win32.hWnd, SW_SHOW ); + ::SetForegroundWindow( win32.hWnd ); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) { + // run through our list to see if we have a handler for nChar + + for (int i = 0; i < g_nCommandCount; i++) { + if (g_Commands[i].m_nKey == nChar) { // find a match? + bool bGo = true; + if (g_Commands[i].m_nModifiers & RAD_PRESS) { + int nModifiers = g_Commands[i].m_nModifiers &~RAD_PRESS; + if (nModifiers) { // are there modifiers present? + if (nModifiers & RAD_ALT) { + if (!(GetAsyncKeyState(VK_MENU) & 0x8000)) { + bGo = false; + } + } + + if (nModifiers & RAD_CONTROL) { + if (!(GetAsyncKeyState(VK_CONTROL) & 0x8000)) { + bGo = false; + } + } + + if (nModifiers & RAD_SHIFT) { + if (!(GetAsyncKeyState(VK_SHIFT) & 0x8000)) { + bGo = false; + } + } + } + else { // no modifiers make sure none of those keys are pressed + if (GetAsyncKeyState(VK_MENU) & 0x8000) { + bGo = false; + } + + if (GetAsyncKeyState(VK_CONTROL) & 0x8000) { + bGo = false; + } + + if (GetAsyncKeyState(VK_SHIFT) & 0x8000) { + bGo = false; + } + } + + if (bGo) { + SendMessage(WM_COMMAND, g_Commands[i].m_nCommand, 0); + break; + } + } + } + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +bool CamOK(unsigned int nKey) { + if (nKey == VK_UP || nKey == VK_LEFT || nKey == VK_RIGHT || nKey == VK_DOWN) { + if (::GetAsyncKeyState(nKey)) { + return true; + } + else { + return false; + } + } + + return true; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { + // OnKeyDown(nChar, nRepCnt, nFlags); + if (nChar == VK_DOWN) { + OnKeyDown(nChar, nRepCnt, nFlags); + } + + CFrameWnd::OnSysKeyDown(nChar, nRepCnt, nFlags); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { + + for (int i = 0; i < g_nCommandCount; i++) { + if (g_Commands[i].m_nKey == nChar) { // find a match? + // check modifiers + unsigned int nState = 0; + if (GetAsyncKeyState(VK_MENU) & 0x8000) { + nState |= RAD_ALT; + } + + if (GetAsyncKeyState(VK_CONTROL) & 0x8000) { + nState |= RAD_CONTROL; + } + + if (GetAsyncKeyState(VK_SHIFT) & 0x8000) { + nState |= RAD_SHIFT; + } + + if ((g_Commands[i].m_nModifiers & 0x7) == nState) { + SendMessage(WM_COMMAND, g_Commands[i].m_nCommand, 0); + break; + } + } + } + + CFrameWnd::OnKeyDown(nChar, nRepCnt, nFlags); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext *pContext) { + + g_Inspectors = new CInspectorDialog( this ); + if ( !g_Inspectors->Create(IDD_DIALOG_INSPECTORS, this) || !::IsWindow(g_Inspectors->GetSafeHwnd()) ) { + common->Warning("Radiant: failed to create the inspector window"); + return FALSE; + } + + LoadWindowPlacement(g_Inspectors->GetSafeHwnd(), "radiant_InspectorsWindow"); + g_Inspectors->ShowWindow(SW_SHOW); + + CRect r; + g_Inspectors->GetWindowRect ( r ); + + //stupid hack to get the window resize itself properly + r.DeflateRect(0,0,0,1); + g_Inspectors->MoveWindow(r); + r.InflateRect(0,0,0,1); + g_Inspectors->MoveWindow(r); + + + if (!LoadWindowPlacement(GetSafeHwnd(), "radiant_MainWindowPlace")) { + } + + CRect rect(5, 25, 100, 100); + CRect rctParent; + GetClientRect(rctParent); + + m_pCamWnd = new CCamWnd(); + SetLastError( ERROR_SUCCESS ); + const BOOL cameraCreated = m_pCamWnd->Create(CAMERA_WINDOW_CLASS, "", QE3_CHILDSTYLE, rect, this, 1234); + if ( !cameraCreated || !::IsWindow(m_pCamWnd->GetSafeHwnd()) ) { + common->Warning("Radiant: failed to create the camera window (Create=%d, hwnd=%p, error=%lu)", + cameraCreated, m_pCamWnd->GetSafeHwnd(), GetLastError()); + return FALSE; + } + + m_pZWnd = new CZWnd(); + if ( !m_pZWnd->Create(Z_WINDOW_CLASS, "", QE3_CHILDSTYLE, rect, this, 1238) || !::IsWindow(m_pZWnd->GetSafeHwnd()) ) { + common->Warning("Radiant: failed to create the Z window"); + return FALSE; + } + + // XYWnd.cpp is retained byte-for-byte from the original Windows source and + // its legacy class registration leaves hInstance unset. Modern Win32 + // rejects that WNDCLASS, so register the shared XY/XZ/YZ class correctly + // before the three MFC wrappers create their windows. + WNDCLASS xyClass; + HINSTANCE radiantInstance = AfxGetInstanceHandle(); + if ( !::GetClassInfo(radiantInstance, XY_WINDOW_CLASS, &xyClass) ) { + memset(&xyClass, 0, sizeof(xyClass)); + xyClass.style = CS_NOCLOSE; + xyClass.hInstance = radiantInstance; + xyClass.lpszClassName = XY_WINDOW_CLASS; + xyClass.lpfnWndProc = ::DefWindowProc; + if ( !AfxRegisterClass(&xyClass) ) { + common->Warning("Radiant: failed to register %s (error %lu)", XY_WINDOW_CLASS, GetLastError()); + return FALSE; + } + } + + m_pXYWnd = new CXYWnd(); + if ( !m_pXYWnd->Create(XY_WINDOW_CLASS, "", QE3_CHILDSTYLE, rect, this, 1235) || !::IsWindow(m_pXYWnd->GetSafeHwnd()) ) { + common->Warning("Radiant: failed to create the XY window"); + return FALSE; + } + m_pXYWnd->SetViewType(XY); + + m_pXZWnd = new CXYWnd(); + if ( !m_pXZWnd->Create(XY_WINDOW_CLASS, "", QE3_CHILDSTYLE, rect, this, 1236) || !::IsWindow(m_pXZWnd->GetSafeHwnd()) ) { + common->Warning("Radiant: failed to create the XZ window"); + return FALSE; + } + m_pXZWnd->SetViewType(XZ); + + m_pYZWnd = new CXYWnd(); + if ( !m_pYZWnd->Create(XY_WINDOW_CLASS, "", QE3_CHILDSTYLE, rect, this, 1237) || !::IsWindow(m_pYZWnd->GetSafeHwnd()) ) { + common->Warning("Radiant: failed to create the YZ window"); + return FALSE; + } + m_pYZWnd->SetViewType(YZ); + + m_pCamWnd->SetXYFriend(m_pXYWnd); + + CRect rctWork; + + LoadWindowPlacement(m_pXYWnd->GetSafeHwnd(), "radiant_xywindow"); + LoadWindowPlacement(m_pXZWnd->GetSafeHwnd(), "radiant_xzwindow"); + LoadWindowPlacement(m_pYZWnd->GetSafeHwnd(), "radiant_yzwindow"); + LoadWindowPlacement(m_pCamWnd->GetSafeHwnd(), "radiant_camerawindow"); + LoadWindowPlacement(m_pZWnd->GetSafeHwnd(), "radiant_zwindow"); + + if (!g_PrefsDlg.m_bXZVis) { + m_pXZWnd->ShowWindow(SW_HIDE); + } + + if (!g_PrefsDlg.m_bYZVis) { + m_pYZWnd->ShowWindow(SW_HIDE); + } + + if (!g_PrefsDlg.m_bZVis) { + m_pZWnd->ShowWindow(SW_HIDE); + } + + CreateQEChildren(); + + if (m_pXYWnd) { + m_pXYWnd->SetActive(true); + } + + Texture_SetMode(g_qeglobals.d_savedinfo.iTexMenu); + + g_Inspectors->SetMode(W_CONSOLE); + return TRUE; +} + +CRect g_rctOld(0, 0, 0, 0); + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSize(UINT nType, int cx, int cy) { + CFrameWnd::OnSize(nType, cx, cy); + + CRect rctParent; + GetClientRect(rctParent); + + UINT nID; + UINT nStyle; + int nWidth; + if (m_wndStatusBar.GetSafeHwnd()) { + m_wndStatusBar.GetPaneInfo( 0, nID, nStyle, nWidth); + m_wndStatusBar.SetPaneInfo( 0, nID, nStyle, rctParent.Width() * 0.15f ); + m_wndStatusBar.GetPaneInfo( 1, nID, nStyle, nWidth); + m_wndStatusBar.SetPaneInfo( 1, nID, nStyle, rctParent.Width() * 0.15f); + m_wndStatusBar.GetPaneInfo( 2, nID, nStyle, nWidth); + m_wndStatusBar.SetPaneInfo( 2, nID, nStyle, rctParent.Width() * 0.15f ); + m_wndStatusBar.GetPaneInfo( 3, nID, nStyle, nWidth); + m_wndStatusBar.SetPaneInfo( 3, nID, nStyle, rctParent.Width() * 0.39f ); + m_wndStatusBar.GetPaneInfo( 4, nID, nStyle, nWidth); + m_wndStatusBar.SetPaneInfo( 4, nID, nStyle, rctParent.Width() * 0.15f ); + m_wndStatusBar.GetPaneInfo( 5, nID, nStyle, nWidth); + m_wndStatusBar.SetPaneInfo( 5, nID, nStyle, rctParent.Width() * 0.01f ); + } +} + +void OpenDialog(void); +void SaveAsDialog(bool bRegion); +void Select_Ungroup(); + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::ToggleCamera() { + if (m_bCamPreview) { + m_bCamPreview = false; + } + else { + m_bCamPreview = true; + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnFileClose() { +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnFileExit() { + PostMessage(WM_CLOSE, 0, 0L); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnFileLoadproject() { + if (ConfirmModified()) { + ProjectDialog(); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnFileNew() { + if (ConfirmModified()) { + Map_New(); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnFileOpen() { + if (ConfirmModified()) { + OpenDialog(); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnFilePointfile() { + if (g_qeglobals.d_pointfile_display_list) { + Pointfile_Clear(); + } + else { + Pointfile_Check(); + } + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnFilePrint() { +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnFilePrintPreview() { +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnFileSave() { + if (!strcmp(currentmap, "unnamed.map")) { + SaveAsDialog(false); + } + else { + Map_SaveFile(currentmap, false); + } + + // DHM - _D3XP + SetTimer(QE_TIMER1, g_PrefsDlg.m_nAutoSave * 60 * 1000, NULL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnFileSaveas() { + SaveAsDialog(false); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnFileSaveCopy() { + char aFile[260] = "\0"; + char aFilter[260] = "Map\0*.map\0\0"; + char aTitle[260] = "Save a Copy\0"; + OPENFILENAME afn; + + memset( &afn, 0, sizeof(OPENFILENAME) ); + + CString strPath = ValueForKey(g_qeglobals.d_project_entity, "basepath"); + AddSlash(strPath); + strPath += "maps"; + if (g_PrefsDlg.m_strMaps.GetLength() > 0) { + strPath += va("\\%s", g_PrefsDlg.m_strMaps); + } + + /* Place the terminating null character in the szFile. */ + aFile[0] = '\0'; + + /* Set the members of the OPENFILENAME structure. */ + afn.lStructSize = sizeof(OPENFILENAME); + afn.hwndOwner = g_pParentWnd->GetSafeHwnd(); + afn.lpstrFilter = aFilter; + afn.nFilterIndex = 1; + afn.lpstrFile = aFile; + afn.nMaxFile = sizeof(aFile); + afn.lpstrFileTitle = NULL; + afn.nMaxFileTitle = 0; + afn.lpstrInitialDir = strPath; + afn.lpstrTitle = aTitle; + afn.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_OVERWRITEPROMPT; + + /* Display the Open dialog box. */ + if (!GetSaveFileName(&afn)) { + return; // canceled + } + + DefaultExtension(afn.lpstrFile, ".map"); + Map_SaveFile(afn.lpstrFile, false); // ignore region + + // Set the title back to the current working map + Sys_SetTitle(currentmap); +} + +/* +================================================================================================== +*/ +void CMainFrame::OnViewShowModels() { + g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_MODELS; + + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnView100() { + if (m_pXYWnd) { + m_pXYWnd->SetScale(1); + } + + if (m_pXZWnd) { + m_pXZWnd->SetScale(1); + } + + if (m_pYZWnd) { + m_pYZWnd->SetScale(1); + } + + Sys_UpdateWindows(W_XY | W_XY_OVERLAY); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewCenter() { + m_pCamWnd->Camera().angles[ROLL] = m_pCamWnd->Camera().angles[PITCH] = 0; + m_pCamWnd->Camera().angles[YAW] = 22.5 * floor((m_pCamWnd->Camera().angles[YAW] + 11) / 22.5); + Sys_UpdateWindows(W_CAMERA | W_XY_OVERLAY); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewConsole() { + g_Inspectors->SetMode(W_CONSOLE); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewDownfloor() { + m_pCamWnd->Cam_ChangeFloor(false); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewEntity() { + g_Inspectors->SetMode(W_ENTITY); +} + +void CMainFrame::OnViewMediaBrowser() { + g_Inspectors->SetMode(W_MEDIA); +} + + + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewFront() { + m_pXYWnd->SetViewType(YZ); + m_pXYWnd->PositionView(); + Sys_UpdateWindows(W_XY); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ + +BOOL DoMru(HWND hWnd,WORD wId) +{ + char szFileName[128]; + OFSTRUCT of; + BOOL fExist; + + GetMenuItem(g_qeglobals.d_lpMruMenu, wId, TRUE, szFileName, sizeof(szFileName)); + + // Test if the file exists. + + fExist = OpenFile(szFileName ,&of,OF_EXIST) != HFILE_ERROR; + + if (fExist) { + + // Place the file on the top of MRU. + AddNewItem(g_qeglobals.d_lpMruMenu,(LPSTR)szFileName); + + // Now perform opening this file !!! + Map_LoadFile (szFileName); + } + else + // Remove the file on MRU. + DelMenuItem(g_qeglobals.d_lpMruMenu,wId,TRUE); + + // Refresh the File menu. + PlaceMenuMRUItem(g_qeglobals.d_lpMruMenu,GetSubMenu(GetMenu(hWnd),0), + ID_FILE_EXIT); + + return fExist; +} + +void CMainFrame::OnMru(unsigned int nID) { + // DHM - _D3XP + if (ConfirmModified()) { + DoMru(GetSafeHwnd(), nID); +} +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewNearest(unsigned int nID) { + Texture_SetMode(nID); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnTextureWad(unsigned int nID) { + Sys_BeginWait(); + + // FIXME: idMaterial Texture_ShowDirectory (nID); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ + +/* +============ +RunBsp + +This is the new all-internal bsp +============ +*/ +void RunBsp (const char *command) { + char sys[2048]; + char name[2048]; + char *in; + + // bring the console window forward for feedback + g_Inspectors->SetMode(W_CONSOLE); + + // decide if we are doing a .map or a .reg + strcpy (name, currentmap); + if ( region_active ) { + Map_SaveFile (name, false); + StripExtension (name); + strcat (name, ".reg"); + } + + if ( !Map_SaveFile ( name, region_active ) ) { + return; + } + + // name should be a full pathname, but we only + // want to pass the maps/ part to dmap + in = strstr(name, "maps/"); + if ( !in ) { + in = strstr(name, "maps\\"); + } + if ( !in ) { + in = name; + } + + if (idStr::Icmpn(command, "bspext", strlen("runbsp")) == 0) { + PROCESS_INFORMATION ProcessInformation; + STARTUPINFO startupinfo; + char buff[2048]; + + idStr base = cvarSystem->GetCVarString( "fs_basepath" ); + idStr cd = cvarSystem->GetCVarString( "fs_cdpath" ); + idStr paths; + if (base.Length()) { + paths += "+set fs_basepath "; + paths += base; + } + if (cd.Length()) { + paths += "+set fs_cdpath "; + paths += cd; + } + + ::GetModuleFileName(AfxGetApp()->m_hInstance, buff, sizeof(buff)); + if (strlen(command) > strlen("bspext")) { + idStr::snPrintf( sys, sizeof(sys), "%s %s +set r_fullscreen 0 +dmap editorOutput %s %s +quit", buff, paths.c_str(), command + strlen("bspext"), in ); + } else { + idStr::snPrintf( sys, sizeof(sys), "%s %s +set r_fullscreen 0 +dmap editorOutput %s +quit", buff, paths.c_str(), in ); + } + + ::GetStartupInfo (&startupinfo); + if (!CreateProcess(NULL, sys, NULL, NULL, FALSE, 0, NULL, NULL, &startupinfo, &ProcessInformation)) { + common->Printf("Could not start bsp process %s %s/n", buff, sys); + } + g_pParentWnd->SetFocus(); + + } else { // assumes bsp is the command + if (strlen(command) > strlen("bsp")) { + idStr::snPrintf( sys, sizeof(sys), "dmap %s %s", command + strlen("bsp"), in ); + } else { + idStr::snPrintf( sys, sizeof(sys), "dmap %s", in ); + } + + cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect\n" ); + + // issue the bsp command + Dmap_f( idCmdArgs( sys, false ) ); + } +} + +void CMainFrame::OnBspCommand(unsigned int nID) { + if (g_PrefsDlg.m_bSnapShots && stricmp(currentmap, "unnamed.map") != 0) { + Map_Snapshot(); + } + + RunBsp(bsp_commands[LOWORD(nID - CMD_BSPCOMMAND)]); + + // DHM - _D3XP + SetTimer(QE_TIMER1, g_PrefsDlg.m_nAutoSave * 60 * 1000, NULL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewShowblocks() { + g_qeglobals.show_blocks = !(g_qeglobals.show_blocks); + CheckMenuItem + ( + ::GetMenu(GetSafeHwnd()), + ID_VIEW_SHOWBLOCKS, + MF_BYCOMMAND | (g_qeglobals.show_blocks ? MF_CHECKED : MF_UNCHECKED) + ); + Sys_UpdateWindows(W_XY); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewShowclip() { + if ((g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_CLIP) & EXCLUDE_CLIP) { + CheckMenuItem(::GetMenu(GetSafeHwnd()), ID_VIEW_SHOWCLIP, MF_BYCOMMAND | MF_UNCHECKED); + } + else { + CheckMenuItem(::GetMenu(GetSafeHwnd()), ID_VIEW_SHOWCLIP, MF_BYCOMMAND | MF_CHECKED); + } + + Sys_UpdateWindows(W_XY | W_CAMERA); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewShowTriggers() { + if ((g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_TRIGGERS) & EXCLUDE_TRIGGERS) { + CheckMenuItem(::GetMenu(GetSafeHwnd()), ID_VIEW_SHOWTRIGGERS, MF_BYCOMMAND | MF_UNCHECKED); + } + else { + CheckMenuItem(::GetMenu(GetSafeHwnd()), ID_VIEW_SHOWTRIGGERS, MF_BYCOMMAND | MF_CHECKED); + } + + Sys_UpdateWindows(W_XY | W_CAMERA); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewShowcoordinates() { + g_qeglobals.d_savedinfo.show_coordinates ^= 1; + CheckMenuItem + ( + ::GetMenu(GetSafeHwnd()), + ID_VIEW_SHOWCOORDINATES, + MF_BYCOMMAND | (g_qeglobals.d_savedinfo.show_coordinates ? MF_CHECKED : MF_UNCHECKED) + ); + Sys_UpdateWindows(W_XY); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewShowent() { + if ((g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_ENT) & EXCLUDE_ENT) { + CheckMenuItem(::GetMenu(GetSafeHwnd()), ID_VIEW_SHOWENT, MF_BYCOMMAND | MF_UNCHECKED); + } + else { + CheckMenuItem(::GetMenu(GetSafeHwnd()), ID_VIEW_SHOWENT, MF_BYCOMMAND | MF_CHECKED); + } + + Sys_UpdateWindows(W_XY | W_CAMERA); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewShowlights() { + if ((g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_LIGHTS) & EXCLUDE_LIGHTS) { + CheckMenuItem(::GetMenu(GetSafeHwnd()), ID_VIEW_SHOWLIGHTS, MF_BYCOMMAND | MF_UNCHECKED); + } + else { + CheckMenuItem(::GetMenu(GetSafeHwnd()), ID_VIEW_SHOWLIGHTS, MF_BYCOMMAND | MF_CHECKED); + } + + Sys_UpdateWindows(W_XY | W_CAMERA); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewShownames() { + g_qeglobals.d_savedinfo.show_names = !(g_qeglobals.d_savedinfo.show_names); + CheckMenuItem + ( + ::GetMenu(GetSafeHwnd()), + ID_VIEW_SHOWNAMES, + MF_BYCOMMAND | (g_qeglobals.d_savedinfo.show_names ? MF_CHECKED : MF_UNCHECKED) + ); + Map_BuildBrushData(); + Sys_UpdateWindows(W_XY); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewShowpath() { + if ((g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_PATHS) & EXCLUDE_PATHS) { + CheckMenuItem(::GetMenu(GetSafeHwnd()), ID_VIEW_SHOWPATH, MF_BYCOMMAND | MF_UNCHECKED); + } + else { + CheckMenuItem(::GetMenu(GetSafeHwnd()), ID_VIEW_SHOWPATH, MF_BYCOMMAND | MF_CHECKED); + } + + Sys_UpdateWindows(W_XY | W_CAMERA); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewShowCombatNodes() { + if ((g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_COMBATNODES) & EXCLUDE_COMBATNODES) { + CheckMenuItem(::GetMenu(GetSafeHwnd()), ID_VIEW_SHOWCOMBATNODES, MF_BYCOMMAND | MF_UNCHECKED); + } + else { + CheckMenuItem(::GetMenu(GetSafeHwnd()), ID_VIEW_SHOWCOMBATNODES, MF_BYCOMMAND | MF_CHECKED); + } + + Sys_UpdateWindows(W_XY | W_CAMERA); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewShowwater() { + if ((g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_DYNAMICS) & EXCLUDE_DYNAMICS) { + CheckMenuItem(::GetMenu(GetSafeHwnd()), ID_VIEW_SHOWWATER, MF_BYCOMMAND | MF_UNCHECKED); + } + else { + CheckMenuItem(::GetMenu(GetSafeHwnd()), ID_VIEW_SHOWWATER, MF_BYCOMMAND | MF_CHECKED); + } + + Sys_UpdateWindows(W_XY | W_CAMERA); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewShowworld() { + if ((g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_WORLD) & EXCLUDE_WORLD) { + CheckMenuItem(::GetMenu(GetSafeHwnd()), ID_VIEW_SHOWWORLD, MF_BYCOMMAND | MF_UNCHECKED); + } + else { + CheckMenuItem(::GetMenu(GetSafeHwnd()), ID_VIEW_SHOWWORLD, MF_BYCOMMAND | MF_CHECKED); + } + + Sys_UpdateWindows(W_XY | W_CAMERA); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewTexture() { + g_Inspectors->SetMode(W_TEXTURE); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewUpfloor() { + m_pCamWnd->Cam_ChangeFloor(true); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewXy() { + m_pXYWnd->SetViewType(XY); + m_pXYWnd->PositionView(); + Sys_UpdateWindows(W_XY); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewZ100() { + z.scale = 1; + Sys_UpdateWindows(W_Z | W_Z_OVERLAY); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewZoomin() { + if ( m_pXYWnd && m_pXYWnd->Active() ) { + m_pXYWnd->SetScale( m_pXYWnd->Scale() * 5.0f / 4.0f ); + if ( m_pXYWnd->Scale() > 256.0f ) { + m_pXYWnd->SetScale( 256.0f ); + } + } + + if ( m_pXZWnd && m_pXZWnd->Active() ) { + m_pXZWnd->SetScale( m_pXZWnd->Scale() * 5.0f / 4.0f ); + if ( m_pXZWnd->Scale() > 256.0f ) { + m_pXZWnd->SetScale( 256.0f ); + } + } + + if ( m_pYZWnd && m_pYZWnd->Active() ) { + m_pYZWnd->SetScale( m_pYZWnd->Scale() * 5.0f / 4.0f ); + if ( m_pYZWnd->Scale() > 256.0f ) { + m_pYZWnd->SetScale( 256.0f ); + } + } + + Sys_UpdateWindows( W_XY | W_XY_OVERLAY ); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewZoomout() { + if ( m_pXYWnd && m_pXYWnd->Active() ) { + m_pXYWnd->SetScale( m_pXYWnd->Scale() * 4.0f / 5.0f ); + if ( m_pXYWnd->Scale() < 0.1f / 32.0f ) { + m_pXYWnd->SetScale( 0.1f / 32.0f ); + } + } + + if ( m_pXZWnd && m_pXZWnd->Active() ) { + m_pXZWnd->SetScale( m_pXZWnd->Scale() * 4.0f / 5.0f ); + if ( m_pXZWnd->Scale() < 0.1f / 32.0f ) { + m_pXZWnd->SetScale( 0.1f / 32.0f ); + } + } + + if ( m_pYZWnd && m_pYZWnd->Active() ) { + m_pYZWnd->SetScale( m_pYZWnd->Scale() * 4.0f / 5.0f ); + if ( m_pYZWnd->Scale() < 0.1f / 32.0f ) { + m_pYZWnd->SetScale( 0.1f / 32.0f ); + } + } + + Sys_UpdateWindows(W_XY | W_XY_OVERLAY); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewZzoomin() { + z.scale *= 5.0f / 4.0f; + if ( z.scale > 4.0f ) { + z.scale = 4.0f; + } + + Sys_UpdateWindows(W_Z | W_Z_OVERLAY); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewZzoomout() { + z.scale *= 4.0f / 5.0f; + if ( z.scale < 0.125f ) { + z.scale = 0.125f; + } + + Sys_UpdateWindows(W_Z | W_Z_OVERLAY); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewSide() { + m_pXYWnd->SetViewType(XZ); + m_pXYWnd->PositionView(); + Sys_UpdateWindows(W_XY); +} + +static void UpdateGrid(void) +{ + // g_qeglobals.d_gridsize = 1 << g_qeglobals.d_gridsize; + if (g_PrefsDlg.m_bSnapTToGrid) { + g_qeglobals.d_savedinfo.m_nTextureTweak = g_qeglobals.d_gridsize; + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnGrid1(unsigned int nID) { + switch (nID) + { + case ID_GRID_1: + g_qeglobals.d_gridsize = 1; + break; + case ID_GRID_2: + g_qeglobals.d_gridsize = 2; + break; + case ID_GRID_4: + g_qeglobals.d_gridsize = 4; + break; + case ID_GRID_8: + g_qeglobals.d_gridsize = 8; + break; + case ID_GRID_16: + g_qeglobals.d_gridsize = 16; + break; + case ID_GRID_32: + g_qeglobals.d_gridsize = 32; + break; + case ID_GRID_64: + g_qeglobals.d_gridsize = 64; + break; + case ID_GRID_POINT5: + g_qeglobals.d_gridsize = 0.5f; + break; + case ID_GRID_POINT25: + g_qeglobals.d_gridsize = 0.25f; + break; + case ID_GRID_POINT125: + g_qeglobals.d_gridsize = 0.125f; + break; + //case ID_GRID_POINT0625: + // g_qeglobals.d_gridsize = 0.0625f; + // break; + } + + UpdateGrid(); + + SetGridStatus(); + SetGridChecks(nID); + Sys_UpdateWindows(W_XY | W_Z); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnTexturesShowinuse() { + Sys_BeginWait(); + Texture_ShowInuse(); + g_Inspectors->texWnd.RedrawWindow(); +} + +// from TexWnd.cpp +extern bool texture_showinuse; + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnUpdateTexturesShowinuse(CCmdUI *pCmdUI) { + pCmdUI->SetCheck(texture_showinuse); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnTexturesInspector() { + DoSurface(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnMiscFindbrush() { + DoFind(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnMiscGamma() { + float fSave = g_qeglobals.d_savedinfo.fGamma; + DoGamma(); + if (fSave != g_qeglobals.d_savedinfo.fGamma) { + MessageBox("You must restart Q3Radiant for Gamma settings to take place"); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnMiscNextleakspot() { + Pointfile_Next(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnMiscPreviousleakspot() { + Pointfile_Prev(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnMiscPrintxy() { + WXY_Print(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ + +void UpdateRadiantColor( float r, float g, float b, float a ) { + if ( g_pParentWnd ) { + g_pParentWnd->RoutineProcessing(); + } +} + +bool DoColor( int iIndex ) { + COLORREF cr = (int)(g_qeglobals.d_savedinfo.colors[iIndex][0]*255) + + (((int)(g_qeglobals.d_savedinfo.colors[iIndex][1]*255))<<8) + + (((int)(g_qeglobals.d_savedinfo.colors[iIndex][2]*255))<<16); + + CDialogColorPicker dlg(cr); + + dlg.UpdateParent = UpdateRadiantColor; + + if ( dlg.DoModal() == IDOK ) { + g_qeglobals.d_savedinfo.colors[iIndex][0] = (dlg.GetColor() & 255)/255.0; + g_qeglobals.d_savedinfo.colors[iIndex][1] = ((dlg.GetColor() >> 8)&255)/255.0; + g_qeglobals.d_savedinfo.colors[iIndex][2] = ((dlg.GetColor() >> 16)&255)/255.0; + + Sys_UpdateWindows (W_ALL); + return true; + } else { + return false; + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +extern void Select_SetKeyVal(const char *key, const char *val); +void CMainFrame::OnMiscSelectentitycolor() { + + entity_t *ent = NULL; + if (QE_SingleBrush(true, true)) { + ent = selected_brushes.next->owner; + CString strColor = ValueForKey(ent, "_color"); + if (strColor.GetLength() > 0) { + float fR, fG, fB; + int n = sscanf(strColor, "%f %f %f", &fR, &fG, &fB); + if (n == 3) { + g_qeglobals.d_savedinfo.colors[COLOR_ENTITY][0] = fR; + g_qeglobals.d_savedinfo.colors[COLOR_ENTITY][1] = fG; + g_qeglobals.d_savedinfo.colors[COLOR_ENTITY][2] = fB; + } + } + } + + if (DoColor(COLOR_ENTITY)) { + char buffer[100]; + sprintf(buffer, "%f %f %f", g_qeglobals.d_savedinfo.colors[COLOR_ENTITY][0], g_qeglobals.d_savedinfo.colors[COLOR_ENTITY][1],g_qeglobals.d_savedinfo.colors[COLOR_ENTITY][2]); + Select_SetKeyVal("_color", buffer); + if (ent) { + g_Inspectors->UpdateEntitySel(ent->eclass); + } + Sys_UpdateWindows(W_ALL); + } +} + +CString strFindKey; +CString strFindValue; +CString strReplaceKey; +CString strReplaceValue; +bool gbWholeStringMatchOnly = true; +bool gbSelectAllMatchingEnts= false; +brush_t* gpPrevEntBrushFound = NULL; + +// all this because there's no ansi stristr(), sigh... +// +LPCSTR String_ToLower(LPCSTR psString) +{ + const int iBufferSize = 4096; + static char sString[8][iBufferSize]; + static int iIndex=0; + + if (strlen(psString)>=iBufferSize) + { + assert(0); + common->Printf("String_ToLower(): Warning, input string was %d bytes too large, performing strlwr() inline!\n",strlen(psString)-(iBufferSize-1)); + return strlwr(const_cast(psString)); + } + + iIndex = ++ iIndex & 7; + + strcpy(sString[iIndex],psString); + strlwr(sString[iIndex]); + + return sString[iIndex]; +} + + +bool FindNextBrush(brush_t* pPrevFoundBrush) // can be NULL for fresh search +{ + bool bFoundSomething = false; + entity_t *pLastFoundEnt; + brush_t *pLastFoundBrush; + + CWaitCursor waitcursor; + + Select_Deselect(true); // bool bDeSelectToListBack + + // see whether to start search from prev_brush->next by checking if prev_brush is still in the active list... + // + brush_t *pStartBrush = active_brushes.next; + + if (pPrevFoundBrush && !gbSelectAllMatchingEnts) + { + brush_t *pPrev = NULL; + for (brush_t* b = active_brushes.next ; b != &active_brushes ; b = b->next) + { + if (pPrev == pPrevFoundBrush && pPrevFoundBrush) + { + pStartBrush = b; + break; + } + pPrev = b; + } + } + + // now do the search proper... + // + int iBrushesScanned = 0; + int iBrushesSelected=0; + int iEntsScanned = 0; + + brush_t* pNextBrush; + for (brush_t* b = pStartBrush; b != &active_brushes ; b = pNextBrush) + { + // setup the ptr before going any further (because selecting a brush down below moves it to a + // different link list), but we need to ensure that the next brush has a different ent-owner than the current + // one, or multi-brush ents will confuse the list process if they get selected (infinite loop badness)... + // + // pNextBrush = &active_brushes; // default to loop-stop condition + pNextBrush = b->next; + while (pNextBrush->owner == b->owner && pNextBrush!=&active_brushes) + { + pNextBrush = pNextBrush->next; + } + + iBrushesScanned++; + + // a simple progress bar so they don't think it's locked up on long searches... + // + static int iDotBodge=0; + if (!(++iDotBodge&15)) + common->Printf("."); // cut down on printing + + bool bMatch = false; + entity_t* ent = b->owner; + + if (ent && ent!= world_entity) // needed! + { + iEntsScanned++; + if (FilterBrush (b)) + continue; + + // only check the find-key if there was one specified... + // + if (!strFindKey.IsEmpty()) + { + const char *psEntFoundValue = ValueForKey(ent, strFindKey); + + if (strlen(psEntFoundValue) + && + ( +// (stricmp(strFindValue, psEntFoundValue)==0) // found this exact key/value + ( + (gbWholeStringMatchOnly && stricmp(psEntFoundValue, strFindValue)==0) + || + (!gbWholeStringMatchOnly && strstr(String_ToLower(psEntFoundValue), String_ToLower(strFindValue))) + ) + || // or + (strFindValue.IsEmpty()) // any value for this key if blank value search specified + ) + ) + { + bMatch = true; + } + } + else + { + // no FIND key specified, so just scan all of them... + // + int iNumEntKeys = GetNumKeys(ent); + for (int i=0; i search specified then any found-value is ok + || + (gbWholeStringMatchOnly && stricmp(psEntFoundValue, strFindValue)==0) + || + (!gbWholeStringMatchOnly && strstr(String_ToLower(psEntFoundValue), String_ToLower(strFindValue))) + ) + { + if (!gbWholeStringMatchOnly && strstr(String_ToLower(psEntFoundValue), String_ToLower(strFindValue))) + { +// OutputDebugString(va("Matching because: psEntFoundValue '%s' & strFindValue '%s'\n",psEntFoundValue, strFindValue)); +// Sys_Printf("Matching because: psEntFoundValue '%s' & strFindValue '%s'\n",psEntFoundValue, strFindValue); + +// if (strstr(psEntFoundValue,"killsplat")) +// { +// DebugBreak(); +// } + } + bMatch = true; + break; + } + } + } + } + + if (bMatch) + { + bFoundSomething = true; + pLastFoundEnt = ent; + pLastFoundBrush = b; + iBrushesSelected++; + + g_bScreenUpdates = false; // !!!!!!!!!!!!!!!!!!!!!!!!!!!! + + Select_Brush(b); + + g_bScreenUpdates = true; // !!!!!!!!!!!!!!!!!!!!!!!!!!!! + + if (!gbSelectAllMatchingEnts) + break; + } + } + } + if (gbSelectAllMatchingEnts) + { + common->Printf("\nBrushes Selected: %d (Brushes Scanned %d, Ents Scanned %d)\n", iBrushesSelected, iBrushesScanned, iEntsScanned); + } + + if (bFoundSomething) + { + idVec3 v3Origin; + + if (pLastFoundEnt->origin[0] != 0.0f || pLastFoundEnt->origin[1] != 0.0f || pLastFoundEnt->origin[2] != 0.0f) + { + VectorCopy(pLastFoundEnt->origin,v3Origin); + } + else + { + // pLastFoundEnt's origin is zero, so use average point of brush mins maxs instead... + // + v3Origin[0] = (pLastFoundBrush->mins[0] + pLastFoundBrush->maxs[0])/2; + v3Origin[1] = (pLastFoundBrush->mins[1] + pLastFoundBrush->maxs[1])/2; + v3Origin[2] = (pLastFoundBrush->mins[2] + pLastFoundBrush->maxs[2])/2; + } + + // got one, jump the camera to it... + // + VectorCopy(v3Origin, g_pParentWnd->GetCamera()->Camera().origin); + g_pParentWnd->GetCamera()->Camera().origin[1] -= 32; // back off a touch to look at it + g_pParentWnd->GetCamera()->Camera().angles[0] = 0; + g_pParentWnd->GetCamera()->Camera().angles[1] = 90; + g_pParentWnd->GetCamera()->Camera().angles[2] = 0; + + // force main screen into XY camera mode (just in case)... + // + g_pParentWnd->SetActiveXY(g_pParentWnd->GetXYWnd()); + g_pParentWnd->GetXYWnd()->PositionView(); + + Sys_UpdateWindows (W_ALL); + // + // and record for next find request (F3)... + // + gpPrevEntBrushFound = pLastFoundBrush; + } + + return bFoundSomething; +} + + +void CMainFrame::OnMiscFindOrReplaceEntity() +{ + CEntKeyFindReplace FindReplace(&strFindKey, &strFindValue, &strReplaceKey, &strReplaceValue, &gbWholeStringMatchOnly, &gbSelectAllMatchingEnts); + switch (FindReplace.DoModal()) + { + case ID_RET_REPLACE: + { + brush_t* next = NULL; + int iOccurences = 0; + for (brush_t* b = active_brushes.next ; b != &active_brushes ; b = next) + { + next = b->next; // important to do this here, in case brush gets linked to a different list + entity_t* ent = b->owner; + + if (ent) // needed! + { + if (FilterBrush (b)) + continue; + + const char *psEntFoundValue = ValueForKey(ent, strFindKey); + + if (stricmp(strFindValue, psEntFoundValue)==0 || // found this exact key/value + (strlen(psEntFoundValue) && strFindValue.IsEmpty()) // or any value for this key if blank value search specified + ) + { + // found this search key/value, so delete it... + // + DeleteKey(ent,strFindKey); + // + // and replace with the new key/value (if specified)... + // + if (!strReplaceKey.IsEmpty() && !strReplaceValue.IsEmpty()) + { + SetKeyValue (ent, strReplaceKey, strReplaceValue); + } + iOccurences++; + } + } + } + if (iOccurences) + { + common->Printf("%d occurence(s) replaced\n",iOccurences); + } + else + { + common->Printf("Nothing found to replace\n"); + } + } + break; + case ID_RET_FIND: + { + gpPrevEntBrushFound = NULL; + FindNextBrush(NULL); + } + break; + } +} +void CMainFrame::OnMiscFindNextEntity() +{ + // try it once, if it fails, try it again from top, and give up if still failed after that... + // + if (!FindNextBrush(gpPrevEntBrushFound)) + { + gpPrevEntBrushFound = NULL; + FindNextBrush(NULL); + } +} + +void CMainFrame::OnMiscSetViewPos() +{ + CString psNewCoords = GetString("Input coords (x y z [rot])\n\nUse spaces to seperate numbers"); + if (!psNewCoords.IsEmpty()) + { + idVec3 v3Viewpos; + float fYaw = 0; + + psNewCoords.Remove(','); + int iArgsFound = sscanf(psNewCoords,"%f %f %f",&v3Viewpos[0], &v3Viewpos[1], &v3Viewpos[2]); + if (iArgsFound == 3) + { + // try for an optional 4th (note how this wasn't part of the sscanf() above, so I can check 1st-3, not just any 3) + // + int iArgsFound = sscanf(psNewCoords,"%f %f %f %f", &v3Viewpos[0], &v3Viewpos[1], &v3Viewpos[2], &fYaw); + if (iArgsFound != 4) + { + fYaw = 0; // jic + } + + g_pParentWnd->GetCamera()->Camera().angles[YAW] = fYaw; + VectorCopy (v3Viewpos, g_pParentWnd->GetCamera()->Camera().origin); + VectorCopy (v3Viewpos, g_pParentWnd->GetXYWnd()->GetOrigin()); + Sys_UpdateWindows (W_ALL); + } + else + { + ErrorBox(va("\"%s\" wasn't 3 valid floats with spaces",psNewCoords)); + } + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnTexturebk() { + DoColor(COLOR_TEXTUREBACK); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnColorsMajor() { + DoColor(COLOR_GRIDMAJOR); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnColorsMinor() { + DoColor(COLOR_GRIDMINOR); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnColorsXybk() { + DoColor(COLOR_GRIDBACK); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnBrush3sided() { + Undo_Start("3 sided"); + Undo_AddBrushList(&selected_brushes); + Brush_MakeSided(3); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnBrush4sided() { + Undo_Start("4 sided"); + Undo_AddBrushList(&selected_brushes); + Brush_MakeSided(4); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnBrush5sided() { + Undo_Start("5 sided"); + Undo_AddBrushList(&selected_brushes); + Brush_MakeSided(5); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnBrush6sided() { + Undo_Start("6 sided"); + Undo_AddBrushList(&selected_brushes); + Brush_MakeSided(6); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnBrush7sided() { + Undo_Start("7 sided"); + Undo_AddBrushList(&selected_brushes); + Brush_MakeSided(7); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnBrush8sided() { + Undo_Start("8 sided"); + Undo_AddBrushList(&selected_brushes); + Brush_MakeSided(8); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnBrush9sided() { + Undo_Start("9 sided"); + Undo_AddBrushList(&selected_brushes); + Brush_MakeSided(9); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnBrushArbitrarysided() { + Undo_Start("arbitrary sided"); + Undo_AddBrushList(&selected_brushes); + DoSides(); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnBrushFlipx() { + Undo_Start("flip X"); + Undo_AddBrushList(&selected_brushes); + + Select_FlipAxis(0); + for (brush_t * b = selected_brushes.next; b != &selected_brushes; b = b->next) { + if (b->owner->eclass->fixedsize) { + char buf[16]; + float a = FloatForKey(b->owner, "angle"); + a = div((180 - a), 180).rem; + SetKeyValue(b->owner, "angle", itoa(a, buf, 10)); + Brush_Build(b); + } + } + Patch_ToggleInverted(); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnBrushFlipy() { + Undo_Start("flip Y"); + Undo_AddBrushList(&selected_brushes); + + Select_FlipAxis(1); + for (brush_t * b = selected_brushes.next; b != &selected_brushes; b = b->next) { + if (b->owner->eclass->fixedsize) { + float a = FloatForKey(b->owner, "angle"); + if (a == 0 || a == 180 || a == 360) { + continue; + } + + if (a == 90 || a == 270) { + a += 180; + } + else if (a > 270) { + a += 90; + } + else if (a > 180) { + a -= 90; + } + else if (a > 90) { + a += 90; + } + else { + a -= 90; + } + + a = (int)a % 360; + + char buf[16]; + SetKeyValue(b->owner, "angle", itoa(a, buf, 10)); + Brush_Build(b); + } + } + Patch_ToggleInverted(); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnBrushFlipz() { + Undo_Start("flip Z"); + Undo_AddBrushList(&selected_brushes); + Select_FlipAxis(2); + Patch_ToggleInverted(); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnBrushRotatex() { + Undo_Start("rotate X"); + Undo_AddBrushList(&selected_brushes); + Select_RotateAxis(0, 90); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnBrushRotatey() { + Undo_Start("rotate Y"); + Undo_AddBrushList(&selected_brushes); + Select_RotateAxis(1, 90); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnBrushRotatez() { + Undo_Start("rotate Z"); + Undo_AddBrushList(&selected_brushes); + Select_RotateAxis(2, 90); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnRegionOff() { + Map_RegionOff(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnRegionSetbrush() { + Map_RegionBrush(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnRegionSetselection() { + Map_RegionSelectedBrushes(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnRegionSettallbrush() { + Map_RegionTallBrush(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnRegionSetxy() { + Map_RegionXY(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionArbitraryrotation() { + // if (ActiveXY()) ActiveXY()->UndoCopy(); + Undo_Start("arbitrary rotation"); + Undo_AddBrushList(&selected_brushes); + + CRotateDlg dlg; + dlg.DoModal(); + + // DoRotate (); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionClone() { + // if (ActiveXY()) ActiveXY()->UndoCopy(); + Select_Clone(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionConnect() { + ConnectEntities(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionMakehollow() { + // if (ActiveXY()) ActiveXY()->UndoCopy(); + Undo_Start("hollow"); + Undo_AddBrushList(&selected_brushes); + CSG_MakeHollow(); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionCsgsubtract() { + // if (ActiveXY()) ActiveXY()->UndoCopy(); + Undo_Start("CSG subtract"); + CSG_Subtract(); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionCsgmerge() { + // if (ActiveXY()) ActiveXY()->UndoCopy(); + Undo_Start("CSG merge"); + Undo_AddBrushList(&selected_brushes); + CSG_Merge(); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionDelete() { + brush_t *brush; + + // if (ActiveXY()) ActiveXY()->UndoCopy(); + Undo_Start("delete"); + Undo_AddBrushList(&selected_brushes); + + // add all deleted entities to the undo + for (brush = selected_brushes.next; brush != &selected_brushes; brush = brush->next) { + Undo_AddEntity(brush->owner); + } + + // NOTE: Select_Delete does NOT delete entities + Select_Delete(); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionDeselect() { + if (!ByeByeSurfaceDialog()) { + if (g_bClipMode) { + OnViewClipper(); + } else if (g_bRotateMode) { + OnSelectMouserotate(); + } else if (g_bScaleMode) { + OnSelectMousescale(); + } else if (g_bPathMode) { + if (ActiveXY()) { + ActiveXY()->KillPathMode(); + } + } else if (g_bAxialMode) { + g_bAxialMode = false; + Sys_UpdateWindows(W_CAMERA); + } else { + if (g_qeglobals.d_select_mode == sel_curvepoint && g_qeglobals.d_num_move_points > 0) { + g_qeglobals.d_num_move_points = 0; + Sys_UpdateWindows(W_ALL); + } else { + Select_Deselect(); + SetStatusText(2, " "); + } + } + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionDragedges() { + if (g_qeglobals.d_select_mode == sel_edge) { + g_qeglobals.d_select_mode = sel_brush; + Sys_UpdateWindows(W_ALL); + } + else { + SetupVertexSelection(); + if (g_qeglobals.d_numpoints) { + g_qeglobals.d_select_mode = sel_edge; + } + + Sys_UpdateWindows(W_ALL); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionDragvertecies() { + if (g_qeglobals.d_select_mode == sel_vertex || g_qeglobals.d_select_mode == sel_curvepoint) { + g_qeglobals.d_select_mode = sel_brush; + Sys_UpdateWindows(W_ALL); + } + else { + // --if (QE_SingleBrush() && selected_brushes.next->patchBrush) + if (OnlyPatchesSelected()) { + Patch_EditPatch(); + } + else if (!AnyPatchesSelected()) { + SetupVertexSelection(); + if (g_qeglobals.d_numpoints) { + g_qeglobals.d_select_mode = sel_vertex; + } + } + + Sys_UpdateWindows(W_ALL); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionCenterOrigin() { + Undo_Start("center origin"); + Undo_AddBrushList(&selected_brushes); + Select_CenterOrigin(); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionSelectcompletetall() { + //if (ActiveXY()) { + // ActiveXY()->UndoCopy(); + //} + + Select_CompleteTall(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionSelectinside() { + Select_Inside(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionSelectpartialtall() { + Select_PartialTall(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionSelecttouching() { + Select_Touching(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionUngroupentity() { + Select_Ungroup(); +} + +void CMainFrame::OnAutocaulk() +{ + Select_AutoCaulk(); +} +void CMainFrame::OnUpdateAutocaulk(CCmdUI* pCmdUI) +{ + pCmdUI->Enable( selected_brushes.next != &selected_brushes); +} + + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnTexturesPopup() { + HandlePopup(this, IDR_POPUP_TEXTURE); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSplinesPopup() { + HandlePopup(this, IDR_POPUP_SPLINE); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnPopupSelection() { + HandlePopup(this, IDR_POPUP_SELECTION); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewChange() { + OnViewNextview(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewCameraupdate() { + g_qeglobals.flatRotation++; + + if (g_qeglobals.flatRotation > 2) { + g_qeglobals.flatRotation = 0; + } + + if (g_qeglobals.flatRotation) { + g_qeglobals.rotateAxis = 0; + if (ActiveXY()->GetViewType() == XY) { + g_qeglobals.rotateAxis = 2; + } else if (ActiveXY()->GetViewType() == XZ) { + g_qeglobals.rotateAxis = 1; + } + } + Select_InitializeRotation(); + Sys_UpdateWindows(W_CAMERA | W_XY); +} + + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSizing(UINT fwSide, LPRECT pRect) { + CFrameWnd::OnSizing(fwSide, pRect); + GetClientRect(g_rctOld); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnHelpAbout() { + DoAbout(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewClipper() { + if (ActiveXY()) { + if (ActiveXY()->ClipMode()) { + ActiveXY()->SetClipMode(false); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_VIEW_CLIPPER, FALSE); + } + else { + if (ActiveXY()->RotateMode()) { + OnSelectMouserotate(); + } + + ActiveXY()->SetClipMode(true); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_VIEW_CLIPPER); + } + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCameraAngledown() { + m_pCamWnd->Camera().angles[0] -= SPEED_TURN; + if (m_pCamWnd->Camera().angles[0] < -85) { + m_pCamWnd->Camera().angles[0] = -85; + } + + Sys_UpdateWindows(W_CAMERA | W_XY_OVERLAY); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCameraAngleup() { + m_pCamWnd->Camera().angles[0] += SPEED_TURN; + if (m_pCamWnd->Camera().angles[0] > 85) { + m_pCamWnd->Camera().angles[0] = 85; + } + + Sys_UpdateWindows(W_CAMERA | W_XY_OVERLAY); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCameraBack() { + VectorMA(m_pCamWnd->Camera().origin, -SPEED_MOVE, m_pCamWnd->Camera().forward, m_pCamWnd->Camera().origin); + + int nUpdate = (g_PrefsDlg.m_bCamXYUpdate) ? (W_CAMERA | W_XY) : (W_CAMERA); + Sys_UpdateWindows(nUpdate); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCameraDown() { + m_pCamWnd->Camera().origin[2] -= SPEED_MOVE; + Sys_UpdateWindows(W_CAMERA | W_XY | W_Z); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCameraForward() { + VectorMA(m_pCamWnd->Camera().origin, SPEED_MOVE, m_pCamWnd->Camera().forward, m_pCamWnd->Camera().origin); + + int nUpdate = (g_PrefsDlg.m_bCamXYUpdate) ? (W_CAMERA | W_XY) : (W_CAMERA); + Sys_UpdateWindows(nUpdate); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCameraLeft() { + m_pCamWnd->Camera().angles[1] += SPEED_TURN; + + int nUpdate = (g_PrefsDlg.m_bCamXYUpdate) ? (W_CAMERA | W_XY) : (W_CAMERA); + Sys_UpdateWindows(nUpdate); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCameraRight() { + m_pCamWnd->Camera().angles[1] -= SPEED_TURN; + + int nUpdate = (g_PrefsDlg.m_bCamXYUpdate) ? (W_CAMERA | W_XY) : (W_CAMERA); + Sys_UpdateWindows(nUpdate); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCameraStrafeleft() { + VectorMA(m_pCamWnd->Camera().origin, -SPEED_MOVE, m_pCamWnd->Camera().right, m_pCamWnd->Camera().origin); + + int nUpdate = (g_PrefsDlg.m_bCamXYUpdate) ? (W_CAMERA | W_XY) : (W_CAMERA); + Sys_UpdateWindows(nUpdate); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCameraStraferight() { + VectorMA(m_pCamWnd->Camera().origin, SPEED_MOVE, m_pCamWnd->Camera().right, m_pCamWnd->Camera().origin); + + int nUpdate = (g_PrefsDlg.m_bCamXYUpdate) ? (W_CAMERA | W_XY) : (W_CAMERA); + Sys_UpdateWindows(nUpdate); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCameraUp() { + m_pCamWnd->Camera().origin[2] += SPEED_MOVE; + Sys_UpdateWindows(W_CAMERA | W_XY | W_Z); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnGridToggle() { + g_qeglobals.d_showgrid ^= 1; + Sys_UpdateWindows(W_XY | W_Z); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnPrefs() { + BOOL bToolbar = g_PrefsDlg.m_bWideToolbar; + g_PrefsDlg.LoadPrefs(); + if (g_PrefsDlg.DoModal() == IDOK) { + if (g_PrefsDlg.m_bWideToolbar != bToolbar) { + MessageBox("You need to restart Q3Radiant for the view changes to take place."); + } + + g_Inspectors->texWnd.UpdatePrefs(); + + CMenu *pMenu = GetMenu(); + if (pMenu) { + pMenu->CheckMenuItem(ID_SNAPTOGRID, MF_BYCOMMAND | (!g_PrefsDlg.m_bNoClamp) ? MF_CHECKED : MF_UNCHECKED); + } + } +} + +// +// ======================================================================================================================= +// 0 = radiant styel 1 = qe4 style +// ======================================================================================================================= +// +void CMainFrame::SetWindowStyle(int nStyle) { +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnTogglecamera() { + if (m_pCamWnd->IsWindowVisible()) { + m_pCamWnd->ShowWindow(SW_HIDE); + } else { + m_pCamWnd->ShowWindow(SW_SHOW); + } +} + + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnToggleview() { + if (m_pXYWnd && m_pXYWnd->GetSafeHwnd()) { + if (m_pXYWnd->IsWindowVisible()) { + m_pXYWnd->ShowWindow(SW_HIDE); + } else { + m_pXYWnd->ShowWindow(SW_SHOW); + } + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnTogglez() { + if (m_pZWnd && m_pZWnd->GetSafeHwnd()) { + if (m_pZWnd->IsWindowVisible()) { + m_pZWnd->ShowWindow(SW_HIDE); + } else { + m_pZWnd->ShowWindow(SW_SHOW); + } + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnToggleLock() { + g_PrefsDlg.m_bTextureLock = !g_PrefsDlg.m_bTextureLock; + + CMenu *pMenu = GetMenu(); + if (pMenu) { + pMenu->CheckMenuItem(ID_TOGGLE_LOCK, MF_BYCOMMAND | (g_PrefsDlg.m_bTextureLock) ? MF_CHECKED : MF_UNCHECKED); + } + + g_PrefsDlg.SavePrefs(); + SetGridStatus(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnEditMapinfo() { + CMapInfo dlg; + dlg.DoModal(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnEditEntityinfo() { + CEntityListDlg::ShowDialog(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewNextview() { + if (m_pXYWnd->GetViewType() == XY) { + m_pXYWnd->SetViewType(XZ); + } + else if (m_pXYWnd->GetViewType() == XZ) { + m_pXYWnd->SetViewType(YZ); + } + else { + m_pXYWnd->SetViewType(XY); + } + + m_pXYWnd->PositionView(); + if (g_qeglobals.flatRotation) { + g_qeglobals.rotateAxis = 0; + if (ActiveXY()->GetViewType() == XY) { + g_qeglobals.rotateAxis = 2; + } else if (ActiveXY()->GetViewType() == XZ) { + g_qeglobals.rotateAxis = 1; + } + } + Sys_UpdateWindows(W_XY | W_CAMERA); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnHelpCommandlist() { + CCommandsDlg dlg; + dlg.DoModal(); +#if 0 + if (g_b3Dfx) { + C3DFXCamWnd *pWnd = new C3DFXCamWnd(); + CRect rect(50, 50, 400, 400); + pWnd->Create(_3DFXCAMERA_WINDOW_CLASS, "", QE3_CHILDSTYLE, rect, this, 1234); + pWnd->ShowWindow(SW_SHOW); + } +#endif +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnFileNewproject() +{ +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::UpdateStatusText() { + for (int n = 0; n < 6; n++) { + if (m_strStatus[n].GetLength() >= 0 && m_wndStatusBar.GetSafeHwnd()) { + m_wndStatusBar.SetPaneText(n, m_strStatus[n]); + } + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::SetStatusText(int nPane, const char *pText) { + if (pText && nPane <= 5 && nPane >= 0) { + m_strStatus[nPane] = pText; + UpdateStatusText(); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::UpdateWindows(int nBits) { + + if (!g_bScreenUpdates) { + return; + } + + if (nBits & (W_XY | W_XY_OVERLAY)) { + if (m_pXYWnd) { + m_pXYWnd->RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW); + } + + if (m_pXZWnd) { + m_pXZWnd->RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW); + } + + if (m_pYZWnd) { + m_pYZWnd->RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW); + } + } + + if (nBits & W_CAMERA || ((nBits & W_CAMERA_IFON) && m_bCamPreview)) { + if (m_pCamWnd) { + m_pCamWnd->RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW); + } + } + + if (nBits & (W_Z | W_Z_OVERLAY)) { + if (m_pZWnd) { + m_pZWnd->RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW); + } + } + + if (nBits & W_TEXTURE) { + g_Inspectors->texWnd.RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void WINAPI Sys_UpdateWindows(int nBits) { + if (g_PrefsDlg.m_bQE4Painting) { + g_nUpdateBits |= nBits; + } + else if ( g_pParentWnd ) { + g_pParentWnd->UpdateWindows(nBits); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnFlipClip() { + if (m_pActiveXY) { + m_pActiveXY->FlipClip(); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnClipSelected() { + if (m_pActiveXY && m_pActiveXY->ClipMode()) { + Undo_Start("clip selected"); + Undo_AddBrushList(&selected_brushes); + m_pActiveXY->Clip(); + Undo_EndBrushList(&selected_brushes); + Undo_End(); + } else { + if (g_bPatchBendMode) { + Patch_BendHandleENTER(); + } else if (g_bAxialMode) { + + } + //else if (g_bPatchBendMode) { + // Patch_InsDelHandleENTER(); + //} + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSplitSelected() { + if (m_pActiveXY) { + Undo_Start("split selected"); + Undo_AddBrushList(&selected_brushes); + m_pActiveXY->SplitClip(); + Undo_EndBrushList(&selected_brushes); + Undo_End(); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +CXYWnd *CMainFrame::ActiveXY() { + return m_pActiveXY; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnToggleviewXz() { + if (m_pXZWnd && m_pXZWnd->GetSafeHwnd()) { + // get windowplacement doesn't actually save this so we will here + g_PrefsDlg.m_bXZVis = m_pXZWnd->IsWindowVisible(); + if (g_PrefsDlg.m_bXZVis) { + m_pXZWnd->ShowWindow(SW_HIDE); + } else { + m_pXZWnd->ShowWindow(SW_SHOW); + } + + g_PrefsDlg.m_bXZVis ^= 1; + g_PrefsDlg.SavePrefs(); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnToggleviewYz() { + if (m_pYZWnd && m_pYZWnd->GetSafeHwnd()) { + g_PrefsDlg.m_bYZVis = m_pYZWnd->IsWindowVisible(); + if (g_PrefsDlg.m_bYZVis) { + m_pYZWnd->ShowWindow(SW_HIDE); + } else { + m_pYZWnd->ShowWindow(SW_SHOW); + } + + g_PrefsDlg.m_bYZVis ^= 1; + g_PrefsDlg.SavePrefs(); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ + +void CMainFrame::OnToggleToolbar() +{ + ShowControlBar(&m_wndToolBar, !m_wndToolBar.IsWindowVisible(), false); +} + +void CMainFrame::OnToggleTextureBar() +{ + ShowControlBar(&m_wndTextureBar, !m_wndTextureBar.IsWindowVisible(), false); +} + + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnColorsBrush() { + DoColor(COLOR_BRUSHES); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnColorsClipper() { + DoColor(COLOR_CLIPPER); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnColorsGridtext() { + DoColor(COLOR_GRIDTEXT); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnColorsSelectedbrush() { + DoColor(COLOR_SELBRUSHES); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnColorsGridblock() { + DoColor(COLOR_GRIDBLOCK); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnColorsViewname() { + DoColor(COLOR_VIEWNAME); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnColorSetoriginal() { + for (int i = 0; i < 3; i++) { + g_qeglobals.d_savedinfo.colors[COLOR_TEXTUREBACK][i] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][i] = 1.0f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDMINOR][i] = 0.75f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDMAJOR][i] = 0.5f; + g_qeglobals.d_savedinfo.colors[COLOR_CAMERABACK][i] = 0.25f; + } + + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBLOCK][0] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBLOCK][1] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBLOCK][2] = 1.0f; + + g_qeglobals.d_savedinfo.colors[COLOR_GRIDTEXT][0] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDTEXT][1] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDTEXT][2] = 0.0f; + + g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][0] = 1.0f; + g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][1] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][2] = 0.0f; + + g_qeglobals.d_savedinfo.colors[COLOR_CLIPPER][0] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_CLIPPER][1] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_CLIPPER][2] = 1.0f; + + g_qeglobals.d_savedinfo.colors[COLOR_BRUSHES][0] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_BRUSHES][1] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_BRUSHES][2] = 0.0f; + + g_qeglobals.d_savedinfo.colors[COLOR_VIEWNAME][0] = 0.5f; + g_qeglobals.d_savedinfo.colors[COLOR_VIEWNAME][1] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_VIEWNAME][2] = 0.75f; + + g_qeglobals.d_savedinfo.colors[COLOR_PRECISION_CROSSHAIR][0] = 1.0; + g_qeglobals.d_savedinfo.colors[COLOR_PRECISION_CROSSHAIR][1] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_PRECISION_CROSSHAIR][2] = 1.0; + + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnColorSetqer() { + for (int i = 0; i < 3; i++) { + g_qeglobals.d_savedinfo.colors[COLOR_TEXTUREBACK][i] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][i] = 1.0f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDMINOR][i] = 1.0f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDMAJOR][i] = 0.5f; + g_qeglobals.d_savedinfo.colors[COLOR_CAMERABACK][i] = 0.25f; + } + + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBLOCK][0] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBLOCK][1] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBLOCK][2] = 1.0f; + + g_qeglobals.d_savedinfo.colors[COLOR_GRIDTEXT][0] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDTEXT][1] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDTEXT][2] = 0.0f; + + g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][0] = 1.0f; + g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][1] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][2] = 0.0f; + + g_qeglobals.d_savedinfo.colors[COLOR_CLIPPER][0] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_CLIPPER][1] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_CLIPPER][2] = 1.0f; + + g_qeglobals.d_savedinfo.colors[COLOR_BRUSHES][0] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_BRUSHES][1] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_BRUSHES][2] = 0.0f; + + g_qeglobals.d_savedinfo.colors[COLOR_VIEWNAME][0] = 0.5f; + g_qeglobals.d_savedinfo.colors[COLOR_VIEWNAME][1] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_VIEWNAME][2] = 0.75f; + + g_qeglobals.d_savedinfo.colors[COLOR_PRECISION_CROSSHAIR][0] = 1.0; + g_qeglobals.d_savedinfo.colors[COLOR_PRECISION_CROSSHAIR][1] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_PRECISION_CROSSHAIR][2] = 1.0; + + Sys_UpdateWindows(W_ALL); +} + +//FIXME: these just need to be read from a def file +void CMainFrame::OnColorSetSuperMal() { + OnColorSetqer(); + g_qeglobals.d_savedinfo.colors[COLOR_TEXTUREBACK][0] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_TEXTUREBACK][1] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_TEXTUREBACK][2] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][0] = 0.35f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][1] = 0.35f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][2] = 0.35f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDMAJOR][0] = 0.5f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDMAJOR][1] = 0.5f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDMAJOR][2] = 0.5f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDMINOR][0] = 0.39f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDMINOR][1] = 0.39f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDMINOR][2] = 0.39f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDTEXT][0] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDTEXT][1] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDTEXT][2] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_BRUSHES][0] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_BRUSHES][1] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_BRUSHES][2] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][0] = 1.0f; + g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][1] = 0.90f; + g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][2] = 0.90f; + g_qeglobals.d_savedinfo.colors[COLOR_VIEWNAME][0] = 0.5f; + g_qeglobals.d_savedinfo.colors[COLOR_VIEWNAME][1] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_VIEWNAME][2] = 0.74f; + + + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnColorSetblack() { + for (int i = 0; i < 3; i++) { + g_qeglobals.d_savedinfo.colors[COLOR_TEXTUREBACK][i] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][i] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDMINOR][i] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_CAMERABACK][i] = 0.25f; + } + + g_qeglobals.d_savedinfo.colors[COLOR_GRIDMAJOR][0] = 0.3f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDMAJOR][1] = 0.5f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDMAJOR][2] = 0.5f; + + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBLOCK][0] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBLOCK][1] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBLOCK][2] = 1.0f; + + g_qeglobals.d_savedinfo.colors[COLOR_GRIDTEXT][0] = 1.0f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDTEXT][1] = 1.0f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDTEXT][2] = 1.0f; + + g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][0] = 1.0f; + g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][1] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][2] = 0.0f; + + g_qeglobals.d_savedinfo.colors[COLOR_CLIPPER][0] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_CLIPPER][1] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_CLIPPER][2] = 1.0f; + + g_qeglobals.d_savedinfo.colors[COLOR_BRUSHES][0] = 1.0f; + g_qeglobals.d_savedinfo.colors[COLOR_BRUSHES][1] = 1.0f; + g_qeglobals.d_savedinfo.colors[COLOR_BRUSHES][2] = 1.0f; + + g_qeglobals.d_savedinfo.colors[COLOR_VIEWNAME][0] = 0.7f; + g_qeglobals.d_savedinfo.colors[COLOR_VIEWNAME][1] = 0.7f; + g_qeglobals.d_savedinfo.colors[COLOR_VIEWNAME][2] = 0.0f; + + g_qeglobals.d_savedinfo.colors[COLOR_PRECISION_CROSSHAIR][0] = 1.0; + g_qeglobals.d_savedinfo.colors[COLOR_PRECISION_CROSSHAIR][1] = 0.0; + g_qeglobals.d_savedinfo.colors[COLOR_PRECISION_CROSSHAIR][2] = 1.0; + + Sys_UpdateWindows(W_ALL); +} + +void CMainFrame::OnColorSetMax() { + for (int i=0 ; i<3 ; i++) { + g_qeglobals.d_savedinfo.colors[COLOR_TEXTUREBACK][i] = 0.25f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][i] = 0.77f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDMINOR][i] = 0.83f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDMAJOR][i] = 0.89f; + g_qeglobals.d_savedinfo.colors[COLOR_CAMERABACK][i] = 0.25f; + } + + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBLOCK][0] = 1.0f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBLOCK][1] = 1.0f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBLOCK][2] = 1.0f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDTEXT][0] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDTEXT][1] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_GRIDTEXT][2] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][0] = 1.0f; + g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][1] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][2] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_CLIPPER][0] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_CLIPPER][1] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_CLIPPER][2] = 1.0f; + g_qeglobals.d_savedinfo.colors[COLOR_BRUSHES][0] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_BRUSHES][1] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_BRUSHES][2] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_VIEWNAME][0] = 0.5f; + g_qeglobals.d_savedinfo.colors[COLOR_VIEWNAME][1] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_VIEWNAME][2] = 0.75f; + + //g_qeglobals.d_savedinfo.colors[COLOR_CAMERABACK][0] = 0.0f; + //g_qeglobals.d_savedinfo.colors[COLOR_CAMERABACK][1] = 1.0f; + //g_qeglobals.d_savedinfo.colors[COLOR_CAMERABACK][2] = 1.0f; + + g_qeglobals.d_savedinfo.colors[COLOR_PRECISION_CROSSHAIR][0] = 1.0f; + g_qeglobals.d_savedinfo.colors[COLOR_PRECISION_CROSSHAIR][1] = 0.0f; + g_qeglobals.d_savedinfo.colors[COLOR_PRECISION_CROSSHAIR][2] = 1.0f; + + Sys_UpdateWindows (W_ALL); + +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSnaptogrid() { + g_PrefsDlg.m_bNoClamp ^= 1; + g_PrefsDlg.SavePrefs(); + + CMenu *pMenu = GetMenu(); + if (pMenu) { + pMenu->CheckMenuItem(ID_SNAPTOGRID, MF_BYCOMMAND | (!g_PrefsDlg.m_bNoClamp) ? MF_CHECKED : MF_UNCHECKED); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectScale() { + // if (ActiveXY()) ActiveXY()->UndoCopy(); + Undo_Start("scale"); + Undo_AddBrushList(&selected_brushes); + + CScaleDialog dlg; + if (dlg.DoModal() == IDOK) { + if (dlg.m_fX > 0 && dlg.m_fY > 0 && dlg.m_fZ > 0) { + Select_Scale(dlg.m_fX, dlg.m_fY, dlg.m_fZ); + Sys_UpdateWindows(W_ALL); + } + else { + common->Printf("Warning.. Tried to scale by a zero value."); + } + } + + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectMouserotate() { + if (ActiveXY()) { + if (ActiveXY()->ClipMode()) { + OnViewClipper(); + } + + if (ActiveXY()->RotateMode()) { + ActiveXY()->SetRotateMode(false); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SELECT_MOUSEROTATE, FALSE); + Map_BuildBrushData(); + } + else { + // may not work if no brush selected, see return value + if (ActiveXY()->SetRotateMode(true)) { + g_qeglobals.rotateAxis = 0; + if (ActiveXY()->GetViewType() == XY) { + g_qeglobals.rotateAxis = 2; + } else if (ActiveXY()->GetViewType() == XZ) { + g_qeglobals.rotateAxis = 1; + } + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SELECT_MOUSEROTATE, TRUE); + } + else { // if MFC called, we need to set back to FALSE ourselves + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SELECT_MOUSEROTATE, FALSE); + } + } + } + Sys_UpdateWindows(W_CAMERA | W_XY); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnEditCopybrush() { + if (ActiveXY()) { + ActiveXY()->Copy(); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnEditPastebrush() { + if (ActiveXY()) { + ActiveXY()->Paste(); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnEditUndo() { + // if (ActiveXY()) ActiveXY()->Undo(); + Undo_Undo(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnEditRedo() { + Undo_Redo(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnUpdateEditUndo(CCmdUI *pCmdUI) { + /* + * BOOL bEnable = false; if (ActiveXY()) bEnable = ActiveXY()->UndoAvailable(); + * pCmdUI->Enable(bEnable); + */ + pCmdUI->Enable(Undo_UndoAvailable()); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnUpdateEditRedo(CCmdUI *pCmdUI) { + pCmdUI->Enable(Undo_RedoAvailable()); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionTextureDec() { + g_qeglobals.d_savedinfo.m_nTextureTweak -= 1.0f; + if ( g_qeglobals.d_savedinfo.m_nTextureTweak == 0.0f ) { + g_qeglobals.d_savedinfo.m_nTextureTweak -= 1.0f; + } + + SetTexValStatus(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionTextureFit() { + Select_FitTexture( 1.0f, 1.0f ); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionTextureInc() { + g_qeglobals.d_savedinfo.m_nTextureTweak += 1.0f; + if ( g_qeglobals.d_savedinfo.m_nTextureTweak == 0.0f ) { + g_qeglobals.d_savedinfo.m_nTextureTweak += 1.0f; + } + + SetTexValStatus(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionTextureRotateclock() { + Select_RotateTexture(abs(g_PrefsDlg.m_nRotation)); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionTextureRotatecounter() { + Select_RotateTexture(-abs(g_PrefsDlg.m_nRotation)); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionTextureScaledown() { + Select_ScaleTexture(0, -g_qeglobals.d_savedinfo.m_nTextureTweak); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionTextureScaleup() { + Select_ScaleTexture(0, g_qeglobals.d_savedinfo.m_nTextureTweak); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionTextureScaleLeft() { + Select_ScaleTexture(g_qeglobals.d_savedinfo.m_nTextureTweak, 0); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionTextureScaleRight() { + Select_ScaleTexture(g_qeglobals.d_savedinfo.m_nTextureTweak, 0); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionTextureShiftdown() { + Select_ShiftTexture(0, -g_qeglobals.d_savedinfo.m_nTextureTweak, true); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionTextureShiftleft() { + Select_ShiftTexture(-g_qeglobals.d_savedinfo.m_nTextureTweak, 0, true); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionTextureShiftright() { + Select_ShiftTexture(g_qeglobals.d_savedinfo.m_nTextureTweak, 0, true); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionTextureShiftup() { + Select_ShiftTexture(0, g_qeglobals.d_savedinfo.m_nTextureTweak, true); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::SetGridChecks(int id) { + HMENU hMenu = ::GetMenu(GetSafeHwnd()); + CheckMenuItem(hMenu, ID_GRID_1, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_GRID_2, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_GRID_4, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_GRID_8, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_GRID_16, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_GRID_32, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_GRID_64, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_GRID_POINT5, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_GRID_POINT25, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_GRID_POINT125, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_GRID_POINT0625, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, id, MF_BYCOMMAND | MF_CHECKED); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnGridNext() { + if (g_qeglobals.d_gridsize >= MAX_GRID) { + return; + } + + g_qeglobals.d_gridsize *= 2.0f; + + float minGrid = MIN_GRID; + int id = ID_GRID_START; + + while (minGrid < g_qeglobals.d_gridsize && id < ID_GRID_END) { + minGrid *= 2.0f; + id++; + } + + UpdateGrid(); + + SetGridChecks(id); + SetGridStatus(); + Sys_UpdateWindows(W_XY | W_Z); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnGridPrev() { + if (g_qeglobals.d_gridsize <= MIN_GRID) { + return; + } + + g_qeglobals.d_gridsize /= 2; + + float maxGrid = MAX_GRID; + int id = ID_GRID_END; + + while (maxGrid > g_qeglobals.d_gridsize && id > ID_GRID_START) { + maxGrid /= 2.0f; + id--; + } + + UpdateGrid(); + + SetGridChecks(id); + SetGridStatus(); + Sys_UpdateWindows(W_XY | W_Z); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::SetGridStatus() { + CString strStatus; + char c1; + char c2; + c1 = (g_PrefsDlg.m_bTextureLock) ? 'M' : ' '; + c2 = (g_PrefsDlg.m_bRotateLock) ? 'R' : ' '; + strStatus.Format + ( + "G:%1.2f T:%1.2f R:%i C:%i L:%c%c", + g_qeglobals.d_gridsize, + g_qeglobals.d_savedinfo.m_nTextureTweak, + g_PrefsDlg.m_nRotation, + g_PrefsDlg.m_nCubicScale, + c1, + c2 + ); + SetStatusText(4, strStatus); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::SetTexValStatus() { + // + // CString strStatus; strStatus.Format("T: %i C: %i", g_nTextureTweak, + // g_nCubicScale); SetStatusText(5, strStatus.GetBuffer(0)); + // + SetGridStatus(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnTextureReplaceall() { + CFindTextureDlg::show(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnScalelockx() { + if (g_nScaleHow & SCALE_X) { + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SCALELOCKX, FALSE); + } + else { + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SCALELOCKX); + } + + g_nScaleHow ^= SCALE_X; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnScalelocky() { + if (g_nScaleHow & SCALE_Y) { + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SCALELOCKY, FALSE); + } + else { + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SCALELOCKY); + } + + g_nScaleHow ^= SCALE_Y; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnScalelockz() { + if (g_nScaleHow & SCALE_Z) { + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SCALELOCKZ, FALSE); + } + else { + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SCALELOCKZ); + } + + g_nScaleHow ^= SCALE_Z; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectMousescale() { + if (ActiveXY()) { + if (ActiveXY()->ClipMode()) { + OnViewClipper(); + } + + if (ActiveXY()->RotateMode()) { + // SetRotateMode(false) always works + ActiveXY()->SetRotateMode(false); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SELECT_MOUSESCALE, FALSE); + } + + if (ActiveXY()->ScaleMode()) { + ActiveXY()->SetScaleMode(false); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SELECT_MOUSESCALE, FALSE); + } + else { + ActiveXY()->SetScaleMode(true); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SELECT_MOUSESCALE); + } + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnFileImport() { +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnFileProjectsettings() { + DoProjectSettings(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnUpdateFileImport(CCmdUI *pCmdUI) { + pCmdUI->Enable(FALSE); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewCubein() { + g_PrefsDlg.m_nCubicScale--; + if (g_PrefsDlg.m_nCubicScale < 1) { + g_PrefsDlg.m_nCubicScale = 1; + } + + g_PrefsDlg.SavePrefs(); + Sys_UpdateWindows(W_CAMERA); + SetTexValStatus(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewCubeout() { + g_PrefsDlg.m_nCubicScale++; + if (g_PrefsDlg.m_nCubicScale > 99) { + g_PrefsDlg.m_nCubicScale = 99; + } + + g_PrefsDlg.SavePrefs(); + Sys_UpdateWindows(W_CAMERA); + SetTexValStatus(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewCubicclipping() { + g_PrefsDlg.m_bCubicClipping ^= 1; + + CMenu *pMenu = GetMenu(); + if (pMenu) { + pMenu->CheckMenuItem + ( + ID_VIEW_CUBICCLIPPING, + MF_BYCOMMAND | (g_PrefsDlg.m_bCubicClipping) ? MF_CHECKED : MF_UNCHECKED + ); + } + + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_VIEW_CUBICCLIPPING, (g_PrefsDlg.m_bCubicClipping) ? TRUE : FALSE); + g_PrefsDlg.SavePrefs(); + Map_BuildBrushData(); + Sys_UpdateWindows(W_CAMERA); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnFileSaveregion() { + SaveAsDialog(true); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnUpdateFileSaveregion(CCmdUI *pCmdUI) { + pCmdUI->Enable (static_cast(region_active)); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionMovedown() { + Undo_Start("move up"); + Undo_AddBrushList(&selected_brushes); + + idVec3 vAmt; + vAmt[0] = vAmt[1] = 0.0f; + vAmt[2] = -g_qeglobals.d_gridsize; + Select_Move(vAmt); + Sys_UpdateWindows(W_CAMERA | W_XY | W_Z); + + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionMoveup() { + idVec3 vAmt; + vAmt[0] = vAmt[1] = 0.0f; + vAmt[2] = g_qeglobals.d_gridsize; + Select_Move(vAmt); + Sys_UpdateWindows(W_CAMERA | W_XY | W_Z); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnToolbarMain() { +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnToolbarTexture() { +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionPrint() { + for (brush_t * b = selected_brushes.next; b != &selected_brushes; b = b->next) { + Brush_Print(b); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::UpdateTextureBar() { + if (m_wndTextureBar.GetSafeHwnd()) { + m_wndTextureBar.GetSurfaceAttributes(); + } +} + +bool g_bTABDown = false; +bool g_bOriginalFlag; + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionTogglesizepaint() { + if (::GetAsyncKeyState('Q')) { + if (!g_bTABDown) { + g_bTABDown = true; + g_bOriginalFlag = ( g_PrefsDlg.m_bSizePaint != FALSE ); + g_PrefsDlg.m_bSizePaint = !g_bOriginalFlag; + Sys_UpdateWindows(W_XY); + return; + } + } + else { + g_bTABDown = false; + g_PrefsDlg.m_bSizePaint = g_bOriginalFlag; + Sys_UpdateWindows(W_XY); + return; + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnBrushMakecone() { + Undo_Start("make cone"); + Undo_AddBrushList(&selected_brushes); + DoSides(true); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnTexturesLoad() { + BROWSEINFO bi; + CString strPath; + char *p = strPath.GetBuffer(MAX_PATH + 1); + bi.hwndOwner = GetSafeHwnd(); + bi.pidlRoot = NULL; + bi.pszDisplayName = p; + bi.lpszTitle = "Load textures from path"; + bi.ulFlags = 0; + bi.lpfn = NULL; + bi.lParam = NULL; + bi.iImage = 0; + + LPITEMIDLIST pidlBrowse; + pidlBrowse = SHBrowseForFolder(&bi); + if (pidlBrowse) { + SHGetPathFromIDList(pidlBrowse, p); + strPath.ReleaseBuffer(); + AddSlash(strPath); + //FIXME: idMaterial + //Texture_ShowDirectory(strPath.GetBuffer(0)); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnToggleRotatelock() { + g_qeglobals.flatRotation = false; + g_qeglobals.rotateAxis++; + if (g_qeglobals.rotateAxis > 2) { + g_qeglobals.rotateAxis = 0; + } + Select_InitializeRotation(); + Sys_UpdateWindows(W_CAMERA | W_XY); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveBevel() { + // Curve_MakeCurvedBrush (false, false, false, false, false, true, true); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveCylinder() { + // Curve_MakeCurvedBrush (false, false, false, true, true, true, true); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveEighthsphere() { + // Curve_MakeCurvedBrush (false, true, false, true, true, false, false); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveEndcap() { + // Curve_MakeCurvedBrush (false, false, false, false, true, true, true); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveHemisphere() { + // Curve_MakeCurvedBrush (false, true, false, true, true, true, true); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveInvertcurve() { + // Curve_Invert (); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveQuarter() { + // Curve_MakeCurvedBrush (false, true, false, true, true, true, false); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveSphere() { + // Curve_MakeCurvedBrush (false, true, true, true, true, true, true); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnFileImportmap() { + CFileDialog dlgFile(TRUE, NULL, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, "Map files (*.map)|*.map||", this); + if (dlgFile.DoModal() == IDOK) { + Map_ImportFile(dlgFile.GetPathName().GetBuffer(0)); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnFileExportmap() { + CFileDialog dlgFile(FALSE, "map", NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, "Map files (*.map)|*.map||", this); + if (dlgFile.DoModal() == IDOK) { + Map_SaveSelected(dlgFile.GetPathName().GetBuffer(0)); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewShowcurves() { + if ((g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_CURVES) & EXCLUDE_CURVES) { + CheckMenuItem(::GetMenu(GetSafeHwnd()), ID_VIEW_SHOWCURVES, MF_BYCOMMAND | MF_UNCHECKED); + } + else { + CheckMenuItem(::GetMenu(GetSafeHwnd()), ID_VIEW_SHOWCURVES, MF_BYCOMMAND | MF_CHECKED); + } + + Sys_UpdateWindows(W_XY | W_CAMERA); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionSelectNudgedown() { + NudgeSelection(3, g_qeglobals.d_savedinfo.m_nTextureTweak); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionSelectNudgeleft() { + NudgeSelection(0, g_qeglobals.d_savedinfo.m_nTextureTweak); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionSelectNudgeright() { + NudgeSelection(2, g_qeglobals.d_savedinfo.m_nTextureTweak); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionSelectNudgeup() { + NudgeSelection(1, g_qeglobals.d_savedinfo.m_nTextureTweak); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::NudgeSelection(int nDirection, float fAmount) { + if (ActiveXY()->RotateMode()) { + int nAxis = 0; + if (ActiveXY()->GetViewType() == XY) { + nAxis = 2; + } else if (g_pParentWnd->ActiveXY()->GetViewType() == XZ) { + nAxis = 1; + fAmount = -fAmount; + } + + if (nDirection == 2 || nDirection == 3) { + fAmount = -fAmount; + } + + float fDeg = -fAmount; + + g_pParentWnd->ActiveXY()->Rotation()[nAxis] += fAmount; + + CString strStatus; + strStatus.Format + ( + "Rotation x:: %.1f y:: %.1f z:: %.1f", + g_pParentWnd->ActiveXY()->Rotation()[0], + g_pParentWnd->ActiveXY()->Rotation()[1], + g_pParentWnd->ActiveXY()->Rotation()[2] + ); + g_pParentWnd->SetStatusText(2, strStatus); + Select_RotateAxis(nAxis, fDeg, false, true); + Sys_UpdateWindows(W_ALL); + } + else if (ActiveXY()->ScaleMode()) { + if (nDirection == 0 || nDirection == 3) { + fAmount = -fAmount; + } + + idVec3 v; + v[0] = v[1] = v[2] = 1.0f; + if (fAmount > 0) { + v[0] = 1.1f; + v[1] = 1.1f; + v[2] = 1.1f; + } + else { + v[0] = 0.9f; + v[1] = 0.9f; + v[2] = 0.9f; + } + + Select_Scale + ( + (g_nScaleHow & SCALE_X) ? v[0] : 1.0f, + (g_nScaleHow & SCALE_Y) ? v[1] : 1.0f, + (g_nScaleHow & SCALE_Z) ? v[2] : 1.0f + ); + Sys_UpdateWindows(W_ALL); + } + else { + // 0 - left, 1 - up, 2 - right, 3 - down + int nDim; + if (nDirection == 0) { + nDim = ActiveXY()->GetViewType() == YZ ? 1 : 0; + fAmount = -fAmount; + } + else if (nDirection == 1) { + nDim = ActiveXY()->GetViewType() == XY ? 1 : 2; + } + else if (nDirection == 2) { + nDim = ActiveXY()->GetViewType() == YZ ? 1 : 0; + } + else { + nDim = ActiveXY()->GetViewType() == XY ? 1 : 2; + fAmount = -fAmount; + } + + Nudge(nDim, fAmount); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +BOOL CMainFrame::PreTranslateMessage(MSG *pMsg) { + return CFrameWnd::PreTranslateMessage(pMsg); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::Nudge(int nDim, float fNudge) { + idVec3 vMove; + vMove[0] = vMove[1] = vMove[2] = 0; + vMove[nDim] = fNudge; + Select_Move(vMove, true); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnTexturesLoadlist() { + CDialogTextures dlg; + dlg.DoModal(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectByBoundingBrush() { + g_PrefsDlg.m_selectByBoundingBrush ^= 1; + m_wndToolBar.GetToolBarCtrl().CheckButton + ( + ID_SELECT_BYBOUNDINGBRUSH, + (g_PrefsDlg.m_selectByBoundingBrush) ? TRUE : FALSE + ); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectBrushesOnly() { + g_PrefsDlg.m_selectOnlyBrushes ^= 1; + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SELECT_BRUSHESONLY, (g_PrefsDlg.m_selectOnlyBrushes) ? TRUE : FALSE); +} + + + + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnDynamicLighting() { + CCamWnd *pCam = new CCamWnd(); + CRect rect(100, 100, 300, 300); + pCam->Create(CAMERA_WINDOW_CLASS, "", WS_OVERLAPPEDWINDOW, rect, GetDesktopWindow(), 12345); + pCam->ShowWindow(SW_SHOW); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveSimplepatchmesh() { + Undo_Start("make simpe patch mesh"); + Undo_AddBrushList(&selected_brushes); + + CPatchDensityDlg dlg; + dlg.DoModal(); + + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnPatchToggleBox() { + g_bPatchShowBounds ^= 1; + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_PATCH_SHOWBOUNDINGBOX, (g_bPatchShowBounds) ? TRUE : FALSE); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnPatchWireframe() { + g_bPatchWireFrame ^= 1; + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_PATCH_WIREFRAME, (g_bPatchWireFrame) ? TRUE : FALSE); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurvePatchcone() { + Undo_Start("make curve cone"); + Undo_AddBrushList(&selected_brushes); + Patch_BrushToMesh(true); + Sys_UpdateWindows(W_ALL); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurvePatchtube() { + Undo_Start("make curve cylinder"); + Undo_AddBrushList(&selected_brushes); + Patch_BrushToMesh(false); + Sys_UpdateWindows(W_ALL); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnPatchWeld() { + g_bPatchWeld ^= 1; + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_PATCH_WELD, (g_bPatchWeld) ? TRUE : FALSE); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurvePatchbevel() { + Undo_Start("make bevel"); + Undo_AddBrushList(&selected_brushes); + Patch_BrushToMesh(false, true, false); + Sys_UpdateWindows(W_ALL); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurvePatchendcap() { + Undo_Start("make end cap"); + Undo_AddBrushList(&selected_brushes); + Patch_BrushToMesh(false, false, true); + Sys_UpdateWindows(W_ALL); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurvePatchinvertedbevel() { + // Patch_BrushToMesh(false, true, false, true); Sys_UpdateWindows (W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurvePatchinvertedendcap() { + // Patch_BrushToMesh(false, false, true, true); Sys_UpdateWindows (W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnPatchDrilldown() { + g_bPatchDrillDown ^= 1; + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_PATCH_DRILLDOWN, (g_bPatchDrillDown) ? TRUE : FALSE); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveInsertcolumn() { + Undo_Start("insert colum"); + Undo_AddBrushList(&selected_brushes); + + // Patch_AdjustSelectedRowCols(0, 2); + Patch_AdjustSelected(true, true, true); + Sys_UpdateWindows(W_ALL); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveInsertrow() { + Undo_Start("insert row"); + Undo_AddBrushList(&selected_brushes); + + // Patch_AdjustSelectedRowCols(2, 0); + Patch_AdjustSelected(true, false, true); + Sys_UpdateWindows(W_ALL); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveDeletecolumn() { + Undo_Start("delete column"); + Undo_AddBrushList(&selected_brushes); + Patch_AdjustSelected(false, true, true); + Sys_UpdateWindows(W_ALL); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveDeleterow() { + Undo_Start("delete row"); + Undo_AddBrushList(&selected_brushes); + Patch_AdjustSelected(false, false, true); + Sys_UpdateWindows(W_ALL); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveInsertAddcolumn() { + Undo_Start("add (2) columns"); + Undo_AddBrushList(&selected_brushes); + Patch_AdjustSelected(true, true, true); + Sys_UpdateWindows(W_ALL); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveInsertAddrow() { + Undo_Start("add (2) rows"); + Undo_AddBrushList(&selected_brushes); + Patch_AdjustSelected(true, false, true); + Sys_UpdateWindows(W_ALL); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveInsertInsertcolumn() { + Undo_Start("insert (2) columns"); + Undo_AddBrushList(&selected_brushes); + Patch_AdjustSelected(true, true, false); + Sys_UpdateWindows(W_ALL); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveInsertInsertrow() { + Undo_Start("insert (2) rows"); + Undo_AddBrushList(&selected_brushes); + Patch_AdjustSelected(true, false, false); + Sys_UpdateWindows(W_ALL); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveNegative() { + Patch_ToggleInverted(); + + // Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveNegativeTextureX() { + Select_FlipTexture(false); + + // Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveNegativeTextureY() { + Select_FlipTexture(true); + // Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveDeleteFirstcolumn() { + Undo_Start("delete first (2) columns"); + Undo_AddBrushList(&selected_brushes); + Patch_AdjustSelected(false, true, true); + Sys_UpdateWindows(W_ALL); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveDeleteFirstrow() { + Undo_Start("delete first (2) rows"); + Undo_AddBrushList(&selected_brushes); + Patch_AdjustSelected(false, false, true); + Sys_UpdateWindows(W_ALL); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveDeleteLastcolumn() { + Undo_Start("delete last (2) columns"); + Undo_AddBrushList(&selected_brushes); + Patch_AdjustSelected(false, true, false); + Sys_UpdateWindows(W_ALL); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveDeleteLastrow() { + Undo_Start("delete last (2) rows"); + Undo_AddBrushList(&selected_brushes); + Patch_AdjustSelected(false, false, false); + Sys_UpdateWindows(W_ALL); + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnPatchBend() { + Patch_BendToggle(); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_PATCH_BEND, (g_bPatchBendMode) ? TRUE : FALSE); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnPatchInsdel() { + Patch_InsDelToggle(); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_PATCH_INSDEL, (g_bPatchInsertMode) ? TRUE : FALSE); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnPatchEnter() { +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +extern bool Sys_KeyDown(int key); +void CMainFrame::OnPatchTab() { + if (g_bPatchBendMode) { + Patch_BendHandleTAB(); + } + else if (g_bPatchInsertMode) { + Patch_InsDelHandleTAB(); + } + else if (g_bAxialMode) { + int faceCount = g_ptrSelectedFaces.GetSize(); + if (faceCount > 0) { + face_t *selFace = reinterpret_cast < face_t * > (g_ptrSelectedFaces.GetAt(0)); + int *ip = (Sys_KeyDown(VK_SHIFT)) ? &g_axialAnchor : &g_axialDest; + (*ip)++; + if ( *ip >= selFace->face_winding->GetNumPoints() ) { + *ip = 0; + } + } + Sys_UpdateWindows(W_CAMERA); + } else { + // + // check to see if the selected brush is part of a func group if it is, deselect + // everything and reselect the next brush in the group + // + brush_t *b = selected_brushes.next; + entity_t *e; + if (b != &selected_brushes) { + if ( idStr::Icmp(b->owner->eclass->name, "worldspawn") != 0 ) { + e = b->owner; + Select_Deselect(); + brush_t *b2; + for (b2 = e->brushes.onext; b2 != &e->brushes; b2 = b2->onext) { + if (b == b2) { + b2 = b2->onext; + break; + } + } + + if (b2 == &e->brushes) { + b2 = b2->onext; + } + + Select_Brush(b2, false); + Sys_UpdateWindows(W_ALL); + } + } + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::UpdatePatchToolbarButtons() { + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_PATCH_BEND, (g_bPatchBendMode) ? TRUE : FALSE); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_PATCH_INSDEL, (g_bPatchInsertMode) ? TRUE : FALSE); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurvePatchdensetube() { + Undo_Start("dense cylinder"); + Undo_AddBrushList(&selected_brushes); + + Patch_BrushToMesh(false); + OnCurveInsertAddrow(); + OnCurveInsertInsertrow(); + Sys_UpdateWindows(W_ALL); + + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurvePatchverydensetube() { + Undo_Start("very dense cylinder"); + Undo_AddBrushList(&selected_brushes); + + Patch_BrushToMesh(false); + OnCurveInsertAddrow(); + OnCurveInsertInsertrow(); + OnCurveInsertAddrow(); + OnCurveInsertInsertrow(); + Sys_UpdateWindows(W_ALL); + + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveCap() { + Patch_CapCurrent(); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveCapInvertedbevel() { + Patch_CapCurrent(true); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveCapInvertedendcap() { + Patch_CapCurrent(false, true); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveRedisperseCols() { + Patch_DisperseColumns(); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveRedisperseRows() { + Patch_DisperseRows(); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnPatchNaturalize() { + Patch_NaturalizeSelected(); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnPatchNaturalizeAlt() { + Patch_NaturalizeSelected(false, false, true); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSnapToGrid() { + Select_SnapToGrid(); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurvePatchsquare() { + Undo_Start("square cylinder"); + Undo_AddBrushList(&selected_brushes); + + Patch_BrushToMesh(false, false, false, true); + Sys_UpdateWindows(W_ALL); + + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::CheckTextureScale(int id) { + CMenu *pMenu = GetMenu(); + if (pMenu) { + pMenu->CheckMenuItem(ID_TEXTURES_TEXTUREWINDOWSCALE_10, MF_BYCOMMAND | MF_UNCHECKED); + pMenu->CheckMenuItem(ID_TEXTURES_TEXTUREWINDOWSCALE_25, MF_BYCOMMAND | MF_UNCHECKED); + pMenu->CheckMenuItem(ID_TEXTURES_TEXTUREWINDOWSCALE_50, MF_BYCOMMAND | MF_UNCHECKED); + pMenu->CheckMenuItem(ID_TEXTURES_TEXTUREWINDOWSCALE_100, MF_BYCOMMAND | MF_UNCHECKED); + pMenu->CheckMenuItem(ID_TEXTURES_TEXTUREWINDOWSCALE_200, MF_BYCOMMAND | MF_UNCHECKED); + pMenu->CheckMenuItem(id, MF_BYCOMMAND | MF_CHECKED); + } + + g_PrefsDlg.SavePrefs(); + //FIXME: idMaterial + //Texture_ResetPosition(); + Sys_UpdateWindows(W_TEXTURE); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnTexturesTexturewindowscale10() { + g_PrefsDlg.m_nTextureScale = 10; + CheckTextureScale(ID_TEXTURES_TEXTUREWINDOWSCALE_10); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnTexturesTexturewindowscale100() { + g_PrefsDlg.m_nTextureScale = 100; + CheckTextureScale(ID_TEXTURES_TEXTUREWINDOWSCALE_100); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnTexturesTexturewindowscale200() { + g_PrefsDlg.m_nTextureScale = 200; + CheckTextureScale(ID_TEXTURES_TEXTUREWINDOWSCALE_200); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnTexturesTexturewindowscale25() { + g_PrefsDlg.m_nTextureScale = 25; + CheckTextureScale(ID_TEXTURES_TEXTUREWINDOWSCALE_25); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnTexturesTexturewindowscale50() { + g_PrefsDlg.m_nTextureScale = 50; + CheckTextureScale(ID_TEXTURES_TEXTUREWINDOWSCALE_50); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnTexturesFlush() { + //FIXME: idMaterial + //Texture_Flush(); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveOverlayClear() { + Patch_ClearOverlays(); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveOverlaySet() { + Patch_SetOverlays(); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveThicken() { + Undo_Start("curve thicken"); + Undo_AddBrushList(&selected_brushes); + + CDialogThick dlg; + if ( dlg.DoModal() == IDOK ) { + Patch_Thicken( dlg.m_nAmount, ( dlg.m_bSeams != FALSE ) ); + Sys_UpdateWindows(W_ALL); + } + + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveCyclecap() { + Patch_NaturalizeSelected(true, true); + Sys_UpdateWindows(W_ALL); +} + +void CMainFrame::OnCurveCyclecapAlt() { + Patch_NaturalizeSelected(true, true, true); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveMatrixTranspose() { + Patch_Transpose(); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnTexturesReloadshaders() { + CWaitCursor wait; + declManager->Reload( false ); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::SetEntityCheck() { + CMenu *pMenu = GetMenu(); + if (pMenu) { + pMenu->CheckMenuItem(ID_VIEW_ENTITIESAS_WIREFRAME, MF_BYCOMMAND | (g_PrefsDlg.m_nEntityShowState == ENTITY_WIRE) ? MF_CHECKED : MF_UNCHECKED); + pMenu->CheckMenuItem(ID_VIEW_ENTITIESAS_SKINNED, MF_BYCOMMAND | (g_PrefsDlg.m_nEntityShowState == ENTITY_SKINNED) ? MF_CHECKED : MF_UNCHECKED); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnShowEntities() { + HandlePopup(this, IDR_POPUP_ENTITY); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewEntitiesasSkinned() { + g_PrefsDlg.m_nEntityShowState = ENTITY_SKINNED; + SetEntityCheck(); + g_PrefsDlg.SavePrefs(); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewEntitiesasWireframe() { + g_PrefsDlg.m_nEntityShowState = ENTITY_WIRE; + SetEntityCheck(); + g_PrefsDlg.SavePrefs(); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewShowhint() { + if ((g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_HINT) & EXCLUDE_HINT) { + CheckMenuItem(::GetMenu(GetSafeHwnd()), ID_VIEW_SHOWHINT, MF_BYCOMMAND | MF_UNCHECKED); + } + else { + CheckMenuItem(::GetMenu(GetSafeHwnd()), ID_VIEW_SHOWHINT, MF_BYCOMMAND | MF_CHECKED); + } + + Sys_UpdateWindows(W_XY | W_CAMERA); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnTexturesShowall() { + Texture_ShowAll(); +} + +void CMainFrame::OnTexturesHideall() { + Texture_HideAll(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnPatchInspector() { + DoPatchInspector(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewOpengllighting() { + g_PrefsDlg.m_bGLLighting ^= 1; + g_PrefsDlg.SavePrefs(); + CheckMenuItem + ( + ::GetMenu(GetSafeHwnd()), + ID_VIEW_OPENGLLIGHTING, + MF_BYCOMMAND | (g_PrefsDlg.m_bGLLighting) ? MF_CHECKED : MF_UNCHECKED + ); + Sys_UpdateWindows(W_XY | W_CAMERA); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectAll() { + Select_AllOfType(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewShowcaulk() { + if ((g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_CAULK) & EXCLUDE_CAULK) { + CheckMenuItem(::GetMenu(GetSafeHwnd()), ID_VIEW_SHOWCAULK, MF_BYCOMMAND | MF_UNCHECKED); + } + else { + CheckMenuItem(::GetMenu(GetSafeHwnd()), ID_VIEW_SHOWCAULK, MF_BYCOMMAND | MF_CHECKED); + } + + Sys_UpdateWindows(W_XY | W_CAMERA); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveFreeze() { + Patch_Freeze(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveUnFreeze() { + Patch_UnFreeze(false); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveUnFreezeAll() { + Patch_UnFreeze(true); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectReselect() { + Select_Reselect(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewShowangles() { + if ((g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_ANGLES) & EXCLUDE_ANGLES) { + CheckMenuItem(::GetMenu(GetSafeHwnd()), ID_VIEW_SHOWANGLES, MF_BYCOMMAND | MF_UNCHECKED); + } + else { + CheckMenuItem(::GetMenu(GetSafeHwnd()), ID_VIEW_SHOWANGLES, MF_BYCOMMAND | MF_CHECKED); + } + + Sys_UpdateWindows(W_XY | W_CAMERA); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnEditSaveprefab() { + CFileDialog dlgFile + ( + FALSE, + "pfb", + NULL, + OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, + "Prefab files (*.pfb)|*.pfb||", + this + ); + char CurPath[1024]; + ::GetCurrentDirectory(1024, CurPath); + + dlgFile.m_ofn.lpstrInitialDir = CurPath; + if (dlgFile.DoModal() == IDOK) { + Map_SaveSelected(dlgFile.GetPathName().GetBuffer(0)); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnEditLoadprefab() { + CFileDialog dlgFile + ( + TRUE, + "pfb", + NULL, + OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, + "Prefab files (*.pfb)|*.pfb||", + this + ); + char CurPath[1024]; + ::GetCurrentDirectory(1024, CurPath); + dlgFile.m_ofn.lpstrInitialDir = CurPath; + if (dlgFile.DoModal() == IDOK) { + Map_ImportFile(dlgFile.GetPathName().GetBuffer(0)); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveMoreendcapsbevelsSquarebevel() { + Undo_Start("square bevel"); + Undo_AddBrushList(&selected_brushes); + + Patch_BrushToMesh(false, true, false, true); + Sys_UpdateWindows(W_ALL); + + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveMoreendcapsbevelsSquareendcap() { + Undo_Start("square endcap"); + Undo_AddBrushList(&selected_brushes); + + Patch_BrushToMesh(false, false, true, true); + Sys_UpdateWindows(W_ALL); + + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnBrushPrimitivesSphere() { + Undo_Start("make sphere"); + Undo_AddBrushList(&selected_brushes); + + DoSides(false, true); + + Undo_EndBrushList(&selected_brushes); + Undo_End(); +} + +extern bool g_bCrossHairs; + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewCrosshair() { + g_bCrossHairs ^= 1; + Sys_UpdateWindows(W_XY); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewHideshowHideselected() { + Select_Hide(); + Select_Deselect(); +} + +void CMainFrame::OnViewHideshowHideNotselected() { + Select_Hide(true); + Select_Deselect(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnViewHideshowShowhidden() { + Select_ShowAllHidden(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnTexturesShadersShow() { + // + // g_PrefsDlg.m_bShowShaders ^= 1; CheckMenuItem ( + // ::GetMenu(GetSafeHwnd()), ID_TEXTURES_SHADERS_SHOW, MF_BYCOMMAND | + // ((g_PrefsDlg.m_bShowShaders) ? MF_CHECKED : MF_UNCHECKED )); + // Sys_UpdateWindows(W_TEXTURE); + // +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnTexturesFlushUnused() { + //FIXME: idMaterial + //Texture_FlushUnused(); + Sys_UpdateWindows(W_TEXTURE); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionInvert() { + Select_Invert(); + Sys_UpdateWindows(W_XY | W_Z | W_CAMERA); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnProjectedLight() { + LightEditorInit( NULL ); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnShowLighttextures() { + g_bShowLightTextures ^= 1; + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SHOW_LIGHTTEXTURES, (g_bShowLightTextures) ? TRUE : FALSE); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnShowLightvolumes() { + g_bShowLightVolumes ^= 1; + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SHOW_LIGHTVOLUMES, (g_bShowLightVolumes) ? TRUE : FALSE); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnActivate(UINT nState, CWnd *pWndOther, BOOL bMinimized) { + CFrameWnd::OnActivate(nState, pWndOther, bMinimized); + + if ( nState != WA_INACTIVE ) { + common->ActivateTool( true ); + if (::IsWindowVisible(win32.hWnd)) { + ::ShowWindow(win32.hWnd, SW_HIDE); + } + + // start playing the editor sound world + soundSystem->SetRenderWorld( g_qeglobals.rw ); + } + else { + //com_editorActive = false; + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSplinesMode() { + g_qeglobals.d_select_mode = sel_addpoint; + g_splineList->clear(); + g_splineList->startEdit(true); + showCameraInspector(); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSplinesLoad() { + g_splineList->load("maps/test.camera"); + g_splineList->buildCamera(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSplinesSave() { + g_splineList->save("maps/test.camera"); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSplinesEdit() { + showCameraInspector(); + Sys_UpdateWindows(W_ALL); +} + +extern void testCamSpeed(); + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSplineTest() { + long start = GetTickCount(); + g_splineList->startCamera(start); + + float cycle = g_splineList->getTotalTime(); + long msecs = cycle * 1000; + long current = start; + idVec3 lookat(0, 0, 0); + idVec3 dir; + + while (current < start + msecs) { + float fov; + g_splineList->getCameraInfo(current, g_pParentWnd->GetCamera()->Camera().origin, dir, &fov); + g_pParentWnd->GetCamera()->Camera().angles[1] = atan2(dir[1], dir[0]) * 180 / 3.14159; + g_pParentWnd->GetCamera()->Camera().angles[0] = asin(dir[2]) * 180 / 3.14159; + g_pParentWnd->UpdateWindows(W_XY | W_CAMERA); + current = GetTickCount(); + } + + g_splineList->setRunning(false); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSplinesTargetPoints() { +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSplinesCameraPoints() { +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnPopupNewcameraInterpolated() { + g_qeglobals.d_select_mode = sel_addpoint; + g_qeglobals.selectObject = g_splineList->startNewCamera(idCameraPosition::INTERPOLATED); + OnSplinesEdit(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnPopupNewcameraSpline() { + g_qeglobals.d_select_mode = sel_addpoint; + g_qeglobals.selectObject = g_splineList->startNewCamera(idCameraPosition::SPLINE); + OnSplinesEdit(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnPopupNewcameraFixed() { + g_qeglobals.d_select_mode = sel_addpoint; + g_qeglobals.selectObject = g_splineList->startNewCamera(idCameraPosition::FIXED); + OnSplinesEdit(); +} + +extern void Patch_AdjustSubdivisions(float hadj, float vadj); + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveIncreaseVert() { + Patch_AdjustSubdivisions( 0.0f, -0.5f ); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveDecreaseVert() { + Patch_AdjustSubdivisions( 0.0f, 0.5f ); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveIncreaseHorz() { + Patch_AdjustSubdivisions( -0.5f, 0.0f ); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnCurveDecreaseHorz() { + Patch_AdjustSubdivisions( 0.5f, 0.0f ); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CMainFrame::OnSelectionMoveonly() { + g_moveOnly ^= 1; + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SELECTION_MOVEONLY, (g_moveOnly) ? TRUE : FALSE); +} + +void CMainFrame::OnSelectBrushlight() +{ + // TODO: Add your command handler code here + +} + +void CMainFrame::OnSelectionCombine() +{ + if (g_qeglobals.d_select_count < 2) { + Sys_Status("Must have at least two things selected.", 0); + Sys_Beep(); + return; + } + + entity_t *e1 = g_qeglobals.d_select_order[0]->owner; + + if (e1 == world_entity) { + Sys_Status("First selection must not be world.", 0); + Sys_Beep(); + return; + } + + idStr str; + idMat3 mat; + idVec3 v; + if (e1->eclass->nShowFlags & ECLASS_LIGHT) { + // copy the lights origin and rotation matrix to + // light_origin and light_rotation + e1->trackLightOrigin = true; + e1->brushes.onext->trackLightOrigin = true; + if (GetVectorForKey(e1, "origin", v)) { + SetKeyVec3(e1, "light_origin", v); + e1->lightOrigin = v; + } + if (!GetMatrixForKey(e1, "rotation", mat)) { + mat.Identity(); + } + sprintf(str, "%g %g %g %g %g %g %g %g %g", mat[0][0], mat[0][1], mat[0][2], mat[1][0], mat[1][1], mat[1][2], mat[2][0], mat[2][1], mat[2][2]); + SetKeyValue(e1, "light_rotation", str, false); + e1->lightRotation = mat; + } + + bool setModel = true; + for (brush_t *b = selected_brushes.next; b != &selected_brushes; b = b->next) { + if (b->owner != e1) { + if (e1->eclass->nShowFlags & ECLASS_LIGHT) { + if (GetVectorForKey(b->owner, "origin", v)) { + e1->origin = b->owner->origin; + SetKeyVec3(e1, "origin", b->owner->origin); + } + if (GetMatrixForKey(b->owner, "rotation", mat)) { + e1->rotation = b->owner->rotation; + mat = b->owner->rotation; + sprintf(str, "%g %g %g %g %g %g %g %g %g", mat[0][0], mat[0][1], mat[0][2], mat[1][0], mat[1][1], mat[1][2], mat[2][0], mat[2][1], mat[2][2]); + SetKeyValue(e1, "rotation", str, false); + } + if (b->modelHandle) { + SetKeyValue(e1, "model", ValueForKey(b->owner, "model")); + setModel = false; + } else { + b->entityModel = true; + } + } + Entity_UnlinkBrush(b); + Entity_LinkBrush(e1, b); + } + } + + if (setModel) { + SetKeyValue(e1, "model", ValueForKey(e1, "name")); + } + + Select_Deselect(); + Select_Brush(g_qeglobals.d_select_order[0]); + Sys_UpdateWindows(W_XY | W_CAMERA); +} + +extern void Patch_Weld(patchMesh_t *p, patchMesh_t *p2); +void CMainFrame::OnPatchCombine() { + patchMesh_t *p, *p2; + p = p2 = NULL; + for (brush_t *b = selected_brushes.next; b != &selected_brushes; b = b->next) { + if (b->pPatch) { + if (p == NULL) { + p = b->pPatch; + } else if (p2 == NULL) { + p2 = b->pPatch; + Patch_Weld(p, p2); + return; + } + } + } +} + +void CMainFrame::OnShowDoom() +{ + int show = ::IsWindowVisible(win32.hWnd) ? SW_HIDE : SW_NORMAL; + if (show == SW_NORMAL) { + g_Inspectors->SetMode(W_TEXTURE); + } + ::ShowWindow(win32.hWnd, show); +} + +void CMainFrame::OnViewRendermode() +{ + m_pCamWnd->ToggleRenderMode(); + CheckMenuItem(::GetMenu(GetSafeHwnd()), ID_VIEW_RENDERMODE, MF_BYCOMMAND | (m_pCamWnd->GetRenderMode()) ? MF_CHECKED : MF_UNCHECKED); + Sys_UpdateWindows(W_ALL); +} + +void CMainFrame::OnViewRebuildrenderdata() +{ + m_pCamWnd->BuildRendererState(); + if (!m_pCamWnd->GetRenderMode()) { + OnViewRendermode(); + } + Sys_UpdateWindows(W_ALL); +} + +void CMainFrame::OnViewRealtimerebuild() +{ + m_pCamWnd->ToggleRebuildMode(); + CheckMenuItem(::GetMenu(GetSafeHwnd()), ID_VIEW_REALTIMEREBUILD, MF_BYCOMMAND | (m_pCamWnd->GetRebuildMode()) ? MF_CHECKED : MF_UNCHECKED); + Sys_UpdateWindows(W_ALL); +} + +void CMainFrame::OnViewRenderentityoutlines() +{ + m_pCamWnd->ToggleEntityMode(); + CheckMenuItem(::GetMenu(GetSafeHwnd()), ID_VIEW_RENDERENTITYOUTLINES, MF_BYCOMMAND | (m_pCamWnd->GetEntityMode()) ? MF_CHECKED : MF_UNCHECKED); + Sys_UpdateWindows(W_ALL); +} + +void CMainFrame::OnViewMaterialanimation() +{ + m_pCamWnd->ToggleAnimationMode(); + CheckMenuItem(::GetMenu(GetSafeHwnd()), ID_VIEW_MATERIALANIMATION, MF_BYCOMMAND | (m_pCamWnd->GetAnimationMode()) ? MF_CHECKED : MF_UNCHECKED); + Sys_UpdateWindows(W_ALL); +} + +extern void Face_SetAxialScale_BrushPrimit(face_t *face, bool y); +void CMainFrame::OnAxialTextureByWidth() { + // temp test code + int faceCount = g_ptrSelectedFaces.GetSize(); + + if (faceCount > 0) { + for (int i = 0; i < faceCount; i++) { + face_t *selFace = reinterpret_cast < face_t * > (g_ptrSelectedFaces.GetAt(i)); + Face_SetAxialScale_BrushPrimit(selFace, false); + } + Sys_UpdateWindows(W_CAMERA); + } + +} + +void CMainFrame::OnAxialTextureByHeight() { + // temp test code + int faceCount = g_ptrSelectedFaces.GetSize(); + + if (faceCount > 0) { + for (int i = 0; i < faceCount; i++) { + face_t *selFace = reinterpret_cast < face_t * > (g_ptrSelectedFaces.GetAt(i)); + Face_SetAxialScale_BrushPrimit(selFace, true); + } + Sys_UpdateWindows(W_CAMERA); + } +} + +void CMainFrame::OnAxialTextureArbitrary() { + if (g_bAxialMode) { + g_bAxialMode = false; + } + int faceCount = g_ptrSelectedFaces.GetSize(); + if (faceCount > 0) { + g_axialAnchor = 0; + g_axialDest = 1; + g_bAxialMode = true; + } + Sys_UpdateWindows(W_CAMERA); +} + +extern void Select_ToOBJ(); +void CMainFrame::OnSelectionExportToobj() +{ + Select_ToOBJ(); +} + +extern void Select_ToCM(); +void CMainFrame::OnSelectionExportToCM() +{ + Select_ToCM(); +} + +void CMainFrame::OnSelectionWireFrameOff() { + Select_WireFrame( false ); +} + +void CMainFrame::OnSelectionWireFrameOn() { + Select_WireFrame( true ); +} + +void CMainFrame::OnSelectionVisibleOn() { + Select_ForceVisible( true ); +} + +void CMainFrame::OnSelectionVisibleOff() { + Select_ForceVisible( false ); +} + + +void CMainFrame::OnViewRenderselection() +{ + m_pCamWnd->ToggleSelectMode(); + CheckMenuItem(::GetMenu(GetSafeHwnd()), ID_VIEW_RENDERSELECTION, MF_BYCOMMAND | (m_pCamWnd->GetSelectMode()) ? MF_CHECKED : MF_UNCHECKED); + Sys_UpdateWindows(W_CAMERA); +} + +void CMainFrame::OnSelectNomodels() +{ + g_PrefsDlg.m_selectNoModels ^= 1; + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SELECT_NOMODELS, (g_PrefsDlg.m_selectNoModels) ? TRUE : FALSE); +} + +void CMainFrame::OnViewShowShowvisportals() +{ + if ((g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_VISPORTALS) & EXCLUDE_VISPORTALS) { + CheckMenuItem(::GetMenu(GetSafeHwnd()), ID_VIEW_SHOW_SHOWVISPORTALS, MF_BYCOMMAND | MF_UNCHECKED); + } + else { + CheckMenuItem(::GetMenu(GetSafeHwnd()), ID_VIEW_SHOW_SHOWVISPORTALS, MF_BYCOMMAND | MF_CHECKED); + } + + Sys_UpdateWindows(W_XY | W_CAMERA); +} + +void CMainFrame::OnViewShowNoDraw() +{ + if ((g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_NODRAW) & EXCLUDE_NODRAW) { + CheckMenuItem(::GetMenu(GetSafeHwnd()), ID_VIEW_SHOW_NODRAW, MF_BYCOMMAND | MF_UNCHECKED); + } + else { + CheckMenuItem(::GetMenu(GetSafeHwnd()), ID_VIEW_SHOW_NODRAW, MF_BYCOMMAND | MF_CHECKED); + } + + Sys_UpdateWindows(W_XY | W_CAMERA); +} + + + +void CMainFrame::OnViewRendersound() +{ + m_pCamWnd->ToggleSoundMode(); + CheckMenuItem(::GetMenu(GetSafeHwnd()), ID_VIEW_RENDERSOUND, MF_BYCOMMAND | (m_pCamWnd->GetSoundMode()) ? MF_CHECKED : MF_UNCHECKED); + Sys_UpdateWindows(W_CAMERA); +} + + +void CMainFrame::OnSoundShowsoundvolumes() +{ + g_qeglobals.d_savedinfo.showSoundAlways ^= 1; + if (g_qeglobals.d_savedinfo.showSoundAlways) { + g_qeglobals.d_savedinfo.showSoundWhenSelected = false; + } + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SOUND_SHOWSOUNDVOLUMES,g_qeglobals.d_savedinfo.showSoundAlways); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SOUND_SHOWSELECTEDSOUNDVOLUMES,g_qeglobals.d_savedinfo.showSoundWhenSelected); + Sys_UpdateWindows(W_XY | W_CAMERA); +} + +void CMainFrame::OnNurbEditor() { + nurbMode ^= 1; + if (nurbMode) { + int num = nurb.GetNumValues(); + idStr temp = va("%i 3 ", num); + for (int i = 0; i < num; i++) { + temp += va("(%i %i) ", (int)nurb.GetValue(i).x, (int)nurb.GetValue(i).y); + } + temp += "\r\n"; + if (OpenClipboard()) { + ::EmptyClipboard(); + HGLOBAL clip; + char* buff; + clip = ::GlobalAlloc(GMEM_DDESHARE, temp.Length()+1); + buff = (char*)::GlobalLock(clip); + strcpy(buff, temp); + ::GlobalUnlock(clip); + ::SetClipboardData(CF_TEXT, clip); + ::CloseClipboard(); + } + nurb.Clear(); + } +} + + +void CMainFrame::OnSoundShowselectedsoundvolumes() +{ + g_qeglobals.d_savedinfo.showSoundWhenSelected ^= 1; + if (g_qeglobals.d_savedinfo.showSoundWhenSelected) { + g_qeglobals.d_savedinfo.showSoundAlways = false; + } + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SOUND_SHOWSOUNDVOLUMES,g_qeglobals.d_savedinfo.showSoundAlways); + m_wndToolBar.GetToolBarCtrl().CheckButton(ID_SOUND_SHOWSELECTEDSOUNDVOLUMES,g_qeglobals.d_savedinfo.showSoundWhenSelected); + Sys_UpdateWindows(W_XY | W_CAMERA); +} + +void CMainFrame::OnSelectAlltargets() +{ + Select_AllTargets(); +} + + +void CMainFrame::OnSelectCompleteEntity() +{ + brush_t* b = NULL; + entity_t* e = NULL; + + b = selected_brushes.next; + if ( b == &selected_brushes ) + { + return; //no brushes selected + } + + e = b->owner; + if ( b->owner == world_entity ) + { + return; //don't select the world entity + } + + for (b = e->brushes.onext; b != &e->brushes; b = b->onext) + { + Select_Brush ( b , false ); + } + Sys_UpdateWindows ( W_ALL ); +} + + + + +//--------------------------------------------------------------------------- +// OnPrecisionCursorCycle +// +// Called when the user presses the "cycle precision cursor mode" key. +// Cycles the precision cursor among the following three modes: +// PRECISION_CURSOR_NONE +// PRECISION_CURSOR_SNAP +// PRECISION_CURSOR_FREE +//--------------------------------------------------------------------------- +void CMainFrame::OnPrecisionCursorCycle() +{ + m_pActiveXY->CyclePrecisionCrosshairMode(); +} + +void CMainFrame::OnGenerateMaterialsList() +{ + idStrList mtrList; + idStr mtrName,mtrFileName; + + + g_Inspectors->consoleWnd.ExecuteCommand ( "clear" ); + Sys_BeginWait (); + common->Printf ( "Generating list of active materials...\n" ); + + for ( brush_t* b = active_brushes.next ; b != &active_brushes ; b=b->next ) { + if ( b->pPatch ){ + mtrName = b->pPatch->d_texture->GetName(); + if ( !mtrList.Find( mtrName) ) { + mtrList.Insert ( mtrName ); + } + + } + else { + for ( face_t* f = b->brush_faces ; f != NULL ; f=f->next) + { + mtrName = f->d_texture->GetName(); + if ( !mtrList.Find( mtrName) ) { + mtrList.Insert ( mtrName ); + } + + } + } + } + + mtrList.Sort(); + for ( int i = 0 ; i < mtrList.Num() ; i++ ) { + common->Printf ( "%s\n" , mtrList[i].c_str()); + } + + mtrFileName = currentmap; +// mtrFileName.ExtractFileName( mtrFileName ); + mtrFileName = mtrFileName.StripPath(); + + common->Printf ( "Done...found %i unique materials\n" , mtrList.Num()); + mtrFileName = mtrFileName + idStr ( "_Materials.txt" ); + g_Inspectors->SetMode ( W_CONSOLE , true ); + g_Inspectors->consoleWnd.SetConsoleText ( va ( "condump %s" , mtrFileName.c_str()) ); + + Sys_EndWait (); +} + +/* +======================================================================================================================= +======================================================================================================================= +*/ + + +void CMainFrame::OnSplinesAddPoints() { + g_Inspectors->entityDlg.AddCurvePoints(); +} + +void CMainFrame::OnSplinesEditPoints() { + g_Inspectors->entityDlg.EditCurvePoints(); +} + +void CMainFrame::OnSplinesDeletePoint() { + g_Inspectors->entityDlg.DeleteCurvePoint(); +} + +void CMainFrame::OnSplinesInsertPoint() { + g_Inspectors->entityDlg.InsertCurvePoint(); +} diff --git a/src/tools/radiant/MainFrm.h b/src/tools/radiant/MainFrm.h new file mode 100644 index 0000000..97863dc --- /dev/null +++ b/src/tools/radiant/MainFrm.h @@ -0,0 +1,559 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#if !defined(AFX_MAINFRM_H__330BBF0A_731C_11D1_B539_00AA00A410FC__INCLUDED_) +#define AFX_MAINFRM_H__330BBF0A_731C_11D1_B539_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + +#include "XYWnd.h" +#include "NewTexWnd.h" +#include "ZWnd.h" +#include "CamWnd.h" +#include "TextureBar.h" + + +const int RAD_SHIFT = 0x01; +const int RAD_ALT = 0x02; +const int RAD_CONTROL = 0x04; +const int RAD_PRESS = 0x08; + +struct SCommandInfo +{ + char* m_strCommand; + unsigned int m_nKey; + unsigned int m_nModifiers; + unsigned int m_nCommand; +}; + +struct SKeyInfo +{ + char* m_strName; + unsigned int m_nVKKey; +}; + + + + +class CMainFrame : public CFrameWnd +{ + DECLARE_DYNAMIC(CMainFrame) +public: + CMainFrame(); + void HandleKey(UINT nChar, UINT nRepCnt, UINT nFlags, bool bDown = true) + { + if (bDown) + OnKeyDown(nChar, nRepCnt, nFlags); + else + OnKeyUp(nChar, nRepCnt, nFlags); + }; + + // Attributes +public: + + // Operations +public: + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CMainFrame) +public: + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + virtual BOOL PreTranslateMessage(MSG* pMsg); +protected: + virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam); + virtual LRESULT DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam); + virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam); + virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext); + //}}AFX_VIRTUAL + + // Implementation +public: + void UpdatePatchToolbarButtons(); + void NudgeSelection(int nDirection, float fAmount); + void UpdateTextureBar(); + void SetButtonMenuStates(); + void SetTexValStatus(); + void SetGridStatus(); + void RoutineProcessing(); + CXYWnd* ActiveXY(); + void UpdateWindows(int nBits); + void SetStatusText(int nPane, const char* pText); + void UpdateStatusText(); + void SetWindowStyle(int nStyle); + bool GetNurbMode() { + return nurbMode; + } + idCurve_NURBS *GetNurb() { + return &nurb; + } + void OnPrecisionCursorCycle(); + + virtual ~CMainFrame(); + CXYWnd* GetXYWnd() {return m_pXYWnd;}; + CXYWnd* GetXZWnd() {return m_pXZWnd;}; + CXYWnd* GetYZWnd() {return m_pYZWnd;}; + CCamWnd* GetCamera() {return m_pCamWnd;}; + CZWnd* GetZWnd() {return m_pZWnd;}; + + void SetActiveXY(CXYWnd* p) + { + if (m_pActiveXY) + m_pActiveXY->SetActive(false); + m_pActiveXY = p; + + if (m_pActiveXY) + m_pActiveXY->SetActive(true); + + }; + +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + +protected: // control bar embedded members + CStatusBar m_wndStatusBar; + CToolBar m_wndToolBar; + CTextureBar m_wndTextureBar; + CSplitterWnd m_wndSplit; + CSplitterWnd m_wndSplit2; + CSplitterWnd m_wndSplit3; + CXYWnd* m_pXYWnd; + CXYWnd* m_pYZWnd; + CXYWnd* m_pXZWnd; + CCamWnd* m_pCamWnd; + CZWnd* m_pZWnd; + CString m_strStatus[15]; + CXYWnd* m_pActiveXY; + bool m_bCamPreview; + bool busy; + bool nurbMode; + idCurve_NURBS nurb; + // Generated message map functions +protected: + bool m_bDoLoop; + void CreateQEChildren(); + void LoadCommandMap(); + void SaveCommandMap(); + void ShowMenuItemKeyBindings(CMenu *pMenu); + void SetEntityCheck(); + void SetGridChecks(int nID); +public: + void Nudge(int nDim, float fNudge); + void SetBusy(bool b) { + busy = b; + } + + + // these are public so i can easily reflect messages + // from child windows.. + //{{AFX_MSG(CMainFrame) + afx_msg void OnBSPStatus(UINT wParam, long lParam); + afx_msg void OnBSPDone(UINT wParam, long lParam); + afx_msg void OnParentNotify(UINT message, LPARAM lParam); + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + afx_msg void OnTimer(UINT nIDEvent); + afx_msg void OnDestroy(); + afx_msg void OnClose(); + afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void ToggleCamera(); + afx_msg void OnFileClose(); + afx_msg void OnFileExit(); + afx_msg void OnFileLoadproject(); + afx_msg void OnFileNew(); + afx_msg void OnFileOpen(); + afx_msg void OnFilePointfile(); + afx_msg void OnFilePrint(); + afx_msg void OnFilePrintPreview(); + afx_msg void OnFileSave(); + afx_msg void OnFileSaveas(); + afx_msg void OnFileSaveCopy(); + afx_msg void OnViewShowModels(); + afx_msg void OnView100(); + afx_msg void OnViewCenter(); + afx_msg void OnViewConsole(); + afx_msg void OnViewDownfloor(); + afx_msg void OnViewEntity(); + afx_msg void OnViewMediaBrowser(); + afx_msg void OnViewFront(); + afx_msg void OnViewShowblocks(); + afx_msg void OnViewShowclip(); + afx_msg void OnViewShowTriggers(); + afx_msg void OnViewShowcoordinates(); + afx_msg void OnViewShowent(); + afx_msg void OnViewShowlights(); + afx_msg void OnViewShownames(); + afx_msg void OnViewShowpath(); + afx_msg void OnViewShowCombatNodes(); + afx_msg void OnViewShowwater(); + afx_msg void OnViewShowworld(); + afx_msg void OnViewTexture(); + afx_msg void OnViewUpfloor(); + afx_msg void OnViewXy(); + afx_msg void OnViewZ100(); + afx_msg void OnViewZoomin(); + afx_msg void OnViewZoomout(); + afx_msg void OnViewZzoomin(); + afx_msg void OnViewZzoomout(); + afx_msg void OnViewSide(); + afx_msg void OnTexturesShowinuse(); + afx_msg void OnTexturesInspector(); + afx_msg void OnMiscFindbrush(); + afx_msg void OnMiscGamma(); + afx_msg void OnMiscNextleakspot(); + afx_msg void OnMiscPreviousleakspot(); + afx_msg void OnMiscPrintxy(); + afx_msg void OnMiscSelectentitycolor(); + afx_msg void OnMiscFindOrReplaceEntity(); + afx_msg void OnMiscFindNextEntity(); + afx_msg void OnMiscSetViewPos(); + afx_msg void OnTexturebk(); + afx_msg void OnColorsMajor(); + afx_msg void OnColorsMinor(); + afx_msg void OnColorsXybk(); + afx_msg void OnBrush3sided(); + afx_msg void OnBrush4sided(); + afx_msg void OnBrush5sided(); + afx_msg void OnBrush6sided(); + afx_msg void OnBrush7sided(); + afx_msg void OnBrush8sided(); + afx_msg void OnBrush9sided(); + afx_msg void OnBrushArbitrarysided(); + afx_msg void OnBrushFlipx(); + afx_msg void OnBrushFlipy(); + afx_msg void OnBrushFlipz(); + afx_msg void OnBrushRotatex(); + afx_msg void OnBrushRotatey(); + afx_msg void OnBrushRotatez(); + afx_msg void OnRegionOff(); + afx_msg void OnRegionSetbrush(); + afx_msg void OnRegionSetselection(); + afx_msg void OnRegionSettallbrush(); + afx_msg void OnRegionSetxy(); + afx_msg void OnSelectionArbitraryrotation(); + afx_msg void OnSelectionClone(); + afx_msg void OnSelectionConnect(); + afx_msg void OnSelectionCsgsubtract(); + afx_msg void OnSelectionCsgmerge(); + afx_msg void OnSelectionDelete(); + afx_msg void OnSelectionDeselect(); + afx_msg void OnSelectionDragedges(); + afx_msg void OnSelectionDragvertecies(); + afx_msg void OnSelectionCenterOrigin(); + afx_msg void OnSelectionMakehollow(); + afx_msg void OnSelectionSelectcompletetall(); + afx_msg void OnSelectionSelectinside(); + afx_msg void OnSelectionSelectpartialtall(); + afx_msg void OnSelectionSelecttouching(); + afx_msg void OnSelectionUngroupentity(); + afx_msg void OnSelectionWireFrameOn(); + afx_msg void OnSelectionWireFrameOff(); + afx_msg void OnSelectionVisibleOn(); + afx_msg void OnSelectionVisibleOff(); + afx_msg void OnAutocaulk(); + afx_msg void OnUpdateAutocaulk(CCmdUI* pCmdUI); + + afx_msg void OnTexturesPopup(); + afx_msg void OnSplinesPopup(); + afx_msg void OnSplinesEditPoints(); + afx_msg void OnSplinesAddPoints(); + afx_msg void OnSplinesDeletePoint(); + afx_msg void OnSplinesInsertPoint(); + afx_msg void OnPopupSelection(); + afx_msg void OnViewChange(); + afx_msg void OnViewCameraupdate(); + afx_msg void OnUpdateViewCameraupdate(CCmdUI* pCmdUI); + afx_msg void OnSizing(UINT fwSide, LPRECT pRect); + afx_msg void OnHelpAbout(); + afx_msg void OnViewClipper(); + afx_msg void OnCameraAngledown(); + afx_msg void OnCameraAngleup(); + afx_msg void OnCameraBack(); + afx_msg void OnCameraDown(); + afx_msg void OnCameraForward(); + afx_msg void OnCameraLeft(); + afx_msg void OnCameraRight(); + afx_msg void OnCameraStrafeleft(); + afx_msg void OnCameraStraferight(); + afx_msg void OnCameraUp(); + afx_msg void OnGridToggle(); + afx_msg void OnPrefs(); + afx_msg void OnToggleToolbar(); + afx_msg void OnToggleTextureBar(); + afx_msg void OnTogglecamera(); + afx_msg void OnToggleview(); + afx_msg void OnTogglez(); + afx_msg void OnToggleLock(); + afx_msg void OnEditMapinfo(); + afx_msg void OnEditEntityinfo(); + afx_msg void OnViewNextview(); + afx_msg void OnHelpCommandlist(); + afx_msg void OnFileNewproject(); + afx_msg void OnFlipClip(); + afx_msg void OnClipSelected(); + afx_msg void OnSplitSelected(); + afx_msg void OnToggleviewXz(); + afx_msg void OnToggleviewYz(); + afx_msg void OnColorsBrush(); + afx_msg void OnColorsClipper(); + afx_msg void OnColorsGridtext(); + afx_msg void OnColorsSelectedbrush(); + afx_msg void OnColorsGridblock(); + afx_msg void OnColorsViewname(); + afx_msg void OnColorSetoriginal(); + afx_msg void OnColorSetqer(); + afx_msg void OnColorSetblack(); + afx_msg void OnColorSetSuperMal(); + afx_msg void OnColorSetMax(); + afx_msg void OnSnaptogrid(); + afx_msg void OnSelectScale(); + afx_msg void OnSelectMouserotate(); + afx_msg void OnEditCopybrush(); + afx_msg void OnEditPastebrush(); + afx_msg void OnEditUndo(); + afx_msg void OnEditRedo(); + afx_msg void OnUpdateEditUndo(CCmdUI* pCmdUI); + afx_msg void OnUpdateEditRedo(CCmdUI* pCmdUI); + afx_msg void OnSelectionInvert(); + afx_msg void OnSelectionTextureDec(); + afx_msg void OnSelectionTextureFit(); + afx_msg void OnSelectionTextureInc(); + afx_msg void OnSelectionTextureRotateclock(); + afx_msg void OnSelectionTextureRotatecounter(); + afx_msg void OnSelectionTextureScaledown(); + afx_msg void OnSelectionTextureScaleup(); + afx_msg void OnSelectionTextureShiftdown(); + afx_msg void OnSelectionTextureShiftleft(); + afx_msg void OnSelectionTextureShiftright(); + afx_msg void OnSelectionTextureShiftup(); + afx_msg void OnGridNext(); + afx_msg void OnGridPrev(); + afx_msg void OnSelectionTextureScaleLeft(); + afx_msg void OnSelectionTextureScaleRight(); + afx_msg void OnTextureReplaceall(); + afx_msg void OnScalelockx(); + afx_msg void OnScalelocky(); + afx_msg void OnScalelockz(); + afx_msg void OnSelectMousescale(); + afx_msg void OnViewCubicclipping(); + afx_msg void OnFileImport(); + afx_msg void OnFileProjectsettings(); + afx_msg void OnUpdateFileImport(CCmdUI* pCmdUI); + afx_msg void OnViewCubein(); + afx_msg void OnViewCubeout(); + afx_msg void OnFileSaveregion(); + afx_msg void OnUpdateFileSaveregion(CCmdUI* pCmdUI); + afx_msg void OnSelectionMovedown(); + afx_msg void OnSelectionMoveup(); + afx_msg void OnToolbarMain(); + afx_msg void OnToolbarTexture(); + afx_msg void OnSelectionPrint(); + afx_msg void OnSelectionTogglesizepaint(); + afx_msg void OnBrushMakecone(); + afx_msg void OnTexturesLoad(); + afx_msg void OnToggleRotatelock(); + afx_msg void OnCurveBevel(); + afx_msg void OnCurveIncreaseVert(); + afx_msg void OnCurveDecreaseVert(); + afx_msg void OnCurveIncreaseHorz(); + afx_msg void OnCurveDecreaseHorz(); + afx_msg void OnCurveCylinder(); + afx_msg void OnCurveEighthsphere(); + afx_msg void OnCurveEndcap(); + afx_msg void OnCurveHemisphere(); + afx_msg void OnCurveInvertcurve(); + afx_msg void OnCurveQuarter(); + afx_msg void OnCurveSphere(); + afx_msg void OnFileImportmap(); + afx_msg void OnFileExportmap(); + afx_msg void OnEditLoadprefab(); + afx_msg void OnViewShowcurves(); + afx_msg void OnSelectionSelectNudgedown(); + afx_msg void OnSelectionSelectNudgeleft(); + afx_msg void OnSelectionSelectNudgeright(); + afx_msg void OnSelectionSelectNudgeup(); + afx_msg void OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnTexturesLoadlist(); + afx_msg void OnDontselectcurve(); + afx_msg void OnDynamicLighting(); + afx_msg void OnCurveSimplepatchmesh(); + afx_msg void OnPatchToggleBox(); + afx_msg void OnPatchWireframe(); + afx_msg void OnCurvePatchcone(); + afx_msg void OnCurvePatchtube(); + afx_msg void OnPatchWeld(); + afx_msg void OnCurvePatchbevel(); + afx_msg void OnCurvePatchendcap(); + afx_msg void OnCurvePatchinvertedbevel(); + afx_msg void OnCurvePatchinvertedendcap(); + afx_msg void OnPatchDrilldown(); + afx_msg void OnCurveInsertcolumn(); + afx_msg void OnCurveInsertrow(); + afx_msg void OnCurveDeletecolumn(); + afx_msg void OnCurveDeleterow(); + afx_msg void OnCurveInsertAddcolumn(); + afx_msg void OnCurveInsertAddrow(); + afx_msg void OnCurveInsertInsertcolumn(); + afx_msg void OnCurveInsertInsertrow(); + afx_msg void OnCurveNegative(); + afx_msg void OnCurveNegativeTextureX(); + afx_msg void OnCurveNegativeTextureY(); + afx_msg void OnCurveDeleteFirstcolumn(); + afx_msg void OnCurveDeleteFirstrow(); + afx_msg void OnCurveDeleteLastcolumn(); + afx_msg void OnCurveDeleteLastrow(); + afx_msg void OnPatchBend(); + afx_msg void OnPatchInsdel(); + afx_msg void OnPatchEnter(); + afx_msg void OnPatchTab(); + afx_msg void OnCurvePatchdensetube(); + afx_msg void OnCurvePatchverydensetube(); + afx_msg void OnCurveCap(); + afx_msg void OnCurveCapInvertedbevel(); + afx_msg void OnCurveCapInvertedendcap(); + afx_msg void OnCurveRedisperseCols(); + afx_msg void OnCurveRedisperseRows(); + afx_msg void OnPatchNaturalize(); + afx_msg void OnPatchNaturalizeAlt(); + afx_msg void OnSnapToGrid(); + afx_msg void OnCurvePatchsquare(); + afx_msg void OnTexturesTexturewindowscale10(); + afx_msg void OnTexturesTexturewindowscale100(); + afx_msg void OnTexturesTexturewindowscale200(); + afx_msg void OnTexturesTexturewindowscale25(); + afx_msg void OnTexturesTexturewindowscale50(); + afx_msg void OnTexturesFlush(); + afx_msg void OnCurveOverlayClear(); + afx_msg void OnCurveOverlaySet(); + afx_msg void OnCurveThicken(); + afx_msg void OnCurveCyclecap(); + afx_msg void OnCurveCyclecapAlt(); + afx_msg void OnCurveMatrixTranspose(); + afx_msg void OnTexturesReloadshaders(); + afx_msg void OnShowEntities(); + afx_msg void OnViewEntitiesasBoundingbox(); + afx_msg void OnViewEntitiesasSelectedskinned(); + afx_msg void OnViewEntitiesasSelectedwireframe(); + afx_msg void OnViewEntitiesasSkinned(); + afx_msg void OnViewEntitiesasSkinnedandboxed(); + afx_msg void OnViewEntitiesasWireframe(); + afx_msg void OnViewShowhint(); + afx_msg void OnUpdateTexturesShowinuse(CCmdUI* pCmdUI); + afx_msg void OnTexturesShowall(); + afx_msg void OnTexturesHideall(); + afx_msg void OnPatchInspector(); + afx_msg void OnViewOpengllighting(); + afx_msg void OnSelectAll(); + afx_msg void OnViewShowcaulk(); + afx_msg void OnCurveFreeze(); + afx_msg void OnCurveUnFreeze(); + afx_msg void OnCurveUnFreezeAll(); + afx_msg void OnSelectReselect(); + afx_msg void OnViewShowangles(); + afx_msg void OnEditSaveprefab(); + afx_msg void OnCurveMoreendcapsbevelsSquarebevel(); + afx_msg void OnCurveMoreendcapsbevelsSquareendcap(); + afx_msg void OnBrushPrimitivesSphere(); + afx_msg void OnViewCrosshair(); + afx_msg void OnViewHideshowHideselected(); + afx_msg void OnViewHideshowHideNotselected(); + afx_msg void OnViewHideshowShowhidden(); + afx_msg void OnTexturesShadersShow(); + afx_msg void OnTexturesFlushUnused(); + afx_msg void OnViewGroups(); + afx_msg void OnDropGroupAddtoWorld(); + afx_msg void OnDropGroupName(); + afx_msg void OnDropGroupNewgroup(); + afx_msg void OnDropGroupRemove(); + afx_msg void OnProjectedLight(); + afx_msg void OnShowLighttextures(); + afx_msg void OnShowLightvolumes(); + afx_msg void OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized); + afx_msg void OnSplinesMode(); + afx_msg void OnSplinesLoad(); + afx_msg void OnSplinesSave(); + afx_msg void OnSplinesEdit(); + afx_msg void OnSplineTest(); + afx_msg void OnSplinesTarget(); + afx_msg void OnSplinesTargetPoints(); + afx_msg void OnSplinesCameraPoints(); + afx_msg void OnPopupNewcameraInterpolated(); + afx_msg void OnPopupNewcameraSpline(); + afx_msg void OnPopupNewcameraFixed(); + afx_msg void OnSelectionMoveonly(); + afx_msg void OnSelectBrushesOnly(); + afx_msg void OnSelectByBoundingBrush(); + afx_msg void OnSelectBrushlight(); + afx_msg void OnSelectionCombine(); + afx_msg void OnPatchCombine(); + afx_msg void OnShowDoom(); + afx_msg void OnViewRendermode(); + afx_msg void OnViewRebuildrenderdata(); + afx_msg void OnViewRealtimerebuild(); + afx_msg void OnViewRenderentityoutlines(); + afx_msg void OnViewMaterialanimation(); + afx_msg void OnAxialTextureByWidth(); + afx_msg void OnAxialTextureByHeight(); + afx_msg void OnAxialTextureArbitrary(); + afx_msg void OnSelectionExportToobj(); + afx_msg void OnSelectionExportToCM(); + afx_msg void OnViewRenderselection(); + afx_msg void OnSelectNomodels(); + afx_msg void OnViewShowShowvisportals(); + afx_msg void OnViewShowNoDraw(); + afx_msg void OnViewRendersound(); + afx_msg void OnSoundShowsoundvolumes(); + afx_msg void OnSoundShowselectedsoundvolumes(); + afx_msg void OnNurbEditor(); + afx_msg void OnSelectCompleteEntity(); + afx_msg void OnGenerateMaterialsList(); + afx_msg void OnMru(unsigned int nID); + afx_msg void OnViewNearest(unsigned int nID); + afx_msg void OnTextureWad(unsigned int nID); + afx_msg void OnBspCommand(unsigned int nID); + afx_msg void OnGrid1(unsigned int nID); + afx_msg void OnDisplayChange(WPARAM wp, LPARAM lp); + afx_msg void OnSelectAlltargets(); + + //}}AFX_MSG + void CheckTextureScale(int id); + + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_MAINFRM_H__330BBF0A_731C_11D1_B539_00AA00A410FC__INCLUDED_) diff --git a/src/tools/radiant/MapInfo.cpp b/src/tools/radiant/MapInfo.cpp new file mode 100644 index 0000000..b65832e --- /dev/null +++ b/src/tools/radiant/MapInfo.cpp @@ -0,0 +1,120 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" +#include "MapInfo.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CMapInfo dialog + + +CMapInfo::CMapInfo(CWnd* pParent /*=NULL*/) + : CDialog(CMapInfo::IDD, pParent) +{ + //{{AFX_DATA_INIT(CMapInfo) + m_nNet = 0; + m_nTotalBrushes = 0; + m_nTotalEntities = 0; + //}}AFX_DATA_INIT +} + + +void CMapInfo::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CMapInfo) + DDX_Control(pDX, IDC_LIST_ENTITIES, m_lstEntity); + DDX_Text(pDX, IDC_EDIT_NET, m_nNet); + DDX_Text(pDX, IDC_EDIT_TOTALBRUSHES, m_nTotalBrushes); + DDX_Text(pDX, IDC_EDIT_TOTALENTITIES, m_nTotalEntities); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CMapInfo, CDialog) + //{{AFX_MSG_MAP(CMapInfo) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CMapInfo message handlers + +BOOL CMapInfo::OnInitDialog() +{ + CDialog::OnInitDialog(); + + m_nTotalBrushes = 0; + m_nTotalEntities = 0; + m_nNet = 0; + for (brush_t* pBrush=active_brushes.next ; pBrush != &active_brushes ; pBrush=pBrush->next) + { + m_nTotalBrushes++; + if (pBrush->owner == world_entity) + m_nNet++; + } + + + CMapStringToPtr mapEntity; + + int nValue = 0; + for (entity_t* pEntity=entities.next ; pEntity != &entities ; pEntity=pEntity->next) + { + m_nTotalEntities++; + nValue = 0; + mapEntity.Lookup(pEntity->eclass->name, reinterpret_cast(nValue)); + nValue++ ; + mapEntity.SetAt(pEntity->eclass->name, reinterpret_cast(nValue)); + } + + m_lstEntity.ResetContent(); + m_lstEntity.SetTabStops(96); + CString strKey; + POSITION pos = mapEntity.GetStartPosition(); + while (pos) + { + mapEntity.GetNextAssoc(pos, strKey, reinterpret_cast(nValue)); + CString strList; + strList.Format("%s\t%i", strKey, nValue); + m_lstEntity.AddString(strList); + } + + UpdateData(FALSE); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} diff --git a/src/tools/radiant/MapInfo.h b/src/tools/radiant/MapInfo.h new file mode 100644 index 0000000..4309c3b --- /dev/null +++ b/src/tools/radiant/MapInfo.h @@ -0,0 +1,76 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#if !defined(AFX_MAPINFO_H__C241B9A2_819F_11D1_B548_00AA00A410FC__INCLUDED_) +#define AFX_MAPINFO_H__C241B9A2_819F_11D1_B548_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// MapInfo.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CMapInfo dialog + +class CMapInfo : public CDialog +{ +// Construction +public: + CMapInfo(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CMapInfo) + enum { IDD = IDD_DLG_MAPINFO }; + CListBox m_lstEntity; + int m_nNet; + int m_nTotalBrushes; + int m_nTotalEntities; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CMapInfo) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CMapInfo) + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_MAPINFO_H__C241B9A2_819F_11D1_B548_00AA00A410FC__INCLUDED_) diff --git a/src/tools/radiant/MediaPreviewDlg.cpp b/src/tools/radiant/MediaPreviewDlg.cpp new file mode 100644 index 0000000..c214677 --- /dev/null +++ b/src/tools/radiant/MediaPreviewDlg.cpp @@ -0,0 +1,181 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" +#include "mediapreviewdlg.h" + + +// CMediaPreviewDlg dialog + +IMPLEMENT_DYNAMIC(CMediaPreviewDlg, CDialog) +CMediaPreviewDlg::CMediaPreviewDlg(CWnd* pParent /*=NULL*/) + : CDialog(CMediaPreviewDlg::IDD, pParent) +{ + mode = MATERIALS; + media = ""; +} + +void CMediaPreviewDlg::SetMedia(const char *_media) { + media = _media; + Refresh(); +} + +void CMediaPreviewDlg::Refresh() { + if (mode == GUIS) { + const idMaterial *mat = declManager->FindMaterial("guisurfs/guipreview"); + materialEdit->SetGui( const_cast( mat ), media ); + drawMaterial.setMedia("guisurfs/guipreview"); + drawMaterial.setScale( 4.4f ); + } else { + drawMaterial.setMedia(media); + drawMaterial.setScale( 1.0f ); + } + wndPreview.setDrawable(&drawMaterial); + wndPreview.Invalidate(); + wndPreview.RedrawWindow(); + RedrawWindow(); +} + +CMediaPreviewDlg::~CMediaPreviewDlg() +{ +} + +void CMediaPreviewDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + DDX_Control(pDX, IDC_PREVIEW, wndPreview); +} + + +BEGIN_MESSAGE_MAP(CMediaPreviewDlg, CDialog) + ON_WM_SIZE() + ON_WM_DESTROY() + ON_WM_LBUTTONDOWN() + ON_WM_LBUTTONUP() + ON_WM_MOUSEMOVE() +END_MESSAGE_MAP() + + +// CMediaPreviewDlg message handlers + +BOOL CMediaPreviewDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + + wndPreview.setDrawable(&testDrawable); + CRect rct; + LONG lSize = sizeof(rct); + if (LoadRegistryInfo("Radiant::EditPreviewWindow", &rct, &lSize)) { + SetWindowPos(NULL, rct.left, rct.top, rct.Width(), rct.Height(), SWP_SHOWWINDOW); + } + + GetClientRect(rct); + int h = (mode == GUIS) ? (rct.Width() - 8) / 1.333333f : rct.Height() - 8; + wndPreview.SetWindowPos(NULL, 4, 4, rct.Width() - 8, h, SWP_SHOWWINDOW); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void CMediaPreviewDlg::OnSize(UINT nType, int cx, int cy) +{ + CDialog::OnSize(nType, cx, cy); + if (wndPreview.GetSafeHwnd() == NULL) { + return; + } + CRect rect; + GetClientRect(rect); + //int h = (mode == GUIS) ? (rect.Width() - 8) / 1.333333f : rect.Height() - 8; + int h = rect.Height() - 8; + wndPreview.SetWindowPos(NULL, 4, 4, rect.Width() - 8, h, SWP_SHOWWINDOW); +} + +void CMediaPreviewDlg::OnDestroy() +{ + if (GetSafeHwnd()) { + CRect rct; + GetWindowRect(rct); + SaveRegistryInfo("Radiant::EditPreviewWindow", &rct, sizeof(rct)); + } + + CDialog::OnDestroy(); +} + +void CMediaPreviewDlg::OnLButtonDown(UINT nFlags, CPoint point) +{ + if (mode == GUIS) { + idUserInterface *gui = uiManager->FindGui( media ); + if (gui) { + sysEvent_t ev; + memset( &ev, 0, sizeof( ev ) ); + ev.evType = SE_KEY; + ev.evValue = K_MOUSE1; + ev.evValue2 = 1; + gui->HandleEvent(&ev,0); + } + } + CDialog::OnLButtonDown(nFlags, point); +} + +void CMediaPreviewDlg::OnLButtonUp(UINT nFlags, CPoint point) +{ + if (mode == GUIS) { + idUserInterface *gui = uiManager->FindGui( media ); + if (gui) { + sysEvent_t ev; + memset( &ev, 0, sizeof( ev ) ); + ev.evType = SE_KEY; + ev.evValue = K_MOUSE1; + ev.evValue2 = 0; + gui->HandleEvent(&ev,0); + } + } + CDialog::OnLButtonUp(nFlags, point); +} + +void CMediaPreviewDlg::OnMouseMove(UINT nFlags, CPoint point) +{ + if (mode == GUIS) { + idUserInterface *gui = uiManager->FindGui( media ); + if (gui) { + CRect rct; + wndPreview.GetClientRect(rct); + sysEvent_t ev; + memset( &ev, 0, sizeof( ev ) ); + ev.evType = SE_MOUSE; + ev.evValue = (point.x / rct.Width()) * 640.0f; + ev.evValue2 = (point.y / rct.Height()) * 480.0f; + gui->HandleEvent(&ev, 0); + } + } + CDialog::OnMouseMove(nFlags, point); +} diff --git a/src/tools/radiant/MediaPreviewDlg.h b/src/tools/radiant/MediaPreviewDlg.h new file mode 100644 index 0000000..63429ec --- /dev/null +++ b/src/tools/radiant/MediaPreviewDlg.h @@ -0,0 +1,69 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#pragma once + + +// CMediaPreviewDlg dialog + +class CMediaPreviewDlg : public CDialog +{ + DECLARE_DYNAMIC(CMediaPreviewDlg) + +public: + enum { MATERIALS, GUIS }; + CMediaPreviewDlg(CWnd* pParent = NULL); // standard constructor + virtual ~CMediaPreviewDlg(); + + void SetMode(int _mode) { + mode = _mode; + } + + void SetMedia(const char *_media); + void Refresh(); + +// Dialog Data + enum { IDD = IDD_DIALOG_EDITPREVIEW }; + +protected: + idGLDrawable testDrawable; + idGLDrawableMaterial drawMaterial; + idGLWidget wndPreview; + int mode; + idStr media; + + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + + DECLARE_MESSAGE_MAP() +public: + virtual BOOL OnInitDialog(); + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnDestroy(); + afx_msg void OnLButtonDown(UINT nFlags, CPoint point); + afx_msg void OnLButtonUp(UINT nFlags, CPoint point); + afx_msg void OnMouseMove(UINT nFlags, CPoint point); +}; diff --git a/src/tools/radiant/NewProjDlg.cpp b/src/tools/radiant/NewProjDlg.cpp new file mode 100644 index 0000000..2666656 --- /dev/null +++ b/src/tools/radiant/NewProjDlg.cpp @@ -0,0 +1,71 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" +#include "NewProjDlg.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CNewProjDlg dialog + + +CNewProjDlg::CNewProjDlg(CWnd* pParent /*=NULL*/) + : CDialog(CNewProjDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(CNewProjDlg) + m_strName = _T(""); + //}}AFX_DATA_INIT +} + + +void CNewProjDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CNewProjDlg) + DDX_Text(pDX, IDC_EDIT_NAME, m_strName); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CNewProjDlg, CDialog) + //{{AFX_MSG_MAP(CNewProjDlg) + // NOTE: the ClassWizard will add message map macros here + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CNewProjDlg message handlers diff --git a/src/tools/radiant/NewProjDlg.h b/src/tools/radiant/NewProjDlg.h new file mode 100644 index 0000000..ccfdff6 --- /dev/null +++ b/src/tools/radiant/NewProjDlg.h @@ -0,0 +1,73 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#if !defined(AFX_NEWPROJDLG_H__1E2527A2_8447_11D1_B548_00AA00A410FC__INCLUDED_) +#define AFX_NEWPROJDLG_H__1E2527A2_8447_11D1_B548_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// NewProjDlg.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CNewProjDlg dialog + +class CNewProjDlg : public CDialog +{ +// Construction +public: + CNewProjDlg(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CNewProjDlg) + enum { IDD = IDD_DLG_NEWPROJECT }; + CString m_strName; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CNewProjDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CNewProjDlg) + // NOTE: the ClassWizard will add member functions here + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_NEWPROJDLG_H__1E2527A2_8447_11D1_B548_00AA00A410FC__INCLUDED_) diff --git a/src/tools/radiant/NewTexWnd.cpp b/src/tools/radiant/NewTexWnd.cpp new file mode 100644 index 0000000..28562f5 --- /dev/null +++ b/src/tools/radiant/NewTexWnd.cpp @@ -0,0 +1,924 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" +#include "NewTexWnd.h" +#include "io.h" + +#include "../../renderer/tr_local.h" + +#ifdef _DEBUG + #define new DEBUG_NEW + #undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +bool Sys_KeyDown( int key ) { + return ( ( ::GetAsyncKeyState( key ) & 0x8000 ) != 0 ); +} + +// CNewTexWnd +IMPLEMENT_DYNCREATE(CNewTexWnd, CWnd); + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +CNewTexWnd::CNewTexWnd() { + m_bNeedRange = true; + hglrcTexture = NULL; + hdcTexture = NULL; + cursor.x = cursor.y = 0; + origin.x = origin.y = 0; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +CNewTexWnd::~CNewTexWnd() { +} + +BEGIN_MESSAGE_MAP(CNewTexWnd, CWnd) +//{{AFX_MSG_MAP(CNewTexWnd) + ON_WM_CREATE() + ON_WM_SIZE() + ON_WM_PARENTNOTIFY() + ON_WM_KEYDOWN() + ON_WM_KEYUP() + ON_WM_PAINT() + ON_WM_VSCROLL() + ON_WM_LBUTTONDOWN() + ON_WM_MBUTTONDOWN() + ON_WM_RBUTTONDOWN() + ON_WM_LBUTTONUP() + ON_WM_MBUTTONUP() + ON_WM_RBUTTONUP() + ON_WM_MOUSEMOVE() + ON_WM_MOUSEWHEEL() + //}}AFX_MSG_MAP + ON_WM_SETFOCUS() +END_MESSAGE_MAP() +// +// ======================================================================================================================= +// CNewTexWnd message handlers +// ======================================================================================================================= +// +BOOL CNewTexWnd::PreCreateWindow(CREATESTRUCT &cs) { + WNDCLASS wc; + HINSTANCE hInstance = AfxGetInstanceHandle(); + if (::GetClassInfo(hInstance, TEXTURE_WINDOW_CLASS, &wc) == FALSE) { + // Register a new class + memset(&wc, 0, sizeof(wc)); + wc.style = CS_NOCLOSE | CS_PARENTDC; // | CS_OWNDC; + wc.hInstance = hInstance; + wc.lpszClassName = TEXTURE_WINDOW_CLASS; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.lpfnWndProc = ::DefWindowProc; + if (AfxRegisterClass(&wc) == FALSE) { + common->Warning("Radiant: failed to register %s (error %lu)", TEXTURE_WINDOW_CLASS, GetLastError()); + return FALSE; + } + } + + cs.lpszClass = TEXTURE_WINDOW_CLASS; + cs.lpszName = "TEX"; + if (cs.style != QE3_CHILDSTYLE && cs.style != QE3_STYLE) { + cs.style = QE3_SPLITTER_STYLE; + } + + return CWnd::PreCreateWindow(cs); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +int CNewTexWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) { + if (CWnd::OnCreate(lpCreateStruct) == -1) { + return -1; + } + + ShowScrollBar(SB_VERT, g_PrefsDlg.m_bTextureScrollbar); + m_bNeedRange = true; + + hdcTexture = GetDC(); + QEW_SetupPixelFormat(hdcTexture->m_hDC, false); + + EnableToolTips(TRUE); + EnableTrackingToolTips(TRUE); + + return 0; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CNewTexWnd::OnSize(UINT nType, int cx, int cy) { + CWnd::OnSize(nType, cx, cy); + GetClientRect(rectClient); + m_bNeedRange = true; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CNewTexWnd::OnParentNotify(UINT message, LPARAM lParam) { + CWnd::OnParentNotify(message, lParam); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CNewTexWnd::UpdatePrefs() { + ShowScrollBar(SB_VERT, g_PrefsDlg.m_bTextureScrollbar); + m_bNeedRange = true; + Invalidate(); + UpdateWindow(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CNewTexWnd::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { + g_pParentWnd->HandleKey(nChar, nRepCnt, nFlags); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CNewTexWnd::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) { + g_pParentWnd->HandleKey(nChar, nRepCnt, nFlags, false); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +const idMaterial *CNewTexWnd::NextPos() { + const idMaterial *mat = NULL; + while (1) { + if (currentIndex >= declManager->GetNumDecls( DECL_MATERIAL )) { + return NULL; + } + + mat = declManager->MaterialByIndex(currentIndex, false); + + currentIndex++; + + //if (mat->getName()[0] == '(') { // fake color texture + // continue; + //} + + if ( !mat->IsValid() ) { + continue; + } + + if (!mat->TestMaterialFlag(MF_EDITOR_VISIBLE)) { + continue; + } + break; + } + + // ensure it is uploaded + declManager->FindMaterial(mat->GetName()); + + int width = mat->GetEditorImage()->uploadWidth * ((float)g_PrefsDlg.m_nTextureScale / 100); + int height = mat->GetEditorImage()->uploadHeight * ((float)g_PrefsDlg.m_nTextureScale / 100); + + if (current.x + width > rectClient.Width() - 8 && currentRow) { + // go to the next row unless the texture is the first on the row + current.x = 8; + current.y -= currentRow + FONT_HEIGHT + 4; + currentRow = 0; + } + + draw = current; + + // Is our texture larger than the row? If so, grow the row height to match it + if (currentRow < height) { + currentRow = height; + } + + // never go less than 64, or the names get all crunched up + current.x += width < 64 ? 64 : width; + current.x += 8; + return mat; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CNewTexWnd::OnPaint() { + + CPaintDC dc(this); // device context for painting + + int nOld = g_qeglobals.d_texturewin.m_nTotalHeight; + + //hdcTexture = GetDC(); + if (!qwglMakeCurrent(dc.GetSafeHdc(), win32.hGLRC)) { + common->Printf("ERROR: wglMakeCurrent failed..\n "); + } + else { + const char *name; + qglClearColor + ( + g_qeglobals.d_savedinfo.colors[COLOR_TEXTUREBACK][0], + g_qeglobals.d_savedinfo.colors[COLOR_TEXTUREBACK][1], + g_qeglobals.d_savedinfo.colors[COLOR_TEXTUREBACK][2], + 0 + ); + qglViewport(0, 0, rectClient.Width(), rectClient.Height()); + qglScissor(0, 0, rectClient.Width(), rectClient.Height()); + qglMatrixMode(GL_PROJECTION); + qglLoadIdentity(); + qglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + qglDisable(GL_DEPTH_TEST); + qglDisable(GL_BLEND); + qglOrtho(0, rectClient.Width(), origin.y - rectClient.Height(), origin.y, -100, 100); + qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + + // init stuff + current.x = 8; + current.y = -8; + currentRow = 0; + currentIndex = 0; + while (1) { + const idMaterial *mat = NextPos(); + if (mat == NULL) { + break; + } + + int width = mat->GetEditorImage()->uploadWidth * ((float)g_PrefsDlg.m_nTextureScale / 100); + int height = mat->GetEditorImage()->uploadHeight * ((float)g_PrefsDlg.m_nTextureScale / 100); + + // Is this texture visible? + if ((draw.y - height - FONT_HEIGHT < origin.y) && (draw.y > origin.y - rectClient.Height())) { + // if in use, draw a background + qglLineWidth(1); + qglColor3f(1, 1, 1); + globalImages->BindNull(); + qglBegin(GL_LINE_LOOP); + qglVertex2f(draw.x - 1, draw.y + 1 - FONT_HEIGHT); + qglVertex2f(draw.x - 1, draw.y - height - 1 - FONT_HEIGHT); + qglVertex2f(draw.x + 1 + width, draw.y - height - 1 - FONT_HEIGHT); + qglVertex2f(draw.x + 1 + width, draw.y + 1 - FONT_HEIGHT); + qglEnd(); + + // Draw the texture + float fScale = (g_PrefsDlg.m_bHiColorTextures == TRUE) ? ((float)g_PrefsDlg.m_nTextureScale / 100) : 1.0; + + mat->GetEditorImage()->Bind(); + QE_CheckOpenGLForErrors(); + qglColor3f(1, 1, 1); + qglBegin(GL_QUADS); + qglTexCoord2f(0, 0); + qglVertex2f(draw.x, draw.y - FONT_HEIGHT); + qglTexCoord2f(1, 0); + qglVertex2f(draw.x + width, draw.y - FONT_HEIGHT); + qglTexCoord2f(1, 1); + qglVertex2f(draw.x + width, draw.y - FONT_HEIGHT - height); + qglTexCoord2f(0, 1); + qglVertex2f(draw.x, draw.y - FONT_HEIGHT - height); + qglEnd(); + + // draw the selection border + if ( !idStr::Icmp(g_qeglobals.d_texturewin.texdef.name, mat->GetName()) ) { + qglLineWidth(3); + qglColor3f(1, 0, 0); + globalImages->BindNull(); + + qglBegin(GL_LINE_LOOP); + qglVertex2f(draw.x - 4, draw.y - FONT_HEIGHT + 4); + qglVertex2f(draw.x - 4, draw.y - FONT_HEIGHT - height - 4); + qglVertex2f(draw.x + 4 + width, draw.y - FONT_HEIGHT - height - 4); + qglVertex2f(draw.x + 4 + width, draw.y - FONT_HEIGHT + 4); + qglEnd(); + + qglLineWidth(1); + } + + // draw the texture name + globalImages->BindNull(); + qglColor3f(1, 1, 1); + qglRasterPos2f(draw.x, draw.y - FONT_HEIGHT + 2); + + // don't draw the directory name + for (name = mat->GetName(); *name && *name != '/' && *name != '\\'; name++) { + ; + } + + if (!*name) { + name = mat->GetName(); + } + else { + name++; + } + qglCallLists(strlen(name), GL_UNSIGNED_BYTE, name); + //qglCallLists(va("%s -- %d, %d" strlen(name), GL_UNSIGNED_BYTE, name); + } + } + + g_qeglobals.d_texturewin.m_nTotalHeight = abs(draw.y) + 100; + + // reset the current texture + globalImages->BindNull(); + qglFinish(); + qwglSwapBuffers(dc.GetSafeHdc()); + TRACE("Texture Paint\n"); + } + + if (g_PrefsDlg.m_bTextureScrollbar && (m_bNeedRange || g_qeglobals.d_texturewin.m_nTotalHeight != nOld)) { + m_bNeedRange = false; + SetScrollRange(SB_VERT, 0, g_qeglobals.d_texturewin.m_nTotalHeight, TRUE); + } + + //ReleaseDC(hdcTexture); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CNewTexWnd::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar *pScrollBar) { + CWnd::OnVScroll(nSBCode, nPos, pScrollBar); + + int n = GetScrollPos(SB_VERT); + switch (nSBCode) + { + case SB_LINEUP: { + n = (n - 15 > 0) ? n - 15 : 0; + break; + } + + case SB_LINEDOWN: { + n = (n + 15 < g_qeglobals.d_texturewin.m_nTotalHeight) ? n + 15 : n; + break; + } + + case SB_PAGEUP: { + n = (n - g_qeglobals.d_texturewin.height > 0) ? n - g_qeglobals.d_texturewin.height : 0; + break; + } + + case SB_PAGEDOWN: { + n = (n + g_qeglobals.d_texturewin.height < g_qeglobals.d_texturewin.m_nTotalHeight) ? n + g_qeglobals.d_texturewin.height : n; + break; + } + + case SB_THUMBPOSITION: { + n = nPos; + break; + } + + case SB_THUMBTRACK: { + n = nPos; + break; + } + } + + SetScrollPos(SB_VERT, n); + origin.y = -n; + Invalidate(); + UpdateWindow(); + + // Sys_UpdateWindows(W_TEXTURE); +} + +BOOL CNewTexWnd::DestroyWindow() { + ReleaseDC(hdcTexture); + return CWnd::DestroyWindow(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +BOOL CNewTexWnd::Create +( + LPCTSTR lpszClassName, + LPCTSTR lpszWindowName, + DWORD dwStyle, + const RECT &rect, + CWnd *pParentWnd, + UINT nID, + CCreateContext *pContext +) { + BOOL ret = CWnd::Create(lpszClassName, lpszWindowName, dwStyle, rect, pParentWnd, nID, pContext); + if (ret) { + hdcTexture = GetDC(); + QEW_SetupPixelFormat(hdcTexture->m_hDC, false); + } + + return ret; +} + +const idMaterial *CNewTexWnd::getMaterialAtPoint(CPoint point) { + + // init stuff + int my = rectClient.Height() - 1 - point.y; + my += origin.y - rectClient.Height(); + + current.x = 8; + current.y = -8; + currentRow = 0; + currentIndex = 0; + + while (1) { + const idMaterial *mat = NextPos(); + if (mat == NULL) { + return NULL; + } + + int width = mat->GetEditorImage()->uploadWidth * ((float)g_PrefsDlg.m_nTextureScale / 100); + int height = mat->GetEditorImage()->uploadHeight * ((float)g_PrefsDlg.m_nTextureScale / 100); + //if (point.x > draw.x && point.x - draw.x < width && my < draw.y && my + draw.y < height + FONT_HEIGHT) { + if (point.x > draw.x && point.x - draw.x < width && my < draw.y && draw.y - my < height + FONT_HEIGHT) { + return mat; + } + + } + +} +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CNewTexWnd::OnLButtonDown(UINT nFlags, CPoint point) { + cursor = point; + + SetFocus(); + bool fitScale = Sys_KeyDown(VK_CONTROL); + bool edit = Sys_KeyDown(VK_SHIFT) && !fitScale; + + const idMaterial *mat = getMaterialAtPoint(point); + if (mat) { + Select_SetDefaultTexture(mat, fitScale, true); + } else { + Sys_Status("Did not select a texture\n", 0); + } + + // + UpdateSurfaceDialog(); + UpdatePatchInspector(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CNewTexWnd::OnMButtonDown(UINT nFlags, CPoint point) { + CWnd::OnMButtonDown(nFlags, point); + +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CNewTexWnd::OnRButtonDown(UINT nFlags, CPoint point) { + cursor = point; + SetFocus(); +} + +/* + ===============================t======================================================================================== + ======================================================================================================================= + */ +void CNewTexWnd::OnLButtonUp(UINT nFlags, CPoint point) { + CWnd::OnLButtonUp(nFlags, point); + g_pParentWnd->SetFocus(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CNewTexWnd::OnMButtonUp(UINT nFlags, CPoint point) { + CWnd::OnMButtonUp(nFlags, point); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CNewTexWnd::OnRButtonUp(UINT nFlags, CPoint point) { + CWnd::OnRButtonUp(nFlags, point); +} + +extern float fDiff(float f1, float f2); + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CNewTexWnd::OnMouseMove(UINT nFlags, CPoint point) { + int scale = 1; + + if (Sys_KeyDown(VK_SHIFT)) { + scale = 4; + } + + // rbutton = drag texture origin + if (Sys_KeyDown(VK_RBUTTON)) { + if (point.y != cursor.y) { + if (Sys_KeyDown(VK_MENU)) { + long *px = &point.x; + long *px2 = &cursor.x; + + if (fDiff(point.y, cursor.y) > fDiff(point.x, cursor.x)) { + px = &point.y; + px2 = &cursor.y; + } + + if (*px > *px2) { + // zoom in + g_PrefsDlg.m_nTextureScale += 4; + if (g_PrefsDlg.m_nTextureScale > 500) { + g_PrefsDlg.m_nTextureScale = 500; + } + } + else if (*px < *px2) { + // zoom out + g_PrefsDlg.m_nTextureScale -= 4; + if (g_PrefsDlg.m_nTextureScale < 1) { + g_PrefsDlg.m_nTextureScale = 1; + } + } + + *px2 = *px; + CPoint screen = cursor; + ClientToScreen(&screen); + SetCursorPos(screen.x, screen.y); + //Sys_SetCursorPos(cursor.x, cursor.y); + InvalidateRect(NULL, false); + UpdateWindow(); + } + else if (point.y != cursor.y || point.x != cursor.x) { + origin.y += (point.y - cursor.y) * scale; + if (origin.y > 0) { + origin.y = 0; + } + + //Sys_SetCursorPos(cursor.x, cursor.y); + CPoint screen = cursor; + ClientToScreen(&screen); + SetCursorPos(screen.x, screen.y); + if (g_PrefsDlg.m_bTextureScrollbar) { + SetScrollPos(SB_VERT, abs(origin.y)); + } + + InvalidateRect(NULL, false); + UpdateWindow(); + } + } + + return; + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CNewTexWnd::LoadMaterials() { +} + + +void Texture_SetTexture(texdef_t *texdef, brushprimit_texdef_t *brushprimit_texdef, bool bFitScale, bool bSetSelection) { + + if (texdef->name[0] == '(') { + Sys_Status("Can't select an entity texture\n", 0); + return; + } + + g_qeglobals.d_texturewin.texdef = *texdef; + + // + // store the texture coordinates for new brush primitive mode be sure that all the + // callers are using the default 2x2 texture + // + if (g_qeglobals.m_bBrushPrimitMode) { + g_qeglobals.d_texturewin.brushprimit_texdef = *brushprimit_texdef; + } + + g_dlgFind.updateTextures(texdef->name); + + if (!g_dlgFind.isOpen() && bSetSelection) { + Select_SetTexture(texdef, brushprimit_texdef, bFitScale); + } + + g_Inspectors->texWnd.EnsureTextureIsVisible(texdef->name); + + if ( g_Inspectors->mediaDlg.IsWindowVisible() ) { + g_Inspectors->mediaDlg.SelectCurrentItem(true, g_qeglobals.d_texturewin.texdef.name, CDialogTextures::MATERIALS); + } + + g_qeglobals.d_texturewin.texdef = *texdef; + // store the texture coordinates for new brush primitive mode be sure that all the + // callers are using the default 2x2 texture + // + if (g_qeglobals.m_bBrushPrimitMode) { + g_qeglobals.d_texturewin.brushprimit_texdef = *brushprimit_texdef; + } + + + Sys_UpdateWindows(W_TEXTURE); + + +} + +const idMaterial *Texture_LoadLight(const char *name) { + return declManager->FindMaterial(name); +} + + +void Texture_ClearInuse(void) { +} + +void Texture_ShowAll(void) { + int count = declManager->GetNumDecls( DECL_MATERIAL ); + for (int i = 0; i < count; i++) { + const idMaterial *mat = declManager->MaterialByIndex(i, false); + if ( mat ) { + mat->SetMaterialFlag(MF_EDITOR_VISIBLE); + } + } + g_Inspectors->SetWindowText("Textures (all)"); + Sys_UpdateWindows(W_TEXTURE); +} + +void Texture_HideAll() { + int count = declManager->GetNumDecls( DECL_MATERIAL ); + for (int i = 0; i < count; i++) { + const idMaterial *mat = declManager->MaterialByIndex(i, false); + if ( mat ) { + mat->ClearMaterialFlag(MF_EDITOR_VISIBLE); + } + } + g_Inspectors->SetWindowText("Textures (all)"); + Sys_UpdateWindows(W_TEXTURE); +} + +const idMaterial *Texture_ForName(const char *name) { + const idMaterial *mat = declManager->FindMaterial(name); + if ( !mat ) { + mat = declManager->FindMaterial("_default"); + } else { + mat->SetMaterialFlag(MF_EDITOR_VISIBLE); + } + return mat; +} + +void Texture_ShowInuse(void) { + Texture_HideAll(); + + brush_t *b; + for (b = active_brushes.next; b != NULL && b != &active_brushes; b = b->next) { + if (b->pPatch) { + Texture_ForName(b->pPatch->d_texture->GetName()); + } else { + for (face_t *f = b->brush_faces; f; f = f->next) { + Texture_ForName(f->texdef.name); + } + } + } + + for (b = selected_brushes.next; b != NULL && b != &selected_brushes; b = b->next) { + if (b->pPatch) { + Texture_ForName(b->pPatch->d_texture->GetName()); + } else { + for (face_t *f = b->brush_faces; f; f = f->next) { + Texture_ForName(f->texdef.name); + } + } + } + + Sys_UpdateWindows(W_TEXTURE); + + g_Inspectors->SetWindowText("Textures (in use)"); +} + +void Texture_Cleanup(CStringList *pList) { +} + +int texture_mode = GL_LINEAR_MIPMAP_LINEAR; +bool texture_showinuse = true; + + +/* + ======================================================================================================================= + Texture_SetMode + ======================================================================================================================= + */ +void Texture_SetMode(int iMenu) { + int iMode; + HMENU hMenu; + bool texturing = true; + + hMenu = GetMenu(g_pParentWnd->GetSafeHwnd()); + + switch (iMenu) + { + case ID_VIEW_NEAREST: + iMode = GL_NEAREST; + break; + case ID_VIEW_NEARESTMIPMAP: + iMode = GL_NEAREST_MIPMAP_NEAREST; + break; + case ID_VIEW_LINEAR: + iMode = GL_NEAREST_MIPMAP_LINEAR; + break; + case ID_VIEW_BILINEAR: + iMode = GL_LINEAR; + break; + case ID_VIEW_BILINEARMIPMAP: + iMode = GL_LINEAR_MIPMAP_NEAREST; + break; + case ID_VIEW_TRILINEAR: + iMode = GL_LINEAR_MIPMAP_LINEAR; + break; + + case ID_TEXTURES_WIREFRAME: + iMode = 0; + texturing = false; + break; + + case ID_TEXTURES_FLATSHADE: + default: + iMode = 0; + texturing = false; + break; + } + + CheckMenuItem(hMenu, ID_VIEW_NEAREST, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_VIEW_NEARESTMIPMAP, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_VIEW_LINEAR, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_VIEW_BILINEARMIPMAP, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_VIEW_BILINEAR, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_VIEW_TRILINEAR, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_TEXTURES_WIREFRAME, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(hMenu, ID_TEXTURES_FLATSHADE, MF_BYCOMMAND | MF_UNCHECKED); + + CheckMenuItem(hMenu, iMenu, MF_BYCOMMAND | MF_CHECKED); + + g_qeglobals.d_savedinfo.iTexMenu = iMenu; + texture_mode = iMode; + + if (!texturing && iMenu == ID_TEXTURES_WIREFRAME) { + g_pParentWnd->GetCamera()->Camera().draw_mode = cd_wire; + Map_BuildBrushData(); + Sys_UpdateWindows(W_ALL); + return; + } + else if (!texturing && iMenu == ID_TEXTURES_FLATSHADE) { + g_pParentWnd->GetCamera()->Camera().draw_mode = cd_solid; + Map_BuildBrushData(); + Sys_UpdateWindows(W_ALL); + return; + } + + if (g_pParentWnd->GetCamera()->Camera().draw_mode != cd_texture) { + g_pParentWnd->GetCamera()->Camera().draw_mode = cd_texture; + Map_BuildBrushData(); + } + + Sys_UpdateWindows(W_ALL); +} + + + +void CNewTexWnd::EnsureTextureIsVisible(const char *name) { + // scroll origin so the texture is completely on screen + // init stuff + current.x = 8; + current.y = -8; + currentRow = 0; + currentIndex = 0; + + while (1) { + const idMaterial *mat = NextPos(); + if (mat == NULL) { + break; + } + + int width = mat->GetEditorImage()->uploadWidth * ((float)g_PrefsDlg.m_nTextureScale / 100); + int height = mat->GetEditorImage()->uploadHeight * ((float)g_PrefsDlg.m_nTextureScale / 100); + + if ( !idStr::Icmp(name, mat->GetName()) ) { + if (current.y > origin.y) { + origin.y = current.y; + Sys_UpdateWindows(W_TEXTURE); + return; + } + + if (current.y - height - 2 * FONT_HEIGHT < origin.y - rectClient.Height()) { + origin.y = current.y - height - 2 * FONT_HEIGHT + rectClient.Height(); + Sys_UpdateWindows(W_TEXTURE); + return; + } + + return; + } + } + +} + + +BOOL CNewTexWnd::OnToolTipNotify( UINT id, NMHDR * pNMHDR, LRESULT * pResult ) { + static char tip[1024]; + CPoint point; + GetCursorPos(&point); + const idMaterial *mat = getMaterialAtPoint(point); + + if (mat) { + TOOLTIPTEXT *pTTT = (TOOLTIPTEXT *)pNMHDR; + strcpy(tip, mat->GetDescription()); + pTTT->lpszText = tip; + pTTT->hinst = NULL; + return(TRUE); + } + return(FALSE); +} + +int CNewTexWnd::OnToolHitTest(CPoint point, TOOLINFO * pTI) +{ + const idMaterial *mat = getMaterialAtPoint(point); + if (mat) { + return 0; + } + return -1; +} + +BOOL CNewTexWnd::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) +{ + OnVScroll((zDelta >= 0) ? SB_LINEUP : SB_LINEDOWN, 0, NULL); + OnVScroll((zDelta >= 0) ? SB_LINEUP : SB_LINEDOWN, 0, NULL); + OnVScroll((zDelta >= 0) ? SB_LINEUP : SB_LINEDOWN, 0, NULL); + OnVScroll((zDelta >= 0) ? SB_LINEUP : SB_LINEDOWN, 0, NULL); + OnVScroll((zDelta >= 0) ? SB_LINEUP : SB_LINEDOWN, 0, NULL); + OnVScroll((zDelta >= 0) ? SB_LINEUP : SB_LINEDOWN, 0, NULL); + return TRUE; +} + +BOOL CNewTexWnd::PreTranslateMessage(MSG* pMsg) +{ + if (pMsg->message == WM_KEYDOWN) { + if (pMsg->wParam == VK_ESCAPE) { + g_pParentWnd->GetCamera()->SetFocus(); + Select_Deselect(); + return TRUE; + } + if (pMsg->wParam == VK_RIGHT || pMsg->wParam == VK_LEFT || pMsg->wParam == VK_UP || pMsg->wParam == VK_DOWN) { + g_pParentWnd->PostMessage(WM_KEYDOWN, pMsg->wParam); + return TRUE; + } + } + return CWnd::PreTranslateMessage(pMsg); +} + +void CNewTexWnd::OnSetFocus(CWnd* pOldWnd) +{ + CWnd::OnSetFocus(pOldWnd); + Invalidate(); + RedrawWindow(); +} diff --git a/src/tools/radiant/NewTexWnd.h b/src/tools/radiant/NewTexWnd.h new file mode 100644 index 0000000..14938a3 --- /dev/null +++ b/src/tools/radiant/NewTexWnd.h @@ -0,0 +1,126 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#if !defined(NEWTEXWND_H) +#define NEWTEXWND_H + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// TexWnd.h : header file +// +#include "../../renderer/tr_local.h" +//#include "texwnd.h" + +///////////////////////////////////////////////////////////////////////////// +// CTexWnd window + +class CNewTexWnd : public CWnd +{ + DECLARE_DYNCREATE(CNewTexWnd); +// Construction +public: + CNewTexWnd(); + void UpdateFilter(const char* pFilter); + void UpdatePrefs(); + void FocusEdit(); + +// Attributes +public: + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CNewTexWnd) + public: + virtual BOOL DestroyWindow(); + virtual BOOL Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext = NULL); + protected: + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + //}}AFX_VIRTUAL + +// Implementation +public: + void EnsureTextureIsVisible(const char *name); + void LoadMaterials(); + virtual ~CNewTexWnd(); + BOOL OnToolTipNotify( UINT id, NMHDR * pNMHDR, LRESULT * pResult ); + int CNewTexWnd::OnToolHitTest(CPoint point, TOOLINFO * pTI); + virtual BOOL PreTranslateMessage(MSG* pMsg); + +protected: + //CTexEdit m_wndFilter; + //CButton m_wndShaders; + bool m_bNeedRange; + HGLRC hglrcTexture; + CDC *hdcTexture; + CPoint cursor; + CPoint origin; + CPoint draw; + CPoint drawRow; + CPoint current; + CRect rectClient; + int currentRow; + int currentIndex; + idList materialList; + + // Generated message map functions +protected: + const idMaterial* NextPos(); + const idMaterial *getMaterialAtPoint(CPoint point); + void InitPos(); + //{{AFX_MSG(CNewTexWnd) + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnParentNotify(UINT message, LPARAM lParam); + afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnPaint(); + afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); + afx_msg void OnLButtonDown(UINT nFlags, CPoint point); + afx_msg void OnMButtonDown(UINT nFlags, CPoint point); + afx_msg void OnRButtonDown(UINT nFlags, CPoint point); + afx_msg void OnLButtonUp(UINT nFlags, CPoint point); + afx_msg void OnMButtonUp(UINT nFlags, CPoint point); + afx_msg void OnRButtonUp(UINT nFlags, CPoint point); + afx_msg void OnMouseMove(UINT nFlags, CPoint point); + afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt); + //}}AFX_MSG + afx_msg void OnShaderClick(); + DECLARE_MESSAGE_MAP() +public: + afx_msg void OnSetFocus(CWnd* pOldWnd); +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(NEWTEXWND_H) diff --git a/src/tools/radiant/PARSE.CPP b/src/tools/radiant/PARSE.CPP new file mode 100644 index 0000000..c118923 --- /dev/null +++ b/src/tools/radiant/PARSE.CPP @@ -0,0 +1,157 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" + +char token[MAXTOKEN]; +bool unget; +const char *script_p; +int scriptline; + +void StartTokenParsing (const char *data) +{ + scriptline = 1; + script_p = data; + unget = false; +} + +bool WINAPI GetToken (bool crossline) +{ + char *token_p; + + if (unget) // is a token allready waiting? + { + unget = false; + return true; + } + +// +// skip space +// +skipspace: + while (*script_p <= 32) + { + if (!*script_p) + { + if (!crossline) + common->Printf("Warning: Line %i is incomplete [01]\n",scriptline); + return false; + } + if (*script_p++ == '\n') + { + if (!crossline) + common->Printf("Warning: Line %i is incomplete [02]\n",scriptline); + scriptline++; + } + } + + if (script_p[0] == '/' && script_p[1] == '/') // comment field + { + if (!crossline) + common->Printf("Warning: Line %i is incomplete [03]\n",scriptline); + while (*script_p++ != '\n') + if (!*script_p) + { + if (!crossline) + common->Printf("Warning: Line %i is incomplete [04]\n",scriptline); + return false; + } + goto skipspace; + } + +// +// copy token +// + token_p = token; + + if (*script_p == '"') + { + script_p++; + //if (*script_p == '"') // handle double quotes i suspect they are put in by other editors cccasionally + // script_p++; + while ( *script_p != '"' ) + { + if (!*script_p) + Error ("EOF inside quoted token"); + *token_p++ = *script_p++; + if (token_p == &token[MAXTOKEN]) + Error ("Token too large on line %i",scriptline); + } + script_p++; + //if (*script_p == '"') // handle double quotes i suspect they are put in by other editors cccasionally + // script_p++; + } + else while ( *script_p > 32 ) + { + *token_p++ = *script_p++; + if (token_p == &token[MAXTOKEN]) + Error ("Token too large on line %i",scriptline); + } + + *token_p = 0; + + return true; +} + +void WINAPI UngetToken (void) +{ + unget = true; +} + + +/* +============== +TokenAvailable + +Returns true if there is another token on the line +============== +*/ +bool TokenAvailable (void) +{ + const char *search_p; + + search_p = script_p; + + while ( *search_p <= 32) + { + if (*search_p == '\n') + return false; + if (*search_p == 0) + return false; + search_p++; + } + + if (*search_p == ';') + return false; + + return true; +} + diff --git a/src/tools/radiant/PARSE.H b/src/tools/radiant/PARSE.H new file mode 100644 index 0000000..2530c0f --- /dev/null +++ b/src/tools/radiant/PARSE.H @@ -0,0 +1,39 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#define MAXTOKEN 1024 + +extern char token[MAXTOKEN]; +extern int scriptline; + +// NOTE: added WINAPI call syntax to export these for plugins in _QERScripLibTable +void StartTokenParsing (const char *data); +bool WINAPI GetToken (bool crossline); +void WINAPI UngetToken (void); +bool TokenAvailable (void); + diff --git a/src/tools/radiant/PMESH.CPP b/src/tools/radiant/PMESH.CPP new file mode 100644 index 0000000..0bc3436 --- /dev/null +++ b/src/tools/radiant/PMESH.CPP @@ -0,0 +1,4461 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "DialogInfo.h" +#include "CapDialog.h" + +// externs +extern void MemFile_fprintf( CMemFile *pMemFile,const char *pText,... ); +extern face_t *Face_Alloc( void ); +void _Write3DMatrix( FILE *f,int y,int x,int z,float *m ); +void _Write3DMatrix( CMemFile *f,int y,int x,int z,float *m ); +extern void SamplePatch( float ctrl[3][3][5],int baseCol,int baseRow,int width,int horzSub,int vertSub,idDrawVert *outVerts,idDrawVert *drawVerts ); +patchMesh_t *Patch_GenerateGeneric( int width,int height,int orientation,const idVec3 &mins,const idVec3 &maxs ); + + + +patchMesh_t * MakeNewPatch( int width,int height ) { + patchMesh_t *pm = reinterpret_cast< patchMesh_t*>(Mem_ClearedAlloc(sizeof(patchMesh_t))); + pm->horzSubdivisions = DEFAULT_CURVE_SUBDIVISION; + pm->vertSubdivisions = DEFAULT_CURVE_SUBDIVISION; + pm->explicitSubdivisions = false; + pm->width = width; + pm->height = height; + pm->verts = reinterpret_cast< idDrawVert*>(Mem_ClearedAlloc(sizeof(idDrawVert) * width * height)); + //pm->ctrl = reinterpret_cast(Mem_ClearedAlloc(sizeof(idDrawVert) * width * height)); + //pm->ctrl = &pm->verts; + return pm; +} + + +void Patch_AdjustSize( patchMesh_t *p,int wadj,int hadj ) { + idDrawVert *newverts = reinterpret_cast< idDrawVert*>(Mem_ClearedAlloc(sizeof(idDrawVert) * (p->width + wadj) * (p->height + hadj))); + int copyWidth = (wadj < 0) ? p->width + wadj : p->width; + int copyHeight = (hadj < 0) ? p->height + hadj : p->height; + int copysize = copyWidth *copyHeight * sizeof(idDrawVert); + + for ( int i = 0; i < p->width; i++ ) { + for ( int j = 0; j < p->height; j++ ) { + newverts[j * (p->width + wadj) + i] = p->ctrl(i, j); + } + } + + p->width += wadj; + p->height += hadj; + Mem_Free(p->verts); + p->verts = newverts; +} + +// algorithm from Journal of graphics tools, 2(1):21-28, 1997 +bool RayIntersectsTri( const idVec3 &origin,const idVec3 &direction,const idVec3 &vert0,const idVec3 &vert1,const idVec3 &vert2,float &scale ) { + idVec3 edge1, edge2, tvec, pvec, qvec; + float det, inv_det; + scale = 0; + + /* find vectors for two edges sharing vert0 */ + edge1 = vert1 - vert0; + edge2 = vert2 - vert0; + + /* begin calculating determinant - also used to calculate U parameter */ + pvec.Cross(direction, edge2); + + /* if determinant is near zero, ray lies in plane of triangle */ + det = edge1 * pvec; + + if ( det > -VECTOR_EPSILON && det < VECTOR_EPSILON ) { + return false; + } + + inv_det = 1.0f / det; + + /* calculate distance from vert0 to ray origin */ + tvec = origin - vert0; + + /* calculate U parameter and test bounds */ + float u = (tvec *pvec) * inv_det; + if ( u < 0.0f || u> 1.0f ) { + return false; + } + + /* prepare to test V parameter */ + qvec.Cross(tvec, edge1); + + /* calculate V parameter and test bounds */ + float v = (direction *qvec) * inv_det; + if ( v < 0.0f || u + v> 1.0f ) { + return false; + } + + scale = tvec.Length(); + return true; +} + +bool Patch_Intersect( patchMesh_t *pm,idVec3 origin,idVec3 direction,float &scale ) { + int i, j; + //float scale; + idSurface_Patch cp (pm->width * 6, pm->height * 6); + + cp.SetSize(pm->width, pm->height); + for ( i = 0; i < pm->width; i++ ) { + for ( j = 0; j < pm->height; j++ ) { + (cp)[j * cp.GetWidth() + i].xyz = pm->ctrl(i, j).xyz; + (cp)[j * cp.GetWidth() + i].st = pm->ctrl(i, j).st; + } + } + + if ( pm->explicitSubdivisions ) { + cp.SubdivideExplicit(pm->horzSubdivisions, pm->vertSubdivisions, false); + } else { + cp.Subdivide(DEFAULT_CURVE_MAX_ERROR, DEFAULT_CURVE_MAX_ERROR, DEFAULT_CURVE_MAX_LENGTH, false); + } + + if ( cp.RayIntersection(origin, direction, scale) ) { + return true; + } else { + return false; + } + + /* + int width = cp.GetWidth(); + int height = cp.GetHeight(); + for ( i = 0 ; i < width - 1; i++ ) { + for ( j = 0 ; j < height - 1; j++ ) { + // v1-v2-v3-v4 makes a quad + int v1, v2, v3, v4; + v1 = j * width + i; + v2 = v1 + 1; + v3 = v1 + width + 1; + v4 = v1 + width; + if (RayIntersectsTri(origin, direction, (cp)[v1].xyz, (cp)[v2].xyz, (cp)[v3].xyz)) { + return true; + } + if (RayIntersectsTri(origin, direction, (cp)[v3].xyz, (cp)[v4].xyz, (cp)[v1].xyz)) { + return true; + } + } + } + return false; + */ +} + +patchMesh_t * Patch_MakeNew( patchMesh_t *p,int newWidth,int newHeight ) { + patchMesh_t *newPatch = MakeNewPatch(newWidth, newHeight); + newPatch->d_texture = p->d_texture; + newPatch->horzSubdivisions = p->horzSubdivisions; + newPatch->vertSubdivisions = p->vertSubdivisions; + newPatch->explicitSubdivisions = p->explicitSubdivisions; + return newPatch; +} + +void Patch_Combine( patchMesh_t *p,patchMesh_t *p2,int sourceCol1,int sourceCol2,int sourceRow1,int sourceRow2,bool invert1,bool invert2 ) { + int i, j, out; + patchMesh_t *newPatch = NULL; + if ( sourceCol1 >= 0 ) { + // adding width + if ( sourceCol2 >= 0 ) { + // from width + newPatch = Patch_MakeNew(p, p->width + p2->width - 1, p->height); + int adj1 = 1; + int adj2 = 1; + int col1 = 0; + int col2 = 1; + if ( sourceCol1 != 0 ) { + adj1 = -1; + col1 = p->width - 1; + } + if ( sourceCol2 != 0 ) { + adj2 = -1; + col2 = p2->width - 2; + } + + out = 0; + for ( i = 0; i < p->width; i++, col1 += adj1 ) { + int in = (invert1) ? p->height - 1 : 0; + for ( j = 0; j < p->height; j++ ) { + newPatch->ctrl(out, j) = p->ctrl(col1, in); + in += (invert1) ? -1 : 1; + } + out++; + } + + for ( i = 1; i < p2->width; i++, col2 += adj2 ) { + int in = (invert2) ? p2->height - 1 : 0; + for ( j = 0; j < p2->height; j++ ) { + newPatch->ctrl(out, j) = p2->ctrl(col2, in); + in += (invert2) ? -1 : 1; + } + out++; + } + } else { + // from height + newPatch = Patch_MakeNew(p, p->width + p2->height - 1, p->height); + int adj1 = 1; + int adj2 = 1; + int col1 = 0; + int row2 = 1; + if ( sourceCol1 != 0 ) { + adj1 = -1; + col1 = p->width - 1; + } + if ( sourceRow2 != 0 ) { + adj2 = -1; + row2 = p2->height - 2; + } + + out = 0; + for ( i = 0; i < p->width; i++, col1 += adj1 ) { + int in = (invert1) ? p->height - 1 : 0; + for ( j = 0; j < p->height; j++ ) { + newPatch->ctrl(out, j) = p->ctrl(col1, in); + in += (invert1) ? -1 : 1; + } + out++; + } + + for ( i = 1; i < p2->height; i++, row2 += adj2 ) { + int in = (invert2) ? p2->width - 1 : 0; + for ( j = 0; j < p2->width; j++ ) { + newPatch->ctrl(out, j) = p2->ctrl(in, row2); + in += (invert2) ? -1 : 1; + } + out++; + } + } + } else { + // adding height + if ( sourceRow1 >= 0 ) { + // from height + newPatch = Patch_MakeNew(p, p->width, p->height + p2->height - 1); + int adj1 = 1; + int adj2 = 1; + int row1 = 0; + int row2 = 0; + if ( sourceRow1 != 0 ) { + adj1 = -1; + row1 = p->height - 1; + } + if ( sourceRow2 != 0 ) { + adj2 = -1; + row2 = p2->height - 2; + } + + out = 0; + for ( i = 0; i < p->height; i++, row1 += adj1 ) { + int in = (invert1) ? p->width - 1 : 0; + for ( j = 0; j < p->width; j++ ) { + newPatch->ctrl(j, out) = p->ctrl(in, row1); + in += (invert1) ? -1 : 1; + } + out++; + } + + for ( i = 1; i < p2->height; i++, row2 += adj2 ) { + int in = (invert2) ? p->width - 1 : 0; + for ( j = 0; j < p2->width; j++ ) { + newPatch->ctrl(j, out) = p2->ctrl(in, row2); + in += (invert1) ? -1 : 1; + } + out++; + } + } else { + // from width + newPatch = Patch_MakeNew(p, p->width, p->height + p2->width - 1); + int adj1 = 1; + int adj2 = 1; + int row1 = 0; + int col2 = 0; + if ( sourceRow1 != 0 ) { + adj1 = -1; + row1 = p->height - 1; + } + if ( sourceCol2 != 0 ) { + adj2 = -1; + col2 = p2->width - 2; + } + + out = 0; + for ( i = 0; i < p->height; i++, row1 += adj1 ) { + int in = (invert1) ? p->width - 1 : 0; + for ( j = 0; j < p->width; j++ ) { + newPatch->ctrl(j, out) = p->ctrl(in, row1); + in += (invert1) ? -1 : 1; + } + out++; + } + + for ( i = 1; i < p2->width; i++, col2 += adj2 ) { + int in = (invert2) ? p->height - 1 : 0; + for ( j = 0; j < p2->height; j++ ) { + newPatch->ctrl(j, out) = p2->ctrl(col2, in); + in += (invert1) ? -1 : 1; + } + out++; + } + } + } + if ( newPatch ) { + AddBrushForPatch(newPatch, true); + Brush_Free(p->pSymbiot, true); + Brush_Free(p2->pSymbiot, true); + Patch_Naturalize(newPatch, true, true); + } +} + +#define WELD_EPSILON 0.001f + +void Patch_Weld( patchMesh_t *p,patchMesh_t *p2 ) { + // check against all 4 edges of p2 + // could roll this up but left it out for some semblence of clarity + // + + if ( p->width == p2->width ) { + int row = 0; + int row2 = 0; + while ( 1 ) { + bool match = true; + + // need to see if any of the corners match then run down or up based + // on the match edges + int col1 = 0; + int col2 = 0; + int adj1 = 1; + int adj2 = 1; + if ( p->ctrl(0, row).xyz.Compare(p2->ctrl(0, row2).xyz, WELD_EPSILON) ) { + } else if ( p->ctrl(0, row).xyz.Compare(p2->ctrl(p2->width - 1, row2).xyz, WELD_EPSILON) ) { + col2 = p2->width - 1; + adj2 = -1; + } else if ( p->ctrl(p->width - 1, row).xyz.Compare(p2->ctrl(p2->width - 1, row2).xyz, WELD_EPSILON) ) { + col2 = p2->width - 1; + adj2 = -1; + col1 = p->width - 1; + adj1 = -1; + } else if ( p->ctrl(p->width - 1, row).xyz.Compare(p2->ctrl(0, row2).xyz, WELD_EPSILON) ) { + col1 = p->width - 1; + adj1 = -1; + } else { + adj1 = 0; + } + + if ( adj1 ) { + for ( int col = 0; col < p->width; col++, col2 += adj2, col1 += adj1 ) { + if ( !p->ctrl(col1, row).xyz.Compare(p2->ctrl(col2, row2).xyz, WELD_EPSILON) ) { + match = false; + break; + } + } + } else { + match = false; + } + + if ( match ) { + // have a match weld these edges + common->Printf("Welding row %i with row %i\n", row, row2); + row = (row == 0) ? p->height - 1 : 0; + Patch_Combine(p, p2, -1, -1, row, row2, (adj1 == -1), (adj2 == -1)); + return; + } else if ( row2 == 0 ) { + row2 = p2->height - 1; + } else if ( row == 0 ) { + row = p->height - 1; + row2 = 0; + } else { + break; + } + } + } + + if ( p->width == p2->height ) { + int row = 0; + int col2 = 0; + while ( 1 ) { + bool match = true; + + int col1 = 0; + int adj1 = 1; + int row2 = 0; + int adj2 = 1; + if ( p->ctrl(0, row).xyz.Compare(p2->ctrl(col2, 0).xyz, WELD_EPSILON) ) { + } else if ( p->ctrl(0, row).xyz.Compare(p2->ctrl(col2, p2->height - 1).xyz, WELD_EPSILON) ) { + row2 = p2->height - 1; + adj2 = -1; + } else if ( p->ctrl(p->width - 1, row).xyz.Compare(p2->ctrl(col2, p2->height - 1).xyz, WELD_EPSILON) ) { + row2 = p2->height - 1; + adj2 = -1; + col1 = p->width - 1; + adj1 = -1; + } else if ( p->ctrl(p->width - 1, row).xyz.Compare(p2->ctrl(col2, 0).xyz, WELD_EPSILON) ) { + col1 = p->width - 1; + adj1 = -1; + } else { + adj1 = 0; + } + + if ( adj1 ) { + for ( int col = 0; col < p->width; col++, col1 += adj1, row2 += adj2 ) { + if ( !p->ctrl(col1, row).xyz.Compare(p2->ctrl(col2, row2).xyz, WELD_EPSILON) ) { + match = false; + break; + } + } + } else { + match = false; + } + + if ( match ) { + // have a match weld these edges + common->Printf("Welding row %i with col %i\n", row, col2); + row = (row == 0) ? p->height - 1 : 0; + Patch_Combine(p, p2, -1, col2, row, -1, (adj1 == -1), (adj2 == -1)); + return; + } else if ( col2 == 0 ) { + col2 = p2->width - 1; + } else if ( row == 0 ) { + row = p->height - 1; + col2 = 0; + } else { + break; + } + } + } + + if ( p->height == p2->width ) { + int col = 0; + int row2 = 0; + while ( 1 ) { + bool match = true; + + + int row1 = 0; + int adj1 = 1; + int col2 = 0; + int adj2 = 1; + if ( p->ctrl(col, 0).xyz.Compare(p2->ctrl(0, row2).xyz, WELD_EPSILON) ) { + } else if ( p->ctrl(col, 0).xyz.Compare(p2->ctrl(p2->width - 1, row2).xyz, WELD_EPSILON) ) { + col2 = p2->width - 1; + adj2 = -1; + } else if ( p->ctrl(col, p->height - 1).xyz.Compare(p2->ctrl(p2->width - 1, row2).xyz, WELD_EPSILON) ) { + col2 = p2->width - 1; + adj2 = -1; + row1 = p2->height - 1; + adj2 = -1; + } else if ( p->ctrl(col, p->height - 1).xyz.Compare(p2->ctrl(0, row2).xyz, WELD_EPSILON) ) { + row1 = p2->height - 1; + adj2 = -1; + } else { + adj1 = 0; + } + + if ( adj1 ) { + for ( int row = 0; row < p->height; row++, row1 += adj1, col2 += adj2 ) { + if ( !p->ctrl(col, row1).xyz.Compare(p2->ctrl(col2, row2).xyz, WELD_EPSILON) ) { + match = false; + break; + } + } + } else { + match = false; + } + + if ( match ) { + // have a match weld these edges + common->Printf("Welding col %i with row %i\n", col, row2); + col = (col == 0) ? p->width - 1 : 0; + Patch_Combine(p, p2, col, -1, -1, row2, (adj1 == -1), (adj2 == -1)); + return; + } else if ( row2 == 0 ) { + row2 = p2->height - 1; + } else if ( col == 0 ) { + col = p->width - 1; + row2 = 0; + } else { + break; + } + } + } + + if ( p->height == p2->height ) { + int col = 0; + int col2 = 0; + while ( 1 ) { + bool match = true; + + + int row1 = 0; + int adj1 = 1; + int row2 = 0; + int adj2 = 1; + if ( p->ctrl(col, 0).xyz.Compare(p2->ctrl(col2, 0).xyz, WELD_EPSILON) ) { + } else if ( p->ctrl(col, 0).xyz.Compare(p2->ctrl(col2, p2->height - 1).xyz, WELD_EPSILON) ) { + row2 = p2->height - 1; + adj2 = -1; + } else if ( p->ctrl(col, p2->height - 1).xyz.Compare(p2->ctrl(col2, p2->height - 1).xyz, WELD_EPSILON) ) { + row2 = p2->height - 1; + adj2 = -1; + row1 = p->height - 1; + adj1 = -1; + } else if ( p->ctrl(col, p2->height - 1).xyz.Compare(p2->ctrl(col2, 0).xyz, WELD_EPSILON) ) { + row1 = p->height - 1; + adj1 = -1; + } else { + adj1 = 0; + } + + if ( adj1 ) { + for ( int row = 0; row < p->height; row++, row1 += adj1, row2 += adj2 ) { + if ( !p->ctrl(col, row1).xyz.Compare(p2->ctrl(col2, row2).xyz, WELD_EPSILON) ) { + match = false; + break; + } + } + } else { + match = false; + } + + if ( match ) { + // have a match weld these edges + common->Printf("Welding col %i with col %i\n", col, col2); + col = (col == 0) ? p->width - 1 : 0; + Patch_Combine(p, p2, col, col2, -1, -1, (adj1 == -1), (adj2 == -1)); + return; + } else if ( col2 == 0 ) { + col2 = p2->width - 1; + } else if ( col == 0 ) { + col = p->width - 1; + col2 = 0; + } else { + break; + } + } + } + + + Sys_Status("Unable to weld patches, no common sized edges.\n"); +} + + +// used for a save spot +patchMesh_t *patchSave = NULL; + +// Tracks the selected patch for point manipulation/update. FIXME: Need to revert back to a generalized +// brush approach +//--int g_nSelectedPatch = -1; + +// HACK: for tracking which view generated the click +// as we dont want to deselect a point on a same point +// click if it is from a different view +int g_nPatchClickedView = -1; +bool g_bSameView = false; + + +// globals +bool g_bPatchShowBounds = false; +bool g_bPatchWireFrame = false; +bool g_bPatchWeld = true; +bool g_bPatchDrillDown = true; +bool g_bPatchInsertMode = false; +bool g_bPatchBendMode = false; +int g_nPatchBendState = -1; +int g_nPatchInsertState = -1; +int g_nBendOriginIndex = 0; +idVec3 g_vBendOrigin; + +bool g_bPatchAxisOnRow = true; +int g_nPatchAxisIndex = 0; +bool g_bPatchLowerEdge = true; + +// BEND states +enum { + BEND_SELECT_ROTATION = 0, + BEND_SELECT_ORIGIN, + BEND_SELECT_EDGE, + BEND_BENDIT, + BEND_STATE_COUNT +}; + +const char *g_pBendStateMsg[] = { + "Use TAB to cycle through available bend axis. Press ENTER when the desired one is highlighted.", "Use TAB to cycle through available rotation axis. This will LOCK around that point. You may also use Shift + Middle Click to select an arbitrary point. Press ENTER when the desired one is highlighted", "Use TAB to choose which side to bend. Press ENTER when the desired one is highlighted.", "Use the MOUSE to bend the patch. It uses the same ui rules as Free Rotation. Press ENTER to accept the bend, press ESC to abandon it and exit Bend mode", "" +}; + +// INSERT states +enum { + INSERT_SELECT_EDGE = 0, + INSERT_STATE_COUNT +}; + +const char *g_pInsertStateMsg[] = { + "Use TAB to cycle through available rows/columns for insertion/deletion. Press INS to insert at the highlight, DEL to remove the pair" +}; + + +float *g_InversePoints[1024]; + +const float fFullBright = 1.0f; +const float fLowerLimit = 0.5f; +const float fDec = 0.05f; + + +void Patch_SetType( patchMesh_t *p,int nType ) { + p->type = (p->type & PATCH_STYLEMASK) | nType; +} + +void Patch_SetStyle( patchMesh_t *p,int nStyle ) { + p->type = (p->type & PATCH_TYPEMASK) | nStyle; +} + +/* +================== +Patch_MemorySize +================== +*/ +int Patch_MemorySize( patchMesh_t *p ) { + return (sizeof(patchMesh_t) + p->width * p->height * sizeof(idDrawVert)); +} + + + +/* +=============== +InterpolateInteriorPoints +=============== +*/ +void InterpolateInteriorPoints( patchMesh_t *p ) { + int i, j, k; + int next, prev; + + for ( i = 0 ; i < p->width ; i += 2 ) { + next = (i == p->width - 1) ? 1 : (i + 1) % p->width; + prev = (i == 0) ? p->width - 2 : i - 1; + for ( j = 0 ; j < p->height ; j++ ) { + for ( k = 0 ; k < 3 ; k++ ) { + p->ctrl(i, j).xyz[k] = (p->ctrl(next, j).xyz[k] + p->ctrl(prev, j).xyz[k]) * 0.5; + } + } + } +} + +/* +================= +MakeMeshNormals + +================= +*/ +int neighbors[8][2] = { + {0,1}, {1,1}, {1,0}, {1, -1}, {0, -1}, { - 1, -1}, { - 1,0}, { - 1,1} +}; + +void Patch_MeshNormals( patchMesh_t *in ) { + int i, j, k, dist; + idVec3 normal; + idVec3 sum; + int count; + idVec3 base; + idVec3 delta; + int x, y; + idDrawVert *dv; + idVec3 around[8], temp; + bool good[8]; + bool wrapWidth, wrapHeight; + float len; + + wrapWidth = false; + for ( i = 0 ; i < in->height ; i++ ) { + VectorSubtract(in->ctrl(0, i).xyz, in->ctrl(in->width - 1, i).xyz, delta); + len = delta.Length(); + if ( len > 1.0f ) { + break; + } + } + if ( i == in->height ) { + wrapWidth = true; + } + + wrapHeight = false; + for ( i = 0 ; i < in->width ; i++ ) { + VectorSubtract(in->ctrl(i, 0).xyz, in->ctrl(i, in->height - 1).xyz, delta); + len = delta.Length(); + if ( len > 1.0f ) { + break; + } + } + if ( i == in->width ) { + wrapHeight = true; + } + + + for ( i = 0 ; i < in->width ; i++ ) { + for ( j = 0 ; j < in->height ; j++ ) { + count = 0; + //--dv = reinterpret_cast(in.ctrl[j*in.width+i]); + dv = &in->ctrl(i, j); + VectorCopy(dv->xyz, base); + for ( k = 0 ; k < 8 ; k++ ) { + around[k] = vec3_origin; + good[k] = false; + + for ( dist = 1 ; dist <= 3 ; dist++ ) { + x = i + neighbors[k][0] * dist; + y = j + neighbors[k][1] * dist; + if ( wrapWidth ) { + if ( x < 0 ) { + x = in->width - 1 + x; + } else if ( x >= in->width ) { + x = 1 + x - in->width; + } + } + if ( wrapHeight ) { + if ( y < 0 ) { + y = in->height - 1 + y; + } else if ( y >= in->height ) { + y = 1 + y - in->height; + } + } + + if ( x < 0 || x >= in->width || y < 0 || y >= in->height ) { + break; // edge of patch + } + //--VectorSubtract( in.ctrl[y*in.width+x]->xyz, base, temp ); + VectorSubtract(in->ctrl(x, y).xyz, base, temp); + if ( temp.Normalize() == 0 ) { + continue; // degenerate edge, get more dist + } else { + good[k] = true; + VectorCopy(temp, around[k]); + break; // good edge + } + } + } + + sum = vec3_origin; + for ( k = 0 ; k < 8 ; k++ ) { + if ( !good[k] || !good[(k + 1) & 7] ) { + continue; // didn't get two points + } + normal = around[(k + 1) & 7].Cross(around[k]); + if ( normal.Normalize() == 0 ) { + continue; + } + VectorAdd(normal, sum, sum); + count++; + } + if ( count == 0 ) { + //printf("bad normal\n"); + count = 1; + //continue; + } + dv->normal = sum; + dv->normal.Normalize(); + } + } +} + +void Patch_MakeDirty( patchMesh_t *p ) { + assert(p); + p->nListID = -1; + p->nListIDCam = -1; + p->nListSelected = -1; +} + + +/* +================== +Patch_CalcBounds +================== +*/ +void Patch_CalcBounds( patchMesh_t *p,idVec3 &vMin,idVec3 &vMax ) { + vMin[0] = vMin[1] = vMin[2] = 999999; + vMax[0] = vMax[1] = vMax[2] = -999999; + + Patch_MakeDirty(p); + for ( int w = 0; w < p->width; w++ ) { + for ( int h = 0; h < p->height; h++ ) { + for ( int j = 0; j < 3; j++ ) { + float f = p->ctrl(w, h).xyz[j]; + if ( f < vMin[j] ) + vMin[j] = f; + if ( f > vMax[j] ) + vMax[j] = f; + } + } + } +} + +/* +================== +Brush_RebuildBrush +================== +*/ +void Brush_RebuildBrush( brush_t *b,idVec3 vMins,idVec3 vMaxs,bool patch ) { + // + // Total hack job + // Rebuilds a brush + int i, j; + face_t *f, *next; + idVec3 pts[4][2]; + texdef_t texdef; + // free faces + + for ( j = 0; j < 3; j++ ) { + if ( (int) vMins[j] == (int) vMaxs[j] ) { + vMins[j] -= 4; + vMaxs[j] += 4; + } + } + + + for ( f = b->brush_faces ; f ; f = next ) { + next = f->next; + if ( f ) { + texdef = f->texdef; + } + Face_Free(f); + } + + b->brush_faces = NULL; + + // left the last face so we can use its texdef + + for ( i = 0 ; i < 3 ; i++ ) { + if ( vMaxs[i] < vMins[i] ) { + Error("Brush_RebuildBrush: backwards"); + } + } + + pts[0][0][0] = vMins[0]; + pts[0][0][1] = vMins[1]; + + pts[1][0][0] = vMins[0]; + pts[1][0][1] = vMaxs[1]; + + pts[2][0][0] = vMaxs[0]; + pts[2][0][1] = vMaxs[1]; + + pts[3][0][0] = vMaxs[0]; + pts[3][0][1] = vMins[1]; + + for ( i = 0 ; i < 4 ; i++ ) { + pts[i][0][2] = vMins[2]; + pts[i][1][0] = pts[i][0][0]; + pts[i][1][1] = pts[i][0][1]; + pts[i][1][2] = vMaxs[2]; + } + + for ( i = 0 ; i < 4 ; i++ ) { + f = Face_Alloc(); + f->texdef = texdef; + f->next = b->brush_faces; + b->brush_faces = f; + j = (i + 1) % 4; + + VectorCopy(pts[j][1], f->planepts[0]); + VectorCopy(pts[i][1], f->planepts[1]); + VectorCopy(pts[i][0], f->planepts[2]); + } + + f = Face_Alloc(); + f->texdef = texdef; + f->next = b->brush_faces; + b->brush_faces = f; + + VectorCopy(pts[0][1], f->planepts[0]); + VectorCopy(pts[1][1], f->planepts[1]); + VectorCopy(pts[2][1], f->planepts[2]); + + f = Face_Alloc(); + f->texdef = texdef; + f->next = b->brush_faces; + b->brush_faces = f; + + VectorCopy(pts[2][0], f->planepts[0]); + VectorCopy(pts[1][0], f->planepts[1]); + VectorCopy(pts[0][0], f->planepts[2]); + + Brush_Build(b); +} + +void WINAPI Patch_Rebuild( patchMesh_t *p ) { + idVec3 vMin, vMax; + Patch_CalcBounds(p, vMin, vMax); + Brush_RebuildBrush(p->pSymbiot, vMin, vMax); + Patch_MakeDirty(p); +} + +/* +================== +AddBrushForPatch +================== + adds a patch brush and ties it to this patch id +*/ +brush_t * AddBrushForPatch( patchMesh_t *pm,bool bLinkToWorld ) { + // find the farthest points in x,y,z + idVec3 vMin, vMax; + Patch_CalcBounds(pm, vMin, vMax); + + for ( int j = 0; j < 3; j++ ) { + if ( idMath::Fabs(vMin[j] - vMax[j]) <= VECTOR_EPSILON ) { + vMin[j] -= 4; + vMax[j] += 4; + } + } + + texdef_t td; + //td.SetName(pm->d_texture->getName()); + brush_t *b = Brush_Create(vMin, vMax, &td); + //brush_t *b = Brush_Create(vMin, vMax, &g_qeglobals.d_texturewin.texdef); + + // FIXME: this entire type of linkage needs to be fixed + b->pPatch = pm; + pm->pSymbiot = b; + pm->bSelected = false; + pm->bOverlay = false; + pm->nListID = -1; + pm->nListIDCam = -1; + + if ( bLinkToWorld ) { + Brush_AddToList(b, &active_brushes); + Entity_LinkBrush(world_entity, b); + Brush_Build(b); + } + + return b; +} + +void Patch_SetPointIntensities( int n ) { +#if 0 + patchMesh_t *p = patchMeshes[n]; + for (int i = 0; i < p->width; i++) { + for (int j = 0; j < p->height; j++) { + + } + } +#endif +} + +// very approximate widths and heights + +/* +================== +Patch_Width +================== +*/ +float Patch_Width( patchMesh_t *p ) { + float f = 0; + for ( int j = 0; j < p->height - 1; j++ ) { + float t = 0; + for ( int i = 0; i < p->width - 1; i++ ) { + idVec3 vTemp; + vTemp = p->ctrl(i, j).xyz - p->ctrl(i + 1, j).xyz; + t += vTemp.Length(); + } + if ( f < t ) { + f = t; + } + } + return f; +} + +/* +================== +Patch_Height +================== +*/ +float Patch_Height( patchMesh_t *p ) { + float f = 0; + for ( int j = 0; j < p->width - 1; j++ ) { + float t = 0; + for ( int i = 0; i < p->height - 1; i++ ) { + idVec3 vTemp; + vTemp = p->ctrl(j, i).xyz - p->ctrl(j, i + 1).xyz; + t += vTemp.Length(); + } + if ( f < t ) { + f = t; + } + } + return f; +} + +/* +================== +Patch_WidthDistanceTo +================== +*/ +float Patch_WidthDistanceTo( patchMesh_t *p,int j ) { + float f = 0; + for ( int i = 0; i < j ; i++ ) { + idVec3 vTemp; + vTemp = p->ctrl(i, 0).xyz - p->ctrl(i + 1, 0).xyz; + f += vTemp.Length(); + } + return f; +} + +/* +================== +Patch_HeightDistanceTo +================== +*/ +float Patch_HeightDistanceTo( patchMesh_t *p,int j ) { + float f = 0; + for ( int i = 0; i < j ; i++ ) { + idVec3 vTemp; + vTemp = p->ctrl(0, i).xyz - p->ctrl(0, i + 1).xyz; + f += vTemp.Length(); + } + return f; +} + + + +/* +================== +Patch_Naturalize +================== +texture = TotalTexture * LengthToThisControlPoint / TotalControlPointLength + +dist( this control point to first control point ) / dist ( last control pt to first) +*/ +void Patch_Naturalize( patchMesh_t *p,bool horz,bool vert,bool alt ) { + int i, j; + + int nWidth = p->d_texture->GetEditorImage()->uploadWidth * 0.5; + int nHeight = p->d_texture->GetEditorImage()->uploadHeight * 0.5; + float fPWidth = Patch_Width(p); + float fPHeight = Patch_Height(p); + float xAccum = 0; + for ( i = 0 ; i < ((alt) ? p->height : p->width) ; i++ ) { + float yAccum = 0; + for ( j = 0 ; j < ((alt) ? p->width : p->height) ; j++ ) { + int r = ((alt) ? j : i); + int c = ((alt) ? i : j); + p->ctrl(r, c).st[0] = (fPWidth / nWidth) * xAccum / fPWidth; + p->ctrl(r, c).st[1] = (fPHeight / nHeight) * yAccum / fPHeight; + if ( alt ) { + yAccum = Patch_WidthDistanceTo(p, j + 1); + } else { + yAccum = Patch_HeightDistanceTo(p, j + 1); + } + } + if ( alt ) { + xAccum = Patch_HeightDistanceTo(p, i + 1); + } else { + xAccum = Patch_WidthDistanceTo(p, i + 1); + } + } + + Patch_MakeDirty(p); +} + +/* + if (bIBevel) + { + VectorCopy(p->ctrl(1,0], p->ctrl(1,1]); + } + + if (bIEndcap) + { + VectorCopy(p->ctrl(3,0], p->ctrl(4,1]); + VectorCopy(p->ctrl(2,0], p->ctrl(3,1]); + VectorCopy(p->ctrl(2,0], p->ctrl(2,1]); + VectorCopy(p->ctrl(2,0], p->ctrl(1,1]); + VectorCopy(p->ctrl(1,0], p->ctrl(0,1]); + VectorCopy(p->ctrl(1,0], p->ctrl(0,2]); + VectorCopy(p->ctrl(1,0], p->ctrl(1,2]); + VectorCopy(p->ctrl(2,0], p->ctrl(2,2]); + VectorCopy(p->ctrl(3,0], p->ctrl(3,2]); + VectorCopy(p->ctrl(3,0], p->ctrl(4,2]); + } +*/ + +int Index3By[][2] = { + {0,0}, {1,0}, {2,0}, {2,1}, {2,2}, {1,2}, {0,2}, {0,1}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0} +}; + +int Index5By[][2] = { + {0,0}, {1,0}, {2,0}, {3,0}, {4,0}, {4,1}, {4,2}, {4,3}, {4,4}, {3,4}, {2,4}, {1,4}, {0,4}, {0,3}, {0,2}, {0,1} +}; + + + +int Interior3By[][2] = { + {1,1} +}; + +int Interior5By[][2] = { + {1,1}, {2,1}, {3,1}, {1,2}, {2,2}, {3,2}, {1,3}, {2,3}, {3,3} +}; + +int Interior3ByCount = sizeof(Interior3By) / sizeof(int[2]); +int Interior5ByCount = sizeof(Interior5By) / sizeof(int[2]); + +face_t * Patch_GetAxisFace( patchMesh_t *p ) { + face_t *f = NULL; + idVec3 vTemp; + brush_t *b = p->pSymbiot; + + for ( f = b->brush_faces ; f ; f = f->next ) { + vTemp = (*f->face_winding)[1].ToVec3() - (*f->face_winding)[0].ToVec3(); + int nScore = 0; + + // default edge faces on caps are 8 high so + // as soon as we hit one that is bigger it should be on the right axis + for ( int j = 0; j < 3; j++ ) { + if ( vTemp[j] > 8 ) + nScore++; + } + + if ( nScore > 0 ) { + break; + } + } + + if ( f == NULL ) { + f = b->brush_faces; + } + return f; +} + +int g_nFaceCycle = 0; + +face_t * nextFace( patchMesh_t *p ) { + brush_t *b = p->pSymbiot; + face_t *f = NULL; + int n = 0; + for ( f = b->brush_faces ; f && n <= g_nFaceCycle; f = f->next ) { + n++; + } + + g_nFaceCycle++; + + if ( g_nFaceCycle > 5 ) { + g_nFaceCycle = 0; + f = b->brush_faces; + } + + return f; +} + + +void Patch_CapTexture( patchMesh_t *p,bool bFaceCycle = false,bool alt = false ) { + Patch_MeshNormals(p); + face_t *f = (bFaceCycle) ? nextFace(p) : Patch_GetAxisFace(p); + idVec3 vSave; + VectorCopy(f->plane, vSave); + float fRotate = f->texdef.rotate; + f->texdef.rotate = 0; + float fScale[2]; + fScale[0] = f->texdef.scale[0]; + fScale[1] = f->texdef.scale[1]; + f->texdef.scale[0] = (float) p->d_texture->GetEditorImage()->uploadWidth / 32.0f; + f->texdef.scale[1] = (float) p->d_texture->GetEditorImage()->uploadHeight / 32.0f; + float fShift[2]; + fShift[0] = f->texdef.shift[0]; + fShift[1] = f->texdef.shift[1]; + f->texdef.shift[0] = 0; + f->texdef.shift[1] = 0; + + for ( int i = 0 ; i < p->width; i++ ) { + for ( int j = 0 ; j < p->height ; j++ ) { + if ( !bFaceCycle ) { + VectorCopy(p->ctrl(i, j).normal, f->plane); + } + idVec5 temp; + temp.x = p->ctrl(i, j).xyz.x; + temp.y = p->ctrl(i, j).xyz.y; + temp.z = p->ctrl(i, j).xyz.z; + EmitTextureCoordinates(temp, f->d_texture, f, true); + p->ctrl(i, j).st.x = temp.s; + p->ctrl(i, j).st.y = temp.t; + } + } + + VectorCopy(vSave, f->plane); + f->texdef.rotate = fRotate; + f->texdef.scale[0] = fScale[0]; + f->texdef.scale[1] = fScale[1]; + f->texdef.shift[0] = fShift[0]; + f->texdef.shift[1] = fShift[1]; + Patch_ScaleTexture(p, 1.0f, -1.0f, false); + Patch_MakeDirty(p); +} + +void FillPatch( patchMesh_t *p,idVec3 v ) { + for ( int i = 0; i < p->width; i++ ) { + for ( int j = 0; j < p->height; j++ ) { + VectorCopy(v, p->ctrl(i, j).xyz); + } + } +} + +brush_t * Cap( patchMesh_t *pParent,bool bByColumn,bool bFirst ) { + brush_t *b; + patchMesh_t *p; + idVec3 vMin, vMax; + int i, j; + + bool bSmall = true; + // make a generic patch + if ( pParent->width <= 9 ) { + b = Patch_GenericMesh(3, 3, 2, false, false, pParent); + } else { + b = Patch_GenericMesh(5, 5, 2, false, false, pParent); + bSmall = false; + } + + if ( !b ) { + Sys_Status("Unable to cap. You may need to ungroup the patch.\n"); + return NULL; + } + + p = b->pPatch; + p->type |= PATCH_CAP; + + vMin[0] = vMin[1] = vMin[2] = 99999; + vMax[0] = vMax[1] = vMax[2] = -99999; + + // we seam the column edge, FIXME: this might need to be able to seem either edge + // + int nSize = (bByColumn) ? pParent->width : pParent->height; + int nIndex = (bFirst) ? 0 : (bByColumn) ? pParent->height - 1 : pParent->width - 1; + + FillPatch(p, pParent->ctrl(0, nIndex).xyz); + + for ( i = 0; i < nSize; i++ ) { + if ( bByColumn ) { + if ( bSmall ) { + VectorCopy(pParent->ctrl(i, nIndex).xyz, p->ctrl(Index3By[i][0], Index3By[i][1]).xyz); + } else { + VectorCopy(pParent->ctrl(i, nIndex).xyz, p->ctrl(Index5By[i][0], Index5By[i][1]).xyz); + } + } else { + if ( bSmall ) { + VectorCopy(pParent->ctrl(nIndex, i).xyz, p->ctrl(Index3By[i][0], Index3By[i][1]).xyz); + } else { + VectorCopy(pParent->ctrl(nIndex, i).xyz, p->ctrl(Index5By[i][0], Index5By[i][1]).xyz); + } + } + + for ( j = 0; j < 3; j++ ) { + float f = (bSmall) ? p->ctrl(Index3By[i][0], Index3By[i][1]).xyz[j] : p->ctrl(Index5By[i][0], Index5By[i][1]).xyz[j]; + if ( f < vMin[j] ) + vMin[j] = f; + if ( f > vMax[j] ) + vMax[j] = f; + } + } + + idVec3 vTemp; + for ( j = 0; j < 3; j++ ) { + vTemp[j] = vMin[j] + abs((vMax[j] - vMin[j]) * 0.5); + } + + int nCount = (bSmall) ? Interior3ByCount : Interior5ByCount; + for ( j = 0; j < nCount; j++ ) { + if ( bSmall ) { + VectorCopy(vTemp, p->ctrl(Interior3By[j][0], Interior3By[j][1]).xyz); + } else { + VectorCopy(vTemp, p->ctrl(Interior5By[j][0], Interior5By[j][1]).xyz); + } + } + + if ( bFirst ) { + idDrawVert vertTemp; + for ( i = 0; i < p->width; i++ ) { + for ( j = 0; j < p->height / 2; j++ ) { + memcpy(&vertTemp, &p->ctrl(i, p->height - 1 - j), sizeof(idDrawVert)); + memcpy(&p->ctrl(i, p->height - 1 - j), &p->ctrl(i, j), sizeof(idDrawVert)); + memcpy(&p->ctrl(i, j), &vertTemp, sizeof(idDrawVert)); + } + } + } + + Patch_Rebuild(p); + Patch_CapTexture(p); + return p->pSymbiot; +} + +brush_t * CapSpecial( patchMesh_t *pParent,int nType,bool bFirst ) { + brush_t *b; + patchMesh_t *p; + idVec3 vMin, vMax, vTemp; + int i, j; + + if ( nType == CCapDialog::IENDCAP ) { + b = Patch_GenericMesh(5, 3, 2, false, false, pParent); + } else { + b = Patch_GenericMesh(3, 3, 2, false, false, pParent); + } + + if ( !b ) { + Sys_Status("Unable to cap. Make sure you ungroup before re-capping."); + return NULL; + } + + p = b->pPatch; + p->type |= PATCH_CAP; + + vMin[0] = vMin[1] = vMin[2] = 99999; + vMax[0] = vMax[1] = vMax[2] = -99999; + + int nSize = pParent->width; + int nIndex = (bFirst) ? 0 : pParent->height - 1; + + // parent bounds are used for some things + Patch_CalcBounds(pParent, vMin, vMax); + + for ( j = 0; j < 3; j++ ) { + vTemp[j] = vMin[j] + abs((vMax[j] - vMin[j]) * 0.5); + } + + if ( nType == CCapDialog::IBEVEL ) { + VectorCopy(pParent->ctrl(0, nIndex).xyz, p->ctrl(0, 0).xyz); + VectorCopy(pParent->ctrl(2, nIndex).xyz, p->ctrl(0, 2).xyz); + VectorCopy(pParent->ctrl(1, nIndex).xyz, p->ctrl(0, 1).xyz); + VectorCopy(pParent->ctrl(1, nIndex).xyz, p->ctrl(2, 2).xyz); + VectorCopy(pParent->ctrl(1, nIndex).xyz, p->ctrl(1, 0).xyz); + VectorCopy(pParent->ctrl(1, nIndex).xyz, p->ctrl(1, 1).xyz); + VectorCopy(pParent->ctrl(1, nIndex).xyz, p->ctrl(1, 2).xyz); + VectorCopy(pParent->ctrl(1, nIndex).xyz, p->ctrl(2, 0).xyz); + VectorCopy(pParent->ctrl(1, nIndex).xyz, p->ctrl(2, 1).xyz); + } else if ( nType == CCapDialog::BEVEL ) { + idVec3 p1, p2, p3, p4, temp, dir; + + VectorCopy(pParent->ctrl(0, nIndex).xyz, p3); + VectorCopy(pParent->ctrl(1, nIndex).xyz, p1); + VectorCopy(pParent->ctrl(2, nIndex).xyz, p2); + + VectorSubtract(p3, p2, dir); + dir.Normalize(); + VectorSubtract(p1, p2, temp); + float dist = DotProduct(temp, dir); + + VectorScale(dir, dist, temp); + + VectorAdd(p2, temp, temp); + + VectorSubtract(temp, p1, temp); + VectorScale(temp, 2, temp); + VectorAdd(p1, temp, p4); + + VectorCopy(p4, p->ctrl(0, 0).xyz); + VectorCopy(p4, p->ctrl(1, 0).xyz); + VectorCopy(p4, p->ctrl(0, 1).xyz); + VectorCopy(p4, p->ctrl(1, 1).xyz); + VectorCopy(p4, p->ctrl(0, 2).xyz); + VectorCopy(p4, p->ctrl(1, 2).xyz); + VectorCopy(p3, p->ctrl(2, 0).xyz); + VectorCopy(p1, p->ctrl(2, 1).xyz); + VectorCopy(p2, p->ctrl(2, 2).xyz); + } else if ( nType == CCapDialog::ENDCAP ) { + VectorAdd(pParent->ctrl(4, nIndex).xyz, pParent->ctrl(0, nIndex).xyz, vTemp); + VectorScale(vTemp, 0.5, vTemp); + VectorCopy(pParent->ctrl(0, nIndex).xyz, p->ctrl(0, 0).xyz); + VectorCopy(vTemp, p->ctrl(1, 0).xyz); + VectorCopy(pParent->ctrl(4, nIndex).xyz, p->ctrl(2, 0).xyz); + + VectorCopy(pParent->ctrl(2, nIndex).xyz, p->ctrl(0, 2).xyz); + VectorCopy(pParent->ctrl(2, nIndex).xyz, p->ctrl(1, 2).xyz); + VectorCopy(pParent->ctrl(2, nIndex).xyz, p->ctrl(2, 2).xyz); + VectorCopy(pParent->ctrl(2, nIndex).xyz, p->ctrl(1, 1).xyz); + + VectorCopy(pParent->ctrl(1, nIndex).xyz, p->ctrl(0, 1).xyz); + VectorCopy(pParent->ctrl(3, nIndex).xyz, p->ctrl(2, 1).xyz); + } else { + VectorCopy(pParent->ctrl(0, nIndex).xyz, p->ctrl(0, 0).xyz); + VectorCopy(pParent->ctrl(1, nIndex).xyz, p->ctrl(1, 0).xyz); + VectorCopy(pParent->ctrl(2, nIndex).xyz, p->ctrl(2, 0).xyz); + VectorCopy(pParent->ctrl(3, nIndex).xyz, p->ctrl(3, 0).xyz); + VectorCopy(pParent->ctrl(4, nIndex).xyz, p->ctrl(4, 0).xyz); + + VectorCopy(pParent->ctrl(1, nIndex).xyz, p->ctrl(0, 1).xyz); + VectorCopy(pParent->ctrl(1, nIndex).xyz, p->ctrl(1, 1).xyz); + VectorCopy(pParent->ctrl(2, nIndex).xyz, p->ctrl(2, 1).xyz); + VectorCopy(pParent->ctrl(3, nIndex).xyz, p->ctrl(3, 1).xyz); + VectorCopy(pParent->ctrl(3, nIndex).xyz, p->ctrl(4, 1).xyz); + + VectorCopy(pParent->ctrl(1, nIndex).xyz, p->ctrl(0, 2).xyz); + VectorCopy(pParent->ctrl(1, nIndex).xyz, p->ctrl(1, 2).xyz); + VectorCopy(pParent->ctrl(2, nIndex).xyz, p->ctrl(2, 2).xyz); + VectorCopy(pParent->ctrl(3, nIndex).xyz, p->ctrl(3, 2).xyz); + VectorCopy(pParent->ctrl(3, nIndex).xyz, p->ctrl(4, 2).xyz); + } + + + bool bEndCap = (nType == CCapDialog::ENDCAP || nType == CCapDialog::IENDCAP); + if ( (!bFirst && !bEndCap) || (bFirst && bEndCap) ) { + idDrawVert vertTemp; + for ( i = 0; i < p->width; i++ ) { + for ( j = 0; j < p->height / 2; j++ ) { + memcpy(&vertTemp, &p->ctrl(i, p->height - 1 - j), sizeof(idDrawVert)); + memcpy(&p->ctrl(i, p->height - 1 - j), &p->ctrl(i, j), sizeof(idDrawVert)); + memcpy(&p->ctrl(i, j), &vertTemp, sizeof(idDrawVert)); + } + } + } + + //--Patch_CalcBounds(p, vMin, vMax); + //--Brush_RebuildBrush(p->pSymbiot, vMin, vMax); + Patch_Rebuild(p); + Patch_CapTexture(p); + return p->pSymbiot; +} + + +void Patch_CapCurrent( bool bInvertedBevel,bool bInvertedEndcap ) { + patchMesh_t *pParent = NULL; + brush_t *b[4]; + brush_t *pCap = NULL; + b[0] = b[1] = b[2] = b[3] = NULL; + int nIndex = 0; + + if ( !QE_SingleBrush() ) { + Sys_Status("Cannot cap multiple selection. Please select a single patch.\n"); + return; + } + + + for ( brush_t*pb = selected_brushes.next ; pb != NULL && pb != &selected_brushes ; pb = pb->next ) { + if ( pb->pPatch ) { + pParent = pb->pPatch; + // decide which if any ends we are going to cap + // if any of these compares hit, it is a closed patch and as such + // the generic capping will work.. if we do not find a closed edge + // then we need to ask which kind of cap to add + if ( pParent->ctrl(0, 0).xyz.Compare(pParent->ctrl(pParent->width - 1, 0).xyz) ) { + pCap = Cap(pParent, true, false); + if ( pCap != NULL ) { + b[nIndex++] = pCap; + } + } + if ( pParent->ctrl(0, pParent->height - 1).xyz.Compare(pParent->ctrl(pParent->width - 1, pParent->height - 1).xyz) ) { + pCap = Cap(pParent, true, true); + if ( pCap != NULL ) { + b[nIndex++] = pCap; + } + } + if ( pParent->ctrl(0, 0).xyz.Compare(pParent->ctrl(0, pParent->height - 1).xyz) ) { + pCap = Cap(pParent, false, false); + if ( pCap != NULL ) { + b[nIndex++] = pCap; + } + } + if ( pParent->ctrl(pParent->width - 1, 0).xyz.Compare(pParent->ctrl(pParent->width - 1, pParent->height - 1).xyz) ) { + pCap = Cap(pParent, false, true); + if ( pCap != NULL ) { + b[nIndex++] = pCap; + } + } + } + } + + if ( pParent ) { + // if we did not cap anything with the above tests + if ( nIndex == 0 ) { + CCapDialog dlg; + if ( dlg.DoModal() == IDOK ) { + b[nIndex++] = CapSpecial(pParent, dlg.getCapType(), false); + b[nIndex++] = CapSpecial(pParent, dlg.getCapType(), true); + } + } + + if ( nIndex > 0 ) { + while ( nIndex > 0 ) { + nIndex--; + if ( b[nIndex] ) { + Select_Brush(b[nIndex]); + } + } + eclass_t*pecNew = Eclass_ForName("func_static", false); + if ( pecNew ) { + entity_t*e = Entity_Create(pecNew); + SetKeyValue(e, "type", "patchCapped"); + } + } + } +} + + +//FIXME: Table drive all this crap +// +void GenerateEndCaps( brush_t *brushParent,bool bBevel,bool bEndcap,bool bInverted ) { + brush_t *b, *b2; + patchMesh_t *p, *p2, *pParent; + idVec3 vTemp, vMin, vMax; + int i, j; + + pParent = brushParent->pPatch; + + Patch_CalcBounds(pParent, vMin, vMax); + // basically generate two endcaps, place them, and link the three brushes with a func_group + + if ( pParent->width > 9 ) { + b = Patch_GenericMesh(5, 3, 2, false, false, pParent); + } else { + b = Patch_GenericMesh(3, 3, 2, false, false, pParent); + } + p = b->pPatch; + + vMin[0] = vMin[1] = vMin[2] = 99999; + vMax[0] = vMax[1] = vMax[2] = -99999; + + for ( i = 0; i < pParent->width; i++ ) { + VectorCopy(pParent->ctrl(i, 0).xyz, p->ctrl(Index3By[i][0], Index3By[i][1]).xyz); + for ( j = 0; j < 3; j++ ) { + if ( pParent->ctrl(i, 0).xyz[j] < vMin[j] ) + vMin[j] = pParent->ctrl(i, 0).xyz[j]; + if ( pParent->ctrl(i, 0).xyz[j] > vMax[j] ) + vMax[j] = pParent->ctrl(i, 0).xyz[j]; + } + } + + for ( j = 0; j < 3; j++ ) { + vTemp[j] = vMin[j] + abs((vMax[j] - vMin[j]) * 0.5); + } + + for ( i = 0; i < Interior3ByCount; i++ ) { + VectorCopy(vTemp, p->ctrl(Interior3By[i][0], Interior3By[i][1]).xyz); + } + + Patch_CalcBounds(p, vMin, vMax); + Brush_RebuildBrush(p->pSymbiot, vMin, vMax); + Select_Brush(p->pSymbiot); + return; + + bool bCreated = false; + + if ( bInverted ) { + if ( bBevel ) { + b = Patch_GenericMesh(3, 3, 2, false, false, pParent); + p = b->pPatch; + VectorCopy(p->ctrl(2, 2).xyz, p->ctrl(1, 2).xyz); + VectorCopy(p->ctrl(2, 2).xyz, p->ctrl(2, 1).xyz); + VectorCopy(p->ctrl(2, 2).xyz, p->ctrl(0, 1).xyz); + VectorCopy(p->ctrl(2, 2).xyz, p->ctrl(1, 0).xyz); + VectorCopy(p->ctrl(2, 2).xyz, p->ctrl(1, 1).xyz); + VectorCopy(p->ctrl(2, 0).xyz, p->ctrl(0, 0).xyz); + + b2 = Patch_GenericMesh(3, 3, 2, false, false, pParent); + p2 = b2->pPatch; + VectorCopy(p2->ctrl(2, 2).xyz, p2->ctrl(1, 2).xyz); + VectorCopy(p2->ctrl(2, 2).xyz, p2->ctrl(2, 1).xyz); + VectorCopy(p2->ctrl(2, 2).xyz, p2->ctrl(0, 1).xyz); + VectorCopy(p2->ctrl(2, 2).xyz, p2->ctrl(1, 0).xyz); + VectorCopy(p2->ctrl(2, 2).xyz, p2->ctrl(1, 1).xyz); + VectorCopy(p2->ctrl(2, 0).xyz, p2->ctrl(0, 0).xyz); + + + bCreated = true; + } else if ( bEndcap ) { + b = Patch_GenericMesh(5, 5, 2, false, false, pParent); + p = b->pPatch; + VectorCopy(p->ctrl(4, 4).xyz, p->ctrl(4, 3).xyz); + VectorCopy(p->ctrl(0, 4).xyz, p->ctrl(1, 4).xyz); + VectorCopy(p->ctrl(0, 4).xyz, p->ctrl(2, 4).xyz); + VectorCopy(p->ctrl(0, 4).xyz, p->ctrl(3, 4).xyz); + + VectorCopy(p->ctrl(4, 0).xyz, p->ctrl(4, 1).xyz); + VectorCopy(p->ctrl(0, 0).xyz, p->ctrl(1, 0).xyz); + VectorCopy(p->ctrl(0, 0).xyz, p->ctrl(2, 0).xyz); + VectorCopy(p->ctrl(0, 0).xyz, p->ctrl(3, 0).xyz); + + for ( i = 1; i < 4; i++ ) { + for ( j = 0; j < 4; j++ ) { + VectorCopy(p->ctrl(4, i).xyz, p->ctrl(j, i).xyz); + } + } + + + b2 = Patch_GenericMesh(5, 5, 2, false, false, pParent); + p2 = b2->pPatch; + VectorCopy(p2->ctrl(4, 4).xyz, p2->ctrl(4, 3).xyz); + VectorCopy(p2->ctrl(0, 4).xyz, p2->ctrl(1, 4).xyz); + VectorCopy(p2->ctrl(0, 4).xyz, p2->ctrl(2, 4).xyz); + VectorCopy(p2->ctrl(0, 4).xyz, p2->ctrl(3, 4).xyz); + + VectorCopy(p2->ctrl(4, 0).xyz, p2->ctrl(4, 1).xyz); + VectorCopy(p2->ctrl(0, 0).xyz, p2->ctrl(1, 0).xyz); + VectorCopy(p2->ctrl(0, 0).xyz, p2->ctrl(2, 0).xyz); + VectorCopy(p2->ctrl(0, 0).xyz, p2->ctrl(3, 0).xyz); + + for ( i = 1; i < 4; i++ ) { + for ( j = 0; j < 4; j++ ) { + VectorCopy(p2->ctrl(4, i).xyz, p2->ctrl(j, i).xyz); + } + } + + + bCreated = true; + } + } else { + if ( bBevel ) { + b = Patch_GenericMesh(3, 3, 2, false, false, pParent); + p = b->pPatch; + VectorCopy(p->ctrl(2, 0).xyz, p->ctrl(2, 1).xyz); + VectorCopy(p->ctrl(0, 0).xyz, p->ctrl(1, 0).xyz); + VectorCopy(p->ctrl(0, 0).xyz, p->ctrl(2, 0).xyz); + + b2 = Patch_GenericMesh(3, 3, 2, false, false, pParent); + p2 = b2->pPatch; + VectorCopy(p2->ctrl(2, 0).xyz, p2->ctrl(2, 1).xyz); + VectorCopy(p2->ctrl(0, 0).xyz, p2->ctrl(1, 0).xyz); + VectorCopy(p2->ctrl(0, 0).xyz, p2->ctrl(2, 0).xyz); + bCreated = true; + } else if ( bEndcap ) { + b = Patch_GenericMesh(5, 5, 2, false, false, pParent); + p = b->pPatch; + VectorCopy(p->ctrl(0, 0).xyz, p->ctrl(1, 0).xyz); + VectorCopy(p->ctrl(0, 0).xyz, p->ctrl(2, 0).xyz); + VectorCopy(p->ctrl(0, 0).xyz, p->ctrl(3, 0).xyz); + VectorCopy(p->ctrl(4, 0).xyz, p->ctrl(4, 1).xyz); + VectorCopy(p->ctrl(0, 0).xyz, p->ctrl(4, 0).xyz); + + VectorCopy(p->ctrl(0, 4).xyz, p->ctrl(1, 4).xyz); + VectorCopy(p->ctrl(0, 4).xyz, p->ctrl(2, 4).xyz); + VectorCopy(p->ctrl(0, 4).xyz, p->ctrl(3, 4).xyz); + VectorCopy(p->ctrl(4, 4).xyz, p->ctrl(4, 3).xyz); + VectorCopy(p->ctrl(0, 4).xyz, p->ctrl(4, 4).xyz); + + b2 = Patch_GenericMesh(5, 5, 2, false, false, pParent); + p2 = b2->pPatch; + VectorCopy(p2->ctrl(0, 0).xyz, p2->ctrl(1, 0).xyz); + VectorCopy(p2->ctrl(0, 0).xyz, p2->ctrl(2, 0).xyz); + VectorCopy(p2->ctrl(0, 0).xyz, p2->ctrl(3, 0).xyz); + VectorCopy(p2->ctrl(4, 0).xyz, p2->ctrl(4, 1).xyz); + VectorCopy(p2->ctrl(0, 0).xyz, p2->ctrl(4, 0).xyz); + + VectorCopy(p2->ctrl(0, 4).xyz, p2->ctrl(1, 4).xyz); + VectorCopy(p2->ctrl(0, 4).xyz, p2->ctrl(2, 4).xyz); + VectorCopy(p2->ctrl(0, 4).xyz, p2->ctrl(3, 4).xyz); + VectorCopy(p2->ctrl(4, 4).xyz, p2->ctrl(4, 3).xyz); + VectorCopy(p2->ctrl(0, 4).xyz, p2->ctrl(4, 4).xyz); + bCreated = true; + } else { + b = Patch_GenericMesh(3, 3, 2, false, false, pParent); + p = b->pPatch; + + VectorCopy(p->ctrl(0, 1).xyz, vTemp); + VectorCopy(p->ctrl(0, 2).xyz, p->ctrl(0, 1).xyz); + VectorCopy(p->ctrl(1, 2).xyz, p->ctrl(0, 2).xyz); + VectorCopy(p->ctrl(2, 2).xyz, p->ctrl(1, 2).xyz); + VectorCopy(p->ctrl(2, 1).xyz, p->ctrl(2, 2).xyz); + VectorCopy(p->ctrl(2, 0).xyz, p->ctrl(2, 1).xyz); + VectorCopy(p->ctrl(1, 0).xyz, p->ctrl(2, 0).xyz); + VectorCopy(p->ctrl(0, 0).xyz, p->ctrl(1, 0).xyz); + VectorCopy(vTemp, p->ctrl(0, 0).xyz); + + b2 = Patch_GenericMesh(3, 3, 2, false, false, pParent); + p2 = b2->pPatch; + VectorCopy(p2->ctrl(0, 1).xyz, vTemp); + VectorCopy(p2->ctrl(0, 2).xyz, p2->ctrl(0, 1).xyz); + VectorCopy(p2->ctrl(1, 2).xyz, p2->ctrl(0, 2).xyz); + VectorCopy(p2->ctrl(2, 2).xyz, p2->ctrl(1, 2).xyz); + VectorCopy(p2->ctrl(2, 1).xyz, p2->ctrl(2, 2).xyz); + VectorCopy(p2->ctrl(2, 0).xyz, p2->ctrl(2, 1).xyz); + VectorCopy(p2->ctrl(1, 0).xyz, p2->ctrl(2, 0).xyz); + VectorCopy(p2->ctrl(0, 0).xyz, p2->ctrl(1, 0).xyz); + VectorCopy(vTemp, p2->ctrl(0, 0).xyz); + bCreated = true; + } + } + + if ( bCreated ) { + idDrawVert vertTemp; + for ( i = 0; i < p->width; i++ ) { + for ( j = 0; j < p->height; j++ ) { + p->ctrl(i, j).xyz[2] = vMin[2]; + p2->ctrl(i, j).xyz[2] = vMax[2]; + } + + for ( j = 0; j < p->height / 2; j++ ) { + memcpy(&vertTemp, &p->ctrl(i, p->height - 1 - j), sizeof(idDrawVert)); + memcpy(&p->ctrl(i, p->height - 1 - j), &p->ctrl(i, j), sizeof(idDrawVert)); + memcpy(&p->ctrl(i, j), &vertTemp, sizeof(idDrawVert)); + } + } + //Select_Delete(); + + Patch_CalcBounds(p, vMin, vMax); + Brush_RebuildBrush(p->pSymbiot, vMin, vMax); + Patch_CalcBounds(p2, vMin, vMax); + Brush_RebuildBrush(p2->pSymbiot, vMin, vMax); + Select_Brush(p->pSymbiot); + Select_Brush(p2->pSymbiot); + } else { + Select_Delete(); + } + //Select_Brush(brushParent); + +} + + +/* +=============== +BrushToPatchMesh +=============== +*/ +void Patch_BrushToMesh( bool bCone,bool bBevel,bool bEndcap,bool bSquare,int nHeight ) { + brush_t *b; + patchMesh_t *p; + int i, j; + + int width = 9; + if ( bBevel & !bSquare ) { + width = 3; + } else if ( bEndcap & !bSquare ) { + width = 5; + } + + if ( !QE_SingleBrush() ) { + return; + } + + b = selected_brushes.next; + + p = MakeNewPatch(width, nHeight); + + p->d_texture = b->brush_faces->d_texture; + + p->type = PATCH_CYLINDER; + if ( bBevel & !bSquare ) { + p->type = PATCH_BEVEL; + int nStep = (b->maxs[2] - b->mins[2]) / (p->height - 1); + int nStart = b->mins[2]; + for ( i = 0; i < p->height; i++ ) { + p->ctrl(0, i).xyz[0] = b->mins[0]; + p->ctrl(0, i).xyz[1] = b->mins[1]; + p->ctrl(0, i).xyz[2] = nStart; + + p->ctrl(1, i).xyz[0] = b->maxs[0]; + p->ctrl(1, i).xyz[1] = b->mins[1]; + p->ctrl(1, i).xyz[2] = nStart; + + p->ctrl(2, i).xyz[0] = b->maxs[0]; + p->ctrl(2, i).xyz[1] = b->maxs[1]; + p->ctrl(2, i).xyz[2] = nStart; + nStart += nStep; + } + } else if ( bEndcap & !bSquare ) { + p->type = PATCH_ENDCAP; + int nStep = (b->maxs[2] - b->mins[2]) / (p->height - 1); + int nStart = b->mins[2]; + for ( i = 0; i < p->height; i++ ) { + p->ctrl(0, i).xyz[0] = b->mins[0]; + p->ctrl(0, i).xyz[1] = b->mins[1]; + p->ctrl(0, i).xyz[2] = nStart; + + p->ctrl(1, i).xyz[0] = b->mins[0]; + p->ctrl(1, i).xyz[1] = b->maxs[1]; + p->ctrl(1, i).xyz[2] = nStart; + + p->ctrl(2, i).xyz[0] = b->mins[0] + ((b->maxs[0] - b->mins[0]) * 0.5); + p->ctrl(2, i).xyz[1] = b->maxs[1]; + p->ctrl(2, i).xyz[2] = nStart; + + p->ctrl(3, i).xyz[0] = b->maxs[0]; + p->ctrl(3, i).xyz[1] = b->maxs[1]; + p->ctrl(3, i).xyz[2] = nStart; + + p->ctrl(4, i).xyz[0] = b->maxs[0]; + p->ctrl(4, i).xyz[1] = b->mins[1]; + p->ctrl(4, i).xyz[2] = nStart; + nStart += nStep; + } + } else { + p->ctrl(1, 0).xyz[0] = b->mins[0]; + p->ctrl(1, 0).xyz[1] = b->mins[1]; + + p->ctrl(3, 0).xyz[0] = b->maxs[0]; + p->ctrl(3, 0).xyz[1] = b->mins[1]; + + p->ctrl(5, 0).xyz[0] = b->maxs[0]; + p->ctrl(5, 0).xyz[1] = b->maxs[1]; + + p->ctrl(7, 0).xyz[0] = b->mins[0]; + p->ctrl(7, 0).xyz[1] = b->maxs[1]; + + for ( i = 1 ; i < p->width - 1 ; i += 2 ) { + p->ctrl(i, 0).xyz[2] = b->mins[2]; + + VectorCopy(p->ctrl(i, 0).xyz, p->ctrl(i, 2).xyz); + + p->ctrl(i, 2).xyz[2] = b->maxs[2]; + + p->ctrl(i, 1).xyz[0] = (p->ctrl(i, 0).xyz[0] + p->ctrl(i, 2).xyz[0]) * 0.5; + p->ctrl(i, 1).xyz[1] = (p->ctrl(i, 0).xyz[1] + p->ctrl(i, 2).xyz[1]) * 0.5; + p->ctrl(i, 1).xyz[2] = (p->ctrl(i, 0).xyz[2] + p->ctrl(i, 2).xyz[2]) * 0.5; + } + InterpolateInteriorPoints(p); + + if ( bSquare ) { + if ( bBevel || bEndcap ) { + if ( bBevel ) { + for ( i = 0; i < p->height; i++ ) { + VectorCopy(p->ctrl(1, i).xyz, p->ctrl(2, i).xyz); + VectorCopy(p->ctrl(7, i).xyz, p->ctrl(6, i).xyz); + } + } else { + for ( i = 0; i < p->height; i++ ) { + VectorCopy(p->ctrl(5, i).xyz, p->ctrl(4, i).xyz); + VectorCopy(p->ctrl(1, i).xyz, p->ctrl(2, i).xyz); + VectorCopy(p->ctrl(7, i).xyz, p->ctrl(6, i).xyz); + VectorCopy(p->ctrl(8, i).xyz, p->ctrl(7, i).xyz); + } + } + } else { + for ( i = 0; i < p->width - 1; i ++ ) { + for ( j = 0; j < p->height; j++ ) { + VectorCopy(p->ctrl(i + 1, j).xyz, p->ctrl(i, j).xyz); + } + } + for ( j = 0; j < p->height; j++ ) { + VectorCopy(p->ctrl(0, j).xyz, p->ctrl(8, j).xyz); + } + } + } + } + + + Patch_Naturalize(p); + + if ( bCone ) { + p->type = PATCH_CONE; + float xc = (b->maxs[0] + b->mins[0]) * 0.5; + float yc = (b->maxs[1] + b->mins[1]) * 0.5; + + for ( i = 0 ; i < p->width ; i ++ ) { + p->ctrl(i, 2).xyz[0] = xc; + p->ctrl(i, 2).xyz[1] = yc; + } + } + b = AddBrushForPatch(p); + + Select_Delete(); + Select_Brush(b); +} + +patchMesh_t * Patch_GenerateGeneric( int width,int height,int orientation,const idVec3 &mins,const idVec3 &maxs ) { + patchMesh_t *p = MakeNewPatch(width, height); + p->d_texture = Texture_ForName(g_qeglobals.d_texturewin.texdef.name); + + p->type = PATCH_GENERIC; + + int nFirst = 0; + int nSecond = 1; + if ( orientation == 0 ) { + nFirst = 1; + nSecond = 2; + } else if ( orientation == 1 ) { + nSecond = 2; + } + + + int xStep = mins[nFirst]; + float xAdj = abs((maxs[nFirst] - mins[nFirst]) / (width - 1)); + float yAdj = abs((maxs[nSecond] - mins[nSecond]) / (height - 1)); + + for ( int i = 0; i < width; i++ ) { + int yStep = mins[nSecond]; + for ( int j = 0; j < height; j++ ) { + p->ctrl(i, j).xyz[nFirst] = xStep; + p->ctrl(i, j).xyz[nSecond] = yStep; + p->ctrl(i, j).xyz[orientation] = g_qeglobals.d_new_brush_bottom[orientation]; + yStep += yAdj; + } + xStep += xAdj; + } + + return p; +} + +/* +================== +Patch_GenericMesh +================== +*/ +brush_t * Patch_GenericMesh( int width,int height,int orientation,bool bDeleteSource,bool bOverride,patchMesh_t *parent ) { + if ( height < 3 || height> 15 || width < 3 || width> 15 ) { + Sys_Status("Invalid patch width or height.\n"); + return NULL; + } + + if ( !bOverride && !QE_SingleBrush() ) { + Sys_Status("Cannot generate a patch from multiple selections.\n"); + return NULL; + } + + brush_t *b = selected_brushes.next; + + patchMesh_t *p = Patch_GenerateGeneric(width, height, orientation, b->mins, b->maxs); + + if ( parent ) { + p->explicitSubdivisions = parent->explicitSubdivisions; + p->horzSubdivisions = parent->horzSubdivisions; + p->vertSubdivisions = parent->vertSubdivisions; + } + + Patch_Naturalize(p); + + b = AddBrushForPatch(p); + + if ( bDeleteSource ) { + Select_Delete(); + Select_Brush(b); + } + + return b; +} + +/* +================== +PointInMoveList +================== +*/ +int PointInMoveList( idVec3 *pf ) { + for ( int i = 0; i < g_qeglobals.d_num_move_points; i++ ) { + if ( pf == g_qeglobals.d_move_points[i] ) { + return i; + } + } + return -1; +} + +/* +================== +PointValueInMoveList +================== +*/ +static int PointValueInMoveList( idVec3 v ) { + for ( int i = 0; i < g_qeglobals.d_num_move_points; i++ ) { + if ( v.Compare(*g_qeglobals.d_move_points[i]) ) { + return i; + } + } + return -1; +} + +/* +================== +RemovePointFromMoveList +================== +*/ +void RemovePointFromMoveList( idVec3 v ) { + int n; + while ( (n = PointValueInMoveList(v)) >= 0 ) { + for ( int i = n; i < g_qeglobals.d_num_move_points - 1; i++ ) { + g_qeglobals.d_move_points[i] = g_qeglobals.d_move_points[i + 1]; + } + g_qeglobals.d_num_move_points--; + } +} + +/* +================== +ColumnSelected +================== +*/ +bool ColumnSelected( patchMesh_t *p,int nCol ) { + for ( int i = 0; i < p->height; i++ ) { + if ( PointInMoveList(&p->ctrl(nCol, i).xyz) == -1 ) { + return false; + } + } + return true; +} + + +/* +================== +AddPoint +================== +*/ +static void AddPoint( patchMesh_t *p,idVec3 *v,bool bWeldOrDrill = true ) { + int nDim1 = (g_pParentWnd->ActiveXY()->GetViewType() == YZ) ? 1 : 0; + int nDim2 = (g_pParentWnd->ActiveXY()->GetViewType() == XY) ? 1 : 2; + g_qeglobals.d_move_points[g_qeglobals.d_num_move_points++] = v; + if ( (g_bPatchWeld || g_bPatchDrillDown) && bWeldOrDrill ) { + for ( int i = 0 ; i < p->width ; i++ ) { + for ( int j = 0 ; j < p->height ; j++ ) { + if ( g_bPatchWeld ) { + if ( (*v).Compare(p->ctrl(i, j).xyz) && PointInMoveList(&p->ctrl(i, j).xyz) == -1 ) { + g_qeglobals.d_move_points[g_qeglobals.d_num_move_points++] = &p->ctrl(i, j).xyz; + continue; + } + } + if ( g_bPatchDrillDown && g_nPatchClickedView != W_CAMERA ) { + if ( (idMath::Fabs((*v)[nDim1] - p->ctrl(i, j).xyz[nDim1]) <= VECTOR_EPSILON) && (idMath::Fabs((*v)[nDim2] - p->ctrl(i, j).xyz[nDim2]) <= VECTOR_EPSILON) ) { + if ( PointInMoveList(&p->ctrl(i, j).xyz) == -1 ) { + g_qeglobals.d_move_points[g_qeglobals.d_num_move_points++] = &p->ctrl(i, j).xyz; + continue; + } + } +#if 0 + int l = 0; + for ( int k = 0; k < 2; k++ ) { + if (idMath::Fabs(v[k] - p->ctrl(i,j).xyz[k]) > VECTOR_EPSILON) + continue; + l++; + } + if (l >= 2 && PointInMoveList(&p->ctrl(i,j).xyz) == -1) { + g_qeglobals.d_move_points[g_qeglobals.d_num_move_points++] = p->ctrl(i,j).xyz; + continue; + } +#endif + } + } + } + } +#if 0 + if (g_qeglobals.d_num_move_points == 1) { + // single point selected + // FIXME: the two loops can probably be reduced to one + for ( int i = 0 ; i < p->width ; i++ ) { + for ( int j = 0 ; j < p->height ; j++ ) { + int n = PointInMoveList(v); + if (n >= 0) { + if (((i & 0x01) && (j & 0x01)) == 0) { + // put any sibling fixed points + // into the inverse list + int p1, p2, p3, p4; + p1 = i + 2; + p2 = i - 2; + p3 = j + 2; + p4 = j - 2; + if (p1 < p->width) { + + } + if (p2 >= 0) { + } + if (p3 < p->height) { + } + if (p4 >= 0) { + } + } + } + } + } + } +#endif +} + +/* +================== +SelectRow +================== +*/ +void SelectRow( patchMesh_t *p,int nRow,bool bMulti ) { + if ( !bMulti ) { + g_qeglobals.d_num_move_points = 0; + } + for ( int i = 0; i < p->width; i++ ) { + AddPoint(p, &p->ctrl(i, nRow).xyz, false); + } + //common->Printf("Selected Row %d\n", nRow); +} + +/* +================== +SelectColumn +================== +*/ +void SelectColumn( patchMesh_t *p,int nCol,bool bMulti ) { + if ( !bMulti ) { + g_qeglobals.d_num_move_points = 0; + } + for ( int i = 0; i < p->height; i++ ) { + AddPoint(p, &p->ctrl(nCol, i).xyz, false); + } + //common->Printf("Selected Col %d\n", nCol); +} + + +/* +================== +AddPatchMovePoint +================== +*/ +void AddPatchMovePoint( idVec3 v,bool bMulti,bool bFull ) { + if ( !g_bSameView && !bMulti && !bFull ) { + g_bSameView = true; + return; + } + + for ( brush_t*pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next ) { + if ( pb->pPatch ) { + patchMesh_t *p = pb->pPatch; + for ( int i = 0 ; i < p->width ; i++ ) { + for ( int j = 0 ; j < p->height ; j++ ) { + if ( v.Compare(p->ctrl(i, j).xyz) ) { + if ( PointInMoveList(&p->ctrl(i, j).xyz) == -1 ) { + if ( bFull ) // if we want the full row/col this is on + { + SelectColumn(p, i, bMulti); + } else { + if ( !bMulti ) + g_qeglobals.d_num_move_points = 0; + AddPoint(p, &p->ctrl(i, j).xyz); + //common->Printf("Selected col:row %d:%d\n", i, j); + } + //--if (!bMulti) + return; + } else { + if ( bFull ) { + if ( ColumnSelected(p, i) ) { + SelectRow(p, j, bMulti); + } else { + SelectColumn(p, i, bMulti); + } + return; + } + if ( g_bSameView ) { + RemovePointFromMoveList(v); + return; + } + } + } + } + } + } + } +} +/* +================== +Patch_UpdateSelected +================== +*/ +void Patch_UpdateSelected( idVec3 vMove ) { + int i, j; + for ( i = 0 ; i < g_qeglobals.d_num_move_points ; i++ ) { + VectorAdd(*g_qeglobals.d_move_points[i], vMove, *g_qeglobals.d_move_points[i]); + if ( g_qeglobals.d_num_move_points == 1 ) { + } + } + + //--patchMesh_t* p = &patchMeshes[g_nSelectedPatch]; + for ( brush_t*pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next ) { + if ( pb->pPatch ) { + patchMesh_t *p = pb->pPatch; + + + g_qeglobals.d_numpoints = 0; + for ( i = 0 ; i < p->width ; i++ ) { + for ( j = 0 ; j < p->height ; j++ ) { + VectorCopy(p->ctrl(i, j).xyz, g_qeglobals.d_points[g_qeglobals.d_numpoints]); + if ( g_qeglobals.d_numpoints < MAX_POINTS - 1 ) { + g_qeglobals.d_numpoints++; + } + } + } + + idVec3 vMin, vMax; + Patch_CalcBounds(p, vMin, vMax); + Brush_RebuildBrush(p->pSymbiot, vMin, vMax); + } + } + //Brush_Free(p->pSymbiot); + //Select_Brush(AddBrushForPatch(g_nSelectedPatch)); +} + + +void Patch_AdjustSubdivisions( float hadj,float vadj ) { + brush_t *pb; + for ( pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next ) { + if ( pb->pPatch ) { + patchMesh_t *p = pb->pPatch; + p->horzSubdivisions += hadj; + p->vertSubdivisions += vadj; + Patch_MakeDirty(p); + } + } + Sys_UpdateWindows(W_ALL); +} + +extern float ShadeForNormal( idVec3 normal ); + +/* +================= +DrawPatchMesh +================= +*/ +//FIXME: this routine needs to be reorganized.. should be about 1/4 the size and complexity +void DrawPatchMesh( patchMesh_t *pm,bool bPoints,int *list,bool bShade = false ) { + int i, j; + + bool bOverlay = pm->bOverlay; + int nDrawMode = g_pParentWnd->GetCamera()->Camera().draw_mode; + + // patches use two display lists, one for camera one for xy + if ( *list <= 0 ) { + if ( *list <= 0 ) { + *list = qglGenLists(1); + } + + if ( *list > 0 ) { + qglNewList(*list, GL_COMPILE_AND_EXECUTE); + } + + //FIXME: finish consolidating all the patch crap + idSurface_Patch *cp = new idSurface_Patch(pm->width * 6, pm->height * 6); + cp->SetSize(pm->width, pm->height); + for ( i = 0; i < pm->width; i++ ) { + for ( j = 0; j < pm->height; j++ ) { + (*cp)[j * cp->GetWidth() + i].xyz = pm->ctrl(i, j).xyz; + (*cp)[j * cp->GetWidth() + i].st = pm->ctrl(i, j).st; + } + } + + if ( pm->explicitSubdivisions ) { + cp->SubdivideExplicit(pm->horzSubdivisions, pm->vertSubdivisions, true); + } else { + cp->Subdivide(DEFAULT_CURVE_MAX_ERROR, DEFAULT_CURVE_MAX_ERROR, DEFAULT_CURVE_MAX_LENGTH, true); + } + + + int width = cp->GetWidth(); + int height = cp->GetHeight(); + /* + for (i = 0; i < width; i++) { + for (j = 0; j < height; j++) { + qglBegin(GL_POINTS); + int index = j * width + i; + qglVertex3fv((*cp)[index].xyz); + qglEnd(); + char msg[64]; + sprintf(msg, "(%0.3f, %0.3f, %0.3f)(%0.3f, %0.3f)", (*cp)[index].xyz.x, (*cp)[index].xyz.y, (*cp)[index].xyz.z, (*cp)[index].st.x, (*cp)[index].st.y); + qglRasterPos3f((*cp)[index].xyz.x + 1, (*cp)[index].xyz.y + 1, (*cp)[index].xyz.z + 1); + qglCallLists (strlen(msg), GL_UNSIGNED_BYTE, msg); + } + } + */ +#ifdef TEST_SURFACE_CLIPPING + int n; + idSurface *surf = cp; + idSurface *front, *back; + + surf->Split(idPlane(1, 0, 0, 0), 0.1f, &front, &back); + if ( front && back ) { + front->TranslateSelf(idVec3(10, 10, 10)); + (*front) += (*back); + surf = front; + } else { + surf = cp; + } + // surf->ClipInPlace( idPlane( 1, 0, 0, 0 ), 0.1f, true ); + + qglBegin(GL_TRIANGLES); + for ( i = 0; i < surf->GetNumIndexes(); i += 3 ) { + n = surf->GetIndexes()[i + 0]; + qglTexCoord2fv((*surf)[n].st.ToFloatPtr()); + qglVertex3fv((*surf)[n].xyz.ToFloatPtr()); + n = surf->GetIndexes()[i + 1]; + qglTexCoord2fv((*surf)[n].st.ToFloatPtr()); + qglVertex3fv((*surf)[n].xyz.ToFloatPtr()); + n = surf->GetIndexes()[i + 2]; + qglTexCoord2fv((*surf)[n].st.ToFloatPtr()); + qglVertex3fv((*surf)[n].xyz.ToFloatPtr()); + } + qglEnd(); + + if ( front ) { + delete front; + } + if ( back ) { + delete back; + } +#else + for ( i = 0 ; i < width - 1; i++ ) { + qglBegin(GL_QUAD_STRIP); + for ( j = 0 ; j < height; j++ ) { + // v1-v2-v3-v4 makes a quad + int v1, v2; + float f; + v1 = j * width + i; + v2 = v1 + 1; + if ( bShade ) { + f = ShadeForNormal((*cp)[v2].normal); + qglColor3f(f, f, f); + } + qglTexCoord2fv((*cp)[v2].st.ToFloatPtr()); + qglVertex3fv((*cp)[v2].xyz.ToFloatPtr()); + if ( bShade ) { + f = ShadeForNormal((*cp)[v1].normal); + qglColor3f(f, f, f); + } + qglTexCoord2fv((*cp)[v1].st.ToFloatPtr()); + qglVertex3fv((*cp)[v1].xyz.ToFloatPtr()); + } + qglEnd(); + } +#endif + + if ( list == &pm->nListSelected ) { + globalImages->BindNull(); + qglPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + qglColor3f(1.0f, 1.0f, 1.0f); + for ( i = 0 ; i < width - 1; i++ ) { + qglBegin(GL_QUAD_STRIP); + for ( j = 0 ; j < height; j++ ) { + int v1, v2; + v1 = j * width + i; + v2 = v1 + 1; + qglVertex3fv((*cp)[v2].xyz.ToFloatPtr()); + qglVertex3fv((*cp)[v1].xyz.ToFloatPtr()); + } + qglEnd(); + } + } + + delete cp; + + if ( *list > 0 ) { + qglEndList(); + } + } else { + qglCallList(*list); + } + + idVec3 *pSelectedPoints[256]; + int nIndex = 0; + + // FIXME: this bend painting code needs to be rolled up significantly as it is a mess right now + if ( bPoints && (g_qeglobals.d_select_mode == sel_curvepoint || g_qeglobals.d_select_mode == sel_area || g_bPatchBendMode || g_bPatchInsertMode) ) { + bOverlay = false; + + // bending or inserting + if ( g_bPatchBendMode || g_bPatchInsertMode ) { + qglPointSize(6); + if ( g_bPatchAxisOnRow ) { + qglColor3f(1, 0, 1); + qglBegin(GL_POINTS); + for ( i = 0; i < pm->width; i++ ) { + qglVertex3fv(reinterpret_cast< float(*)>(&pm->ctrl(i, g_nPatchAxisIndex).xyz)); + } + qglEnd(); + + // could do all of this in one loop but it was pretty messy + if ( g_bPatchInsertMode ) { + qglColor3f(0, 0, 1); + qglBegin(GL_POINTS); + for ( i = 0; i < pm->width; i++ ) { + qglVertex3fv(reinterpret_cast< float(*)>(&pm->ctrl(i, g_nPatchAxisIndex).xyz)); + qglVertex3fv(reinterpret_cast< float(*)>(&pm->ctrl(i, g_nPatchAxisIndex + 1).xyz)); + } + qglEnd(); + } else { + if ( g_nPatchBendState == BEND_SELECT_EDGE || g_nPatchBendState == BEND_BENDIT || g_nPatchBendState == BEND_SELECT_ORIGIN ) { + qglColor3f(0, 0, 1); + qglBegin(GL_POINTS); + if ( g_nPatchBendState == BEND_SELECT_ORIGIN ) { + qglVertex3fv(g_vBendOrigin.ToFloatPtr()); + } else { + for ( i = 0; i < pm->width; i++ ) { + if ( g_bPatchLowerEdge ) { + for ( j = 0; j < g_nPatchAxisIndex; j++ ) { + qglVertex3fv(reinterpret_cast< float(*)>(&pm->ctrl(i, j).xyz)); + } + } else { + for ( j = pm->height - 1; j > g_nPatchAxisIndex; j-- ) { + qglVertex3fv(reinterpret_cast< float(*)>(&pm->ctrl(i, j).xyz)); + } + } + } + } + qglEnd(); + } + } + } else { + qglColor3f(1, 0, 1); + qglBegin(GL_POINTS); + for ( i = 0; i < pm->height; i++ ) { + qglVertex3fv(reinterpret_cast< float(*)>(&pm->ctrl(g_nPatchAxisIndex, i).xyz)); + } + qglEnd(); + + // could do all of this in one loop but it was pretty messy + if ( g_bPatchInsertMode ) { + qglColor3f(0, 0, 1); + qglBegin(GL_POINTS); + for ( i = 0; i < pm->height; i++ ) { + qglVertex3fv(reinterpret_cast< float(*)>(&pm->ctrl(g_nPatchAxisIndex, i).xyz)); + qglVertex3fv(reinterpret_cast< float(*)>(&pm->ctrl(g_nPatchAxisIndex + 1, i).xyz)); + } + qglEnd(); + } else { + if ( g_nPatchBendState == BEND_SELECT_EDGE || g_nPatchBendState == BEND_BENDIT || g_nPatchBendState == BEND_SELECT_ORIGIN ) { + qglColor3f(0, 0, 1); + qglBegin(GL_POINTS); + for ( i = 0; i < pm->height; i++ ) { + if ( g_nPatchBendState == BEND_SELECT_ORIGIN ) { + qglVertex3fv(reinterpret_cast< float(*)>(&pm->ctrl(g_nBendOriginIndex, i).xyz)); + } else { + if ( g_bPatchLowerEdge ) { + for ( j = 0; j < g_nPatchAxisIndex; j++ ) { + qglVertex3fv(reinterpret_cast< float(*)>(&pm->ctrl(j, i).xyz)); + } + } else { + for ( j = pm->width - 1; j > g_nPatchAxisIndex; j-- ) { + qglVertex3fv(reinterpret_cast< float(*)>(&pm->ctrl(j, i).xyz)); + } + } + } + } + qglEnd(); + } + } + } + } else { + qglPointSize(6); + for ( i = 0 ; i < pm->width ; i++ ) { + for ( j = 0 ; j < pm->height ; j++ ) { + qglBegin(GL_POINTS); + // FIXME: need to not do loop lookups inside here + int n = PointValueInMoveList(pm->ctrl(i, j).xyz); + if ( n >= 0 ) { + pSelectedPoints[nIndex++] = &pm->ctrl(i, j).xyz; + } + + if ( i & 0x01 || j & 0x01 ) { + qglColor3f(1, 0, 1); + } else { + qglColor3f(0, 1, 0); + } + qglVertex3fv(pm->ctrl(i, j).xyz.ToFloatPtr()); + qglEnd(); + } + } + } + + if ( nIndex > 0 ) { + qglBegin(GL_POINTS); + qglColor3f(0, 0, 1); + while ( nIndex-- > 0 ) { + qglVertex3fv((*pSelectedPoints[nIndex]).ToFloatPtr()); + } + qglEnd(); + } + } + if ( bOverlay ) { + qglPointSize(6); + qglColor3f(0.5, 0.5, 0.5); + for ( i = 0 ; i < pm->width ; i++ ) { + qglBegin(GL_POINTS); + for ( j = 0 ; j < pm->height ; j++ ) { + if ( i & 0x01 || j & 0x01 ) { + qglColor3f(0.5, 0, 0.5); + } else { + qglColor3f(0, 0.5, 0); + } + qglVertex3fv(pm->ctrl(i, j).xyz.ToFloatPtr()); + } + qglEnd(); + } + } +} + +/* +================== +Patch_DrawXY +================== +*/ +void Patch_DrawXY( patchMesh_t *pm ) { + qglPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + if ( pm->bSelected ) { + qglColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES].ToFloatPtr()); + //qglDisable (GL_LINE_STIPPLE); + //qglLineWidth (1); + } else { + qglColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_BRUSHES].ToFloatPtr()); + } + + DrawPatchMesh(pm, pm->bSelected, &pm->nListID); + qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + if ( pm->bSelected ) { + //qglLineWidth (2); + //qglEnable (GL_LINE_STIPPLE); + } +} + +/* +================== +Patch_DrawCam +================== +*/ +void Patch_DrawCam( patchMesh_t *pm,bool selected ) { + + int nDrawMode = g_pParentWnd->GetCamera()->Camera().draw_mode; + + if ( !selected ) { + qglColor3f(1, 1, 1); + } + + if ( g_bPatchWireFrame || nDrawMode == cd_wire ) { + qglDisable(GL_CULL_FACE); + qglPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + globalImages->BindNull(); + DrawPatchMesh(pm, pm->bSelected, &pm->nListIDCam, true); + qglEnable(GL_CULL_FACE); + } else { + qglEnable(GL_CULL_FACE); + qglCullFace(GL_FRONT); + qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + + if ( nDrawMode == cd_texture || nDrawMode == cd_light ) { + pm->d_texture->GetEditorImage()->Bind(); + } + + if ( !selected && pm->d_texture->GetEditorAlpha() != 1.0f ) { + qglEnable(GL_BLEND); + qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + DrawPatchMesh(pm, pm->bSelected, &pm->nListIDCam, true); + + if ( !selected && pm->d_texture->GetEditorAlpha() != 1.0f ) { + qglDisable(GL_BLEND); + } + + globalImages->BindNull(); + + if ( !selected ) { + qglCullFace(GL_BACK); + qglPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + qglDisable(GL_BLEND); + } else { + qglEnable(GL_BLEND); + qglColor4f(g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][0], g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][1], g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][2], 0.25); + qglDisable(GL_CULL_FACE); + } + DrawPatchMesh(pm, pm->bSelected, (selected) ? &pm->nListSelected : &pm->nListIDCam, !selected); + qglEnable(GL_CULL_FACE); + } + +#if 0 // this paints normal indicators on the ctrl points + //--qglDisable (GL_DEPTH_TEST); + qglColor3f (1,1,1); + for (int i = 0; i < pm->width; i++) { + for (int j = 0; j < pm->height; j++) { + idVec3 temp; + qglBegin (GL_LINES); + qglVertex3fv (pm->ctrl(i,j).xyz); + VectorMA (pm->ctrl(i,j).xyz, 8, pm->ctrl(i,j].normal, temp); + qglVertex3fv (temp); + qglEnd (); + } + } + //--qglEnable (GL_DEPTH_TEST); +#endif + +} + + + + +void ConvexHullForSection( float section[2][4][7] ) { +} + +void BrushesForSection( float section[2][4][7] ) { +} + + +/* +================== +Patch_Move +================== +*/ +void Patch_Move( patchMesh_t *pm,const idVec3 vMove,bool bRebuild ) { + Patch_MakeDirty(pm); + for ( int w = 0; w < pm->width; w++ ) { + for ( int h = 0; h < pm->height; h++ ) { + VectorAdd(pm->ctrl(w, h).xyz, vMove, pm->ctrl(w, h).xyz); + } + } + if ( bRebuild ) { + idVec3 vMin, vMax; + Patch_CalcBounds(pm, vMin, vMax); + //Brush_RebuildBrush(patchMeshes[n].pSymbiot, vMin, vMax); + } + UpdatePatchInspector(); +} + +/* +================== +Patch_ApplyMatrix +================== +*/ +void Patch_ApplyMatrix( patchMesh_t *p,const idVec3 vOrigin,const idMat3 matrix,bool bSnap ) { + idVec3 vTemp; + + for ( int w = 0; w < p->width; w++ ) { + for ( int h = 0; h < p->height; h++ ) { + if ( (g_qeglobals.d_select_mode == sel_curvepoint || g_bPatchBendMode) && PointInMoveList(&p->ctrl(w, h).xyz) == -1 ) { + continue; + } + vTemp = p->ctrl(w, h).xyz - vOrigin; + vTemp *= matrix; + p->ctrl(w, h).xyz = vTemp + vOrigin; + } + } + idVec3 vMin, vMax; + Patch_CalcBounds(p, vMin, vMax); + Brush_RebuildBrush(p->pSymbiot, vMin, vMax); +} + +/* +================== +Patch_EditPatch +================== +*/ +void Patch_EditPatch() { + //--patchMesh_t* p = &patchMeshes[n]; + g_qeglobals.d_numpoints = 0; + g_qeglobals.d_num_move_points = 0; + + for ( brush_t*pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next ) { + if ( pb->pPatch ) { + patchMesh_t *p = pb->pPatch; + for ( int i = 0 ; i < p->width ; i++ ) { + for ( int j = 0 ; j < p->height ; j++ ) { + VectorCopy(p->ctrl(i, j).xyz, g_qeglobals.d_points[g_qeglobals.d_numpoints]); + if ( g_qeglobals.d_numpoints < MAX_POINTS - 1 ) { + g_qeglobals.d_numpoints++; + } + } + } + } + } + g_qeglobals.d_select_mode = sel_curvepoint; + //--g_nSelectedPatch = n; +} + + + +/* +================== +Patch_Deselect +================== +*/ +//FIXME: need all sorts of asserts throughout a lot of this crap +void Patch_Deselect() { + //--g_nSelectedPatch = -1; + g_qeglobals.d_select_mode = sel_brush; + + for ( brush_t*b = selected_brushes.next ; b != &selected_brushes ; b = b->next ) { + if ( b->pPatch ) { + b->pPatch->bSelected = false; + } + } + + if ( g_bPatchBendMode ) { + Patch_BendToggle(); + } + + if ( g_bPatchInsertMode ) { + Patch_InsDelToggle(); + } +} + + +/* +================== +Patch_Select +================== +*/ +void Patch_Select( patchMesh_t *p ) { + // maintained for point manip.. which i need to fix as this + // is pf error prone + //--g_nSelectedPatch = n; + p->bSelected = true; +} + + +/* +================== +Patch_Deselect +================== +*/ +void Patch_Deselect( patchMesh_t *p ) { + p->bSelected = false; +} + + +/* +================== +Patch_Delete +================== +*/ +void Patch_Delete( patchMesh_t *p ) { + if ( p->pSymbiot ) { + p->pSymbiot->pPatch = NULL; + } + + Mem_Free(p->verts); + if ( p->epairs ) { + delete p->epairs; + } + Mem_Free(p); + + p = NULL; + + UpdatePatchInspector(); +} + + +/* +================== +Patch_Scale +================== +*/ +void Patch_Scale( patchMesh_t *p,const idVec3 vOrigin,const idVec3 vAmt,bool bRebuild ) { + for ( int w = 0; w < p->width; w++ ) { + for ( int h = 0; h < p->height; h++ ) { + if ( g_qeglobals.d_select_mode == sel_curvepoint && PointInMoveList(&p->ctrl(w, h).xyz) == -1 ) + continue; + for ( int i = 0 ; i < 3 ; i++ ) { + p->ctrl(w, h).xyz[i] -= vOrigin[i]; + p->ctrl(w, h).xyz[i] *= vAmt[i]; + p->ctrl(w, h).xyz[i] += vOrigin[i]; + } + } + } + if ( bRebuild ) { + idVec3 vMin, vMax; + Patch_CalcBounds(p, vMin, vMax); + Brush_RebuildBrush(p->pSymbiot, vMin, vMax); + } + UpdatePatchInspector(); +} + + +/* +================== +Patch_Cleanup +================== +*/ +void Patch_Cleanup() { + //--g_nSelectedPatch = -1; + //numPatchMeshes = 0; +} + + + +/* +================== +Patch_SetView +================== +*/ +void Patch_SetView( int n ) { + g_bSameView = (n == g_nPatchClickedView); + g_nPatchClickedView = n; +} + + +/* +================== +Patch_SetTexture +================== +*/ +// FIXME: need array validation throughout +void Patch_SetTexture( patchMesh_t *p,texdef_t *tex_def ) { + p->d_texture = Texture_ForName(tex_def->name); + UpdatePatchInspector(); +} + +/* +================== +Patch_SetTexture +================== +*/ +// FIXME: need array validation throughout +void Patch_SetTextureName( patchMesh_t *p,const char *name ) { + p->d_texture = Texture_ForName(name); + UpdatePatchInspector(); +} + + +/* +================== +Patch_DragScale +================== +*/ +bool Patch_DragScale( patchMesh_t *p,idVec3 vAmt,idVec3 vMove ) { + idVec3 vMin, vMax, vScale, vTemp, vMid; + int i; + + Patch_CalcBounds(p, vMin, vMax); + + VectorSubtract(vMax, vMin, vTemp); + + // if we are scaling in the same dimension the patch has no depth + for ( i = 0; i < 3; i ++ ) { + if ( vTemp[i] == 0 && vMove[i] != 0 ) { + //Patch_Move(n, vMove, true); + return false; + } + } + + for ( i = 0 ; i < 3 ; i++ ) + vMid[i] = (vMin[i] + ((vMax[i] - vMin[i]) / 2)); + + for ( i = 0; i < 3; i++ ) { + if ( vAmt[i] != 0 ) { + vScale[i] = 1.0f + vAmt[i] / vTemp[i]; + } else { + vScale[i] = 1.0f; + } + } + + Patch_Scale(p, vMid, vScale, false); + + VectorSubtract(vMax, vMin, vTemp); + + Patch_CalcBounds(p, vMin, vMax); + + VectorSubtract(vMax, vMin, vMid); + + VectorSubtract(vMid, vTemp, vTemp); + + VectorScale(vTemp, 0.5, vTemp); + + // abs of both should always be equal + if ( !vMove.Compare(vAmt) ) { + for ( i = 0; i < 3; i++ ) { + if ( vMove[i] != vAmt[i] ) { + vTemp[i] = -(vTemp[i]); + } + } + } + + Patch_Move(p, vTemp); + return true; +} + + +/* +================== +Patch_InsertColumn +================== +*/ +void Patch_InsertColumn( patchMesh_t *p,bool bAdd ) { + int h, w, i, j; + idVec3 vTemp; + + if ( p->width + 2 >= MAX_PATCH_WIDTH ) { + return; + } + + Patch_AdjustSize(p, 2, 0); + + // re-adjust til after routine + //p->width -= 2; + + if ( bAdd ) { + // add column? + for ( h = 0; h < p->height; h++ ) { + j = p->width - 3; + + VectorSubtract(p->ctrl(j, h).xyz, p->ctrl(j - 1, h).xyz, vTemp); + + for ( i = 0; i < 3; i++ ) { + vTemp[i] /= 3; + } + + memcpy(&p->ctrl(j + 2, h), &p->ctrl(j, h), sizeof(idDrawVert)); + memcpy(&p->ctrl(j, h), &p->ctrl(j - 1, h), sizeof(idDrawVert)); + + VectorAdd(p->ctrl(j, h).xyz, vTemp, p->ctrl(j, h).xyz); + memcpy(&p->ctrl(j + 1, h), &p->ctrl(j, h), sizeof(idDrawVert)); + VectorAdd(p->ctrl(j + 1, h).xyz, vTemp, p->ctrl(j + 1, h).xyz); + } + } else { + for ( h = 0; h < p->height; h++ ) { + w = p->width - 3; + while ( w >= 0 ) { + memcpy(&p->ctrl(w + 2, h), &p->ctrl(w, h), sizeof(idDrawVert)); + w--; + } + VectorSubtract(p->ctrl(1, h).xyz, p->ctrl(0, h).xyz, vTemp); + for ( i = 0; i < 3; i++ ) { + vTemp[i] /= 3; + } + VectorCopy(p->ctrl(0, h).xyz, p->ctrl(1, h).xyz); + VectorAdd(p->ctrl(1, h).xyz, vTemp, p->ctrl(1, h).xyz); + VectorCopy(p->ctrl(1, h).xyz, p->ctrl(2, h).xyz); + VectorAdd(p->ctrl(2, h).xyz, vTemp, p->ctrl(2, h).xyz); + } + } + //p->width += 2; + UpdatePatchInspector(); +} + + +/* +================== +Patch_InsertRow +================== +*/ +void Patch_InsertRow( patchMesh_t *p,bool bAdd ) { + int h, w, i, j; + idVec3 vTemp; + + if ( p->height + 2 >= MAX_PATCH_HEIGHT ) { + return; + } + + Patch_AdjustSize(p, 0, 2); + + if ( bAdd ) { + // add column? + for ( w = 0; w < p->width; w++ ) { + j = p->height - 3; + VectorSubtract(p->ctrl(w, j).xyz, p->ctrl(w, j - 1).xyz, vTemp); + for ( i = 0; i < 3; i++ ) { + vTemp[i] /= 3; + } + + memcpy(&p->ctrl(w, j + 2), &p->ctrl(w, j), sizeof(idDrawVert)); + memcpy(&p->ctrl(w, j), &p->ctrl(w, j - 1), sizeof(idDrawVert)); + + VectorAdd(p->ctrl(w, j).xyz, vTemp, p->ctrl(w, j).xyz); + memcpy(&p->ctrl(w, j + 1), &p->ctrl(w, j), sizeof(idDrawVert)); + VectorAdd(p->ctrl(w, j + 1).xyz, vTemp, p->ctrl(w, j + 1).xyz); + } + } else { + for ( w = 0; w < p->width; w++ ) { + h = p->height - 3; + while ( h >= 0 ) { + memcpy(&p->ctrl(w, h + 2), &p->ctrl(w, h), sizeof(idDrawVert)); + h--; + } + VectorSubtract(p->ctrl(w, 1).xyz, p->ctrl(w, 0).xyz, vTemp); + for ( i = 0; i < 3; i++ ) { + vTemp[i] /= 3; + } + + VectorCopy(p->ctrl(w, 0).xyz, p->ctrl(w, 1).xyz); + VectorAdd(p->ctrl(w, 1).xyz, vTemp, p->ctrl(w, 1).xyz); + VectorCopy(p->ctrl(w, 1).xyz, p->ctrl(w, 2).xyz); + VectorAdd(p->ctrl(w, 2).xyz, vTemp, p->ctrl(w, 2).xyz); + } + } + + UpdatePatchInspector(); +} + + +/* +================== +Patch_RemoveRow +================== +*/ +void Patch_RemoveRow( patchMesh_t *p,bool bFirst ) { + if ( p->height <= MIN_PATCH_HEIGHT ) { + return; + } + + if ( bFirst ) { + for ( int w = 0; w < p->width; w++ ) { + for ( int h = 0; h < p->height - 2; h++ ) { + memcpy(&p->ctrl(w, h), &p->ctrl(w, h + 2), sizeof(idDrawVert)); + } + } + } + + Patch_AdjustSize(p, 0, -2); + + UpdatePatchInspector(); +} + + +/* +================== +Patch_RemoveColumn +================== +*/ +void Patch_RemoveColumn( patchMesh_t *p,bool bFirst ) { + if ( p->width <= MIN_PATCH_WIDTH ) { + return; + } + + if ( bFirst ) { + for ( int h = 0; h < p->height; h++ ) { + for ( int w = 0; w < p->width - 2; w++ ) { + memcpy(&p->ctrl(w, h), &p->ctrl(w + 2, h), sizeof(idDrawVert)); + } + } + } + + Patch_AdjustSize(p, -2, 0); + + UpdatePatchInspector(); +} + + +void Patch_DisperseRows() { + idVec3 vTemp, vTemp2; + int i, w, h; + + + for ( brush_t*pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next ) { + if ( pb->pPatch ) { + patchMesh_t *p = pb->pPatch; + Patch_Rebuild(p); + for ( w = 0; w < p->width; w++ ) { + // for each row, we need to evenly disperse p->height number + // of points across the old bounds + + // calc total distance to interpolate + VectorSubtract(p->ctrl(w, p->height - 1).xyz, p->ctrl(w, 0).xyz, vTemp); + + //vTemp[0] = vTemp[1] = vTemp[2] = 0; + //for (h = 0; h < p->height - nRows; h ++) + //{ + // VectorAdd(vTemp, p->ctrl(w,h], vTemp); + //} + + // amount per cycle + for ( i = 0; i < 3; i ++ ) { + vTemp2[i] = vTemp[i] / (p->height - 1); + } + + // move along + for ( h = 0; h < p->height - 1; h++ ) { + VectorAdd(p->ctrl(w, h).xyz, vTemp2, p->ctrl(w, h + 1).xyz); + } + Patch_Naturalize(p); + } + } + } + UpdatePatchInspector(); +} + +/* +================== +Patch_AdjustColumns +================== +*/ +void Patch_DisperseColumns() { + idVec3 vTemp, vTemp2; + int i, w, h; + + for ( brush_t*pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next ) { + if ( pb->pPatch ) { + patchMesh_t *p = pb->pPatch; + Patch_Rebuild(p); + + for ( h = 0; h < p->height; h++ ) { + // for each column, we need to evenly disperse p->width number + // of points across the old bounds + + // calc total distance to interpolate + VectorSubtract(p->ctrl(p->width - 1, h).xyz, p->ctrl(0, h).xyz, vTemp); + + // amount per cycle + for ( i = 0; i < 3; i ++ ) { + vTemp2[i] = vTemp[i] / (p->width - 1); + } + + // move along + for ( w = 0; w < p->width - 1; w++ ) { + VectorAdd(p->ctrl(w, h).xyz, vTemp2, p->ctrl(w + 1, h).xyz); + } + } + Patch_Naturalize(p); + } + } + UpdatePatchInspector(); +} + + + +/* +================== +Patch_AdjustSelected +================== +*/ +void Patch_AdjustSelected( bool bInsert,bool bColumn,bool bFlag ) { + bool bUpdate = false; + for ( brush_t*pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next ) { + if ( pb->pPatch ) { + if ( bInsert ) { + if ( bColumn ) { + Patch_InsertColumn(pb->pPatch, bFlag); + } else { + Patch_InsertRow(pb->pPatch, bFlag); + } + } else { + if ( bColumn ) { + Patch_RemoveColumn(pb->pPatch, bFlag); + } else { + Patch_RemoveRow(pb->pPatch, bFlag); + } + } + bUpdate = true; + idVec3 vMin, vMax; + patchMesh_t *p = pb->pPatch; + Patch_CalcBounds(p, vMin, vMax); + Brush_RebuildBrush(p->pSymbiot, vMin, vMax); + } + } + if ( bUpdate ) { + Sys_UpdateWindows(W_ALL); + } +} + +void Parse1DMatrix( int x,float *p ) { + GetToken(true); // ( + for ( int i = 0; i < x; i++ ) { + GetToken(false); + p[i] = atof(token); + } + GetToken(true); // ) +} + +void Parse2DMatrix( int y,int x,float *p ) { + GetToken(true); // ( + for ( int i = 0; i < y; i++ ) { + Parse1DMatrix(x, p + i * x); + } + GetToken(true); // ) +} + +void Parse3DMatrix( int z,int y,int x,float *p ) { + GetToken(true); // ( + for ( int i = 0; i < z; i++ ) { + Parse2DMatrix(y, x, p + i * (x * MAX_PATCH_HEIGHT)); + } + GetToken(true); // ) +} + +// parses a patch +brush_t * Patch_Parse( bool bOld ) { + const idMaterial *tex = declManager->FindMaterial(NULL); + GetToken(true); + + if ( strcmp(token, "{") ) { + return NULL; + } + + patchMesh_t *pm = NULL; + + if ( g_qeglobals.bSurfacePropertiesPlugin ) { + assert(true); + //GETPLUGINTEXDEF(pm)->ParsePatchTexdef(); + } else { + // texture def + GetToken(true); + + // band-aid + if ( strcmp(token, "(") ) { + if ( g_qeglobals.mapVersion < 2.0f ) { + tex = Texture_ForName(va("textures/%s", token)); + } else { + tex = Texture_ForName(token); + } + GetToken(true); + } else { + common->Printf("Warning: Patch read with no texture, using notexture... \n"); + } + + if ( strcmp(token, "(") ) { + return NULL; + } + + // width, height, flags (currently only negative) + GetToken(false); + int width = atoi(token); + + GetToken(false); + int height = atoi(token); + + pm = MakeNewPatch(width, height); + pm->d_texture = tex; + + if ( !bOld ) { + GetToken(false); + pm->horzSubdivisions = atoi(token); + GetToken(false); + pm->vertSubdivisions = atoi(token); + pm->explicitSubdivisions = true; + } + + GetToken(false); + pm->contents = atoi(token); + + GetToken(false); + pm->flags = atoi(token); + + GetToken(false); + pm->value = atoi(token); + + //if (!bOld) + //{ + // GetToken(false); + // pm->type = atoi(token); + //} + + GetToken(false); + if ( strcmp(token, ")") ) + return NULL; + } + + + + float ctrl[MAX_PATCH_WIDTH][MAX_PATCH_HEIGHT][5]; + Parse3DMatrix(pm->width, pm->height, 5, reinterpret_cast< float*>(&ctrl)); + + int w, h; + + for ( w = 0; w < pm->width; w++ ) { + for ( h = 0; h < pm->height; h++ ) { + pm->ctrl(w, h).xyz[0] = ctrl[w][h][0]; + pm->ctrl(w, h).xyz[1] = ctrl[w][h][1]; + pm->ctrl(w, h).xyz[2] = ctrl[w][h][2]; + pm->ctrl(w, h).st[0] = ctrl[w][h][3]; + pm->ctrl(w, h).st[1] = ctrl[w][h][4]; + } + } + + GetToken(true); + + if ( g_qeglobals.m_bBrushPrimitMode ) { + // we are in brush primit mode, but maybe it's a classic patch that needs converting, test "}" + if ( strcmp(token, "}") && strcmp(token, "(") ) { + ParseEpair(pm->epairs); + GetToken(true); + } + } + + if ( strcmp(token, "}") ) { + return NULL; + } + + brush_t *b = AddBrushForPatch(pm, false); + + return b; +} + + +/* +================== +Patch_Write +================== +*/ +void Patch_Write( patchMesh_t *p,CMemFile *file ) { + if ( g_qeglobals.bSurfacePropertiesPlugin ) { + common->Printf("WARNING: Patch_Write to a CMemFile and Surface Properties plugin not done\n"); + } + + if ( p->explicitSubdivisions ) { + MemFile_fprintf(file, " {\n patchDef3\n {\n"); + MemFile_fprintf(file, " \"%s\"\n", p->d_texture->GetName()); + MemFile_fprintf(file, " ( %i %i %i %i %i %i %i ) \n", p->width, p->height, p->horzSubdivisions, p->vertSubdivisions, p->contents, p->flags, p->value); + } else { + MemFile_fprintf(file, " {\n patchDef2\n {\n"); + MemFile_fprintf(file, " \"%s\"\n", p->d_texture->GetName()); + MemFile_fprintf(file, " ( %i %i %i %i %i ) \n", p->width, p->height, p->contents, p->flags, p->value); + } + + + float ctrl[MAX_PATCH_WIDTH][MAX_PATCH_HEIGHT][5]; + + int w, h; + for ( w = 0; w < p->width; w++ ) { + for ( h = 0; h < p->height; h++ ) { + ctrl[w][h][0] = p->ctrl(w, h).xyz[0]; + ctrl[w][h][1] = p->ctrl(w, h).xyz[1]; + ctrl[w][h][2] = p->ctrl(w, h).xyz[2]; + ctrl[w][h][3] = p->ctrl(w, h).st[0]; + ctrl[w][h][4] = p->ctrl(w, h).st[1]; + } + } + + _Write3DMatrix(file, p->width, p->height, 5, reinterpret_cast< float*>(&ctrl)); + + if ( g_qeglobals.m_bBrushPrimitMode ) { + if ( p->epairs ) { + int count = p->epairs->GetNumKeyVals(); + for ( int i = 0; i < count; i++ ) { + MemFile_fprintf(file, "\"%s\" \"%s\"\n", p->epairs->GetKeyVal(i)->GetKey().c_str(), p->epairs->GetKeyVal(i)->GetValue().c_str()); + } + } + } + + MemFile_fprintf(file, " }\n }\n"); +} + +void Patch_Write( patchMesh_t *p,FILE *file ) { + if ( p->explicitSubdivisions ) { + fprintf(file, " {\n patchDef3\n {\n"); + fprintf(file, " \"%s\"\n", p->d_texture->GetName()); + fprintf(file, " ( %i %i %i %i %i %i %i ) \n", p->width, p->height, p->horzSubdivisions, p->vertSubdivisions, p->contents, p->flags, p->value); + } else { + fprintf(file, " {\n patchDef2\n {\n"); + fprintf(file, " \"%s\"\n", p->d_texture->GetName()); + fprintf(file, " ( %i %i %i %i %i ) \n", p->width, p->height, p->contents, p->flags, p->value); + } + + float ctrl[MAX_PATCH_WIDTH][MAX_PATCH_HEIGHT][5]; + + int w, h; + for ( w = 0; w < p->width; w++ ) { + for ( h = 0; h < p->height; h++ ) { + ctrl[w][h][0] = p->ctrl(w, h).xyz[0]; + ctrl[w][h][1] = p->ctrl(w, h).xyz[1]; + ctrl[w][h][2] = p->ctrl(w, h).xyz[2]; + ctrl[w][h][3] = p->ctrl(w, h).st[0]; + ctrl[w][h][4] = p->ctrl(w, h).st[1]; + } + } + + _Write3DMatrix(file, p->width, p->height, 5, reinterpret_cast< float*>(&ctrl)); + + if ( g_qeglobals.m_bBrushPrimitMode ) { + if ( p->epairs ) { + int count = p->epairs->GetNumKeyVals(); + for ( int i = 0; i < count; i++ ) { + fprintf(file, "\"%s\" \"%s\"\n", p->epairs->GetKeyVal(i)->GetKey().c_str(), p->epairs->GetKeyVal(i)->GetValue().c_str()); + } + } + } + + fprintf(file, " }\n }\n"); +} + + +/* +================== +Patch_RotateTexture +================== +*/ +void Patch_RotateTexture( patchMesh_t *p,float fAngle ) { + idVec3 vMin, vMax; + Patch_CalcBounds(p, vMin, vMax); + Patch_MakeDirty(p); + for ( int w = 0; w < p->width; w++ ) { + for ( int h = 0; h < p->height; h++ ) { + if ( g_qeglobals.d_select_mode == sel_curvepoint && PointInMoveList(&p->ctrl(w, h).xyz) == -1 ) { + continue; + } + + float x = p->ctrl(w, h).st[0]; + float y = p->ctrl(w, h).st[1]; + p->ctrl(w, h).st[0] = x * cos(DEG2RAD(fAngle)) - y * sin(DEG2RAD(fAngle)); + p->ctrl(w, h).st[1] = y * cos(DEG2RAD(fAngle)) + x * sin(DEG2RAD(fAngle)); + } + } +} + + +/* +================== +Patch_ScaleTexture +================== +*/ +void Patch_ScaleTexture( patchMesh_t *p,float fx,float fy,bool absolute ) { + if ( fx == 0 ) { + fx = 1.0f; + } + if ( fy == 0 ) { + fy = 1.0f; + } + + if ( absolute ) { + Patch_ResetTexturing(1, 1); + } + + for ( int w = 0; w < p->width; w++ ) { + for ( int h = 0; h < p->height; h++ ) { + if ( g_qeglobals.d_select_mode == sel_curvepoint && PointInMoveList(&p->ctrl(w, h).xyz) == -1 ) { + continue; + } + p->ctrl(w, h).st[0] *= fx; + p->ctrl(w, h).st[1] *= fy; + } + } + Patch_MakeDirty(p); +} + +/* +================== +Patch_ShiftTexture +================== +*/ +void Patch_ShiftTexture( patchMesh_t *p,float fx,float fy,bool autoAdjust ) { + //if (fx) + // fx = (fx > 0) ? 0.1 : -0.1; + //if (fy) + // fy = (fy > 0) ? 0.1 : -0.1; + + if ( autoAdjust ) { + fx /= p->d_texture->GetEditorImage()->uploadWidth; + fy /= p->d_texture->GetEditorImage()->uploadHeight; + } + + for ( int w = 0; w < p->width; w++ ) { + for ( int h = 0; h < p->height; h++ ) { + if ( g_qeglobals.d_select_mode == sel_curvepoint && PointInMoveList(&p->ctrl(w, h).xyz) == -1 ) + continue; + + p->ctrl(w, h).st[0] += fx; + p->ctrl(w, h).st[1] += fy; + } + } + Patch_MakeDirty(p); +} + +void patchInvert( patchMesh_t *p ) { + idDrawVert vertTemp; + Patch_MakeDirty(p); + for ( int i = 0 ; i < p->width ; i++ ) { + for ( int j = 0; j < p->height / 2; j++ ) { + memcpy(&vertTemp, &p->ctrl(i, p->height - 1 - j), sizeof(idDrawVert)); + memcpy(&p->ctrl(i, p->height - 1 - j), &p->ctrl(i, j), sizeof(idDrawVert)); + memcpy(&p->ctrl(i, j), &vertTemp, sizeof(idDrawVert)); + } + } +} + +/* +================== +Patch_ToggleInverted +================== +*/ +void Patch_ToggleInverted() { + bool bUpdate = false; + + for ( brush_t*pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next ) { + if ( pb->pPatch ) { + bUpdate = true; + patchInvert(pb->pPatch); + } + } + + if ( bUpdate ) { + Sys_UpdateWindows(W_ALL); + } + UpdatePatchInspector(); +} + +void Patch_FlipTexture( patchMesh_t *p,bool y ) { + idVec2 temp; + Patch_MakeDirty(p); + if ( y ) { + for ( int i = 0 ; i < p->height ; i++ ) { + for ( int j = 0; j < p->width / 2; j++ ) { + temp = p->ctrl(p->width - 1 - j, i).st; + p->ctrl(p->width - 1 - j, i).st = p->ctrl(j, i).st; + p->ctrl(j, i).st = temp; + } + } + } else { + for ( int i = 0 ; i < p->width ; i++ ) { + for ( int j = 0; j < p->height / 2; j++ ) { + temp = p->ctrl(i, p->height - 1 - j).st; + p->ctrl(i, p->height - 1 - j).st = p->ctrl(i, j).st; + p->ctrl(i, j).st = temp; + } + } + } +} + + +/* +================== +Patch_ToggleInverted +================== +*/ +void Patch_InvertTexture( bool bY ) { + bool bUpdate = false; + for ( brush_t*pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next ) { + if ( pb->pPatch ) { + bUpdate = true; + Patch_FlipTexture(pb->pPatch, bY); + } + } + + if ( bUpdate ) { + Sys_UpdateWindows(W_ALL); + } + + UpdatePatchInspector(); +} + + + + +/* +================== +Patch_Save +================== + Saves patch ctrl info (originally to deal with a + cancel in the surface dialog +*/ +void Patch_Save( patchMesh_t *p ) { + if ( patchSave ) { + Mem_Free(patchSave->verts); + Mem_Free(patchSave); + } + + patchSave = MakeNewPatch(p->width, p->height); + memcpy(patchSave->verts, p->verts, sizeof(p->verts[0]) * p->width * p->height); +} + + +/* +================== +Patch_Restore +================== +*/ +void Patch_Restore( patchMesh_t *p ) { + if ( patchSave ) { + p->width = patchSave->width; + p->height = patchSave->height; + memcpy(p->verts, patchSave->verts, sizeof(p->verts[0]) * p->width * p->height); + Mem_Free(patchSave->verts); + Mem_Free(patchSave); + patchSave = NULL; + } +} + +void Patch_FitTexture( patchMesh_t *p,float fx,float fy ) { + Patch_MakeDirty(p); + for ( int i = 0 ; i < p->width ; i++ ) { + for ( int j = 0 ; j < p->height ; j++ ) { + p->ctrl(i, j).st[0] = fx * (float) i / (p->width - 1); + p->ctrl(i, j).st[1] = fy * (float) j / (p->height - 1); + } + } +} + +void Patch_ResetTexturing( float fx,float fy ) { + for ( brush_t*pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next ) { + if ( pb->pPatch ) { + patchMesh_t *p = pb->pPatch; + Patch_MakeDirty(p); + for ( int i = 0 ; i < p->width ; i++ ) { + for ( int j = 0 ; j < p->height ; j++ ) { + p->ctrl(i, j).st[0] = fx * (float) i / (p->width - 1); + p->ctrl(i, j).st[1] = fy * (float) j / (p->height - 1); + } + } + } + } +} + + +void Patch_FitTexturing() { + for ( brush_t*pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next ) { + if ( pb->pPatch ) { + patchMesh_t *p = pb->pPatch; + Patch_MakeDirty(p); + for ( int i = 0 ; i < p->width ; i++ ) { + for ( int j = 0 ; j < p->height ; j++ ) { + p->ctrl(i, j).st[0] = 1 * (float) i / (p->width - 1); + p->ctrl(i, j).st[1] = 1 * (float) j / (p->height - 1); + } + } + } + } +} + +void Patch_SetTextureInfo( texdef_t *pt ) { + for ( brush_t*pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next ) { + if ( pb->pPatch ) { + if ( pt->rotate ) + Patch_RotateTexture(pb->pPatch, pt->rotate); + + if ( pt->shift[0] || pt->shift[1] ) + Patch_ShiftTexture(pb->pPatch, pt->shift[0], pt->shift[1], false); + + if ( pt->scale[0] || pt->scale[1] ) + Patch_ScaleTexture(pb->pPatch, pt->scale[0], pt->scale[1], false); + + patchMesh_t *p = pb->pPatch; + p->value = pt->value; + } + } +} + +bool WINAPI OnlyPatchesSelected() { + if ( g_ptrSelectedFaces.GetSize() > 0 || selected_brushes.next == &selected_brushes ) { + return false; + } + for ( brush_t*pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next ) { + if ( !pb->pPatch ) { + return false; + } + } + return true; +} + +bool WINAPI AnyPatchesSelected() { + if ( g_ptrSelectedFaces.GetSize() > 0 || selected_brushes.next == &selected_brushes ) { + return false; + } + for ( brush_t*pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next ) { + if ( pb->pPatch ) { + return true; + } + } + return false; +} + +patchMesh_t * SinglePatchSelected() { + if ( selected_brushes.next->pPatch ) { + return selected_brushes.next->pPatch; + } + return NULL; +} + +void Patch_BendToggle() { + if ( g_bPatchBendMode ) { + g_bPatchBendMode = false; + HideInfoDialog(); + g_pParentWnd->UpdatePatchToolbarButtons() ; + return; + } + + brush_t *b = selected_brushes.next; + + if ( !QE_SingleBrush() || !b->pPatch ) { + Sys_Status("Must bend a single patch"); + return; + } + + Patch_Save(b->pPatch); + g_bPatchBendMode = true; + g_nPatchBendState = BEND_SELECT_ROTATION; + g_bPatchAxisOnRow = true; + g_nPatchAxisIndex = 1; + ShowInfoDialog(g_pBendStateMsg[BEND_SELECT_ROTATION]); +} + +void Patch_BendHandleTAB() { + if ( !g_bPatchBendMode ) { + return; + } + + brush_t *b = selected_brushes.next; + if ( !QE_SingleBrush() || !b->pPatch ) { + Patch_BendToggle(); + Sys_Status("No patch to bend!"); + return; + } + + patchMesh_t *p = b->pPatch; + + bool bShift = ((GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0); + + if ( g_nPatchBendState == BEND_SELECT_ROTATION ) { + // only able to deal with odd numbered rows/cols + g_nPatchAxisIndex += (bShift) ? -2 : 2; + if ( g_bPatchAxisOnRow ) { + if ( (bShift) ? g_nPatchAxisIndex <= 0 : g_nPatchAxisIndex >= p->height ) { + g_bPatchAxisOnRow = false; + g_nPatchAxisIndex = (bShift) ? p->width - 1 : 1; + } + } else { + if ( (bShift) ? g_nPatchAxisIndex <= 0 : g_nPatchAxisIndex >= p->width ) { + g_bPatchAxisOnRow = true; + g_nPatchAxisIndex = (bShift) ? p->height - 1 : 1; + } + } + } else if ( g_nPatchBendState == BEND_SELECT_ORIGIN ) { + g_nBendOriginIndex += (bShift) ? -1 : 1; + if ( g_bPatchAxisOnRow ) { + if ( bShift ) { + if ( g_nBendOriginIndex < 0 ) + g_nBendOriginIndex = p->width - 1; + } else { + if ( g_nBendOriginIndex > p->width - 1 ) + g_nBendOriginIndex = 0; + } + VectorCopy(p->ctrl(g_nBendOriginIndex, g_nPatchAxisIndex).xyz, g_vBendOrigin); + } else { + if ( bShift ) { + if ( g_nBendOriginIndex < 0 ) + g_nBendOriginIndex = p->height - 1; + } else { + if ( g_nBendOriginIndex > p->height - 1 ) + g_nBendOriginIndex = 0; + } + VectorCopy(p->ctrl(g_nPatchAxisIndex, g_nBendOriginIndex).xyz, g_vBendOrigin); + } + } else if ( g_nPatchBendState == BEND_SELECT_EDGE ) { + g_bPatchLowerEdge ^= 1; + } + Sys_UpdateWindows(W_ALL); +} + +void Patch_BendHandleENTER() { + if ( !g_bPatchBendMode ) { + return; + } + + if ( g_nPatchBendState < BEND_BENDIT ) { + g_nPatchBendState++; + ShowInfoDialog(g_pBendStateMsg[g_nPatchBendState]); + if ( g_nPatchBendState == BEND_SELECT_ORIGIN ) { + g_vBendOrigin[0] = g_vBendOrigin[1] = g_vBendOrigin[2] = 0; + g_nBendOriginIndex = 0; + Patch_BendHandleTAB(); + } else if ( g_nPatchBendState == BEND_SELECT_EDGE ) { + g_bPatchLowerEdge = true; + } else if ( g_nPatchBendState == BEND_BENDIT ) { + // basically we go into rotation mode, set the axis to the center of the + } + } else { + // done + Patch_BendToggle(); + } + Sys_UpdateWindows(W_ALL); +} + + +void Patch_BendHandleESC() { + if ( !g_bPatchBendMode ) { + return; + } + Patch_BendToggle(); + brush_t *b = selected_brushes.next; + if ( QE_SingleBrush() && b->pPatch ) { + Patch_Restore(b->pPatch); + } + Sys_UpdateWindows(W_ALL); +} + +void Patch_SetBendRotateOrigin( patchMesh_t *p ) { + int nType = g_pParentWnd->ActiveXY()->GetViewType(); + int nDim3 = (nType == XY) ? 2 : (nType == YZ) ? 0 : 1; + g_vBendOrigin[nDim3] = 0; + VectorCopy(g_vBendOrigin, g_pParentWnd->ActiveXY()->RotateOrigin()); + return; +} + +// also sets the rotational origin +void Patch_SelectBendAxis() { + brush_t *b = selected_brushes.next; + if ( !QE_SingleBrush() || !b->pPatch ) { + // should not ever happen + Patch_BendToggle(); + return; + } + + patchMesh_t *p = b->pPatch; + if ( g_bPatchAxisOnRow ) { + SelectRow(p, g_nPatchAxisIndex, false); + } else { + SelectColumn(p, g_nPatchAxisIndex, false); + } + + Patch_SetBendRotateOrigin(p); +} + +void Patch_SelectBendNormal() { + brush_t *b = selected_brushes.next; + if ( !QE_SingleBrush() || !b->pPatch ) { + // should not ever happen + Patch_BendToggle(); + return; + } + + patchMesh_t *p = b->pPatch; + + g_qeglobals.d_num_move_points = 0; + if ( g_bPatchAxisOnRow ) { + if ( g_bPatchLowerEdge ) { + for ( int j = 0; j < g_nPatchAxisIndex; j++ ) + SelectRow(p, j, true); + } else { + for ( int j = p->height - 1; j > g_nPatchAxisIndex; j-- ) + SelectRow(p, j, true); + } + } else { + if ( g_bPatchLowerEdge ) { + for ( int j = 0; j < g_nPatchAxisIndex; j++ ) + SelectColumn(p, j, true); + } else { + for ( int j = p->width - 1; j > g_nPatchAxisIndex; j-- ) + SelectColumn(p, j, true); + } + } + Patch_SetBendRotateOrigin(p); +} + + + +void Patch_InsDelToggle() { + if ( g_bPatchInsertMode ) { + g_bPatchInsertMode = false; + HideInfoDialog(); + g_pParentWnd->UpdatePatchToolbarButtons() ; + return; + } + + brush_t *b = selected_brushes.next; + + if ( !QE_SingleBrush() || !b->pPatch ) { + Sys_Status("Must work with a single patch"); + return; + } + + Patch_Save(b->pPatch); + g_bPatchInsertMode = true; + g_nPatchInsertState = INSERT_SELECT_EDGE; + g_bPatchAxisOnRow = true; + g_nPatchAxisIndex = 0; + ShowInfoDialog(g_pInsertStateMsg[INSERT_SELECT_EDGE]); +} + +void Patch_InsDelESC() { + if ( !g_bPatchInsertMode ) { + return; + } + Patch_InsDelToggle(); + Sys_UpdateWindows(W_ALL); +} + + +void Patch_InsDelHandleENTER() { +} + +void Patch_InsDelHandleTAB() { + if ( !g_bPatchInsertMode ) { + Patch_InsDelToggle(); + return; + } + + brush_t *b = selected_brushes.next; + if ( !QE_SingleBrush() || !b->pPatch ) { + Patch_BendToggle(); + common->Printf("No patch to bend!"); + return; + } + + patchMesh_t *p = b->pPatch; + + // only able to deal with odd numbered rows/cols + g_nPatchAxisIndex += 2; + if ( g_bPatchAxisOnRow ) { + if ( g_nPatchAxisIndex >= p->height - 1 ) { + g_bPatchAxisOnRow = false; + g_nPatchAxisIndex = 0; + } + } else { + if ( g_nPatchAxisIndex >= p->width - 1 ) { + g_bPatchAxisOnRow = true; + g_nPatchAxisIndex = 0; + } + } + Sys_UpdateWindows(W_ALL); +} + + +void _Write1DMatrix( FILE *f,int x,float *m ) { + int i; + + fprintf(f, "( "); + for ( i = 0 ; i < x ; i++ ) { + if ( m[i] == (int) m[i] ) { + fprintf(f, "%i ", (int) m[i]); + } else { + fprintf(f, "%f ", m[i]); + } + } + fprintf(f, ")"); +} + +void _Write2DMatrix( FILE *f,int y,int x,float *m ) { + int i; + + fprintf(f, "( "); + for ( i = 0 ; i < y ; i++ ) { + _Write1DMatrix(f, x, m + i * x); + fprintf(f, " "); + } + fprintf(f, ")\n"); +} + + +void _Write3DMatrix( FILE *f,int z,int y,int x,float *m ) { + int i; + + fprintf(f, "(\n"); + for ( i = 0 ; i < z ; i++ ) { + _Write2DMatrix(f, y, x, m + i * (x * MAX_PATCH_HEIGHT)); + } + fprintf(f, ")\n"); +} + +void _Write1DMatrix( CMemFile *f,int x,float *m ) { + int i; + + MemFile_fprintf(f, "( "); + for ( i = 0 ; i < x ; i++ ) { + if ( m[i] == (int) m[i] ) { + MemFile_fprintf(f, "%i ", (int) m[i]); + } else { + MemFile_fprintf(f, "%f ", m[i]); + } + } + MemFile_fprintf(f, ")"); +} + +void _Write2DMatrix( CMemFile *f,int y,int x,float *m ) { + int i; + + MemFile_fprintf(f, "( "); + for ( i = 0 ; i < y ; i++ ) { + _Write1DMatrix(f, x, m + i * x); + MemFile_fprintf(f, " "); + } + MemFile_fprintf(f, ")\n"); +} + + +void _Write3DMatrix( CMemFile *f,int z,int y,int x,float *m ) { + int i; + + MemFile_fprintf(f, "(\n"); + for ( i = 0 ; i < z ; i++ ) { + _Write2DMatrix(f, y, x, m + i * (x * MAX_PATCH_HEIGHT)); + } + MemFile_fprintf(f, ")\n"); +} + + +void Patch_NaturalizeSelected( bool bCap,bool bCycleCap,bool alt ) { + for ( brush_t*pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next ) { + if ( pb->pPatch ) { + if ( bCap ) { + Patch_CapTexture(pb->pPatch, bCycleCap, alt); + } else { + Patch_Naturalize(pb->pPatch, true, true, alt); + } + } + } +} + +void Patch_SubdivideSelected( bool subdivide,int horz,int vert ) { + for ( brush_t*pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next ) { + if ( pb->pPatch ) { + pb->pPatch->explicitSubdivisions = subdivide; + if ( horz <= 0 ) { + horz = 1; + } + if ( vert <= 0 ) { + vert = 1; + } + pb->pPatch->horzSubdivisions = horz; + pb->pPatch->vertSubdivisions = vert; + Patch_MakeDirty(pb->pPatch); + } + } +} + + + +bool within( idVec3 vTest,idVec3 vTL,idVec3 vBR ) { + int nDim1 = (g_pParentWnd->ActiveXY()->GetViewType() == YZ) ? 1 : 0; + int nDim2 = (g_pParentWnd->ActiveXY()->GetViewType() == XY) ? 1 : 2; + if ( (vTest[nDim1] > vTL[nDim1] && vTest[nDim1] < vBR[nDim1]) || (vTest[nDim1] < vTL[nDim1] && vTest[nDim1] > vBR[nDim1]) ) { + if ( (vTest[nDim2] > vTL[nDim2] && vTest[nDim2] < vBR[nDim2]) || (vTest[nDim2] < vTL[nDim2] && vTest[nDim2] > vBR[nDim2]) ) { + return true; + } + } + return false; +} + + +void Patch_SelectAreaPoints() { + //jhefty - make patch selection additive ALWAYS + //g_qeglobals.d_num_move_points = 0; + g_nPatchClickedView = -1; + + for ( brush_t*pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next ) { + if ( pb->pPatch ) { + patchMesh_t *p = pb->pPatch; + for ( int i = 0; i < p->width; i++ ) { + for ( int j = 0; j < p->height; j++ ) { + if ( within(p->ctrl(i, j).xyz, g_qeglobals.d_vAreaTL, g_qeglobals.d_vAreaBR) ) { + g_qeglobals.d_move_points[g_qeglobals.d_num_move_points++] = &p->ctrl(i, j).xyz; + } + } + } + } + } +} + +const char * Patch_GetTextureName() { + brush_t *b = selected_brushes.next; + if ( b->pPatch ) { + patchMesh_t *p = b->pPatch; + if ( p->d_texture->GetName() ) + return p->d_texture->GetName(); + } + return ""; +} + +patchMesh_t * Patch_Duplicate( patchMesh_t *pFrom ) { + patchMesh_t *p = MakeNewPatch(pFrom->width, pFrom->height); + p->contents = pFrom->contents; + p->value = pFrom->value; + p->horzSubdivisions = pFrom->horzSubdivisions; + p->vertSubdivisions = pFrom->vertSubdivisions; + p->explicitSubdivisions = pFrom->explicitSubdivisions; + p->d_texture = pFrom->d_texture; + p->bSelected = false; + p->bOverlay = false; + p->nListID = -1; + + memcpy(p->verts, pFrom->verts, p->width * p->height * sizeof(idDrawVert)); + + AddBrushForPatch(p); + return p; +} + + +void Patch_Thicken( int nAmount,bool bSeam ) { + int i, j, h, w; + brush_t *b; + patchMesh_t *pSeam; + idVec3 vMin, vMax; + CPtrArray brushes; + + nAmount = -nAmount; + + + if ( !QE_SingleBrush() ) { + Sys_Status("Cannot thicken multiple patches. Please select a single patch.\n"); + return; + } + + for ( brush_t*pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next ) { + if ( !pb->pPatch ) { + return; + } + + patchMesh_t *p = pb->pPatch; + Patch_MeshNormals(p); + patchMesh_t *pNew = Patch_Duplicate(p); + for ( i = 0; i < p->width; i++ ) { + for ( j = 0; j < p->height; j++ ) { + VectorMA(p->ctrl(i, j).xyz, nAmount, p->ctrl(i, j).normal, pNew->ctrl(i, j).xyz); + } + } + + Patch_Rebuild(pNew); + pNew->type |= PATCH_THICK; + brushes.Add(pNew->pSymbiot); + + if ( bSeam ) { + // FIXME: this should detect if any edges of the patch are closed and act appropriately + // + if ( !(p->type & PATCH_CYLINDER) ) { + b = Patch_GenericMesh(3, p->height, 2, false, true, p); + pSeam = b->pPatch; + pSeam->type |= PATCH_SEAM; + for ( i = 0; i < p->height; i++ ) { + VectorCopy(p->ctrl(0, i).xyz, pSeam->ctrl(0, i).xyz); + VectorCopy(pNew->ctrl(0, i).xyz, pSeam->ctrl(2, i).xyz); + VectorAdd(pSeam->ctrl(0, i).xyz, pSeam->ctrl(2, i).xyz, pSeam->ctrl(1, i).xyz); + VectorScale(pSeam->ctrl(1, i).xyz, 0.5, pSeam->ctrl(1, i).xyz); + } + + + Patch_CalcBounds(pSeam, vMin, vMax); + Brush_RebuildBrush(pSeam->pSymbiot, vMin, vMax); + //--Patch_CapTexture(pSeam); + Patch_Naturalize(pSeam); + patchInvert(pSeam); + brushes.Add(b); + + w = p->width - 1; + b = Patch_GenericMesh(3, p->height, 2, false, true, p); + pSeam = b->pPatch; + pSeam->type |= PATCH_SEAM; + for ( i = 0; i < p->height; i++ ) { + VectorCopy(p->ctrl(w, i).xyz, pSeam->ctrl(0, i).xyz); + VectorCopy(pNew->ctrl(w, i).xyz, pSeam->ctrl(2, i).xyz); + VectorAdd(pSeam->ctrl(0, i).xyz, pSeam->ctrl(2, i).xyz, pSeam->ctrl(1, i).xyz); + VectorScale(pSeam->ctrl(1, i).xyz, 0.5, pSeam->ctrl(1, i).xyz); + } + Patch_CalcBounds(pSeam, vMin, vMax); + Brush_RebuildBrush(pSeam->pSymbiot, vMin, vMax); + //--Patch_CapTexture(pSeam); + Patch_Naturalize(pSeam); + brushes.Add(b); + } + + //--{ + // otherwise we will add one per end + b = Patch_GenericMesh(p->width, 3, 2, false, true, p); + pSeam = b->pPatch; + pSeam->type |= PATCH_SEAM; + for ( i = 0; i < p->width; i++ ) { + VectorCopy(p->ctrl(i, 0).xyz, pSeam->ctrl(i, 0).xyz); + VectorCopy(pNew->ctrl(i, 0).xyz, pSeam->ctrl(i, 2).xyz); + VectorAdd(pSeam->ctrl(i, 0).xyz, pSeam->ctrl(i, 2).xyz, pSeam->ctrl(i, 1).xyz); + VectorScale(pSeam->ctrl(i, 1).xyz, 0.5, pSeam->ctrl(i, 1).xyz); + } + + + Patch_CalcBounds(pSeam, vMin, vMax); + Brush_RebuildBrush(pSeam->pSymbiot, vMin, vMax); + //--Patch_CapTexture(pSeam); + Patch_Naturalize(pSeam); + patchInvert(pSeam); + brushes.Add(b); + + h = p->height - 1; + b = Patch_GenericMesh(p->width, 3, 2, false, true, p); + pSeam = b->pPatch; + pSeam->type |= PATCH_SEAM; + for ( i = 0; i < p->width; i++ ) { + VectorCopy(p->ctrl(i, h).xyz, pSeam->ctrl(i, 0).xyz); + VectorCopy(pNew->ctrl(i, h).xyz, pSeam->ctrl(i, 2).xyz); + VectorAdd(pSeam->ctrl(i, 0).xyz, pSeam->ctrl(i, 2).xyz, pSeam->ctrl(i, 1).xyz); + VectorScale(pSeam->ctrl(i, 1).xyz, 0.5, pSeam->ctrl(i, 1).xyz); + } + Patch_CalcBounds(pSeam, vMin, vMax); + Brush_RebuildBrush(pSeam->pSymbiot, vMin, vMax); + //--Patch_CapTexture(pSeam); + Patch_Naturalize(pSeam); + brushes.Add(b); + //--} + } + patchInvert(pNew); + } + + for ( i = 0; i < brushes.GetSize(); i++ ) { + Select_Brush(reinterpret_cast< brush_t*>(brushes.GetAt(i))); + } + + if ( brushes.GetSize() > 0 ) { + eclass_t*pecNew = Eclass_ForName("func_static", false); + if ( pecNew ) { + entity_t*e = Entity_Create(pecNew); + SetKeyValue(e, "type", "patchThick"); + } + } + + UpdatePatchInspector(); +} + + +/* +lets get another list together as far as necessities.. + +*snapping stuff to the grid (i will only snap movements by the mouse to the grid.. snapping the rotational bend stuff will fubar everything) + +capping bevels/endcaps + +hot keys + +texture fix for caps + +clear clipboard + +*region fix + +*surface dialog + +*/ + +void Patch_SetOverlays() { + for ( brush_t*pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next ) { + if ( pb->pPatch ) { + pb->pPatch->bOverlay = true; + } + } +} + + + +void Patch_ClearOverlays() { + brush_t *pb; + for ( pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next ) { + if ( pb->pPatch ) { + pb->pPatch->bOverlay = false; + } + } + + for ( pb = active_brushes.next ; pb != &active_brushes ; pb = pb->next ) { + if ( pb->pPatch ) { + pb->pPatch->bOverlay = false; + } + } +} + +// freezes selected vertices +void Patch_Freeze() { + brush_t *pb; + for ( pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next ) { + if ( pb->pPatch ) { + pb->pPatch->bOverlay = false; + } + } + + for ( pb = active_brushes.next ; pb != &active_brushes ; pb = pb->next ) { + if ( pb->pPatch ) { + pb->pPatch->bOverlay = false; + } + } +} + +void Patch_UnFreeze( bool bAll ) { +} + + +void Patch_Transpose() { + int i, j, w; + idDrawVert dv; + for ( brush_t*pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next ) { + if ( pb->pPatch ) { + patchMesh_t *p = pb->pPatch; + + if ( p->width > p->height ) { + for ( i = 0 ; i < p->height ; i++ ) { + for ( j = i + 1 ; j < p->width ; j++ ) { + if ( j < p->height ) { + // swap the value + memcpy(&dv, &p->ctrl(j, i), sizeof(idDrawVert)); + memcpy(&p->ctrl(j, i), &p->ctrl(i, j), sizeof(idDrawVert)); + memcpy(&p->ctrl(i, j), &dv, sizeof(idDrawVert)); + } else { + // just copy + memcpy(&p->ctrl(j, i), &p->ctrl(i, j), sizeof(idDrawVert)); + } + } + } + } else { + for ( i = 0 ; i < p->width ; i++ ) { + for ( j = i + 1 ; j < p->height ; j++ ) { + if ( j < p->width ) { + // swap the value + memcpy(&dv, &p->ctrl(i, j), sizeof(idDrawVert)); + memcpy(&p->ctrl(i, j), &p->ctrl(j, i), sizeof(idDrawVert)); + memcpy(&p->ctrl(j, i), &dv, sizeof(idDrawVert)); + } else { + // just copy + memcpy(&p->ctrl(i, j), &p->ctrl(j, i), sizeof(idDrawVert)); + } + } + } + } + + w = p->width; + p->width = p->height; + p->height = w; + patchInvert(p); + Patch_Rebuild(p); + } + } +} + + + +void Select_SnapToGrid() { + int i, j, k; + for ( brush_t*pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next ) { + if ( pb->pPatch ) { + patchMesh_t *p = pb->pPatch; + for ( i = 0; i < p->width; i++ ) { + for ( j = 0; j < p->height; j++ ) { + for ( k = 0; k < 3; k++ ) { + p->ctrl(i, j).xyz[k] = floor(p->ctrl(i, j).xyz[k] / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize; + } + } + } + idVec3 vMin, vMax; + Patch_CalcBounds(p, vMin, vMax); + Brush_RebuildBrush(p->pSymbiot, vMin, vMax); + } else { + Brush_SnapToGrid(pb); + } + } +} + + +void Patch_FindReplaceTexture( brush_t *pb,const char *pFind,const char *pReplace,bool bForce ) { + if ( pb->pPatch ) { + patchMesh_t *p = pb->pPatch; + if ( bForce || idStr::Icmp(p->d_texture->GetName(), pFind) == 0 ) { + p->d_texture = Texture_ForName(pReplace); + //strcpy(p->d_texture->name, pReplace); + } + } +} + +void Patch_ReplaceQTexture( brush_t *pb,idMaterial *pOld,idMaterial *pNew ) { + if ( pb->pPatch ) { + patchMesh_t *p = pb->pPatch; + if ( p->d_texture == pOld ) { + p->d_texture = pNew; + } + } +} + +void Patch_Clone( patchMesh_t *p,brush_t *pNewOwner ) { +} + +void Patch_FromTriangle( idVec5 vx,idVec5 vy,idVec5 vz ) { + patchMesh_t *p = MakeNewPatch(3, 3); + p->d_texture = Texture_ForName(g_qeglobals.d_texturewin.texdef.name); + p->type = PATCH_TRIANGLE; + + // 0 0 goes to x + // 0 1 goes to x + // 0 2 goes to x + + // 1 0 goes to mid of x and z + // 1 1 goes to mid of x y and z + // 1 2 goes to mid of x and y + + // 2 0 goes to z + // 2 1 goes to mid of y and z + // 2 2 goes to y + + idVec5 vMidXZ; + idVec5 vMidXY; + idVec5 vMidYZ; + + + vMidXZ.Lerp(vx, vz, 0.5); + vMidXY.Lerp(vx, vy, 0.5); + vMidYZ.Lerp(vy, vz, 0.5); + + p->ctrl(0, 0).xyz = vx.ToVec3(); + p->ctrl(0, 1).xyz = vx.ToVec3(); + p->ctrl(0, 2).xyz = vx.ToVec3(); + p->ctrl(0, 0).st[0] = vx[3]; + p->ctrl(0, 0).st[1] = vx[4]; + p->ctrl(0, 1).st[0] = vx[3]; + p->ctrl(0, 1).st[1] = vx[4]; + p->ctrl(0, 2).st[0] = vx[3]; + p->ctrl(0, 2).st[1] = vx[4]; + + p->ctrl(1, 0).xyz = vMidXY.ToVec3(); + p->ctrl(1, 1).xyz = vx.ToVec3(); + p->ctrl(1, 2).xyz = vMidXZ.ToVec3(); + p->ctrl(1, 0).st[0] = vMidXY[3]; + p->ctrl(1, 0).st[1] = vMidXY[4]; + p->ctrl(1, 1).st[0] = vx[3]; + p->ctrl(1, 1).st[1] = vx[4]; + p->ctrl(1, 2).st[0] = vMidXZ[3]; + p->ctrl(1, 2).st[1] = vMidXZ[4]; + + p->ctrl(2, 0).xyz = vy.ToVec3(); + p->ctrl(2, 1).xyz = vMidYZ.ToVec3(); + p->ctrl(2, 2).xyz = vz.ToVec3(); + p->ctrl(2, 0).st[0] = vy[3]; + p->ctrl(2, 0).st[1] = vy[4]; + p->ctrl(2, 1).st[0] = vMidYZ[3]; + p->ctrl(2, 1).st[1] = vMidYZ[4]; + p->ctrl(2, 2).st[0] = vz[3]; + p->ctrl(2, 2).st[1] = vz[4]; + + + //Patch_Naturalize(p); + + brush_t *b = AddBrushForPatch(p); +} + + +/* +============== +Patch_SetEpair +sets an epair for the given patch +============== +*/ +void Patch_SetEpair( patchMesh_t *p,const char *pKey,const char *pValue ) { + if ( g_qeglobals.m_bBrushPrimitMode ) { + if ( p->epairs == NULL ) { + p->epairs = new idDict; + } + p->epairs->Set(pKey, pValue); + } +} + +/* +================= +Patch_GetKeyValue +================= +*/ +const char * Patch_GetKeyValue( patchMesh_t *p,const char *pKey ) { + if ( g_qeglobals.m_bBrushPrimitMode ) { + if ( p->epairs ) { + return p->epairs->GetString(pKey); + } + } + return ""; +} + + +//Real nitpicky, but could you make CTRL-S save the current map with the current name? (ie: File/Save) +/* +Feature addition. +When reading in textures, please check for the presence of a file called "textures.link" or something, which contains one line such as; + +g:\quake3\baseq3\textures\common + + So that, when I'm reading in, lets say, my \eerie directory, it goes through and adds my textures to the palette, along with everything in common. + + Don't forget to add "Finer texture alignment" to the list. I'd like to be able to move in 0.1 increments using the Shift-Arrow Keys. + + No. Sometimes textures are drawn the wrong way on patches. We'd like the ability to flip a texture. Like the way X/Y scale -1 used to worked. + + 1) Easier way of deleting rows, columns +2) Fine tuning of textures on patches (X/Y shifts other than with the surface dialog) +2) Patch matrix transposition + + 1) Actually, bump texture flipping on patches to the top of the list of things to do. +2) When you select a patch, and hit S, it should read in the selected patch texture. Should not work if you multiselect patches and hit S +3) Brandon has a wierd anomoly. He fine-tunes a patch with caps. It looks fine when the patch is selected, but as soon as he escapes out, it reverts to it's pre-tuned state. When he selects the patch again, it looks tuned + + +*1) Flipping textures on patches +*2) When you select a patch, and hit S, it should read in the selected patch texture. Should not work if you multiselect patches and hit S +3) Easier way of deleting rows columns +*4) Thick Curves +5) Patch matrix transposition +6) Inverted cylinder capping +*7) bugs +*8) curve speed + + Have a new feature request. "Compute Bounding Box" for mapobjects (md3 files). This would be used for misc_mapobject (essentially, drop in 3DS Max models into our maps) + + Ok, Feature Request. Load and draw MD3's in the Camera view with proper bounding boxes. This should be off misc_model + + Feature Addition: View/Hide Hint Brushes -- This should be a specific case. +*/ + + + diff --git a/src/tools/radiant/PMESH.H b/src/tools/radiant/PMESH.H new file mode 100644 index 0000000..3139f96 --- /dev/null +++ b/src/tools/radiant/PMESH.H @@ -0,0 +1,124 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +// patch stuff +patchMesh_t* MakeNewPatch(int width, int height); +brush_t* AddBrushForPatch(patchMesh_t *pm, bool bLinkToWorld = true); +brush_t* Patch_GenericMesh(int nWidth, int nHeight, int nOrientation = 2, bool bDeleteSource = true, bool bOverride = false, patchMesh_t *parent = NULL); +void Patch_ReadFile (char *name); +void Patch_WriteFile (char *name); +void Patch_BuildPoints (brush_t *b); +void Patch_Move(patchMesh_t *p, const idVec3 vMove, bool bRebuild = false); +//++timo had to add a default value for bSnap (see Patch_ApplyMatrix call from Select_ApplyMatrix in select.cpp) +void Patch_ApplyMatrix(patchMesh_t *p, const idVec3 vOrigin, const idMat3 matrix, bool bSnap = false); +void Patch_EditPatch(); +void Patch_Deselect(); +void Patch_Deselect(patchMesh_t *p); +void Patch_Delete(patchMesh_t *p); +int Patch_MemorySize(patchMesh_t *p); +void Patch_Select(patchMesh_t *p); +void Patch_Scale(patchMesh_t *p, const idVec3 vOrigin, const idVec3 vAmt, bool bRebuilt = true); +void Patch_Cleanup(); +void Patch_SetView(int n); +void Patch_SetTexture(patchMesh_t *p, texdef_t *tex_def); +void Patch_SetTextureName(patchMesh_t *p, const char *name); +void Patch_BrushToMesh(bool bCone = false, bool bBevel = false, bool bEndcap = false, bool bSquare = false, int nHeight = 3); +bool Patch_DragScale(patchMesh_t *p, idVec3 vAmt, idVec3 vMove); +void Patch_ReadBuffer(char* pBuff, bool bSelect = false); +void Patch_WriteFile (CMemFile* pMemFile); +void Patch_UpdateSelected(idVec3 vMove); +void Patch_AddRow(patchMesh_t *p); +brush_t* Patch_Parse(bool bOld); +void Patch_Write (patchMesh_t *p, FILE *f); +void Patch_Write (patchMesh_t *p, CMemFile *file); +void Patch_AdjustColumns(patchMesh_t *p, int nCols); +void Patch_AdjustRows(patchMesh_t *p, int nRows); +void Patch_AdjustSelected(bool bInsert, bool bColumn, bool bFlag); +patchMesh_t* Patch_Duplicate(patchMesh_t *pFrom); +void Patch_RotateTexture(patchMesh_t *p, float fAngle); +void Patch_ScaleTexture(patchMesh_t *p, float fx, float fy, bool absolute); +void Patch_ShiftTexture(patchMesh_t *p, float fx, float fy, bool autoAdjust); +void Patch_DrawCam(patchMesh_t *p, bool selected); +void Patch_DrawXY(patchMesh_t *p); +void Patch_InsertColumn(patchMesh_t *p, bool bAdd); +void Patch_InsertRow(patchMesh_t *p, bool bAdd); +void Patch_RemoveRow(patchMesh_t *p, bool bFirst); +void Patch_RemoveColumn(patchMesh_t *p, bool bFirst); +void Patch_ToggleInverted(); +void Patch_Restore(patchMesh_t *p); +void Patch_Save(patchMesh_t *p); +void Patch_SetTextureInfo(texdef_t* pt); +void Patch_NaturalTexturing(); +void Patch_ResetTexturing(float fx, float fy); +void Patch_FitTexture(patchMesh_t *p, float fx, float fy); +void Patch_FitTexturing(); +void Patch_BendToggle(); +void Patch_StartInsDel(); +void Patch_BendHandleTAB(); +void Patch_BendHandleENTER(); +void Patch_SelectBendNormal(); +void Patch_SelectBendAxis(); +patchMesh_t* SinglePatchSelected(); +void Patch_CapCurrent(bool bInvertedBevel = false, bool bInvertedEndcap = false); +void Patch_DisperseRows(); +void Patch_DisperseColumns(); +void Patch_NaturalizeSelected(bool bCap = false, bool bCycleCap = false, bool alt = false); +void Patch_SubdivideSelected(bool subdivide, int horz, int vert); +void Patch_Naturalize(patchMesh_t *p, bool horz = true, bool vert = true, bool alt = false); +void Patch_SelectAreaPoints(); +void Patch_InvertTexture(bool bY); +void Patch_InsDelToggle(); +void Patch_InsDelHandleTAB(); +void Patch_InsDelHandleENTER(); +void Patch_SetOverlays(); +void Patch_ClearOverlays(); +void Patch_Thicken(int nAmount, bool bSeam); +void Patch_Transpose(); +void Patch_Freeze(); +void Patch_MakeDirty(patchMesh_t *p); +void Patch_UnFreeze(bool bAll); +const char* Patch_GetTextureName(); +void Patch_FindReplaceTexture(brush_t *pb, const char *pFind, const char *pReplace, bool bForce); +void Patch_ReplaceQTexture(brush_t *pb, idMaterial *pOld, idMaterial *pNew); +void Select_SnapToGrid(); +void Patch_FromTriangle(idVec5 vx, idVec5 vy, idVec5 vz); +const char* Patch_GetKeyValue(patchMesh_t *p, const char *pKey); +void Patch_SetEpair(patchMesh_t *p, const char *pKey, const char *pValue); +void Patch_FlipTexture(patchMesh_t *p, bool y); + +bool WINAPI OnlyPatchesSelected(); +bool WINAPI AnyPatchesSelected(); +void WINAPI Patch_Rebuild(patchMesh_t *p); + +extern bool g_bPatchShowBounds; +extern bool g_bPatchWireFrame; +extern bool g_bPatchWeld; +extern bool g_bPatchDrillDown; +extern bool g_bPatchInsertMode; +extern bool g_bPatchBendMode; +extern idVec3 g_vBendOrigin; diff --git a/src/tools/radiant/PatchDensityDlg.cpp b/src/tools/radiant/PatchDensityDlg.cpp new file mode 100644 index 0000000..8909082 --- /dev/null +++ b/src/tools/radiant/PatchDensityDlg.cpp @@ -0,0 +1,96 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" +#include "PatchDensityDlg.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CPatchDensityDlg dialog + + +CPatchDensityDlg::CPatchDensityDlg(CWnd* pParent /*=NULL*/) + : CDialog(CPatchDensityDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(CPatchDensityDlg) + //}}AFX_DATA_INIT +} + + +void CPatchDensityDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CPatchDensityDlg) + DDX_Control(pDX, IDC_COMBO_WIDTH, m_wndWidth); + DDX_Control(pDX, IDC_COMBO_HEIGHT, m_wndHeight); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CPatchDensityDlg, CDialog) + //{{AFX_MSG_MAP(CPatchDensityDlg) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CPatchDensityDlg message handlers + +int g_nXLat[] = {3,5,7,9,11,13,15}; + +void CPatchDensityDlg::OnOK() +{ + int nWidth = m_wndWidth.GetCurSel(); + int nHeight = m_wndHeight.GetCurSel(); + + if (nWidth >= 0 && nWidth <= 6 && nHeight >= 0 && nHeight <= 6) + { + Patch_GenericMesh(g_nXLat[nWidth], g_nXLat[nHeight], g_pParentWnd->ActiveXY()->GetViewType()); + Sys_UpdateWindows(W_ALL); + } + + CDialog::OnOK(); +} + +BOOL CPatchDensityDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + + m_wndWidth.SetCurSel(0); + m_wndHeight.SetCurSel(0); + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} diff --git a/src/tools/radiant/PatchDensityDlg.h b/src/tools/radiant/PatchDensityDlg.h new file mode 100644 index 0000000..fb8e47c --- /dev/null +++ b/src/tools/radiant/PatchDensityDlg.h @@ -0,0 +1,75 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#if !defined(AFX_PATCHDENSITYDLG_H__509162A1_1023_11D2_AFFB_00AA00A410FC__INCLUDED_) +#define AFX_PATCHDENSITYDLG_H__509162A1_1023_11D2_AFFB_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// PatchDensityDlg.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CPatchDensityDlg dialog + +class CPatchDensityDlg : public CDialog +{ +// Construction +public: + CPatchDensityDlg(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CPatchDensityDlg) + enum { IDD = IDD_DIALOG_NEWPATCH }; + CComboBox m_wndWidth; + CComboBox m_wndHeight; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CPatchDensityDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CPatchDensityDlg) + virtual void OnOK(); + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_PATCHDENSITYDLG_H__509162A1_1023_11D2_AFFB_00AA00A410FC__INCLUDED_) diff --git a/src/tools/radiant/PatchDialog.cpp b/src/tools/radiant/PatchDialog.cpp new file mode 100644 index 0000000..87f7422 --- /dev/null +++ b/src/tools/radiant/PatchDialog.cpp @@ -0,0 +1,358 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" +#include "PatchDialog.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CPatchDialog dialog + +CPatchDialog g_PatchDialog; + +CPatchDialog::CPatchDialog(CWnd* pParent /*=NULL*/) + : CDialog(CPatchDialog::IDD, pParent) +{ + //{{AFX_DATA_INIT(CPatchDialog) + m_strName = _T(""); + m_fS = 0.0f; + m_fT = 0.0f; + m_fX = 0.0f; + m_fY = 0.0f; + m_fZ = 0.0f; + m_fHScale = 0.05f; + m_fHShift = 0.05f; + m_fRotate = 45; + m_fVScale = 0.05f; + m_fVShift = 0.05f; + //}}AFX_DATA_INIT + m_Patch = NULL; +} + + +void CPatchDialog::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CPatchDialog) + DDX_Control(pDX, IDC_SPIN_VSHIFT, m_wndVShift); + DDX_Control(pDX, IDC_SPIN_VSCALE, m_wndVScale); + DDX_Control(pDX, IDC_SPIN_ROTATE, m_wndRotate); + DDX_Control(pDX, IDC_SPIN_HSHIFT, m_wndHShift); + DDX_Control(pDX, IDC_SPIN_HSCALE, m_wndHScale); + DDX_Control(pDX, IDC_COMBO_TYPE, m_wndType); + DDX_Control(pDX, IDC_COMBO_ROW, m_wndRows); + DDX_Control(pDX, IDC_COMBO_COL, m_wndCols); + DDX_Text(pDX, IDC_EDIT_NAME, m_strName); + DDX_Text(pDX, IDC_EDIT_S, m_fS); + DDX_Text(pDX, IDC_EDIT_T, m_fT); + DDX_Text(pDX, IDC_EDIT_X, m_fX); + DDX_Text(pDX, IDC_EDIT_Y, m_fY); + DDX_Text(pDX, IDC_EDIT_Z, m_fZ); + DDX_Text(pDX, IDC_HSCALE, m_fHScale); + DDX_Text(pDX, IDC_HSHIFT, m_fHShift); + DDX_Text(pDX, IDC_ROTATE, m_fRotate); + DDX_Text(pDX, IDC_VSCALE, m_fVScale); + DDX_Text(pDX, IDC_VSHIFT, m_fVShift); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CPatchDialog, CDialog) + //{{AFX_MSG_MAP(CPatchDialog) + ON_BN_CLICKED(IDC_BTN_PATCHDETAILS, OnBtnPatchdetails) + ON_BN_CLICKED(IDC_BTN_PATCHFIT, OnBtnPatchfit) + ON_BN_CLICKED(IDC_BTN_PATCHNATURAL, OnBtnPatchnatural) + ON_BN_CLICKED(IDC_BTN_PATCHRESET, OnBtnPatchreset) + ON_CBN_SELCHANGE(IDC_COMBO_COL, OnSelchangeComboCol) + ON_CBN_SELCHANGE(IDC_COMBO_ROW, OnSelchangeComboRow) + ON_CBN_SELCHANGE(IDC_COMBO_TYPE, OnSelchangeComboType) + ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_HSCALE, OnDeltaposSpin) + ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_ROTATE, OnDeltaposSpin) + ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_VSCALE, OnDeltaposSpin) + ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_VSHIFT, OnDeltaposSpin) + ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_HSHIFT, OnDeltaposSpin) + ON_WM_DESTROY() + ON_BN_CLICKED(IDC_APPLY, OnApply) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CPatchDialog message handlers + +void CPatchDialog::OnBtnPatchdetails() +{ + Patch_NaturalizeSelected(true); + Sys_UpdateWindows(W_ALL); +} + +void CPatchDialog::OnBtnPatchfit() +{ + Patch_FitTexturing(); + Sys_UpdateWindows(W_ALL); +} + +void CPatchDialog::OnBtnPatchnatural() +{ + Patch_NaturalizeSelected(); + Sys_UpdateWindows(W_ALL); +} + +void CPatchDialog::OnBtnPatchreset() +{ + //CTextureLayout dlg; + //if (dlg.DoModal() == IDOK) + //{ + // Patch_ResetTexturing(dlg.m_fX, dlg.m_fY); + //} + //Sys_UpdateWindows(W_ALL); +} + +void CPatchDialog::OnSelchangeComboCol() +{ + UpdateRowColInfo(); +} + +void CPatchDialog::OnSelchangeComboRow() +{ + UpdateRowColInfo(); +} + +void CPatchDialog::OnSelchangeComboType() +{ + // TODO: Add your control notification handler code here + +} + +void CPatchDialog::OnOK() +{ + m_Patch = NULL; + + CDialog::OnOK(); +} + +void CPatchDialog::OnDeltaposSpin(NMHDR* pNMHDR, LRESULT* pResult) +{ + NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR; + UpdateSpinners((pNMUpDown->iDelta > 0), pNMUpDown->hdr.idFrom); + *pResult = 0; +} + +BOOL CPatchDialog::OnInitDialog() +{ + CDialog::OnInitDialog(); + + m_wndHScale.SetRange(0, 1000); + m_wndVScale.SetRange(0, 1000); + m_wndHShift.SetRange(0, 1000); + m_wndVShift.SetRange(0, 1000); + m_wndRotate.SetRange(0, 1000); + + GetPatchInfo(); + + // TODO: Add extra initialization here + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + + + +void CPatchDialog::GetPatchInfo() +{ + m_Patch = SinglePatchSelected(); + if (m_Patch != NULL) + { + CString str; + int i; + m_wndRows.ResetContent(); + for (i = 0; i < m_Patch->height; i++) + { + str.Format("%i", i); + m_wndRows.AddString(str); + } + m_wndRows.SetCurSel(0); + m_wndCols.ResetContent(); + for (i = 0; i < m_Patch->width; i++) + { + str.Format("%i", i); + m_wndCols.AddString(str); + } + m_wndCols.SetCurSel(0); + } + UpdateRowColInfo(); +} + +void CPatchDialog::SetPatchInfo() +{ + +} + +void DoPatchInspector() +{ + if (g_PatchDialog.GetSafeHwnd() == NULL) + { + g_PatchDialog.Create(IDD_DIALOG_PATCH); + CRect rct; + LONG lSize = sizeof(rct); + if (LoadRegistryInfo("Radiant::PatchWindow", &rct, &lSize)) + { + g_PatchDialog.SetWindowPos(NULL, rct.left, rct.top, 0,0, SWP_NOSIZE); + } + } + g_PatchDialog.ShowWindow(SW_SHOW); + g_PatchDialog.GetPatchInfo(); +} + +void UpdatePatchInspector() +{ + if (g_PatchDialog.GetSafeHwnd() != NULL) + { + g_PatchDialog.UpdateInfo(); + } + +} + +void CPatchDialog::OnDestroy() +{ + if (GetSafeHwnd()) + { + CRect rct; + GetWindowRect(rct); + SaveRegistryInfo("Radiant::PatchWindow", &rct, sizeof(rct)); + } + CDialog::OnDestroy(); +} + +void CPatchDialog::UpdateRowColInfo() +{ + m_fX = m_fY = m_fZ = m_fS = m_fT = 0.0; + + if (m_Patch != NULL) + { + int r = m_wndRows.GetCurSel(); + int c = m_wndCols.GetCurSel(); + if (r >= 0 && r < m_Patch->height && c >= 0 && c < m_Patch->width) + { + m_fX = m_Patch->ctrl(c,r).xyz[0]; + m_fY = m_Patch->ctrl(c,r).xyz[1]; + m_fZ = m_Patch->ctrl(c,r).xyz[2]; + m_fS = m_Patch->ctrl(c,r).st[0]; + m_fT = m_Patch->ctrl(c,r).st[1]; + } + } + UpdateData(FALSE); +} + +void CPatchDialog::UpdateInfo() +{ + GetPatchInfo(); +} + +void CPatchDialog::OnApply() +{ + UpdateData(TRUE); + if (m_Patch != NULL) + { + int r = m_wndRows.GetCurSel(); + int c = m_wndCols.GetCurSel(); + if (r >= 0 && r < m_Patch->height && c >= 0 && c < m_Patch->width) + { + m_Patch->ctrl(c,r).xyz[0] = m_fX; + m_Patch->ctrl(c,r).xyz[1] = m_fY; + m_Patch->ctrl(c,r).xyz[2] = m_fZ; + m_Patch->ctrl(c,r).st[0] = m_fS; + m_Patch->ctrl(c,r).st[1] = m_fT; + Patch_MakeDirty(m_Patch); + Sys_UpdateWindows(W_ALL); + } + } +} + +void CPatchDialog::UpdateSpinners(bool bUp, int nID) +{ + texdef_t td; + + td.rotate = 0.0; + td.scale[0] = td.scale[1] = 0.0; + td.shift[0] = td.shift[1] = 0.0; + td.value = 0; + + + UpdateData(TRUE); + + if (nID == IDC_SPIN_ROTATE) + { + if (bUp) + td.rotate = m_fRotate; + else + td.rotate = -m_fRotate; + } + else if (nID == IDC_SPIN_HSCALE) + { + if (bUp) + td.scale[0] = 1 - m_fHScale; + else + td.scale[0] = 1 + m_fHScale; + } + else if (nID == IDC_SPIN_VSCALE) + { + if (bUp) + td.scale[1] = 1 - m_fVScale; + else + td.scale[1] = 1 + m_fVScale; + } + + else if (nID == IDC_SPIN_HSHIFT) + { + if (bUp) + td.shift[0] = m_fHShift; + else + td.shift[0] = -m_fHShift; + } + else if (nID == IDC_SPIN_VSHIFT) + { + if (bUp) + td.shift[1] = m_fVShift; + else + td.shift[1] = -m_fVShift; + } + + Patch_SetTextureInfo(&td); + Sys_UpdateWindows(W_CAMERA); +} + + diff --git a/src/tools/radiant/PatchDialog.h b/src/tools/radiant/PatchDialog.h new file mode 100644 index 0000000..9e6e5c7 --- /dev/null +++ b/src/tools/radiant/PatchDialog.h @@ -0,0 +1,108 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#if !defined(AFX_PATCHDIALOG_H__DE62DFB4_E9EC_11D2_A509_0020AFEB881A__INCLUDED_) +#define AFX_PATCHDIALOG_H__DE62DFB4_E9EC_11D2_A509_0020AFEB881A__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// PatchDialog.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CPatchDialog dialog + +class CPatchDialog : public CDialog +{ + patchMesh_t *m_Patch; +// Construction +public: + void UpdateInfo(); + void SetPatchInfo(); + void GetPatchInfo(); + CPatchDialog(CWnd* pParent = NULL); // standard constructor + void UpdateSpinners(bool bUp, int nID); + +// Dialog Data + //{{AFX_DATA(CPatchDialog) + enum { IDD = IDD_DIALOG_PATCH }; + CSpinButtonCtrl m_wndVShift; + CSpinButtonCtrl m_wndVScale; + CSpinButtonCtrl m_wndRotate; + CSpinButtonCtrl m_wndHShift; + CSpinButtonCtrl m_wndHScale; + CComboBox m_wndType; + CComboBox m_wndRows; + CComboBox m_wndCols; + CString m_strName; + float m_fS; + float m_fT; + float m_fX; + float m_fY; + float m_fZ; + float m_fHScale; + float m_fHShift; + float m_fRotate; + float m_fVScale; + float m_fVShift; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CPatchDialog) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + void UpdateRowColInfo(); + + // Generated message map functions + //{{AFX_MSG(CPatchDialog) + afx_msg void OnBtnPatchdetails(); + afx_msg void OnBtnPatchfit(); + afx_msg void OnBtnPatchnatural(); + afx_msg void OnBtnPatchreset(); + afx_msg void OnSelchangeComboCol(); + afx_msg void OnSelchangeComboRow(); + afx_msg void OnSelchangeComboType(); + virtual void OnOK(); + afx_msg void OnDeltaposSpin(NMHDR* pNMHDR, LRESULT* pResult); + virtual BOOL OnInitDialog(); + afx_msg void OnDestroy(); + afx_msg void OnApply(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_PATCHDIALOG_H__DE62DFB4_E9EC_11D2_A509_0020AFEB881A__INCLUDED_) diff --git a/src/tools/radiant/PointFile.cpp b/src/tools/radiant/PointFile.cpp new file mode 100644 index 0000000..a368a22 --- /dev/null +++ b/src/tools/radiant/PointFile.cpp @@ -0,0 +1,163 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" + +#define MAX_POINTFILE 8192 +static idVec3 s_pointvecs[MAX_POINTFILE]; +static int s_num_points, s_check_point; + +void Pointfile_Delete (void) +{ + char name[1024]; + + strcpy (name, currentmap); + StripExtension (name); + strcat (name, ".lin"); + + remove(name); +} + +// advance camera to next point +void Pointfile_Next (void) +{ + idVec3 dir; + + if (s_check_point >= s_num_points-2) + { + Sys_Status ("End of pointfile", 0); + return; + } + s_check_point++; + VectorCopy (s_pointvecs[s_check_point], g_pParentWnd->GetCamera()->Camera().origin); + VectorCopy (s_pointvecs[s_check_point], g_pParentWnd->GetXYWnd()->GetOrigin()); + VectorSubtract (s_pointvecs[s_check_point+1], g_pParentWnd->GetCamera()->Camera().origin, dir); + dir.Normalize(); + g_pParentWnd->GetCamera()->Camera().angles[1] = atan2 (dir[1], dir[0])*180/3.14159; + g_pParentWnd->GetCamera()->Camera().angles[0] = asin (dir[2])*180/3.14159; + + Sys_UpdateWindows (W_ALL); +} + +// advance camera to previous point +void Pointfile_Prev (void) +{ + idVec3 dir; + + if ( s_check_point == 0) + { + Sys_Status ("Start of pointfile", 0); + return; + } + s_check_point--; + VectorCopy (s_pointvecs[s_check_point], g_pParentWnd->GetCamera()->Camera().origin); + VectorCopy (s_pointvecs[s_check_point], g_pParentWnd->GetXYWnd()->GetOrigin()); + VectorSubtract (s_pointvecs[s_check_point+1], g_pParentWnd->GetCamera()->Camera().origin, dir); + dir.Normalize(); + g_pParentWnd->GetCamera()->Camera().angles[1] = atan2 (dir[1], dir[0])*180/3.14159; + g_pParentWnd->GetCamera()->Camera().angles[0] = asin (dir[2])*180/3.14159; + + Sys_UpdateWindows (W_ALL); +} + +void WINAPI Pointfile_Check (void) +{ + char name[1024]; + FILE *f; + idVec3 v; + + strcpy (name, currentmap); + StripExtension (name); + strcat (name, ".lin"); + + f = fopen (name, "r"); + if (!f) + return; + + common->Printf ("Reading pointfile %s\n", name); + + if (!g_qeglobals.d_pointfile_display_list) + g_qeglobals.d_pointfile_display_list = qglGenLists(1); + + s_num_points = 0; + qglNewList (g_qeglobals.d_pointfile_display_list, GL_COMPILE); + qglColor3f (1, 0, 0); + qglDisable(GL_TEXTURE_2D); + qglDisable(GL_TEXTURE_1D); + qglLineWidth (2); + qglBegin(GL_LINE_STRIP); + do + { + if (fscanf (f, "%f %f %f\n", &v[0], &v[1], &v[2]) != 3) + break; + if (s_num_points < MAX_POINTFILE) + { + VectorCopy (v, s_pointvecs[s_num_points]); + s_num_points++; + } + qglVertex3fv( v.ToFloatPtr() ); + } while (1); + qglEnd(); + qglLineWidth (0.5); + qglEndList (); + + s_check_point = 0; + fclose (f); + //Pointfile_Next (); +} + +void Pointfile_Draw( void ) +{ + int i; + + qglColor3f( 1.0F, 0.0F, 0.0F ); + qglDisable(GL_TEXTURE_2D); + qglDisable(GL_TEXTURE_1D); + qglLineWidth (2); + qglBegin(GL_LINE_STRIP); + for ( i = 0; i < s_num_points; i++ ) + { + qglVertex3fv( s_pointvecs[i].ToFloatPtr() ); + } + qglEnd(); + qglLineWidth( 0.5 ); +} + +void Pointfile_Clear (void) +{ + if (!g_qeglobals.d_pointfile_display_list) + return; + + qglDeleteLists (g_qeglobals.d_pointfile_display_list, 1); + g_qeglobals.d_pointfile_display_list = 0; + Sys_UpdateWindows (W_ALL); +} + diff --git a/src/tools/radiant/PrefsDlg.cpp b/src/tools/radiant/PrefsDlg.cpp new file mode 100644 index 0000000..522e7c9 --- /dev/null +++ b/src/tools/radiant/PrefsDlg.cpp @@ -0,0 +1,452 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "shlobj.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +#define MOUSE_KEY "radiant_MouseButtons" +#define TLOCK_KEY "radiant_TextureLock" +#define RLOCK_KEY "radiant_RotateLock" +#define LOADLAST_KEY "radiant_LoadLast" +#define LOADLASTMAP_KEY "radiant_LoadLastMap" +#define LASTPROJ_KEY "radiant_LastProject" +#define LASTMAP_KEY "radiant_LastMap" +#define RUN_KEY "radiant_RunBefore" +#define FACE_KEY "radiant_NewFaceGrab" +#define BSP_KEY "radiant_InternalBSP" +#define RCLICK_KEY "radiant_NewRightClick" +#define VERTEX_KEY "radiant_NewVertex" +#define AUTOSAVE_KEY "radiant_Autosave" +#define AUTOSAVETIME_KEY "radiant_AutosaveMinutes" +#define PAK_KEY "radiant_UsePAK" +#define NEWAPPLY_KEY "radiant_ApplyDismissesSurface" +#define HACK_KEY "radiant_Gatewayescapehack" +#define TEXTURE_KEY "radiant_NewTextureWindowStuff" +#define TINYBRUSH_KEY "radiant_CleanTinyBrushes" +#define TINYSIZE_KEY "radiant_CleanTinyBrusheSize" +#define SNAPSHOT_KEY "radiant_Snapshots" +#define PAKFILE_KEY "radiant_PAKFile" +#define STATUS_KEY "radiant_StatusPointSize" +#define MOVESPEED_KEY "radiant_MoveSpeed" +#define ANGLESPEED_KEY "radiant_AngleSpeed" +#define SETGAME_KEY "radiant_UseSetGame" +#define CAMXYUPDATE_KEY "radiant_CamXYUpdate" +#define LIGHTDRAW_KEY "radiant_NewLightStyle" +#define WHATGAME_KEY "radiant_WhichGame" +#define CUBICCLIP_KEY "radiant_CubicClipping" +#define CUBICSCALE_KEY "radiant_CubicScale" +#define ALTEDGE_KEY "radiant_ALTEdgeDrag" +#define FACECOLORS_KEY "radiant_FaceColors" +#define QE4PAINT_KEY "radiant_QE4Paint" +#define SNAPT_KEY "radiant_SnapT" +#define XZVIS_KEY "radiant_XZVIS" +#define YZVIS_KEY "radiant_YZVIS" +#define ZVIS_KEY "radiant_ZVIS" +#define SIZEPAINT_KEY "radiant_SizePainting" +#define DLLENTITIES_KEY "radiant_DLLEntities" +#define WIDETOOLBAR_KEY "radiant_WideToolBar" +#define NOCLAMP_KEY "radiant_NoClamp" +#define PREFAB_KEY "radiant_PrefabPath" +#define USERINI_KEY "radiant_UserINIPath" +#define ROTATION_KEY "radiant_Rotation" +#define SGIOPENGL_KEY "radiant_SGIOpenGL" +#define BUGGYICD_KEY "radiant_BuggyICD" +#define HICOLOR_KEY "radiant_HiColorTextures" +#define CHASEMOUSE_KEY "radiant_ChaseMouse" +#define ENTITYSHOW_KEY "radiant_EntityShow" +#define TEXTURESCALE_KEY "radiant_TextureScale" +#define TEXTURESCROLLBAR_KEY "radiant_TextureScrollbar" +#define DISPLAYLISTS_KEY "radiant_UseDisplayLists" +#define NORMALIZECOLORS_KEY "radiant_NormalizeColors" +#define SHADERS_KEY "radiant_UseShaders" +#define SWITCHCLIP_KEY "radiant_SwitchClipKey" +#define SELWHOLEENTS_KEY "radiant_SelectWholeEntitiesKey" +#define TEXTURESUBSET_KEY "radiant_UseTextureSubsetLoading" +#define TEXTUREQUALITY_KEY "radiant_TextureQuality" +#define SHOWSHADERS_KEY "radiant_ShowShaders" +#define SHADERTEST_KEY "radiant_ShaderTest" +#define GLLIGHTING_KEY "radiant_UseGLLighting" +#define NOSTIPPLE_KEY "radiant_NoStipple" +#define UNDOLEVELS_KEY "radiant_UndoLevels" +#define MAPS_KEY "radiant_RadiantMapPath" +#define MODELS_KEY "radiant_ModelPath" +#define NEWMAPFORMAT_KEY "radiant_NewMapFormat" + +#define WINDOW_DEF 0 +#define TLOCK_DEF 1 +#define LOADLAST_DEF 1 +#define RUN_DEF 0 + +///////////////////////////////////////////////////////////////////////////// +// CPrefsDlg dialog + + +CPrefsDlg::CPrefsDlg(CWnd* pParent /*=NULL*/) + : CDialog(CPrefsDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(CPrefsDlg) + m_bLoadLast = FALSE; + m_bFace = FALSE; + m_bRightClick = FALSE; + m_bVertex = FALSE; + m_bAutoSave = TRUE; + m_bNewApplyHandling = FALSE; + m_strAutoSave = _T("5"); + m_bLoadLastMap = FALSE; + m_bTextureWindow = FALSE; + m_bSnapShots = FALSE; + m_fTinySize = 0.5; + m_bCleanTiny = FALSE; + m_nStatusSize = 10; + m_bCamXYUpdate = FALSE; + m_bNewLightDraw = FALSE; + m_bALTEdge = FALSE; + m_bQE4Painting = TRUE; + m_bSnapTToGrid = FALSE; + m_bXZVis = FALSE; + m_bYZVis = FALSE; + m_bZVis = FALSE; + m_bSizePaint = FALSE; + m_bWideToolbar = TRUE; + m_bNoClamp = FALSE; + m_nRotation = 0; + m_bHiColorTextures = TRUE; + m_bChaseMouse = FALSE; + m_bTextureScrollbar = TRUE; + m_bDisplayLists = TRUE; + m_bNoStipple = FALSE; + m_strMaps = _T(""); + m_strModels = _T(""); + m_bNewMapFormat = TRUE; + //}}AFX_DATA_INIT + //LoadPrefs(); + m_selectByBoundingBrush = FALSE; + m_selectOnlyBrushes = FALSE; + m_selectNoModels = FALSE; + m_nEntityShowState = 0; + m_nTextureScale = 2; + m_bSwitchClip = FALSE; + m_bSelectWholeEntities = TRUE; + m_nTextureQuality = 3; + m_bGLLighting = FALSE; + m_nUndoLevels = 63; +} + +void CPrefsDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CPrefsDlg) + DDX_Control(pDX, IDC_SPIN_UNDO, m_wndUndoSpin); + DDX_Control(pDX, IDC_SPIN_POINTSIZE, m_wndFontSpin); + DDX_Control(pDX, IDC_SLIDER_TEXTUREQUALITY, m_wndTexturequality); + DDX_Control(pDX, IDC_SLIDER_CAMSPEED, m_wndCamSpeed); + DDX_Control(pDX, IDC_SPIN_AUTOSAVE, m_wndSpin); + DDX_Check(pDX, IDC_CHECK_LOADLAST, m_bLoadLast); + DDX_Check(pDX, IDC_CHECK_FACE, m_bFace); + DDX_Check(pDX, IDC_CHECK_RIGHTCLICK, m_bRightClick); + DDX_Check(pDX, IDC_CHECK_AUTOSAVE, m_bAutoSave); + DDX_Text(pDX, IDC_EDIT_AUTOSAVE, m_strAutoSave); + DDX_Check(pDX, IDC_CHECK_LOADLASTMAP, m_bLoadLastMap); + DDX_Check(pDX, IDC_CHECK_TEXTUREWINDOW, m_bTextureWindow); + DDX_Check(pDX, IDC_CHECK_SNAPSHOTS, m_bSnapShots); + DDX_Text(pDX, IDC_EDIT_STATUSPOINTSIZE, m_nStatusSize); + DDV_MinMaxInt(pDX, m_nStatusSize, 2, 14); + DDX_Check(pDX, IDC_CHECK_CAMXYUPDATE, m_bCamXYUpdate); + DDX_Check(pDX, IDC_CHECK_LIGHTDRAW, m_bNewLightDraw); + DDX_Check(pDX, IDC_CHECK_ALTDRAG, m_bALTEdge); + DDX_Check(pDX, IDC_CHECK_QE4PAINTING, m_bQE4Painting); + DDX_Check(pDX, IDC_CHECK_SNAPT, m_bSnapTToGrid); + DDX_Check(pDX, IDC_CHECK_SIZEPAINT, m_bSizePaint); + DDX_Check(pDX, IDC_CHECK_WIDETOOLBAR, m_bWideToolbar); + DDX_Check(pDX, IDC_CHECK_NOCLAMP, m_bNoClamp); + DDX_Text(pDX, IDC_EDIT_ROTATION, m_nRotation); + DDX_Check(pDX, IDC_CHECK_HICOLOR, m_bHiColorTextures); + DDX_Check(pDX, IDC_CHECK_MOUSECHASE, m_bChaseMouse); + DDX_Check(pDX, IDC_CHECK_TEXTURESCROLLBAR, m_bTextureScrollbar); + DDX_Check(pDX, IDC_CHECK_DISPLAYLISTS, m_bDisplayLists); + DDX_Check(pDX, IDC_CHECK_NOSTIPPLE, m_bNoStipple); + DDX_Text(pDX, IDC_EDIT_UNDOLEVELS, m_nUndoLevels); + DDV_MinMaxInt(pDX, m_nUndoLevels, 1, 64); + DDX_Text(pDX, IDC_EDIT_MAPS, m_strMaps); + DDX_Check(pDX, IDC_CHECK_NEWMAPFORMAT, m_bNewMapFormat); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CPrefsDlg, CDialog) + //{{AFX_MSG_MAP(CPrefsDlg) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CPrefsDlg message handlers + +BOOL CPrefsDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + m_wndSpin.SetRange(1,60); + m_wndCamSpeed.SetRange(10, 5000); + m_wndCamSpeed.SetPos(m_nMoveSpeed); + + this->m_wndTexturequality.SetRange(0, 3); + this->m_wndTexturequality.SetPos(m_nTextureQuality); + + m_wndFontSpin.SetRange(4,24); + m_wndUndoSpin.SetRange(1,64); + + GetDlgItem(IDC_CHECK_HICOLOR)->EnableWindow(TRUE); + GetDlgItem(IDC_CHECK_NOCLAMP)->EnableWindow(TRUE); + + //GetDlgItem(IDC_CHECK_NOCLAMP)->EnableWindow(FALSE); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void CPrefsDlg::OnOK() +{ + m_nMoveSpeed = m_wndCamSpeed.GetPos(); + m_nAngleSpeed = (float)m_nMoveSpeed * 0.50; + this->m_nTextureQuality = m_wndTexturequality.GetPos(); + SavePrefs(); + + if ( g_pParentWnd ) { + g_pParentWnd->SetGridStatus(); + } + Sys_UpdateWindows(W_ALL); + Undo_SetMaxSize(m_nUndoLevels); + CDialog::OnOK(); +} + +int GetCvarInt(const char *name, const int def) { + idCVar *cvar = cvarSystem->Find( name ); + if ( cvar ) { + return cvar->GetInteger(); + } else { + return def; + } +} + +const char *GetCvarString( const char *name, const char *def ) { + idCVar *cvar = cvarSystem->Find( name ); + if ( cvar ) { + return cvar->GetString(); + } else { + return def; + } +} + +static const char hexDigits[] = "0123456789ABCDEF"; + +void SetCvarInt( const char *name, const int value ) { + cvarSystem->SetCVarInteger( name, value, CVAR_TOOL ); +} + +void SetCvarString( const char *name, const char *value ) { + cvarSystem->SetCVarString( name, value, CVAR_TOOL ); +} + +void SetCvarBinary(const char *name, void *pv, int size) { + unsigned char *in = new unsigned char[size]; + idStr s; + memset( in, 0, size ); + memcpy( in, pv, size ); + for ( int i = 0; i < size; i++ ) { + s += hexDigits[in[i] >> 4]; + s += hexDigits[in[i] & 0x0f]; + } + delete []in; + SetCvarString(name, s); +} + +bool GetCvarBinary( const char *name, void *pv, int size ) { + bool ret = false; + unsigned char *out = new unsigned char[size]; + idStr s = GetCvarString( name, "" ); + if ( s.Length() / 2 == size ) { + int j = 0; + for ( int i = 0; i < s.Length(); i += 2 ) { + char c; + if (s[i] > '9') { + c = s[i] - 'A' + 0x0a; + } else { + c = s[i] - 0x30; + } + c <<= 4; + if (s[i+1] > '9') { + c |= s[i+1] - 'A' + 0x0a; + } else { + c |= s[i+1] - 0x30; + } + out[j++] = c; + } + memcpy(pv, out, size); + ret = true; + } + delete []out; + return ret; +} + +void CPrefsDlg::LoadPrefs() { + CString strBuff; + CString strPrefab = g_strAppPath; + AddSlash(strPrefab); + strPrefab += "Prefabs\\"; + + m_nMouseButtons = 3; + + m_bTextureLock = GetCvarInt( TLOCK_KEY, TLOCK_DEF ); + m_bRotateLock = GetCvarInt( RLOCK_KEY, TLOCK_DEF ); + m_strLastProject = GetCvarString( LASTPROJ_KEY, "" ); + m_strLastMap = GetCvarString( LASTMAP_KEY, "" ); + m_bLoadLast = GetCvarInt( LOADLAST_KEY, LOADLAST_DEF ); + m_bRunBefore = GetCvarInt( RUN_KEY, RUN_DEF ); + m_bFace = GetCvarInt( FACE_KEY, 1 ); + m_bRightClick = GetCvarInt( RCLICK_KEY, 1 ); + m_bVertex = GetCvarInt( VERTEX_KEY, 1 ); + m_bAutoSave = GetCvarInt( AUTOSAVE_KEY, 1 ); + m_bNewApplyHandling = GetCvarInt( NEWAPPLY_KEY, 0 ); + m_bLoadLastMap = GetCvarInt( LOADLASTMAP_KEY, 0 ); + m_bGatewayHack = GetCvarInt( HACK_KEY, 0 ); + m_bTextureWindow = GetCvarInt( TEXTURE_KEY, 0 ); + m_bCleanTiny = GetCvarInt( TINYBRUSH_KEY, 0 ); + strBuff = GetCvarString( TINYSIZE_KEY, "0.5" ); + m_fTinySize = atof(strBuff ); + m_nAutoSave = GetCvarInt( AUTOSAVETIME_KEY, 5 ); + if ( m_nAutoSave <= 0 ) { m_nAutoSave = 1; } + m_strAutoSave.Format("%i", m_nAutoSave ); + m_bSnapShots = GetCvarInt( SNAPSHOT_KEY, 0 ); + m_nStatusSize = GetCvarInt( STATUS_KEY, 10 ); + m_nMoveSpeed = GetCvarInt( MOVESPEED_KEY, 400 ); + m_nAngleSpeed = GetCvarInt( ANGLESPEED_KEY, 300 ); + m_bCamXYUpdate = GetCvarInt( CAMXYUPDATE_KEY, 1 ); + m_bNewLightDraw = GetCvarInt( LIGHTDRAW_KEY, 1 ); + m_bCubicClipping = ( GetCvarInt( CUBICCLIP_KEY, 1) != 0 ); + m_nCubicScale = GetCvarInt( CUBICSCALE_KEY, 13 ); + m_bALTEdge = GetCvarInt( ALTEDGE_KEY, 0 ); + m_bQE4Painting = GetCvarInt( QE4PAINT_KEY, 1 ); + m_bSnapTToGrid = GetCvarInt( SNAPT_KEY, 0 ); + m_bXZVis = GetCvarInt( XZVIS_KEY, 0 ); + m_bYZVis = GetCvarInt( YZVIS_KEY, 0 ); + m_bZVis = GetCvarInt( ZVIS_KEY, 1 ); + m_bSizePaint = GetCvarInt( SIZEPAINT_KEY, 0 ); + m_bWideToolbar = GetCvarInt( WIDETOOLBAR_KEY, 1 ); + m_bNoClamp = GetCvarInt( NOCLAMP_KEY, 0 ); + m_nRotation = GetCvarInt( ROTATION_KEY, 45 ); + m_bHiColorTextures = GetCvarInt( HICOLOR_KEY, 1 ); + m_bChaseMouse = GetCvarInt( CHASEMOUSE_KEY, 1 ); + m_nEntityShowState = GetCvarInt( ENTITYSHOW_KEY, 0 ); + m_nTextureScale = GetCvarInt( TEXTURESCALE_KEY, 50 ); + m_bTextureScrollbar = GetCvarInt( TEXTURESCROLLBAR_KEY, TRUE ); + m_bDisplayLists = GetCvarInt( DISPLAYLISTS_KEY, TRUE ); + m_bSwitchClip = GetCvarInt( SWITCHCLIP_KEY, TRUE ); + m_bSelectWholeEntities = GetCvarInt( SELWHOLEENTS_KEY, TRUE ); + m_nTextureQuality = GetCvarInt( TEXTUREQUALITY_KEY, 6 ); + m_bGLLighting = GetCvarInt( GLLIGHTING_KEY, FALSE ); + m_bNoStipple = GetCvarInt( NOSTIPPLE_KEY, 0 ); + m_nUndoLevels = GetCvarInt( UNDOLEVELS_KEY, 63 ); + m_strMaps = GetCvarString( MAPS_KEY, "" ); + m_strModels = GetCvarString( MODELS_KEY, "" ); + m_bNoStipple = GetCvarInt( NEWMAPFORMAT_KEY, 1 ); + + if ( m_bRunBefore == FALSE ) { + SetGamePrefs(); + } +} + +void CPrefsDlg::SavePrefs() { + if ( GetSafeHwnd() ) { + UpdateData(TRUE); + } + + m_nMouseButtons = 3; + + SetCvarInt( TLOCK_KEY, m_bTextureLock ); + SetCvarInt( RLOCK_KEY, m_bRotateLock ); + SetCvarInt( LOADLAST_KEY, m_bLoadLast ); + SetCvarString( LASTPROJ_KEY, m_strLastProject ); + SetCvarString( LASTMAP_KEY, m_strLastMap ); + SetCvarInt( RUN_KEY, m_bRunBefore ); + SetCvarInt( FACE_KEY, m_bFace ); + SetCvarInt( RCLICK_KEY, m_bRightClick ); + SetCvarInt( VERTEX_KEY, m_bVertex ); + SetCvarInt( AUTOSAVE_KEY, m_bAutoSave ); + SetCvarInt( LOADLASTMAP_KEY, m_bLoadLastMap ); + SetCvarInt( TEXTURE_KEY, m_bTextureWindow ); + m_nAutoSave = atoi( m_strAutoSave ); + SetCvarInt( AUTOSAVETIME_KEY, m_nAutoSave ); + SetCvarInt( SNAPSHOT_KEY, m_bSnapShots ); + SetCvarInt( STATUS_KEY, m_nStatusSize ); + SetCvarInt( CAMXYUPDATE_KEY, m_bCamXYUpdate ); + SetCvarInt( LIGHTDRAW_KEY, m_bNewLightDraw ); + SetCvarInt( MOVESPEED_KEY, m_nMoveSpeed ); + SetCvarInt( ANGLESPEED_KEY, m_nAngleSpeed ); + SetCvarInt( CUBICCLIP_KEY, m_bCubicClipping ); + SetCvarInt( CUBICSCALE_KEY, m_nCubicScale ); + SetCvarInt( ALTEDGE_KEY, m_bALTEdge ); + SetCvarInt( QE4PAINT_KEY, m_bQE4Painting ); + SetCvarInt( SNAPT_KEY, m_bSnapTToGrid ); + SetCvarInt( XZVIS_KEY, m_bXZVis ); + SetCvarInt( YZVIS_KEY, m_bYZVis ); + SetCvarInt( ZVIS_KEY, m_bZVis ); + SetCvarInt( SIZEPAINT_KEY, m_bSizePaint ); + SetCvarInt( WIDETOOLBAR_KEY, m_bWideToolbar ); + SetCvarInt( NOCLAMP_KEY, m_bNoClamp ); + SetCvarInt( ROTATION_KEY, m_nRotation ); + SetCvarInt( HICOLOR_KEY, m_bHiColorTextures ); + SetCvarInt( CHASEMOUSE_KEY, m_bChaseMouse ); + SetCvarInt( ENTITYSHOW_KEY, m_nEntityShowState ); + SetCvarInt( TEXTURESCALE_KEY, m_nTextureScale ); + SetCvarInt( TEXTURESCROLLBAR_KEY, m_bTextureScrollbar ); + SetCvarInt( DISPLAYLISTS_KEY, m_bDisplayLists ); + SetCvarInt( SWITCHCLIP_KEY, m_bSwitchClip ); + SetCvarInt( SELWHOLEENTS_KEY, m_bSelectWholeEntities ); + SetCvarInt( TEXTUREQUALITY_KEY, m_nTextureQuality ); + SetCvarInt( GLLIGHTING_KEY, m_bGLLighting ); + SetCvarInt( NOSTIPPLE_KEY, m_bNoStipple ); + SetCvarInt( UNDOLEVELS_KEY, m_nUndoLevels ); + SetCvarString( MAPS_KEY, m_strMaps ); + SetCvarString( MODELS_KEY, m_strModels ); + SetCvarInt( NEWMAPFORMAT_KEY, m_bNewMapFormat ); + common->WriteFlaggedCVarsToFile( "editor.cfg", CVAR_TOOL, "sett" ); +} + +void CPrefsDlg::SetGamePrefs() { + m_bHiColorTextures = TRUE; + m_bWideToolbar = TRUE; + SavePrefs(); +} diff --git a/src/tools/radiant/PrefsDlg.h b/src/tools/radiant/PrefsDlg.h new file mode 100644 index 0000000..f567050 --- /dev/null +++ b/src/tools/radiant/PrefsDlg.h @@ -0,0 +1,138 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef __PREFSDLG_H__ +#define __PREFSDLG_H__ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + +///////////////////////////////////////////////////////////////////////////// +// CPrefsDlg dialog + +#define MAX_TEXTURE_QUALITY 3 + +class CPrefsDlg : public CDialog +{ +// Construction +public: + CPrefsDlg(CWnd* pParent = NULL); // standard constructor + + void LoadPrefs(); + void SavePrefs(); + void SetGamePrefs(); + +// Dialog Data + //{{AFX_DATA(CPrefsDlg) + enum { IDD = IDD_DLG_PREFS }; + + CSpinButtonCtrl m_wndUndoSpin; + CSpinButtonCtrl m_wndFontSpin; + CSliderCtrl m_wndTexturequality; + CSliderCtrl m_wndCamSpeed; + CSpinButtonCtrl m_wndSpin; + BOOL m_bTextureLock; + BOOL m_bLoadLast; + BOOL m_bRunBefore; + CString m_strLastProject; + CString m_strLastMap; + BOOL m_bFace; + BOOL m_bRightClick; + BOOL m_bVertex; + BOOL m_bAutoSave; + BOOL m_bNewApplyHandling; + CString m_strAutoSave; + BOOL m_bLoadLastMap; + BOOL m_bGatewayHack; + BOOL m_bTextureWindow; + BOOL m_bSnapShots; + float m_fTinySize; + BOOL m_bCleanTiny; + int m_nStatusSize; + BOOL m_bCamXYUpdate; + BOOL m_bNewLightDraw; + BOOL m_bALTEdge; + BOOL m_bQE4Painting; + BOOL m_bSnapTToGrid; + BOOL m_bXZVis; + BOOL m_bYZVis; + BOOL m_bZVis; + BOOL m_bSizePaint; + BOOL m_bRotateLock; + BOOL m_bWideToolbar; + BOOL m_bNoClamp; + int m_nRotation; + BOOL m_bHiColorTextures; + BOOL m_bChaseMouse; + BOOL m_bTextureScrollbar; + BOOL m_bDisplayLists; + BOOL m_bNoStipple; + int m_nUndoLevels; + CString m_strMaps; + CString m_strModels; + BOOL m_bNewMapFormat; + //}}AFX_DATA + int m_nMouseButtons; + int m_nAngleSpeed; + int m_nMoveSpeed; + int m_nAutoSave; + bool m_bCubicClipping; + int m_nCubicScale; + BOOL m_selectOnlyBrushes; + BOOL m_selectNoModels; + BOOL m_selectByBoundingBrush; + int m_nEntityShowState; + int m_nTextureScale; + BOOL m_bNormalizeColors; + BOOL m_bSwitchClip; + BOOL m_bSelectWholeEntities; + int m_nTextureQuality; + BOOL m_bGLLighting; + + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CPrefsDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +protected: + // Generated message map functions + //{{AFX_MSG(CPrefsDlg) + afx_msg void OnBtnBrowse(); + virtual BOOL OnInitDialog(); + virtual void OnOK(); + afx_msg void OnBtnBrowsepak(); + afx_msg void OnBtnBrowseprefab(); + afx_msg void OnBtnBrowseuserini(); + afx_msg void OnSelchangeComboWhatgame(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +#endif /* !__PREFSDLG_H__ */ diff --git a/src/tools/radiant/PreviewDlg.cpp b/src/tools/radiant/PreviewDlg.cpp new file mode 100644 index 0000000..14f68f1 --- /dev/null +++ b/src/tools/radiant/PreviewDlg.cpp @@ -0,0 +1,655 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" +#include "WaitDlg.h" +#include "PreviewDlg.h" +#include "CommentsDlg.h" + +const int PARENTID = 99999; + +extern HTREEITEM FindTreeItem(CTreeCtrl *tree, HTREEITEM root, const char *text, HTREEITEM forceParent); + +// CPreviewDlg dialog + +IMPLEMENT_DYNAMIC(CPreviewDlg, CDialog) +CPreviewDlg::CPreviewDlg(CWnd* pParent /*=NULL*/) + : CDialog(CPreviewDlg::IDD, pParent) +{ + currentMode = MODELS; + disablePreview = false; +} + +CPreviewDlg::~CPreviewDlg() +{ +} + +void CPreviewDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + DDX_Control(pDX, IDC_TREE_MEDIA, treeMedia); + DDX_Control(pDX, IDC_EDIT_INFO, editInfo); + DDX_Control(pDX, IDC_PREVIEW, wndPreview); +} + + +BEGIN_MESSAGE_MAP(CPreviewDlg, CDialog) + ON_NOTIFY(TVN_SELCHANGED, IDC_TREE_MEDIA, OnTvnSelchangedTreeMedia) + ON_BN_CLICKED(IDC_BUTTON_RELOAD, OnBnClickedButtonReload) + ON_BN_CLICKED(IDC_BUTTON_ADD, OnBnClickedButtonAdd) + ON_BN_CLICKED(IDC_BUTTON_PLAY, OnBnClickedButtonPlay) +END_MESSAGE_MAP() + + +// CPreviewDlg message handlers + +BOOL CPreviewDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + + m_image.Create(IDB_BITMAP_MATERIAL, 16, 1, RGB(255, 255, 255)); + treeMedia.SetImageList(&m_image, TVSIL_NORMAL); + if ( disablePreview ) { + wndPreview.ShowWindow( SW_HIDE ); + } else { + wndPreview.setDrawable(&m_testDrawable); + } + + SetMode(currentMode); + BuildTree(); + + if ( mediaName.Length() ) { + HTREEITEM root = treeMedia.GetRootItem(); + HTREEITEM sel = FindTreeItem(&treeMedia, root, mediaName, NULL ); + if (sel) { + treeMedia.SelectItem(sel); + } + } + mediaName = ""; + return TRUE; // return TRUE unless you set the focus to a control + +} + +void CPreviewDlg::BuildTree() { + + CWaitCursor cursor; + quickTree.Clear(); + treeMedia.DeleteAllItems(); + + idFileList *files; + + if ( currentMode == GUIS ) { + files = fileSystem->ListFilesTree( "guis", ".gui" ); + AddStrList( "base", files->GetList(), GUIS ); + fileSystem->FreeFileList( files ); + } else if ( currentMode == MODELS ) { + files = fileSystem->ListFilesTree( "models", ".lwo" ); + AddStrList( "base", files->GetList(), MODELS ); + fileSystem->FreeFileList( files ); + files = fileSystem->ListFilesTree( "models", ".ase" ); + AddStrList( "base", files->GetList(), MODELS ); + fileSystem->FreeFileList( files ); + files = fileSystem->ListFilesTree( "models", ".ma" ); + AddStrList( "base", files->GetList(), MODELS ); + fileSystem->FreeFileList( files ); + } else if ( currentMode == SOUNDS ) { + AddSounds( true ); + } else if ( currentMode == MATERIALS ) { + AddMaterials( true ); + } else if ( currentMode == PARTICLES ) { + AddParticles( true ); + } else if ( currentMode == SKINS ) { + AddSkins( true ); + } +} + +void CPreviewDlg::RebuildTree( const char *_data ) { + data = _data; + data.ToLower(); + BuildTree(); +} + +void CPreviewDlg::AddCommentedItems() { + const char *buffer = NULL; + const char *path; + items.Clear(); + path = (currentMode == GUIS) ? "guis/guis.commented" : "models/models.commented"; + idParser src( LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_ALLOWBACKSLASHSTRINGCONCAT ); + if (fileSystem->ReadFile(path, (void**)&buffer, NULL) && buffer) { + src.LoadMemory(buffer, strlen(buffer), path); + if (src.IsLoaded()) { + idToken token, tok1, tok2, tok3; + while( src.ReadToken( &token ) ) { + if (token == "{") { + // start a new commented item + CommentedItem ci; + if (src.ReadToken(&tok1) && src.ReadToken(&tok2) && src.ReadToken(&tok3)) { + ci.Name = tok1; + ci.Path = tok2; + ci.Comments = tok3; + items.Append(ci); + } + } + } + } + fileSystem->FreeFile((void*)buffer); + } + commentItem = treeMedia.InsertItem("Commented"); + int c = items.Num(); + if (c) { + for (int i = 0; i < c; i++) { + HTREEITEM child = treeMedia.InsertItem(items[i].Name, commentItem); + treeMedia.SetItemData(child, -1 - i); + treeMedia.SetItemImage(child, 2, 2); + } + } +} + + + +void CPreviewDlg::AddStrList( const char *root, const idStrList &list, int id ) { + idStr out, path; + HTREEITEM base = treeMedia.GetRootItem(); + if (base) { + out = treeMedia.GetItemText(base); + if (stricmp(root, out)) { + base = NULL; + } + } + + if (base == NULL) { + base = treeMedia.InsertItem(root); + treeMedia.SetItemData(base, PARENTID); + } + + HTREEITEM item = base; + HTREEITEM add; + + int count = list.Num(); + + idStr last, qt; + for (int i = 0; i < count; i++) { + idStr name = list[i]; + + // now break the name down convert to slashes + name.BackSlashesToSlashes(); + name.Strip(' '); + + int index; + int len = last.Length(); + if (len == 0) { + index = name.Last('/'); + if (index >= 0) { + name.Left(index, last); + } + } + else if (idStr::Icmpn(last, name, len) == 0 && name.Last('/') <= len) { + name.Right(name.Length() - len - 1, out); + add = treeMedia.InsertItem(out, item); + qt = root; + qt += "/"; + qt += name; + quickTree.Set(qt, add); + treeMedia.SetItemImage(add, 2, 2); + treeMedia.SetItemData(add, id); + continue; + } + else { + last.Empty(); + } + + index = 0; + item = base; + path = ""; + while (index >= 0) { + index = name.Find('/'); + if (index >= 0) { + HTREEITEM newItem = NULL; + HTREEITEM *check = NULL; + name.Left( index, out ); + path += out; + qt = root; + qt += "/"; + qt += path; + if (quickTree.Get(qt, &check)) { + newItem = *check; + } + //HTREEITEM newItem = FindTreeItem(&treeMedia, item, name.Left(index, out), item); + if (newItem == NULL) { + newItem = treeMedia.InsertItem(out, item); + qt = root; + qt += "/"; + qt += path; + quickTree.Set(qt, newItem); + treeMedia.SetItemImage(newItem, 0, 1); + treeMedia.SetItemData(newItem, PARENTID); + } + + assert(newItem); + item = newItem; + name.Right(name.Length() - index - 1, out); + name = out; + path += "/"; + } + else { + add = treeMedia.InsertItem(name, item); + qt = root; + qt += "/"; + qt += path; + qt += name; + quickTree.Set(qt, add); + treeMedia.SetItemImage(add, 2, 2); + treeMedia.SetItemData(add, id); + path = ""; + } + } + } + +} + +void CPreviewDlg::OnTvnSelchangedTreeMedia(NMHDR *pNMHDR, LRESULT *pResult) +{ + LPNMTREEVIEW pNMTreeView = reinterpret_cast(pNMHDR); + HTREEITEM item = treeMedia.GetSelectedItem(); + mediaName = ""; + CWnd *add = GetDlgItem(IDC_BUTTON_ADD); + if (add) { + add->EnableWindow(treeMedia.GetItemData(item) == GUIS || treeMedia.GetItemData(item) == MODELS); + } + if (item) { + + editInfo.SetWindowText("No comments for this item"); + int id = treeMedia.GetItemData(item); + if ( id == GUIS || id == MODELS || id == MATERIALS || id == WAVES || id == PARTICLES || id == SKINS ) { + mediaName = treeMedia.GetItemText( item ); + + // have to build the name back up + HTREEITEM parent = treeMedia.GetParentItem( item ); + while ( parent != NULL ) { + idStr strParent = treeMedia.GetItemText( parent ); + strParent += "/"; + strParent += mediaName; + mediaName = strParent; + parent = treeMedia.GetParentItem( parent ); + } + // strip the leading "base/" + if (id == MATERIALS) { + mediaName.Strip("Materials/"); + } else if (id == WAVES) { + mediaName.Strip( "Wave files/" ); + } else if (id == PARTICLES) { + mediaName.Strip("Particles/"); + mediaName += ".prt"; + } else if ( id == SKINS ) { + mediaName.Strip( "Matching Skins/" ); + mediaName.Strip( "Skins/" ); + } else { + mediaName.Strip( "base/" ); + } + + } else if (id == WAVES || id == SOUNDS) { + mediaName = treeMedia.GetItemText( item ); + } else if (id < 0) { + if ( treeMedia.ItemHasChildren(item) == FALSE ) { + int dw = abs(( int )treeMedia.GetItemData( item )) - 1; + if ( dw < items.Num() ) { + idStr work = items[dw].Path; + work += "\r\n\r\n"; + work += items[dw].Comments; + editInfo.SetWindowText( work ); + mediaName = items[dw].Path; + } + } + } + + if ( currentMode == MODELS || currentMode == SKINS ) { + idStr modelMedia; + if ( currentMode == MODELS ) { + modelMedia = mediaName; + } else { + modelMedia = data; + } + if ( modelMedia.Length() ) { + int size = fileSystem->ReadFile( modelMedia, NULL, NULL ); + int lsize; + if ( strstr( modelMedia, ".lwo" ) ) { + lsize = 128 * 1024; + } + else { + lsize = 768 * 1024; + } + if ( size > lsize ) { + if ( MessageBox("Model appears to be quite large, are you sure you want to preview it?", "High Poly Model?", MB_YESNO ) == IDNO ) { + *pResult = 0; + return; + } + } + m_drawModel.setMedia( modelMedia ); + if ( currentMode == SKINS ) { + m_drawModel.SetSkin( mediaName ); + } + } + m_drawModel.SetRealTime(0); + wndPreview.setDrawable( &m_drawModel ); + wndPreview.Invalidate(); + wndPreview.RedrawWindow(); + RedrawWindow(); + } + else if ( currentMode == PARTICLES ) { + m_drawModel.setMedia( mediaName ); + m_drawModel.SetRealTime(50); + wndPreview.setDrawable( &m_drawModel ); + wndPreview.Invalidate(); + wndPreview.RedrawWindow(); + RedrawWindow(); + } else if ( currentMode == GUIS ) { + const idMaterial *mat = declManager->FindMaterial("guisurfs/guipreview"); + materialEdit->SetGui( const_cast( mat ), mediaName ); + m_drawMaterial.setMedia("guisurfs/guipreview"); + m_drawMaterial.setScale(4.4f); + wndPreview.setDrawable(&m_drawMaterial); + wndPreview.Invalidate(); + wndPreview.RedrawWindow(); + idUserInterface *gui = uiManager->FindGui( mediaName, false, false, true ); + if ( gui ) { + idStr str = gui->Comment(); + str.Replace( "\n", "\r\n" ); + if ( str != "" ) { + editInfo.SetWindowText( str ); + } + } + RedrawWindow(); + } else if (currentMode == MATERIALS) { + m_drawMaterial.setMedia(mediaName); + m_drawMaterial.setScale(1.0); + wndPreview.setDrawable(&m_drawMaterial); + wndPreview.Invalidate(); + wndPreview.RedrawWindow(); + RedrawWindow(); + } + + //m_drawGui.setMedia(matName); + //wndPreview.setDrawable(&m_drawMaterial); + //wndPreview.RedrawWindow(); + } + + *pResult = 0; +} + + +BOOL CPreviewDlg::Create(LPCTSTR lpszTemplateName, CWnd* pParentWnd) +{ + BOOL b = CDialog::Create(lpszTemplateName, pParentWnd); + ShowWindow(SW_SHOW); + return b; +} + +void CPreviewDlg::OnCancel() +{ + if ( AfxGetApp()->GetMainWnd() == GetParent() && GetParent() ) { + GetParent()->EnableWindow(TRUE); + soundSystem->StopAllSounds( SOUNDWORLD_EDITOR ); + ShowWindow(SW_HIDE); + } else { + CDialog::OnCancel(); + } + returnCode = IDCANCEL; +} + +void CPreviewDlg::OnOK() +{ + if ( AfxGetApp()->GetMainWnd() == GetParent() && GetParent() ) { + GetParent()->EnableWindow(TRUE); + soundSystem->StopAllSounds( SOUNDWORLD_EDITOR ); + ShowWindow(SW_HIDE); + } else { + CDialog::OnOK(); + } + returnCode = IDOK; +} + +bool CPreviewDlg::Waiting() { + AfxGetApp()->PumpMessage(); + return (returnCode == -1); +} + +void CPreviewDlg::SetModal() { + returnCode = -1; +} +void CPreviewDlg::OnBnClickedButtonReload() +{ + BuildTree(); + soundSystem->StopAllSounds( SOUNDWORLD_EDITOR ); +} + +void CPreviewDlg::OnBnClickedButtonAdd() +{ + HTREEITEM item = treeMedia.GetSelectedItem(); + if (treeMedia.ItemHasChildren(item) == FALSE && (treeMedia.GetItemData(item) == GUIS || treeMedia.GetItemData(item) == MODELS)) { + CCommentsDlg dlg; + dlg.strPath = mediaName; + if (dlg.DoModal()) { + CommentedItem ci; + ci.Name = dlg.strName; + ci.Path = dlg.strPath; + ci.Comments = dlg.strComments; + items.Append(ci); + item = treeMedia.InsertItem(ci.Name, commentItem); + treeMedia.SetItemData(item, -1 - (items.Num() + 1)); + treeMedia.SetItemImage(item, 2, 2); + const char *path; + path = (currentMode == GUIS) ? "guis/guis.commented" : "models/models.commented"; + idStr str; + void *buffer; + fileSystem->ReadFile( path, &buffer ); + str = (char *) buffer; + fileSystem->FreeFile( buffer ); + str += "\r\n\r\n{\r\n\t\""; + str += ci.Name; + str += "\"\r\n\t\""; + str += ci.Path; + str += "\"\r\n\t\""; + str += ci.Comments; + str += "\"\r\n}\r\n"; + fileSystem->WriteFile(path, (void*)&str[0], str.Length(), "fs_devpath"); + + } + } +} + + +void CPreviewDlg::AddSounds(bool rootItems) { + int i, j; + idStrList list(1024); + idStrList list2(1024); + HTREEITEM base = treeMedia.InsertItem("Sound Shaders"); + + for( i = 0; i < declManager->GetNumDecls( DECL_SOUND ); i++ ) { + const idSoundShader *poo = declManager->SoundByIndex( i, false ); + list.AddUnique( poo->GetFileName() ); + } + list.Sort(); + + for ( i = 0; i < list.Num(); i++ ) { + HTREEITEM child = treeMedia.InsertItem(list[i], base); + treeMedia.SetItemData(child, SOUNDPARENT); + treeMedia.SetItemImage(child, 0, 1); + list2.Clear(); + for (j = 0; j < declManager->GetNumDecls( DECL_SOUND ); j++) { + const idSoundShader *poo = declManager->SoundByIndex( j, false ); + if ( idStr::Icmp( list[i], poo->GetFileName() ) == 0 ) { + list2.Append( poo->GetName() ); + } + } + list2.Sort(); + for (j = 0; j < list2.Num(); j++) { + HTREEITEM child2 = treeMedia.InsertItem( list2[j], child ); + treeMedia.SetItemData(child2, SOUNDS); + treeMedia.SetItemImage(child2, 2, 2); + } + } + + idFileList *files; + files = fileSystem->ListFilesTree( "sound", ".wav" ); + AddStrList( "Wave files", files->GetList(), WAVES ); + fileSystem->FreeFileList( files ); +} + +void CPreviewDlg::SetMode( int mode, const char *preSelect ) { + + currentMode = mode; + if ( preSelect ) { + mediaName = preSelect; + } + + if (GetSafeHwnd() == NULL) { + return; + } + + CWnd *wnd; + switch (currentMode) { + case GUIS : + case SKINS : + case MODELS : + case PARTICLES : + wndPreview.ShowWindow(SW_SHOW); + wnd = GetDlgItem(IDC_BUTTON_PLAY); + if (wnd) { + wnd->ShowWindow(SW_HIDE); + } + wnd = GetDlgItem(IDC_BUTTON_ADD); + if (wnd) { + wnd->ShowWindow(SW_SHOW); + } + wnd = GetDlgItem(IDC_EDIT_INFO); + if (wnd) { + wnd->ShowWindow(SW_SHOW); + } + break; + case MATERIALS : + wndPreview.ShowWindow(SW_SHOW); + wnd = GetDlgItem(IDC_BUTTON_PLAY); + if (wnd) { + wnd->ShowWindow(SW_HIDE); + } + wnd = GetDlgItem(IDC_BUTTON_ADD); + if (wnd) { + wnd->ShowWindow(SW_HIDE); + } + wnd = GetDlgItem(IDC_EDIT_INFO); + if (wnd) { + wnd->ShowWindow(SW_HIDE); + } + break; + case SOUNDS : + case WAVES : + wndPreview.ShowWindow(SW_HIDE); + wnd = GetDlgItem(IDC_BUTTON_PLAY); + if (wnd) { + wnd->ShowWindow(SW_SHOW); + } + wnd = GetDlgItem(IDC_BUTTON_ADD); + if (wnd) { + wnd->ShowWindow(SW_HIDE); + } + wnd = GetDlgItem(IDC_EDIT_INFO); + if (wnd) { + wnd->ShowWindow(SW_HIDE); + } + break; + } +} + +void CPreviewDlg::OnBnClickedButtonPlay() { + soundSystem->PlayShaderDirectly( SOUNDWORLD_EDITOR, mediaName ); +} + +void CPreviewDlg::AddMaterials(bool rootItems) { + idStrList list(1024); + //char temp[2048]; + int count = declManager->GetNumDecls( DECL_MATERIAL ); + if (count > 0) { + for (int i = 0; i < count; i++) { + const idMaterial *mat = declManager->MaterialByIndex(i, false); + if (!rootItems) { + if (strchr(mat->GetName(), '/') == NULL && strchr(mat->GetName(), '\\') == NULL) { + continue; + } + } + list.Append(mat->GetName()); + } + list.Sort(); + AddStrList("Materials", list, MATERIALS); + } + +} + +void CPreviewDlg::AddParticles(bool rootItems) { + // Quake 4 uses BSE effects rather than Doom's .prt declarations. +} + +void CPreviewDlg::AddSkins( bool rootItems ) { + idStrList list(1024); + idStrList list2(1024); + idStr str; + int count = declManager->GetNumDecls( DECL_SKIN ); + if (count > 0) { + for (int i = 0; i < count; i++) { + const idDeclSkin *skin = declManager->SkinByIndex(i); + if (!rootItems) { + if (strchr(skin->GetName(), '/') == NULL && strchr(skin->GetName(), '\\') == NULL) { + continue; + } + } + if ( data.Length() ) { + for ( int j = 0; j < skin->GetNumModelAssociations(); j++ ){ + str = skin->GetAssociatedModel( j ); + str.ToLower(); + if ( data.Cmp(str) == 0 ) { + list.Append(skin->GetName()); + } + } + } + list2.Append(skin->GetName()); + } + list.Sort(); + list2.Sort(); + AddStrList( "Matching Skins", list, SKINS ); + AddStrList( "Skins", list2, SKINS ); + } +} + +void CPreviewDlg::OnShowWindow( BOOL bShow, UINT status ) { + if ( bShow && AfxGetApp()->GetMainWnd() == GetParent() && GetParent() ) { + GetParent()->EnableWindow( FALSE ); + } +} diff --git a/src/tools/radiant/PreviewDlg.h b/src/tools/radiant/PreviewDlg.h new file mode 100644 index 0000000..e36cdd6 --- /dev/null +++ b/src/tools/radiant/PreviewDlg.h @@ -0,0 +1,101 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#pragma once +#include "afxcmn.h" +#include "afxwin.h" + + +// CPreviewDlg dialog + +struct CommentedItem { + idStr Name; + idStr Path; + idStr Comments; +}; + +class CPreviewDlg : public CDialog +{ +public: + enum {MODELS, GUIS, SOUNDS, MATERIALS, SCRIPTS, SOUNDPARENT, WAVES, PARTICLES, MODELPARENT, GUIPARENT, COMMENTED, SKINS}; + CPreviewDlg(CWnd* pParent = NULL); // standard constructor + virtual ~CPreviewDlg(); + void SetMode( int mode, const char *preSelect = NULL ); + void RebuildTree( const char *data ); + void SetDisablePreview( bool b ) { + disablePreview = b; + } + + idStr mediaName; + int returnCode; + + bool Waiting(); + void SetModal(); +// Dialog Data + enum { IDD = IDD_DIALOG_PREVIEW }; +private: + DECLARE_DYNAMIC(CPreviewDlg) + + CTreeCtrl treeMedia; + CEdit editInfo; + HTREEITEM commentItem; + CImageList m_image; + idGLDrawable m_testDrawable; + idGLDrawableMaterial m_drawMaterial; + idGLDrawableModel m_drawModel; + idGLWidget wndPreview; + idHashTable quickTree; + idList items; + virtual BOOL OnInitDialog(); + int currentMode; + void AddCommentedItems(); + idStr data; + bool disablePreview; + +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + void BuildTree(); + void AddStrList(const char *root, const idStrList &list, int type); + void AddSounds(bool rootItems); + void AddMaterials(bool rootItems); + void AddParticles(bool rootItems); + void AddSkins( bool rootItems ); + + DECLARE_MESSAGE_MAP() + +public: + afx_msg void OnTvnSelchangedTreeMedia(NMHDR *pNMHDR, LRESULT *pResult); + virtual BOOL Create(LPCTSTR lpszTemplateName, CWnd* pParentWnd = NULL); +protected: + virtual void OnCancel(); + virtual void OnOK(); + virtual void OnShowWindow( BOOL bShow, UINT status ); +public: + afx_msg void OnBnClickedButtonReload(); + afx_msg void OnBnClickedButtonAdd(); + afx_msg void OnBnClickedButtonPlay(); +}; diff --git a/src/tools/radiant/PropertyList.cpp b/src/tools/radiant/PropertyList.cpp new file mode 100644 index 0000000..1ab0886 --- /dev/null +++ b/src/tools/radiant/PropertyList.cpp @@ -0,0 +1,540 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" +#include "PropertyList.h" + +#include "../comafx/DialogColorPicker.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CPropertyList + +CPropertyList::CPropertyList() { + measureItem = NULL; + updateInspectors = false; +} + +CPropertyList::~CPropertyList() { +} + + +BEGIN_MESSAGE_MAP(CPropertyList, CListBox) + //{{AFX_MSG_MAP(CPropertyList) + ON_WM_CREATE() + ON_CONTROL_REFLECT(LBN_SELCHANGE, OnSelchange) + ON_WM_LBUTTONUP() + ON_WM_KILLFOCUS() + ON_WM_LBUTTONDOWN() + ON_WM_MOUSEMOVE() + //}}AFX_MSG_MAP + ON_CBN_CLOSEUP(IDC_PROPCMBBOX, OnKillfocusCmbBox) + ON_CBN_SELCHANGE(IDC_PROPCMBBOX, OnSelchangeCmbBox) + ON_EN_KILLFOCUS(IDC_PROPEDITBOX, OnKillfocusEditBox) + ON_EN_CHANGE(IDC_PROPEDITBOX, OnChangeEditBox) + ON_BN_CLICKED(IDC_PROPBTNCTRL, OnButton) +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CPropertyList message handlers + +BOOL CPropertyList::PreCreateWindow(CREATESTRUCT& cs) { + if (!CListBox::PreCreateWindow(cs)) { + return FALSE; + } + + cs.style &= ~(LBS_OWNERDRAWVARIABLE | LBS_SORT); + cs.style |= LBS_OWNERDRAWFIXED; + + m_bTracking = FALSE; + m_nDivider = 0; + m_bDivIsSet = FALSE; + + return TRUE; +} + +void CPropertyList::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct) { + if (measureItem && !measureItem->m_curValue.IsEmpty()) { + CRect rect; + GetClientRect(rect); + if (m_nDivider==0) { + m_nDivider = rect.Width() / 2; + } + rect.left = m_nDivider; + CDC * dc = GetDC(); + dc->DrawText(measureItem->m_curValue, rect, DT_CALCRECT | DT_LEFT | DT_WORDBREAK); + ReleaseDC(dc); + lpMeasureItemStruct->itemHeight = (rect.Height() >= 20) ? rect.Height() : 20; //pixels + } else { + lpMeasureItemStruct->itemHeight = 20; //pixels + } +} + + +void CPropertyList::DrawItem(LPDRAWITEMSTRUCT lpDIS) { + CDC dc; + dc.Attach(lpDIS->hDC); + CRect rectFull = lpDIS->rcItem; + CRect rect = rectFull; + if (m_nDivider==0) { + m_nDivider = rect.Width() / 2; + } + rect.left = m_nDivider; + CRect rect2 = rectFull; + rect2.right = rect.left - 1; + UINT nIndex = lpDIS->itemID; + + if (nIndex != (UINT) -1) { + //get the CPropertyItem for the current row + CPropertyItem* pItem = (CPropertyItem*) GetItemDataPtr(nIndex); + //draw two rectangles, one for each row column + if (pItem->m_nItemType == PIT_VAR) { + dc.FillSolidRect(rect2,RGB(220,220,220)); + } else { + dc.FillSolidRect(rect2,RGB(192,192,192)); + } + dc.DrawEdge(rect2,EDGE_SUNKEN,BF_BOTTOMRIGHT); + dc.DrawEdge(rect,EDGE_SUNKEN,BF_BOTTOM); + + if (lpDIS->itemState == ODS_SELECTED) { + dc.DrawFocusRect(rect2); + } + + //write the property name in the first rectangle + dc.SetBkMode(TRANSPARENT); + dc.DrawText(pItem->m_propName,CRect(rect2.left+3,rect2.top+3, + rect2.right-3,rect2.bottom+3), + DT_LEFT | DT_SINGLELINE); + + //write the initial property value in the second rectangle + dc.DrawText(pItem->m_curValue,CRect(rect.left+3,rect.top+3, rect.right+3,rect.bottom+3), DT_LEFT | (pItem->m_nItemType == PIT_VAR) ? DT_WORDBREAK : DT_SINGLELINE); + } + dc.Detach(); +} + +int CPropertyList::AddItem(CString txt) { + measureItem = NULL; + int nIndex = AddString(txt); + return nIndex; +} + +int CPropertyList::AddPropItem(CPropertyItem* pItem) { + if (pItem->m_nItemType == PIT_VAR) { + measureItem = pItem; + } else { + measureItem = NULL; + } + int nIndex = AddString(_T("")); + measureItem = NULL; + SetItemDataPtr(nIndex,pItem); + return nIndex; +} + +int CPropertyList::OnCreate(LPCREATESTRUCT lpCreateStruct) { + if (CListBox::OnCreate(lpCreateStruct) == -1) { + return -1; + } + + m_bDivIsSet = FALSE; + m_nDivider = 0; + m_bTracking = FALSE; + + m_hCursorSize = AfxGetApp()->LoadStandardCursor(IDC_SIZEWE); + m_hCursorArrow = AfxGetApp()->LoadStandardCursor(IDC_ARROW); + + m_SSerif8Font.CreatePointFont(80,_T("MS Sans Serif")); + + return 0; +} + +void CPropertyList::OnSelchange() { + CRect rect; + CString lBoxSelText; + static int recurse = 0; + //m_curSel = GetCurSel(); + + + GetItemRect(m_curSel,rect); + rect.left = m_nDivider; + + CPropertyItem* pItem = (CPropertyItem*) GetItemDataPtr(m_curSel); + + if (updateInspectors) { + g_Inspectors->entityDlg.SetKeyVal(pItem->m_propName, pItem->m_curValue); + } + + if (m_btnCtrl) { + m_btnCtrl.ShowWindow(SW_HIDE); + } + + if (pItem->m_nItemType==PIT_COMBO) { + //display the combo box. If the combo box has already been + //created then simply move it to the new location, else create it + m_nLastBox = 0; + if (m_cmbBox) { + m_cmbBox.MoveWindow(rect); + } else { + rect.bottom += 300; + m_cmbBox.Create(CBS_DROPDOWNLIST | WS_VSCROLL | WS_VISIBLE | WS_CHILD | WS_BORDER,rect,this,IDC_PROPCMBBOX); + m_cmbBox.SetFont(&m_SSerif8Font); + } + + //add the choices for this particular property + CString cmbItems = pItem->m_cmbItems; + lBoxSelText = pItem->m_curValue; + + m_cmbBox.ResetContent(); + m_cmbBox.AddString(""); + int i,i2; + i=0; + while ((i2=cmbItems.Find('|',i)) != -1) { + m_cmbBox.AddString(cmbItems.Mid(i,i2-i)); + i=i2+1; + } + + m_cmbBox.ShowWindow(SW_SHOW); + //m_cmbBox.SetFocus(); + + //jump to the property's current value in the combo box + int j = m_cmbBox.FindStringExact(0,lBoxSelText); + if (j != CB_ERR) { + m_cmbBox.SetCurSel(j); + } else { + m_cmbBox.SetCurSel(0); + } + //m_cmbBox.ShowDropDown(); + } + else if (pItem->m_nItemType==PIT_EDIT) { + //display edit box + m_nLastBox = 1; + m_prevSel = m_curSel; + rect.bottom -= 3; + if (m_editBox) { + m_editBox.MoveWindow(rect); + } else { + m_editBox.Create(ES_LEFT | ES_AUTOHSCROLL | WS_VISIBLE | WS_CHILD | WS_BORDER,rect,this,IDC_PROPEDITBOX); + m_editBox.SetFont(&m_SSerif8Font); + } + + lBoxSelText = pItem->m_curValue; + + m_editBox.ShowWindow(SW_SHOW); + m_editBox.SetFocus(); + //set the text in the edit box to the property's current value + bool b = updateInspectors; + updateInspectors = false; + m_editBox.SetWindowText(lBoxSelText); + updateInspectors = b; + } else if (pItem->m_nItemType != PIT_VAR) { + DisplayButton(rect); + } +} + +void CPropertyList::DisplayButton(CRect region) { + //displays a button if the property is a file/color/font chooser + m_nLastBox = 2; + m_prevSel = m_curSel; + + if (region.Width() > 25) { + region.left = region.right - 25; + } + region.bottom -= 3; + + if (m_btnCtrl) { + m_btnCtrl.MoveWindow(region); + } else { + m_btnCtrl.Create("...",BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD,region,this,IDC_PROPBTNCTRL); + m_btnCtrl.SetFont(&m_SSerif8Font); + } + + m_btnCtrl.ShowWindow(SW_SHOW); + m_btnCtrl.SetFocus(); +} + +void CPropertyList::ResetContent() { + if (m_btnCtrl.GetSafeHwnd()) { + m_btnCtrl.ShowWindow(SW_HIDE); + } + int c = this->GetCount(); + for (int i = 0; i < c; i++) { + CPropertyItem *pi = reinterpret_cast(GetItemDataPtr(i)); + if (pi) { + delete pi; + } + } + CListBox::ResetContent(); +} + +void CPropertyList::OnKillFocus(CWnd* pNewWnd) { + //m_btnCtrl.ShowWindow(SW_HIDE); + CListBox::OnKillFocus(pNewWnd); +} + +void CPropertyList::OnKillfocusCmbBox() { + m_cmbBox.ShowWindow(SW_HIDE); + Invalidate(); +} + +void CPropertyList::OnKillfocusEditBox() { + CString newStr; + m_editBox.ShowWindow(SW_HIDE); + Invalidate(); +} + +void CPropertyList::OnSelchangeCmbBox() { + CString selStr; + if (m_cmbBox) { + m_cmbBox.GetLBText(m_cmbBox.GetCurSel(),selStr); + CPropertyItem* pItem = (CPropertyItem*) GetItemDataPtr(m_curSel); + pItem->m_curValue = selStr; + if (updateInspectors) { + g_Inspectors->entityDlg.UpdateFromListBox(); + } + } +} + +void CPropertyList::OnChangeEditBox() { + CString newStr; + m_editBox.GetWindowText(newStr); + + CPropertyItem* pItem = (CPropertyItem*) GetItemDataPtr(m_curSel); + pItem->m_curValue = newStr; +} + +void CPropertyList::OnButton() { + CPropertyItem* pItem = (CPropertyItem*) GetItemDataPtr(m_curSel); + + //display the appropriate common dialog depending on what type + //of chooser is associated with the property + if (pItem->m_nItemType == PIT_COLOR) { + idVec3 color; + sscanf(pItem->m_curValue, "%f %f %f", &color.x, &color.y, &color.z); + + COLORREF cr = (int)(color.x * 255) + (((int)(color.y * 255))<<8) + (((int)(color.z * 255))<<16); + + CDialogColorPicker dlg(cr); + + dlg.UpdateParent = UpdateRadiantColor; + + if (dlg.DoModal() == IDOK) { + color.x = (dlg.GetColor() & 255)/255.0; + color.y = ((dlg.GetColor() >> 8)&255)/255.0; + color.z = ((dlg.GetColor() >> 16)&255)/255.0; + pItem->m_curValue = color.ToString(4); + } + if (updateInspectors) { + g_Inspectors->entityDlg.UpdateFromListBox(); + } + m_btnCtrl.ShowWindow(SW_HIDE); + Invalidate(); + } else if (pItem->m_nItemType == PIT_FILE) { + CString SelectedFile; + CString Filter("Gif Files (*.gif)|*.gif||"); + + CFileDialog FileDlg(TRUE, NULL, NULL, NULL, Filter); + + CString currPath = pItem->m_curValue; + FileDlg.m_ofn.lpstrTitle = "Select file"; + if (currPath.GetLength() > 0) { + FileDlg.m_ofn.lpstrInitialDir = currPath.Left(currPath.GetLength() - currPath.ReverseFind('\\')); + } + + if(IDOK == FileDlg.DoModal()) { + SelectedFile = FileDlg.GetPathName(); + m_btnCtrl.ShowWindow(SW_HIDE); + pItem->m_curValue = SelectedFile; + Invalidate(); + } + } else if (pItem->m_nItemType == PIT_FONT) { + CFontDialog FontDlg(NULL,CF_EFFECTS | CF_SCREENFONTS,NULL,this); + if(IDOK == FontDlg.DoModal()) { + CString faceName = FontDlg.GetFaceName(); + m_btnCtrl.ShowWindow(SW_HIDE); + pItem->m_curValue = faceName; + Invalidate(); + } + } else if (pItem->m_nItemType == PIT_MODEL) { + CPreviewDlg *dlg = CEntityDlg::ShowModelChooser(); + if (dlg->returnCode == IDOK) { + pItem->m_curValue = dlg->mediaName; + m_btnCtrl.ShowWindow(SW_HIDE); + if (updateInspectors) { + g_Inspectors->entityDlg.UpdateFromListBox(); + } + Invalidate(); + } + } else if (pItem->m_nItemType == PIT_GUI) { + CPreviewDlg *dlg = CEntityDlg::ShowGuiChooser(); + if (dlg->returnCode == IDOK) { + pItem->m_curValue = dlg->mediaName; + m_btnCtrl.ShowWindow(SW_HIDE); + if (updateInspectors) { + g_Inspectors->entityDlg.UpdateFromListBox(); + } + Invalidate(); + } + } else if (pItem->m_nItemType == PIT_MATERIAL) { + CPreviewDlg *dlg = CEntityDlg::ShowMaterialChooser(); + if (dlg->returnCode == IDOK) { + pItem->m_curValue = dlg->mediaName; + m_btnCtrl.ShowWindow(SW_HIDE); + if (updateInspectors) { + g_Inspectors->entityDlg.UpdateFromListBox(); + } + Invalidate(); + } + } +} + +void CPropertyList::OnLButtonUp(UINT nFlags, CPoint point) { + if (m_bTracking) { + //if columns were being resized then this indicates + //that mouse is up so resizing is done. Need to redraw + //columns to reflect their new widths. + + m_bTracking = FALSE; + //if mouse was captured then release it + if (GetCapture()==this) { + ::ReleaseCapture(); + } + + ::ClipCursor(NULL); + + CClientDC dc(this); + InvertLine(&dc,CPoint(point.x,m_nDivTop),CPoint(point.x,m_nDivBtm)); + //set the divider position to the new value + m_nDivider = point.x; + + //redraw + Invalidate(); + } else { + BOOL loc; + int i = ItemFromPoint(point,loc); + m_curSel = i; + CListBox::OnLButtonUp(nFlags, point); + } +} + +void CPropertyList::OnLButtonDown(UINT nFlags, CPoint point) { + if ((point.x>=m_nDivider-5) && (point.x<=m_nDivider+5)) { + //if mouse clicked on divider line, then start resizing + ::SetCursor(m_hCursorSize); + CRect windowRect; + GetWindowRect(windowRect); + windowRect.left += 10; windowRect.right -= 10; + //do not let mouse leave the list box boundary + ::ClipCursor(windowRect); + + if (m_cmbBox) { + m_cmbBox.ShowWindow(SW_HIDE); + } + if (m_editBox) { + m_editBox.ShowWindow(SW_HIDE); + } + + CRect clientRect; + GetClientRect(clientRect); + + m_bTracking = TRUE; + m_nDivTop = clientRect.top; + m_nDivBtm = clientRect.bottom; + m_nOldDivX = point.x; + + CClientDC dc(this); + InvertLine(&dc,CPoint(m_nOldDivX,m_nDivTop),CPoint(m_nOldDivX,m_nDivBtm)); + + //capture the mouse + SetCapture(); + } else { + m_bTracking = FALSE; + CListBox::OnLButtonDown(nFlags, point); + } +} + +void CPropertyList::OnMouseMove(UINT nFlags, CPoint point) { + if (m_bTracking) { + //move divider line to the mouse pos. if columns are + //currently being resized + CClientDC dc(this); + //remove old divider line + InvertLine(&dc,CPoint(m_nOldDivX,m_nDivTop),CPoint(m_nOldDivX,m_nDivBtm)); + //draw new divider line + InvertLine(&dc,CPoint(point.x,m_nDivTop),CPoint(point.x,m_nDivBtm)); + m_nOldDivX = point.x; + } else if ((point.x >= m_nDivider-5) && (point.x <= m_nDivider+5)) { + //set the cursor to a sizing cursor if the cursor is over the row divider + ::SetCursor(m_hCursorSize); + } else { + CListBox::OnMouseMove(nFlags, point); + } +} + +void CPropertyList::InvertLine(CDC* pDC,CPoint ptFrom,CPoint ptTo) { + int nOldMode = pDC->SetROP2(R2_NOT); + pDC->MoveTo(ptFrom); + pDC->LineTo(ptTo); + pDC->SetROP2(nOldMode); +} + +void CPropertyList::PreSubclassWindow() { + m_bDivIsSet = FALSE; + m_nDivider = 0; + m_bTracking = FALSE; + m_curSel = 1; + + m_hCursorSize = AfxGetApp()->LoadStandardCursor(IDC_SIZEWE); + m_hCursorArrow = AfxGetApp()->LoadStandardCursor(IDC_ARROW); + + m_SSerif8Font.CreatePointFont(80,_T("MS Sans Serif")); +} + + +void CPropertyList::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { + if (m_cmbBox) { + m_cmbBox.ShowWindow(SW_HIDE); + } + if (m_editBox) { + m_editBox.ShowWindow(SW_HIDE); + } + if (m_btnCtrl) { + m_btnCtrl.ShowWindow(SW_HIDE); + } + Invalidate(); + + CListBox::OnVScroll(nSBCode, nPos, pScrollBar); +} + diff --git a/src/tools/radiant/PropertyList.h b/src/tools/radiant/PropertyList.h new file mode 100644 index 0000000..ee3cf52 --- /dev/null +++ b/src/tools/radiant/PropertyList.h @@ -0,0 +1,170 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#if !defined(AFX_PROPERTYLIST_H__74205380_1B56_11D4_BC48_00105AA2186F__INCLUDED_) +#define AFX_PROPERTYLIST_H__74205380_1B56_11D4_BC48_00105AA2186F__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// PropertyList.h : header file +// + +#define PIT_COMBO 0 //PIT = property item type +#define PIT_EDIT 1 +#define PIT_COLOR 2 +#define PIT_FONT 3 +#define PIT_FILE 4 +#define PIT_SCRIPT 5 +#define PIT_MODEL 6 +#define PIT_SOUND 7 +#define PIT_GUI 8 +#define PIT_MATERIAL 9 +#define PIT_VAR 10 + +#define IDC_PROPCMBBOX 712 +#define IDC_PROPEDITBOX 713 +#define IDC_PROPBTNCTRL 714 + + +///////////////////////////////////////////////////////////////////////////// +//CPropertyList Items +class CPropertyItem +{ +// Attributes +public: + CString m_propName; + CString m_curValue; + int m_nItemType; + CString m_cmbItems; + int data; + +public: + CPropertyItem(CString propName, CString curValue, + int nItemType, CString cmbItems) + { + m_propName = propName; + m_curValue = curValue; + m_nItemType = nItemType; + m_cmbItems = cmbItems; + data = -1; + } + void SetData(int d) { + data = d; + } +}; + +///////////////////////////////////////////////////////////////////////////// +// CPropertyList window + +class CPropertyList : public CListBox +{ +// Construction +public: + CPropertyList(); + +// Attributes +public: + +// Operations +public: + int AddItem(CString txt); + int AddPropItem(CPropertyItem* pItem); + void ResetContent(); + CEdit *GetEditBox() { + return &m_editBox; + } + void SetUpdateInspectors(bool b) { + updateInspectors = b; + } + void SetDivider( int div ) { + m_nDivider = div; + } + afx_msg void OnKillfocusEditBox(); + afx_msg void OnChangeEditBox(); + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CPropertyList) + public: + virtual void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct); + virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct); + afx_msg void OnSelchange(); + protected: + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + virtual void PreSubclassWindow(); + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~CPropertyList(); + + // Generated message map functions +protected: + //{{AFX_MSG(CPropertyList) + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + afx_msg void OnLButtonUp(UINT nFlags, CPoint point); + afx_msg void OnKillFocus(CWnd* pNewWnd); + afx_msg void OnLButtonDown(UINT nFlags, CPoint point); + afx_msg void OnMouseMove(UINT nFlags, CPoint point); + afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); + + //}}AFX_MSG + afx_msg void OnKillfocusCmbBox(); + afx_msg void OnSelchangeCmbBox(); + afx_msg void OnButton(); + + DECLARE_MESSAGE_MAP() + + void InvertLine(CDC* pDC,CPoint ptFrom,CPoint ptTo); + void DisplayButton(CRect region); + + CComboBox m_cmbBox; + CEdit m_editBox; + CButton m_btnCtrl; + CFont m_SSerif8Font; + + int m_curSel,m_prevSel; + int m_nDivider; + int m_nDivTop; + int m_nDivBtm; + int m_nOldDivX; + int m_nLastBox; + BOOL m_bTracking; + BOOL m_bDivIsSet; + HCURSOR m_hCursorArrow; + HCURSOR m_hCursorSize; + CPropertyItem *measureItem; + bool updateInspectors; +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_PROPERTYLIST_H__74205380_1B56_11D4_BC48_00105AA2186F__INCLUDED_) diff --git a/src/tools/radiant/QE3.CPP b/src/tools/radiant/QE3.CPP new file mode 100644 index 0000000..f63ea1e --- /dev/null +++ b/src/tools/radiant/QE3.CPP @@ -0,0 +1,440 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include +#include +#include "WaitDlg.h" + +QEGlobals_t g_qeglobals; + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void WINAPI QE_CheckOpenGLForErrors(void) { + CString strMsg; + int i = qglGetError(); + if (i != GL_NO_ERROR) { + if (i == GL_OUT_OF_MEMORY) { + // + // strMsg.Format("OpenGL out of memory error %s\nDo you wish to save before + // exiting?", gluErrorString((GLenum)i)); + // + if (g_pParentWnd->MessageBox(strMsg, EDITOR_WINDOWTEXT " Error", MB_YESNO) == IDYES) { + Map_SaveFile(NULL, false); + } + + exit(1); + } + else { + // strMsg.Format("Warning: OpenGL Error %s\n ", gluErrorString((GLenum)i)); + common->Printf(strMsg.GetBuffer(0)); + } + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +bool DoesFileExist(const char *pBuff, long &lSize) { + CFile file; + if (file.Open(pBuff, CFile::modeRead | CFile::shareDenyNone)) { + lSize += file.GetLength(); + file.Close(); + return true; + } + + return false; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +bool ExtractPath_and_Filename(const char *pPath, CString &strPath, CString &strFilename) { + CString strPathName = pPath; + int nSlash = strPathName.ReverseFind('\\'); + if (nSlash >= 0) { + strPath = strPathName.Left(nSlash + 1); + strFilename = strPathName.Right(strPathName.GetLength() - nSlash - 1); + } + else { + strFilename = pPath; + } + + return true; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Map_Snapshot() { + CString strMsg; + + // + // we need to do the following 1. make sure the snapshot directory exists (create + // it if it doesn't) 2. find out what the lastest save is based on number 3. inc + // that and save the map + // + CString strOrgPath, strOrgFile; + ExtractPath_and_Filename(currentmap, strOrgPath, strOrgFile); + AddSlash(strOrgPath); + strOrgPath += "snapshots"; + + bool bGo = true; + struct _stat Stat; + if (_stat(strOrgPath, &Stat) == -1) { + bGo = (_mkdir(strOrgPath) != -1); + } + + AddSlash(strOrgPath); + if (bGo) { + int nCount = 0; + long lSize = 0; + CString strNewPath = strOrgPath; + strNewPath += strOrgFile; + + CString strFile; + while (bGo) { + strFile.Format("%s.%i", strNewPath, nCount); + bGo = DoesFileExist(strFile, lSize); + nCount++; + } + + // strFile has the next available slot + Map_SaveFile(strFile.GetBuffer(0), false); + Sys_SetTitle(currentmap); + if (lSize > 12 * 1024 * 1024) { // total size of saves > 4 mb + common->Printf + ( + "The snapshot files in the [%s] directory total more than 4 megabytes. You might consider cleaning the directory up.", + strOrgPath + ); + } + } + else { + strMsg.Format("Snapshot save failed.. unabled to create directory\n%s", strOrgPath); + g_pParentWnd->MessageBox(strMsg); + } +} + +/* + ======================================================================================================================= + QE_CheckAutoSave If five minutes have passed since making a change and the map hasn't been saved, save it out. + ======================================================================================================================= + */ +void QE_CheckAutoSave(void) { + static bool inAutoSave = false; + static bool autoToggle = false; + if (inAutoSave) { + Sys_Status("Did not autosave due recursive entry into autosave routine\n"); + return; + } + + if ( !mapModified ) { + return; + } + + inAutoSave = true; + + if ( g_PrefsDlg.m_bAutoSave ) { + CString strMsg = g_PrefsDlg.m_bSnapShots ? "Autosaving snapshot..." : "Autosaving..."; + Sys_Status(strMsg.GetBuffer(0), 0); + + if (g_PrefsDlg.m_bSnapShots && stricmp(currentmap, "unnamed.map") != 0) { + Map_Snapshot(); + } else { + Map_SaveFile(ValueForKey(g_qeglobals.d_project_entity, (autoToggle == 0) ? "autosave1" : "autosave2" ), false, true); + autoToggle ^= 1; + } + Sys_Status("Autosaving...Saved.", 0); + mapModified = 0; // DHM - _D3XP + } else { + common->Printf("Autosave skipped...\n"); + Sys_Status("Autosave skipped...", 0); + } + + inAutoSave = false; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ + +const char *g_pPathFixups[] = { + "basepath", + + // "remotebasepath", + "entitypath", + + // "texturepath", + "autosave", + + // "mapspath" +}; + +const int g_nPathFixupCount = sizeof(g_pPathFixups) / sizeof (const char *); + +/* + ======================================================================================================================= + QE_LoadProject + ======================================================================================================================= + */ +bool QE_LoadProject(char *projectfile) { + char *data; + ID_TIME_T time; + + common->Printf("QE_LoadProject (%s)\n", projectfile); + + if ( fileSystem->ReadFile( projectfile, reinterpret_cast < void ** > (&data), &time) <= 0 ) { + return false; + } + + g_strProject = projectfile; + g_PrefsDlg.m_strLastProject = projectfile; + g_PrefsDlg.SavePrefs(); + + CString strData = data; + + fileSystem->FreeFile( data ); + + StartTokenParsing(strData.GetBuffer(0)); + g_qeglobals.d_project_entity = Entity_Parse(true); + if (!g_qeglobals.d_project_entity) { + Error("Couldn't parse %s", projectfile); + } + + // set here some default project settings you need + if (strlen(ValueForKey(g_qeglobals.d_project_entity, "brush_primit")) == 0) { + SetKeyValue(g_qeglobals.d_project_entity, "brush_primit", "0"); + } + + g_qeglobals.m_bBrushPrimitMode = IntForKey(g_qeglobals.d_project_entity, "brush_primit"); + + Eclass_InitForSourceDirectory(ValueForKey(g_qeglobals.d_project_entity, "entitypath")); + g_Inspectors->FillClassList(); // list in entity window + + Map_New(); + + // FillTextureMenu(); + FillBSPMenu(); + + return true; +} + +/* + ======================================================================================================================= + QE_SaveProject £ + ======================================================================================================================= + */ +bool QE_SaveProject(const char *pProjectFile) { + + idFile *file = fileSystem->OpenFileWrite(pProjectFile); + if ( !file ) { + return false; + } + + file->Write("{\n", 2); + + int count = g_qeglobals.d_project_entity->epairs.GetNumKeyVals(); + for (int i = 0; i < count; i++) { + file->WriteFloatString( "\"%s\" \"%s\"\n", g_qeglobals.d_project_entity->epairs.GetKeyVal(i)->GetKey().c_str(), g_qeglobals.d_project_entity->epairs.GetKeyVal(i)->GetValue().c_str()); + } + + file->Write("}\n", 2); + + fileSystem->CloseFile( file ); + + return true; +} + +/* QE_KeyDown */ +#define SPEED_MOVE 32 +#define SPEED_TURN 22.5 + +/* + ======================================================================================================================= + ConnectEntities Sets target / name on the two entities selected from the first selected to the secon + ======================================================================================================================= + */ +void ConnectEntities(void) { + entity_t *e1; + const char *target; + idStr strTarget; + int i, t; + + if (g_qeglobals.d_select_count < 2) { + Sys_Status("Must have at least two brushes selected.", 0); + Sys_Beep(); + return; + } + + e1 = g_qeglobals.d_select_order[0]->owner; + + for (i = 0; i < g_qeglobals.d_select_count; i++) { + if (g_qeglobals.d_select_order[i]->owner == world_entity) { + Sys_Status("Can't connect to the world.", 0); + Sys_Beep(); + return; + } + } + + for (i = 1; i < g_qeglobals.d_select_count; i++) { + if (e1 == g_qeglobals.d_select_order[i]->owner) { + Sys_Status("Brushes are from same entity.", 0); + Sys_Beep(); + return; + } + } + + target = ValueForKey(e1, "target"); + if ( target && *target) { + for (t = 1; t < 2048; t++) { + target = ValueForKey(e1, va("target%i", t)); + if (target && *target) { + continue; + } else { + break; + } + } + } else { + t = 0; + } + + for (i = 1; i < g_qeglobals.d_select_count; i++) { + target = ValueForKey(g_qeglobals.d_select_order[i]->owner, "name"); + if (target && *target) { + strTarget = target; + } else { + UniqueTargetName(strTarget); + } + if (t == 0) { + SetKeyValue(e1, "target", strTarget); + } else { + SetKeyValue(e1, va("target%i", t), strTarget); + } + t++; + } + + Sys_UpdateWindows(W_XY | W_CAMERA); + + Select_Deselect(); + Select_Brush(g_qeglobals.d_select_order[1]); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +bool QE_SingleBrush(bool bQuiet, bool entityOK) { + if ((selected_brushes.next == &selected_brushes) || (selected_brushes.next->next != &selected_brushes)) { + if (!bQuiet) { + Sys_Status("Error: you must have a single brush selected\n"); + } + + return false; + } + + if (!entityOK && selected_brushes.next->owner->eclass->fixedsize) { + if (!bQuiet) { + Sys_Status("Error: you cannot manipulate fixed size entities\n"); + } + + return false; + } + + return true; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void QE_Init(void) { + /* initialize variables */ + g_qeglobals.d_gridsize = 8; + g_qeglobals.d_showgrid = true; + + /* + * other stuff £ + * FIXME: idMaterial Texture_Init (true); Cam_Init (); XY_Init (); + */ + Z_Init(); +} + + +int g_numbrushes, g_numentities; + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void QE_CountBrushesAndUpdateStatusBar(void) { + static int s_lastbrushcount, s_lastentitycount; + static bool s_didonce; + + // entity_t *e; + brush_t *b, *next; + + g_numbrushes = 0; + g_numentities = 0; + + if (active_brushes.next != NULL) { + for (b = active_brushes.next; b != NULL && b != &active_brushes; b = next) { + next = b->next; + if (b->brush_faces) { + if (!b->owner->eclass->fixedsize) { + g_numbrushes++; + } + else { + g_numentities++; + } + } + } + } + + /* + * if ( entities.next != NULL ) { for ( e = entities.next ; e != &entities && + * g_numentities != MAX_MAP_ENTITIES ; e = e->next) { g_numentities++; } } + */ + if (((g_numbrushes != s_lastbrushcount) || (g_numentities != s_lastentitycount)) || (!s_didonce)) { + Sys_UpdateStatusBar(); + + s_lastbrushcount = g_numbrushes; + s_lastentitycount = g_numentities; + s_didonce = true; + } +} + diff --git a/src/tools/radiant/QE3.H b/src/tools/radiant/QE3.H new file mode 100644 index 0000000..a89d863 --- /dev/null +++ b/src/tools/radiant/QE3.H @@ -0,0 +1,497 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef __QE3_H__ +#define __QE3_H__ + +#ifdef ID_DEBUG_MEMORY +#undef new +#undef DEBUG_NEW +#define DEBUG_NEW new +#endif + +// this define to use HTREEITEM and MFC stuff in the headers +#define QERTYPES_USE_MFC +#include "qertypes.h" +#include "cmdlib.h" +#include "parse.h" + +#include +#include "afxres.h" +#include "../../sys/win32/rc/Radiant_resource.h" // main symbols +#include "../../sys/win32/win_local.h" + +#include "qedefs.h" + +// stuff from old qfiles.h +#define MAX_MAP_ENTITIES 2048 +const int MAX_MOVE_POINTS = 4096; +const int MAX_MOVE_PLANES = 2048; + +// assume that all maps fit within this +const float HUGE_DISTANCE = 100000; + +#include "textures.h" +#include "EditorBrush.h" +#include "EditorEntity.h" +#include "EditorMap.h" +#include "select.h" +#include "splines.h" + +#include "z.h" +#include "mru.h" +#include "waitdlg.h" +#include "MainFrm.h" +#include "PrefsDlg.h" +#include "FindTextureDlg.h" +#include "dialogtextures.h" +#include "InspectorDialog.h" +#include "undo.h" +#include "PMESH.H" + +// the dec offsetof macro doesn't work very well... +#define myoffsetof(type,identifier) ((size_t)&((type *)0)->identifier) + + +void Error (char *error, ...); +void Warning (char *error, ...); + +typedef struct +{ + int p1, p2; + face_t *f1, *f2; +} pedge_t; + +typedef struct +{ + int iSize; + int iTexMenu; // nearest, linear, etc + float fGamma; // gamma for textures + char szProject[256]; // last project loaded + idVec3 colors[COLOR_LAST]; + bool show_names; + bool show_coordinates; + int exclude; + float m_nTextureTweak; + bool editorExpanded; + RECT oldEditRect; + bool showSoundAlways; + bool showSoundWhenSelected; +} SavedInfo_t; + +// +// system functions +// +// TTimo NOTE: WINAPI funcs can be accessed by plugins +void Sys_UpdateStatusBar( void ); +void WINAPI Sys_UpdateWindows (int bits); +void Sys_Beep (void); +double Sys_DoubleTime (void); +void Sys_GetCursorPos (int *x, int *y); +void Sys_SetCursorPos (int x, int y); +void Sys_SetTitle (const char *text); +void Sys_BeginWait (void); +bool Sys_Waiting(); +void Sys_EndWait (void); +void Sys_Status(const char *psz, int part = -1); + +/* +** most of the QE globals are stored in this structure +*/ +typedef struct +{ + bool d_showgrid; + float d_gridsize; + + int rotateAxis; // 0, 1 or 2 + int flatRotation; // 0, 1 or 2, 0 == off, 1 == rotate about the rotation origin, 1 == rotate about the selection mid point + + int d_num_entities; + + entity_t *d_project_entity; + + idVec3 d_new_brush_bottom, d_new_brush_top; + + HINSTANCE d_hInstance; + + idVec3 d_points[MAX_POINTS]; + int d_numpoints; + pedge_t d_edges[MAX_EDGES]; + int d_numedges; + + int d_num_move_points; + idVec3 *d_move_points[MAX_MOVE_POINTS]; + + int d_num_move_planes; + idPlane *d_move_planes[MAX_MOVE_PLANES]; + + qtexture_t *d_qtextures; + + texturewin_t d_texturewin; + + int d_pointfile_display_list; + + LPMRUMENU d_lpMruMenu; + + SavedInfo_t d_savedinfo; + + int d_workcount; + + // connect entities uses the last two brushes selected + int d_select_count; + brush_t *d_select_order[MAX_MAP_ENTITIES]; + idVec3 d_select_translate; // for dragging w/o making new display lists + select_t d_select_mode; + idPointListInterface *selectObject; // + + int d_font_list; + + int d_parsed_brushes; + + bool show_blocks; + + // Timo + // tells if we are internally using brush primitive (texture coordinates and map format) + // this is a shortcut for IntForKey( g_qeglobals.d_project_entity, "brush_primit" ) + // NOTE: must keep the two ones in sync + BOOL m_bBrushPrimitMode; + + // used while importing brush data from file or memory buffer + // tells if conversion between map format and internal preferences ( m_bBrushPrimitMode ) is needed + bool bNeedConvert; + bool bOldBrushes; + bool bPrimitBrushes; + float mapVersion; + + idVec3 d_vAreaTL; + idVec3 d_vAreaBR; + + // tells if we are using .INI files for prefs instead of registry + bool use_ini; + // even in .INI mode we use the registry for all void* prefs + char use_ini_registry[64]; + + //Timo + // tells we have surface properties plugin + bool bSurfacePropertiesPlugin; + // tells we are using a BSP frontend plugin + bool bBSPFrontendPlugin; + + // the editor has its own soundWorld and renderWorld, completely distinct from the game + idRenderWorld *rw; + // Quake 4 replaced Doom 3's idSoundWorld pointer API with numeric worlds. + int sw; +} QEGlobals_t; + + +void Pointfile_Delete (void); +void WINAPI Pointfile_Check (void); +void Pointfile_Next (void); +void Pointfile_Prev (void); +void Pointfile_Clear (void); +void Pointfile_Draw( void ); +void Pointfile_Load( void ); + +// +// drag.c +// +void Drag_Begin (int x, int y, int buttons, + const idVec3 &xaxis, const idVec3 &yaxis, + const idVec3 &origin, const idVec3 &dir); +void Drag_MouseMoved (int x, int y, int buttons); +void Drag_MouseUp (int nButtons = 0); +extern bool g_moveOnly; +// +// csg.c +// +void CSG_MakeHollow (void); +void CSG_Subtract (void); +void CSG_Merge (void); + +// +// vertsel.c +// + +void SetupVertexSelection (void); +void SelectEdgeByRay (idVec3 org, idVec3 dir); +void SelectVertexByRay (idVec3 org, idVec3 dir); + +void ConnectEntities (void); + +extern int update_bits; + +extern int screen_width; +extern int screen_height; + +extern HANDLE bsp_process; +extern HANDLE g_hBSPOutput; +extern HANDLE g_hBSPInput; + + +char *TranslateString (char *buf); + +void ProjectDialog (void); + +void FillTextureMenu (CStringArray* pArray = NULL); +void FillBSPMenu (void); + +BOOL CALLBACK Win_Dialog ( + HWND hwndDlg, // handle to dialog box + UINT uMsg, // message + WPARAM wParam, // first message parameter + LPARAM lParam // second message parameter +); + + +// +// win_cam.c +// +void WCam_Create (HINSTANCE hInstance); + + +// +// win_xy.c +// +void WXY_Create (HINSTANCE hInstance); + +// +// win_z.c +// +void WZ_Create (HINSTANCE hInstance); + +// +// win_ent.c +// + + +// +// win_main.c +// +void Main_Create (HINSTANCE hInstance); +extern bool SaveWindowState(HWND hWnd, const char *pszName); +extern bool LoadWindowState(HWND hWnd, const char *pszName); + +extern bool SaveRegistryInfo(const char *pszName, void *pvBuf, long lSize); +extern bool LoadRegistryInfo(const char *pszName, void *pvBuf, long *plSize); + + + +// win_dlg.c + +void DoGamma(void); +void DoFind(void); +void DoRotate(void); +void DoSides(bool bCone = false, bool bSphere = false, bool bTorus = false); +void DoAbout(void); +void DoSurface(); + +/* +** QE function declarations +*/ +void QE_CheckAutoSave( void ); +void QE_CountBrushesAndUpdateStatusBar( void ); +void WINAPI QE_CheckOpenGLForErrors(void); +void QE_ExpandBspString (char *bspaction, char *out, char *mapname, bool useTemps); +void QE_Init (void); +bool QE_KeyDown (int key, int nFlags = 0); +bool QE_LoadProject (char *projectfile); +bool QE_LoadQuake4Project (void); +bool QE_SingleBrush (bool bQuiet = false, bool entityOK = false); + + +// sys stuff +void Sys_MarkMapModified (void); + +/* +** QE Win32 function declarations +*/ +int WINAPI QEW_SetupPixelFormat(HDC hDC, bool zbuffer ); + +/* +** extern declarations +*/ +extern QEGlobals_t g_qeglobals; +extern int mapModified; // for quit confirmation (0 = clean, 1 = unsaved, + +//++timo clean (moved into qertypes.h) +//enum VIEWTYPE {YZ, XZ, XY}; + + +extern bool g_bAxialMode; +extern int g_axialAnchor; +extern int g_axialDest; + +extern void Face_FlipTexture_BrushPrimit(face_t *face, bool y); +extern void Brush_FlipTexture_BrushPrimit(brush_t *b, bool y); + +// Timo +// new brush primitive stuff +//void ComputeAxisBase( idVec3 &normal,idVec3 &texS,idVec3 &texT ); +void FaceToBrushPrimitFace(face_t *f); +void EmitBrushPrimitTextureCoordinates(face_t *, idWinding *, patchMesh_t *patch = NULL); +// EmitTextureCoordinates, is old code used for brush to brush primitive conversion +void EmitTextureCoordinates ( idVec5 &xyzst, const idMaterial *q, face_t *f, bool force = false); +void BrushPrimit_Parse(brush_t *, bool newFormat, const idVec3 origin); +// compute a fake shift scale rot representation from the texture matrix +void TexMatToFakeTexCoords( float texMat[2][3], float shift[2], float *rot, float scale[2] ); +void FakeTexCoordsToTexMat( float shift[2], float rot, float scale[2], float texMat[2][3] ); +void ConvertTexMatWithQTexture( brushprimit_texdef_t *texMat1, const idMaterial *qtex1, brushprimit_texdef_t *texMat2, const idMaterial *qtex2, float sScale = 1.0, float tScale = 1.0 ); +// texture locking +void Face_MoveTexture_BrushPrimit(face_t *f, idVec3 delta); +void Select_ShiftTexture_BrushPrimit( face_t *f, float x, float y, bool autoAdjust ); +void RotateFaceTexture_BrushPrimit(face_t *f, int nAxis, float fDeg, idVec3 vOrigin ); +// used in CCamWnd::ShiftTexture_BrushPrimit +void ComputeBest2DVector( idVec3 v, idVec3 X, idVec3 Y, int &x, int &y ); + +void ApplyMatrix_BrushPrimit(face_t *f, idMat3 matrix, idVec3 origin); +// low level functions .. put in mathlib? +#define BPMatCopy(a,b) {b[0][0] = a[0][0]; b[0][1] = a[0][1]; b[0][2] = a[0][2]; b[1][0] = a[1][0]; b[1][1] = a[1][1]; b[1][2] = a[1][2];} +// apply a scale transformation to the BP matrix +#define BPMatScale(m,sS,sT) {m[0][0]*=sS; m[1][0]*=sS; m[0][1]*=sT; m[1][1]*=sT;} +// apply a translation transformation to a BP matrix +#define BPMatTranslate(m,s,t) {m[0][2] += m[0][0]*s + m[0][1]*t; m[1][2] += m[1][0]*s+m[1][1]*t;} +// 2D homogeneous matrix product C = A*B +void BPMatMul(float A[2][3], float B[2][3], float C[2][3]); +// apply a rotation (degrees) +void BPMatRotate(float A[2][3], float theta); +#ifdef _DEBUG +void BPMatDump(float A[2][3]); +#endif + + +// +// eclass.cpp +// +extern bool parsing_single; +extern bool eclass_found; +extern eclass_t *eclass_e; +void Eclass_ScanFile( char *filename ); +void FillClassList (void); + +extern bool g_bShowLightVolumes; +extern bool g_bShowLightTextures; +extern const idMaterial *Texture_LoadLight(const char *name); + +#define FONT_HEIGHT 10 + +void UniqueTargetName(idStr& rStr); + +#define MAP_VERSION 2.0 + +extern CMainFrame* g_pParentWnd; +extern CString g_strAppPath; +extern CPrefsDlg& g_PrefsDlg; +extern CFindTextureDlg& g_dlgFind; +extern idCVar radiant_entityMode; + +// layout styles +#define QR_SPLIT 0 +#define QR_QE4 1 +#define QR_4WAY 2 +#define QR_SPLITZ 3 + + +// externs +extern void AddSlash(CString&); +extern void DLLBuildDone(); +extern void CleanUpEntities(); +extern void QE_CountBrushesAndUpdateStatusBar(); +extern void QE_CheckAutoSave(); +extern qtexture_t *notexture; +extern qtexture_t *current_texture; +extern bool SaveWindowState(HWND hWnd, const char *pszName); +extern void Map_Snapshot(); +extern void WXY_Print(); +extern void AddProp( void ); +extern int inspector_mode; +extern bool g_bRotateMode; +extern bool g_bClipMode; +extern bool g_bScaleMode; +extern int g_nScaleHow; +extern bool g_bPathMode; +extern void RunScript(char* pBuffer); +extern HINSTANCE g_hOpenGL32; + +extern void FindReplaceTextures(const char* pFind, const char* pReplace, bool bSelected, bool bForce); +extern void DoProjectSettings(); +extern bool region_active; +extern void Texture_ShowDirectory (char* pPath, bool Linked = false); +extern void Map_ImportFile (char *filename); +extern void Map_SaveSelected(char* pFilename); +extern bool g_bNewFace; +extern bool g_bSwitch; +extern brush_t g_brFrontSplits; +extern brush_t g_brBackSplits; +extern CClipPoint g_Clip1; +extern CClipPoint g_Clip2; +extern brush_t* g_pSplitList; +extern CClipPoint g_PathPoints[256]; +extern void AcquirePath(int nCount, PFNPathCallback* pFunc); +extern bool g_bScreenUpdates; +extern SCommandInfo g_Commands[]; +extern int g_nCommandCount; +extern SKeyInfo g_Keys[]; +extern int g_nKeyCount; +extern int inspector_mode; +extern const char *bsp_commands[256]; +extern void HandlePopup(CWnd* pWindow, unsigned int uId); +extern z_t z; +extern CString g_strProject; +extern void TextureAxisFromPlane( const idPlane &pln, idVec3 &xv, idVec3 &yv); +extern bool QE_SaveProject (const char* pProjectFile); +extern void Clamp(float& f, int nClamp); +extern bool WriteFileString( FILE *fp, char *string, ... ); +extern void MemFile_fprintf(CMemFile* pMemFile, const char* pText, ...); +extern void SaveWindowPlacement(HWND hwnd, const char* pName); +extern bool LoadWindowPlacement(HWND hwnd, const char* pName); +extern bool ConfirmModified (void); +extern void DoPatchInspector(); +void UpdatePatchInspector(); +extern int g_nSmartX; +extern int g_nSmartY; +extern brush_t* CreateEntityBrush(int x, int y, CXYWnd* pWnd); +int PointInMoveList( idVec3 *pf ); + +extern bool ByeByeSurfaceDialog(); +extern void UpdateSurfaceDialog(); +extern void UpdateLightInspector(); + +BOOL UpdateEntitySel(eclass_t *pec); +void SetInspectorMode(int iType); +BOOL GetSelectAllCriteria(CString &strKey, CString &strVal); + +int GetCvarInt(const char *name, const int def); +const char *GetCvarString(const char *name, const char *def); +void SetCvarInt(const char *name, const int value); +void SetCvarString(const char *name, const char *value); +void SetCvarBinary(const char *name, void *pv, int size); +bool GetCvarBinary(const char *name, void *pv, int size); + +void UpdateRadiantColor( float r, float g, float b, float a ); + +#endif /* !__QE3_H__ */ diff --git a/src/tools/radiant/QEDEFS.H b/src/tools/radiant/QEDEFS.H new file mode 100644 index 0000000..c521c26 --- /dev/null +++ b/src/tools/radiant/QEDEFS.H @@ -0,0 +1,171 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#ifndef __QEDEFS_H__ +#define __QEDEFS_H__ + +#define QE_VERSION 0x0501 + +#define QE3_STYLE (WS_OVERLAPPED | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | WS_CHILD) +#define QE3_STYLE2 (WS_OVERLAPPED | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_MINIMIZEBOX | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU) +#define QE3_CHILDSTYLE (WS_OVERLAPPED | WS_MINIMIZEBOX | WS_THICKFRAME | WS_CAPTION | WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_MAXIMIZEBOX) + +#define QE3_SPLITTER_STYLE (WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS) + + + +#define QE_AUTOSAVE_INTERVAL 5 // number of minutes between autosaves + +#define _3DFXCAMERA_WINDOW_CLASS "Q3DFXCamera" +#define CAMERA_WINDOW_CLASS "QCamera" +#define XY_WINDOW_CLASS "QXY" +#define Z_WINDOW_CLASS "QZ" +#define ENT_WINDOW_CLASS "QENT" +#define TEXTURE_WINDOW_CLASS "QTEX" + +#define ZWIN_WIDTH 40 +#define CWIN_SIZE (0.4) + +#define MAX_EDGES 512 +#define MAX_POINTS 1024 + +#define CMD_TEXTUREWAD 60000 +#define CMD_BSPCOMMAND 61000 + +#define PITCH 0 +#define YAW 1 +#define ROLL 2 + +#define QE_TIMER0 1 +#define QE_TIMER1 2 + +#define PLANE_X 0 +#define PLANE_Y 1 +#define PLANE_Z 2 +#define PLANE_ANYX 3 +#define PLANE_ANYY 4 +#define PLANE_ANYZ 5 + +// #define ON_EPSILON 0.01 + +#define KEY_FORWARD 1 +#define KEY_BACK 2 +#define KEY_TURNLEFT 4 +#define KEY_TURNRIGHT 8 +#define KEY_LEFT 16 +#define KEY_RIGHT 32 +#define KEY_LOOKUP 64 +#define KEY_LOOKDOWN 128 +#define KEY_UP 256 +#define KEY_DOWN 512 + +// xy.c +#define EXCLUDE_LIGHTS 0x00000001 +#define EXCLUDE_ENT 0x00000002 +#define EXCLUDE_PATHS 0x00000004 +#define EXCLUDE_DYNAMICS 0x00000008 +#define EXCLUDE_WORLD 0x00000010 +#define EXCLUDE_CLIP 0x00000020 +//#define EXCLUDE_DETAIL 0x00000040 +#define EXCLUDE_CURVES 0x00000080 +#define INCLUDE_EASY 0x00000100 +#define INCLUDE_NORMAL 0x00000200 +#define INCLUDE_HARD 0x00000400 +#define INCLUDE_DEATHMATCH 0x00000800 +#define EXCLUDE_HINT 0x00001000 +#define EXCLUDE_CAULK 0x00002000 +#define EXCLUDE_ANGLES 0x00004000 +#define EXCLUDE_VISPORTALS 0x00008000 +#define EXCLUDE_NODRAW 0x00010000 +#define EXCLUDE_COMBATNODES 0x00020000 +#define EXCLUDE_TRIGGERS 0x00040000 +// _D3XP +#define EXCLUDE_MODELS 0x00080000 + + +// +// menu indexes for modifying menus +// +#define MENU_VIEW 2 +#define MENU_BSP 4 +#define MENU_TEXTURE 6 +#define MENU_PLUGIN 11 + + +// odd things not in windows header... +#define VK_COMMA 188 +#define VK_PERIOD 190 + +/* +** window bits +*/ +//++timo moved to qertypes.h +// clean +/* +#define W_CAMERA 0x0001 +#define W_XY 0x0002 +#define W_XY_OVERLAY 0x0004 +#define W_Z 0x0008 +#define W_TEXTURE 0x0010 +#define W_Z_OVERLAY 0x0020 +#define W_CONSOLE 0x0040 +#define W_ENTITY 0x0080 +#define W_CAMERA_IFON 0x0100 +#define W_XZ 0x0200 //--| only used for patch vertex manip stuff +#define W_YZ 0x0400 //--| +#define W_ALL 0xFFFFFFFF +*/ + +enum { + COLOR_TEXTUREBACK, + COLOR_GRIDBACK, + COLOR_GRIDMINOR, + COLOR_GRIDMAJOR, + COLOR_CAMERABACK, + COLOR_ENTITY, + COLOR_GRIDBLOCK, + COLOR_GRIDTEXT, + COLOR_BRUSHES, + COLOR_SELBRUSHES, + COLOR_CLIPPER, + COLOR_VIEWNAME, + COLOR_PRECISION_CROSSHAIR, + COLOR_LAST +}; + +// classes +#define ENTITY_WIREFRAME 0x00001 +#define ENTITY_SKIN_MODEL 0x00010 +#define ENTITY_SELECTED_ONLY 0x00100 +#define ENTITY_BOXED 0x01000 + +// menu settings +#define ENTITY_WIRE 0x00001 +#define ENTITY_SKINNED 0x00002 + + +#endif diff --git a/src/tools/radiant/QERTYPES.H b/src/tools/radiant/QERTYPES.H new file mode 100644 index 0000000..ea419d2 --- /dev/null +++ b/src/tools/radiant/QERTYPES.H @@ -0,0 +1,429 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#ifndef _QERTYPE_H +#define _QERTYPE_H + +#define MAXPOINTS 16 + +class texdef_t +{ +public: + texdef_t() + { + name = ""; + shift[0] = shift[1] = 0.0; + rotate = 0; + scale[0] = scale[1] = 0; + value = 0; + } + ~texdef_t() + { + if ( name && name[0] ) { + delete []name; + } + name = NULL; + } + + void SetName( const char *p ) + { + if ( name && name[0] ) { + delete []name; + } + if ( p && p[0] ) { + name = strcpy( new char[strlen(p)+1], p ); + } + else { + name = ""; + } + } + + texdef_t& operator =(const texdef_t& rhs) + { + if ( &rhs != this ) { + SetName(rhs.name); + shift[0] = rhs.shift[0]; + shift[1] = rhs.shift[1]; + rotate = rhs.rotate; + scale[0] = rhs.scale[0]; + scale[1] = rhs.scale[1]; + value = rhs.value; + } + return *this; + } + //char name[128]; + char * name; + float shift[2]; + float rotate; + float scale[2]; + int value; +}; + +// Timo +// new brush primitive texdef +//typedef struct brushprimit_texdef_s +//{ +// float coords[2][3]; +//} brushprimit_texdef_t; + +class brushprimit_texdef_t { +public: + float coords[2][3]; + brushprimit_texdef_t() { + memset(&coords, 0, sizeof(coords)); + coords[0][0] = 1.0; + coords[1][1] = 1.0; + } +}; + +class texturewin_t +{ +public: + texturewin_t() { + memset(&brushprimit_texdef.coords, 0, sizeof(brushprimit_texdef.coords)); + brushprimit_texdef.coords[0][0] = 1.0; + brushprimit_texdef.coords[1][1] = 1.0; + } + + ~texturewin_t() { + } + int width, height; + int originy; + // add brushprimit_texdef_t for brush primitive coordinates storage + brushprimit_texdef_t brushprimit_texdef; + int m_nTotalHeight; + // surface plugin, must be casted to a IPluginTexdef* + void* pTexdef; + texdef_t texdef; +}; + +#define QER_TRANS 0x00000001 +#define QER_NOCARVE 0x00000002 + +typedef struct qtexture_s +{ + struct qtexture_s *next; + char name[64]; // includes partial directory and extension + int width, height; + int contents; + int flags; + int value; + int texture_number; // gl bind number + + // name of the .shader file + char shadername[1024]; // old shader stuff + bool bFromShader; // created from a shader + float fTrans; // amount of transparency + int nShaderFlags; // qer_ shader flags + idVec3 color; // for flat shade mode + bool inuse; // true = is present on the level + + // cast this one to an IPluginQTexture if you are using it + // NOTE: casting can be done with a GETPLUGINQTEXTURE defined in isurfaceplugin.h + // TODO: if the __ISURFACEPLUGIN_H_ header is used, use a union { void *pData; IPluginQTexture *pPluginQTexture } kind of thing ? + void *pData; + + //++timo FIXME: this is the actual filename of the texture + // this will be removed after shader code cleanup + char filename[64]; + +} qtexture_t; + +//++timo texdef and brushprimit_texdef are static +// TODO : do dynamic ? +typedef struct face_s +{ + struct face_s *next; + struct face_s *original; //used for vertex movement + idVec3 planepts[3]; + idVec3 orgplanepts[3]; // used for arbitrary rotation + texdef_t texdef; + + idPlane plane; + idPlane originalPlane; + bool dirty; + + idWinding *face_winding; + + idVec3 d_color; + const idMaterial *d_texture; + + // Timo new brush primit texdef + brushprimit_texdef_t brushprimit_texdef; + + // cast this one to an IPluginTexdef if you are using it + // NOTE: casting can be done with a GETPLUGINTEXDEF defined in isurfaceplugin.h + // TODO: if the __ISURFACEPLUGIN_H_ header is used, use a union { void *pData; IPluginTexdef *pPluginTexdef } kind of thing ? + void *pData; +} face_t; + +typedef struct { + idVec3 xyz; + float sideST[2]; + float capST[2]; +} curveVertex_t; + +typedef struct { + curveVertex_t v[2]; +} sideVertex_t; + + +#define MIN_PATCH_WIDTH 3 +#define MIN_PATCH_HEIGHT 3 + +#define MAX_PATCH_WIDTH 64 +#define MAX_PATCH_HEIGHT 64 + +// patch type info +// type in lower 16 bits, flags in upper +// endcaps directly follow this patch in the list + +// types +#define PATCH_GENERIC 0x00000000 // generic flat patch +#define PATCH_CYLINDER 0x00000001 // cylinder +#define PATCH_BEVEL 0x00000002 // bevel +#define PATCH_ENDCAP 0x00000004 // endcap +#define PATCH_HEMISPHERE 0x00000008 // hemisphere +#define PATCH_CONE 0x00000010 // cone +#define PATCH_TRIANGLE 0x00000020 // simple tri, assumes 3x3 patch + +// behaviour styles +#define PATCH_CAP 0x00001000 // flat patch applied as a cap +#define PATCH_SEAM 0x00002000 // flat patch applied as a seam +#define PATCH_THICK 0x00004000 // patch applied as a thick portion + +// styles +#define PATCH_BEZIER 0x00000000 // default bezier +#define PATCH_BSPLINE 0x10000000 // bspline + +#define PATCH_TYPEMASK 0x00000fff // +#define PATCH_BTYPEMASK 0x0000f000 // +#define PATCH_STYLEMASK 0xffff0000 // + + +struct brush_s; +typedef struct brush_s brush_t; + +typedef struct { + int width, height; // in control points, not patches + int horzSubdivisions; + int vertSubdivisions; + bool explicitSubdivisions; + int contents, flags, value, type; + const idMaterial *d_texture; + idDrawVert *verts; + //idDrawVert *ctrl; + brush_t * pSymbiot; + bool bSelected; + bool bOverlay; + int nListID; + int nListIDCam; + int nListSelected; + + idDict * epairs; + // cast this one to an IPluginTexdef if you are using it + // NOTE: casting can be done with a GETPLUGINTEXDEF defined in isurfaceplugin.h + // TODO: if the __ISURFACEPLUGIN_H_ header is used, use a union { void *pData; IPluginTexdef *pPluginTexdef } kind of thing ? + void * pData; + ID_INLINE idDrawVert &ctrl( int col, int row ) { + if ( col < 0 || col >= width || row < 0 || row >= height ) { + common->Warning( "patchMesh_t::ctrl: control point out of range" ); + return verts[0]; + } + else { + return verts[row * width + col]; + } + } +} patchMesh_t; + +enum { + LIGHT_TARGET, + LIGHT_RIGHT, + LIGHT_UP, + LIGHT_RADIUS, + LIGHT_X, + LIGHT_Y, + LIGHT_Z, + LIGHT_START, + LIGHT_END, + LIGHT_CENTER +}; + + +typedef struct brush_s +{ + struct brush_s *prev, *next; // links in active/selected + struct brush_s *oprev, *onext; // links in entity + brush_t * list; //keep a handy link to the list its in + struct entity_s *owner; + idVec3 mins, maxs; + + idVec3 lightCenter; // for moving the shading center of point lights + idVec3 lightRight; + idVec3 lightTarget; + idVec3 lightUp; + idVec3 lightRadius; + idVec3 lightOffset; + idVec3 lightColor; + idVec3 lightStart; + idVec3 lightEnd; + bool pointLight; + bool startEnd; + int lightTexture; + + bool trackLightOrigin; // this brush is a special case light brush + bool entityModel; + + face_t *brush_faces; + + bool bModelFailed; + // + // curve brush extensions + // all are derived from brush_faces + bool hiddenBrush; + bool forceWireFrame; + bool forceVisibile; + + patchMesh_t *pPatch; + struct entity_s *pUndoOwner; + + int undoId; //undo ID + int redoId; //redo ID + int ownerId; //entityId of the owner entity for undo + + // TTimo: HTREEITEM is MFC, some plugins really don't like it +#ifdef QERTYPES_USE_MFC + int numberId; // brush number + HTREEITEM itemOwner; // owner for grouping +#else + int numberId; + DWORD itemOwner; +#endif + + idRenderModel *modelHandle; + + // brush primitive only + idDict epairs; + +} brush_t; + + +#define MAX_FLAGS 8 + + +typedef struct trimodel_t +{ + idVec3 v[3]; + float st[3][2]; +} trimodel; + + +// eclass show flags + +#define ECLASS_LIGHT 0x00000001 +#define ECLASS_ANGLE 0x00000002 +#define ECLASS_PATH 0x00000004 +#define ECLASS_MISCMODEL 0x00000008 +#define ECLASS_PLUGINENTITY 0x00000010 +#define ECLASS_PROJECTEDLIGHT 0x00000020 +#define ECLASS_WORLDSPAWN 0x00000040 +#define ECLASS_SPEAKER 0x00000080 +#define ECLASS_PARTICLE 0x00000100 +#define ECLASS_ROTATABLE 0x00000200 +#define ECLASS_CAMERAVIEW 0x00000400 +#define ECLASS_MOVER 0x00000800 +#define ECLASS_ENV 0x00001000 +#define ECLASS_COMBATNODE 0x00002000 +#define ECLASS_LIQUID 0x00004000 + +enum EVAR_TYPES { + EVAR_STRING, + EVAR_INT, + EVAR_FLOAT, + EVAR_BOOL, + EVAR_COLOR, + EVAR_MATERIAL, + EVAR_MODEL, + EVAR_GUI, + EVAR_SOUND +}; + +typedef struct evar_s { + int type; + idStr name; + idStr desc; +} evar_t; + +typedef struct eclass_s +{ + struct eclass_s *next; + idStr name; + bool fixedsize; + bool unknown; // wasn't found in source + idVec3 mins, maxs; + idVec3 color; + texdef_t texdef; + idStr comments; + idStr desc; + + idRenderModel *modelHandle; + idRenderModel *entityModel; + + int nFrame; + unsigned int nShowFlags; + idStr defMaterial; + idDict args; + idDict defArgs; + idList vars; + + HMODULE hPlug; +} eclass_t; + +extern eclass_t *eclass; + +/* +** window bits +*/ +#define W_CAMERA 0x0001 +#define W_XY 0x0002 +#define W_XY_OVERLAY 0x0004 +#define W_Z 0x0008 +#define W_TEXTURE 0x0010 +#define W_Z_OVERLAY 0x0020 +#define W_CONSOLE 0x0040 +#define W_ENTITY 0x0080 +#define W_CAMERA_IFON 0x0100 +#define W_XZ 0x0200 //--| only used for patch vertex manip stuff +#define W_YZ 0x0400 //--| +#define W_MEDIA 0x1000 +#define W_GAME 0x2000 +#define W_ALL 0xFFFFFFFF + +// used in some Drawing routines +enum VIEWTYPE {YZ, XZ, XY}; + +#endif diff --git a/src/tools/radiant/Quake4Project.cpp b/src/tools/radiant/Quake4Project.cpp new file mode 100644 index 0000000..6fa6053 --- /dev/null +++ b/src/tools/radiant/Quake4Project.cpp @@ -0,0 +1,55 @@ +/* +=========================================================================== + +Quake 4 Reconstructed GPL Source Code +Copyright (C) 2026 Justin Marshall(IceColdDuke). + +Quake 4 does not ship the Doom-era .qe4 project file. Radiant derives its +small project dictionary from the active engine filesystem instead. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "InspectorDialog.h" + +bool QE_LoadQuake4Project( void ) { + const char *gameCVar = cvarSystem->GetCVarString( "fs_game" ); + idStr game = ( gameCVar != NULL && gameCVar[0] != '\0' ) ? gameCVar : BASE_GAMEDIR; + idStr baseRoot = cvarSystem->GetCVarString( "fs_basepath" ); + idStr devRoot = cvarSystem->GetCVarString( "fs_devpath" ); + if ( devRoot.IsEmpty() ) { + devRoot = baseRoot; + } + + idStr basePath = fileSystem->BuildOSPath( baseRoot, game, "" ); + idStr mapsPath = fileSystem->BuildOSPath( devRoot, game, "maps" ); + idStr autosave1 = fileSystem->BuildOSPath( devRoot, game, "maps/autosave.map" ); + idStr autosave2 = fileSystem->BuildOSPath( devRoot, game, "maps/autosave2.map" ); + + g_qeglobals.d_project_entity = Entity_New(); + g_qeglobals.d_project_entity->brushes.onext = &g_qeglobals.d_project_entity->brushes; + g_qeglobals.d_project_entity->brushes.oprev = &g_qeglobals.d_project_entity->brushes; + SetKeyValue( g_qeglobals.d_project_entity, "basepath", basePath ); + SetKeyValue( g_qeglobals.d_project_entity, "mapspath", mapsPath ); + SetKeyValue( g_qeglobals.d_project_entity, "entitypath", "def" ); + SetKeyValue( g_qeglobals.d_project_entity, "texturepath", "textures" ); + SetKeyValue( g_qeglobals.d_project_entity, "autosave1", autosave1 ); + SetKeyValue( g_qeglobals.d_project_entity, "autosave2", autosave2 ); + SetKeyValue( g_qeglobals.d_project_entity, "brush_primit", "1" ); + SetKeyValue( g_qeglobals.d_project_entity, "bsp", "" ); + SetKeyValue( g_qeglobals.d_project_entity, "bsp -noLight", "" ); + SetKeyValue( g_qeglobals.d_project_entity, "bsp -noOptimize", "" ); + + g_strProject = "Quake 4 filesystem"; + g_qeglobals.m_bBrushPrimitMode = true; + Eclass_InitForSourceDirectory( "def" ); + g_Inspectors->FillClassList(); + Map_New(); + FillBSPMenu(); + common->Printf( "Radiant project: %s (%s)\n", game.c_str(), mapsPath.c_str() ); + return true; +} diff --git a/src/tools/radiant/Radiant.cpp b/src/tools/radiant/Radiant.cpp new file mode 100644 index 0000000..3ffcf25 --- /dev/null +++ b/src/tools/radiant/Radiant.cpp @@ -0,0 +1,481 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "radiant.h" +#include "MainFrm.h" +#include "lightdlg.h" + +#include // for _beginthreadex and _endthreadex +#include // for MSGF_DDEMGR + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +idCVar radiant_entityMode( "radiant_entityMode", "0", CVAR_TOOL | CVAR_ARCHIVE, "" ); + +///////////////////////////////////////////////////////////////////////////// +// CRadiantApp + +BEGIN_MESSAGE_MAP(CRadiantApp, CWinApp) + //{{AFX_MSG_MAP(CRadiantApp) + ON_COMMAND(ID_HELP, OnHelp) + //}}AFX_MSG_MAP + // Standard file based document commands + ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew) + ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen) + // Standard print setup command + ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup) +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CRadiantApp construction + +CRadiantApp::CRadiantApp() +{ + // TODO: add construction code here, + // Place all significant initialization in InitInstance +} + +///////////////////////////////////////////////////////////////////////////// +// The one and only CRadiantApp object + +CRadiantApp theApp; +HINSTANCE g_DoomInstance = NULL; +bool g_editorAlive = false; + +void RadiantPrint( const char *text ) { + if ( g_editorAlive && g_Inspectors ) { + if (g_Inspectors->consoleWnd.GetSafeHwnd()) { + g_Inspectors->consoleWnd.AddText( text ); + } + } +} + +void RadiantShutdown( void ) { + g_editorAlive = false; + if ( g_pParentWnd != NULL && ::IsWindow( g_pParentWnd->GetSafeHwnd() ) ) { + g_pParentWnd->DestroyWindow(); + } + theApp.m_pMainWnd = NULL; + if ( g_qeglobals.rw != NULL ) { + soundSystem->SetRenderWorld( NULL ); + renderSystem->FreeRenderWorld( g_qeglobals.rw ); + g_qeglobals.rw = NULL; + } + g_DoomInstance = NULL; + common->ActivateTool( false ); + ::ShowWindow( win32.hWnd, SW_SHOW ); +} + +/* +================= +RadiantInit + +This is also called when you 'quit' in doom +================= +*/ +void RadiantInit( void ) { + + // make sure the renderer is initialized + if ( !renderSystem->IsOpenGLRunning() ) { + common->Printf( "no OpenGL running\n" ); + return; + } + + g_editorAlive = true; + common->ActivateTool( true ); + + // allocate a renderWorld and a soundWorld + if ( g_qeglobals.rw == NULL ) { + g_qeglobals.rw = renderSystem->AllocRenderWorld(); + g_qeglobals.rw->InitFromMap( NULL ); + } + g_qeglobals.sw = SOUNDWORLD_EDITOR; + soundSystem->SetRenderWorld( g_qeglobals.rw ); + + if ( g_DoomInstance ) { + if ( ::IsWindowVisible( win32.hWnd ) ) { + ::ShowWindow( win32.hWnd, SW_HIDE ); + g_pParentWnd->ShowWindow( SW_SHOW ); + g_pParentWnd->SetFocus(); + } + } else { + Sys_GrabMouseCursor( false ); + + g_DoomInstance = win32.hInstance; + CWinApp* pApp = AfxGetApp(); + CWinThread *pThread = AfxGetThread(); + + InitAfx(); + + // App global initializations (rare) + pApp->InitApplication(); + + // Perform specific initializations + pThread->InitInstance(); + + qglFinish(); + //qwglMakeCurrent(0, 0); + qwglMakeCurrent(win32.hDC, win32.hGLRC); + + // hide the doom window by default + ::ShowWindow( win32.hWnd, SW_HIDE ); + } +} + + +extern void Map_VerifyCurrentMap(const char *map); + +void RadiantSync( const char *mapName, const idVec3 &viewOrg, const idAngles &viewAngles ) { + if ( g_DoomInstance == NULL ) { + RadiantInit(); + } + + if ( g_DoomInstance ) { + idStr osPath; + osPath = fileSystem->RelativePathToOSPath( mapName ); + Map_VerifyCurrentMap( osPath ); + idAngles flip = viewAngles; + flip.pitch = -flip.pitch; + g_pParentWnd->GetCamera()->SetView( viewOrg, flip ); + g_pParentWnd->SetFocus(); + Sys_UpdateWindows( W_ALL ); + g_pParentWnd->RoutineProcessing(); + } +} + +void RadiantRun( void ) { + static bool exceptionErr = false; + int show = ::IsWindowVisible(win32.hWnd); + + try { + if (!exceptionErr && !show) { + //qglPushAttrib(GL_ALL_ATTRIB_BITS); + qglDepthMask(true); + theApp.Run(); + //qglPopAttrib(); + //qwglMakeCurrent(0, 0); + qwglMakeCurrent(win32.hDC, win32.hGLRC); + } + } + catch( idException &ex ) { + ::MessageBox(NULL, ex.error, "Exception error", MB_OK); + RadiantShutdown(); + } +} + +///////////////////////////////////////////////////////////////////////////// +// CRadiantApp initialization + +HINSTANCE g_hOpenGL32 = NULL; +HINSTANCE g_hOpenGL = NULL; +bool g_bBuildList = false; + +BOOL CRadiantApp::InitInstance() +{ + //g_hOpenGL32 = ::LoadLibrary("opengl32.dll"); + AfxEnableControlContainer(); + + // Standard initialization + // If you are not using these features and wish to reduce the size + // of your final executable, you should remove from the following + // the specific initialization routines you do not need. + //AfxEnableMemoryTracking(FALSE); + +#ifdef _AFXDLL + //Enable3dControls(); // Call this when using MFC in a shared DLL +#else + //Enable3dControlsStatic(); // Call this when linking to MFC statically +#endif + + // If there's a .INI file in the directory use it instead of registry + + char RadiantPath[_MAX_PATH]; + GetModuleFileName( NULL, RadiantPath, _MAX_PATH ); + + // search for exe + CFileFind Finder; + Finder.FindFile( RadiantPath ); + Finder.FindNextFile(); + // extract root + CString Root = Finder.GetRoot(); + // build root\*.ini + CString IniPath = Root + "\\REGISTRY.INI"; + // search for ini file + Finder.FindNextFile(); + if (Finder.FindFile( IniPath )) + { + Finder.FindNextFile(); + // use the .ini file instead of the registry + free((void*)m_pszProfileName); + m_pszProfileName=_tcsdup(_T(Finder.GetFilePath())); + // look for the registry key for void* buffers storage ( these can't go into .INI files ) + int i=0; + CString key; + HKEY hkResult; + DWORD dwDisp; + DWORD type; + char iBuf[3]; + do + { + sprintf( iBuf, "%d", i ); + key = "Software\\Q3Radiant\\IniPrefs" + CString(iBuf); + // does this key exists ? + if ( RegOpenKeyEx( HKEY_CURRENT_USER, key, 0, KEY_ALL_ACCESS, &hkResult ) != ERROR_SUCCESS ) + { + // this key doesn't exist, so it's the one we'll use + strcpy( g_qeglobals.use_ini_registry, key.GetBuffer(0) ); + RegCreateKeyEx( HKEY_CURRENT_USER, key, 0, NULL, + REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkResult, &dwDisp ); + RegSetValueEx( hkResult, "RadiantName", 0, REG_SZ, reinterpret_cast(RadiantPath), strlen( RadiantPath )+1 ); + RegCloseKey( hkResult ); + break; + } + else + { + char RadiantAux[ _MAX_PATH ]; + unsigned long size = _MAX_PATH; + // the key exists, is it the one we are looking for ? + RegQueryValueEx( hkResult, "RadiantName", 0, &type, reinterpret_cast(RadiantAux), &size ); + RegCloseKey( hkResult ); + if ( !strcmp( RadiantAux, RadiantPath ) ) + { + // got it ! + strcpy( g_qeglobals.use_ini_registry, key.GetBuffer(0) ); + break; + } + } + i++; + } while (1); + g_qeglobals.use_ini = true; + } + else + { + // Change the registry key under which our settings are stored. + SetRegistryKey( EDITOR_REGISTRY_KEY ); + g_qeglobals.use_ini = false; + } + + LoadStdProfileSettings(); // Load standard INI file options (including MRU) + + + // Register the application's document templates. Document templates + // serve as the connection between documents, frame windows and views. + +// CMultiDocTemplate* pDocTemplate; +// pDocTemplate = new CMultiDocTemplate( +// IDR_RADIANTYPE, +// RUNTIME_CLASS(CRadiantDoc), +// RUNTIME_CLASS(CMainFrame), // custom MDI child frame +// RUNTIME_CLASS(CRadiantView)); +// AddDocTemplate(pDocTemplate); + + // create main MDI Frame window + + g_PrefsDlg.LoadPrefs(); + + qglEnableClientState( GL_VERTEX_ARRAY ); + + CString strTemp = m_lpCmdLine; + strTemp.MakeLower(); + if (strTemp.Find("builddefs") >= 0) { + g_bBuildList = true; + } + + CMainFrame* pMainFrame = new CMainFrame; + if (!pMainFrame->LoadFrame(IDR_MENU_QUAKE3)) { + return FALSE; + } + + if (pMainFrame->m_hAccelTable) { + ::DestroyAcceleratorTable(pMainFrame->m_hAccelTable); + } + + pMainFrame->LoadAccelTable(MAKEINTRESOURCE(IDR_MINIACCEL)); + + m_pMainWnd = pMainFrame; + + // The main window has been initialized, so show and update it. + pMainFrame->ShowWindow(m_nCmdShow); + pMainFrame->UpdateWindow(); + + return TRUE; +} + +///////////////////////////////////////////////////////////////////////////// +// CRadiantApp commands + +int CRadiantApp::ExitInstance() +{ + g_pParentWnd = NULL; + return CWinApp::ExitInstance(); +} + + +BOOL CRadiantApp::OnIdle(LONG lCount) { + if (g_pParentWnd) { + g_pParentWnd->RoutineProcessing(); + } + return FALSE; + //return CWinApp::OnIdle(lCount); +} + +void CRadiantApp::OnHelp() +{ + ShellExecute(m_pMainWnd->GetSafeHwnd(), "open", "http://www.idDevNet.com", NULL, NULL, SW_SHOW); +} + +int CRadiantApp::Run( void ) +{ + BOOL bIdle = TRUE; + LONG lIdleCount = 0; + + +#if _MSC_VER >= 1300 + MSG *msg = AfxGetCurrentMessage(); // TODO Robert fix me!! +#else + MSG *msg = &m_msgCur; +#endif + + // phase1: check to see if we can do idle work + while (bIdle && !::PeekMessage(msg, NULL, NULL, NULL, PM_NOREMOVE)) { + // call OnIdle while in bIdle state + if (!OnIdle(lIdleCount++)) { + bIdle = FALSE; // assume "no idle" state + } + } + + // phase2: pump messages while available + do { + // pump message, but quit on WM_QUIT + if (!PumpMessage()) { + return ExitInstance(); + } + + // reset "no idle" state after pumping "normal" message + if (IsIdleMessage(msg)) { + bIdle = TRUE; + lIdleCount = 0; + } + + } while (::PeekMessage(msg, NULL, NULL, NULL, PM_NOREMOVE)); + + return 0; +} + + +/* +============================================================= + +REGISTRY INFO + +============================================================= +*/ + +bool SaveRegistryInfo(const char *pszName, void *pvBuf, long lSize) +{ + SetCvarBinary(pszName, pvBuf, lSize); + common->WriteFlaggedCVarsToFile( "editor.cfg", CVAR_TOOL, "sett" ); + return true; +} + +bool LoadRegistryInfo(const char *pszName, void *pvBuf, long *plSize) +{ + return GetCvarBinary(pszName, pvBuf, *plSize); +} + +bool SaveWindowState(HWND hWnd, const char *pszName) +{ + RECT rc; + GetWindowRect(hWnd, &rc); + if (hWnd != g_pParentWnd->GetSafeHwnd()) { + if (::GetParent(hWnd) != g_pParentWnd->GetSafeHwnd()) { + ::SetParent(hWnd, g_pParentWnd->GetSafeHwnd()); + } + MapWindowPoints(NULL, g_pParentWnd->GetSafeHwnd(), (POINT *)&rc, 2); + } + return SaveRegistryInfo(pszName, &rc, sizeof(rc)); +} + + +bool LoadWindowState(HWND hWnd, const char *pszName) +{ + RECT rc; + LONG lSize = sizeof(rc); + + if (LoadRegistryInfo(pszName, &rc, &lSize)) + { + if (rc.left < 0) + rc.left = 0; + if (rc.top < 0) + rc.top = 0; + if (rc.right < rc.left + 16) + rc.right = rc.left + 16; + if (rc.bottom < rc.top + 16) + rc.bottom = rc.top + 16; + + MoveWindow(hWnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, FALSE); + return true; + } + + return false; +} + +/* +=============================================================== + + STATUS WINDOW + +=============================================================== +*/ + +void Sys_UpdateStatusBar( void ) +{ + extern int g_numbrushes, g_numentities; + + char numbrushbuffer[100] = ""; + + sprintf( numbrushbuffer, "Brushes: %d Entities: %d", g_numbrushes, g_numentities ); + Sys_Status( numbrushbuffer, 2 ); +} + +void Sys_Status(const char *psz, int part ) +{ + if ( part < 0 ) { + common->Printf("%s", psz); + part = 0; + } + g_pParentWnd->SetStatusText(part, psz); +} diff --git a/src/tools/radiant/Radiant.h b/src/tools/radiant/Radiant.h new file mode 100644 index 0000000..864128a --- /dev/null +++ b/src/tools/radiant/Radiant.h @@ -0,0 +1,78 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#if !defined(AFX_RADIANT_H__330BBF06_731C_11D1_B539_00AA00A410FC__INCLUDED_) +#define AFX_RADIANT_H__330BBF06_731C_11D1_B539_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + +#ifndef __AFXWIN_H__ + #error include 'stdafx.h' before including this file for PCH +#endif + +///////////////////////////////////////////////////////////////////////////// +// CRadiantApp: +// See Radiant.cpp for the implementation of this class +// + +class CRadiantApp : public CWinApp +{ + +public: + CRadiantApp(); + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CRadiantApp) + public: + virtual BOOL InitInstance(); + virtual int ExitInstance(); + virtual BOOL OnIdle(LONG lCount); + virtual int Run( void ); + //}}AFX_VIRTUAL + +// Implementation + + //{{AFX_MSG(CRadiantApp) + afx_msg void OnHelp(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#define DATA_TO_DIALOG FALSE +#define DIALOG_TO_DATA TRUE + +#endif // !defined(AFX_RADIANT_H__330BBF06_731C_11D1_B539_00AA00A410FC__INCLUDED_) diff --git a/src/tools/radiant/RotateDlg.cpp b/src/tools/radiant/RotateDlg.cpp new file mode 100644 index 0000000..752a274 --- /dev/null +++ b/src/tools/radiant/RotateDlg.cpp @@ -0,0 +1,137 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" +#include "RotateDlg.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CRotateDlg dialog + + +CRotateDlg::CRotateDlg(CWnd* pParent /*=NULL*/) + : CDialog(CRotateDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(CRotateDlg) + m_strX = _T(""); + m_strY = _T(""); + m_strZ = _T(""); + //}}AFX_DATA_INIT +} + + +void CRotateDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CRotateDlg) + DDX_Control(pDX, IDC_SPIN3, m_wndSpin3); + DDX_Control(pDX, IDC_SPIN2, m_wndSpin2); + DDX_Control(pDX, IDC_SPIN1, m_wndSpin1); + DDX_Text(pDX, IDC_ROTX, m_strX); + DDX_Text(pDX, IDC_ROTY, m_strY); + DDX_Text(pDX, IDC_ROTZ, m_strZ); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CRotateDlg, CDialog) + //{{AFX_MSG_MAP(CRotateDlg) + ON_BN_CLICKED(IDC_APPLY, OnApply) + ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN1, OnDeltaposSpin1) + ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN2, OnDeltaposSpin2) + ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN3, OnDeltaposSpin3) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CRotateDlg message handlers + +void CRotateDlg::OnOK() +{ + OnApply(); + CDialog::OnOK(); +} + +void CRotateDlg::OnApply() +{ + UpdateData(TRUE); + float f = atof(m_strX); + if (f != 0.0) + Select_RotateAxis(0,f); + f = atof(m_strY); + if (f != 0.0) + Select_RotateAxis(1,f); + f = atof(m_strZ); + if (f != 0.0) + Select_RotateAxis(2,f); +} + +BOOL CRotateDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + m_wndSpin1.SetRange(0, 359); + m_wndSpin2.SetRange(0, 359); + m_wndSpin3.SetRange(0, 359); + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void CRotateDlg::OnDeltaposSpin1(NMHDR* pNMHDR, LRESULT* pResult) +{ + NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR; + Select_RotateAxis(0, pNMUpDown->iDelta); + *pResult = 0; +} + +void CRotateDlg::OnDeltaposSpin2(NMHDR* pNMHDR, LRESULT* pResult) +{ + NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR; + Select_RotateAxis(1, pNMUpDown->iDelta); + *pResult = 0; +} + +void CRotateDlg::OnDeltaposSpin3(NMHDR* pNMHDR, LRESULT* pResult) +{ + NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR; + Select_RotateAxis(2, pNMUpDown->iDelta); + *pResult = 0; +} + +void CRotateDlg::ApplyNoPaint() +{ + +} diff --git a/src/tools/radiant/RotateDlg.h b/src/tools/radiant/RotateDlg.h new file mode 100644 index 0000000..804d9a2 --- /dev/null +++ b/src/tools/radiant/RotateDlg.h @@ -0,0 +1,84 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#if !defined(AFX_ROTATEDLG_H__D4B79152_7A7E_11D1_B541_00AA00A410FC__INCLUDED_) +#define AFX_ROTATEDLG_H__D4B79152_7A7E_11D1_B541_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// RotateDlg.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CRotateDlg dialog + +class CRotateDlg : public CDialog +{ +// Construction +public: + CRotateDlg(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CRotateDlg) + enum { IDD = IDD_ROTATE }; + CSpinButtonCtrl m_wndSpin3; + CSpinButtonCtrl m_wndSpin2; + CSpinButtonCtrl m_wndSpin1; + CString m_strX; + CString m_strY; + CString m_strZ; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CRotateDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + void ApplyNoPaint(); + + // Generated message map functions + //{{AFX_MSG(CRotateDlg) + virtual void OnOK(); + afx_msg void OnApply(); + virtual BOOL OnInitDialog(); + afx_msg void OnDeltaposSpin1(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnDeltaposSpin2(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnDeltaposSpin3(NMHDR* pNMHDR, LRESULT* pResult); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_ROTATEDLG_H__D4B79152_7A7E_11D1_B541_00AA00A410FC__INCLUDED_) diff --git a/src/tools/radiant/SELECT.CPP b/src/tools/radiant/SELECT.CPP new file mode 100644 index 0000000..3ec63b0 --- /dev/null +++ b/src/tools/radiant/SELECT.CPP @@ -0,0 +1,2210 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "../../renderer/model_local.h" // for idRenderModelPrt + +// externs +CPtrArray g_SelectedFaces; +CPtrArray g_SelectedFaceBrushes; +CPtrArray &g_ptrSelectedFaces = g_SelectedFaces; +CPtrArray &g_ptrSelectedFaceBrushes = g_SelectedFaceBrushes; + +extern void Brush_Resize(brush_t *b, idVec3 vMin, idVec3 vMax); + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +qertrace_t Test_Ray(const idVec3 &origin, const idVec3 &dir, int flags) { + brush_t *brush; + face_t *face; + float dist; + qertrace_t t; + + memset(&t, 0, sizeof(t)); + t.dist = HUGE_DISTANCE*2; + + // check for points first + CDragPoint *drag = PointRay(origin, dir, &dist); + if (drag) { + t.dist = dist; + t.brush = NULL; + t.face = NULL; + t.point = drag; + t.selected = false; + return t; + } + + if (flags & SF_CYCLE) { + CPtrArray array; + brush_t *pToSelect = (selected_brushes.next != &selected_brushes) ? selected_brushes.next : NULL; + Select_Deselect(); + + // go through active brushes and accumulate all "hit" brushes + for (brush = active_brushes.next; brush != &active_brushes; brush = brush->next) { + // if ( (flags & SF_ENTITIES_FIRST) && brush->owner == world_entity) continue; + if (FilterBrush(brush)) { + continue; + } + + if (g_PrefsDlg.m_selectOnlyBrushes) { + if (brush->pPatch || brush->modelHandle > 0) { + continue; + } + } + + if (g_PrefsDlg.m_selectNoModels) { + if (brush->modelHandle > 0) { + continue; + } + } + + // if (!g_bShowPatchBounds && brush->pPatch) continue; + face = Brush_Ray(origin, dir, brush, &dist, true); + + if (face) { + array.Add(brush); + } + } + + int nSize = array.GetSize(); + if (nSize > 0) { + bool bFound = false; + for (int i = 0; i < nSize; i++) { + brush_t *b = reinterpret_cast < brush_t * > (array.GetAt(i)); + + // did we hit the last one selected yet ? + if (b == pToSelect) { + // yes we want to select the next one in the list + int n = (i > 0) ? i - 1 : nSize - 1; + pToSelect = reinterpret_cast < brush_t * > (array.GetAt(n)); + bFound = true; + break; + } + } + + if (!bFound) { + pToSelect = reinterpret_cast < brush_t * > (array.GetAt(0)); + } + } + + if (pToSelect) { + face = Brush_Ray(origin, dir, pToSelect, &dist, true); + t.dist = dist; + t.brush = pToSelect; + t.face = face; + t.selected = false; + return t; + } + } + + if (!(flags & SF_SELECTED_ONLY)) { + for (brush = active_brushes.next; brush != &active_brushes; brush = brush->next) { + if ((flags & SF_ENTITIES_FIRST) && brush->owner == world_entity) { + continue; + } + + if (FilterBrush(brush)) { + continue; + } + + if (g_PrefsDlg.m_selectOnlyBrushes) { + if (brush->pPatch || brush->modelHandle > 0) { + continue; + } + } + + if (g_PrefsDlg.m_selectNoModels) { + if (brush->modelHandle > 0) { + continue; + } + } + + face = Brush_Ray(origin, dir, brush, &dist, true); + if (dist > 0 && dist < t.dist) { + t.dist = dist; + t.brush = brush; + t.face = face; + t.selected = false; + } + } + } + + for (brush = selected_brushes.next; brush != &selected_brushes; brush = brush->next) { + if ((flags & SF_ENTITIES_FIRST) && brush->owner == world_entity) { + continue; + } + + if (FilterBrush(brush)) { + continue; + } + + if (g_PrefsDlg.m_selectOnlyBrushes) { + if (brush->pPatch || brush->modelHandle > 0) { + continue; + } + } + + if (g_PrefsDlg.m_selectNoModels) { + if (brush->modelHandle > 0) { + continue; + } + } + + face = Brush_Ray(origin, dir, brush, &dist, true); + if (dist > 0 && dist < t.dist) { + t.dist = dist; + t.brush = brush; + t.face = face; + t.selected = true; + } + } + + // if entites first, but didn't find any, check regular + if ((flags & SF_ENTITIES_FIRST) && t.brush == NULL) { + return Test_Ray(origin, dir, flags - SF_ENTITIES_FIRST); + } + + return t; +} + +extern void AddSelectablePoint(brush_t *b, idVec3 v, int type, bool priority); +extern void ClearSelectablePoints(brush_t *b); +extern idVec3 Brush_TransformedPoint(brush_t *b, const idVec3 &in); + +/* + ======================================================================================================================= + Select_Brush + ======================================================================================================================= + */ +void Select_Brush(brush_t *brush, bool bComplete, bool bStatus) { + brush_t *b; + entity_t *e; + + g_ptrSelectedFaces.RemoveAll(); + g_ptrSelectedFaceBrushes.RemoveAll(); + + // selected_face = NULL; + if (g_qeglobals.d_select_count < MAX_MAP_ENTITIES) { + g_qeglobals.d_select_order[g_qeglobals.d_select_count] = brush; + } + + g_qeglobals.d_select_count++; + + e = brush->owner; + if (e) { + + if ( e == world_entity && radiant_entityMode.GetBool() ) { + return; + } + // select complete entity on first click + if (e != world_entity && bComplete == true) { + for (b = selected_brushes.next; b != &selected_brushes; b = b->next) { + if (b->owner == e) { + goto singleselect; + } + } + + for (b = e->brushes.onext; b != &e->brushes; b = b->onext) { + Brush_RemoveFromList(b); + Brush_AddToList(b, &selected_brushes); + } + } + else + { +singleselect: + Brush_RemoveFromList(brush); + Brush_AddToList(brush, &selected_brushes); + UpdateSurfaceDialog(); + UpdatePatchInspector(); + UpdateLightInspector(); + } + + if (e->eclass) { + g_Inspectors->UpdateEntitySel(brush->owner->eclass); + if ( radiant_entityMode.GetBool() && e->eclass->nShowFlags & (ECLASS_LIGHT | ECLASS_SPEAKER) ) { + const char *p = ValueForKey(e, "s_shader"); + if (p && *p) { + g_Inspectors->mediaDlg.SelectCurrentItem(true, p, CDialogTextures::SOUNDS); + } + + } + if ( ( e->eclass->nShowFlags & ECLASS_LIGHT ) && !brush->entityModel ) { + if (brush->pointLight) { + // add center drag point if not at the origin + if (brush->lightCenter[0] || brush->lightCenter[1] || brush->lightCenter[2]) { + AddSelectablePoint(brush, Brush_TransformedPoint(brush, brush->lightCenter), LIGHT_CENTER, false); + } + } + else { + AddSelectablePoint(brush, Brush_TransformedPoint(brush, brush->lightTarget), LIGHT_TARGET, true); + AddSelectablePoint(brush, Brush_TransformedPoint(brush, brush->lightUp), LIGHT_UP, false); + AddSelectablePoint(brush, Brush_TransformedPoint(brush, brush->lightRight), LIGHT_RIGHT, false); + if (brush->startEnd) { + AddSelectablePoint(brush, Brush_TransformedPoint(brush, brush->lightStart), LIGHT_START, false); + AddSelectablePoint(brush, Brush_TransformedPoint(brush, brush->lightEnd), LIGHT_END, false); + } + } + UpdateLightInspector(); + } + if (e->eclass->nShowFlags & ECLASS_CAMERAVIEW) { + g_pParentWnd->GetCamera()->UpdateCameraView(); + } + } + } + + if (bStatus) { + idVec3 vMin, vMax, vSize; + Select_GetBounds(vMin, vMax); + VectorSubtract(vMax, vMin, vSize); + + CString strStatus; + strStatus.Format("Selection X:: %.1f Y:: %.1f Z:: %.1f", vSize[0], vSize[1], vSize[2]); + g_pParentWnd->SetStatusText(2, strStatus); + } +} + +/* + ======================================================================================================================= + Select_Ray If the origin is inside a brush, that brush will be ignored. + ======================================================================================================================= + */ +void Select_Ray(idVec3 origin, idVec3 dir, int flags) { + qertrace_t t; + + t = Test_Ray(origin, dir, flags); + + if (!t.brush) { + return; + } + + if (flags == SF_SINGLEFACE) { + int nCount = g_SelectedFaces.GetSize(); + bool bOk = true; + for (int i = 0; i < nCount; i++) { + if (t.face == reinterpret_cast < face_t * > (g_SelectedFaces.GetAt(i))) { + bOk = false; + + // need to move remove i'th entry + g_SelectedFaces.RemoveAt(i, 1); + g_SelectedFaceBrushes.RemoveAt(i, 1); + nCount--; + } + } + + if (bOk) { + if ( t.selected ) { + face_t *face; + + // DeSelect brush + Brush_RemoveFromList(t.brush); + Brush_AddToList(t.brush, &active_brushes); + + // Select all brush faces + for ( face = t.brush->brush_faces; face; face = face->next ) { + //Don't add face that was clicked + if ( face != t.face ) { + g_SelectedFaces.Add( face ); + g_SelectedFaceBrushes.Add( t.brush ); + } + } + } else { + g_SelectedFaces.Add(t.face); + g_SelectedFaceBrushes.Add(t.brush); + } + } + + // selected_face = t.face; selected_face_brush = t.brush; + Sys_UpdateWindows(W_ALL); + g_qeglobals.d_select_mode = sel_brush; + + + //common->Printf("before\n"); + //extern void Face_Info_BrushPrimit(face_t *face); + //Face_Info_BrushPrimit(t.face); + //common->Printf("after\n"); + + // + // Texture_SetTexture requires a brushprimit_texdef fitted to the default width=2 + // height=2 texture + // + brushprimit_texdef_t brushprimit_texdef; + ConvertTexMatWithQTexture(&t.face->brushprimit_texdef, t.face->d_texture, &brushprimit_texdef, NULL); + Texture_SetTexture(&t.face->texdef, &brushprimit_texdef, false, false); + UpdateSurfaceDialog(); + + + return; + } + + // move the brush to the other list + g_qeglobals.d_select_mode = sel_brush; + + if (t.selected) { + Brush_RemoveFromList(t.brush); + Brush_AddToList(t.brush, &active_brushes); + UpdatePatchInspector(); + UpdateSurfaceDialog(); + + entity_t *e = t.brush->owner; + if (e->eclass->nShowFlags & ECLASS_LIGHT && !t.brush->entityModel) { + if (t.brush->pointLight) { + } + else { + ClearSelectablePoints(t.brush); + } + } + } + else { + Select_Brush(t.brush, !(GetAsyncKeyState(VK_MENU) & 0x8000)); + } + + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Select_Delete(void) { + brush_t *brush; + + g_ptrSelectedFaces.RemoveAll(); + g_ptrSelectedFaceBrushes.RemoveAll(); + + // selected_face = NULL; + g_qeglobals.d_select_mode = sel_brush; + + g_qeglobals.d_select_count = 0; + g_qeglobals.d_num_move_points = 0; + while (selected_brushes.next != &selected_brushes) { + brush = selected_brushes.next; + if (brush->pPatch) { + // Patch_Delete(brush->nPatchID); + Patch_Delete(brush->pPatch); + } + + Brush_Free(brush); + } + + // FIXME: remove any entities with no brushes + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Select_Deselect(bool bDeselectFaces) { + brush_t *b; + + ClearSelectablePoints(NULL); + Patch_Deselect(); + + g_pParentWnd->ActiveXY()->UndoClear(); + + g_qeglobals.d_workcount++; + g_qeglobals.d_select_count = 0; + g_qeglobals.d_num_move_points = 0; + b = selected_brushes.next; + + if (b == &selected_brushes) { + if (bDeselectFaces) { + g_ptrSelectedFaces.RemoveAll(); + g_ptrSelectedFaceBrushes.RemoveAll(); + + // selected_face = NULL; + } + + Sys_UpdateWindows(W_ALL); + return; + } + + if (bDeselectFaces) { + g_ptrSelectedFaces.RemoveAll(); + g_ptrSelectedFaceBrushes.RemoveAll(); + + // selected_face = NULL; + } + + g_qeglobals.d_select_mode = sel_brush; + + // grab top / bottom height for new brushes + if (b->mins[2] < b->maxs[2]) { + g_qeglobals.d_new_brush_bottom = b->mins; + g_qeglobals.d_new_brush_top = b->maxs; + } + + selected_brushes.next->prev = &active_brushes; + selected_brushes.prev->next = active_brushes.next; + active_brushes.next->prev = selected_brushes.prev; + active_brushes.next = selected_brushes.next; + selected_brushes.prev = selected_brushes.next = &selected_brushes; + + g_pParentWnd->GetCamera()->UpdateCameraView(); + + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + Select_Move + ======================================================================================================================= + */ +void Select_Move(idVec3 delta, bool bSnap) { + brush_t *b; + + // actually move the selected brushes + bool updateOrigin = true; + entity_t *lastOwner = selected_brushes.next->owner; + + for (b = selected_brushes.next; b != &selected_brushes; b = b->next) { + Brush_Move(b, delta, bSnap, updateOrigin); + if (updateOrigin) { + updateOrigin = false; + } + + if (b->next->owner != lastOwner) { + updateOrigin = true; + lastOwner = b->next->owner; + } + } + + idVec3 vMin, vMax; + Select_GetBounds(vMin, vMax); + + CString strStatus; + strStatus.Format("Origin X:: %.1f Y:: %.1f Z:: %.1f", vMin[0], vMax[1], vMax[2]); + g_pParentWnd->SetStatusText(2, strStatus); + g_pParentWnd->GetCamera()->UpdateCameraView(); + + // Sys_UpdateWindows (W_ALL); +} + +/* + ======================================================================================================================= + Select_Clone Creates an exact duplicate of the selection in place, then moves the selected brushes off of their old + positions + ======================================================================================================================= + */ +void Select_Clone(void) { + ASSERT(g_pParentWnd->ActiveXY()); + g_bScreenUpdates = false; + g_pParentWnd->ActiveXY()->Copy(); + g_pParentWnd->ActiveXY()->Paste(); + g_pParentWnd->NudgeSelection(2, g_qeglobals.d_gridsize); + g_pParentWnd->NudgeSelection(3, g_qeglobals.d_gridsize); + g_bScreenUpdates = true; + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + Select_SetTexture Timo:: bFitScale to compute scale on the plane and counteract plane / axial plane snapping Timo:: + brush primitive texturing the brushprimit_texdef given must be understood as a qtexture_t width=2 height=2 ( HiRes + ) Timo:: texture plugin, added an IPluginTexdef* parameter must be casted to an IPluginTexdef! if not NULL, get + ->Copy() of it into each face or brush ( and remember to hook ) if NULL, means we have no information, ask for a + default + ======================================================================================================================= + */ +void WINAPI Select_SetTexture(texdef_t *texdef,brushprimit_texdef_t *brushprimit_texdef,bool bFitScale,void *pPlugTexdef,bool update) { + brush_t *b; + int nCount = g_ptrSelectedFaces.GetSize(); + if (nCount > 0) { + Undo_Start("set face textures"); + ASSERT(g_ptrSelectedFaces.GetSize() == g_ptrSelectedFaceBrushes.GetSize()); + for (int i = 0; i < nCount; i++) { + face_t *selFace = reinterpret_cast < face_t * > (g_ptrSelectedFaces.GetAt(i)); + brush_t *selBrush = reinterpret_cast < brush_t * > (g_ptrSelectedFaceBrushes.GetAt(i)); + Undo_AddBrush(selBrush); + SetFaceTexdef(selBrush,selFace,texdef,brushprimit_texdef,bFitScale); + Brush_Build(selBrush, bFitScale); + Undo_EndBrush(selBrush); + } + + Undo_End(); + } + else if (selected_brushes.next != &selected_brushes) { + Undo_Start("set brush textures"); + for (b = selected_brushes.next; b != &selected_brushes; b = b->next) { + if (!b->owner->eclass->fixedsize) { + Undo_AddBrush(b); + Brush_SetTexture(b, texdef, brushprimit_texdef, bFitScale); + Undo_EndBrush(b); + } else if (b->owner->eclass->nShowFlags & ECLASS_LIGHT) { + if ( idStr::Cmpn(texdef->name, "lights/", strlen("lights/")) == 0 ) { + SetKeyValue(b->owner, "texture", texdef->name); + g_Inspectors->UpdateEntitySel(b->owner->eclass); + UpdateLightInspector(); + Brush_Build(b); + } else { + Undo_AddBrush(b); + Brush_SetTexture(b, texdef, brushprimit_texdef, bFitScale); + Undo_EndBrush(b); + } + } + } + + Undo_End(); + } + + if (update) { + Sys_UpdateWindows(W_ALL); + } +} + +/* + ======================================================================================================================= + TRANSFORMATIONS + ======================================================================================================================= + */ +void Select_GetBounds(idVec3 &mins, idVec3 &maxs) { + brush_t *b; + int i; + + for (i = 0; i < 3; i++) { + mins[i] = 999999; + maxs[i] = -999999; + } + + for (b = selected_brushes.next; b != &selected_brushes; b = b->next) { + for (i = 0; i < 3; i++) { + if (b->mins[i] < mins[i]) { + mins[i] = b->mins[i]; + } + + if (b->maxs[i] > maxs[i]) { + maxs[i] = b->maxs[i]; + } + } + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Select_GetTrueMid(idVec3 &mid) { + idVec3 mins, maxs; + Select_GetBounds(mins, maxs); + + for (int i = 0; i < 3; i++) { + mid[i] = (mins[i] + ((maxs[i] - mins[i]) / 2)); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Select_GetMid(idVec3 &mid) { +#if 0 + Select_GetTrueMid(mid); + return; +#else + idVec3 mins, maxs; + int i; + + //if (g_PrefsDlg.m_bNoClamp) { + // Select_GetTrueMid(mid); + // return; + //} + + Select_GetBounds(mins, maxs); + + for (i = 0; i < 3; i++) { + mid[i] = g_qeglobals.d_gridsize * floor(((mins[i] + maxs[i]) * 0.5) / g_qeglobals.d_gridsize); + } +#endif +} + +idVec3 select_origin; +idMat3 select_matrix; +idMat3 select_bmatrix; +idRotation select_rotation; +bool select_fliporder; +int select_flipAxis; +float select_orgDeg; + +void Select_InitializeRotation() { + for (brush_t *b = selected_brushes.next; b != &selected_brushes; b = b->next) { + for (face_t *f = b->brush_faces; f; f = f->next) { + for (int i = 0; i < 3; i++) { + f->orgplanepts[i] = f->planepts[i]; + } + } + } + select_orgDeg = 0.0; +} + +void Select_FinalizeRotation() { + +} + +bool Select_OnlyModelsSelected() { + for (brush_t *b = selected_brushes.next; b != &selected_brushes; b = b->next) { + if (!b->modelHandle) { + return false; + } + } + return true; +} + +bool OkForRotationKey(brush_t *b) { + if (b->owner->eclass->nShowFlags & ECLASS_WORLDSPAWN) { + return false; + } + + if (stricmp(b->owner->epairs.GetString("name"), b->owner->epairs.GetString("model")) == 0) { + return false; + } + + return true; +} + +/* +================= +VectorRotate3 + + rotation order is roll - pitch - yaw +================= +*/ +void VectorRotate3( const idVec3 &vIn, const idVec3 &vRotation, idVec3 &out) { +#if 1 + int i, nIndex[3][2]; + idVec3 vWork, va; + + va = vIn; + vWork = va; + nIndex[0][0] = 1; nIndex[0][1] = 2; + nIndex[1][0] = 2; nIndex[1][1] = 0; + nIndex[2][0] = 0; nIndex[2][1] = 1; + + for (i = 0; i < 3; i++) { + if ( vRotation[i] != 0.0f ) { + double dAngle = DEG2RAD( vRotation[i] ); + double c = cos( dAngle ); + double s = sin( dAngle ); + vWork[nIndex[i][0]] = va[nIndex[i][0]] * c - va[nIndex[i][1]] * s; + vWork[nIndex[i][1]] = va[nIndex[i][0]] * s + va[nIndex[i][1]] * c; + } + va = vWork; + } + out = vWork; +#else + idAngles angles; + + angles.pitch = vRotation[1]; + angles.yaw = vRotation[2]; + angles.roll = vRotation[0]; + + out = vIn * angles.ToMat3(); +#endif +} + +/* +================= +VectorRotate3Origin +================= +*/ +void VectorRotate3Origin( const idVec3 &vIn, const idVec3 &vRotation, const idVec3 &vOrigin, idVec3 &out ) { + out = vIn - vOrigin; + VectorRotate3( out, vRotation, out ); + out += vOrigin; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +extern void Brush_Rotate(brush_t *b, idMat3 matrix, idVec3 origin, bool bBuild); + +void Select_ApplyMatrix(bool bSnap, bool rotateOrigins) { + brush_t *b; + face_t *f; + int i; + idVec3 temp; + idStr str; + char text[128]; + entity_t *lastOwner = NULL; + + for (b = selected_brushes.next; b != &selected_brushes; b = b->next) { + + bool doBrush = true; + if (!(b->owner->eclass->nShowFlags & ECLASS_WORLDSPAWN) && b->owner != lastOwner) { + if (b->modelHandle || b->owner->eclass->nShowFlags & ECLASS_ROTATABLE) { + if (rotateOrigins) { + b->owner->rotation *= select_matrix; + b->owner->origin *= select_rotation; + SetKeyVec3(b->owner, "origin", b->owner->origin); + if (b->trackLightOrigin) { + b->owner->lightRotation *= select_matrix; + b->owner->lightOrigin *= select_rotation; + SetKeyVec3(b->owner, "light_origin", b->owner->lightOrigin); + } + } else { + + b->owner->rotation *= select_matrix; + if ( select_fliporder ) { + if ( select_flipAxis == 0 ) { + temp = b->owner->rotation[1]; + b->owner->rotation[1] = b->owner->rotation[2]; + b->owner->rotation[2] = temp; + } else if ( select_flipAxis == 1 ) { + temp = b->owner->rotation[0]; + b->owner->rotation[0] = b->owner->rotation[1]; + b->owner->rotation[1] = temp; + } else { + temp = b->owner->rotation[0]; + b->owner->rotation[0] = b->owner->rotation[2]; + b->owner->rotation[2] = temp; + } + } + + if (b->trackLightOrigin) { + b->owner->lightRotation = select_matrix * b->owner->lightRotation; + } + } + b->owner->rotation.OrthoNormalizeSelf(); + b->owner->lightRotation.OrthoNormalizeSelf(); + + if (b->modelHandle) { + idBounds bo, bo2; + bo2.Zero(); + if ( dynamic_cast( b->modelHandle ) ) { + bo2.ExpandSelf( 12.0f ); + } else { + bo2 = b->modelHandle->Bounds(); + } + bo.FromTransformedBounds(bo2, b->owner->origin, b->owner->rotation); + Brush_Resize(b, bo[0], bo[1]); + doBrush = false; + } + if (b->owner->eclass->fixedsize) { + doBrush = false; + } + } else if (b->owner->eclass->fixedsize && !rotateOrigins) { + doBrush = false; + } else { + b->owner->origin -= select_origin; + b->owner->origin *= select_matrix; + b->owner->origin += select_origin; + sprintf(text, "%i %i %i", (int)b->owner->origin[0], (int)b->owner->origin[1], (int)b->owner->origin[2]); + + SetKeyValue(b->owner, "origin", text); + } + + + + if (OkForRotationKey(b)) { + sprintf(str, "%g %g %g %g %g %g %g %g %g",b->owner->rotation[0][0],b->owner->rotation[0][1],b->owner->rotation[0][2], + b->owner->rotation[1][0],b->owner->rotation[1][1],b->owner->rotation[1][2],b->owner->rotation[2][0], + b->owner->rotation[2][1],b->owner->rotation[2][2]); + SetKeyValue(b->owner, "rotation", str); + } + + if (b->trackLightOrigin) { + sprintf(str, "%g %g %g %g %g %g %g %g %g",b->owner->lightRotation[0][0],b->owner->lightRotation[0][1],b->owner->lightRotation[0][2], + b->owner->lightRotation[1][0],b->owner->lightRotation[1][1],b->owner->lightRotation[1][2],b->owner->lightRotation[2][0], + b->owner->lightRotation[2][1],b->owner->lightRotation[2][2]); + SetKeyValue(b->owner, "light_rotation", str); + } + DeleteKey(b->owner, "angle"); + DeleteKey(b->owner, "angles"); + } + + if (doBrush) { + for (f = b->brush_faces; f; f = f->next) { + for (i = 0; i < 3; i++) { + f->planepts[i] = ( ((g_bRotateMode) ? f->orgplanepts[i] : f->planepts[i]) - select_origin ) * ((g_bRotateMode) ? select_bmatrix : select_matrix) + select_origin; + } + + if ( select_fliporder ) { + VectorCopy(f->planepts[0], temp); + VectorCopy(f->planepts[2], f->planepts[0]); + VectorCopy(temp, f->planepts[2]); + } + } + } + + if (b->owner->eclass->fixedsize && b->owner->eclass->entityModel == NULL) { + idVec3 min, max; + if (b->trackLightOrigin) { + min = b->owner->lightOrigin + b->owner->eclass->mins; + max = b->owner->lightOrigin + b->owner->eclass->maxs; + } else { + min = b->owner->origin + b->owner->eclass->mins; + max = b->owner->origin + b->owner->eclass->maxs; + } + Brush_Resize(b, min, max); + } else { + Brush_Build(b, bSnap); + } + + if (b->pPatch) { + Patch_ApplyMatrix(b->pPatch, select_origin, select_matrix, bSnap); + } + + if ( b->owner->curve ) { + int c = b->owner->curve->GetNumValues(); + for ( i = 0; i < c; i++ ) { + idVec3 v = b->owner->curve->GetValue( i ); + v -= select_origin; + v *= select_matrix; + v += select_origin; + b->owner->curve->SetValue( i, v ); + } + } + + lastOwner = b->owner; + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void RotateFaceTexture(face_t *f, int nAxis, float fDeg) { + idVec3 p1, p2, p3, rota; + p1[0] = p1[1] = p1[2] = 0; + VectorCopy(p1, p2); + VectorCopy(p1, p3); + VectorCopy(p1, rota); + ComputeAbsolute(f, p1, p2, p3); + + rota[nAxis] = fDeg; + VectorRotate3Origin(p1, rota, select_origin, p1); + VectorRotate3Origin(p2, rota, select_origin, p2); + VectorRotate3Origin(p3, rota, select_origin, p3); + + idPlane normal2; + idVec3 vNormal; + vNormal[0] = f->plane[0]; + vNormal[1] = f->plane[1]; + vNormal[2] = f->plane[2]; + VectorRotate3(vNormal, rota, vNormal); + normal2[0] = vNormal[0]; + normal2[1] = vNormal[1]; + normal2[2] = vNormal[2]; + AbsoluteToLocal(normal2, f, p1, p2, p3); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void RotateTextures(int nAxis, float fDeg, idVec3 vOrigin) { + for (brush_t * b = selected_brushes.next; b != &selected_brushes; b = b->next) { + for (face_t * f = b->brush_faces; f; f = f->next) { + if (g_qeglobals.m_bBrushPrimitMode) { + RotateFaceTexture_BrushPrimit(f, nAxis, fDeg, vOrigin); + } + else { + RotateFaceTexture(f, nAxis, fDeg); + } + + // ++timo removed that call .. works fine .. ??????? Brush_Build(b, false); + } + + Brush_Build(b, false); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Select_ApplyMatrix_BrushPrimit() { + for (brush_t * b = selected_brushes.next; b != &selected_brushes; b = b->next) { + for (face_t * f = b->brush_faces; f; f = f->next) { + ApplyMatrix_BrushPrimit(f, select_matrix, select_origin); + } + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Select_RotateAxis(int axis, float deg, bool bPaint, bool bMouse) { + idVec3 temp; + + if (deg == 0) { + return; + } + + if (bMouse) { + if (g_qeglobals.flatRotation == 2) { + Select_GetTrueMid(select_origin); + } else { + VectorCopy(g_pParentWnd->ActiveXY()->RotateOrigin(), select_origin); + } + } else { + Select_GetMid(select_origin); + } + + select_fliporder = false; + + idVec3 vec = vec3_origin; + vec[axis] = 1.0f; + + if (g_bRotateMode) { + select_orgDeg += deg; + } + + select_rotation.Set( select_origin, vec, deg ); + select_matrix = select_rotation.ToMat3(); + idRotation rot(select_origin, vec, select_orgDeg); + rot.Normalize360(); + select_bmatrix = rot.ToMat3(); + + + if (g_PrefsDlg.m_bRotateLock) { + select_matrix.TransposeSelf(); + Select_ApplyMatrix_BrushPrimit(); + //RotateTextures(axis, -deg, select_origin); + } + + select_matrix.TransposeSelf(); + Select_ApplyMatrix( !bMouse, ( g_qeglobals.flatRotation != 0 ) ); + + if (bPaint) { + Sys_UpdateWindows(W_ALL); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void ProjectOnPlane( const idVec3 &normal, float dist, idVec3 &ez, idVec3 &p) { + if (idMath::Fabs(ez[0]) == 1) { + p[0] = (dist - normal[1] * p[1] - normal[2] * p[2]) / normal[0]; + } + else if (idMath::Fabs(ez[1]) == 1) { + p[1] = (dist - normal[0] * p[0] - normal[2] * p[2]) / normal[1]; + } + else { + p[2] = (dist - normal[0] * p[0] - normal[1] * p[1]) / normal[2]; + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Back(idVec3 &dir, idVec3 &p) { + if (idMath::Fabs(dir[0]) == 1) { + p[0] = 0; + } + else if (idMath::Fabs(dir[1]) == 1) { + p[1] = 0; + } + else { + p[2] = 0; + } +} + +// +// ======================================================================================================================= +// using scale[0] and scale[1] +// ======================================================================================================================= +// +void ComputeScale(idVec3 &rex, idVec3 &rey, idVec3 &p, face_t *f) { + float px = DotProduct(rex, p); + float py = DotProduct(rey, p); + px *= f->texdef.scale[0]; + py *= f->texdef.scale[1]; + + idVec3 aux; + VectorCopy(rex, aux); + VectorScale(aux, px, aux); + VectorCopy(aux, p); + VectorCopy(rey, aux); + VectorScale(aux, py, aux); + VectorAdd(p, aux, p); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void ComputeAbsolute(face_t *f, idVec3 &p1, idVec3 &p2, idVec3 &p3) { + idVec3 ex, ey, ez; // local axis base + +#ifdef _DEBUG + if (g_qeglobals.m_bBrushPrimitMode) { + common->Printf("Warning : illegal call of ComputeAbsolute in brush primitive mode\n"); + } +#endif + // compute first local axis base + TextureAxisFromPlane( f->plane, ex, ey ); + ez = ex.Cross( ey ); + + idVec3 aux; + VectorCopy(ex, aux); + VectorScale(aux, -f->texdef.shift[0], aux); + VectorCopy(aux, p1); + VectorCopy(ey, aux); + VectorScale(aux, -f->texdef.shift[1], aux); + VectorAdd(p1, aux, p1); + VectorCopy(p1, p2); + VectorAdd(p2, ex, p2); + VectorCopy(p1, p3); + VectorAdd(p3, ey, p3); + VectorCopy(ez, aux); + VectorScale(aux, -f->texdef.rotate, aux); + VectorRotate3(p1, aux, p1); + VectorRotate3(p2, aux, p2); + VectorRotate3(p3, aux, p3); + + // computing rotated local axis base + idVec3 rex, rey; + VectorCopy(ex, rex); + VectorRotate3(rex, aux, rex); + VectorCopy(ey, rey); + VectorRotate3(rey, aux, rey); + + ComputeScale(rex, rey, p1, f); + ComputeScale(rex, rey, p2, f); + ComputeScale(rex, rey, p3, f); + + // project on normal plane along ez assumes plane normal is normalized + ProjectOnPlane(f->plane.Normal(), -f->plane[3], ez, p1); + ProjectOnPlane(f->plane.Normal(), -f->plane[3], ez, p2); + ProjectOnPlane(f->plane.Normal(), -f->plane[3], ez, p3); +}; + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void AbsoluteToLocal( const idPlane &normal2, face_t *f, idVec3 &p1, idVec3 &p2, idVec3 &p3) { + idVec3 ex, ey, ez; + +#ifdef _DEBUG + if (g_qeglobals.m_bBrushPrimitMode) { + common->Printf("Warning : illegal call of AbsoluteToLocal in brush primitive mode\n"); + } +#endif + // computing new local axis base + TextureAxisFromPlane( normal2, ex, ey ); + ez = ex.Cross( ey ); + + // projecting back on (ex,ey) + Back(ez, p1); + Back(ez, p2); + Back(ez, p3); + + idVec3 aux; + + // rotation + VectorCopy(p2, aux); + VectorSubtract(aux, p1, aux); + + float x = DotProduct(aux, ex); + float y = DotProduct(aux, ey); + f->texdef.rotate = RAD2DEG( atan2(y, x) ); + + idVec3 rex, rey; + + // computing rotated local axis base + VectorCopy(ez, aux); + VectorScale(aux, f->texdef.rotate, aux); + VectorCopy(ex, rex); + VectorRotate3(rex, aux, rex); + VectorCopy(ey, rey); + VectorRotate3(rey, aux, rey); + + // scale + VectorCopy(p2, aux); + VectorSubtract(aux, p1, aux); + f->texdef.scale[0] = DotProduct(aux, rex); + VectorCopy(p3, aux); + VectorSubtract(aux, p1, aux); + f->texdef.scale[1] = DotProduct(aux, rey); + + // shift only using p1 + x = DotProduct(rex, p1); + y = DotProduct(rey, p1); + x /= f->texdef.scale[0]; + y /= f->texdef.scale[1]; + + VectorCopy(rex, p1); + VectorScale(p1, x, p1); + VectorCopy(rey, aux); + VectorScale(aux, y, aux); + VectorAdd(p1, aux, p1); + VectorCopy(ez, aux); + VectorScale(aux, -f->texdef.rotate, aux); + VectorRotate3(p1, aux, p1); + f->texdef.shift[0] = -DotProduct(p1, ex); + f->texdef.shift[1] = -DotProduct(p1, ey); + + // stored rot is good considering local axis base change it if necessary + f->texdef.rotate = -f->texdef.rotate; + + Clamp(f->texdef.shift[0], f->d_texture->GetEditorImage()->uploadWidth); + Clamp(f->texdef.shift[1], f->d_texture->GetEditorImage()->uploadHeight); + Clamp(f->texdef.rotate, 360); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Select_FlipAxis(int axis) { + + Select_GetMid( select_origin ); + + for ( int i = 0; i < 3; i++) { + VectorCopy(vec3_origin, select_matrix[i]); + select_matrix[i][i] = 1; + } + + select_matrix[axis][axis] = -1; + + select_matrix.Identity(); + select_matrix[axis][axis] = -1; + + select_fliporder = true; + select_flipAxis = axis; + + // texture locking + if (g_PrefsDlg.m_bRotateLock) { + // + // axis flipping inverts space orientation, we have to use a general texture + // locking algorithm instead of the RotateFaceTexture + // + if (g_qeglobals.m_bBrushPrimitMode) { + Select_ApplyMatrix_BrushPrimit(); + } + else { + // + // there's never been flip locking for non BP mode, this would be tricky to write + // and there's not much interest for it with the coming of BP format what could be + // done is converting regular to BP, locking, then back to regular :) + // Sys_FPrintf(SYS_WRN, "WARNING: regular texturing doesn't have texture lock on + // flipping operations\n"); + // + } + } + + // geometric transformation + Select_ApplyMatrix(true, false); + Sys_UpdateWindows(W_ALL);} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Select_Scale(float x, float y, float z) { + Select_GetMid(select_origin); + for (brush_t * b = selected_brushes.next; b != &selected_brushes; b = b->next) { + for (face_t * f = b->brush_faces; f; f = f->next) { + for (int i = 0; i < 3; i++) { + f->planepts[i][0] -= select_origin[0]; + f->planepts[i][1] -= select_origin[1]; + f->planepts[i][2] -= select_origin[2]; + f->planepts[i][0] *= x; + + // + // f->planepts[i][0] = floor(f->planepts[i][0] / g_qeglobals.d_gridsize + 0.5) * + // g_qeglobals.d_gridsize; + // + f->planepts[i][1] *= y; + + // + // f->planepts[i][1] = floor(f->planepts[i][1] / g_qeglobals.d_gridsize + 0.5) * + // g_qeglobals.d_gridsize; + // + f->planepts[i][2] *= z; + + // + // f->planepts[i][2] = floor(f->planepts[i][2] / g_qeglobals.d_gridsize + 0.5) * + // g_qeglobals.d_gridsize; + // + f->planepts[i][0] += select_origin[0]; + f->planepts[i][1] += select_origin[1]; + f->planepts[i][2] += select_origin[2]; + } + } + + Brush_Build(b, false); + if (b->pPatch) { + idVec3 v; + v[0] = x; + v[1] = y; + v[2] = z; + + // Patch_Scale(b->nPatchID, select_origin, v); + Patch_Scale(b->pPatch, select_origin, v); + } + } +} + +/* + ======================================================================================================================= + GROUP SELECTIONS + ======================================================================================================================= + */ +void Select_CompleteTall(void) { + brush_t *b, *next; + + // int i; + idVec3 mins, maxs; + + if (!QE_SingleBrush()) { + return; + } + + g_qeglobals.d_select_mode = sel_brush; + + VectorCopy(selected_brushes.next->mins, mins); + VectorCopy(selected_brushes.next->maxs, maxs); + Select_Delete(); + + int nDim1 = (g_pParentWnd->ActiveXY()->GetViewType() == YZ) ? 1 : 0; + int nDim2 = (g_pParentWnd->ActiveXY()->GetViewType() == XY) ? 1 : 2; + + for (b = active_brushes.next; b != &active_brushes; b = next) { + next = b->next; + + if ((b->maxs[nDim1] > maxs[nDim1] || b->mins[nDim1] < mins[nDim1]) || (b->maxs[nDim2] > maxs[nDim2] || b->mins[nDim2] < mins[nDim2])) { + if (!(b->owner->origin[nDim1] > mins[nDim1] && b->owner->origin[nDim1] < maxs[nDim1] && b->owner->origin[nDim2] > mins[nDim2] && b->owner->origin[nDim2] < maxs[nDim2])) { + continue; + } + if (b->owner->eclass->nShowFlags & ECLASS_WORLDSPAWN) { + continue; + } + } + + if (FilterBrush(b)) { + continue; + } + + Brush_RemoveFromList(b); + Brush_AddToList(b, &selected_brushes); + } + + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Select_PartialTall(void) { + brush_t *b, *next; + + // int i; + idVec3 mins, maxs; + + if (!QE_SingleBrush()) { + return; + } + + g_qeglobals.d_select_mode = sel_brush; + + VectorCopy(selected_brushes.next->mins, mins); + VectorCopy(selected_brushes.next->maxs, maxs); + Select_Delete(); + + int nDim1 = (g_pParentWnd->ActiveXY()->GetViewType() == YZ) ? 1 : 0; + int nDim2 = (g_pParentWnd->ActiveXY()->GetViewType() == XY) ? 1 : 2; + + for (b = active_brushes.next; b != &active_brushes; b = next) { + next = b->next; + + if + ( + (b->mins[nDim1] > maxs[nDim1] || b->maxs[nDim1] < mins[nDim1]) || + (b->mins[nDim2] > maxs[nDim2] || b->maxs[nDim2] < mins[nDim2]) + ) { + continue; + } + + if (FilterBrush(b)) { + continue; + } + + Brush_RemoveFromList(b); + Brush_AddToList(b, &selected_brushes); + +#if 0 + // old stuff + for (i = 0; i < 2; i++) { + if (b->mins[i] > maxs[i] || b->maxs[i] < mins[i]) { + break; + } + } + + if (i == 2) { + Brush_RemoveFromList(b); + Brush_AddToList(b, &selected_brushes); + } +#endif + } + + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Select_Touching(void) { + brush_t *b, *next; + int i; + idVec3 mins, maxs; + + if (!QE_SingleBrush()) { + return; + } + + g_qeglobals.d_select_mode = sel_brush; + + VectorCopy(selected_brushes.next->mins, mins); + VectorCopy(selected_brushes.next->maxs, maxs); + + for (b = active_brushes.next; b != &active_brushes; b = next) { + next = b->next; + + if (FilterBrush(b)) { + continue; + } + + for (i = 0; i < 3; i++) { + if (b->mins[i] > maxs[i] + 1 || b->maxs[i] < mins[i] - 1) { + break; + } + } + + if (i == 3) { + Brush_RemoveFromList(b); + Brush_AddToList(b, &selected_brushes); + } + } + + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Select_Inside(void) { + brush_t *b, *next; + int i; + idVec3 mins, maxs; + + if (!QE_SingleBrush()) { + return; + } + + g_qeglobals.d_select_mode = sel_brush; + + VectorCopy(selected_brushes.next->mins, mins); + VectorCopy(selected_brushes.next->maxs, maxs); + Select_Delete(); + + for (b = active_brushes.next; b != &active_brushes; b = next) { + next = b->next; + + if (FilterBrush(b)) { + continue; + } + + for (i = 0; i < 3; i++) { + if (b->maxs[i] > maxs[i] || b->mins[i] < mins[i]) { + break; + } + } + + if (i == 3) { + Brush_RemoveFromList(b); + Brush_AddToList(b, &selected_brushes); + } + } + + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + Select_Ungroup Turn the currently selected entity back into normal brushes + ======================================================================================================================= + */ +void Select_Ungroup() { + int numselectedgroups; + entity_t *e; + brush_t *b, *sb; + + numselectedgroups = 0; + for (sb = selected_brushes.next; sb != &selected_brushes; sb = sb->next) { + e = sb->owner; + + if (!e || e == world_entity) { + continue; + } + + for (b = e->brushes.onext; b != &e->brushes; b = e->brushes.onext) { + Entity_UnlinkBrush(b); + Entity_LinkBrush(world_entity, b); + Brush_Build(b); + b->owner = world_entity; + } + + Entity_Free(e); + numselectedgroups++; + } + + if (numselectedgroups <= 0) { + Sys_Status("No grouped entities selected.\n"); + return; + } + + common->Printf("Ungrouped %d entit%s.\n", numselectedgroups, (numselectedgroups == 1) ? "y" : "ies"); + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Select_ShiftTexture(float x, float y, bool autoAdjust) { + brush_t *b; + face_t *f; + + int nFaceCount = g_ptrSelectedFaces.GetSize(); + + if (selected_brushes.next == &selected_brushes && nFaceCount == 0) { + return; + } + + x = -x; + + Undo_Start("Select shift textures"); + for (b = selected_brushes.next; b != &selected_brushes; b = b->next) { + for (f = b->brush_faces; f; f = f->next) { + if (g_qeglobals.m_bBrushPrimitMode) { + // use face normal to compute a true translation + Select_ShiftTexture_BrushPrimit(f, x, y, autoAdjust); + } + else { + f->texdef.shift[0] += x; + f->texdef.shift[1] += y; + } + } + + Brush_Build(b); + if (b->pPatch) { + // Patch_ShiftTexture(b->nPatchID, x, y); + Patch_ShiftTexture(b->pPatch, x, y, autoAdjust); + } + } + + if (nFaceCount > 0) { + for (int i = 0; i < nFaceCount; i++) { + face_t *selFace = reinterpret_cast < face_t * > (g_ptrSelectedFaces.GetAt(i)); + brush_t *selBrush = reinterpret_cast < brush_t * > (g_ptrSelectedFaceBrushes.GetAt(i)); + if (g_qeglobals.m_bBrushPrimitMode) { + // + // use face normal to compute a true translation Select_ShiftTexture_BrushPrimit( + // selected_face, x, y ); use camera view to compute texture shift + // + Select_ShiftTexture_BrushPrimit(selFace, x, y, autoAdjust); + } + else { + selFace->texdef.shift[0] += x; + selFace->texdef.shift[1] += y; + } + + Brush_Build(selBrush); + } + } + + Undo_End(); + Sys_UpdateWindows(W_CAMERA); +} + +extern void Face_SetExplicitScale_BrushPrimit(face_t *face, float s, float t); +extern void Face_ScaleTexture_BrushPrimit(face_t *face, float sS, float sT); + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Select_ScaleTexture(float x, float y, bool update, bool absolute) { + brush_t *b; + face_t *f; + + int nFaceCount = g_ptrSelectedFaces.GetSize(); + + if (selected_brushes.next == &selected_brushes && nFaceCount == 0) { + return; + } + + Undo_Start("Select_SetExplicitScale_BrushPrimit"); + for (b = selected_brushes.next; b != &selected_brushes; b = b->next) { + for (f = b->brush_faces; f; f = f->next) { + if (g_qeglobals.m_bBrushPrimitMode && f->face_winding) { + if (absolute) { + Face_SetExplicitScale_BrushPrimit(f, x, y); + } else { + Face_ScaleTexture_BrushPrimit(f, x, y); + } + } + else { + f->texdef.scale[0] += x; + f->texdef.scale[1] += y; + } + } + + Brush_Build(b); + if (b->pPatch) { + Patch_ScaleTexture(b->pPatch, x, y, absolute); + } + } + + if (nFaceCount > 0) { + for (int i = 0; i < nFaceCount; i++) { + face_t *selFace = reinterpret_cast < face_t * > (g_ptrSelectedFaces.GetAt(i)); + brush_t *selBrush = reinterpret_cast < brush_t * > (g_ptrSelectedFaceBrushes.GetAt(i)); + if (g_qeglobals.m_bBrushPrimitMode) { + if (absolute) { + Face_SetExplicitScale_BrushPrimit(selFace, x, y); + } else { + Face_ScaleTexture_BrushPrimit(selFace, x, y); + } + } + else { + selFace->texdef.scale[0] += x; + selFace->texdef.scale[1] += y; + } + + Brush_Build(selBrush); + } + } + + Undo_End(); + if (update) { + Sys_UpdateWindows(W_CAMERA); + } +} + +extern void Face_RotateTexture_BrushPrimit(face_t *face, float amount, idVec3 origin); + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Select_RotateTexture(float amt, bool absolute) { + brush_t *b; + face_t *f; + + int nFaceCount = g_ptrSelectedFaces.GetSize(); + + if (selected_brushes.next == &selected_brushes && nFaceCount == 0) { + return; + } + + Undo_Start("Select_RotateTexture_BrushPrimit"); + for (b = selected_brushes.next; b != &selected_brushes; b = b->next) { + for (f = b->brush_faces; f; f = f->next) { + if (g_qeglobals.m_bBrushPrimitMode) { + Face_RotateTexture_BrushPrimit(f, amt, b->owner->origin); + } + else { + f->texdef.rotate += amt; + f->texdef.rotate = static_cast(f->texdef.rotate) % 360; + } + } + + Brush_Build(b); + if (b->pPatch) { + // Patch_RotateTexture(b->nPatchID, amt); + Patch_RotateTexture(b->pPatch, amt); + } + } + + if (nFaceCount > 0) { + for (int i = 0; i < nFaceCount; i++) { + face_t *selFace = reinterpret_cast < face_t * > (g_ptrSelectedFaces.GetAt(i)); + brush_t *selBrush = reinterpret_cast < brush_t * > (g_ptrSelectedFaceBrushes.GetAt(i)); + if (g_qeglobals.m_bBrushPrimitMode) { + idVec3 org; + org.Zero(); + //Face_RotateTexture_BrushPrimit(selFace, amt, selBrush->owner->origin); + Face_RotateTexture_BrushPrimit(selFace, amt, org); + } + else { + selFace->texdef.rotate += amt; + selFace->texdef.rotate = static_cast(selFace->texdef.rotate) % 360; + } + + Brush_Build(selBrush); + } + } + + Undo_End(); + Sys_UpdateWindows(W_CAMERA); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void FindReplaceTextures(const char *pFind, const char *pReplace, bool bSelected, bool bForce) { + brush_t *pList = (bSelected) ? &selected_brushes : &active_brushes; + if (!bSelected) { + Select_Deselect(); + } + + for (brush_t * pBrush = pList->next; pBrush != pList; pBrush = pBrush->next) { + if (pBrush->pPatch) { + Patch_FindReplaceTexture(pBrush, pFind, pReplace, bForce); + } + + for (face_t * pFace = pBrush->brush_faces; pFace; pFace = pFace->next) { + if (bForce || idStr::Icmp(pFace->texdef.name, pFind) == 0 ) { + pFace->d_texture = Texture_ForName(pReplace); + + // strcpy(pFace->texdef.name, pReplace); + pFace->texdef.SetName(pReplace); + } + } + + Brush_Build(pBrush); + } + + Sys_UpdateWindows(W_CAMERA); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Select_AllOfType() { + brush_t *b, *next; + entity_t *e; + if ((selected_brushes.next == &selected_brushes) || (selected_brushes.next->next != &selected_brushes)) { + CString strName; + if (g_ptrSelectedFaces.GetSize() == 0) { + strName = g_qeglobals.d_texturewin.texdef.name; + } + else { + face_t *selFace = reinterpret_cast < face_t * > (g_ptrSelectedFaces.GetAt(0)); + strName = selFace->texdef.name; + } + + Select_Deselect(); + for (b = active_brushes.next; b != &active_brushes; b = next) { + next = b->next; + + if (FilterBrush(b)) { + continue; + } + + if (b->pPatch) { + if ( idStr::Icmp(strName, b->pPatch->d_texture->GetName()) == 0 ) { + Brush_RemoveFromList(b); + Brush_AddToList(b, &selected_brushes); + } + } + else { + for (face_t * pFace = b->brush_faces; pFace; pFace = pFace->next) { + if ( idStr::Icmp(strName, pFace->texdef.name) == 0 ) { + Brush_RemoveFromList(b); + Brush_AddToList(b, &selected_brushes); + } + } + } + } + + Sys_UpdateWindows(W_ALL); + return; + } + + b = selected_brushes.next; + e = b->owner; + if (e != NULL) { + if (e != world_entity) { + CString strName = e->eclass->name; + idStr strKey, strVal; + bool bCriteria = g_Inspectors->GetSelectAllCriteria(strKey, strVal); + common->Printf("Selecting all %s(s)\n", strName); + Select_Deselect(); + + for (b = active_brushes.next; b != &active_brushes; b = next) { + next = b->next; + + if (FilterBrush(b)) { + continue; + } + + e = b->owner; + if (e != NULL) { + if ( idStr::Icmp(e->eclass->name, strName) == 0 ) { + bool doIt = true; + if (bCriteria) { + CString str = ValueForKey(e, strKey); + if (str.CompareNoCase(strVal) != 0) { + doIt = false; + } + } + + if (doIt) { + Brush_RemoveFromList(b); + Brush_AddToList(b, &selected_brushes); + } + } + } + } + } + } + + + if ( selected_brushes.next && selected_brushes.next->owner ) { + g_Inspectors->UpdateEntitySel( selected_brushes.next->owner->eclass ); + } + + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Select_Reselect() { + CPtrArray holdArray; + brush_t *b; + for ( b = selected_brushes.next; b && b != &selected_brushes; b = b->next ) { + holdArray.Add(reinterpret_cast < void * > (b)); + } + + int n = holdArray.GetSize(); + while (n-- > 0) { + b = reinterpret_cast < brush_t * > (holdArray.GetAt(n)); + Select_Brush(b); + } + + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Select_FitTexture(float height, float width) { + brush_t *b; + int nFaceCount = g_ptrSelectedFaces.GetSize(); + + if (selected_brushes.next == &selected_brushes && nFaceCount == 0) { + return; + } + + Undo_Start("Select_FitTexture"); + for (b = selected_brushes.next; b != &selected_brushes; b = b->next) { + if (b->pPatch) { + Patch_FitTexture(b->pPatch, width, height); + } + else { + Brush_FitTexture(b, height, width); + Brush_Build(b); + } + } + + if (nFaceCount > 0) { + for (int i = 0; i < nFaceCount; i++) { + face_t *selFace = reinterpret_cast < face_t * > (g_ptrSelectedFaces.GetAt(i)); + brush_t *selBrush = reinterpret_cast < brush_t * > (g_ptrSelectedFaceBrushes.GetAt(i)); + Face_FitTexture(selFace, height, width); + Brush_Build(selBrush); + } + } + + Undo_End(); + Sys_UpdateWindows(W_CAMERA); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Select_AxialTexture() { +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Select_Hide(bool invert) { + + if (invert) { + for (brush_t * b = active_brushes.next; b && b != &active_brushes; b = b->next) { + b->hiddenBrush = true; + } + } else { + for (brush_t * b = selected_brushes.next; b && b != &selected_brushes; b = b->next) { + b->hiddenBrush = true; + } + } + Sys_UpdateWindows(W_ALL); +} + +void Select_WireFrame( bool wireFrame ) { + for (brush_t * b = selected_brushes.next; b && b != &selected_brushes; b = b->next) { + b->forceWireFrame = wireFrame; + } + Sys_UpdateWindows(W_ALL); +} + +void Select_ForceVisible( bool visible ) { + for (brush_t * b = selected_brushes.next; b && b != &selected_brushes; b = b->next) { + b->forceVisibile = visible; + } + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Select_ShowAllHidden() { + brush_t *b; + for (b = selected_brushes.next; b && b != &selected_brushes; b = b->next) { + b->hiddenBrush = false; + } + + for (b = active_brushes.next; b && b != &active_brushes; b = b->next) { + b->hiddenBrush = false; + } + + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + Select_Invert + ======================================================================================================================= + */ +void Select_Invert(void) { + brush_t *next, *prev; + + Sys_Status("inverting selection...\n"); + + next = active_brushes.next; + prev = active_brushes.prev; + if (selected_brushes.next != &selected_brushes) { + active_brushes.next = selected_brushes.next; + active_brushes.prev = selected_brushes.prev; + active_brushes.next->prev = &active_brushes; + active_brushes.prev->next = &active_brushes; + } + else { + active_brushes.next = &active_brushes; + active_brushes.prev = &active_brushes; + } + + if (next != &active_brushes) { + selected_brushes.next = next; + selected_brushes.prev = prev; + selected_brushes.next->prev = &selected_brushes; + selected_brushes.prev->next = &selected_brushes; + } + else { + selected_brushes.next = &selected_brushes; + selected_brushes.prev = &selected_brushes; + } + + Sys_UpdateWindows(W_ALL); + + Sys_Status("done.\n"); +} + +/* + ======================================================================================================================= + Select_Name + ======================================================================================================================= + */ +void Select_Name(const char *pName) { + if (g_qeglobals.m_bBrushPrimitMode) { + for (brush_t * b = selected_brushes.next; b && b != &selected_brushes; b = b->next) { + Brush_SetEpair(b, "Name", pName); + } + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Select_CenterOrigin() { + idVec3 mid; + + Select_GetTrueMid(mid); + mid.Snap(); + + brush_t *b = selected_brushes.next; + entity_t *e = b->owner; + if (e != NULL) { + if (e != world_entity) { + char text[1024]; + sprintf(text, "%i %i %i", (int)mid[0], (int)mid[1], (int)mid[2]); + SetKeyValue(e, "origin", text); + VectorCopy(mid, e->origin); + } + } + + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +int Select_NumSelectedFaces() { + return g_ptrSelectedFaces.GetSize(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +face_t *Select_GetSelectedFace(int index) { + assert(index >= 0 && index < Select_NumSelectedFaces()); + return reinterpret_cast < face_t * > (g_ptrSelectedFaces.GetAt(index)); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +brush_t *Select_GetSelectedFaceBrush(int index) { + assert(index >= 0 && index < Select_NumSelectedFaces()); + return reinterpret_cast < brush_t * > (g_ptrSelectedFaceBrushes.GetAt(index)); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Select_SetDefaultTexture(const idMaterial *mat, bool fitScale, bool setTexture) { + texdef_t tex; + brushprimit_texdef_t brushprimit_tex; + memset(&tex, 0, sizeof(tex)); + memset(&brushprimit_tex, 0, sizeof(brushprimit_tex)); + if (g_qeglobals.m_bBrushPrimitMode) { + // brushprimit fitted to a 2x2 texture + brushprimit_tex.coords[0][0] = 1.0f; + brushprimit_tex.coords[1][1] = 1.0f; + } + else { + tex.scale[0] = (g_PrefsDlg.m_bHiColorTextures) ? 0.5 : 1; + tex.scale[1] = (g_PrefsDlg.m_bHiColorTextures) ? 0.5 : 1; + } + + tex.SetName(mat->GetName()); + Texture_SetTexture(&tex, &brushprimit_tex, fitScale, setTexture); + + CString strTex; + strTex.Format + ( + "%s (%s) W: %i H: %i", + mat->GetName(), + mat->GetDescription(), + mat->GetEditorImage()->uploadWidth, + mat->GetEditorImage()->uploadHeight + ); + g_pParentWnd->SetStatusText(3, strTex); +} + + +void Select_UpdateTextureName(const char *name) { + brush_t *b; + int nCount = g_ptrSelectedFaces.GetSize(); + if (nCount > 0) { + Undo_Start("set face texture name"); + ASSERT(g_ptrSelectedFaces.GetSize() == g_ptrSelectedFaceBrushes.GetSize()); + for (int i = 0; i < nCount; i++) { + face_t *selFace = reinterpret_cast < face_t * > (g_ptrSelectedFaces.GetAt(i)); + brush_t *selBrush = reinterpret_cast < brush_t * > (g_ptrSelectedFaceBrushes.GetAt(i)); + Undo_AddBrush(selBrush); + selFace->texdef.SetName(name); + Brush_Build(selBrush); + Undo_EndBrush(selBrush); + } + + Undo_End(); + } + else if (selected_brushes.next != &selected_brushes) { + Undo_Start("set brush textures"); + for (b = selected_brushes.next; b != &selected_brushes; b = b->next) { + if (!b->owner->eclass->fixedsize) { + Undo_AddBrush(b); + Brush_SetTextureName(b, name); + Undo_EndBrush(b); + } else if (b->owner->eclass->nShowFlags & ECLASS_LIGHT) { + if ( idStr::Cmpn(name, "lights/", strlen("lights/")) == 0 ) { + SetKeyValue(b->owner, "texture", name); + g_Inspectors->UpdateEntitySel(b->owner->eclass); + UpdateLightInspector(); + Brush_Build(b); + } + } + } + + Undo_End(); + } + + Sys_UpdateWindows(W_ALL); +} + + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Select_FlipTexture(bool y) { + + int faceCount = g_ptrSelectedFaces.GetSize(); + + Undo_Start("Select_FlipTexture"); + for (brush_t *b = selected_brushes.next; b != &selected_brushes; b = b->next) { + if (b->pPatch) { + Patch_FlipTexture(b->pPatch, y); + } else { + Brush_FlipTexture_BrushPrimit(b, y); + } + } + + if (faceCount > 0) { + for (int i = 0; i < faceCount; i++) { + face_t *selFace = reinterpret_cast < face_t * > (g_ptrSelectedFaces.GetAt(i)); + brush_t *selBrush = reinterpret_cast < brush_t * > (g_ptrSelectedFaceBrushes.GetAt(i)); + Face_FlipTexture_BrushPrimit(selFace, y); + } + } + + Undo_End(); + Sys_UpdateWindows(W_CAMERA); +} + + + + +/* + ======================================================================================================================= + Select_SetKeyVal + sets values on non-world entities + ======================================================================================================================= + */ +void Select_SetKeyVal(const char *key, const char *val) { + for (brush_t * b = selected_brushes.next; b && b != &selected_brushes; b = b->next) { + if (b->owner != world_entity) { + SetKeyValue(b->owner, key, val, false); + } + } +} + +/* + ======================================================================================================================= + Select_CopyPatchTextureCoords( patchMesh_t *p ) + ======================================================================================================================= + */ +void Select_CopyPatchTextureCoords( patchMesh_t *p ) { + for (brush_t * b = selected_brushes.next; b && b != &selected_brushes; b = b->next) { + if (b->pPatch) { + if ( b->pPatch->width <= p->width && b->pPatch->height <= p->height ) { + for ( int i = 0; i < b->pPatch->width; i ++ ) { + for ( int j = 0; j < b->pPatch->height; j++ ) { + b->pPatch->ctrl(i, j).st = p->ctrl(i, j).st; + } + } + } + } + } +} + + +/* + ======================================================================================================================= + Select_SetProjectFaceOntoPatch + ======================================================================================================================= + */ +void Select_ProjectFaceOntoPatch( face_t *face ) { + for (brush_t * b = selected_brushes.next; b && b != &selected_brushes; b = b->next) { + if (b->pPatch) { + EmitBrushPrimitTextureCoordinates(face, NULL, b->pPatch); + Patch_MakeDirty(b->pPatch); + } + } +} + +/* + ======================================================================================================================= + Select_SetPatchFit + ======================================================================================================================= + */ +extern float Patch_Width(patchMesh_t *p); +extern float Patch_Height(patchMesh_t *p); +void Select_SetPatchFit(float dim1, float dim2, float srcWidth, float srcHeight, float rot) { + for (brush_t * b = selected_brushes.next; b && b != &selected_brushes; b = b->next) { + if (b->pPatch) { + float w = Patch_Width(b->pPatch); + float h = Patch_Height(b->pPatch); + Patch_RotateTexture(b->pPatch, -90 + rot); + Patch_FitTexture(b->pPatch, dim1 * (w / srcWidth), dim2 * (h / srcHeight)); + Patch_FlipTexture(b->pPatch, true); + } + } +} + +void Select_SetPatchST(float s1, float t1, float s2, float t2) { +} + + +void Select_AllTargets() { + for (brush_t * b = selected_brushes.next; b && b != &selected_brushes; b = b->next) { + if (b->owner != world_entity) { + const idKeyValue *kv = b->owner->epairs.MatchPrefix("target", NULL); + while (kv) { + entity_t *ent = FindEntity("name", kv->GetValue()); + if (ent) { + Select_Brush(ent->brushes.onext, true, false); + } + kv = b->owner->epairs.MatchPrefix("target", kv); + } + } + } +} diff --git a/src/tools/radiant/SELECT.H b/src/tools/radiant/SELECT.H new file mode 100644 index 0000000..353916a --- /dev/null +++ b/src/tools/radiant/SELECT.H @@ -0,0 +1,144 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef __SELECT_H_ +#define __SELECT_H_ + +typedef enum +{ + sel_brush, + // sel_sticky_brush, + // sel_face, + sel_vertex, + sel_edge, + sel_singlevertex, + sel_curvepoint, + sel_area, + sel_addpoint, // for dropping points + sel_editpoint // for editing points +} select_t; + +class CDragPoint { +public: + idVec3 vec; + brush_t *pBrush; + int nType; + bool priority; + CDragPoint() {}; + CDragPoint(brush_t *b, idVec3 v, int type, bool p) { + pBrush = b; + VectorCopy(v, vec); + nType = type; + priority = p; + } + + void Set(brush_t *b, idVec3 v, int type) { + pBrush = b; + VectorCopy(v, vec); + nType = type; + } + + bool PointWithin(idVec3 p, int nView = -1); +}; + + +typedef struct +{ + brush_t *brush; + face_t *face; + CDragPoint *point; + float dist; + bool selected; +} qertrace_t; + + +#define SF_SELECTED_ONLY 0x01 +#define SF_ENTITIES_FIRST 0x02 +#define SF_SINGLEFACE 0x04 +#define SF_IGNORECURVES 0x08 +#define SF_IGNOREGROUPS 0x10 +#define SF_CYCLE 0x20 + + +qertrace_t Test_Ray ( const idVec3 &origin, const idVec3 &dir, int flags ); +CDragPoint *PointRay( const idVec3 &org, const idVec3 &dir, float *dist); +void SelectCurvePointByRay( const idVec3 &org, const idVec3 &dir, int buttons); +void SelectSplinePointByRay( const idVec3 &org, const idVec3 &dir, int buttons); + +void Select_GetBounds (idVec3 &mins, idVec3 &maxs); +void Select_Brush (brush_t *b, bool bComplete = true, bool bStatus = true); +void Select_Ray (idVec3 origin, idVec3 dir, int flags); +void Select_Delete (void); +void Select_Deselect (bool bDeselectFaces = true); +void Select_Invert(void); +void Select_Clone (void); +void Select_Move (idVec3 delta, bool bSnap = true); +void WINAPI Select_SetTexture (texdef_t *texdef, brushprimit_texdef_t *brushprimit_texdef, bool bFitScale = false, void* pPlugTexdef = NULL, bool update = true); +void Select_FlipAxis (int axis); +void Select_RotateAxis (int axis, float deg, bool bPaint = true, bool bMouse = false); +void Select_CompleteTall (void); +void Select_PartialTall (void); +void Select_Touching (void); +void Select_Inside (void); +void Select_CenterOrigin(); +void Select_AllOfType(); +void Select_Reselect(); +void Select_FitTexture(float height = 1.0, float width = 1.0); +void Select_InitializeRotation(); +void Select_FinalizeRotation(); + +// absolute texture coordinates +// TTimo NOTE: this is stuff for old brushes format and rotation texture lock .. sort of in-between with bush primitives +void ComputeAbsolute(face_t* f, idVec3& p1, idVec3& p2, idVec3& p3); +void AbsoluteToLocal( const idPlane &normal2, face_t* f, idVec3& p1, idVec3& p2, idVec3& p3); +void Select_Hide(bool invert = false); +void Select_ShowAllHidden(); +void Select_WireFrame( bool wireFrame ); +void Select_ForceVisible( bool visible ); +void Select_Name(const char *pName); +void Select_AddProjectedLight(); +void Select_GetMid (idVec3 &mid); +void Select_SetDefaultTexture(const idMaterial *mat, bool fitScale, bool setTexture); +void Select_UpdateTextureName(const char *name); + +void Select_FlipTexture(bool y); +void Select_SetPatchFit(float dim1, float dim2, float srcWidth, float srcHeight, float rot); +void Select_SetPatchST(float s1, float t1, float s2, float t2); +void Select_ProjectFaceOntoPatch( face_t *face ); +void Select_CopyPatchTextureCoords( patchMesh_t *p ); +void Select_AllTargets(); +void Select_Scale(float x, float y, float z); +void Select_RotateTexture(float amt, bool absolute = false); +void Select_ScaleTexture(float x, float y, bool update = true, bool absolute = true); +void Select_DefaultTextureScale(bool horz, bool vert, bool update = true); +void Select_ShiftTexture(float x, float y, bool autoAdjust = false); +void Select_GetTrueMid (idVec3 &mid); +void Select_Scale(float x, float y, float z); + + +#endif \ No newline at end of file diff --git a/src/tools/radiant/ScaleDialog.cpp b/src/tools/radiant/ScaleDialog.cpp new file mode 100644 index 0000000..b806b84 --- /dev/null +++ b/src/tools/radiant/ScaleDialog.cpp @@ -0,0 +1,75 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" +#include "ScaleDialog.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CScaleDialog dialog + + +CScaleDialog::CScaleDialog(CWnd* pParent /*=NULL*/) + : CDialog(CScaleDialog::IDD, pParent) +{ + //{{AFX_DATA_INIT(CScaleDialog) + m_fZ = 1.0f; + m_fX = 1.0f; + m_fY = 1.0f; + //}}AFX_DATA_INIT +} + + +void CScaleDialog::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CScaleDialog) + DDX_Text(pDX, IDC_EDIT_Z, m_fZ); + DDX_Text(pDX, IDC_EDIT_X, m_fX); + DDX_Text(pDX, IDC_EDIT_Y, m_fY); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CScaleDialog, CDialog) + //{{AFX_MSG_MAP(CScaleDialog) + // NOTE: the ClassWizard will add message map macros here + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CScaleDialog message handlers diff --git a/src/tools/radiant/ScaleDialog.h b/src/tools/radiant/ScaleDialog.h new file mode 100644 index 0000000..3a6068a --- /dev/null +++ b/src/tools/radiant/ScaleDialog.h @@ -0,0 +1,75 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#if !defined(AFX_SCALEDIALOG_H__8A9B33B2_9922_11D1_B568_00AA00A410FC__INCLUDED_) +#define AFX_SCALEDIALOG_H__8A9B33B2_9922_11D1_B568_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// ScaleDialog.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CScaleDialog dialog + +class CScaleDialog : public CDialog +{ +// Construction +public: + CScaleDialog(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CScaleDialog) + enum { IDD = IDD_DIALOG_SCALE }; + float m_fZ; + float m_fX; + float m_fY; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CScaleDialog) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CScaleDialog) + // NOTE: the ClassWizard will add member functions here + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_SCALEDIALOG_H__8A9B33B2_9922_11D1_B568_00AA00A410FC__INCLUDED_) diff --git a/src/tools/radiant/SurfaceDlg.cpp b/src/tools/radiant/SurfaceDlg.cpp new file mode 100644 index 0000000..807ff23 --- /dev/null +++ b/src/tools/radiant/SurfaceDlg.cpp @@ -0,0 +1,629 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" +#include "SurfaceDlg.h" +#include "mainfrm.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CSurfaceDlg dialog + +CSurfaceDlg g_dlgSurface; + + +CSurfaceDlg::CSurfaceDlg(CWnd* pParent /*=NULL*/) + : CDialog(CSurfaceDlg::IDD, pParent) { + //{{AFX_DATA_INIT(CSurfaceDlg) + m_nHorz = 3; + m_nVert = 3; + m_horzScale = 1.0f; + m_horzShift = 0.5f; + m_rotate = 15.0f; + m_vertScale = 1.0f; + m_vertShift = 0.5f; + m_strMaterial = _T(""); + m_subdivide = FALSE; + m_fHeight = 1.0f; + m_fWidth = 1.0f; + m_absolute = FALSE; + //}}AFX_DATA_INIT +} + + +void CSurfaceDlg::DoDataExchange(CDataExchange* pDX) { + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CSurfaceDlg) + DDX_Control(pDX, IDC_ROTATE, m_wndRotateEdit); + DDX_Control(pDX, IDC_EDIT_VERT, m_wndVert); + DDX_Control(pDX, IDC_EDIT_HORZ, m_wndHorz); + DDX_Control(pDX, IDC_SLIDER_VERT, m_wndVerticalSubdivisions); + DDX_Control(pDX, IDC_SLIDER_HORZ, m_wndHorzSubdivisions); + DDX_Control(pDX, IDC_SPIN_WIDTH, m_wndWidth); + DDX_Control(pDX, IDC_SPIN_HEIGHT, m_wndHeight); + DDX_Control(pDX, IDC_SPIN_VSHIFT, m_wndVShift); + DDX_Control(pDX, IDC_SPIN_ROTATE, m_wndRotate); + DDX_Control(pDX, IDC_SPIN_HSHIFT, m_wndHShift); + DDX_Text(pDX, IDC_EDIT_HORZ, m_nHorz); + DDV_MinMaxInt(pDX, m_nHorz, 1, 64); + DDX_Text(pDX, IDC_EDIT_VERT, m_nVert); + DDV_MinMaxInt(pDX, m_nVert, 1, 64); + DDX_Text(pDX, IDC_HSCALE, m_horzScale); + DDX_Text(pDX, IDC_HSHIFT, m_horzShift); + DDX_Text(pDX, IDC_ROTATE, m_rotate); + DDX_Text(pDX, IDC_VSCALE, m_vertScale); + DDX_Text(pDX, IDC_VSHIFT, m_vertShift); + DDX_Text(pDX, IDC_TEXTURE, m_strMaterial); + DDX_Check(pDX, IDC_CHECK_SUBDIVIDE, m_subdivide); + DDX_Text(pDX, IDC_EDIT_HEIGHT, m_fHeight); + DDX_Text(pDX, IDC_EDIT_WIDTH, m_fWidth); + DDX_Check(pDX, IDC_CHECK_ABSOLUTE, m_absolute); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CSurfaceDlg, CDialog) + //{{AFX_MSG_MAP(CSurfaceDlg) + ON_WM_HSCROLL() + ON_WM_KEYDOWN() + ON_WM_VSCROLL() + ON_WM_CLOSE() + ON_WM_DESTROY() + ON_BN_CLICKED(IDCANCEL, OnBtnCancel) + ON_BN_CLICKED(IDC_BTN_COLOR, OnBtnColor) + ON_WM_CTLCOLOR() + ON_WM_CREATE() + ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_HSHIFT, OnDeltaPosSpin) + ON_BN_CLICKED(IDC_BTN_PATCHDETAILS, OnBtnPatchdetails) + ON_BN_CLICKED(IDC_BTN_PATCHNATURAL, OnBtnPatchnatural) + ON_BN_CLICKED(IDC_BTN_PATCHRESET, OnBtnPatchreset) + ON_BN_CLICKED(IDC_BTN_AXIAL, OnBtnAxial) + ON_BN_CLICKED(IDC_BTN_BRUSHFIT, OnBtnBrushfit) + ON_BN_CLICKED(IDC_BTN_FACEFIT, OnBtnFacefit) + ON_BN_CLICKED(IDC_CHECK_SUBDIVIDE, OnCheckSubdivide) + ON_EN_CHANGE(IDC_EDIT_HORZ, OnChangeEditHorz) + ON_EN_CHANGE(IDC_EDIT_VERT, OnChangeEditVert) + ON_EN_SETFOCUS(IDC_HSCALE, OnSetfocusHscale) + ON_EN_KILLFOCUS(IDC_HSCALE, OnKillfocusHscale) + ON_EN_KILLFOCUS(IDC_VSCALE, OnKillfocusVscale) + ON_EN_SETFOCUS(IDC_VSCALE, OnSetfocusVscale) + ON_EN_KILLFOCUS(IDC_EDIT_WIDTH, OnKillfocusEditWidth) + ON_EN_SETFOCUS(IDC_EDIT_WIDTH, OnSetfocusEditWidth) + ON_EN_KILLFOCUS(IDC_EDIT_HEIGHT, OnKillfocusEditHeight) + ON_EN_SETFOCUS(IDC_EDIT_HEIGHT, OnSetfocusEditHeight) + ON_BN_CLICKED(IDC_BTN_FLIPX, OnBtnFlipx) + ON_BN_CLICKED(IDC_BTN_FLIPY, OnBtnFlipy) + ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_ROTATE, OnDeltaPosSpin) + ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_VSHIFT, OnDeltaPosSpin) + ON_EN_KILLFOCUS(IDC_ROTATE, OnKillfocusRotate) + ON_EN_SETFOCUS(IDC_ROTATE, OnSetfocusRotate) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CSurfaceDlg message handlers + + +/* +=================================================== + + SURFACE INSPECTOR + +=================================================== +*/ + +texdef_t g_old_texdef; +texdef_t g_patch_texdef; +HWND g_surfwin = NULL; +bool g_changed_surface; + +/* +============== +SetTexMods + +Set the fields to the current texdef +if one face selected -> will read this face texdef, else current texdef +if only patches selected, will read the patch texdef +=============== +*/ +extern void Face_GetScale_BrushPrimit(face_t *face, float *s, float *t, float *rot); +void CSurfaceDlg::SetTexMods() { + UpdateData(TRUE); + m_strMaterial = g_qeglobals.d_texturewin.texdef.name; + patchMesh_t *p = SinglePatchSelected(); + if (p) { + m_subdivide = p->explicitSubdivisions; + m_strMaterial = p->d_texture->GetName(); + } else { + m_subdivide = false; + } + + int faceCount = g_ptrSelectedFaces.GetSize(); + face_t *selFace = NULL; + if (faceCount) { + selFace = reinterpret_cast < face_t * > (g_ptrSelectedFaces.GetAt(0)); + } else { + if (selected_brushes.next != &selected_brushes) { + brush_t *b = selected_brushes.next; + if (!b->pPatch) { + selFace = b->brush_faces; + } + } + } + + if (selFace) { + float rot; + Face_GetScale_BrushPrimit(selFace, &m_horzScale, &m_vertScale, &rot); + } else { + m_horzScale = 1.0f; + m_vertScale = 1.0f; + } + + UpdateData(FALSE); +} + + +bool g_bNewFace = false; +bool g_bNewApplyHandling = false; +bool g_bGatewayhack = false; + + +/* +================= +UpdateSpinners +================= +*/ + +void CSurfaceDlg::UpdateSpinners(bool up, int nID) { + UpdateData(TRUE); + float hdiv = 0.0f; + float vdiv = 0.0f; + switch (nID) { + case IDC_SPIN_ROTATE : + Select_RotateTexture((up) ? m_rotate : -m_rotate); + break; + case IDC_SPIN_HSCALE : + m_horzScale += (up) ? 0.1f : -0.1f; + hdiv = (m_horzScale == 0.0f) ? 1.0f : m_horzScale; + Select_ScaleTexture( 1.0f / hdiv, 0.0f, true, ( m_absolute != FALSE ) ); + UpdateData(FALSE); + break; + case IDC_SPIN_VSCALE : + m_vertScale += (up) ? 0.1f : -0.1f; + vdiv = (m_vertScale == 0.0f) ? 1.0f : m_vertScale; + Select_ScaleTexture( 0.0f, 1.0f / vdiv, true, ( m_absolute != FALSE ) ); + UpdateData(FALSE); + break; + case IDC_SPIN_HSHIFT : + Select_ShiftTexture((up) ? m_horzShift : -m_horzShift, 0); + break; + case IDC_SPIN_VSHIFT : + Select_ShiftTexture(0, (up) ? m_vertShift : -m_vertShift); + break; + } + g_changed_surface = true; +} + +void CSurfaceDlg::UpdateSpinners(int nScrollCode, int nPos, CScrollBar* pBar) { + + return; + UpdateData(TRUE); + if ((nScrollCode != SB_LINEUP) && (nScrollCode != SB_LINEDOWN)) { + return; + } + + bool up = (nScrollCode == SB_LINEUP); + +// FIXME: bad resource define +#define IDC_ROTATEA 0 +#define IDC_HSCALEA 0 +#define IDC_VSCALEA 0 +#define IDC_HSHIFTA 0 +#define IDC_VSHIFTA 0 + + if (pBar->GetSafeHwnd() == ::GetDlgItem(GetSafeHwnd(), IDC_ROTATEA)) { + Select_RotateTexture((up) ? m_rotate : -m_rotate); + } else if (pBar->GetSafeHwnd() == ::GetDlgItem(GetSafeHwnd(), IDC_HSCALEA)) { + Select_ScaleTexture((up) ? -m_horzScale : m_horzScale, 0, true, ( m_absolute != FALSE ) ); + } else if (pBar->GetSafeHwnd() == ::GetDlgItem(GetSafeHwnd(), IDC_VSCALEA)) { + Select_ScaleTexture(0, (up) ? -m_vertScale : m_vertScale, true, ( m_absolute != FALSE ) ); + } else if (pBar->GetSafeHwnd() == ::GetDlgItem(GetSafeHwnd(), IDC_HSHIFTA)) { + Select_ShiftTexture((up) ? -m_horzShift : m_horzShift, 0); + } else if (pBar->GetSafeHwnd() == ::GetDlgItem(GetSafeHwnd(), IDC_VSHIFTA)) { + Select_ShiftTexture((up) ? -m_vertShift : m_vertShift, 0); + } + + g_changed_surface = true; +} + +void UpdateSurfaceDialog() { + if (g_surfwin) { + g_dlgSurface.SetTexMods(); + } + g_pParentWnd->UpdateTextureBar(); +} + +bool ByeByeSurfaceDialog(); + +void DoSurface (void) { + + g_bNewFace = ( g_PrefsDlg.m_bFace != FALSE ); + g_bNewApplyHandling = ( g_PrefsDlg.m_bNewApplyHandling != FALSE ); + g_bGatewayhack = ( g_PrefsDlg.m_bGatewayHack != FALSE ); + // save current state for cancel + g_old_texdef = g_qeglobals.d_texturewin.texdef; + g_changed_surface = false; + + if (g_surfwin == NULL && g_dlgSurface.GetSafeHwnd() == NULL) { + g_patch_texdef.scale[0] = 0.05f; + g_patch_texdef.scale[1] = 0.05f; + g_patch_texdef.shift[0] = 0.05f; + g_patch_texdef.shift[1] = 0.05f; + // use rotation increment from preferences + g_patch_texdef.rotate = g_PrefsDlg.m_nRotation; + + g_dlgSurface.Create(IDD_SURFACE); + CRect rct; + LONG lSize = sizeof(rct); + if (LoadRegistryInfo("radiant_SurfaceWindow", &rct, &lSize)) { + g_dlgSurface.SetWindowPos( NULL, rct.left, rct.top, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW ); + } + g_dlgSurface.ShowWindow(SW_SHOW); + Sys_UpdateWindows(W_ALL); + } else { + g_surfwin = g_dlgSurface.GetSafeHwnd(); + g_dlgSurface.SetTexMods (); + g_dlgSurface.ShowWindow(SW_SHOW); + } +} + +bool ByeByeSurfaceDialog() { + if (g_surfwin) { + if (g_bGatewayhack) { + PostMessage(g_surfwin, WM_COMMAND, IDC_APPLY, 0); + } else { + PostMessage(g_surfwin, WM_COMMAND, IDCANCEL, 0); + } + return true; + } else { + return false; + } +} + +BOOL CSurfaceDlg::OnInitDialog() { + CDialog::OnInitDialog(); + + g_surfwin = GetSafeHwnd(); + SetTexMods (); + + //m_wndHScale.SetRange(0, 100); + //m_wndVScale.SetRange(0, 100); + m_wndHShift.SetRange(0, 100); + m_wndVShift.SetRange(0, 100); + m_wndRotate.SetRange(0, 100); + m_wndWidth.SetRange(1, 32); + m_wndHeight.SetRange(1, 32); + + m_wndVerticalSubdivisions.SetRange(1, 32); + m_wndVerticalSubdivisions.SetBuddy(&m_wndVert, FALSE); + m_wndHorzSubdivisions.SetRange(1, 32); + m_wndHorzSubdivisions.SetBuddy(&m_wndHorz, FALSE); + m_wndVerticalSubdivisions.SetPos(m_nVert); + m_wndHorzSubdivisions.SetPos(m_nHorz); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void CSurfaceDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { + UpdateData(TRUE); + if (pScrollBar->IsKindOf(RUNTIME_CLASS(CSliderCtrl))) { + CSliderCtrl *ctrl = reinterpret_cast(pScrollBar); + assert(ctrl); + if (ctrl == &m_wndVerticalSubdivisions) { + m_nVert = ctrl->GetPos(); + } else { + m_nHorz = ctrl->GetPos(); + } + UpdateData(FALSE); + + if (m_subdivide) { + Patch_SubdivideSelected( ( m_subdivide != FALSE ), m_nHorz, m_nVert ); + } + } + Sys_UpdateWindows(W_CAMERA | W_XY); +} + +void CSurfaceDlg::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { + + CDialog::OnKeyDown(nChar, nRepCnt, nFlags); +} + +void CSurfaceDlg::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { + //UpdateSpinners(nSBCode, nPos, pScrollBar); + //Sys_UpdateWindows(W_CAMERA); +} + + +void CSurfaceDlg::OnOK() { + //GetTexMods(); + UpdateData(TRUE); + if (m_strMaterial.Find(":") >= 0) { + const idMaterial *mat = declManager->FindMaterial(m_strMaterial); + Select_UpdateTextureName(m_strMaterial); + } + g_surfwin = NULL; + CDialog::OnOK(); + Sys_UpdateWindows(W_ALL); +} + +void CSurfaceDlg::OnClose() { + g_surfwin = NULL; + CDialog::OnClose(); +} + +void CSurfaceDlg::OnCancel() { + if (g_bGatewayhack) { + OnOK(); + } else { + OnBtnCancel(); + } +} + +void CSurfaceDlg::OnDestroy() { + if (GetSafeHwnd()) { + CRect rct; + GetWindowRect(rct); + SaveRegistryInfo("radiant_SurfaceWindow", &rct, sizeof(rct)); + } + CDialog::OnDestroy(); + g_surfwin = NULL; + Sys_UpdateWindows(W_ALL); +} + +void CSurfaceDlg::OnBtnCancel() { + g_qeglobals.d_texturewin.texdef = g_old_texdef; + if (g_changed_surface) { + //++timo if !g_qeglobals.m_bBrushPrimitMode send a NULL brushprimit_texdef + if (!g_qeglobals.m_bBrushPrimitMode) { + common->Printf("Warning : non brush primitive mode call to CSurfaceDlg::GetTexMods broken\n"); + common->Printf(" ( Select_SetTexture not called )\n"); + } + // Select_SetTexture(&g_qeglobals.d_texturewin.texdef); + } + g_surfwin = NULL; + DestroyWindow(); +} + +void CSurfaceDlg::OnBtnColor() { +} + +HBRUSH CSurfaceDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { + HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); + return hbr; +} + +int CSurfaceDlg::OnCreate(LPCREATESTRUCT lpCreateStruct) { + if (CDialog::OnCreate(lpCreateStruct) == -1) + return -1; + + return 0; +} + +BOOL CSurfaceDlg::PreCreateWindow(CREATESTRUCT& cs) { + // TODO: Add your specialized code here and/or call the base class + + return CDialog::PreCreateWindow(cs); +} + + +void CSurfaceDlg::OnDeltaPosSpin(NMHDR* pNMHDR, LRESULT* pResult) { + NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR; + UpdateSpinners((pNMUpDown->iDelta > 0), pNMUpDown->hdr.idFrom); + *pResult = 0; +} + +void CSurfaceDlg::OnBtnPatchdetails() { + Patch_NaturalizeSelected(true); + g_pParentWnd->GetCamera()->MarkWorldDirty (); + Sys_UpdateWindows(W_ALL); +} + +void CSurfaceDlg::OnBtnPatchnatural() { + Select_SetTexture (&g_qeglobals.d_texturewin.texdef, &g_qeglobals.d_texturewin.brushprimit_texdef, false); + Patch_NaturalizeSelected(); + g_pParentWnd->GetCamera()->MarkWorldDirty (); + g_changed_surface = true; + Sys_UpdateWindows(W_ALL); +} + +void CSurfaceDlg::OnBtnPatchreset() { + //CTextureLayout dlg; + //if (dlg.DoModal() == IDOK) { + // Patch_ResetTexturing(dlg.m_fX, dlg.m_fY); + //} + //Sys_UpdateWindows(W_ALL); +} + +void CSurfaceDlg::OnBtnAxial() { +} + +void CSurfaceDlg::OnBtnBrushfit() { + // TODO: Add your control notification handler code here + +} + +void CSurfaceDlg::OnBtnFacefit() { + UpdateData(TRUE); +/* + brush_t *b; + for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next) { + if (!b->patchBrush) { + for (face_t* pFace = b->brush_faces; pFace; pFace = pFace->next) { + g_ptrSelectedFaces.Add(pFace); + g_ptrSelectedFaceBrushes.Add(b); + } + } + } +*/ + Select_FitTexture(m_fHeight, m_fWidth); + g_pParentWnd->GetCamera()->MarkWorldDirty (); + //SetTexMods(); + g_changed_surface = true; + Sys_UpdateWindows(W_ALL); +} + + +void CSurfaceDlg::OnCheckSubdivide() { + UpdateData( TRUE ); + // turn any patches in explicit subdivides + Patch_SubdivideSelected( ( m_subdivide != FALSE ), m_nHorz, m_nVert ); + g_pParentWnd->GetCamera()->MarkWorldDirty (); + Sys_UpdateWindows( W_CAMERA | W_XY ); +} + +void CSurfaceDlg::OnChangeEditHorz() +{ + // TODO: If this is a RICHEDIT control, the control will not + // send this notification unless you override the CDialog::OnInitDialog() + // function and call CRichEditCtrl().SetEventMask() + // with the ENM_CHANGE flag ORed into the mask. + + // TODO: Add your control notification handler code here + UpdateData(TRUE); + // turn any patches in explicit subdivides + Patch_SubdivideSelected( ( m_subdivide != FALSE ), m_nHorz, m_nVert ); + Sys_UpdateWindows(W_CAMERA | W_XY); + +} + +void CSurfaceDlg::OnChangeEditVert() +{ + // TODO: If this is a RICHEDIT control, the control will not + // send this notification unless you override the CDialog::OnInitDialog() + // function and call CRichEditCtrl().SetEventMask() + // with the ENM_CHANGE flag ORed into the mask. + + // TODO: Add your control notification handler code here + UpdateData(TRUE); + // turn any patches in explicit subdivides + Patch_SubdivideSelected( ( m_subdivide != FALSE ), m_nHorz, m_nVert ); + Sys_UpdateWindows(W_CAMERA | W_XY); + +} + +BOOL CSurfaceDlg::PreTranslateMessage(MSG* pMsg) +{ + if (pMsg->message == WM_KEYDOWN) { + if (pMsg->wParam == VK_RETURN) { + if (focusControl) { + UpdateData(TRUE); + if (focusControl == &m_wndHScale) { + Select_ScaleTexture( m_horzScale, 1.0f, true, ( m_absolute != FALSE ) ); + } else if (focusControl == &m_wndVScale) { + Select_ScaleTexture( 1.0f, m_vertScale, true, ( m_absolute != FALSE ) ); + } else if (focusControl == &m_wndRotateEdit) { + Select_RotateTexture( m_rotate, true ); + } else if (focusControl == &m_wndHeight || focusControl == &m_wndWidth) { + Select_FitTexture( m_fHeight, m_fWidth ); + } + } + return TRUE; + } + } + return CDialog::PreTranslateMessage(pMsg); +} + +void CSurfaceDlg::OnSetfocusHscale() +{ + focusControl = &m_wndHScale; +} + +void CSurfaceDlg::OnKillfocusHscale() +{ + focusControl = NULL; +} + +void CSurfaceDlg::OnKillfocusVscale() +{ + focusControl = NULL; +} + +void CSurfaceDlg::OnSetfocusVscale() +{ + focusControl = &m_wndVScale; +} + +void CSurfaceDlg::OnKillfocusEditWidth() +{ + focusControl = NULL; +} + +void CSurfaceDlg::OnSetfocusEditWidth() +{ + focusControl = &m_wndWidth; +} + +void CSurfaceDlg::OnKillfocusEditHeight() +{ + focusControl = NULL; +} + +void CSurfaceDlg::OnSetfocusEditHeight() +{ + focusControl = &m_wndHeight; +} + +void CSurfaceDlg::OnBtnFlipx() +{ + Select_FlipTexture(false); +} + +void CSurfaceDlg::OnBtnFlipy() +{ + Select_FlipTexture(true); +} + +void CSurfaceDlg::OnKillfocusRotate() +{ + focusControl = NULL; +} + +void CSurfaceDlg::OnSetfocusRotate() +{ + focusControl = &m_wndRotateEdit; +} diff --git a/src/tools/radiant/SurfaceDlg.h b/src/tools/radiant/SurfaceDlg.h new file mode 100644 index 0000000..2363c29 --- /dev/null +++ b/src/tools/radiant/SurfaceDlg.h @@ -0,0 +1,139 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#if !defined(AFX_SURFACEDLG_H__D84E0C22_9EEA_11D1_B570_00AA00A410FC__INCLUDED_) +#define AFX_SURFACEDLG_H__D84E0C22_9EEA_11D1_B570_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// SurfaceDlg.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CSurfaceDlg dialog + +class CSurfaceDlg : public CDialog +{ + bool m_bPatchMode; + CWnd *focusControl; + + // Construction +public: + CSurfaceDlg(CWnd* pParent = NULL); // standard constructor + void SetTexMods(); + +// Dialog Data + //{{AFX_DATA(CSurfaceDlg) + enum { IDD = IDD_SURFACE }; + CEdit m_wndRotateEdit; + CEdit m_wndVert; + CEdit m_wndHorz; + CSliderCtrl m_wndVerticalSubdivisions; + CSliderCtrl m_wndHorzSubdivisions; + CSpinButtonCtrl m_wndWidth; + CSpinButtonCtrl m_wndHeight; + CSpinButtonCtrl m_wndVShift; + CSpinButtonCtrl m_wndVScale; + CSpinButtonCtrl m_wndRotate; + CSpinButtonCtrl m_wndHShift; + CSpinButtonCtrl m_wndHScale; + int m_nHorz; + int m_nVert; + float m_horzScale; + float m_horzShift; + float m_rotate; + float m_vertScale; + float m_vertShift; + CString m_strMaterial; + BOOL m_subdivide; + float m_fHeight; + float m_fWidth; + BOOL m_absolute; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CSurfaceDlg) + public: + virtual BOOL PreTranslateMessage(MSG* pMsg); + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + //}}AFX_VIRTUAL + +// Implementation +protected: + + void UpdateSpinners(int nScrollCode, int nPos, CScrollBar* pBar); + void UpdateSpinners(bool bUp, int nID); + // Generated message map functions + //{{AFX_MSG(CSurfaceDlg) + virtual BOOL OnInitDialog(); + afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); + afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); + afx_msg void OnApply(); + virtual void OnOK(); + afx_msg void OnClose(); + virtual void OnCancel(); + afx_msg void OnDestroy(); + afx_msg void OnBtnCancel(); + afx_msg void OnBtnColor(); + afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor); + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + afx_msg void OnDeltaPosSpin(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnBtnPatchdetails(); + afx_msg void OnBtnPatchnatural(); + afx_msg void OnBtnPatchreset(); + afx_msg void OnBtnAxial(); + afx_msg void OnBtnBrushfit(); + afx_msg void OnBtnFacefit(); + afx_msg void OnCheckSubdivide(); + afx_msg void OnChangeEditHorz(); + afx_msg void OnChangeEditVert(); + afx_msg void OnSetfocusHscale(); + afx_msg void OnKillfocusHscale(); + afx_msg void OnKillfocusVscale(); + afx_msg void OnSetfocusVscale(); + afx_msg void OnKillfocusEditWidth(); + afx_msg void OnSetfocusEditWidth(); + afx_msg void OnKillfocusEditHeight(); + afx_msg void OnSetfocusEditHeight(); + afx_msg void OnBtnFlipx(); + afx_msg void OnBtnFlipy(); + afx_msg void OnKillfocusRotate(); + afx_msg void OnSetfocusRotate(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_SURFACEDLG_H__D84E0C22_9EEA_11D1_B570_00AA00A410FC__INCLUDED_) diff --git a/src/tools/radiant/TabsDlg.cpp b/src/tools/radiant/TabsDlg.cpp new file mode 100644 index 0000000..acb168c --- /dev/null +++ b/src/tools/radiant/TabsDlg.cpp @@ -0,0 +1,352 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "QE3.H" +#include "TabsDlg.h" +// CTabsDlg dialog + +//IMPLEMENT_DYNAMIC ( CTabsDlg , CDialog ) +CTabsDlg::CTabsDlg(UINT ID , CWnd* pParent /*=NULL*/) + : CDialog(ID, pParent) +{ + m_DragTabActive = false; +} + +BEGIN_MESSAGE_MAP(CTabsDlg, CDialog) + //}}AFX_MSG_MAP +// ON_NOTIFY(TCN_SELCHANGE, IDC_TAB1, OnTcnSelchangeTab1) +ON_WM_LBUTTONDOWN() +ON_WM_LBUTTONUP() +ON_WM_MOUSEMOVE() +ON_WM_DESTROY() +END_MESSAGE_MAP() + +void CTabsDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + DDX_Control(pDX, IDC_TAB_INSPECTOR, m_Tabs); +} + +// CTabsDlg message handlers + +BOOL CTabsDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + + return TRUE; // return TRUE unless you set the focus to a control +} + +void CTabsDlg::OnTcnSelchange(NMHDR *pNMHDR, LRESULT *pResult) +{ + int ID = TabCtrl_GetCurSel ( pNMHDR->hwndFrom ); + + if ( ID >= 0 ) + { + TCITEM item; + item.mask = TCIF_PARAM; + + ShowAllWindows ( FALSE ); + TabCtrl_GetItem (m_Tabs.GetSafeHwnd() , ID , &item); + + DockedWindowInfo* info = (DockedWindowInfo*)item.lParam; + ASSERT ( info ); + + info->m_TabControlIndex = ID; + info->m_Window->ShowWindow(TRUE); + } +} + +void CTabsDlg::DockWindow ( int ID , bool dock ) +{ + DockedWindowInfo* info = NULL; + m_Windows.Lookup ( (WORD)ID , (void*&)info ); + + ASSERT ( info ); + ASSERT ( m_Tabs.GetSafeHwnd() ); + + ShowAllWindows ( FALSE ); + + if ( !dock ) + { + //make a containing window and assign the dialog to it + CRect rect; + CString classname = AfxRegisterWndClass ( CS_DBLCLKS , 0 , 0 , 0 ); + info->m_State = DockedWindowInfo::FLOATING; + + info->m_Window->GetWindowRect(rect); + info->m_Container.CreateEx ( WS_EX_TOOLWINDOW , classname , info->m_Title , WS_THICKFRAME | WS_SYSMENU | WS_POPUP | WS_CAPTION, rect , this , 0 ); + info->m_Window->SetParent ( &info->m_Container ); + info->m_Window->ShowWindow(TRUE); + + info->m_Container.SetDockManager(this); + info->m_Container.ShowWindow(TRUE); + info->m_Container.SetDialog ( info->m_Window , info->m_ID ); + + if (info->m_TabControlIndex >= 0 ) + { + m_Tabs.DeleteItem( info->m_TabControlIndex ); + } + + if ( m_Tabs.GetItemCount() > 0 ) + { + m_Tabs.SetCurFocus( 0 ); + } + + CString placementName = info->m_Title + "Placement"; + LoadWindowPlacement(info->m_Container , placementName); + } + else + { + info->m_State = DockedWindowInfo::DOCKED; + + info->m_TabControlIndex = m_Tabs.InsertItem( TCIF_TEXT | TCIF_IMAGE | TCIF_PARAM , 0 , info->m_Title , info->m_ImageID , (LPARAM)info); + + info->m_Window->SetParent ( this ); + info->m_Window->ShowWindow (TRUE); + + info->m_Container.SetDockManager( NULL ); //so it doesn't try to call back and redock this window + info->m_Container.DestroyWindow (); + + CRect rect; + GetWindowRect ( rect ); + + //stupid hack to get the window reitself properly + rect.DeflateRect(0,0,0,1); + MoveWindow(rect); + rect.InflateRect(0,0,0,1); + MoveWindow(rect); + } + + UpdateTabControlIndices (); + FocusWindow ( ID ); + + if ( info->m_DockCallback ) + { + info->m_DockCallback ( dock , info->m_ID , info->m_Window ); + } + SaveWindowPlacement (); +} + +int CTabsDlg::PreTranslateMessage ( MSG* msg ) +{ + if ( msg->message == WM_LBUTTONDBLCLK && msg->hwnd == m_Tabs.GetSafeHwnd() ) + { + HandleUndock (); + return TRUE; + } + + //steal lbutton clicks for the main dialog too, but let the tabs do their default thing as well + if ( msg->message == WM_LBUTTONDOWN && msg->hwnd == m_Tabs.GetSafeHwnd()) { + m_Tabs.SendMessage ( msg->message , msg->wParam , msg->lParam ); + m_DragTabActive = true; + } + else if ( msg->message == WM_LBUTTONUP && msg->hwnd == m_Tabs.GetSafeHwnd()) { + m_Tabs.SendMessage ( msg->message , msg->wParam , msg->lParam ); + m_DragTabActive = false; + } + + return CDialog::PreTranslateMessage(msg); +} + +bool CTabsDlg::RectWithinDockManager ( CRect& rect ) +{ + CRect tabsRect,intersectionRect; + + m_Tabs.GetWindowRect ( tabsRect ); + intersectionRect.IntersectRect( tabsRect , rect ); + + return !(intersectionRect.IsRectEmpty()); +} + +void CTabsDlg::OnLButtonDown(UINT nFlags, CPoint point) +{ + CDialog::OnLButtonDown(nFlags, point); +} + +void CTabsDlg::OnLButtonUp(UINT nFlags, CPoint point) +{ + if ( m_DragTabActive && ((abs ( point.x - m_DragDownPoint.x ) > 50) || (abs ( point.y - m_DragDownPoint.y ) > 50))) + { + HandleUndock(); + m_DragTabActive = false; + } + CDialog::OnLButtonUp(nFlags, point); +} + + +void CTabsDlg::HandleUndock () +{ + TCITEM item; + item.mask = TCIF_PARAM; + + int curSel = TabCtrl_GetCurSel ( m_Tabs.GetSafeHwnd()); + + TabCtrl_GetItem (m_Tabs.GetSafeHwnd() , curSel , &item); + + DockedWindowInfo* info = (DockedWindowInfo*)item.lParam; + ASSERT ( info ); + + DockWindow ( info->m_ID , false ); +} + +void CTabsDlg::OnMouseMove(UINT nFlags, CPoint point) +{ + CDialog::OnMouseMove(nFlags, point); +} + + +void CTabsDlg::AddDockedWindow ( CWnd* wnd , int ID , int imageID , const CString& title , bool dock , pfnOnDockEvent dockCallback ) +{ + DockedWindowInfo* info = NULL; + m_Windows.Lookup( (WORD)ID , (void*&)info); + + ASSERT ( wnd ); + ASSERT ( info == NULL ); + + info = new DockedWindowInfo ( wnd , ID , imageID , title , dockCallback); + + m_Windows.SetAt ( (WORD)ID , info ); + DockWindow ( ID , dock ); + + UpdateTabControlIndices (); +} + +void CTabsDlg::ShowAllWindows ( bool show ) +{ + POSITION pos; + WORD ID; + DockedWindowInfo* info = NULL; + for( pos = m_Windows.GetStartPosition(); pos != NULL ; ) + { + m_Windows.GetNextAssoc( pos, ID, (void*&)info ); + ASSERT ( info->m_Window ); + if ( info->m_State == DockedWindowInfo::DOCKED ) + { + info->m_Window->ShowWindow( show ); + } + } +} + +void CTabsDlg::FocusWindow ( int ID ) +{ + DockedWindowInfo* info = NULL; + m_Windows.Lookup( (WORD)ID , (void*&)info); + + ASSERT ( info ); + ASSERT ( info->m_Window ); + + if ( info->m_State == DockedWindowInfo::DOCKED ) + { + TabCtrl_SetCurFocus ( m_Tabs.GetSafeHwnd() , info->m_TabControlIndex ); + } + else + { + info->m_Container.SetFocus(); + } +} + +void CTabsDlg::UpdateTabControlIndices () +{ + TCITEM item; + item.mask = TCIF_PARAM; + + DockedWindowInfo* info = NULL; + int itemCount = m_Tabs.GetItemCount(); + + for ( int i = 0 ; i < itemCount ; i ++ ) + { + if ( !m_Tabs.GetItem( i , &item ) ) + { + Sys_Error ( "UpdateTabControlIndices(): GetItem failed!\n" ); + } + info = (DockedWindowInfo*)item.lParam; + + info->m_TabControlIndex = i; + } +} +void CTabsDlg::OnDestroy() +{ + TCITEM item; + item.mask = TCIF_PARAM; + + DockedWindowInfo* info = NULL; + + for ( int i = 0 ; i < m_Tabs.GetItemCount() ; i ++ ) + { + m_Tabs.GetItem( i , &item ); + info = (DockedWindowInfo*)item.lParam; + ASSERT( info ); + + delete info; + } + CDialog::OnDestroy(); +} + + +bool CTabsDlg::IsDocked ( CWnd* wnd ) +{ + bool docked = false; + DockedWindowInfo* info = NULL; + + CString placementName; + POSITION pos; + WORD wID; + + for( pos = m_Windows.GetStartPosition(); pos != NULL ; ) + { + m_Windows.GetNextAssoc( pos, wID, (void*&)info ); + + if ( info->m_Window == wnd ) { + docked = (info->m_State == DockedWindowInfo::DOCKED); + break; + } + } + return docked; +} + +void CTabsDlg::SaveWindowPlacement( int ID ) +{ + DockedWindowInfo* info = NULL; + + CString placementName; + POSITION pos; + WORD wID = ID; + + for( pos = m_Windows.GetStartPosition(); pos != NULL ; ) + { + m_Windows.GetNextAssoc( pos, wID, (void*&)info ); + + if ( (info->m_State == DockedWindowInfo::FLOATING) && ((ID == -1) || (ID == info->m_ID))) { + placementName = info->m_Title + "Placement"; + ::SaveWindowPlacement(info->m_Container.GetSafeHwnd() , placementName); + } + } +} \ No newline at end of file diff --git a/src/tools/radiant/TabsDlg.h b/src/tools/radiant/TabsDlg.h new file mode 100644 index 0000000..3851b6e --- /dev/null +++ b/src/tools/radiant/TabsDlg.h @@ -0,0 +1,119 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#pragma once +#include "afxcmn.h" +#include "TearoffContainerWindow.h" + + +// CTabsDlg dialog +class CTabsDlg : public CDialog +{ +// DECLARE_DYNAMIC ( CTabsDlg ) + // Construction +public: + + CTabsDlg(UINT ID ,CWnd* pParent = NULL); // standard constructor + + typedef void (*pfnOnDockEvent)( bool , int , CWnd* ); + + void AddDockedWindow ( CWnd* wnd , int ID , int imageID , const CString& title , bool dock , pfnOnDockEvent dockCallback = NULL); + void DockWindow ( int ID , bool dock ); + bool RectWithinDockManager ( CRect& rect ); + void FocusWindow ( int ID ); + void SetImageList ( CImageList* list ) + { + ASSERT ( list ); + m_Tabs.SetImageList( list ); + } + + bool IsDocked ( CWnd* wnd ); + + protected: + int CTabsDlg::PreTranslateMessage ( MSG* msg ); + +// Implementation +protected: + CImageList m_TabImages; + CPoint m_DragDownPoint; + CMapWordToPtr m_Windows; + bool m_DragTabActive; + + void DoDataExchange(CDataExchange* pDX); + + //private struct that holds the info we need about each window + struct DockedWindowInfo { + DockedWindowInfo ( CWnd* wnd , int ID , int imageID , const CString& title = "" , pfnOnDockEvent dockCallback = NULL) + { + ASSERT ( wnd ); + m_Window = wnd; + m_ID = ID; + m_ImageID = imageID; + m_TabControlIndex = -1; + if ( title.GetLength() == 0 ) + { + m_Window->GetWindowText( m_Title ); + + } + else + { + m_Title = title; + } + m_State = DOCKED; + m_DockCallback = dockCallback; + } + + enum eState {DOCKED,FLOATING} ; + CTearoffContainerWindow m_Container; //the floating window that will hold m_Window when it's undocked + CWnd* m_Window; + CString m_Title; + int m_ImageID; + int m_ID; + int m_TabControlIndex; + eState m_State; + pfnOnDockEvent m_DockCallback; + }; + void ShowAllWindows ( bool show = true ); + + void HandleUndock (); + void UpdateTabControlIndices (); + + // Generated message map functions + virtual BOOL OnInitDialog(); + DECLARE_MESSAGE_MAP() + +public: + CTabCtrl m_Tabs; + afx_msg void OnTcnSelchange(NMHDR *pNMHDR, LRESULT *pResult); + afx_msg void OnLButtonDown(UINT nFlags, CPoint point); + afx_msg void OnLButtonUp(UINT nFlags, CPoint point); + afx_msg void OnMouseMove(UINT nFlags, CPoint point); + afx_msg void OnDestroy(); + + void SaveWindowPlacement ( int ID = -1 ); +}; diff --git a/src/tools/radiant/TearoffContainerWindow.cpp b/src/tools/radiant/TearoffContainerWindow.cpp new file mode 100644 index 0000000..55e1244 --- /dev/null +++ b/src/tools/radiant/TearoffContainerWindow.cpp @@ -0,0 +1,150 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +// TearoffContainerWindow.cpp : implementation file +// + +#include "TabsDlg.h" +#include "TearoffContainerWindow.h" + + +// CTearoffContainerWindow + +IMPLEMENT_DYNAMIC(CTearoffContainerWindow, CWnd) +CTearoffContainerWindow::CTearoffContainerWindow() +{ + m_DragPreviewActive = false; + m_ContainedDialog = NULL; + m_DockManager = NULL; +} + +CTearoffContainerWindow::~CTearoffContainerWindow() +{ +} + + +BEGIN_MESSAGE_MAP(CTearoffContainerWindow, CWnd) + ON_WM_NCLBUTTONDBLCLK() + ON_WM_CLOSE() + ON_WM_SIZE() + ON_WM_DESTROY() + ON_WM_SETFOCUS() +END_MESSAGE_MAP() + +// CTearoffContainerWindow message handlers + + +void CTearoffContainerWindow::OnNcLButtonDblClk(UINT nHitTest, CPoint point) +{ + if ( nHitTest == HTCAPTION ) + { + m_DockManager->DockWindow ( m_DialogID , true ); + } + + CWnd::OnNcLButtonDblClk(nHitTest, point); +} + + +void CTearoffContainerWindow::SetDialog ( CWnd* dlg , int ID ) +{ + m_DialogID = ID; + m_ContainedDialog = dlg; + + CRect rect; + CPoint point (-10 , -10); + m_ContainedDialog->GetWindowRect ( rect ); + + rect.OffsetRect(point); //move the window slightly so you can tell it's been popped up + + //stupid hack to get the window resize itself properly + rect.DeflateRect(0,0,0,1); + MoveWindow(rect); + rect.InflateRect(0,0,0,1); + MoveWindow(rect); +} + +void CTearoffContainerWindow::SetDockManager ( CTabsDlg* dlg ) +{ + m_DockManager = dlg; +} +void CTearoffContainerWindow::OnClose() +{ + if ( m_DockManager ) + { + //send it back to the docking window (for now at least) + m_DockManager->DockWindow ( m_DialogID , true ); + } +} + + +BOOL CTearoffContainerWindow:: PreTranslateMessage( MSG* pMsg ) +{ + if ( pMsg->message == WM_NCLBUTTONUP ) + { +/* CRect rect; + GetWindowRect ( rect ); + + rect.DeflateRect( 0,0,0,rect.Height() - GetSystemMetrics(SM_CYSMSIZE)); + if ( m_DockManager->RectWithinDockManager ( rect )) + { + m_DockManager->DockDialog ( m_DialogID , true ); + } +*/ + } + + return CWnd::PreTranslateMessage(pMsg); +} +void CTearoffContainerWindow::OnSize(UINT nType, int cx, int cy) +{ + if ( m_ContainedDialog ) + { + m_ContainedDialog->MoveWindow ( 0,0,cx,cy); + } + + CWnd::OnSize(nType, cx, cy); +} + +void CTearoffContainerWindow::OnDestroy() +{ + CWnd::OnDestroy(); + + // TODO: Add your message handler code here +} + +void CTearoffContainerWindow::OnSetFocus(CWnd* pOldWnd) +{ + CWnd::OnSetFocus(pOldWnd); + if ( m_ContainedDialog ) + { + m_ContainedDialog->SetFocus(); + } + // TODO: Add your message handler code here +} diff --git a/src/tools/radiant/TearoffContainerWindow.h b/src/tools/radiant/TearoffContainerWindow.h new file mode 100644 index 0000000..a9d191e --- /dev/null +++ b/src/tools/radiant/TearoffContainerWindow.h @@ -0,0 +1,59 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#pragma once + +// CTearoffContainerWindow + +class CTabsDlg; +class CTearoffContainerWindow : public CWnd +{ + DECLARE_DYNAMIC(CTearoffContainerWindow) + +public: + CTearoffContainerWindow(); + virtual ~CTearoffContainerWindow(); + + CWnd* m_ContainedDialog; //dialog that is being docked/undocked + int m_DialogID; //identifier for this dialog + CTabsDlg* m_DockManager; //the dialog that contains m_ContainedDialog when docked + +protected: + DECLARE_MESSAGE_MAP() + bool m_DragPreviewActive; +public: + afx_msg void OnNcLButtonDblClk(UINT nHitTest, CPoint point); + void SetDialog ( CWnd* dlg , int ID ); + void SetDockManager ( CTabsDlg* dlg ); + afx_msg void OnClose(); + BOOL PreTranslateMessage( MSG* pMsg ); + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnDestroy(); + afx_msg void OnSetFocus(CWnd* pOldWnd); +}; + + diff --git a/src/tools/radiant/TextureBar.cpp b/src/tools/radiant/TextureBar.cpp new file mode 100644 index 0000000..f3452c1 --- /dev/null +++ b/src/tools/radiant/TextureBar.cpp @@ -0,0 +1,215 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" +#include "TextureBar.h" + +//++timo TODO : the whole CTextureBar has to be modified for the new texture code + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CTextureBar dialog + + +CTextureBar::CTextureBar() + : CDialogBar() +{ + //{{AFX_DATA_INIT(CTextureBar) + m_nHShift = 0; + m_nHScale = 0; + m_nRotate = 0; + m_nVShift = 0; + m_nVScale = 0; + m_nRotateAmt = 45; + //}}AFX_DATA_INIT +} + + +void CTextureBar::DoDataExchange(CDataExchange* pDX) +{ + CDialogBar::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CTextureBar) + DDX_Control(pDX, IDC_SPIN_ROTATE, m_spinRotate); + DDX_Control(pDX, IDC_SPIN_VSCALE, m_spinVScale); + DDX_Control(pDX, IDC_SPIN_VSHIFT, m_spinVShift); + DDX_Control(pDX, IDC_SPIN_HSCALE, m_spinHScale); + DDX_Control(pDX, IDC_SPIN_HSHIFT, m_spinHShift); + DDX_Text(pDX, IDC_HSHIFT, m_nHShift); + DDX_Text(pDX, IDC_HSCALE, m_nHScale); + DDX_Text(pDX, IDC_ROTATE, m_nRotate); + DDX_Text(pDX, IDC_VSHIFT, m_nVShift); + DDX_Text(pDX, IDC_VSCALE, m_nVScale); + DDX_Text(pDX, IDC_EDIT_ROTATEAMT, m_nRotateAmt); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CTextureBar, CDialogBar) + //{{AFX_MSG_MAP(CTextureBar) + ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_HSHIFT, OnDeltaposSpinHshift) + ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_VSHIFT, OnDeltaposSpinVshift) + ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_HSCALE, OnDeltaposSpinHScale) + ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_VSCALE, OnDeltaposSpinVScale) + ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_ROTATE, OnDeltaposSpinRotate) + ON_COMMAND(ID_SELECTION_PRINT, OnSelectionPrint) + ON_WM_CREATE() + ON_BN_CLICKED(IDC_BTN_APPLYTEXTURESTUFF, OnBtnApplytexturestuff) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CTextureBar message handlers + +void CTextureBar::OnDeltaposSpinHshift(NMHDR* pNMHDR, LRESULT* pResult) +{ + NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR; + *pResult = 0; + + if (pNMUpDown->iDelta < 0) + Select_ShiftTexture(abs(g_qeglobals.d_savedinfo.m_nTextureTweak), 0); + else + Select_ShiftTexture(-abs(g_qeglobals.d_savedinfo.m_nTextureTweak), 0); + GetSurfaceAttributes(); +} + +void CTextureBar::OnDeltaposSpinVshift(NMHDR* pNMHDR, LRESULT* pResult) +{ + NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR; + // TODO: Add your control notification handler code here + + *pResult = 0; + if (pNMUpDown->iDelta < 0) + Select_ShiftTexture(0, abs(g_qeglobals.d_savedinfo.m_nTextureTweak)); + else + Select_ShiftTexture(0, -abs(g_qeglobals.d_savedinfo.m_nTextureTweak)); + GetSurfaceAttributes(); +} + +void CTextureBar::OnDeltaposSpinHScale(NMHDR* pNMHDR, LRESULT* pResult) +{ + NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR; + // TODO: Add your control notification handler code here + + *pResult = 0; + if (pNMUpDown->iDelta < 0) + Select_ScaleTexture((float)abs(g_qeglobals.d_savedinfo.m_nTextureTweak),0); + else + Select_ScaleTexture((float)-abs(g_qeglobals.d_savedinfo.m_nTextureTweak),0); + GetSurfaceAttributes(); +} + +void CTextureBar::OnDeltaposSpinVScale(NMHDR* pNMHDR, LRESULT* pResult) +{ + NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR; + // TODO: Add your control notification handler code here + + *pResult = 0; + if (pNMUpDown->iDelta < 0) + Select_ScaleTexture(0, (float)abs(g_qeglobals.d_savedinfo.m_nTextureTweak)); + else + Select_ScaleTexture(0, (float)-abs(g_qeglobals.d_savedinfo.m_nTextureTweak)); + GetSurfaceAttributes(); +} + +void CTextureBar::OnDeltaposSpinRotate(NMHDR* pNMHDR, LRESULT* pResult) +{ + NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR; + *pResult = 0; + UpdateData(TRUE); + if (pNMUpDown->iDelta < 0) + Select_RotateTexture(abs(m_nRotateAmt)); + else + Select_RotateTexture(-abs(m_nRotateAmt)); + GetSurfaceAttributes(); +} + + +void CTextureBar::OnSelectionPrint() +{ + // TODO: Add your command handler code here + +} + +int CTextureBar::OnCreate(LPCREATESTRUCT lpCreateStruct) +{ + if (CDialogBar::OnCreate(lpCreateStruct) == -1) + return -1; + return 0; +} + + +void CTextureBar::OnBtnApplytexturestuff() +{ + SetSurfaceAttributes(); +} + +void CTextureBar::GetSurfaceAttributes() +{ + texdef_t* pt = (g_ptrSelectedFaces.GetSize() > 0) ? &(reinterpret_cast(g_ptrSelectedFaces.GetAt(0)))->texdef : &g_qeglobals.d_texturewin.texdef; + + if (pt) + { + m_nHShift = pt->shift[0]; + m_nVShift = pt->shift[1]; + m_nHScale = pt->scale[0]; + m_nVScale = pt->scale[1]; + m_nRotate = pt->rotate; + UpdateData(FALSE); + } +} + +//++timo implement brush primitive here +void CTextureBar::SetSurfaceAttributes() +{ + if (g_ptrSelectedFaces.GetSize() > 0) + { + if (g_qeglobals.m_bBrushPrimitMode) + { + common->Printf("Warning : brush primitive mode not implemented in CTextureBar"); + } + face_t *selFace = reinterpret_cast(g_ptrSelectedFaces.GetAt(0)); + + texdef_t* pt = &selFace->texdef; + UpdateData(TRUE); + pt->shift[0] = m_nHShift; + pt->shift[1] = m_nVShift; + pt->scale[0] = m_nHScale; + pt->scale[1] = m_nVScale; + pt->rotate = m_nRotate; + Sys_UpdateWindows(W_CAMERA); + } +} diff --git a/src/tools/radiant/TextureBar.h b/src/tools/radiant/TextureBar.h new file mode 100644 index 0000000..748f6fb --- /dev/null +++ b/src/tools/radiant/TextureBar.h @@ -0,0 +1,91 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#if !defined(AFX_TEXTUREBAR_H__86220273_B656_11D1_B59F_00AA00A410FC__INCLUDED_) +#define AFX_TEXTUREBAR_H__86220273_B656_11D1_B59F_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// TextureBar.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CTextureBar dialog + +class CTextureBar : public CDialogBar +{ +// Construction +public: + void GetSurfaceAttributes(); + void SetSurfaceAttributes(); + CTextureBar(); + +// Dialog Data + //{{AFX_DATA(CTextureBar) + enum { IDD = IDD_TEXTUREBAR }; + CSpinButtonCtrl m_spinRotate; + CSpinButtonCtrl m_spinVScale; + CSpinButtonCtrl m_spinVShift; + CSpinButtonCtrl m_spinHScale; + CSpinButtonCtrl m_spinHShift; + int m_nHShift; + int m_nHScale; + int m_nRotate; + int m_nVShift; + int m_nVScale; + int m_nRotateAmt; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CTextureBar) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + // Generated message map functions + //{{AFX_MSG(CTextureBar) + afx_msg void OnDeltaposSpinHshift(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnDeltaposSpinVshift(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnDeltaposSpinHScale(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnDeltaposSpinVScale(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnDeltaposSpinRotate(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnSelectionPrint(); + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + afx_msg void OnBtnApplytexturestuff(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_TEXTUREBAR_H__86220273_B656_11D1_B59F_00AA00A410FC__INCLUDED_) diff --git a/src/tools/radiant/Textures.h b/src/tools/radiant/Textures.h new file mode 100644 index 0000000..5464775 --- /dev/null +++ b/src/tools/radiant/Textures.h @@ -0,0 +1,59 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +// a texturename of the form (0 0 0) will +// create a solid color texture + +void Texture_Init (bool bHardInit = true); +void Texture_FlushUnused (); +void Texture_Flush (bool bReload = false); +void Texture_ClearInuse (void); +void Texture_ShowInuse (void); +void Texture_ShowDirectory (int menunum, bool bLinked = false); +void Texture_ShowAll(); +void Texture_HideAll(); +void Texture_Cleanup(CStringList *pList = NULL); + +// TTimo: added bNoAlpha flag to ignore alpha channel when parsing a .TGA file, transparency is usually achieved through qer_trans keyword in shaders +// in some cases loading an empty alpha channel causes display bugs (brushes not seen) +//qtexture_t *Texture_ForName (const char *name, bool bReplace = false, bool bShader = false, bool bNoAlpha = false, bool bReload = false, bool makeShader = true); + +const idMaterial *Texture_ForName(const char *name); + +void Texture_Init (void); +void Texture_SetTexture (texdef_t *texdef, brushprimit_texdef_t *brushprimit_texdef, bool bFitScale = false, bool bSetSelection = true); + +void Texture_SetMode(int iMenu); // GL_TEXTURE_NEAREST, etc.. +void Texture_ResetPosition(); + +void FreeShaders(); +void LoadShaders(); +void ReloadShaders(); +int WINAPI Texture_LoadSkin(char *pName, int *pnWidth, int *pnHeight); +void Texture_StartPos (void); +qtexture_t *Texture_NextPos (int *x, int *y); diff --git a/src/tools/radiant/Undo.cpp b/src/tools/radiant/Undo.cpp new file mode 100644 index 0000000..12d9d4a --- /dev/null +++ b/src/tools/radiant/Undo.cpp @@ -0,0 +1,909 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" + +/* + + QERadiant Undo/Redo + + +basic setup: + +<-g_undolist---------g_lastundo> <---map data---> <-g_lastredo---------g_redolist-> + + + undo/redo on the world_entity is special, only the epair changes are remembered + and the world entity never gets deleted. + + FIXME: maybe reset the Undo system at map load + maybe also reset the entityId at map load +*/ + +typedef struct undo_s +{ + double time; //time operation was performed + int id; //every undo has an unique id + int done; //true when undo is build + char *operation; //name of the operation + brush_t brushlist; //deleted brushes + entity_t entitylist; //deleted entities + struct undo_s *prev, *next; //next and prev undo in list +} undo_t; + +undo_t *g_undolist; //first undo in the list +undo_t *g_lastundo; //last undo in the list +undo_t *g_redolist; //first redo in the list +undo_t *g_lastredo; //last undo in list +int g_undoMaxSize = 64; //maximum number of undos +int g_undoSize = 0; //number of undos in the list +int g_undoMaxMemorySize = 2*1024*1024; //maximum undo memory (default 2 MB) +int g_undoMemorySize = 0; //memory size of undo buffer +int g_undoId = 1; //current undo ID (zero is invalid id) +int g_redoId = 1; //current redo ID (zero is invalid id) + + +/* +============= +Undo_MemorySize +============= +*/ +int Undo_MemorySize(void) +{ + /* + int size; + undo_t *undo; + brush_t *pBrush; + entity_t *pEntity; + + size = 0; + for (undo = g_undolist; undo; undo = undo->next) + { + for (pBrush = undo->brushlist.next ; pBrush != NULL && pBrush != &undo->brushlist ; pBrush = pBrush->next) + { + size += Brush_MemorySize(pBrush); + } + for (pEntity = undo->entitylist.next; pEntity != NULL && pEntity != &undo->entitylist; pEntity = pEntity->next) + { + size += Entity_MemorySize(pEntity); + } + size += sizeof(undo_t); + } + return size; + */ + return g_undoMemorySize; +} + +/* +============= +Undo_ClearRedo +============= +*/ +void Undo_ClearRedo(void) +{ + undo_t *redo, *nextredo; + brush_t *pBrush, *pNextBrush; + entity_t *pEntity, *pNextEntity; + + for (redo = g_redolist; redo; redo = nextredo) + { + nextredo = redo->next; + for (pBrush = redo->brushlist.next ; pBrush != NULL && pBrush != &redo->brushlist ; pBrush = pNextBrush) + { + pNextBrush = pBrush->next; + Brush_Free(pBrush); + } + for (pEntity = redo->entitylist.next; pEntity != NULL && pEntity != &redo->entitylist; pEntity = pNextEntity) + { + pNextEntity = pEntity->next; + Entity_Free(pEntity); + } + Mem_Free(redo); + } + g_redolist = NULL; + g_lastredo = NULL; + g_redoId = 1; +} + +/* +============= +Undo_Clear + + Clears the undo buffer. +============= +*/ +void Undo_Clear(void) +{ + undo_t *undo, *nextundo; + brush_t *pBrush, *pNextBrush; + entity_t *pEntity, *pNextEntity; + + Undo_ClearRedo(); + for (undo = g_undolist; undo; undo = nextundo) + { + nextundo = undo->next; + for (pBrush = undo->brushlist.next ; pBrush != NULL && pBrush != &undo->brushlist ; pBrush = pNextBrush) + { + pNextBrush = pBrush->next; + g_undoMemorySize -= Brush_MemorySize(pBrush); + Brush_Free(pBrush); + } + for (pEntity = undo->entitylist.next; pEntity != NULL && pEntity != &undo->entitylist; pEntity = pNextEntity) + { + pNextEntity = pEntity->next; + g_undoMemorySize -= Entity_MemorySize(pEntity); + Entity_Free(pEntity); + } + g_undoMemorySize -= sizeof(undo_t); + Mem_Free(undo); + } + g_undolist = NULL; + g_lastundo = NULL; + g_undoSize = 0; + g_undoMemorySize = 0; + g_undoId = 1; +} + +/* +============= +Undo_SetMaxSize +============= +*/ +void Undo_SetMaxSize(int size) +{ + Undo_Clear(); + if (size < 1) g_undoMaxSize = 1; + else g_undoMaxSize = size; +} + +/* +============= +Undo_GetMaxSize +============= +*/ +int Undo_GetMaxSize(void) +{ + return g_undoMaxSize; +} + +/* +============= +Undo_SetMaxMemorySize +============= +*/ +void Undo_SetMaxMemorySize(int size) +{ + Undo_Clear(); + if (size < 1024) g_undoMaxMemorySize = 1024; + else g_undoMaxMemorySize = size; +} + +/* +============= +Undo_GetMaxMemorySize +============= +*/ +int Undo_GetMaxMemorySize(void) +{ + return g_undoMaxMemorySize; +} + +/* +============= +Undo_FreeFirstUndo +============= +*/ +void Undo_FreeFirstUndo(void) +{ + undo_t *undo; + brush_t *pBrush, *pNextBrush; + entity_t *pEntity, *pNextEntity; + + //remove the oldest undo from the undo buffer + undo = g_undolist; + g_undolist = g_undolist->next; + g_undolist->prev = NULL; + // + for (pBrush = undo->brushlist.next ; pBrush != NULL && pBrush != &undo->brushlist ; pBrush = pNextBrush) + { + pNextBrush = pBrush->next; + g_undoMemorySize -= Brush_MemorySize(pBrush); + Brush_Free(pBrush); + } + for (pEntity = undo->entitylist.next; pEntity != NULL && pEntity != &undo->entitylist; pEntity = pNextEntity) + { + pNextEntity = pEntity->next; + g_undoMemorySize -= Entity_MemorySize(pEntity); + Entity_Free(pEntity); + } + g_undoMemorySize -= sizeof(undo_t); + Mem_Free(undo); + g_undoSize--; +} + +/* +============= +Undo_GeneralStart +============= +*/ +void Undo_GeneralStart(char *operation) +{ + undo_t *undo; + brush_t *pBrush; + entity_t *pEntity; + + + if (g_lastundo) + { + if (!g_lastundo->done) + { + common->Printf("Undo_Start: WARNING last undo not finished.\n"); + } + } + + undo = (undo_t *) Mem_ClearedAlloc(sizeof(undo_t)); + if (!undo) return; + memset(undo, 0, sizeof(undo_t)); + undo->brushlist.next = &undo->brushlist; + undo->brushlist.prev = &undo->brushlist; + undo->entitylist.next = &undo->entitylist; + undo->entitylist.prev = &undo->entitylist; + if (g_lastundo) g_lastundo->next = undo; + else g_undolist = undo; + undo->prev = g_lastundo; + undo->next = NULL; + g_lastundo = undo; + + undo->time = Sys_DoubleTime(); + // + if (g_undoId > g_undoMaxSize * 2) g_undoId = 1; + if (g_undoId <= 0) g_undoId = 1; + undo->id = g_undoId++; + undo->done = false; + undo->operation = operation; + //reset the undo IDs of all brushes using the new ID + for (pBrush = active_brushes.next; pBrush != NULL && pBrush != &active_brushes; pBrush = pBrush->next) + { + if (pBrush->undoId == undo->id) + { + pBrush->undoId = 0; + } + } + for (pBrush = selected_brushes.next; pBrush != NULL && pBrush != &selected_brushes; pBrush = pBrush->next) + { + if (pBrush->undoId == undo->id) + { + pBrush->undoId = 0; + } + } + //reset the undo IDs of all entities using thew new ID + for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pEntity->next) + { + if (pEntity->undoId == undo->id) + { + pEntity->undoId = 0; + } + } + g_undoMemorySize += sizeof(undo_t); + g_undoSize++; + //undo buffer is bound to a max + if (g_undoSize > g_undoMaxSize) + { + Undo_FreeFirstUndo(); + } +} + +/* +============= +Undo_BrushInUndo +============= +*/ +int Undo_BrushInUndo(undo_t *undo, brush_t *brush) +{ + brush_t *b; + + for (b = undo->brushlist.next; b != &undo->brushlist; b = b->next) + { + if (b == brush) return true; + } + return false; +} + +/* +============= +Undo_EntityInUndo +============= +*/ +int Undo_EntityInUndo(undo_t *undo, entity_t *ent) +{ + entity_t *e; + + for (e = undo->entitylist.next; e != &undo->entitylist; e = e->next) + { + if (e == ent) return true; + } + return false; +} + +/* +============= +Undo_Start +============= +*/ +void Undo_Start(char *operation) +{ + Undo_ClearRedo(); + Undo_GeneralStart(operation); +} + +/* +============= +Undo_AddBrush +============= +*/ +void Undo_AddBrush(brush_t *pBrush) +{ + if (!g_lastundo) + { + Sys_Status("Undo_AddBrushList: no last undo.\n"); + return; + } + if (g_lastundo->entitylist.next != &g_lastundo->entitylist) + { + Sys_Status("Undo_AddBrushList: WARNING adding brushes after entity.\n"); + } + //if the brush is already in the undo + if (Undo_BrushInUndo(g_lastundo, pBrush)) + return; + //clone the brush + brush_t* pClone = Brush_FullClone(pBrush); + //save the ID of the owner entity + pClone->ownerId = pBrush->owner->entityId; + + if (pBrush->owner && !(pBrush->owner->eclass->nShowFlags & ECLASS_WORLDSPAWN)) { + Undo_AddEntity(pBrush->owner); + } + + //save the old undo ID for previous undos + pClone->undoId = pBrush->undoId; + Brush_AddToList (pClone, &g_lastundo->brushlist); + // + g_undoMemorySize += Brush_MemorySize(pClone); +} + +/* +============= +Undo_AddBrushList +============= +*/ +void Undo_AddBrushList(brush_t *brushlist) +{ + brush_t *pBrush; + + if (!g_lastundo) + { + Sys_Status("Undo_AddBrushList: no last undo.\n"); + return; + } + if (g_lastundo->entitylist.next != &g_lastundo->entitylist) + { + Sys_Status("Undo_AddBrushList: WARNING adding brushes after entity.\n"); + } + //copy the brushes to the undo + for (pBrush = brushlist->next ; pBrush != NULL && pBrush != brushlist; pBrush=pBrush->next) + { + //if the brush is already in the undo + if (Undo_BrushInUndo(g_lastundo, pBrush)) + continue; + //clone the brush + brush_t* pClone = Brush_FullClone(pBrush); + //save the ID of the owner entity + pClone->ownerId = pBrush->owner->entityId; + //save the old undo ID from previous undos + pClone->undoId = pBrush->undoId; + + if ( pBrush->owner && pBrush->owner != world_entity ) { + Undo_AddEntity(pBrush->owner); + } + + + Brush_AddToList (pClone, &g_lastundo->brushlist); + // + g_undoMemorySize += Brush_MemorySize(pClone); + } +} + +/* +============= +Undo_EndBrush +============= +*/ +void Undo_EndBrush(brush_t *pBrush) +{ + if (!g_lastundo) + { + //Sys_Status("Undo_End: no last undo.\n"); + return; + } + if (g_lastundo->done) + { + //Sys_Status("Undo_End: last undo already finished.\n"); + return; + } + pBrush->undoId = g_lastundo->id; +} + +/* +============= +Undo_EndBrushList +============= +*/ +void Undo_EndBrushList(brush_t *brushlist) +{ + if (!g_lastundo) + { + //Sys_Status("Undo_End: no last undo.\n"); + return; + } + if (g_lastundo->done) + { + //Sys_Status("Undo_End: last undo already finished.\n"); + return; + } + for (brush_t* pBrush = brushlist->next; pBrush != NULL && pBrush != brushlist; pBrush=pBrush->next) + { + pBrush->undoId = g_lastundo->id; + } +} + +/* +============= +Undo_AddEntity +============= +*/ +void Undo_AddEntity(entity_t *entity) +{ + entity_t* pClone; + + if (!g_lastundo) + { + Sys_Status("Undo_AddEntity: no last undo.\n"); + return; + } + //if the entity is already in the undo + if (Undo_EntityInUndo(g_lastundo, entity)) + return; + //clone the entity + pClone = Entity_Clone(entity); + //NOTE: Entity_Clone adds the entity to the entity list + // so we remove it from that list here + Entity_RemoveFromList(pClone); + //save the old undo ID for previous undos + pClone->undoId = entity->undoId; + //save the entity ID (we need a full clone) + pClone->entityId = entity->entityId; + // + Entity_AddToList(pClone, &g_lastundo->entitylist); + // + g_undoMemorySize += Entity_MemorySize(pClone); +} + +/* +============= +Undo_EndEntity +============= +*/ +void Undo_EndEntity(entity_t *entity) +{ + if (!g_lastundo) + { + //Sys_Status("Undo_End: no last undo.\n"); + return; + } + if (g_lastundo->done) + { + //Sys_Status("Undo_End: last undo already finished.\n"); + return; + } + if (entity == world_entity) + { + //Sys_Status("Undo_AddEntity: undo on world entity.\n"); + //NOTE: we never delete the world entity when undoing an operation + // we only transfer the epairs + return; + } + entity->undoId = g_lastundo->id; +} + +/* +============= +Undo_End +============= +*/ +void Undo_End(void) +{ + if (!g_lastundo) + { + //Sys_Status("Undo_End: no last undo.\n"); + return; + } + if (g_lastundo->done) + { + //Sys_Status("Undo_End: last undo already finished.\n"); + return; + } + g_lastundo->done = true; + + //undo memory size is bound to a max + while (g_undoMemorySize > g_undoMaxMemorySize) + { + //always keep one undo + if (g_undolist == g_lastundo) break; + Undo_FreeFirstUndo(); + } + // + //Sys_Status("undo size = %d, undo memory = %d\n", g_undoSize, g_undoMemorySize); +} + +/* +============= +Undo_Undo +============= +*/ +void Undo_Undo(void) +{ + undo_t *undo, *redo; + brush_t *pBrush, *pNextBrush; + entity_t *pEntity, *pNextEntity, *pUndoEntity; + + if (!g_lastundo) + { + Sys_Status("Nothing left to undo.\n"); + return; + } + if (!g_lastundo->done) + { + Sys_Status("Undo_Undo: WARNING: last undo not yet finished!\n"); + } + // get the last undo + undo = g_lastundo; + if (g_lastundo->prev) g_lastundo->prev->next = NULL; + else g_undolist = NULL; + g_lastundo = g_lastundo->prev; + + //allocate a new redo + redo = (undo_t *) Mem_ClearedAlloc(sizeof(undo_t)); + if (!redo) return; + memset(redo, 0, sizeof(undo_t)); + redo->brushlist.next = &redo->brushlist; + redo->brushlist.prev = &redo->brushlist; + redo->entitylist.next = &redo->entitylist; + redo->entitylist.prev = &redo->entitylist; + if (g_lastredo) g_lastredo->next = redo; + else g_redolist = redo; + redo->prev = g_lastredo; + redo->next = NULL; + g_lastredo = redo; + redo->time = Sys_DoubleTime(); + redo->id = g_redoId++; + redo->done = true; + redo->operation = undo->operation; + + //reset the redo IDs of all brushes using the new ID + for (pBrush = active_brushes.next; pBrush != NULL && pBrush != &active_brushes; pBrush = pBrush->next) + { + if (pBrush->redoId == redo->id) + { + pBrush->redoId = 0; + } + } + for (pBrush = selected_brushes.next; pBrush != NULL && pBrush != &selected_brushes; pBrush = pBrush->next) + { + if (pBrush->redoId == redo->id) + { + pBrush->redoId = 0; + } + } + //reset the redo IDs of all entities using thew new ID + for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pEntity->next) + { + if (pEntity->redoId == redo->id) + { + pEntity->redoId = 0; + } + } + + // remove current selection + Select_Deselect(); + // move "created" brushes to the redo + for (pBrush = active_brushes.next; pBrush != NULL && pBrush != &active_brushes; pBrush=pNextBrush) + { + pNextBrush = pBrush->next; + if (pBrush->undoId == undo->id) + { + //Brush_Free(pBrush); + //move the brush to the redo + Brush_RemoveFromList(pBrush); + Brush_AddToList(pBrush, &redo->brushlist); + //make sure the ID of the owner is stored + pBrush->ownerId = pBrush->owner->entityId; + //unlink the brush from the owner entity + Entity_UnlinkBrush(pBrush); + } + } + // move "created" entities to the redo + for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pNextEntity) + { + pNextEntity = pEntity->next; + if (pEntity->undoId == undo->id) + { + // check if this entity is in the undo + for (pUndoEntity = undo->entitylist.next; pUndoEntity != NULL && pUndoEntity != &undo->entitylist; pUndoEntity = pUndoEntity->next) + { + // move brushes to the undo entity + if (pUndoEntity->entityId == pEntity->entityId) + { + pUndoEntity->brushes.next = pEntity->brushes.next; + pUndoEntity->brushes.prev = pEntity->brushes.prev; + pEntity->brushes.next = &pEntity->brushes; + pEntity->brushes.prev = &pEntity->brushes; + } + } + // + //Entity_Free(pEntity); + //move the entity to the redo + Entity_RemoveFromList(pEntity); + Entity_AddToList(pEntity, &redo->entitylist); + } + } + // add the undo entities back into the entity list + for (pEntity = undo->entitylist.next; pEntity != NULL && pEntity != &undo->entitylist; pEntity = undo->entitylist.next) + { + g_undoMemorySize -= Entity_MemorySize(pEntity); + //if this is the world entity + if (pEntity->entityId == world_entity->entityId) + { + //free the epairs of the world entity + Entity_FreeEpairs(world_entity); + //set back the original epairs + world_entity->epairs = pEntity->epairs; + //free the world_entity clone that stored the epairs + Entity_Free(pEntity); + } + else + { + Entity_RemoveFromList(pEntity); + Entity_AddToList(pEntity, &entities); + pEntity->redoId = redo->id; + } + } + // add the undo brushes back into the selected brushes + for (pBrush = undo->brushlist.next; pBrush != NULL && pBrush != &undo->brushlist; pBrush = undo->brushlist.next) + { + g_undoMemorySize -= Brush_MemorySize(pBrush); + Brush_RemoveFromList(pBrush); + Brush_AddToList(pBrush, &active_brushes); + for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pEntity->next) + { + if (pEntity->entityId == pBrush->ownerId) + { + Entity_LinkBrush(pEntity, pBrush); + break; + } + } + //if the brush is not linked then it should be linked into the world entity + if (pEntity == NULL || pEntity == &entities) + { + Entity_LinkBrush(world_entity, pBrush); + } + //build the brush + //Brush_Build(pBrush); + Select_Brush(pBrush); + pBrush->redoId = redo->id; + } + // + common->Printf("%s undone.\n", undo->operation); + // free the undo + g_undoMemorySize -= sizeof(undo_t); + Mem_Free(undo); + g_undoSize--; + g_undoId--; + if (g_undoId <= 0) g_undoId = 2 * g_undoMaxSize; + // + + Sys_BeginWait(); + brush_t *b, *next; + for (b = active_brushes.next ; b != NULL && b != &active_brushes ; b=next) { + next = b->next; + Brush_Build( b, true, false, false ); + } + for (b = selected_brushes.next ; b != NULL && b != &selected_brushes ; b=next) { + next = b->next; + Brush_Build( b, true, false, false ); + } + Sys_EndWait(); + + g_bScreenUpdates = true; + Sys_UpdateWindows(W_ALL); +} + +/* +============= +Undo_Redo +============= +*/ +void Undo_Redo(void) +{ + undo_t *redo; + brush_t *pBrush, *pNextBrush; + entity_t *pEntity, *pNextEntity, *pRedoEntity; + + if (!g_lastredo) + { + Sys_Status("Nothing left to redo.\n"); + return; + } + if (g_lastundo) + { + if (!g_lastundo->done) + { + Sys_Status("WARNING: last undo not finished.\n"); + } + } + // get the last redo + redo = g_lastredo; + if (g_lastredo->prev) g_lastredo->prev->next = NULL; + else g_redolist = NULL; + g_lastredo = g_lastredo->prev; + // + Undo_GeneralStart(redo->operation); + // remove current selection + Select_Deselect(); + // move "created" brushes back to the last undo + for (pBrush = active_brushes.next; pBrush != NULL && pBrush != &active_brushes; pBrush = pNextBrush) + { + pNextBrush = pBrush->next; + if (pBrush->redoId == redo->id) + { + //move the brush to the undo + Brush_RemoveFromList(pBrush); + Brush_AddToList(pBrush, &g_lastundo->brushlist); + g_undoMemorySize += Brush_MemorySize(pBrush); + pBrush->ownerId = pBrush->owner->entityId; + Entity_UnlinkBrush(pBrush); + } + } + // move "created" entities back to the last undo + for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pNextEntity) + { + pNextEntity = pEntity->next; + if (pEntity->redoId == redo->id) + { + // check if this entity is in the redo + for (pRedoEntity = redo->entitylist.next; pRedoEntity != NULL && pRedoEntity != &redo->entitylist; pRedoEntity = pRedoEntity->next) + { + // move brushes to the redo entity + if (pRedoEntity->entityId == pEntity->entityId) + { + pRedoEntity->brushes.next = pEntity->brushes.next; + pRedoEntity->brushes.prev = pEntity->brushes.prev; + pEntity->brushes.next = &pEntity->brushes; + pEntity->brushes.prev = &pEntity->brushes; + } + } + // + //Entity_Free(pEntity); + //move the entity to the redo + Entity_RemoveFromList(pEntity); + Entity_AddToList(pEntity, &g_lastundo->entitylist); + g_undoMemorySize += Entity_MemorySize(pEntity); + } + } + // add the undo entities back into the entity list + for (pEntity = redo->entitylist.next; pEntity != NULL && pEntity != &redo->entitylist; pEntity = redo->entitylist.next) + { + //if this is the world entity + if (pEntity->entityId == world_entity->entityId) + { + //free the epairs of the world entity + Entity_FreeEpairs(world_entity); + //set back the original epairs + world_entity->epairs = pEntity->epairs; + //free the world_entity clone that stored the epairs + Entity_Free(pEntity); + } + else + { + Entity_RemoveFromList(pEntity); + Entity_AddToList(pEntity, &entities); + } + } + // add the redo brushes back into the selected brushes + for (pBrush = redo->brushlist.next; pBrush != NULL && pBrush != &redo->brushlist; pBrush = redo->brushlist.next) + { + Brush_RemoveFromList(pBrush); + Brush_AddToList(pBrush, &active_brushes); + for (pEntity = entities.next; pEntity != NULL && pEntity != &entities; pEntity = pEntity->next) + { + if (pEntity->entityId == pBrush->ownerId) + { + Entity_LinkBrush(pEntity, pBrush); + break; + } + } + //if the brush is not linked then it should be linked into the world entity + if (pEntity == NULL || pEntity == &entities) + { + Entity_LinkBrush(world_entity, pBrush); + } + //build the brush + //Brush_Build(pBrush); + Select_Brush(pBrush); + } + // + Undo_End(); + // + common->Printf("%s redone.\n", redo->operation); + // + g_redoId--; + // free the undo + Mem_Free(redo); + // + g_bScreenUpdates = true; + Sys_UpdateWindows(W_ALL); +} + +/* +============= +Undo_RedoAvailable +============= +*/ +int Undo_RedoAvailable(void) +{ + if (g_lastredo) return true; + return false; +} + +/* +============= +Undo_UndoAvailable +============= +*/ +int Undo_UndoAvailable(void) +{ + if (g_lastundo) + { + if (g_lastundo->done) + return true; + } + return false; +} diff --git a/src/tools/radiant/Undo.h b/src/tools/radiant/Undo.h new file mode 100644 index 0000000..8da4e81 --- /dev/null +++ b/src/tools/radiant/Undo.h @@ -0,0 +1,65 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +//start operation +void Undo_Start(char *operation); +//end operation +void Undo_End(void); +//add brush to the undo +void Undo_AddBrush(brush_t *pBrush); +//add a list with brushes to the undo +void Undo_AddBrushList(brush_t *brushlist); +//end a brush after the operation is performed +void Undo_EndBrush(brush_t *pBrush); +//end a list with brushes after the operation is performed +void Undo_EndBrushList(brush_t *brushlist); +//add entity to undo +void Undo_AddEntity(entity_t *entity); +//end an entity after the operation is performed +void Undo_EndEntity(entity_t *entity); +//undo last operation +void Undo_Undo(void); +//redo last undone operation +void Undo_Redo(void); +//returns true if there is something to be undone available +int Undo_UndoAvailable(void); +//returns true if there is something to redo available +int Undo_RedoAvailable(void); +//clear the undo buffer +void Undo_Clear(void); +//set maximum undo size (default 64) +void Undo_SetMaxSize(int size); +//get maximum undo size +int Undo_GetMaxSize(void); +//set maximum undo memory in bytes (default 2 MB) +void Undo_SetMaxMemorySize(int size); +//get maximum undo memory in bytes +int Undo_GetMaxMemorySize(void); +//returns the amount of memory used by undo +int Undo_MemorySize(void); + diff --git a/src/tools/radiant/VERTSEL.CPP b/src/tools/radiant/VERTSEL.CPP new file mode 100644 index 0000000..d77c568 --- /dev/null +++ b/src/tools/radiant/VERTSEL.CPP @@ -0,0 +1,428 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" + +#define NEWEDGESEL 1 + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +int FindPoint(idVec3 point) { + int i, j; + + for (i = 0; i < g_qeglobals.d_numpoints; i++) { + for (j = 0; j < 3; j++) { + if (idMath::Fabs(point[j] - g_qeglobals.d_points[i][j]) > 0.1) { + break; + } + } + + if (j == 3) { + return i; + } + } + + VectorCopy(point, g_qeglobals.d_points[g_qeglobals.d_numpoints]); + if (g_qeglobals.d_numpoints < MAX_POINTS - 1) { + g_qeglobals.d_numpoints++; + } + + return g_qeglobals.d_numpoints - 1; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +int FindEdge(int p1, int p2, face_t *f) { + int i; + + for (i = 0; i < g_qeglobals.d_numedges; i++) { + if (g_qeglobals.d_edges[i].p1 == p2 && g_qeglobals.d_edges[i].p2 == p1) { + g_qeglobals.d_edges[i].f2 = f; + return i; + } + } + + g_qeglobals.d_edges[g_qeglobals.d_numedges].p1 = p1; + g_qeglobals.d_edges[g_qeglobals.d_numedges].p2 = p2; + g_qeglobals.d_edges[g_qeglobals.d_numedges].f1 = f; + + if (g_qeglobals.d_numedges < MAX_EDGES - 1) { + g_qeglobals.d_numedges++; + } + + return g_qeglobals.d_numedges - 1; +} + +#ifdef NEWEDGESEL +void MakeFace (brush_t * b, face_t * f) +#else +void MakeFace (face_t * f) +#endif +{ + idWinding *w; + int i; + int pnum[128]; + +#ifdef NEWEDGESEL + w = Brush_MakeFaceWinding(b, f); +#else + w = Brush_MakeFaceWinding(selected_brushes.next, f); +#endif + if (!w) { + return; + } + for (i = 0; i < w->GetNumPoints(); i++) { + pnum[i] = FindPoint( (*w)[i].ToVec3() ); + } + for (i = 0; i < w->GetNumPoints(); i++) { + FindEdge(pnum[i], pnum[(i + 1) % w->GetNumPoints()], f); + } + delete w; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void SetupVertexSelection(void) { + face_t *f; + brush_t *b; + + g_qeglobals.d_numpoints = 0; + g_qeglobals.d_numedges = 0; + +#ifdef NEWEDGESEL + for (b = selected_brushes.next; b != &selected_brushes; b = b->next) { + for (f = b->brush_faces; f; f = f->next) { + MakeFace(b, f); + } + } + +#else + if (!QE_SingleBrush()) { + return; + } + + b = selected_brushes.next; + for (f = b->brush_faces; f; f = f->next) { + MakeFace(b, f); + } +#endif +} + +#ifdef NEWEDGESEL +void SelectFaceEdge (brush_t * b, face_t * f, int p1, int p2) +#else +void SelectFaceEdge (face_t * f, int p1, int p2) +#endif +{ + idWinding *w; + int i, j, k; + int pnum[128]; + +#ifdef NEWEDGESEL + w = Brush_MakeFaceWinding(b, f); +#else + w = Brush_MakeFaceWinding(selected_brushes.next, f); +#endif + if (!w) { + return; + } + for (i = 0; i < w->GetNumPoints(); i++) { + pnum[i] = FindPoint( (*w)[i].ToVec3() ); + } + for (i = 0; i < w->GetNumPoints(); i++) { + if (pnum[i] == p1 && pnum[(i + 1) % w->GetNumPoints()] == p2) { + VectorCopy(g_qeglobals.d_points[pnum[i]], f->planepts[0]); + VectorCopy(g_qeglobals.d_points[pnum[(i + 1) % w->GetNumPoints()]], f->planepts[1]); + VectorCopy(g_qeglobals.d_points[pnum[(i + 2) % w->GetNumPoints()]], f->planepts[2]); + for (j = 0; j < 3; j++) { + for (k = 0; k < 3; k++) { + f->planepts[j][k] = + floor(f->planepts[j][k] / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize; + } + } + AddPlanept(&f->planepts[0]); + AddPlanept(&f->planepts[1]); + break; + } + } + if ( i == w->GetNumPoints() ) { + Sys_Status("SelectFaceEdge: failed\n"); + } + delete w; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void SelectVertex(int p1) { + brush_t *b; + idWinding *w; + int i, j, k; + face_t *f; + +#ifdef NEWEDGESEL + for (b = selected_brushes.next; b != &selected_brushes; b = b->next) { + for (f = b->brush_faces; f; f = f->next) { + w = Brush_MakeFaceWinding(b, f); + if (!w) { + continue; + } + + for (i = 0; i < w->GetNumPoints(); i++) { + if ( FindPoint( (*w)[i].ToVec3() ) == p1 ) { + VectorCopy((*w)[(i + w->GetNumPoints() - 1) % w->GetNumPoints()], f->planepts[0]); + VectorCopy((*w)[i], f->planepts[1]); + VectorCopy((*w)[(i + 1) % w->GetNumPoints()], f->planepts[2]); + for (j = 0; j < 3; j++) { + for (k = 0; k < 3; k++) { + // f->planepts[j][k] = floor(f->planepts[j][k]/g_qeglobals.d_gridsize+0.5)*g_qeglobals.d_gridsize; + } + } + + AddPlanept(&f->planepts[1]); + + // MessageBeep(-1); + break; + } + } + + delete w; + } + } + +#else + b = selected_brushes.next; + for (f = b->brush_faces; f; f = f->next) { + w = Brush_MakeFaceWinding(b, f); + if (!w) { + continue; + } + + for (i = 0; i < w->GetNumPoints(); i++) { + if (FindPoint(w[i]) == p1) { + VectorCopy(w[(i + w->GetNumPoints() - 1) % w->GetNumPoints()], f->planepts[0]); + VectorCopy(w[i], f->planepts[1]); + VectorCopy(w[(i + 1) % w->GetNumPoints()], f->planepts[2]); + for (j = 0; j < 3; j++) { + for (k = 0; k < 3; k++) { + // f->planepts[j][k] = floor(f->planepts[j][k]/g_qeglobals.d_gridsize+0.5)*g_qeglobals.d_gridsize; + } + } + + AddPlanept(&f->planepts[1]); + + // MessageBeep(-1); + break; + } + } + + delete w; + } +#endif +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void SelectEdgeByRay(idVec3 org, idVec3 dir) { + int i, j, besti; + float d, bestd; + idVec3 mid, temp; + pedge_t *e; + + // find the edge closest to the ray + besti = -1; + bestd = 8; + + for (i = 0; i < g_qeglobals.d_numedges; i++) { + for (j = 0; j < 3; j++) { + mid[j] = 0.5 * (g_qeglobals.d_points[g_qeglobals.d_edges[i].p1][j] + g_qeglobals.d_points[g_qeglobals.d_edges[i].p2][j]); + } + + temp = mid - org; + d = temp * dir; + temp = org + d * dir; + temp = mid - temp; + d = temp.Length(); + if ( d < bestd ) { + bestd = d; + besti = i; + } + } + + if (besti == -1) { + Sys_Status("Click didn't hit an edge\n"); + return; + } + + Sys_Status("hit edge\n"); + + // + // make the two faces that border the edge use the two edge points as primary drag + // points + // + g_qeglobals.d_num_move_points = 0; + e = &g_qeglobals.d_edges[besti]; +#ifdef NEWEDGESEL + for (brush_t * b = selected_brushes.next; b != &selected_brushes; b = b->next) { + SelectFaceEdge(b, e->f1, e->p1, e->p2); + SelectFaceEdge(b, e->f2, e->p2, e->p1); + } + +#else + SelectFaceEdge(e->f1, e->p1, e->p2); + SelectFaceEdge(e->f2, e->p2, e->p1); +#endif +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void SelectVertexByRay(idVec3 org, idVec3 dir) { + int i, besti; + float d, bestd; + idVec3 temp; + + float scale = g_pParentWnd->ActiveXY()->Scale(); + // find the point closest to the ray + besti = -1; + bestd = 8 / scale / 2; + + for (i = 0; i < g_qeglobals.d_numpoints; i++) { + temp = g_qeglobals.d_points[i] - org; + d = temp * dir; + temp = org + d * dir; + temp = g_qeglobals.d_points[i] - temp; + d = temp.Length(); + if ( d < bestd ) { + bestd = d; + besti = i; + } + } + + if (besti == -1 || bestd > 8 / scale / 2 ) { + Sys_Status("Click didn't hit a vertex\n"); + return; + } + + Sys_Status("hit vertex\n"); + g_qeglobals.d_move_points[g_qeglobals.d_num_move_points++] = &g_qeglobals.d_points[besti]; + + // SelectVertex (besti); +} + +extern void AddPatchMovePoint(idVec3 v, bool bMulti, bool bFull); + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void SelectCurvePointByRay(const idVec3 &org, const idVec3 &dir, int buttons) { + int i, besti; + float d, bestd; + idVec3 temp; + + // find the point closest to the ray + float scale = g_pParentWnd->ActiveXY()->Scale(); + besti = -1; + bestd = 8 / scale / 2; + //bestd = 8; + + for (i = 0; i < g_qeglobals.d_numpoints; i++) { + temp = g_qeglobals.d_points[i] - org; + d = temp * dir; + temp = org + d * dir; + temp = g_qeglobals.d_points[i] - temp; + d = temp.Length(); + if ( d <= bestd ) { + bestd = d; + besti = i; + } + } + + if (besti == -1) { + if (g_pParentWnd->ActiveXY()->AreaSelectOK()) { + g_qeglobals.d_select_mode = sel_area; + VectorCopy(org, g_qeglobals.d_vAreaTL); + VectorCopy(org, g_qeglobals.d_vAreaBR); + } + + return; + } + + // Sys_Status ("hit vertex\n"); + AddPatchMovePoint( g_qeglobals.d_points[besti], ( buttons & MK_CONTROL ) != 0, ( buttons & MK_SHIFT ) != 0 ); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void SelectSplinePointByRay(const idVec3 &org, const idVec3 &dir, int buttons) { + int i, besti; + float d, bestd; + idVec3 temp; + + // find the point closest to the ray + besti = -1; + bestd = 8; + + for (i = 0; i < g_qeglobals.d_numpoints; i++) { + temp = g_qeglobals.d_points[i] - org; + d = temp * dir; + temp = org + d * dir; + temp = g_qeglobals.d_points[i] - temp; + d = temp.Length(); + if ( d <= bestd ) { + bestd = d; + besti = i; + } + } + + if (besti == -1) { + return; + } + + Sys_Status("hit curve point\n"); + g_qeglobals.d_num_move_points = 0; + g_qeglobals.d_move_points[g_qeglobals.d_num_move_points++] = &g_qeglobals.d_points[besti]; + + // g_splineList->setSelectedPoint(&g_qeglobals.d_points[besti]); +} diff --git a/src/tools/radiant/WIN_DLG.CPP b/src/tools/radiant/WIN_DLG.CPP new file mode 100644 index 0000000..52d9459 --- /dev/null +++ b/src/tools/radiant/WIN_DLG.CPP @@ -0,0 +1,635 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" + +BOOL CALLBACK EditCommandDlgProc ( + HWND hwndDlg, // handle to dialog box + UINT uMsg, // message + WPARAM wParam, // first message parameter + LPARAM lParam // second message parameter + ) +{ + char key[1024]; + char value[1024]; + const char *temp; + int index; + HWND hOwner; + + hOwner = GetParent (hwndDlg); + + switch (uMsg) + { + case WM_INITDIALOG: + index = SendDlgItemMessage (hOwner, IDC_CMD_LIST, LB_GETCURSEL, 0, 0); + if (index >= 0) + { + SendDlgItemMessage(hOwner, IDC_CMD_LIST, LB_GETTEXT, index, (LPARAM) (LPCTSTR) key); + temp = ValueForKey (g_qeglobals.d_project_entity, key); + strcpy (value, temp); + SetDlgItemText(hwndDlg, IDC_CMDMENUTEXT, key); + SetDlgItemText(hwndDlg, IDC_CMDCOMMAND, value); + } + return FALSE; + break; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDOK: + if (!GetDlgItemText(hwndDlg, IDC_CMDMENUTEXT, key, 64)) + { + common->Printf ("Command not added\n"); + return FALSE; + } + + if (!GetDlgItemText(hwndDlg, IDC_CMDCOMMAND, value, 64)) + { + common->Printf ("Command not added\n"); + return FALSE; + } + + //if (key[0] == 'b' && key[1] == 's' && key[2] == 'p') + //{ + SetKeyValue (g_qeglobals.d_project_entity, key, value); + FillBSPMenu (); + //} + //else + // common->Printf ("BSP commands must be preceded by \"bsp\""); + + EndDialog(hwndDlg, 1); + return TRUE; + + case IDCANCEL: + EndDialog(hwndDlg, 0); + return TRUE; + } + } + return FALSE; +} + +BOOL CALLBACK AddCommandDlgProc ( + HWND hwndDlg, // handle to dialog box + UINT uMsg, // message + WPARAM wParam, // first message parameter + LPARAM lParam // second message parameter + ) +{ + char key[64]; + char value[128]; + + switch (uMsg) + { + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDOK: + if (!GetDlgItemText(hwndDlg, IDC_CMDMENUTEXT, key, 64)) + { + common->Printf ("Command not added\n"); + return FALSE; + } + + if (!GetDlgItemText(hwndDlg, IDC_CMDCOMMAND, value, 64)) + { + common->Printf ("Command not added\n"); + return FALSE; + } + + if (key[0] == 'b' && key[1] == 's' && key[2] == 'p') + { + SetKeyValue (g_qeglobals.d_project_entity, key, value); + FillBSPMenu (); + } + else + common->Printf ("BSP commands must be preceded by \"bsp\""); + + EndDialog(hwndDlg, 1); + return TRUE; + + case IDCANCEL: + EndDialog(hwndDlg, 0); + return TRUE; + } + } + return FALSE; +} + +void UpdateBSPCommandList (HWND hwndDlg) +{ + int i; + + SendDlgItemMessage(hwndDlg, IDC_CMD_LIST, LB_RESETCONTENT, 0 , 0); + + i = 0; + int count = g_qeglobals.d_project_entity->epairs.GetNumKeyVals(); + for (int j = 0; j < count; j++) { + if (g_qeglobals.d_project_entity->epairs.GetKeyVal(j)->GetKey()[0] == 'b' && g_qeglobals.d_project_entity->epairs.GetKeyVal(j)->GetKey()[1] == 's' && g_qeglobals.d_project_entity->epairs.GetKeyVal(j)->GetKey()[2] == 'p') { + SendDlgItemMessage(hwndDlg, IDC_CMD_LIST, LB_ADDSTRING, i , (LPARAM) g_qeglobals.d_project_entity->epairs.GetKeyVal(j)->GetKey().c_str()); + i++; + } + } +} + + +// FIXME: turn this into an MFC dialog +BOOL CALLBACK ProjectDlgProc ( + HWND hwndDlg, // handle to dialog box + UINT uMsg, // message + WPARAM wParam, // first message parameter + LPARAM lParam // second message parameter + ) +{ + char key[1024]; + char value[1024]; + int index; + + switch (uMsg) + { + case WM_INITDIALOG: + SetDlgItemText(hwndDlg, IDC_PRJBASEPATH, ValueForKey (g_qeglobals.d_project_entity, "basepath")); + SetDlgItemText(hwndDlg, IDC_PRJMAPSPATH, ValueForKey (g_qeglobals.d_project_entity, "mapspath")); + SetDlgItemText(hwndDlg, IDC_PRJRSHCMD, ValueForKey (g_qeglobals.d_project_entity, "rshcmd")); + SetDlgItemText(hwndDlg, IDC_PRJREMOTEBASE, ValueForKey (g_qeglobals.d_project_entity, "remotebasepath")); + SetDlgItemText(hwndDlg, IDC_PRJENTITYPATH, ValueForKey (g_qeglobals.d_project_entity, "entitypath")); + SetDlgItemText(hwndDlg, IDC_PRJTEXPATH, ValueForKey (g_qeglobals.d_project_entity, "texturepath")); + UpdateBSPCommandList (hwndDlg); + // Timo + // additional fields + CheckDlgButton( hwndDlg, IDC_CHECK_BPRIMIT, (g_qeglobals.m_bBrushPrimitMode) ? BST_CHECKED : BST_UNCHECKED ); +// SendMessage( ::GetDlgItem( hwndDlg, IDC_CHECK_BPRIMIT ), BM_SETCHECK, (WPARAM) g_qeglobals.m_bBrushPrimitMode, 0 ); + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDC_ADDCMD: +// DialogBox(g_qeglobals.d_hInstance, (char *)IDD_ADDCMD, g_qeglobals.d_hwndMain, AddCommandDlgProc); + DialogBox(g_qeglobals.d_hInstance, (char *)IDD_ADDCMD, hwndDlg, AddCommandDlgProc); + UpdateBSPCommandList (hwndDlg); + break; + + case IDC_EDITCMD: +// DialogBox(g_qeglobals.d_hInstance, (char *)IDD_ADDCMD, g_qeglobals.d_hwndMain, EditCommandDlgProc); + DialogBox(g_qeglobals.d_hInstance, (char *)IDD_ADDCMD, hwndDlg, EditCommandDlgProc); + UpdateBSPCommandList (hwndDlg); + break; + + case IDC_REMCMD: + index = SendDlgItemMessage (hwndDlg, IDC_CMD_LIST, LB_GETCURSEL, 0, 0); + SendDlgItemMessage(hwndDlg, IDC_CMD_LIST, LB_GETTEXT, index, (LPARAM) (LPCTSTR) key); + DeleteKey (g_qeglobals.d_project_entity, key); + common->Printf ("Selected %d\n", index); + UpdateBSPCommandList (hwndDlg); + break; + + case IDOK: + GetDlgItemText(hwndDlg, IDC_PRJBASEPATH, value, 1024); + SetKeyValue (g_qeglobals.d_project_entity, "basepath", value); + GetDlgItemText(hwndDlg, IDC_PRJMAPSPATH, value, 1024); + SetKeyValue (g_qeglobals.d_project_entity, "mapspath", value); + GetDlgItemText(hwndDlg, IDC_PRJRSHCMD, value, 1024); + SetKeyValue (g_qeglobals.d_project_entity, "rshcmd", value); + GetDlgItemText(hwndDlg, IDC_PRJREMOTEBASE, value, 1024); + SetKeyValue (g_qeglobals.d_project_entity, "remotebasepath", value); + GetDlgItemText(hwndDlg, IDC_PRJENTITYPATH, value, 1024); + SetKeyValue (g_qeglobals.d_project_entity, "entitypath", value); + GetDlgItemText(hwndDlg, IDC_PRJTEXPATH, value, 1024); + SetKeyValue (g_qeglobals.d_project_entity, "texturepath", value); + // Timo + // read additional fields + if ( IsDlgButtonChecked( hwndDlg, IDC_CHECK_BPRIMIT ) ) + { + g_qeglobals.m_bBrushPrimitMode = TRUE; + } + else + { + g_qeglobals.m_bBrushPrimitMode = FALSE; + } + SetKeyValue ( g_qeglobals.d_project_entity, "brush_primit", ( g_qeglobals.m_bBrushPrimitMode ? "1" : "0" ) ); + + EndDialog(hwndDlg, 1); + QE_SaveProject(g_strProject); + return TRUE; + + case IDCANCEL: + EndDialog(hwndDlg, 0); + return TRUE; + } + } + return FALSE; +} + +void DoProjectSettings() +{ + DialogBox(g_qeglobals.d_hInstance, (char *)IDD_PROJECT, g_pParentWnd->GetSafeHwnd(), ProjectDlgProc); +} + + + +BOOL CALLBACK GammaDlgProc ( + HWND hwndDlg, // handle to dialog box + UINT uMsg, // message + WPARAM wParam, // first message parameter + LPARAM lParam // second message parameter + ) +{ + char sz[256]; + + switch (uMsg) + { + case WM_INITDIALOG: + sprintf(sz, "%1.1f", g_qeglobals.d_savedinfo.fGamma); + SetWindowText(GetDlgItem(hwndDlg, IDC_G_EDIT), sz); + return TRUE; + case WM_COMMAND: + switch (LOWORD(wParam)) + { + + case IDOK: + GetWindowText(GetDlgItem(hwndDlg, IDC_G_EDIT), sz, 255); + g_qeglobals.d_savedinfo.fGamma = atof(sz); + EndDialog(hwndDlg, 1); + return TRUE; + + case IDCANCEL: + EndDialog(hwndDlg, 0); + return TRUE; + } + } + return FALSE; +} + + + +void DoGamma(void) +{ + if ( DialogBox(g_qeglobals.d_hInstance, (char *)IDD_GAMMA, g_pParentWnd->GetSafeHwnd(), GammaDlgProc)) + { + } +} + +//================================================ + + +void SelectBrush (int entitynum, int brushnum) +{ + entity_t *e; + brush_t *b; + int i; + + if (entitynum == 0) + e = world_entity; + else + { + e = entities.next; + while (--entitynum) + { + e=e->next; + if (e == &entities) + { + Sys_Status ("No such entity.", 0); + return; + } + } + } + + b = e->brushes.onext; + if (b == &e->brushes) + { + Sys_Status ("No such brush.", 0); + return; + } + while (brushnum--) + { + b=b->onext; + if (b == &e->brushes) + { + Sys_Status ("No such brush.", 0); + return; + } + } + + Brush_RemoveFromList (b); + Brush_AddToList (b, &selected_brushes); + + + Sys_UpdateWindows (W_ALL); + for (i=0 ; i<3 ; i++) + { + if (g_pParentWnd->GetXYWnd()) + g_pParentWnd->GetXYWnd()->GetOrigin()[i] = (b->mins[i] + b->maxs[i])/2; + + if (g_pParentWnd->GetXZWnd()) + g_pParentWnd->GetXZWnd()->GetOrigin()[i] = (b->mins[i] + b->maxs[i])/2; + + if (g_pParentWnd->GetYZWnd()) + g_pParentWnd->GetYZWnd()->GetOrigin()[i] = (b->mins[i] + b->maxs[i])/2; + } + + Sys_Status ("Selected.", 0); +} + +/* +================= +GetSelectionIndex +================= +*/ +void GetSelectionIndex (int *ent, int *brush) +{ + brush_t *b, *b2; + entity_t *entity; + + *ent = *brush = 0; + + b = selected_brushes.next; + if (b == &selected_brushes) + return; + + // find entity + if (b->owner != world_entity) + { + (*ent)++; + for (entity = entities.next ; entity != &entities + ; entity=entity->next, (*ent)++) + ; + } + + // find brush + for (b2=b->owner->brushes.onext + ; b2 != b && b2 != &b->owner->brushes + ; b2=b2->onext, (*brush)++) + ; +} + +BOOL CALLBACK FindBrushDlgProc ( + HWND hwndDlg, // handle to dialog box + UINT uMsg, // message + WPARAM wParam, // first message parameter + LPARAM lParam // second message parameter + ) +{ + char entstr[256]; + char brushstr[256]; + HWND h; + int ent, brush; + + switch (uMsg) + { + case WM_INITDIALOG: + // set entity and brush number + GetSelectionIndex (&ent, &brush); + sprintf (entstr, "%i", ent); + sprintf (brushstr, "%i", brush); + SetWindowText(GetDlgItem(hwndDlg, IDC_FIND_ENTITY), entstr); + SetWindowText(GetDlgItem(hwndDlg, IDC_FIND_BRUSH), brushstr); + + h = GetDlgItem(hwndDlg, IDC_FIND_ENTITY); + SetFocus (h); + return FALSE; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDOK: + GetWindowText(GetDlgItem(hwndDlg, IDC_FIND_ENTITY), entstr, 255); + GetWindowText(GetDlgItem(hwndDlg, IDC_FIND_BRUSH), brushstr, 255); + SelectBrush (atoi(entstr), atoi(brushstr)); + EndDialog(hwndDlg, 1); + return TRUE; + + case IDCANCEL: + EndDialog(hwndDlg, 0); + return TRUE; + } + } + return FALSE; +} + + + +void DoFind(void) +{ + DialogBox(g_qeglobals.d_hInstance, (char *)IDD_FINDBRUSH, g_pParentWnd->GetSafeHwnd(), FindBrushDlgProc); +} + +/* +=================================================== + + ARBITRARY ROTATE + +=================================================== +*/ + + +BOOL CALLBACK RotateDlgProc ( + HWND hwndDlg, // handle to dialog box + UINT uMsg, // message + WPARAM wParam, // first message parameter + LPARAM lParam // second message parameter + ) +{ + char str[256]; + HWND h; + float v; + + switch (uMsg) + { + case WM_INITDIALOG: + h = GetDlgItem(hwndDlg, IDC_FIND_ENTITY); + SetFocus (h); + return FALSE; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + + case IDOK: + GetWindowText(GetDlgItem(hwndDlg, IDC_ROTX), str, 255); + v = atof(str); + if (v) + Select_RotateAxis (0, v); + + GetWindowText(GetDlgItem(hwndDlg, IDC_ROTY), str, 255); + v = atof(str); + if (v) + Select_RotateAxis (1, v); + + GetWindowText(GetDlgItem(hwndDlg, IDC_ROTZ), str, 255); + v = atof(str); + if (v) + Select_RotateAxis (2, v); + + EndDialog(hwndDlg, 1); + return TRUE; + + case IDCANCEL: + EndDialog(hwndDlg, 0); + return TRUE; + } + } + + return FALSE; +} + + + +void DoRotate(void) +{ + DialogBox(g_qeglobals.d_hInstance, (char *)IDD_ROTATE, g_pParentWnd->GetSafeHwnd(), RotateDlgProc); +} + +/* +=================================================== + + ARBITRARY SIDES + +=================================================== +*/ + +bool g_bDoCone = false; +bool g_bDoSphere = false; +BOOL CALLBACK SidesDlgProc ( + HWND hwndDlg, // handle to dialog box + UINT uMsg, // message + WPARAM wParam, // first message parameter + LPARAM lParam // second message parameter + ) +{ + char str[256]; + HWND h; + + switch (uMsg) + { + case WM_INITDIALOG: + h = GetDlgItem(hwndDlg, IDC_SIDES); + SetFocus (h); + return FALSE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + + case IDOK: + GetWindowText(GetDlgItem(hwndDlg, IDC_SIDES), str, 255); + if (g_bDoCone) + Brush_MakeSidedCone(atoi(str)); + else if (g_bDoSphere) + Brush_MakeSidedSphere(atoi(str)); + else + Brush_MakeSided (atoi(str)); + + EndDialog(hwndDlg, 1); + break; + + case IDCANCEL: + EndDialog(hwndDlg, 0); + break; + } + default: + return FALSE; + } +} + + +void DoSides(bool bCone, bool bSphere, bool bTorus) +{ + g_bDoCone = bCone; + g_bDoSphere = bSphere; + //g_bDoTorus = bTorus; + DialogBox(g_qeglobals.d_hInstance, (char *)IDD_SIDES, g_pParentWnd->GetSafeHwnd(), SidesDlgProc); +} + + +//====================================================================== + +/* +=================== +DoAbout +=================== +*/ +BOOL CALLBACK AboutDlgProc( HWND hwndDlg, + UINT uMsg, + WPARAM wParam, + LPARAM lParam ) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + char buffer[1024]; + idStr::snPrintf(buffer, 1024, "DOOM Radiant Build %d\nCopyright ©1999-2004 Id Software, Inc.\n", BUILD_NUMBER); +// SetDlgItemText( hwndDlg, IDC_ABOUT_INFO, buffer); + + idStr::snPrintf( buffer, 1024, "Renderer:\t%s", qglGetString( GL_RENDERER ) ); + SetDlgItemText( hwndDlg, IDC_ABOUT_GLRENDERER, buffer ); + + idStr::snPrintf( buffer, 1024, "Version:\t\t%s", qglGetString( GL_VERSION ) ); + SetDlgItemText( hwndDlg, IDC_ABOUT_GLVERSION, buffer ); + + idStr::snPrintf( buffer, 1024, "Vendor:\t\t%s", qglGetString( GL_VENDOR ) ); + SetDlgItemText( hwndDlg, IDC_ABOUT_GLVENDOR, buffer); + + char extensions[4096]; + idStr::snPrintf( extensions, 4096, "%s", qglGetString( GL_EXTENSIONS ) ); + HWND hWndExtensions = GetDlgItem( hwndDlg, IDC_ABOUT_GLEXTENSIONS ); + + char *start = extensions; + char *end; + do { + end = strchr(start, ' '); + if ( end ) { + *end = 0; + } + SendMessage( hWndExtensions, LB_ADDSTRING, 0, (LPARAM)start ); + start = end + 1; + } while ( end ); + } + + return TRUE; + + case WM_CLOSE: + EndDialog( hwndDlg, 1 ); + return TRUE; + + case WM_COMMAND: + if ( LOWORD( wParam ) == IDOK ) + EndDialog(hwndDlg, 1); + return TRUE; + } + return FALSE; +} + +void DoAbout(void) +{ + DialogBox( g_qeglobals.d_hInstance, ( char * ) IDD_ABOUT, g_pParentWnd->GetSafeHwnd(), AboutDlgProc ); +} + + diff --git a/src/tools/radiant/WIN_QE3.CPP b/src/tools/radiant/WIN_QE3.CPP new file mode 100644 index 0000000..be55472 --- /dev/null +++ b/src/tools/radiant/WIN_QE3.CPP @@ -0,0 +1,508 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "mru.h" + +extern CEdit *g_pEdit; + +int screen_width; +int screen_height; +bool have_quit; + +int update_bits; + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Sys_MarkMapModified(void) { + idStr title; + + if (mapModified != 1) { + mapModified = 1; // mark the map as changed + title = currentmap; + title += " *"; + title.BackSlashesToSlashes(); + Sys_SetTitle(title); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Sys_SetTitle(const char *text) { + g_pParentWnd->SetWindowText(va("%s: %s",EDITOR_WINDOWTEXT, text)); +} + +/* + ======================================================================================================================= + Wait Functions + ======================================================================================================================= + */ +HCURSOR waitcursor; + +void Sys_BeginWait(void) { + waitcursor = SetCursor(LoadCursor(NULL, IDC_WAIT)); +} + +bool Sys_Waiting() { + return (waitcursor != NULL); +} + +void Sys_EndWait(void) { + if (waitcursor) { + SetCursor(waitcursor); + waitcursor = NULL; + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Sys_GetCursorPos(int *x, int *y) { + POINT lpPoint; + + GetCursorPos(&lpPoint); + *x = lpPoint.x; + *y = lpPoint.y; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Sys_SetCursorPos(int x, int y) { + SetCursorPos(x, y); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Sys_Beep(void) { + MessageBeep(MB_ICONASTERISK); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +char *TranslateString(char *buf) { + static char buf2[32768]; + int i, l; + char *out; + + l = strlen(buf); + out = buf2; + for (i = 0; i < l; i++) { + if (buf[i] == '\n') { + *out++ = '\r'; + *out++ = '\n'; + } + else { + *out++ = buf[i]; + } + } + + *out++ = 0; + + return buf2; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +double Sys_DoubleTime(void) { + return clock() / 1000.0; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void PrintPixels(HDC hDC) { + int i; + PIXELFORMATDESCRIPTOR p[64]; + + printf("### flags color layer\n"); + for (i = 1; i < 64; i++) { + if (!DescribePixelFormat(hDC, i, sizeof(p[0]), &p[i])) { + break; + } + + printf("%3i %5i %5i %5i\n", i, p[i].dwFlags, p[i].cColorBits, p[i].bReserved); + } + + printf("%i modes\n", i - 1); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +int WINAPI QEW_SetupPixelFormat(HDC hDC, bool zbuffer) +{ +#if 1 + + int pixelFormat = ChoosePixelFormat(hDC, &win32.pfd); + if (pixelFormat > 0) { + if (SetPixelFormat(hDC, pixelFormat, &win32.pfd) == NULL) { + Error("SetPixelFormat failed."); + } + } + else { + Error("ChoosePixelFormat failed."); + } + + return pixelFormat; +#else + static PIXELFORMATDESCRIPTOR pfd = { + 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 + 24, // 24-bit color depth + 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 + 32, // depth bits + 0, // no stencil buffer + 0, // no auxiliary buffer + PFD_MAIN_PLANE, // main layer + 0, // reserved + 0, + 0, + 0 // layer masks ignored + }; + int pixelformat = 0; + + zbuffer = true; + if (!zbuffer) { + pfd.cDepthBits = 0; + } + + if ((pixelformat = ChoosePixelFormat(hDC, &pfd)) == 0) { + printf("%d", GetLastError()); + Error("ChoosePixelFormat failed"); + } + + if (!SetPixelFormat(hDC, pixelformat, &pfd)) { + Error("SetPixelFormat failed"); + } + + return pixelformat; +#endif +} + +/* + ======================================================================================================================= + Error For abnormal program terminations + ======================================================================================================================= + */ +void Error(char *error, ...) { + va_list argptr; + char text[1024]; + char text2[1024]; + int err; + + err = GetLastError(); + + int i = qglGetError(); + + va_start(argptr, error); + vsprintf(text, error, argptr); + va_end(argptr); + + sprintf + ( + text2, + "%s\nGetLastError() = %i - %i\nAn unrecoverable error has occured. Would you like to edit Preferences before exiting Q3Radiant?", + text, + err, + i + ); + + if (g_pParentWnd->MessageBox(text2, "Error", MB_YESNO) == IDYES) { + g_PrefsDlg.LoadPrefs(); + g_PrefsDlg.DoModal(); + } + + common->FatalError( text ); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Warning(char *error, ...) { + va_list argptr; + char text[1024]; + int err; + + err = GetLastError(); + + int i = qglGetError(); + + va_start(argptr, error); + vsprintf(text, error, argptr); + va_end(argptr); + + common->Printf(text); +} + +/* + ======================================================================================================================= + FILE DIALOGS + ======================================================================================================================= + */ +bool ConfirmModified(void) { + if (!mapModified) { + return true; + } + + if (g_pParentWnd->MessageBox("This will lose changes to the map", "warning", MB_OKCANCEL) == IDCANCEL) { + return false; + } + + return true; +} + +static OPENFILENAME ofn; /* common dialog box structure */ +static char szDirName[MAX_PATH]; /* directory string */ +static char szFile[260]; /* filename string */ +static char szFileTitle[260]; /* file title string */ +static char szFilter[260] = /* filter string */ +"Map file (*.map, *.reg)\0*.map;*.reg\0"; +static char szProjectFilter[260] = /* filter string */ +"Q3Radiant project (*.qe4, *.prj)\0*.qe4\0*.prj\0\0"; +static char chReplace; /* string separator for szFilter */ +static int i, cbString; /* integer count variables */ +static HANDLE hf; /* file handle */ + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void OpenDialog(void) { + /* Obtain the system directory name and store it in szDirName. */ + strcpy(szDirName, ValueForKey(g_qeglobals.d_project_entity, "mapspath")); + if (strlen(szDirName) == 0) { + strcpy(szDirName, ValueForKey(g_qeglobals.d_project_entity, "basepath")); + strcat(szDirName, "\\maps"); + } + + if (g_PrefsDlg.m_strMaps.GetLength() > 0) { + strcat(szDirName, va("\\%s", g_PrefsDlg.m_strMaps)); + } + + /* Place the terminating null character in the szFile. */ + szFile[0] = '\0'; + + /* Set the members of the OPENFILENAME structure. */ + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = g_pParentWnd->GetSafeHwnd(); + ofn.lpstrFilter = szFilter; + ofn.nFilterIndex = 1; + ofn.lpstrFile = szFile; + ofn.nMaxFile = sizeof(szFile); + ofn.lpstrFileTitle = szFileTitle; + ofn.nMaxFileTitle = sizeof(szFileTitle); + ofn.lpstrInitialDir = szDirName; + ofn.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; + + /* Display the Open dialog box. */ + if (!GetOpenFileName(&ofn)) { + return; // canceled + } + + // Add the file in MRU. FIXME + AddNewItem(g_qeglobals.d_lpMruMenu, ofn.lpstrFile); + + // Refresh the File menu. FIXME + PlaceMenuMRUItem(g_qeglobals.d_lpMruMenu, GetSubMenu(GetMenu(g_pParentWnd->GetSafeHwnd()), 0), ID_FILE_EXIT); + + /* Open the file. */ + Map_LoadFile(ofn.lpstrFile); + + g_PrefsDlg.SavePrefs(); + +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void ProjectDialog(void) { + /* Obtain the system directory name and store it in szDirName. */ + strcpy(szDirName, ValueForKey(g_qeglobals.d_project_entity, "basepath")); + + /* Place the terminating null character in the szFile. */ + szFile[0] = '\0'; + + /* Set the members of the OPENFILENAME structure. */ + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = g_pParentWnd->GetSafeHwnd(); + ofn.lpstrFilter = szProjectFilter; + ofn.nFilterIndex = 1; + ofn.lpstrFile = szFile; + ofn.nMaxFile = sizeof(szFile); + ofn.lpstrFileTitle = szFileTitle; + ofn.nMaxFileTitle = sizeof(szFileTitle); + ofn.lpstrInitialDir = szDirName; + ofn.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; + + /* Display the Open dialog box. */ + if (!GetOpenFileName(&ofn)) { + return; // canceled + } + + // Refresh the File menu. + PlaceMenuMRUItem(g_qeglobals.d_lpMruMenu, GetSubMenu(GetMenu(g_pParentWnd->GetSafeHwnd()), 0), ID_FILE_EXIT); + + /* Open the file. */ + if (!QE_LoadProject(ofn.lpstrFile)) { + Error("Couldn't load project file"); + } +} + +extern void AddSlash(CString &strPath); + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void SaveAsDialog(bool bRegion) { + strcpy(szDirName, ValueForKey(g_qeglobals.d_project_entity, "basepath")); + + CString strPath = szDirName; + AddSlash(strPath); + strPath += "maps"; + if (g_PrefsDlg.m_strMaps.GetLength() > 0) { + strPath += va("\\%s", g_PrefsDlg.m_strMaps); + } + + /* Place the terminating null character in the szFile. */ + szFile[0] = '\0'; + + /* Set the members of the OPENFILENAME structure. */ + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = g_pParentWnd->GetSafeHwnd(); + ofn.lpstrFilter = szFilter; + ofn.nFilterIndex = 1; + ofn.lpstrFile = szFile; + ofn.nMaxFile = sizeof(szFile); + ofn.lpstrFileTitle = szFileTitle; + ofn.nMaxFileTitle = sizeof(szFileTitle); + ofn.lpstrInitialDir = strPath; + ofn.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_OVERWRITEPROMPT; + + /* Display the Open dialog box. */ + if (!GetSaveFileName(&ofn)) { + return; // canceled + } + + if (bRegion) { + DefaultExtension(ofn.lpstrFile, ".reg"); + } + else { + DefaultExtension(ofn.lpstrFile, ".map"); + } + + if (!bRegion) { + strcpy(currentmap, ofn.lpstrFile); + AddNewItem(g_qeglobals.d_lpMruMenu, ofn.lpstrFile); + PlaceMenuMRUItem(g_qeglobals.d_lpMruMenu, GetSubMenu(GetMenu(g_pParentWnd->GetSafeHwnd()), 0), ID_FILE_EXIT); + } + + Map_SaveFile(ofn.lpstrFile, bRegion); // ignore region +} + +/* + * Menu modifications £ + * FillBSPMenu + */ +const char *bsp_commands[256]; + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void FillBSPMenu(void) { + HMENU hmenu; + int i; + static int count; + + hmenu = GetSubMenu(GetMenu(g_pParentWnd->GetSafeHwnd()), MENU_BSP); + + for (i = 0; i < count; i++) { + DeleteMenu(hmenu, CMD_BSPCOMMAND + i, MF_BYCOMMAND); + } + + i = 0; + count = g_qeglobals.d_project_entity->epairs.GetNumKeyVals(); + for (int j = 0; j < count; j++) { + if (g_qeglobals.d_project_entity->epairs.GetKeyVal(j)->GetKey()[0] == 'b' && g_qeglobals.d_project_entity->epairs.GetKeyVal(j)->GetKey()[1] == 's' && g_qeglobals.d_project_entity->epairs.GetKeyVal(j)->GetKey()[2] == 'p') { + bsp_commands[i] = g_qeglobals.d_project_entity->epairs.GetKeyVal(j)->GetKey().c_str(); + AppendMenu(hmenu, MF_ENABLED | MF_STRING, CMD_BSPCOMMAND + i, (LPCTSTR) g_qeglobals.d_project_entity->epairs.GetKeyVal(j)->GetKey().c_str()); + i++; + } + } + + count = i; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void AddSlash(CString &strPath) { + if (strPath.GetLength() > 0) { + if (strPath.GetAt(strPath.GetLength() - 1) != '\\') { + strPath += '\\'; + } + } +} diff --git a/src/tools/radiant/WIN_QE3.RC2 b/src/tools/radiant/WIN_QE3.RC2 new file mode 100644 index 0000000..06dab11 --- /dev/null +++ b/src/tools/radiant/WIN_QE3.RC2 @@ -0,0 +1,693 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.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 + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_MENU1 MENU DISCARDABLE +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&New", ID_FILE_NEW + MENUITEM "&Open", ID_FILE_OPEN + MENUITEM "&Save", ID_FILE_SAVE + MENUITEM "Save &as...", ID_FILE_SAVEAS + MENUITEM "&Pointfile", ID_FILE_POINTFILE + MENUITEM "Load &project", ID_FILE_LOADPROJECT + MENUITEM "E&xit", ID_FILE_EXIT + END + POPUP "&Edit" + BEGIN + MENUITEM "&Copy brush", ID_EDIT_COPYBRUSH, GRAYED + MENUITEM "&Paste brush", ID_EDIT_PASTEBRUSH, GRAYED + END + POPUP "&View" + BEGIN + MENUITEM "Texture View\tT", ID_VIEW_TEXTURE + MENUITEM "Console View\tO", ID_VIEW_CONSOLE + MENUITEM "Entity View\tN", ID_VIEW_ENTITY + MENUITEM SEPARATOR + MENUITEM "&Center\tEnd", ID_VIEW_CENTER + MENUITEM "&Up Floor\tPage Up", ID_VIEW_UPFLOOR + MENUITEM "&Down Floor\tPage Down", ID_VIEW_DOWNFLOOR + MENUITEM SEPARATOR + MENUITEM "&XY 100%", ID_VIEW_100 + MENUITEM "XY Zoom &In\tDelete", ID_VIEW_ZOOMIN + MENUITEM "XY Zoom &Out\tInsert", ID_VIEW_ZOOMOUT + MENUITEM SEPARATOR + MENUITEM "Show &Names", ID_VIEW_SHOWNAMES, CHECKED + MENUITEM "Show Blocks", ID_VIEW_SHOWBLOCKS + MENUITEM "Show C&oordinates", ID_VIEW_SHOWCOORDINATES + , CHECKED + MENUITEM "Show &Entities", ID_VIEW_SHOWENT, CHECKED + MENUITEM "Show &Path", ID_VIEW_SHOWPATH, CHECKED + MENUITEM "Show &Lights", ID_VIEW_SHOWLIGHTS, CHECKED + MENUITEM "Show &Water", ID_VIEW_SHOWWATER, CHECKED + MENUITEM "Show Clip &Brush", ID_VIEW_SHOWCLIP, CHECKED + MENUITEM "Show Wor&ld", ID_VIEW_SHOWWORLD, CHECKED + MENUITEM "Show Detail\tctrl-D", ID_VIEW_SHOWDETAIL, CHECKED + MENUITEM SEPARATOR + MENUITEM "&Z 100%", ID_VIEW_Z100 + MENUITEM "Z Zoo&m In\tctrl-Delete", ID_VIEW_ZZOOMIN + MENUITEM "Z Zoom O&ut\tctrl-Insert", ID_VIEW_ZZOOMOUT + END + POPUP "&Selection" + BEGIN + MENUITEM "Drag &Edges\tE", ID_SELECTION_DRAGEDGES + MENUITEM "Drag &Vertecies\tV", ID_SELECTION_DRAGVERTECIES + MENUITEM "&Clone\tspace", ID_SELECTION_CLONE + MENUITEM "Deselect\tEsc", ID_SELECTION_DESELECT + MENUITEM "&Delete\tBackspace", ID_SELECTION_DELETE + MENUITEM "Flip &X", ID_BRUSH_FLIPX + MENUITEM "Flip &Y", ID_BRUSH_FLIPY + MENUITEM "Flip &Z", ID_BRUSH_FLIPZ + MENUITEM "Rotate X", ID_BRUSH_ROTATEX + MENUITEM "Rotate Y", ID_BRUSH_ROTATEY + MENUITEM "Rotate Z", ID_BRUSH_ROTATEZ + MENUITEM "Arbitrary rotation", ID_SELECTION_ARBITRARYROTATION + + MENUITEM "Make &Hollow", ID_SELECTION_MAKEHOLLOW + MENUITEM "CSG &Subtract", ID_SELECTION_CSGSUBTRACT + MENUITEM "Select Complete &Tall", ID_SELECTION_SELECTCOMPLETETALL + + MENUITEM "Select T&ouching", ID_SELECTION_SELECTTOUCHING + MENUITEM "Select &Partial Tall", ID_SELECTION_SELECTPARTIALTALL + + MENUITEM "Select &Inside", ID_SELECTION_SELECTINSIDE + MENUITEM "Connect entities\tCtrl-k", ID_SELECTION_CONNECT + MENUITEM "Ungroup entity", ID_SELECTION_UNGROUPENTITY + MENUITEM "Make detail\tCtrl-m", ID_SELECTION_MAKE_DETAIL + MENUITEM "Make structural", ID_SELECTION_MAKE_STRUCTURAL + END + POPUP "&Bsp" + BEGIN + MENUITEM SEPARATOR + END + POPUP "&Grid" + BEGIN + MENUITEM "Grid1\t&1", ID_GRID_1 + MENUITEM "Grid2\t&2", ID_GRID_2 + MENUITEM "Grid4\t&3", ID_GRID_4 + MENUITEM "Grid8\t&4", ID_GRID_8, CHECKED + MENUITEM "Grid16\t&5", ID_GRID_16 + MENUITEM "Grid32\t&6", ID_GRID_32 + MENUITEM "Grid64\t&7", ID_GRID_64 + END + POPUP "&Textures" + BEGIN + MENUITEM "Show In &Use\tU", ID_TEXTURES_SHOWINUSE + MENUITEM "&Surface inspector\tS", ID_TEXTURES_INSPECTOR + MENUITEM SEPARATOR + MENUITEM "&Wireframe", ID_TEXTURES_WIREFRAME + MENUITEM "&Flat shade", ID_TEXTURES_FLATSHADE + MENUITEM "&Nearest", ID_VIEW_NEAREST + MENUITEM "Nearest &Mipmap", ID_VIEW_NEARESTMIPMAP + MENUITEM "&Linear", ID_VIEW_LINEAR + MENUITEM "&Bilinear", ID_VIEW_BILINEAR + MENUITEM "B&ilinear Mipmap", ID_VIEW_BILINEARMIPMAP + MENUITEM "T&rilinear", ID_VIEW_TRILINEAR + MENUITEM SEPARATOR + END + POPUP "&Misc" + BEGIN + MENUITEM "&Benchmark", ID_MISC_BENCHMARK + POPUP "&Colors" + BEGIN + MENUITEM "&Texture Background", ID_TEXTUREBK + MENUITEM "Grid Background", ID_COLORS_XYBK + MENUITEM "Grid Major", ID_COLORS_MAJOR + MENUITEM "Grid Minor", ID_COLORS_MINOR + END + MENUITEM "&Gamma", ID_MISC_GAMMA + MENUITEM "Find brush", ID_MISC_FINDBRUSH + MENUITEM "Next leak spot\tctrl-l", ID_MISC_NEXTLEAKSPOT + MENUITEM "Previous leak spot\tctrl-p", ID_MISC_PREVIOUSLEAKSPOT + MENUITEM "&Print XY View", ID_MISC_PRINTXY + MENUITEM "&Select Entity Color\tK", ID_MISC_SELECTENTITYCOLOR + END + POPUP "&Region" + BEGIN + MENUITEM "&Off", ID_REGION_OFF + MENUITEM "&Set XY", ID_REGION_SETXY + MENUITEM "Set &Tall Brush", ID_REGION_SETTALLBRUSH + MENUITEM "Set &Brush", ID_REGION_SETBRUSH + MENUITEM "Set Se&lected Brushes", ID_REGION_SETSELECTION + END + POPUP "&Brush" + BEGIN + MENUITEM "3 sided\tctrl-3", ID_BRUSH_3SIDED + MENUITEM "4 sided\tctrl-4", ID_BRUSH_4SIDED + MENUITEM "5 sided\tctrl-5", ID_BRUSH_5SIDED + MENUITEM "6 sided\tctrl-6", ID_BRUSH_6SIDED + MENUITEM "7 sided\tctrl-7", ID_BRUSH_7SIDED + MENUITEM "8 sided\tctrl-8", ID_BRUSH_8SIDED + MENUITEM "9 sided\tctrl-9", ID_BRUSH_9SIDED + MENUITEM "Arbitrary sided", ID_BRUSH_ARBITRARYSIDED + END + POPUP "&Help" + BEGIN + MENUITEM "&About", ID_HELP_ABOUT + END +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_FINDTEXTURE DIALOG DISCARDABLE 0, 0, 129, 53 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Find Texture" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,10,30,50,14 + PUSHBUTTON "Cancel",IDCANCEL,70,30,50,14 + EDITTEXT IDC_EDIT1,10,10,110,14,ES_AUTOHSCROLL +END + +IDD_ENTITY DIALOGEX 0, 0, 234, 389 +STYLE DS_3DLOOK | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CLIPSIBLINGS | + WS_CAPTION | WS_THICKFRAME +EXSTYLE WS_EX_TOOLWINDOW | WS_EX_CLIENTEDGE +CAPTION "Entity" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + LISTBOX IDC_E_LIST,5,5,180,99,LBS_SORT | LBS_NOINTEGRALHEIGHT | + LBS_WANTKEYBOARDINPUT | WS_VSCROLL | WS_TABSTOP, + WS_EX_CLIENTEDGE + EDITTEXT IDC_E_COMMENT,5,106,180,50,ES_MULTILINE | ES_READONLY | + WS_VSCROLL,WS_EX_CLIENTEDGE + PUSHBUTTON "135",IDC_E_135,5,290,15,15 + PUSHBUTTON "180",IDC_E_180,5,305,15,15 + PUSHBUTTON "225",IDC_E_225,5,320,15,15 + PUSHBUTTON "270",IDC_E_270,21,320,15,15 + PUSHBUTTON "90",IDC_E_90,21,290,15,15 + PUSHBUTTON "45",IDC_E_45,35,290,15,15 + PUSHBUTTON "0",IDC_E_0,35,305,15,15 + PUSHBUTTON "315",IDC_E_315,35,320,15,15 + PUSHBUTTON "Up",IDC_E_UP,60,295,15,15 + PUSHBUTTON "Dn",IDC_E_DOWN,60,310,15,15 + CONTROL "",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,5,160,50,8 + CONTROL "",IDC_CHECK2,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,5,170,50,8 + CONTROL "",IDC_CHECK3,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,5,180,50,8 + CONTROL "",IDC_CHECK4,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,5,190,50,8 + CONTROL "",IDC_CHECK5,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,65,160,50,8 + CONTROL "",IDC_CHECK6,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,65,170,50,8 + CONTROL "",IDC_CHECK7,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,65,180,50,8 + CONTROL "",IDC_CHECK8,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,65,190,50,8 + CONTROL "!Easy",IDC_CHECK9,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 125,160,50,8 + CONTROL "!Medium",IDC_CHECK10,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,125,170,50,8 + CONTROL "!Hard",IDC_CHECK11,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,125,180,50,10 + CONTROL "!DeathMatch",IDC_CHECK12,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,125,190,55,10 + LISTBOX IDC_E_PROPS,5,205,180,50,LBS_SORT | LBS_USETABSTOPS | + LBS_NOINTEGRALHEIGHT | LBS_WANTKEYBOARDINPUT | + WS_VSCROLL | WS_TABSTOP,WS_EX_CLIENTEDGE + PUSHBUTTON "Del Key/Pair",IDC_E_DELPROP,105,295,45,15 + EDITTEXT IDC_E_STATUS,83,312,95,30,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL | WS_HSCROLL + LTEXT "Key",IDC_STATIC_KEY,5,260,25,10 + LTEXT "Value",IDC_STATIC_VALUE,5,275,25,10 + EDITTEXT IDC_E_KEY_FIELD,40,260,135,14,ES_AUTOHSCROLL + EDITTEXT IDC_E_VALUE_FIELD,40,275,135,14,ES_AUTOHSCROLL +END + +IDD_GAMMA DIALOGEX 0, 0, 127, 76 +STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU +CAPTION "Gamma" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,10,40,50,14 + PUSHBUTTON "Cancel",IDCANCEL,65,40,50,14 + EDITTEXT IDC_G_EDIT,30,15,66,13,ES_AUTOHSCROLL,WS_EX_CLIENTEDGE +END + +IDD_FINDBRUSH DIALOGEX 0, 0, 127, 76 +STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU +CAPTION "Find brush" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,5,55,50,14 + PUSHBUTTON "Cancel",IDCANCEL,65,55,50,14 + EDITTEXT IDC_FIND_ENTITY,80,15,46,13,ES_AUTOHSCROLL, + WS_EX_CLIENTEDGE + EDITTEXT IDC_FIND_BRUSH,80,30,46,13,ES_AUTOHSCROLL, + WS_EX_CLIENTEDGE + LTEXT "Entity number",IDC_STATIC,10,15,60,8 + LTEXT "Brush number",IDC_STATIC,10,30,65,8 +END + +IDD_ROTATE DIALOG DISCARDABLE 0, 0, 186, 71 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Arbitrary rotation" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,129,7,50,14 + PUSHBUTTON "Cancel",IDCANCEL,129,24,50,14 + EDITTEXT IDC_ROTX,30,5,40,14,ES_AUTOHSCROLL + LTEXT "x",IDC_STATIC,5,10,8,8 + EDITTEXT IDC_ROTZ,30,45,40,14,ES_AUTOHSCROLL + LTEXT "y",IDC_STATIC,5,25,8,8 + EDITTEXT IDC_ROTY,30,25,40,14,ES_AUTOHSCROLL + LTEXT "z",IDC_STATIC,5,45,8,8 +END + +IDD_SIDES DIALOG DISCARDABLE 0, 0, 186, 55 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Arbitrrary sides" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,129,7,50,14 + PUSHBUTTON "Cancel",IDCANCEL,129,24,50,14 + EDITTEXT IDC_SIDES,50,15,40,14,ES_AUTOHSCROLL + LTEXT "Sides",IDC_STATIC,15,15,18,8 +END + +IDD_ABOUT DIALOG DISCARDABLE 0, 0, 274, 212 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "About QuakeEd" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,217,7,50,14 + CONTROL 127,IDC_STATIC,"Static",SS_BITMAP,7,7,83,58 + CONTROL "QuakeEd 4.0(beta)\nCopyright (C) 1997 id Software, Inc.", + IDC_STATIC,"Static",SS_LEFTNOWORDWRAP | WS_GROUP,100,10, + 110,23 + GROUPBOX "OpenGL Properties",IDC_STATIC,5,75,265,50 + LTEXT "Vendor:\t\tWHOEVER",IDC_ABOUT_GLVENDOR,10,90,125,10 + LTEXT "Version:\t\t1.1",IDC_ABOUT_GLVERSION,10,100,125,10 + LTEXT "Renderer:\tWHATEVER",IDC_ABOUT_GLRENDERER,10,110,125,10 + LTEXT "WHATEVER",IDC_ABOUT_GLEXTENSIONS,10,140,255,60 + GROUPBOX "OpenGL Extensions",IDC_STATIC,5,130,265,80 +END + +IDD_SURFACE DIALOG DISCARDABLE 400, 100, 392, 181 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Surface inspector" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,5,155,40,14 + PUSHBUTTON "Cancel",IDCANCEL,105,155,40,14 + EDITTEXT IDC_HSHIFT,85,45,35,15,ES_AUTOHSCROLL + SCROLLBAR IDC_HSHIFTA,120,45,10,15,SBS_VERT + LTEXT "Horizontal shift",IDC_STATIC,10,45,65,8 + LTEXT "Vertical shift",IDC_STATIC,10,60,65,8 + LTEXT "Horizontal stretch",IDC_STATIC,10,75,65,8 + LTEXT "Vertical stretch",IDC_STATIC,10,90,65,8 + LTEXT "Rotate",IDC_STATIC,10,105,65,8 + LTEXT "value",IDC_STATIC,10,120,65,8 + EDITTEXT IDC_VSHIFT,85,60,35,15,ES_AUTOHSCROLL + SCROLLBAR IDC_VSHIFTA,120,60,10,15,SBS_VERT + EDITTEXT IDC_HSCALE,85,75,35,15,ES_AUTOHSCROLL + SCROLLBAR IDC_HSCALEA,120,75,10,15,SBS_VERT + EDITTEXT IDC_VSCALE,85,90,35,15,ES_AUTOHSCROLL + SCROLLBAR IDC_VSCALEA,120,90,10,15,SBS_VERT + EDITTEXT IDC_ROTATE,85,105,35,15,ES_AUTOHSCROLL + SCROLLBAR IDC_ROTATEA,120,105,10,15,SBS_VERT + EDITTEXT IDC_VALUE,85,120,35,15,ES_AUTOHSCROLL + EDITTEXT IDC_TEXTURE,50,15,80,14,ES_AUTOHSCROLL + CONTROL "light",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 160,10,41,8 + CONTROL "slick",IDC_CHECK2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 160,20,41,8 + CONTROL "sky",IDC_CHECK3,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 160,30,41,8 + CONTROL "warp",IDC_CHECK4,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 160,40,41,8 + CONTROL "trans33",IDC_CHECK5,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,160,50,41,8 + CONTROL "trans66",IDC_CHECK6,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,160,60,41,8 + CONTROL "flowing",IDC_CHECK7,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,160,70,41,8 + CONTROL "nodraw",IDC_CHECK8,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,160,80,41,8 + CONTROL "100",IDC_CHECK9,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 160,90,41,8 + CONTROL "200",IDC_CHECK10,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 160,100,41,8 + CONTROL "400",IDC_CHECK11,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 160,110,41,8 + CONTROL "800",IDC_CHECK12,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 160,120,41,8 + CONTROL "1000",IDC_CHECK13,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 160,130,41,8 + CONTROL "2000",IDC_CHECK14,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 160,140,41,8 + CONTROL "4000",IDC_CHECK15,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 160,150,41,8 + CONTROL "8000",IDC_CHECK16,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 160,160,41,8 + LTEXT "Texture",IDC_STATIC,10,18,30,8 + PUSHBUTTON "Apply",IDAPPLY,55,155,40,14 + CONTROL "10000",IDC_CHECK17,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,210,10,41,8 + CONTROL "20000",IDC_CHECK18,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,210,20,41,8 + CONTROL "40000",IDC_CHECK19,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,210,30,41,8 + CONTROL "80000",IDC_CHECK20,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,210,40,41,8 + CONTROL "100000",IDC_CHECK21,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,210,50,41,8 + CONTROL "200000",IDC_CHECK22,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,210,60,41,8 + CONTROL "400000",IDC_CHECK23,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,210,70,41,8 + CONTROL "800000",IDC_CHECK24,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,210,80,41,8 + CONTROL "1000000",IDC_CHECK25,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,210,90,41,8 + CONTROL "2000000",IDC_CHECK26,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,210,100,41,8 + CONTROL "4000000",IDC_CHECK27,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,210,110,41,8 + CONTROL "8000000",IDC_CHECK28,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,210,120,41,8 + CONTROL "10000000",IDC_CHECK29,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,210,130,40,8 + CONTROL "20000000",IDC_CHECK30,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,210,140,45,8 + CONTROL "40000000",IDC_CHECK31,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,210,150,45,8 + CONTROL "80000000",IDC_CHECK32,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,210,160,45,8 + CONTROL "solid",IDC_CHECK33,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,280,10,41,8 + CONTROL "window",IDC_CHECK34,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,280,20,41,8 + CONTROL "aux",IDC_CHECK35,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 280,31,41,8 + CONTROL "lava",IDC_CHECK36,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 280,41,41,8 + CONTROL "slime",IDC_CHECK37,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,280,50,41,8 + CONTROL "water",IDC_CHECK38,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,280,60,41,8 + CONTROL "mist",IDC_CHECK39,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 280,71,41,8 + CONTROL "80",IDC_CHECK40,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 280,81,41,8 + CONTROL "100",IDC_CHECK41,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 280,90,41,8 + CONTROL "200",IDC_CHECK42,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 280,100,41,8 + CONTROL "400",IDC_CHECK43,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 280,111,41,8 + CONTROL "800",IDC_CHECK44,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 280,121,41,8 + CONTROL "1000",IDC_CHECK45,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 280,130,41,8 + CONTROL "2000",IDC_CHECK46,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 280,140,41,8 + CONTROL "4000",IDC_CHECK47,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 280,151,41,8 + CONTROL "8000",IDC_CHECK48,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 280,161,41,8 + CONTROL "playerclip",IDC_CHECK49,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,330,10,41,8 + CONTROL "monsterclip",IDC_CHECK50,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,330,20,50,8 + CONTROL "current_0",IDC_CHECK51,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,330,31,50,8 + CONTROL "current_90",IDC_CHECK52,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,330,41,50,8 + CONTROL "current_180",IDC_CHECK53,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,330,50,50,8 + CONTROL "current_270",IDC_CHECK54,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,330,60,50,8 + CONTROL "current_up",IDC_CHECK55,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,330,71,50,8 + CONTROL "current_dn",IDC_CHECK56,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,330,81,50,8 + CONTROL "origin",IDC_CHECK57,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,330,90,41,8 + CONTROL "monster",IDC_CHECK58,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,330,100,41,8 + CONTROL "corpse",IDC_CHECK59,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,330,111,41,8 + CONTROL "detail",IDC_CHECK60,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,330,121,41,8 + CONTROL "translucent",IDC_CHECK61,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,330,130,50,8 + CONTROL "ladder",IDC_CHECK62,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,330,140,45,8 + CONTROL "40000000",IDC_CHECK63,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,330,151,45,8 + CONTROL "80000000",IDC_CHECK64,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,330,161,45,8 + GROUPBOX "Surf flags",IDC_STATIC,150,0,115,175 + GROUPBOX "Content flags",IDC_STATIC,270,0,115,175 +END + + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Id Software\0" + VALUE "FileDescription", "qe3\0" + VALUE "FileVersion", "1, 0, 0, 1\0" + VALUE "InternalName", "qe3\0" + VALUE "LegalCopyright", "Copyright © 1996\0" + VALUE "OriginalFilename", "qe3.exe\0" + VALUE "ProductName", "Id Software qe3\0" + VALUE "ProductVersion", "1, 0, 0, 1\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // !_MAC + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDR_ACCELERATOR1 ACCELERATORS DISCARDABLE +BEGIN + "3", ID_BRUSH_3SIDED, VIRTKEY, CONTROL, NOINVERT + "4", ID_BRUSH_4SIDED, VIRTKEY, CONTROL, NOINVERT + "5", ID_BRUSH_5SIDED, VIRTKEY, CONTROL, NOINVERT + "6", ID_BRUSH_6SIDED, VIRTKEY, CONTROL, NOINVERT + "7", ID_BRUSH_7SIDED, VIRTKEY, CONTROL, NOINVERT + "8", ID_BRUSH_8SIDED, VIRTKEY, CONTROL, NOINVERT + "9", ID_BRUSH_9SIDED, VIRTKEY, CONTROL, NOINVERT + "D", ID_VIEW_SHOWDETAIL, VIRTKEY, CONTROL, NOINVERT + "K", ID_SELECTION_CONNECT, VIRTKEY, CONTROL, NOINVERT + "L", ID_MISC_NEXTLEAKSPOT, VIRTKEY, CONTROL, NOINVERT + "M", ID_SELECTION_MAKE_DETAIL, VIRTKEY, CONTROL, NOINVERT + "O", ID_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT + "P", ID_MISC_PREVIOUSLEAKSPOT, VIRTKEY, CONTROL, NOINVERT + "S", ID_FILE_SAVE, VIRTKEY, CONTROL, NOINVERT + VK_DELETE, ID_VIEW_ZZOOMIN, VIRTKEY, CONTROL, NOINVERT + VK_INSERT, ID_VIEW_ZZOOMOUT, VIRTKEY, CONTROL, NOINVERT + "X", ID_FILE_EXIT, VIRTKEY, CONTROL, NOINVERT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Toolbar +// + +IDR_TOOLBAR1 TOOLBAR DISCARDABLE 16, 15 +BEGIN + BUTTON ID_BRUSH_FLIPX + BUTTON ID_BRUSH_ROTATEX + BUTTON ID_BRUSH_FLIPY + BUTTON ID_BRUSH_ROTATEY + BUTTON ID_BRUSH_FLIPZ + BUTTON ID_BRUSH_ROTATEZ + BUTTON ID_SELECTION_SELECTCOMPLETETALL + BUTTON ID_SELECTION_SELECTTOUCHING + BUTTON ID_SELECTION_SELECTPARTIALTALL + BUTTON ID_SELECTION_SELECTINSIDE + BUTTON ID_SELECTION_CSGSUBTRACT + BUTTON ID_SELECTION_MAKEHOLLOW + BUTTON ID_TEXTURES_WIREFRAME + BUTTON ID_TEXTURES_FLATSHADE + BUTTON ID_VIEW_TRILINEAR +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDR_TOOLBAR1 BITMAP DISCARDABLE "toolbar1.bmp" +IDB_BITMAP1 BITMAP DISCARDABLE "q.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_ENTITY, DIALOG + BEGIN + RIGHTMARGIN, 227 + BOTTOMMARGIN, 387 + END + + IDD_GAMMA, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 120 + TOPMARGIN, 7 + BOTTOMMARGIN, 68 + END + + IDD_FINDBRUSH, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 120 + TOPMARGIN, 7 + BOTTOMMARGIN, 68 + END + + IDD_ROTATE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 64 + END + + IDD_SIDES, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 48 + END + + IDD_ABOUT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 267 + TOPMARGIN, 7 + BOTTOMMARGIN, 205 + END + + IDD_SURFACE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 385 + TOPMARGIN, 7 + BOTTOMMARGIN, 174 + END +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 "icon1.ico" +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/src/tools/radiant/WaitDlg.cpp b/src/tools/radiant/WaitDlg.cpp new file mode 100644 index 0000000..414a914 --- /dev/null +++ b/src/tools/radiant/WaitDlg.cpp @@ -0,0 +1,138 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "WaitDlg.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CWaitDlg dialog + + +CWaitDlg::CWaitDlg(CWnd* pParent, const char *msg) + : CDialog(CWaitDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(CWaitDlg) + waitStr = msg; + //}}AFX_DATA_INIT + cancelPressed = false; + Create(CWaitDlg::IDD); + //g_pParentWnd->SetBusy(true); +} + +CWaitDlg::~CWaitDlg() { + g_pParentWnd->SetBusy(false); +} + +void CWaitDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CWaitDlg) + DDX_Text(pDX, IDC_WAITSTR, waitStr); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CWaitDlg, CDialog) + //{{AFX_MSG_MAP(CWaitDlg) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CWaitDlg message handlers + +BOOL CWaitDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + //GetDlgItem(IDC_WAITSTR)->SetWindowText(waitStr); + GetDlgItem(IDC_WAITSTR)->SetFocus(); + UpdateData(FALSE); + ShowWindow(SW_SHOW); + + // cancel disabled by default + AllowCancel( false ); + + // TODO: Add extra initialization here + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void CWaitDlg::SetText(const char *msg, bool append) { + if (append) { + waitStr = text; + waitStr += "\r\n"; + waitStr += msg; + } else { + waitStr = msg; + text = msg; + } + UpdateData(FALSE); + Invalidate(); + UpdateWindow(); + ShowWindow (SW_SHOWNORMAL); +} + +void CWaitDlg::AllowCancel( bool enable ) { + // this shows or hides the Cancel button + CWnd* pCancelButton = GetDlgItem (IDCANCEL); + ASSERT (pCancelButton); + if ( enable ) { + pCancelButton->ShowWindow (SW_NORMAL); + } else { + pCancelButton->ShowWindow (SW_HIDE); + } +} + +bool CWaitDlg::CancelPressed( void ) { +#if _MSC_VER >= 1300 + MSG *msg = AfxGetCurrentMessage(); // TODO Robert fix me!! +#else + MSG *msg = &m_msgCur; +#endif + + while( ::PeekMessage(msg, NULL, NULL, NULL, PM_NOREMOVE) ) { + // pump message + if ( !AfxGetApp()->PumpMessage() ) { + } + } + + return cancelPressed; +} + +void CWaitDlg::OnCancel() { + cancelPressed = true; +} diff --git a/src/tools/radiant/WaitDlg.h b/src/tools/radiant/WaitDlg.h new file mode 100644 index 0000000..342723d --- /dev/null +++ b/src/tools/radiant/WaitDlg.h @@ -0,0 +1,82 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#if !defined(AFX_WAITDLG_H__2B7A6C91_8D3F_4BEE_B564_33A0CFFA241B__INCLUDED_) +#define AFX_WAITDLG_H__2B7A6C91_8D3F_4BEE_B564_33A0CFFA241B__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// WaitDlg.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CWaitDlg dialog + +class CWaitDlg : public CDialog +{ +// Construction +public: + CWaitDlg(CWnd* pParent = NULL, const char *msg = "Wait..."); // standard constructor + ~CWaitDlg(); + void SetText(const char *msg, bool append = false); + void AllowCancel( bool enable ); + bool CancelPressed( void ); + +// Dialog Data + //{{AFX_DATA(CWaitDlg) + enum { IDD = IDD_DLG_WAIT }; + CString waitStr; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CWaitDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CWaitDlg) + virtual BOOL OnInitDialog(); + virtual void OnCancel(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + +private: + idStr text; + bool cancelPressed; +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_WAITDLG_H__2B7A6C91_8D3F_4BEE_B564_33A0CFFA241B__INCLUDED_) diff --git a/src/tools/radiant/WaveOpen.cpp b/src/tools/radiant/WaveOpen.cpp new file mode 100644 index 0000000..5971652 --- /dev/null +++ b/src/tools/radiant/WaveOpen.cpp @@ -0,0 +1,105 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" +#include "WaveOpen.h" +#include "mmsystem.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CWaveOpen + +IMPLEMENT_DYNAMIC(CWaveOpen, CFileDialog) + +CWaveOpen::CWaveOpen(BOOL bOpenFileDialog, LPCTSTR lpszDefExt, LPCTSTR lpszFileName, + DWORD dwFlags, LPCTSTR lpszFilter, CWnd* pParentWnd) : + CFileDialog(bOpenFileDialog, lpszDefExt, lpszFileName, dwFlags, lpszFilter, pParentWnd) +{ + m_ofn.Flags |= (OFN_EXPLORER | OFN_ENABLETEMPLATE); + m_ofn.lpTemplateName = MAKEINTRESOURCE(IDD_PLAYWAVE); +} + + +BEGIN_MESSAGE_MAP(CWaveOpen, CFileDialog) + //{{AFX_MSG_MAP(CWaveOpen) + ON_BN_CLICKED(IDC_BTN_PLAY, OnBtnPlay) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + +void CWaveOpen::OnFileNameChange() +{ + CString str = GetPathName(); + str.MakeLower(); + CWnd *pWnd = GetDlgItem(IDC_BTN_PLAY); + if (pWnd == NULL) + { + return; + } + if (str.Find(".wav") >= 0) + { + pWnd->EnableWindow(TRUE); + } + else + { + pWnd->EnableWindow(FALSE); + } +} + +void CWaveOpen::OnBtnPlay() +{ + sndPlaySound(NULL, NULL); + CString str = GetPathName(); + if (str.GetLength() > 0) + { + sndPlaySound(str, SND_FILENAME | SND_ASYNC); + } +} + +BOOL CWaveOpen::OnInitDialog() +{ + CFileDialog::OnInitDialog(); + + CWnd *pWnd = GetDlgItem(IDC_BTN_PLAY); + if (pWnd != NULL) + { + pWnd->EnableWindow(FALSE); + } + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} diff --git a/src/tools/radiant/WaveOpen.h b/src/tools/radiant/WaveOpen.h new file mode 100644 index 0000000..59ebda2 --- /dev/null +++ b/src/tools/radiant/WaveOpen.h @@ -0,0 +1,64 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#if !defined(AFX_WAVEOPEN_H__0FB9DA11_EB02_11D2_A50A_0020AFEB881A__INCLUDED_) +#define AFX_WAVEOPEN_H__0FB9DA11_EB02_11D2_A50A_0020AFEB881A__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// WaveOpen.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CWaveOpen dialog + +class CWaveOpen : public CFileDialog +{ + DECLARE_DYNAMIC(CWaveOpen) + +public: + CWaveOpen(BOOL bOpenFileDialog, // TRUE for FileOpen, FALSE for FileSaveAs + LPCTSTR lpszDefExt = NULL, + LPCTSTR lpszFileName = NULL, + DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, + LPCTSTR lpszFilter = NULL, + CWnd* pParentWnd = NULL); + + virtual void OnFileNameChange( ); +protected: + //{{AFX_MSG(CWaveOpen) + afx_msg void OnBtnPlay(); + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_WAVEOPEN_H__0FB9DA11_EB02_11D2_A50A_0020AFEB881A__INCLUDED_) diff --git a/src/tools/radiant/XYWnd.cpp b/src/tools/radiant/XYWnd.cpp new file mode 100644 index 0000000..f1e6468 --- /dev/null +++ b/src/tools/radiant/XYWnd.cpp @@ -0,0 +1,4569 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" +#include "XYWnd.h" +#include "DialogInfo.h" +#include "splines.h" +#include "../../renderer/tr_local.h" +#include "../../renderer/model_local.h" // for idRenderModelLiquid + +#ifdef _DEBUG + #define new DEBUG_NEW + #undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +const char *g_pDimStrings[] = { "x:%.f", "y:%.f", "z:%.f" }; +const char *g_pOrgStrings[] = { "(x:%.f y:%.f)", "(x:%.f z:%.f)", "(y:%.f z:%.f)" }; +CString g_strDim; +CString g_strStatus; + +bool g_bCrossHairs = false; +bool g_bScaleMode; +int g_nScaleHow; +bool g_bRotateMode; +bool g_bClipMode; +bool g_bRogueClipMode; +bool g_bSwitch; +CClipPoint g_Clip1; +CClipPoint g_Clip2; +CClipPoint g_Clip3; +CClipPoint *g_pMovingClip; +brush_t g_brFrontSplits; +brush_t g_brBackSplits; + +brush_t g_brClipboard; +brush_t g_brUndo; +entity_t g_enClipboard; + +idVec3 g_vRotateOrigin; +idVec3 g_vRotation; + +bool g_bPathMode; +CClipPoint g_PathPoints[256]; +CClipPoint *g_pMovingPath; +int g_nPathCount; +int g_nPathLimit; + +bool g_bSmartGo; + +bool g_bPointMode; +CClipPoint g_PointPoints[512]; +CClipPoint *g_pMovingPoint; +int g_nPointCount; +int g_nPointLimit; + +const int XY_LEFT = 0x01; +const int XY_RIGHT = 0x02; +const int XY_UP = 0x04; +const int XY_DOWN = 0x08; + +PFNPathCallback *g_pPathFunc = NULL; +void Select_Ungroup(); + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void AcquirePath(int nCount, PFNPathCallback *pFunc) { + g_nPathCount = 0; + g_nPathLimit = nCount; + g_pPathFunc = pFunc; + g_bPathMode = true; +} + +CPtrArray g_ptrMenus; + +CMemFile g_Clipboard(4096); +CMemFile g_PatchClipboard(4096); + +extern int pressx; +extern int pressy; + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +float fDiff(float f1, float f2) { + if (f1 > f2) { + return f1 - f2; + } + else { + return f2 - f1; + } +} + +#define MAX_DRAG_POINTS 128 + +CPtrArray dragPoints; +static CDragPoint *activeDrag = NULL; +static bool activeDragging = false; + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +bool CDragPoint::PointWithin(idVec3 p, int nView) { + if (nView == -1) { + if (fDiff(p[0], vec[0]) <= 3 && fDiff(p[1], vec[1]) <= 3 && fDiff(p[2], vec[2]) <= 3) { + return true; + } + } + else { + int nDim1 = (nView == YZ) ? 1 : 0; + int nDim2 = (nView == XY) ? 1 : 2; + if (fDiff(p[nDim1], vec[nDim1]) <= 3 && fDiff(p[nDim2], vec[nDim2]) <= 3) { + return true; + } + } + + return false; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +CDragPoint *PointRay(const idVec3 &org, const idVec3 &dir, float *dist) { + int i, besti; + float d, bestd; + idVec3 temp; + CDragPoint *drag = NULL; + CDragPoint *priority = NULL; + + // find the point closest to the ray + float scale = g_pParentWnd->ActiveXY()->Scale(); + besti = -1; + bestd = 12 / scale / 2; + + int count = dragPoints.GetSize(); + for (i = 0; i < count; i++) { + drag = reinterpret_cast < CDragPoint * > (dragPoints[i]); + temp = drag->vec - org; + d = temp * dir; + temp = org + d * dir; + temp = drag->vec - temp; + d = temp.Length(); + if ( d < bestd ) { + bestd = d; + besti = i; + if (priority == NULL) { + priority = reinterpret_cast < CDragPoint * > (dragPoints[besti]); + if (!priority->priority) { + priority = NULL; + } + } + } + } + + if (besti == -1) { + return NULL; + } + + drag = reinterpret_cast < CDragPoint * > (dragPoints[besti]); + if (priority && !drag->priority) { + drag = priority; + } + + return drag; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void ClearSelectablePoints(brush_t *b) { + if (b == NULL) { + dragPoints.RemoveAll(); + } + else { + CPtrArray ptr; + ptr.Copy(dragPoints); + dragPoints.RemoveAll(); + + int count = ptr.GetSize(); + for (int i = 0; i < count; i++) { + if (b == reinterpret_cast < CDragPoint * > ( ptr.GetAt(i))->pBrush ) { + continue; + } + else { + dragPoints.Add(ptr.GetAt(i)); + } + } + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void AddSelectablePoint(brush_t *b, idVec3 v, int type, bool priority) { + dragPoints.Add(new CDragPoint(b, v, type, priority)); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void UpdateSelectablePoint(brush_t *b, idVec3 v, int type) { + int count = dragPoints.GetSize(); + for (int i = 0; i < count; i++) { + CDragPoint *drag = reinterpret_cast < CDragPoint * > (dragPoints.GetAt(i)); + if (b == drag->pBrush && type == drag->nType) { + VectorCopy(v, drag->vec); + return; + } + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void VectorToAngles(idVec3 vec, idVec3 angles) { + float forward; + float yaw, pitch; + + if ((vec[0] == 0) && (vec[1] == 0)) { + yaw = 0; + if (vec[2] > 0) { + pitch = 90; + } + else { + pitch = 270; + } + } + else { + yaw = RAD2DEG( atan2(vec[1], vec[0]) ); + if (yaw < 0) { + yaw += 360; + } + + forward = (float)idMath::Sqrt(vec[0] * vec[0] + vec[1] * vec[1]); + pitch = RAD2DEG( atan2(vec[2], forward) ); + if (pitch < 0) { + pitch += 360; + } + } + + angles[0] = pitch; + angles[1] = yaw; + angles[2] = 0; +} + +/* + ======================================================================================================================= + RotateLight target is relative to the light origin up and right are relative to the target up and right are + perpendicular and are on a plane through the target with the target vector as normal delta is the movement of the + target relative to the light + ======================================================================================================================= +*/ +void VectorSnapGrid(idVec3 &v) { + v.x = floor(v.x / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize; + v.y = floor(v.y / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize; + v.z = floor(v.z / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize; +} + +/* + ======================================================================================================================= + ======================================================================================================================= +*/ +static void RotateLight(idVec3 &target, idVec3 &up, idVec3 &right, const idVec3 &delta) { + idVec3 newtarget, cross, dst; + idVec3 normal; + double angle, dist, d, len; + idMat3 rot; + + // calculate new target + newtarget = target + delta; + + // get the up and right vector relative to the light origin + up += target; + right += target; + + len = target.Length() * newtarget.Length(); + + if (len > 0.1) { + // calculate the rotation angle between the vectors + double dp = target * newtarget; + double dv = dp / len; + + angle = RAD2DEG( idMath::ACos( dv ) ); + + // get a vector orthogonal to the rotation plane + cross = target.Cross( newtarget ); + cross.Normalize(); + + if (cross[0] || cross[1] || cross[2]) { + // build the rotation matrix + rot = idRotation( vec3_origin, cross, angle ).ToMat3(); + + rot.ProjectVector(target, dst); + target = dst; + rot.ProjectVector( up, dst ); + up = dst; + rot.ProjectVector( right, dst); + right = dst; + } + } + + // + // project the up and right vectors onto a plane that goes through the target and + // has normal vector target.Normalize() + // + normal = target; + normal.Normalize(); + dist = normal * target; + + d = (normal * up) - dist; + up -= d * normal; + + d = (normal * right) - dist; + right -= d * normal; + + // + // FIXME: maybe calculate the right vector with a cross product between the target + // and up vector, just to make sure the up and right vectors are perpendicular + // get the up and right vectors relative to the target + // + up -= target; + right -= target; + + // move the target in the (target - light_origin) direction + target = newtarget; + VectorSnapGrid(target); + VectorSnapGrid(up); + VectorSnapGrid(right); +} + +/* + ======================================================================================================================= + ======================================================================================================================= +*/ +extern idVec3 Brush_TransformedPoint(brush_t *b, const idVec3 &in); +extern idMat3 Brush_RotationMatrix(brush_t *b); +bool UpdateActiveDragPoint(const idVec3 &move) { + if (activeDrag) { + idMat3 mat = Brush_RotationMatrix(activeDrag->pBrush); + idMat3 invmat = mat.Transpose(); + idVec3 target, up, right, start, end; + CString str; + if (activeDrag->nType == LIGHT_TARGET) { + GetVectorForKey(activeDrag->pBrush->owner, "light_target", target); + GetVectorForKey(activeDrag->pBrush->owner, "light_up", up); + GetVectorForKey(activeDrag->pBrush->owner, "light_right", right); + target *= mat; + up *= mat; + right *= mat; + RotateLight(target, up, right, move); + target *= invmat; + up *= invmat; + right *= invmat; + SetKeyVec3(activeDrag->pBrush->owner, "light_target", target); + SetKeyVec3(activeDrag->pBrush->owner, "light_up", up); + SetKeyVec3(activeDrag->pBrush->owner, "light_right", right); + target += (activeDrag->pBrush->trackLightOrigin) ? activeDrag->pBrush->owner->lightOrigin : activeDrag->pBrush->owner->origin; + UpdateSelectablePoint(activeDrag->pBrush, Brush_TransformedPoint(activeDrag->pBrush, target), LIGHT_TARGET); + up += target; + UpdateSelectablePoint(activeDrag->pBrush, Brush_TransformedPoint(activeDrag->pBrush,up), LIGHT_UP); + right += target; + UpdateSelectablePoint(activeDrag->pBrush, Brush_TransformedPoint(activeDrag->pBrush,right), LIGHT_RIGHT); + } + else if (activeDrag->nType == LIGHT_UP) { + GetVectorForKey(activeDrag->pBrush->owner, "light_up", up); + up *= mat; + up += move; + up *= invmat; + SetKeyVec3(activeDrag->pBrush->owner, "light_up", up); + GetVectorForKey(activeDrag->pBrush->owner, "light_target", target); + target += (activeDrag->pBrush->trackLightOrigin) ? activeDrag->pBrush->owner->lightOrigin : activeDrag->pBrush->owner->origin; + up += target; + UpdateSelectablePoint(activeDrag->pBrush, Brush_TransformedPoint(activeDrag->pBrush,up), LIGHT_UP); + } + else if (activeDrag->nType == LIGHT_RIGHT) { + GetVectorForKey(activeDrag->pBrush->owner, "light_right", right); + right *= mat; + right += move; + right *= invmat; + SetKeyVec3(activeDrag->pBrush->owner, "light_right", right); + GetVectorForKey(activeDrag->pBrush->owner, "light_target", target); + target += (activeDrag->pBrush->trackLightOrigin) ? activeDrag->pBrush->owner->lightOrigin : activeDrag->pBrush->owner->origin; + right += target; + UpdateSelectablePoint(activeDrag->pBrush, Brush_TransformedPoint(activeDrag->pBrush,right), LIGHT_RIGHT); + } + else if (activeDrag->nType == LIGHT_START) { + GetVectorForKey(activeDrag->pBrush->owner, "light_start", start); + start *= mat; + start += move; + start *= invmat; + SetKeyVec3(activeDrag->pBrush->owner, "light_start", start); + start += (activeDrag->pBrush->trackLightOrigin) ? activeDrag->pBrush->owner->lightOrigin : activeDrag->pBrush->owner->origin; + UpdateSelectablePoint(activeDrag->pBrush, Brush_TransformedPoint(activeDrag->pBrush,start), LIGHT_START); + } + else if (activeDrag->nType == LIGHT_END) { + GetVectorForKey(activeDrag->pBrush->owner, "light_end", end); + end *= mat; + end += move; + end *= invmat; + SetKeyVec3(activeDrag->pBrush->owner, "light_end", end); + end += (activeDrag->pBrush->trackLightOrigin) ? activeDrag->pBrush->owner->lightOrigin : activeDrag->pBrush->owner->origin; + UpdateSelectablePoint(activeDrag->pBrush, Brush_TransformedPoint(activeDrag->pBrush,end), LIGHT_END); + } + else if (activeDrag->nType == LIGHT_CENTER) { + GetVectorForKey(activeDrag->pBrush->owner, "light_center", end); + end *= mat; + end += move; + end *= invmat; + SetKeyVec3(activeDrag->pBrush->owner, "light_center", end); + end += (activeDrag->pBrush->trackLightOrigin) ? activeDrag->pBrush->owner->lightOrigin : activeDrag->pBrush->owner->origin; + UpdateSelectablePoint(activeDrag->pBrush, Brush_TransformedPoint(activeDrag->pBrush, end), LIGHT_CENTER); + } + + // FIXME: just build the frustrum values + Brush_Build(activeDrag->pBrush); + return true; + } + + return false; +} + +/* + ======================================================================================================================= + ======================================================================================================================= +*/ +bool SetDragPointCursor(idVec3 p, int nView) { + activeDrag = NULL; + + int numDragPoints = dragPoints.GetSize(); + for (int i = 0; i < numDragPoints; i++) { + if (reinterpret_cast < CDragPoint * > (dragPoints[i])->PointWithin(p, nView)) { + activeDrag = reinterpret_cast < CDragPoint * > (dragPoints[i]); + return true; + } + } + + return false; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void SetActiveDrag(CDragPoint *p) { + activeDrag = p; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void ClearActiveDrag() { + activeDrag = NULL; +} + +// CXYWnd +IMPLEMENT_DYNCREATE(CXYWnd, CWnd); + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +CXYWnd::CXYWnd() { + g_brClipboard.next = &g_brClipboard; + g_brUndo.next = &g_brUndo; + g_nScaleHow = 0; + g_bRotateMode = false; + g_bClipMode = false; + g_bRogueClipMode = false; + g_bSwitch = true; + g_pMovingClip = NULL; + g_pMovingPath = NULL; + g_brFrontSplits.next = &g_brFrontSplits; + g_brBackSplits.next = &g_brBackSplits; + m_bActive = false; + + m_bRButtonDown = false; + m_nUpdateBits = W_XY; + g_bPathMode = false; + g_nPathCount = 0; + g_nPathLimit = 0; + m_nTimerID = -1; + m_nButtonstate = 0; + XY_Init(); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +CXYWnd::~CXYWnd() { + int nSize = g_ptrMenus.GetSize(); + while (nSize > 0) { + CMenu *pMenu = reinterpret_cast < CMenu * > (g_ptrMenus.GetAt(nSize - 1)); + ASSERT(pMenu); + pMenu->DestroyMenu(); + delete pMenu; + nSize--; + } + + g_ptrMenus.RemoveAll(); + m_mnuDrop.DestroyMenu(); +} + +BEGIN_MESSAGE_MAP(CXYWnd, CWnd) +//{{AFX_MSG_MAP(CXYWnd) + ON_WM_CREATE() + ON_WM_LBUTTONDOWN() + ON_WM_MBUTTONDOWN() + ON_WM_RBUTTONDOWN() + ON_WM_LBUTTONUP() + ON_WM_MBUTTONUP() + ON_WM_RBUTTONUP() + ON_WM_MOUSEMOVE() + ON_WM_PAINT() + ON_WM_KEYDOWN() + ON_WM_SIZE() + ON_WM_DESTROY() + ON_COMMAND(ID_SELECT_MOUSEROTATE, OnSelectMouserotate) + ON_WM_TIMER() + ON_WM_KEYUP() + ON_WM_NCCALCSIZE() + ON_WM_KILLFOCUS() + ON_WM_SETFOCUS() + ON_WM_CLOSE() + ON_WM_ERASEBKGND() + ON_WM_MOUSEWHEEL() + ON_COMMAND(ID_DROP_NEWMODEL, OnDropNewmodel) + //}}AFX_MSG_MAP + ON_COMMAND_RANGE(ID_ENTITY_START, ID_ENTITY_END, OnEntityCreate) +END_MESSAGE_MAP() +// CXYWnd message handlers +LONG WINAPI XYWndProc(HWND, UINT, WPARAM, LPARAM); + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +BOOL CXYWnd::PreCreateWindow(CREATESTRUCT &cs) { + WNDCLASS wc; + HINSTANCE hInstance = AfxGetInstanceHandle(); + if (::GetClassInfo(hInstance, XY_WINDOW_CLASS, &wc) == FALSE) { + // Register a new class + memset(&wc, 0, sizeof(wc)); + wc.style = CS_NOCLOSE; + wc.lpszClassName = XY_WINDOW_CLASS; + wc.hCursor = NULL; // LoadCursor (NULL,IDC_ARROW); + wc.lpfnWndProc = ::DefWindowProc; + if (AfxRegisterClass(&wc) == FALSE) { + Error("CCamWnd RegisterClass: failed"); + } + } + + cs.lpszClass = XY_WINDOW_CLASS; + cs.lpszName = "VIEW"; + if (cs.style != QE3_CHILDSTYLE) { + cs.style = QE3_SPLITTER_STYLE; + } + + return CWnd::PreCreateWindow(cs); +} + +HDC s_hdcXY; +HGLRC s_hglrcXY; + +static unsigned s_stipple[32] = { + 0xaaaaaaaa, + 0x55555555, + 0xaaaaaaaa, + 0x55555555, + 0xaaaaaaaa, + 0x55555555, + 0xaaaaaaaa, + 0x55555555, + 0xaaaaaaaa, + 0x55555555, + 0xaaaaaaaa, + 0x55555555, + 0xaaaaaaaa, + 0x55555555, + 0xaaaaaaaa, + 0x55555555, + 0xaaaaaaaa, + 0x55555555, + 0xaaaaaaaa, + 0x55555555, + 0xaaaaaaaa, + 0x55555555, + 0xaaaaaaaa, + 0x55555555, + 0xaaaaaaaa, + 0x55555555, + 0xaaaaaaaa, + 0x55555555, + 0xaaaaaaaa, + 0x55555555, + 0xaaaaaaaa, + 0x55555555, +}; + +/* + ======================================================================================================================= + WXY_WndProc + ======================================================================================================================= + */ +LONG WINAPI XYWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + switch (uMsg) + { + case WM_DESTROY: + return 0; + + case WM_NCCALCSIZE: // don't let windows copy pixels + DefWindowProc(hWnd, uMsg, wParam, lParam); + return WVR_REDRAW; + + case WM_KILLFOCUS: + case WM_SETFOCUS: + SendMessage(hWnd, WM_NCACTIVATE, uMsg == WM_SETFOCUS, 0); + return 0; + + case WM_CLOSE: + DestroyWindow(hWnd); + return 0; + } + + return DefWindowProc(hWnd, uMsg, wParam, lParam); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +static void WXY_InitPixelFormat(PIXELFORMATDESCRIPTOR *pPFD) { + memset(pPFD, 0, sizeof(*pPFD)); + + pPFD->nSize = sizeof(PIXELFORMATDESCRIPTOR); + pPFD->nVersion = 1; + pPFD->dwFlags = PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW; + pPFD->iPixelType = PFD_TYPE_RGBA; + pPFD->cColorBits = 24; + pPFD->cDepthBits = 32; + pPFD->iLayerType = PFD_MAIN_PLANE; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void WXY_Print(void) { + DOCINFO di; + + PRINTDLG pd; + + /* initialize the PRINTDLG struct and execute it */ + memset(&pd, 0, sizeof(pd)); + pd.lStructSize = sizeof(pd); + pd.hwndOwner = g_pParentWnd->GetXYWnd()->GetSafeHwnd(); + pd.Flags = PD_RETURNDC; + pd.hInstance = 0; + if (!PrintDlg(&pd) || !pd.hDC) { + g_pParentWnd->MessageBox("Could not PrintDlg()", "QE4 Print Error", MB_OK | MB_ICONERROR); + return; + } + + /* StartDoc */ + memset(&di, 0, sizeof(di)); + di.cbSize = sizeof(di); + di.lpszDocName = "QE4"; + if (StartDoc(pd.hDC, &di) <= 0) { + g_pParentWnd->MessageBox("Could not StartDoc()", "QE4 Print Error", MB_OK | MB_ICONERROR); + return; + } + + /* StartPage */ + if (StartPage(pd.hDC) <= 0) { + g_pParentWnd->MessageBox("Could not StartPage()", "QE4 Print Error", MB_OK | MB_ICONERROR); + return; + } { /* read pixels from the XY window */ + int bmwidth = 320, bmheight = 320; + int pwidth, pheight; + + RECT r; + + GetWindowRect(g_pParentWnd->GetXYWnd()->GetSafeHwnd(), &r); + + bmwidth = r.right - r.left; + bmheight = r.bottom - r.top; + + pwidth = GetDeviceCaps(pd.hDC, PHYSICALWIDTH) - GetDeviceCaps(pd.hDC, PHYSICALOFFSETX); + pheight = GetDeviceCaps(pd.hDC, PHYSICALHEIGHT) - GetDeviceCaps(pd.hDC, PHYSICALOFFSETY); + + StretchBlt(pd.hDC, 0, 0, pwidth, pheight, s_hdcXY, 0, 0, bmwidth, bmheight, SRCCOPY); + } + + /* EndPage and EndDoc */ + if (EndPage(pd.hDC) <= 0) { + g_pParentWnd->MessageBox("QE4 Print Error", "Could not EndPage()", MB_OK | MB_ICONERROR); + return; + } + + if (EndDoc(pd.hDC) <= 0) { + g_pParentWnd->MessageBox("QE4 Print Error", "Could not EndDoc()", MB_OK | MB_ICONERROR); + return; + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +int CXYWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) { + if (CWnd::OnCreate(lpCreateStruct) == -1) { + return -1; + } + + s_hdcXY = ::GetDC(GetSafeHwnd()); + QEW_SetupPixelFormat(s_hdcXY, false); + + qglPolygonStipple((unsigned char *)s_stipple); + qglLineStipple(3, 0xaaaa); + return 0; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +float ptSum(idVec3 pt) { + return pt[0] + pt[1] + pt[2]; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::DropClipPoint(UINT nFlags, CPoint point) { + CRect rctZ; + GetClientRect(rctZ); + if (g_pMovingClip) { + SetCapture(); + SnapToPoint(point.x, rctZ.Height() - 1 - point.y, *g_pMovingClip); + } + else { + idVec3 *pPt = NULL; + if (g_Clip1.Set() == false) { + pPt = g_Clip1; + g_Clip1.Set(true); + g_Clip1.m_ptScreen = point; + } + else if (g_Clip2.Set() == false) { + pPt = g_Clip2; + g_Clip2.Set(true); + g_Clip2.m_ptScreen = point; + } + else if (g_Clip3.Set() == false) { + pPt = g_Clip3; + g_Clip3.Set(true); + g_Clip3.m_ptScreen = point; + } + else { + RetainClipMode(true); + pPt = g_Clip1; + g_Clip1.Set(true); + g_Clip1.m_ptScreen = point; + } + + SnapToPoint(point.x, rctZ.Height() - 1 - point.y, *pPt); + + // Put the off-viewaxis coordinate at the top or bottom of selected brushes + if ( GetAsyncKeyState(VK_CONTROL) & 0x8000 ) { + if ( selected_brushes.next != &selected_brushes ) { + idVec3 smins, smaxs; + Select_GetBounds( smins, smaxs ); + + if ( m_nViewType == XY ) { + if ( GetAsyncKeyState(VK_SHIFT) & 0x8000 ) { + pPt->z = smaxs.z; + } else { + pPt->z = smins.z; + } + } else if ( m_nViewType == YZ ) { + if ( GetAsyncKeyState(VK_SHIFT) & 0x8000 ) { + pPt->x = smaxs.x; + } else { + pPt->x = smins.x; + } + } else { + if ( GetAsyncKeyState(VK_SHIFT) & 0x8000 ) { + pPt->y = smaxs.y; + } else { + pPt->y = smins.y; + } + } + } + } + } + + Sys_UpdateWindows(XY | W_CAMERA_IFON); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::DropPathPoint(UINT nFlags, CPoint point) { + CRect rctZ; + GetClientRect(rctZ); + if (g_pMovingPath) { + SetCapture(); + SnapToPoint(point.x, rctZ.Height() - 1 - point.y, *g_pMovingPath); + } + else { + g_PathPoints[g_nPathCount].Set(true); + g_PathPoints[g_nPathCount].m_ptScreen = point; + SnapToPoint(point.x, rctZ.Height() - 1 - point.y, g_PathPoints[g_nPathCount]); + g_nPathCount++; + if (g_nPathCount == g_nPathLimit) { + if (g_pPathFunc) { + g_pPathFunc(true, g_nPathCount); + } + + g_nPathCount = 0; + g_bPathMode = false; + g_pPathFunc = NULL; + } + } + + Sys_UpdateWindows(XY | W_CAMERA_IFON); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::AddPointPoint(UINT nFlags, idVec3 *pVec) { + g_PointPoints[g_nPointCount].Set(true); + + // g_PointPoints[g_nPointCount].m_ptScreen = point; + g_PointPoints[g_nPointCount].m_ptClip = *pVec; + g_PointPoints[g_nPointCount].SetPointPtr(pVec); + g_nPointCount++; + Sys_UpdateWindows(XY | W_CAMERA_IFON); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::OnLButtonDown(UINT nFlags, CPoint point) { + g_pParentWnd->SetActiveXY(this); + UndoCopy(); + + if (g_pParentWnd->GetNurbMode()) { + int i, num = g_pParentWnd->GetNurb()->GetNumValues(); + idList temp; + for (i = 0; i < num; i++) { + temp.Append(g_pParentWnd->GetNurb()->GetValue(i)); + } + CRect rctZ; + GetClientRect(rctZ); + idVec3 v3; + SnapToPoint(point.x, rctZ.Height() - 1 - point.y, v3); + temp.Append(idVec2(v3.x, v3.y)); + num++; + g_pParentWnd->GetNurb()->Clear(); + for (i = 0; i < num; i++) { + g_pParentWnd->GetNurb()->AddValue((1000 * i)/num, temp[i]); + } + } + if (ClipMode() && !RogueClipMode()) { + DropClipPoint(nFlags, point); + } + else if (PathMode()) { + DropPathPoint(nFlags, point); + } + else { + OriginalButtonDown(nFlags, point); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::OnMButtonDown(UINT nFlags, CPoint point) { + OriginalButtonDown(nFlags, point); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +float Betwixt(float f1, float f2) { + if (f1 > f2) { + return f2 + ((f1 - f2) / 2); + } + else { + return f1 + ((f2 - f1) / 2); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::ProduceSplits(brush_t **pFront, brush_t **pBack) { + *pFront = NULL; + *pBack = NULL; + if (ClipMode()) { + if (g_Clip1.Set() && g_Clip2.Set()) { + face_t face; + VectorCopy(g_Clip1.m_ptClip, face.planepts[0]); + VectorCopy(g_Clip2.m_ptClip, face.planepts[1]); + VectorCopy(g_Clip3.m_ptClip, face.planepts[2]); + if (selected_brushes.next && (selected_brushes.next->next == &selected_brushes)) { + if (g_Clip3.Set() == false) { + if (m_nViewType == XY) { + face.planepts[0][2] = selected_brushes.next->mins[2]; + face.planepts[1][2] = selected_brushes.next->mins[2]; + face.planepts[2][0] = Betwixt(g_Clip1.m_ptClip[0], g_Clip2.m_ptClip[0]); + face.planepts[2][1] = Betwixt(g_Clip1.m_ptClip[1], g_Clip2.m_ptClip[1]); + face.planepts[2][2] = selected_brushes.next->maxs[2]; + } + else if (m_nViewType == YZ) { + face.planepts[0][0] = selected_brushes.next->mins[0]; + face.planepts[1][0] = selected_brushes.next->mins[0]; + face.planepts[2][1] = Betwixt(g_Clip1.m_ptClip[1], g_Clip2.m_ptClip[1]); + face.planepts[2][2] = Betwixt(g_Clip1.m_ptClip[2], g_Clip2.m_ptClip[2]); + face.planepts[2][0] = selected_brushes.next->maxs[0]; + } + else { + face.planepts[0][1] = selected_brushes.next->mins[1]; + face.planepts[1][1] = selected_brushes.next->mins[1]; + face.planepts[2][0] = Betwixt(g_Clip1.m_ptClip[0], g_Clip2.m_ptClip[0]); + face.planepts[2][2] = Betwixt(g_Clip1.m_ptClip[2], g_Clip2.m_ptClip[2]); + face.planepts[2][1] = selected_brushes.next->maxs[1]; + } + } + + Brush_SplitBrushByFace(selected_brushes.next, &face, pFront, pBack); + } + } + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CleanList(brush_t *pList) { + brush_t *pBrush = pList->next; + while (pBrush != NULL && pBrush != pList) { + brush_t *pNext = pBrush->next; + Brush_Free(pBrush); + pBrush = pNext; + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::ProduceSplitLists() { + if (AnyPatchesSelected()) { + Sys_Status("Deslecting patches for clip operation.\n"); + + brush_t *next; + for (brush_t * pb = selected_brushes.next; pb != &selected_brushes; pb = next) { + next = pb->next; + if (pb->pPatch) { + Brush_RemoveFromList(pb); + Brush_AddToList(pb, &active_brushes); + UpdatePatchInspector(); + } + } + } + + CleanList(&g_brFrontSplits); + CleanList(&g_brBackSplits); + g_brFrontSplits.next = &g_brFrontSplits; + g_brBackSplits.next = &g_brBackSplits; + + brush_t *pBrush; + for (pBrush = selected_brushes.next; pBrush != NULL && pBrush != &selected_brushes; pBrush = pBrush->next) { + brush_t *pFront = NULL; + brush_t *pBack = NULL; + if (ClipMode()) { + if (g_Clip1.Set() && g_Clip2.Set()) { + face_t face; + VectorCopy(g_Clip1.m_ptClip, face.planepts[0]); + VectorCopy(g_Clip2.m_ptClip, face.planepts[1]); + VectorCopy(g_Clip3.m_ptClip, face.planepts[2]); + if (g_Clip3.Set() == false) { + if (g_pParentWnd->ActiveXY()->GetViewType() == XY) { + face.planepts[0][2] = pBrush->mins[2]; + face.planepts[1][2] = pBrush->mins[2]; + face.planepts[2][0] = Betwixt(g_Clip1.m_ptClip[0], g_Clip2.m_ptClip[0]); + face.planepts[2][1] = Betwixt(g_Clip1.m_ptClip[1], g_Clip2.m_ptClip[1]); + face.planepts[2][2] = pBrush->maxs[2]; + } + else if (g_pParentWnd->ActiveXY()->GetViewType() == YZ) { + face.planepts[0][0] = pBrush->mins[0]; + face.planepts[1][0] = pBrush->mins[0]; + face.planepts[2][1] = Betwixt(g_Clip1.m_ptClip[1], g_Clip2.m_ptClip[1]); + face.planepts[2][2] = Betwixt(g_Clip1.m_ptClip[2], g_Clip2.m_ptClip[2]); + face.planepts[2][0] = pBrush->maxs[0]; + } + else { + face.planepts[0][1] = pBrush->mins[1]; + face.planepts[1][1] = pBrush->mins[1]; + face.planepts[2][0] = Betwixt(g_Clip1.m_ptClip[0], g_Clip2.m_ptClip[0]); + face.planepts[2][2] = Betwixt(g_Clip1.m_ptClip[2], g_Clip2.m_ptClip[2]); + face.planepts[2][1] = pBrush->maxs[1]; + } + } + + Brush_SplitBrushByFace(pBrush, &face, &pFront, &pBack); + if (pBack) { + Brush_AddToList(pBack, &g_brBackSplits); + } + + if (pFront) { + Brush_AddToList(pFront, &g_brFrontSplits); + } + } + } + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void Brush_CopyList(brush_t *pFrom, brush_t *pTo) { + brush_t *pBrush = pFrom->next; + while (pBrush != NULL && pBrush != pFrom) { + brush_t *pNext = pBrush->next; + Brush_RemoveFromList(pBrush); + Brush_AddToList(pBrush, pTo); + pBrush = pNext; + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::OnRButtonDown(UINT nFlags, CPoint point) { + g_pParentWnd->SetActiveXY(this); + m_ptDown = point; + m_bRButtonDown = true; + + if (g_PrefsDlg.m_nMouseButtons == 3) { // 3 button mouse + if ((GetAsyncKeyState(VK_CONTROL) & 0x8000)) { + if (ClipMode()) { // already there? + DropClipPoint(nFlags, point); + } + else { + SetClipMode(true); + g_bRogueClipMode = true; + DropClipPoint(nFlags, point); + } + + return; + } + } + + OriginalButtonDown(nFlags, point); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::OnLButtonUp(UINT nFlags, CPoint point) { + + if (ClipMode()) { + if (g_pMovingClip) { + ReleaseCapture(); + g_pMovingClip = NULL; + } + } + + OriginalButtonUp(nFlags, point); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::OnMButtonUp(UINT nFlags, CPoint point) { + OriginalButtonUp(nFlags, point); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::OnRButtonUp(UINT nFlags, CPoint point) { + m_bRButtonDown = false; + if (point == m_ptDown) { // mouse didn't move + bool bGo = true; + if ((GetAsyncKeyState(VK_MENU) & 0x8000)) { + bGo = false; + } + + if ((GetAsyncKeyState(VK_CONTROL) & 0x8000)) { + bGo = false; + } + + if ((GetAsyncKeyState(VK_SHIFT) & 0x8000)) { + bGo = false; + } + + if (bGo) { + HandleDrop(); + } + } + + OriginalButtonUp(nFlags, point); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::OriginalButtonDown(UINT nFlags, CPoint point) { + CRect rctZ; + GetClientRect(rctZ); + SetWindowPos(&wndTop, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + if (g_pParentWnd->GetTopWindow() != this) { + BringWindowToTop(); + } + + SetFocus(); + SetCapture(); + XY_MouseDown(point.x, rctZ.Height() - 1 - point.y, nFlags); + m_nScrollFlags = nFlags; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::OriginalButtonUp(UINT nFlags, CPoint point) { + CRect rctZ; + GetClientRect(rctZ); + XY_MouseUp(point.x, rctZ.Height() - 1 - point.y, nFlags); + if (!(nFlags & (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON))) { + ReleaseCapture(); + } +} + +idVec3 tdp; + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::OnMouseMove(UINT nFlags, CPoint point) { + + m_ptDown.x = 0; + m_ptDown.y = 0; + + if + ( + g_PrefsDlg.m_bChaseMouse == TRUE && + (point.x < 0 || point.y < 0 || point.x > m_nWidth || point.y > m_nHeight) && + GetCapture() == this + ) { + float fAdjustment = (g_qeglobals.d_gridsize / 8 * 64) / m_fScale; + + // m_ptDrag = point; + m_ptDragAdj.x = 0; + m_ptDragAdj.y = 0; + if (point.x < 0) { + m_ptDragAdj.x = -fAdjustment; + } + else if (point.x > m_nWidth) { + m_ptDragAdj.x = fAdjustment; + } + + if (point.y < 0) { + m_ptDragAdj.y = -fAdjustment; + } + else if (point.y > m_nHeight) { + m_ptDragAdj.y = fAdjustment; + } + + if (m_nTimerID == -1) { + m_nTimerID = SetTimer(100, 50, NULL); + m_ptDrag = point; + m_ptDragTotal = 0; + } + + return; + } + + // else if (m_nTimerID != -1) + if (m_nTimerID != -1) { + KillTimer(m_nTimerID); + pressx -= m_ptDragTotal.x; + pressy += m_ptDragTotal.y; + m_nTimerID = -1; + + // return; + } + + bool bCrossHair = false; + if (!m_bRButtonDown) { + tdp[0] = tdp[1] = tdp[2] = 0.0; + SnapToPoint(point.x, m_nHeight - 1 - point.y, tdp); + + g_strStatus.Format("x:: %.1f y:: %.1f z:: %.1f", tdp[0], tdp[1], tdp[2]); + g_pParentWnd->SetStatusText(1, g_strStatus); + + // + // i need to generalize the point code.. having 3 flavors pretty much sucks.. once + // the new curve stuff looks like it is going to stick i will rationalize this + // down to a single interface.. + // + if (PointMode()) { + if (g_pMovingPoint && GetCapture() == this) { + bCrossHair = true; + SnapToPoint(point.x, m_nHeight - 1 - point.y, g_pMovingPoint->m_ptClip); + g_pMovingPoint->UpdatePointPtr(); + Sys_UpdateWindows(XY | W_CAMERA_IFON); + } + else { + g_pMovingPoint = NULL; + + int nDim1 = (m_nViewType == YZ) ? 1 : 0; + int nDim2 = (m_nViewType == XY) ? 1 : 2; + for (int n = 0; n < g_nPointCount; n++) { + if + ( + fDiff(g_PointPoints[n].m_ptClip[nDim1], tdp[nDim1]) < 3 && + fDiff(g_PointPoints[n].m_ptClip[nDim2], tdp[nDim2]) < 3 + ) { + bCrossHair = true; + g_pMovingPoint = &g_PointPoints[n]; + } + } + } + } + else if (ClipMode()) { + if (g_pMovingClip && GetCapture() == this) { + bCrossHair = true; + SnapToPoint(point.x, m_nHeight - 1 - point.y, g_pMovingClip->m_ptClip); + Sys_UpdateWindows(XY | W_CAMERA_IFON); + } + else { + g_pMovingClip = NULL; + + int nDim1 = (m_nViewType == YZ) ? 1 : 0; + int nDim2 = (m_nViewType == XY) ? 1 : 2; + if (g_Clip1.Set()) { + if + ( + fDiff(g_Clip1.m_ptClip[nDim1], tdp[nDim1]) < 3 && + fDiff(g_Clip1.m_ptClip[nDim2], tdp[nDim2]) < 3 + ) { + bCrossHair = true; + g_pMovingClip = &g_Clip1; + } + } + + if (g_Clip2.Set()) { + if + ( + fDiff(g_Clip2.m_ptClip[nDim1], tdp[nDim1]) < 3 && + fDiff(g_Clip2.m_ptClip[nDim2], tdp[nDim2]) < 3 + ) { + bCrossHair = true; + g_pMovingClip = &g_Clip2; + } + } + + if (g_Clip3.Set()) { + if + ( + fDiff(g_Clip3.m_ptClip[nDim1], tdp[nDim1]) < 3 && + fDiff(g_Clip3.m_ptClip[nDim2], tdp[nDim2]) < 3 + ) { + bCrossHair = true; + g_pMovingClip = &g_Clip3; + } + } + } + + if (bCrossHair == false) { + XY_MouseMoved(point.x, m_nHeight - 1 - point.y, nFlags); + } + } + else if (PathMode()) { + if (g_pMovingPath && GetCapture() == this) { + bCrossHair = true; + SnapToPoint(point.x, m_nHeight - 1 - point.y, g_pMovingPath->m_ptClip); + Sys_UpdateWindows(XY | W_CAMERA_IFON); + } + else { + g_pMovingPath = NULL; + + int nDim1 = (m_nViewType == YZ) ? 1 : 0; + int nDim2 = (m_nViewType == XY) ? 1 : 2; + for (int n = 0; n < g_nPathCount; n++) { + if + ( + fDiff(g_PathPoints[n].m_ptClip[nDim1], tdp[nDim1]) < 3 && + fDiff(g_PathPoints[n].m_ptClip[nDim2], tdp[nDim2]) < 3 + ) { + bCrossHair = true; + g_pMovingPath = &g_PathPoints[n]; + } + } + } + } + else { + bCrossHair = XY_MouseMoved(point.x, m_nHeight - 1 - point.y, nFlags); + } + } + else { + bCrossHair = XY_MouseMoved(point.x, m_nHeight - 1 - point.y, nFlags); + } + + if (bCrossHair) { + SetCursor(::LoadCursor(NULL, IDC_CROSS)); + } + else { + SetCursor(::LoadCursor(NULL, IDC_ARROW)); + } + + /// If precision crosshair is active, force redraw of the 2d view on mouse move + if( m_precisionCrosshairMode != PRECISION_CROSSHAIR_NONE ) + { + /// Force 2d view redraw (so that the precision cursor moves with the mouse) + Sys_UpdateWindows( W_XY ); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::RetainClipMode(bool bMode) { + bool bSave = g_bRogueClipMode; + SetClipMode(bMode); + if (bMode == true) { + g_bRogueClipMode = bSave; + } + else { + g_bRogueClipMode = false; + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::SetClipMode(bool bMode) { + g_bClipMode = bMode; + g_bRogueClipMode = false; + if (bMode) { + g_Clip1.Reset(); + g_Clip2.Reset(); + g_Clip3.Reset(); + CleanList(&g_brFrontSplits); + CleanList(&g_brBackSplits); + g_brFrontSplits.next = &g_brFrontSplits; + g_brBackSplits.next = &g_brBackSplits; + } + else { + if (g_pMovingClip) { + ReleaseCapture(); + g_pMovingClip = NULL; + } + + CleanList(&g_brFrontSplits); + CleanList(&g_brBackSplits); + g_brFrontSplits.next = &g_brFrontSplits; + g_brBackSplits.next = &g_brBackSplits; + Sys_UpdateWindows(XY | W_CAMERA_IFON); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +bool CXYWnd::ClipMode() { + return g_bClipMode; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +bool CXYWnd::RogueClipMode() { + return g_bRogueClipMode; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +bool CXYWnd::PathMode() { + return g_bPathMode; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +bool CXYWnd::PointMode() { + return g_bPointMode; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::SetPointMode(bool b) { + g_bPointMode = b; + if (!b) { + g_nPointCount = 0; + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::OnPaint() { + CPaintDC dc(this); // device context for painting + bool bPaint = true; + if (!qwglMakeCurrent(dc.m_hDC, win32.hGLRC)) { + common->Printf("ERROR: wglMakeCurrent failed.. Error:%i\n", qglGetError()); + common->Printf("Please restart Q3Radiant if the Map view is not working\n"); + bPaint = false; + } + + if (bPaint) { + QE_CheckOpenGLForErrors(); + XY_Draw(); + QE_CheckOpenGLForErrors(); + + if (m_nViewType != XY) { + qglPushMatrix(); + if (m_nViewType == YZ) { + qglRotatef(-90, 0, 1, 0); // put Z going up + } + + qglRotatef(-90, 1, 0, 0); // put Z going up + } + + if ( g_bCrossHairs ) { + qglColor4f( 0.2f, 0.9f, 0.2f, 0.8f ); + qglBegin(GL_LINES); + if (m_nViewType == XY) { + qglVertex2f(-16384, tdp[1]); + qglVertex2f(16384, tdp[1]); + qglVertex2f(tdp[0], -16384); + qglVertex2f(tdp[0], 16384); + } + else if (m_nViewType == YZ) { + qglVertex3f(tdp[0], -16384, tdp[2]); + qglVertex3f(tdp[0], 16384, tdp[2]); + qglVertex3f(tdp[0], tdp[1], -16384); + qglVertex3f(tdp[0], tdp[1], 16384); + } + else { + qglVertex3f(-16384, tdp[1], tdp[2]); + qglVertex3f(16384, tdp[1], tdp[2]); + qglVertex3f(tdp[0], tdp[1], -16384); + qglVertex3f(tdp[0], tdp[1], 16384); + } + + qglEnd(); + } + + if (ClipMode()) { + qglPointSize(4); + qglColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_CLIPPER].ToFloatPtr()); + qglBegin(GL_POINTS); + if (g_Clip1.Set()) { + qglVertex3fv(g_Clip1); + } + + if (g_Clip2.Set()) { + qglVertex3fv(g_Clip2); + } + + if (g_Clip3.Set()) { + qglVertex3fv(g_Clip3); + } + + qglEnd(); + qglPointSize(1); + + CString strMsg; + if (g_Clip1.Set()) { + qglRasterPos3f(g_Clip1.m_ptClip[0] + 2, g_Clip1.m_ptClip[1] + 2, g_Clip1.m_ptClip[2] + 2); + strMsg = "1"; + + // strMsg.Format("1 (%f, %f, %f)", g_Clip1[0], g_Clip1[1], g_Clip1[2]); + qglCallLists(strMsg.GetLength(), GL_UNSIGNED_BYTE, strMsg); + } + + if (g_Clip2.Set()) { + qglRasterPos3f(g_Clip2.m_ptClip[0] + 2, g_Clip2.m_ptClip[1] + 2, g_Clip2.m_ptClip[2] + 2); + strMsg = "2"; + + // strMsg.Format("2 (%f, %f, %f)", g_Clip2[0], g_Clip2[1], g_Clip2[2]); + qglCallLists(strMsg.GetLength(), GL_UNSIGNED_BYTE, strMsg); + } + + if (g_Clip3.Set()) { + qglRasterPos3f(g_Clip3.m_ptClip[0] + 2, g_Clip3.m_ptClip[1] + 2, g_Clip3.m_ptClip[2] + 2); + strMsg = "3"; + + // strMsg.Format("3 (%f, %f, %f)", g_Clip3[0], g_Clip3[1], g_Clip3[2]); + qglCallLists(strMsg.GetLength(), GL_UNSIGNED_BYTE, strMsg); + } + + if (g_Clip1.Set() && g_Clip2.Set() && selected_brushes.next != &selected_brushes) { + ProduceSplitLists(); + + brush_t *pBrush; + brush_t *pList = ((m_nViewType == XZ) ? !g_bSwitch : g_bSwitch) ? &g_brBackSplits : &g_brFrontSplits; + for (pBrush = pList->next; pBrush != NULL && pBrush != pList; pBrush = pBrush->next) { + qglColor3f(1, 1, 0); + + face_t *face; + int order; + for (face = pBrush->brush_faces, order = 0; face; face = face->next, order++) { + idWinding *w = face->face_winding; + if (!w) { + continue; + } + + // draw the polygon + qglBegin(GL_LINE_LOOP); + for (int i = 0; i < w->GetNumPoints(); i++) { + qglVertex3fv( (*w)[i].ToFloatPtr() ); + } + + qglEnd(); + } + } + } + } + + if (PathMode()) { + qglPointSize(4); + qglColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_CLIPPER].ToFloatPtr()); + qglBegin(GL_POINTS); + + int n; + for ( n = 0; n < g_nPathCount; n++) { + qglVertex3fv(g_PathPoints[n]); + } + + qglEnd(); + qglPointSize(1); + + CString strMsg; + for (n = 0; n < g_nPathCount; n++) { + qglRasterPos3f + ( + g_PathPoints[n].m_ptClip[0] + 2, + g_PathPoints[n].m_ptClip[1] + 2, + g_PathPoints[n].m_ptClip[2] + 2 + ); + strMsg.Format("%i", n + 1); + qglCallLists(strMsg.GetLength(), GL_UNSIGNED_BYTE, strMsg); + } + } + + if (m_nViewType != XY) { + qglPopMatrix(); + } + + qwglSwapBuffers(dc.m_hDC); + TRACE("XY Paint\n"); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { + g_pParentWnd->HandleKey(nChar, nRepCnt, nFlags); +} + +// +// ======================================================================================================================= +// FIXME: the brush_t *pBrush is never used. ( Entity_Create uses selected_brushes ) +// ======================================================================================================================= +// +void CreateEntityFromName(char *pName, brush_t *pBrush, bool forceFixed, idVec3 min, idVec3 max, idVec3 org) { + eclass_t *pecNew; + entity_t *petNew; + if (stricmp(pName, "worldspawn") == 0) { + g_pParentWnd->MessageBox("Can't create an entity with worldspawn.", "info", 0); + return; + } + + pecNew = Eclass_ForName(pName, false); + + if ((GetAsyncKeyState(VK_SHIFT) & 0x8000)) { + Select_Ungroup(); + } + + // create it + petNew = Entity_Create(pecNew, forceFixed); + + if (petNew && idStr::Icmp(pName, "light") == 0 ) { + idVec3 rad = max - min; + rad *= 0.5; + if (rad.x != 0 && rad.y != 0 && rad.z != 0) { + SetKeyValue(petNew, "light_radius", va("%g %g %g", idMath::Fabs(rad.x), idMath::Fabs(rad.y), idMath::Fabs(rad.z))); + DeleteKey(petNew, "light"); + } + } + + + if (petNew == NULL) { + if (!((selected_brushes.next == &selected_brushes) || (selected_brushes.next->next != &selected_brushes))) { + brush_t *b = selected_brushes.next; + if (b->owner != world_entity && ((b->owner->eclass->fixedsize && pecNew->fixedsize) || forceFixed)) { + idVec3 mins, maxs; + idVec3 origin; + for (int i = 0; i < 3; i++) { + origin[i] = b->mins[i] - pecNew->mins[i]; + } + + VectorAdd(pecNew->mins, origin, mins); + VectorAdd(pecNew->maxs, origin, maxs); + + brush_t *nb = Brush_Create(mins, maxs, &pecNew->texdef); + Entity_LinkBrush(b->owner, nb); + nb->owner->eclass = pecNew; + SetKeyValue(nb->owner, "classname", pName); + Brush_Free(b); + Brush_Build(nb); + Brush_AddToList(nb, &active_brushes); + Select_Brush(nb); + return; + } + } + + g_pParentWnd->MessageBox("Failed to create entity.", "info", 0); + return; + } + + Select_Deselect(); + + // + // entity_t* pEntity = world_entity; if (selected_brushes.next != + // &selected_brushes) pEntity = selected_brushes.next->owner; + // + Select_Brush(petNew->brushes.onext); + Brush_Build(petNew->brushes.onext); + +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +brush_t *CreateEntityBrush(int x, int y, CXYWnd *pWnd) { + idVec3 mins, maxs; + int i; + float temp; + brush_t *n; + + pWnd->SnapToPoint(x, y, mins); + x += 32; + y += 32; + pWnd->SnapToPoint(x, y, maxs); + + int nDim = (pWnd->GetViewType() == XY) ? 2 : (pWnd->GetViewType() == YZ) ? 0 : 1; + mins[nDim] = g_qeglobals.d_gridsize * ((int)(g_qeglobals.d_new_brush_bottom[nDim] / g_qeglobals.d_gridsize)); + maxs[nDim] = g_qeglobals.d_gridsize * ((int)(g_qeglobals.d_new_brush_top[nDim] / g_qeglobals.d_gridsize)); + + if (maxs[nDim] <= mins[nDim]) { + maxs[nDim] = mins[nDim] + g_qeglobals.d_gridsize; + } + + for (i = 0; i < 3; i++) { + if (mins[i] == maxs[i]) { + maxs[i] += 16; // don't create a degenerate brush + } + + if (mins[i] > maxs[i]) { + temp = mins[i]; + mins[i] = maxs[i]; + maxs[i] = temp; + } + } + + n = Brush_Create(mins, maxs, &g_qeglobals.d_texturewin.texdef); + if (!n) { + return NULL; + } + + Brush_AddToList(n, &selected_brushes); + Entity_LinkBrush(world_entity, n); + Brush_Build(n); + return n; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CreateRightClickEntity(CXYWnd *pWnd, int x, int y, char *pName) { + idVec3 min, max, org; + Select_GetBounds(min, max); + Select_GetMid(org); + + CRect rctZ; + pWnd->GetClientRect(rctZ); + + brush_t *pBrush; + if (selected_brushes.next == &selected_brushes) { + pBrush = CreateEntityBrush(x, rctZ.Height() - 1 - y, pWnd); + min.Zero(); + max.Zero(); + CreateEntityFromName(pName, pBrush, true, min, max, org); + } + else { + pBrush = selected_brushes.next; + CreateEntityFromName(pName, pBrush, false, min, max, org); + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +brush_t *CreateSmartBrush(idVec3 v) { + idVec3 mins, maxs; + int i; + brush_t *n; + + for (i = 0; i < 3; i++) { + mins[i] = v[i] - 16; + maxs[i] = v[i] + 16; + } + + n = Brush_Create(mins, maxs, &g_qeglobals.d_texturewin.texdef); + if (!n) { + return NULL; + } + + Brush_AddToList(n, &selected_brushes); + + // Entity_LinkBrush(world_entity, n); + Brush_Build(n); + return n; +} + +CString g_strSmartEntity; +int g_nSmartX; +int g_nSmartY; +bool g_bSmartWaiting; + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void _SmartPointDone(bool b, int n) { + g_bSmartWaiting = false; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CreateSmartEntity(CXYWnd *pWnd, int x, int y, const char *pName) { + g_nSmartX = x; + g_nSmartY = y; + g_strSmartEntity = pName; + if (g_strSmartEntity.Find("Smart_Train") >= 0) { + ShowInfoDialog("Select the path of the train by left clicking in XY, YZ and/or XZ views. You can move an already dropped point by grabbing and moving it. When you are finished, press ENTER to accept and create the entity and path(s), press ESC to abandon the creation"); + g_bPathMode = true; + g_nPathLimit = 0; + g_nPathCount = 0; + g_bSmartGo = true; + } + else if (g_strSmartEntity.Find("Smart_Monster...") >= 0) { + g_bPathMode = true; + g_nPathLimit = 0; + g_nPathCount = 0; + } + else if (g_strSmartEntity.Find("Smart_Rotating") >= 0) { + g_bSmartWaiting = true; + ShowInfoDialog("Left click to specify the rotation origin"); + AcquirePath(1, &_SmartPointDone); + while (g_bSmartWaiting) { + MSG msg; + if (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + HideInfoDialog(); + + CPtrArray array; + g_bScreenUpdates = false; + CreateRightClickEntity(g_pParentWnd->ActiveXY(), g_nSmartX, g_nSmartY, "func_rotating"); + array.Add(reinterpret_cast < void * > (selected_brushes.next)); + Select_Deselect(); + + brush_t *pBrush = CreateSmartBrush(g_PathPoints[0]); + array.Add(pBrush); + Select_Deselect(); + Select_Brush(reinterpret_cast < brush_t * > (array.GetAt(0))); + Select_Brush(reinterpret_cast < brush_t * > (array.GetAt(1))); + ConnectEntities(); + g_bScreenUpdates = true; + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void FinishSmartCreation() { + CPtrArray array; + HideInfoDialog(); + + brush_t *pEntities = NULL; + if (g_strSmartEntity.Find("Smart_Train") >= 0) { + g_bScreenUpdates = false; + CreateRightClickEntity(g_pParentWnd->ActiveXY(), g_nSmartX, g_nSmartY, "func_train"); + array.Add(reinterpret_cast < void * > (selected_brushes.next)); + int n; + for (n = 0; n < g_nPathCount; n++) { + Select_Deselect(); + CreateRightClickEntity + ( + g_pParentWnd->ActiveXY(), + g_PathPoints[n].m_ptScreen.x, + g_PathPoints[n].m_ptScreen.y, + "path_corner" + ); + array.Add(reinterpret_cast < void * > (selected_brushes.next)); + } + + for (n = 0; n < g_nPathCount; n++) { + Select_Deselect(); + Select_Brush(reinterpret_cast < brush_t * > (array.GetAt(n))); + Select_Brush(reinterpret_cast < brush_t * > (array.GetAt(n + 1))); + ConnectEntities(); + } + + g_bScreenUpdates = true; + } + + g_nPathCount = 0; + g_bPathMode = false; + Sys_UpdateWindows(W_ALL); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::KillPathMode() { + g_bSmartGo = false; + g_bPathMode = false; + if (g_pPathFunc) { + g_pPathFunc(false, g_nPathCount); + } + + g_nPathCount = 0; + g_pPathFunc = NULL; + Sys_UpdateWindows(W_ALL); +} + +// +// ======================================================================================================================= +// gets called for drop down menu messages TIP: it's not always about EntityCreate +// ======================================================================================================================= +// +void CXYWnd::OnEntityCreate(unsigned int nID) { + if (m_mnuDrop.GetSafeHmenu()) { + CString strItem; + m_mnuDrop.GetMenuString(nID, strItem, MF_BYCOMMAND); + + if (strItem.CompareNoCase("Add to...") == 0) { + // + // ++timo TODO: fill the menu with current groups? this one is for adding to + // existing groups only + // + common->Printf("TODO: Add to... in CXYWnd::OnEntityCreate\n"); + } + else if (strItem.CompareNoCase("Remove") == 0) { + // remove selected brushes from their current group + brush_t *b; + for (b = selected_brushes.next; b != &selected_brushes; b = b->next) { + } + } + + // ++timo FIXME: remove when all hooks are in + if + ( + strItem.CompareNoCase("Add to...") == 0 || + strItem.CompareNoCase("Remove") == 0 || + strItem.CompareNoCase("Name...") == 0 || + strItem.CompareNoCase("New group...") == 0 + ) { + common->Printf("TODO: hook drop down group menu\n"); + return; + } + + if (strItem.Find("Smart_") >= 0) { + CreateSmartEntity(this, m_ptDown.x, m_ptDown.y, strItem); + } + else { + CreateRightClickEntity(this, m_ptDown.x, m_ptDown.y, strItem.GetBuffer(0)); + } + + Sys_UpdateWindows(W_ALL); + + // OnLButtonDown((MK_LBUTTON | MK_SHIFT), CPoint(m_ptDown.x+2, m_ptDown.y+2)); + } +} + +BOOL CXYWnd::OnCmdMsg( UINT nID, int nCode, void *pExtra, AFX_CMDHANDLERINFO *pHandlerInfo ) +{ + if ( CWnd::OnCmdMsg( nID, nCode, pExtra, pHandlerInfo ) ) { + return TRUE; + } + return AfxGetMainWnd()->OnCmdMsg( nID, nCode, pExtra, pHandlerInfo ); +} + +bool MergeMenu(CMenu * pMenuDestination, const CMenu * pMenuAdd, bool bTopLevel /*=false*/) +{ + // get the number menu items in the menus + int iMenuAddItemCount = pMenuAdd->GetMenuItemCount(); + int iMenuDestItemCount = pMenuDestination->GetMenuItemCount(); + + // if there are no items return + if (iMenuAddItemCount == 0) + return true; + + // if we are not at top level and the destination menu is not empty + // -> we append a seperator + if (!bTopLevel && iMenuDestItemCount > 0) + pMenuDestination->AppendMenu(MF_SEPARATOR); + + // iterate through the top level of + for(int iLoop = 0; iLoop < iMenuAddItemCount; iLoop++) + { + // get the menu string from the add menu + CString sMenuAddString; + pMenuAdd->GetMenuString(iLoop, sMenuAddString, MF_BYPOSITION); + + // try to get the submenu of the current menu item + CMenu* pSubMenu = pMenuAdd->GetSubMenu(iLoop); + + // check if we have a sub menu + if (!pSubMenu) + { + // normal menu item + // read the source and append at the destination + UINT nState = pMenuAdd->GetMenuState(iLoop, MF_BYPOSITION); + UINT nItemID = pMenuAdd->GetMenuItemID(iLoop); + if (pMenuDestination->AppendMenu(nState, nItemID, sMenuAddString)) + { + // menu item added, don't forget to correct the item count + iMenuDestItemCount++; + } + else + { + TRACE("MergeMenu: AppendMenu failed!\n"); + return false; + } + } + else + { + // create or insert a new popup menu item + + // default insert pos is like ap + int iInsertPosDefault = -1; + + // if we are at top level merge into existing popups rather than + // creating new ones + if(bTopLevel) + { + ASSERT(sMenuAddString != "&?" && sMenuAddString != + "?"); + CString csAdd(sMenuAddString); + csAdd.Remove('&'); // for comparison of menu items supress '&' + bool bAdded = false; + + // try to find existing popup + for( int iLoop1 = 0; iLoop1 < iMenuDestItemCount; iLoop1++ ) + { + // get the menu string from the destination menu + CString sDest; + pMenuDestination->GetMenuString(iLoop1, sDest, MF_BYPOSITION); + sDest.Remove('&'); // for a better compare (s.a.) + + if (csAdd == sDest) + { + // we got a hit -> merge the two popups + // try to get the submenu of the desired destination menu item + CMenu* pSubMenuDest = + pMenuDestination->GetSubMenu(iLoop1); + + if (pSubMenuDest) + { + // merge the popup recursivly and continue with outer for loop + if (!MergeMenu(pSubMenuDest, pSubMenu, false)) + return false; + bAdded = true; + break; + } + } + + // alternativ insert before or + if (iInsertPosDefault == -1 && (sDest == "Window" + || sDest == "?" || sDest == "Help")) + { + iInsertPosDefault = iLoop1; + } + } // for (iLoop1) + if (bAdded) + { + // menu added, so go on with loop over pMenuAdd's top level + continue; + } + } // if (bTopLevel) + + // if the top level search did not find a position append the menu + if( iInsertPosDefault == -1 ) + { + iInsertPosDefault = pMenuDestination->GetMenuItemCount(); + } + + // create a new popup and insert before or + CMenu NewPopupMenu; + if (!NewPopupMenu.CreatePopupMenu()) + { + TRACE("MergeMenu: CreatePopupMenu failed!\n"); + return false; + } + + // merge the new popup recursivly + if (!MergeMenu(&NewPopupMenu, pSubMenu, false)) + return false; + + // insert the new popup menu into the destination menu + HMENU hNewMenu = NewPopupMenu.GetSafeHmenu(); + if (pMenuDestination->InsertMenu(iInsertPosDefault, + MF_BYPOSITION | MF_POPUP | MF_ENABLED, + (UINT)hNewMenu, sMenuAddString )) + { + // don't forget to correct the item count + iMenuDestItemCount++; + } + else + { + TRACE("MergeMenu: InsertMenu failed!\n"); + return false; + } + + // don't destroy the new menu + NewPopupMenu.Detach(); + } // if (pSubMenu) + } // for (iLoop) + return true; +} + + + + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::HandleDrop() { + if (g_PrefsDlg.m_bRightClick == false) { + return; + } + + if (!m_mnuDrop.GetSafeHmenu()) { // first time, load it up + m_mnuDrop.CreatePopupMenu(); + + CMenu *drop = new CMenu; + drop->LoadMenu( IDR_MENU_DROP ); + + MergeMenu( &m_mnuDrop, drop, false ); + + int nID = ID_ENTITY_START; + + CMenu *pMakeEntityPop = &m_mnuDrop; + + // Todo: Make this a config option maybe? + const int entitiesOnSubMenu = false; + if ( entitiesOnSubMenu ) { + pMakeEntityPop = new CMenu; + pMakeEntityPop->CreateMenu(); + } + + CMenu *pChild = NULL; + + eclass_t *e; + CString strActive; + CString strLast; + CString strName; + for (e = eclass; e; e = e->next) { + strLast = strName; + strName = e->name; + + int n_ = strName.Find("_"); + if (n_ > 0) { + CString strLeft = strName.Left(n_); + CString strRight = strName.Right(strName.GetLength() - n_ - 1); + if (strLeft == strActive) { // this is a child + ASSERT(pChild); + pChild->AppendMenu(MF_STRING, nID++, strName); + } + else { + if (pChild) { + pMakeEntityPop->AppendMenu ( + MF_POPUP, + reinterpret_cast < unsigned int > (pChild->GetSafeHmenu()), + strActive + ); + g_ptrMenus.Add(pChild); + + // pChild->DestroyMenu(); delete pChild; + pChild = NULL; + } + + strActive = strLeft; + pChild = new CMenu; + pChild->CreateMenu(); + pChild->AppendMenu(MF_STRING, nID++, strName); + } + } + else { + if (pChild) { + pMakeEntityPop->AppendMenu ( + MF_POPUP, + reinterpret_cast < unsigned int > (pChild->GetSafeHmenu()), + strActive + ); + g_ptrMenus.Add(pChild); + + // pChild->DestroyMenu(); delete pChild; + pChild = NULL; + } + + strActive = ""; + pMakeEntityPop->AppendMenu(MF_STRING, nID++, strName); + } + } + if ( pMakeEntityPop != &m_mnuDrop ) { + m_mnuDrop.AppendMenu ( + MF_POPUP, + reinterpret_cast < unsigned int > (pMakeEntityPop->GetSafeHmenu()), + "Make Entity" + ); + } + } + + CPoint ptMouse; + GetCursorPos(&ptMouse); + m_mnuDrop.TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON, ptMouse.x, ptMouse.y, this); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::XY_Init() { + m_vOrigin[0] = 0; + m_vOrigin[1] = 20; + m_vOrigin[2] = 46; + m_fScale = 1; + m_precisionCrosshairMode = PRECISION_CROSSHAIR_NONE; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::SnapToPoint(int x, int y, idVec3 &point) { + if (g_PrefsDlg.m_bNoClamp) { + XY_ToPoint(x, y, point); + } + else { + XY_ToGridPoint(x, y, point); + } + + // -- else -- XY_ToPoint(x, y, point); -- //XY_ToPoint(x, y, point); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::XY_ToPoint(int x, int y, idVec3 &point) { + float fx = x; + float fy = y; + float fw = m_nWidth; + float fh = m_nHeight; + if (m_nViewType == XY) { + point[0] = m_vOrigin[0] + (fx - fw / 2) / m_fScale; + point[1] = m_vOrigin[1] + (fy - fh / 2) / m_fScale; + + // point[2] = 0; + } + else if (m_nViewType == YZ) { + // + // //point[0] = 0; point[1] = m_vOrigin[0] + (fx - fw / 2) / m_fScale; point[2] = + // m_vOrigin[1] + (fy - fh / 2 ) / m_fScale; + // + point[1] = m_vOrigin[1] + (fx - fw / 2) / m_fScale; + point[2] = m_vOrigin[2] + (fy - fh / 2) / m_fScale; + } + else { + // + // point[0] = m_vOrigin[0] + (fx - fw / 2) / m_fScale; /point[1] = 0; point[2] = + // m_vOrigin[1] + (fy - fh / 2) / m_fScale; + // + point[0] = m_vOrigin[0] + (fx - fw / 2) / m_fScale; + + // point[1] = 0; + point[2] = m_vOrigin[2] + (fy - fh / 2) / m_fScale; + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::XY_ToGridPoint(int x, int y, idVec3 &point) { + if (m_nViewType == XY) { + point[0] = m_vOrigin[0] + (x - m_nWidth / 2) / m_fScale; + point[1] = m_vOrigin[1] + (y - m_nHeight / 2) / m_fScale; + + // point[2] = 0; + point[0] = floor(point[0] / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize; + point[1] = floor(point[1] / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize; + } + else if (m_nViewType == YZ) { + // + // point[0] = 0; point[1] = m_vOrigin[0] + (x - m_nWidth / 2) / m_fScale; point[2] + // = m_vOrigin[1] + (y - m_nHeight / 2) / m_fScale; + // + point[1] = m_vOrigin[1] + (x - m_nWidth / 2) / m_fScale; + point[2] = m_vOrigin[2] + (y - m_nHeight / 2) / m_fScale; + point[1] = floor(point[1] / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize; + point[2] = floor(point[2] / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize; + } + else { + // + // point[1] = 0; point[0] = m_vOrigin[0] + (x - m_nWidth / 2) / m_fScale; point[2] + // = m_vOrigin[1] + (y - m_nHeight / 2) / m_fScale; + // + point[0] = m_vOrigin[0] + (x - m_nWidth / 2) / m_fScale; + point[2] = m_vOrigin[2] + (y - m_nHeight / 2) / m_fScale; + point[0] = floor(point[0] / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize; + point[2] = floor(point[2] / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize; + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +idVec3 dragOrigin; +idVec3 dragDir; +idVec3 dragX; +idVec3 dragY; + +void CXYWnd::XY_MouseDown(int x, int y, int buttons) { + idVec3 point,center; + idVec3 origin, dir, right, up; + + m_nButtonstate = buttons; + m_nPressx = x; + m_nPressy = y; + VectorCopy(vec3_origin, m_vPressdelta); + + point.Zero(); + + XY_ToPoint(x, y, point); + + VectorCopy(point, origin); + + dir.Zero(); + if (m_nViewType == XY) { + origin[2] = HUGE_DISTANCE; + dir[2] = -1; + right[0] = 1 / m_fScale; + right[1] = 0; + right[2] = 0; + up[0] = 0; + up[1] = 1 / m_fScale; + up[2] = 0; + point[2] = g_pParentWnd->GetCamera()->Camera().origin[2]; + } + else if (m_nViewType == YZ) { + origin[0] = HUGE_DISTANCE; + dir[0] = -1; + right[1] = 1 / m_fScale; + right[2] = 0; + right[0] = 0; + up[0] = 0; + up[2] = 1 / m_fScale; + up[1] = 0; + point[0] = g_pParentWnd->GetCamera()->Camera().origin[0]; + } + else { + origin[1] = HUGE_DISTANCE; + dir[1] = -1; + right[0] = 1 / m_fScale; + right[2] = 0; + right[1] = 0; + up[0] = 0; + up[2] = 1 / m_fScale; + up[1] = 0; + point[1] = g_pParentWnd->GetCamera()->Camera().origin[1]; + } + + dragOrigin = m_vOrigin; + dragDir = dir; + dragX = right; + dragY = up; + + m_bPress_selection = (selected_brushes.next != &selected_brushes); + + GetCursorPos(&m_ptCursor); + + // Sys_GetCursorPos (&m_ptCursor.x, &m_ptCursor.y); + if (buttons == MK_LBUTTON && activeDrag) { + activeDragging = true; + } + else { + activeDragging = false; + } + + // lbutton = manipulate selection shift-LBUTTON = select + if + ( + (buttons == MK_LBUTTON) || + (buttons == (MK_LBUTTON | MK_SHIFT)) || + (buttons == (MK_LBUTTON | MK_CONTROL)) || + (buttons == (MK_LBUTTON | MK_CONTROL | MK_SHIFT)) + ) { + if (g_qeglobals.d_select_mode == sel_addpoint) { + XY_ToGridPoint(x, y, point); + if (g_qeglobals.selectObject) { + g_qeglobals.selectObject->addPoint(point); + } + + return; + } + + Patch_SetView((m_nViewType == XY) ? W_XY : (m_nViewType == YZ) ? W_YZ : W_XZ); + Drag_Begin(x, y, buttons, right, up, origin, dir); + return; + } + + int nMouseButton = g_PrefsDlg.m_nMouseButtons == 2 ? MK_RBUTTON : MK_MBUTTON; + + // control mbutton = move camera + if (m_nButtonstate == (MK_CONTROL | nMouseButton)) { + VectorCopyXY(point, g_pParentWnd->GetCamera()->Camera().origin); + Sys_UpdateWindows(W_CAMERA | W_XY_OVERLAY); + } + + // mbutton = angle camera + if + ( + (g_PrefsDlg.m_nMouseButtons == 3 && m_nButtonstate == MK_MBUTTON) || + (g_PrefsDlg.m_nMouseButtons == 2 && m_nButtonstate == (MK_SHIFT | MK_CONTROL | MK_RBUTTON)) + ) { + VectorSubtract(point, g_pParentWnd->GetCamera()->Camera().origin, point); + + int n1 = (m_nViewType == XY) ? 1 : 2; + int n2 = (m_nViewType == YZ) ? 1 : 0; + int nAngle = (m_nViewType == XY) ? YAW : PITCH; + if (point[n1] || point[n2]) { + g_pParentWnd->GetCamera()->Camera().angles[nAngle] = RAD2DEG( atan2(point[n1], point[n2]) ); + Sys_UpdateWindows(W_CAMERA_IFON | W_XY_OVERLAY); + } + } + + // shift mbutton = move z checker + if (m_nButtonstate == (MK_SHIFT | nMouseButton)) { + if (RotateMode() || g_bPatchBendMode) { + SnapToPoint(x, y, point); + VectorCopyXY(point, g_vRotateOrigin); + if (g_bPatchBendMode) { + VectorCopy(point, g_vBendOrigin); + } + + Sys_UpdateWindows(W_XY); + return; + } + else { + SnapToPoint(x, y, point); + if (m_nViewType == XY) { + z.origin[0] = point[0]; + z.origin[1] = point[1]; + } + else if (m_nViewType == YZ) { + z.origin[0] = point[1]; + z.origin[1] = point[2]; + } + else { + z.origin[0] = point[0]; + z.origin[1] = point[2]; + } + + Sys_UpdateWindows(W_XY_OVERLAY | W_Z); + return; + } + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::XY_MouseUp(int x, int y, int buttons) { + activeDragging = false; + Drag_MouseUp(buttons); + if (!m_bPress_selection) { + Sys_UpdateWindows(W_ALL); + } + + m_nButtonstate = 0; + while (::ShowCursor(TRUE) < 0) + ; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +bool CXYWnd::DragDelta(int x, int y, idVec3 &move) { + idVec3 xvec, yvec, delta; + int i; + + xvec[0] = 1 / m_fScale; + xvec[1] = xvec[2] = 0; + yvec[1] = 1 / m_fScale; + yvec[0] = yvec[2] = 0; + + for (i = 0; i < 3; i++) { + delta[i] = xvec[i] * (x - m_nPressx) + yvec[i] * (y - m_nPressy); + if (!g_PrefsDlg.m_bNoClamp) { + delta[i] = floor(delta[i] / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize; + } + } + + VectorSubtract(delta, m_vPressdelta, move); + VectorCopy(delta, m_vPressdelta); + + + if (move[0] || move[1] || move[2]) { + return true; + } + + return false; +} + +/* + ======================================================================================================================= + NewBrushDrag + ======================================================================================================================= + */ +void CXYWnd::NewBrushDrag(int x, int y) { + idVec3 mins, maxs, junk; + int i; + float temp; + brush_t *n; + + if ( radiant_entityMode.GetBool() ) { + return; + } + + if (!DragDelta(x, y, junk)) { + return; + } + + // delete the current selection + if (selected_brushes.next != &selected_brushes) { + Brush_Free(selected_brushes.next); + } + + SnapToPoint(m_nPressx, m_nPressy, mins); + + int nDim = (m_nViewType == XY) ? 2 : (m_nViewType == YZ) ? 0 : 1; + + mins[nDim] = g_qeglobals.d_gridsize * ((int)(g_qeglobals.d_new_brush_bottom[nDim] / g_qeglobals.d_gridsize)); + SnapToPoint(x, y, maxs); + maxs[nDim] = g_qeglobals.d_gridsize * ((int)(g_qeglobals.d_new_brush_top[nDim] / g_qeglobals.d_gridsize)); + if (maxs[nDim] <= mins[nDim]) { + maxs[nDim] = mins[nDim] + g_qeglobals.d_gridsize; + } + + for (i = 0; i < 3; i++) { + if (mins[i] == maxs[i]) { + return; // don't create a degenerate brush + } + + if (mins[i] > maxs[i]) { + temp = mins[i]; + mins[i] = maxs[i]; + maxs[i] = temp; + } + } + + n = Brush_Create(mins, maxs, &g_qeglobals.d_texturewin.texdef); + if (!n) { + return; + } + + idVec3 vSize; + VectorSubtract(maxs, mins, vSize); + g_strStatus.Format("Size X:: %.1f Y:: %.1f Z:: %.1f", vSize[0], vSize[1], vSize[2]); + g_pParentWnd->SetStatusText(2, g_strStatus); + + Brush_AddToList(n, &selected_brushes); + + Entity_LinkBrush(world_entity, n); + + Brush_Build(n); + + // Sys_UpdateWindows (W_ALL); + Sys_UpdateWindows(W_XY | W_CAMERA); +} + +/* + ======================================================================================================================= + XY_MouseMoved + ======================================================================================================================= + */ +bool CXYWnd::XY_MouseMoved(int x, int y, int buttons) { + idVec3 point; + + if (!m_nButtonstate) { + if (g_bCrossHairs) { + ::ShowCursor(FALSE); + Sys_UpdateWindows(W_XY | W_XY_OVERLAY); + ::ShowCursor(TRUE); + } + + return false; + } + + // + // lbutton without selection = drag new brush if (m_nButtonstate == MK_LBUTTON && + // !m_bPress_selection && g_qeglobals.d_select_mode != sel_curvepoint && + // g_qeglobals.d_select_mode != sel_splineedit) + // + if (m_nButtonstate == MK_LBUTTON && !m_bPress_selection && g_qeglobals.d_select_mode == sel_brush) { + NewBrushDrag(x, y); + return false; + } + + // lbutton (possibly with control and or shift) with selection = drag selection + if (m_nButtonstate & MK_LBUTTON) { + Drag_MouseMoved(x, y, buttons); + Sys_UpdateWindows(W_XY_OVERLAY | W_CAMERA_IFON | W_Z); + return false; + } + + int nMouseButton = g_PrefsDlg.m_nMouseButtons == 2 ? MK_RBUTTON : MK_MBUTTON; + + // control mbutton = move camera + if (m_nButtonstate == (MK_CONTROL | nMouseButton)) { + SnapToPoint(x, y, point); + VectorCopyXY(point, g_pParentWnd->GetCamera()->Camera().origin); + Sys_UpdateWindows(W_XY_OVERLAY | W_CAMERA); + return false; + } + + // shift mbutton = move z checker + if (m_nButtonstate == (MK_SHIFT | nMouseButton)) { + if (RotateMode() || g_bPatchBendMode) { + SnapToPoint(x, y, point); + VectorCopyXY(point, g_vRotateOrigin); + if (g_bPatchBendMode) { + VectorCopy(point, g_vBendOrigin); + } + + Sys_UpdateWindows(W_XY); + return false; + } + else { + SnapToPoint(x, y, point); + if (m_nViewType == XY) { + z.origin[0] = point[0]; + z.origin[1] = point[1]; + } + else if (m_nViewType == YZ) { + z.origin[0] = point[1]; + z.origin[1] = point[2]; + } + else { + z.origin[0] = point[0]; + z.origin[1] = point[2]; + } + } + + Sys_UpdateWindows(W_XY_OVERLAY | W_Z); + return false; + } + + // mbutton = angle camera + if + ( + (g_PrefsDlg.m_nMouseButtons == 3 && m_nButtonstate == MK_MBUTTON) || + (g_PrefsDlg.m_nMouseButtons == 2 && m_nButtonstate == (MK_SHIFT | MK_CONTROL | MK_RBUTTON)) + ) { + SnapToPoint(x, y, point); + VectorSubtract(point, g_pParentWnd->GetCamera()->Camera().origin, point); + + int n1 = (m_nViewType == XY) ? 1 : 2; + int n2 = (m_nViewType == YZ) ? 1 : 0; + int nAngle = (m_nViewType == XY) ? YAW : PITCH; + if (point[n1] || point[n2]) { + g_pParentWnd->GetCamera()->Camera().angles[nAngle] = RAD2DEG( atan2(point[n1], point[n2]) ); + Sys_UpdateWindows(W_CAMERA_IFON | W_XY_OVERLAY); + } + + return false; + } + + // rbutton = drag xy origin + if (m_nButtonstate == MK_RBUTTON) { + Sys_GetCursorPos(&x, &y); + + if (x != m_ptCursor.x || y != m_ptCursor.y) { + if ((GetAsyncKeyState(VK_MENU) & 0x8000)) { + int *px = &x; + long *px2 = &m_ptCursor.x; + + if (fDiff(y, m_ptCursor.y) > fDiff(x, m_ptCursor.x)) { + px = &y; + px2 = &m_ptCursor.y; + } + + if (*px > *px2) { + // zoom in + SetScale( Scale() * 1.1f ); + if ( Scale() < 0.1f ) { + SetScale( 0.1f ); + } + } + else if (*px < *px2) { + // zoom out + SetScale( Scale() * 0.9f ); + if ( Scale() > 16.0f ) { + SetScale( 16.0f ); + } + } + + *px2 = *px; + Sys_UpdateWindows(W_XY | W_XY_OVERLAY); + } + else { + int nDim1 = (m_nViewType == YZ) ? 1 : 0; + int nDim2 = (m_nViewType == XY) ? 1 : 2; + m_vOrigin[nDim1] -= (x - m_ptCursor.x) / m_fScale; + m_vOrigin[nDim2] += (y - m_ptCursor.y) / m_fScale; + SetCursorPos(m_ptCursor.x, m_ptCursor.y); + ::ShowCursor(FALSE); + + // XY_Draw(); RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW); + Sys_UpdateWindows(W_XY | W_XY_OVERLAY); + + // ::ShowCursor(TRUE); + } + } + + return false; + } + + return false; +} + +/* + ======================================================================================================================= + DRAWING £ + XY_DrawGrid + ======================================================================================================================= + */ +void CXYWnd::XY_DrawGrid() { + float x, y, xb, xe, yb, ye; + int w, h; + char text[32]; + + int startPos = max ( 64 , g_qeglobals.d_gridsize ); + + w = m_nWidth / 2 / m_fScale; + h = m_nHeight / 2 / m_fScale; + + int nDim1 = (m_nViewType == YZ) ? 1 : 0; + int nDim2 = (m_nViewType == XY) ? 1 : 2; + + // int nDim1 = 0; int nDim2 = 1; + xb = m_vOrigin[nDim1] - w; + if (xb < region_mins[nDim1]) { + xb = region_mins[nDim1]; + } + + xb = startPos * floor(xb / startPos); + + xe = m_vOrigin[nDim1] + w; + if (xe > region_maxs[nDim1]) { + xe = region_maxs[nDim1]; + } + + xe = startPos * ceil(xe / startPos); + + yb = m_vOrigin[nDim2] - h; + if (yb < region_mins[nDim2]) { + yb = region_mins[nDim2]; + } + + yb = startPos * floor(yb / startPos); + + ye = m_vOrigin[nDim2] + h; + if (ye > region_maxs[nDim2]) { + ye = region_maxs[nDim2]; + } + + ye = startPos * ceil(ye / startPos); + + // draw major blocks + qglColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_GRIDMAJOR].ToFloatPtr()); + + int stepSize = 64 * 0.1 / m_fScale; + if (stepSize < 64) { + stepSize = max ( 64 , g_qeglobals.d_gridsize ); + } + else { + int i; + for (i = 1; i < stepSize; i <<= 1) { + } + + stepSize = i; + } + + if (g_qeglobals.d_showgrid) { + qglBegin(GL_LINES); + + for (x = xb; x <= xe; x += stepSize) { + qglVertex2f(x, yb); + qglVertex2f(x, ye); + } + + for (y = yb; y <= ye; y += stepSize) { + qglVertex2f(xb, y); + qglVertex2f(xe, y); + } + + qglEnd(); + } + + // draw minor blocks + if ( m_fScale > .1 && + g_qeglobals.d_showgrid && + g_qeglobals.d_gridsize * m_fScale >= 4 && + !g_qeglobals.d_savedinfo.colors[COLOR_GRIDMINOR].Compare( g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK] ) ) { + + qglColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_GRIDMINOR].ToFloatPtr()); + + qglBegin(GL_LINES); + for (x = xb; x < xe; x += g_qeglobals.d_gridsize) { + if (!((int)x & (startPos - 1))) { + continue; + } + + qglVertex2f(x, yb); + qglVertex2f(x, ye); + } + + for (y = yb; y < ye; y += g_qeglobals.d_gridsize) { + if (!((int)y & (startPos - 1))) { + continue; + } + + qglVertex2f(xb, y); + qglVertex2f(xe, y); + } + + qglEnd(); + } + + + // draw ZClip boundaries (if applicable)... + // + if (m_nViewType == XZ || m_nViewType == YZ) + { + if (g_pParentWnd->GetZWnd()->m_pZClip) // should always be the case at this point I think, but this is safer + { + if (g_pParentWnd->GetZWnd()->m_pZClip->IsEnabled()) + { + qglColor3f(ZCLIP_COLOUR); + qglLineWidth(2); + qglBegin (GL_LINES); + + qglVertex2f (xb, g_pParentWnd->GetZWnd()->m_pZClip->GetTop()); + qglVertex2f (xe, g_pParentWnd->GetZWnd()->m_pZClip->GetTop()); + + qglVertex2f (xb, g_pParentWnd->GetZWnd()->m_pZClip->GetBottom()); + qglVertex2f (xe, g_pParentWnd->GetZWnd()->m_pZClip->GetBottom()); + + qglEnd (); + qglLineWidth(1); + } + } + } + + + + + // draw coordinate text if needed + if (g_qeglobals.d_savedinfo.show_coordinates) { + // glColor4f(0, 0, 0, 0); + qglColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_GRIDTEXT].ToFloatPtr()); + + float lastRaster = xb; + + for (x = xb; x < xe; x += stepSize) { + qglRasterPos2f(x, m_vOrigin[nDim2] + h - 10 / m_fScale); + sprintf(text, "%i", (int)x); + qglCallLists(strlen(text), GL_UNSIGNED_BYTE, text); + } + + for (y = yb; y < ye; y += stepSize) { + qglRasterPos2f(m_vOrigin[nDim1] - w + 1, y); + sprintf(text, "%i", (int)y); + qglCallLists(strlen(text), GL_UNSIGNED_BYTE, text); + } + + if (Active()) { + qglColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_VIEWNAME].ToFloatPtr()); + } + + qglRasterPos2f(m_vOrigin[nDim1] - w + 35 / m_fScale, m_vOrigin[nDim2] + h - 20 / m_fScale); + + char cView[20]; + if (m_nViewType == XY) { + strcpy(cView, "XY Top"); + } + else if (m_nViewType == XZ) { + strcpy(cView, "XZ Front"); + } + else { + strcpy(cView, "YZ Side"); + } + + qglCallLists(strlen(cView), GL_UNSIGNED_BYTE, cView); + } + + /* + * if (true) { qglColor3f(g_qeglobals.d_savedinfo.colors[COLOR_GRIDMINOR]); + * qglBegin (GL_LINES); qglVertex2f (x, yb); qglVertex2f (x, ye); qglEnd(); } + */ +} + +/* + ======================================================================================================================= + XY_DrawBlockGrid + ======================================================================================================================= + */ +void CXYWnd::XY_DrawBlockGrid() { + float x, y, xb, xe, yb, ye; + int w, h; + char text[32]; + + w = m_nWidth / 2 / m_fScale; + h = m_nHeight / 2 / m_fScale; + + int nDim1 = (m_nViewType == YZ) ? 1 : 0; + int nDim2 = (m_nViewType == XY) ? 1 : 2; + + xb = m_vOrigin[nDim1] - w; + if (xb < region_mins[nDim1]) { + xb = region_mins[nDim1]; + } + + xb = 1024 * floor(xb / 1024); + + xe = m_vOrigin[nDim1] + w; + if (xe > region_maxs[nDim1]) { + xe = region_maxs[nDim1]; + } + + xe = 1024 * ceil(xe / 1024); + + yb = m_vOrigin[nDim2] - h; + if (yb < region_mins[nDim2]) { + yb = region_mins[nDim2]; + } + + yb = 1024 * floor(yb / 1024); + + ye = m_vOrigin[nDim2] + h; + if (ye > region_maxs[nDim2]) { + ye = region_maxs[nDim2]; + } + + ye = 1024 * ceil(ye / 1024); + + // draw major blocks + qglColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_GRIDBLOCK].ToFloatPtr()); + qglLineWidth(0.5); + + qglBegin(GL_LINES); + + for (x = xb; x <= xe; x += 1024) { + qglVertex2f(x, yb); + qglVertex2f(x, ye); + } + + for (y = yb; y <= ye; y += 1024) { + qglVertex2f(xb, y); + qglVertex2f(xe, y); + } + + qglEnd(); + qglLineWidth(0.25); + + // draw coordinate text if needed + for (x = xb; x < xe; x += 1024) { + for (y = yb; y < ye; y += 1024) { + qglRasterPos2f(x + 512, y + 512); + sprintf(text, "%i,%i", (int)floor(x / 1024), (int)floor(y / 1024)); + qglCallLists(strlen(text), GL_UNSIGNED_BYTE, text); + } + } + + qglColor4f(0, 0, 0, 0); +} + +void GLColoredBoxWithLabel(float x, float y, float size, idVec4 color, const char *text, idVec4 textColor, float xofs, float yofs, float lineSize) { + globalImages->BindNull(); + qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + qglDisable(GL_CULL_FACE); + qglDisable(GL_BLEND); + qglColor4f(color[0], color[1], color[2], color[3]); + qglBegin(GL_QUADS); + qglVertex3f(x - size, y - size, 0); + qglVertex3f(x + size, y - size, 0); + qglVertex3f(x + size, y + size, 0); + qglVertex3f(x - size, y + size, 0); + qglEnd(); + + qglColor4f(textColor[0], textColor[1], textColor[2], textColor[3]); + qglLineWidth(lineSize); + qglRasterPos2f(x + xofs, y + yofs); + qglCallLists(strlen(text), GL_UNSIGNED_BYTE, text); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::DrawRotateIcon() { + float x, y; + + if (m_nViewType == XY) { + x = g_vRotateOrigin[0]; + y = g_vRotateOrigin[1]; + } + else if (m_nViewType == YZ) { + x = g_vRotateOrigin[1]; + y = g_vRotateOrigin[2]; + } + else { + x = g_vRotateOrigin[0]; + y = g_vRotateOrigin[2]; + } + + qglEnable(GL_BLEND); + globalImages->BindNull(); + qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + qglDisable(GL_CULL_FACE); + qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + qglColor4f( 0.8f, 0.1f, 0.9f, 0.25f ); + + qglBegin(GL_QUADS); + qglVertex3f(x - 4, y - 4, 0); + qglVertex3f(x + 4, y - 4, 0); + qglVertex3f(x + 4, y + 4, 0); + qglVertex3f(x - 4, y + 4, 0); + qglEnd(); + qglDisable(GL_BLEND); + + qglColor4f( 1.0f, 0.2f, 1.0f, 1.0f ); + qglBegin(GL_POINTS); + qglVertex3f(x, y, 0); + qglEnd(); + + + int w = m_nWidth / 2 / m_fScale; + int h = m_nHeight / 2 / m_fScale; + int nDim1 = (m_nViewType == YZ) ? 1 : 0; + int nDim2 = (m_nViewType == XY) ? 1 : 2; + x = m_vOrigin[nDim1] - w + 35 / m_fScale; + y = m_vOrigin[nDim2] + h - 40 / m_fScale; + const char *p = "Rotate Z Axis"; + if (g_qeglobals.rotateAxis == 1) { + p = "Rotate Y Axis"; + } else if (g_qeglobals.rotateAxis == 0) { + p = "Rotate X Axis"; + } + idStr str = p; + if (g_qeglobals.flatRotation) { + str += g_qeglobals.flatRotation == 2 ? " Flat [center] " : " Flat [ rot origin ] "; + } + qglRasterPos2f(x, y); + qglCallLists(str.Length(), GL_UNSIGNED_BYTE, str.c_str()); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::DrawCameraIcon() { + float x, y, a; + + if (m_nViewType == XY) { + x = g_pParentWnd->GetCamera()->Camera().origin[0]; + y = g_pParentWnd->GetCamera()->Camera().origin[1]; + a = g_pParentWnd->GetCamera()->Camera().angles[YAW] * idMath::M_DEG2RAD; + } + else if (m_nViewType == YZ) { + x = g_pParentWnd->GetCamera()->Camera().origin[1]; + y = g_pParentWnd->GetCamera()->Camera().origin[2]; + a = g_pParentWnd->GetCamera()->Camera().angles[PITCH] * idMath::M_DEG2RAD; + } + else { + x = g_pParentWnd->GetCamera()->Camera().origin[0]; + y = g_pParentWnd->GetCamera()->Camera().origin[2]; + a = g_pParentWnd->GetCamera()->Camera().angles[PITCH] * idMath::M_DEG2RAD; + } + + float scale = 1.0/m_fScale; //jhefty - keep the camera icon proportionally the same size + + qglColor3f(0.0, 0.0, 1.0); + qglBegin(GL_LINE_STRIP); + qglVertex3f(x - 16*scale, y, 0); + qglVertex3f(x, y + 8*scale, 0); + qglVertex3f(x + 16*scale, y, 0); + qglVertex3f(x, y - 8*scale, 0); + qglVertex3f(x - 16*scale, y, 0); + qglVertex3f(x + 16*scale, y, 0); + qglEnd(); + + qglBegin(GL_LINE_STRIP); + qglVertex3f(x + (48 * cos( a + idMath::PI * 0.25f )*scale), y + (48 * sin( a + idMath::PI * 0.25f )*scale), 0); + qglVertex3f(x, y, 0); + qglVertex3f(x + (48 * cos( a - idMath::PI * 0.25f )*scale), y + (48 * sin( a - idMath::PI * 0.25f )*scale), 0); + qglEnd(); + +#if 0 + + char text[128]; + qglRasterPos2f(x + 64, y + 64); + sprintf(text, "%f", g_pParentWnd->GetCamera()->Camera().angles[YAW]); + qglCallLists(strlen(text), GL_UNSIGNED_BYTE, text); +#endif +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::DrawZIcon(void) { + if (m_nViewType == XY) { + float x = z.origin[0]; + float y = z.origin[1]; + qglEnable(GL_BLEND); + globalImages->BindNull(); + qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + qglDisable(GL_CULL_FACE); + qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + qglColor4f(0.0, 0.0, 1.0, 0.25); + qglBegin(GL_QUADS); + qglVertex3f(x - 8, y - 8, 0); + qglVertex3f(x + 8, y - 8, 0); + qglVertex3f(x + 8, y + 8, 0); + qglVertex3f(x - 8, y + 8, 0); + qglEnd(); + qglDisable(GL_BLEND); + + qglColor4f(0.0, 0.0, 1.0, 1); + + qglBegin(GL_LINE_LOOP); + qglVertex3f(x - 8, y - 8, 0); + qglVertex3f(x + 8, y - 8, 0); + qglVertex3f(x + 8, y + 8, 0); + qglVertex3f(x - 8, y + 8, 0); + qglEnd(); + + qglBegin(GL_LINE_STRIP); + qglVertex3f(x - 4, y + 4, 0); + qglVertex3f(x + 4, y + 4, 0); + qglVertex3f(x - 4, y - 4, 0); + qglVertex3f(x + 4, y - 4, 0); + qglEnd(); + } +} + +/* + ======================================================================================================================= + FilterBrush + ======================================================================================================================= + */ +bool FilterBrush(brush_t *pb) { + + if (!pb->owner) { + return false; // during construction + } + + if (pb->hiddenBrush) { + return true; + } + + if ( pb->forceVisibile ) { + return false; + } + + if (g_pParentWnd->GetZWnd()->m_pZClip) // ZClip class up and running? (and hence Z window built) + { + if (g_pParentWnd->GetZWnd()->m_pZClip->IsEnabled()) + { + // ZClipping active... + // + if (pb->mins[2] > g_pParentWnd->GetZWnd()->m_pZClip->GetTop() // brush bottom edge is above clip top + || + pb->maxs[2] < g_pParentWnd->GetZWnd()->m_pZClip->GetBottom()// brush top edge is below clip bottom + ) + { + return TRUE; + } + } + } + + if (g_qeglobals.d_savedinfo.exclude & (EXCLUDE_CAULK | EXCLUDE_VISPORTALS)) { + // + // filter out the brush only if all faces are caulk if not don't hide the whole + // brush, proceed on a per-face basis (Cam_Draw) ++timo TODO: set this as a + // preference .. show caulk: hide any brush with caulk // don't draw caulk faces + // + face_t *f; + f = pb->brush_faces; + while (f) { + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_CAULK) { + if (!strstr(f->texdef.name, "caulk")) { + break; + } + } else { + if (strstr(f->texdef.name, "visportal")) { + return true; + } + } + + f = f->next; + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_CAULK) { + if (!f) { + return true; + } + } + + // ++timo FIXME: .. same deal here? + if (strstr(pb->brush_faces->texdef.name, "donotenter")) { + return true; + } + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_HINT) { + if (strstr(pb->brush_faces->texdef.name, "hint")) { + return true; + } + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_CLIP) { + if (strstr(pb->brush_faces->texdef.name, "clip")) { + return true; + } + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_TRIGGERS) { + if (strstr(pb->brush_faces->texdef.name, "trig")) { + return true; + } + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_NODRAW) { + if (strstr(pb->brush_faces->texdef.name, "nodraw")) { + return true; + } + } + + + if (strstr(pb->brush_faces->texdef.name, "skip")) { + return true; + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_DYNAMICS) { + if (pb->modelHandle > 0) { + idRenderModel *model = pb->modelHandle; + if ( dynamic_cast(model) ) { + return true; + } + } + } + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_CURVES) { + if (pb->pPatch) { + return true; + } + } + + if (pb->owner == world_entity) { + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_WORLD) { + return true; + } + + return false; + } + else { + if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_ENT ) { + return ( idStr::Cmpn( pb->owner->eclass->name, "func_static", 10 ) != 0 ); + } + } + + if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_LIGHTS && pb->owner->eclass->nShowFlags & ECLASS_LIGHT ) { + return true; + } + + if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_COMBATNODES && pb->owner->eclass->nShowFlags & ECLASS_COMBATNODE ) { + return true; + } + + if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_PATHS && pb->owner->eclass->nShowFlags & ECLASS_PATH) { + return true; + } + + if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_MODELS && ( pb->owner->eclass->entityModel != NULL || pb->modelHandle > 0 ) ) { + return true; + } + + return false; +} + +/* + ======================================================================================================================= + PATH LINES £ + DrawPathLines Draws connections between entities. Needs to consider all entities, not just ones on screen, because + the lines can be visible when neither end is. Called for both camera view and xy view. + ======================================================================================================================= + */ +void DrawPathLines(void) { + int i, k; + idVec3 mid, mid1; + entity_t *se, *te; + brush_t *sb, *tb; + const char *psz; + idVec3 dir, s1, s2; + float len, f; + int arrows; + int num_entities; + const char *ent_target[MAX_MAP_ENTITIES]; + entity_t *ent_entity[MAX_MAP_ENTITIES]; + + if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_PATHS) { + return; + } + + num_entities = 0; + for (te = entities.next; te != &entities && num_entities != MAX_MAP_ENTITIES; te = te->next) { + for (int i = 0; i < 2048; i++) { + if (i == 0) { + ent_target[num_entities] = ValueForKey(te, "target"); + } else { + ent_target[num_entities] = ValueForKey(te, va("target%i", i)); + } + if (ent_target[num_entities][0]) { + ent_entity[num_entities] = te; + num_entities++; + } else if (i > 16) { + break; + } + } + } + + for (se = entities.next; se != &entities; se = se->next) { + psz = ValueForKey(se, "name"); + + if (psz == NULL || psz[0] == '\0') { + continue; + } + + sb = se->brushes.onext; + if (sb == &se->brushes) { + continue; + } + + for (k = 0; k < num_entities; k++) { + if (strcmp(ent_target[k], psz)) { + continue; + } + + te = ent_entity[k]; + tb = te->brushes.onext; + if (tb == &te->brushes) { + continue; + } + + mid = sb->owner->origin; + mid1 = tb->owner->origin; + + VectorSubtract(mid1, mid, dir); + len = dir.Normalize(); + s1[0] = -dir[1] * 8 + dir[0] * 8; + s2[0] = dir[1] * 8 + dir[0] * 8; + s1[1] = dir[0] * 8 + dir[1] * 8; + s2[1] = -dir[0] * 8 + dir[1] * 8; + + qglColor3f(se->eclass->color[0], se->eclass->color[1], se->eclass->color[2]); + + qglBegin(GL_LINES); + qglVertex3fv(mid.ToFloatPtr()); + qglVertex3fv(mid1.ToFloatPtr()); + + arrows = (int)(len / 256) + 1; + + for (i = 0; i < arrows; i++) { + f = len * (i + 0.5) / arrows; + + mid1 = mid + (f * dir); + + qglVertex3fv(mid1.ToFloatPtr()); + qglVertex3f(mid1[0] + s1[0], mid1[1] + s1[1], mid1[2]); + qglVertex3fv(mid1.ToFloatPtr()); + qglVertex3f(mid1[0] + s2[0], mid1[1] + s2[1], mid1[2]); + } + + qglEnd(); + } + } + + return; +} + +// +// ======================================================================================================================= +// can be greatly simplified but per usual i am in a hurry which is not an excuse, just a fact +// ======================================================================================================================= +// +void CXYWnd::PaintSizeInfo(int nDim1, int nDim2, idVec3 vMinBounds, idVec3 vMaxBounds) { + idVec3 vSize; + VectorSubtract(vMaxBounds, vMinBounds, vSize); + + qglColor3f + ( + g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][0] * .65, + g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][1] * .65, + g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][2] * .65 + ); + + if (m_nViewType == XY) { + qglBegin(GL_LINES); + + qglVertex3f(vMinBounds[nDim1], vMinBounds[nDim2] - 6.0f / m_fScale, 0.0f); + qglVertex3f(vMinBounds[nDim1], vMinBounds[nDim2] - 10.0f / m_fScale, 0.0f); + + qglVertex3f(vMinBounds[nDim1], vMinBounds[nDim2] - 10.0f / m_fScale, 0.0f); + qglVertex3f(vMaxBounds[nDim1], vMinBounds[nDim2] - 10.0f / m_fScale, 0.0f); + + qglVertex3f(vMaxBounds[nDim1], vMinBounds[nDim2] - 6.0f / m_fScale, 0.0f); + qglVertex3f(vMaxBounds[nDim1], vMinBounds[nDim2] - 10.0f / m_fScale, 0.0f); + + qglVertex3f(vMaxBounds[nDim1] + 6.0f / m_fScale, vMinBounds[nDim2], 0.0f); + qglVertex3f(vMaxBounds[nDim1] + 10.0f / m_fScale, vMinBounds[nDim2], 0.0f); + + qglVertex3f(vMaxBounds[nDim1] + 10.0f / m_fScale, vMinBounds[nDim2], 0.0f); + qglVertex3f(vMaxBounds[nDim1] + 10.0f / m_fScale, vMaxBounds[nDim2], 0.0f); + + qglVertex3f(vMaxBounds[nDim1] + 6.0f / m_fScale, vMaxBounds[nDim2], 0.0f); + qglVertex3f(vMaxBounds[nDim1] + 10.0f / m_fScale, vMaxBounds[nDim2], 0.0f); + + qglEnd(); + + qglRasterPos3f(Betwixt(vMinBounds[nDim1], vMaxBounds[nDim1]), vMinBounds[nDim2] - 20.0 / m_fScale, 0.0f); + g_strDim.Format(g_pDimStrings[nDim1], vSize[nDim1]); + qglCallLists(g_strDim.GetLength(), GL_UNSIGNED_BYTE, g_strDim); + + qglRasterPos3f(vMaxBounds[nDim1] + 16.0 / m_fScale, Betwixt(vMinBounds[nDim2], vMaxBounds[nDim2]), 0.0f); + g_strDim.Format(g_pDimStrings[nDim2], vSize[nDim2]); + qglCallLists(g_strDim.GetLength(), GL_UNSIGNED_BYTE, g_strDim); + + qglRasterPos3f(vMinBounds[nDim1] + 4, vMaxBounds[nDim2] + 8 / m_fScale, 0.0f); + g_strDim.Format(g_pOrgStrings[0], vMinBounds[nDim1], vMaxBounds[nDim2]); + qglCallLists(g_strDim.GetLength(), GL_UNSIGNED_BYTE, g_strDim); + } + else if (m_nViewType == XZ) { + qglBegin(GL_LINES); + + qglVertex3f(vMinBounds[nDim1], 0, vMinBounds[nDim2] - 6.0f / m_fScale); + qglVertex3f(vMinBounds[nDim1], 0, vMinBounds[nDim2] - 10.0f / m_fScale); + + qglVertex3f(vMinBounds[nDim1], 0, vMinBounds[nDim2] - 10.0f / m_fScale); + qglVertex3f(vMaxBounds[nDim1], 0, vMinBounds[nDim2] - 10.0f / m_fScale); + + qglVertex3f(vMaxBounds[nDim1], 0, vMinBounds[nDim2] - 6.0f / m_fScale); + qglVertex3f(vMaxBounds[nDim1], 0, vMinBounds[nDim2] - 10.0f / m_fScale); + + qglVertex3f(vMaxBounds[nDim1] + 6.0f / m_fScale, 0, vMinBounds[nDim2]); + qglVertex3f(vMaxBounds[nDim1] + 10.0f / m_fScale, 0, vMinBounds[nDim2]); + + qglVertex3f(vMaxBounds[nDim1] + 10.0f / m_fScale, 0, vMinBounds[nDim2]); + qglVertex3f(vMaxBounds[nDim1] + 10.0f / m_fScale, 0, vMaxBounds[nDim2]); + + qglVertex3f(vMaxBounds[nDim1] + 6.0f / m_fScale, 0, vMaxBounds[nDim2]); + qglVertex3f(vMaxBounds[nDim1] + 10.0f / m_fScale, 0, vMaxBounds[nDim2]); + + qglEnd(); + + qglRasterPos3f(Betwixt(vMinBounds[nDim1], vMaxBounds[nDim1]), 0, vMinBounds[nDim2] - 20.0 / m_fScale); + g_strDim.Format(g_pDimStrings[nDim1], vSize[nDim1]); + qglCallLists(g_strDim.GetLength(), GL_UNSIGNED_BYTE, g_strDim); + + qglRasterPos3f(vMaxBounds[nDim1] + 16.0 / m_fScale, 0, Betwixt(vMinBounds[nDim2], vMaxBounds[nDim2])); + g_strDim.Format(g_pDimStrings[nDim2], vSize[nDim2]); + qglCallLists(g_strDim.GetLength(), GL_UNSIGNED_BYTE, g_strDim); + + qglRasterPos3f(vMinBounds[nDim1] + 4, 0, vMaxBounds[nDim2] + 8 / m_fScale); + g_strDim.Format(g_pOrgStrings[1], vMinBounds[nDim1], vMaxBounds[nDim2]); + qglCallLists(g_strDim.GetLength(), GL_UNSIGNED_BYTE, g_strDim); + } + else { + qglBegin(GL_LINES); + + qglVertex3f(0, vMinBounds[nDim1], vMinBounds[nDim2] - 6.0f / m_fScale); + qglVertex3f(0, vMinBounds[nDim1], vMinBounds[nDim2] - 10.0f / m_fScale); + + qglVertex3f(0, vMinBounds[nDim1], vMinBounds[nDim2] - 10.0f / m_fScale); + qglVertex3f(0, vMaxBounds[nDim1], vMinBounds[nDim2] - 10.0f / m_fScale); + + qglVertex3f(0, vMaxBounds[nDim1], vMinBounds[nDim2] - 6.0f / m_fScale); + qglVertex3f(0, vMaxBounds[nDim1], vMinBounds[nDim2] - 10.0f / m_fScale); + + qglVertex3f(0, vMaxBounds[nDim1] + 6.0f / m_fScale, vMinBounds[nDim2]); + qglVertex3f(0, vMaxBounds[nDim1] + 10.0f / m_fScale, vMinBounds[nDim2]); + + qglVertex3f(0, vMaxBounds[nDim1] + 10.0f / m_fScale, vMinBounds[nDim2]); + qglVertex3f(0, vMaxBounds[nDim1] + 10.0f / m_fScale, vMaxBounds[nDim2]); + + qglVertex3f(0, vMaxBounds[nDim1] + 6.0f / m_fScale, vMaxBounds[nDim2]); + qglVertex3f(0, vMaxBounds[nDim1] + 10.0f / m_fScale, vMaxBounds[nDim2]); + + qglEnd(); + + qglRasterPos3f(0, Betwixt(vMinBounds[nDim1], vMaxBounds[nDim1]), vMinBounds[nDim2] - 20.0 / m_fScale); + g_strDim.Format(g_pDimStrings[nDim1], vSize[nDim1]); + qglCallLists(g_strDim.GetLength(), GL_UNSIGNED_BYTE, g_strDim); + + qglRasterPos3f(0, vMaxBounds[nDim1] + 16.0 / m_fScale, Betwixt(vMinBounds[nDim2], vMaxBounds[nDim2])); + g_strDim.Format(g_pDimStrings[nDim2], vSize[nDim2]); + qglCallLists(g_strDim.GetLength(), GL_UNSIGNED_BYTE, g_strDim); + + qglRasterPos3f(0, vMinBounds[nDim1] + 4.0, vMaxBounds[nDim2] + 8 / m_fScale); + g_strDim.Format(g_pOrgStrings[2], vMinBounds[nDim1], vMaxBounds[nDim2]); + qglCallLists(g_strDim.GetLength(), GL_UNSIGNED_BYTE, g_strDim); + } +} + +/* XY_Draw */ +long g_lCount = 0; +long g_lTotal = 0; +extern void DrawBrushEntityName(brush_t *b); + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::XY_Draw() { + brush_t *brush; + float w, h; + entity_t *e; + idVec3 mins, maxs; + int drawn, culled; + int i; + + if (!active_brushes.next) { + return; // not valid yet + } + + // clear + m_bDirty = false; + + GL_State( GLS_DEFAULT ); + qglViewport(0, 0, m_nWidth, m_nHeight); + qglScissor(0, 0, m_nWidth, m_nHeight); + qglClearColor + ( + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][0], + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][1], + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][2], + 0 + ); + + qglDisable(GL_DEPTH_TEST); + qglDisable(GL_CULL_FACE); + qglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // set up viewpoint + qglMatrixMode(GL_PROJECTION); + qglLoadIdentity(); + + w = m_nWidth / 2 / m_fScale; + h = m_nHeight / 2 / m_fScale; + + int nDim1 = (m_nViewType == YZ) ? 1 : 0; + int nDim2 = (m_nViewType == XY) ? 1 : 2; + mins[0] = m_vOrigin[nDim1] - w; + maxs[0] = m_vOrigin[nDim1] + w; + mins[1] = m_vOrigin[nDim2] - h; + maxs[1] = m_vOrigin[nDim2] + h; + + idBounds viewBounds( mins, maxs ); + viewBounds[0].z = -99999; + viewBounds[1].z = 99999; + + qglOrtho(mins[0], maxs[0], mins[1], maxs[1], MIN_WORLD_COORD, MAX_WORLD_COORD); + + // draw stuff + globalImages->BindNull(); + // now draw the grid + qglLineWidth(0.25); + XY_DrawGrid(); + qglLineWidth(0.5); + + drawn = culled = 0; + + if (m_nViewType != XY) { + qglPushMatrix(); + if (m_nViewType == YZ) { + qglRotatef(-90, 0, 1, 0); // put Z going up + } + + // else + qglRotatef(-90, 1, 0, 0); // put Z going up + } + + e = world_entity; + + for ( brush = active_brushes.next; brush != &active_brushes; brush = brush->next ) { + if ( brush->forceVisibile || ( brush->owner->eclass->nShowFlags & ( ECLASS_LIGHT | ECLASS_PROJECTEDLIGHT ) ) ) { + } else if ( brush->mins[nDim1] > maxs[0] || brush->mins[nDim2] > maxs[1] || brush->maxs[nDim1] < mins[0] || brush->maxs[nDim2] < mins[1] ) { + culled++; + continue; // off screen + } + + if ( FilterBrush(brush) ) { + continue; + } + + drawn++; + + if (brush->owner != e && brush->owner) { + qglColor3fv(brush->owner->eclass->color.ToFloatPtr()); + } + else { + qglColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_BRUSHES].ToFloatPtr()); + } + + Brush_DrawXY( brush, m_nViewType ); + } + + DrawPathLines(); + + // draw pointfile + if (g_qeglobals.d_pointfile_display_list) { + qglCallList(g_qeglobals.d_pointfile_display_list); + } + + if (!(m_nViewType == XY)) { + qglPopMatrix(); + } + + // draw block grid + if (g_qeglobals.show_blocks) { + XY_DrawBlockGrid(); + } + + // now draw selected brushes + if (m_nViewType != XY) { + qglPushMatrix(); + if (m_nViewType == YZ) { + qglRotatef(-90, 0, 1, 0); // put Z going up + } + + // else + qglRotatef(-90, 1, 0, 0); // put Z going up + } + + qglPushMatrix(); + qglTranslatef + ( + g_qeglobals.d_select_translate[0], + g_qeglobals.d_select_translate[1], + g_qeglobals.d_select_translate[2] + ); + + if (RotateMode()) { + qglColor3f( 0.8f, 0.1f, 0.9f ); + } + else if (ScaleMode()) { + qglColor3f( 0.1f, 0.8f, 0.1f ); + } + else { + qglColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES].ToFloatPtr()); + } + + if (g_PrefsDlg.m_bNoStipple == FALSE) { + qglEnable(GL_LINE_STIPPLE); + qglLineStipple(3, 0xaaaa); + } + + qglLineWidth(1); + + idVec3 vMinBounds; + idVec3 vMaxBounds; + vMinBounds[0] = vMinBounds[1] = vMinBounds[2] = 999999.9f; + vMaxBounds[0] = vMaxBounds[1] = vMaxBounds[2] = -999999.9f; + + int nSaveDrawn = drawn; + bool bFixedSize = false; + for (brush = selected_brushes.next; brush != &selected_brushes; brush = brush->next) { + drawn++; + Brush_DrawXY(brush, m_nViewType, true); + + if (!bFixedSize) { + if (brush->owner->eclass->fixedsize) { + bFixedSize = true; + } + + if (g_PrefsDlg.m_bSizePaint) { + for (i = 0; i < 3; i++) { + if (brush->mins[i] < vMinBounds[i]) { + vMinBounds[i] = brush->mins[i]; + } + + if (brush->maxs[i] > vMaxBounds[i]) { + vMaxBounds[i] = brush->maxs[i]; + } + } + } + } + } + + if (g_PrefsDlg.m_bNoStipple == FALSE) { + qglDisable(GL_LINE_STIPPLE); + } + + qglLineWidth(0.5); + + if (!bFixedSize && !RotateMode() && !ScaleMode() && drawn - nSaveDrawn > 0 && g_PrefsDlg.m_bSizePaint) { + PaintSizeInfo(nDim1, nDim2, vMinBounds, vMaxBounds); + } + + // edge / vertex flags + if (g_qeglobals.d_select_mode == sel_vertex) { + qglPointSize(4); + qglColor3f(0, 1, 0); + qglBegin(GL_POINTS); + for (i = 0; i < g_qeglobals.d_numpoints; i++) { + qglVertex3fv(g_qeglobals.d_points[i].ToFloatPtr()); + } + + qglEnd(); + qglPointSize(1); + } + else if (g_qeglobals.d_select_mode == sel_edge) { + float *v1, *v2; + + qglPointSize(4); + qglColor3f(0, 0, 1); + qglBegin(GL_POINTS); + for (i = 0; i < g_qeglobals.d_numedges; i++) { + v1 = g_qeglobals.d_points[g_qeglobals.d_edges[i].p1].ToFloatPtr(); + v2 = g_qeglobals.d_points[g_qeglobals.d_edges[i].p2].ToFloatPtr(); + qglVertex3f((v1[0] + v2[0]) * 0.5, (v1[1] + v2[1]) * 0.5, (v1[2] + v2[2]) * 0.5); + } + + qglEnd(); + qglPointSize(1); + } + + g_splineList->draw (static_cast(g_qeglobals.d_select_mode == sel_editpoint || g_qeglobals.d_select_mode == sel_addpoint)); + + if (g_pParentWnd->GetNurbMode() && g_pParentWnd->GetNurb()->GetNumValues()) { + int maxage = g_pParentWnd->GetNurb()->GetNumValues(); + int time = 0; + qglColor3f(0, 0, 1); + qglPointSize(1); + qglBegin(GL_POINTS); + g_pParentWnd->GetNurb()->SetOrder(3); + for (i = 0; i < 100; i++) { + idVec2 v = g_pParentWnd->GetNurb()->GetCurrentValue(time); + qglVertex3f(v.x, v.y, 0.0f); + time += 10; + } + qglEnd(); + qglPointSize(4); + qglColor3f(0, 0, 1); + qglBegin(GL_POINTS); + for (i = 0; i < maxage; i++) { + idVec2 v = g_pParentWnd->GetNurb()->GetValue(i); + qglVertex3f(v.x, v.y, 0.0f); + } + qglEnd(); + qglPointSize(1); + } + + qglPopMatrix(); + + qglTranslatef + ( + -g_qeglobals.d_select_translate[0], + -g_qeglobals.d_select_translate[1], + -g_qeglobals.d_select_translate[2] + ); + + if (!(m_nViewType == XY)) { + qglPopMatrix(); + } + + // area selection hack + if (g_qeglobals.d_select_mode == sel_area) { + qglEnable(GL_BLEND); + qglPolygonMode ( GL_FRONT_AND_BACK , GL_FILL ); + qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + qglColor4f(0.0, 0.0, 1.0, 0.25); + qglRectf + ( + g_qeglobals.d_vAreaTL[nDim1], + g_qeglobals.d_vAreaTL[nDim2], + g_qeglobals.d_vAreaBR[nDim1], + g_qeglobals.d_vAreaBR[nDim2] + ); + qglDisable(GL_BLEND); + qglPolygonMode ( GL_FRONT_AND_BACK , GL_LINE ); + qglColor3f(1.0f, 1.0f, 1.0f); + qglRectf + ( + g_qeglobals.d_vAreaTL[nDim1], + g_qeglobals.d_vAreaTL[nDim2], + g_qeglobals.d_vAreaBR[nDim1], + g_qeglobals.d_vAreaBR[nDim2] + ); + + } + + // now draw camera point + DrawCameraIcon(); + DrawZIcon(); + + if (RotateMode()) { + DrawRotateIcon(); + } + + /// Draw a "precision crosshair" if enabled + if( m_precisionCrosshairMode != PRECISION_CROSSHAIR_NONE ) + DrawPrecisionCrosshair(); + + qglFlush(); + + // QE_CheckOpenGLForErrors(); +} + + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +idVec3 &CXYWnd::GetOrigin() { + return m_vOrigin; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::SetOrigin(idVec3 org) { + m_vOrigin[0] = org[0]; + m_vOrigin[1] = org[1]; + m_vOrigin[2] = org[2]; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::OnSize(UINT nType, int cx, int cy) { + CWnd::OnSize(nType, cx, cy); + + CRect rect; + GetClientRect(rect); + m_nWidth = rect.Width(); + m_nHeight = rect.Height(); + InvalidateRect(NULL, false); +} + +brush_t hold_brushes; + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::Clip() { + if (ClipMode()) { + hold_brushes.next = &hold_brushes; + ProduceSplitLists(); + + // brush_t* pList = (g_bSwitch) ? &g_brFrontSplits : &g_brBackSplits; + brush_t *pList; + if (g_PrefsDlg.m_bSwitchClip) { + pList = ((m_nViewType == XZ) ? g_bSwitch : !g_bSwitch) ? &g_brFrontSplits : &g_brBackSplits; + } + else { + pList = ((m_nViewType == XZ) ? !g_bSwitch : g_bSwitch) ? &g_brFrontSplits : &g_brBackSplits; + } + + if (pList->next != pList) { + Brush_CopyList(pList, &hold_brushes); + CleanList(&g_brFrontSplits); + CleanList(&g_brBackSplits); + Select_Delete(); + Brush_CopyList(&hold_brushes, &selected_brushes); + if (RogueClipMode()) { + RetainClipMode(false); + } + else { + RetainClipMode(true); + } + + Sys_UpdateWindows(W_ALL); + } + } + else if (PathMode()) { + FinishSmartCreation(); + if (g_pPathFunc) { + g_pPathFunc(true, g_nPathCount); + } + + g_pPathFunc = NULL; + g_nPathCount = 0; + g_bPathMode = false; + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::SplitClip() { + ProduceSplitLists(); + if ((g_brFrontSplits.next != &g_brFrontSplits) && (g_brBackSplits.next != &g_brBackSplits)) { + Select_Delete(); + Brush_CopyList(&g_brFrontSplits, &selected_brushes); + Brush_CopyList(&g_brBackSplits, &selected_brushes); + CleanList(&g_brFrontSplits); + CleanList(&g_brBackSplits); + if (RogueClipMode()) { + RetainClipMode(false); + } + else { + RetainClipMode(true); + } + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::FlipClip() { + g_bSwitch = !g_bSwitch; + Sys_UpdateWindows(XY | W_CAMERA_IFON); +} + +// +// ======================================================================================================================= +// makes sure the selected brush or camera is in view +// ======================================================================================================================= +// +void CXYWnd::PositionView() { + int nDim1 = (m_nViewType == YZ) ? 1 : 0; + int nDim2 = (m_nViewType == XY) ? 1 : 2; + brush_t *b = selected_brushes.next; + if (b && b->next != b) { + m_vOrigin[nDim1] = b->mins[nDim1]; + m_vOrigin[nDim2] = b->mins[nDim2]; + } + else { + m_vOrigin[nDim1] = g_pParentWnd->GetCamera()->Camera().origin[nDim1]; + m_vOrigin[nDim2] = g_pParentWnd->GetCamera()->Camera().origin[nDim2]; + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::VectorCopyXY(const idVec3 &in, idVec3 &out) { + if (m_nViewType == XY) { + out[0] = in[0]; + out[1] = in[1]; + } + else if (m_nViewType == XZ) { + out[0] = in[0]; + out[2] = in[2]; + } + else { + out[1] = in[1]; + out[2] = in[2]; + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::OnDestroy() { + CWnd::OnDestroy(); + + // delete this; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::SetViewType(int n) { + m_nViewType = n; + char *p = "YZ Side"; + if (m_nViewType == XY) { + p = "XY Top"; + } else if (m_nViewType == XZ) { + p = "XZ Front"; + } + SetWindowText(p); +}; + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::Redraw(unsigned int nBits) { + m_nUpdateBits = nBits; + RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW); + m_nUpdateBits = W_XY; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +bool CXYWnd::RotateMode() { + return g_bRotateMode; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +bool CXYWnd::ScaleMode() { + return g_bScaleMode; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +extern bool Select_OnlyModelsSelected(); +bool CXYWnd::SetRotateMode(bool bMode) { + if (bMode && selected_brushes.next != &selected_brushes) { + g_bRotateMode = true; + if (Select_OnlyModelsSelected()) { + Select_GetTrueMid(g_vRotateOrigin); + } else { + Select_GetMid(g_vRotateOrigin); + } + g_vRotation.Zero(); + Select_InitializeRotation(); + } + else { + if (bMode) { + Sys_Status("Need a brush selected to turn on Mouse Rotation mode\n"); + } + + g_bRotateMode = false; + Select_FinalizeRotation(); + } + + RedrawWindow(); + return g_bRotateMode; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::SetScaleMode(bool bMode) { + g_bScaleMode = bMode; + RedrawWindow(); +} + +// +// ======================================================================================================================= +// xy - z xz - y yz - x +// ======================================================================================================================= +// +void CXYWnd::OnSelectMouserotate() { + // TODO: Add your command handler code here +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CleanCopyEntities() { + entity_t *pe = g_enClipboard.next; + while (pe != NULL && pe != &g_enClipboard) { + entity_t *next = pe->next; + pe->epairs.Clear(); + + Entity_Free(pe); + pe = next; + } + + g_enClipboard.next = g_enClipboard.prev = &g_enClipboard; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +entity_t *Entity_CopyClone(entity_t *e) { + entity_t *n; + + n = Entity_New(); + n->brushes.onext = n->brushes.oprev = &n->brushes; + n->eclass = e->eclass; + n->rotation = e->rotation; + + // add the entity to the entity list + n->next = g_enClipboard.next; + g_enClipboard.next = n; + n->next->prev = n; + n->prev = &g_enClipboard; + + n->epairs = e->epairs; + + return n; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +bool OnList(entity_t *pFind, CPtrArray *pList) { + int nSize = pList->GetSize(); + while (nSize-- > 0) { + entity_t *pEntity = reinterpret_cast < entity_t * > (pList->GetAt(nSize)); + if (pEntity == pFind) { + return true; + } + } + + return false; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::Copy() +{ +#if 1 + CWaitCursor WaitCursor; + g_Clipboard.SetLength(0); + g_PatchClipboard.SetLength(0); + + Map_SaveSelected(&g_Clipboard, &g_PatchClipboard); + + bool bClipped = false; + UINT nClipboard = ::RegisterClipboardFormat("RadiantClippings"); + if (nClipboard > 0) { + if (OpenClipboard()) { + ::EmptyClipboard(); + + long lSize = g_Clipboard.GetLength(); + HANDLE h = ::GlobalAlloc(GMEM_ZEROINIT | GMEM_MOVEABLE | GMEM_DDESHARE, lSize + sizeof (long)); + if (h != NULL) { + unsigned char *cp = reinterpret_cast < unsigned char * > (::GlobalLock(h)); + memcpy(cp, &lSize, sizeof (long)); + cp += sizeof (long); + g_Clipboard.SeekToBegin(); + g_Clipboard.Read(cp, lSize); + ::GlobalUnlock(h); + ::SetClipboardData(nClipboard, h); + ::CloseClipboard(); + bClipped = true; + } + } + } + + if (!bClipped) { + common->Printf("Unable to register Windows clipboard formats, copy/paste between editors will not be possible"); + } + + /* + * CString strOut; ::GetTempPath(1024, strOut.GetBuffer(1024)); + * strOut.ReleaseBuffer(); AddSlash(strOut); strOut += "RadiantClipboard.$$$"; + * Map_SaveSelected(strOut.GetBuffer(0)); + */ +#else + CPtrArray holdArray; + CleanList(&g_brClipboard); + CleanCopyEntities(); + for (brush_t * pBrush = selected_brushes.next; pBrush != NULL && pBrush != &selected_brushes; pBrush = pBrush->next) { + if (pBrush->owner == world_entity) { + brush_t *pClone = Brush_Clone(pBrush); + pClone->owner = NULL; + Brush_AddToList(pClone, &g_brClipboard); + } + else { + if (!OnList(pBrush->owner, &holdArray)) { + entity_t *e = pBrush->owner; + holdArray.Add(reinterpret_cast < void * > (e)); + + entity_t *pEClone = Entity_CopyClone(e); + for (brush_t * pEB = e->brushes.onext; pEB != &e->brushes; pEB = pEB->onext) { + brush_t *pClone = Brush_Clone(pEB); + + // Brush_AddToList (pClone, &g_brClipboard); + Entity_LinkBrush(pEClone, pClone); + Brush_Build(pClone); + } + } + } + } +#endif +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::Undo() { + /* + * if (g_brUndo.next != &g_brUndo) { g_bScreenUpdates = false; Select_Delete(); + * for (brush_t* pBrush = g_brUndo.next ; pBrush != NULL && pBrush != &g_brUndo ; + * pBrush=pBrush->next) { brush_t* pClone = Brush_Clone(pBrush); Brush_AddToList + * (pClone, &active_brushes); Entity_LinkBrush (pBrush->pUndoOwner, pClone); + * Brush_Build(pClone); Select_Brush(pClone); } CleanList(&g_brUndo); + * g_bScreenUpdates = true; Sys_UpdateWindows(W_ALL); } else common->Printf("Nothing + * to undo.../n"); + */ +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::UndoClear() { + /* CleanList(&g_brUndo); */ +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::UndoCopy() { + /* + * CleanList(&g_brUndo); for (brush_t* pBrush = selected_brushes.next ; pBrush != + * NULL && pBrush != &selected_brushes ; pBrush=pBrush->next) { brush_t* pClone = + * Brush_Clone(pBrush); pClone->pUndoOwner = pBrush->owner; Brush_AddToList + * (pClone, &g_brUndo); } + */ +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +bool CXYWnd::UndoAvailable() { + return(g_brUndo.next != &g_brUndo); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::Paste() +{ +#if 1 + + CWaitCursor WaitCursor; + bool bPasted = false; + UINT nClipboard = ::RegisterClipboardFormat("RadiantClippings"); + if (nClipboard > 0 && OpenClipboard() && ::IsClipboardFormatAvailable(nClipboard)) { + HANDLE h = ::GetClipboardData(nClipboard); + if (h) { + g_Clipboard.SetLength(0); + + unsigned char *cp = reinterpret_cast < unsigned char * > (::GlobalLock(h)); + long lSize = 0; + memcpy(&lSize, cp, sizeof (long)); + cp += sizeof (long); + g_Clipboard.Write(cp, lSize); + } + + ::GlobalUnlock(h); + ::CloseClipboard(); + } + + if (g_Clipboard.GetLength() > 0) { + g_Clipboard.SeekToBegin(); + + int nLen = g_Clipboard.GetLength(); + char *pBuffer = new char[nLen + 1]; + memset(pBuffer, 0, sizeof(pBuffer)); + g_Clipboard.Read(pBuffer, nLen); + pBuffer[nLen] = '\0'; + Map_ImportBuffer(pBuffer, !(GetAsyncKeyState(VK_SHIFT) & 0x8000)); + delete[] pBuffer; + } + + #if 0 + if (g_PatchClipboard.GetLength() > 0) { + g_PatchClipboard.SeekToBegin(); + + int nLen = g_PatchClipboard.GetLength(); + char *pBuffer = new char[nLen + 1]; + g_PatchClipboard.Read(pBuffer, nLen); + pBuffer[nLen] = '\0'; + Patch_ReadBuffer(pBuffer, true); + delete[] pBuffer; + } + #endif +#else + if (g_brClipboard.next != &g_brClipboard || g_enClipboard.next != &g_enClipboard) { + Select_Deselect(); + + for (brush_t * pBrush = g_brClipboard.next; pBrush != NULL && pBrush != &g_brClipboard; pBrush = pBrush->next) { + brush_t *pClone = Brush_Clone(pBrush); + + // pClone->owner = pBrush->owner; + if (pClone->owner == NULL) { + Entity_LinkBrush(world_entity, pClone); + } + + Brush_AddToList(pClone, &selected_brushes); + Brush_Build(pClone); + } + + for + ( + entity_t * pEntity = g_enClipboard.next; + pEntity != NULL && pEntity != &g_enClipboard; + pEntity = pEntity->next + ) { + entity_t *pEClone = Entity_Clone(pEntity); + for (brush_t * pEB = pEntity->brushes.onext; pEB != &pEntity->brushes; pEB = pEB->onext) { + brush_t *pClone = Brush_Clone(pEB); + Brush_AddToList(pClone, &selected_brushes); + Entity_LinkBrush(pEClone, pClone); + Brush_Build(pClone); + if (pClone->owner && pClone->owner != world_entity) { + g_Inspectors->UpdateEntitySel(pClone->owner->eclass); + } + } + } + + Sys_UpdateWindows(W_ALL); + } + else { + common->Printf("Nothing to paste.../n"); + } +#endif +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +idVec3 &CXYWnd::Rotation() { + return g_vRotation; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +idVec3 &CXYWnd::RotateOrigin() { + return g_vRotateOrigin; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::OnTimer(UINT nIDEvent) { + if (nIDEvent == 100) { + int nDim1 = (m_nViewType == YZ) ? 1 : 0; + int nDim2 = (m_nViewType == XY) ? 1 : 2; + m_vOrigin[nDim1] += m_ptDragAdj.x / m_fScale; + m_vOrigin[nDim2] -= m_ptDragAdj.y / m_fScale; + Sys_UpdateWindows(W_XY | W_CAMERA); + + // int nH = (m_ptDrag.y == 0) ? -1 : m_ptDrag.y; + m_ptDrag += m_ptDragAdj; + m_ptDragTotal += m_ptDragAdj; + XY_MouseMoved(m_ptDrag.x, m_nHeight - 1 - m_ptDrag.y, m_nScrollFlags); + + // + // m_vOrigin[nDim1] -= m_ptDrag.x / m_fScale; m_vOrigin[nDim1] -= m_ptDrag.x / + // m_fScale; + // + } +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) { + g_pParentWnd->HandleKey(nChar, nRepCnt, nFlags, false); + + // CWnd::OnKeyUp(nChar, nRepCnt, nFlags); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS FAR *lpncsp) { + CWnd::OnNcCalcSize(bCalcValidRects, lpncsp); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::OnKillFocus(CWnd *pNewWnd) { + CWnd::OnKillFocus(pNewWnd); + SendMessage(WM_NCACTIVATE, FALSE, 0); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::OnSetFocus(CWnd *pOldWnd) { + CWnd::OnSetFocus(pOldWnd); + SendMessage(WM_NCACTIVATE, TRUE, 0); +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void CXYWnd::OnClose() { + CWnd::OnClose(); +} + +// +// ======================================================================================================================= +// should be static as should be the rotate scale stuff +// ======================================================================================================================= +// +bool CXYWnd::AreaSelectOK() { + return RotateMode() ? false : ScaleMode() ? false : true; +} + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +BOOL CXYWnd::OnEraseBkgnd(CDC *pDC) { + return TRUE; + + // return CWnd::OnEraseBkgnd(pDC); +} + +extern void AssignModel(); +void CXYWnd::OnDropNewmodel() +{ + CPoint point; + GetCursorPos(&point); + CreateRightClickEntity(this, m_ptDown.x, m_ptDown.y, "func_static"); + g_Inspectors->SetMode(W_ENTITY); + g_Inspectors->AssignModel(); +} + +BOOL CXYWnd::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) +{ + if (zDelta > 0) { + g_pParentWnd->OnViewZoomin(); + } else { + g_pParentWnd->OnViewZoomout(); + } + return TRUE; +} + + + + + //--------------------------------------------------------------------------- + // CyclePrecisionCrosshairMode + // + // Called when the user presses the "cycle precision cursor mode" key. + // Cycles the precision cursor among the following three modes: + // PRECISION_CURSOR_NONE + // PRECISION_CURSOR_SNAP + // PRECISION_CURSOR_FREE + //--------------------------------------------------------------------------- + void CXYWnd::CyclePrecisionCrosshairMode( void ) + { + common->Printf("TODO: Make DrawPrecisionCrosshair work..." ); + + /// Cycle to next mode, wrap if necessary + m_precisionCrosshairMode ++; + if( m_precisionCrosshairMode >= PRECISION_CROSSHAIR_MAX ) + m_precisionCrosshairMode = PRECISION_CROSSHAIR_NONE; + Sys_UpdateWindows( W_XY ); + } + + //--------------------------------------------------------------------------- +// DrawPrecisionCrosshair +// +// Draws a precision crosshair beneath the cursor in the 2d (XY) view, +// depending on one of the following values for m_precisionCrosshairMode: +// +// PRECISION_CROSSHAIR_NONE No crosshair is drawn. Do not force refresh of XY view. +// PRECISION_CROSSHAIR_SNAP Crosshair snaps to grid size. Force refresh of XY view. +// PRECISION_CROSSHAIR_FREE Crosshair does not snap to grid. Force refresh of XY view. +//--------------------------------------------------------------------------- +void CXYWnd::DrawPrecisionCrosshair( void ) +{ + // FIXME: m_mouseX, m_mouseY, m_axisHoriz, m_axisVert, etc... are never set + return; + + idVec3 mouse3dPos (0.0f, 0.0f, 0.0f); + float x, y; + idVec4 crossEndColor (1.0f, 0.0f, 1.0f, 1.0f); // the RGBA color of the precision crosshair at its ends + idVec4 crossMidColor; // the RGBA color of the precision crosshair at the crossing point + + /// Transform the mouse coordinates into axis-correct map-coordinates + if( m_precisionCrosshairMode == PRECISION_CROSSHAIR_SNAP ) + SnapToPoint( m_mouseX, m_mouseY, mouse3dPos ); + else + XY_ToPoint( m_mouseX, m_mouseY, mouse3dPos ); + x = mouse3dPos[ m_axisHoriz ]; + y = mouse3dPos[ m_axisVert ]; + + /// Use the color specified by the user + + crossEndColor[0] = g_qeglobals.d_savedinfo.colors[ COLOR_PRECISION_CROSSHAIR ][0]; + crossEndColor[1] = g_qeglobals.d_savedinfo.colors[ COLOR_PRECISION_CROSSHAIR ][1]; + crossEndColor[2] = g_qeglobals.d_savedinfo.colors[ COLOR_PRECISION_CROSSHAIR ][2]; + crossEndColor[3] = 1.0f; + + crossMidColor = crossEndColor; + + if( m_precisionCrosshairMode == PRECISION_CROSSHAIR_FREE ) + crossMidColor[ 3 ] = 0.0f; // intersection-color is 100% transparent (alpha = 0.0f) + + /// Set up OpenGL states (for drawing smooth-shaded plain-colored lines) + qglEnable( GL_BLEND ); + qglDisable( GL_TEXTURE_2D ); + qglShadeModel( GL_SMOOTH ); + qglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + + /// Draw a fullscreen-sized crosshair over the cursor + qglBegin( GL_LINES ); + { + /// Draw the horizontal precision line (in two pieces) + qglColor4fv( crossEndColor.ToFloatPtr() ); + qglVertex2f( m_mcLeft, y ); + qglColor4fv( crossMidColor.ToFloatPtr() ); + qglVertex2f( x, y ); + qglColor4fv( crossMidColor.ToFloatPtr() ); + qglVertex2f( x, y ); + qglColor4fv( crossEndColor.ToFloatPtr() ); + qglVertex2f( m_mcRight, y ); + + /// Draw the vertical precision line (in two pieces) + qglColor4fv( crossEndColor.ToFloatPtr() ); + qglVertex2f( x, m_mcTop ); + qglColor4fv( crossMidColor.ToFloatPtr() ); + qglVertex2f( x, y ); + qglColor4fv( crossMidColor.ToFloatPtr() ); + qglVertex2f( x, y ); + qglColor4fv( crossEndColor.ToFloatPtr() ); + qglVertex2f( x, m_mcBottom ); + } + qglEnd(); // GL_LINES + + // Radiant was in opaque, flat-shaded mode by default; restore this to prevent possible slowdown + qglShadeModel( GL_FLAT ); + qglDisable( GL_BLEND ); +} diff --git a/src/tools/radiant/XYWnd.h b/src/tools/radiant/XYWnd.h new file mode 100644 index 0000000..bdd36a8 --- /dev/null +++ b/src/tools/radiant/XYWnd.h @@ -0,0 +1,264 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#if !defined(AFX_XYWND_H__44B4BA04_781B_11D1_B53C_00AA00A410FC__INCLUDED_) +#define AFX_XYWND_H__44B4BA04_781B_11D1_B53C_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// XYWnd.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CXYWnd window + +#include "qe3.h" +#include "CamWnd.h" + +const int SCALE_X = 0x01; +const int SCALE_Y = 0x02; +const int SCALE_Z = 0x04; + +bool FilterBrush(brush_t *pb); + +typedef void (PFNPathCallback)(bool, int); +// as i didn't really encapsulate anything this +// should really be a struct.. +class CClipPoint +{ +public: + CClipPoint(){ Reset(); }; + void Reset(){ m_ptClip[0] = m_ptClip[1] = m_ptClip[2] = 0.0; m_bSet = false; m_pVec3 = NULL;}; + bool Set(){ return m_bSet; }; + void Set(bool b) { m_bSet = b; }; + void UpdatePointPtr() { if (m_pVec3) VectorCopy(m_ptClip, *m_pVec3); }; + void SetPointPtr(idVec3* p) { m_pVec3 = p; }; + idVec3 m_ptClip; // the 3d point + idVec3* m_pVec3; // optional ptr for 3rd party updates + CPoint m_ptScreen; // the onscreen xy point (for mousability) + bool m_bSet; + operator idVec3&() {return m_ptClip;}; + operator idVec3*() {return &m_ptClip;}; + operator float*() {return m_ptClip.ToFloatPtr();}; +}; + +class CXYWnd : public CWnd +{ + DECLARE_DYNCREATE(CXYWnd); +// Construction +public: + CXYWnd(); + +// Attributes +public: + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CXYWnd) + protected: + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + //}}AFX_VIRTUAL + +// Implementation +public: + bool AreaSelectOK(); + idVec3& RotateOrigin(); + idVec3& Rotation(); + void UndoClear(); + bool UndoAvailable(); + void KillPathMode(); + void Undo(); + void UndoCopy(); + void Copy(); + void Paste(); + void Redraw(unsigned int nBits); + void VectorCopyXY( const idVec3 &in, idVec3 &out ); + void PositionView(); + void FlipClip(); + void SplitClip(); + void Clip(); + idVec3& GetOrigin(); + void SetOrigin(idVec3 org); // PGM + void XY_Init(); + void XY_Draw(); + void DrawZIcon(); + void DrawRotateIcon(); + void DrawCameraIcon(); + void XY_DrawBlockGrid(); + void XY_DrawGrid(); + bool XY_MouseMoved (int x, int y, int buttons); + void NewBrushDrag (int x, int y); + bool DragDelta (int x, int y, idVec3 &move); + void XY_MouseUp(int x, int y, int buttons); + void XY_MouseDown (int x, int y, int buttons); + void XY_ToGridPoint (int x, int y, idVec3 &point); + void XY_ToPoint (int x, int y, idVec3 &point); + void SnapToPoint (int x, int y, idVec3 &point); + void SetActive(bool b) {m_bActive = b;}; + bool Active() {return m_bActive;}; + void DropClipPoint(UINT nFlags, CPoint point); + + int GetAxisHoriz() { return m_axisHoriz; }; + int GetAxisVert() { return m_axisVert; }; + void AnalogMouseZoom( int mouseDeltaY ); + + + bool RogueClipMode(); + bool ClipMode(); + void SetClipMode(bool bMode); + void RetainClipMode(bool bMode); + + bool RotateMode(); + bool SetRotateMode(bool bMode); + bool ScaleMode(); + void SetScaleMode(bool bMode); + + bool PathMode(); + void DropPathPoint(UINT nFlags, CPoint point); + + bool PointMode(); + void AddPointPoint(UINT nFlags, idVec3* pVec); + void SetPointMode(bool b); + + + virtual ~CXYWnd(); + void SetViewType(int n); + int GetViewType() {return m_nViewType; }; + void SetScale(float f) {m_fScale = f;}; + float Scale() {return m_fScale;}; + int Width() {return m_nWidth;} + int Height() {return m_nHeight;} + bool m_bActive; + + void UpdateViewDependencies( void ); + + void DrawPrecisionCrosshair(); + void CyclePrecisionCrosshairMode(); + enum + { + PRECISION_CROSSHAIR_NONE = 0, + PRECISION_CROSSHAIR_SNAP = 1, + PRECISION_CROSSHAIR_FREE = 2, + PRECISION_CROSSHAIR_MAX, + }; + + int m_precisionCrosshairMode; + int m_mouseX; + int m_mouseY; + + + // Generated message map functions +protected: + int m_nUpdateBits; + int m_nWidth; + int m_nHeight; + float m_fScale; + float m_TopClip; + float m_BottomClip; + bool m_bDirty; + idVec3 m_vOrigin; + CPoint m_ptCursor; + bool m_bRButtonDown; + + int m_nButtonstate; + int m_nPressx; + int m_nPressy; + idVec3 m_vPressdelta; + bool m_bPress_selection; + + int m_axisHoriz; // and are one of AXIS_X, AXIS_Y, AXIS_Z and + int m_axisVert; // reflect which axes are represented horizontally and vertically in the 2d view (XY, XZ, etc) + + /// Each of the following _mc fields are stored in map-coordinates, NOT screen-pixels + float m_mcWidth; + float m_mcHeight; + float m_mcLeft; + float m_mcRight; + float m_mcTop; + float m_mcBottom; + + + friend CCamWnd; + //friend C3DFXCamWnd; + + CMenu m_mnuDrop; + int m_nViewType; + + unsigned int m_nTimerID; + int m_nScrollFlags; + CPoint m_ptDrag; + CPoint m_ptDragAdj; + CPoint m_ptDragTotal; + + void OriginalButtonUp(UINT nFlags, CPoint point); + void OriginalButtonDown(UINT nFlags, CPoint point); + void ProduceSplits(brush_t** pFront, brush_t** pBack); + void ProduceSplitLists(); + void HandleDrop(); + void PaintSizeInfo(int nDim1, int nDim2, idVec3 vMinBounds, idVec3 vMaxBounds); + void DrawSelectedCentroid( int nDim1, int nDim2, idVec3 vMinBounds, idVec3 vMaxBounds ); + + void OnEntityCreate(unsigned int nID); + CPoint m_ptDown; + //{{AFX_MSG(CXYWnd) + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + afx_msg void OnLButtonDown(UINT nFlags, CPoint point); + afx_msg void OnMButtonDown(UINT nFlags, CPoint point); + afx_msg void OnRButtonDown(UINT nFlags, CPoint point); + afx_msg void OnLButtonUp(UINT nFlags, CPoint point); + afx_msg void OnMButtonUp(UINT nFlags, CPoint point); + afx_msg void OnRButtonUp(UINT nFlags, CPoint point); + afx_msg void OnMouseMove(UINT nFlags, CPoint point); + afx_msg void OnPaint(); + afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnDestroy(); + afx_msg void OnSelectMouserotate(); + afx_msg void OnTimer(UINT nIDEvent); + afx_msg void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS FAR* lpncsp); + afx_msg void OnKillFocus(CWnd* pNewWnd); + afx_msg void OnSetFocus(CWnd* pOldWnd); + afx_msg void OnClose(); + afx_msg BOOL OnEraseBkgnd(CDC* pDC); + afx_msg void OnDropNewmodel(); + afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt); + afx_msg BOOL OnCmdMsg( UINT nID, int nCode, void *pExtra, AFX_CMDHANDLERINFO *pHandlerInfo ); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_XYWND_H__44B4BA04_781B_11D1_B53C_00AA00A410FC__INCLUDED_) diff --git a/src/tools/radiant/Z.CPP b/src/tools/radiant/Z.CPP new file mode 100644 index 0000000..69da237 --- /dev/null +++ b/src/tools/radiant/Z.CPP @@ -0,0 +1,492 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" + +#define PAGEFLIPS 2 + +z_t z; + +/* + ======================================================================================================================= + Z_Init + ======================================================================================================================= + */ +void Z_Init(void) { + z.origin[0] = 0; + z.origin[1] = 20; + z.origin[2] = 46; + + z.scale = 1; +} + +/* MOUSE ACTIONS */ +static int cursorx, cursory; + +/* + ======================================================================================================================= + Z_MouseDown + ======================================================================================================================= + */ +void Z_MouseDown(int x, int y, int buttons) { + idVec3 org, dir, vup, vright; + brush_t *b; + + Sys_GetCursorPos(&cursorx, &cursory); + + vup[0] = 0; + vup[1] = 0; + vup[2] = 1 / z.scale; + + VectorCopy(z.origin, org); + org[2] += (y - (z.height / 2)) / z.scale; + org[1] = MIN_WORLD_COORD; + + b = selected_brushes.next; + if (b != &selected_brushes) { + org[0] = (b->mins[0] + b->maxs[0]) / 2; + } + + dir[0] = 0; + dir[1] = 1; + dir[2] = 0; + + vright[0] = 0; + vright[1] = 0; + vright[2] = 0; + + + // new mouse code for ZClip, I'll do this stuff before falling through into the standard ZWindow mouse code... + // + if (g_pParentWnd->GetZWnd()->m_pZClip) // should always be the case I think, but this is safer + { + bool bToggle = false; + bool bSetTop = false; + bool bSetBot = false; + bool bReset = false; + + if (g_PrefsDlg.m_nMouseButtons == 2) + { + // 2 button mice... + // + bToggle = (GetKeyState(VK_F1) & 0x8000) != 0; + bSetTop = (GetKeyState(VK_F2) & 0x8000) != 0; + bSetBot = (GetKeyState(VK_F3) & 0x8000) != 0; + bReset = (GetKeyState(VK_F4) & 0x8000) != 0; + } + else + { + // 3 button mice... + // + bToggle = (buttons == (MK_RBUTTON|MK_SHIFT|MK_CONTROL)); + bSetTop = (buttons == (MK_RBUTTON|MK_SHIFT)); + bSetBot = (buttons == (MK_RBUTTON|MK_CONTROL)); + bReset = (GetKeyState(VK_F4) & 0x8000) != 0; + } + + if (bToggle) + { + g_pParentWnd->GetZWnd()->m_pZClip->Enable(!(g_pParentWnd->GetZWnd()->m_pZClip->IsEnabled())); + Sys_UpdateWindows (W_ALL); + return; + } + + if (bSetTop) + { + g_pParentWnd->GetZWnd()->m_pZClip->SetTop(org[2]); + Sys_UpdateWindows (W_ALL); + return; + } + + if (bSetBot) + { + g_pParentWnd->GetZWnd()->m_pZClip->SetBottom(org[2]); + Sys_UpdateWindows (W_ALL); + return; + } + + if (bReset) + { + g_pParentWnd->GetZWnd()->m_pZClip->Reset(); + Sys_UpdateWindows (W_ALL); + return; + } + } + + // + // LBUTTON = manipulate selection shift-LBUTTON = select middle button = grab + // texture ctrl-middle button = set entire brush to texture ctrl-shift-middle + // button = set single face to texture + // + + // see code above for these next 3, I just commented them here as well for clarity... + // + // ctrl-shift-RIGHT button = toggle ZClip on/off + // shift-RIGHT button = set ZClip top marker + // ctrl-RIGHT button = set ZClip bottom marker + + int nMouseButton = g_PrefsDlg.m_nMouseButtons == 2 ? MK_RBUTTON : MK_MBUTTON; + if + ( + (buttons == MK_LBUTTON) || + (buttons == (MK_LBUTTON | MK_SHIFT)) || + (buttons == MK_MBUTTON) // || (buttons == (MK_MBUTTON|MK_CONTROL)) + || + (buttons == (nMouseButton | MK_SHIFT | MK_CONTROL)) + ) { + Drag_Begin(x, y, buttons, vright, vup, org, dir); + return; + } + + // control mbutton = move camera + if ((buttons == (MK_CONTROL | nMouseButton)) || (buttons == (MK_CONTROL | MK_LBUTTON))) { + g_pParentWnd->GetCamera()->Camera().origin[2] = org[2]; + Sys_UpdateWindows(W_CAMERA | W_XY_OVERLAY | W_Z); + } +} + +/* + ======================================================================================================================= + Z_MouseUp + ======================================================================================================================= + */ +void Z_MouseUp(int x, int y, int buttons) { + Drag_MouseUp(); +} + +/* + ======================================================================================================================= + Z_MouseMoved + ======================================================================================================================= + */ +void Z_MouseMoved(int x, int y, int buttons) { + if (!buttons) { + return; + } + + if (buttons == MK_LBUTTON) { + Drag_MouseMoved(x, y, buttons); + Sys_UpdateWindows(W_Z | W_CAMERA_IFON | W_XY); + return; + } + + // rbutton = drag z origin + if (buttons == MK_RBUTTON) { + Sys_GetCursorPos(&x, &y); + if (y != cursory) { + z.origin[2] += y - cursory; + Sys_SetCursorPos(cursorx, cursory); + Sys_UpdateWindows(W_Z); + } + + return; + } + + // control mbutton = move camera + int nMouseButton = g_PrefsDlg.m_nMouseButtons == 2 ? MK_RBUTTON : MK_MBUTTON; + if ((buttons == (MK_CONTROL | nMouseButton)) || (buttons == (MK_CONTROL | MK_LBUTTON))) { + g_pParentWnd->GetCamera()->Camera().origin[2] = z.origin[2] + (y - (z.height / 2)) / z.scale; + Sys_UpdateWindows(W_CAMERA | W_XY_OVERLAY | W_Z); + } +} + +/* + ======================================================================================================================= + DRAWING £ + Z_DrawGrid + ======================================================================================================================= + */ +void Z_DrawGrid(void) { + float zz, zb, ze; + int w, h; + char text[32]; + + w = z.width / 2 / z.scale; + h = z.height / 2 / z.scale; + + zb = z.origin[2] - h; + if (zb < region_mins[2]) { + zb = region_mins[2]; + } + + zb = 64 * floor(zb / 64); + + ze = z.origin[2] + h; + if (ze > region_maxs[2]) { + ze = region_maxs[2]; + } + + ze = 64 * ceil(ze / 64); + + // draw major blocks + qglColor3fv( g_qeglobals.d_savedinfo.colors[COLOR_GRIDMAJOR].ToFloatPtr() ); + + qglBegin(GL_LINES); + + qglVertex2f(0, zb); + qglVertex2f(0, ze); + + for (zz = zb; zz < ze; zz += 64) { + qglVertex2f(-w, zz); + qglVertex2f(w, zz); + } + + qglEnd(); + + // draw minor blocks + if ( g_qeglobals.d_showgrid && + g_qeglobals.d_gridsize * z.scale >= 4 && + !g_qeglobals.d_savedinfo.colors[COLOR_GRIDMINOR].Compare( g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK] ) ) { + + qglColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_GRIDMINOR].ToFloatPtr()); + + qglBegin(GL_LINES); + for (zz = zb; zz < ze; zz += g_qeglobals.d_gridsize) { + if (!((int)zz & 63)) { + continue; + } + + qglVertex2f(-w, zz); + qglVertex2f(w, zz); + } + + qglEnd(); + } + + // draw coordinate text if needed + qglColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_GRIDTEXT].ToFloatPtr()); + + for (zz = zb; zz < ze; zz += 64) { + qglRasterPos2f(-w + 1, zz); + sprintf(text, "%i", (int)zz); + qglCallLists(strlen(text), GL_UNSIGNED_BYTE, text); + } +} + +#define CAM_HEIGHT 48 // height of main part +#define CAM_GIZMO 8 // height of the gizmo + +/* + ======================================================================================================================= + ======================================================================================================================= + */ +void ZDrawCameraIcon(void) { + float x, y; + int xCam = z.width / 4; + + x = 0; + y = g_pParentWnd->GetCamera()->Camera().origin[2]; + + qglColor3f(0.0, 0.0, 1.0); + qglBegin(GL_LINE_STRIP); + qglVertex3f(x - xCam, y, 0); + qglVertex3f(x, y + CAM_GIZMO, 0); + qglVertex3f(x + xCam, y, 0); + qglVertex3f(x, y - CAM_GIZMO, 0); + qglVertex3f(x - xCam, y, 0); + qglVertex3f(x + xCam, y, 0); + qglVertex3f(x + xCam, y - CAM_HEIGHT, 0); + qglVertex3f(x - xCam, y - CAM_HEIGHT, 0); + qglVertex3f(x - xCam, y, 0); + qglEnd(); +} + +void ZDrawZClip() +{ + float x,y; + + x = 0; + y = g_pParentWnd->GetCamera()->Camera().origin[2]; + + if (g_pParentWnd->GetZWnd()->m_pZClip) // should always be the case I think + g_pParentWnd->GetZWnd()->m_pZClip->Paint(); +} + + +GLbitfield glbitClear = GL_COLOR_BUFFER_BIT; // HACK + +/* + ======================================================================================================================= + Z_Draw + ======================================================================================================================= + */ +void Z_Draw(void) { + brush_t *brush; + float w, h; + float top, bottom; + idVec3 org_top, org_bottom, dir_up, dir_down; + int xCam = z.width / 3; + + if (!active_brushes.next) { + return; // not valid yet + } + + // clear + qglViewport(0, 0, z.width, z.height); + qglScissor(0, 0, z.width, z.height); + + qglClearColor + ( + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][0], + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][1], + g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][2], + 0 + ); + + /* + * GL Bug £ + * When not using hw acceleration, gl will fault if we clear the depth buffer bit + * on the first pass. The hack fix is to set the GL_DEPTH_BUFFER_BIT only after + * Z_Draw() has been called once. Yeah, right. £ + * qglClear(glbitClear); + */ + qglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // + // glbitClear |= GL_DEPTH_BUFFER_BIT; + // qglClear(GL_DEPTH_BUFFER_BIT); + // + qglMatrixMode(GL_PROJECTION); + qglLoadIdentity(); + + w = z.width / 2 / z.scale; + h = z.height / 2 / z.scale; + qglOrtho(-w, w, z.origin[2] - h, z.origin[2] + h, -8, 8); + + globalImages->BindNull(); + qglDisable(GL_DEPTH_TEST); + qglDisable(GL_BLEND); + + // now draw the grid + Z_DrawGrid(); + + // draw stuff + qglDisable(GL_CULL_FACE); + + qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + + globalImages->BindNull(); + + // draw filled interiors and edges + dir_up[0] = 0; + dir_up[1] = 0; + dir_up[2] = 1; + dir_down[0] = 0; + dir_down[1] = 0; + dir_down[2] = -1; + VectorCopy(z.origin, org_top); + org_top[2] = 4096; + VectorCopy(z.origin, org_bottom); + org_bottom[2] = -4096; + + for (brush = active_brushes.next; brush != &active_brushes; brush = brush->next) { + if + ( + brush->mins[0] >= z.origin[0] || + brush->maxs[0] <= z.origin[0] || + brush->mins[1] >= z.origin[1] || + brush->maxs[1] <= z.origin[1] + ) { + continue; + } + + if (!Brush_Ray(org_top, dir_down, brush, &top)) { + continue; + } + + top = org_top[2] - top; + if (!Brush_Ray(org_bottom, dir_up, brush, &bottom)) { + continue; + } + + bottom = org_bottom[2] + bottom; + + //q = declManager->FindMaterial(brush->brush_faces->texdef.name); + qglColor3f(brush->owner->eclass->color.x, brush->owner->eclass->color.y, brush->owner->eclass->color.z); + qglBegin(GL_QUADS); + qglVertex2f(-xCam, bottom); + qglVertex2f(xCam, bottom); + qglVertex2f(xCam, top); + qglVertex2f(-xCam, top); + qglEnd(); + + qglColor3f(1, 1, 1); + qglBegin(GL_LINE_LOOP); + qglVertex2f(-xCam, bottom); + qglVertex2f(xCam, bottom); + qglVertex2f(xCam, top); + qglVertex2f(-xCam, top); + qglEnd(); + } + + // now draw selected brushes + for (brush = selected_brushes.next; brush != &selected_brushes; brush = brush->next) { + if + ( + !( + brush->mins[0] >= z.origin[0] || + brush->maxs[0] <= z.origin[0] || + brush->mins[1] >= z.origin[1] || + brush->maxs[1] <= z.origin[1] + ) + ) { + if (Brush_Ray(org_top, dir_down, brush, &top)) { + top = org_top[2] - top; + if (Brush_Ray(org_bottom, dir_up, brush, &bottom)) { + bottom = org_bottom[2] + bottom; + + //q = declManager->FindMaterial(brush->brush_faces->texdef.name); + qglColor3f(brush->owner->eclass->color.x, brush->owner->eclass->color.y, brush->owner->eclass->color.z); + qglBegin(GL_QUADS); + qglVertex2f(-xCam, bottom); + qglVertex2f(xCam, bottom); + qglVertex2f(xCam, top); + qglVertex2f(-xCam, top); + qglEnd(); + } + } + } + + qglColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES].ToFloatPtr()); + qglBegin(GL_LINE_LOOP); + qglVertex2f(-xCam, brush->mins[2]); + qglVertex2f(xCam, brush->mins[2]); + qglVertex2f(xCam, brush->maxs[2]); + qglVertex2f(-xCam, brush->maxs[2]); + qglEnd(); + } + + ZDrawCameraIcon(); + ZDrawZClip(); + + qglFinish(); + QE_CheckOpenGLForErrors(); +} diff --git a/src/tools/radiant/Z.H b/src/tools/radiant/Z.H new file mode 100644 index 0000000..79c560a --- /dev/null +++ b/src/tools/radiant/Z.H @@ -0,0 +1,46 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +// window system independent camera view code + +typedef struct +{ + int width, height; + + idVec3 origin; // at center of window + float scale; +} z_t; + +extern z_t z; + +void Z_Init (void); +void Z_MouseDown (int x, int y, int buttons); +void Z_MouseUp (int x, int y, int buttons); +void Z_MouseMoved (int x, int y, int buttons); +void Z_Draw (void); + diff --git a/src/tools/radiant/ZClip.cpp b/src/tools/radiant/ZClip.cpp new file mode 100644 index 0000000..b8d6e32 --- /dev/null +++ b/src/tools/radiant/ZClip.cpp @@ -0,0 +1,199 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" + +#include "zclip.h" + + +CZClip::CZClip() +{ + LONG + lSize = sizeof(m_bEnabled); + if (!LoadRegistryInfo("radiant_ZClipEnabled", &m_bEnabled, &lSize)) + m_bEnabled = false; + + lSize = sizeof(m_iZClipTop); + if (!LoadRegistryInfo("radiant_ZClipTop", &m_iZClipTop, &lSize)) + m_iZClipTop = 64; + + lSize = sizeof(m_iZClipBottom); + if (!LoadRegistryInfo("radiant_ZClipBottom", &m_iZClipBottom, &lSize)) + m_iZClipBottom = -64; + + Legalise(); +} + +CZClip::~CZClip() +{ + // TODO: registry save + + SaveRegistryInfo("radiant_ZClipEnabled", &m_bEnabled, sizeof(m_bEnabled)); + SaveRegistryInfo("radiant_ZClipTop", &m_iZClipTop, sizeof(m_iZClipTop)); + SaveRegistryInfo("radiant_ZClipBottom", &m_iZClipBottom, sizeof(m_iZClipBottom)); +} + +void CZClip::Reset(void) +{ + m_iZClipTop = 64; // arb. starting values, but must be at least 64 apart + m_iZClipBottom = -64; + m_bEnabled = false; + + Legalise(); +} + + +int CZClip::GetTop(void) +{ + return m_iZClipTop; +} + +int CZClip::GetBottom(void) +{ + return m_iZClipBottom; +} + +void CZClip::Legalise(void) +{ + // need swapping? + // + if (m_iZClipTop < m_iZClipBottom) + { + int iTemp = m_iZClipTop; + m_iZClipTop = m_iZClipBottom; + m_iZClipBottom = iTemp; + } + + // too close together? + // +#define ZCLIP_MIN_SPACING 64 + + if (abs(m_iZClipTop - m_iZClipBottom) < ZCLIP_MIN_SPACING) + m_iZClipBottom = m_iZClipTop - ZCLIP_MIN_SPACING; +} + + +void CZClip::SetTop(int iNewZ) +{ + m_iZClipTop = iNewZ; + + Legalise(); +} + +void CZClip::SetBottom(int iNewZ) +{ + m_iZClipBottom = iNewZ; + + Legalise(); +} + +bool CZClip::IsEnabled(void) +{ + return m_bEnabled; +} + + +bool CZClip::Enable(bool bOnOff) +{ + m_bEnabled = !m_bEnabled; + return IsEnabled(); +} + +#define ZCLIP_BAR_THICKNESS 8 +#define ZCLIP_ARROWHEIGHT (ZCLIP_BAR_THICKNESS*8) + +void CZClip::Paint(void) +{ + float x, y; + int xCam = z.width/4; // hmmm, a rather unpleasant and obscure global name, but it was already called that so... + + qglColor3f (ZCLIP_COLOUR);//1.0, 0.0, 1.0); + + // draw TOP marker... + // + x = 0; + y = m_iZClipTop; + + if (m_bEnabled) + qglBegin(GL_QUADS); + else + qglBegin(GL_LINE_LOOP); + + qglVertex3f (x-xCam,y,0); + qglVertex3f (x-xCam,y+ZCLIP_BAR_THICKNESS,0); + qglVertex3f (x+xCam,y+ZCLIP_BAR_THICKNESS,0); + qglVertex3f (x+xCam,y,0); + qglEnd (); + + qglColor3f (ZCLIP_COLOUR_DIM);//0.8, 0.0, 0.8); + + if (m_bEnabled) + qglBegin(GL_TRIANGLES); + else + qglBegin(GL_LINE_LOOP); + qglVertex3f (x,(y+ZCLIP_BAR_THICKNESS),0); + qglVertex3f (x-xCam,(y+ZCLIP_BAR_THICKNESS)+(ZCLIP_ARROWHEIGHT/2),0); + qglVertex3f (x+xCam,(y+ZCLIP_BAR_THICKNESS)+(ZCLIP_ARROWHEIGHT/2),0); + qglEnd (); + + // draw bottom marker... + // + qglColor3f (ZCLIP_COLOUR);//1.0, 0.0, 1.0); + x = 0; + y = m_iZClipBottom; + + if (m_bEnabled) + qglBegin(GL_QUADS); + else + qglBegin(GL_LINE_LOOP); + qglVertex3f (x-xCam,y,0); + qglVertex3f (x-xCam,y-ZCLIP_BAR_THICKNESS,0); + qglVertex3f (x+xCam,y-ZCLIP_BAR_THICKNESS,0); + qglVertex3f (x+xCam,y,0); + qglEnd (); + + qglColor3f (ZCLIP_COLOUR_DIM);//0.8, 0.0, 0.8); + + if (m_bEnabled) + qglBegin(GL_TRIANGLES); + else + qglBegin(GL_LINE_LOOP); + qglVertex3f (x,(y-ZCLIP_BAR_THICKNESS),0); + qglVertex3f (x-xCam,(y-ZCLIP_BAR_THICKNESS)-(ZCLIP_ARROWHEIGHT/2),0); + qglVertex3f (x+xCam,(y-ZCLIP_BAR_THICKNESS)-(ZCLIP_ARROWHEIGHT/2),0); + qglEnd (); +} + + +///////////////// eof /////////////////// + + diff --git a/src/tools/radiant/ZClip.h b/src/tools/radiant/ZClip.h new file mode 100644 index 0000000..e1de5e7 --- /dev/null +++ b/src/tools/radiant/ZClip.h @@ -0,0 +1,68 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef ZCLIP_H +#define ZCLIP_H + +// I don't like doing macros without braces and with whitespace, but the compiler moans if I do these differently, +// and since they're only for use within glColor3f() calls anyway then this is ok... (that's my excuse anyway) +// +#define ZCLIP_COLOUR 1.0f, 0.0f, 1.0f +#define ZCLIP_COLOUR_DIM 0.8f, 0.0f, 0.8f + + +class CZClip +{ +public: + CZClip(); + ~CZClip(); + + int GetTop(void); + int GetBottom(void); + void SetTop(int iNewZ); + void SetBottom(int iNewZ); + void Reset(void); + bool IsEnabled(void); + bool Enable(bool bOnOff); + void Paint(void); + +protected: + void Legalise(void); + + bool m_bEnabled; + int m_iZClipTop; + int m_iZClipBottom; +}; + + +#endif // #ifndef ZCLIP_H + + +///////////// eof /////////////// + + diff --git a/src/tools/radiant/ZWnd.cpp b/src/tools/radiant/ZWnd.cpp new file mode 100644 index 0000000..9d95cf7 --- /dev/null +++ b/src/tools/radiant/ZWnd.cpp @@ -0,0 +1,273 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" +#include "ZWnd.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CZWnd +IMPLEMENT_DYNCREATE(CZWnd, CWnd); + + +CZWnd::CZWnd() +{ + m_pZClip = NULL; +} + +CZWnd::~CZWnd() +{ +} + + +BEGIN_MESSAGE_MAP(CZWnd, CWnd) + //{{AFX_MSG_MAP(CZWnd) + ON_WM_CREATE() + ON_WM_DESTROY() + ON_WM_KEYDOWN() + ON_WM_LBUTTONDOWN() + ON_WM_MBUTTONDOWN() + ON_WM_RBUTTONDOWN() + ON_WM_PAINT() + ON_WM_GETMINMAXINFO() + ON_WM_MOUSEMOVE() + ON_WM_SIZE() + ON_WM_NCCALCSIZE() + ON_WM_KILLFOCUS() + ON_WM_SETFOCUS() + ON_WM_CLOSE() + ON_WM_LBUTTONUP() + ON_WM_MBUTTONUP() + ON_WM_RBUTTONUP() + ON_WM_KEYUP() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + +///////////////////////////////////////////////////////////////////////////// +// CZWnd message handlers + +int CZWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) +{ + if (CWnd::OnCreate(lpCreateStruct) == -1) + return -1; + + m_dcZ = ::GetDC(GetSafeHwnd()); + QEW_SetupPixelFormat(m_dcZ, false); + + m_pZClip = new CZClip(); + + return 0; +} + +void CZWnd::OnDestroy() +{ + if (m_pZClip) + { + delete m_pZClip; + m_pZClip = NULL; + } + + CWnd::OnDestroy(); +} + +void CZWnd::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) +{ + g_pParentWnd->HandleKey(nChar, nRepCnt, nFlags); +} + +void CZWnd::OnLButtonDown(UINT nFlags, CPoint point) +{ + SetFocus(); + SetCapture(); + CRect rctZ; + GetClientRect(rctZ); + Z_MouseDown (point.x, rctZ.Height() - 1 - point.y , nFlags); +} + +void CZWnd::OnMButtonDown(UINT nFlags, CPoint point) +{ + SetFocus(); + SetCapture(); + CRect rctZ; + GetClientRect(rctZ); + Z_MouseDown (point.x, rctZ.Height() - 1 - point.y , nFlags); +} + +void CZWnd::OnRButtonDown(UINT nFlags, CPoint point) +{ + SetFocus(); + SetCapture(); + CRect rctZ; + GetClientRect(rctZ); + Z_MouseDown (point.x, rctZ.Height() - 1 - point.y , nFlags); +} + +void CZWnd::OnPaint() +{ + CPaintDC dc(this); // device context for painting + //if (!wglMakeCurrent(m_dcZ, m_hglrcZ)) + //if (!qwglMakeCurrent(dc.m_hDC, m_hglrcZ)) + if (!qwglMakeCurrent(dc.m_hDC, win32.hGLRC)) + { + common->Printf("ERROR: wglMakeCurrent failed..\n "); + common->Printf("Please restart " EDITOR_WINDOWTEXT " if the Z view is not working\n"); + } + else + { + QE_CheckOpenGLForErrors(); + + Z_Draw (); + //qwglSwapBuffers(m_dcZ); + qwglSwapBuffers(dc.m_hDC); + TRACE("Z Paint\n"); + } +} + +void CZWnd::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI) +{ + lpMMI->ptMinTrackSize.x = ZWIN_WIDTH; +} + +void CZWnd::OnMouseMove(UINT nFlags, CPoint point) +{ + CRect rctZ; + GetClientRect(rctZ); + float fz = z.origin[2] + ((rctZ.Height() - 1 - point.y) - (z.height/2)) / z.scale; + fz = floor(fz / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize; + CString strStatus; + strStatus.Format("Z:: %.1f", fz); + g_pParentWnd->SetStatusText(1, strStatus); + Z_MouseMoved (point.x, rctZ.Height() - 1 - point.y, nFlags); +} + +void CZWnd::OnSize(UINT nType, int cx, int cy) +{ + CWnd::OnSize(nType, cx, cy); + CRect rctZ; + GetClientRect(rctZ); + z.width = rctZ.right; + z.height = rctZ.bottom; + if (z.width < 10) + z.width = 10; + if (z.height < 10) + z.height = 10; + Invalidate(); +} + +void CZWnd::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS FAR* lpncsp) +{ + CWnd::OnNcCalcSize(bCalcValidRects, lpncsp); +} + +void CZWnd::OnKillFocus(CWnd* pNewWnd) +{ + CWnd::OnKillFocus(pNewWnd); + SendMessage(WM_NCACTIVATE, FALSE , 0 ); +} + +void CZWnd::OnSetFocus(CWnd* pOldWnd) +{ + CWnd::OnSetFocus(pOldWnd); + SendMessage(WM_NCACTIVATE, TRUE , 0 ); +} + +void CZWnd::OnClose() +{ + CWnd::OnClose(); +} + +void CZWnd::OnLButtonUp(UINT nFlags, CPoint point) +{ + CRect rctZ; + GetClientRect(rctZ); + Z_MouseUp (point.x, rctZ.bottom - 1 - point.y, nFlags); + if (! (nFlags & (MK_LBUTTON|MK_RBUTTON|MK_MBUTTON))) + ReleaseCapture (); +} + +void CZWnd::OnMButtonUp(UINT nFlags, CPoint point) +{ + CRect rctZ; + GetClientRect(rctZ); + Z_MouseUp (point.x, rctZ.bottom - 1 - point.y, nFlags); + if (! (nFlags & (MK_LBUTTON|MK_RBUTTON|MK_MBUTTON))) + ReleaseCapture (); +} + +void CZWnd::OnRButtonUp(UINT nFlags, CPoint point) +{ + CRect rctZ; + GetClientRect(rctZ); + Z_MouseUp (point.x, rctZ.bottom - 1 - point.y, nFlags); + if (! (nFlags & (MK_LBUTTON|MK_RBUTTON|MK_MBUTTON))) + ReleaseCapture (); +} + + +BOOL CZWnd::PreCreateWindow(CREATESTRUCT& cs) +{ + WNDCLASS wc; + HINSTANCE hInstance = AfxGetInstanceHandle(); + if (::GetClassInfo(hInstance, Z_WINDOW_CLASS, &wc) == FALSE) + { + // Register a new class + memset (&wc, 0, sizeof(wc)); + wc.style = CS_NOCLOSE;// | CS_OWNDC; + wc.hInstance = hInstance; + wc.lpszClassName = Z_WINDOW_CLASS; + wc.hCursor = LoadCursor (NULL,IDC_ARROW); + wc.lpfnWndProc = ::DefWindowProc; + if (AfxRegisterClass(&wc) == FALSE) { + common->Warning("Radiant: failed to register %s (error %lu)", Z_WINDOW_CLASS, GetLastError()); + return FALSE; + } + } + + cs.lpszClass = Z_WINDOW_CLASS; + cs.lpszName = "Z"; + if (cs.style != QE3_CHILDSTYLE) + cs.style = QE3_SPLITTER_STYLE; + + return CWnd::PreCreateWindow(cs); +} + + +void CZWnd::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) +{ + g_pParentWnd->HandleKey(nChar, nRepCnt, nFlags, false); +} diff --git a/src/tools/radiant/ZWnd.h b/src/tools/radiant/ZWnd.h new file mode 100644 index 0000000..ab117f8 --- /dev/null +++ b/src/tools/radiant/ZWnd.h @@ -0,0 +1,100 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#if !defined(AFX_ZWND_H__44B4BA02_781B_11D1_B53C_00AA00A410FC__INCLUDED_) +#define AFX_ZWND_H__44B4BA02_781B_11D1_B53C_00AA00A410FC__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 +// ZWnd.h : header file +// + +#include "zclip.h" + +///////////////////////////////////////////////////////////////////////////// +// CZWnd window + +class CZWnd : public CWnd +{ + DECLARE_DYNCREATE(CZWnd); +// Construction +public: + CZWnd(); + +// Attributes +public: + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CZWnd) + protected: + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~CZWnd(); + + CZClip *m_pZClip; + + // Generated message map functions +protected: + HDC m_dcZ; + HGLRC m_hglrcZ; + //{{AFX_MSG(CZWnd) + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + afx_msg void OnDestroy(); + afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnLButtonDown(UINT nFlags, CPoint point); + afx_msg void OnMButtonDown(UINT nFlags, CPoint point); + afx_msg void OnRButtonDown(UINT nFlags, CPoint point); + afx_msg void OnPaint(); + afx_msg void OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI); + afx_msg void OnMouseMove(UINT nFlags, CPoint point); + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS FAR* lpncsp); + afx_msg void OnKillFocus(CWnd* pNewWnd); + afx_msg void OnSetFocus(CWnd* pOldWnd); + afx_msg void OnClose(); + afx_msg void OnLButtonUp(UINT nFlags, CPoint point); + afx_msg void OnMButtonUp(UINT nFlags, CPoint point); + afx_msg void OnRButtonUp(UINT nFlags, CPoint point); + afx_msg void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_ZWND_H__44B4BA02_781B_11D1_B53C_00AA00A410FC__INCLUDED_) diff --git a/src/tools/radiant/autocaulk.cpp b/src/tools/radiant/autocaulk.cpp new file mode 100644 index 0000000..c30b6e9 --- /dev/null +++ b/src/tools/radiant/autocaulk.cpp @@ -0,0 +1,340 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "Radiant.h" +#include "autocaulk.h" + +// Note: the code in here looks pretty goofy in places, and probably doesn't use the new Q4 class stuff fully, +// but I just got it in and compiling from the JK2/SOF2 Radiants via some ugly code replaces, and it works, so there. +// Also, a bunch of Radiant fields no longer exist in this codebase, likewise the whole point of passing in the bool +// to this code, but I've just left it as-is. A designer tested it and pronounced it fine. + +//#pragma warning( disable : 4786) +//#include +//using namespace std; +//#pragma warning( disable : 4786) + +#undef strnicmp +#define strnicmp idStr::Icmpn + +#if 1 + + +//extern void ClearBounds (idVec3 mins, idVec3 maxs); +//extern void AddPointToBounds (const idVec3 v, idVec3 mins, idVec3 maxs); +void ClearBounds (idVec3 &mins, idVec3 &maxs) +{ + mins[0] = mins[1] = mins[2] = 99999; + maxs[0] = maxs[1] = maxs[2] = -99999; +} + +void AddPointToBounds( const idVec3 &v, idVec3 &mins, idVec3 &maxs ) +{ + int i; + float val; + + for (i=0 ; i<3 ; i++) + { + val = v[i]; + if (val < mins[i]) + mins[i] = val; + if (val > maxs[i]) + maxs[i] = val; + } +} + + +static void FloorBounds(idVec3 &mins, idVec3 &maxs) +{ + for (int i=0 ; i<3 ; i++) + { + mins[i] = floor(mins[i] + 0.5); + maxs[i] = floor(maxs[i] + 0.5); + } +} + + +static LPCSTR vtos(idVec3 &v3) +{ + return va("%.3ff,%.3f,%.3f",v3[0],v3[1],v3[2]); +} +struct PairBrushFace_t +{ + face_t* pFace; + brush_t* pBrush; +}; +idList < PairBrushFace_t > FacesToCaulk; +void Select_AutoCaulk() +{ + /*Sys_Printf*/common->Printf("Caulking...\n"); + + FacesToCaulk.Clear(); + + int iSystemBrushesSkipped = 0; + face_t *pSelectedFace; + + brush_t *next; + for (brush_t *pSelectedBrush = selected_brushes.next ; pSelectedBrush != &selected_brushes ; pSelectedBrush = next) + { + next = pSelectedBrush->next; + + if (pSelectedBrush->owner->eclass->fixedsize) + continue; // apparently this means it's a model, so skip it... + + // new check, we can't caulk a brush that has any "system/" faces... + // + bool bSystemFacePresent = false; + for ( pSelectedFace = pSelectedBrush->brush_faces; pSelectedFace; pSelectedFace = pSelectedFace->next) + { + if (!strnicmp(pSelectedFace->d_texture->GetName(),"system/",7)) + { + bSystemFacePresent = true; + break; + } + } + if (bSystemFacePresent) + { + iSystemBrushesSkipped++; + continue; // verboten to caulk this. + } + + for (int iBrushListToScan = 0; iBrushListToScan<2; iBrushListToScan++) + { + brush_t *snext; + for (brush_t *pScannedBrush = (iBrushListToScan?active_brushes.next:selected_brushes.next); pScannedBrush != (iBrushListToScan?&active_brushes:&selected_brushes) ; pScannedBrush = snext) + { + snext = pScannedBrush->next; + + if ( pScannedBrush == pSelectedBrush) + continue; + + if (pScannedBrush->owner->eclass->fixedsize || pScannedBrush->pPatch || pScannedBrush->hiddenBrush) + continue; + + if (FilterBrush(pScannedBrush)) + continue; + +// idMaterial stuff no longer support this, not sure what else to do. +// Searching for other occurences of QER_NOCARVE just shows people REMing the code and ignoring ths issue... +// +// if (pScannedBrush->brush_faces->d_texture->bFromShader && (pScannedBrush->brush_faces->d_texture->TestMaterialFlag(QER_NOCARVE))) +// continue; + + // basic-reject first to see if brushes can even possibly touch (coplanar counts as touching) + // + int i; + for (i=0 ; i<3 ; i++) + { + if (pSelectedBrush->mins[i] > pScannedBrush->maxs[i] || + pSelectedBrush->maxs[i] < pScannedBrush->mins[i]) + { + break; + } + } + if (i != 3) + continue; // can't be touching + + // ok, now for the clever stuff, we need to detect only those faces that are both coplanar and smaller + // or equal to the face they're coplanar with... + // + for (pSelectedFace = pSelectedBrush->brush_faces; pSelectedFace; pSelectedFace = pSelectedFace->next) + { + idWinding *pSelectedWinding = pSelectedFace->face_winding; + + if (!pSelectedWinding) + continue; // freed face, probably won't happen here, but who knows with this program? + + // SquaredFace_t SelectedSquaredFace; + // WindingToSquaredFace( &SelectedSquaredFace, pSelectedWinding); + + for (face_t *pScannedFace = pScannedBrush->brush_faces; pScannedFace; pScannedFace = pScannedFace->next) + { + // don't even try caulking against a system face, because these are often transparent and will leave holes + // + if (!strnicmp(pScannedFace->d_texture->GetName(),"system/",7)) + continue; + + // and don't try caulking against something inherently transparent... + // + if (pScannedFace->d_texture->TestMaterialFlag(QER_TRANS)) + continue; + + idWinding *pScannedWinding = pScannedFace->face_winding; + + if (!pScannedWinding) + continue; // freed face, probably won't happen here, but who knows with this program? + + // SquaredFace_t ScannedSquaredFace; + // WindingToSquaredFace( &ScannedSquaredFace, pScannedWinding); + + /* if (VectorCompare(ScannedSquaredFace.v3NormalisedRotationVector, SelectedSquaredFace.v3NormalisedRotationVector) + && + VectorCompare(ScannedSquaredFace.v3NormalisedElevationVector, SelectedSquaredFace.v3NormalisedElevationVector) + ) + */ + { + // brush faces are in parallel planes to each other, so check that their normals + // are opposite, by adding them together and testing for zero... + // (if normals are opposite, then faces can be against/touching each other?) + // + idVec3 v3ZeroTest; + idVec3 v3Zero;v3Zero.Zero(); //static idVec3 v3Zero={0,0,0}; + + VectorAdd(pSelectedFace->plane.Normal(),pScannedFace->plane.Normal(),v3ZeroTest); + if (v3ZeroTest == v3Zero) + { + // planes are facing each other... + // + // coplanar? (this is some maths of Gil's, which I don't even pretend to understand) + // + float fTotalDist = 0; + for (int _i=0; _i<3; _i++) + { + fTotalDist += fabs( DotProduct(pSelectedFace->plane.Normal(),(*pSelectedWinding)[0]) + - + DotProduct(pSelectedFace->plane.Normal(),(*pScannedWinding)[i]) + ); + } + //OutputDebugString(va("Dist = %g\n",fTotalDist)); + + if (fTotalDist > 0.01) + continue; + + // every point in the selected face must be within (or equal to) the bounds of the + // scanned face... + // + // work out the bounds first... + // + idVec3 v3ScannedBoundsMins, v3ScannedBoundsMaxs; + ClearBounds (v3ScannedBoundsMins, v3ScannedBoundsMaxs); + int iPoint; + for (iPoint=0; iPointGetNumPoints(); iPoint++) + { + AddPointToBounds( (*pScannedWinding)[iPoint].ToVec3(), v3ScannedBoundsMins, v3ScannedBoundsMaxs); + } + // floor 'em... (or .001 differences mess things up... + // + FloorBounds(v3ScannedBoundsMins, v3ScannedBoundsMaxs); + + + // now check points from selected face... + // + bool bWithin = true; + for (iPoint=0; iPoint < pSelectedWinding->GetNumPoints(); iPoint++) + { + for (int iXYZ=0; iXYZ<3; iXYZ++) + { + float f = floor((*pSelectedWinding)[iPoint][iXYZ] + 0.5); + if (! + ( + f >= v3ScannedBoundsMins[iXYZ] + && + f <= v3ScannedBoundsMaxs[iXYZ] + ) + ) + { + bWithin = false; + } + } + } + + if (bWithin) + { + PairBrushFace_t PairBrushFace; + PairBrushFace.pFace = pSelectedFace; + PairBrushFace.pBrush= pSelectedBrush; + FacesToCaulk.Append(PairBrushFace); + } + } + } + } + } + } + } + } + + + // apply caulk... + // + int iFacesCaulked = 0; + if (FacesToCaulk.Num()) + { + LPCSTR psCaulkName = "textures/common/caulk"; + const idMaterial *pCaulk = Texture_ForName(psCaulkName); + + if (pCaulk) + { + // + // and call some other junk that Radiant wants so so we can use it later... + // + texdef_t tex; + memset (&tex, 0, sizeof(tex)); + tex.scale[0] = 1; + tex.scale[1] = 1; + //tex.flags = pCaulk->flags; // field missing in Q4 + //tex.value = pCaulk->value; // ditto + //tex.contents = pCaulk->contents; // ditto + tex.SetName( pCaulk->GetName() ); + + //Texture_SetTexture (&tex); + + for (int iListEntry = 0; iListEntry < FacesToCaulk.Num(); iListEntry++) + { + PairBrushFace_t &PairBrushFace = FacesToCaulk[iListEntry]; + face_t *pFace = PairBrushFace.pFace; + brush_t*pBrush= PairBrushFace.pBrush; + + pFace->d_texture = pCaulk; + pFace->texdef = tex; + + Face_FitTexture(pFace, 1, 1); // this doesn't work here for some reason... duh. + Brush_Build(pBrush); + + iFacesCaulked++; + } + } + else + { + /*Sys_Printf*/common->Printf(" Unable to locate caulk texture at: \"%s\"!\n",psCaulkName); + } + } + + /*Sys_Printf*/common->Printf("( %d faces caulked )\n",iFacesCaulked); + + if (iSystemBrushesSkipped) + { + /*Sys_Printf*/common->Printf("( %d system-faced brushes skipped )\n",iSystemBrushesSkipped); + } + + Sys_UpdateWindows (W_ALL); +} +#endif \ No newline at end of file diff --git a/src/tools/radiant/autocaulk.h b/src/tools/radiant/autocaulk.h new file mode 100644 index 0000000..c1a2341 --- /dev/null +++ b/src/tools/radiant/autocaulk.h @@ -0,0 +1,39 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef AUTOCAULK_H +#define AUTOCAULK_H + + +void Select_AutoCaulk(); + + +#endif // AUTOCAULK_H + +///////////////// eof ////////////// + diff --git a/src/tools/radiant/cmdlib.cpp b/src/tools/radiant/cmdlib.cpp new file mode 100644 index 0000000..3528a0a --- /dev/null +++ b/src/tools/radiant/cmdlib.cpp @@ -0,0 +1,236 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "qe3.h" +#include "cmdlib.h" + +#define PATHSEPERATOR '/' + +// rad additions +// 11.29.99 +PFN_ERR *g_pfnError = NULL; +PFN_PRINTF *g_pfnPrintf = NULL; +PFN_ERR_NUM *g_pfnErrorNum = NULL; +PFN_PRINTF_NUM *g_pfnPrintfNum = NULL; + + +void Error(const char *pFormat, ...) +{ + if (g_pfnError) + { + va_list arg_ptr; + va_start(arg_ptr, pFormat); + g_pfnError(pFormat, arg_ptr); + va_end(arg_ptr); + } +} + +void Printf(const char *pFormat, ...) +{ + if (g_pfnPrintf) + { + va_list arg_ptr; + va_start(arg_ptr, pFormat); + g_pfnPrintf(pFormat, arg_ptr); + va_end(arg_ptr); + } +} + +void ErrorNum(int nErr, const char *pFormat, ...) +{ + if (g_pfnErrorNum) + { + va_list arg_ptr; + va_start(arg_ptr, pFormat); + g_pfnErrorNum(nErr, pFormat, arg_ptr); + va_end(arg_ptr); + } +} + +void PrintfNum(int nErr, const char *pFormat, ...) +{ + if (g_pfnPrintfNum) + { + va_list arg_ptr; + va_start(arg_ptr, pFormat); + g_pfnPrintfNum(nErr, pFormat, arg_ptr); + va_end(arg_ptr); + } +} + +void SetErrorHandler(PFN_ERR pe) +{ + g_pfnError = pe; +} + +void SetPrintfHandler(PFN_PRINTF pe) +{ + g_pfnPrintf = pe; +} + +void SetErrorHandlerNum(PFN_ERR_NUM pe) +{ + g_pfnErrorNum = pe; +} + +void SetPrintfHandler(PFN_PRINTF_NUM pe) +{ + g_pfnPrintfNum = pe; +} + + +/* +================ +Q_filelength +================ +*/ +int Q_filelength (FILE *f) +{ + int pos; + int end; + + pos = ftell (f); + fseek (f, 0, SEEK_END); + end = ftell (f); + fseek (f, pos, SEEK_SET); + + return end; +} + +/* +============== +LoadFile +============== +*/ +int LoadFile (const char *filename, void **bufferptr) +{ + FILE *f; + int length; + void *buffer; + + *bufferptr = NULL; + + if ( filename == NULL || strlen(filename) == 0 ) { + return -1; + } + + f = fopen( filename, "rb" ); + if ( !f ) { + return -1; + } + length = Q_filelength( f ); + buffer = Mem_ClearedAlloc( length+1 ); + ((char *)buffer)[length] = 0; + if ( (int)fread( buffer, 1, length, f ) != length ) { + Error( "File read failure" ); + } + fclose( f ); + + *bufferptr = buffer; + return length; +} + +/* +============== +DefaultExtension +============== +*/ +void DefaultExtension (char *path, char *extension) +{ + char *src; + // + // if path doesn't have a .EXT, append extension + // (extension should include the .) + // + src = path + strlen(path) - 1; + + while (*src != PATHSEPERATOR && src != path) + { + if (*src == '.') + return; // it has an extension + src--; + } + + strcat (path, extension); +} + +/* +============== +DefaultPath +============== +*/ +void DefaultPath (char *path, char *basepath) +{ + char temp[128]; + + if (path[0] == PATHSEPERATOR) + return; // absolute path location + strcpy (temp,path); + strcpy (path,basepath); + strcat (path,temp); +} + +/* +============== +StripFilename +============== +*/ +void StripFilename (char *path) +{ + int length; + + length = strlen(path)-1; + while (length > 0 && path[length] != PATHSEPERATOR) { + length--; + } + path[length] = 0; +} + +/* +============== +StripExtension +============== +*/ +void StripExtension (char *path) +{ + int length; + + length = strlen(path)-1; + while (length > 0 && path[length] != '.') + { + length--; + if (path[length] == '/') + return; // no extension + } + if (length) { + path[length] = 0; + } +} diff --git a/src/tools/radiant/cmdlib.h b/src/tools/radiant/cmdlib.h new file mode 100644 index 0000000..b30172e --- /dev/null +++ b/src/tools/radiant/cmdlib.h @@ -0,0 +1,63 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef __CMDLIB__ +#define __CMDLIB__ + +#include +#include +#include +#include +#include +#include +#include + + +int LoadFile( const char *filename, void **bufferptr ); +void DefaultExtension( char *path, char *extension ); +void DefaultPath( char *path, char *basepath ); +void StripFilename( char *path ); +void StripExtension( char *path ); + +// error and printf functions +typedef void (PFN_ERR)( const char *pFormat, ... ); +typedef void (PFN_PRINTF)( const char *pFormat, ... ); +typedef void (PFN_ERR_NUM)( int nNum, const char *pFormat, ... ); +typedef void (PFN_PRINTF_NUM)( int nNum, const char *pFormat, ... ); + +void Error( const char *pFormat, ... ); +void Printf( const char *pFormat, ... ); +void ErrorNum( int n, const char *pFormat, ... ); +void PrintfNum( int n, const char *pFormat, ... ); + +void SetErrorHandler( PFN_ERR pe ); +void SetPrintfHandler( PFN_PRINTF pe ); +void SetErrorHandlerNum( PFN_ERR_NUM pe ); +void SetPrintfHandlerNum( PFN_PRINTF_NUM pe ); + +#endif /* !__CMDLIB__ */ diff --git a/src/tools/radiant/splines.cpp b/src/tools/radiant/splines.cpp new file mode 100644 index 0000000..274c2c9 --- /dev/null +++ b/src/tools/radiant/splines.cpp @@ -0,0 +1,2037 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "splines.h" + +idCameraDef splineList; +idCameraDef *g_splineList = &splineList; + +/* +================ +glLabeledPoint +================ +*/ +void glLabeledPoint(idVec4 &color, idVec3 &point, float size, const char *label) { + qglColor3fv( color.ToFloatPtr() ); + qglPointSize( size ); + qglBegin( GL_POINTS ); + qglVertex3fv( point.ToFloatPtr() ); + qglEnd(); + idVec3 v = point; + v.x += 1; + v.y += 1; + v.z += 1; + qglRasterPos3fv( v.ToFloatPtr() ); + qglCallLists( strlen(label), GL_UNSIGNED_BYTE, label ); +} + +/* +================ +glBox +================ +*/ +void glBox(idVec4 &color, idVec3 &point, float size) { + idVec3 mins(point); + idVec3 maxs(point); + mins[0] -= size; + mins[1] += size; + mins[2] -= size; + maxs[0] += size; + maxs[1] -= size; + maxs[2] += size; + idVec4 saveColor; + qglGetFloatv(GL_CURRENT_COLOR, saveColor.ToFloatPtr()); + qglColor3fv( color.ToFloatPtr() ); + qglBegin(GL_LINE_LOOP); + qglVertex3f(mins[0],mins[1],mins[2]); + qglVertex3f(maxs[0],mins[1],mins[2]); + qglVertex3f(maxs[0],maxs[1],mins[2]); + qglVertex3f(mins[0],maxs[1],mins[2]); + qglEnd(); + qglBegin(GL_LINE_LOOP); + qglVertex3f(mins[0],mins[1],maxs[2]); + qglVertex3f(maxs[0],mins[1],maxs[2]); + qglVertex3f(maxs[0],maxs[1],maxs[2]); + qglVertex3f(mins[0],maxs[1],maxs[2]); + qglEnd(); + + qglBegin(GL_LINES); + qglVertex3f(mins[0],mins[1],mins[2]); + qglVertex3f(mins[0],mins[1],maxs[2]); + qglVertex3f(mins[0],maxs[1],maxs[2]); + qglVertex3f(mins[0],maxs[1],mins[2]); + qglVertex3f(maxs[0],mins[1],mins[2]); + qglVertex3f(maxs[0],mins[1],maxs[2]); + qglVertex3f(maxs[0],maxs[1],maxs[2]); + qglVertex3f(maxs[0],maxs[1],mins[2]); + qglEnd(); + qglColor4fv(saveColor.ToFloatPtr()); + +} + +/* +================ +splineTest +================ +*/ +void splineTest() { + //g_splineList->load("p:/doom/base/maps/test_base1.camera"); +} + +/* +================ +splineDraw +================ +*/ +void splineDraw() { + //g_splineList->addToRenderer(); +} + +/* +================ +debugLine +================ +*/ +void debugLine(idVec4 &color, float x, float y, float z, float x2, float y2, float z2) { + idVec3 from(x, y, z); + idVec3 to(x2, y2, z2); + session->rw->DebugLine(color, from, to); +} + + +/* +================================================================================= + +idPointListInterface + +================================================================================= +*/ + +/* +================ +idPointListInterface::selectPointByRay +================ +*/ +int idPointListInterface::selectPointByRay(const idVec3 &origin, const idVec3 &direction, bool single) { + int i, besti, count; + float d, bestd; + idVec3 temp, temp2; + + // find the point closest to the ray + besti = -1; + bestd = 8; + count = numPoints(); + + for (i=0; i < count; i++) { + temp = *getPoint(i); + temp2 = temp; + temp -= origin; + d = DotProduct(temp, direction); + VectorMA (origin, d, direction, temp); + temp2 -= temp; + d = temp2.Length(); + if (d <= bestd) { + bestd = d; + besti = i; + } + } + + if (besti >= 0) { + selectPoint(besti, single); + } + + return besti; +} + +/* +================ +idPointListInterface::isPointSelected +================ +*/ +int idPointListInterface::isPointSelected(int index) { + int count = selectedPoints.Num(); + for (int i = 0; i < count; i++) { + if (selectedPoints[i] == index) { + return i; + } + } + return -1; +} + +/* +================ +idPointListInterface::selectPoint +================ +*/ +int idPointListInterface::selectPoint(int index, bool single) { + if (index >= 0 && index < numPoints()) { + if (single) { + deselectAll(); + } else { + if (isPointSelected(index) >= 0) { + selectedPoints.Remove(index); + } + } + return selectedPoints.Append(index); + } + return -1; +} + +/* +================ +idPointListInterface::selectAll +================ +*/ +void idPointListInterface::selectAll() { + selectedPoints.Clear(); + for (int i = 0; i < numPoints(); i++) { + selectedPoints.Append(i); + } +} + +/* +================ +idPointListInterface::deselectAll +================ +*/ +void idPointListInterface::deselectAll() { + selectedPoints.Clear(); +} + +/* +================ +idPointListInterface::getSelectedPoint +================ +*/ +idVec3 *idPointListInterface::getSelectedPoint( int index ) { + assert(index >= 0 && index < numSelectedPoints()); + return getPoint(selectedPoints[index]); +} + +/* +================ +idPointListInterface::updateSelection +================ +*/ +void idPointListInterface::updateSelection(const idVec3 &move) { + int count = selectedPoints.Num(); + for (int i = 0; i < count; i++) { + *getPoint(selectedPoints[i]) += move; + } +} + +/* +================ +idPointListInterface::drawSelection +================ +*/ +void idPointListInterface::drawSelection() { + int count = selectedPoints.Num(); + for (int i = 0; i < count; i++) { + glBox(colorRed, *getPoint(selectedPoints[i]), 4); + } +} + +/* +================================================================================= + +idSplineList + +================================================================================= +*/ + +/* +================ +idSplineList::clearControl +================ +*/ +void idSplineList::clearControl() { + for (int i = 0; i < controlPoints.Num(); i++) { + delete controlPoints[i]; + } + controlPoints.Clear(); +} + +/* +================ +idSplineList::clearSpline +================ +*/ +void idSplineList::clearSpline() { + for (int i = 0; i < splinePoints.Num(); i++) { + delete splinePoints[i]; + } + splinePoints.Clear(); +} + +/* +================ +idSplineList::clear +================ +*/ +void idSplineList::clear() { + clearControl(); + clearSpline(); + splineTime.Clear(); + selected = NULL; + dirty = true; + activeSegment = 0; + granularity = 0.025f; + pathColor = idVec4(1.0f, 0.5f, 0.0f, 1.0f); + controlColor = idVec4(0.7f, 0.0f, 1.0f, 1.0f); + segmentColor = idVec4(0.0f, 0.0f, 1.0f, 1.0); + activeColor = idVec4(1.0f, 0.0f, 0.0f, 1.0f); +} + +/* +================ +idSplineList::setColors +================ +*/ +void idSplineList::setColors(idVec4 &path, idVec4 &segment, idVec4 &control, idVec4 &active) { + pathColor = path; + segmentColor = segment; + controlColor = control; + activeColor = active; +} + +/* +================ +idSplineList::validTime +================ +*/ +bool idSplineList::validTime() { + if (dirty) { + buildSpline(); + } + // gcc doesn't allow static casting away from bools + // why? I've no idea... + return (bool)(splineTime.Num() > 0 && splineTime.Num() == splinePoints.Num()); +} + +/* +================ +idSplineList::addToRenderer +================ +*/ +void idSplineList::addToRenderer() { + int i; + idVec3 mins, maxs; + + if (controlPoints.Num() == 0) { + return; + } + + for(i = 0; i < controlPoints.Num(); i++) { + VectorCopy(*controlPoints[i], mins); + VectorCopy(mins, maxs); + mins[0] -= 8; + mins[1] += 8; + mins[2] -= 8; + maxs[0] += 8; + maxs[1] -= 8; + maxs[2] += 8; + debugLine( colorYellow, mins[0], mins[1], mins[2], maxs[0], mins[1], mins[2]); + debugLine( colorYellow, maxs[0], mins[1], mins[2], maxs[0], maxs[1], mins[2]); + debugLine( colorYellow, maxs[0], maxs[1], mins[2], mins[0], maxs[1], mins[2]); + debugLine( colorYellow, mins[0], maxs[1], mins[2], mins[0], mins[1], mins[2]); + + debugLine( colorYellow, mins[0], mins[1], maxs[2], maxs[0], mins[1], maxs[2]); + debugLine( colorYellow, maxs[0], mins[1], maxs[2], maxs[0], maxs[1], maxs[2]); + debugLine( colorYellow, maxs[0], maxs[1], maxs[2], mins[0], maxs[1], maxs[2]); + debugLine( colorYellow, mins[0], maxs[1], maxs[2], mins[0], mins[1], maxs[2]); + + } + + int step = 0; + idVec3 step1; + for(i = 3; i < controlPoints.Num(); i++) { + for (float tension = 0.0f; tension < 1.001f; tension += 0.1f) { + float x = 0; + float y = 0; + float z = 0; + for (int j = 0; j < 4; j++) { + x += controlPoints[i - (3 - j)]->x * calcSpline(j, tension); + y += controlPoints[i - (3 - j)]->y * calcSpline(j, tension); + z += controlPoints[i - (3 - j)]->z * calcSpline(j, tension); + } + if (step == 0) { + step1[0] = x; + step1[1] = y; + step1[2] = z; + step = 1; + } else { + debugLine( colorWhite, step1[0], step1[1], step1[2], x, y, z); + step = 0; + } + + } + } +} + +/* +================ +idSplineList::buildSpline +================ +*/ +void idSplineList::buildSpline() { + int start = Sys_Milliseconds(); + clearSpline(); + for(int i = 3; i < controlPoints.Num(); i++) { + for (float tension = 0.0f; tension < 1.001f; tension += granularity) { + float x = 0; + float y = 0; + float z = 0; + for (int j = 0; j < 4; j++) { + x += controlPoints[i - (3 - j)]->x * calcSpline(j, tension); + y += controlPoints[i - (3 - j)]->y * calcSpline(j, tension); + z += controlPoints[i - (3 - j)]->z * calcSpline(j, tension); + } + splinePoints.Append(new idVec3(x, y, z)); + } + } + dirty = false; + //common->Printf("Spline build took %f seconds\n", (float)(Sys_Milliseconds() - start) / 1000); +} + +/* +================ +idSplineList::draw +================ +*/ +void idSplineList::draw(bool editMode) { + int i; + + if (controlPoints.Num() == 0) { + return; + } + + if (dirty) { + buildSpline(); + } + + + qglColor3fv( controlColor.ToFloatPtr() ); + qglPointSize( 5 ); + + qglBegin(GL_POINTS); + for (i = 0; i < controlPoints.Num(); i++) { + qglVertex3fv( (*controlPoints[i]).ToFloatPtr() ); + } + qglEnd(); + + if (editMode) { + for(i = 0; i < controlPoints.Num(); i++) { + glBox(activeColor, *controlPoints[i], 4); + } + } + + //Draw the curve + qglColor3fv( pathColor.ToFloatPtr() ); + qglBegin(GL_LINE_STRIP); + int count = splinePoints.Num(); + for (i = 0; i < count; i++) { + qglVertex3fv( (*splinePoints[i]).ToFloatPtr() ); + } + qglEnd(); + + if (editMode) { + qglColor3fv( segmentColor.ToFloatPtr() ); + qglPointSize(3); + qglBegin(GL_POINTS); + for (i = 0; i < count; i++) { + qglVertex3fv( (*splinePoints[i]).ToFloatPtr() ); + } + qglEnd(); + } + if (count > 0) { + //assert(activeSegment >=0 && activeSegment < count); + if (activeSegment >=0 && activeSegment < count) { + glBox(activeColor, *splinePoints[activeSegment], 6); + glBox(colorYellow, *splinePoints[activeSegment], 8); + } + } + +} + +/* +================ +idSplineList::totalDistance +================ +*/ +float idSplineList::totalDistance() { + + // FIXME: save dist and return + // + if (controlPoints.Num() == 0) { + return 0.0f; + } + + if (dirty) { + buildSpline(); + } + + float dist = 0.0f; + idVec3 temp; + int count = splinePoints.Num(); + for(int i = 1; i < count; i++) { + temp = *splinePoints[i-1]; + temp -= *splinePoints[i]; + dist += temp.Length(); + } + return dist; +} + +/* +================ +idSplineList::initPosition +================ +*/ +void idSplineList::initPosition(long bt, long totalTime) { + + if (dirty) { + buildSpline(); + } + + if (splinePoints.Num() == 0) { + return; + } + + baseTime = bt; + time = totalTime; + + // calc distance to travel ( this will soon be broken into time segments ) + splineTime.Clear(); + splineTime.Append(bt); + double dist = totalDistance(); + double distSoFar = 0.0; + idVec3 temp; + int count = splinePoints.Num(); + //for(int i = 2; i < count - 1; i++) { + for(int i = 1; i < count; i++) { + temp = *splinePoints[i-1]; + temp -= *splinePoints[i]; + distSoFar += temp.Length(); + double percent = distSoFar / dist; + percent *= totalTime; + splineTime.Append(percent + bt); + } + assert(splineTime.Num() == splinePoints.Num()); + activeSegment = 0; +} + +/* +================ +idSplineList::calcSpline +================ +*/ +float idSplineList::calcSpline(int step, float tension) { + switch(step) { + case 0: return (pow(1 - tension, 3)) / 6; + case 1: return (3 * pow(tension, 3) - 6 * pow(tension, 2) + 4) / 6; + case 2: return (-3 * pow(tension, 3) + 3 * pow(tension, 2) + 3 * tension + 1) / 6; + case 3: return pow(tension, 3) / 6; + } + return 0.0f; +} + +/* +================ +idSplineList::updateSelection +================ +*/ +void idSplineList::updateSelection(const idVec3 &move) { + if (selected) { + dirty = true; + VectorAdd(*selected, move, *selected); + } +} + +/* +================ +idSplineList::setSelectedPoint +================ +*/ +void idSplineList::setSelectedPoint(idVec3 *p) { + if (p) { + p->SnapInt(); + for(int i = 0; i < controlPoints.Num(); i++) { + if ( (*p).Compare( *controlPoints[i], VECTOR_EPSILON ) ) { + selected = controlPoints[i]; + } + } + } else { + selected = NULL; + } +} + +/* +================ +idSplineList::getPosition +================ +*/ +const idVec3 *idSplineList::getPosition(long t) { + static idVec3 interpolatedPos; + + int count = splineTime.Num(); + if (count == 0) { + return &vec3_zero; + } + + assert(splineTime.Num() == splinePoints.Num()); + +#if 0 + float velocity = getVelocity(t); + float timePassed = t - lastTime; + lastTime = t; + + // convert to seconds + timePassed /= 1000; + + float distToTravel = timePassed * velocity; + + distSoFar += distToTravel; + float tempDistance = 0; + + idVec3 temp; + int count = splinePoints.Num(); + //for(int i = 2; i < count - 1; i++) { + for(int i = 1; i < count; i++) { + temp = *splinePoints[i-1]; + temp -= *splinePoints[i]; + tempDistance += temp.Length(); + if (tempDistance >= distSoFar) { + break; + } + } + + if (i == count) { + interpolatedPos = splinePoints[i-1]; + } else { + double timeHi = splineTime[i + 1]; + double timeLo = splineTime[i - 1]; + double percent = (timeHi - t) / (timeHi - timeLo); + idVec3 v1 = *splinePoints[i - 1]; + idVec3 v2 = *splinePoints[i + 1]; + v2 *= (1.0f - percent); + v1 *= percent; + v2 += v1; + interpolatedPos = v2; + } + return &interpolatedPos; + +#else + while (activeSegment < count) { + if (splineTime[activeSegment] >= t) { + if (activeSegment > 0 && activeSegment < count - 1) { + double timeHi = splineTime[activeSegment + 1]; + double timeLo = splineTime[activeSegment - 1]; + //float percent = (float)(baseTime + time - t) / time; + double percent = (timeHi - t) / (timeHi - timeLo); + // pick two bounding points + idVec3 v1 = *splinePoints[activeSegment-1]; + idVec3 v2 = *splinePoints[activeSegment+1]; + v2 *= (1.0f - percent); + v1 *= percent; + v2 += v1; + interpolatedPos = v2; + return &interpolatedPos; + } + return splinePoints[activeSegment]; + } else { + activeSegment++; + } + } + return splinePoints[count-1]; +#endif +} + +/* +================ +idSplineList::parse +================ +*/ +void idSplineList::parse( idParser *src ) { + idToken token; + idStr key; + + src->ExpectTokenString( "{" ); + + while ( 1 ) { + if ( !src->ExpectAnyToken( &token ) ) { + break; + } + if ( token == "}" ) { + break; + } + // if token is not a brace, it is a key for a key/value pair + if ( token == "(" ) { + src->UnreadToken( &token ); + // read the control point + idVec3 point; + src->Parse1DMatrix( 3, point.ToFloatPtr() ); + addPoint(point.x, point.y, point.z); + } + else { + key = token; + src->ReadTokenOnLine( &token ); + if ( !key.Icmp( "granularity" ) ) { + granularity = atof(token.c_str()); + } + else if ( !key.Icmp( "name" ) ) { + name = token; + } + else { + src->Error( "unknown spline list key: %s", key.c_str() ); + break; + } + } + } + dirty = true; +} + +/* +================ +idSplineList::write +================ +*/ +void idSplineList::write( idFile *f, const char *p) { + f->Printf( "\t\t%s {\n", p ); + + //f->Printf( "\t\tname %s\n", name.c_str() ); + f->Printf( "\t\t\tgranularity %f\n", granularity ); + int count = controlPoints.Num(); + for (int i = 0; i < count; i++) { + f->Printf( "\t\t\t( %f %f %f )\n", controlPoints[i]->x, controlPoints[i]->y, controlPoints[i]->z ); + } + f->Printf( "\t\t}\n" ); +} + +/* +================================================================================= + +idCamaraDef + +================================================================================= +*/ + +/* +================ +idCameraDef::clear +================ +*/ +void idCameraDef::clear() { + currentCameraPosition = 0; + cameraRunning = false; + lastDirection.Zero(); + baseTime = 30; + activeTarget = 0; + name = "camera01"; + fov.SetFOV(90); + int i; + for (i = 0; i < targetPositions.Num(); i++) { + delete targetPositions[i]; + } + for (i = 0; i < events.Num(); i++) { + delete events[i]; + } + delete cameraPosition; + cameraPosition = NULL; + events.Clear(); + targetPositions.Clear(); +} + +/* +================ +idCameraDef::startNewCamera +================ +*/ +idCameraPosition *idCameraDef::startNewCamera( idCameraPosition::positionType type ) { + clear(); + if (type == idCameraPosition::SPLINE) { + cameraPosition = new idSplinePosition(); + } else if (type == idCameraPosition::INTERPOLATED) { + cameraPosition = new idInterpolatedPosition(); + } else { + cameraPosition = new idFixedPosition(); + } + return cameraPosition; +} + +/* +================ +idCameraDef::addTarget +================ +*/ +void idCameraDef::addTarget(const char *name, idCameraPosition::positionType type) { + const char *text = (name == NULL) ? va("target0%d", numTargets()+1) : name; + idCameraPosition *pos = newFromType(type); + if (pos) { + pos->setName(name); + targetPositions.Append(pos); + activeTarget = numTargets()-1; + if (activeTarget == 0) { + // first one + addEvent(idCameraEvent::EVENT_TARGET, name, 0); + } + } +} + +/* +================ +idCameraDef::getActiveTarget +================ +*/ +idCameraPosition *idCameraDef::getActiveTarget() { + if (targetPositions.Num() == 0) { + addTarget(NULL, idCameraPosition::FIXED); + } + return targetPositions[activeTarget]; +} + +/* +================ +idCameraDef::getActiveTarget +================ +*/ +idCameraPosition *idCameraDef::getActiveTarget(int index) { + if (targetPositions.Num() == 0) { + addTarget(NULL, idCameraPosition::FIXED); + return targetPositions[0]; + } + return targetPositions[index]; +} + +/* +================ +idCameraDef::setActiveTargetByName +================ +*/ +void idCameraDef::setActiveTargetByName( const char *name ) { + for (int i = 0; i < targetPositions.Num(); i++) { + if (idStr::Icmp(name, targetPositions[i]->getName()) == 0) { + setActiveTarget(i); + return; + } + } +} + +/* +================ +idCameraDef::setActiveTarget +================ +*/ +void idCameraDef::setActiveTarget( int index ) { + assert(index >= 0 && index < targetPositions.Num()); + activeTarget = index; +} + +/* +================ +idCameraDef::draw +================ +*/ +void idCameraDef::draw( bool editMode ) { + // gcc doesn't allow casting away from bools + // why? I've no idea... + if (cameraPosition) { + cameraPosition->draw((bool)((editMode || cameraRunning) && cameraEdit)); + int count = targetPositions.Num(); + for (int i = 0; i < count; i++) { + targetPositions[i]->draw((bool)((editMode || cameraRunning) && i == activeTarget && !cameraEdit)); + } + } +} + +/* +================ +idCameraDef::numPoints +================ +*/ +int idCameraDef::numPoints() { + if (cameraEdit) { + return cameraPosition->numPoints(); + } + return getActiveTarget()->numPoints(); +} + +/* +================ +idCameraDef::getPoint +================ +*/ +const idVec3 *idCameraDef::getPoint(int index) { + if (cameraEdit) { + return cameraPosition->getPoint(index); + } + return getActiveTarget()->getPoint(index); +} + +/* +================ +idCameraDef::stopEdit +================ +*/ +void idCameraDef::stopEdit() { + editMode = false; + if (cameraEdit) { + cameraPosition->stopEdit(); + } else { + getActiveTarget()->stopEdit(); + } +} + +/* +================ +idCameraDef::startEdit +================ +*/ +void idCameraDef::startEdit(bool camera) { + cameraEdit = camera; + if (camera) { + cameraPosition->startEdit(); + for (int i = 0; i < targetPositions.Num(); i++) { + targetPositions[i]->stopEdit(); + } + } else { + getActiveTarget()->startEdit(); + cameraPosition->stopEdit(); + } + editMode = true; +} + +/* +================ +idCameraDef::getPositionObj +================ +*/ +idCameraPosition *idCameraDef::getPositionObj() { + if (cameraPosition == NULL) { + cameraPosition = new idFixedPosition(); + } + return cameraPosition; +} + +/* +================ +idCameraDef::getActiveSegmentInfo +================ +*/ +void idCameraDef::getActiveSegmentInfo(int segment, idVec3 &origin, idVec3 &direction, float *fov) { +#if 0 + if (!cameraSpline.validTime()) { + buildCamera(); + } + double d = (double)segment / numSegments(); + getCameraInfo(d * totalTime * 1000, origin, direction, fov); +#endif +/* + if (!cameraSpline.validTime()) { + buildCamera(); + } + origin = *cameraSpline.getSegmentPoint(segment); + + + idVec3 temp; + + int numTargets = getTargetSpline()->controlPoints.Num(); + int count = cameraSpline.splineTime.Num(); + if (numTargets == 0) { + // follow the path + if (cameraSpline.getActiveSegment() < count - 1) { + temp = *cameraSpline.splinePoints[cameraSpline.getActiveSegment()+1]; + } + } else if (numTargets == 1) { + temp = *getTargetSpline()->controlPoints[0]; + } else { + temp = *getTargetSpline()->getSegmentPoint(segment); + } + + temp -= origin; + temp.Normalize(); + direction = temp; +*/ +} + +/* +================ +idCameraDef::getCameraInfo +================ +*/ +bool idCameraDef::getCameraInfo(long time, idVec3 &origin, idVec3 &direction, float *fv) { + char buff[ 1024 ]; + int i; + + if ((time - startTime) / 1000 <= totalTime) { + + for( i = 0; i < events.Num(); i++ ) { + if (time >= startTime + events[i]->getTime() && !events[i]->getTriggered()) { + events[i]->setTriggered(true); + if (events[i]->getType() == idCameraEvent::EVENT_TARGET) { + setActiveTargetByName(events[i]->getParam()); + getActiveTarget()->start(startTime + events[i]->getTime()); + //common->Printf("Triggered event switch to target: %s\n",events[i]->getParam()); + } else if (events[i]->getType() == idCameraEvent::EVENT_TRIGGER) { +#if 0 +//FIXME: seperate game and editor spline code + idEntity *ent; + ent = gameLocal.FindEntity( events[i]->getParam() ); + if (ent) { + ent->Signal( SIG_TRIGGER ); + ent->ProcessEvent( &EV_Activate, gameLocal.world ); + } +#endif + } else if (events[i]->getType() == idCameraEvent::EVENT_FOV) { + memset(buff, 0, sizeof(buff)); + strcpy(buff, events[i]->getParam()); + const char *param1 = strtok(buff, " \t,\0"); + const char *param2 = strtok(NULL, " \t,\0"); + fov.reset(fov.GetFOV(time), atof(param1), time, atoi(param2)); + //*fv = fov = atof(events[i]->getParam()); + } else if (events[i]->getType() == idCameraEvent::EVENT_CAMERA) { + } else if (events[i]->getType() == idCameraEvent::EVENT_STOP) { + return false; + } + } + } + } else { + } + + origin = *cameraPosition->getPosition(time); + + *fv = fov.GetFOV(time); + + idVec3 temp = origin; + + int numTargets = targetPositions.Num(); + if (numTargets == 0) { +/* + // follow the path + if (cameraSpline.getActiveSegment() < count - 1) { + temp = *cameraSpline.splinePoints[cameraSpline.getActiveSegment()+1]; + if (temp == origin) { + int index = cameraSpline.getActiveSegment() + 2; + while (temp == origin && index < count - 1) { + temp = *cameraSpline.splinePoints[index++]; + } + } + } +*/ + } else { + temp = *getActiveTarget()->getPosition(time); + } + + temp -= origin; + temp.Normalize(); + direction = temp; + + return true; +} + +/* +================ +idCameraDef::waitEvent +================ +*/ +bool idCameraDef::waitEvent(int index) { + //for (int i = 0; i < events.Num(); i++) { + // if (events[i]->getSegment() == index && events[i]->getType() == idCameraEvent::EVENT_WAIT) { + // return true; + // } + //} + return false; +} + +/* +================ +idCameraDef::buildCamera +================ +*/ +#define NUM_CCELERATION_SEGS 10 +#define CELL_AMT 5 + +void idCameraDef::buildCamera() { + int i; + int lastSwitch = 0; + idList waits; + idList targets; + + totalTime = baseTime; + cameraPosition->setTime(totalTime * 1000); + // we have a base time layout for the path and the target path + // now we need to layer on any wait or speed changes + for (i = 0; i < events.Num(); i++) { + idCameraEvent *ev = events[i]; + events[i]->setTriggered(false); + switch (events[i]->getType()) { + case idCameraEvent::EVENT_TARGET : { + targets.Append(i); + break; + } + case idCameraEvent::EVENT_FEATHER : { + long startTime = 0; + float speed = 0; + long loopTime = 10; + float stepGoal = cameraPosition->getBaseVelocity() / (1000 / loopTime); + while (startTime <= 1000) { + cameraPosition->addVelocity(startTime, loopTime, speed); + speed += stepGoal; + if (speed > cameraPosition->getBaseVelocity()) { + speed = cameraPosition->getBaseVelocity(); + } + startTime += loopTime; + } + + startTime = totalTime * 1000 - 1000; + long endTime = startTime + 1000; + speed = cameraPosition->getBaseVelocity(); + while (startTime < endTime) { + speed -= stepGoal; + if (speed < 0) { + speed = 0; + } + cameraPosition->addVelocity(startTime, loopTime, speed); + startTime += loopTime; + } + break; + + } + case idCameraEvent::EVENT_WAIT : { + waits.Append(atof(events[i]->getParam())); + + //FIXME: this is quite hacky for Wolf E3, accel and decel needs + // do be parameter based etc.. + long startTime = events[i]->getTime() - 1000; + if (startTime < 0) { + startTime = 0; + } + float speed = cameraPosition->getBaseVelocity(); + long loopTime = 10; + float steps = speed / ((events[i]->getTime() - startTime) / loopTime); + while (startTime <= events[i]->getTime() - loopTime) { + cameraPosition->addVelocity(startTime, loopTime, speed); + speed -= steps; + startTime += loopTime; + } + cameraPosition->addVelocity(events[i]->getTime(), atof(events[i]->getParam()) * 1000, 0); + + startTime = events[i]->getTime() + atof(events[i]->getParam()) * 1000; + long endTime = startTime + 1000; + speed = 0; + while (startTime <= endTime) { + cameraPosition->addVelocity(startTime, loopTime, speed); + speed += steps; + startTime += loopTime; + } + break; + } + case idCameraEvent::EVENT_TARGETWAIT : { + //targetWaits.Append(i); + break; + } + case idCameraEvent::EVENT_SPEED : { +/* + // take the average delay between up to the next five segments + float adjust = atof(events[i]->getParam()); + int index = events[i]->getSegment(); + total = 0; + count = 0; + + // get total amount of time over the remainder of the segment + for (j = index; j < cameraSpline.numSegments() - 1; j++) { + total += cameraSpline.getSegmentTime(j + 1) - cameraSpline.getSegmentTime(j); + count++; + } + + // multiply that by the adjustment + double newTotal = total * adjust; + // what is the difference.. + newTotal -= total; + totalTime += newTotal / 1000; + + // per segment difference + newTotal /= count; + int additive = newTotal; + + // now propogate that difference out to each segment + for (j = index; j < cameraSpline.numSegments(); j++) { + cameraSpline.addSegmentTime(j, additive); + additive += newTotal; + } + break; +*/ + } + } + } + + + for (i = 0; i < waits.Num(); i++) { + totalTime += waits[i]; + } + + // on a new target switch, we need to take time to this point ( since last target switch ) + // and allocate it across the active target, then reset time to this point + long timeSoFar = 0; + long total = totalTime * 1000; + for (i = 0; i < targets.Num(); i++) { + long t; + if (i < targets.Num() - 1) { + t = events[targets[i+1]]->getTime(); + } else { + t = total - timeSoFar; + } + // t is how much time to use for this target + setActiveTargetByName(events[targets[i]]->getParam()); + getActiveTarget()->setTime(t); + timeSoFar += t; + } +} + +/* +================ +idCameraDef::startCamera +================ +*/ +void idCameraDef::startCamera(long t) { + cameraPosition->clearVelocities(); + cameraPosition->start(t); + buildCamera(); + //for (int i = 0; i < targetPositions.Num(); i++) { + // targetPositions[i]-> + //} + startTime = t; + cameraRunning = true; +} + +/* +================ +idCameraDef::parse +================ +*/ +void idCameraDef::parse( idParser *src ) { + idToken token; + + src->ReadToken(&token); + src->ExpectTokenString( "{" ); + while ( 1 ) { + + src->ExpectAnyToken( &token ); + + if ( token == "}" ) { + break; + } + else if ( !token.Icmp( "time" ) ) { + baseTime = src->ParseFloat(); + } + else if ( !token.Icmp( "camera_fixed") ) { + cameraPosition = new idFixedPosition(); + cameraPosition->parse( src ); + } + else if ( !token.Icmp( "camera_interpolated") ) { + cameraPosition = new idInterpolatedPosition(); + cameraPosition->parse( src ); + } + else if ( !token.Icmp( "camera_spline") ) { + cameraPosition = new idSplinePosition(); + cameraPosition->parse( src ); + } + else if ( !token.Icmp( "target_fixed") ) { + idFixedPosition *pos = new idFixedPosition(); + pos->parse( src ); + targetPositions.Append(pos); + } + else if ( !token.Icmp( "target_interpolated") ) { + idInterpolatedPosition *pos = new idInterpolatedPosition(); + pos->parse( src ); + targetPositions.Append(pos); + } + else if ( !token.Icmp( "target_spline") ) { + idSplinePosition *pos = new idSplinePosition(); + pos->parse( src ); + targetPositions.Append(pos); + } + else if ( !token.Icmp( "fov") ) { + fov.parse( src ); + } + else if ( !token.Icmp( "event") ) { + idCameraEvent *event = new idCameraEvent(); + event->parse( src ); + addEvent(event); + } + else { + src->Error( "unknown camera def: %s", token.c_str() ); + break; + } + } + + if ( !cameraPosition ) { + common->Printf( "no camera position specified\n" ); + // prevent a crash later on + cameraPosition = new idFixedPosition(); + } +} + +/* +================ +idCameraDef::load +================ +*/ +bool idCameraDef::load( const char *filename ) { + idParser *src; + + src = new idParser( filename, LEXFL_NOSTRINGCONCAT | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES ); + if ( !src->IsLoaded() ) { + common->Printf( "couldn't load %s\n", filename ); + delete src; + return false; + } + + clear(); + parse( src ); + + delete src; + + return true; +} + +/* +================ +idCameraDef::save +================ +*/ +void idCameraDef::save(const char *filename) { + idFile *f = fileSystem->OpenFileWrite( filename, "fs_devpath" ); + if ( f ) { + int i; + f->Printf( "cameraPathDef { \n" ); + f->Printf( "\ttime %f\n", baseTime ); + + cameraPosition->write( f, va("camera_%s",cameraPosition->typeStr()) ); + + for (i = 0; i < numTargets(); i++) { + targetPositions[i]->write( f, va("target_%s", targetPositions[i]->typeStr()) ); + } + + for (i = 0; i < events.Num(); i++) { + events[i]->write( f, "event" ); + } + + fov.write( f, "fov" ); + + f->Printf( "}\n" ); + } + fileSystem->CloseFile( f ); +} + +/* +================ +idCameraDef::sortEvents +================ +*/ +int idCameraDef::sortEvents(const void *p1, const void *p2) { + idCameraEvent *ev1 = (idCameraEvent*)(p1); + idCameraEvent *ev2 = (idCameraEvent*)(p2); + + if (ev1->getTime() > ev2->getTime()) { + return -1; + } + if (ev1->getTime() < ev2->getTime()) { + return 1; + } + return 0; +} + +/* +================ +idCameraDef::addEvent +================ +*/ +void idCameraDef::addEvent(idCameraEvent *event) { + events.Append(event); + //events.Sort(&sortEvents); + +} + +/* +================ +idCameraDef::addEvent +================ +*/ +void idCameraDef::addEvent(idCameraEvent::eventType t, const char *param, long time) { + addEvent(new idCameraEvent(t, param, time)); + buildCamera(); +} + +/* +================ +idCameraDef::newFromType +================ +*/ +idCameraPosition *idCameraDef::newFromType( idCameraPosition::positionType t ) { + switch (t) { + case idCameraPosition::FIXED : return new idFixedPosition(); + case idCameraPosition::INTERPOLATED : return new idInterpolatedPosition(); + case idCameraPosition::SPLINE : return new idSplinePosition(); + }; + return NULL; +} + + +/* +================================================================================= + +idCamaraEvent + +================================================================================= +*/ + +/* +================ +idCameraEvent::eventStr +================ +*/ +const char *idCameraEvent::eventStr[] = { + "NA", + "WAIT", + "TARGETWAIT", + "SPEED", + "TARGET", + "SNAPTARGET", + "FOV", + "CMD", + "TRIGGER", + "STOP", + "CAMERA", + "FADEOUT", + "FADEIN", + "FEATHER" +}; + +/* +================ +idCameraEvent::parse +================ +*/ +void idCameraEvent::parse( idParser *src ) { + idToken token; + idStr key; + + src->ExpectTokenString( "{" ); + + while ( 1 ) { + + if ( !src->ExpectAnyToken( &token ) ) { + break; + } + if ( token == "}" ) { + break; + } + + key = token; + src->ReadTokenOnLine( &token ); + if ( !key.Icmp( "type" ) ) { + type = static_cast(atoi(token.c_str())); + } + else if ( !key.Icmp( "param" ) ) { + paramStr = token; + } + else if ( !key.Icmp( "time" ) ) { + time = atoi(token.c_str()); + } + else { + src->Error( "unknown camera event key: %s", key.c_str() ); + break; + } + } +} + +/* +================ +idCameraEvent::write +================ +*/ +void idCameraEvent::write( idFile *f, const char *name) { + f->Printf( "\t%s {\n", name ); + f->Printf( "\t\ttype %d\n", static_cast(type) ); + f->Printf( "\t\tparam \"%s\"\n", paramStr.c_str() ); + f->Printf( "\t\ttime %d\n", time ); + f->Printf( "\t}\n" ); +} + +/* +================================================================================= + +idCamaraPosition + +================================================================================= +*/ + +/* +================ +idCameraPosition::positionStr +================ +*/ +const char *idCameraPosition::positionStr[] = { + "Fixed", + "Interpolated", + "Spline", +}; + +/* +================ +idCameraPosition::positionStr +================ +*/ +void idCameraPosition::clearVelocities() { + for (int i = 0; i < velocities.Num(); i++) { + delete velocities[i]; + velocities[i] = NULL; + } + velocities.Clear(); +} + +/* +================ +idCameraPosition::positionStr +================ +*/ +float idCameraPosition::getVelocity( long t ) { + long check = t - startTime; + for ( int i = 0; i < velocities.Num(); i++ ) { + if (check >= velocities[i]->startTime && check <= velocities[i]->startTime + velocities[i]->time) { + return velocities[i]->speed; + } + } + return baseVelocity; +} + +/* +================ +idCameraPosition::parseToken +================ +*/ +bool idCameraPosition::parseToken( const idStr &key, idParser *src ) { + idToken token; + + if ( !key.Icmp( "time" ) ) { + time = src->ParseInt(); + return true; + } + else if ( !key.Icmp( "type" ) ) { + type = static_cast ( src->ParseInt() ); + return true; + } + else if ( !key.Icmp( "velocity" ) ) { + long t = atol(token); + long d = src->ParseInt(); + float s = src->ParseFloat(); + addVelocity(t, d, s); + return true; + } + else if ( !key.Icmp( "baseVelocity" ) ) { + baseVelocity = src->ParseFloat(); + return true; + } + else if ( !key.Icmp( "name" ) ) { + src->ReadToken( &token ); + name = token; + return true; + } + else if ( !key.Icmp( "time" ) ) { + time = src->ParseInt(); + return true; + } + else { + src->Error( "unknown camera position key: %s", key.c_str() ); + return false; + } +} + +/* +================ +idCameraPosition::write +================ +*/ +void idCameraPosition::write( idFile *f, const char *p ) { + f->Printf( "\t\ttime %i\n", time ); + f->Printf( "\t\ttype %i\n", static_cast(type) ); + f->Printf( "\t\tname %s\n", name.c_str() ); + f->Printf( "\t\tbaseVelocity %f\n", baseVelocity ); + for (int i = 0; i < velocities.Num(); i++) { + f->Printf( "\t\tvelocity %i %i %f\n", velocities[i]->startTime, velocities[i]->time, velocities[i]->speed ); + } +} + +/* +================================================================================= + +idInterpolatedPosition + +================================================================================= +*/ + +/* +================ +idInterpolatedPosition::getPoint +================ +*/ +idVec3 *idInterpolatedPosition::getPoint( int index ) { + assert( index >= 0 && index < 2 ); + if ( index == 0 ) { + return &startPos; + } + return &endPos; +} + +/* +================ +idInterpolatedPosition::addPoint +================ +*/ +void idInterpolatedPosition::addPoint( const float x, const float y, const float z ) { + if (first) { + startPos.Set(x, y, z); + first = false; + } else { + endPos.Set(x, y, z); + first = true; + } +} + +/* +================ +idInterpolatedPosition::addPoint +================ +*/ +void idInterpolatedPosition::addPoint( const idVec3 &v ) { + if (first) { + startPos = v; + first = false; + } + else { + endPos = v; + first = true; + } +} + +/* +================ +idInterpolatedPosition::draw +================ +*/ +void idInterpolatedPosition::draw( bool editMode ) { + glLabeledPoint(colorBlue, startPos, (editMode) ? 5 : 3, "Start interpolated"); + glLabeledPoint(colorBlue, endPos, (editMode) ? 5 : 3, "End interpolated"); + qglBegin(GL_LINES); + qglVertex3fv( startPos.ToFloatPtr() ); + qglVertex3fv( endPos.ToFloatPtr() ); + qglEnd(); +} + +/* +================ +idInterpolatedPosition::start +================ +*/ +void idInterpolatedPosition::start( long t ) { + idCameraPosition::start(t); + lastTime = startTime; + distSoFar = 0.0f; + idVec3 temp = startPos; + temp -= endPos; + calcVelocity(temp.Length()); +} + +/* +================ +idInterpolatedPosition::getPosition +================ +*/ +const idVec3 *idInterpolatedPosition::getPosition( long t ) { + static idVec3 interpolatedPos; + + if (t - startTime > 6000) { + int i = 0; + } + + float velocity = getVelocity(t); + float timePassed = t - lastTime; + lastTime = t; + + // convert to seconds + timePassed /= 1000; + + if (velocity != getBaseVelocity()) { + int i = 0; + } + + float distToTravel = timePassed * velocity; + + idVec3 temp = startPos; + temp -= endPos; + float distance = temp.Length(); + + distSoFar += distToTravel; + float percent = (float)(distSoFar) / distance; + + if ( percent > 1.0f ) { + percent = 1.0f; + } else if ( percent < 0.0f ) { + percent = 0.0f; + } + + // the following line does a straigt calc on percentage of time + // float percent = (float)(startTime + time - t) / time; + + idVec3 v1 = startPos; + idVec3 v2 = endPos; + v1 *= (1.0f - percent); + v2 *= percent; + v1 += v2; + interpolatedPos = v1; + return &interpolatedPos; +} + +/* +================ +idInterpolatedPosition::parse +================ +*/ +void idInterpolatedPosition::parse( idParser *src ) { + idToken token; + + src->ExpectTokenString( "{" ); + while ( 1 ) { + if ( !src->ExpectAnyToken( &token ) ) { + break; + } + if ( token == "}" ) { + break; + } + + if ( !token.Icmp( "startPos" ) ) { + src->Parse1DMatrix( 3, startPos.ToFloatPtr() ); + } + else if ( !token.Icmp( "endPos" ) ) { + src->Parse1DMatrix( 3, endPos.ToFloatPtr() ); + } + else { + idCameraPosition::parseToken( token, src); + } + } +} + +/* +================ +idInterpolatedPosition::write +================ +*/ +void idInterpolatedPosition::write( idFile *f, const char *p ) { + f->Printf( "\t%s {\n", p ); + idCameraPosition::write( f, p ); + f->Printf( "\t\tstartPos ( %f %f %f )\n", startPos.x, startPos.y, startPos.z ); + f->Printf( "\t\tendPos ( %f %f %f )\n", endPos.x, endPos.y, endPos.z ); + f->Printf( "\t}\n" ); +} + +/* +================================================================================= + +idCameraFOV + +================================================================================= +*/ + +/* +================ +idCameraFOV::GetFOV +================ +*/ +float idCameraFOV::GetFOV( long t ) { + if (time) { + assert(startTime); + float percent = (t - startTime) / length; + if ( percent < 0.0f ) { + percent = 0.0f; + } else if ( percent > 1.0f ) { + percent = 1.0f; + } + float temp = endFOV - startFOV; + temp *= percent; + fov = startFOV + temp; + } + return fov; +} + +/* +================ +idCameraFOV::reset +================ +*/ +void idCameraFOV::reset( float startfov, float endfov, int start, int len ) { + startFOV = startfov; + endFOV = endfov; + startTime = start; + length = len; +} + +/* +================ +idCameraFOV::parse +================ +*/ +void idCameraFOV::parse( idParser *src ) { + idToken token; + + src->ExpectTokenString( "{" ); + while ( 1 ) { + if ( !src->ExpectAnyToken( &token ) ) { + break; + } + if ( token == "}" ) { + break; + } + + if ( !token.Icmp( "fov" ) ) { + fov = src->ParseFloat(); + } + else if ( !token.Icmp( "startFOV" ) ) { + startFOV = src->ParseFloat(); + } + else if ( !token.Icmp( "endFOV" ) ) { + endFOV = src->ParseFloat(); + } + else if ( !token.Icmp( "time" ) ) { + time = src->ParseInt(); + } + else { + src->Error( "unknown camera FOV key: %s", token.c_str() ); + break; + } + } +} + +/* +================ +idCameraFOV::write +================ +*/ +void idCameraFOV::write( idFile *f, const char *p ) { + f->Printf( "\t%s {\n", p ); + f->Printf( "\t\tfov %f\n", fov ); + f->Printf( "\t\tstartFOV %f\n", startFOV ); + f->Printf( "\t\tendFOV %f\n", endFOV ); + f->Printf( "\t\ttime %i\n", time ); + f->Printf( "\t}\n" ); +} + +/* +================================================================================= + +idFixedPosition + +================================================================================= +*/ + +/* +================ +idFixedPosition::parse +================ +*/ +void idFixedPosition::parse( idParser *src ) { + idToken token; + + src->ExpectTokenString( "{" ); + while ( 1 ) { + if ( !src->ExpectAnyToken( &token ) ) { + break; + } + if ( token == "}" ) { + break; + } + + if ( !token.Icmp( "pos" ) ) { + src->Parse1DMatrix( 3, pos.ToFloatPtr() ); + } + else { + idCameraPosition::parseToken( token, src ); + } + } +} + +/* +================ +idFixedPosition::write +================ +*/ +void idFixedPosition::write( idFile *f, const char *p ) { + f->Printf( "\t%s {\n", p ); + idCameraPosition::write( f, p ); + f->Printf( "\t\tpos ( %f %f %f )\n", pos.x, pos.y, pos.z ); + f->Printf( "\t}\n" ); +} + +/* +================================================================================= + +idSplinePosition + +================================================================================= +*/ + +/* +================ +idSplinePosition::start +================ +*/ +void idSplinePosition::start( long t ) { + idCameraPosition::start( t ); + target.initPosition(t, time); + lastTime = startTime; + distSoFar = 0.0f; + calcVelocity(target.totalDistance()); +} + +/* +================ +idSplinePosition::parse +================ +*/ +void idSplinePosition::parse( idParser *src ) { + idToken token; + + src->ExpectTokenString( "{" ); + while ( 1 ) { + if ( !src->ExpectAnyToken( &token ) ) { + break; + } + if ( token == "}" ) { + break; + } + if ( !token.Icmp( "target" ) ) { + target.parse( src ); + } + else { + idCameraPosition::parseToken( token, src ); + } + } +} + +/* +================ +idSplinePosition::write +================ +*/ +void idSplinePosition::write( idFile *f, const char *p ) { + f->Printf( "\t%s {\n", p ); + idCameraPosition::write( f, p ); + target.write( f, "target" ); + f->Printf( "\t}\n" ); +} + +/* +================ +idSplinePosition::getPosition +================ +*/ +const idVec3 *idSplinePosition::getPosition(long t) { + static idVec3 interpolatedPos; + + float velocity = getVelocity(t); + float timePassed = t - lastTime; + lastTime = t; + + // convert to seconds + timePassed /= 1000; + + float distToTravel = timePassed * velocity; + + distSoFar += distToTravel; + double tempDistance = target.totalDistance(); + + double percent = (double)(distSoFar) / tempDistance; + + double targetDistance = percent * tempDistance; + tempDistance = 0; + + double lastDistance1,lastDistance2; + lastDistance1 = lastDistance2 = 0; + //FIXME: calc distances on spline build + idVec3 temp; + int count = target.numSegments(); + //for(int i = 2; i < count - 1; i++) { + int i; + for( i = 1; i < count; i++) { + temp = *target.getSegmentPoint(i-1); + temp -= *target.getSegmentPoint(i); + tempDistance += temp.Length(); + if (i & 1) { + lastDistance1 = tempDistance; + } else { + lastDistance2 = tempDistance; + } + if (tempDistance >= targetDistance) { + break; + } + } + + if (i >= count - 1) { + interpolatedPos = *target.getSegmentPoint(i-1); + } else { +#if 0 + double timeHi = target.getSegmentTime(i + 1); + double timeLo = target.getSegmentTime(i - 1); + double percent = (timeHi - t) / (timeHi - timeLo); + idVec3 v1 = *target.getSegmentPoint(i - 1); + idVec3 v2 = *target.getSegmentPoint(i + 1); + v2 *= (1.0f - percent); + v1 *= percent; + v2 += v1; + interpolatedPos = v2; +#else + if (lastDistance1 > lastDistance2) { + double d = lastDistance2; + lastDistance2 = lastDistance1; + lastDistance1 = d; + } + + idVec3 v1 = *target.getSegmentPoint(i - 1); + idVec3 v2 = *target.getSegmentPoint(i); + double percent = (lastDistance2 - targetDistance) / (lastDistance2 - lastDistance1); + v2 *= (1.0f - percent); + v1 *= percent; + v2 += v1; + interpolatedPos = v2; +#endif + } + return &interpolatedPos; + +} diff --git a/src/tools/radiant/splines.h b/src/tools/radiant/splines.h new file mode 100644 index 0000000..12eafb4 --- /dev/null +++ b/src/tools/radiant/splines.h @@ -0,0 +1,399 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef __SPLINES_H__ +#define __SPLINES_H__ + +extern void glBox(idVec4 &color, idVec3 &point, float size); +extern void glLabeledPoint(idVec4 &color, idVec3 &point, float size, const char *label); + + +class idPointListInterface { +public: + idPointListInterface() { selectedPoints.Clear(); }; + ~idPointListInterface() {}; + + virtual int numPoints() { return 0; } + virtual void addPoint( const float x, const float y, const float z ) {} + virtual void addPoint( const idVec3 &v ) {} + virtual void removePoint( int index ) {} + virtual idVec3 * getPoint( int index ) { return NULL; } + + int numSelectedPoints() { return selectedPoints.Num(); } + idVec3 * getSelectedPoint( int index ); + int selectPointByRay( const idVec3 &origin, const idVec3 &direction, bool single ); + int isPointSelected( int index ); + int selectPoint( int index, bool single ); + void selectAll(); + void deselectAll(); + virtual void updateSelection( const idVec3 &move ); + void drawSelection(); + +protected: + idList selectedPoints; +}; + + +class idSplineList { + friend class idCamera; + +public: + + idSplineList() { clear(); } + idSplineList( const char *p ) { clear(); name = p; } + ~idSplineList() { clear(); } + + void clearControl(); + void clearSpline(); + void parse( idParser *src ); + void write( idFile *f, const char *name ); + + void clear(); + void initPosition( long startTime, long totalTime ); + const idVec3 * getPosition( long time ); + + void draw( bool editMode ); + void addToRenderer(); + + void setSelectedPoint( idVec3 *p ); + idVec3 * getSelectedPoint() { return selected; } + + void addPoint( const idVec3 &v ) { controlPoints.Append(new idVec3(v) ); dirty = true; } + void addPoint( float x, float y, float z ) { controlPoints.Append(new idVec3(x, y, z)); dirty = true; } + + void updateSelection(const idVec3 &move); + void startEdit() { editMode = true; } + void stopEdit() { editMode = false; } + void buildSpline(); + void setGranularity( float f ) { granularity = f; } + float getGranularity() { return granularity; } + + int numPoints() { return controlPoints.Num(); } + idVec3 * getPoint(int index) { assert(index >= 0 && index < controlPoints.Num()); return controlPoints[index]; } + idVec3 * getSegmentPoint(int index) { assert(index >= 0 && index < splinePoints.Num()); return splinePoints[index]; } + void setSegmentTime(int index, int time) { assert(index >= 0 && index < splinePoints.Num()); splineTime[index] = time; } + int getSegmentTime(int index) { assert(index >= 0 && index < splinePoints.Num()); return splineTime[index]; } + void addSegmentTime(int index, int time) { assert(index >= 0 && index < splinePoints.Num()); splineTime[index] += time; } + float totalDistance(); + + int getActiveSegment() { return activeSegment; } + void setActiveSegment( int i ) { /* assert(i >= 0 && (splinePoints.Num() > 0 && i < splinePoints.Num())); */ activeSegment = i; } + int numSegments() { return splinePoints.Num(); } + + void setColors(idVec4 &path, idVec4 &segment, idVec4 &control, idVec4 &active); + + const char * getName() { return name.c_str(); } + void setName( const char *p ) { name = p; } + + bool validTime(); + void setTime( long t ) { time = t; } + void setBaseTime( long t ) { baseTime = t; } + +protected: + idStr name; + float calcSpline(int step, float tension); + idList controlPoints; + idList splinePoints; + idList splineTime; + idVec3 * selected; + idVec4 pathColor, segmentColor, controlColor, activeColor; + float granularity; + bool editMode; + bool dirty; + int activeSegment; + long baseTime; + long time; +}; + +// time in milliseconds +// velocity where 1.0 equal rough walking speed +struct idVelocity { + idVelocity( long start, long duration, float s ) { startTime = start; time = duration; speed = s; } + long startTime; + long time; + float speed; +}; + +// can either be a look at or origin position for a camera +class idCameraPosition : public idPointListInterface { +public: + + idCameraPosition() { time = 0; name = "position"; } + idCameraPosition( const char *p ) { name = p; } + idCameraPosition( long t ) { time = t; } + virtual ~idCameraPosition() { clear(); } + + // this can be done with RTTI syntax but i like the derived classes setting a type + // makes serialization a bit easier to see + // + enum positionType { + FIXED = 0x00, + INTERPOLATED, + SPLINE, + POSITION_COUNT + }; + + virtual void clearVelocities(); + virtual void clear() { editMode = false; time = 5000; clearVelocities(); } + virtual void start( long t ) { startTime = t; } + long getTime() { return time; } + virtual void setTime(long t) { time = t; } + float getVelocity( long t ); + float getBaseVelocity() { return baseVelocity; } + void addVelocity( long start, long duration, float speed ) { velocities.Append(new idVelocity(start, duration, speed)); } + virtual const idVec3 *getPosition( long t ) { return NULL; } + virtual void draw( bool editMode ) {}; + virtual void parse( idParser *src ) {}; + virtual void write( idFile *f, const char *name); + virtual bool parseToken( const idStr &key, idParser *src ); + const char * getName() { return name.c_str(); } + void setName( const char *p ) { name = p; } + virtual void startEdit() { editMode = true; } + virtual void stopEdit() { editMode = false; } + virtual void draw() {}; + const char * typeStr() { return positionStr[static_cast(type)]; } + void calcVelocity( float distance ) { float secs = (float)time / 1000; baseVelocity = distance / secs; } + +protected: + static const char * positionStr[POSITION_COUNT]; + long startTime; + long time; + positionType type; + idStr name; + bool editMode; + idList velocities; + float baseVelocity; +}; + +class idFixedPosition : public idCameraPosition { +public: + + idFixedPosition() : idCameraPosition() { init(); } + idFixedPosition(idVec3 p) : idCameraPosition() { init(); pos = p; } + ~idFixedPosition() { } + + void init() { pos.Zero(); type = idCameraPosition::FIXED; } + + virtual void addPoint( const idVec3 &v ) { pos = v; } + virtual void addPoint( const float x, const float y, const float z ) { pos.Set(x, y, z); } + virtual const idVec3 *getPosition( long t ) { return &pos; } + void parse( idParser *src ); + void write( idFile *f, const char *name ); + virtual int numPoints() { return 1; } + virtual idVec3 * getPoint( int index ) { assert( index == 0 ); return &pos; } + virtual void draw( bool editMode ) { glLabeledPoint(colorBlue, pos, (editMode) ? 5 : 3, "Fixed point"); } + +protected: + idVec3 pos; +}; + +class idInterpolatedPosition : public idCameraPosition { +public: + idInterpolatedPosition() : idCameraPosition() { init(); } + idInterpolatedPosition( idVec3 start, idVec3 end, long time ) : idCameraPosition(time) { init(); startPos = start; endPos = end; } + ~idInterpolatedPosition() { } + + void init() { type = idCameraPosition::INTERPOLATED; first = true; startPos.Zero(); endPos.Zero(); } + + virtual const idVec3 *getPosition(long t); + void parse( idParser *src ); + void write( idFile *f, const char *name ); + virtual int numPoints() { return 2; } + virtual idVec3 * getPoint( int index ); + virtual void addPoint( const float x, const float y, const float z ); + virtual void addPoint( const idVec3 &v ); + virtual void draw( bool editMode ); + virtual void start( long t ); + +protected: + bool first; + idVec3 startPos; + idVec3 endPos; + long lastTime; + float distSoFar; +}; + +class idSplinePosition : public idCameraPosition { +public: + + idSplinePosition() : idCameraPosition() { init(); } + idSplinePosition( long time ) : idCameraPosition( time ) { init(); } + ~idSplinePosition() { } + + void init() { type = idCameraPosition::SPLINE; } + virtual void start( long t ); + virtual const idVec3 *getPosition( long t ); + void addControlPoint( idVec3 &v ) { target.addPoint(v); } + void parse( idParser *src ); + void write( idFile *f, const char *name ); + virtual int numPoints() { return target.numPoints(); } + virtual idVec3 * getPoint( int index ) { return target.getPoint(index); } + virtual void addPoint( const idVec3 &v ) { target.addPoint( v ); } + virtual void draw( bool editMode ) { target.draw( editMode ); } + virtual void updateSelection( const idVec3 &move ) { idCameraPosition::updateSelection(move); target.buildSpline(); } + +protected: + idSplineList target; + long lastTime; + float distSoFar; +}; + +class idCameraFOV { +public: + idCameraFOV() { time = 0; fov = 90; } + idCameraFOV( int v ) { time = 0; fov = v; } + idCameraFOV( int s, int e, long t ) { startFOV = s; endFOV = e; time = t; } + ~idCameraFOV() { } + + void SetFOV( float f ) { fov = f; } + float GetFOV( long t ); + void start( long t ) { startTime = t; } + void reset( float startfov, float endfov, int start, int len ); + void parse( idParser *src ); + void write( idFile *f, const char *name ); + +protected: + float fov; + float startFOV; + float endFOV; + int startTime; + int time; + int length; +}; + +class idCameraEvent { +public: + enum eventType { + EVENT_NA = 0x00, + EVENT_WAIT, + EVENT_TARGETWAIT, + EVENT_SPEED, + EVENT_TARGET, + EVENT_SNAPTARGET, + EVENT_FOV, + EVENT_CMD, + EVENT_TRIGGER, + EVENT_STOP, + EVENT_CAMERA, + EVENT_FADEOUT, + EVENT_FADEIN, + EVENT_FEATHER, + EVENT_COUNT + }; + + idCameraEvent() { paramStr = ""; type = EVENT_NA; time = 0; } + idCameraEvent( eventType t, const char *param, long n ) { type = t; paramStr = param; time = n; } + ~idCameraEvent() { } + + eventType getType() { return type; } + const char * typeStr() { return eventStr[static_cast(type)]; } + const char * getParam() { return paramStr.c_str(); } + long getTime() { return time; } + void setTime(long n) { time = n; } + void parse( idParser *src ); + void write( idFile *f, const char *name ); + void setTriggered( bool b ) { triggered = b; } + bool getTriggered() { return triggered; } + + static const char * eventStr[EVENT_COUNT]; + +protected: + eventType type; + idStr paramStr; + long time; + bool triggered; + +}; + +class idCameraDef { +public: + idCameraDef() { cameraPosition = NULL; clear(); } + ~idCameraDef() { clear(); } + + void clear(); + idCameraPosition * startNewCamera(idCameraPosition::positionType type); + void addEvent( idCameraEvent::eventType t, const char *param, long time ); + void addEvent( idCameraEvent *event ); + static int sortEvents( const void *p1, const void *p2 ); + int numEvents() { return events.Num(); } + idCameraEvent * getEvent(int index) { assert(index >= 0 && index < events.Num()); return events[index]; } + void parse( idParser *src ); + bool load( const char *filename ); + void save( const char *filename ); + void buildCamera(); + + void addTarget( const char *name, idCameraPosition::positionType type ); + + idCameraPosition * getActiveTarget(); + idCameraPosition * getActiveTarget( int index ); + int numTargets() { return targetPositions.Num(); } + void setActiveTargetByName(const char *name); + void setActiveTarget( int index ); + void setRunning( bool b ) { cameraRunning = b; } + void setBaseTime( float f ) { baseTime = f; } + float getBaseTime() { return baseTime; } + float getTotalTime() { return totalTime; } + void startCamera( long t ); + void stopCamera() { cameraRunning = true; } + void getActiveSegmentInfo(int segment, idVec3 &origin, idVec3 &direction, float *fv); + bool getCameraInfo(long time, idVec3 &origin, idVec3 &direction, float *fv); + void draw( bool editMode ); + int numPoints(); + const idVec3 * getPoint( int index ); + void stopEdit(); + void startEdit( bool camera ); + bool waitEvent( int index ); + const char * getName() { return name.c_str(); } + void setName( const char *p ) { name = p; } + idCameraPosition * getPositionObj(); + + static idCameraPosition *newFromType( idCameraPosition::positionType t ); + +protected: + idStr name; + int currentCameraPosition; + idVec3 lastDirection; + bool cameraRunning; + idCameraPosition * cameraPosition; + idList targetPositions; + idList events; + idCameraFOV fov; + int activeTarget; + float totalTime; + float baseTime; + long startTime; + + bool cameraEdit; + bool editMode; +}; + +extern bool g_splineMode; + +extern idCameraDef *g_splineList; + +#endif /* !__SPLINES_H__ */ diff --git a/src/tools/reconstruction/CMakeLists.txt b/src/tools/reconstruction/CMakeLists.txt deleted file mode 100644 index 65855e5..0000000 --- a/src/tools/reconstruction/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -add_custom_target(reconstruction_manifests SOURCES - ${CMAKE_CURRENT_SOURCE_DIR}/Export-EvidenceManifest.ps1 - ${CMAKE_CURRENT_SOURCE_DIR}/Export-ReconstructionLedger.ps1 - ${CMAKE_CURRENT_SOURCE_DIR}/Seed-DoomImplementations.ps1 - ${CMAKE_CURRENT_SOURCE_DIR}/Seed-DoomHeaders.ps1 -) - -set_target_properties(reconstruction_manifests PROPERTIES FOLDER "Reconstruction") - -if(MSVC) - set(DIA_SDK_ROOT "${CMAKE_GENERATOR_INSTANCE}/DIA SDK") - if(EXISTS "${DIA_SDK_ROOT}/include/dia2.h") - add_executable(q4_pdb_inventory pdb_inventory.cpp) - target_include_directories(q4_pdb_inventory PRIVATE "${DIA_SDK_ROOT}/include") - target_link_directories(q4_pdb_inventory PRIVATE "${DIA_SDK_ROOT}/lib") - target_link_libraries(q4_pdb_inventory PRIVATE diaguids ole32 oleaut32) - target_compile_definitions(q4_pdb_inventory PRIVATE UNICODE _UNICODE) - set_target_properties(q4_pdb_inventory PROPERTIES FOLDER "Reconstruction") - else() - message(WARNING "DIA SDK not found; q4_pdb_inventory will not be built") - endif() -endif()